From 42bd9a99919ffdc7b69c38571031e97a98a8cebf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 28 Nov 2015 23:40:10 -0800 Subject: [PATCH 0001/5498] f2fs: catch up to v4.4-rc1 The last patch is: commit beaa57dd986d4f398728c060692fc2452895cfd8 Author: Chao Yu Date: Thu Oct 22 18:24:12 2015 +0800 f2fs: fix to skip shrinking extent nodes In f2fs_shrink_extent_tree we should stop shrink flow if we have already shrunk enough nodes in extent cache. Signed-off-by: Jaegeuk Kim --- fs/f2fs/Kconfig | 33 +- fs/f2fs/Makefile | 4 + fs/f2fs/acl.c | 152 +++- fs/f2fs/acl.h | 7 +- fs/f2fs/checkpoint.c | 458 +++++++----- fs/f2fs/crypto.c | 491 +++++++++++++ fs/f2fs/crypto_fname.c | 440 ++++++++++++ fs/f2fs/crypto_key.c | 254 +++++++ fs/f2fs/crypto_policy.c | 209 ++++++ fs/f2fs/data.c | 1318 ++++++++++++++++++++++++----------- fs/f2fs/debug.c | 125 +++- fs/f2fs/dir.c | 533 +++++++++----- fs/f2fs/extent_cache.c | 748 ++++++++++++++++++++ fs/f2fs/f2fs.h | 907 +++++++++++++++++++++--- fs/f2fs/f2fs_crypto.h | 151 ++++ fs/f2fs/file.c | 1033 +++++++++++++++++++++++---- fs/f2fs/gc.c | 368 ++++++---- fs/f2fs/gc.h | 34 +- fs/f2fs/hash.c | 3 +- fs/f2fs/inline.c | 583 ++++++++++++---- fs/f2fs/inode.c | 152 +++- fs/f2fs/namei.c | 518 +++++++++++--- fs/f2fs/node.c | 461 ++++++------ fs/f2fs/node.h | 80 +-- fs/f2fs/recovery.c | 169 +++-- fs/f2fs/segment.c | 682 ++++++++++++------ fs/f2fs/segment.h | 92 +-- fs/f2fs/shrinker.c | 139 ++++ fs/f2fs/super.c | 399 +++++++++-- fs/f2fs/trace.c | 159 +++++ fs/f2fs/trace.h | 46 ++ fs/f2fs/xattr.c | 18 +- fs/f2fs/xattr.h | 10 +- include/linux/f2fs_fs.h | 61 +- include/trace/events/f2fs.h | 359 ++++++++-- 35 files changed, 9041 insertions(+), 2155 deletions(-) create mode 100644 fs/f2fs/crypto.c create mode 100644 fs/f2fs/crypto_fname.c create mode 100644 fs/f2fs/crypto_key.c create mode 100644 fs/f2fs/crypto_policy.c create mode 100644 fs/f2fs/extent_cache.c create mode 100644 fs/f2fs/f2fs_crypto.h create mode 100644 fs/f2fs/shrinker.c create mode 100644 fs/f2fs/trace.c create mode 100644 fs/f2fs/trace.h diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig index 736a348509f7..b0a9dc929f88 100644 --- a/fs/f2fs/Kconfig +++ b/fs/f2fs/Kconfig @@ -1,5 +1,5 @@ config F2FS_FS - tristate "F2FS filesystem support (EXPERIMENTAL)" + tristate "F2FS filesystem support" depends on BLOCK help F2FS is based on Log-structured File System (LFS), which supports @@ -45,7 +45,7 @@ config F2FS_FS_POSIX_ACL default y help Posix Access Control Lists (ACLs) support permissions for users and - gourps beyond the owner/group/world scheme. + groups beyond the owner/group/world scheme. To learn more about Access Control Lists, visit the POSIX ACLs for Linux website . @@ -71,3 +71,32 @@ config F2FS_CHECK_FS Enables BUG_ONs which check the filesystem consistency in runtime. If you want to improve the performance, say N. + +config F2FS_FS_ENCRYPTION + bool "F2FS Encryption" + depends on F2FS_FS + depends on F2FS_FS_XATTR + select CRYPTO_AES + select CRYPTO_CBC + select CRYPTO_ECB + select CRYPTO_XTS + select CRYPTO_CTS + select CRYPTO_CTR + select CRYPTO_SHA256 + select KEYS + select ENCRYPTED_KEYS + help + Enable encryption of f2fs files and directories. This + feature is similar to ecryptfs, but it is more memory + efficient since it avoids caching the encrypted and + decrypted pages in the page cache. + +config F2FS_IO_TRACE + bool "F2FS IO tracer" + depends on F2FS_FS + depends on FUNCTION_TRACER + help + F2FS IO trace is based on a function trace, which gathers process + information and block IO patterns in the filesystem level. + + If unsure, say N. diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile index 2e35da12d292..08e101ed914c 100644 --- a/fs/f2fs/Makefile +++ b/fs/f2fs/Makefile @@ -2,6 +2,10 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o f2fs-y := dir.o file.o inode.o namei.o hash.o super.o inline.o f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o +f2fs-y += shrinker.o extent_cache.o f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o +f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o +f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o \ + crypto_key.o crypto_fname.o diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 83b9b5a8d112..c8f25f7241f0 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -62,7 +62,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) if (count == 0) return NULL; - acl = posix_acl_alloc(count, GFP_KERNEL); + acl = posix_acl_alloc(count, GFP_NOFS); if (!acl) return ERR_PTR(-ENOMEM); @@ -116,7 +116,7 @@ static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) int i; f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * - sizeof(struct f2fs_acl_entry), GFP_KERNEL); + sizeof(struct f2fs_acl_entry), GFP_NOFS); if (!f2fs_acl) return ERR_PTR(-ENOMEM); @@ -162,7 +162,8 @@ fail: return ERR_PTR(-EINVAL); } -struct posix_acl *f2fs_get_acl(struct inode *inode, int type) +static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type, + struct page *dpage) { int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; void *value = NULL; @@ -172,12 +173,13 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type) if (type == ACL_TYPE_ACCESS) name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; - retval = f2fs_getxattr(inode, name_index, "", NULL, 0); + retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage); if (retval > 0) { value = kmalloc(retval, GFP_F2FS_ZERO); if (!value) return ERR_PTR(-ENOMEM); - retval = f2fs_getxattr(inode, name_index, "", value, retval); + retval = f2fs_getxattr(inode, name_index, "", value, + retval, dpage); } if (retval > 0) @@ -194,6 +196,11 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type) return acl; } +struct posix_acl *f2fs_get_acl(struct inode *inode, int type) +{ + return __f2fs_get_acl(inode, type, NULL); +} + static int __f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl, struct page *ipage) { @@ -229,7 +236,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, if (acl) { value = f2fs_acl_to_disk(acl, &size); if (IS_ERR(value)) { - cond_clear_inode_flag(fi, FI_ACL_MODE); + clear_inode_flag(fi, FI_ACL_MODE); return (int)PTR_ERR(value); } } @@ -240,7 +247,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, if (!error) set_cached_acl(inode, type, acl); - cond_clear_inode_flag(fi, FI_ACL_MODE); + clear_inode_flag(fi, FI_ACL_MODE); return error; } @@ -249,12 +256,135 @@ int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type) return __f2fs_set_acl(inode, type, acl, NULL); } -int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage) +/* + * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create + * are copied from posix_acl.c + */ +static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl, + gfp_t flags) +{ + struct posix_acl *clone = NULL; + + if (acl) { + int size = sizeof(struct posix_acl) + acl->a_count * + sizeof(struct posix_acl_entry); + clone = kmemdup(acl, size, flags); + if (clone) + atomic_set(&clone->a_refcount, 1); + } + return clone; +} + +static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) +{ + struct posix_acl_entry *pa, *pe; + struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; + umode_t mode = *mode_p; + int not_equiv = 0; + + /* assert(atomic_read(acl->a_refcount) == 1); */ + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_perm &= (mode >> 6) | ~S_IRWXO; + mode &= (pa->e_perm << 6) | ~S_IRWXU; + break; + + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + + case ACL_GROUP_OBJ: + group_obj = pa; + break; + + case ACL_OTHER: + pa->e_perm &= mode | ~S_IRWXO; + mode &= pa->e_perm | ~S_IRWXO; + break; + + case ACL_MASK: + mask_obj = pa; + not_equiv = 1; + break; + + default: + return -EIO; + } + } + + if (mask_obj) { + mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (mask_obj->e_perm << 3) | ~S_IRWXG; + } else { + if (!group_obj) + return -EIO; + group_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (group_obj->e_perm << 3) | ~S_IRWXG; + } + + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; + return not_equiv; +} + +static int f2fs_acl_create(struct inode *dir, umode_t *mode, + struct posix_acl **default_acl, struct posix_acl **acl, + struct page *dpage) +{ + struct posix_acl *p; + struct posix_acl *clone; + int ret; + + *acl = NULL; + *default_acl = NULL; + + if (S_ISLNK(*mode) || !IS_POSIXACL(dir)) + return 0; + + p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage); + if (!p || p == ERR_PTR(-EOPNOTSUPP)) { + *mode &= ~current_umask(); + return 0; + } + if (IS_ERR(p)) + return PTR_ERR(p); + + clone = f2fs_acl_clone(p, GFP_NOFS); + if (!clone) + goto no_mem; + + ret = f2fs_acl_create_masq(clone, mode); + if (ret < 0) + goto no_mem_clone; + + if (ret == 0) + posix_acl_release(clone); + else + *acl = clone; + + if (!S_ISDIR(*mode)) + posix_acl_release(p); + else + *default_acl = p; + + return 0; + +no_mem_clone: + posix_acl_release(clone); +no_mem: + posix_acl_release(p); + return -ENOMEM; +} + +int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage, + struct page *dpage) { - struct posix_acl *default_acl, *acl; + struct posix_acl *default_acl = NULL, *acl = NULL; int error = 0; - error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); + error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage); if (error) return error; @@ -264,7 +394,7 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage) posix_acl_release(default_acl); } if (acl) { - if (error) + if (!error) error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl, ipage); posix_acl_release(acl); diff --git a/fs/f2fs/acl.h b/fs/f2fs/acl.h index e0864651cdc1..b2334d11dae8 100644 --- a/fs/f2fs/acl.h +++ b/fs/f2fs/acl.h @@ -37,15 +37,16 @@ struct f2fs_acl_header { #ifdef CONFIG_F2FS_FS_POSIX_ACL extern struct posix_acl *f2fs_get_acl(struct inode *, int); -extern int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type); -extern int f2fs_init_acl(struct inode *, struct inode *, struct page *); +extern int f2fs_set_acl(struct inode *, struct posix_acl *, int); +extern int f2fs_init_acl(struct inode *, struct inode *, struct page *, + struct page *); #else #define f2fs_check_acl NULL #define f2fs_get_acl NULL #define f2fs_set_acl NULL static inline int f2fs_init_acl(struct inode *inode, struct inode *dir, - struct page *page) + struct page *ipage, struct page *dpage) { return 0; } diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index dd10a031c052..463a67cc6cdb 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -20,10 +20,11 @@ #include "f2fs.h" #include "node.h" #include "segment.h" +#include "trace.h" #include static struct kmem_cache *ino_entry_slab; -static struct kmem_cache *inode_entry_slab; +struct kmem_cache *inode_entry_slab; /* * We guarantee no failure on the returned page. @@ -46,10 +47,21 @@ repeat: /* * We guarantee no failure on the returned page. */ -struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) +static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index, + bool is_meta) { struct address_space *mapping = META_MAPPING(sbi); struct page *page; + struct f2fs_io_info fio = { + .sbi = sbi, + .type = META, + .rw = READ_SYNC | REQ_META | REQ_PRIO, + .blk_addr = index, + .encrypted_page = NULL, + }; + + if (unlikely(!is_meta)) + fio.rw &= ~REQ_META; repeat: page = grab_cache_page(mapping, index); if (!page) { @@ -59,101 +71,124 @@ repeat: if (PageUptodate(page)) goto out; - if (f2fs_submit_page_bio(sbi, page, index, - READ_SYNC | REQ_META | REQ_PRIO)) + fio.page = page; + + if (f2fs_submit_page_bio(&fio)) { + f2fs_put_page(page, 1); goto repeat; + } lock_page(page); if (unlikely(page->mapping != mapping)) { f2fs_put_page(page, 1); goto repeat; } + + /* + * if there is any IO error when accessing device, make our filesystem + * readonly and make sure do not write checkpoint with non-uptodate + * meta page. + */ + if (unlikely(!PageUptodate(page))) + f2fs_stop_checkpoint(sbi); out: + mark_page_accessed(page); return page; } -struct page *get_meta_page_ra(struct f2fs_sb_info *sbi, pgoff_t index) +struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) { - bool readahead = false; - struct page *page; - - page = find_get_page(META_MAPPING(sbi), index); - if (!page || (page && !PageUptodate(page))) - readahead = true; - f2fs_put_page(page, 0); + return __get_meta_page(sbi, index, true); +} - if (readahead) - ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR); - return get_meta_page(sbi, index); +/* for POR only */ +struct page *get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index) +{ + return __get_meta_page(sbi, index, false); } -static inline block_t get_max_meta_blks(struct f2fs_sb_info *sbi, int type) +bool is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type) { switch (type) { case META_NAT: - return NM_I(sbi)->max_nid / NAT_ENTRY_PER_BLOCK; + break; case META_SIT: - return SIT_BLK_CNT(sbi); + if (unlikely(blkaddr >= SIT_BLK_CNT(sbi))) + return false; + break; case META_SSA: + if (unlikely(blkaddr >= MAIN_BLKADDR(sbi) || + blkaddr < SM_I(sbi)->ssa_blkaddr)) + return false; + break; case META_CP: - return 0; + if (unlikely(blkaddr >= SIT_I(sbi)->sit_base_addr || + blkaddr < __start_cp_addr(sbi))) + return false; + break; case META_POR: - return MAX_BLKADDR(sbi); + if (unlikely(blkaddr >= MAX_BLKADDR(sbi) || + blkaddr < MAIN_BLKADDR(sbi))) + return false; + break; default: BUG(); } + + return true; } /* * Readahead CP/NAT/SIT/SSA pages */ -int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type) +int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, + int type, bool sync) { block_t prev_blk_addr = 0; struct page *page; block_t blkno = start; - block_t max_blks = get_max_meta_blks(sbi, type); - struct f2fs_io_info fio = { + .sbi = sbi, .type = META, - .rw = READ_SYNC | REQ_META | REQ_PRIO + .rw = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : READA, + .encrypted_page = NULL, }; + if (unlikely(type == META_POR)) + fio.rw &= ~REQ_META; + for (; nrpages-- > 0; blkno++) { - block_t blk_addr; + + if (!is_valid_blkaddr(sbi, blkno, type)) + goto out; switch (type) { case META_NAT: - /* get nat block addr */ - if (unlikely(blkno >= max_blks)) + if (unlikely(blkno >= + NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid))) blkno = 0; - blk_addr = current_nat_addr(sbi, + /* get nat block addr */ + fio.blk_addr = current_nat_addr(sbi, blkno * NAT_ENTRY_PER_BLOCK); break; case META_SIT: /* get sit block addr */ - if (unlikely(blkno >= max_blks)) - goto out; - blk_addr = current_sit_addr(sbi, + fio.blk_addr = current_sit_addr(sbi, blkno * SIT_ENTRY_PER_BLOCK); - if (blkno != start && prev_blk_addr + 1 != blk_addr) + if (blkno != start && prev_blk_addr + 1 != fio.blk_addr) goto out; - prev_blk_addr = blk_addr; + prev_blk_addr = fio.blk_addr; break; case META_SSA: case META_CP: case META_POR: - if (unlikely(blkno >= max_blks)) - goto out; - if (unlikely(blkno < SEG0_BLKADDR(sbi))) - goto out; - blk_addr = blkno; + fio.blk_addr = blkno; break; default: BUG(); } - page = grab_cache_page(META_MAPPING(sbi), blk_addr); + page = grab_cache_page(META_MAPPING(sbi), fio.blk_addr); if (!page) continue; if (PageUptodate(page)) { @@ -161,7 +196,8 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type continue; } - f2fs_submit_page_mbio(sbi, page, blk_addr, &fio); + fio.page = page; + f2fs_submit_page_mbio(&fio); f2fs_put_page(page, 0); } out: @@ -169,6 +205,20 @@ out: return blkno - start; } +void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index) +{ + struct page *page; + bool readahead = false; + + page = find_get_page(META_MAPPING(sbi), index); + if (!page || (page && !PageUptodate(page))) + readahead = true; + f2fs_put_page(page, 0); + + if (readahead) + ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR, true); +} + static int f2fs_write_meta_page(struct page *page, struct writeback_control *wbc) { @@ -176,9 +226,9 @@ static int f2fs_write_meta_page(struct page *page, trace_f2fs_writepage(page, META); - if (unlikely(sbi->por_doing)) + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; - if (wbc->for_reclaim) + if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0)) goto redirty_out; if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; @@ -187,6 +237,9 @@ static int f2fs_write_meta_page(struct page *page, write_meta_page(sbi, page); dec_page_count(sbi, F2FS_DIRTY_META); unlock_page(page); + + if (wbc->for_reclaim) + f2fs_submit_merged_bio(sbi, META, WRITE); return 0; redirty_out: @@ -224,7 +277,7 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, long nr_to_write) { struct address_space *mapping = META_MAPPING(sbi); - pgoff_t index = 0, end = LONG_MAX; + pgoff_t index = 0, end = LONG_MAX, prev = LONG_MAX; struct pagevec pvec; long nwritten = 0; struct writeback_control wbc = { @@ -244,6 +297,13 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; + if (prev == LONG_MAX) + prev = page->index - 1; + if (nr_to_write != LONG_MAX && page->index != prev + 1) { + pagevec_release(&pvec); + goto stop; + } + lock_page(page); if (unlikely(page->mapping != mapping)) { @@ -259,18 +319,19 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - if (f2fs_write_meta_page(page, &wbc)) { + if (mapping->a_ops->writepage(page, &wbc)) { unlock_page(page); break; } nwritten++; + prev = page->index; if (unlikely(nwritten >= nr_to_write)) break; } pagevec_release(&pvec); cond_resched(); } - +stop: if (nwritten) f2fs_submit_merged_bio(sbi, type, WRITE); @@ -285,6 +346,8 @@ static int f2fs_set_meta_page_dirty(struct page *page) if (!PageDirty(page)) { __set_page_dirty_nobuffers(page); inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_META); + SetPagePrivate(page); + f2fs_trace_pid(page); return 1; } return 0; @@ -294,50 +357,58 @@ const struct address_space_operations f2fs_meta_aops = { .writepage = f2fs_write_meta_page, .writepages = f2fs_write_meta_pages, .set_page_dirty = f2fs_set_meta_page_dirty, + .invalidatepage = f2fs_invalidate_page, + .releasepage = f2fs_release_page, }; static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) { - struct ino_entry *e; + struct inode_management *im = &sbi->im[type]; + struct ino_entry *e, *tmp; + + tmp = f2fs_kmem_cache_alloc(ino_entry_slab, GFP_NOFS); retry: - spin_lock(&sbi->ino_lock[type]); + radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); - e = radix_tree_lookup(&sbi->ino_root[type], ino); + spin_lock(&im->ino_lock); + e = radix_tree_lookup(&im->ino_root, ino); if (!e) { - e = kmem_cache_alloc(ino_entry_slab, GFP_ATOMIC); - if (!e) { - spin_unlock(&sbi->ino_lock[type]); - goto retry; - } - if (radix_tree_insert(&sbi->ino_root[type], ino, e)) { - spin_unlock(&sbi->ino_lock[type]); - kmem_cache_free(ino_entry_slab, e); + e = tmp; + if (radix_tree_insert(&im->ino_root, ino, e)) { + spin_unlock(&im->ino_lock); + radix_tree_preload_end(); goto retry; } memset(e, 0, sizeof(struct ino_entry)); e->ino = ino; - list_add_tail(&e->list, &sbi->ino_list[type]); + list_add_tail(&e->list, &im->ino_list); + if (type != ORPHAN_INO) + im->ino_num++; } - spin_unlock(&sbi->ino_lock[type]); + spin_unlock(&im->ino_lock); + radix_tree_preload_end(); + + if (e != tmp) + kmem_cache_free(ino_entry_slab, tmp); } static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) { + struct inode_management *im = &sbi->im[type]; struct ino_entry *e; - spin_lock(&sbi->ino_lock[type]); - e = radix_tree_lookup(&sbi->ino_root[type], ino); + spin_lock(&im->ino_lock); + e = radix_tree_lookup(&im->ino_root, ino); if (e) { list_del(&e->list); - radix_tree_delete(&sbi->ino_root[type], ino); - if (type == ORPHAN_INO) - sbi->n_orphans--; - spin_unlock(&sbi->ino_lock[type]); + radix_tree_delete(&im->ino_root, ino); + im->ino_num--; + spin_unlock(&im->ino_lock); kmem_cache_free(ino_entry_slab, e); return; } - spin_unlock(&sbi->ino_lock[type]); + spin_unlock(&im->ino_lock); } void add_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type) @@ -355,10 +426,12 @@ void remove_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type) /* mode should be APPEND_INO or UPDATE_INO */ bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode) { + struct inode_management *im = &sbi->im[mode]; struct ino_entry *e; - spin_lock(&sbi->ino_lock[mode]); - e = radix_tree_lookup(&sbi->ino_root[mode], ino); - spin_unlock(&sbi->ino_lock[mode]); + + spin_lock(&im->ino_lock); + e = radix_tree_lookup(&im->ino_root, ino); + spin_unlock(&im->ino_lock); return e ? true : false; } @@ -368,36 +441,42 @@ void release_dirty_inode(struct f2fs_sb_info *sbi) int i; for (i = APPEND_INO; i <= UPDATE_INO; i++) { - spin_lock(&sbi->ino_lock[i]); - list_for_each_entry_safe(e, tmp, &sbi->ino_list[i], list) { + struct inode_management *im = &sbi->im[i]; + + spin_lock(&im->ino_lock); + list_for_each_entry_safe(e, tmp, &im->ino_list, list) { list_del(&e->list); - radix_tree_delete(&sbi->ino_root[i], e->ino); + radix_tree_delete(&im->ino_root, e->ino); kmem_cache_free(ino_entry_slab, e); + im->ino_num--; } - spin_unlock(&sbi->ino_lock[i]); + spin_unlock(&im->ino_lock); } } int acquire_orphan_inode(struct f2fs_sb_info *sbi) { + struct inode_management *im = &sbi->im[ORPHAN_INO]; int err = 0; - spin_lock(&sbi->ino_lock[ORPHAN_INO]); - if (unlikely(sbi->n_orphans >= sbi->max_orphans)) + spin_lock(&im->ino_lock); + if (unlikely(im->ino_num >= sbi->max_orphans)) err = -ENOSPC; else - sbi->n_orphans++; - spin_unlock(&sbi->ino_lock[ORPHAN_INO]); + im->ino_num++; + spin_unlock(&im->ino_lock); return err; } void release_orphan_inode(struct f2fs_sb_info *sbi) { - spin_lock(&sbi->ino_lock[ORPHAN_INO]); - f2fs_bug_on(sbi, sbi->n_orphans == 0); - sbi->n_orphans--; - spin_unlock(&sbi->ino_lock[ORPHAN_INO]); + struct inode_management *im = &sbi->im[ORPHAN_INO]; + + spin_lock(&im->ino_lock); + f2fs_bug_on(sbi, im->ino_num == 0); + im->ino_num--; + spin_unlock(&im->ino_lock); } void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) @@ -412,46 +491,58 @@ void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) __remove_ino_entry(sbi, ino, ORPHAN_INO); } -static void recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) +static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) { - struct inode *inode = f2fs_iget(sbi->sb, ino); - f2fs_bug_on(sbi, IS_ERR(inode)); + struct inode *inode; + + inode = f2fs_iget(sbi->sb, ino); + if (IS_ERR(inode)) { + /* + * there should be a bug that we can't find the entry + * to orphan inode. + */ + f2fs_bug_on(sbi, PTR_ERR(inode) == -ENOENT); + return PTR_ERR(inode); + } + clear_nlink(inode); /* truncate all the data during iput */ iput(inode); + return 0; } -void recover_orphan_inodes(struct f2fs_sb_info *sbi) +int recover_orphan_inodes(struct f2fs_sb_info *sbi) { - block_t start_blk, orphan_blkaddr, i, j; + block_t start_blk, orphan_blocks, i, j; + int err; if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG)) - return; - - sbi->por_doing = true; + return 0; - start_blk = __start_cp_addr(sbi) + 1 + - le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload); - orphan_blkaddr = __start_sum_addr(sbi) - 1; + start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); + orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi); - ra_meta_pages(sbi, start_blk, orphan_blkaddr, META_CP); + ra_meta_pages(sbi, start_blk, orphan_blocks, META_CP, true); - for (i = 0; i < orphan_blkaddr; i++) { + for (i = 0; i < orphan_blocks; i++) { struct page *page = get_meta_page(sbi, start_blk + i); struct f2fs_orphan_block *orphan_blk; orphan_blk = (struct f2fs_orphan_block *)page_address(page); for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { nid_t ino = le32_to_cpu(orphan_blk->ino[j]); - recover_orphan_inode(sbi, ino); + err = recover_orphan_inode(sbi, ino); + if (err) { + f2fs_put_page(page, 1); + return err; + } } f2fs_put_page(page, 1); } /* clear Orphan Flag */ clear_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG); - sbi->por_doing = false; - return; + return 0; } static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) @@ -459,28 +550,28 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) struct list_head *head; struct f2fs_orphan_block *orphan_blk = NULL; unsigned int nentries = 0; - unsigned short index; - unsigned short orphan_blocks = - (unsigned short)GET_ORPHAN_BLOCKS(sbi->n_orphans); + unsigned short index = 1; + unsigned short orphan_blocks; struct page *page = NULL; struct ino_entry *orphan = NULL; + struct inode_management *im = &sbi->im[ORPHAN_INO]; - for (index = 0; index < orphan_blocks; index++) - grab_meta_page(sbi, start_blk + index); + orphan_blocks = GET_ORPHAN_BLOCKS(im->ino_num); - index = 1; - spin_lock(&sbi->ino_lock[ORPHAN_INO]); - head = &sbi->ino_list[ORPHAN_INO]; + /* + * we don't need to do spin_lock(&im->ino_lock) here, since all the + * orphan inode operations are covered under f2fs_lock_op(). + * And, spin_lock should be avoided due to page operations below. + */ + head = &im->ino_list; /* loop for each orphan inode entry and write them in Jornal block */ list_for_each_entry(orphan, head, list) { if (!page) { - page = find_get_page(META_MAPPING(sbi), start_blk++); - f2fs_bug_on(sbi, !page); + page = grab_meta_page(sbi, start_blk++); orphan_blk = (struct f2fs_orphan_block *)page_address(page); memset(orphan_blk, 0, sizeof(*orphan_blk)); - f2fs_put_page(page, 0); } orphan_blk->ino[nentries++] = cpu_to_le32(orphan->ino); @@ -509,8 +600,6 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) set_page_dirty(page); f2fs_put_page(page, 1); } - - spin_unlock(&sbi->ino_lock[ORPHAN_INO]); } static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, @@ -532,7 +621,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, if (crc_offset >= blk_size) goto invalid_cp1; - crc = le32_to_cpu(*((__u32 *)((unsigned char *)cp_block + crc_offset))); + crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); if (!f2fs_crc_valid(crc, cp_block, crc_offset)) goto invalid_cp1; @@ -547,7 +636,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, if (crc_offset >= blk_size) goto invalid_cp2; - crc = le32_to_cpu(*((__u32 *)((unsigned char *)cp_block + crc_offset))); + crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); if (!f2fs_crc_valid(crc, cp_block, crc_offset)) goto invalid_cp2; @@ -573,7 +662,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) unsigned long blk_size = sbi->blocksize; unsigned long long cp1_version = 0, cp2_version = 0; unsigned long long cp_start_blk_no; - unsigned int cp_blks = 1 + le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload); + unsigned int cp_blks = 1 + __cp_payload(sbi); block_t cp_blk_no; int i; @@ -634,7 +723,7 @@ fail_no_cp: return -EINVAL; } -static int __add_dirty_inode(struct inode *inode, struct dir_inode_entry *new) +static int __add_dirty_inode(struct inode *inode, struct inode_entry *new) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -651,10 +740,11 @@ static int __add_dirty_inode(struct inode *inode, struct dir_inode_entry *new) void update_dirty_page(struct inode *inode, struct page *page) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct dir_inode_entry *new; + struct inode_entry *new; int ret = 0; - if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)) + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && + !S_ISLNK(inode->i_mode)) return; if (!S_ISDIR(inode->i_mode)) { @@ -675,12 +765,13 @@ void update_dirty_page(struct inode *inode, struct page *page) kmem_cache_free(inode_entry_slab, new); out: SetPagePrivate(page); + f2fs_trace_pid(page); } void add_dirty_dir_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct dir_inode_entry *new = + struct inode_entry *new = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS); int ret = 0; @@ -698,7 +789,7 @@ void add_dirty_dir_inode(struct inode *inode) void remove_dirty_dir_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct dir_inode_entry *entry; + struct inode_entry *entry; if (!S_ISDIR(inode->i_mode)) return; @@ -728,9 +819,12 @@ void remove_dirty_dir_inode(struct inode *inode) void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi) { struct list_head *head; - struct dir_inode_entry *entry; + struct inode_entry *entry; struct inode *inode; retry: + if (unlikely(f2fs_cp_error(sbi))) + return; + spin_lock(&sbi->dir_inode_lock); head = &sbi->dir_inode_list; @@ -738,7 +832,7 @@ retry: spin_unlock(&sbi->dir_inode_lock); return; } - entry = list_entry(head->next, struct dir_inode_entry, list); + entry = list_entry(head->next, struct inode_entry, list); inode = igrab(entry->inode); spin_unlock(&sbi->dir_inode_lock); if (inode) { @@ -750,6 +844,7 @@ retry: * wribacking dentry pages in the freeing inode. */ f2fs_submit_merged_bio(sbi, DATA, WRITE); + cond_resched(); } goto retry; } @@ -830,20 +925,22 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; nid_t last_nid = nm_i->next_scan_nid; block_t start_blk; - struct page *cp_page; unsigned int data_sum_blocks, orphan_blocks; __u32 crc32 = 0; - void *kaddr; int i; - int cp_payload_blks = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload); + int cp_payload_blks = __cp_payload(sbi); + block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg); + bool invalidate = false; /* * This avoids to conduct wrong roll-forward operations and uses * metapages, so should be called prior to sync_meta_pages below. */ - discard_next_dnode(sbi, NEXT_FREE_BLKADDR(sbi, curseg)); + if (discard_next_dnode(sbi, discard_blk)) + invalidate = true; /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { @@ -883,34 +980,41 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) ckpt->next_free_nid = cpu_to_le32(last_nid); /* 2 cp + n data seg summary + orphan inode blocks */ - data_sum_blocks = npages_for_summary_flush(sbi); + data_sum_blocks = npages_for_summary_flush(sbi, false); if (data_sum_blocks < NR_CURSEG_DATA_TYPE) set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); else clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); - orphan_blocks = GET_ORPHAN_BLOCKS(sbi->n_orphans); + orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num); ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + orphan_blocks); - if (cpc->reason == CP_UMOUNT) { - set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + if (__remain_node_summaries(cpc->reason)) ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS+ cp_payload_blks + data_sum_blocks + orphan_blocks + NR_CURSEG_NODE_TYPE); - } else { - clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + else ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS + cp_payload_blks + data_sum_blocks + orphan_blocks); - } - if (sbi->n_orphans) + if (cpc->reason == CP_UMOUNT) + set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + else + clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + + if (cpc->reason == CP_FASTBOOT) + set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + else + clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + + if (orphan_num) set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); else clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); - if (sbi->need_fsck) + if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) set_ckpt_flags(ckpt, CP_FSCK_FLAG); /* update SIT/NAT bitmap */ @@ -924,40 +1028,32 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) start_blk = __start_cp_addr(sbi); + /* need to wait for end_io results */ + wait_on_all_pages_writeback(sbi); + if (unlikely(f2fs_cp_error(sbi))) + return; + /* write out checkpoint buffer at block 0 */ - cp_page = grab_meta_page(sbi, start_blk++); - kaddr = page_address(cp_page); - memcpy(kaddr, ckpt, (1 << sbi->log_blocksize)); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); - - for (i = 1; i < 1 + cp_payload_blks; i++) { - cp_page = grab_meta_page(sbi, start_blk++); - kaddr = page_address(cp_page); - memcpy(kaddr, (char *)ckpt + i * F2FS_BLKSIZE, - (1 << sbi->log_blocksize)); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); - } + update_meta_page(sbi, ckpt, start_blk++); - if (sbi->n_orphans) { + for (i = 1; i < 1 + cp_payload_blks; i++) + update_meta_page(sbi, (char *)ckpt + i * F2FS_BLKSIZE, + start_blk++); + + if (orphan_num) { write_orphan_inodes(sbi, start_blk); start_blk += orphan_blocks; } write_data_summaries(sbi, start_blk); start_blk += data_sum_blocks; - if (cpc->reason == CP_UMOUNT) { + if (__remain_node_summaries(cpc->reason)) { write_node_summaries(sbi, start_blk); start_blk += NR_CURSEG_NODE_TYPE; } /* writeout checkpoint block */ - cp_page = grab_meta_page(sbi, start_blk); - kaddr = page_address(cp_page); - memcpy(kaddr, ckpt, (1 << sbi->log_blocksize)); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); + update_meta_page(sbi, ckpt, start_blk); /* wait for previous submitted node/meta pages writeback */ wait_on_all_pages_writeback(sbi); @@ -975,13 +1071,24 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* Here, we only have one bio having CP pack */ sync_meta_pages(sbi, META_FLUSH, LONG_MAX); + /* wait for previous submitted meta pages writeback */ + wait_on_all_pages_writeback(sbi); + + /* + * invalidate meta page which is used temporarily for zeroing out + * block at the end of warm node chain. + */ + if (invalidate) + invalidate_mapping_pages(META_MAPPING(sbi), discard_blk, + discard_blk); + release_dirty_inode(sbi); if (unlikely(f2fs_cp_error(sbi))) return; - clear_prefree_segments(sbi); - F2FS_RESET_SB_DIRT(sbi); + clear_prefree_segments(sbi, cpc); + clear_sbi_flag(sbi, SBI_IS_DIRTY); } /* @@ -992,14 +1099,19 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); unsigned long long ckpt_ver; - trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops"); - mutex_lock(&sbi->cp_mutex); - if (!sbi->s_dirty && cpc->reason != CP_DISCARD) + if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) && + (cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC || + (cpc->reason == CP_DISCARD && !sbi->discard_blks))) goto out; if (unlikely(f2fs_cp_error(sbi))) goto out; + if (f2fs_readonly(sbi->sb)) + goto out; + + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops"); + if (block_operations(sbi)) goto out; @@ -1026,6 +1138,13 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) unblock_operations(sbi); stat_inc_cp_count(sbi->stat_info); + + if (cpc->reason == CP_RECOVERY) + f2fs_msg(sbi->sb, KERN_NOTICE, + "checkpoint: version = %llx", ckpt_ver); + + /* do checkpoint periodically */ + sbi->cp_expires = round_jiffies_up(jiffies + HZ * sbi->cp_interval); out: mutex_unlock(&sbi->cp_mutex); trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint"); @@ -1036,20 +1155,17 @@ void init_ino_entry_info(struct f2fs_sb_info *sbi) int i; for (i = 0; i < MAX_INO_ENTRY; i++) { - INIT_RADIX_TREE(&sbi->ino_root[i], GFP_ATOMIC); - spin_lock_init(&sbi->ino_lock[i]); - INIT_LIST_HEAD(&sbi->ino_list[i]); + struct inode_management *im = &sbi->im[i]; + + INIT_RADIX_TREE(&im->ino_root, GFP_ATOMIC); + spin_lock_init(&im->ino_lock); + INIT_LIST_HEAD(&im->ino_list); + im->ino_num = 0; } - /* - * considering 512 blocks in a segment 8 blocks are needed for cp - * and log segment summaries. Remaining blocks are used to keep - * orphan entries with the limitation one reserved segment - * for cp pack we can have max 1020*504 orphan entries - */ - sbi->n_orphans = 0; sbi->max_orphans = (sbi->blocks_per_seg - F2FS_CP_PACKS - - NR_CURSEG_TYPE) * F2FS_ORPHANS_PER_BLOCK; + NR_CURSEG_TYPE - __cp_payload(sbi)) * + F2FS_ORPHANS_PER_BLOCK; } int __init create_checkpoint_caches(void) @@ -1058,8 +1174,8 @@ int __init create_checkpoint_caches(void) sizeof(struct ino_entry)); if (!ino_entry_slab) return -ENOMEM; - inode_entry_slab = f2fs_kmem_cache_create("f2fs_dirty_dir_entry", - sizeof(struct dir_inode_entry)); + inode_entry_slab = f2fs_kmem_cache_create("f2fs_inode_entry", + sizeof(struct inode_entry)); if (!inode_entry_slab) { kmem_cache_destroy(ino_entry_slab); return -ENOMEM; diff --git a/fs/f2fs/crypto.c b/fs/f2fs/crypto.c new file mode 100644 index 000000000000..4a62ef14e932 --- /dev/null +++ b/fs/f2fs/crypto.c @@ -0,0 +1,491 @@ +/* + * linux/fs/f2fs/crypto.c + * + * Copied from linux/fs/ext4/crypto.c + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * This contains encryption functions for f2fs + * + * Written by Michael Halcrow, 2014. + * + * Filename encryption additions + * Uday Savagaonkar, 2014 + * Encryption policy handling additions + * Ildar Muslukhov, 2014 + * Remove ext4_encrypted_zeroout(), + * add f2fs_restore_and_release_control_page() + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + * + * The usage of AES-XTS should conform to recommendations in NIST + * Special Publication 800-38E and IEEE P1619/D16. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "xattr.h" + +/* Encryption added and removed here! (L: */ + +static unsigned int num_prealloc_crypto_pages = 32; +static unsigned int num_prealloc_crypto_ctxs = 128; + +module_param(num_prealloc_crypto_pages, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_pages, + "Number of crypto pages to preallocate"); +module_param(num_prealloc_crypto_ctxs, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_ctxs, + "Number of crypto contexts to preallocate"); + +static mempool_t *f2fs_bounce_page_pool; + +static LIST_HEAD(f2fs_free_crypto_ctxs); +static DEFINE_SPINLOCK(f2fs_crypto_ctx_lock); + +static struct workqueue_struct *f2fs_read_workqueue; +static DEFINE_MUTEX(crypto_init); + +static struct kmem_cache *f2fs_crypto_ctx_cachep; +struct kmem_cache *f2fs_crypt_info_cachep; + +/** + * f2fs_release_crypto_ctx() - Releases an encryption context + * @ctx: The encryption context to release. + * + * If the encryption context was allocated from the pre-allocated pool, returns + * it to that pool. Else, frees it. + * + * If there's a bounce page in the context, this frees that. + */ +void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx) +{ + unsigned long flags; + + if (ctx->flags & F2FS_WRITE_PATH_FL && ctx->w.bounce_page) { + mempool_free(ctx->w.bounce_page, f2fs_bounce_page_pool); + ctx->w.bounce_page = NULL; + } + ctx->w.control_page = NULL; + if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) { + kmem_cache_free(f2fs_crypto_ctx_cachep, ctx); + } else { + spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags); + list_add(&ctx->free_list, &f2fs_free_crypto_ctxs); + spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags); + } +} + +/** + * f2fs_get_crypto_ctx() - Gets an encryption context + * @inode: The inode for which we are doing the crypto + * + * Allocates and initializes an encryption context. + * + * Return: An allocated and initialized encryption context on success; error + * value or NULL otherwise. + */ +struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode) +{ + struct f2fs_crypto_ctx *ctx = NULL; + unsigned long flags; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (ci == NULL) + return ERR_PTR(-ENOKEY); + + /* + * We first try getting the ctx from a free list because in + * the common case the ctx will have an allocated and + * initialized crypto tfm, so it's probably a worthwhile + * optimization. For the bounce page, we first try getting it + * from the kernel allocator because that's just about as fast + * as getting it from a list and because a cache of free pages + * should generally be a "last resort" option for a filesystem + * to be able to do its job. + */ + spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags); + ctx = list_first_entry_or_null(&f2fs_free_crypto_ctxs, + struct f2fs_crypto_ctx, free_list); + if (ctx) + list_del(&ctx->free_list); + spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags); + if (!ctx) { + ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_NOFS); + if (!ctx) + return ERR_PTR(-ENOMEM); + ctx->flags |= F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } else { + ctx->flags &= ~F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } + ctx->flags &= ~F2FS_WRITE_PATH_FL; + return ctx; +} + +/* + * Call f2fs_decrypt on every single page, reusing the encryption + * context. + */ +static void completion_pages(struct work_struct *work) +{ + struct f2fs_crypto_ctx *ctx = + container_of(work, struct f2fs_crypto_ctx, r.work); + struct bio *bio = ctx->r.bio; + struct bio_vec *bv; + int i; + + bio_for_each_segment_all(bv, bio, i) { + struct page *page = bv->bv_page; + int ret = f2fs_decrypt(ctx, page); + + if (ret) { + WARN_ON_ONCE(1); + SetPageError(page); + } else + SetPageUptodate(page); + unlock_page(page); + } + f2fs_release_crypto_ctx(ctx); + bio_put(bio); +} + +void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *ctx, struct bio *bio) +{ + INIT_WORK(&ctx->r.work, completion_pages); + ctx->r.bio = bio; + queue_work(f2fs_read_workqueue, &ctx->r.work); +} + +static void f2fs_crypto_destroy(void) +{ + struct f2fs_crypto_ctx *pos, *n; + + list_for_each_entry_safe(pos, n, &f2fs_free_crypto_ctxs, free_list) + kmem_cache_free(f2fs_crypto_ctx_cachep, pos); + INIT_LIST_HEAD(&f2fs_free_crypto_ctxs); + if (f2fs_bounce_page_pool) + mempool_destroy(f2fs_bounce_page_pool); + f2fs_bounce_page_pool = NULL; +} + +/** + * f2fs_crypto_initialize() - Set up for f2fs encryption. + * + * We only call this when we start accessing encrypted files, since it + * results in memory getting allocated that wouldn't otherwise be used. + * + * Return: Zero on success, non-zero otherwise. + */ +int f2fs_crypto_initialize(void) +{ + int i, res = -ENOMEM; + + if (f2fs_bounce_page_pool) + return 0; + + mutex_lock(&crypto_init); + if (f2fs_bounce_page_pool) + goto already_initialized; + + for (i = 0; i < num_prealloc_crypto_ctxs; i++) { + struct f2fs_crypto_ctx *ctx; + + ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_KERNEL); + if (!ctx) + goto fail; + list_add(&ctx->free_list, &f2fs_free_crypto_ctxs); + } + + /* must be allocated at the last step to avoid race condition above */ + f2fs_bounce_page_pool = + mempool_create_page_pool(num_prealloc_crypto_pages, 0); + if (!f2fs_bounce_page_pool) + goto fail; + +already_initialized: + mutex_unlock(&crypto_init); + return 0; +fail: + f2fs_crypto_destroy(); + mutex_unlock(&crypto_init); + return res; +} + +/** + * f2fs_exit_crypto() - Shutdown the f2fs encryption system + */ +void f2fs_exit_crypto(void) +{ + f2fs_crypto_destroy(); + + if (f2fs_read_workqueue) + destroy_workqueue(f2fs_read_workqueue); + if (f2fs_crypto_ctx_cachep) + kmem_cache_destroy(f2fs_crypto_ctx_cachep); + if (f2fs_crypt_info_cachep) + kmem_cache_destroy(f2fs_crypt_info_cachep); +} + +int __init f2fs_init_crypto(void) +{ + int res = -ENOMEM; + + f2fs_read_workqueue = alloc_workqueue("f2fs_crypto", WQ_HIGHPRI, 0); + if (!f2fs_read_workqueue) + goto fail; + + f2fs_crypto_ctx_cachep = KMEM_CACHE(f2fs_crypto_ctx, + SLAB_RECLAIM_ACCOUNT); + if (!f2fs_crypto_ctx_cachep) + goto fail; + + f2fs_crypt_info_cachep = KMEM_CACHE(f2fs_crypt_info, + SLAB_RECLAIM_ACCOUNT); + if (!f2fs_crypt_info_cachep) + goto fail; + + return 0; +fail: + f2fs_exit_crypto(); + return res; +} + +void f2fs_restore_and_release_control_page(struct page **page) +{ + struct f2fs_crypto_ctx *ctx; + struct page *bounce_page; + + /* The bounce data pages are unmapped. */ + if ((*page)->mapping) + return; + + /* The bounce data page is unmapped. */ + bounce_page = *page; + ctx = (struct f2fs_crypto_ctx *)page_private(bounce_page); + + /* restore control page */ + *page = ctx->w.control_page; + + f2fs_restore_control_page(bounce_page); +} + +void f2fs_restore_control_page(struct page *data_page) +{ + struct f2fs_crypto_ctx *ctx = + (struct f2fs_crypto_ctx *)page_private(data_page); + + set_page_private(data_page, (unsigned long)NULL); + ClearPagePrivate(data_page); + unlock_page(data_page); + f2fs_release_crypto_ctx(ctx); +} + +/** + * f2fs_crypt_complete() - The completion callback for page encryption + * @req: The asynchronous encryption request context + * @res: The result of the encryption operation + */ +static void f2fs_crypt_complete(struct crypto_async_request *req, int res) +{ + struct f2fs_completion_result *ecr = req->data; + + if (res == -EINPROGRESS) + return; + ecr->res = res; + complete(&ecr->completion); +} + +typedef enum { + F2FS_DECRYPT = 0, + F2FS_ENCRYPT, +} f2fs_direction_t; + +static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx, + struct inode *inode, + f2fs_direction_t rw, + pgoff_t index, + struct page *src_page, + struct page *dest_page) +{ + u8 xts_tweak[F2FS_XTS_TWEAK_SIZE]; + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct scatterlist dst, src; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", + __func__); + return -ENOMEM; + } + ablkcipher_request_set_callback( + req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + f2fs_crypt_complete, &ecr); + + BUILD_BUG_ON(F2FS_XTS_TWEAK_SIZE < sizeof(index)); + memcpy(xts_tweak, &index, sizeof(index)); + memset(&xts_tweak[sizeof(index)], 0, + F2FS_XTS_TWEAK_SIZE - sizeof(index)); + + sg_init_table(&dst, 1); + sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0); + sg_init_table(&src, 1); + sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0); + ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE, + xts_tweak); + if (rw == F2FS_DECRYPT) + res = crypto_ablkcipher_decrypt(req); + else + res = crypto_ablkcipher_encrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } + ablkcipher_request_free(req); + if (res) { + printk_ratelimited(KERN_ERR + "%s: crypto_ablkcipher_encrypt() returned %d\n", + __func__, res); + return res; + } + return 0; +} + +static struct page *alloc_bounce_page(struct f2fs_crypto_ctx *ctx) +{ + ctx->w.bounce_page = mempool_alloc(f2fs_bounce_page_pool, GFP_NOWAIT); + if (ctx->w.bounce_page == NULL) + return ERR_PTR(-ENOMEM); + ctx->flags |= F2FS_WRITE_PATH_FL; + return ctx->w.bounce_page; +} + +/** + * f2fs_encrypt() - Encrypts a page + * @inode: The inode for which the encryption should take place + * @plaintext_page: The page to encrypt. Must be locked. + * + * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx + * encryption context. + * + * Called on the page write path. The caller must call + * f2fs_restore_control_page() on the returned ciphertext page to + * release the bounce buffer and the encryption context. + * + * Return: An allocated page with the encrypted content on success. Else, an + * error value or NULL. + */ +struct page *f2fs_encrypt(struct inode *inode, + struct page *plaintext_page) +{ + struct f2fs_crypto_ctx *ctx; + struct page *ciphertext_page = NULL; + int err; + + BUG_ON(!PageLocked(plaintext_page)); + + ctx = f2fs_get_crypto_ctx(inode); + if (IS_ERR(ctx)) + return (struct page *)ctx; + + /* The encryption operation will require a bounce page. */ + ciphertext_page = alloc_bounce_page(ctx); + if (IS_ERR(ciphertext_page)) + goto err_out; + + ctx->w.control_page = plaintext_page; + err = f2fs_page_crypto(ctx, inode, F2FS_ENCRYPT, plaintext_page->index, + plaintext_page, ciphertext_page); + if (err) { + ciphertext_page = ERR_PTR(err); + goto err_out; + } + + SetPagePrivate(ciphertext_page); + set_page_private(ciphertext_page, (unsigned long)ctx); + lock_page(ciphertext_page); + return ciphertext_page; + +err_out: + f2fs_release_crypto_ctx(ctx); + return ciphertext_page; +} + +/** + * f2fs_decrypt() - Decrypts a page in-place + * @ctx: The encryption context. + * @page: The page to decrypt. Must be locked. + * + * Decrypts page in-place using the ctx encryption context. + * + * Called from the read completion callback. + * + * Return: Zero on success, non-zero otherwise. + */ +int f2fs_decrypt(struct f2fs_crypto_ctx *ctx, struct page *page) +{ + BUG_ON(!PageLocked(page)); + + return f2fs_page_crypto(ctx, page->mapping->host, + F2FS_DECRYPT, page->index, page, page); +} + +/* + * Convenience function which takes care of allocating and + * deallocating the encryption context + */ +int f2fs_decrypt_one(struct inode *inode, struct page *page) +{ + struct f2fs_crypto_ctx *ctx = f2fs_get_crypto_ctx(inode); + int ret; + + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + ret = f2fs_decrypt(ctx, page); + f2fs_release_crypto_ctx(ctx); + return ret; +} + +bool f2fs_valid_contents_enc_mode(uint32_t mode) +{ + return (mode == F2FS_ENCRYPTION_MODE_AES_256_XTS); +} + +/** + * f2fs_validate_encryption_key_size() - Validate the encryption key size + * @mode: The key mode. + * @size: The key size to validate. + * + * Return: The validated key size for @mode. Zero if invalid. + */ +uint32_t f2fs_validate_encryption_key_size(uint32_t mode, uint32_t size) +{ + if (size == f2fs_encryption_key_size(mode)) + return size; + return 0; +} diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c new file mode 100644 index 000000000000..ab377d496a39 --- /dev/null +++ b/fs/f2fs/crypto_fname.c @@ -0,0 +1,440 @@ +/* + * linux/fs/f2fs/crypto_fname.c + * + * Copied from linux/fs/ext4/crypto.c + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * This contains functions for filename crypto management in f2fs + * + * Written by Uday Savagaonkar, 2014. + * + * Adjust f2fs dentry structure + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "f2fs_crypto.h" +#include "xattr.h" + +/** + * f2fs_dir_crypt_complete() - + */ +static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res) +{ + struct f2fs_completion_result *ecr = req->data; + + if (res == -EINPROGRESS) + return; + ecr->res = res; + complete(&ecr->completion); +} + +bool f2fs_valid_filenames_enc_mode(uint32_t mode) +{ + return (mode == F2FS_ENCRYPTION_MODE_AES_256_CTS); +} + +static unsigned max_name_len(struct inode *inode) +{ + return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize : + F2FS_NAME_LEN; +} + +/** + * f2fs_fname_encrypt() - + * + * This function encrypts the input filename, and returns the length of the + * ciphertext. Errors are returned as negative numbers. We trust the caller to + * allocate sufficient memory to oname string. + */ +static int f2fs_fname_encrypt(struct inode *inode, + const struct qstr *iname, struct f2fs_str *oname) +{ + u32 ciphertext_len; + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + char iv[F2FS_CRYPTO_BLOCK_SIZE]; + struct scatterlist src_sg, dst_sg; + int padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); + char *workbuf, buf[32], *alloc_buf = NULL; + unsigned lim = max_name_len(inode); + + if (iname->len <= 0 || iname->len > lim) + return -EIO; + + ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ? + F2FS_CRYPTO_BLOCK_SIZE : iname->len; + ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding); + ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len; + + if (ciphertext_len <= sizeof(buf)) { + workbuf = buf; + } else { + alloc_buf = kmalloc(ciphertext_len, GFP_NOFS); + if (!alloc_buf) + return -ENOMEM; + workbuf = alloc_buf; + } + + /* Allocate request */ + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", __func__); + kfree(alloc_buf); + return -ENOMEM; + } + ablkcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + f2fs_dir_crypt_complete, &ecr); + + /* Copy the input */ + memcpy(workbuf, iname->name, iname->len); + if (iname->len < ciphertext_len) + memset(workbuf + iname->len, 0, ciphertext_len - iname->len); + + /* Initialize IV */ + memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); + + /* Create encryption request */ + sg_init_one(&src_sg, workbuf, ciphertext_len); + sg_init_one(&dst_sg, oname->name, ciphertext_len); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); + res = crypto_ablkcipher_encrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } + kfree(alloc_buf); + ablkcipher_request_free(req); + if (res < 0) { + printk_ratelimited(KERN_ERR + "%s: Error (error code %d)\n", __func__, res); + } + oname->len = ciphertext_len; + return res; +} + +/* + * f2fs_fname_decrypt() + * This function decrypts the input filename, and returns + * the length of the plaintext. + * Errors are returned as negative numbers. + * We trust the caller to allocate sufficient memory to oname string. + */ +static int f2fs_fname_decrypt(struct inode *inode, + const struct f2fs_str *iname, struct f2fs_str *oname) +{ + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct scatterlist src_sg, dst_sg; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + char iv[F2FS_CRYPTO_BLOCK_SIZE]; + unsigned lim = max_name_len(inode); + + if (iname->len <= 0 || iname->len > lim) + return -EIO; + + /* Allocate request */ + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", __func__); + return -ENOMEM; + } + ablkcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + f2fs_dir_crypt_complete, &ecr); + + /* Initialize IV */ + memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); + + /* Create decryption request */ + sg_init_one(&src_sg, iname->name, iname->len); + sg_init_one(&dst_sg, oname->name, oname->len); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); + res = crypto_ablkcipher_decrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } + ablkcipher_request_free(req); + if (res < 0) { + printk_ratelimited(KERN_ERR + "%s: Error in f2fs_fname_decrypt (error code %d)\n", + __func__, res); + return res; + } + + oname->len = strnlen(oname->name, iname->len); + return oname->len; +} + +static const char *lookup_table = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; + +/** + * f2fs_fname_encode_digest() - + * + * Encodes the input digest using characters from the set [a-zA-Z0-9_+]. + * The encoded string is roughly 4/3 times the size of the input string. + */ +static int digest_encode(const char *src, int len, char *dst) +{ + int i = 0, bits = 0, ac = 0; + char *cp = dst; + + while (i < len) { + ac += (((unsigned char) src[i]) << bits); + bits += 8; + do { + *cp++ = lookup_table[ac & 0x3f]; + ac >>= 6; + bits -= 6; + } while (bits >= 6); + i++; + } + if (bits) + *cp++ = lookup_table[ac & 0x3f]; + return cp - dst; +} + +static int digest_decode(const char *src, int len, char *dst) +{ + int i = 0, bits = 0, ac = 0; + const char *p; + char *cp = dst; + + while (i < len) { + p = strchr(lookup_table, src[i]); + if (p == NULL || src[i] == 0) + return -2; + ac += (p - lookup_table) << bits; + bits += 6; + if (bits >= 8) { + *cp++ = ac & 0xff; + ac >>= 8; + bits -= 8; + } + i++; + } + if (ac) + return -1; + return cp - dst; +} + +/** + * f2fs_fname_crypto_round_up() - + * + * Return: The next multiple of block size + */ +u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize) +{ + return ((size + blksize - 1) / blksize) * blksize; +} + +/** + * f2fs_fname_crypto_alloc_obuff() - + * + * Allocates an output buffer that is sufficient for the crypto operation + * specified by the context and the direction. + */ +int f2fs_fname_crypto_alloc_buffer(struct inode *inode, + u32 ilen, struct f2fs_str *crypto_str) +{ + unsigned int olen; + int padding = 16; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (ci) + padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); + if (padding < F2FS_CRYPTO_BLOCK_SIZE) + padding = F2FS_CRYPTO_BLOCK_SIZE; + olen = f2fs_fname_crypto_round_up(ilen, padding); + crypto_str->len = olen; + if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2) + olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2; + /* Allocated buffer can hold one more character to null-terminate the + * string */ + crypto_str->name = kmalloc(olen + 1, GFP_NOFS); + if (!(crypto_str->name)) + return -ENOMEM; + return 0; +} + +/** + * f2fs_fname_crypto_free_buffer() - + * + * Frees the buffer allocated for crypto operation. + */ +void f2fs_fname_crypto_free_buffer(struct f2fs_str *crypto_str) +{ + if (!crypto_str) + return; + kfree(crypto_str->name); + crypto_str->name = NULL; +} + +/** + * f2fs_fname_disk_to_usr() - converts a filename from disk space to user space + */ +int f2fs_fname_disk_to_usr(struct inode *inode, + f2fs_hash_t *hash, + const struct f2fs_str *iname, + struct f2fs_str *oname) +{ + const struct qstr qname = FSTR_TO_QSTR(iname); + char buf[24]; + int ret; + + if (is_dot_dotdot(&qname)) { + oname->name[0] = '.'; + oname->name[iname->len - 1] = '.'; + oname->len = iname->len; + return oname->len; + } + + if (F2FS_I(inode)->i_crypt_info) + return f2fs_fname_decrypt(inode, iname, oname); + + if (iname->len <= F2FS_FNAME_CRYPTO_DIGEST_SIZE) { + ret = digest_encode(iname->name, iname->len, oname->name); + oname->len = ret; + return ret; + } + if (hash) { + memcpy(buf, hash, 4); + memset(buf + 4, 0, 4); + } else + memset(buf, 0, 8); + memcpy(buf + 8, iname->name + iname->len - 16, 16); + oname->name[0] = '_'; + ret = digest_encode(buf, 24, oname->name + 1); + oname->len = ret + 1; + return ret + 1; +} + +/** + * f2fs_fname_usr_to_disk() - converts a filename from user space to disk space + */ +int f2fs_fname_usr_to_disk(struct inode *inode, + const struct qstr *iname, + struct f2fs_str *oname) +{ + int res; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (is_dot_dotdot(iname)) { + oname->name[0] = '.'; + oname->name[iname->len - 1] = '.'; + oname->len = iname->len; + return oname->len; + } + + if (ci) { + res = f2fs_fname_encrypt(inode, iname, oname); + return res; + } + /* Without a proper key, a user is not allowed to modify the filenames + * in a directory. Consequently, a user space name cannot be mapped to + * a disk-space name */ + return -EACCES; +} + +int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct f2fs_filename *fname) +{ + struct f2fs_crypt_info *ci; + int ret = 0, bigname = 0; + + memset(fname, 0, sizeof(struct f2fs_filename)); + fname->usr_fname = iname; + + if (!f2fs_encrypted_inode(dir) || is_dot_dotdot(iname)) { + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; + } + ret = f2fs_get_encryption_info(dir); + if (ret) + return ret; + ci = F2FS_I(dir)->i_crypt_info; + if (ci) { + ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len, + &fname->crypto_buf); + if (ret < 0) + return ret; + ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf); + if (ret < 0) + goto errout; + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; + return 0; + } + if (!lookup) + return -EACCES; + + /* We don't have the key and we are doing a lookup; decode the + * user-supplied name + */ + if (iname->name[0] == '_') + bigname = 1; + if ((bigname && (iname->len != 33)) || + (!bigname && (iname->len > 43))) + return -ENOENT; + + fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); + if (fname->crypto_buf.name == NULL) + return -ENOMEM; + ret = digest_decode(iname->name + bigname, iname->len - bigname, + fname->crypto_buf.name); + if (ret < 0) { + ret = -ENOENT; + goto errout; + } + fname->crypto_buf.len = ret; + if (bigname) { + memcpy(&fname->hash, fname->crypto_buf.name, 4); + } else { + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; + } + return 0; +errout: + f2fs_fname_crypto_free_buffer(&fname->crypto_buf); + return ret; +} + +void f2fs_fname_free_filename(struct f2fs_filename *fname) +{ + kfree(fname->crypto_buf.name); + fname->crypto_buf.name = NULL; + fname->usr_fname = NULL; + fname->disk_name.name = NULL; +} diff --git a/fs/f2fs/crypto_key.c b/fs/f2fs/crypto_key.c new file mode 100644 index 000000000000..9f77de2ef317 --- /dev/null +++ b/fs/f2fs/crypto_key.c @@ -0,0 +1,254 @@ +/* + * linux/fs/f2fs/crypto_key.c + * + * Copied from linux/fs/f2fs/crypto_key.c + * + * Copyright (C) 2015, Google, Inc. + * + * This contains encryption key functions for f2fs + * + * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "xattr.h" + +static void derive_crypt_complete(struct crypto_async_request *req, int rc) +{ + struct f2fs_completion_result *ecr = req->data; + + if (rc == -EINPROGRESS) + return; + + ecr->res = rc; + complete(&ecr->completion); +} + +/** + * f2fs_derive_key_aes() - Derive a key using AES-128-ECB + * @deriving_key: Encryption key used for derivatio. + * @source_key: Source key to which to apply derivation. + * @derived_key: Derived key. + * + * Return: Zero on success; non-zero otherwise. + */ +static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE], + char source_key[F2FS_AES_256_XTS_KEY_SIZE], + char derived_key[F2FS_AES_256_XTS_KEY_SIZE]) +{ + int res = 0; + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct scatterlist src_sg, dst_sg; + struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, + 0); + + if (IS_ERR(tfm)) { + res = PTR_ERR(tfm); + tfm = NULL; + goto out; + } + crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY); + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + res = -ENOMEM; + goto out; + } + ablkcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + derive_crypt_complete, &ecr); + res = crypto_ablkcipher_setkey(tfm, deriving_key, + F2FS_AES_128_ECB_KEY_SIZE); + if (res < 0) + goto out; + + sg_init_one(&src_sg, source_key, F2FS_AES_256_XTS_KEY_SIZE); + sg_init_one(&dst_sg, derived_key, F2FS_AES_256_XTS_KEY_SIZE); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, + F2FS_AES_256_XTS_KEY_SIZE, NULL); + res = crypto_ablkcipher_encrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } +out: + if (req) + ablkcipher_request_free(req); + if (tfm) + crypto_free_ablkcipher(tfm); + return res; +} + +static void f2fs_free_crypt_info(struct f2fs_crypt_info *ci) +{ + if (!ci) + return; + + key_put(ci->ci_keyring_key); + crypto_free_ablkcipher(ci->ci_ctfm); + kmem_cache_free(f2fs_crypt_info_cachep, ci); +} + +void f2fs_free_encryption_info(struct inode *inode, struct f2fs_crypt_info *ci) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_crypt_info *prev; + + if (ci == NULL) + ci = ACCESS_ONCE(fi->i_crypt_info); + if (ci == NULL) + return; + prev = cmpxchg(&fi->i_crypt_info, ci, NULL); + if (prev != ci) + return; + + f2fs_free_crypt_info(ci); +} + +int _f2fs_get_encryption_info(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_crypt_info *crypt_info; + char full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + + (F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1]; + struct key *keyring_key = NULL; + struct f2fs_encryption_key *master_key; + struct f2fs_encryption_context ctx; + struct user_key_payload *ukp; + struct crypto_ablkcipher *ctfm; + const char *cipher_str; + char raw_key[F2FS_MAX_KEY_SIZE]; + char mode; + int res; + + res = f2fs_crypto_initialize(); + if (res) + return res; +retry: + crypt_info = ACCESS_ONCE(fi->i_crypt_info); + if (crypt_info) { + if (!crypt_info->ci_keyring_key || + key_validate(crypt_info->ci_keyring_key) == 0) + return 0; + f2fs_free_encryption_info(inode, crypt_info); + goto retry; + } + + res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + &ctx, sizeof(ctx), NULL); + if (res < 0) + return res; + else if (res != sizeof(ctx)) + return -EINVAL; + res = 0; + + crypt_info = kmem_cache_alloc(f2fs_crypt_info_cachep, GFP_NOFS); + if (!crypt_info) + return -ENOMEM; + + crypt_info->ci_flags = ctx.flags; + crypt_info->ci_data_mode = ctx.contents_encryption_mode; + crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; + crypt_info->ci_ctfm = NULL; + crypt_info->ci_keyring_key = NULL; + memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, + sizeof(crypt_info->ci_master_key)); + if (S_ISREG(inode->i_mode)) + mode = crypt_info->ci_data_mode; + else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + mode = crypt_info->ci_filename_mode; + else + BUG(); + + switch (mode) { + case F2FS_ENCRYPTION_MODE_AES_256_XTS: + cipher_str = "xts(aes)"; + break; + case F2FS_ENCRYPTION_MODE_AES_256_CTS: + cipher_str = "cts(cbc(aes))"; + break; + default: + printk_once(KERN_WARNING + "f2fs: unsupported key mode %d (ino %u)\n", + mode, (unsigned) inode->i_ino); + res = -ENOKEY; + goto out; + } + + memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX, + F2FS_KEY_DESC_PREFIX_SIZE); + sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE, + "%*phN", F2FS_KEY_DESCRIPTOR_SIZE, + ctx.master_key_descriptor); + full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + + (2 * F2FS_KEY_DESCRIPTOR_SIZE)] = '\0'; + keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); + if (IS_ERR(keyring_key)) { + res = PTR_ERR(keyring_key); + keyring_key = NULL; + goto out; + } + crypt_info->ci_keyring_key = keyring_key; + BUG_ON(keyring_key->type != &key_type_logon); + ukp = ((struct user_key_payload *)keyring_key->payload.data); + if (ukp->datalen != sizeof(struct f2fs_encryption_key)) { + res = -EINVAL; + goto out; + } + master_key = (struct f2fs_encryption_key *)ukp->data; + BUILD_BUG_ON(F2FS_AES_128_ECB_KEY_SIZE != + F2FS_KEY_DERIVATION_NONCE_SIZE); + BUG_ON(master_key->size != F2FS_AES_256_XTS_KEY_SIZE); + res = f2fs_derive_key_aes(ctx.nonce, master_key->raw, + raw_key); + if (res) + goto out; + + ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); + if (!ctfm || IS_ERR(ctfm)) { + res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; + printk(KERN_DEBUG + "%s: error %d (inode %u) allocating crypto tfm\n", + __func__, res, (unsigned) inode->i_ino); + goto out; + } + crypt_info->ci_ctfm = ctfm; + crypto_ablkcipher_clear_flags(ctfm, ~0); + crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), + CRYPTO_TFM_REQ_WEAK_KEY); + res = crypto_ablkcipher_setkey(ctfm, raw_key, + f2fs_encryption_key_size(mode)); + if (res) + goto out; + + memzero_explicit(raw_key, sizeof(raw_key)); + if (cmpxchg(&fi->i_crypt_info, NULL, crypt_info) != NULL) { + f2fs_free_crypt_info(crypt_info); + goto retry; + } + return 0; + +out: + if (res == -ENOKEY && !S_ISREG(inode->i_mode)) + res = 0; + + f2fs_free_crypt_info(crypt_info); + memzero_explicit(raw_key, sizeof(raw_key)); + return res; +} + +int f2fs_has_encryption_key(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + + return (fi->i_crypt_info != NULL); +} diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c new file mode 100644 index 000000000000..d4a96af513c2 --- /dev/null +++ b/fs/f2fs/crypto_policy.c @@ -0,0 +1,209 @@ +/* + * copied from linux/fs/ext4/crypto_policy.c + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility. + * + * This contains encryption policy functions for f2fs with some modifications + * to support f2fs-specific xattr APIs. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ +#include +#include +#include +#include + +#include "f2fs.h" +#include "xattr.h" + +static int f2fs_inode_has_encryption_context(struct inode *inode) +{ + int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0, NULL); + return (res > 0); +} + +/* + * check whether the policy is consistent with the encryption context + * for the inode + */ +static int f2fs_is_encryption_context_consistent_with_policy( + struct inode *inode, const struct f2fs_encryption_policy *policy) +{ + struct f2fs_encryption_context ctx; + int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, + sizeof(ctx), NULL); + + if (res != sizeof(ctx)) + return 0; + + return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor, + F2FS_KEY_DESCRIPTOR_SIZE) == 0 && + (ctx.flags == policy->flags) && + (ctx.contents_encryption_mode == + policy->contents_encryption_mode) && + (ctx.filenames_encryption_mode == + policy->filenames_encryption_mode)); +} + +static int f2fs_create_encryption_context_from_policy( + struct inode *inode, const struct f2fs_encryption_policy *policy) +{ + struct f2fs_encryption_context ctx; + + ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1; + memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, + F2FS_KEY_DESCRIPTOR_SIZE); + + if (!f2fs_valid_contents_enc_mode(policy->contents_encryption_mode)) { + printk(KERN_WARNING + "%s: Invalid contents encryption mode %d\n", __func__, + policy->contents_encryption_mode); + return -EINVAL; + } + + if (!f2fs_valid_filenames_enc_mode(policy->filenames_encryption_mode)) { + printk(KERN_WARNING + "%s: Invalid filenames encryption mode %d\n", __func__, + policy->filenames_encryption_mode); + return -EINVAL; + } + + if (policy->flags & ~F2FS_POLICY_FLAGS_VALID) + return -EINVAL; + + ctx.contents_encryption_mode = policy->contents_encryption_mode; + ctx.filenames_encryption_mode = policy->filenames_encryption_mode; + ctx.flags = policy->flags; + BUILD_BUG_ON(sizeof(ctx.nonce) != F2FS_KEY_DERIVATION_NONCE_SIZE); + get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE); + + return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, + sizeof(ctx), NULL, XATTR_CREATE); +} + +int f2fs_process_policy(const struct f2fs_encryption_policy *policy, + struct inode *inode) +{ + if (policy->version != 0) + return -EINVAL; + + if (!S_ISDIR(inode->i_mode)) + return -EINVAL; + + if (!f2fs_inode_has_encryption_context(inode)) { + if (!f2fs_empty_dir(inode)) + return -ENOTEMPTY; + return f2fs_create_encryption_context_from_policy(inode, + policy); + } + + if (f2fs_is_encryption_context_consistent_with_policy(inode, policy)) + return 0; + + printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n", + __func__); + return -EINVAL; +} + +int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy) +{ + struct f2fs_encryption_context ctx; + int res; + + if (!f2fs_encrypted_inode(inode)) + return -ENODATA; + + res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + &ctx, sizeof(ctx), NULL); + if (res != sizeof(ctx)) + return -ENODATA; + if (ctx.format != F2FS_ENCRYPTION_CONTEXT_FORMAT_V1) + return -EINVAL; + + policy->version = 0; + policy->contents_encryption_mode = ctx.contents_encryption_mode; + policy->filenames_encryption_mode = ctx.filenames_encryption_mode; + policy->flags = ctx.flags; + memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor, + F2FS_KEY_DESCRIPTOR_SIZE); + return 0; +} + +int f2fs_is_child_context_consistent_with_parent(struct inode *parent, + struct inode *child) +{ + struct f2fs_crypt_info *parent_ci, *child_ci; + int res; + + if ((parent == NULL) || (child == NULL)) { + pr_err("parent %p child %p\n", parent, child); + BUG_ON(1); + } + + /* no restrictions if the parent directory is not encrypted */ + if (!f2fs_encrypted_inode(parent)) + return 1; + /* if the child directory is not encrypted, this is always a problem */ + if (!f2fs_encrypted_inode(child)) + return 0; + res = f2fs_get_encryption_info(parent); + if (res) + return 0; + res = f2fs_get_encryption_info(child); + if (res) + return 0; + parent_ci = F2FS_I(parent)->i_crypt_info; + child_ci = F2FS_I(child)->i_crypt_info; + if (!parent_ci && !child_ci) + return 1; + if (!parent_ci || !child_ci) + return 0; + + return (memcmp(parent_ci->ci_master_key, + child_ci->ci_master_key, + F2FS_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ci->ci_data_mode == child_ci->ci_data_mode) && + (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && + (parent_ci->ci_flags == child_ci->ci_flags)); +} + +/** + * f2fs_inherit_context() - Sets a child context from its parent + * @parent: Parent inode from which the context is inherited. + * @child: Child inode that inherits the context from @parent. + * + * Return: Zero on success, non-zero otherwise + */ +int f2fs_inherit_context(struct inode *parent, struct inode *child, + struct page *ipage) +{ + struct f2fs_encryption_context ctx; + struct f2fs_crypt_info *ci; + int res; + + res = f2fs_get_encryption_info(parent); + if (res < 0) + return res; + + ci = F2FS_I(parent)->i_crypt_info; + BUG_ON(ci == NULL); + + ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1; + + ctx.contents_encryption_mode = ci->ci_data_mode; + ctx.filenames_encryption_mode = ci->ci_filename_mode; + ctx.flags = ci->ci_flags; + memcpy(ctx.master_key_descriptor, ci->ci_master_key, + F2FS_KEY_DESCRIPTOR_SIZE); + + get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE); + return f2fs_setxattr(child, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, + sizeof(ctx), ipage, XATTR_CREATE); +} diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f988b01b6f89..ec69f207b708 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -15,13 +15,17 @@ #include #include #include +#include #include #include #include +#include +#include #include "f2fs.h" #include "node.h" #include "segment.h" +#include "trace.h" #include static void f2fs_read_end_io(struct bio *bio, int err) @@ -29,6 +33,15 @@ static void f2fs_read_end_io(struct bio *bio, int err) struct bio_vec *bvec; int i; + if (f2fs_bio_encrypted(bio)) { + if (err) { + f2fs_release_crypto_ctx(bio->bi_private); + } else { + f2fs_end_io_crypto_work(bio->bi_private, bio); + return; + } + } + bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; @@ -52,6 +65,8 @@ static void f2fs_write_end_io(struct bio *bio, int err) bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; + f2fs_restore_and_release_control_page(&page); + if (unlikely(err)) { set_page_dirty(page); set_bit(AS_EIO, &page->mapping->flags); @@ -61,11 +76,6 @@ static void f2fs_write_end_io(struct bio *bio, int err) dec_page_count(sbi, F2FS_WRITEBACK); } - if (sbi->wait_io) { - complete(sbi->wait_io); - sbi->wait_io = NULL; - } - if (!get_pages(sbi, F2FS_WRITEBACK) && !list_empty(&sbi->cp_wait.task_list)) wake_up(&sbi->cp_wait); @@ -81,13 +91,12 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, { struct bio *bio; - /* No failure on bio allocation */ - bio = bio_alloc(GFP_NOIO, npages); + bio = f2fs_bio_alloc(npages); bio->bi_bdev = sbi->sb->s_bdev; bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(blk_addr); bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; - bio->bi_private = sbi; + bio->bi_private = is_read ? NULL : sbi; return bio; } @@ -95,34 +104,16 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, static void __submit_merged_bio(struct f2fs_bio_info *io) { struct f2fs_io_info *fio = &io->fio; - int rw; if (!io->bio) return; - rw = fio->rw; - - if (is_read_io(rw)) { - trace_f2fs_submit_read_bio(io->sbi->sb, rw, - fio->type, io->bio); - submit_bio(rw, io->bio); - } else { - trace_f2fs_submit_write_bio(io->sbi->sb, rw, - fio->type, io->bio); - /* - * META_FLUSH is only from the checkpoint procedure, and we - * should wait this metadata bio for FS consistency. - */ - if (fio->type == META_FLUSH) { - DECLARE_COMPLETION_ONSTACK(wait); - io->sbi->wait_io = &wait; - submit_bio(rw, io->bio); - wait_for_completion(&wait); - } else { - submit_bio(rw, io->bio); - } - } + if (is_read_io(fio->rw)) + trace_f2fs_submit_read_bio(io->sbi->sb, fio, io->bio); + else + trace_f2fs_submit_write_bio(io->sbi->sb, fio, io->bio); + submit_bio(fio->rw, io->bio); io->bio = NULL; } @@ -152,63 +143,67 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, * Fill the locked page with data located in the block address. * Return unlocked page. */ -int f2fs_submit_page_bio(struct f2fs_sb_info *sbi, struct page *page, - block_t blk_addr, int rw) +int f2fs_submit_page_bio(struct f2fs_io_info *fio) { struct bio *bio; + struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page; - trace_f2fs_submit_page_bio(page, blk_addr, rw); + trace_f2fs_submit_page_bio(page, fio); + f2fs_trace_ios(fio, 0); /* Allocate a new bio */ - bio = __bio_alloc(sbi, blk_addr, 1, is_read_io(rw)); + bio = __bio_alloc(fio->sbi, fio->blk_addr, 1, is_read_io(fio->rw)); if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { bio_put(bio); - f2fs_put_page(page, 1); return -EFAULT; } - submit_bio(rw, bio); + submit_bio(fio->rw, bio); return 0; } -void f2fs_submit_page_mbio(struct f2fs_sb_info *sbi, struct page *page, - block_t blk_addr, struct f2fs_io_info *fio) +void f2fs_submit_page_mbio(struct f2fs_io_info *fio) { + struct f2fs_sb_info *sbi = fio->sbi; enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); struct f2fs_bio_info *io; bool is_read = is_read_io(fio->rw); + struct page *bio_page; io = is_read ? &sbi->read_io : &sbi->write_io[btype]; - verify_block_addr(sbi, blk_addr); + verify_block_addr(sbi, fio->blk_addr); down_write(&io->io_rwsem); if (!is_read) inc_page_count(sbi, F2FS_WRITEBACK); - if (io->bio && (io->last_block_in_bio != blk_addr - 1 || + if (io->bio && (io->last_block_in_bio != fio->blk_addr - 1 || io->fio.rw != fio->rw)) __submit_merged_bio(io); alloc_new: if (io->bio == NULL) { int bio_blocks = MAX_BIO_BLOCKS(sbi); - io->bio = __bio_alloc(sbi, blk_addr, bio_blocks, is_read); + io->bio = __bio_alloc(sbi, fio->blk_addr, bio_blocks, is_read); io->fio = *fio; } - if (bio_add_page(io->bio, page, PAGE_CACHE_SIZE, 0) < + bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; + + if (bio_add_page(io->bio, bio_page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { __submit_merged_bio(io); goto alloc_new; } - io->last_block_in_bio = blk_addr; + io->last_block_in_bio = fio->blk_addr; + f2fs_trace_ios(fio, 0); up_write(&io->io_rwsem); - trace_f2fs_submit_page_mbio(page, fio->rw, fio->type, blk_addr); + trace_f2fs_submit_page_mbio(fio->page, fio); } /* @@ -217,7 +212,7 @@ alloc_new: * ->node_page * update block addresses in the node page */ -static void __set_data_blkaddr(struct dnode_of_data *dn, block_t new_addr) +void set_data_blkaddr(struct dnode_of_data *dn) { struct f2fs_node *rn; __le32 *addr_array; @@ -230,7 +225,7 @@ static void __set_data_blkaddr(struct dnode_of_data *dn, block_t new_addr) /* Get physical address of data block */ addr_array = blkaddr_in_node(rn); - addr_array[ofs_in_node] = cpu_to_le32(new_addr); + addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr); set_page_dirty(node_page); } @@ -245,8 +240,8 @@ int reserve_new_block(struct dnode_of_data *dn) trace_f2fs_reserve_new_block(dn->inode, dn->nid, dn->ofs_in_node); - __set_data_blkaddr(dn, NEW_ADDR); dn->data_blkaddr = NEW_ADDR; + set_data_blkaddr(dn); mark_inode_dirty(dn->inode); sync_inode_page(dn); return 0; @@ -257,9 +252,6 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index) bool need_put = dn->inode_page ? false : true; int err; - /* if inode_page exists, index should be zero */ - f2fs_bug_on(F2FS_I_SB(dn->inode), !need_put && index); - err = get_dnode_of_data(dn, index, ALLOC_NODE); if (err) return err; @@ -271,174 +263,108 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index) return err; } -static int check_extent_cache(struct inode *inode, pgoff_t pgofs, - struct buffer_head *bh_result) +int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index) { - struct f2fs_inode_info *fi = F2FS_I(inode); - pgoff_t start_fofs, end_fofs; - block_t start_blkaddr; - - if (is_inode_flag_set(fi, FI_NO_EXTENT)) - return 0; + struct extent_info ei; + struct inode *inode = dn->inode; - read_lock(&fi->ext.ext_lock); - if (fi->ext.len == 0) { - read_unlock(&fi->ext.ext_lock); + if (f2fs_lookup_extent_cache(inode, index, &ei)) { + dn->data_blkaddr = ei.blk + index - ei.fofs; return 0; } - stat_inc_total_hit(inode->i_sb); - - start_fofs = fi->ext.fofs; - end_fofs = fi->ext.fofs + fi->ext.len - 1; - start_blkaddr = fi->ext.blk_addr; - - if (pgofs >= start_fofs && pgofs <= end_fofs) { - unsigned int blkbits = inode->i_sb->s_blocksize_bits; - size_t count; - - clear_buffer_new(bh_result); - map_bh(bh_result, inode->i_sb, - start_blkaddr + pgofs - start_fofs); - count = end_fofs - pgofs + 1; - if (count < (UINT_MAX >> blkbits)) - bh_result->b_size = (count << blkbits); - else - bh_result->b_size = UINT_MAX; - - stat_inc_read_hit(inode->i_sb); - read_unlock(&fi->ext.ext_lock); - return 1; - } - read_unlock(&fi->ext.ext_lock); - return 0; + return f2fs_reserve_block(dn, index); } -void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn) +struct page *get_read_data_page(struct inode *inode, pgoff_t index, + int rw, bool for_write) { - struct f2fs_inode_info *fi = F2FS_I(dn->inode); - pgoff_t fofs, start_fofs, end_fofs; - block_t start_blkaddr, end_blkaddr; - int need_update = true; - - f2fs_bug_on(F2FS_I_SB(dn->inode), blk_addr == NEW_ADDR); - fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + - dn->ofs_in_node; - - /* Update the page address in the parent node */ - __set_data_blkaddr(dn, blk_addr); + struct address_space *mapping = inode->i_mapping; + struct dnode_of_data dn; + struct page *page; + struct extent_info ei; + int err; + struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(inode), + .type = DATA, + .rw = rw, + .encrypted_page = NULL, + }; - if (is_inode_flag_set(fi, FI_NO_EXTENT)) - return; + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + return read_mapping_page(mapping, index, NULL); - write_lock(&fi->ext.ext_lock); + page = f2fs_grab_cache_page(mapping, index, for_write); + if (!page) + return ERR_PTR(-ENOMEM); - start_fofs = fi->ext.fofs; - end_fofs = fi->ext.fofs + fi->ext.len - 1; - start_blkaddr = fi->ext.blk_addr; - end_blkaddr = fi->ext.blk_addr + fi->ext.len - 1; + if (f2fs_lookup_extent_cache(inode, index, &ei)) { + dn.data_blkaddr = ei.blk + index - ei.fofs; + goto got_it; + } - /* Drop and initialize the matched extent */ - if (fi->ext.len == 1 && fofs == start_fofs) - fi->ext.len = 0; + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, index, LOOKUP_NODE); + if (err) + goto put_err; + f2fs_put_dnode(&dn); - /* Initial extent */ - if (fi->ext.len == 0) { - if (blk_addr != NULL_ADDR) { - fi->ext.fofs = fofs; - fi->ext.blk_addr = blk_addr; - fi->ext.len = 1; - } - goto end_update; + if (unlikely(dn.data_blkaddr == NULL_ADDR)) { + err = -ENOENT; + goto put_err; } - - /* Front merge */ - if (fofs == start_fofs - 1 && blk_addr == start_blkaddr - 1) { - fi->ext.fofs--; - fi->ext.blk_addr--; - fi->ext.len++; - goto end_update; +got_it: + if (PageUptodate(page)) { + unlock_page(page); + return page; } - /* Back merge */ - if (fofs == end_fofs + 1 && blk_addr == end_blkaddr + 1) { - fi->ext.len++; - goto end_update; + /* + * A new dentry page is allocated but not able to be written, since its + * new inode page couldn't be allocated due to -ENOSPC. + * In such the case, its blkaddr can be remained as NEW_ADDR. + * see, f2fs_add_link -> get_new_data_page -> init_inode_metadata. + */ + if (dn.data_blkaddr == NEW_ADDR) { + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + SetPageUptodate(page); + unlock_page(page); + return page; } - /* Split the existing extent */ - if (fi->ext.len > 1 && - fofs >= start_fofs && fofs <= end_fofs) { - if ((end_fofs - fofs) < (fi->ext.len >> 1)) { - fi->ext.len = fofs - start_fofs; - } else { - fi->ext.fofs = fofs + 1; - fi->ext.blk_addr = start_blkaddr + - fofs - start_fofs + 1; - fi->ext.len -= fofs - start_fofs + 1; - } - } else { - need_update = false; - } + fio.blk_addr = dn.data_blkaddr; + fio.page = page; + err = f2fs_submit_page_bio(&fio); + if (err) + goto put_err; + return page; - /* Finally, if the extent is very fragmented, let's drop the cache. */ - if (fi->ext.len < F2FS_MIN_EXTENT_LEN) { - fi->ext.len = 0; - set_inode_flag(fi, FI_NO_EXTENT); - need_update = true; - } -end_update: - write_unlock(&fi->ext.ext_lock); - if (need_update) - sync_inode_page(dn); - return; +put_err: + f2fs_put_page(page, 1); + return ERR_PTR(err); } -struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync) +struct page *find_data_page(struct inode *inode, pgoff_t index) { struct address_space *mapping = inode->i_mapping; - struct dnode_of_data dn; struct page *page; - int err; page = find_get_page(mapping, index); if (page && PageUptodate(page)) return page; f2fs_put_page(page, 0); - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, LOOKUP_NODE); - if (err) - return ERR_PTR(err); - f2fs_put_dnode(&dn); - - if (dn.data_blkaddr == NULL_ADDR) - return ERR_PTR(-ENOENT); - - /* By fallocate(), there is no cached page, but with NEW_ADDR */ - if (unlikely(dn.data_blkaddr == NEW_ADDR)) - return ERR_PTR(-EINVAL); - - page = grab_cache_page(mapping, index); - if (!page) - return ERR_PTR(-ENOMEM); - - if (PageUptodate(page)) { - unlock_page(page); + page = get_read_data_page(inode, index, READ_SYNC, false); + if (IS_ERR(page)) return page; - } - err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, dn.data_blkaddr, - sync ? READ_SYNC : READA); - if (err) - return ERR_PTR(err); + if (PageUptodate(page)) + return page; - if (sync) { - wait_on_page_locked(page); - if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 0); - return ERR_PTR(-EIO); - } + wait_on_page_locked(page); + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 0); + return ERR_PTR(-EIO); } return page; } @@ -448,51 +374,17 @@ struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync) * Because, the callers, functions in dir.c and GC, should be able to know * whether this page exists or not. */ -struct page *get_lock_data_page(struct inode *inode, pgoff_t index) +struct page *get_lock_data_page(struct inode *inode, pgoff_t index, + bool for_write) { struct address_space *mapping = inode->i_mapping; - struct dnode_of_data dn; struct page *page; - int err; - repeat: - page = grab_cache_page(mapping, index); - if (!page) - return ERR_PTR(-ENOMEM); - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, LOOKUP_NODE); - if (err) { - f2fs_put_page(page, 1); - return ERR_PTR(err); - } - f2fs_put_dnode(&dn); - - if (unlikely(dn.data_blkaddr == NULL_ADDR)) { - f2fs_put_page(page, 1); - return ERR_PTR(-ENOENT); - } - - if (PageUptodate(page)) - return page; - - /* - * A new dentry page is allocated but not able to be written, since its - * new inode page couldn't be allocated due to -ENOSPC. - * In such the case, its blkaddr can be remained as NEW_ADDR. - * see, f2fs_add_link -> get_new_data_page -> init_inode_metadata. - */ - if (dn.data_blkaddr == NEW_ADDR) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); - SetPageUptodate(page); + page = get_read_data_page(inode, index, READ_SYNC, for_write); + if (IS_ERR(page)) return page; - } - - err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, - dn.data_blkaddr, READ_SYNC); - if (err) - return ERR_PTR(err); + /* wait for read completion */ lock_page(page); if (unlikely(!PageUptodate(page))) { f2fs_put_page(page, 1); @@ -511,7 +403,8 @@ repeat: * * Also, caller should grab and release a rwsem by calling f2fs_lock_op() and * f2fs_unlock_op(). - * Note that, ipage is set only by make_empty_dir. + * Note that, ipage is set only by make_empty_dir, and if any error occur, + * ipage should be released by this function. */ struct page *get_new_data_page(struct inode *inode, struct page *ipage, pgoff_t index, bool new_i_size) @@ -520,53 +413,50 @@ struct page *get_new_data_page(struct inode *inode, struct page *page; struct dnode_of_data dn; int err; +repeat: + page = f2fs_grab_cache_page(mapping, index, true); + if (!page) { + /* + * before exiting, we should make sure ipage will be released + * if any error occur. + */ + f2fs_put_page(ipage, 1); + return ERR_PTR(-ENOMEM); + } set_new_dnode(&dn, inode, ipage, NULL, 0); err = f2fs_reserve_block(&dn, index); - if (err) + if (err) { + f2fs_put_page(page, 1); return ERR_PTR(err); -repeat: - page = grab_cache_page(mapping, index); - if (!page) { - err = -ENOMEM; - goto put_err; } + if (!ipage) + f2fs_put_dnode(&dn); if (PageUptodate(page)) - return page; + goto got_it; if (dn.data_blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); } else { - err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, - dn.data_blkaddr, READ_SYNC); - if (err) - goto put_err; + f2fs_put_page(page, 1); - lock_page(page); - if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 1); - err = -EIO; - goto put_err; - } - if (unlikely(page->mapping != mapping)) { - f2fs_put_page(page, 1); + page = get_read_data_page(inode, index, READ_SYNC, true); + if (IS_ERR(page)) goto repeat; - } - } - if (new_i_size && - i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { - i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT)); + /* wait for read completion */ + lock_page(page); + } +got_it: + if (new_i_size && i_size_read(inode) < + ((loff_t)(index + 1) << PAGE_CACHE_SHIFT)) { + i_size_write(inode, ((loff_t)(index + 1) << PAGE_CACHE_SHIFT)); /* Only the directory inode sets new_i_size */ set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); } return page; - -put_err: - f2fs_put_dnode(&dn); - return ERR_PTR(err); } static int __allocate_data_block(struct dnode_of_data *dn) @@ -574,70 +464,136 @@ static int __allocate_data_block(struct dnode_of_data *dn) struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_inode_info *fi = F2FS_I(dn->inode); struct f2fs_summary sum; - block_t new_blkaddr; struct node_info ni; + int seg = CURSEG_WARM_DATA; pgoff_t fofs; - int type; if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) return -EPERM; + + dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); + if (dn->data_blkaddr == NEW_ADDR) + goto alloc; + if (unlikely(!inc_valid_block_count(sbi, dn->inode, 1))) return -ENOSPC; - __set_data_blkaddr(dn, NEW_ADDR); - dn->data_blkaddr = NEW_ADDR; - +alloc: get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); - type = CURSEG_WARM_DATA; - - allocate_data_block(sbi, NULL, NULL_ADDR, &new_blkaddr, &sum, type); + if (dn->ofs_in_node == 0 && dn->inode_page == dn->node_page) + seg = CURSEG_DIRECT_IO; - /* direct IO doesn't use extent cache to maximize the performance */ - set_inode_flag(F2FS_I(dn->inode), FI_NO_EXTENT); - update_extent_cache(new_blkaddr, dn); - clear_inode_flag(F2FS_I(dn->inode), FI_NO_EXTENT); + allocate_data_block(sbi, NULL, dn->data_blkaddr, &dn->data_blkaddr, + &sum, seg); + set_data_blkaddr(dn); /* update i_size */ fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + dn->ofs_in_node; - if (i_size_read(dn->inode) < ((fofs + 1) << PAGE_CACHE_SHIFT)) - i_size_write(dn->inode, ((fofs + 1) << PAGE_CACHE_SHIFT)); + if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)) + i_size_write(dn->inode, + ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)); + + /* direct IO doesn't use extent cache to maximize the performance */ + f2fs_drop_largest_extent(dn->inode, fofs); - dn->data_blkaddr = new_blkaddr; return 0; } +static void __allocate_data_blocks(struct inode *inode, loff_t offset, + size_t count) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + u64 start = F2FS_BYTES_TO_BLK(offset); + u64 len = F2FS_BYTES_TO_BLK(count); + bool allocated; + u64 end_offset; + + while (len) { + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); + + /* When reading holes, we need its node page */ + set_new_dnode(&dn, inode, NULL, NULL, 0); + if (get_dnode_of_data(&dn, start, ALLOC_NODE)) + goto out; + + allocated = false; + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + + while (dn.ofs_in_node < end_offset && len) { + block_t blkaddr; + + if (unlikely(f2fs_cp_error(sbi))) + goto sync_out; + + blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) { + if (__allocate_data_block(&dn)) + goto sync_out; + allocated = true; + } + len--; + start++; + dn.ofs_in_node++; + } + + if (allocated) + sync_inode_page(&dn); + + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + } + return; + +sync_out: + if (allocated) + sync_inode_page(&dn); + f2fs_put_dnode(&dn); +out: + f2fs_unlock_op(sbi); + return; +} + /* - * get_data_block() now supported readahead/bmap/rw direct_IO with mapped bh. + * f2fs_map_blocks() now supported readahead/bmap/rw direct_IO with + * f2fs_map_blocks structure. * If original data blocks are allocated, then give them to blockdev. * Otherwise, * a. preallocate requested block addresses * b. do not use extent cache for better performance * c. give the block addresses to blockdev */ -static int __get_data_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create, bool fiemap) +static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, + int create, int flag) { - unsigned int blkbits = inode->i_sb->s_blocksize_bits; - unsigned maxblocks = bh_result->b_size >> blkbits; + unsigned int maxblocks = map->m_len; struct dnode_of_data dn; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA; pgoff_t pgofs, end_offset; int err = 0, ofs = 1; + struct extent_info ei; bool allocated = false; - /* Get the page offset from the block offset(iblock) */ - pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits)); + map->m_len = 0; + map->m_flags = 0; + + /* it only supports block size == page size */ + pgofs = (pgoff_t)map->m_lblk; - if (check_extent_cache(inode, pgofs, bh_result)) + if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) { + map->m_pblk = ei.blk + pgofs - ei.fofs; + map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs); + map->m_flags = F2FS_MAP_MAPPED; goto out; + } - if (create) { - f2fs_balance_fs(F2FS_I_SB(inode)); + if (create) f2fs_lock_op(F2FS_I_SB(inode)); - } /* When reading holes, we need its node page */ set_new_dnode(&dn, inode, NULL, NULL, 0); @@ -647,23 +603,40 @@ static int __get_data_block(struct inode *inode, sector_t iblock, err = 0; goto unlock_out; } - if (dn.data_blkaddr == NEW_ADDR && !fiemap) - goto put_out; - if (dn.data_blkaddr != NULL_ADDR) { - map_bh(bh_result, inode->i_sb, dn.data_blkaddr); - } else if (create) { - err = __allocate_data_block(&dn); - if (err) - goto put_out; - allocated = true; - map_bh(bh_result, inode->i_sb, dn.data_blkaddr); - } else { - goto put_out; + if (dn.data_blkaddr == NEW_ADDR || dn.data_blkaddr == NULL_ADDR) { + if (create) { + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; + goto put_out; + } + err = __allocate_data_block(&dn); + if (err) + goto put_out; + allocated = true; + map->m_flags = F2FS_MAP_NEW; + } else { + if (flag != F2FS_GET_BLOCK_FIEMAP || + dn.data_blkaddr != NEW_ADDR) { + if (flag == F2FS_GET_BLOCK_BMAP) + err = -ENOENT; + goto put_out; + } + + /* + * preallocated unwritten block should be mapped + * for fiemap. + */ + if (dn.data_blkaddr == NEW_ADDR) + map->m_flags = F2FS_MAP_UNWRITTEN; + } } + map->m_flags |= F2FS_MAP_MAPPED; + map->m_pblk = dn.data_blkaddr; + map->m_len = 1; + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); - bh_result->b_size = (((size_t)1) << blkbits); dn.ofs_in_node++; pgofs++; @@ -681,27 +654,45 @@ get_next: err = 0; goto unlock_out; } - if (dn.data_blkaddr == NEW_ADDR && !fiemap) - goto put_out; end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); } - if (maxblocks > (bh_result->b_size >> blkbits)) { + if (maxblocks > map->m_len) { block_t blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); - if (blkaddr == NULL_ADDR && create) { - err = __allocate_data_block(&dn); - if (err) - goto sync_out; - allocated = true; - blkaddr = dn.data_blkaddr; + + if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { + if (create) { + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; + goto sync_out; + } + err = __allocate_data_block(&dn); + if (err) + goto sync_out; + allocated = true; + map->m_flags |= F2FS_MAP_NEW; + blkaddr = dn.data_blkaddr; + } else { + /* + * we only merge preallocated unwritten blocks + * for fiemap. + */ + if (flag != F2FS_GET_BLOCK_FIEMAP || + blkaddr != NEW_ADDR) + goto sync_out; + } } + /* Give more consecutive addresses for the readahead */ - if (blkaddr == (bh_result->b_blocknr + ofs)) { + if ((map->m_pblk != NEW_ADDR && + blkaddr == (map->m_pblk + ofs)) || + (map->m_pblk == NEW_ADDR && + blkaddr == NEW_ADDR)) { ofs++; dn.ofs_in_node++; pgofs++; - bh_result->b_size += (((size_t)1) << blkbits); + map->m_len++; goto get_next; } } @@ -714,42 +705,325 @@ unlock_out: if (create) f2fs_unlock_op(F2FS_I_SB(inode)); out: - trace_f2fs_get_data_block(inode, iblock, bh_result, err); + trace_f2fs_map_blocks(inode, map, err); return err; } +static int __get_data_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh, int create, int flag) +{ + struct f2fs_map_blocks map; + int ret; + + map.m_lblk = iblock; + map.m_len = bh->b_size >> inode->i_blkbits; + + ret = f2fs_map_blocks(inode, &map, create, flag); + if (!ret) { + map_bh(bh, inode->i_sb, map.m_pblk); + bh->b_state = (bh->b_state & ~F2FS_MAP_FLAGS) | map.m_flags; + bh->b_size = map.m_len << inode->i_blkbits; + } + return ret; +} + static int get_data_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create, int flag) +{ + return __get_data_block(inode, iblock, bh_result, create, flag); +} + +static int get_data_block_dio(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { - return __get_data_block(inode, iblock, bh_result, create, false); + return __get_data_block(inode, iblock, bh_result, create, + F2FS_GET_BLOCK_DIO); } -static int get_data_block_fiemap(struct inode *inode, sector_t iblock, +static int get_data_block_bmap(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { - return __get_data_block(inode, iblock, bh_result, create, true); + return __get_data_block(inode, iblock, bh_result, create, + F2FS_GET_BLOCK_BMAP); +} + +static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) +{ + return (offset >> inode->i_blkbits); +} + +static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) +{ + return (blk << inode->i_blkbits); } int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { - return generic_block_fiemap(inode, fieinfo, - start, len, get_data_block_fiemap); + struct buffer_head map_bh; + sector_t start_blk, last_blk; + loff_t isize = i_size_read(inode); + u64 logical = 0, phys = 0, size = 0; + u32 flags = 0; + bool past_eof = false, whole_file = false; + int ret = 0; + + ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); + if (ret) + return ret; + + if (f2fs_has_inline_data(inode)) { + ret = f2fs_inline_data_fiemap(inode, fieinfo, start, len); + if (ret != -EAGAIN) + return ret; + } + + mutex_lock(&inode->i_mutex); + + if (len >= isize) { + whole_file = true; + len = isize; + } + + if (logical_to_blk(inode, len) == 0) + len = blk_to_logical(inode, 1); + + start_blk = logical_to_blk(inode, start); + last_blk = logical_to_blk(inode, start + len - 1); +next: + memset(&map_bh, 0, sizeof(struct buffer_head)); + map_bh.b_size = len; + + ret = get_data_block(inode, start_blk, &map_bh, 0, + F2FS_GET_BLOCK_FIEMAP); + if (ret) + goto out; + + /* HOLE */ + if (!buffer_mapped(&map_bh)) { + start_blk++; + + if (!past_eof && blk_to_logical(inode, start_blk) >= isize) + past_eof = 1; + + if (past_eof && size) { + flags |= FIEMAP_EXTENT_LAST; + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + } else if (size) { + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + size = 0; + } + + /* if we have holes up to/past EOF then we're done */ + if (start_blk > last_blk || past_eof || ret) + goto out; + } else { + if (start_blk > last_blk && !whole_file) { + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + goto out; + } + + /* + * if size != 0 then we know we already have an extent + * to add, so add it. + */ + if (size) { + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + if (ret) + goto out; + } + + logical = blk_to_logical(inode, start_blk); + phys = blk_to_logical(inode, map_bh.b_blocknr); + size = map_bh.b_size; + flags = 0; + if (buffer_unwritten(&map_bh)) + flags = FIEMAP_EXTENT_UNWRITTEN; + + start_blk += logical_to_blk(inode, size); + + /* + * If we are past the EOF, then we need to make sure as + * soon as we find a hole that the last extent we found + * is marked with FIEMAP_EXTENT_LAST + */ + if (!past_eof && logical + size >= isize) + past_eof = true; + } + cond_resched(); + if (fatal_signal_pending(current)) + ret = -EINTR; + else + goto next; +out: + if (ret == 1) + ret = 0; + + mutex_unlock(&inode->i_mutex); + return ret; +} + +/* + * This function was originally taken from fs/mpage.c, and customized for f2fs. + * Major change was from block_size == page_size in f2fs by default. + */ +static int f2fs_mpage_readpages(struct address_space *mapping, + struct list_head *pages, struct page *page, + unsigned nr_pages) +{ + struct bio *bio = NULL; + unsigned page_idx; + sector_t last_block_in_bio = 0; + struct inode *inode = mapping->host; + const unsigned blkbits = inode->i_blkbits; + const unsigned blocksize = 1 << blkbits; + sector_t block_in_file; + sector_t last_block; + sector_t last_block_in_file; + sector_t block_nr; + struct block_device *bdev = inode->i_sb->s_bdev; + struct f2fs_map_blocks map; + + map.m_pblk = 0; + map.m_lblk = 0; + map.m_len = 0; + map.m_flags = 0; + + for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { + + prefetchw(&page->flags); + if (pages) { + page = list_entry(pages->prev, struct page, lru); + list_del(&page->lru); + if (add_to_page_cache_lru(page, mapping, + page->index, GFP_KERNEL)) + goto next_page; + } + + block_in_file = (sector_t)page->index; + last_block = block_in_file + nr_pages; + last_block_in_file = (i_size_read(inode) + blocksize - 1) >> + blkbits; + if (last_block > last_block_in_file) + last_block = last_block_in_file; + + /* + * Map blocks using the previous result first. + */ + if ((map.m_flags & F2FS_MAP_MAPPED) && + block_in_file > map.m_lblk && + block_in_file < (map.m_lblk + map.m_len)) + goto got_it; + + /* + * Then do more f2fs_map_blocks() calls until we are + * done with this page. + */ + map.m_flags = 0; + + if (block_in_file < last_block) { + map.m_lblk = block_in_file; + map.m_len = last_block - block_in_file; + + if (f2fs_map_blocks(inode, &map, 0, + F2FS_GET_BLOCK_READ)) + goto set_error_page; + } +got_it: + if ((map.m_flags & F2FS_MAP_MAPPED)) { + block_nr = map.m_pblk + block_in_file - map.m_lblk; + SetPageMappedToDisk(page); + + if (!PageUptodate(page) && !cleancache_get_page(page)) { + SetPageUptodate(page); + goto confused; + } + } else { + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + SetPageUptodate(page); + unlock_page(page); + goto next_page; + } + + /* + * This page will go to BIO. Do we need to send this + * BIO off first? + */ + if (bio && (last_block_in_bio != block_nr - 1)) { +submit_and_realloc: + submit_bio(READ, bio); + bio = NULL; + } + if (bio == NULL) { + struct f2fs_crypto_ctx *ctx = NULL; + + if (f2fs_encrypted_inode(inode) && + S_ISREG(inode->i_mode)) { + + ctx = f2fs_get_crypto_ctx(inode); + if (IS_ERR(ctx)) + goto set_error_page; + + /* wait the page to be moved by cleaning */ + f2fs_wait_on_encrypted_page_writeback( + F2FS_I_SB(inode), block_nr); + } + + bio = bio_alloc(GFP_KERNEL, + min_t(int, nr_pages, bio_get_nr_vecs(bdev))); + if (!bio) { + if (ctx) + f2fs_release_crypto_ctx(ctx); + goto set_error_page; + } + bio->bi_bdev = bdev; + bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(block_nr); + bio->bi_end_io = f2fs_read_end_io; + bio->bi_private = ctx; + } + + if (bio_add_page(bio, page, blocksize, 0) < blocksize) + goto submit_and_realloc; + + last_block_in_bio = block_nr; + goto next_page; +set_error_page: + SetPageError(page); + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + unlock_page(page); + goto next_page; +confused: + if (bio) { + submit_bio(READ, bio); + bio = NULL; + } + unlock_page(page); +next_page: + if (pages) + page_cache_release(page); + } + BUG_ON(pages && !list_empty(pages)); + if (bio) + submit_bio(READ, bio); + return 0; } static int f2fs_read_data_page(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; - int ret; + int ret = -EAGAIN; trace_f2fs_readpage(page, DATA); /* If the file has inline data, try to read it directly */ if (f2fs_has_inline_data(inode)) ret = f2fs_read_inline_data(inode, page); - else - ret = mpage_readpage(page, get_data_block); - + if (ret == -EAGAIN) + ret = f2fs_mpage_readpages(page->mapping, NULL, page, 1); return ret; } @@ -758,18 +1032,21 @@ static int f2fs_read_data_pages(struct file *file, struct list_head *pages, unsigned nr_pages) { struct inode *inode = file->f_mapping->host; + struct page *page = list_entry(pages->prev, struct page, lru); + + trace_f2fs_readpages(inode, page, nr_pages); /* If the file has inline data, skip readpages */ if (f2fs_has_inline_data(inode)) return 0; - return mpage_readpages(mapping, pages, nr_pages, get_data_block); + return f2fs_mpage_readpages(mapping, pages, NULL, nr_pages); } -int do_write_data_page(struct page *page, struct f2fs_io_info *fio) +int do_write_data_page(struct f2fs_io_info *fio) { + struct page *page = fio->page; struct inode *inode = page->mapping->host; - block_t old_blkaddr, new_blkaddr; struct dnode_of_data dn; int err = 0; @@ -778,11 +1055,26 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio) if (err) return err; - old_blkaddr = dn.data_blkaddr; + fio->blk_addr = dn.data_blkaddr; /* This page is already truncated */ - if (old_blkaddr == NULL_ADDR) + if (fio->blk_addr == NULL_ADDR) { + ClearPageUptodate(page); goto out_writepage; + } + + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + + /* wait for GCed encrypted page writeback */ + f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode), + fio->blk_addr); + + fio->encrypted_page = f2fs_encrypt(inode, fio->page); + if (IS_ERR(fio->encrypted_page)) { + err = PTR_ERR(fio->encrypted_page); + goto out_writepage; + } + } set_page_writeback(page); @@ -790,15 +1082,20 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio) * If current allocation needs SSR, * it had better in-place writes for updated data. */ - if (unlikely(old_blkaddr != NEW_ADDR && + if (unlikely(fio->blk_addr != NEW_ADDR && !is_cold_data(page) && need_inplace_update(inode))) { - rewrite_data_page(page, old_blkaddr, fio); + rewrite_data_page(fio); set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE); + trace_f2fs_do_write_data_page(page, IPU); } else { - write_data_page(page, &dn, &new_blkaddr, fio); - update_extent_cache(new_blkaddr, &dn); + write_data_page(&dn, fio); + set_data_blkaddr(&dn); + f2fs_update_extent_cache(&dn); + trace_f2fs_do_write_data_page(page, OPU); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + if (page->index == 0) + set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); } out_writepage: f2fs_put_dnode(&dn); @@ -817,8 +1114,11 @@ static int f2fs_write_data_page(struct page *page, bool need_balance_fs = false; int err = 0; struct f2fs_io_info fio = { + .sbi = sbi, .type = DATA, .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + .page = page, + .encrypted_page = NULL, }; trace_f2fs_writepage(page, DATA); @@ -836,21 +1136,25 @@ static int f2fs_write_data_page(struct page *page, zero_user_segment(page, offset, PAGE_CACHE_SIZE); write: - if (unlikely(sbi->por_doing)) + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto redirty_out; + if (f2fs_is_drop_cache(inode)) + goto out; + if (f2fs_is_volatile_file(inode) && !wbc->for_reclaim && + available_free_memory(sbi, BASE_CHECK)) goto redirty_out; /* Dentry blocks are controlled by checkpoint */ if (S_ISDIR(inode->i_mode)) { if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - err = do_write_data_page(page, &fio); + err = do_write_data_page(&fio); goto done; } /* we should bypass data pages to proceed the kworkder jobs */ if (unlikely(f2fs_cp_error(sbi))) { SetPageError(page); - unlock_page(page); goto out; } @@ -859,11 +1163,12 @@ write: else if (has_not_enough_free_secs(sbi, 0)) goto redirty_out; + err = -EAGAIN; f2fs_lock_op(sbi); - if (f2fs_has_inline_data(inode) || f2fs_may_inline(inode)) - err = f2fs_write_inline_data(inode, page, offset); - else - err = do_write_data_page(page, &fio); + if (f2fs_has_inline_data(inode)) + err = f2fs_write_inline_data(inode, page); + if (err == -EAGAIN) + err = do_write_data_page(&fio); f2fs_unlock_op(sbi); done: if (err && err != -ENOENT) @@ -872,6 +1177,8 @@ done: clear_cold_data(page); out: inode_dec_dirty_pages(inode); + if (err) + ClearPageUptodate(page); unlock_page(page); if (need_balance_fs) f2fs_balance_fs(sbi); @@ -893,6 +1200,137 @@ static int __f2fs_writepage(struct page *page, struct writeback_control *wbc, return ret; } +/* + * This function was copied from write_cche_pages from mm/page-writeback.c. + * The major change is making write step of cold data page separately from + * warm/hot data page. + */ +static int f2fs_write_cache_pages(struct address_space *mapping, + struct writeback_control *wbc, writepage_t writepage, + void *data) +{ + int ret = 0; + int done = 0; + struct pagevec pvec; + int nr_pages; + pgoff_t uninitialized_var(writeback_index); + pgoff_t index; + pgoff_t end; /* Inclusive */ + pgoff_t done_index; + int cycled; + int range_whole = 0; + int tag; + int step = 0; + + pagevec_init(&pvec, 0); +next: + if (wbc->range_cyclic) { + writeback_index = mapping->writeback_index; /* prev offset */ + index = writeback_index; + if (index == 0) + cycled = 1; + else + cycled = 0; + end = -1; + } else { + index = wbc->range_start >> PAGE_CACHE_SHIFT; + end = wbc->range_end >> PAGE_CACHE_SHIFT; + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) + range_whole = 1; + cycled = 1; /* ignore range_cyclic tests */ + } + if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) + tag = PAGECACHE_TAG_TOWRITE; + else + tag = PAGECACHE_TAG_DIRTY; +retry: + if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) + tag_pages_for_writeback(mapping, index, end); + done_index = index; + while (!done && (index <= end)) { + int i; + + nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, tag, + min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1); + if (nr_pages == 0) + break; + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + + if (page->index > end) { + done = 1; + break; + } + + done_index = page->index; + + lock_page(page); + + if (unlikely(page->mapping != mapping)) { +continue_unlock: + unlock_page(page); + continue; + } + + if (!PageDirty(page)) { + /* someone wrote it for us */ + goto continue_unlock; + } + + if (step == is_cold_data(page)) + goto continue_unlock; + + if (PageWriteback(page)) { + if (wbc->sync_mode != WB_SYNC_NONE) + f2fs_wait_on_page_writeback(page, DATA); + else + goto continue_unlock; + } + + BUG_ON(PageWriteback(page)); + if (!clear_page_dirty_for_io(page)) + goto continue_unlock; + + ret = (*writepage)(page, wbc, data); + if (unlikely(ret)) { + if (ret == AOP_WRITEPAGE_ACTIVATE) { + unlock_page(page); + ret = 0; + } else { + done_index = page->index + 1; + done = 1; + break; + } + } + + if (--wbc->nr_to_write <= 0 && + wbc->sync_mode == WB_SYNC_NONE) { + done = 1; + break; + } + } + pagevec_release(&pvec); + cond_resched(); + } + + if (step < 1) { + step++; + goto next; + } + + if (!cycled && !done) { + cycled = 1; + index = 0; + end = writeback_index - 1; + goto retry; + } + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) + mapping->writeback_index = done_index; + + return ret; +} + static int f2fs_write_data_pages(struct address_space *mapping, struct writeback_control *wbc) { @@ -908,23 +1346,30 @@ static int f2fs_write_data_pages(struct address_space *mapping, if (!mapping->a_ops->writepage) return 0; + /* skip writing if there is no dirty page in this inode */ + if (!get_dirty_pages(inode) && wbc->sync_mode == WB_SYNC_NONE) + return 0; + if (S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_NONE && get_dirty_pages(inode) < nr_pages_to_skip(sbi, DATA) && available_free_memory(sbi, DIRTY_DENTS)) goto skip_write; + /* during POR, we don't need to trigger writepage at all. */ + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto skip_write; + diff = nr_pages_to_write(sbi, DATA, wbc); if (!S_ISDIR(inode->i_mode)) { mutex_lock(&sbi->writepages); locked = true; } - ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); + ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); + f2fs_submit_merged_bio(sbi, DATA, WRITE); if (locked) mutex_unlock(&sbi->writepages); - f2fs_submit_merged_bio(sbi, DATA, WRITE); - remove_dirty_dir_inode(inode); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); @@ -951,7 +1396,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, { struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct page *page; + struct page *page = NULL; + struct page *ipage; pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; struct dnode_of_data dn; int err = 0; @@ -959,44 +1405,66 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, trace_f2fs_write_begin(inode, pos, len, flags); f2fs_balance_fs(sbi); -repeat: - err = f2fs_convert_inline_data(inode, pos + len, NULL); - if (err) - goto fail; + /* + * We should check this at this moment to avoid deadlock on inode page + * and #0 page. The locking rule for inline_data conversion should be: + * lock_page(page #0) -> lock_page(inode_page) + */ + if (index != 0) { + err = f2fs_convert_inline_inode(inode); + if (err) + goto fail; + } +repeat: page = grab_cache_page_write_begin(mapping, index, flags); if (!page) { err = -ENOMEM; goto fail; } - /* to avoid latency during memory pressure */ - unlock_page(page); - *pagep = page; - if (f2fs_has_inline_data(inode) && (pos + len) <= MAX_INLINE_DATA) - goto inline_data; - f2fs_lock_op(sbi); - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = f2fs_reserve_block(&dn, index); - f2fs_unlock_op(sbi); - if (err) { - f2fs_put_page(page, 0); - goto fail; + + /* check inline_data */ + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto unlock_fail; } -inline_data: - lock_page(page); - if (unlikely(page->mapping != mapping)) { - f2fs_put_page(page, 1); - goto repeat; + + set_new_dnode(&dn, inode, ipage, ipage, 0); + + if (f2fs_has_inline_data(inode)) { + if (pos + len <= MAX_INLINE_DATA) { + read_inline_data(page, ipage); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + sync_inode_page(&dn); + goto put_next; + } + err = f2fs_convert_inline_page(&dn, page); + if (err) + goto put_fail; } + err = f2fs_get_block(&dn, index); + if (err) + goto put_fail; +put_next: + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + f2fs_wait_on_page_writeback(page, DATA); - if ((len == PAGE_CACHE_SIZE) || PageUptodate(page)) - return 0; + /* wait for GCed encrypted page writeback */ + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); + + if (len == PAGE_CACHE_SIZE) + goto out_update; + if (PageUptodate(page)) + goto out_clear; if ((pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { unsigned start = pos & (PAGE_CACHE_SIZE - 1); @@ -1004,26 +1472,26 @@ inline_data: /* Reading beyond i_size is simple: memset to zero */ zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); - goto out; + goto out_update; } - if (f2fs_has_inline_data(inode)) { - err = f2fs_read_inline_data(inode, page); - if (err) { - page_cache_release(page); - goto fail; - } - } else if (dn.data_blkaddr == NEW_ADDR) { + if (dn.data_blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_CACHE_SIZE); } else { - err = f2fs_submit_page_bio(sbi, page, dn.data_blkaddr, - READ_SYNC); + struct f2fs_io_info fio = { + .sbi = sbi, + .type = DATA, + .rw = READ_SYNC, + .blk_addr = dn.data_blkaddr, + .page = page, + .encrypted_page = NULL, + }; + err = f2fs_submit_page_bio(&fio); if (err) goto fail; lock_page(page); if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 1); err = -EIO; goto fail; } @@ -1031,12 +1499,26 @@ inline_data: f2fs_put_page(page, 1); goto repeat; } + + /* avoid symlink page */ + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + err = f2fs_decrypt_one(inode, page); + if (err) + goto fail; + } } -out: +out_update: SetPageUptodate(page); +out_clear: clear_cold_data(page); return 0; + +put_fail: + f2fs_put_dnode(&dn); +unlock_fail: + f2fs_unlock_op(sbi); fail: + f2fs_put_page(page, 1); f2fs_write_failed(mapping, pos + len); return err; } @@ -1050,10 +1532,7 @@ static int f2fs_write_end(struct file *file, trace_f2fs_write_end(inode, pos, len, copied); - if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) - register_inmem_page(inode, page); - else - set_page_dirty(page); + set_page_dirty(page); if (pos + copied > i_size_read(inode)) { i_size_write(inode, pos + copied); @@ -1065,14 +1544,11 @@ static int f2fs_write_end(struct file *file, return copied; } -static int check_direct_IO(struct inode *inode, int rw, - struct iov_iter *iter, loff_t offset) +static int check_direct_IO(struct inode *inode, struct iov_iter *iter, + loff_t offset) { unsigned blocksize_mask = inode->i_sb->s_blocksize - 1; - if (rw == READ) - return 0; - if (offset & blocksize_mask) return -EINVAL; @@ -1082,8 +1558,8 @@ static int check_direct_IO(struct inode *inode, int rw, return 0; } -static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, - struct iov_iter *iter, loff_t offset) +static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, + loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -1091,16 +1567,32 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, size_t count = iov_iter_count(iter); int err; - /* Let buffer I/O handle the inline data case. */ - if (f2fs_has_inline_data(inode)) - return 0; + /* we don't need to use inline_data strictly */ + if (f2fs_has_inline_data(inode)) { + err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } - if (check_direct_IO(inode, rw, iter, offset)) + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return 0; + err = check_direct_IO(inode, iter, offset); + if (err) + return err; + trace_f2fs_direct_IO_enter(inode, offset, count, rw); - err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block); + if (rw & WRITE) { + __allocate_data_blocks(inode, offset, count); + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) { + err = -EIO; + goto out; + } + } + + err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block_dio); +out: if (err < 0 && (rw & WRITE)) f2fs_write_failed(mapping, offset + count); @@ -1109,21 +1601,42 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, return err; } -static void f2fs_invalidate_data_page(struct page *page, unsigned int offset, - unsigned int length) +void f2fs_invalidate_page(struct page *page, unsigned int offset, + unsigned int length) { struct inode *inode = page->mapping->host; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - if (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE) + if (inode->i_ino >= F2FS_ROOT_INO(sbi) && + (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE)) + return; + + if (PageDirty(page)) { + if (inode->i_ino == F2FS_META_INO(sbi)) + dec_page_count(sbi, F2FS_DIRTY_META); + else if (inode->i_ino == F2FS_NODE_INO(sbi)) + dec_page_count(sbi, F2FS_DIRTY_NODES); + else + inode_dec_dirty_pages(inode); + } + + /* This is atomic written page, keep Private */ + if (IS_ATOMIC_WRITTEN_PAGE(page)) return; - if (PageDirty(page)) - inode_dec_dirty_pages(inode); ClearPagePrivate(page); } -static int f2fs_release_data_page(struct page *page, gfp_t wait) +int f2fs_release_page(struct page *page, gfp_t wait) { + /* If this is dirty page, keep PagePrivate */ + if (PageDirty(page)) + return 0; + + /* This is atomic written page, keep Private */ + if (IS_ATOMIC_WRITTEN_PAGE(page)) + return 0; + ClearPagePrivate(page); return 1; } @@ -1136,7 +1649,18 @@ static int f2fs_set_data_page_dirty(struct page *page) trace_f2fs_set_page_dirty(page, DATA); SetPageUptodate(page); - mark_inode_dirty(inode); + + if (f2fs_is_atomic_file(inode)) { + if (!IS_ATOMIC_WRITTEN_PAGE(page)) { + register_inmem_page(inode, page); + return 1; + } + /* + * Previously, this page has been registered, we just + * return here. + */ + return 0; + } if (!PageDirty(page)) { __set_page_dirty_nobuffers(page); @@ -1153,7 +1677,11 @@ static sector_t f2fs_bmap(struct address_space *mapping, sector_t block) if (f2fs_has_inline_data(inode)) return 0; - return generic_block_bmap(mapping, block, get_data_block); + /* make sure allocating whole blocks */ + if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) + filemap_write_and_wait(mapping); + + return generic_block_bmap(mapping, block, get_data_block_bmap); } const struct address_space_operations f2fs_dblock_aops = { @@ -1164,8 +1692,8 @@ const struct address_space_operations f2fs_dblock_aops = { .write_begin = f2fs_write_begin, .write_end = f2fs_write_end, .set_page_dirty = f2fs_set_data_page_dirty, - .invalidatepage = f2fs_invalidate_data_page, - .releasepage = f2fs_release_data_page, + .invalidatepage = f2fs_invalidate_page, + .releasepage = f2fs_release_page, .direct_IO = f2fs_direct_IO, .bmap = f2fs_bmap, }; diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 0a91ab813a9e..478e5d54154f 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -33,19 +33,28 @@ static void update_general_status(struct f2fs_sb_info *sbi) int i; /* validation check of the segment numbers */ - si->hit_ext = sbi->read_hit_ext; - si->total_ext = sbi->total_hit_ext; + si->hit_largest = atomic64_read(&sbi->read_hit_largest); + si->hit_cached = atomic64_read(&sbi->read_hit_cached); + si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree); + si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree; + si->total_ext = atomic64_read(&sbi->total_hit_ext); + si->ext_tree = sbi->total_ext_tree; + si->ext_node = atomic_read(&sbi->total_ext_node); si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES); si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); si->ndirty_dirs = sbi->n_dirty_dirs; si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); + si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); + si->wb_pages = get_pages(sbi, F2FS_WRITEBACK); si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->rsvd_segs = reserved_segments(sbi); si->overp_segs = overprovision_segments(sbi); si->valid_count = valid_user_blocks(sbi); si->valid_node_count = valid_node_count(sbi); si->valid_inode_count = valid_inode_count(sbi); - si->inline_inode = sbi->inline_inode; + si->inline_xattr = atomic_read(&sbi->inline_xattr); + si->inline_inode = atomic_read(&sbi->inline_inode); + si->inline_dir = atomic_read(&sbi->inline_dir); si->utilization = utilization(sbi); si->free_segs = free_segments(sbi); @@ -55,7 +64,9 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->node_pages = NODE_MAPPING(sbi)->nrpages; si->meta_pages = META_MAPPING(sbi)->nrpages; si->nats = NM_I(sbi)->nat_cnt; - si->sits = SIT_I(sbi)->dirty_sentries; + si->dirty_nats = NM_I(sbi)->dirty_nat_cnt; + si->sits = MAIN_SEGS(sbi); + si->dirty_sits = SIT_I(sbi)->dirty_sentries; si->fnids = NM_I(sbi)->fcnt; si->bg_gc = sbi->bg_gc; si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg) @@ -77,6 +88,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->segment_count[i] = sbi->segment_count[i]; si->block_count[i] = sbi->block_count[i]; } + + si->inplace_count = atomic_read(&sbi->inplace_count); } /* @@ -85,7 +98,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) static void update_sit_info(struct f2fs_sb_info *sbi) { struct f2fs_stat_info *si = F2FS_STAT(sbi); - unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist; + unsigned long long blks_per_sec, hblks_per_sec, total_vblocks; + unsigned long long bimodal, dist; unsigned int segno, vblocks; int ndirty = 0; @@ -103,10 +117,10 @@ static void update_sit_info(struct f2fs_sb_info *sbi) ndirty++; } } - dist = MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100; - si->bimodal = bimodal / dist; + dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100); + si->bimodal = div64_u64(bimodal, dist); if (si->dirty_count) - si->avg_vblocks = total_vblocks / ndirty; + si->avg_vblocks = div_u64(total_vblocks, ndirty); else si->avg_vblocks = 0; } @@ -118,6 +132,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi) { struct f2fs_stat_info *si = F2FS_STAT(sbi); unsigned npages; + int i; if (si->base_mem) goto get_cache; @@ -133,7 +148,8 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct sit_info); si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry); si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi)); - si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + si->base_mem += 3 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + si->base_mem += SIT_VBLOCK_MAP_SIZE; if (sbi->segs_per_sec > 1) si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry); si->base_mem += __bitmap_size(sbi, SIT_BITMAP); @@ -156,19 +172,35 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct f2fs_nm_info); si->base_mem += __bitmap_size(sbi, NAT_BITMAP); +get_cache: + si->cache_mem = 0; + /* build gc */ - si->base_mem += sizeof(struct f2fs_gc_kthread); + if (sbi->gc_thread) + si->cache_mem += sizeof(struct f2fs_gc_kthread); + + /* build merge flush thread */ + if (SM_I(sbi)->cmd_control_info) + si->cache_mem += sizeof(struct flush_cmd_control); -get_cache: /* free nids */ - si->cache_mem = NM_I(sbi)->fcnt; - si->cache_mem += NM_I(sbi)->nat_cnt; + si->cache_mem += NM_I(sbi)->fcnt * sizeof(struct free_nid); + si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry); + si->cache_mem += NM_I(sbi)->dirty_nat_cnt * + sizeof(struct nat_entry_set); + si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); + si->cache_mem += sbi->n_dirty_dirs * sizeof(struct inode_entry); + for (i = 0; i <= UPDATE_INO; i++) + si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); + si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree); + si->cache_mem += atomic_read(&sbi->total_ext_node) * + sizeof(struct extent_node); + + si->page_mem = 0; npages = NODE_MAPPING(sbi)->nrpages; - si->cache_mem += npages << PAGE_CACHE_SHIFT; + si->page_mem += (unsigned long long)npages << PAGE_CACHE_SHIFT; npages = META_MAPPING(sbi)->nrpages; - si->cache_mem += npages << PAGE_CACHE_SHIFT; - si->cache_mem += sbi->n_orphans * sizeof(struct ino_entry); - si->cache_mem += sbi->n_dirty_dirs * sizeof(struct dir_inode_entry); + si->page_mem += (unsigned long long)npages << PAGE_CACHE_SHIFT; } static int stat_show(struct seq_file *s, void *v) @@ -198,8 +230,12 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, "Other: %u)\n - Data: %u\n", si->valid_node_count - si->valid_inode_count, si->valid_count - si->valid_node_count); + seq_printf(s, " - Inline_xattr Inode: %u\n", + si->inline_xattr); seq_printf(s, " - Inline_data Inode: %u\n", si->inline_inode); + seq_printf(s, " - Inline_dentry Inode: %u\n", + si->inline_dir); seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", si->main_area_segs, si->main_area_sections, si->main_area_zones); @@ -236,22 +272,37 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, "CP calls: %d\n", si->cp_count); seq_printf(s, "GC calls: %d (BG: %d)\n", si->call_count, si->bg_gc); - seq_printf(s, " - data segments : %d\n", si->data_segs); - seq_printf(s, " - node segments : %d\n", si->node_segs); - seq_printf(s, "Try to move %d blocks\n", si->tot_blks); - seq_printf(s, " - data blocks : %d\n", si->data_blks); - seq_printf(s, " - node blocks : %d\n", si->node_blks); - seq_printf(s, "\nExtent Hit Ratio: %d / %d\n", - si->hit_ext, si->total_ext); + seq_printf(s, " - data segments : %d (%d)\n", + si->data_segs, si->bg_data_segs); + seq_printf(s, " - node segments : %d (%d)\n", + si->node_segs, si->bg_node_segs); + seq_printf(s, "Try to move %d blocks (BG: %d)\n", si->tot_blks, + si->bg_data_blks + si->bg_node_blks); + seq_printf(s, " - data blocks : %d (%d)\n", si->data_blks, + si->bg_data_blks); + seq_printf(s, " - node blocks : %d (%d)\n", si->node_blks, + si->bg_node_blks); + seq_puts(s, "\nExtent Cache:\n"); + seq_printf(s, " - Hit Count: L1-1:%llu L1-2:%llu L2:%llu\n", + si->hit_largest, si->hit_cached, + si->hit_rbtree); + seq_printf(s, " - Hit Ratio: %llu%% (%llu / %llu)\n", + !si->total_ext ? 0 : + div64_u64(si->hit_total * 100, si->total_ext), + si->hit_total, si->total_ext); + seq_printf(s, " - Inner Struct Count: tree: %d, node: %d\n", + si->ext_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); + seq_printf(s, " - inmem: %4d, wb: %4d\n", + si->inmem_pages, si->wb_pages); seq_printf(s, " - nodes: %4d in %4d\n", si->ndirty_node, si->node_pages); seq_printf(s, " - dents: %4d in dirs:%4d\n", si->ndirty_dent, si->ndirty_dirs); seq_printf(s, " - meta: %4d in %4d\n", si->ndirty_meta, si->meta_pages); - seq_printf(s, " - NATs: %9d\n - SITs: %9d\n", - si->nats, si->sits); + seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", + si->dirty_nats, si->nats, si->dirty_sits, si->sits); seq_printf(s, " - free_nids: %9d\n", si->fnids); seq_puts(s, "\nDistribution of User Blocks:"); @@ -269,6 +320,7 @@ static int stat_show(struct seq_file *s, void *v) for (j = 0; j < si->util_free; j++) seq_putc(s, '-'); seq_puts(s, "]\n\n"); + seq_printf(s, "IPU: %u blocks\n", si->inplace_count); seq_printf(s, "SSR: %u blocks in %u segments\n", si->block_count[SSR], si->segment_count[SSR]); seq_printf(s, "LFS: %u blocks in %u segments\n", @@ -281,9 +333,14 @@ static int stat_show(struct seq_file *s, void *v) /* memory footprint */ update_mem_info(si->sbi); - seq_printf(s, "\nMemory: %u KB = static: %u + cached: %u\n", - (si->base_mem + si->cache_mem) >> 10, - si->base_mem >> 10, si->cache_mem >> 10); + seq_printf(s, "\nMemory: %llu KB\n", + (si->base_mem + si->cache_mem + si->page_mem) >> 10); + seq_printf(s, " - static: %llu KB\n", + si->base_mem >> 10); + seq_printf(s, " - cached: %llu KB\n", + si->cache_mem >> 10); + seq_printf(s, " - paged : %llu KB\n", + si->page_mem >> 10); } mutex_unlock(&f2fs_stat_mutex); return 0; @@ -321,6 +378,16 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) si->sbi = sbi; sbi->stat_info = si; + atomic64_set(&sbi->total_hit_ext, 0); + atomic64_set(&sbi->read_hit_rbtree, 0); + atomic64_set(&sbi->read_hit_largest, 0); + atomic64_set(&sbi->read_hit_cached, 0); + + atomic_set(&sbi->inline_xattr, 0); + atomic_set(&sbi->inline_inode, 0); + atomic_set(&sbi->inline_dir, 0); + atomic_set(&sbi->inplace_count, 0); + mutex_lock(&f2fs_stat_mutex); list_add_tail(&si->stat_list, &f2fs_stat_list); mutex_unlock(&f2fs_stat_mutex); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index b54f87149c09..73ca524a9aaf 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -37,7 +37,7 @@ static unsigned int bucket_blocks(unsigned int level) return 4; } -static unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { +unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { [F2FS_FT_UNKNOWN] = DT_UNKNOWN, [F2FS_FT_REG_FILE] = DT_REG, [F2FS_FT_DIR] = DT_DIR, @@ -59,9 +59,8 @@ static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK, }; -static void set_de_type(struct f2fs_dir_entry *de, struct inode *inode) +void set_de_type(struct f2fs_dir_entry *de, umode_t mode) { - umode_t mode = inode->i_mode; de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; } @@ -77,79 +76,101 @@ static unsigned long dir_block_index(unsigned int level, return bidx; } -static bool early_match_name(size_t namelen, f2fs_hash_t namehash, - struct f2fs_dir_entry *de) +static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, + struct f2fs_filename *fname, + f2fs_hash_t namehash, + int *max_slots, + struct page **res_page) { - if (le16_to_cpu(de->name_len) != namelen) - return false; + struct f2fs_dentry_block *dentry_blk; + struct f2fs_dir_entry *de; + struct f2fs_dentry_ptr d; - if (de->hash_code != namehash) - return false; + dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page); - return true; + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + de = find_target_dentry(fname, namehash, max_slots, &d); + if (de) + *res_page = dentry_page; + else + kunmap(dentry_page); + + /* + * For the most part, it should be a bug when name_len is zero. + * We stop here for figuring out where the bugs has occurred. + */ + f2fs_bug_on(F2FS_P_SB(dentry_page), d.max < 0); + return de; } -static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, - struct qstr *name, int *max_slots, - f2fs_hash_t namehash, struct page **res_page) +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname, + f2fs_hash_t namehash, int *max_slots, + struct f2fs_dentry_ptr *d) { struct f2fs_dir_entry *de; unsigned long bit_pos = 0; - struct f2fs_dentry_block *dentry_blk = kmap(dentry_page); - const void *dentry_bits = &dentry_blk->dentry_bitmap; int max_len = 0; + struct f2fs_str de_name = FSTR_INIT(NULL, 0); + struct f2fs_str *name = &fname->disk_name; - while (bit_pos < NR_DENTRY_IN_BLOCK) { - if (!test_bit_le(bit_pos, dentry_bits)) { - if (bit_pos == 0) - max_len = 1; - else if (!test_bit_le(bit_pos - 1, dentry_bits)) - max_len++; + if (max_slots) + *max_slots = 0; + while (bit_pos < d->max) { + if (!test_bit_le(bit_pos, d->bitmap)) { bit_pos++; + max_len++; continue; } - de = &dentry_blk->dentry[bit_pos]; - if (early_match_name(name->len, namehash, de)) { - if (!memcmp(dentry_blk->filename[bit_pos], - name->name, - name->len)) { - *res_page = dentry_page; + + de = &d->dentry[bit_pos]; + + /* encrypted case */ + de_name.name = d->filename[bit_pos]; + de_name.len = le16_to_cpu(de->name_len); + + /* show encrypted name */ + if (fname->hash) { + if (de->hash_code == fname->hash) goto found; - } - } - if (max_len > *max_slots) { + } else if (de_name.len == name->len && + de->hash_code == namehash && + !memcmp(de_name.name, name->name, name->len)) + goto found; + + if (max_slots && max_len > *max_slots) *max_slots = max_len; - max_len = 0; - } + max_len = 0; - /* - * For the most part, it should be a bug when name_len is zero. - * We stop here for figuring out where the bugs has occurred. - */ - f2fs_bug_on(F2FS_P_SB(dentry_page), !de->name_len); + /* remain bug on condition */ + if (unlikely(!de->name_len)) + d->max = -1; bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); } de = NULL; - kunmap(dentry_page); found: - if (max_len > *max_slots) + if (max_slots && max_len > *max_slots) *max_slots = max_len; return de; } static struct f2fs_dir_entry *find_in_level(struct inode *dir, - unsigned int level, struct qstr *name, - f2fs_hash_t namehash, struct page **res_page) + unsigned int level, + struct f2fs_filename *fname, + struct page **res_page) { - int s = GET_DENTRY_SLOTS(name->len); + struct qstr name = FSTR_TO_QSTR(&fname->disk_name); + int s = GET_DENTRY_SLOTS(name.len); unsigned int nbucket, nblock; unsigned int bidx, end_block; struct page *dentry_page; struct f2fs_dir_entry *de = NULL; bool room = false; - int max_slots = 0; + int max_slots; + f2fs_hash_t namehash; + + namehash = f2fs_dentry_hash(&name); f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH); @@ -162,14 +183,14 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, for (; bidx < end_block; bidx++) { /* no need to allocate new dentry pages to all the indices */ - dentry_page = find_data_page(dir, bidx, true); + dentry_page = find_data_page(dir, bidx); if (IS_ERR(dentry_page)) { room = true; continue; } - de = find_in_block(dentry_page, name, &max_slots, - namehash, res_page); + de = find_in_block(dentry_page, fname, namehash, &max_slots, + res_page); if (de) break; @@ -197,27 +218,34 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, { unsigned long npages = dir_blocks(dir); struct f2fs_dir_entry *de = NULL; - f2fs_hash_t name_hash; unsigned int max_depth; unsigned int level; + struct f2fs_filename fname; + int err; - if (npages == 0) + *res_page = NULL; + + err = f2fs_fname_setup_filename(dir, child, 1, &fname); + if (err) return NULL; - *res_page = NULL; + if (f2fs_has_inline_dentry(dir)) { + de = find_in_inline_dir(dir, &fname, res_page); + goto out; + } + + if (npages == 0) + goto out; - name_hash = f2fs_dentry_hash(child); max_depth = F2FS_I(dir)->i_current_depth; for (level = 0; level < max_depth; level++) { - de = find_in_level(dir, level, child, name_hash, res_page); + de = find_in_level(dir, level, &fname, res_page); if (de) break; } - if (!de && F2FS_I(dir)->chash != name_hash) { - F2FS_I(dir)->chash = name_hash; - F2FS_I(dir)->clevel = level - 1; - } +out: + f2fs_fname_free_filename(&fname); return de; } @@ -227,7 +255,10 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p) struct f2fs_dir_entry *de; struct f2fs_dentry_block *dentry_blk; - page = get_lock_data_page(dir, 0); + if (f2fs_has_inline_dentry(dir)) + return f2fs_parent_inline_dir(dir, p); + + page = get_lock_data_page(dir, 0, false); if (IS_ERR(page)) return NULL; @@ -247,7 +278,7 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr) de = f2fs_find_entry(dir, qstr, &page); if (de) { res = le32_to_cpu(de->ino); - kunmap(page); + f2fs_dentry_kunmap(dir, page); f2fs_put_page(page, 0); } @@ -257,11 +288,12 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr) void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, struct page *page, struct inode *inode) { + enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA; lock_page(page); - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, type); de->ino = cpu_to_le32(inode->i_ino); - set_de_type(de, inode); - kunmap(page); + set_de_type(de, inode->i_mode); + f2fs_dentry_kunmap(dir, page); set_page_dirty(page); dir->i_mtime = dir->i_ctime = CURRENT_TIME; mark_inode_dirty(dir); @@ -282,10 +314,14 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage) set_page_dirty(ipage); } -int update_dent_inode(struct inode *inode, const struct qstr *name) +int update_dent_inode(struct inode *inode, struct inode *to, + const struct qstr *name) { struct page *page; + if (file_enc_name(to)) + return 0; + page = get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(page)) return PTR_ERR(page); @@ -296,36 +332,48 @@ int update_dent_inode(struct inode *inode, const struct qstr *name) return 0; } +void do_make_empty_dir(struct inode *inode, struct inode *parent, + struct f2fs_dentry_ptr *d) +{ + struct f2fs_dir_entry *de; + + de = &d->dentry[0]; + de->name_len = cpu_to_le16(1); + de->hash_code = 0; + de->ino = cpu_to_le32(inode->i_ino); + memcpy(d->filename[0], ".", 1); + set_de_type(de, inode->i_mode); + + de = &d->dentry[1]; + de->hash_code = 0; + de->name_len = cpu_to_le16(2); + de->ino = cpu_to_le32(parent->i_ino); + memcpy(d->filename[1], "..", 2); + set_de_type(de, parent->i_mode); + + test_and_set_bit_le(0, (void *)d->bitmap); + test_and_set_bit_le(1, (void *)d->bitmap); +} + static int make_empty_dir(struct inode *inode, struct inode *parent, struct page *page) { struct page *dentry_page; struct f2fs_dentry_block *dentry_blk; - struct f2fs_dir_entry *de; + struct f2fs_dentry_ptr d; + + if (f2fs_has_inline_dentry(inode)) + return make_empty_inline_dir(inode, parent, page); dentry_page = get_new_data_page(inode, page, 0, true); if (IS_ERR(dentry_page)) return PTR_ERR(dentry_page); - dentry_blk = kmap_atomic(dentry_page); - de = &dentry_blk->dentry[0]; - de->name_len = cpu_to_le16(1); - de->hash_code = 0; - de->ino = cpu_to_le32(inode->i_ino); - memcpy(dentry_blk->filename[0], ".", 1); - set_de_type(de, inode); - - de = &dentry_blk->dentry[1]; - de->hash_code = 0; - de->name_len = cpu_to_le16(2); - de->ino = cpu_to_le32(parent->i_ino); - memcpy(dentry_blk->filename[1], "..", 2); - set_de_type(de, inode); + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + do_make_empty_dir(inode, parent, &d); - test_and_set_bit_le(0, &dentry_blk->dentry_bitmap); - test_and_set_bit_le(1, &dentry_blk->dentry_bitmap); kunmap_atomic(dentry_blk); set_page_dirty(dentry_page); @@ -333,8 +381,8 @@ static int make_empty_dir(struct inode *inode, return 0; } -static struct page *init_inode_metadata(struct inode *inode, - struct inode *dir, const struct qstr *name) +struct page *init_inode_metadata(struct inode *inode, struct inode *dir, + const struct qstr *name, struct page *dpage) { struct page *page; int err; @@ -350,13 +398,19 @@ static struct page *init_inode_metadata(struct inode *inode, goto error; } - err = f2fs_init_acl(inode, dir, page); + err = f2fs_init_acl(inode, dir, page, dpage); if (err) goto put_error; err = f2fs_init_security(inode, dir, name, page); if (err) goto put_error; + + if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) { + err = f2fs_inherit_context(dir, inode, page); + if (err) + goto put_error; + } } else { page = get_node_page(F2FS_I_SB(dir), inode->i_ino); if (IS_ERR(page)) @@ -395,10 +449,10 @@ error: return ERR_PTR(err); } -static void update_parent_metadata(struct inode *dir, struct inode *inode, +void update_parent_metadata(struct inode *dir, struct inode *inode, unsigned int current_depth) { - if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { + if (inode && is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { if (S_ISDIR(inode->i_mode)) { inc_nlink(dir); set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); @@ -413,58 +467,88 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode, set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); } - if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) + if (inode && is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) clear_inode_flag(F2FS_I(inode), FI_INC_LINK); } -static int room_for_filename(struct f2fs_dentry_block *dentry_blk, int slots) +int room_for_filename(const void *bitmap, int slots, int max_slots) { int bit_start = 0; int zero_start, zero_end; next: - zero_start = find_next_zero_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - bit_start); - if (zero_start >= NR_DENTRY_IN_BLOCK) - return NR_DENTRY_IN_BLOCK; + zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start); + if (zero_start >= max_slots) + return max_slots; - zero_end = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - zero_start); + zero_end = find_next_bit_le(bitmap, max_slots, zero_start); if (zero_end - zero_start >= slots) return zero_start; bit_start = zero_end + 1; - if (zero_end + 1 >= NR_DENTRY_IN_BLOCK) - return NR_DENTRY_IN_BLOCK; + if (zero_end + 1 >= max_slots) + return max_slots; goto next; } +void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, + const struct qstr *name, f2fs_hash_t name_hash, + unsigned int bit_pos) +{ + struct f2fs_dir_entry *de; + int slots = GET_DENTRY_SLOTS(name->len); + int i; + + de = &d->dentry[bit_pos]; + de->hash_code = name_hash; + de->name_len = cpu_to_le16(name->len); + memcpy(d->filename[bit_pos], name->name, name->len); + de->ino = cpu_to_le32(ino); + set_de_type(de, mode); + for (i = 0; i < slots; i++) + test_and_set_bit_le(bit_pos + i, (void *)d->bitmap); +} + /* * Caller should grab and release a rwsem by calling f2fs_lock_op() and * f2fs_unlock_op(). */ int __f2fs_add_link(struct inode *dir, const struct qstr *name, - struct inode *inode) + struct inode *inode, nid_t ino, umode_t mode) { unsigned int bit_pos; unsigned int level; unsigned int current_depth; unsigned long bidx, block; f2fs_hash_t dentry_hash; - struct f2fs_dir_entry *de; unsigned int nbucket, nblock; - size_t namelen = name->len; struct page *dentry_page = NULL; struct f2fs_dentry_block *dentry_blk = NULL; - int slots = GET_DENTRY_SLOTS(namelen); - struct page *page; - int err = 0; - int i; + struct f2fs_dentry_ptr d; + struct page *page = NULL; + struct f2fs_filename fname; + struct qstr new_name; + int slots, err; + + err = f2fs_fname_setup_filename(dir, name, 0, &fname); + if (err) + return err; + + new_name.name = fname_name(&fname); + new_name.len = fname_len(&fname); + + if (f2fs_has_inline_dentry(dir)) { + err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); + if (!err || err != -EAGAIN) + goto out; + else + err = 0; + } - dentry_hash = f2fs_dentry_hash(name); level = 0; + slots = GET_DENTRY_SLOTS(new_name.len); + dentry_hash = f2fs_dentry_hash(&new_name); + current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { level = F2FS_I(dir)->clevel; @@ -472,8 +556,10 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, } start: - if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) - return -ENOSPC; + if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) { + err = -ENOSPC; + goto out; + } /* Increase the depth, if required */ if (level == current_depth) @@ -487,11 +573,14 @@ start: for (block = bidx; block <= (bidx + nblock - 1); block++) { dentry_page = get_new_data_page(dir, NULL, block, true); - if (IS_ERR(dentry_page)) - return PTR_ERR(dentry_page); + if (IS_ERR(dentry_page)) { + err = PTR_ERR(dentry_page); + goto out; + } dentry_blk = kmap(dentry_page); - bit_pos = room_for_filename(dentry_blk, slots); + bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, + slots, NR_DENTRY_IN_BLOCK); if (bit_pos < NR_DENTRY_IN_BLOCK) goto add_dentry; @@ -505,30 +594,33 @@ start: add_dentry: f2fs_wait_on_page_writeback(dentry_page, DATA); - down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, name); - if (IS_ERR(page)) { - err = PTR_ERR(page); - goto fail; + if (inode) { + down_write(&F2FS_I(inode)->i_sem); + page = init_inode_metadata(inode, dir, &new_name, NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto fail; + } + if (f2fs_encrypted_inode(dir)) + file_set_enc_name(inode); } - de = &dentry_blk->dentry[bit_pos]; - de->hash_code = dentry_hash; - de->name_len = cpu_to_le16(namelen); - memcpy(dentry_blk->filename[bit_pos], name->name, name->len); - de->ino = cpu_to_le32(inode->i_ino); - set_de_type(de, inode); - for (i = 0; i < slots; i++) - test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); + + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos); + set_page_dirty(dentry_page); - /* we don't need to mark_inode_dirty now */ - F2FS_I(inode)->i_pino = dir->i_ino; - update_inode(inode, page); - f2fs_put_page(page, 1); + if (inode) { + /* we don't need to mark_inode_dirty now */ + F2FS_I(inode)->i_pino = dir->i_ino; + update_inode(inode, page); + f2fs_put_page(page, 1); + } update_parent_metadata(dir, inode, current_depth); fail: - up_write(&F2FS_I(inode)->i_sem); + if (inode) + up_write(&F2FS_I(inode)->i_sem); if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) { update_inode_page(dir); @@ -536,6 +628,8 @@ fail: } kunmap(dentry_page); f2fs_put_page(dentry_page, 1); +out: + f2fs_fname_free_filename(&fname); return err; } @@ -545,7 +639,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir) int err = 0; down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, NULL); + page = init_inode_metadata(inode, dir, NULL, NULL); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; @@ -560,19 +654,50 @@ fail: return err; } +void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + + down_write(&F2FS_I(inode)->i_sem); + + if (S_ISDIR(inode->i_mode)) { + drop_nlink(dir); + if (page) + update_inode(dir, page); + else + update_inode_page(dir); + } + inode->i_ctime = CURRENT_TIME; + + drop_nlink(inode); + if (S_ISDIR(inode->i_mode)) { + drop_nlink(inode); + i_size_write(inode, 0); + } + up_write(&F2FS_I(inode)->i_sem); + update_inode_page(inode); + + if (inode->i_nlink == 0) + add_orphan_inode(sbi, inode->i_ino); + else + release_orphan_inode(sbi); +} + /* * It only removes the dentry from the dentry page, corresponding name * entry in name page does not need to be touched during deletion. */ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, - struct inode *inode) + struct inode *dir, struct inode *inode) { struct f2fs_dentry_block *dentry_blk; unsigned int bit_pos; - struct inode *dir = page->mapping->host; int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); int i; + if (f2fs_has_inline_dentry(dir)) + return f2fs_delete_inline_entry(dentry, page, dir, inode); + lock_page(page); f2fs_wait_on_page_writeback(page, DATA); @@ -590,33 +715,13 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, dir->i_ctime = dir->i_mtime = CURRENT_TIME; - if (inode) { - struct f2fs_sb_info *sbi = F2FS_I_SB(dir); - - down_write(&F2FS_I(inode)->i_sem); - - if (S_ISDIR(inode->i_mode)) { - drop_nlink(dir); - update_inode_page(dir); - } - inode->i_ctime = CURRENT_TIME; - drop_nlink(inode); - if (S_ISDIR(inode->i_mode)) { - drop_nlink(inode); - i_size_write(inode, 0); - } - up_write(&F2FS_I(inode)->i_sem); - update_inode_page(inode); - - if (inode->i_nlink == 0) - add_orphan_inode(sbi, inode->i_ino); - else - release_orphan_inode(sbi); - } + if (inode) + f2fs_drop_nlink(dir, inode, NULL); - if (bit_pos == NR_DENTRY_IN_BLOCK) { - truncate_hole(dir, page->index, page->index + 1); + if (bit_pos == NR_DENTRY_IN_BLOCK && + !truncate_hole(dir, page->index, page->index + 1)) { clear_page_dirty_for_io(page); + ClearPagePrivate(page); ClearPageUptodate(page); inode_dec_dirty_pages(dir); } @@ -628,11 +733,14 @@ bool f2fs_empty_dir(struct inode *dir) unsigned long bidx; struct page *dentry_page; unsigned int bit_pos; - struct f2fs_dentry_block *dentry_blk; + struct f2fs_dentry_block *dentry_blk; unsigned long nblock = dir_blocks(dir); + if (f2fs_has_inline_dentry(dir)) + return f2fs_empty_inline_dir(dir); + for (bidx = 0; bidx < nblock; bidx++) { - dentry_page = get_lock_data_page(dir, bidx); + dentry_page = get_lock_data_page(dir, bidx, false); if (IS_ERR(dentry_page)) { if (PTR_ERR(dentry_page) == -ENOENT) continue; @@ -640,7 +748,6 @@ bool f2fs_empty_dir(struct inode *dir) return false; } - dentry_blk = kmap_atomic(dentry_page); if (bidx == 0) bit_pos = 2; @@ -659,19 +766,87 @@ bool f2fs_empty_dir(struct inode *dir) return true; } +bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, + unsigned int start_pos, struct f2fs_str *fstr) +{ + unsigned char d_type = DT_UNKNOWN; + unsigned int bit_pos; + struct f2fs_dir_entry *de = NULL; + struct f2fs_str de_name = FSTR_INIT(NULL, 0); + + bit_pos = ((unsigned long)ctx->pos % d->max); + + while (bit_pos < d->max) { + bit_pos = find_next_bit_le(d->bitmap, d->max, bit_pos); + if (bit_pos >= d->max) + break; + + de = &d->dentry[bit_pos]; + if (de->file_type < F2FS_FT_MAX) + d_type = f2fs_filetype_table[de->file_type]; + else + d_type = DT_UNKNOWN; + + de_name.name = d->filename[bit_pos]; + de_name.len = le16_to_cpu(de->name_len); + + if (f2fs_encrypted_inode(d->inode)) { + int save_len = fstr->len; + int ret; + + de_name.name = kmalloc(de_name.len, GFP_NOFS); + if (!de_name.name) + return false; + + memcpy(de_name.name, d->filename[bit_pos], de_name.len); + + ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code, + &de_name, fstr); + kfree(de_name.name); + if (ret < 0) + return true; + + de_name = *fstr; + fstr->len = save_len; + } + + if (!dir_emit(ctx, de_name.name, de_name.len, + le32_to_cpu(de->ino), d_type)) + return true; + + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + ctx->pos = start_pos + bit_pos; + } + return false; +} + static int f2fs_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); unsigned long npages = dir_blocks(inode); - unsigned int bit_pos = 0; struct f2fs_dentry_block *dentry_blk = NULL; - struct f2fs_dir_entry *de = NULL; struct page *dentry_page = NULL; struct file_ra_state *ra = &file->f_ra; unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); - unsigned char d_type = DT_UNKNOWN; + struct f2fs_dentry_ptr d; + struct f2fs_str fstr = FSTR_INIT(NULL, 0); + int err = 0; - bit_pos = ((unsigned long)ctx->pos % NR_DENTRY_IN_BLOCK); + if (f2fs_encrypted_inode(inode)) { + err = f2fs_get_encryption_info(inode); + if (err) + return err; + + err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN, + &fstr); + if (err < 0) + return err; + } + + if (f2fs_has_inline_dentry(inode)) { + err = f2fs_read_inline_dir(file, ctx, &fstr); + goto out; + } /* readahead for multi pages of dir */ if (npages - n > 1 && !ra_has_index(ra, n)) @@ -679,33 +854,17 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES)); for (; n < npages; n++) { - dentry_page = get_lock_data_page(inode, n); + dentry_page = get_lock_data_page(inode, n, false); if (IS_ERR(dentry_page)) continue; dentry_blk = kmap(dentry_page); - while (bit_pos < NR_DENTRY_IN_BLOCK) { - bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - bit_pos); - if (bit_pos >= NR_DENTRY_IN_BLOCK) - break; - - de = &dentry_blk->dentry[bit_pos]; - if (de->file_type < F2FS_FT_MAX) - d_type = f2fs_filetype_table[de->file_type]; - else - d_type = DT_UNKNOWN; - if (!dir_emit(ctx, - dentry_blk->filename[bit_pos], - le16_to_cpu(de->name_len), - le32_to_cpu(de->ino), d_type)) - goto stop; - bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); - ctx->pos = n * NR_DENTRY_IN_BLOCK + bit_pos; - } - bit_pos = 0; + make_dentry_ptr(inode, &d, (void *)dentry_blk, 1); + + if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) + goto stop; + ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; kunmap(dentry_page); f2fs_put_page(dentry_page, 1); @@ -716,8 +875,9 @@ stop: kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } - - return 0; +out: + f2fs_fname_crypto_free_buffer(&fstr); + return err; } const struct file_operations f2fs_dir_operations = { @@ -726,4 +886,7 @@ const struct file_operations f2fs_dir_operations = { .iterate = f2fs_readdir, .fsync = f2fs_sync_file, .unlocked_ioctl = f2fs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = f2fs_compat_ioctl, +#endif }; diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c new file mode 100644 index 000000000000..7ddba812e11b --- /dev/null +++ b/fs/f2fs/extent_cache.c @@ -0,0 +1,748 @@ +/* + * f2fs extent cache support + * + * Copyright (c) 2015 Motorola Mobility + * Copyright (c) 2015 Samsung Electronics + * Authors: Jaegeuk Kim + * Chao Yu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "f2fs.h" +#include "node.h" +#include + +static struct kmem_cache *extent_tree_slab; +static struct kmem_cache *extent_node_slab; + +static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_info *ei, + struct rb_node *parent, struct rb_node **p) +{ + struct extent_node *en; + + en = kmem_cache_alloc(extent_node_slab, GFP_ATOMIC); + if (!en) + return NULL; + + en->ei = *ei; + INIT_LIST_HEAD(&en->list); + + rb_link_node(&en->rb_node, parent, p); + rb_insert_color(&en->rb_node, &et->root); + et->count++; + atomic_inc(&sbi->total_ext_node); + return en; +} + +static void __detach_extent_node(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_node *en) +{ + rb_erase(&en->rb_node, &et->root); + et->count--; + atomic_dec(&sbi->total_ext_node); + + if (et->cached_en == en) + et->cached_en = NULL; +} + +static struct extent_tree *__grab_extent_tree(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et; + nid_t ino = inode->i_ino; + + down_write(&sbi->extent_tree_lock); + et = radix_tree_lookup(&sbi->extent_tree_root, ino); + if (!et) { + et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS); + f2fs_radix_tree_insert(&sbi->extent_tree_root, ino, et); + memset(et, 0, sizeof(struct extent_tree)); + et->ino = ino; + et->root = RB_ROOT; + et->cached_en = NULL; + rwlock_init(&et->lock); + atomic_set(&et->refcount, 0); + et->count = 0; + sbi->total_ext_tree++; + } + atomic_inc(&et->refcount); + up_write(&sbi->extent_tree_lock); + + /* never died until evict_inode */ + F2FS_I(inode)->extent_tree = et; + + return et; +} + +static struct extent_node *__lookup_extent_tree(struct f2fs_sb_info *sbi, + struct extent_tree *et, unsigned int fofs) +{ + struct rb_node *node = et->root.rb_node; + struct extent_node *en = et->cached_en; + + if (en) { + struct extent_info *cei = &en->ei; + + if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) { + stat_inc_cached_node_hit(sbi); + return en; + } + } + + while (node) { + en = rb_entry(node, struct extent_node, rb_node); + + if (fofs < en->ei.fofs) { + node = node->rb_left; + } else if (fofs >= en->ei.fofs + en->ei.len) { + node = node->rb_right; + } else { + stat_inc_rbtree_node_hit(sbi); + return en; + } + } + return NULL; +} + +static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_info *ei) +{ + struct rb_node **p = &et->root.rb_node; + struct extent_node *en; + + en = __attach_extent_node(sbi, et, ei, NULL, p); + if (!en) + return NULL; + + et->largest = en->ei; + et->cached_en = en; + return en; +} + +static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, + struct extent_tree *et, bool free_all) +{ + struct rb_node *node, *next; + struct extent_node *en; + unsigned int count = et->count; + + node = rb_first(&et->root); + while (node) { + next = rb_next(node); + en = rb_entry(node, struct extent_node, rb_node); + + if (free_all) { + spin_lock(&sbi->extent_lock); + if (!list_empty(&en->list)) + list_del_init(&en->list); + spin_unlock(&sbi->extent_lock); + } + + if (free_all || list_empty(&en->list)) { + __detach_extent_node(sbi, et, en); + kmem_cache_free(extent_node_slab, en); + } + node = next; + } + + return count - et->count; +} + +static void __drop_largest_extent(struct inode *inode, + pgoff_t fofs, unsigned int len) +{ + struct extent_info *largest = &F2FS_I(inode)->extent_tree->largest; + + if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs) + largest->len = 0; +} + +void f2fs_drop_largest_extent(struct inode *inode, pgoff_t fofs) +{ + if (!f2fs_may_extent_tree(inode)) + return; + + __drop_largest_extent(inode, fofs, 1); +} + +void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et; + struct extent_node *en; + struct extent_info ei; + + if (!f2fs_may_extent_tree(inode)) + return; + + et = __grab_extent_tree(inode); + + if (!i_ext || le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN) + return; + + set_extent_info(&ei, le32_to_cpu(i_ext->fofs), + le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); + + write_lock(&et->lock); + if (et->count) + goto out; + + en = __init_extent_tree(sbi, et, &ei); + if (en) { + spin_lock(&sbi->extent_lock); + list_add_tail(&en->list, &sbi->extent_list); + spin_unlock(&sbi->extent_lock); + } +out: + write_unlock(&et->lock); +} + +static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, + struct extent_info *ei) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et = F2FS_I(inode)->extent_tree; + struct extent_node *en; + bool ret = false; + + f2fs_bug_on(sbi, !et); + + trace_f2fs_lookup_extent_tree_start(inode, pgofs); + + read_lock(&et->lock); + + if (et->largest.fofs <= pgofs && + et->largest.fofs + et->largest.len > pgofs) { + *ei = et->largest; + ret = true; + stat_inc_largest_node_hit(sbi); + goto out; + } + + en = __lookup_extent_tree(sbi, et, pgofs); + if (en) { + *ei = en->ei; + spin_lock(&sbi->extent_lock); + if (!list_empty(&en->list)) + list_move_tail(&en->list, &sbi->extent_list); + et->cached_en = en; + spin_unlock(&sbi->extent_lock); + ret = true; + } +out: + stat_inc_total_hit(sbi); + read_unlock(&et->lock); + + trace_f2fs_lookup_extent_tree_end(inode, pgofs, ei); + return ret; +} + + +/* + * lookup extent at @fofs, if hit, return the extent + * if not, return NULL and + * @prev_ex: extent before fofs + * @next_ex: extent after fofs + * @insert_p: insert point for new extent at fofs + * in order to simpfy the insertion after. + * tree must stay unchanged between lookup and insertion. + */ +static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et, + unsigned int fofs, + struct extent_node **prev_ex, + struct extent_node **next_ex, + struct rb_node ***insert_p, + struct rb_node **insert_parent) +{ + struct rb_node **pnode = &et->root.rb_node; + struct rb_node *parent = NULL, *tmp_node; + struct extent_node *en = et->cached_en; + + *insert_p = NULL; + *insert_parent = NULL; + *prev_ex = NULL; + *next_ex = NULL; + + if (RB_EMPTY_ROOT(&et->root)) + return NULL; + + if (en) { + struct extent_info *cei = &en->ei; + + if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) + goto lookup_neighbors; + } + + while (*pnode) { + parent = *pnode; + en = rb_entry(*pnode, struct extent_node, rb_node); + + if (fofs < en->ei.fofs) + pnode = &(*pnode)->rb_left; + else if (fofs >= en->ei.fofs + en->ei.len) + pnode = &(*pnode)->rb_right; + else + goto lookup_neighbors; + } + + *insert_p = pnode; + *insert_parent = parent; + + en = rb_entry(parent, struct extent_node, rb_node); + tmp_node = parent; + if (parent && fofs > en->ei.fofs) + tmp_node = rb_next(parent); + *next_ex = tmp_node ? + rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + + tmp_node = parent; + if (parent && fofs < en->ei.fofs) + tmp_node = rb_prev(parent); + *prev_ex = tmp_node ? + rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + return NULL; + +lookup_neighbors: + if (fofs == en->ei.fofs) { + /* lookup prev node for merging backward later */ + tmp_node = rb_prev(&en->rb_node); + *prev_ex = tmp_node ? + rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + } + if (fofs == en->ei.fofs + en->ei.len - 1) { + /* lookup next node for merging frontward later */ + tmp_node = rb_next(&en->rb_node); + *next_ex = tmp_node ? + rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + } + return en; +} + +static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_info *ei, + struct extent_node **den, + struct extent_node *prev_ex, + struct extent_node *next_ex) +{ + struct extent_node *en = NULL; + + if (prev_ex && __is_back_mergeable(ei, &prev_ex->ei)) { + prev_ex->ei.len += ei->len; + ei = &prev_ex->ei; + en = prev_ex; + } + + if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) { + if (en) { + __detach_extent_node(sbi, et, prev_ex); + *den = prev_ex; + } + next_ex->ei.fofs = ei->fofs; + next_ex->ei.blk = ei->blk; + next_ex->ei.len += ei->len; + en = next_ex; + } + + if (en) { + __try_update_largest_extent(et, en); + et->cached_en = en; + } + return en; +} + +static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_info *ei, + struct rb_node **insert_p, + struct rb_node *insert_parent) +{ + struct rb_node **p = &et->root.rb_node; + struct rb_node *parent = NULL; + struct extent_node *en = NULL; + + if (insert_p && insert_parent) { + parent = insert_parent; + p = insert_p; + goto do_insert; + } + + while (*p) { + parent = *p; + en = rb_entry(parent, struct extent_node, rb_node); + + if (ei->fofs < en->ei.fofs) + p = &(*p)->rb_left; + else if (ei->fofs >= en->ei.fofs + en->ei.len) + p = &(*p)->rb_right; + else + f2fs_bug_on(sbi, 1); + } +do_insert: + en = __attach_extent_node(sbi, et, ei, parent, p); + if (!en) + return NULL; + + __try_update_largest_extent(et, en); + et->cached_en = en; + return en; +} + +static unsigned int f2fs_update_extent_tree_range(struct inode *inode, + pgoff_t fofs, block_t blkaddr, unsigned int len) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et = F2FS_I(inode)->extent_tree; + struct extent_node *en = NULL, *en1 = NULL; + struct extent_node *prev_en = NULL, *next_en = NULL; + struct extent_info ei, dei, prev; + struct rb_node **insert_p = NULL, *insert_parent = NULL; + unsigned int end = fofs + len; + unsigned int pos = (unsigned int)fofs; + + if (!et) + return false; + + trace_f2fs_update_extent_tree_range(inode, fofs, blkaddr, len); + + write_lock(&et->lock); + + if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) { + write_unlock(&et->lock); + return false; + } + + prev = et->largest; + dei.len = 0; + + /* + * drop largest extent before lookup, in case it's already + * been shrunk from extent tree + */ + __drop_largest_extent(inode, fofs, len); + + /* 1. lookup first extent node in range [fofs, fofs + len - 1] */ + en = __lookup_extent_tree_ret(et, fofs, &prev_en, &next_en, + &insert_p, &insert_parent); + if (!en) + en = next_en; + + /* 2. invlidate all extent nodes in range [fofs, fofs + len - 1] */ + while (en && en->ei.fofs < end) { + unsigned int org_end; + int parts = 0; /* # of parts current extent split into */ + + next_en = en1 = NULL; + + dei = en->ei; + org_end = dei.fofs + dei.len; + f2fs_bug_on(sbi, pos >= org_end); + + if (pos > dei.fofs && pos - dei.fofs >= F2FS_MIN_EXTENT_LEN) { + en->ei.len = pos - en->ei.fofs; + prev_en = en; + parts = 1; + } + + if (end < org_end && org_end - end >= F2FS_MIN_EXTENT_LEN) { + if (parts) { + set_extent_info(&ei, end, + end - dei.fofs + dei.blk, + org_end - end); + en1 = __insert_extent_tree(sbi, et, &ei, + NULL, NULL); + next_en = en1; + } else { + en->ei.fofs = end; + en->ei.blk += end - dei.fofs; + en->ei.len -= end - dei.fofs; + next_en = en; + } + parts++; + } + + if (!next_en) { + struct rb_node *node = rb_next(&en->rb_node); + + next_en = node ? + rb_entry(node, struct extent_node, rb_node) + : NULL; + } + + if (parts) + __try_update_largest_extent(et, en); + else + __detach_extent_node(sbi, et, en); + + /* + * if original extent is split into zero or two parts, extent + * tree has been altered by deletion or insertion, therefore + * invalidate pointers regard to tree. + */ + if (parts != 1) { + insert_p = NULL; + insert_parent = NULL; + } + + /* update in global extent list */ + spin_lock(&sbi->extent_lock); + if (!parts && !list_empty(&en->list)) + list_del(&en->list); + if (en1) + list_add_tail(&en1->list, &sbi->extent_list); + spin_unlock(&sbi->extent_lock); + + /* release extent node */ + if (!parts) + kmem_cache_free(extent_node_slab, en); + + en = next_en; + } + + /* 3. update extent in extent cache */ + if (blkaddr) { + struct extent_node *den = NULL; + + set_extent_info(&ei, fofs, blkaddr, len); + en1 = __try_merge_extent_node(sbi, et, &ei, &den, + prev_en, next_en); + if (!en1) + en1 = __insert_extent_tree(sbi, et, &ei, + insert_p, insert_parent); + + /* give up extent_cache, if split and small updates happen */ + if (dei.len >= 1 && + prev.len < F2FS_MIN_EXTENT_LEN && + et->largest.len < F2FS_MIN_EXTENT_LEN) { + et->largest.len = 0; + set_inode_flag(F2FS_I(inode), FI_NO_EXTENT); + } + + spin_lock(&sbi->extent_lock); + if (en1) { + if (list_empty(&en1->list)) + list_add_tail(&en1->list, &sbi->extent_list); + else + list_move_tail(&en1->list, &sbi->extent_list); + } + if (den && !list_empty(&den->list)) + list_del(&den->list); + spin_unlock(&sbi->extent_lock); + + if (den) + kmem_cache_free(extent_node_slab, den); + } + + if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) + __free_extent_tree(sbi, et, true); + + write_unlock(&et->lock); + + return !__is_extent_same(&prev, &et->largest); +} + +unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) +{ + struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; + struct extent_node *en, *tmp; + unsigned long ino = F2FS_ROOT_INO(sbi); + struct radix_tree_root *root = &sbi->extent_tree_root; + unsigned int found; + unsigned int node_cnt = 0, tree_cnt = 0; + int remained; + + if (!test_opt(sbi, EXTENT_CACHE)) + return 0; + + if (!down_write_trylock(&sbi->extent_tree_lock)) + goto out; + + /* 1. remove unreferenced extent tree */ + while ((found = radix_tree_gang_lookup(root, + (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { + unsigned i; + + ino = treevec[found - 1]->ino + 1; + for (i = 0; i < found; i++) { + struct extent_tree *et = treevec[i]; + + if (!atomic_read(&et->refcount)) { + write_lock(&et->lock); + node_cnt += __free_extent_tree(sbi, et, true); + write_unlock(&et->lock); + + radix_tree_delete(root, et->ino); + kmem_cache_free(extent_tree_slab, et); + sbi->total_ext_tree--; + tree_cnt++; + + if (node_cnt + tree_cnt >= nr_shrink) + goto unlock_out; + } + } + } + up_write(&sbi->extent_tree_lock); + + /* 2. remove LRU extent entries */ + if (!down_write_trylock(&sbi->extent_tree_lock)) + goto out; + + remained = nr_shrink - (node_cnt + tree_cnt); + + spin_lock(&sbi->extent_lock); + list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) { + if (!remained--) + break; + list_del_init(&en->list); + } + spin_unlock(&sbi->extent_lock); + + /* + * reset ino for searching victims from beginning of global extent tree. + */ + ino = F2FS_ROOT_INO(sbi); + + while ((found = radix_tree_gang_lookup(root, + (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { + unsigned i; + + ino = treevec[found - 1]->ino + 1; + for (i = 0; i < found; i++) { + struct extent_tree *et = treevec[i]; + + write_lock(&et->lock); + node_cnt += __free_extent_tree(sbi, et, false); + write_unlock(&et->lock); + + if (node_cnt + tree_cnt >= nr_shrink) + goto unlock_out; + } + } +unlock_out: + up_write(&sbi->extent_tree_lock); +out: + trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt); + + return node_cnt + tree_cnt; +} + +unsigned int f2fs_destroy_extent_node(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et = F2FS_I(inode)->extent_tree; + unsigned int node_cnt = 0; + + if (!et) + return 0; + + write_lock(&et->lock); + node_cnt = __free_extent_tree(sbi, et, true); + write_unlock(&et->lock); + + return node_cnt; +} + +void f2fs_destroy_extent_tree(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et = F2FS_I(inode)->extent_tree; + unsigned int node_cnt = 0; + + if (!et) + return; + + if (inode->i_nlink && !is_bad_inode(inode) && et->count) { + atomic_dec(&et->refcount); + return; + } + + /* free all extent info belong to this extent tree */ + node_cnt = f2fs_destroy_extent_node(inode); + + /* delete extent tree entry in radix tree */ + down_write(&sbi->extent_tree_lock); + atomic_dec(&et->refcount); + f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count); + radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); + kmem_cache_free(extent_tree_slab, et); + sbi->total_ext_tree--; + up_write(&sbi->extent_tree_lock); + + F2FS_I(inode)->extent_tree = NULL; + + trace_f2fs_destroy_extent_tree(inode, node_cnt); +} + +bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs, + struct extent_info *ei) +{ + if (!f2fs_may_extent_tree(inode)) + return false; + + return f2fs_lookup_extent_tree(inode, pgofs, ei); +} + +void f2fs_update_extent_cache(struct dnode_of_data *dn) +{ + struct f2fs_inode_info *fi = F2FS_I(dn->inode); + pgoff_t fofs; + + if (!f2fs_may_extent_tree(dn->inode)) + return; + + f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR); + + + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + + dn->ofs_in_node; + + if (f2fs_update_extent_tree_range(dn->inode, fofs, dn->data_blkaddr, 1)) + sync_inode_page(dn); +} + +void f2fs_update_extent_cache_range(struct dnode_of_data *dn, + pgoff_t fofs, block_t blkaddr, unsigned int len) + +{ + if (!f2fs_may_extent_tree(dn->inode)) + return; + + if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, len)) + sync_inode_page(dn); +} + +void init_extent_cache_info(struct f2fs_sb_info *sbi) +{ + INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO); + init_rwsem(&sbi->extent_tree_lock); + INIT_LIST_HEAD(&sbi->extent_list); + spin_lock_init(&sbi->extent_lock); + sbi->total_ext_tree = 0; + atomic_set(&sbi->total_ext_node, 0); +} + +int __init create_extent_cache(void) +{ + extent_tree_slab = f2fs_kmem_cache_create("f2fs_extent_tree", + sizeof(struct extent_tree)); + if (!extent_tree_slab) + return -ENOMEM; + extent_node_slab = f2fs_kmem_cache_create("f2fs_extent_node", + sizeof(struct extent_node)); + if (!extent_node_slab) { + kmem_cache_destroy(extent_tree_slab); + return -ENOMEM; + } + return 0; +} + +void destroy_extent_cache(void) +{ + kmem_cache_destroy(extent_node_slab); + kmem_cache_destroy(extent_tree_slab); +} diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8171e80b2ee9..e6ceeb1bd6bb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) @@ -28,7 +30,7 @@ do { \ if (unlikely(condition)) { \ WARN_ON(1); \ - sbi->need_fsck = true; \ + set_sbi_flag(sbi, SBI_NEED_FSCK); \ } \ } while (0) #define f2fs_down_write(x, y) down_write(x) @@ -46,8 +48,12 @@ #define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040 #define F2FS_MOUNT_INLINE_XATTR 0x00000080 #define F2FS_MOUNT_INLINE_DATA 0x00000100 -#define F2FS_MOUNT_FLUSH_MERGE 0x00000200 -#define F2FS_MOUNT_NOBARRIER 0x00000400 +#define F2FS_MOUNT_INLINE_DENTRY 0x00000200 +#define F2FS_MOUNT_FLUSH_MERGE 0x00000400 +#define F2FS_MOUNT_NOBARRIER 0x00000800 +#define F2FS_MOUNT_FASTBOOT 0x00001000 +#define F2FS_MOUNT_EXTENT_CACHE 0x00002000 +#define F2FS_MOUNT_FORCE_FG_GC 0x00004000 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) @@ -67,6 +73,15 @@ struct f2fs_mount_info { unsigned int opt; }; +#define F2FS_FEATURE_ENCRYPT 0x0001 + +#define F2FS_HAS_FEATURE(sb, mask) \ + ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) +#define F2FS_SET_FEATURE(sb, mask) \ + F2FS_SB(sb)->raw_super->feature |= cpu_to_le32(mask) +#define F2FS_CLEAR_FEATURE(sb, mask) \ + F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask) + #define CRCPOLY_LE 0xedb88320 static inline __u32 f2fs_crc32(void *buf, size_t len) @@ -98,10 +113,19 @@ enum { enum { CP_UMOUNT, + CP_FASTBOOT, CP_SYNC, + CP_RECOVERY, CP_DISCARD, }; +#define DEF_BATCHED_TRIM_SECTIONS 32 +#define BATCHED_TRIM_SEGMENTS(sbi) \ + (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) +#define BATCHED_TRIM_BLOCKS(sbi) \ + (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) +#define DEF_CP_INTERVAL 60 /* 60 secs */ + struct cp_control { int reason; __u64 trim_start; @@ -134,8 +158,14 @@ struct ino_entry { nid_t ino; /* inode number */ }; -/* for the list of directory inodes */ -struct dir_inode_entry { +/* + * for the list of directory inodes or gc inodes. + * NOTE: there are two slab users for this structure, if we add/modify/delete + * fields in structure for one of slab users, it may affect fields or size of + * other one, in this condition, it's better to split both of slab and related + * data structure. + */ +struct inode_entry { struct list_head list; /* list head */ struct inode *inode; /* vfs inode pointer */ }; @@ -194,11 +224,32 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, */ #define F2FS_IOC_GETFLAGS FS_IOC_GETFLAGS #define F2FS_IOC_SETFLAGS FS_IOC_SETFLAGS +#define F2FS_IOC_GETVERSION FS_IOC_GETVERSION +#define FS_IOC_SHUTDOWN _IOR('X', 125, __u32) /* Shutdown */ + +/* + * Flags for going down operation used by FS_IOC_GOINGDOWN + */ +#define FS_GOING_DOWN_FULLSYNC 0x0 /* going down with full sync */ +#define FS_GOING_DOWN_METASYNC 0x1 /* going down with metadata */ +#define FS_GOING_DOWN_NOSYNC 0x2 /* going down */ +#define FS_GOING_DOWN_METAFLUSH 0x3 /* going down with meta flush */ #define F2FS_IOCTL_MAGIC 0xf5 #define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) #define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) #define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) +#define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4) +#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) +#define F2FS_IOC_GARBAGE_COLLECT _IO(F2FS_IOCTL_MAGIC, 6) +#define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7) + +#define F2FS_IOC_SET_ENCRYPTION_POLICY \ + _IOR('f', 19, struct f2fs_encryption_policy) +#define F2FS_IOC_GET_ENCRYPTION_PWSALT \ + _IOW('f', 20, __u8[16]) +#define F2FS_IOC_GET_ENCRYPTION_POLICY \ + _IOW('f', 21, struct f2fs_encryption_policy) #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* @@ -211,6 +262,54 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, /* * For INODE and NODE manager */ +/* for directory operations */ +struct f2fs_str { + unsigned char *name; + u32 len; +}; + +struct f2fs_filename { + const struct qstr *usr_fname; + struct f2fs_str disk_name; + f2fs_hash_t hash; +#ifdef CONFIG_F2FS_FS_ENCRYPTION + struct f2fs_str crypto_buf; +#endif +}; + +#define FSTR_INIT(n, l) { .name = n, .len = l } +#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) + +struct f2fs_dentry_ptr { + struct inode *inode; + const void *bitmap; + struct f2fs_dir_entry *dentry; + __u8 (*filename)[F2FS_SLOT_LEN]; + int max; +}; + +static inline void make_dentry_ptr(struct inode *inode, + struct f2fs_dentry_ptr *d, void *src, int type) +{ + d->inode = inode; + + if (type == 1) { + struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src; + d->max = NR_DENTRY_IN_BLOCK; + d->bitmap = &t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; + } else { + struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src; + d->max = NR_INLINE_DENTRY; + d->bitmap = &t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; + } +} + /* * XATTR_NODE_OFFSET stores xattrs to one node block per file keeping -1 * as its node offset to distinguish from index node blocks. @@ -227,25 +326,93 @@ enum { */ }; -#define F2FS_LINK_MAX 32000 /* maximum link count per file */ +#define F2FS_LINK_MAX 0xffffffff /* maximum link count per file */ #define MAX_DIR_RA_PAGES 4 /* maximum ra pages of dir */ +/* vector size for gang look-up from extent cache that consists of radix tree */ +#define EXT_TREE_VEC_SIZE 64 + /* for in-memory extent cache entry */ -#define F2FS_MIN_EXTENT_LEN 16 /* minimum extent length */ +#define F2FS_MIN_EXTENT_LEN 64 /* minimum extent length */ + +/* number of extent info in extent cache we try to shrink */ +#define EXTENT_CACHE_SHRINK_NUMBER 128 struct extent_info { - rwlock_t ext_lock; /* rwlock for consistency */ - unsigned int fofs; /* start offset in a file */ - u32 blk_addr; /* start block address of the extent */ - unsigned int len; /* length of the extent */ + unsigned int fofs; /* start offset in a file */ + u32 blk; /* start block address of the extent */ + unsigned int len; /* length of the extent */ +}; + +struct extent_node { + struct rb_node rb_node; /* rb node located in rb-tree */ + struct list_head list; /* node in global extent list of sbi */ + struct extent_info ei; /* extent info */ +}; + +struct extent_tree { + nid_t ino; /* inode number */ + struct rb_root root; /* root of extent info rb-tree */ + struct extent_node *cached_en; /* recently accessed extent node */ + struct extent_info largest; /* largested extent info */ + rwlock_t lock; /* protect extent info rb-tree */ + atomic_t refcount; /* reference count of rb-tree */ + unsigned int count; /* # of extent node in rb-tree*/ }; +/* + * This structure is taken from ext4_map_blocks. + * + * Note that, however, f2fs uses NEW and MAPPED flags for f2fs_map_blocks(). + */ +#define F2FS_MAP_NEW (1 << BH_New) +#define F2FS_MAP_MAPPED (1 << BH_Mapped) +#define F2FS_MAP_UNWRITTEN (1 << BH_Unwritten) +#define F2FS_MAP_FLAGS (F2FS_MAP_NEW | F2FS_MAP_MAPPED |\ + F2FS_MAP_UNWRITTEN) + +struct f2fs_map_blocks { + block_t m_pblk; + block_t m_lblk; + unsigned int m_len; + unsigned int m_flags; +}; + +/* for flag in get_data_block */ +#define F2FS_GET_BLOCK_READ 0 +#define F2FS_GET_BLOCK_DIO 1 +#define F2FS_GET_BLOCK_FIEMAP 2 +#define F2FS_GET_BLOCK_BMAP 3 + /* * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. */ #define FADVISE_COLD_BIT 0x01 #define FADVISE_LOST_PINO_BIT 0x02 +#define FADVISE_ENCRYPT_BIT 0x04 +#define FADVISE_ENC_NAME_BIT 0x08 + +#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT) +#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT) +#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT) +#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT) +#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT) +#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT) +#define file_is_encrypt(inode) is_file(inode, FADVISE_ENCRYPT_BIT) +#define file_set_encrypt(inode) set_file(inode, FADVISE_ENCRYPT_BIT) +#define file_clear_encrypt(inode) clear_file(inode, FADVISE_ENCRYPT_BIT) +#define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT) +#define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT) + +/* Encryption algorithms */ +#define F2FS_ENCRYPTION_MODE_INVALID 0 +#define F2FS_ENCRYPTION_MODE_AES_256_XTS 1 +#define F2FS_ENCRYPTION_MODE_AES_256_GCM 2 +#define F2FS_ENCRYPTION_MODE_AES_256_CBC 3 +#define F2FS_ENCRYPTION_MODE_AES_256_CTS 4 + +#include "f2fs_crypto.h" #define DEF_DIR_LEVEL 0 @@ -266,31 +433,74 @@ struct f2fs_inode_info { unsigned int clevel; /* maximum level of given file name */ nid_t i_xattr_nid; /* node id that contains xattrs */ unsigned long long xattr_ver; /* cp version of xattr modification */ - struct extent_info ext; /* in-memory extent cache entry */ - struct dir_inode_entry *dirty_dir; /* the pointer of dirty dir */ + struct inode_entry *dirty_dir; /* the pointer of dirty dir */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct mutex inmem_lock; /* lock for inmemory pages */ + + struct extent_tree *extent_tree; /* cached extent_tree entry */ + +#ifdef CONFIG_F2FS_FS_ENCRYPTION + /* Encryption params */ + struct f2fs_crypt_info *i_crypt_info; +#endif }; static inline void get_extent_info(struct extent_info *ext, struct f2fs_extent i_ext) { - write_lock(&ext->ext_lock); ext->fofs = le32_to_cpu(i_ext.fofs); - ext->blk_addr = le32_to_cpu(i_ext.blk_addr); + ext->blk = le32_to_cpu(i_ext.blk); ext->len = le32_to_cpu(i_ext.len); - write_unlock(&ext->ext_lock); } static inline void set_raw_extent(struct extent_info *ext, struct f2fs_extent *i_ext) { - read_lock(&ext->ext_lock); i_ext->fofs = cpu_to_le32(ext->fofs); - i_ext->blk_addr = cpu_to_le32(ext->blk_addr); + i_ext->blk = cpu_to_le32(ext->blk); i_ext->len = cpu_to_le32(ext->len); - read_unlock(&ext->ext_lock); +} + +static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, + u32 blk, unsigned int len) +{ + ei->fofs = fofs; + ei->blk = blk; + ei->len = len; +} + +static inline bool __is_extent_same(struct extent_info *ei1, + struct extent_info *ei2) +{ + return (ei1->fofs == ei2->fofs && ei1->blk == ei2->blk && + ei1->len == ei2->len); +} + +static inline bool __is_extent_mergeable(struct extent_info *back, + struct extent_info *front) +{ + return (back->fofs + back->len == front->fofs && + back->blk + back->len == front->blk); +} + +static inline bool __is_back_mergeable(struct extent_info *cur, + struct extent_info *back) +{ + return __is_extent_mergeable(back, cur); +} + +static inline bool __is_front_mergeable(struct extent_info *cur, + struct extent_info *front) +{ + return __is_extent_mergeable(cur, front); +} + +static inline void __try_update_largest_extent(struct extent_tree *et, + struct extent_node *en) +{ + if (en->ei.len > et->largest.len) + et->largest = en->ei; } struct f2fs_nm_info { @@ -299,11 +509,12 @@ struct f2fs_nm_info { nid_t available_nids; /* maximum available node ids */ nid_t next_scan_nid; /* the next nid to be scanned */ unsigned int ram_thresh; /* control the memory footprint */ + unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */ /* NAT cache management */ struct radix_tree_root nat_root;/* root of the nat entry cache */ struct radix_tree_root nat_set_root;/* root of the nat set cache */ - rwlock_t nat_tree_lock; /* protect nat_tree_lock */ + struct rw_semaphore nat_tree_lock; /* protect nat_tree_lock */ struct list_head nat_entries; /* cached nat entry list (clean) */ unsigned int nat_cnt; /* the # of cached nat entries */ unsigned int dirty_nat_cnt; /* total num of nat entries in set */ @@ -369,7 +580,8 @@ enum { CURSEG_HOT_NODE, /* direct node blocks of directory files */ CURSEG_WARM_NODE, /* direct node blocks of normal files */ CURSEG_COLD_NODE, /* indirect node blocks */ - NO_CHECK_TYPE + NO_CHECK_TYPE, + CURSEG_DIRECT_IO, /* to use for the direct IO path */ }; struct flush_cmd { @@ -408,6 +620,9 @@ struct f2fs_sm_info { int nr_discards; /* # of discards in the list */ int max_discards; /* max. discards to be issued */ + /* for batched trimming */ + unsigned int trim_sections; /* # of sections to trim */ + struct list_head sit_entry_set; /* sit entry set list */ unsigned int ipu_policy; /* in-place-update policy */ @@ -433,6 +648,7 @@ enum count_type { F2FS_DIRTY_DENTS, F2FS_DIRTY_NODES, F2FS_DIRTY_META, + F2FS_INMEM_PAGES, NR_COUNT_TYPE, }; @@ -454,11 +670,19 @@ enum page_type { META, NR_PAGE_TYPE, META_FLUSH, + INMEM, /* the below types are used by tracepoints only. */ + INMEM_DROP, + IPU, + OPU, }; struct f2fs_io_info { + struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */ + block_t blk_addr; /* block address to be written */ + struct page *page; /* page to be written */ + struct page *encrypted_page; /* encrypted page */ }; #define is_read_io(rw) (((rw) & 1) == READ) @@ -470,13 +694,28 @@ struct f2fs_bio_info { struct rw_semaphore io_rwsem; /* blocking op for bio */ }; +/* for inner inode cache management */ +struct inode_management { + struct radix_tree_root ino_root; /* ino entry array */ + spinlock_t ino_lock; /* for ino entry lock */ + struct list_head ino_list; /* inode list head */ + unsigned long ino_num; /* number of entries */ +}; + +/* For s_flag in struct f2fs_sb_info */ +enum { + SBI_IS_DIRTY, /* dirty flag for checkpoint */ + SBI_IS_CLOSE, /* specify unmounting */ + SBI_NEED_FSCK, /* need fsck.f2fs to fix */ + SBI_POR_DOING, /* recovery is doing or not */ +}; + struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ struct buffer_head *raw_super_buf; /* buffer head of raw sb */ struct f2fs_super_block *raw_super; /* raw super block pointer */ - int s_dirty; /* dirty flag for checkpoint */ - bool need_fsck; /* need fsck.f2fs to fix */ + int s_flag; /* flags for sbi */ /* for node-related operations */ struct f2fs_nm_info *nm_info; /* node manager */ @@ -488,7 +727,6 @@ struct f2fs_sb_info { /* for bio operations */ struct f2fs_bio_info read_io; /* for read bios */ struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ - struct completion *wait_io; /* for completion bios */ /* for checkpoint */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ @@ -497,22 +735,26 @@ struct f2fs_sb_info { struct rw_semaphore cp_rwsem; /* blocking FS operations */ struct rw_semaphore node_write; /* locking node writes */ struct mutex writepages; /* mutex for writepages() */ - bool por_doing; /* recovery is doing or not */ wait_queue_head_t cp_wait; + long cp_expires, cp_interval; /* next expected periodic cp */ - /* for inode management */ - struct radix_tree_root ino_root[MAX_INO_ENTRY]; /* ino entry array */ - spinlock_t ino_lock[MAX_INO_ENTRY]; /* for ino entry lock */ - struct list_head ino_list[MAX_INO_ENTRY]; /* inode list head */ + struct inode_management im[MAX_INO_ENTRY]; /* manage inode cache */ /* for orphan inode, use 0'th array */ - unsigned int n_orphans; /* # of orphan inodes */ unsigned int max_orphans; /* max orphan inodes */ /* for directory inode management */ struct list_head dir_inode_list; /* dir inode list */ spinlock_t dir_inode_lock; /* for dir inode list lock */ + /* for extent tree cache */ + struct radix_tree_root extent_tree_root;/* cache extent cache entries */ + struct rw_semaphore extent_tree_lock; /* locking extent radix tree */ + struct list_head extent_list; /* lru list for shrinker */ + spinlock_t extent_lock; /* locking extent lru list */ + int total_ext_tree; /* extent tree count */ + atomic_t total_ext_node; /* extent info count */ + /* basic filesystem units */ unsigned int log_sectors_per_block; /* log2 sectors per block */ unsigned int log_blocksize; /* log2 block size */ @@ -534,6 +776,7 @@ struct f2fs_sb_info { block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ block_t alloc_valid_block_count; /* # of allocated blocks */ + block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ u32 s_next_generation; /* for NFS support */ atomic_t nr_pages[NR_COUNT_TYPE]; /* # of pages, see count_type */ @@ -556,8 +799,14 @@ struct f2fs_sb_info { struct f2fs_stat_info *stat_info; /* FS status information */ unsigned int segment_count[2]; /* # of allocated segments */ unsigned int block_count[2]; /* # of allocated blocks */ - int total_hit_ext, read_hit_ext; /* extent cache hit ratio */ - int inline_inode; /* # of inline_data inodes */ + atomic_t inplace_count; /* # of inplace update */ + atomic64_t total_hit_ext; /* # of lookup extent cache */ + atomic64_t read_hit_rbtree; /* # of hit rbtree extent node */ + atomic64_t read_hit_largest; /* # of hit largest extent node */ + atomic64_t read_hit_cached; /* # of hit cached extent node */ + atomic_t inline_xattr; /* # of inline_xattr inodes */ + atomic_t inline_inode; /* # of inline_data inodes */ + atomic_t inline_dir; /* # of inline_dentry inodes */ int bg_gc; /* background gc calls */ unsigned int n_dirty_dirs; /* # of dir inodes */ #endif @@ -567,6 +816,11 @@ struct f2fs_sb_info { /* For sysfs suppport */ struct kobject s_kobj; struct completion s_kobj_unregister; + + /* For shrinker support */ + struct list_head s_list; + struct mutex umount_mutex; + unsigned int shrinker_run_no; }; /* @@ -652,14 +906,19 @@ static inline struct address_space *NODE_MAPPING(struct f2fs_sb_info *sbi) return sbi->node_inode->i_mapping; } -static inline void F2FS_SET_SB_DIRT(struct f2fs_sb_info *sbi) +static inline bool is_sbi_flag_set(struct f2fs_sb_info *sbi, unsigned int type) +{ + return sbi->s_flag & (0x01 << type); +} + +static inline void set_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) { - sbi->s_dirty = 1; + sbi->s_flag |= (0x01 << type); } -static inline void F2FS_RESET_SB_DIRT(struct f2fs_sb_info *sbi) +static inline void clear_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) { - sbi->s_dirty = 0; + sbi->s_flag &= ~(0x01 << type); } static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) @@ -707,6 +966,28 @@ static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi) up_write(&sbi->cp_rwsem); } +static inline int __get_cp_reason(struct f2fs_sb_info *sbi) +{ + int reason = CP_SYNC; + + if (test_opt(sbi, FASTBOOT)) + reason = CP_FASTBOOT; + if (is_sbi_flag_set(sbi, SBI_IS_CLOSE)) + reason = CP_UMOUNT; + return reason; +} + +static inline bool __remain_node_summaries(int reason) +{ + return (reason == CP_UMOUNT || reason == CP_FASTBOOT); +} + +static inline bool __exist_node_summaries(struct f2fs_sb_info *sbi) +{ + return (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG) || + is_set_ckpt_flags(F2FS_CKPT(sbi), CP_FASTBOOT_FLAG)); +} + /* * Check whether the given nid is within node id range. */ @@ -771,7 +1052,7 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) { atomic_inc(&sbi->nr_pages[count_type]); - F2FS_SET_SB_DIRT(sbi); + set_sbi_flag(sbi, SBI_IS_DIRTY); } static inline void inode_inc_dirty_pages(struct inode *inode) @@ -788,7 +1069,8 @@ static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) static inline void inode_dec_dirty_pages(struct inode *inode) { - if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)) + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && + !S_ISLNK(inode->i_mode)) return; atomic_dec(&F2FS_I(inode)->dirty_pages); @@ -833,12 +1115,17 @@ static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag) return 0; } +static inline block_t __cp_payload(struct f2fs_sb_info *sbi) +{ + return le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload); +} + static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); int offset; - if (le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload) > 0) { + if (__cp_payload(sbi) > 0) { if (flag == NAT_BITMAP) return &ckpt->sit_nat_version_bitmap; else @@ -946,6 +1233,24 @@ static inline unsigned int valid_inode_count(struct f2fs_sb_info *sbi) return sbi->total_valid_inode_count; } +static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, + pgoff_t index, bool for_write) +{ + if (!for_write) + return grab_cache_page(mapping, index); + return grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS); +} + +static inline void f2fs_copy_page(struct page *src, struct page *dst) +{ + char *src_kaddr = kmap(src); + char *dst_kaddr = kmap(dst); + + memcpy(dst_kaddr, src_kaddr, PAGE_SIZE); + kunmap(dst); + kunmap(src); +} + static inline void f2fs_put_page(struct page *page, int unlock) { if (!page) @@ -978,16 +1283,31 @@ static inline void *f2fs_kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) { void *entry; -retry: - entry = kmem_cache_alloc(cachep, flags); - if (!entry) { - cond_resched(); - goto retry; - } + entry = kmem_cache_alloc(cachep, flags); + if (!entry) + entry = kmem_cache_alloc(cachep, flags | __GFP_NOFAIL); return entry; } +static inline struct bio *f2fs_bio_alloc(int npages) +{ + struct bio *bio; + + /* No failure on bio allocation */ + bio = bio_alloc(GFP_NOIO, npages); + if (!bio) + bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, npages); + return bio; +} + +static inline void f2fs_radix_tree_insert(struct radix_tree_root *root, + unsigned long index, void *item) +{ + while (radix_tree_insert(root, index, item)) + cond_resched(); +} + #define RAW_IS_INODE(p) ((p)->footer.nid == (p)->footer.ino) static inline bool IS_INODE(struct page *page) @@ -1020,7 +1340,25 @@ static inline int f2fs_test_bit(unsigned int nr, char *addr) return mask & *addr; } -static inline int f2fs_set_bit(unsigned int nr, char *addr) +static inline void f2fs_set_bit(unsigned int nr, char *addr) +{ + int mask; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + *addr |= mask; +} + +static inline void f2fs_clear_bit(unsigned int nr, char *addr) +{ + int mask; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + *addr &= ~mask; +} + +static inline int f2fs_test_and_set_bit(unsigned int nr, char *addr) { int mask; int ret; @@ -1032,7 +1370,7 @@ static inline int f2fs_set_bit(unsigned int nr, char *addr) return ret; } -static inline int f2fs_clear_bit(unsigned int nr, char *addr) +static inline int f2fs_test_and_clear_bit(unsigned int nr, char *addr) { int mask; int ret; @@ -1044,6 +1382,15 @@ static inline int f2fs_clear_bit(unsigned int nr, char *addr) return ret; } +static inline void f2fs_change_bit(unsigned int nr, char *addr) +{ + int mask; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + *addr ^= mask; +} + /* used for f2fs_inode_info->flags */ enum { FI_NEW_INODE, /* indicate newly allocated inode */ @@ -1052,16 +1399,22 @@ enum { FI_INC_LINK, /* need to increment i_nlink */ FI_ACL_MODE, /* indicate acl mode */ FI_NO_ALLOC, /* should not allocate any blocks */ + FI_FREE_NID, /* free allocated nide */ FI_UPDATE_DIR, /* should update inode block for consistency */ FI_DELAY_IPUT, /* used for the recovery */ FI_NO_EXTENT, /* not to use the extent cache */ FI_INLINE_XATTR, /* used for inline xattr */ FI_INLINE_DATA, /* used for inline data*/ + FI_INLINE_DENTRY, /* used for inline dentry */ FI_APPEND_WRITE, /* inode has appended data */ FI_UPDATE_WRITE, /* inode has in-place-update data */ FI_NEED_IPU, /* used for ipu per file */ FI_ATOMIC_FILE, /* indicate atomic file */ FI_VOLATILE_FILE, /* indicate volatile file */ + FI_FIRST_BLOCK_WRITTEN, /* indicate #0 data block was written */ + FI_DROP_CACHE, /* drop dirty page cache */ + FI_DATA_EXIST, /* indicate data exists */ + FI_INLINE_DOTS, /* indicate inline dot dentries */ }; static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) @@ -1087,15 +1440,6 @@ static inline void set_acl_inode(struct f2fs_inode_info *fi, umode_t mode) set_inode_flag(fi, FI_ACL_MODE); } -static inline int cond_clear_inode_flag(struct f2fs_inode_info *fi, int flag) -{ - if (is_inode_flag_set(fi, FI_ACL_MODE)) { - clear_inode_flag(fi, FI_ACL_MODE); - return 1; - } - return 0; -} - static inline void get_inline_info(struct f2fs_inode_info *fi, struct f2fs_inode *ri) { @@ -1103,6 +1447,12 @@ static inline void get_inline_info(struct f2fs_inode_info *fi, set_inode_flag(fi, FI_INLINE_XATTR); if (ri->i_inline & F2FS_INLINE_DATA) set_inode_flag(fi, FI_INLINE_DATA); + if (ri->i_inline & F2FS_INLINE_DENTRY) + set_inode_flag(fi, FI_INLINE_DENTRY); + if (ri->i_inline & F2FS_DATA_EXIST) + set_inode_flag(fi, FI_DATA_EXIST); + if (ri->i_inline & F2FS_INLINE_DOTS) + set_inode_flag(fi, FI_INLINE_DOTS); } static inline void set_raw_inline(struct f2fs_inode_info *fi, @@ -1114,6 +1464,12 @@ static inline void set_raw_inline(struct f2fs_inode_info *fi, ri->i_inline |= F2FS_INLINE_XATTR; if (is_inode_flag_set(fi, FI_INLINE_DATA)) ri->i_inline |= F2FS_INLINE_DATA; + if (is_inode_flag_set(fi, FI_INLINE_DENTRY)) + ri->i_inline |= F2FS_INLINE_DENTRY; + if (is_inode_flag_set(fi, FI_DATA_EXIST)) + ri->i_inline |= F2FS_DATA_EXIST; + if (is_inode_flag_set(fi, FI_INLINE_DOTS)) + ri->i_inline |= F2FS_INLINE_DOTS; } static inline int f2fs_has_inline_xattr(struct inode *inode) @@ -1148,6 +1504,22 @@ static inline int f2fs_has_inline_data(struct inode *inode) return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA); } +static inline void f2fs_clear_inline_inode(struct inode *inode) +{ + clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + clear_inode_flag(F2FS_I(inode), FI_DATA_EXIST); +} + +static inline int f2fs_exist_data(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_DATA_EXIST); +} + +static inline int f2fs_has_inline_dots(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DOTS); +} + static inline bool f2fs_is_atomic_file(struct inode *inode) { return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE); @@ -1158,12 +1530,48 @@ static inline bool f2fs_is_volatile_file(struct inode *inode) return is_inode_flag_set(F2FS_I(inode), FI_VOLATILE_FILE); } +static inline bool f2fs_is_first_block_written(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); +} + +static inline bool f2fs_is_drop_cache(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_DROP_CACHE); +} + static inline void *inline_data_addr(struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); return (void *)&(ri->i_addr[1]); } +static inline int f2fs_has_inline_dentry(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DENTRY); +} + +static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page) +{ + if (!f2fs_has_inline_dentry(dir)) + kunmap(page); +} + +static inline int is_file(struct inode *inode, int type) +{ + return F2FS_I(inode)->i_advise & type; +} + +static inline void set_file(struct inode *inode, int type) +{ + F2FS_I(inode)->i_advise |= type; +} + +static inline void clear_file(struct inode *inode, int type) +{ + F2FS_I(inode)->i_advise &= ~type; +} + static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; @@ -1180,6 +1588,48 @@ static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi) sbi->sb->s_flags |= MS_RDONLY; } +static inline bool is_dot_dotdot(const struct qstr *str) +{ + if (str->len == 1 && str->name[0] == '.') + return true; + + if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') + return true; + + return false; +} + +static inline bool f2fs_may_extent_tree(struct inode *inode) +{ + mode_t mode = inode->i_mode; + + if (!test_opt(F2FS_I_SB(inode), EXTENT_CACHE) || + is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) + return false; + + return S_ISREG(mode); +} + +static inline void *f2fs_kvmalloc(size_t size, gfp_t flags) +{ + void *ret; + + ret = kmalloc(size, flags | __GFP_NOWARN); + if (!ret) + ret = __vmalloc(size, flags, PAGE_KERNEL); + return ret; +} + +static inline void *f2fs_kvzalloc(size_t size, gfp_t flags) +{ + void *ret; + + ret = kzalloc(size, flags | __GFP_NOWARN); + if (!ret) + ret = __vmalloc(size, flags | __GFP_ZERO, PAGE_KERNEL); + return ret; +} + #define get_inode_mode(i) \ ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) @@ -1196,7 +1646,7 @@ static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi) int f2fs_sync_file(struct file *, loff_t, loff_t, int); void truncate_data_blocks(struct dnode_of_data *); int truncate_blocks(struct inode *, u64, bool); -void f2fs_truncate(struct inode *); +int f2fs_truncate(struct inode *, bool); int f2fs_getattr(struct vfsmount *, struct dentry *, struct kstat *); int f2fs_setattr(struct dentry *, struct iattr *); int truncate_hole(struct inode *, pgoff_t, pgoff_t); @@ -1224,28 +1674,45 @@ struct dentry *f2fs_get_parent(struct dentry *child); /* * dir.c */ +extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; +void set_de_type(struct f2fs_dir_entry *, umode_t); +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *, + f2fs_hash_t, int *, struct f2fs_dentry_ptr *); +bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, + unsigned int, struct f2fs_str *); +void do_make_empty_dir(struct inode *, struct inode *, + struct f2fs_dentry_ptr *); +struct page *init_inode_metadata(struct inode *, struct inode *, + const struct qstr *, struct page *); +void update_parent_metadata(struct inode *, struct inode *, unsigned int); +int room_for_filename(const void *, int, int); +void f2fs_drop_nlink(struct inode *, struct inode *, struct page *); struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, struct page **); struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); ino_t f2fs_inode_by_name(struct inode *, struct qstr *); void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); -int update_dent_inode(struct inode *, const struct qstr *); -int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *); -void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *); +int update_dent_inode(struct inode *, struct inode *, const struct qstr *); +void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, + const struct qstr *, f2fs_hash_t , unsigned int); +int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, + umode_t); +void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, + struct inode *); int f2fs_do_tmpfile(struct inode *, struct inode *); -int f2fs_make_empty(struct inode *, struct inode *); bool f2fs_empty_dir(struct inode *); static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) { return __f2fs_add_link(dentry->d_parent->d_inode, &dentry->d_name, - inode); + inode, inode->i_ino, inode->i_mode); } /* * super.c */ +int f2fs_commit_super(struct f2fs_sb_info *, bool); int f2fs_sync_fs(struct super_block *, int); extern __printf(3, 4) void f2fs_msg(struct super_block *, const char *, const char *, ...); @@ -1262,15 +1729,15 @@ struct dnode_of_data; struct node_info; bool available_free_memory(struct f2fs_sb_info *, int); +int need_dentry_mark(struct f2fs_sb_info *, nid_t); bool is_checkpointed_node(struct f2fs_sb_info *, nid_t); -bool has_fsynced_inode(struct f2fs_sb_info *, nid_t); bool need_inode_block_update(struct f2fs_sb_info *, nid_t); void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); int truncate_inode_blocks(struct inode *, pgoff_t); int truncate_xattr_node(struct inode *, struct page *); int wait_on_node_pages_writeback(struct f2fs_sb_info *, nid_t); -void remove_inode_page(struct inode *); +int remove_inode_page(struct inode *); struct page *new_inode_page(struct inode *); struct page *new_node_page(struct dnode_of_data *, unsigned int, struct page *); void ra_node_page(struct f2fs_sb_info *, nid_t); @@ -1281,6 +1748,7 @@ int sync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *); bool alloc_nid(struct f2fs_sb_info *, nid_t *); void alloc_nid_done(struct f2fs_sb_info *, nid_t); void alloc_nid_failed(struct f2fs_sb_info *, nid_t); +int try_to_free_nids(struct f2fs_sb_info *, int); void recover_inline_xattr(struct inode *, struct page *); void recover_xattr_data(struct inode *, struct page *, block_t); int recover_inode_page(struct f2fs_sb_info *, struct page *); @@ -1296,32 +1764,33 @@ void destroy_node_manager_caches(void); * segment.c */ void register_inmem_page(struct inode *, struct page *); -void commit_inmem_pages(struct inode *, bool); +int commit_inmem_pages(struct inode *, bool); void f2fs_balance_fs(struct f2fs_sb_info *); void f2fs_balance_fs_bg(struct f2fs_sb_info *); int f2fs_issue_flush(struct f2fs_sb_info *); int create_flush_cmd_control(struct f2fs_sb_info *); void destroy_flush_cmd_control(struct f2fs_sb_info *); void invalidate_blocks(struct f2fs_sb_info *, block_t); +bool is_checkpointed_data(struct f2fs_sb_info *, block_t); void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); -void clear_prefree_segments(struct f2fs_sb_info *); +void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *); void release_discard_addrs(struct f2fs_sb_info *); -void discard_next_dnode(struct f2fs_sb_info *, block_t); -int npages_for_summary_flush(struct f2fs_sb_info *); +bool discard_next_dnode(struct f2fs_sb_info *, block_t); +int npages_for_summary_flush(struct f2fs_sb_info *, bool); void allocate_new_segments(struct f2fs_sb_info *); int f2fs_trim_fs(struct f2fs_sb_info *, struct fstrim_range *); struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); +void update_meta_page(struct f2fs_sb_info *, void *, block_t); void write_meta_page(struct f2fs_sb_info *, struct page *); -void write_node_page(struct f2fs_sb_info *, struct page *, - struct f2fs_io_info *, unsigned int, block_t, block_t *); -void write_data_page(struct page *, struct dnode_of_data *, block_t *, - struct f2fs_io_info *); -void rewrite_data_page(struct page *, block_t, struct f2fs_io_info *); -void recover_data_page(struct f2fs_sb_info *, struct page *, - struct f2fs_summary *, block_t, block_t); +void write_node_page(unsigned int, struct f2fs_io_info *); +void write_data_page(struct dnode_of_data *, struct f2fs_io_info *); +void rewrite_data_page(struct f2fs_io_info *); +void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *, + block_t, block_t, unsigned char, bool); void allocate_data_block(struct f2fs_sb_info *, struct page *, block_t, block_t *, struct f2fs_summary *, int); void f2fs_wait_on_page_writeback(struct page *, enum page_type); +void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t); void write_data_summaries(struct f2fs_sb_info *, block_t); void write_node_summaries(struct f2fs_sb_info *, block_t); int lookup_journal_in_cursum(struct f2fs_summary_block *, @@ -1337,8 +1806,10 @@ void destroy_segment_manager_caches(void); */ struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); -struct page *get_meta_page_ra(struct f2fs_sb_info *, pgoff_t); -int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int); +struct page *get_tmp_page(struct f2fs_sb_info *, pgoff_t); +bool is_valid_blkaddr(struct f2fs_sb_info *, block_t, int); +int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int, bool); +void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t); long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); void add_dirty_inode(struct f2fs_sb_info *, nid_t, int type); void remove_dirty_inode(struct f2fs_sb_info *, nid_t, int type); @@ -1348,7 +1819,7 @@ int acquire_orphan_inode(struct f2fs_sb_info *); void release_orphan_inode(struct f2fs_sb_info *); void add_orphan_inode(struct f2fs_sb_info *, nid_t); void remove_orphan_inode(struct f2fs_sb_info *, nid_t); -void recover_orphan_inodes(struct f2fs_sb_info *); +int recover_orphan_inodes(struct f2fs_sb_info *); int get_valid_checkpoint(struct f2fs_sb_info *); void update_dirty_page(struct inode *, struct page *); void add_dirty_dir_inode(struct inode *); @@ -1363,17 +1834,20 @@ void destroy_checkpoint_caches(void); * data.c */ void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); -int f2fs_submit_page_bio(struct f2fs_sb_info *, struct page *, block_t, int); -void f2fs_submit_page_mbio(struct f2fs_sb_info *, struct page *, block_t, - struct f2fs_io_info *); +int f2fs_submit_page_bio(struct f2fs_io_info *); +void f2fs_submit_page_mbio(struct f2fs_io_info *); +void set_data_blkaddr(struct dnode_of_data *); int reserve_new_block(struct dnode_of_data *); +int f2fs_get_block(struct dnode_of_data *, pgoff_t); int f2fs_reserve_block(struct dnode_of_data *, pgoff_t); -void update_extent_cache(block_t, struct dnode_of_data *); -struct page *find_data_page(struct inode *, pgoff_t, bool); -struct page *get_lock_data_page(struct inode *, pgoff_t); +struct page *get_read_data_page(struct inode *, pgoff_t, int, bool); +struct page *find_data_page(struct inode *, pgoff_t); +struct page *get_lock_data_page(struct inode *, pgoff_t, bool); struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool); -int do_write_data_page(struct page *, struct f2fs_io_info *); +int do_write_data_page(struct f2fs_io_info *); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); +void f2fs_invalidate_page(struct page *, unsigned int, unsigned int); +int f2fs_release_page(struct page *, gfp_t); /* * gc.c @@ -1381,10 +1855,8 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); int start_gc_thread(struct f2fs_sb_info *); void stop_gc_thread(struct f2fs_sb_info *); block_t start_bidx_of_node(unsigned int, struct f2fs_inode_info *); -int f2fs_gc(struct f2fs_sb_info *); +int f2fs_gc(struct f2fs_sb_info *, bool); void build_gc_manager(struct f2fs_sb_info *); -int __init create_gc_caches(void); -void destroy_gc_caches(void); /* * recovery.c @@ -1401,11 +1873,14 @@ struct f2fs_stat_info { struct f2fs_sb_info *sbi; int all_area_segs, sit_area_segs, nat_area_segs, ssa_area_segs; int main_area_segs, main_area_sections, main_area_zones; - int hit_ext, total_ext; + unsigned long long hit_largest, hit_cached, hit_rbtree; + unsigned long long hit_total, total_ext; + int ext_tree, ext_node; int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta; - int nats, sits, fnids; + int nats, dirty_nats, sits, dirty_sits, fnids; int total_count, utilization; - int bg_gc, inline_inode; + int bg_gc, inmem_pages, wb_pages; + int inline_xattr, inline_inode, inline_dir; unsigned int valid_count, valid_node_count, valid_inode_count; unsigned int bimodal, avg_vblocks; int util_free, util_valid, util_invalid; @@ -1413,14 +1888,17 @@ struct f2fs_stat_info { int dirty_count, node_pages, meta_pages; int prefree_count, call_count, cp_count; int tot_segs, node_segs, data_segs, free_segs, free_secs; + int bg_node_segs, bg_data_segs; int tot_blks, data_blks, node_blks; + int bg_data_blks, bg_node_blks; int curseg[NR_CURSEG_TYPE]; int cursec[NR_CURSEG_TYPE]; int curzone[NR_CURSEG_TYPE]; unsigned int segment_count[2]; unsigned int block_count[2]; - unsigned base_mem, cache_mem; + unsigned int inplace_count; + unsigned long long base_mem, cache_mem, page_mem; }; static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) @@ -1433,49 +1911,76 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) #define stat_inc_bggc_count(sbi) ((sbi)->bg_gc++) #define stat_inc_dirty_dir(sbi) ((sbi)->n_dirty_dirs++) #define stat_dec_dirty_dir(sbi) ((sbi)->n_dirty_dirs--) -#define stat_inc_total_hit(sb) ((F2FS_SB(sb))->total_hit_ext++) -#define stat_inc_read_hit(sb) ((F2FS_SB(sb))->read_hit_ext++) +#define stat_inc_total_hit(sbi) (atomic64_inc(&(sbi)->total_hit_ext)) +#define stat_inc_rbtree_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_rbtree)) +#define stat_inc_largest_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_largest)) +#define stat_inc_cached_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_cached)) +#define stat_inc_inline_xattr(inode) \ + do { \ + if (f2fs_has_inline_xattr(inode)) \ + (atomic_inc(&F2FS_I_SB(inode)->inline_xattr)); \ + } while (0) +#define stat_dec_inline_xattr(inode) \ + do { \ + if (f2fs_has_inline_xattr(inode)) \ + (atomic_dec(&F2FS_I_SB(inode)->inline_xattr)); \ + } while (0) #define stat_inc_inline_inode(inode) \ do { \ if (f2fs_has_inline_data(inode)) \ - ((F2FS_I_SB(inode))->inline_inode++); \ + (atomic_inc(&F2FS_I_SB(inode)->inline_inode)); \ } while (0) #define stat_dec_inline_inode(inode) \ do { \ if (f2fs_has_inline_data(inode)) \ - ((F2FS_I_SB(inode))->inline_inode--); \ + (atomic_dec(&F2FS_I_SB(inode)->inline_inode)); \ + } while (0) +#define stat_inc_inline_dir(inode) \ + do { \ + if (f2fs_has_inline_dentry(inode)) \ + (atomic_inc(&F2FS_I_SB(inode)->inline_dir)); \ + } while (0) +#define stat_dec_inline_dir(inode) \ + do { \ + if (f2fs_has_inline_dentry(inode)) \ + (atomic_dec(&F2FS_I_SB(inode)->inline_dir)); \ } while (0) - #define stat_inc_seg_type(sbi, curseg) \ ((sbi)->segment_count[(curseg)->alloc_type]++) #define stat_inc_block_count(sbi, curseg) \ ((sbi)->block_count[(curseg)->alloc_type]++) - -#define stat_inc_seg_count(sbi, type) \ +#define stat_inc_inplace_blocks(sbi) \ + (atomic_inc(&(sbi)->inplace_count)) +#define stat_inc_seg_count(sbi, type, gc_type) \ do { \ struct f2fs_stat_info *si = F2FS_STAT(sbi); \ (si)->tot_segs++; \ - if (type == SUM_TYPE_DATA) \ + if (type == SUM_TYPE_DATA) { \ si->data_segs++; \ - else \ + si->bg_data_segs += (gc_type == BG_GC) ? 1 : 0; \ + } else { \ si->node_segs++; \ + si->bg_node_segs += (gc_type == BG_GC) ? 1 : 0; \ + } \ } while (0) #define stat_inc_tot_blk_count(si, blks) \ (si->tot_blks += (blks)) -#define stat_inc_data_blk_count(sbi, blks) \ +#define stat_inc_data_blk_count(sbi, blks, gc_type) \ do { \ struct f2fs_stat_info *si = F2FS_STAT(sbi); \ stat_inc_tot_blk_count(si, blks); \ si->data_blks += (blks); \ + si->bg_data_blks += (gc_type == BG_GC) ? (blks) : 0; \ } while (0) -#define stat_inc_node_blk_count(sbi, blks) \ +#define stat_inc_node_blk_count(sbi, blks, gc_type) \ do { \ struct f2fs_stat_info *si = F2FS_STAT(sbi); \ stat_inc_tot_blk_count(si, blks); \ si->node_blks += (blks); \ + si->bg_node_blks += (gc_type == BG_GC) ? (blks) : 0; \ } while (0) int f2fs_build_stats(struct f2fs_sb_info *); @@ -1489,15 +1994,22 @@ void f2fs_destroy_root_stats(void); #define stat_inc_dirty_dir(sbi) #define stat_dec_dirty_dir(sbi) #define stat_inc_total_hit(sb) -#define stat_inc_read_hit(sb) +#define stat_inc_rbtree_node_hit(sb) +#define stat_inc_largest_node_hit(sbi) +#define stat_inc_cached_node_hit(sbi) +#define stat_inc_inline_xattr(inode) +#define stat_dec_inline_xattr(inode) #define stat_inc_inline_inode(inode) #define stat_dec_inline_inode(inode) +#define stat_inc_inline_dir(inode) +#define stat_dec_inline_dir(inode) #define stat_inc_seg_type(sbi, curseg) #define stat_inc_block_count(sbi, curseg) -#define stat_inc_seg_count(si, type) +#define stat_inc_inplace_blocks(sbi) +#define stat_inc_seg_count(sbi, type, gc_type) #define stat_inc_tot_blk_count(si, blks) -#define stat_inc_data_blk_count(si, blks) -#define stat_inc_node_blk_count(sbi, blks) +#define stat_inc_data_blk_count(sbi, blks, gc_type) +#define stat_inc_node_blk_count(sbi, blks, gc_type) static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } @@ -1513,15 +2025,188 @@ extern const struct address_space_operations f2fs_node_aops; extern const struct address_space_operations f2fs_meta_aops; extern const struct inode_operations f2fs_dir_inode_operations; extern const struct inode_operations f2fs_symlink_inode_operations; +extern const struct inode_operations f2fs_encrypted_symlink_inode_operations; extern const struct inode_operations f2fs_special_inode_operations; +extern struct kmem_cache *inode_entry_slab; /* * inline.c */ -bool f2fs_may_inline(struct inode *); +bool f2fs_may_inline_data(struct inode *); +bool f2fs_may_inline_dentry(struct inode *); +void read_inline_data(struct page *, struct page *); +bool truncate_inline_inode(struct page *, u64); int f2fs_read_inline_data(struct inode *, struct page *); -int f2fs_convert_inline_data(struct inode *, pgoff_t, struct page *); -int f2fs_write_inline_data(struct inode *, struct page *, unsigned int); -void truncate_inline_data(struct inode *, u64); +int f2fs_convert_inline_page(struct dnode_of_data *, struct page *); +int f2fs_convert_inline_inode(struct inode *); +int f2fs_write_inline_data(struct inode *, struct page *); bool recover_inline_data(struct inode *, struct page *); +struct f2fs_dir_entry *find_in_inline_dir(struct inode *, + struct f2fs_filename *, struct page **); +struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **); +int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); +int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, + nid_t, umode_t); +void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *, + struct inode *, struct inode *); +bool f2fs_empty_inline_dir(struct inode *); +int f2fs_read_inline_dir(struct file *, struct dir_context *, + struct f2fs_str *); +int f2fs_inline_data_fiemap(struct inode *, + struct fiemap_extent_info *, __u64, __u64); + +/* + * shrinker.c + */ +unsigned long f2fs_shrink_count(struct shrinker *, struct shrink_control *); +unsigned long f2fs_shrink_scan(struct shrinker *, struct shrink_control *); +void f2fs_join_shrinker(struct f2fs_sb_info *); +void f2fs_leave_shrinker(struct f2fs_sb_info *); + +/* + * extent_cache.c + */ +unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int); +void f2fs_drop_largest_extent(struct inode *, pgoff_t); +void f2fs_init_extent_tree(struct inode *, struct f2fs_extent *); +unsigned int f2fs_destroy_extent_node(struct inode *); +void f2fs_destroy_extent_tree(struct inode *); +bool f2fs_lookup_extent_cache(struct inode *, pgoff_t, struct extent_info *); +void f2fs_update_extent_cache(struct dnode_of_data *); +void f2fs_update_extent_cache_range(struct dnode_of_data *dn, + pgoff_t, block_t, unsigned int); +void init_extent_cache_info(struct f2fs_sb_info *); +int __init create_extent_cache(void); +void destroy_extent_cache(void); + +/* + * crypto support + */ +static inline int f2fs_encrypted_inode(struct inode *inode) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + return file_is_encrypt(inode); +#else + return 0; +#endif +} + +static inline void f2fs_set_encrypted_inode(struct inode *inode) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + file_set_encrypt(inode); +#endif +} + +static inline bool f2fs_bio_encrypted(struct bio *bio) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + return unlikely(bio->bi_private != NULL); +#else + return false; +#endif +} + +static inline int f2fs_sb_has_crypto(struct super_block *sb) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); +#else + return 0; +#endif +} + +static inline bool f2fs_may_encrypt(struct inode *inode) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + mode_t mode = inode->i_mode; + + return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)); +#else + return 0; +#endif +} + +/* crypto_policy.c */ +int f2fs_is_child_context_consistent_with_parent(struct inode *, + struct inode *); +int f2fs_inherit_context(struct inode *, struct inode *, struct page *); +int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *); +int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *); + +/* crypt.c */ +extern struct kmem_cache *f2fs_crypt_info_cachep; +bool f2fs_valid_contents_enc_mode(uint32_t); +uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t); +struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *); +void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *); +struct page *f2fs_encrypt(struct inode *, struct page *); +int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *); +int f2fs_decrypt_one(struct inode *, struct page *); +void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *); + +/* crypto_key.c */ +void f2fs_free_encryption_info(struct inode *, struct f2fs_crypt_info *); +int _f2fs_get_encryption_info(struct inode *inode); + +/* crypto_fname.c */ +bool f2fs_valid_filenames_enc_mode(uint32_t); +u32 f2fs_fname_crypto_round_up(u32, u32); +int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *); +int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *, + const struct f2fs_str *, struct f2fs_str *); +int f2fs_fname_usr_to_disk(struct inode *, const struct qstr *, + struct f2fs_str *); + +#ifdef CONFIG_F2FS_FS_ENCRYPTION +void f2fs_restore_and_release_control_page(struct page **); +void f2fs_restore_control_page(struct page *); + +int __init f2fs_init_crypto(void); +int f2fs_crypto_initialize(void); +void f2fs_exit_crypto(void); + +int f2fs_has_encryption_key(struct inode *); + +static inline int f2fs_get_encryption_info(struct inode *inode) +{ + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (!ci || + (ci->ci_keyring_key && + (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD))))) + return _f2fs_get_encryption_info(inode); + return 0; +} + +void f2fs_fname_crypto_free_buffer(struct f2fs_str *); +int f2fs_fname_setup_filename(struct inode *, const struct qstr *, + int lookup, struct f2fs_filename *); +void f2fs_fname_free_filename(struct f2fs_filename *); +#else +static inline void f2fs_restore_and_release_control_page(struct page **p) { } +static inline void f2fs_restore_control_page(struct page *p) { } + +static inline int __init f2fs_init_crypto(void) { return 0; } +static inline void f2fs_exit_crypto(void) { } + +static inline int f2fs_has_encryption_key(struct inode *i) { return 0; } +static inline int f2fs_get_encryption_info(struct inode *i) { return 0; } +static inline void f2fs_fname_crypto_free_buffer(struct f2fs_str *p) { } + +static inline int f2fs_fname_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct f2fs_filename *fname) +{ + memset(fname, 0, sizeof(struct f2fs_filename)); + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; +} + +static inline void f2fs_fname_free_filename(struct f2fs_filename *fname) { } +#endif #endif diff --git a/fs/f2fs/f2fs_crypto.h b/fs/f2fs/f2fs_crypto.h new file mode 100644 index 000000000000..c2c1c2b63b25 --- /dev/null +++ b/fs/f2fs/f2fs_crypto.h @@ -0,0 +1,151 @@ +/* + * linux/fs/f2fs/f2fs_crypto.h + * + * Copied from linux/fs/ext4/ext4_crypto.h + * + * Copyright (C) 2015, Google, Inc. + * + * This contains encryption header content for f2fs + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ +#ifndef _F2FS_CRYPTO_H +#define _F2FS_CRYPTO_H + +#include + +#define F2FS_KEY_DESCRIPTOR_SIZE 8 + +/* Policy provided via an ioctl on the topmost directory */ +struct f2fs_encryption_policy { + char version; + char contents_encryption_mode; + char filenames_encryption_mode; + char flags; + char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE]; +} __attribute__((__packed__)); + +#define F2FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 +#define F2FS_KEY_DERIVATION_NONCE_SIZE 16 + +#define F2FS_POLICY_FLAGS_PAD_4 0x00 +#define F2FS_POLICY_FLAGS_PAD_8 0x01 +#define F2FS_POLICY_FLAGS_PAD_16 0x02 +#define F2FS_POLICY_FLAGS_PAD_32 0x03 +#define F2FS_POLICY_FLAGS_PAD_MASK 0x03 +#define F2FS_POLICY_FLAGS_VALID 0x03 + +/** + * Encryption context for inode + * + * Protector format: + * 1 byte: Protector format (1 = this version) + * 1 byte: File contents encryption mode + * 1 byte: File names encryption mode + * 1 byte: Flags + * 8 bytes: Master Key descriptor + * 16 bytes: Encryption Key derivation nonce + */ +struct f2fs_encryption_context { + char format; + char contents_encryption_mode; + char filenames_encryption_mode; + char flags; + char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE]; + char nonce[F2FS_KEY_DERIVATION_NONCE_SIZE]; +} __attribute__((__packed__)); + +/* Encryption parameters */ +#define F2FS_XTS_TWEAK_SIZE 16 +#define F2FS_AES_128_ECB_KEY_SIZE 16 +#define F2FS_AES_256_GCM_KEY_SIZE 32 +#define F2FS_AES_256_CBC_KEY_SIZE 32 +#define F2FS_AES_256_CTS_KEY_SIZE 32 +#define F2FS_AES_256_XTS_KEY_SIZE 64 +#define F2FS_MAX_KEY_SIZE 64 + +#define F2FS_KEY_DESC_PREFIX "f2fs:" +#define F2FS_KEY_DESC_PREFIX_SIZE 5 + +struct f2fs_encryption_key { + __u32 mode; + char raw[F2FS_MAX_KEY_SIZE]; + __u32 size; +} __attribute__((__packed__)); + +struct f2fs_crypt_info { + char ci_data_mode; + char ci_filename_mode; + char ci_flags; + struct crypto_ablkcipher *ci_ctfm; + struct key *ci_keyring_key; + char ci_master_key[F2FS_KEY_DESCRIPTOR_SIZE]; +}; + +#define F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 +#define F2FS_WRITE_PATH_FL 0x00000002 + +struct f2fs_crypto_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + char flags; /* Flags */ +}; + +struct f2fs_completion_result { + struct completion completion; + int res; +}; + +#define DECLARE_F2FS_COMPLETION_RESULT(ecr) \ + struct f2fs_completion_result ecr = { \ + COMPLETION_INITIALIZER((ecr).completion), 0 } + +static inline int f2fs_encryption_key_size(int mode) +{ + switch (mode) { + case F2FS_ENCRYPTION_MODE_AES_256_XTS: + return F2FS_AES_256_XTS_KEY_SIZE; + case F2FS_ENCRYPTION_MODE_AES_256_GCM: + return F2FS_AES_256_GCM_KEY_SIZE; + case F2FS_ENCRYPTION_MODE_AES_256_CBC: + return F2FS_AES_256_CBC_KEY_SIZE; + case F2FS_ENCRYPTION_MODE_AES_256_CTS: + return F2FS_AES_256_CTS_KEY_SIZE; + default: + BUG(); + } + return 0; +} + +#define F2FS_FNAME_NUM_SCATTER_ENTRIES 4 +#define F2FS_CRYPTO_BLOCK_SIZE 16 +#define F2FS_FNAME_CRYPTO_DIGEST_SIZE 32 + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct f2fs_encrypted_symlink_data { + __le16 len; + char encrypted_path[1]; +} __attribute__((__packed__)); + +/** + * This function is used to calculate the disk space required to + * store a filename of length l in encrypted symlink format. + */ +static inline u32 encrypted_symlink_data_len(u32 l) +{ + return (l + sizeof(struct f2fs_encrypted_symlink_data) - 1); +} +#endif /* _F2FS_CRYPTO_H */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 8e68bb64f835..a441953caaf5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -20,12 +20,16 @@ #include #include #include +#include +#include #include "f2fs.h" #include "node.h" #include "segment.h" #include "xattr.h" #include "acl.h" +#include "gc.h" +#include "trace.h" #include static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, @@ -41,18 +45,18 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, sb_start_pagefault(inode->i_sb); - /* force to convert with normal data indices */ - err = f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, page); - if (err) - goto out; + f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); /* block allocation */ f2fs_lock_op(sbi); set_new_dnode(&dn, inode, NULL, NULL, 0); err = f2fs_reserve_block(&dn, page->index); - f2fs_unlock_op(sbi); - if (err) + if (err) { + f2fs_unlock_op(sbi); goto out; + } + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); file_update_time(vma->vm_file); lock_page(page); @@ -71,7 +75,8 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, goto mapped; /* page is wholly or partially inside EOF */ - if (((page->index + 1) << PAGE_CACHE_SHIFT) > i_size_read(inode)) { + if (((loff_t)(page->index + 1) << PAGE_CACHE_SHIFT) > + i_size_read(inode)) { unsigned offset; offset = i_size_read(inode) & ~PAGE_CACHE_MASK; zero_user_segment(page, offset, PAGE_CACHE_SIZE); @@ -83,6 +88,13 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, mapped: /* fill the page */ f2fs_wait_on_page_writeback(page, DATA); + + /* wait for GCed encrypted page writeback */ + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); + + /* if gced page is attached, don't write to cold segment */ + clear_cold_data(page); out: sb_end_pagefault(inode->i_sb); return block_page_mkwrite_return(err); @@ -92,7 +104,6 @@ static const struct vm_operations_struct f2fs_file_vm_ops = { .fault = filemap_fault, .map_pages = filemap_map_pages, .page_mkwrite = f2fs_vm_page_mkwrite, - .remap_pages = generic_file_remap_pages, }; static int get_parent_ino(struct inode *inode, nid_t *pino) @@ -105,7 +116,7 @@ static int get_parent_ino(struct inode *inode, nid_t *pino) if (!dentry) return 0; - if (update_dent_inode(inode, &dentry->d_name)) { + if (update_dent_inode(inode, inode, &dentry->d_name)) { dput(dentry); return 0; } @@ -122,6 +133,8 @@ static inline bool need_do_checkpoint(struct inode *inode) if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) need_cp = true; + else if (file_enc_name(inode) && need_dentry_mark(sbi, inode->i_ino)) + need_cp = true; else if (file_wrong_pino(inode)) need_cp = true; else if (!space_for_roll_forward(sbi)) @@ -130,10 +143,45 @@ static inline bool need_do_checkpoint(struct inode *inode) need_cp = true; else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi))) need_cp = true; + else if (test_opt(sbi, FASTBOOT)) + need_cp = true; + else if (sbi->active_logs == 2) + need_cp = true; return need_cp; } +static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino) +{ + struct page *i = find_get_page(NODE_MAPPING(sbi), ino); + bool ret = false; + /* But we need to avoid that there are some inode updates */ + if ((i && PageDirty(i)) || need_inode_block_update(sbi, ino)) + ret = true; + f2fs_put_page(i, 0); + return ret; +} + +static void try_to_fix_pino(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + nid_t pino; + + down_write(&fi->i_sem); + fi->xattr_ver = 0; + if (file_wrong_pino(inode) && inode->i_nlink == 1 && + get_parent_ino(inode, &pino)) { + fi->i_pino = pino; + file_got_pino(inode); + up_write(&fi->i_sem); + + mark_inode_dirty_sync(inode); + f2fs_write_inode(inode, NULL); + } else { + up_write(&fi->i_sem); + } +} + int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) { struct inode *inode = file->f_mapping->host; @@ -164,19 +212,21 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) return ret; } + /* if the inode is dirty, let's recover all the time */ + if (!datasync) { + f2fs_write_inode(inode, NULL); + 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)) { - struct page *i = find_get_page(NODE_MAPPING(sbi), ino); - /* But we need to avoid that there are some inode updates */ - if ((i && PageDirty(i)) || need_inode_block_update(sbi, ino)) { - f2fs_put_page(i, 0); + /* it may call write_inode just prior to fsync */ + if (need_inode_page_update(sbi, ino)) goto go_write; - } - f2fs_put_page(i, 0); if (is_inode_flag_set(fi, FI_UPDATE_WRITE) || exist_written_data(sbi, ino, UPDATE_INO)) @@ -196,51 +246,45 @@ go_write: up_read(&fi->i_sem); if (need_cp) { - nid_t pino; - /* all the dirty node pages should be flushed for POR */ ret = f2fs_sync_fs(inode->i_sb, 1); - down_write(&fi->i_sem); - F2FS_I(inode)->xattr_ver = 0; - if (file_wrong_pino(inode) && inode->i_nlink == 1 && - get_parent_ino(inode, &pino)) { - F2FS_I(inode)->i_pino = pino; - file_got_pino(inode); - up_write(&fi->i_sem); - mark_inode_dirty_sync(inode); - ret = f2fs_write_inode(inode, NULL); - if (ret) - goto out; - } else { - up_write(&fi->i_sem); - } - } else { + /* + * 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); + sync_node_pages(sbi, ino, &wbc); - if (need_inode_block_update(sbi, ino)) { - mark_inode_dirty_sync(inode); - ret = f2fs_write_inode(inode, NULL); - if (ret) - goto out; - goto sync_nodes; - } + /* if cp_error was enabled, we should avoid infinite loop */ + if (unlikely(f2fs_cp_error(sbi))) + goto out; - ret = wait_on_node_pages_writeback(sbi, ino); - if (ret) - goto out; + if (need_inode_block_update(sbi, ino)) { + mark_inode_dirty_sync(inode); + f2fs_write_inode(inode, NULL); + goto sync_nodes; + } - /* 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); + 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(F2FS_I_SB(inode)); - } + 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; } @@ -296,7 +340,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) goto fail; /* handle inline data case */ - if (f2fs_has_inline_data(inode)) { + if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) { if (whence == SEEK_HOLE) data_ofs = isize; goto found; @@ -306,7 +350,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) dirty = __get_first_dirty_index(inode->i_mapping, pgofs, whence); - for (; data_ofs < isize; data_ofs = pgofs << PAGE_CACHE_SHIFT) { + for (; data_ofs < isize; data_ofs = (loff_t)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) { @@ -327,7 +371,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) /* find data/hole in dnode block */ for (; dn.ofs_in_node < end_offset; dn.ofs_in_node++, pgofs++, - data_ofs = pgofs << PAGE_CACHE_SHIFT) { + data_ofs = (loff_t)pgofs << PAGE_CACHE_SHIFT) { block_t blkaddr; blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); @@ -374,16 +418,43 @@ static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence) 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; +} + int truncate_data_blocks_range(struct dnode_of_data *dn, int count) { - int nr_free = 0, ofs = dn->ofs_in_node; struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_node *raw_node; + int nr_free = 0, ofs = dn->ofs_in_node, len = count; __le32 *addr; raw_node = F2FS_NODE(dn->node_page); @@ -394,11 +465,24 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) if (blkaddr == NULL_ADDR) continue; - update_extent_cache(NULL_ADDR, dn); + dn->data_blkaddr = NULL_ADDR; + set_data_blkaddr(dn); invalidate_blocks(sbi, blkaddr); + if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page)) + clear_inode_flag(F2FS_I(dn->inode), + FI_FIRST_BLOCK_WRITTEN); nr_free++; } + if (nr_free) { + pgoff_t fofs; + /* + * once we invalidate valid blkaddr in range [ofs, ofs + count], + * we will invalidate all blkaddr in the whole range. + */ + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), + F2FS_I(dn->inode)) + ofs; + f2fs_update_extent_cache_range(dn, fofs, 0, len); dec_valid_block_count(sbi, dn->inode, nr_free); set_page_dirty(dn->node_page); sync_inode_page(dn); @@ -415,32 +499,35 @@ void truncate_data_blocks(struct dnode_of_data *dn) truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); } -static void truncate_partial_data_page(struct inode *inode, u64 from) +static int truncate_partial_data_page(struct inode *inode, u64 from, + bool cache_only) { unsigned offset = from & (PAGE_CACHE_SIZE - 1); + pgoff_t index = from >> PAGE_CACHE_SHIFT; + struct address_space *mapping = inode->i_mapping; struct page *page; - if (f2fs_has_inline_data(inode)) - return truncate_inline_data(inode, from); + if (!offset && !cache_only) + return 0; - if (!offset) - return; + if (cache_only) { + page = f2fs_grab_cache_page(mapping, index, false); + if (page && PageUptodate(page)) + goto truncate_out; + f2fs_put_page(page, 1); + return 0; + } - page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false); + page = get_lock_data_page(inode, index, true); if (IS_ERR(page)) - return; - - lock_page(page); - if (unlikely(!PageUptodate(page) || - page->mapping != inode->i_mapping)) - goto out; - + return 0; +truncate_out: f2fs_wait_on_page_writeback(page, DATA); zero_user(page, offset, PAGE_CACHE_SIZE - offset); - set_page_dirty(page); - -out: + if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) + set_page_dirty(page); f2fs_put_page(page, 1); + return 0; } int truncate_blocks(struct inode *inode, u64 from, bool lock) @@ -450,27 +537,36 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) struct dnode_of_data dn; pgoff_t free_from; int count = 0, err = 0; + struct page *ipage; + bool truncate_page = false; trace_f2fs_truncate_blocks_enter(inode, from); - if (f2fs_has_inline_data(inode)) - goto done; - - free_from = (pgoff_t) - ((from + blocksize - 1) >> (sbi->log_blocksize)); + free_from = (pgoff_t)F2FS_BYTES_TO_BLK(from + blocksize - 1); if (lock) f2fs_lock_op(sbi); - set_new_dnode(&dn, inode, NULL, NULL, 0); + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto out; + } + + if (f2fs_has_inline_data(inode)) { + if (truncate_inline_inode(ipage, from)) + set_page_dirty(ipage); + f2fs_put_page(ipage, 1); + truncate_page = true; + goto out; + } + + set_new_dnode(&dn, inode, ipage, NULL, 0); err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE); if (err) { if (err == -ENOENT) goto free_next; - if (lock) - f2fs_unlock_op(sbi); - trace_f2fs_truncate_blocks_exit(inode, err); - return err; + goto out; } count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); @@ -486,28 +582,42 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) f2fs_put_dnode(&dn); free_next: err = truncate_inode_blocks(inode, free_from); +out: if (lock) f2fs_unlock_op(sbi); -done: + /* lastly zero out the first data page */ - truncate_partial_data_page(inode, from); + if (!err) + err = truncate_partial_data_page(inode, from, truncate_page); trace_f2fs_truncate_blocks_exit(inode, err); return err; } -void f2fs_truncate(struct inode *inode) +int f2fs_truncate(struct inode *inode, bool lock) { + int err; + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) - return; + return 0; trace_f2fs_truncate(inode); - if (!truncate_blocks(inode, i_size_read(inode), true)) { - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); + /* we should check inline_data size */ + if (f2fs_has_inline_data(inode) && !f2fs_may_inline_data(inode)) { + err = f2fs_convert_inline_inode(inode); + if (err) + return err; } + + err = truncate_blocks(inode, i_size_read(inode), lock); + if (err) + return err; + + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + return 0; } int f2fs_getattr(struct vfsmount *mnt, @@ -561,20 +671,23 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) return err; if (attr->ia_valid & ATTR_SIZE) { - err = f2fs_convert_inline_data(inode, attr->ia_size, NULL); - if (err) - return err; + if (f2fs_encrypted_inode(inode) && + f2fs_get_encryption_info(inode)) + return -EACCES; - if (attr->ia_size != i_size_read(inode)) { + if (attr->ia_size <= i_size_read(inode)) { truncate_setsize(inode, attr->ia_size); - f2fs_truncate(inode); + err = f2fs_truncate(inode, true); + if (err) + return err; f2fs_balance_fs(F2FS_I_SB(inode)); } else { /* - * giving a chance to truncate blocks past EOF which - * are fallocated with FALLOC_FL_KEEP_SIZE. + * do not trim all blocks after i_size if target size is + * larger than i_size. */ - f2fs_truncate(inode); + truncate_setsize(inode, attr->ia_size); + inode->i_mtime = inode->i_ctime = CURRENT_TIME; } } @@ -606,14 +719,14 @@ const struct inode_operations f2fs_file_inode_operations = { .fiemap = f2fs_fiemap, }; -static void fill_zero(struct inode *inode, pgoff_t index, +static int fill_zero(struct inode *inode, pgoff_t index, loff_t start, loff_t len) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *page; if (!len) - return; + return 0; f2fs_balance_fs(sbi); @@ -621,33 +734,43 @@ static void fill_zero(struct inode *inode, pgoff_t index, page = get_new_data_page(inode, NULL, index, false); f2fs_unlock_op(sbi); - if (!IS_ERR(page)) { - f2fs_wait_on_page_writeback(page, DATA); - zero_user(page, start, len); - set_page_dirty(page); - f2fs_put_page(page, 1); - } + if (IS_ERR(page)) + return PTR_ERR(page); + + f2fs_wait_on_page_writeback(page, DATA); + zero_user(page, start, len); + set_page_dirty(page); + f2fs_put_page(page, 1); + return 0; } int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) { - pgoff_t index; int err; - for (index = pg_start; index < pg_end; index++) { + while (pg_start < pg_end) { struct dnode_of_data dn; + pgoff_t end_offset, count; set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, LOOKUP_NODE); + err = get_dnode_of_data(&dn, pg_start, LOOKUP_NODE); if (err) { - if (err == -ENOENT) + if (err == -ENOENT) { + pg_start++; continue; + } return err; } - if (dn.data_blkaddr != NULL_ADDR) - truncate_data_blocks_range(&dn, 1); + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + count = min(end_offset - dn.ofs_in_node, pg_end - pg_start); + + f2fs_bug_on(F2FS_I_SB(inode), count == 0 || count > end_offset); + + truncate_data_blocks_range(&dn, count); f2fs_put_dnode(&dn); + + pg_start += count; } return 0; } @@ -658,16 +781,11 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) loff_t off_start, off_end; int ret = 0; - if (!S_ISREG(inode->i_mode)) - return -EOPNOTSUPP; - - /* skip punching hole beyond i_size */ - if (offset >= inode->i_size) - return ret; - - ret = f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, NULL); - if (ret) - return ret; + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; @@ -676,14 +794,22 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); if (pg_start == pg_end) { - fill_zero(inode, pg_start, off_start, + ret = fill_zero(inode, pg_start, off_start, off_end - off_start); + if (ret) + return ret; } else { - if (off_start) - fill_zero(inode, pg_start++, off_start, - PAGE_CACHE_SIZE - off_start); - if (off_end) - fill_zero(inode, pg_end, 0, off_end); + if (off_start) { + ret = fill_zero(inode, pg_start++, off_start, + PAGE_CACHE_SIZE - off_start); + if (ret) + return ret; + } + if (off_end) { + ret = fill_zero(inode, pg_end, 0, off_end); + if (ret) + return ret; + } if (pg_start < pg_end) { struct address_space *mapping = inode->i_mapping; @@ -692,8 +818,8 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(sbi); - blk_start = pg_start << PAGE_CACHE_SHIFT; - blk_end = pg_end << PAGE_CACHE_SHIFT; + blk_start = (loff_t)pg_start << PAGE_CACHE_SHIFT; + blk_end = (loff_t)pg_end << PAGE_CACHE_SHIFT; truncate_inode_pages_range(mapping, blk_start, blk_end - 1); @@ -706,6 +832,320 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) return ret; } +static int __exchange_data_block(struct inode *inode, pgoff_t src, + pgoff_t dst, bool full) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + block_t new_addr; + bool do_replace = false; + int ret; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = get_dnode_of_data(&dn, src, LOOKUP_NODE_RA); + if (ret && ret != -ENOENT) { + return ret; + } else if (ret == -ENOENT) { + new_addr = NULL_ADDR; + } else { + new_addr = dn.data_blkaddr; + if (!is_checkpointed_data(sbi, new_addr)) { + dn.data_blkaddr = NULL_ADDR; + /* do not invalidate this block address */ + set_data_blkaddr(&dn); + f2fs_update_extent_cache(&dn); + do_replace = true; + } + f2fs_put_dnode(&dn); + } + + if (new_addr == NULL_ADDR) + return full ? truncate_hole(inode, dst, dst + 1) : 0; + + if (do_replace) { + struct page *ipage = get_node_page(sbi, inode->i_ino); + struct node_info ni; + + if (IS_ERR(ipage)) { + ret = PTR_ERR(ipage); + goto err_out; + } + + set_new_dnode(&dn, inode, ipage, NULL, 0); + ret = f2fs_reserve_block(&dn, dst); + if (ret) + goto err_out; + + truncate_data_blocks_range(&dn, 1); + + get_node_info(sbi, dn.nid, &ni); + f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr, + ni.version, true); + f2fs_put_dnode(&dn); + } else { + struct page *psrc, *pdst; + + psrc = get_lock_data_page(inode, src, true); + if (IS_ERR(psrc)) + return PTR_ERR(psrc); + pdst = get_new_data_page(inode, NULL, dst, false); + if (IS_ERR(pdst)) { + f2fs_put_page(psrc, 1); + return PTR_ERR(pdst); + } + f2fs_copy_page(psrc, pdst); + set_page_dirty(pdst); + f2fs_put_page(pdst, 1); + f2fs_put_page(psrc, 1); + + return truncate_hole(inode, src, src + 1); + } + return 0; + +err_out: + if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) { + dn.data_blkaddr = new_addr; + set_data_blkaddr(&dn); + f2fs_update_extent_cache(&dn); + f2fs_put_dnode(&dn); + } + return ret; +} + +static int f2fs_do_collapse(struct inode *inode, pgoff_t start, pgoff_t end) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + pgoff_t nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; + int ret = 0; + + for (; end < nrpages; start++, end++) { + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); + ret = __exchange_data_block(inode, end, start, true); + f2fs_unlock_op(sbi); + if (ret) + break; + } + return ret; +} + +static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) +{ + pgoff_t pg_start, pg_end; + loff_t new_size; + int ret; + + if (offset + len >= i_size_read(inode)) + return -EINVAL; + + /* collapse range should be aligned to block size of f2fs. */ + if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1)) + return -EINVAL; + + f2fs_balance_fs(F2FS_I_SB(inode)); + + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } + + pg_start = offset >> PAGE_CACHE_SHIFT; + pg_end = (offset + len) >> PAGE_CACHE_SHIFT; + + /* write out all dirty pages from offset */ + ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + if (ret) + return ret; + + truncate_pagecache(inode, offset); + + ret = f2fs_do_collapse(inode, pg_start, pg_end); + if (ret) + return ret; + + /* write out all moved pages, if possible */ + filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + truncate_pagecache(inode, offset); + + new_size = i_size_read(inode) - len; + truncate_pagecache(inode, new_size); + + ret = truncate_blocks(inode, new_size, true); + if (!ret) + i_size_write(inode, new_size); + + return ret; +} + +static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, + int mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct address_space *mapping = inode->i_mapping; + pgoff_t index, pg_start, pg_end; + loff_t new_size = i_size_read(inode); + loff_t off_start, off_end; + int ret = 0; + + ret = inode_newsize_ok(inode, (len + offset)); + if (ret) + return ret; + + f2fs_balance_fs(sbi); + + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } + + ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1); + if (ret) + return ret; + + truncate_pagecache_range(inode, offset, offset + len - 1); + + pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; + pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; + + off_start = offset & (PAGE_CACHE_SIZE - 1); + off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); + + if (pg_start == pg_end) { + ret = fill_zero(inode, pg_start, off_start, + off_end - off_start); + if (ret) + return ret; + + if (offset + len > new_size) + new_size = offset + len; + new_size = max_t(loff_t, new_size, offset + len); + } else { + if (off_start) { + ret = fill_zero(inode, pg_start++, off_start, + PAGE_CACHE_SIZE - off_start); + if (ret) + return ret; + + new_size = max_t(loff_t, new_size, + (loff_t)pg_start << PAGE_CACHE_SHIFT); + } + + for (index = pg_start; index < pg_end; index++) { + struct dnode_of_data dn; + struct page *ipage; + + f2fs_lock_op(sbi); + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + ret = PTR_ERR(ipage); + f2fs_unlock_op(sbi); + goto out; + } + + set_new_dnode(&dn, inode, ipage, NULL, 0); + ret = f2fs_reserve_block(&dn, index); + if (ret) { + f2fs_unlock_op(sbi); + goto out; + } + + if (dn.data_blkaddr != NEW_ADDR) { + invalidate_blocks(sbi, dn.data_blkaddr); + + dn.data_blkaddr = NEW_ADDR; + set_data_blkaddr(&dn); + + dn.data_blkaddr = NULL_ADDR; + f2fs_update_extent_cache(&dn); + } + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + + new_size = max_t(loff_t, new_size, + (loff_t)(index + 1) << PAGE_CACHE_SHIFT); + } + + if (off_end) { + ret = fill_zero(inode, pg_end, 0, off_end); + if (ret) + goto out; + + new_size = max_t(loff_t, new_size, offset + len); + } + } + +out: + if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) { + i_size_write(inode, new_size); + mark_inode_dirty(inode); + update_inode_page(inode); + } + + return ret; +} + +static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + pgoff_t pg_start, pg_end, delta, nrpages, idx; + loff_t new_size; + int ret = 0; + + new_size = i_size_read(inode) + len; + if (new_size > inode->i_sb->s_maxbytes) + return -EFBIG; + + if (offset >= i_size_read(inode)) + return -EINVAL; + + /* insert range should be aligned to block size of f2fs. */ + if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1)) + return -EINVAL; + + f2fs_balance_fs(sbi); + + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } + + ret = truncate_blocks(inode, i_size_read(inode), true); + if (ret) + return ret; + + /* write out all dirty pages from offset */ + ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + if (ret) + return ret; + + truncate_pagecache(inode, offset); + + pg_start = offset >> PAGE_CACHE_SHIFT; + pg_end = (offset + len) >> PAGE_CACHE_SHIFT; + delta = pg_end - pg_start; + nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; + + for (idx = nrpages - 1; idx >= pg_start && idx != -1; idx--) { + f2fs_lock_op(sbi); + ret = __exchange_data_block(inode, idx, idx + delta, false); + f2fs_unlock_op(sbi); + if (ret) + break; + } + + /* write out all moved pages, if possible */ + filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + truncate_pagecache(inode, offset); + + if (!ret) + i_size_write(inode, new_size); + return ret; +} + static int expand_inode_data(struct inode *inode, loff_t offset, loff_t len, int mode) { @@ -721,9 +1161,11 @@ static int expand_inode_data(struct inode *inode, loff_t offset, if (ret) return ret; - ret = f2fs_convert_inline_data(inode, offset + len, NULL); - if (ret) - return ret; + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; @@ -747,9 +1189,10 @@ noalloc: if (pg_start == pg_end) new_size = offset + len; else if (index == pg_start && off_start) - new_size = (index + 1) << PAGE_CACHE_SHIFT; + new_size = (loff_t)(index + 1) << PAGE_CACHE_SHIFT; else if (index == pg_end) - new_size = (index << PAGE_CACHE_SHIFT) + off_end; + new_size = ((loff_t)index << PAGE_CACHE_SHIFT) + + off_end; else new_size += PAGE_CACHE_SIZE; } @@ -765,33 +1208,69 @@ noalloc: return ret; } +#define FALLOC_FL_INSERT_RANGE 0X20 + static long f2fs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { struct inode *inode = file_inode(file); - long ret; + long ret = 0; + + /* f2fs only support ->fallocate for regular file */ + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + if (f2fs_encrypted_inode(inode) && + (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE))) + return -EOPNOTSUPP; - if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | + FALLOC_FL_INSERT_RANGE)) return -EOPNOTSUPP; mutex_lock(&inode->i_mutex); - if (mode & FALLOC_FL_PUNCH_HOLE) + if (mode & FALLOC_FL_PUNCH_HOLE) { + if (offset >= inode->i_size) + goto out; + ret = punch_hole(inode, offset, len); - else + } else if (mode & FALLOC_FL_COLLAPSE_RANGE) { + ret = f2fs_collapse_range(inode, offset, len); + } else if (mode & FALLOC_FL_ZERO_RANGE) { + ret = f2fs_zero_range(inode, offset, len, mode); + } else if (mode & FALLOC_FL_INSERT_RANGE) { + ret = f2fs_insert_range(inode, offset, len); + } else { ret = expand_inode_data(inode, offset, len, mode); + } if (!ret) { inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } +out: mutex_unlock(&inode->i_mutex); trace_f2fs_fallocate(inode, mode, offset, len, ret); return ret; } +static int f2fs_release_file(struct inode *inode, struct file *filp) +{ + /* some remained atomic pages should discarded */ + if (f2fs_is_atomic_file(inode)) + commit_inmem_pages(inode, true); + if (f2fs_is_volatile_file(inode)) { + set_inode_flag(F2FS_I(inode), FI_DROP_CACHE); + filemap_fdatawrite(inode->i_mapping); + clear_inode_flag(F2FS_I(inode), FI_DROP_CACHE); + } + return 0; +} + #define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) #define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) @@ -862,19 +1341,32 @@ out: return ret; } +static int f2fs_ioc_getversion(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + + return put_user(inode->i_generation, (int __user *)arg); +} + static int f2fs_ioc_start_atomic_write(struct file *filp) { struct inode *inode = file_inode(filp); - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int ret; if (!inode_owner_or_capable(inode)) return -EACCES; - f2fs_balance_fs(sbi); + f2fs_balance_fs(F2FS_I_SB(inode)); - set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + if (f2fs_is_atomic_file(inode)) + return 0; - return f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, NULL); + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + + set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + return 0; } static int f2fs_ioc_commit_atomic_write(struct file *filp) @@ -892,10 +1384,15 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) if (ret) return ret; - if (f2fs_is_atomic_file(inode)) - commit_inmem_pages(inode, false); + if (f2fs_is_atomic_file(inode)) { + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + ret = commit_inmem_pages(inode, false); + if (ret) + goto err_out; + } - ret = f2fs_sync_file(filp, 0, LONG_MAX, 0); + ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0); +err_out: mnt_drop_write_file(filp); return ret; } @@ -903,14 +1400,99 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) static int f2fs_ioc_start_volatile_write(struct file *filp) { struct inode *inode = file_inode(filp); + int ret; if (!inode_owner_or_capable(inode)) return -EACCES; + if (f2fs_is_volatile_file(inode)) + return 0; + + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); return 0; } +static int f2fs_ioc_release_volatile_write(struct file *filp) +{ + struct inode *inode = file_inode(filp); + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + if (!f2fs_is_volatile_file(inode)) + return 0; + + if (!f2fs_is_first_block_written(inode)) + return truncate_partial_data_page(inode, 0, true); + + return punch_hole(inode, 0, F2FS_BLKSIZE); +} + +static int f2fs_ioc_abort_volatile_write(struct file *filp) +{ + struct inode *inode = file_inode(filp); + int ret; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + f2fs_balance_fs(F2FS_I_SB(inode)); + + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + commit_inmem_pages(inode, true); + + mnt_drop_write_file(filp); + return ret; +} + +static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct super_block *sb = sbi->sb; + __u32 in; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(in, (__u32 __user *)arg)) + return -EFAULT; + + switch (in) { + case FS_GOING_DOWN_FULLSYNC: + sb = freeze_bdev(sb->s_bdev); + if (sb && !IS_ERR(sb)) { + f2fs_stop_checkpoint(sbi); + thaw_bdev(sb->s_bdev, sb); + } + break; + case FS_GOING_DOWN_METASYNC: + /* do checkpoint only */ + f2fs_sync_fs(sb, 1); + f2fs_stop_checkpoint(sbi); + break; + case FS_GOING_DOWN_NOSYNC: + f2fs_stop_checkpoint(sbi); + break; + case FS_GOING_DOWN_METAFLUSH: + sync_meta_pages(sbi, META, LONG_MAX); + f2fs_stop_checkpoint(sbi); + break; + default: + return -EINVAL; + } + return 0; +} + static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -941,6 +1523,132 @@ static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) return 0; } +static bool uuid_is_nonzero(__u8 u[16]) +{ + int i; + + for (i = 0; i < 16; i++) + if (u[i]) + return true; + return false; +} + +static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + struct f2fs_encryption_policy policy; + struct inode *inode = file_inode(filp); + + if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg, + sizeof(policy))) + return -EFAULT; + + return f2fs_process_policy(&policy, inode); +#else + return -EOPNOTSUPP; +#endif +} + +static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + struct f2fs_encryption_policy policy; + struct inode *inode = file_inode(filp); + int err; + + err = f2fs_get_policy(inode, &policy); + if (err) + return err; + + if (copy_to_user((struct f2fs_encryption_policy __user *)arg, &policy, + sizeof(policy))) + return -EFAULT; + return 0; +#else + return -EOPNOTSUPP; +#endif +} + +static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int err; + + if (!f2fs_sb_has_crypto(inode->i_sb)) + return -EOPNOTSUPP; + + if (uuid_is_nonzero(sbi->raw_super->encrypt_pw_salt)) + goto got_it; + + err = mnt_want_write_file(filp); + if (err) + return err; + + /* update superblock with uuid */ + generate_random_uuid(sbi->raw_super->encrypt_pw_salt); + + err = f2fs_commit_super(sbi, false); + + mnt_drop_write_file(filp); + if (err) { + /* undo new data */ + memset(sbi->raw_super->encrypt_pw_salt, 0, 16); + return err; + } +got_it: + if (copy_to_user((__u8 __user *)arg, sbi->raw_super->encrypt_pw_salt, + 16)) + return -EFAULT; + return 0; +} + +static int f2fs_ioc_gc(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + __u32 sync; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(sync, (__u32 __user *)arg)) + return -EFAULT; + + if (f2fs_readonly(sbi->sb)) + return -EROFS; + + if (!sync) { + if (!mutex_trylock(&sbi->gc_mutex)) + return -EBUSY; + } else { + mutex_lock(&sbi->gc_mutex); + } + + return f2fs_gc(sbi, sync); +} + +static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct cp_control cpc; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (f2fs_readonly(sbi->sb)) + return -EROFS; + + cpc.reason = __get_cp_reason(sbi); + + mutex_lock(&sbi->gc_mutex); + write_checkpoint(sbi, &cpc); + mutex_unlock(&sbi->gc_mutex); + + return 0; +} + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -948,19 +1656,49 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_getflags(filp, arg); case F2FS_IOC_SETFLAGS: return f2fs_ioc_setflags(filp, arg); + case F2FS_IOC_GETVERSION: + return f2fs_ioc_getversion(filp, arg); case F2FS_IOC_START_ATOMIC_WRITE: return f2fs_ioc_start_atomic_write(filp); case F2FS_IOC_COMMIT_ATOMIC_WRITE: return f2fs_ioc_commit_atomic_write(filp); case F2FS_IOC_START_VOLATILE_WRITE: return f2fs_ioc_start_volatile_write(filp); + case F2FS_IOC_RELEASE_VOLATILE_WRITE: + return f2fs_ioc_release_volatile_write(filp); + case F2FS_IOC_ABORT_VOLATILE_WRITE: + return f2fs_ioc_abort_volatile_write(filp); + case FS_IOC_SHUTDOWN: + return f2fs_ioc_shutdown(filp, arg); case FITRIM: return f2fs_ioc_fitrim(filp, arg); + case F2FS_IOC_SET_ENCRYPTION_POLICY: + return f2fs_ioc_set_encryption_policy(filp, arg); + case F2FS_IOC_GET_ENCRYPTION_POLICY: + return f2fs_ioc_get_encryption_policy(filp, arg); + case F2FS_IOC_GET_ENCRYPTION_PWSALT: + return f2fs_ioc_get_encryption_pwsalt(filp, arg); + case F2FS_IOC_GARBAGE_COLLECT: + return f2fs_ioc_gc(filp, arg); + case F2FS_IOC_WRITE_CHECKPOINT: + return f2fs_ioc_write_checkpoint(filp, arg); default: return -ENOTTY; } } +static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct inode *inode = file_inode(iocb->ki_filp); + + if (f2fs_encrypted_inode(inode) && + !f2fs_has_encryption_key(inode) && + f2fs_get_encryption_info(inode)) + return -EACCES; + + return generic_file_write_iter(iocb, from); +} + #ifdef CONFIG_COMPAT long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -980,11 +1718,10 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) const struct file_operations f2fs_file_operations = { .llseek = f2fs_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, - .write_iter = generic_file_write_iter, - .open = generic_file_open, + .write_iter = f2fs_file_write_iter, + .open = f2fs_file_open, + .release = f2fs_release_file, .mmap = f2fs_file_mmap, .fsync = f2fs_sync_file, .fallocate = f2fs_fallocate, diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 2a8f4acdb86b..fedbf67a0842 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -24,8 +24,6 @@ #include "gc.h" #include -static struct kmem_cache *winode_slab; - static int gc_thread_func(void *data) { struct f2fs_sb_info *sbi = data; @@ -46,7 +44,7 @@ static int gc_thread_func(void *data) break; if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { - wait_ms = increase_sleep_time(gc_th, wait_ms); + increase_sleep_time(gc_th, &wait_ms); continue; } @@ -67,22 +65,25 @@ static int gc_thread_func(void *data) continue; if (!is_idle(sbi)) { - wait_ms = increase_sleep_time(gc_th, wait_ms); + increase_sleep_time(gc_th, &wait_ms); mutex_unlock(&sbi->gc_mutex); continue; } if (has_enough_invalid_blocks(sbi)) - wait_ms = decrease_sleep_time(gc_th, wait_ms); + decrease_sleep_time(gc_th, &wait_ms); else - wait_ms = increase_sleep_time(gc_th, wait_ms); + increase_sleep_time(gc_th, &wait_ms); stat_inc_bggc_count(sbi); /* if return value is not zero, no victim was selected */ - if (f2fs_gc(sbi)) + if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC))) wait_ms = gc_th->no_gc_sleep_time; + trace_f2fs_background_gc(sbi->sb, wait_ms, + prefree_segments(sbi), free_segments(sbi)); + /* balancing f2fs's metadata periodically */ f2fs_balance_fs_bg(sbi); @@ -96,8 +97,6 @@ int start_gc_thread(struct f2fs_sb_info *sbi) dev_t dev = sbi->sb->s_bdev->bd_dev; int err = 0; - if (!test_opt(sbi, BG_GC)) - goto out; gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); if (!gc_th) { err = -ENOMEM; @@ -261,6 +260,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct victim_sel_policy p; unsigned int secno, max_cost; + unsigned int last_segment = MAIN_SEGS(sbi); int nsearched = 0; mutex_lock(&dirty_i->seglist_lock); @@ -271,6 +271,9 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, p.min_segno = NULL_SEGNO; p.min_cost = max_cost = get_max_cost(sbi, &p); + if (p.max_search == 0) + goto out; + if (p.alloc_mode == LFS && gc_type == FG_GC) { p.min_segno = check_bg_victims(sbi); if (p.min_segno != NULL_SEGNO) @@ -281,9 +284,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, unsigned long cost; unsigned int segno; - segno = find_next_bit(p.dirty_segmap, MAIN_SEGS(sbi), p.offset); - if (segno >= MAIN_SEGS(sbi)) { + segno = find_next_bit(p.dirty_segmap, last_segment, p.offset); + if (segno >= last_segment) { if (sbi->last_victim[p.gc_mode]) { + last_segment = sbi->last_victim[p.gc_mode]; sbi->last_victim[p.gc_mode] = 0; p.offset = 0; continue; @@ -331,6 +335,7 @@ got_it: sbi->cur_victim_sec, prefree_segments(sbi), free_segments(sbi)); } +out: mutex_unlock(&dirty_i->seglist_lock); return (p.min_segno == NULL_SEGNO) ? 0 : 1; @@ -340,37 +345,39 @@ static const struct victim_selection default_v_ops = { .get_victim = get_victim_by_default, }; -static struct inode *find_gc_inode(nid_t ino, struct list_head *ilist) +static struct inode *find_gc_inode(struct gc_inode_list *gc_list, nid_t ino) { struct inode_entry *ie; - list_for_each_entry(ie, ilist, list) - if (ie->inode->i_ino == ino) - return ie->inode; + ie = radix_tree_lookup(&gc_list->iroot, ino); + if (ie) + return ie->inode; return NULL; } -static void add_gc_inode(struct inode *inode, struct list_head *ilist) +static void add_gc_inode(struct gc_inode_list *gc_list, struct inode *inode) { struct inode_entry *new_ie; - if (inode == find_gc_inode(inode->i_ino, ilist)) { + if (inode == find_gc_inode(gc_list, inode->i_ino)) { iput(inode); return; } - - new_ie = f2fs_kmem_cache_alloc(winode_slab, GFP_NOFS); + new_ie = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS); new_ie->inode = inode; - list_add_tail(&new_ie->list, ilist); + + f2fs_radix_tree_insert(&gc_list->iroot, inode->i_ino, new_ie); + list_add_tail(&new_ie->list, &gc_list->ilist); } -static void put_gc_inode(struct list_head *ilist) +static void put_gc_inode(struct gc_inode_list *gc_list) { struct inode_entry *ie, *next_ie; - list_for_each_entry_safe(ie, next_ie, ilist, list) { + list_for_each_entry_safe(ie, next_ie, &gc_list->ilist, list) { + radix_tree_delete(&gc_list->iroot, ie->inode->i_ino); iput(ie->inode); list_del(&ie->list); - kmem_cache_free(winode_slab, ie); + kmem_cache_free(inode_entry_slab, ie); } } @@ -393,23 +400,27 @@ static int check_valid_map(struct f2fs_sb_info *sbi, * On validity, copy that node with cold status, otherwise (invalid node) * ignore that. */ -static void gc_node_segment(struct f2fs_sb_info *sbi, +static int gc_node_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, unsigned int segno, int gc_type) { bool initial = true; struct f2fs_summary *entry; + block_t start_addr; int off; + start_addr = START_BLOCK(sbi, segno); + next_step: entry = sum; for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { nid_t nid = le32_to_cpu(entry->nid); struct page *node_page; + struct node_info ni; /* stop BG_GC if there is not enough free sections. */ if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) - return; + return 0; if (check_valid_map(sbi, segno, off) == 0) continue; @@ -428,6 +439,12 @@ next_step: continue; } + get_node_info(sbi, nid, &ni); + if (ni.blk_addr != start_addr + off) { + f2fs_put_page(node_page, 1); + continue; + } + /* set page dirty and write it */ if (gc_type == FG_GC) { f2fs_wait_on_page_writeback(node_page, NODE); @@ -437,7 +454,7 @@ next_step: set_page_dirty(node_page); } f2fs_put_page(node_page, 1); - stat_inc_node_blk_count(sbi, 1); + stat_inc_node_blk_count(sbi, 1, gc_type); } if (initial) { @@ -453,13 +470,11 @@ next_step: }; sync_node_pages(sbi, 0, &wbc); - /* - * In the case of FG_GC, it'd be better to reclaim this victim - * completely. - */ - if (get_valid_blocks(sbi, segno, 1) != 0) - goto next_step; + /* return 1 only if FG_GC succefully reclaimed one */ + if (get_valid_blocks(sbi, segno, 1) == 0) + return 1; } + return 0; } /* @@ -489,7 +504,7 @@ block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi) return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi); } -static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, +static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, struct node_info *dni, block_t blkaddr, unsigned int *nofs) { struct page *node_page; @@ -502,13 +517,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, node_page = get_node_page(sbi, nid); if (IS_ERR(node_page)) - return 0; + return false; get_node_info(sbi, nid, dni); if (sum->version != dni->version) { f2fs_put_page(node_page, 1); - return 0; + return false; } *nofs = ofs_of_node(node_page); @@ -516,16 +531,106 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, f2fs_put_page(node_page, 1); if (source_blkaddr != blkaddr) - return 0; - return 1; + return false; + return true; } -static void move_data_page(struct inode *inode, struct page *page, int gc_type) +static void move_encrypted_block(struct inode *inode, block_t bidx) { struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(inode), .type = DATA, - .rw = WRITE_SYNC, + .rw = READ_SYNC, + .encrypted_page = NULL, }; + struct dnode_of_data dn; + struct f2fs_summary sum; + struct node_info ni; + struct page *page; + int err; + + /* do not read out */ + page = f2fs_grab_cache_page(inode->i_mapping, bidx, false); + if (!page) + return; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE); + if (err) + goto out; + + if (unlikely(dn.data_blkaddr == NULL_ADDR)) { + ClearPageUptodate(page); + goto put_out; + } + + /* + * don't cache encrypted data into meta inode until previous dirty + * data were writebacked to avoid racing between GC and flush. + */ + f2fs_wait_on_page_writeback(page, DATA); + + get_node_info(fio.sbi, dn.nid, &ni); + set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); + + /* read page */ + fio.page = page; + fio.blk_addr = dn.data_blkaddr; + + fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), + fio.blk_addr, + FGP_LOCK|FGP_CREAT, + GFP_NOFS); + if (!fio.encrypted_page) + goto put_out; + + err = f2fs_submit_page_bio(&fio); + if (err) + goto put_page_out; + + /* write page */ + lock_page(fio.encrypted_page); + + if (unlikely(!PageUptodate(fio.encrypted_page))) + goto put_page_out; + if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) + goto put_page_out; + + set_page_dirty(fio.encrypted_page); + f2fs_wait_on_page_writeback(fio.encrypted_page, DATA); + if (clear_page_dirty_for_io(fio.encrypted_page)) + dec_page_count(fio.sbi, F2FS_DIRTY_META); + + set_page_writeback(fio.encrypted_page); + + /* allocate block address */ + f2fs_wait_on_page_writeback(dn.node_page, NODE); + allocate_data_block(fio.sbi, NULL, fio.blk_addr, + &fio.blk_addr, &sum, CURSEG_COLD_DATA); + fio.rw = WRITE_SYNC; + f2fs_submit_page_mbio(&fio); + + dn.data_blkaddr = fio.blk_addr; + set_data_blkaddr(&dn); + f2fs_update_extent_cache(&dn); + set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + if (page->index == 0) + set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); +put_page_out: + f2fs_put_page(fio.encrypted_page, 1); +put_out: + f2fs_put_dnode(&dn); +out: + f2fs_put_page(page, 1); +} + +static void move_data_page(struct inode *inode, block_t bidx, int gc_type) +{ + struct page *page; + + page = get_lock_data_page(inode, bidx, true); + if (IS_ERR(page)) + return; if (gc_type == BG_GC) { if (PageWriteback(page)) @@ -533,12 +638,19 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type) set_page_dirty(page); set_cold_data(page); } else { + struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(inode), + .type = DATA, + .rw = WRITE_SYNC, + .page = page, + .encrypted_page = NULL, + }; + set_page_dirty(page); f2fs_wait_on_page_writeback(page, DATA); - if (clear_page_dirty_for_io(page)) inode_dec_dirty_pages(inode); set_cold_data(page); - do_write_data_page(page, &fio); + do_write_data_page(&fio); clear_cold_data(page); } out: @@ -552,8 +664,8 @@ out: * If the parent node is not valid or the data block address is different, * the victim data block is ignored. */ -static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, - struct list_head *ilist, unsigned int segno, int gc_type) +static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, + struct gc_inode_list *gc_list, unsigned int segno, int gc_type) { struct super_block *sb = sbi->sb; struct f2fs_summary *entry; @@ -575,7 +687,7 @@ next_step: /* stop BG_GC if there is not enough free sections. */ if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) - return; + return 0; if (check_valid_map(sbi, segno, off) == 0) continue; @@ -586,7 +698,7 @@ next_step: } /* Get an inode by ino with checking validity */ - if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0) + if (!is_alive(sbi, entry, &dni, start_addr + off, &nofs)) continue; if (phase == 1) { @@ -601,31 +713,37 @@ next_step: if (IS_ERR(inode) || is_bad_inode(inode)) continue; - start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + /* if encrypted inode, let's go phase 3 */ + if (f2fs_encrypted_inode(inode) && + S_ISREG(inode->i_mode)) { + add_gc_inode(gc_list, inode); + continue; + } - data_page = find_data_page(inode, - start_bidx + ofs_in_node, false); - if (IS_ERR(data_page)) - goto next_iput; + start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + data_page = get_read_data_page(inode, + start_bidx + ofs_in_node, READA, true); + if (IS_ERR(data_page)) { + iput(inode); + continue; + } f2fs_put_page(data_page, 0); - add_gc_inode(inode, ilist); - } else { - inode = find_gc_inode(dni.ino, ilist); - if (inode) { - start_bidx = start_bidx_of_node(nofs, - F2FS_I(inode)); - data_page = get_lock_data_page(inode, - start_bidx + ofs_in_node); - if (IS_ERR(data_page)) - continue; - move_data_page(inode, data_page, gc_type); - stat_inc_data_blk_count(sbi, 1); - } + add_gc_inode(gc_list, inode); + continue; + } + + /* phase 3 */ + inode = find_gc_inode(gc_list, dni.ino); + if (inode) { + start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)) + + ofs_in_node; + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + move_encrypted_block(inode, start_bidx); + else + move_data_page(inode, start_bidx, gc_type); + stat_inc_data_blk_count(sbi, 1, gc_type); } - continue; -next_iput: - iput(inode); } if (++phase < 4) @@ -634,34 +752,33 @@ next_iput: if (gc_type == FG_GC) { f2fs_submit_merged_bio(sbi, DATA, WRITE); - /* - * In the case of FG_GC, it'd be better to reclaim this victim - * completely. - */ - if (get_valid_blocks(sbi, segno, 1) != 0) { - phase = 2; - goto next_step; - } + /* return 1 only if FG_GC succefully reclaimed one */ + if (get_valid_blocks(sbi, segno, 1) == 0) + return 1; } + return 0; } static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, - int gc_type, int type) + int gc_type) { struct sit_info *sit_i = SIT_I(sbi); int ret; + mutex_lock(&sit_i->sentry_lock); - ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, type, LFS); + ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, + NO_CHECK_TYPE, LFS); mutex_unlock(&sit_i->sentry_lock); return ret; } -static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, - struct list_head *ilist, int gc_type) +static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, + struct gc_inode_list *gc_list, int gc_type) { struct page *sum_page; struct f2fs_summary_block *sum; struct blk_plug plug; + int nfree = 0; /* read segment summary of victim */ sum_page = get_sum_page(sbi, segno); @@ -670,72 +787,99 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, sum = page_address(sum_page); + /* + * this is to avoid deadlock: + * - lock_page(sum_page) - f2fs_replace_block + * - check_valid_map() - mutex_lock(sentry_lock) + * - mutex_lock(sentry_lock) - change_curseg() + * - lock_page(sum_page) + */ + unlock_page(sum_page); + switch (GET_SUM_TYPE((&sum->footer))) { case SUM_TYPE_NODE: - gc_node_segment(sbi, sum->entries, segno, gc_type); + nfree = gc_node_segment(sbi, sum->entries, segno, gc_type); break; case SUM_TYPE_DATA: - gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); + nfree = gc_data_segment(sbi, sum->entries, gc_list, + segno, gc_type); break; } blk_finish_plug(&plug); - stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); + stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type); stat_inc_call_count(sbi->stat_info); - f2fs_put_page(sum_page, 1); + f2fs_put_page(sum_page, 0); + return nfree; } -int f2fs_gc(struct f2fs_sb_info *sbi) +int f2fs_gc(struct f2fs_sb_info *sbi, bool sync) { - struct list_head ilist; unsigned int segno, i; - int gc_type = BG_GC; - int nfree = 0; - int ret = -1; - struct cp_control cpc = { - .reason = CP_SYNC, + int gc_type = sync ? FG_GC : BG_GC; + int sec_freed = 0; + int ret = -EINVAL; + struct cp_control cpc; + struct gc_inode_list gc_list = { + .ilist = LIST_HEAD_INIT(gc_list.ilist), + .iroot = RADIX_TREE_INIT(GFP_NOFS), }; - INIT_LIST_HEAD(&ilist); + cpc.reason = __get_cp_reason(sbi); gc_more: + segno = NULL_SEGNO; + if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) goto stop; if (unlikely(f2fs_cp_error(sbi))) goto stop; - if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) { + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) { gc_type = FG_GC; - write_checkpoint(sbi, &cpc); + if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi)) + write_checkpoint(sbi, &cpc); } - if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) + if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type)) goto stop; ret = 0; /* readahead multi ssa blocks those have contiguous address */ if (sbi->segs_per_sec > 1) ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec, - META_SSA); - - for (i = 0; i < sbi->segs_per_sec; i++) - do_garbage_collect(sbi, segno + i, &ilist, gc_type); + META_SSA, true); - if (gc_type == FG_GC) { - sbi->cur_victim_sec = NULL_SEGNO; - nfree++; - WARN_ON(get_valid_blocks(sbi, segno, sbi->segs_per_sec)); + for (i = 0; i < sbi->segs_per_sec; i++) { + /* + * for FG_GC case, halt gcing left segments once failed one + * of segments in selected section to avoid long latency. + */ + if (!do_garbage_collect(sbi, segno + i, &gc_list, gc_type) && + gc_type == FG_GC) + break; } - if (has_not_enough_free_secs(sbi, nfree)) - goto gc_more; + if (i == sbi->segs_per_sec && gc_type == FG_GC) + sec_freed++; if (gc_type == FG_GC) - write_checkpoint(sbi, &cpc); + sbi->cur_victim_sec = NULL_SEGNO; + + if (!sync) { + if (has_not_enough_free_secs(sbi, sec_freed)) + goto gc_more; + + if (gc_type == FG_GC) + write_checkpoint(sbi, &cpc); + } stop: mutex_unlock(&sbi->gc_mutex); - put_gc_inode(&ilist); + put_gc_inode(&gc_list); + + if (sync) + ret = sec_freed ? 0 : -EAGAIN; return ret; } @@ -743,17 +887,3 @@ void build_gc_manager(struct f2fs_sb_info *sbi) { DIRTY_I(sbi)->v_ops = &default_v_ops; } - -int __init create_gc_caches(void) -{ - winode_slab = f2fs_kmem_cache_create("f2fs_gc_inodes", - sizeof(struct inode_entry)); - if (!winode_slab) - return -ENOMEM; - return 0; -} - -void destroy_gc_caches(void) -{ - kmem_cache_destroy(winode_slab); -} diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 16f0b2b22999..b4a65be9f7d3 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -35,9 +35,9 @@ struct f2fs_gc_kthread { unsigned int gc_idle; }; -struct inode_entry { - struct list_head list; - struct inode *inode; +struct gc_inode_list { + struct list_head ilist; + struct radix_tree_root iroot; }; /* @@ -64,26 +64,26 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi) return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100; } -static inline long increase_sleep_time(struct f2fs_gc_kthread *gc_th, long wait) +static inline void increase_sleep_time(struct f2fs_gc_kthread *gc_th, + long *wait) { - if (wait == gc_th->no_gc_sleep_time) - return wait; + if (*wait == gc_th->no_gc_sleep_time) + return; - wait += gc_th->min_sleep_time; - if (wait > gc_th->max_sleep_time) - wait = gc_th->max_sleep_time; - return wait; + *wait += gc_th->min_sleep_time; + if (*wait > gc_th->max_sleep_time) + *wait = gc_th->max_sleep_time; } -static inline long decrease_sleep_time(struct f2fs_gc_kthread *gc_th, long wait) +static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th, + long *wait) { - if (wait == gc_th->no_gc_sleep_time) - wait = gc_th->max_sleep_time; + if (*wait == gc_th->no_gc_sleep_time) + *wait = gc_th->max_sleep_time; - wait -= gc_th->min_sleep_time; - if (wait <= gc_th->min_sleep_time) - wait = gc_th->min_sleep_time; - return wait; + *wait -= gc_th->min_sleep_time; + if (*wait <= gc_th->min_sleep_time) + *wait = gc_th->min_sleep_time; } static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index a844fcfb9a8d..71b7206c431e 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -79,8 +79,7 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info) const unsigned char *name = name_info->name; size_t len = name_info->len; - if ((len <= 2) && (name[0] == '.') && - (name[1] == '.' || name[1] == '\0')) + if (is_dot_dotdot(name_info)) return 0; /* Initialize the default seed for the hash checksum functions */ diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 88036fd75797..bda7126466c0 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -12,38 +12,77 @@ #include #include "f2fs.h" +#include "node.h" -bool f2fs_may_inline(struct inode *inode) +bool f2fs_may_inline_data(struct inode *inode) { - block_t nr_blocks; - loff_t i_size; - if (!test_opt(F2FS_I_SB(inode), INLINE_DATA)) return false; if (f2fs_is_atomic_file(inode)) return false; - nr_blocks = F2FS_I(inode)->i_xattr_nid ? 3 : 2; - if (inode->i_blocks > nr_blocks) + if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) + return false; + + if (i_size_read(inode) > MAX_INLINE_DATA) return false; - i_size = i_size_read(inode); - if (i_size > MAX_INLINE_DATA) + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return false; return true; } -int f2fs_read_inline_data(struct inode *inode, struct page *page) +bool f2fs_may_inline_dentry(struct inode *inode) +{ + if (!test_opt(F2FS_I_SB(inode), INLINE_DENTRY)) + return false; + + if (!S_ISDIR(inode->i_mode)) + return false; + + return true; +} + +void read_inline_data(struct page *page, struct page *ipage) { - struct page *ipage; void *src_addr, *dst_addr; - if (page->index) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); - goto out; - } + if (PageUptodate(page)) + return; + + f2fs_bug_on(F2FS_P_SB(page), page->index); + + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); + + /* Copy the whole inline data block */ + src_addr = inline_data_addr(ipage); + dst_addr = kmap_atomic(page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + flush_dcache_page(page); + kunmap_atomic(dst_addr); + SetPageUptodate(page); +} + +bool truncate_inline_inode(struct page *ipage, u64 from) +{ + void *addr; + + if (from >= MAX_INLINE_DATA) + return false; + + addr = inline_data_addr(ipage); + + f2fs_wait_on_page_writeback(ipage, NODE); + memset(addr + from, 0, MAX_INLINE_DATA - from); + + return true; +} + +int f2fs_read_inline_data(struct inode *inode, struct page *page) +{ + struct page *ipage; ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(ipage)) { @@ -51,112 +90,121 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) return PTR_ERR(ipage); } - zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); + if (!f2fs_has_inline_data(inode)) { + f2fs_put_page(ipage, 1); + return -EAGAIN; + } - /* Copy the whole inline data block */ - src_addr = inline_data_addr(ipage); - dst_addr = kmap(page); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); - kunmap(page); - f2fs_put_page(ipage, 1); + if (page->index) + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + else + read_inline_data(page, ipage); -out: SetPageUptodate(page); + f2fs_put_page(ipage, 1); unlock_page(page); - return 0; } -static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) +int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) { - int err = 0; - struct page *ipage; - struct dnode_of_data dn; void *src_addr, *dst_addr; - block_t new_blk_addr; - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(dn->inode), .type = DATA, .rw = WRITE_SYNC | REQ_PRIO, + .page = page, + .encrypted_page = NULL, }; + int dirty, err; - f2fs_lock_op(sbi); - ipage = get_node_page(sbi, inode->i_ino); - if (IS_ERR(ipage)) { - err = PTR_ERR(ipage); - goto out; - } + f2fs_bug_on(F2FS_I_SB(dn->inode), page->index); - /* someone else converted inline_data already */ - if (!f2fs_has_inline_data(inode)) - goto out; + if (!f2fs_exist_data(dn->inode)) + goto clear_out; - /* - * i_addr[0] is not used for inline data, - * so reserving new block will not destroy inline data - */ - set_new_dnode(&dn, inode, ipage, NULL, 0); - err = f2fs_reserve_block(&dn, 0); + err = f2fs_reserve_block(dn, 0); if (err) - goto out; + return err; f2fs_wait_on_page_writeback(page, DATA); + + if (PageUptodate(page)) + goto no_update; + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); /* Copy the whole inline data block */ - src_addr = inline_data_addr(ipage); - dst_addr = kmap(page); + src_addr = inline_data_addr(dn->inode_page); + dst_addr = kmap_atomic(page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); - kunmap(page); + flush_dcache_page(page); + kunmap_atomic(dst_addr); SetPageUptodate(page); +no_update: + set_page_dirty(page); + + /* clear dirty state */ + dirty = clear_page_dirty_for_io(page); /* write data page to try to make data consistent */ set_page_writeback(page); - write_data_page(page, &dn, &new_blk_addr, &fio); - update_extent_cache(new_blk_addr, &dn); + fio.blk_addr = dn->data_blkaddr; + write_data_page(dn, &fio); + set_data_blkaddr(dn); + f2fs_update_extent_cache(dn); f2fs_wait_on_page_writeback(page, DATA); + if (dirty) + inode_dec_dirty_pages(dn->inode); - /* clear inline data and flag after data writeback */ - zero_user_segment(ipage, INLINE_DATA_OFFSET, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); - clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); - stat_dec_inline_inode(inode); + /* this converted inline_data should be recovered. */ + set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE); - sync_inode_page(&dn); - f2fs_put_dnode(&dn); -out: - f2fs_unlock_op(sbi); - return err; + /* clear inline data and flag after data writeback */ + truncate_inline_inode(dn->inode_page, 0); +clear_out: + stat_dec_inline_inode(dn->inode); + f2fs_clear_inline_inode(dn->inode); + sync_inode_page(dn); + f2fs_put_dnode(dn); + return 0; } -int f2fs_convert_inline_data(struct inode *inode, pgoff_t to_size, - struct page *page) +int f2fs_convert_inline_inode(struct inode *inode) { - struct page *new_page = page; - int err; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + struct page *ipage, *page; + int err = 0; - if (!f2fs_has_inline_data(inode)) - return 0; - else if (to_size <= MAX_INLINE_DATA) - return 0; + page = grab_cache_page(inode->i_mapping, 0); + if (!page) + return -ENOMEM; - if (!page || page->index != 0) { - new_page = grab_cache_page(inode->i_mapping, 0); - if (!new_page) - return -ENOMEM; + f2fs_lock_op(sbi); + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto out; } - err = __f2fs_convert_inline_data(inode, new_page); - if (!page || page->index != 0) - f2fs_put_page(new_page, 1); + set_new_dnode(&dn, inode, ipage, ipage, 0); + + if (f2fs_has_inline_data(inode)) + err = f2fs_convert_inline_page(&dn, page); + + f2fs_put_dnode(&dn); +out: + f2fs_unlock_op(sbi); + + f2fs_put_page(page, 1); return err; } -int f2fs_write_inline_data(struct inode *inode, - struct page *page, unsigned size) +int f2fs_write_inline_data(struct inode *inode, struct page *page) { void *src_addr, *dst_addr; - struct page *ipage; struct dnode_of_data dn; int err; @@ -164,49 +212,28 @@ int f2fs_write_inline_data(struct inode *inode, err = get_dnode_of_data(&dn, 0, LOOKUP_NODE); if (err) return err; - ipage = dn.inode_page; - f2fs_wait_on_page_writeback(ipage, NODE); - zero_user_segment(ipage, INLINE_DATA_OFFSET, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); - src_addr = kmap(page); - dst_addr = inline_data_addr(ipage); - memcpy(dst_addr, src_addr, size); - kunmap(page); - - /* Release the first data block if it is allocated */ if (!f2fs_has_inline_data(inode)) { - truncate_data_blocks_range(&dn, 1); - set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); - stat_inc_inline_inode(inode); + f2fs_put_dnode(&dn); + return -EAGAIN; } + f2fs_bug_on(F2FS_I_SB(inode), page->index); + + f2fs_wait_on_page_writeback(dn.inode_page, NODE); + src_addr = kmap_atomic(page); + dst_addr = inline_data_addr(dn.inode_page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + kunmap_atomic(src_addr); + set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + sync_inode_page(&dn); f2fs_put_dnode(&dn); - return 0; } -void truncate_inline_data(struct inode *inode, u64 from) -{ - struct page *ipage; - - if (from >= MAX_INLINE_DATA) - return; - - ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); - if (IS_ERR(ipage)) - return; - - f2fs_wait_on_page_writeback(ipage, NODE); - - zero_user_segment(ipage, INLINE_DATA_OFFSET + from, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); - set_page_dirty(ipage); - f2fs_put_page(ipage, 1); -} - bool recover_inline_data(struct inode *inode, struct page *npage) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -236,6 +263,10 @@ process_inline: src_addr = inline_data_addr(npage); dst_addr = inline_data_addr(ipage); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + update_inode(inode, ipage); f2fs_put_page(ipage, 1); return true; @@ -244,16 +275,334 @@ process_inline: if (f2fs_has_inline_data(inode)) { ipage = get_node_page(sbi, inode->i_ino); f2fs_bug_on(sbi, IS_ERR(ipage)); - f2fs_wait_on_page_writeback(ipage, NODE); - zero_user_segment(ipage, INLINE_DATA_OFFSET, - INLINE_DATA_OFFSET + MAX_INLINE_DATA); - clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + if (!truncate_inline_inode(ipage, 0)) + return false; + f2fs_clear_inline_inode(inode); update_inode(inode, ipage); f2fs_put_page(ipage, 1); } else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) { - truncate_blocks(inode, 0, false); - set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + if (truncate_blocks(inode, 0, false)) + return false; goto process_inline; } return false; } + +struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, + struct f2fs_filename *fname, struct page **res_page) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); + struct f2fs_inline_dentry *inline_dentry; + struct qstr name = FSTR_TO_QSTR(&fname->disk_name); + struct f2fs_dir_entry *de; + struct f2fs_dentry_ptr d; + struct page *ipage; + f2fs_hash_t namehash; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return NULL; + + namehash = f2fs_dentry_hash(&name); + + inline_dentry = inline_data_addr(ipage); + + make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2); + de = find_target_dentry(fname, namehash, NULL, &d); + unlock_page(ipage); + if (de) + *res_page = ipage; + else + f2fs_put_page(ipage, 0); + + /* + * For the most part, it should be a bug when name_len is zero. + * We stop here for figuring out where the bugs has occurred. + */ + f2fs_bug_on(sbi, d.max < 0); + return de; +} + +struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *dir, + struct page **p) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + struct f2fs_dir_entry *de; + struct f2fs_inline_dentry *dentry_blk; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return NULL; + + dentry_blk = inline_data_addr(ipage); + de = &dentry_blk->dentry[1]; + *p = ipage; + unlock_page(ipage); + return de; +} + +int make_empty_inline_dir(struct inode *inode, struct inode *parent, + struct page *ipage) +{ + struct f2fs_inline_dentry *dentry_blk; + struct f2fs_dentry_ptr d; + + dentry_blk = inline_data_addr(ipage); + + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); + do_make_empty_dir(inode, parent, &d); + + set_page_dirty(ipage); + + /* update i_size to MAX_INLINE_DATA */ + if (i_size_read(inode) < MAX_INLINE_DATA) { + i_size_write(inode, MAX_INLINE_DATA); + set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); + } + return 0; +} + +/* + * NOTE: ipage is grabbed by caller, but if any error occurs, we should + * release ipage in this function. + */ +static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, + struct f2fs_inline_dentry *inline_dentry) +{ + struct page *page; + struct dnode_of_data dn; + struct f2fs_dentry_block *dentry_blk; + int err; + + page = grab_cache_page(dir->i_mapping, 0); + if (!page) { + f2fs_put_page(ipage, 1); + return -ENOMEM; + } + + set_new_dnode(&dn, dir, ipage, NULL, 0); + err = f2fs_reserve_block(&dn, 0); + if (err) + goto out; + + f2fs_wait_on_page_writeback(page, DATA); + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); + + dentry_blk = kmap_atomic(page); + + /* copy data from inline dentry block to new dentry block */ + memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap, + INLINE_DENTRY_BITMAP_SIZE); + memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0, + SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE); + /* + * we do not need to zero out remainder part of dentry and filename + * field, since we have used bitmap for marking the usage status of + * them, besides, we can also ignore copying/zeroing reserved space + * of dentry block, because them haven't been used so far. + */ + memcpy(dentry_blk->dentry, inline_dentry->dentry, + sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY); + memcpy(dentry_blk->filename, inline_dentry->filename, + NR_INLINE_DENTRY * F2FS_SLOT_LEN); + + kunmap_atomic(dentry_blk); + SetPageUptodate(page); + set_page_dirty(page); + + /* clear inline dir and flag after data writeback */ + truncate_inline_inode(ipage, 0); + + stat_dec_inline_dir(dir); + clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); + + if (i_size_read(dir) < PAGE_CACHE_SIZE) { + i_size_write(dir, PAGE_CACHE_SIZE); + set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } + + sync_inode_page(&dn); +out: + f2fs_put_page(page, 1); + return err; +} + +int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, + struct inode *inode, nid_t ino, umode_t mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + unsigned int bit_pos; + f2fs_hash_t name_hash; + size_t namelen = name->len; + struct f2fs_inline_dentry *dentry_blk = NULL; + struct f2fs_dentry_ptr d; + int slots = GET_DENTRY_SLOTS(namelen); + struct page *page = NULL; + int err = 0; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + dentry_blk = inline_data_addr(ipage); + bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, + slots, NR_INLINE_DENTRY); + if (bit_pos >= NR_INLINE_DENTRY) { + err = f2fs_convert_inline_dir(dir, ipage, dentry_blk); + if (err) + return err; + err = -EAGAIN; + goto out; + } + + if (inode) { + down_write(&F2FS_I(inode)->i_sem); + page = init_inode_metadata(inode, dir, name, ipage); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto fail; + } + } + + f2fs_wait_on_page_writeback(ipage, NODE); + + name_hash = f2fs_dentry_hash(name); + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); + f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos); + + set_page_dirty(ipage); + + /* we don't need to mark_inode_dirty now */ + if (inode) { + F2FS_I(inode)->i_pino = dir->i_ino; + update_inode(inode, page); + f2fs_put_page(page, 1); + } + + update_parent_metadata(dir, inode, 0); +fail: + if (inode) + up_write(&F2FS_I(inode)->i_sem); + + if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) { + update_inode(dir, ipage); + clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } +out: + f2fs_put_page(ipage, 1); + return err; +} + +void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, + struct inode *dir, struct inode *inode) +{ + struct f2fs_inline_dentry *inline_dentry; + int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); + unsigned int bit_pos; + int i; + + lock_page(page); + f2fs_wait_on_page_writeback(page, NODE); + + inline_dentry = inline_data_addr(page); + bit_pos = dentry - inline_dentry->dentry; + for (i = 0; i < slots; i++) + test_and_clear_bit_le(bit_pos + i, + &inline_dentry->dentry_bitmap); + + set_page_dirty(page); + + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + + if (inode) + f2fs_drop_nlink(dir, inode, page); + + f2fs_put_page(page, 1); +} + +bool f2fs_empty_inline_dir(struct inode *dir) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + unsigned int bit_pos = 2; + struct f2fs_inline_dentry *dentry_blk; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return false; + + dentry_blk = inline_data_addr(ipage); + bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, + NR_INLINE_DENTRY, + bit_pos); + + f2fs_put_page(ipage, 1); + + if (bit_pos < NR_INLINE_DENTRY) + return false; + + return true; +} + +int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, + struct f2fs_str *fstr) +{ + struct inode *inode = file_inode(file); + struct f2fs_inline_dentry *inline_dentry = NULL; + struct page *ipage = NULL; + struct f2fs_dentry_ptr d; + + if (ctx->pos == NR_INLINE_DENTRY) + return 0; + + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + inline_dentry = inline_data_addr(ipage); + + make_dentry_ptr(inode, &d, (void *)inline_dentry, 2); + + if (!f2fs_fill_dentries(ctx, &d, 0, fstr)) + ctx->pos = NR_INLINE_DENTRY; + + f2fs_put_page(ipage, 1); + return 0; +} + +int f2fs_inline_data_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, __u64 start, __u64 len) +{ + __u64 byteaddr, ilen; + __u32 flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED | + FIEMAP_EXTENT_LAST; + struct node_info ni; + struct page *ipage; + int err = 0; + + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + if (!f2fs_has_inline_data(inode)) { + err = -EAGAIN; + goto out; + } + + ilen = min_t(size_t, MAX_INLINE_DATA, i_size_read(inode)); + if (start >= ilen) + goto out; + if (start + len < ilen) + ilen = start + len; + ilen -= start; + + get_node_info(F2FS_I_SB(inode), inode->i_ino, &ni); + byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits; + byteaddr += (char *)inline_data_addr(ipage) - (char *)F2FS_INODE(ipage); + err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags); +out: + f2fs_put_page(ipage, 1); + return err; +} diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 0deead4505e7..54927b62f525 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "f2fs.h" #include "node.h" @@ -34,8 +33,8 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_NOATIME; if (flags & FS_DIRSYNC_FL) new_fl |= S_DIRSYNC; - set_mask_bits(&inode->i_flags, - S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl); + inode_set_flags(inode, new_fl, + S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) @@ -51,6 +50,15 @@ static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) } } +static bool __written_first_block(struct f2fs_inode *ri) +{ + block_t addr = le32_to_cpu(ri->i_addr[0]); + + if (addr != NEW_ADDR && addr != NULL_ADDR) + return true; + return false; +} + static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri) { if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { @@ -67,6 +75,25 @@ static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri) } } +static void __recover_inline_status(struct inode *inode, struct page *ipage) +{ + void *inline_data = inline_data_addr(ipage); + __le32 *start = inline_data; + __le32 *end = start + MAX_INLINE_DATA / sizeof(__le32); + + while (start < end) { + if (*start++) { + f2fs_wait_on_page_writeback(ipage, NODE); + + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage)); + set_page_dirty(ipage); + return; + } + } + return; +} + static int do_read_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -111,13 +138,26 @@ static int do_read_inode(struct inode *inode) fi->i_pino = le32_to_cpu(ri->i_pino); fi->i_dir_level = ri->i_dir_level; - get_extent_info(&fi->ext, ri->i_ext); + f2fs_init_extent_tree(inode, &ri->i_ext); + get_inline_info(fi, ri); + /* check data exist */ + if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) + __recover_inline_status(inode, node_page); + /* get rdev by using inline_info */ __get_inode_rdev(inode, ri); + if (__written_first_block(ri)) + set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); + f2fs_put_page(node_page, 1); + + stat_inc_inline_xattr(inode); + stat_inc_inline_inode(inode); + stat_inc_inline_dir(inode); + return 0; } @@ -156,9 +196,12 @@ make_now: inode->i_op = &f2fs_dir_inode_operations; inode->i_fop = &f2fs_dir_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; - mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); + mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &f2fs_symlink_inode_operations; + if (f2fs_encrypted_inode(inode)) + inode->i_op = &f2fs_encrypted_symlink_inode_operations; + else + inode->i_op = &f2fs_symlink_inode_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { @@ -193,7 +236,12 @@ void update_inode(struct inode *inode, struct page *node_page) ri->i_links = cpu_to_le32(inode->i_nlink); ri->i_size = cpu_to_le64(i_size_read(inode)); ri->i_blocks = cpu_to_le64(inode->i_blocks); - set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext); + + if (F2FS_I(inode)->extent_tree) + set_raw_extent(&F2FS_I(inode)->extent_tree->largest, + &ri->i_ext); + else + memset(&ri->i_ext, 0, sizeof(ri->i_ext)); set_raw_inline(F2FS_I(inode), ri); ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec); @@ -248,16 +296,12 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) return 0; /* - * We need to lock here to prevent from producing dirty node pages + * We need to balance fs here to prevent from producing dirty node pages * during the urgent cleaning time when runing out of free sections. */ - f2fs_lock_op(sbi); update_inode_page(inode); - f2fs_unlock_op(sbi); - - if (wbc) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi); return 0; } @@ -267,14 +311,16 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) void f2fs_evict_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - nid_t xnid = F2FS_I(inode)->i_xattr_nid; + struct f2fs_inode_info *fi = F2FS_I(inode); + nid_t xnid = fi->i_xattr_nid; + int err = 0; /* some remained atomic pages should discarded */ - if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) + if (f2fs_is_atomic_file(inode)) commit_inmem_pages(inode, true); trace_f2fs_evict_inode(inode); - truncate_inode_pages_final(&inode->i_data); + truncate_inode_pages(&inode->i_data, 0); if (inode->i_ino == F2FS_NODE_INO(sbi) || inode->i_ino == F2FS_META_INO(sbi)) @@ -283,31 +329,63 @@ void f2fs_evict_inode(struct inode *inode) f2fs_bug_on(sbi, get_dirty_pages(inode)); remove_dirty_dir_inode(inode); + f2fs_destroy_extent_tree(inode); + if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; sb_start_intwrite(inode->i_sb); - set_inode_flag(F2FS_I(inode), FI_NO_ALLOC); + set_inode_flag(fi, FI_NO_ALLOC); i_size_write(inode, 0); if (F2FS_HAS_BLOCKS(inode)) - f2fs_truncate(inode); + err = f2fs_truncate(inode, true); - f2fs_lock_op(sbi); - remove_inode_page(inode); - stat_dec_inline_inode(inode); - f2fs_unlock_op(sbi); + if (!err) { + f2fs_lock_op(sbi); + err = remove_inode_page(inode); + f2fs_unlock_op(sbi); + } sb_end_intwrite(inode->i_sb); no_delete: + stat_dec_inline_xattr(inode); + stat_dec_inline_dir(inode); + stat_dec_inline_inode(inode); + invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino); if (xnid) invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid); - if (is_inode_flag_set(F2FS_I(inode), FI_APPEND_WRITE)) + if (is_inode_flag_set(fi, FI_APPEND_WRITE)) add_dirty_inode(sbi, inode->i_ino, APPEND_INO); - if (is_inode_flag_set(F2FS_I(inode), FI_UPDATE_WRITE)) + if (is_inode_flag_set(fi, FI_UPDATE_WRITE)) add_dirty_inode(sbi, inode->i_ino, UPDATE_INO); + if (is_inode_flag_set(fi, FI_FREE_NID)) { + if (err && err != -ENOENT) + alloc_nid_done(sbi, inode->i_ino); + else + alloc_nid_failed(sbi, inode->i_ino); + clear_inode_flag(fi, FI_FREE_NID); + } + + if (err && err != -ENOENT) { + if (!exist_written_data(sbi, inode->i_ino, ORPHAN_INO)) { + /* + * get here because we failed to release resource + * of inode previously, reminder our user to run fsck + * for fixing. + */ + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_msg(sbi->sb, KERN_WARNING, + "inode (ino:%lu) resource leak, run fsck " + "to fix this issue!", inode->i_ino); + } + } out_clear: +#ifdef CONFIG_F2FS_FS_ENCRYPTION + if (fi->i_crypt_info) + f2fs_free_encryption_info(inode, fi->i_crypt_info); +#endif clear_inode(inode); } @@ -315,6 +393,7 @@ out_clear: void handle_failed_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int err = 0; clear_nlink(inode); make_bad_inode(inode); @@ -322,12 +401,29 @@ void handle_failed_inode(struct inode *inode) i_size_write(inode, 0); if (F2FS_HAS_BLOCKS(inode)) - f2fs_truncate(inode); + err = f2fs_truncate(inode, false); - remove_inode_page(inode); - stat_dec_inline_inode(inode); + if (!err) + err = remove_inode_page(inode); + + /* + * if we skip truncate_node in remove_inode_page bacause we failed + * before, it's better to find another way to release resource of + * this inode (e.g. valid block count, node block or nid). Here we + * choose to add this inode to orphan list, so that we can call iput + * for releasing in orphan recovery flow. + * + * Note: we should add inode to orphan list before f2fs_unlock_op() + * so we can prevent losing this orphan when encoutering checkpoint + * and following suddenly power-off. + */ + if (err && err != -ENOENT) { + err = acquire_orphan_inode(sbi); + if (!err) + add_orphan_inode(sbi, inode->i_ino); + } - alloc_nid_failed(sbi, inode->i_ino); + set_inode_flag(F2FS_I(inode), FI_FREE_NID); f2fs_unlock_op(sbi); /* iput will drop the inode object */ diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 0d2526e5aa11..38349a1a3837 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -9,11 +9,13 @@ * published by the Free Software Foundation. */ #include +#include #include #include #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -52,21 +54,34 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (err) { err = -EINVAL; nid_free = true; - goto out; + goto fail; } + + /* If the directory encrypted, then we should encrypt the inode. */ + if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) + f2fs_set_encrypted_inode(inode); + + if (f2fs_may_inline_data(inode)) + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + if (f2fs_may_inline_dentry(inode)) + set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY); + + f2fs_init_extent_tree(inode, NULL); + + stat_inc_inline_xattr(inode); + stat_inc_inline_inode(inode); + stat_inc_inline_dir(inode); + trace_f2fs_new_inode(inode, 0); mark_inode_dirty(inode); return inode; -out: - clear_nlink(inode); - unlock_new_inode(inode); fail: trace_f2fs_new_inode(inode, err); make_bad_inode(inode); - iput(inode); if (nid_free) - alloc_nid_failed(sbi, ino); + set_inode_flag(F2FS_I(inode), FI_FREE_NID); + iput(inode); return ERR_PTR(err); } @@ -75,7 +90,14 @@ static int is_multimedia_file(const unsigned char *s, const char *sub) size_t slen = strlen(s); size_t sublen = strlen(sub); - if (sublen > slen) + /* + * filename format of multimedia file should be defined as: + * "filename + '.' + extension". + */ + if (slen < sublen + 2) + return 0; + + if (s[slen - sublen - 1] != '.') return 0; return !strncasecmp(s + slen - sublen, sub, sublen); @@ -131,6 +153,9 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, d_instantiate(dentry, inode); unlock_new_inode(inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); return 0; out: handle_failed_inode(inode); @@ -144,6 +169,10 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, struct f2fs_sb_info *sbi = F2FS_I_SB(dir); int err; + if (f2fs_encrypted_inode(dir) && + !f2fs_is_child_context_consistent_with_parent(dir, inode)) + return -EPERM; + f2fs_balance_fs(sbi); inode->i_ctime = CURRENT_TIME; @@ -157,6 +186,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, f2fs_unlock_op(sbi); d_instantiate(dentry, inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); return 0; out: clear_inode_flag(F2FS_I(inode), FI_INC_LINK); @@ -174,30 +206,78 @@ struct dentry *f2fs_get_parent(struct dentry *child) return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino)); } +static int __recover_dot_dentries(struct inode *dir, nid_t pino) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct qstr dot = {.len = 1, .name = "."}; + struct qstr dotdot = {.len = 2, .name = ".."}; + struct f2fs_dir_entry *de; + struct page *page; + int err = 0; + + f2fs_lock_op(sbi); + + de = f2fs_find_entry(dir, &dot, &page); + if (de) { + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + } else { + err = __f2fs_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR); + if (err) + goto out; + } + + de = f2fs_find_entry(dir, &dotdot, &page); + if (de) { + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + } else { + err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR); + } +out: + if (!err) { + clear_inode_flag(F2FS_I(dir), FI_INLINE_DOTS); + mark_inode_dirty(dir); + } + + f2fs_unlock_op(sbi); + return err; +} + static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct inode *inode = NULL; struct f2fs_dir_entry *de; struct page *page; + nid_t ino; + int err = 0; if (dentry->d_name.len > F2FS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); de = f2fs_find_entry(dir, &dentry->d_name, &page); - if (de) { - nid_t ino = le32_to_cpu(de->ino); - kunmap(page); - f2fs_put_page(page, 0); + if (!de) + return d_splice_alias(inode, dentry); - inode = f2fs_iget(dir->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); + ino = le32_to_cpu(de->ino); + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); - stat_inc_inline_inode(inode); - } + inode = f2fs_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (f2fs_has_inline_dots(inode)) { + err = __recover_dot_dentries(inode, dir->i_ino); + if (err) + goto err_out; + } return d_splice_alias(inode, dentry); + +err_out: + iget_failed(inode); + return ERR_PTR(err); } static int f2fs_unlink(struct inode *dir, struct dentry *dentry) @@ -219,35 +299,65 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) err = acquire_orphan_inode(sbi); if (err) { f2fs_unlock_op(sbi); - kunmap(page); + f2fs_dentry_kunmap(dir, page); f2fs_put_page(page, 0); goto fail; } - f2fs_delete_entry(de, page, inode); + f2fs_delete_entry(de, page, dir, inode); f2fs_unlock_op(sbi); /* In order to evict this inode, we set it dirty */ mark_inode_dirty(inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); fail: trace_f2fs_unlink_exit(inode, err); return err; } +static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct page *page; + + page = page_follow_link_light(dentry, nd); + if (IS_ERR(page)) + return page; + + /* this is broken symlink case */ + if (*nd_get_link(nd) == 0) { + kunmap(page); + page_cache_release(page); + return ERR_PTR(-ENOENT); + } + return page; +} + static int f2fs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct inode *inode; - size_t symlen = strlen(symname) + 1; + size_t len = strlen(symname); + size_t p_len; + char *p_str; + struct f2fs_str disk_link = FSTR_INIT(NULL, 0); + struct f2fs_encrypted_symlink_data *sd = NULL; int err; + if (len > dir->i_sb->s_blocksize) + return -ENAMETOOLONG; + f2fs_balance_fs(sbi); inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); if (IS_ERR(inode)) return PTR_ERR(inode); - inode->i_op = &f2fs_symlink_inode_operations; + if (f2fs_encrypted_inode(inode)) + inode->i_op = &f2fs_encrypted_symlink_inode_operations; + else + inode->i_op = &f2fs_symlink_inode_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; f2fs_lock_op(sbi); @@ -255,12 +365,69 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, if (err) goto out; f2fs_unlock_op(sbi); - - err = page_symlink(inode, symname, symlen); alloc_nid_done(sbi, inode->i_ino); + if (f2fs_encrypted_inode(dir)) { + struct qstr istr = QSTR_INIT(symname, len); + + err = f2fs_get_encryption_info(inode); + if (err) + goto err_out; + + err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link); + if (err) + goto err_out; + + err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link); + if (err < 0) + goto err_out; + + p_len = encrypted_symlink_data_len(disk_link.len) + 1; + + if (p_len > dir->i_sb->s_blocksize) { + err = -ENAMETOOLONG; + goto err_out; + } + + sd = kzalloc(p_len, GFP_NOFS); + if (!sd) { + err = -ENOMEM; + goto err_out; + } + memcpy(sd->encrypted_path, disk_link.name, disk_link.len); + sd->len = cpu_to_le16(disk_link.len); + p_str = (char *)sd; + } else { + p_len = len + 1; + p_str = (char *)symname; + } + + err = page_symlink(inode, p_str, p_len); + +err_out: d_instantiate(dentry, inode); unlock_new_inode(inode); + + /* + * Let's flush symlink data in order to avoid broken symlink as much as + * possible. Nevertheless, fsyncing is the best way, but there is no + * way to get a file descriptor in order to flush that. + * + * Note that, it needs to do dir->fsync to make this recoverable. + * If the symlink path is stored into inline_data, there is no + * performance regression. + */ + if (!err) { + filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); + } else { + f2fs_unlink(dir, dentry); + } + + kfree(sd); + f2fs_fname_crypto_free_buffer(&disk_link); return err; out: handle_failed_inode(inode); @@ -282,7 +449,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_op = &f2fs_dir_inode_operations; inode->i_fop = &f2fs_dir_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; - mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); + mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); set_inode_flag(F2FS_I(inode), FI_INC_LINK); f2fs_lock_op(sbi); @@ -296,6 +463,8 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) d_instantiate(dentry, inode); unlock_new_inode(inode); + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); return 0; out_fail: @@ -338,27 +507,113 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, f2fs_unlock_op(sbi); alloc_nid_done(sbi, inode->i_ino); + d_instantiate(dentry, inode); unlock_new_inode(inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); return 0; out: handle_failed_inode(inode); return err; } +static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode, struct inode **whiteout) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct inode *inode; + int err; + + if (!whiteout) + f2fs_balance_fs(sbi); + + inode = f2fs_new_inode(dir, mode); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + if (whiteout) { + init_special_inode(inode, inode->i_mode, WHITEOUT_DEV); + inode->i_op = &f2fs_special_inode_operations; + } else { + inode->i_op = &f2fs_file_inode_operations; + inode->i_fop = &f2fs_file_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + } + + f2fs_lock_op(sbi); + err = acquire_orphan_inode(sbi); + if (err) + goto out; + + err = f2fs_do_tmpfile(inode, dir); + if (err) + goto release_out; + + /* + * add this non-linked tmpfile to orphan list, in this way we could + * remove all unused data of tmpfile after abnormal power-off. + */ + add_orphan_inode(sbi, inode->i_ino); + f2fs_unlock_op(sbi); + + alloc_nid_done(sbi, inode->i_ino); + + if (whiteout) { + inode_dec_link_count(inode); + *whiteout = inode; + } else { + d_tmpfile(dentry, inode); + } + unlock_new_inode(inode); + return 0; + +release_out: + release_orphan_inode(sbi); +out: + handle_failed_inode(inode); + return err; +} + +static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + if (f2fs_encrypted_inode(dir)) { + int err = f2fs_get_encryption_info(dir); + if (err) + return err; + } + + return __f2fs_tmpfile(dir, dentry, mode, NULL); +} + +static int f2fs_create_whiteout(struct inode *dir, struct inode **whiteout) +{ + return __f2fs_tmpfile(dir, NULL, S_IFCHR | WHITEOUT_MODE, whiteout); +} + static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir); struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; + struct inode *whiteout = NULL; struct page *old_dir_page; - struct page *old_page, *new_page; + struct page *old_page, *new_page = NULL; struct f2fs_dir_entry *old_dir_entry = NULL; struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *new_entry; int err = -ENOENT; + if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && + !f2fs_is_child_context_consistent_with_parent(new_dir, + old_inode)) { + err = -EPERM; + goto out; + } + f2fs_balance_fs(sbi); old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); @@ -372,17 +627,23 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_old; } + if (flags & RENAME_WHITEOUT) { + err = f2fs_create_whiteout(old_dir, &whiteout); + if (err) + goto out_dir; + } + if (new_inode) { err = -ENOTEMPTY; if (old_dir_entry && !f2fs_empty_dir(new_inode)) - goto out_dir; + goto out_whiteout; err = -ENOENT; new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, &new_page); if (!new_entry) - goto out_dir; + goto out_whiteout; f2fs_lock_op(sbi); @@ -390,7 +651,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (err) goto put_out_dir; - if (update_dent_inode(old_inode, &new_dentry->d_name)) { + if (update_dent_inode(old_inode, new_inode, + &new_dentry->d_name)) { release_orphan_inode(sbi); goto put_out_dir; } @@ -419,7 +681,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, err = f2fs_add_link(new_dentry, old_inode); if (err) { f2fs_unlock_op(sbi); - goto out_dir; + goto out_whiteout; } if (old_dir_entry) { @@ -430,20 +692,32 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, down_write(&F2FS_I(old_inode)->i_sem); file_lost_pino(old_inode); + if (new_inode && file_enc_name(new_inode)) + file_set_enc_name(old_inode); up_write(&F2FS_I(old_inode)->i_sem); old_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(old_inode); - f2fs_delete_entry(old_entry, old_page, NULL); + f2fs_delete_entry(old_entry, old_page, old_dir, NULL); + + if (whiteout) { + whiteout->i_state |= I_LINKABLE; + set_inode_flag(F2FS_I(whiteout), FI_INC_LINK); + err = f2fs_add_link(old_dentry, whiteout); + if (err) + goto put_out_dir; + whiteout->i_state &= ~I_LINKABLE; + iput(whiteout); + } if (old_dir_entry) { - if (old_dir != new_dir) { + if (old_dir != new_dir && !whiteout) { f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir); update_inode_page(old_inode); } else { - kunmap(old_dir_page); + f2fs_dentry_kunmap(old_inode, old_dir_page); f2fs_put_page(old_dir_page, 0); } drop_nlink(old_dir); @@ -452,19 +726,27 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } f2fs_unlock_op(sbi); + + if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) + f2fs_sync_fs(sbi->sb, 1); return 0; put_out_dir: f2fs_unlock_op(sbi); - kunmap(new_page); - f2fs_put_page(new_page, 0); + if (new_page) { + f2fs_dentry_kunmap(new_dir, new_page); + f2fs_put_page(new_page, 0); + } +out_whiteout: + if (whiteout) + iput(whiteout); out_dir: if (old_dir_entry) { - kunmap(old_dir_page); + f2fs_dentry_kunmap(old_inode, old_dir_page); f2fs_put_page(old_dir_page, 0); } out_old: - kunmap(old_page); + f2fs_dentry_kunmap(old_dir, old_page); f2fs_put_page(old_page, 0); out: return err; @@ -483,6 +765,14 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, int old_nlink = 0, new_nlink = 0; int err = -ENOENT; + if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) && + (old_dir != new_dir) && + (!f2fs_is_child_context_consistent_with_parent(new_dir, + old_inode) || + !f2fs_is_child_context_consistent_with_parent(old_dir, + new_inode))) + return -EPERM; + f2fs_balance_fs(sbi); old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); @@ -529,13 +819,17 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_lock_op(sbi); - err = update_dent_inode(old_inode, &new_dentry->d_name); + err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name); if (err) goto out_unlock; + if (file_enc_name(new_inode)) + file_set_enc_name(old_inode); - err = update_dent_inode(new_inode, &old_dentry->d_name); + err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name); if (err) goto out_undo; + if (file_enc_name(old_inode)) + file_set_enc_name(new_inode); /* update ".." directory entry info of old dentry */ if (old_dir_entry) @@ -588,27 +882,33 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, update_inode_page(new_dir); f2fs_unlock_op(sbi); + + if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) + f2fs_sync_fs(sbi->sb, 1); return 0; out_undo: - /* Still we may fail to recover name info of f2fs_inode here */ - update_dent_inode(old_inode, &old_dentry->d_name); + /* + * Still we may fail to recover name info of f2fs_inode here + * Drop it, once its name is set as encrypted + */ + update_dent_inode(old_inode, old_inode, &old_dentry->d_name); out_unlock: f2fs_unlock_op(sbi); out_new_dir: if (new_dir_entry) { - kunmap(new_dir_page); + f2fs_dentry_kunmap(new_inode, new_dir_page); f2fs_put_page(new_dir_page, 0); } out_old_dir: if (old_dir_entry) { - kunmap(old_dir_page); + f2fs_dentry_kunmap(old_inode, old_dir_page); f2fs_put_page(old_dir_page, 0); } out_new: - kunmap(new_page); + f2fs_dentry_kunmap(new_dir, new_page); f2fs_put_page(new_page, 0); out_old: - kunmap(old_page); + f2fs_dentry_kunmap(old_dir, old_page); f2fs_put_page(old_page, 0); out: return err; @@ -618,7 +918,7 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; if (flags & RENAME_EXCHANGE) { @@ -629,51 +929,103 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, * VFS has already handled the new dentry existence case, * here, we just deal with "RENAME_NOREPLACE" as regular rename. */ - return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry); + return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } -static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +#ifdef CONFIG_F2FS_FS_ENCRYPTION +static void *f2fs_encrypted_follow_link(struct dentry *dentry, + struct nameidata *nd) { - struct f2fs_sb_info *sbi = F2FS_I_SB(dir); - struct inode *inode; - int err; - - inode = f2fs_new_inode(dir, mode); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - inode->i_op = &f2fs_file_inode_operations; - inode->i_fop = &f2fs_file_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - - f2fs_lock_op(sbi); - err = acquire_orphan_inode(sbi); - if (err) - goto out; - - err = f2fs_do_tmpfile(inode, dir); - if (err) - goto release_out; + struct page *cpage = NULL; + char *caddr, *paddr = NULL; + struct f2fs_str cstr; + struct f2fs_str pstr = FSTR_INIT(NULL, 0); + struct inode *inode = dentry->d_inode; + struct f2fs_encrypted_symlink_data *sd; + loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); + u32 max_size = inode->i_sb->s_blocksize; + int res; + + res = f2fs_get_encryption_info(inode); + if (res) + return ERR_PTR(res); + + cpage = read_mapping_page(inode->i_mapping, 0, NULL); + if (IS_ERR(cpage)) + return cpage; + caddr = kmap(cpage); + caddr[size] = 0; + + /* Symlink is encrypted */ + sd = (struct f2fs_encrypted_symlink_data *)caddr; + cstr.len = le16_to_cpu(sd->len); + cstr.name = kmalloc(cstr.len, GFP_NOFS); + if (!cstr.name) { + res = -ENOMEM; + goto errout; + } + memcpy(cstr.name, sd->encrypted_path, cstr.len); - /* - * add this non-linked tmpfile to orphan list, in this way we could - * remove all unused data of tmpfile after abnormal power-off. - */ - add_orphan_inode(sbi, inode->i_ino); - f2fs_unlock_op(sbi); + /* this is broken symlink case */ + if (cstr.name[0] == 0 && cstr.len == 0) { + res = -ENOENT; + goto errout; + } - alloc_nid_done(sbi, inode->i_ino); - d_tmpfile(dentry, inode); - unlock_new_inode(inode); - return 0; + if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) > + max_size) { + /* Symlink data on the disk is corrupted */ + res = -EIO; + goto errout; + } + res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr); + if (res) + goto errout; + + res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr); + if (res < 0) + goto errout; + + kfree(cstr.name); + + paddr = pstr.name; + + /* Null-terminate the name */ + paddr[res] = '\0'; + nd_set_link(nd, paddr); + + kunmap(cpage); + page_cache_release(cpage); + return NULL; +errout: + kfree(cstr.name); + f2fs_fname_crypto_free_buffer(&pstr); + kunmap(cpage); + page_cache_release(cpage); + return ERR_PTR(res); +} -release_out: - release_orphan_inode(sbi); -out: - handle_failed_inode(inode); - return err; +void kfree_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + char *s = nd_get_link(nd); + if (!IS_ERR(s)) + kfree(s); } +const struct inode_operations f2fs_encrypted_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = f2fs_encrypted_follow_link, + .put_link = kfree_put_link, + .getattr = f2fs_getattr, + .setattr = f2fs_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = f2fs_listxattr, + .removexattr = generic_removexattr, +}; +#endif + const struct inode_operations f2fs_dir_inode_operations = { .create = f2fs_create, .lookup = f2fs_lookup, @@ -699,7 +1051,7 @@ const struct inode_operations f2fs_dir_inode_operations = { const struct inode_operations f2fs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .follow_link = f2fs_follow_link, .put_link = page_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 44b8afef43d9..413d7724bdf9 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -19,6 +19,7 @@ #include "f2fs.h" #include "node.h" #include "segment.h" +#include "trace.h" #include #define on_build_free_nids(nmi) mutex_is_locked(&nm_i->build_lock) @@ -31,22 +32,46 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct sysinfo val; + unsigned long avail_ram; unsigned long mem_size = 0; bool res = false; si_meminfo(&val); - /* give 25%, 25%, 50% memory for each components respectively */ + + /* only uses low memory */ + avail_ram = val.totalram - val.totalhigh; + + /* + * give 25%, 25%, 50%, 50%, 50% memory for each components respectively + */ if (type == FREE_NIDS) { - mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >> 12; - res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 2); + mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >> + PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == NAT_ENTRIES) { - mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> 12; - res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 2); + mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> + PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == DIRTY_DENTS) { if (sbi->sb->s_bdi->dirty_exceeded) return false; mem_size = get_pages(sbi, F2FS_DIRTY_DENTS); - res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 1); + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else if (type == INO_ENTRIES) { + int i; + + for (i = 0; i <= UPDATE_INO; i++) + mem_size += (sbi->im[i].ino_num * + sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else if (type == EXTENT_CACHE) { + mem_size = (sbi->total_ext_tree * sizeof(struct extent_tree) + + atomic_read(&sbi->total_ext_node) * + sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else { + if (sbi->sb->s_bdi->dirty_exceeded) + return false; } return res; } @@ -131,20 +156,16 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, if (get_nat_flag(ne, IS_DIRTY)) return; -retry: + head = radix_tree_lookup(&nm_i->nat_set_root, set); if (!head) { - head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_ATOMIC); + head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_NOFS); INIT_LIST_HEAD(&head->entry_list); INIT_LIST_HEAD(&head->set_list); head->set = set; head->entry_cnt = 0; - - if (radix_tree_insert(&nm_i->nat_set_root, set, head)) { - cond_resched(); - goto retry; - } + f2fs_radix_tree_insert(&nm_i->nat_set_root, set, head); } list_move_tail(&ne->list, &head->entry_list); nm_i->dirty_nat_cnt++; @@ -155,7 +176,7 @@ retry: static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i, struct nat_entry *ne) { - nid_t set = ne->ni.nid / NAT_ENTRY_PER_BLOCK; + nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid); struct nat_entry_set *head; head = radix_tree_lookup(&nm_i->nat_set_root, set); @@ -174,32 +195,35 @@ static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i, start, nr); } -bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) +int need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; - bool is_cp = true; + bool need = false; - read_lock(&nm_i->nat_tree_lock); + down_read(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); - if (e && !get_nat_flag(e, IS_CHECKPOINTED)) - is_cp = false; - read_unlock(&nm_i->nat_tree_lock); - return is_cp; + if (e) { + if (!get_nat_flag(e, IS_CHECKPOINTED) && + !get_nat_flag(e, HAS_FSYNCED_INODE)) + need = true; + } + up_read(&nm_i->nat_tree_lock); + return need; } -bool has_fsynced_inode(struct f2fs_sb_info *sbi, nid_t ino) +bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; - bool fsynced = false; + bool is_cp = true; - read_lock(&nm_i->nat_tree_lock); - e = __lookup_nat_cache(nm_i, ino); - if (e && get_nat_flag(e, HAS_FSYNCED_INODE)) - fsynced = true; - read_unlock(&nm_i->nat_tree_lock); - return fsynced; + down_read(&nm_i->nat_tree_lock); + e = __lookup_nat_cache(nm_i, nid); + if (e && !get_nat_flag(e, IS_CHECKPOINTED)) + is_cp = false; + up_read(&nm_i->nat_tree_lock); + return is_cp; } bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) @@ -208,13 +232,13 @@ bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) struct nat_entry *e; bool need_update = true; - read_lock(&nm_i->nat_tree_lock); + down_read(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, ino); if (e && get_nat_flag(e, HAS_LAST_FSYNC) && (get_nat_flag(e, IS_CHECKPOINTED) || get_nat_flag(e, HAS_FSYNCED_INODE))) need_update = false; - read_unlock(&nm_i->nat_tree_lock); + up_read(&nm_i->nat_tree_lock); return need_update; } @@ -222,13 +246,8 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) { struct nat_entry *new; - new = kmem_cache_alloc(nat_entry_slab, GFP_ATOMIC); - if (!new) - return NULL; - if (radix_tree_insert(&nm_i->nat_root, nid, new)) { - kmem_cache_free(nat_entry_slab, new); - return NULL; - } + new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS); + f2fs_radix_tree_insert(&nm_i->nat_root, nid, new); memset(new, 0, sizeof(struct nat_entry)); nat_set_nid(new, nid); nat_reset_flag(new); @@ -241,18 +260,14 @@ static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, struct f2fs_nat_entry *ne) { struct nat_entry *e; -retry: - write_lock(&nm_i->nat_tree_lock); + + down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); if (!e) { e = grab_nat_entry(nm_i, nid); - if (!e) { - write_unlock(&nm_i->nat_tree_lock); - goto retry; - } node_info_from_raw_nat(&e->ni, ne); } - write_unlock(&nm_i->nat_tree_lock); + up_write(&nm_i->nat_tree_lock); } static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, @@ -260,16 +275,12 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; -retry: - write_lock(&nm_i->nat_tree_lock); + + down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, ni->nid); if (!e) { e = grab_nat_entry(nm_i, ni->nid); - if (!e) { - write_unlock(&nm_i->nat_tree_lock); - goto retry; - } - e->ni = *ni; + copy_node_info(&e->ni, ni); f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR); } else if (new_blkaddr == NEW_ADDR) { /* @@ -277,7 +288,7 @@ retry: * previous nat entry can be remained in nat cache. * So, reinitialize it with new information. */ - e->ni = *ni; + copy_node_info(&e->ni, ni); f2fs_bug_on(sbi, ni->blk_addr != NULL_ADDR); } @@ -295,6 +306,10 @@ retry: if (nat_get_blkaddr(e) != NEW_ADDR && new_blkaddr == NULL_ADDR) { unsigned char version = nat_get_version(e); nat_set_version(e, inc_node_version(version)); + + /* in order to reuse the nid */ + if (nm_i->next_scan_nid > ni->nid) + nm_i->next_scan_nid = ni->nid; } /* change address */ @@ -304,23 +319,24 @@ retry: __set_nat_cache_dirty(nm_i, e); /* update fsync_mark if its inode nat entry is still alive */ - e = __lookup_nat_cache(nm_i, ni->ino); + if (ni->nid != ni->ino) + e = __lookup_nat_cache(nm_i, ni->ino); if (e) { if (fsync_done && ni->nid == ni->ino) set_nat_flag(e, HAS_FSYNCED_INODE, true); set_nat_flag(e, HAS_LAST_FSYNC, fsync_done); } - write_unlock(&nm_i->nat_tree_lock); + up_write(&nm_i->nat_tree_lock); } int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink) { struct f2fs_nm_info *nm_i = NM_I(sbi); + int nr = nr_shrink; - if (available_free_memory(sbi, NAT_ENTRIES)) + if (!down_write_trylock(&nm_i->nat_tree_lock)) return 0; - write_lock(&nm_i->nat_tree_lock); while (nr_shrink && !list_empty(&nm_i->nat_entries)) { struct nat_entry *ne; ne = list_first_entry(&nm_i->nat_entries, @@ -328,8 +344,8 @@ int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink) __del_from_nat_cache(nm_i, ne); nr_shrink--; } - write_unlock(&nm_i->nat_tree_lock); - return nr_shrink; + up_write(&nm_i->nat_tree_lock); + return nr - nr_shrink; } /* @@ -347,21 +363,22 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) struct nat_entry *e; int i; - memset(&ne, 0, sizeof(struct f2fs_nat_entry)); ni->nid = nid; /* Check nat cache */ - read_lock(&nm_i->nat_tree_lock); + down_read(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); if (e) { ni->ino = nat_get_ino(e); ni->blk_addr = nat_get_blkaddr(e); ni->version = nat_get_version(e); } - read_unlock(&nm_i->nat_tree_lock); + up_read(&nm_i->nat_tree_lock); if (e) return; + memset(&ne, 0, sizeof(struct f2fs_nat_entry)); + /* Check current segment summary */ mutex_lock(&curseg->curseg_mutex); i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0); @@ -472,7 +489,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct page *npage[4]; - struct page *parent; + struct page *parent = NULL; int offset[4]; unsigned int noffset[4]; nid_t nids[4]; @@ -489,6 +506,14 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) if (IS_ERR(npage[0])) return PTR_ERR(npage[0]); } + + /* if inline_data is set, should not report any block indices */ + if (f2fs_has_inline_data(dn->inode) && index) { + err = -ENOENT; + f2fs_put_page(npage[0], 1); + goto release_out; + } + parent = npage[0]; if (level != 0) nids[1] = get_nid(parent, offset[0], true); @@ -586,7 +611,7 @@ static void truncate_node(struct dnode_of_data *dn) } invalidate: clear_node_page_dirty(dn->node_page); - F2FS_SET_SB_DIRT(sbi); + set_sbi_flag(sbi, SBI_IS_DIRTY); f2fs_put_page(dn->node_page, 1); @@ -877,17 +902,20 @@ int truncate_xattr_node(struct inode *inode, struct page *page) * Caller should grab and release a rwsem by calling f2fs_lock_op() and * f2fs_unlock_op(). */ -void remove_inode_page(struct inode *inode) +int remove_inode_page(struct inode *inode) { struct dnode_of_data dn; + int err; set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); - if (get_dnode_of_data(&dn, 0, LOOKUP_NODE)) - return; + err = get_dnode_of_data(&dn, 0, LOOKUP_NODE); + if (err) + return err; - if (truncate_xattr_node(inode, dn.inode_page)) { + err = truncate_xattr_node(inode, dn.inode_page); + if (err) { f2fs_put_dnode(&dn); - return; + return err; } /* remove potential inline_data blocks */ @@ -901,6 +929,7 @@ void remove_inode_page(struct inode *inode) /* will put inode & node pages */ truncate_node(&dn); + return 0; } struct page *new_inode_page(struct inode *inode) @@ -970,25 +999,32 @@ fail: /* * Caller should do after getting the following values. * 0: f2fs_put_page(page, 0) - * LOCKED_PAGE: f2fs_put_page(page, 1) - * error: nothing + * LOCKED_PAGE or error: f2fs_put_page(page, 1) */ static int read_node_page(struct page *page, int rw) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); struct node_info ni; + struct f2fs_io_info fio = { + .sbi = sbi, + .type = NODE, + .rw = rw, + .page = page, + .encrypted_page = NULL, + }; get_node_info(sbi, page->index, &ni); if (unlikely(ni.blk_addr == NULL_ADDR)) { - f2fs_put_page(page, 1); + ClearPageUptodate(page); return -ENOENT; } if (PageUptodate(page)) return LOCKED_PAGE; - return f2fs_submit_page_bio(sbi, page, ni.blk_addr, rw); + fio.blk_addr = ni.blk_addr; + return f2fs_submit_page_bio(&fio); } /* @@ -1011,10 +1047,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) return; err = read_node_page(apage, READA); - if (err == 0) - f2fs_put_page(apage, 0); - else if (err == LOCKED_PAGE) - f2fs_put_page(apage, 1); + f2fs_put_page(apage, err ? 1 : 0); } struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) @@ -1027,13 +1060,15 @@ repeat: return ERR_PTR(-ENOMEM); err = read_node_page(page, READ_SYNC); - if (err < 0) + if (err < 0) { + f2fs_put_page(page, 1); return ERR_PTR(err); - else if (err == LOCKED_PAGE) - goto got_it; + } else if (err != LOCKED_PAGE) { + lock_page(page); + } - lock_page(page); if (unlikely(!PageUptodate(page) || nid != nid_of_node(page))) { + ClearPageUptodate(page); f2fs_put_page(page, 1); return ERR_PTR(-EIO); } @@ -1041,7 +1076,7 @@ repeat: f2fs_put_page(page, 1); goto repeat; } -got_it: + mark_page_accessed(page); return page; } @@ -1067,10 +1102,12 @@ repeat: return ERR_PTR(-ENOMEM); err = read_node_page(page, READ_SYNC); - if (err < 0) + if (err < 0) { + f2fs_put_page(page, 1); return ERR_PTR(err); - else if (err == LOCKED_PAGE) + } else if (err == LOCKED_PAGE) { goto page_hit; + } blk_start_plug(&plug); @@ -1096,6 +1133,7 @@ page_hit: f2fs_put_page(page, 1); return ERR_PTR(-EIO); } + mark_page_accessed(page); return page; } @@ -1182,13 +1220,9 @@ continue_unlock: /* called by fsync() */ if (ino && IS_DNODE(page)) { set_fsync_mark(page, 1); - if (IS_INODE(page)) { - if (!is_checkpointed_node(sbi, ino) && - !has_fsynced_inode(sbi, ino)) - set_dentry_mark(page, 1); - else - set_dentry_mark(page, 0); - } + if (IS_INODE(page)) + set_dentry_mark(page, + need_dentry_mark(sbi, ino)); nwritten++; } else { set_fsync_mark(page, 0); @@ -1269,16 +1303,18 @@ static int f2fs_write_node_page(struct page *page, { struct f2fs_sb_info *sbi = F2FS_P_SB(page); nid_t nid; - block_t new_addr; struct node_info ni; struct f2fs_io_info fio = { + .sbi = sbi, .type = NODE, .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + .page = page, + .encrypted_page = NULL, }; trace_f2fs_writepage(page, NODE); - if (unlikely(sbi->por_doing)) + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; @@ -1289,25 +1325,35 @@ static int f2fs_write_node_page(struct page *page, nid = nid_of_node(page); f2fs_bug_on(sbi, page->index != nid); + if (wbc->for_reclaim) { + if (!down_read_trylock(&sbi->node_write)) + goto redirty_out; + } else { + down_read(&sbi->node_write); + } + get_node_info(sbi, nid, &ni); /* This page is already truncated */ if (unlikely(ni.blk_addr == NULL_ADDR)) { + ClearPageUptodate(page); dec_page_count(sbi, F2FS_DIRTY_NODES); + up_read(&sbi->node_write); unlock_page(page); return 0; } - if (wbc->for_reclaim) - goto redirty_out; - - down_read(&sbi->node_write); set_page_writeback(page); - write_node_page(sbi, page, &fio, nid, ni.blk_addr, &new_addr); - set_node_addr(sbi, &ni, new_addr, is_fsync_dnode(page)); + fio.blk_addr = ni.blk_addr; + write_node_page(nid, &fio); + set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page)); dec_page_count(sbi, F2FS_DIRTY_NODES); up_read(&sbi->node_write); unlock_page(page); + + if (wbc->for_reclaim) + f2fs_submit_merged_bio(sbi, NODE, WRITE); + return 0; redirty_out: @@ -1350,26 +1396,12 @@ static int f2fs_set_node_page_dirty(struct page *page) __set_page_dirty_nobuffers(page); inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES); SetPagePrivate(page); + f2fs_trace_pid(page); return 1; } return 0; } -static void f2fs_invalidate_node_page(struct page *page, unsigned int offset, - unsigned int length) -{ - struct inode *inode = page->mapping->host; - if (PageDirty(page)) - dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_NODES); - ClearPagePrivate(page); -} - -static int f2fs_release_node_page(struct page *page, gfp_t wait) -{ - ClearPagePrivate(page); - return 1; -} - /* * Structure of the f2fs node operations */ @@ -1377,8 +1409,8 @@ const struct address_space_operations f2fs_node_aops = { .writepage = f2fs_write_node_page, .writepages = f2fs_write_node_pages, .set_page_dirty = f2fs_set_node_page_dirty, - .invalidatepage = f2fs_invalidate_node_page, - .releasepage = f2fs_release_node_page, + .invalidatepage = f2fs_invalidate_page, + .releasepage = f2fs_release_page, }; static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, @@ -1410,13 +1442,13 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) if (build) { /* do not add allocated nids */ - read_lock(&nm_i->nat_tree_lock); + down_read(&nm_i->nat_tree_lock); ne = __lookup_nat_cache(nm_i, nid); if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || nat_get_blkaddr(ne) != NULL_ADDR)) allocated = true; - read_unlock(&nm_i->nat_tree_lock); + up_read(&nm_i->nat_tree_lock); if (allocated) return 0; } @@ -1425,15 +1457,22 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) i->nid = nid; i->state = NID_NEW; + if (radix_tree_preload(GFP_NOFS)) { + kmem_cache_free(free_nid_slab, i); + return 0; + } + spin_lock(&nm_i->free_nid_list_lock); if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) { spin_unlock(&nm_i->free_nid_list_lock); + radix_tree_preload_end(); kmem_cache_free(free_nid_slab, i); return 0; } list_add_tail(&i->list, &nm_i->free_nid_list); nm_i->fcnt++; spin_unlock(&nm_i->free_nid_list_lock); + radix_tree_preload_end(); return 1; } @@ -1492,7 +1531,8 @@ static void build_free_nids(struct f2fs_sb_info *sbi) return; /* readahead nat pages to be scanned */ - ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, META_NAT); + ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, + META_NAT, true); while (1) { struct page *page = get_current_nat_page(sbi, nid); @@ -1504,7 +1544,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) if (unlikely(nid >= nm_i->max_nid)) nid = 0; - if (i++ == FREE_NID_PAGES) + if (++i >= FREE_NID_PAGES) break; } @@ -1522,6 +1562,9 @@ static void build_free_nids(struct f2fs_sb_info *sbi) remove_free_nid(nm_i, nid); } mutex_unlock(&curseg->curseg_mutex); + + ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid), + nm_i->ra_nid_pages, META_NAT, false); } /* @@ -1541,6 +1584,8 @@ retry: /* We should not use stale free nids created by build_free_nids */ if (nm_i->fcnt && !on_build_free_nids(nm_i)) { + struct node_info ni; + f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list)); list_for_each_entry(i, &nm_i->free_nid_list, list) if (i->state == NID_NEW) @@ -1551,6 +1596,13 @@ retry: i->state = NID_ALLOC; nm_i->fcnt--; spin_unlock(&nm_i->free_nid_list_lock); + + /* check nid is allocated already */ + get_node_info(sbi, *nid, &ni); + if (ni.blk_addr != NULL_ADDR) { + alloc_nid_done(sbi, *nid); + goto retry; + } return true; } spin_unlock(&nm_i->free_nid_list_lock); @@ -1607,6 +1659,32 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) kmem_cache_free(free_nid_slab, i); } +int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct free_nid *i, *next; + int nr = nr_shrink; + + if (!mutex_trylock(&nm_i->build_lock)) + return 0; + + spin_lock(&nm_i->free_nid_list_lock); + list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) { + if (nr_shrink <= 0 || nm_i->fcnt <= NAT_ENTRY_PER_BLOCK) + break; + if (i->state == NID_ALLOC) + continue; + __del_from_free_nid_list(nm_i, i); + kmem_cache_free(free_nid_slab, i); + nm_i->fcnt--; + nr_shrink--; + } + spin_unlock(&nm_i->free_nid_list_lock); + mutex_unlock(&nm_i->build_lock); + + return nr - nr_shrink; +} + void recover_inline_xattr(struct inode *inode, struct page *page) { void *src_addr, *dst_addr; @@ -1714,80 +1792,41 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) return 0; } -/* - * ra_sum_pages() merge contiguous pages into one bio and submit. - * these pre-read pages are allocated in bd_inode's mapping tree. - */ -static int ra_sum_pages(struct f2fs_sb_info *sbi, struct page **pages, - int start, int nrpages) -{ - struct inode *inode = sbi->sb->s_bdev->bd_inode; - struct address_space *mapping = inode->i_mapping; - int i, page_idx = start; - struct f2fs_io_info fio = { - .type = META, - .rw = READ_SYNC | REQ_META | REQ_PRIO - }; - - for (i = 0; page_idx < start + nrpages; page_idx++, i++) { - /* alloc page in bd_inode for reading node summary info */ - pages[i] = grab_cache_page(mapping, page_idx); - if (!pages[i]) - break; - f2fs_submit_page_mbio(sbi, pages[i], page_idx, &fio); - } - - f2fs_submit_merged_bio(sbi, META, READ); - return i; -} - int restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum) { struct f2fs_node *rn; struct f2fs_summary *sum_entry; - struct inode *inode = sbi->sb->s_bdev->bd_inode; block_t addr; int bio_blocks = MAX_BIO_BLOCKS(sbi); - struct page *pages[bio_blocks]; - int i, idx, last_offset, nrpages, err = 0; + int i, idx, last_offset, nrpages; /* scan the node segment */ last_offset = sbi->blocks_per_seg; addr = START_BLOCK(sbi, segno); sum_entry = &sum->entries[0]; - for (i = 0; !err && i < last_offset; i += nrpages, addr += nrpages) { + for (i = 0; i < last_offset; i += nrpages, addr += nrpages) { nrpages = min(last_offset - i, bio_blocks); /* readahead node pages */ - nrpages = ra_sum_pages(sbi, pages, addr, nrpages); - if (!nrpages) - return -ENOMEM; + ra_meta_pages(sbi, addr, nrpages, META_POR, true); - for (idx = 0; idx < nrpages; idx++) { - if (err) - goto skip; + for (idx = addr; idx < addr + nrpages; idx++) { + struct page *page = get_tmp_page(sbi, idx); - lock_page(pages[idx]); - if (unlikely(!PageUptodate(pages[idx]))) { - err = -EIO; - } else { - rn = F2FS_NODE(pages[idx]); - sum_entry->nid = rn->footer.nid; - sum_entry->version = 0; - sum_entry->ofs_in_node = 0; - sum_entry++; - } - unlock_page(pages[idx]); -skip: - page_cache_release(pages[idx]); + rn = F2FS_NODE(page); + sum_entry->nid = rn->footer.nid; + sum_entry->version = 0; + sum_entry->ofs_in_node = 0; + sum_entry++; + f2fs_put_page(page, 1); } - invalidate_mapping_pages(inode->i_mapping, addr, + invalidate_mapping_pages(META_MAPPING(sbi), addr, addr + nrpages); } - return err; + return 0; } static void remove_nats_in_journal(struct f2fs_sb_info *sbi) @@ -1804,21 +1843,15 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) nid_t nid = le32_to_cpu(nid_in_journal(sum, i)); raw_ne = nat_in_journal(sum, i); -retry: - write_lock(&nm_i->nat_tree_lock); - ne = __lookup_nat_cache(nm_i, nid); - if (ne) - goto found; - ne = grab_nat_entry(nm_i, nid); + down_write(&nm_i->nat_tree_lock); + ne = __lookup_nat_cache(nm_i, nid); if (!ne) { - write_unlock(&nm_i->nat_tree_lock); - goto retry; + ne = grab_nat_entry(nm_i, nid); + node_info_from_raw_nat(&ne->ni, &raw_ne); } - node_info_from_raw_nat(&ne->ni, &raw_ne); -found: __set_nat_cache_dirty(nm_i, ne); - write_unlock(&nm_i->nat_tree_lock); + up_write(&nm_i->nat_tree_lock); } update_nats_in_cursum(sum, -i); mutex_unlock(&curseg->curseg_mutex); @@ -1852,6 +1885,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, struct f2fs_nat_block *nat_blk; struct nat_entry *ne, *cur; struct page *page = NULL; + struct f2fs_nm_info *nm_i = NM_I(sbi); /* * there are two steps to flush nat entries: @@ -1889,10 +1923,10 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, } raw_nat_from_node_info(raw_ne, &ne->ni); - write_lock(&NM_I(sbi)->nat_tree_lock); + down_write(&NM_I(sbi)->nat_tree_lock); nat_reset_flag(ne); __clear_nat_cache_dirty(NM_I(sbi), ne); - write_unlock(&NM_I(sbi)->nat_tree_lock); + up_write(&NM_I(sbi)->nat_tree_lock); if (nat_get_blkaddr(ne) == NULL_ADDR) add_free_nid(sbi, nid, false); @@ -1903,10 +1937,12 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, else f2fs_put_page(page, 1); - if (!set->entry_cnt) { - radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set); - kmem_cache_free(nat_entry_set_slab, set); - } + f2fs_bug_on(sbi, set->entry_cnt); + + down_write(&nm_i->nat_tree_lock); + radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set); + up_write(&nm_i->nat_tree_lock); + kmem_cache_free(nat_entry_set_slab, set); } /* @@ -1917,12 +1953,14 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_summary_block *sum = curseg->sum_blk; - struct nat_entry_set *setvec[NATVEC_SIZE]; + struct nat_entry_set *setvec[SETVEC_SIZE]; struct nat_entry_set *set, *tmp; unsigned int found; nid_t set_idx = 0; LIST_HEAD(sets); + if (!nm_i->dirty_nat_cnt) + return; /* * if there are no enough space in journal to store dirty nat * entries, remove all entries from journal and merge them @@ -1931,17 +1969,16 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL)) remove_nats_in_journal(sbi); - if (!nm_i->dirty_nat_cnt) - return; - + down_write(&nm_i->nat_tree_lock); while ((found = __gang_lookup_nat_set(nm_i, - set_idx, NATVEC_SIZE, setvec))) { + set_idx, SETVEC_SIZE, setvec))) { unsigned idx; set_idx = setvec[found - 1]->set + 1; for (idx = 0; idx < found; idx++) __adjust_nat_entry_set(setvec[idx], &sets, MAX_NAT_JENTRIES(sum)); } + up_write(&nm_i->nat_tree_lock); /* flush dirty nats in nat entry set */ list_for_each_entry_safe(set, tmp, &sets, set_list) @@ -1970,16 +2007,17 @@ static int init_node_manager(struct f2fs_sb_info *sbi) nm_i->fcnt = 0; nm_i->nat_cnt = 0; nm_i->ram_thresh = DEF_RAM_THRESHOLD; + nm_i->ra_nid_pages = DEF_RA_NID_PAGES; INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); INIT_LIST_HEAD(&nm_i->free_nid_list); - INIT_RADIX_TREE(&nm_i->nat_root, GFP_ATOMIC); - INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_ATOMIC); + INIT_RADIX_TREE(&nm_i->nat_root, GFP_NOIO); + INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_NOIO); INIT_LIST_HEAD(&nm_i->nat_entries); mutex_init(&nm_i->build_lock); spin_lock_init(&nm_i->free_nid_list_lock); - rwlock_init(&nm_i->nat_tree_lock); + init_rwsem(&nm_i->nat_tree_lock); nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP); @@ -2015,6 +2053,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i, *next_i; struct nat_entry *natvec[NATVEC_SIZE]; + struct nat_entry_set *setvec[SETVEC_SIZE]; nid_t nid = 0; unsigned int found; @@ -2035,16 +2074,32 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) spin_unlock(&nm_i->free_nid_list_lock); /* destroy nat cache */ - write_lock(&nm_i->nat_tree_lock); + down_write(&nm_i->nat_tree_lock); while ((found = __gang_lookup_nat_cache(nm_i, nid, NATVEC_SIZE, natvec))) { unsigned idx; + nid = nat_get_nid(natvec[found - 1]) + 1; for (idx = 0; idx < found; idx++) __del_from_nat_cache(nm_i, natvec[idx]); } f2fs_bug_on(sbi, nm_i->nat_cnt); - write_unlock(&nm_i->nat_tree_lock); + + /* destroy nat set cache */ + nid = 0; + while ((found = __gang_lookup_nat_set(nm_i, + nid, SETVEC_SIZE, setvec))) { + unsigned idx; + + nid = setvec[found - 1]->set + 1; + for (idx = 0; idx < found; idx++) { + /* entry_cnt is not zero, when cp_error was occurred */ + f2fs_bug_on(sbi, !list_empty(&setvec[idx]->entry_list)); + radix_tree_delete(&nm_i->nat_set_root, setvec[idx]->set); + kmem_cache_free(nat_entry_set_slab, setvec[idx]); + } + } + up_write(&nm_i->nat_tree_lock); kfree(nm_i->nat_bitmap); sbi->nm_info = NULL; @@ -2061,17 +2116,17 @@ int __init create_node_manager_caches(void) free_nid_slab = f2fs_kmem_cache_create("free_nid", sizeof(struct free_nid)); if (!free_nid_slab) - goto destory_nat_entry; + goto destroy_nat_entry; nat_entry_set_slab = f2fs_kmem_cache_create("nat_entry_set", sizeof(struct nat_entry_set)); if (!nat_entry_set_slab) - goto destory_free_nid; + goto destroy_free_nid; return 0; -destory_free_nid: +destroy_free_nid: kmem_cache_destroy(free_nid_slab); -destory_nat_entry: +destroy_nat_entry: kmem_cache_destroy(nat_entry_slab); fail: return -ENOMEM; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 8d5e6e0dd840..e4fffd2d98c4 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -14,9 +14,11 @@ /* node block offset on the NAT area dedicated to the given start node id */ #define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK) -/* # of pages to perform readahead before building free nids */ +/* # of pages to perform synchronous readahead before building free nids */ #define FREE_NID_PAGES 4 +#define DEF_RA_NID_PAGES 4 /* # of nid pages to be readaheaded */ + /* maximum readahead size for node during getting data blocks */ #define MAX_RA_NODE 128 @@ -25,10 +27,19 @@ /* vector size for gang look-up from nat cache that consists of radix tree */ #define NATVEC_SIZE 64 +#define SETVEC_SIZE 32 /* return value for read_node_page */ #define LOCKED_PAGE 1 +/* For flag in struct node_info */ +enum { + IS_CHECKPOINTED, /* is it checkpointed before? */ + HAS_FSYNCED_INODE, /* is the inode fsynced before? */ + HAS_LAST_FSYNC, /* has the latest node fsync mark? */ + IS_DIRTY, /* this nat entry is dirty? */ +}; + /* * For node information */ @@ -37,18 +48,11 @@ struct node_info { nid_t ino; /* inode number of the node's owner */ block_t blk_addr; /* block address of the node */ unsigned char version; /* version of the node */ -}; - -enum { - IS_CHECKPOINTED, /* is it checkpointed before? */ - HAS_FSYNCED_INODE, /* is the inode fsynced before? */ - HAS_LAST_FSYNC, /* has the latest node fsync mark? */ - IS_DIRTY, /* this nat entry is dirty? */ + unsigned char flag; /* for node information bits */ }; struct nat_entry { struct list_head list; /* for clean or dirty nat list */ - unsigned char flag; /* for node information bits */ struct node_info ni; /* in-memory node information */ }; @@ -63,20 +67,30 @@ struct nat_entry { #define inc_node_version(version) (++version) +static inline void copy_node_info(struct node_info *dst, + struct node_info *src) +{ + dst->nid = src->nid; + dst->ino = src->ino; + dst->blk_addr = src->blk_addr; + dst->version = src->version; + /* should not copy flag here */ +} + static inline void set_nat_flag(struct nat_entry *ne, unsigned int type, bool set) { unsigned char mask = 0x01 << type; if (set) - ne->flag |= mask; + ne->ni.flag |= mask; else - ne->flag &= ~mask; + ne->ni.flag &= ~mask; } static inline bool get_nat_flag(struct nat_entry *ne, unsigned int type) { unsigned char mask = 0x01 << type; - return ne->flag & mask; + return ne->ni.flag & mask; } static inline void nat_reset_flag(struct nat_entry *ne) @@ -106,7 +120,10 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne, enum mem_type { FREE_NIDS, /* indicates the free nid list */ NAT_ENTRIES, /* indicates the cached nat entry */ - DIRTY_DENTS /* indicates dirty dentry pages */ + DIRTY_DENTS, /* indicates dirty dentry pages */ + INO_ENTRIES, /* indicates inode entries */ + EXTENT_CACHE, /* indicates extent cache */ + BASE_CHECK, /* check kernel status */ }; struct nat_entry_set { @@ -192,21 +209,26 @@ static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid) { unsigned int block_off = NAT_BLOCK_OFFSET(start_nid); - if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) - f2fs_clear_bit(block_off, nm_i->nat_bitmap); - else - f2fs_set_bit(block_off, nm_i->nat_bitmap); + f2fs_change_bit(block_off, nm_i->nat_bitmap); } static inline void fill_node_footer(struct page *page, nid_t nid, nid_t ino, unsigned int ofs, bool reset) { struct f2fs_node *rn = F2FS_NODE(page); + unsigned int old_flag = 0; + if (reset) memset(rn, 0, sizeof(*rn)); + else + old_flag = le32_to_cpu(rn->footer.flag); + rn->footer.nid = cpu_to_le32(nid); rn->footer.ino = cpu_to_le32(ino); - rn->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT); + + /* should remain old flag bits such as COLD_BIT_SHIFT */ + rn->footer.flag = cpu_to_le32((ofs << OFFSET_BIT_SHIFT) | + (old_flag & OFFSET_BIT_MASK)); } static inline void copy_node_footer(struct page *dst, struct page *src) @@ -323,28 +345,6 @@ static inline nid_t get_nid(struct page *p, int off, bool i) * - Mark cold node blocks in their node footer * - Mark cold data pages in page cache */ -static inline int is_file(struct inode *inode, int type) -{ - return F2FS_I(inode)->i_advise & type; -} - -static inline void set_file(struct inode *inode, int type) -{ - F2FS_I(inode)->i_advise |= type; -} - -static inline void clear_file(struct inode *inode, int type) -{ - F2FS_I(inode)->i_advise &= ~type; -} - -#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT) -#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT) -#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT) -#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT) -#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT) -#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT) - static inline int is_cold_data(struct page *page) { return PageChecked(page); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index ebd013225788..6a3f04fa34e3 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -83,6 +83,11 @@ static int recover_dentry(struct inode *inode, struct page *ipage) goto out; } + if (file_enc_name(inode)) { + iput(dir); + return 0; + } + name.len = le32_to_cpu(raw_inode->i_namelen); name.name = raw_inode->i_name; @@ -93,10 +98,9 @@ static int recover_dentry(struct inode *inode, struct page *ipage) } retry: de = f2fs_find_entry(dir, &name, &page); - if (de && inode->i_ino == le32_to_cpu(de->ino)) { - clear_inode_flag(F2FS_I(inode), FI_INC_LINK); + if (de && inode->i_ino == le32_to_cpu(de->ino)) goto out_unmap_put; - } + if (de) { einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino)); if (IS_ERR(einode)) { @@ -111,11 +115,11 @@ retry: iput(einode); goto out_unmap_put; } - f2fs_delete_entry(de, page, einode); + f2fs_delete_entry(de, page, dir, einode); iput(einode); goto retry; } - err = __f2fs_add_link(dir, &name, inode); + err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode); if (err) goto out_err; @@ -129,7 +133,7 @@ retry: goto out; out_unmap_put: - kunmap(page); + f2fs_dentry_kunmap(dir, page); f2fs_put_page(page, 0); out_err: iput(dir); @@ -144,6 +148,7 @@ out: static void recover_inode(struct inode *inode, struct page *page) { struct f2fs_inode *raw = F2FS_INODE(page); + char *name; inode->i_mode = le16_to_cpu(raw->i_mode); i_size_write(inode, le64_to_cpu(raw->i_size)); @@ -154,8 +159,13 @@ static void recover_inode(struct inode *inode, struct page *page) inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec); inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); + if (file_enc_name(inode)) + name = ""; + else + name = F2FS_INODE(page)->i_name; + f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s", - ino_of_node(page), F2FS_INODE(page)->i_name); + ino_of_node(page), name); } static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) @@ -170,13 +180,15 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); + ra_meta_pages(sbi, blkaddr, 1, META_POR, true); + while (1) { struct fsync_inode_entry *entry; - if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) + if (!is_valid_blkaddr(sbi, blkaddr, META_POR)) return 0; - page = get_meta_page_ra(sbi, blkaddr); + page = get_tmp_page(sbi, blkaddr); if (cp_ver != cpver_of_node(page)) break; @@ -185,11 +197,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) goto next; entry = get_fsync_inode(head, ino_of_node(page)); - if (entry) { - if (IS_INODE(page) && is_dent_dnode(page)) - set_inode_flag(F2FS_I(entry->inode), - FI_INC_LINK); - } else { + if (!entry) { if (IS_INODE(page) && is_dent_dnode(page)) { err = recover_inode_page(sbi, page); if (err) @@ -210,8 +218,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) if (IS_ERR(entry->inode)) { err = PTR_ERR(entry->inode); kmem_cache_free(fsync_entry_slab, entry); - if (err == -ENOENT) + if (err == -ENOENT) { + err = 0; goto next; + } break; } list_add_tail(&entry->list, head); @@ -227,6 +237,8 @@ next: /* check next segment */ blkaddr = next_blkaddr_of_node(page); f2fs_put_page(page, 1); + + ra_meta_pages_cond(sbi, blkaddr); } f2fs_put_page(page, 1); return err; @@ -252,6 +264,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, struct f2fs_summary_block *sum_node; struct f2fs_summary sum; struct page *sum_page, *node_page; + struct dnode_of_data tdn = *dn; nid_t ino, nid; struct inode *inode; unsigned int offset; @@ -279,17 +292,15 @@ got_it: /* Use the locked dnode page and inode */ nid = le32_to_cpu(sum.nid); if (dn->inode->i_ino == nid) { - struct dnode_of_data tdn = *dn; tdn.nid = nid; + if (!dn->inode_page_locked) + lock_page(dn->inode_page); tdn.node_page = dn->inode_page; tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); - truncate_data_blocks_range(&tdn, 1); - return 0; + goto truncate_out; } else if (dn->nid == nid) { - struct dnode_of_data tdn = *dn; tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); - truncate_data_blocks_range(&tdn, 1); - return 0; + goto truncate_out; } /* Get the node page */ @@ -313,18 +324,33 @@ got_it: bidx = start_bidx_of_node(offset, F2FS_I(inode)) + le16_to_cpu(sum.ofs_in_node); - if (ino != dn->inode->i_ino) { - truncate_hole(inode, bidx, bidx + 1); + /* + * if inode page is locked, unlock temporarily, but its reference + * count keeps alive. + */ + if (ino == dn->inode->i_ino && dn->inode_page_locked) + unlock_page(dn->inode_page); + + set_new_dnode(&tdn, inode, NULL, NULL, 0); + if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) + goto out; + + if (tdn.data_blkaddr == blkaddr) + truncate_data_blocks_range(&tdn, 1); + + f2fs_put_dnode(&tdn); +out: + if (ino != dn->inode->i_ino) iput(inode); - } else { - struct dnode_of_data tdn; - set_new_dnode(&tdn, inode, dn->inode_page, NULL, 0); - if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) - return 0; - if (tdn.data_blkaddr != NULL_ADDR) - truncate_data_blocks_range(&tdn, 1); - f2fs_put_page(tdn.node_page, 1); - } + else if (dn->inode_page_locked) + lock_page(dn->inode_page); + return 0; + +truncate_out: + if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr) + truncate_data_blocks_range(&tdn, 1); + if (dn->inode->i_ino == nid && !dn->inode_page_locked) + unlock_page(dn->inode_page); return 0; } @@ -334,7 +360,6 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, struct f2fs_inode_info *fi = F2FS_I(inode); unsigned int start, end; struct dnode_of_data dn; - struct f2fs_summary sum; struct node_info ni; int err = 0, recovered = 0; @@ -342,6 +367,10 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (IS_INODE(page)) { recover_inline_xattr(inode, page); } else if (f2fs_has_xattr_block(ofs_of_node(page))) { + /* + * Deprecated; xattr blocks should be found from cold log. + * But, we should remain this for backward compatibility. + */ recover_xattr_data(inode, page, blkaddr); goto out; } @@ -354,15 +383,11 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, start = start_bidx_of_node(ofs_of_node(page), fi); end = start + ADDRS_PER_PAGE(page, fi); - f2fs_lock_op(sbi); - set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, start, ALLOC_NODE); - if (err) { - f2fs_unlock_op(sbi); + if (err) goto out; - } f2fs_wait_on_page_writeback(dn.node_page, NODE); @@ -370,13 +395,36 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); f2fs_bug_on(sbi, ofs_of_node(dn.node_page) != ofs_of_node(page)); - for (; start < end; start++) { + for (; start < end; start++, dn.ofs_in_node++) { block_t src, dest; src = datablock_addr(dn.node_page, dn.ofs_in_node); dest = datablock_addr(page, dn.ofs_in_node); - if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR) { + /* skip recovering if dest is the same as src */ + if (src == dest) + continue; + + /* dest is invalid, just invalidate src block */ + if (dest == NULL_ADDR) { + truncate_data_blocks_range(&dn, 1); + continue; + } + + /* + * dest is reserved block, invalidate src block + * and then reserve one new block in dnode page. + */ + if (dest == NEW_ADDR) { + truncate_data_blocks_range(&dn, 1); + err = reserve_new_block(&dn); + f2fs_bug_on(sbi, err); + continue; + } + + /* dest is valid block, try to recover from src to dest */ + if (is_valid_blkaddr(sbi, dest, META_POR)) { + if (src == NULL_ADDR) { err = reserve_new_block(&dn); /* We should not get -ENOSPC */ @@ -388,18 +436,13 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (err) goto err; - set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); - /* write dummy data page */ - recover_data_page(sbi, NULL, &sum, src, dest); - update_extent_cache(dest, &dn); + f2fs_replace_block(sbi, &dn, src, dest, + ni.version, false); recovered++; } - dn.ofs_in_node++; } - /* write node page in place */ - set_summary(&sum, dn.nid, 0, 0); if (IS_INODE(dn.node_page)) sync_inode_page(&dn); @@ -409,7 +452,6 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, set_page_dirty(dn.node_page); err: f2fs_put_dnode(&dn); - f2fs_unlock_op(sbi); out: f2fs_msg(sbi->sb, KERN_NOTICE, "recover_data: ino = %lx, recovered = %d blocks, err = %d", @@ -433,10 +475,12 @@ static int recover_data(struct f2fs_sb_info *sbi, while (1) { struct fsync_inode_entry *entry; - if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) + if (!is_valid_blkaddr(sbi, blkaddr, META_POR)) break; - page = get_meta_page_ra(sbi, blkaddr); + ra_meta_pages_cond(sbi, blkaddr); + + page = get_tmp_page(sbi, blkaddr); if (cp_ver != cpver_of_node(page)) { f2fs_put_page(page, 1); @@ -496,14 +540,12 @@ int recover_fsync_data(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&inode_list); - /* step #1: find fsynced inode numbers */ - sbi->por_doing = true; - /* prevent checkpoint */ mutex_lock(&sbi->cp_mutex); blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); + /* step #1: find fsynced inode numbers */ err = find_fsync_dnodes(sbi, &inode_list); if (err) goto out; @@ -523,25 +565,34 @@ out: /* truncate meta pages to be used by the recovery */ truncate_inode_pages_range(META_MAPPING(sbi), - MAIN_BLKADDR(sbi) << PAGE_CACHE_SHIFT, -1); + (loff_t)MAIN_BLKADDR(sbi) << PAGE_CACHE_SHIFT, -1); if (err) { - truncate_inode_pages_final(NODE_MAPPING(sbi)); - truncate_inode_pages_final(META_MAPPING(sbi)); + truncate_inode_pages(NODE_MAPPING(sbi), 0); + truncate_inode_pages(META_MAPPING(sbi), 0); } - sbi->por_doing = false; + clear_sbi_flag(sbi, SBI_POR_DOING); if (err) { - discard_next_dnode(sbi, blkaddr); + bool invalidate = false; + + if (discard_next_dnode(sbi, blkaddr)) + invalidate = true; /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) sync_meta_pages(sbi, META, LONG_MAX); + + /* invalidate temporary meta page */ + if (invalidate) + invalidate_mapping_pages(META_MAPPING(sbi), + blkaddr, blkaddr); + set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); mutex_unlock(&sbi->cp_mutex); } else if (need_writecp) { struct cp_control cpc = { - .reason = CP_SYNC, + .reason = CP_RECOVERY, }; mutex_unlock(&sbi->cp_mutex); write_checkpoint(sbi, &cpc); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3c31221affe6..f77b3258454a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -14,12 +14,13 @@ #include #include #include -#include #include +#include #include "f2fs.h" #include "segment.h" #include "node.h" +#include "trace.h" #include #define __reverse_ffz(x) __reverse_ffs(~(x)) @@ -28,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. @@ -37,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; @@ -67,9 +87,9 @@ 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) @@ -77,8 +97,6 @@ static unsigned long __find_rev_next_bit(const unsigned long *addr, 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; @@ -88,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) @@ -103,20 +116,23 @@ 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); @@ -128,8 +144,6 @@ static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, 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; @@ -139,36 +153,33 @@ 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); @@ -179,6 +190,11 @@ void register_inmem_page(struct inode *inode, struct page *page) struct f2fs_inode_info *fi = F2FS_I(inode); struct inmem_pages *new; + f2fs_trace_pid(page); + + set_page_private(page, (unsigned long)ATOMIC_WRITTEN_PAGE); + SetPagePrivate(page); + new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS); /* add atomic page indices to the list */ @@ -189,43 +205,76 @@ void register_inmem_page(struct inode *inode, struct page *page) mutex_lock(&fi->inmem_lock); get_page(page); list_add_tail(&new->list, &fi->inmem_pages); + inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); mutex_unlock(&fi->inmem_lock); + + trace_f2fs_register_inmem_page(page, INMEM); } -void commit_inmem_pages(struct inode *inode, bool abort) +int commit_inmem_pages(struct inode *inode, bool abort) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); struct inmem_pages *cur, *tmp; bool submit_bio = false; struct f2fs_io_info fio = { + .sbi = sbi, .type = DATA, - .rw = WRITE_SYNC, + .rw = WRITE_SYNC | REQ_PRIO, + .encrypted_page = NULL, }; + int err = 0; - f2fs_balance_fs(sbi); - f2fs_lock_op(sbi); + /* + * The abort is true only when f2fs_evict_inode is called. + * Basically, the f2fs_evict_inode doesn't produce any data writes, so + * that we don't need to call f2fs_balance_fs. + * Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this + * inode becomes free by iget_locked in f2fs_iget. + */ + if (!abort) { + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); + } mutex_lock(&fi->inmem_lock); list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { lock_page(cur->page); - if (!abort && cur->page->mapping == inode->i_mapping) { - f2fs_wait_on_page_writeback(cur->page, DATA); - if (clear_page_dirty_for_io(cur->page)) - inode_dec_dirty_pages(inode); - do_write_data_page(cur->page, &fio); - submit_bio = true; + if (!abort) { + if (cur->page->mapping == inode->i_mapping) { + set_page_dirty(cur->page); + f2fs_wait_on_page_writeback(cur->page, DATA); + if (clear_page_dirty_for_io(cur->page)) + inode_dec_dirty_pages(inode); + trace_f2fs_commit_inmem_page(cur->page, INMEM); + fio.page = cur->page; + err = do_write_data_page(&fio); + 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); } + set_page_private(cur->page, 0); + ClearPagePrivate(cur->page); f2fs_put_page(cur->page, 1); + list_del(&cur->list); kmem_cache_free(inmem_entry_slab, cur); + dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); } - if (submit_bio) - f2fs_submit_merged_bio(sbi, DATA, WRITE); mutex_unlock(&fi->inmem_lock); - filemap_fdatawait_range(inode->i_mapping, 0, LLONG_MAX); - f2fs_unlock_op(sbi); + if (!abort) { + f2fs_unlock_op(sbi); + if (submit_bio) + f2fs_submit_merged_bio(sbi, DATA, WRITE); + } + return err; } /* @@ -240,15 +289,28 @@ 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); } } void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) { - /* check the # of cached NAT entries and prefree segments */ - if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK) || - excess_prefree_segs(sbi)) + /* try to shrink extent cache when there is no enough memory */ + if (!available_free_memory(sbi, EXTENT_CACHE)) + f2fs_shrink_extent_tree(sbi, EXTENT_CACHE_SHRINK_NUMBER); + + /* check the # of cached NAT entries */ + if (!available_free_memory(sbi, NAT_ENTRIES)) + try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK); + + if (!available_free_memory(sbi, FREE_NIDS)) + try_to_free_nids(sbi, NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES); + + /* 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) || + jiffies > sbi->cp_expires) f2fs_sync_fs(sbi->sb, true); } @@ -262,10 +324,12 @@ repeat: return 0; if (!llist_empty(&fcc->issue_list)) { - struct bio *bio = bio_alloc(GFP_NOIO, 0); + struct bio *bio; struct flush_cmd *cmd, *next; int ret; + bio = f2fs_bio_alloc(0); + fcc->dispatch_list = llist_del_all(&fcc->issue_list); fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); @@ -297,8 +361,15 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) if (test_opt(sbi, NOBARRIER)) return 0; - if (!test_opt(sbi, FLUSH_MERGE)) - return blkdev_issue_flush(sbi->sb->s_bdev, GFP_KERNEL, NULL); + if (!test_opt(sbi, FLUSH_MERGE)) { + struct bio *bio = f2fs_bio_alloc(0); + int ret; + + bio->bi_bdev = sbi->sb->s_bdev; + ret = submit_bio_wait(WRITE_FLUSH, bio); + bio_put(bio); + return ret; + } init_completion(&cmd.wait); @@ -427,69 +498,94 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, { sector_t start = SECTOR_FROM_BLOCK(blkstart); sector_t len = SECTOR_FROM_BLOCK(blklen); + struct seg_entry *se; + unsigned int offset; + block_t i; + + for (i = blkstart; i < blkstart + blklen; i++) { + se = get_seg_entry(sbi, GET_SEGNO(sbi, i)); + offset = GET_BLKOFF_FROM_SEG0(sbi, i); + + if (!f2fs_test_and_set_bit(offset, se->discard_map)) + sbi->discard_blks--; + } trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); } -void discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr) +bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr) { - if (f2fs_issue_discard(sbi, blkaddr, 1)) { - struct page *page = grab_meta_page(sbi, blkaddr); - /* zero-filled page */ - set_page_dirty(page); - f2fs_put_page(page, 1); + int err = -ENOTSUPP; + + if (test_opt(sbi, DISCARD)) { + struct seg_entry *se = get_seg_entry(sbi, + GET_SEGNO(sbi, blkaddr)); + unsigned int offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); + + if (f2fs_test_bit(offset, se->discard_map)) + return false; + + err = f2fs_issue_discard(sbi, blkaddr, 1); + } + + if (err) { + update_meta_page(sbi, NULL, blkaddr); + return true; } + return false; } -static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) +static void __add_discard_entry(struct f2fs_sb_info *sbi, + struct cp_control *cpc, struct seg_entry *se, + unsigned int start, unsigned int end) { struct list_head *head = &SM_I(sbi)->discard_list; - struct discard_entry *new; + struct discard_entry *new, *last; + + if (!list_empty(head)) { + last = list_last_entry(head, struct discard_entry, list); + if (START_BLOCK(sbi, cpc->trim_start) + start == + last->blkaddr + last->len) { + last->len += end - start; + goto done; + } + } + + new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); + INIT_LIST_HEAD(&new->list); + new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start; + new->len = end - start; + list_add_tail(&new->list, head); +done: + SM_I(sbi)->nr_discards += end - start; +} + +static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{ int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); int max_blocks = sbi->blocks_per_seg; struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); unsigned long *cur_map = (unsigned long *)se->cur_valid_map; unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; - unsigned long dmap[entries]; + unsigned long *discard_map = (unsigned long *)se->discard_map; + unsigned long *dmap = SIT_I(sbi)->tmp_map; unsigned int start = 0, end = -1; bool force = (cpc->reason == CP_DISCARD); int i; - if (!force && !test_opt(sbi, DISCARD)) + if (se->valid_blocks == max_blocks) return; - if (force && !se->valid_blocks) { - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - /* - * if this segment is registered in the prefree list, then - * we should skip adding a discard candidate, and let the - * checkpoint do that later. - */ - mutex_lock(&dirty_i->seglist_lock); - if (test_bit(cpc->trim_start, dirty_i->dirty_segmap[PRE])) { - mutex_unlock(&dirty_i->seglist_lock); - cpc->trimmed += sbi->blocks_per_seg; + if (!force) { + if (!test_opt(sbi, DISCARD) || !se->valid_blocks || + SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards) return; - } - mutex_unlock(&dirty_i->seglist_lock); - - new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); - INIT_LIST_HEAD(&new->list); - new->blkaddr = START_BLOCK(sbi, cpc->trim_start); - new->len = sbi->blocks_per_seg; - list_add_tail(&new->list, head); - SM_I(sbi)->nr_discards += sbi->blocks_per_seg; - cpc->trimmed += sbi->blocks_per_seg; - return; } - /* zero block will be discarded through the prefree list */ - if (!se->valid_blocks || se->valid_blocks == max_blocks) - return; - /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ for (i = 0; i < entries; i++) - dmap[i] = (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; + dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : + (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) { start = __find_rev_next_bit(dmap, max_blocks, end + 1); @@ -497,18 +593,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) break; end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); - - if (end - start < cpc->trim_minlen) - continue; - - new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); - INIT_LIST_HEAD(&new->list); - new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start; - new->len = end - start; - cpc->trimmed += end - start; - - list_add_tail(&new->list, head); - SM_I(sbi)->nr_discards += end - start; + __add_discard_entry(sbi, cpc, se, start, end); } } @@ -538,7 +623,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) mutex_unlock(&dirty_i->seglist_lock); } -void clear_prefree_segments(struct f2fs_sb_info *sbi) +void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct list_head *head = &(SM_I(sbi)->discard_list); struct discard_entry *entry, *this; @@ -571,7 +656,11 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi) /* send small discards */ list_for_each_entry_safe(entry, this, head, list) { + if (cpc->reason == CP_DISCARD && entry->len < cpc->trim_minlen) + goto skip; f2fs_issue_discard(sbi, entry->blkaddr, entry->len); + cpc->trimmed += entry->len; +skip: list_del(&entry->list); SM_I(sbi)->nr_discards -= entry->len; kmem_cache_free(discard_entry_slab, entry); @@ -620,11 +709,15 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) /* Update valid block bitmap */ if (del > 0) { - if (f2fs_set_bit(offset, se->cur_valid_map)) + if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) f2fs_bug_on(sbi, 1); + if (!f2fs_test_and_set_bit(offset, se->discard_map)) + sbi->discard_blks--; } else { - if (!f2fs_clear_bit(offset, se->cur_valid_map)) + if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) f2fs_bug_on(sbi, 1); + if (f2fs_test_and_clear_bit(offset, se->discard_map)) + sbi->discard_blks++; } if (!f2fs_test_bit(offset, se->ckpt_valid_map)) se->ckpt_valid_blocks += del; @@ -668,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 */ @@ -683,7 +800,7 @@ static void __add_sum_entry(struct f2fs_sb_info *sbi, int type, /* * Calculate the number of current summary pages for writing */ -int npages_for_summary_flush(struct f2fs_sb_info *sbi) +int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) { int valid_sum_count = 0; int i, sum_in_page; @@ -691,8 +808,13 @@ int npages_for_summary_flush(struct f2fs_sb_info *sbi) for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { if (sbi->ckpt->alloc_type[i] == SSR) valid_sum_count += sbi->blocks_per_seg; - else - valid_sum_count += curseg_blkoff(sbi, i); + else { + if (for_ra) + valid_sum_count += le16_to_cpu( + F2FS_CKPT(sbi)->cur_data_blkoff[i]); + else + valid_sum_count += curseg_blkoff(sbi, i); + } } sum_in_page = (PAGE_CACHE_SIZE - 2 * SUM_JOURNAL_SIZE - @@ -713,16 +835,25 @@ struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) return get_meta_page(sbi, GET_SUM_BLOCK(sbi, segno)); } -static void write_sum_page(struct f2fs_sb_info *sbi, - struct f2fs_summary_block *sum_blk, block_t blk_addr) +void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr) { struct page *page = grab_meta_page(sbi, blk_addr); - void *kaddr = page_address(page); - memcpy(kaddr, sum_blk, PAGE_CACHE_SIZE); + void *dst = page_address(page); + + if (src) + memcpy(dst, src, PAGE_CACHE_SIZE); + else + memset(dst, 0, PAGE_CACHE_SIZE); set_page_dirty(page); f2fs_put_page(page, 1); } +static void write_sum_page(struct f2fs_sb_info *sbi, + struct f2fs_summary_block *sum_blk, block_t blk_addr) +{ + update_meta_page(sbi, (void *)sum_blk, blk_addr); +} + static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); @@ -751,7 +882,7 @@ static void get_new_segment(struct f2fs_sb_info *sbi, int go_left = 0; int i; - write_lock(&free_i->segmap_lock); + spin_lock(&free_i->segmap_lock); if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { segno = find_next_zero_bit(free_i->free_segmap, @@ -824,7 +955,7 @@ got_it: f2fs_bug_on(sbi, test_bit(segno, free_i->free_segmap)); __set_inuse(sbi, segno); *newseg = segno; - write_unlock(&free_i->segmap_lock); + spin_unlock(&free_i->segmap_lock); } static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) @@ -875,7 +1006,7 @@ static void __next_free_blkoff(struct f2fs_sb_info *sbi, { struct seg_entry *se = get_seg_entry(sbi, seg->segno); int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); - unsigned long target_map[entries]; + unsigned long *target_map = SIT_I(sbi)->tmp_map; unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; unsigned long *cur_map = (unsigned long *)se->cur_valid_map; int i, pos; @@ -975,18 +1106,22 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi, stat_inc_seg_type(sbi, curseg); } +static void __allocate_new_segments(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + unsigned int old_segno; + + old_segno = curseg->segno; + SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true); + locate_dirty_segment(sbi, old_segno); +} + void allocate_new_segments(struct f2fs_sb_info *sbi) { - struct curseg_info *curseg; - unsigned int old_curseg; int i; - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { - curseg = CURSEG_I(sbi, i); - old_curseg = curseg->segno; - SIT_I(sbi)->s_ops->allocate_segment(sbi, i, true); - locate_dirty_segment(sbi, old_curseg); - } + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) + __allocate_new_segments(sbi, i); } static const struct segment_allocation default_salloc_ops = { @@ -995,13 +1130,12 @@ static const struct segment_allocation default_salloc_ops = { int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) { - __u64 start = range->start >> sbi->log_blocksize; - __u64 end = start + (range->len >> sbi->log_blocksize) - 1; + __u64 start = F2FS_BYTES_TO_BLK(range->start); + __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; unsigned int start_segno, end_segno; struct cp_control cpc; - if (range->minlen > SEGMENT_SIZE(sbi) || start >= MAX_BLKADDR(sbi) || - range->len < sbi->blocksize) + if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) return -EINVAL; cpc.trimmed = 0; @@ -1013,14 +1147,28 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : GET_SEGNO(sbi, end); cpc.reason = CP_DISCARD; - cpc.trim_start = start_segno; - cpc.trim_end = end_segno; - cpc.trim_minlen = range->minlen >> sbi->log_blocksize; + cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); /* do checkpoint to issue discard commands safely */ - write_checkpoint(sbi, &cpc); + for (; start_segno <= end_segno; start_segno = cpc.trim_end + 1) { + cpc.trim_start = start_segno; + + if (sbi->discard_blks == 0) + break; + else if (sbi->discard_blks < BATCHED_TRIM_BLOCKS(sbi)) + cpc.trim_end = end_segno; + else + cpc.trim_end = min_t(unsigned int, + rounddown(start_segno + + BATCHED_TRIM_SEGMENTS(sbi), + sbi->segs_per_sec) - 1, end_segno); + + mutex_lock(&sbi->gc_mutex); + write_checkpoint(sbi, &cpc); + mutex_unlock(&sbi->gc_mutex); + } out: - range->len = cpc.trimmed << sbi->log_blocksize; + range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return 0; } @@ -1050,8 +1198,8 @@ static int __get_segment_type_4(struct page *page, enum page_type p_type) else return CURSEG_COLD_DATA; } else { - if (IS_DNODE(page) && !is_cold_node(page)) - return CURSEG_HOT_NODE; + if (IS_DNODE(page) && is_cold_node(page)) + return CURSEG_WARM_NODE; else return CURSEG_COLD_NODE; } @@ -1097,10 +1245,19 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg; + bool direct_io = (type == CURSEG_DIRECT_IO); + + type = direct_io ? CURSEG_WARM_DATA : type; curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); + mutex_lock(&sit_i->sentry_lock); + + /* direct_io'ed data is aligned to the segment for better performance */ + if (direct_io && curseg->next_blkoff && + !has_not_enough_free_secs(sbi, 0)) + __allocate_new_segments(sbi, type); *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); @@ -1111,7 +1268,6 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, */ __add_sum_entry(sbi, type, sum); - mutex_lock(&sit_i->sentry_lock); __refresh_next_blkoff(sbi, curseg); stat_inc_block_count(sbi, curseg); @@ -1132,84 +1288,98 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, mutex_unlock(&curseg->curseg_mutex); } -static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, - block_t old_blkaddr, block_t *new_blkaddr, - struct f2fs_summary *sum, struct f2fs_io_info *fio) +static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { - int type = __get_segment_type(page, fio->type); + int type = __get_segment_type(fio->page, fio->type); - allocate_data_block(sbi, page, old_blkaddr, new_blkaddr, sum, type); + allocate_data_block(fio->sbi, fio->page, fio->blk_addr, + &fio->blk_addr, sum, type); /* writeout dirty page into bdev */ - f2fs_submit_page_mbio(sbi, page, *new_blkaddr, fio); + f2fs_submit_page_mbio(fio); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) { struct f2fs_io_info fio = { + .sbi = sbi, .type = META, - .rw = WRITE_SYNC | REQ_META | REQ_PRIO + .rw = WRITE_SYNC | REQ_META | REQ_PRIO, + .blk_addr = page->index, + .page = page, + .encrypted_page = NULL, }; + if (unlikely(page->index >= MAIN_BLKADDR(sbi))) + fio.rw &= ~REQ_META; + set_page_writeback(page); - f2fs_submit_page_mbio(sbi, page, page->index, &fio); + f2fs_submit_page_mbio(&fio); } -void write_node_page(struct f2fs_sb_info *sbi, struct page *page, - struct f2fs_io_info *fio, - unsigned int nid, block_t old_blkaddr, block_t *new_blkaddr) +void write_node_page(unsigned int nid, struct f2fs_io_info *fio) { struct f2fs_summary sum; + set_summary(&sum, nid, 0, 0); - do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, fio); + do_write_page(&sum, fio); } -void write_data_page(struct page *page, struct dnode_of_data *dn, - block_t *new_blkaddr, struct f2fs_io_info *fio) +void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) { - struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct f2fs_sb_info *sbi = fio->sbi; struct f2fs_summary sum; struct node_info ni; f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR); get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); - - do_write_page(sbi, page, dn->data_blkaddr, new_blkaddr, &sum, fio); + do_write_page(&sum, fio); + dn->data_blkaddr = fio->blk_addr; } -void rewrite_data_page(struct page *page, block_t old_blkaddr, - struct f2fs_io_info *fio) +void rewrite_data_page(struct f2fs_io_info *fio) { - f2fs_submit_page_mbio(F2FS_P_SB(page), page, old_blkaddr, fio); + stat_inc_inplace_blocks(fio->sbi); + f2fs_submit_page_mbio(fio); } -void recover_data_page(struct f2fs_sb_info *sbi, - struct page *page, struct f2fs_summary *sum, - block_t old_blkaddr, block_t new_blkaddr) +static void __f2fs_replace_block(struct f2fs_sb_info *sbi, + struct f2fs_summary *sum, + block_t old_blkaddr, block_t new_blkaddr, + bool recover_curseg) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg; unsigned int segno, old_cursegno; struct seg_entry *se; int type; + unsigned short old_blkoff; segno = GET_SEGNO(sbi, new_blkaddr); se = get_seg_entry(sbi, segno); type = se->type; - if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { - if (old_blkaddr == NULL_ADDR) - type = CURSEG_COLD_DATA; - else + if (!recover_curseg) { + /* for recovery flow */ + if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { + if (old_blkaddr == NULL_ADDR) + type = CURSEG_COLD_DATA; + else + type = CURSEG_WARM_DATA; + } + } else { + if (!IS_CURSEG(sbi, segno)) type = CURSEG_WARM_DATA; } + curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); mutex_lock(&sit_i->sentry_lock); old_cursegno = curseg->segno; + old_blkoff = curseg->next_blkoff; /* change the current segment */ if (segno != curseg->segno) { @@ -1220,33 +1390,77 @@ void recover_data_page(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) { + if (old_cursegno != curseg->segno) { + curseg->next_segno = old_cursegno; + change_curseg(sbi, type, true); + } + curseg->next_blkoff = old_blkoff; + } + mutex_unlock(&sit_i->sentry_lock); mutex_unlock(&curseg->curseg_mutex); } +void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, + block_t old_addr, block_t new_addr, + unsigned char version, bool recover_curseg) +{ + struct f2fs_summary sum; + + set_summary(&sum, dn->nid, dn->ofs_in_node, version); + + __f2fs_replace_block(sbi, &sum, old_addr, new_addr, recover_curseg); + + dn->data_blkaddr = new_addr; + set_data_blkaddr(dn); + f2fs_update_extent_cache(dn); +} + static inline bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page, enum page_type type) { enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io = &sbi->write_io[btype]; struct bio_vec *bvec; + struct page *target; int i; down_read(&io->io_rwsem); - if (!io->bio) - goto out; + if (!io->bio) { + up_read(&io->io_rwsem); + return false; + } bio_for_each_segment_all(bvec, io->bio, i) { - if (page == bvec->bv_page) { + + if (bvec->bv_page->mapping) { + target = bvec->bv_page; + } else { + struct f2fs_crypto_ctx *ctx; + + /* encrypted page */ + ctx = (struct f2fs_crypto_ctx *)page_private( + bvec->bv_page); + target = ctx->w.control_page; + } + + if (page == target) { up_read(&io->io_rwsem); return true; } } -out: up_read(&io->io_rwsem); return false; } @@ -1263,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); @@ -1339,7 +1570,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) segno = le32_to_cpu(ckpt->cur_data_segno[type]); blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - CURSEG_HOT_DATA]); - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) + if (__exist_node_summaries(sbi)) blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type); else blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); @@ -1348,7 +1579,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) CURSEG_HOT_NODE]); blk_off = le16_to_cpu(ckpt->cur_node_blkoff[type - CURSEG_HOT_NODE]); - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) + if (__exist_node_summaries(sbi)) blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, type - CURSEG_HOT_NODE); else @@ -1359,7 +1590,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) sum = (struct f2fs_summary_block *)page_address(new); if (IS_NODESEG(type)) { - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) { + if (__exist_node_summaries(sbi)) { struct f2fs_summary *ns = &sum->entries[0]; int i; for (i = 0; i < sbi->blocks_per_seg; i++, ns++) { @@ -1396,12 +1627,22 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) int err; if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) { + int npages = npages_for_summary_flush(sbi, true); + + if (npages >= 2) + ra_meta_pages(sbi, start_sum_block(sbi), npages, + META_CP, true); + /* restore for compacted data summary */ if (read_compacted_summaries(sbi)) return -EINVAL; type = CURSEG_HOT_NODE; } + if (__exist_node_summaries(sbi)) + ra_meta_pages(sbi, sum_blk_addr(sbi, NR_CURSEG_TYPE, type), + NR_CURSEG_TYPE - type, META_CP, true); + for (; type <= CURSEG_COLD_NODE; type++) { err = read_normal_summaries(sbi, type); if (err) @@ -1495,8 +1736,7 @@ void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) { - if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG)) - write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); + write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); } int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type, @@ -1524,17 +1764,7 @@ int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type, static struct page *get_current_sit_page(struct f2fs_sb_info *sbi, unsigned int segno) { - struct sit_info *sit_i = SIT_I(sbi); - unsigned int offset = SIT_BLOCK_OFFSET(segno); - block_t blk_addr = sit_i->sit_base_addr + offset; - - check_seg_range(sbi, segno); - - /* calculate sit block address */ - if (f2fs_test_bit(offset, sit_i->sit_bitmap)) - blk_addr += sit_i->sit_blocks; - - return get_meta_page(sbi, blk_addr); + return get_meta_page(sbi, current_sit_addr(sbi, segno)); } static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, @@ -1568,7 +1798,7 @@ static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, static struct sit_entry_set *grab_sit_entry_set(void) { struct sit_entry_set *ses = - f2fs_kmem_cache_alloc(sit_entry_set_slab, GFP_ATOMIC); + f2fs_kmem_cache_alloc(sit_entry_set_slab, GFP_NOFS); ses->entry_cnt = 0; INIT_LIST_HEAD(&ses->set_list); @@ -1664,6 +1894,9 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) mutex_lock(&curseg->curseg_mutex); mutex_lock(&sit_i->sentry_lock); + if (!sit_i->dirty_sentries) + goto out; + /* * add and account sit entries of dirty bitmap in sit entry * set temporarily @@ -1678,16 +1911,13 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (!__has_cursum_space(sum, sit_i->dirty_sentries, SIT_JOURNAL)) remove_sits_in_journal(sbi); - if (!sit_i->dirty_sentries) - goto out; - /* * there are two steps to flush sit entries: * #1, flush sit entries to journal in current cold data summary block. * #2, flush sit entries to sit page. */ list_for_each_entry_safe(ses, tmp, head, set_list) { - struct page *page; + struct page *page = NULL; struct f2fs_sit_block *raw_sit = NULL; unsigned int start_segno = ses->start_segno; unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK, @@ -1710,7 +1940,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) se = get_seg_entry(sbi, segno); /* add discard candidates */ - if (SM_I(sbi)->nr_discards < SM_I(sbi)->max_discards) { + if (cpc->reason != CP_DISCARD) { cpc->trim_start = segno; add_discard_addrs(sbi, cpc); } @@ -1770,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; @@ -1784,14 +2015,21 @@ static int build_sit_info(struct f2fs_sb_info *sbi) = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); sit_i->sentries[start].ckpt_valid_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); - if (!sit_i->sentries[start].cur_valid_map - || !sit_i->sentries[start].ckpt_valid_map) + sit_i->sentries[start].discard_map + = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->sentries[start].cur_valid_map || + !sit_i->sentries[start].ckpt_valid_map || + !sit_i->sentries[start].discard_map) return -ENOMEM; } + sit_i->tmp_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->tmp_map) + 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; } @@ -1836,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; @@ -1853,7 +2091,7 @@ static int build_free_segmap(struct f2fs_sb_info *sbi) free_i->start_segno = GET_SEGNO_FROM_SEG0(sbi, MAIN_BLKADDR(sbi)); free_i->free_segments = 0; free_i->free_sections = 0; - rwlock_init(&free_i->segmap_lock); + spin_lock_init(&free_i->segmap_lock); return 0; } @@ -1890,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; @@ -1919,6 +2157,11 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) got_it: check_block_count(sbi, start, &sit); seg_info_from_raw_sit(se, &sit); + + /* build discard map only one time */ + memcpy(se->discard_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); + sbi->discard_blks += sbi->blocks_per_seg - se->valid_blocks; + if (sbi->segs_per_sec > 1) { struct sec_entry *e = get_sec_entry(sbi, start); e->valid_blocks += se->valid_blocks; @@ -1977,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; @@ -1999,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; } @@ -2066,6 +2309,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->nr_discards = 0; sm_info->max_discards = 0; + sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS; + INIT_LIST_HEAD(&sm_info->sit_entry_set); if (test_opt(sbi, FLUSH_MERGE) && !f2fs_readonly(sbi->sb)) { @@ -2102,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); } @@ -2110,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) @@ -2149,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); } @@ -2166,11 +2411,14 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) for (start = 0; start < MAIN_SEGS(sbi); start++) { kfree(sit_i->sentries[start].cur_valid_map); kfree(sit_i->sentries[start].ckpt_valid_map); + kfree(sit_i->sentries[start].discard_map); } } - vfree(sit_i->sentries); - vfree(sit_i->sec_entries); - kfree(sit_i->dirty_sentries_bitmap); + kfree(sit_i->tmp_map); + + 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); @@ -2200,7 +2448,7 @@ int __init create_segment_manager_caches(void) goto fail; sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set", - sizeof(struct nat_entry_set)); + sizeof(struct sit_entry_set)); if (!sit_entry_set_slab) goto destory_discard_entry; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 2495bec1c621..3bbeca13f70d 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -136,10 +136,12 @@ enum { /* * BG_GC means the background cleaning job. * FG_GC means the on-demand cleaning job. + * FORCE_FG_GC means on-demand cleaning job in background. */ enum { BG_GC = 0, - FG_GC + FG_GC, + FORCE_FG_GC, }; /* for a function parameter to select a victim segment */ @@ -163,6 +165,7 @@ struct seg_entry { */ unsigned short ckpt_valid_blocks; unsigned char *ckpt_valid_map; + unsigned char *discard_map; unsigned char type; /* segment type like CURSEG_XXX_TYPE */ unsigned long long mtime; /* modification time of the segment */ }; @@ -175,6 +178,15 @@ struct segment_allocation { void (*allocate_segment)(struct f2fs_sb_info *, int, bool); }; +/* + * this value is set in page as a private data which indicate that + * the page is atomically written, and it is in inmem_pages list. + */ +#define ATOMIC_WRITTEN_PAGE 0x0000ffff + +#define IS_ATOMIC_WRITTEN_PAGE(page) \ + (page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE) + struct inmem_pages { struct list_head list; struct page *page; @@ -189,6 +201,7 @@ struct sit_info { char *sit_bitmap; /* SIT bitmap pointer */ unsigned int bitmap_size; /* SIT bitmap size */ + unsigned long *tmp_map; /* bitmap for temporal use */ unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */ unsigned int dirty_sentries; /* # of dirty sentries */ unsigned int sents_per_block; /* # of SIT entries per block */ @@ -207,7 +220,7 @@ struct free_segmap_info { unsigned int start_segno; /* start segment number logically */ unsigned int free_segments; /* # of free segments */ unsigned int free_sections; /* # of free sections */ - rwlock_t segmap_lock; /* free segmap lock */ + spinlock_t segmap_lock; /* free segmap lock */ unsigned long *free_segmap; /* free segment bitmap */ unsigned long *free_secmap; /* free section bitmap */ }; @@ -318,9 +331,9 @@ static inline unsigned int find_next_inuse(struct free_segmap_info *free_i, unsigned int max, unsigned int segno) { unsigned int ret; - read_lock(&free_i->segmap_lock); + spin_lock(&free_i->segmap_lock); ret = find_next_bit(free_i->free_segmap, max, segno); - read_unlock(&free_i->segmap_lock); + spin_unlock(&free_i->segmap_lock); return ret; } @@ -331,16 +344,17 @@ static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno) unsigned int start_segno = secno * sbi->segs_per_sec; unsigned int next; - write_lock(&free_i->segmap_lock); + spin_lock(&free_i->segmap_lock); clear_bit(segno, free_i->free_segmap); free_i->free_segments++; - next = find_next_bit(free_i->free_segmap, MAIN_SEGS(sbi), start_segno); + next = find_next_bit(free_i->free_segmap, + start_segno + sbi->segs_per_sec, start_segno); if (next >= start_segno + sbi->segs_per_sec) { clear_bit(secno, free_i->free_secmap); free_i->free_sections++; } - write_unlock(&free_i->segmap_lock); + spin_unlock(&free_i->segmap_lock); } static inline void __set_inuse(struct f2fs_sb_info *sbi, @@ -362,7 +376,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi, unsigned int start_segno = secno * sbi->segs_per_sec; unsigned int next; - write_lock(&free_i->segmap_lock); + spin_lock(&free_i->segmap_lock); if (test_and_clear_bit(segno, free_i->free_segmap)) { free_i->free_segments++; @@ -373,7 +387,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi, free_i->free_sections++; } } - write_unlock(&free_i->segmap_lock); + spin_unlock(&free_i->segmap_lock); } static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi, @@ -381,13 +395,13 @@ static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi, { struct free_segmap_info *free_i = FREE_I(sbi); unsigned int secno = segno / sbi->segs_per_sec; - write_lock(&free_i->segmap_lock); + spin_lock(&free_i->segmap_lock); if (!test_and_set_bit(segno, free_i->free_segmap)) { free_i->free_segments--; if (!test_and_set_bit(secno, free_i->free_secmap)) free_i->free_sections--; } - write_unlock(&free_i->segmap_lock); + spin_unlock(&free_i->segmap_lock); } static inline void get_sit_bitmap(struct f2fs_sb_info *sbi, @@ -460,7 +474,7 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); - if (unlikely(sbi->por_doing)) + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return false; return (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + @@ -551,16 +565,15 @@ static inline unsigned short curseg_blkoff(struct f2fs_sb_info *sbi, int type) return curseg->next_blkoff; } -#ifdef CONFIG_F2FS_CHECK_FS static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) { - BUG_ON(segno > TOTAL_SEGS(sbi) - 1); + f2fs_bug_on(sbi, segno > TOTAL_SEGS(sbi) - 1); } static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) { - BUG_ON(blk_addr < SEG0_BLKADDR(sbi)); - BUG_ON(blk_addr >= MAX_BLKADDR(sbi)); + f2fs_bug_on(sbi, blk_addr < SEG0_BLKADDR(sbi) + || blk_addr >= MAX_BLKADDR(sbi)); } /* @@ -569,16 +582,11 @@ static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) static inline void check_block_count(struct f2fs_sb_info *sbi, int segno, struct f2fs_sit_entry *raw_sit) { +#ifdef CONFIG_F2FS_CHECK_FS bool is_valid = test_bit_le(0, raw_sit->valid_map) ? true : false; int valid_blocks = 0; int cur_pos = 0, next_pos; - /* check segment usage */ - BUG_ON(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg); - - /* check boundary of a given segment number */ - BUG_ON(segno > TOTAL_SEGS(sbi) - 1); - /* check bitmap with valid block count */ do { if (is_valid) { @@ -594,35 +602,11 @@ static inline void check_block_count(struct f2fs_sb_info *sbi, is_valid = !is_valid; } while (cur_pos < sbi->blocks_per_seg); BUG_ON(GET_SIT_VBLOCKS(raw_sit) != valid_blocks); -} -#else -static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) -{ - if (segno > TOTAL_SEGS(sbi) - 1) - sbi->need_fsck = true; -} - -static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) -{ - if (blk_addr < SEG0_BLKADDR(sbi) || blk_addr >= MAX_BLKADDR(sbi)) - sbi->need_fsck = true; -} - -/* - * Summary block is always treated as an invalid block - */ -static inline void check_block_count(struct f2fs_sb_info *sbi, - int segno, struct f2fs_sit_entry *raw_sit) -{ - /* check segment usage */ - if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg) - sbi->need_fsck = true; - - /* check boundary of a given segment number */ - if (segno > TOTAL_SEGS(sbi) - 1) - sbi->need_fsck = true; -} #endif + /* check segment usage, and check boundary of a given segment number */ + f2fs_bug_on(sbi, GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg + || segno > TOTAL_SEGS(sbi) - 1); +} static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, unsigned int start) @@ -657,10 +641,7 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start) { unsigned int block_off = SIT_BLOCK_OFFSET(start); - if (f2fs_test_bit(block_off, sit_i->sit_bitmap)) - f2fs_clear_bit(block_off, sit_i->sit_bitmap); - else - f2fs_set_bit(block_off, sit_i->sit_bitmap); + f2fs_change_bit(block_off, sit_i->sit_bitmap); } static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi) @@ -714,6 +695,9 @@ static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi) */ static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type) { + if (sbi->sb->s_bdi->dirty_exceeded) + return 0; + if (type == DATA) return sbi->blocks_per_seg; else if (type == NODE) diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c new file mode 100644 index 000000000000..da0d8e0b55a5 --- /dev/null +++ b/fs/f2fs/shrinker.c @@ -0,0 +1,139 @@ +/* + * f2fs shrinker support + * the basic infra was copied from fs/ubifs/shrinker.c + * + * Copyright (c) 2015 Motorola Mobility + * Copyright (c) 2015 Jaegeuk Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include + +#include "f2fs.h" + +static LIST_HEAD(f2fs_list); +static DEFINE_SPINLOCK(f2fs_list_lock); +static unsigned int shrinker_run_no; + +static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi) +{ + return NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt; +} + +static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) +{ + if (NM_I(sbi)->fcnt > NAT_ENTRY_PER_BLOCK) + return NM_I(sbi)->fcnt - NAT_ENTRY_PER_BLOCK; + return 0; +} + +static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi) +{ + return sbi->total_ext_tree + atomic_read(&sbi->total_ext_node); +} + +unsigned long f2fs_shrink_count(struct shrinker *shrink, + struct shrink_control *sc) +{ + struct f2fs_sb_info *sbi; + struct list_head *p; + unsigned long count = 0; + + spin_lock(&f2fs_list_lock); + p = f2fs_list.next; + while (p != &f2fs_list) { + sbi = list_entry(p, struct f2fs_sb_info, s_list); + + /* stop f2fs_put_super */ + if (!mutex_trylock(&sbi->umount_mutex)) { + p = p->next; + continue; + } + spin_unlock(&f2fs_list_lock); + + /* count extent cache entries */ + count += __count_extent_cache(sbi); + + /* shrink clean nat cache entries */ + count += __count_nat_entries(sbi); + + /* count free nids cache entries */ + count += __count_free_nids(sbi); + + spin_lock(&f2fs_list_lock); + p = p->next; + mutex_unlock(&sbi->umount_mutex); + } + spin_unlock(&f2fs_list_lock); + return count; +} + +unsigned long f2fs_shrink_scan(struct shrinker *shrink, + struct shrink_control *sc) +{ + unsigned long nr = sc->nr_to_scan; + struct f2fs_sb_info *sbi; + struct list_head *p; + unsigned int run_no; + unsigned long freed = 0; + + spin_lock(&f2fs_list_lock); + do { + run_no = ++shrinker_run_no; + } while (run_no == 0); + p = f2fs_list.next; + while (p != &f2fs_list) { + sbi = list_entry(p, struct f2fs_sb_info, s_list); + + if (sbi->shrinker_run_no == run_no) + break; + + /* stop f2fs_put_super */ + if (!mutex_trylock(&sbi->umount_mutex)) { + p = p->next; + continue; + } + spin_unlock(&f2fs_list_lock); + + sbi->shrinker_run_no = run_no; + + /* shrink extent cache entries */ + freed += f2fs_shrink_extent_tree(sbi, nr >> 1); + + /* shrink clean nat cache entries */ + if (freed < nr) + freed += try_to_free_nats(sbi, nr - freed); + + /* shrink free nids cache entries */ + if (freed < nr) + freed += try_to_free_nids(sbi, nr - freed); + + spin_lock(&f2fs_list_lock); + p = p->next; + list_move_tail(&sbi->s_list, &f2fs_list); + mutex_unlock(&sbi->umount_mutex); + if (freed >= nr) + break; + } + spin_unlock(&f2fs_list_lock); + return freed; +} + +void f2fs_join_shrinker(struct f2fs_sb_info *sbi) +{ + spin_lock(&f2fs_list_lock); + list_add_tail(&sbi->s_list, &f2fs_list); + spin_unlock(&f2fs_list_lock); +} + +void f2fs_leave_shrinker(struct f2fs_sb_info *sbi) +{ + f2fs_shrink_extent_tree(sbi, __count_extent_cache(sbi)); + + spin_lock(&f2fs_list_lock); + list_del(&sbi->s_list); + spin_unlock(&f2fs_list_lock); +} diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 41d6f700f4ee..3a65e0132352 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -30,6 +30,7 @@ #include "segment.h" #include "xattr.h" #include "gc.h" +#include "trace.h" #define CREATE_TRACE_POINTS #include @@ -38,9 +39,17 @@ static struct proc_dir_entry *f2fs_proc_root; static struct kmem_cache *f2fs_inode_cachep; static struct kset *f2fs_kset; +/* f2fs-wide shrinker description */ +static struct shrinker f2fs_shrinker_info = { + .scan_objects = f2fs_shrink_scan, + .count_objects = f2fs_shrink_count, + .seeks = DEFAULT_SEEKS, +}; + enum { Opt_gc_background, Opt_disable_roll_forward, + Opt_norecovery, Opt_discard, Opt_noheap, Opt_user_xattr, @@ -51,14 +60,20 @@ enum { Opt_disable_ext_identify, Opt_inline_xattr, Opt_inline_data, + Opt_inline_dentry, Opt_flush_merge, Opt_nobarrier, + Opt_fastboot, + Opt_extent_cache, + Opt_noextent_cache, + Opt_noinline_data, Opt_err, }; static match_table_t f2fs_tokens = { {Opt_gc_background, "background_gc=%s"}, {Opt_disable_roll_forward, "disable_roll_forward"}, + {Opt_norecovery, "norecovery"}, {Opt_discard, "discard"}, {Opt_noheap, "no_heap"}, {Opt_user_xattr, "user_xattr"}, @@ -69,8 +84,13 @@ static match_table_t f2fs_tokens = { {Opt_disable_ext_identify, "disable_ext_identify"}, {Opt_inline_xattr, "inline_xattr"}, {Opt_inline_data, "inline_data"}, + {Opt_inline_dentry, "inline_dentry"}, {Opt_flush_merge, "flush_merge"}, {Opt_nobarrier, "nobarrier"}, + {Opt_fastboot, "fastboot"}, + {Opt_extent_cache, "extent_cache"}, + {Opt_noextent_cache, "noextent_cache"}, + {Opt_noinline_data, "noinline_data"}, {Opt_err, NULL}, }; @@ -188,12 +208,15 @@ F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, max_small_discards, max_discards); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); +F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, cp_interval); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -203,12 +226,15 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(gc_idle), ATTR_LIST(reclaim_segments), ATTR_LIST(max_small_discards), + ATTR_LIST(batched_trim_sections), ATTR_LIST(ipu_policy), ATTR_LIST(min_ipu_util), ATTR_LIST(min_fsync_blocks), ATTR_LIST(max_victim_search), ATTR_LIST(dir_level), ATTR_LIST(ram_thresh), + ATTR_LIST(ra_nid_pages), + ATTR_LIST(cp_interval), NULL, }; @@ -245,6 +271,7 @@ static void init_once(void *foo) static int parse_options(struct super_block *sb, char *options) { struct f2fs_sb_info *sbi = F2FS_SB(sb); + struct request_queue *q; substring_t args[MAX_OPT_ARGS]; char *p, *name; int arg = 0; @@ -269,11 +296,16 @@ static int parse_options(struct super_block *sb, char *options) if (!name) return -ENOMEM; - if (strlen(name) == 2 && !strncmp(name, "on", 2)) + if (strlen(name) == 2 && !strncmp(name, "on", 2)) { set_opt(sbi, BG_GC); - else if (strlen(name) == 3 && !strncmp(name, "off", 3)) + clear_opt(sbi, FORCE_FG_GC); + } else if (strlen(name) == 3 && !strncmp(name, "off", 3)) { clear_opt(sbi, BG_GC); - else { + clear_opt(sbi, FORCE_FG_GC); + } else if (strlen(name) == 4 && !strncmp(name, "sync", 4)) { + set_opt(sbi, BG_GC); + set_opt(sbi, FORCE_FG_GC); + } else { kfree(name); return -EINVAL; } @@ -282,8 +314,21 @@ static int parse_options(struct super_block *sb, char *options) case Opt_disable_roll_forward: set_opt(sbi, DISABLE_ROLL_FORWARD); break; + case Opt_norecovery: + /* this option mounts f2fs with ro */ + set_opt(sbi, DISABLE_ROLL_FORWARD); + if (!f2fs_readonly(sb)) + return -EINVAL; + break; case Opt_discard: - set_opt(sbi, DISCARD); + q = bdev_get_queue(sb->s_bdev); + if (blk_queue_discard(q)) { + set_opt(sbi, DISCARD); + } else { + f2fs_msg(sb, KERN_WARNING, + "mounting with \"discard\" option, but " + "the device does not support discard"); + } break; case Opt_noheap: set_opt(sbi, NOHEAP); @@ -340,12 +385,27 @@ static int parse_options(struct super_block *sb, char *options) case Opt_inline_data: set_opt(sbi, INLINE_DATA); break; + case Opt_inline_dentry: + set_opt(sbi, INLINE_DENTRY); + break; case Opt_flush_merge: set_opt(sbi, FLUSH_MERGE); break; case Opt_nobarrier: set_opt(sbi, NOBARRIER); break; + case Opt_fastboot: + set_opt(sbi, FASTBOOT); + break; + case Opt_extent_cache: + set_opt(sbi, EXTENT_CACHE); + break; + case Opt_noextent_cache: + clear_opt(sbi, EXTENT_CACHE); + break; + case Opt_noinline_data: + clear_opt(sbi, INLINE_DATA); + break; default: f2fs_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" or missing value", @@ -371,7 +431,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) atomic_set(&fi->dirty_pages, 0); fi->i_current_depth = 1; fi->i_advise = 0; - rwlock_init(&fi->ext.ext_lock); init_rwsem(&fi->i_sem); INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); @@ -384,6 +443,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; +#ifdef CONFIG_F2FS_FS_ENCRYPTION + fi->i_crypt_info = NULL; +#endif return &fi->vfs_inode; } @@ -396,8 +458,37 @@ static int f2fs_drop_inode(struct inode *inode) * - f2fs_gc -> iput -> evict * - inode_wait_for_writeback(inode) */ - if (!inode_unhashed(inode) && inode->i_state & I_SYNC) + if (!inode_unhashed(inode) && inode->i_state & I_SYNC) { + if (!inode->i_nlink && !is_bad_inode(inode)) { + /* to avoid evict_inode call simultaneously */ + atomic_inc(&inode->i_count); + spin_unlock(&inode->i_lock); + + /* some remained atomic pages should discarded */ + if (f2fs_is_atomic_file(inode)) + commit_inmem_pages(inode, true); + + /* should remain fi->extent_tree for writepage */ + f2fs_destroy_extent_node(inode); + + sb_start_intwrite(inode->i_sb); + i_size_write(inode, 0); + + if (F2FS_HAS_BLOCKS(inode)) + f2fs_truncate(inode, true); + + sb_end_intwrite(inode->i_sb); + +#ifdef CONFIG_F2FS_FS_ENCRYPTION + if (F2FS_I(inode)->i_crypt_info) + f2fs_free_encryption_info(inode, + F2FS_I(inode)->i_crypt_info); +#endif + spin_lock(&inode->i_lock); + atomic_dec(&inode->i_count); + } return 0; + } return generic_drop_inode(inode); } @@ -432,17 +523,27 @@ static void f2fs_put_super(struct super_block *sb) } kobject_del(&sbi->s_kobj); - f2fs_destroy_stats(sbi); stop_gc_thread(sbi); - /* We don't need to do checkpoint when it's clean */ - if (sbi->s_dirty) { + /* prevent remaining shrinker jobs */ + mutex_lock(&sbi->umount_mutex); + + /* + * We don't need to do checkpoint when superblock is clean. + * But, the previous checkpoint was not done by umount, it needs to do + * clean checkpoint again. + */ + if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) || + !is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG)) { struct cp_control cpc = { .reason = CP_UMOUNT, }; write_checkpoint(sbi, &cpc); } + /* write_checkpoint can update stat informaion */ + f2fs_destroy_stats(sbi); + /* * normally superblock is clean, so we need to release this. * In addition, EIO will skip do checkpoint, we need this as well. @@ -450,6 +551,9 @@ static void f2fs_put_super(struct super_block *sb) release_dirty_inode(sbi); release_discard_addrs(sbi); + f2fs_leave_shrinker(sbi); + mutex_unlock(&sbi->umount_mutex); + iput(sbi->node_inode); iput(sbi->meta_inode); @@ -473,15 +577,17 @@ int f2fs_sync_fs(struct super_block *sb, int sync) trace_f2fs_sync_fs(sb, sync); if (sync) { - struct cp_control cpc = { - .reason = CP_SYNC, - }; + struct cp_control cpc; + + cpc.reason = __get_cp_reason(sbi); + mutex_lock(&sbi->gc_mutex); write_checkpoint(sbi, &cpc); mutex_unlock(&sbi->gc_mutex); } else { f2fs_balance_fs(sbi); } + f2fs_trace_ios(NULL, 1); return 0; } @@ -534,10 +640,14 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) { struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); - if (!f2fs_readonly(sbi->sb) && test_opt(sbi, BG_GC)) - seq_printf(seq, ",background_gc=%s", "on"); - else + if (!f2fs_readonly(sbi->sb) && test_opt(sbi, BG_GC)) { + if (test_opt(sbi, FORCE_FG_GC)) + seq_printf(seq, ",background_gc=%s", "sync"); + else + seq_printf(seq, ",background_gc=%s", "on"); + } else { seq_printf(seq, ",background_gc=%s", "off"); + } if (test_opt(sbi, DISABLE_ROLL_FORWARD)) seq_puts(seq, ",disable_roll_forward"); if (test_opt(sbi, DISCARD)) @@ -562,10 +672,20 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",disable_ext_identify"); if (test_opt(sbi, INLINE_DATA)) seq_puts(seq, ",inline_data"); + else + seq_puts(seq, ",noinline_data"); + if (test_opt(sbi, INLINE_DENTRY)) + seq_puts(seq, ",inline_dentry"); if (!f2fs_readonly(sbi->sb) && test_opt(sbi, FLUSH_MERGE)) seq_puts(seq, ",flush_merge"); if (test_opt(sbi, NOBARRIER)) seq_puts(seq, ",nobarrier"); + if (test_opt(sbi, FASTBOOT)) + seq_puts(seq, ",fastboot"); + if (test_opt(sbi, EXTENT_CACHE)) + seq_puts(seq, ",extent_cache"); + else + seq_puts(seq, ",noextent_cache"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); return 0; @@ -586,7 +706,7 @@ static int segment_info_seq_show(struct seq_file *seq, void *offset) struct seg_entry *se = get_seg_entry(sbi, i); if ((i % 10) == 0) - seq_printf(seq, "%-5d", i); + seq_printf(seq, "%-10d", i); seq_printf(seq, "%d|%-3u", se->type, get_valid_blocks(sbi, i, 1)); if ((i % 10) == 9 || i == (total_segs - 1)) @@ -611,6 +731,23 @@ static const struct file_operations f2fs_seq_segment_info_fops = { .release = single_release, }; +static void default_options(struct f2fs_sb_info *sbi) +{ + /* init some FS parameters */ + sbi->active_logs = NR_CURSEG_TYPE; + + set_opt(sbi, BG_GC); + set_opt(sbi, INLINE_DATA); + set_opt(sbi, EXTENT_CACHE); + +#ifdef CONFIG_F2FS_FS_XATTR + set_opt(sbi, XATTR_USER); +#endif +#ifdef CONFIG_F2FS_FS_POSIX_ACL + set_opt(sbi, POSIX_ACL); +#endif +} + static int f2fs_remount(struct super_block *sb, int *flags, char *data) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -618,6 +755,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) int err, active_logs; bool need_restart_gc = false; bool need_stop_gc = false; + bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE); sync_filesystem(sb); @@ -629,7 +767,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) active_logs = sbi->active_logs; sbi->mount_opt.opt = 0; - sbi->active_logs = NR_CURSEG_TYPE; + default_options(sbi); /* parse mount options */ err = parse_options(sb, data); @@ -643,6 +781,14 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) if (f2fs_readonly(sb) && (*flags & MS_RDONLY)) goto skip; + /* disallow enable/disable extent_cache dynamically */ + if (no_extent_cache == !!test_opt(sbi, EXTENT_CACHE)) { + err = -EINVAL; + f2fs_msg(sbi->sb, KERN_WARNING, + "switch extent_cache option is not allowed"); + goto restore_opts; + } + /* * We stop the GC thread if FS is mounted as RO * or if background_gc = off is passed in mount @@ -654,7 +800,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) f2fs_sync_fs(sb, 1); need_restart_gc = true; } - } else if (test_opt(sbi, BG_GC) && !sbi->gc_thread) { + } else if (!sbi->gc_thread) { err = start_gc_thread(sbi); if (err) goto restore_opts; @@ -667,7 +813,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) */ if ((*flags & MS_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) { destroy_flush_cmd_control(sbi); - } else if (test_opt(sbi, FLUSH_MERGE) && !SM_I(sbi)->cmd_control_info) { + } else if (!SM_I(sbi)->cmd_control_info) { err = create_flush_cmd_control(sbi); if (err) goto restore_gc; @@ -872,7 +1018,11 @@ static void init_sb_info(struct f2fs_sb_info *sbi) atomic_set(&sbi->nr_pages[i], 0); sbi->dir_level = DEF_DIR_LEVEL; - sbi->need_fsck = false; + sbi->cp_interval = DEF_CP_INTERVAL; + clear_sbi_flag(sbi, SBI_NEED_FSCK); + + INIT_LIST_HEAD(&sbi->s_list); + mutex_init(&sbi->umount_mutex); } /* @@ -882,29 +1032,36 @@ static void init_sb_info(struct f2fs_sb_info *sbi) */ static int read_raw_super_block(struct super_block *sb, struct f2fs_super_block **raw_super, - struct buffer_head **raw_super_buf) + struct buffer_head **raw_super_buf, + int *recovery) { int block = 0; + struct buffer_head *buffer; + struct f2fs_super_block *super; + int err = 0; retry: - *raw_super_buf = sb_bread(sb, block); - if (!*raw_super_buf) { + buffer = sb_bread(sb, block); + if (!buffer) { + *recovery = 1; f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", block + 1); if (block == 0) { block++; goto retry; } else { - return -EIO; + err = -EIO; + goto out; } } - *raw_super = (struct f2fs_super_block *) - ((char *)(*raw_super_buf)->b_data + F2FS_SUPER_OFFSET); + super = (struct f2fs_super_block *) + ((char *)(buffer)->b_data + F2FS_SUPER_OFFSET); /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, *raw_super)) { - brelse(*raw_super_buf); + if (sanity_check_raw_super(sb, super)) { + brelse(buffer); + *recovery = 1; f2fs_msg(sb, KERN_ERR, "Can't find valid F2FS filesystem in %dth superblock", block + 1); @@ -912,24 +1069,76 @@ retry: block++; goto retry; } else { - return -EINVAL; + err = -EINVAL; + goto out; } } + if (!*raw_super) { + *raw_super_buf = buffer; + *raw_super = super; + } else { + /* already have a valid superblock */ + brelse(buffer); + } + + /* check the validity of the second superblock */ + if (block == 0) { + block++; + goto retry; + } + +out: + /* No valid superblock */ + if (!*raw_super) + return err; + return 0; } +int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) +{ + struct buffer_head *sbh = sbi->raw_super_buf; + sector_t block = sbh->b_blocknr; + int err; + + /* write back-up superblock first */ + sbh->b_blocknr = block ? 0 : 1; + mark_buffer_dirty(sbh); + err = sync_dirty_buffer(sbh); + + sbh->b_blocknr = block; + + /* if we are in recovery path, skip writing valid superblock */ + if (recover || err) + goto out; + + /* write current valid superblock */ + mark_buffer_dirty(sbh); + err = sync_dirty_buffer(sbh); +out: + clear_buffer_write_io_error(sbh); + set_buffer_uptodate(sbh); + return err; +} + static int f2fs_fill_super(struct super_block *sb, void *data, int silent) { struct f2fs_sb_info *sbi; struct f2fs_super_block *raw_super; struct buffer_head *raw_super_buf; struct inode *root; - long err = -EINVAL; - bool retry = true; - int i; + long err; + bool retry = true, need_fsck = false; + char *options = NULL; + int recovery, i; try_onemore: + err = -EINVAL; + raw_super = NULL; + raw_super_buf = NULL; + recovery = 0; + /* allocate memory for f2fs-specific super block info */ sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL); if (!sbi) @@ -941,26 +1150,22 @@ try_onemore: goto free_sbi; } - err = read_raw_super_block(sb, &raw_super, &raw_super_buf); + err = read_raw_super_block(sb, &raw_super, &raw_super_buf, &recovery); if (err) goto free_sbi; sb->s_fs_info = sbi; - /* init some FS parameters */ - sbi->active_logs = NR_CURSEG_TYPE; - - set_opt(sbi, BG_GC); - -#ifdef CONFIG_F2FS_FS_XATTR - set_opt(sbi, XATTR_USER); -#endif -#ifdef CONFIG_F2FS_FS_POSIX_ACL - set_opt(sbi, POSIX_ACL); -#endif + default_options(sbi); /* parse mount options */ - err = parse_options(sb, (char *)data); - if (err) + options = kstrdup((const char *)data, GFP_KERNEL); + if (data && !options) { + err = -ENOMEM; goto free_sb_buf; + } + + err = parse_options(sb, options); + if (err) + goto free_options; sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); sb->s_max_links = F2FS_LINK_MAX; @@ -983,7 +1188,9 @@ try_onemore: mutex_init(&sbi->writepages); mutex_init(&sbi->cp_mutex); init_rwsem(&sbi->node_write); - sbi->por_doing = false; + + /* disallow all the data/node/meta page writes */ + set_sbi_flag(sbi, SBI_POR_DOING); spin_lock_init(&sbi->stat_lock); init_rwsem(&sbi->read_io.io_rwsem); @@ -1004,7 +1211,7 @@ try_onemore: if (IS_ERR(sbi->meta_inode)) { f2fs_msg(sb, KERN_ERR, "Failed to read F2FS meta data inode"); err = PTR_ERR(sbi->meta_inode); - goto free_sb_buf; + goto free_options; } err = get_valid_checkpoint(sbi); @@ -1032,6 +1239,8 @@ try_onemore: INIT_LIST_HEAD(&sbi->dir_inode_list); spin_lock_init(&sbi->dir_inode_lock); + init_extent_cache_info(sbi); + init_ino_entry_info(sbi); /* setup f2fs internal modules */ @@ -1058,8 +1267,12 @@ try_onemore: goto free_nm; } + f2fs_join_shrinker(sbi); + /* if there are nt orphan nodes free them */ - recover_orphan_inodes(sbi); + err = recover_orphan_inodes(sbi); + if (err) + goto free_node_inode; /* read root inode and dentry */ root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); @@ -1091,14 +1304,6 @@ try_onemore: proc_create_data("segment_info", S_IRUGO, sbi->s_proc, &f2fs_seq_segment_info_fops, sb); - if (test_opt(sbi, DISCARD)) { - struct request_queue *q = bdev_get_queue(sb->s_bdev); - if (!blk_queue_discard(q)) - f2fs_msg(sb, KERN_WARNING, - "mounting with \"discard\" option, but " - "the device does not support discard"); - } - sbi->s_kobj.kset = f2fs_kset; init_completion(&sbi->s_kobj_unregister); err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, @@ -1106,29 +1311,52 @@ try_onemore: if (err) goto free_proc; - if (!retry) - sbi->need_fsck = true; - /* recover fsynced data */ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { + /* + * mount should be failed, when device has readonly mode, and + * previous checkpoint was not done by clean system shutdown. + */ + if (bdev_read_only(sb->s_bdev) && + !is_set_ckpt_flags(sbi->ckpt, CP_UMOUNT_FLAG)) { + err = -EROFS; + goto free_kobj; + } + + if (need_fsck) + set_sbi_flag(sbi, SBI_NEED_FSCK); + err = recover_fsync_data(sbi); if (err) { + need_fsck = true; f2fs_msg(sb, KERN_ERR, "Cannot recover all fsync data errno=%ld", err); goto free_kobj; } } + /* recover_fsync_data() cleared this already */ + clear_sbi_flag(sbi, SBI_POR_DOING); /* * If filesystem is not mounted as read-only then * do start the gc_thread. */ - if (!f2fs_readonly(sb)) { + if (test_opt(sbi, BG_GC) && !f2fs_readonly(sb)) { /* After POR, we can run background GC thread.*/ err = start_gc_thread(sbi); if (err) goto free_kobj; } + kfree(options); + + /* recover broken superblock */ + if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) { + f2fs_msg(sb, KERN_INFO, "Recover invalid superblock"); + f2fs_commit_super(sbi, true); + } + + sbi->cp_expires = round_jiffies_up(jiffies); + return 0; free_kobj: @@ -1143,7 +1371,10 @@ free_root_inode: dput(sb->s_root); sb->s_root = NULL; free_node_inode: + mutex_lock(&sbi->umount_mutex); + f2fs_leave_shrinker(sbi); iput(sbi->node_inode); + mutex_unlock(&sbi->umount_mutex); free_nm: destroy_node_manager(sbi); free_sm: @@ -1153,6 +1384,8 @@ free_cp: free_meta_inode: make_bad_inode(sbi->meta_inode); iput(sbi->meta_inode); +free_options: + kfree(options); free_sb_buf: brelse(raw_super_buf); free_sbi: @@ -1160,7 +1393,7 @@ free_sbi: /* give only one another chance */ if (retry) { - retry = 0; + retry = false; shrink_dcache_sb(sb); goto try_onemore; } @@ -1173,11 +1406,18 @@ static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags, return mount_bdev(fs_type, flags, dev_name, data, f2fs_fill_super); } +static void kill_f2fs_super(struct super_block *sb) +{ + if (sb->s_root) + set_sbi_flag(F2FS_SB(sb), SBI_IS_CLOSE); + kill_block_super(sb); +} + static struct file_system_type f2fs_fs_type = { .owner = THIS_MODULE, .name = "f2fs", .mount = f2fs_mount, - .kill_sb = kill_block_super, + .kill_sb = kill_f2fs_super, .fs_flags = FS_REQUIRES_DEV, }; MODULE_ALIAS_FS("f2fs"); @@ -1205,6 +1445,8 @@ static int __init init_f2fs_fs(void) { int err; + f2fs_build_trace_ios(); + err = init_inodecache(); if (err) goto fail; @@ -1214,30 +1456,42 @@ static int __init init_f2fs_fs(void) err = create_segment_manager_caches(); if (err) goto free_node_manager_caches; - err = create_gc_caches(); + err = create_checkpoint_caches(); if (err) goto free_segment_manager_caches; - err = create_checkpoint_caches(); + err = create_extent_cache(); if (err) - goto free_gc_caches; + goto free_checkpoint_caches; f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); if (!f2fs_kset) { err = -ENOMEM; - goto free_checkpoint_caches; + goto free_extent_cache; } - err = register_filesystem(&f2fs_fs_type); + err = f2fs_init_crypto(); if (err) goto free_kset; + + err = register_shrinker(&f2fs_shrinker_info); + if (err) + goto free_crypto; + + err = register_filesystem(&f2fs_fs_type); + if (err) + goto free_shrinker; f2fs_create_root_stats(); f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); return 0; +free_shrinker: + unregister_shrinker(&f2fs_shrinker_info); +free_crypto: + f2fs_exit_crypto(); free_kset: kset_unregister(f2fs_kset); +free_extent_cache: + destroy_extent_cache(); free_checkpoint_caches: destroy_checkpoint_caches(); -free_gc_caches: - destroy_gc_caches(); free_segment_manager_caches: destroy_segment_manager_caches(); free_node_manager_caches: @@ -1252,13 +1506,16 @@ static void __exit exit_f2fs_fs(void) { remove_proc_entry("fs/f2fs", NULL); f2fs_destroy_root_stats(); + unregister_shrinker(&f2fs_shrinker_info); unregister_filesystem(&f2fs_fs_type); + f2fs_exit_crypto(); + destroy_extent_cache(); destroy_checkpoint_caches(); - destroy_gc_caches(); destroy_segment_manager_caches(); destroy_node_manager_caches(); destroy_inodecache(); kset_unregister(f2fs_kset); + f2fs_destroy_trace_ios(); } module_init(init_f2fs_fs) diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c new file mode 100644 index 000000000000..145fb659ad44 --- /dev/null +++ b/fs/f2fs/trace.c @@ -0,0 +1,159 @@ +/* + * f2fs IO tracer + * + * Copyright (c) 2014 Motorola Mobility + * Copyright (c) 2014 Jaegeuk Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include "f2fs.h" +#include "trace.h" + +static RADIX_TREE(pids, GFP_ATOMIC); +static spinlock_t pids_lock; +static struct last_io_info last_io; + +static inline void __print_last_io(void) +{ + if (!last_io.len) + return; + + trace_printk("%3x:%3x %4x %-16s %2x %5x %12x %4x\n", + last_io.major, last_io.minor, + last_io.pid, "----------------", + last_io.type, + last_io.fio.rw, last_io.fio.blk_addr, + last_io.len); + memset(&last_io, 0, sizeof(last_io)); +} + +static int __file_type(struct inode *inode, pid_t pid) +{ + if (f2fs_is_atomic_file(inode)) + return __ATOMIC_FILE; + else if (f2fs_is_volatile_file(inode)) + return __VOLATILE_FILE; + else if (S_ISDIR(inode->i_mode)) + return __DIR_FILE; + else if (inode->i_ino == F2FS_NODE_INO(F2FS_I_SB(inode))) + return __NODE_FILE; + else if (inode->i_ino == F2FS_META_INO(F2FS_I_SB(inode))) + return __META_FILE; + else if (pid) + return __NORMAL_FILE; + else + return __MISC_FILE; +} + +void f2fs_trace_pid(struct page *page) +{ + struct inode *inode = page->mapping->host; + pid_t pid = task_pid_nr(current); + void *p; + + page->private = pid; + + if (radix_tree_preload(GFP_NOFS)) + return; + + spin_lock(&pids_lock); + p = radix_tree_lookup(&pids, pid); + if (p == current) + goto out; + if (p) + radix_tree_delete(&pids, pid); + + f2fs_radix_tree_insert(&pids, pid, current); + + trace_printk("%3x:%3x %4x %-16s\n", + MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), + pid, current->comm); +out: + spin_unlock(&pids_lock); + radix_tree_preload_end(); +} + +void f2fs_trace_ios(struct f2fs_io_info *fio, int flush) +{ + struct inode *inode; + pid_t pid; + int major, minor; + + if (flush) { + __print_last_io(); + return; + } + + inode = fio->page->mapping->host; + pid = page_private(fio->page); + + major = MAJOR(inode->i_sb->s_dev); + minor = MINOR(inode->i_sb->s_dev); + + if (last_io.major == major && last_io.minor == minor && + last_io.pid == pid && + last_io.type == __file_type(inode, pid) && + last_io.fio.rw == fio->rw && + last_io.fio.blk_addr + last_io.len == fio->blk_addr) { + last_io.len++; + return; + } + + __print_last_io(); + + last_io.major = major; + last_io.minor = minor; + last_io.pid = pid; + last_io.type = __file_type(inode, pid); + last_io.fio = *fio; + last_io.len = 1; + return; +} + +void f2fs_build_trace_ios(void) +{ + spin_lock_init(&pids_lock); +} + +#define PIDVEC_SIZE 128 +static unsigned int gang_lookup_pids(pid_t *results, unsigned long first_index, + unsigned int max_items) +{ + struct radix_tree_iter iter; + void **slot; + unsigned int ret = 0; + + if (unlikely(!max_items)) + return 0; + + radix_tree_for_each_slot(slot, &pids, &iter, first_index) { + results[ret] = iter.index; + if (++ret == PIDVEC_SIZE) + break; + } + return ret; +} + +void f2fs_destroy_trace_ios(void) +{ + pid_t pid[PIDVEC_SIZE]; + pid_t next_pid = 0; + unsigned int found; + + spin_lock(&pids_lock); + while ((found = gang_lookup_pids(pid, next_pid, PIDVEC_SIZE))) { + unsigned idx; + + next_pid = pid[found - 1] + 1; + for (idx = 0; idx < found; idx++) + radix_tree_delete(&pids, pid[idx]); + } + spin_unlock(&pids_lock); +} diff --git a/fs/f2fs/trace.h b/fs/f2fs/trace.h new file mode 100644 index 000000000000..67db24ac1e85 --- /dev/null +++ b/fs/f2fs/trace.h @@ -0,0 +1,46 @@ +/* + * f2fs IO tracer + * + * Copyright (c) 2014 Motorola Mobility + * Copyright (c) 2014 Jaegeuk Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __F2FS_TRACE_H__ +#define __F2FS_TRACE_H__ + +#ifdef CONFIG_F2FS_IO_TRACE +#include + +enum file_type { + __NORMAL_FILE, + __DIR_FILE, + __NODE_FILE, + __META_FILE, + __ATOMIC_FILE, + __VOLATILE_FILE, + __MISC_FILE, +}; + +struct last_io_info { + int major, minor; + pid_t pid; + enum file_type type; + struct f2fs_io_info fio; + block_t len; +}; + +extern void f2fs_trace_pid(struct page *); +extern void f2fs_trace_ios(struct f2fs_io_info *, int); +extern void f2fs_build_trace_ios(void); +extern void f2fs_destroy_trace_ios(void); +#else +#define f2fs_trace_pid(p) +#define f2fs_trace_ios(i, n) +#define f2fs_build_trace_ios() +#define f2fs_destroy_trace_ios() + +#endif +#endif /* __F2FS_TRACE_H__ */ diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index deca8728117b..9d44a39b2e29 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -83,7 +83,7 @@ static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name, } if (strcmp(name, "") == 0) return -EINVAL; - return f2fs_getxattr(dentry->d_inode, type, name, buffer, size); + return f2fs_getxattr(dentry->d_inode, type, name, buffer, size, NULL); } static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, @@ -135,7 +135,8 @@ static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name, if (strcmp(name, "") != 0) return -EINVAL; - *((char *)buffer) = F2FS_I(inode)->i_advise; + if (buffer) + *((char *)buffer) = F2FS_I(inode)->i_advise; return sizeof(char); } @@ -152,6 +153,7 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, return -EINVAL; F2FS_I(inode)->i_advise |= *(char *)value; + mark_inode_dirty(inode); return 0; } @@ -398,7 +400,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, } int f2fs_getxattr(struct inode *inode, int index, const char *name, - void *buffer, size_t buffer_size) + void *buffer, size_t buffer_size, struct page *ipage) { struct f2fs_xattr_entry *entry; void *base_addr; @@ -412,7 +414,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name, if (len > F2FS_NAME_LEN) return -ERANGE; - base_addr = read_all_xattrs(inode, NULL); + base_addr = read_all_xattrs(inode, ipage); if (!base_addr) return -ENOMEM; @@ -497,9 +499,12 @@ static int __f2fs_setxattr(struct inode *inode, int index, len = strlen(name); - if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN(inode)) + if (len > F2FS_NAME_LEN) return -ERANGE; + if (size > MAX_VALUE_LEN(inode)) + return -E2BIG; + base_addr = read_all_xattrs(inode, ipage); if (!base_addr) goto exit; @@ -582,6 +587,9 @@ static int __f2fs_setxattr(struct inode *inode, int index, inode->i_ctime = CURRENT_TIME; clear_inode_flag(fi, FI_ACL_MODE); } + if (index == F2FS_XATTR_INDEX_ENCRYPTION && + !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) + f2fs_set_encrypted_inode(inode); if (ipage) update_inode(inode, ipage); diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index 34ab7dbcf5e3..71a7100d5492 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -35,6 +35,10 @@ #define F2FS_XATTR_INDEX_LUSTRE 5 #define F2FS_XATTR_INDEX_SECURITY 6 #define F2FS_XATTR_INDEX_ADVISE 7 +/* Should be same as EXT4_XATTR_INDEX_ENCRYPTION */ +#define F2FS_XATTR_INDEX_ENCRYPTION 9 + +#define F2FS_XATTR_NAME_ENCRYPTION_CONTEXT "c" struct f2fs_xattr_header { __le32 h_magic; /* magic number for identification */ @@ -115,7 +119,8 @@ extern const struct xattr_handler *f2fs_xattr_handlers[]; extern int f2fs_setxattr(struct inode *, int, const char *, const void *, size_t, struct page *, int); -extern int f2fs_getxattr(struct inode *, int, const char *, void *, size_t); +extern int f2fs_getxattr(struct inode *, int, const char *, void *, + size_t, struct page *); extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t); #else @@ -126,7 +131,8 @@ static inline int f2fs_setxattr(struct inode *inode, int index, return -EOPNOTSUPP; } static inline int f2fs_getxattr(struct inode *inode, int index, - const char *name, void *buffer, size_t buffer_size) + const char *name, void *buffer, + size_t buffer_size, struct page *dpage) { return -EOPNOTSUPP; } diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 860313a33a43..25c6324a0dd0 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -19,12 +19,16 @@ #define F2FS_MAX_LOG_SECTOR_SIZE 12 /* 12 bits for 4096 bytes */ #define F2FS_LOG_SECTORS_PER_BLOCK 3 /* log number for sector/blk */ #define F2FS_BLKSIZE 4096 /* support only 4KB block */ +#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */ #define F2FS_MAX_EXTENSION 64 /* # of extension entries */ #define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE) #define NULL_ADDR ((block_t)0) /* used as block_t addresses */ #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ +#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) +#define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS) + /* 0, 1(node nid), 2(meta nid) are reserved node id */ #define F2FS_RESERVED_NODE_NUM 3 @@ -33,7 +37,8 @@ #define F2FS_META_INO(sbi) (sbi->meta_ino_num) /* This flag is used by node and meta inodes, and by recovery */ -#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) +#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) +#define GFP_F2FS_HIGH_ZERO (GFP_NOFS | __GFP_ZERO | __GFP_HIGHMEM) /* * For further optimization on multi-head logs, on-disk layout supports maximum @@ -45,6 +50,8 @@ #define MAX_ACTIVE_NODE_LOGS 8 #define MAX_ACTIVE_DATA_LOGS 8 +#define VERSION_LEN 256 + /* * For superblock */ @@ -81,11 +88,18 @@ struct f2fs_super_block { __le32 extension_count; /* # of extensions below */ __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */ __le32 cp_payload; + __u8 version[VERSION_LEN]; /* the kernel version */ + __u8 init_version[VERSION_LEN]; /* the initial kernel version */ + __le32 feature; /* defined features */ + __u8 encryption_level; /* versioning level for encryption */ + __u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */ + __u8 reserved[871]; /* valid reserved region */ } __packed; /* * For checkpoint */ +#define CP_FASTBOOT_FLAG 0x00000020 #define CP_FSCK_FLAG 0x00000010 #define CP_ERROR_FLAG 0x00000008 #define CP_COMPACT_SUM_FLAG 0x00000004 @@ -147,7 +161,7 @@ struct f2fs_orphan_block { */ struct f2fs_extent { __le32 fofs; /* start file offset of the extent */ - __le32 blk_addr; /* start block address of the extent */ + __le32 blk; /* start block address of the extent */ __le32 len; /* lengh of the extent */ } __packed; @@ -170,14 +184,13 @@ struct f2fs_extent { #define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ #define F2FS_INLINE_DATA 0x02 /* file inline data flag */ +#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */ +#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ +#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ #define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ F2FS_INLINE_XATTR_ADDRS - 1)) -#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) -\ - sizeof(__le32) * (DEF_ADDRS_PER_INODE + \ - DEF_NIDS_PER_INODE - 1)) - struct f2fs_inode { __le16 i_mode; /* file mode */ __u8 i_advise; /* file hints */ @@ -225,6 +238,8 @@ enum { OFFSET_BIT_SHIFT }; +#define OFFSET_BIT_MASK (0x07) /* (0x01 << OFFSET_BIT_SHIFT) - 1 */ + struct node_footer { __le32 nid; /* node id */ __le32 ino; /* inode nunmber */ @@ -402,15 +417,25 @@ typedef __le32 f2fs_hash_t; #define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS) -/* the number of dentry in a block */ -#define NR_DENTRY_IN_BLOCK 214 - /* MAX level for dir lookup */ #define MAX_DIR_HASH_DEPTH 63 /* MAX buckets in one level of dir */ #define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1)) +/* + * space utilization of regular dentry and inline dentry + * regular dentry inline dentry + * bitmap 1 * 27 = 27 1 * 23 = 23 + * reserved 1 * 3 = 3 1 * 7 = 7 + * dentry 11 * 214 = 2354 11 * 182 = 2002 + * filename 8 * 214 = 1712 8 * 182 = 1456 + * total 4096 3488 + * + * Note: there are more reserved space in inline dentry than in regular + * dentry, when converting inline dentry we should handle this carefully. + */ +#define NR_DENTRY_IN_BLOCK 214 /* the number of dentry in a block */ #define SIZE_OF_DIR_ENTRY 11 /* by byte */ #define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \ BITS_PER_BYTE) @@ -435,6 +460,24 @@ struct f2fs_dentry_block { __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; } __packed; +/* for inline dir */ +#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + BITS_PER_BYTE + 1)) +#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \ + BITS_PER_BYTE - 1) / BITS_PER_BYTE) +#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE)) + +/* inline directory entry structure */ +struct f2fs_inline_dentry { + __u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE]; + __u8 reserved[INLINE_RESERVED_SIZE]; + struct f2fs_dir_entry dentry[NR_INLINE_DENTRY]; + __u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN]; +} __packed; + /* file types used in inode_info->flags */ enum { F2FS_FT_UNKNOWN, diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index bbc4de9baef7..78296a10294e 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -14,7 +14,11 @@ { NODE, "NODE" }, \ { DATA, "DATA" }, \ { META, "META" }, \ - { META_FLUSH, "META_FLUSH" }) + { META_FLUSH, "META_FLUSH" }, \ + { INMEM, "INMEM" }, \ + { INMEM_DROP, "INMEM_DROP" }, \ + { IPU, "IN-PLACE" }, \ + { OPU, "OUT-OF-PLACE" }) #define F2FS_BIO_MASK(t) (t & (READA | WRITE_FLUSH_FUA)) #define F2FS_BIO_EXTRA_MASK(t) (t & (REQ_META | REQ_PRIO)) @@ -72,10 +76,13 @@ #define show_cpreason(type) \ __print_symbolic(type, \ { CP_UMOUNT, "Umount" }, \ + { CP_FASTBOOT, "Fastboot" }, \ { CP_SYNC, "Sync" }, \ + { CP_RECOVERY, "Recovery" }, \ { CP_DISCARD, "Discard" }) struct victim_sel_policy; +struct f2fs_map_blocks; DECLARE_EVENT_CLASS(f2fs__inode, @@ -148,14 +155,14 @@ DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter, TRACE_EVENT(f2fs_sync_file_exit, - TP_PROTO(struct inode *inode, bool need_cp, int datasync, int ret), + TP_PROTO(struct inode *inode, int need_cp, int datasync, int ret), TP_ARGS(inode, need_cp, datasync, ret), TP_STRUCT__entry( __field(dev_t, dev) __field(ino_t, ino) - __field(bool, need_cp) + __field(int, need_cp) __field(int, datasync) __field(int, ret) ), @@ -190,7 +197,7 @@ TRACE_EVENT(f2fs_sync_fs, TP_fast_assign( __entry->dev = sb->s_dev; - __entry->dirty = F2FS_SB(sb)->s_dirty; + __entry->dirty = is_sbi_flag_set(F2FS_SB(sb), SBI_IS_DIRTY); __entry->wait = wait; ), @@ -440,69 +447,64 @@ TRACE_EVENT(f2fs_truncate_partial_nodes, __entry->err) ); -TRACE_EVENT_CONDITION(f2fs_submit_page_bio, +TRACE_EVENT(f2fs_map_blocks, + TP_PROTO(struct inode *inode, struct f2fs_map_blocks *map, int ret), - TP_PROTO(struct page *page, sector_t blkaddr, int type), - - TP_ARGS(page, blkaddr, type), - - TP_CONDITION(page->mapping), + TP_ARGS(inode, map, ret), TP_STRUCT__entry( __field(dev_t, dev) __field(ino_t, ino) - __field(pgoff_t, index) - __field(sector_t, blkaddr) - __field(int, type) + __field(block_t, m_lblk) + __field(block_t, m_pblk) + __field(unsigned int, m_len) + __field(int, ret) ), TP_fast_assign( - __entry->dev = page->mapping->host->i_sb->s_dev; - __entry->ino = page->mapping->host->i_ino; - __entry->index = page->index; - __entry->blkaddr = blkaddr; - __entry->type = type; + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->m_lblk = map->m_lblk; + __entry->m_pblk = map->m_pblk; + __entry->m_len = map->m_len; + __entry->ret = ret; ), - TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, " - "blkaddr = 0x%llx, bio_type = %s%s", + TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, " + "start blkaddr = 0x%llx, len = 0x%llx, err = %d", show_dev_ino(__entry), - (unsigned long)__entry->index, - (unsigned long long)__entry->blkaddr, - show_bio_type(__entry->type)) + (unsigned long long)__entry->m_lblk, + (unsigned long long)__entry->m_pblk, + (unsigned long long)__entry->m_len, + __entry->ret) ); -TRACE_EVENT(f2fs_get_data_block, - TP_PROTO(struct inode *inode, sector_t iblock, - struct buffer_head *bh, int ret), +TRACE_EVENT(f2fs_background_gc, - TP_ARGS(inode, iblock, bh, ret), + TP_PROTO(struct super_block *sb, long wait_ms, + unsigned int prefree, unsigned int free), + + TP_ARGS(sb, wait_ms, prefree, free), TP_STRUCT__entry( __field(dev_t, dev) - __field(ino_t, ino) - __field(sector_t, iblock) - __field(sector_t, bh_start) - __field(size_t, bh_size) - __field(int, ret) + __field(long, wait_ms) + __field(unsigned int, prefree) + __field(unsigned int, free) ), TP_fast_assign( - __entry->dev = inode->i_sb->s_dev; - __entry->ino = inode->i_ino; - __entry->iblock = iblock; - __entry->bh_start = bh->b_blocknr; - __entry->bh_size = bh->b_size; - __entry->ret = ret; + __entry->dev = sb->s_dev; + __entry->wait_ms = wait_ms; + __entry->prefree = prefree; + __entry->free = free; ), - TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, " - "start blkaddr = 0x%llx, len = 0x%llx bytes, err = %d", - show_dev_ino(__entry), - (unsigned long long)__entry->iblock, - (unsigned long long)__entry->bh_start, - (unsigned long long)__entry->bh_size, - __entry->ret) + TP_printk("dev = (%d,%d), wait_ms = %ld, prefree = %u, free = %u", + show_dev(__entry), + __entry->wait_ms, + __entry->prefree, + __entry->free) ); TRACE_EVENT(f2fs_get_victim, @@ -680,11 +682,63 @@ TRACE_EVENT(f2fs_reserve_new_block, __entry->ofs_in_node) ); +DECLARE_EVENT_CLASS(f2fs__submit_page_bio, + + TP_PROTO(struct page *page, struct f2fs_io_info *fio), + + TP_ARGS(page, fio), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(pgoff_t, index) + __field(block_t, blkaddr) + __field(int, rw) + __field(int, type) + ), + + TP_fast_assign( + __entry->dev = page->mapping->host->i_sb->s_dev; + __entry->ino = page->mapping->host->i_ino; + __entry->index = page->index; + __entry->blkaddr = fio->blk_addr; + __entry->rw = fio->rw; + __entry->type = fio->type; + ), + + TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, " + "blkaddr = 0x%llx, rw = %s%s, type = %s", + show_dev_ino(__entry), + (unsigned long)__entry->index, + (unsigned long long)__entry->blkaddr, + show_bio_type(__entry->rw), + show_block_type(__entry->type)) +); + +DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_bio, + + TP_PROTO(struct page *page, struct f2fs_io_info *fio), + + TP_ARGS(page, fio), + + TP_CONDITION(page->mapping) +); + +DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio, + + TP_PROTO(struct page *page, struct f2fs_io_info *fio), + + TP_ARGS(page, fio), + + TP_CONDITION(page->mapping) +); + DECLARE_EVENT_CLASS(f2fs__submit_bio, - TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio), + TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, + struct bio *bio), - TP_ARGS(sb, rw, type, bio), + TP_ARGS(sb, fio, bio), TP_STRUCT__entry( __field(dev_t, dev) @@ -696,8 +750,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio, TP_fast_assign( __entry->dev = sb->s_dev; - __entry->rw = rw; - __entry->type = type; + __entry->rw = fio->rw; + __entry->type = fio->type; __entry->sector = bio->bi_iter.bi_sector; __entry->size = bio->bi_iter.bi_size; ), @@ -712,18 +766,20 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio, DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_write_bio, - TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio), + TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, + struct bio *bio), - TP_ARGS(sb, rw, type, bio), + TP_ARGS(sb, fio, bio), TP_CONDITION(bio) ); DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_read_bio, - TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio), + TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, + struct bio *bio), - TP_ARGS(sb, rw, type, bio), + TP_ARGS(sb, fio, bio), TP_CONDITION(bio) ); @@ -831,6 +887,13 @@ DEFINE_EVENT(f2fs__page, f2fs_writepage, TP_ARGS(page, type) ); +DEFINE_EVENT(f2fs__page, f2fs_do_write_data_page, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + DEFINE_EVENT(f2fs__page, f2fs_readpage, TP_PROTO(struct page *page, int type), @@ -852,6 +915,20 @@ DEFINE_EVENT(f2fs__page, f2fs_vm_page_mkwrite, TP_ARGS(page, type) ); +DEFINE_EVENT(f2fs__page, f2fs_register_inmem_page, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +DEFINE_EVENT(f2fs__page, f2fs_commit_inmem_page, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + TRACE_EVENT(f2fs_writepages, TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type), @@ -916,36 +993,30 @@ TRACE_EVENT(f2fs_writepages, __entry->for_sync) ); -TRACE_EVENT(f2fs_submit_page_mbio, +TRACE_EVENT(f2fs_readpages, - TP_PROTO(struct page *page, int rw, int type, block_t blk_addr), + TP_PROTO(struct inode *inode, struct page *page, unsigned int nrpage), - TP_ARGS(page, rw, type, blk_addr), + TP_ARGS(inode, page, nrpage), TP_STRUCT__entry( __field(dev_t, dev) __field(ino_t, ino) - __field(int, rw) - __field(int, type) - __field(pgoff_t, index) - __field(block_t, block) + __field(pgoff_t, start) + __field(unsigned int, nrpage) ), TP_fast_assign( - __entry->dev = page->mapping->host->i_sb->s_dev; - __entry->ino = page->mapping->host->i_ino; - __entry->rw = rw; - __entry->type = type; - __entry->index = page->index; - __entry->block = blk_addr; + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->start = page->index; + __entry->nrpage = nrpage; ), - TP_printk("dev = (%d,%d), ino = %lu, %s%s, %s, index = %lu, blkaddr = 0x%llx", + TP_printk("dev = (%d,%d), ino = %lu, start = %lu nrpage = %u", show_dev_ino(__entry), - show_bio_type(__entry->rw), - show_block_type(__entry->type), - (unsigned long)__entry->index, - (unsigned long long)__entry->block) + (unsigned long)__entry->start, + __entry->nrpage) ); TRACE_EVENT(f2fs_write_checkpoint, @@ -998,14 +1069,15 @@ TRACE_EVENT(f2fs_issue_discard, TRACE_EVENT(f2fs_issue_flush, - TP_PROTO(struct super_block *sb, bool nobarrier, bool flush_merge), + TP_PROTO(struct super_block *sb, unsigned int nobarrier, + unsigned int flush_merge), TP_ARGS(sb, nobarrier, flush_merge), TP_STRUCT__entry( __field(dev_t, dev) - __field(bool, nobarrier) - __field(bool, flush_merge) + __field(unsigned int, nobarrier) + __field(unsigned int, flush_merge) ), TP_fast_assign( @@ -1019,6 +1091,145 @@ TRACE_EVENT(f2fs_issue_flush, __entry->nobarrier ? "skip (nobarrier)" : "issue", __entry->flush_merge ? " with flush_merge" : "") ); + +TRACE_EVENT(f2fs_lookup_extent_tree_start, + + TP_PROTO(struct inode *inode, unsigned int pgofs), + + TP_ARGS(inode, pgofs), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u", + show_dev_ino(__entry), + __entry->pgofs) +); + +TRACE_EVENT_CONDITION(f2fs_lookup_extent_tree_end, + + TP_PROTO(struct inode *inode, unsigned int pgofs, + struct extent_info *ei), + + TP_ARGS(inode, pgofs, ei), + + TP_CONDITION(ei), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + __field(unsigned int, fofs) + __field(u32, blk) + __field(unsigned int, len) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + __entry->fofs = ei->fofs; + __entry->blk = ei->blk; + __entry->len = ei->len; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, " + "ext_info(fofs: %u, blk: %u, len: %u)", + show_dev_ino(__entry), + __entry->pgofs, + __entry->fofs, + __entry->blk, + __entry->len) +); + +TRACE_EVENT(f2fs_update_extent_tree_range, + + TP_PROTO(struct inode *inode, unsigned int pgofs, block_t blkaddr, + unsigned int len), + + TP_ARGS(inode, pgofs, blkaddr, len), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + __field(u32, blk) + __field(unsigned int, len) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + __entry->blk = blkaddr; + __entry->len = len; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, " + "blkaddr = %u, len = %u", + show_dev_ino(__entry), + __entry->pgofs, + __entry->blk, + __entry->len) +); + +TRACE_EVENT(f2fs_shrink_extent_tree, + + TP_PROTO(struct f2fs_sb_info *sbi, unsigned int node_cnt, + unsigned int tree_cnt), + + TP_ARGS(sbi, node_cnt, tree_cnt), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, node_cnt) + __field(unsigned int, tree_cnt) + ), + + TP_fast_assign( + __entry->dev = sbi->sb->s_dev; + __entry->node_cnt = node_cnt; + __entry->tree_cnt = tree_cnt; + ), + + TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u", + show_dev(__entry), + __entry->node_cnt, + __entry->tree_cnt) +); + +TRACE_EVENT(f2fs_destroy_extent_tree, + + TP_PROTO(struct inode *inode, unsigned int node_cnt), + + TP_ARGS(inode, node_cnt), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, node_cnt) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->node_cnt = node_cnt; + ), + + TP_printk("dev = (%d,%d), ino = %lu, destroyed: node_cnt = %u", + show_dev_ino(__entry), + __entry->node_cnt) +); + #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ -- GitLab From 4fe128eba11d8fade2a19a05def0f7f125ed1c31 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Sat, 28 Nov 2015 14:38:40 +0800 Subject: [PATCH 0002/5498] f2fs: Fix a system panic caused by f2fs_follow_link In linux 3.10, we can not make sure the return value of nd_get_link function is valid. So this patch add a check before use it. Signed-off-by: Yunlei He Signed-off-by: Shuoran Liu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 38349a1a3837..191fc2517787 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -319,13 +319,18 @@ fail: static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd) { struct page *page; + char *link; page = page_follow_link_light(dentry, nd); if (IS_ERR(page)) return page; + link = nd_get_link(nd); + if (IS_ERR(link)) + return link; + /* this is broken symlink case */ - if (*nd_get_link(nd) == 0) { + if (*link == 0) { kunmap(page); page_cache_release(page); return ERR_PTR(-ENOENT); -- GitLab From 0f29a041c8cf38f953643433854bbfd4165c73d4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 29 Oct 2015 09:13:04 +0800 Subject: [PATCH 0003/5498] f2fs: report error of f2fs_create_root_stats f2fs_create_root_stats can fail due to no memory, report it to user. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 7 +++++-- fs/f2fs/f2fs.h | 4 ++-- fs/f2fs/super.c | 6 +++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 478e5d54154f..b0966f3b1c9a 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -406,20 +406,23 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi) kfree(si); } -void __init f2fs_create_root_stats(void) +int __init f2fs_create_root_stats(void) { struct dentry *file; f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL); if (!f2fs_debugfs_root) - return; + return -ENOMEM; file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root, NULL, &stat_fops); if (!file) { debugfs_remove(f2fs_debugfs_root); f2fs_debugfs_root = NULL; + return -ENOMEM; } + + return 0; } void f2fs_destroy_root_stats(void) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e6ceeb1bd6bb..c2344fe71ea7 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1985,7 +1985,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) int f2fs_build_stats(struct f2fs_sb_info *); void f2fs_destroy_stats(struct f2fs_sb_info *); -void __init f2fs_create_root_stats(void); +int __init f2fs_create_root_stats(void); void f2fs_destroy_root_stats(void); #else #define stat_inc_cp_count(si) @@ -2013,7 +2013,7 @@ void f2fs_destroy_root_stats(void); static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } -static inline void __init f2fs_create_root_stats(void) { } +static inline int __init f2fs_create_root_stats(void) { return 0; } static inline void f2fs_destroy_root_stats(void) { } #endif diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3a65e0132352..67864ab376c8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1478,10 +1478,14 @@ static int __init init_f2fs_fs(void) err = register_filesystem(&f2fs_fs_type); if (err) goto free_shrinker; - f2fs_create_root_stats(); + err = f2fs_create_root_stats(); + if (err) + goto free_filesystem; f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); return 0; +free_filesystem: + unregister_filesystem(&f2fs_fs_type); free_shrinker: unregister_shrinker(&f2fs_shrinker_info); free_crypto: -- GitLab From 00829e91d72761eff9b36134a5afe3f50337b180 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 28 Oct 2015 17:56:14 +0800 Subject: [PATCH 0004/5498] f2fs: commit atomic written page in LFS mode We should always commit atomic written pages in LFS mode, otherwise data will become corrupted if we encounter suddent power cut after partial pages committed in IPU mode. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ec69f207b708..b6dd053b1cb2 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1084,6 +1084,7 @@ int do_write_data_page(struct f2fs_io_info *fio) */ if (unlikely(fio->blk_addr != NEW_ADDR && !is_cold_data(page) && + !IS_ATOMIC_WRITTEN_PAGE(page) && need_inplace_update(inode))) { rewrite_data_page(fio); set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE); -- GitLab From 6b243a28906120c14c4d0e57c8f23e80568e50b1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 27 Oct 2015 09:53:45 +0800 Subject: [PATCH 0005/5498] f2fs: support file defragment This patch introduces a new ioctl F2FS_IOC_DEFRAGMENT to support file defragment in a specified range of regular file. This ioctl can be used in very limited workload: if user expects high sequential read performance in randomly written file, this interface can be used for defragmentation, after that file can be written as continuous as possible in the device. Meanwhile, it has side-effect, it will make holes in segments where blocks located originally, so it's better to trigger GC to eliminate fragment in segments. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +- fs/f2fs/f2fs.h | 8 ++ fs/f2fs/file.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b6dd053b1cb2..464b4e2c8b30 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -567,7 +567,7 @@ out: * b. do not use extent cache for better performance * c. give the block addresses to blockdev */ -static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, +int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int create, int flag) { unsigned int maxblocks = map->m_len; @@ -1356,6 +1356,10 @@ static int f2fs_write_data_pages(struct address_space *mapping, available_free_memory(sbi, DIRTY_DENTS)) goto skip_write; + /* skip writing during file defragment */ + if (is_inode_flag_set(F2FS_I(inode), FI_DO_DEFRAG)) + goto skip_write; + /* during POR, we don't need to trigger writepage at all. */ if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto skip_write; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c2344fe71ea7..3c0fff5f47cf 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -243,6 +243,7 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, #define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) #define F2FS_IOC_GARBAGE_COLLECT _IO(F2FS_IOCTL_MAGIC, 6) #define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7) +#define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8) #define F2FS_IOC_SET_ENCRYPTION_POLICY \ _IOR('f', 19, struct f2fs_encryption_policy) @@ -259,6 +260,11 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, #define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS #endif +struct f2fs_defragment { + u64 start; + u64 len; +}; + /* * For INODE and NODE manager */ @@ -1415,6 +1421,7 @@ enum { FI_DROP_CACHE, /* drop dirty page cache */ FI_DATA_EXIST, /* indicate data exists */ FI_INLINE_DOTS, /* indicate inline dot dentries */ + FI_DO_DEFRAG, /* indicate defragment is running */ }; static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) @@ -1845,6 +1852,7 @@ struct page *find_data_page(struct inode *, pgoff_t); struct page *get_lock_data_page(struct inode *, pgoff_t, bool); struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool); int do_write_data_page(struct f2fs_io_info *); +int f2fs_map_blocks(struct inode *, struct f2fs_map_blocks *, int, int); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); void f2fs_invalidate_page(struct page *, unsigned int, unsigned int); int f2fs_release_page(struct page *, gfp_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a441953caaf5..a1507acb83ea 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1649,6 +1649,199 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) return 0; } +static int f2fs_defragment_range(struct f2fs_sb_info *sbi, + struct file *filp, + struct f2fs_defragment *range) +{ + struct inode *inode = file_inode(filp); + struct f2fs_map_blocks map; + struct extent_info ei; + pgoff_t pg_start, pg_end; + unsigned int blk_per_seg = 1 << sbi->log_blocks_per_seg; + unsigned int total = 0, sec_num; + unsigned int pages_per_sec = sbi->segs_per_sec * + (1 << sbi->log_blocks_per_seg); + block_t blk_end = 0; + bool fragmented = false; + int err; + + /* if in-place-update policy is enabled, don't waste time here */ + if (need_inplace_update(inode)) + return -EINVAL; + + pg_start = range->start >> PAGE_CACHE_SHIFT; + pg_end = (range->start + range->len) >> PAGE_CACHE_SHIFT; + + f2fs_balance_fs(sbi); + + mutex_lock(&inode->i_mutex); + + /* writeback all dirty pages in the range */ + err = filemap_write_and_wait_range(inode->i_mapping, range->start, + range->start + range->len); + if (err) + goto out; + + /* + * lookup mapping info in extent cache, skip defragmenting if physical + * block addresses are continuous. + */ + if (f2fs_lookup_extent_cache(inode, pg_start, &ei)) { + if (ei.fofs + ei.len >= pg_end) + goto out; + } + + map.m_lblk = pg_start; + map.m_len = pg_end - pg_start; + + /* + * lookup mapping info in dnode page cache, skip defragmenting if all + * physical block addresses are continuous even if there are hole(s) + * in logical blocks. + */ + while (map.m_lblk < pg_end) { + map.m_flags = 0; + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); + if (err) + goto out; + + if (!(map.m_flags & F2FS_MAP_FLAGS)) { + map.m_lblk++; + map.m_len--; + continue; + } + + if (blk_end && blk_end != map.m_pblk) { + fragmented = true; + break; + } + blk_end = map.m_pblk + map.m_len; + + map.m_lblk += map.m_len; + map.m_len = pg_end - map.m_lblk; + } + + if (!fragmented) + goto out; + + map.m_lblk = pg_start; + map.m_len = pg_end - pg_start; + + sec_num = (map.m_len + pages_per_sec - 1) / pages_per_sec; + + /* + * make sure there are enough free section for LFS allocation, this can + * avoid defragment running in SSR mode when free section are allocated + * intensively + */ + if (has_not_enough_free_secs(sbi, sec_num)) { + err = -EAGAIN; + goto out; + } + + while (map.m_lblk < pg_end) { + pgoff_t idx; + int cnt = 0; + +do_map: + map.m_flags = 0; + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); + if (err) + goto clear_out; + + if (!(map.m_flags & F2FS_MAP_FLAGS)) { + map.m_lblk++; + continue; + } + + set_inode_flag(F2FS_I(inode), FI_DO_DEFRAG); + + idx = map.m_lblk; + while (idx < map.m_lblk + map.m_len && cnt < blk_per_seg) { + struct page *page; + + page = get_lock_data_page(inode, idx, true); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto clear_out; + } + + set_page_dirty(page); + f2fs_put_page(page, 1); + + idx++; + cnt++; + total++; + } + + map.m_lblk = idx; + map.m_len = pg_end - idx; + + if (idx < pg_end && cnt < blk_per_seg) + goto do_map; + + clear_inode_flag(F2FS_I(inode), FI_DO_DEFRAG); + + err = filemap_fdatawrite(inode->i_mapping); + if (err) + goto out; + } +clear_out: + clear_inode_flag(F2FS_I(inode), FI_DO_DEFRAG); +out: + mutex_unlock(&inode->i_mutex); + if (!err) + range->len = (u64)total << PAGE_CACHE_SHIFT; + return err; +} + +static int f2fs_ioc_defragment(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_defragment range; + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + err = mnt_want_write_file(filp); + if (err) + return err; + + if (f2fs_readonly(sbi->sb)) { + err = -EROFS; + goto out; + } + + if (copy_from_user(&range, (struct f2fs_defragment __user *)arg, + sizeof(range))) { + err = -EFAULT; + goto out; + } + + /* verify alignment of offset & size */ + if (range.start & (F2FS_BLKSIZE - 1) || + range.len & (F2FS_BLKSIZE - 1)) { + err = -EINVAL; + goto out; + } + + err = f2fs_defragment_range(sbi, filp, &range); + if (err < 0) + goto out; + + if (copy_to_user((struct f2fs_defragment __user *)arg, &range, + sizeof(range))) + err = -EFAULT; +out: + mnt_drop_write_file(filp); + return err; +} + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -1682,6 +1875,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_gc(filp, arg); case F2FS_IOC_WRITE_CHECKPOINT: return f2fs_ioc_write_checkpoint(filp, arg); + case F2FS_IOC_DEFRAGMENT: + return f2fs_ioc_defragment(filp, arg); default: return -ENOTTY; } -- GitLab From 08db178ac9b11e477c23068e32496f0ea08bff6d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 16 Nov 2015 20:38:25 +0800 Subject: [PATCH 0006/5498] f2fs: fix memory leak of kobject in error path of fill_super f2fs_sb_info::s_kobj should be released in error path of fill_super, otherwise it will lead to memory leak. This bug was found by kmemleak: dmesg: kmemleak: 2 new suspected memory leaks (see /sys/kernel/debug/kmemleak) cat /sys/kernel/debug/kmemleak unreferenced object 0xffff8800838dc358 (size 8): comm "mount", pid 4154, jiffies 4297482839 (age 1911.412s) hex dump (first 8 bytes): 7a 72 61 6d 31 00 ff ff zram1... backtrace: [] kmemleak_alloc+0x28/0x50 [] __kmalloc_track_caller+0xef/0x1c0 [] kstrdup+0x45/0x80 [] kstrdup_const+0x28/0x30 [] kvasprintf_const+0x63/0xa0 [] kobject_set_name_vargs+0x3c/0xa0 [] kobject_add_varg+0x25/0x60 [] kobject_init_and_add+0x53/0x70 [] f2fs_fill_super+0x9d9/0xc40 [f2fs] [] mount_bdev+0x192/0x1d0 [] f2fs_mount+0x15/0x20 [f2fs] [] mount_fs+0x43/0x170 [] vfs_kern_mount+0x76/0x160 [] do_mount+0x258/0xdc0 [] SyS_mount+0x7b/0xc0 [] entry_SYSCALL_64_fastpath+0x12/0x6f ... Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 67864ab376c8..bd7e9c6c42c8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1361,6 +1361,8 @@ try_onemore: free_kobj: kobject_del(&sbi->s_kobj); + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); free_proc: if (sbi->s_proc) { remove_proc_entry("segment_info", sbi->s_proc); -- GitLab From 7f0c3c03b767e680f926544921ebbc726960fb95 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 10 Nov 2015 18:44:20 +0800 Subject: [PATCH 0007/5498] f2fs: fix to enable missing ioctl interfaces in ->compat_ioctl In 64-bit kernel f2fs can supports 32-bit ioctl system call by identifying encoded code which is converted from 32-bit one to 64-bit one in ->compat_ioctl. When we introduced new interfaces in ->ioctl, we forgot to enable them in ->compat_ioctl, so enable them for fixing. Signed-off-by: Chao Yu [Jaegeuk Kim: fix wrongly added spaces together] Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 5 +++-- fs/f2fs/file.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3c0fff5f47cf..5a0c63e2dba6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -256,8 +256,9 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, /* * ioctl commands in 32 bit emulation */ -#define F2FS_IOC32_GETFLAGS FS_IOC32_GETFLAGS -#define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS +#define F2FS_IOC32_GETFLAGS FS_IOC32_GETFLAGS +#define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS +#define F2FS_IOC32_GETVERSION FS_IOC32_GETVERSION #endif struct f2fs_defragment { diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a1507acb83ea..4ef150356d55 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1904,6 +1904,22 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC32_SETFLAGS: cmd = F2FS_IOC_SETFLAGS; break; + case F2FS_IOC32_GETVERSION: + cmd = F2FS_IOC_GETVERSION; + break; + case F2FS_IOC_START_ATOMIC_WRITE: + case F2FS_IOC_COMMIT_ATOMIC_WRITE: + case F2FS_IOC_START_VOLATILE_WRITE: + case F2FS_IOC_RELEASE_VOLATILE_WRITE: + case F2FS_IOC_ABORT_VOLATILE_WRITE: + case FS_IOC_SHUTDOWN: + case F2FS_IOC_SET_ENCRYPTION_POLICY: + case F2FS_IOC_GET_ENCRYPTION_PWSALT: + case F2FS_IOC_GET_ENCRYPTION_POLICY: + case F2FS_IOC_GARBAGE_COLLECT: + case F2FS_IOC_WRITE_CHECKPOINT: + case F2FS_IOC_DEFRAGMENT: + break; default: return -ENOIOCTLCMD; } -- GitLab From 27e6ba12db1c2b44d8b2c514164e305b41babfeb Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 10 Nov 2015 18:45:07 +0800 Subject: [PATCH 0008/5498] f2fs: fix to remove directory inode from dirty list If last dirty dentry page was writebacked in reclaim path, we should remove its directory inode from global dirty list to avoid unnecessary flush for this inode when doing checkpoint. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 464b4e2c8b30..fca81e88d481 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1183,8 +1183,10 @@ out: unlock_page(page); if (need_balance_fs) f2fs_balance_fs(sbi); - if (wbc->for_reclaim) + if (wbc->for_reclaim) { f2fs_submit_merged_bio(sbi, DATA, WRITE); + remove_dirty_dir_inode(inode); + } return 0; redirty_out: -- GitLab From 0e6e770d6341790ca4bd8b5427ed573a5b22900b Mon Sep 17 00:00:00 2001 From: Fan Li Date: Thu, 12 Nov 2015 08:43:04 +0800 Subject: [PATCH 0009/5498] f2fs: optimize __find_rev_next_bit 1. Skip __reverse_ulong if the bitmap is empty. 2. Reduce branches and codes. According to my test, the performance of this new version is 5% higher on an empty bitmap of 64bytes, and remains about the same in the worst scenario. Signed-off-by: Fan li Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f77b3258454a..efbf6b5f1dc3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -86,6 +86,7 @@ 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. + * @size must be integral times of unsigned long. * Example: * MSB <--> LSB * f2fs_set_bit(0, bitmap) => 1000 0000 @@ -95,47 +96,36 @@ static unsigned long __find_rev_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { const unsigned long *p = addr + BIT_WORD(offset); - unsigned long result = offset & ~(BITS_PER_LONG - 1); + unsigned long result = size; unsigned long tmp; if (offset >= size) return size; - size -= result; + size -= (offset & ~(BITS_PER_LONG - 1)); offset %= BITS_PER_LONG; - if (!offset) - goto aligned; - tmp = __reverse_ulong((unsigned char *)p); - tmp &= ~0UL >> offset; - - if (size < BITS_PER_LONG) - goto found_first; - if (tmp) - goto found_middle; + while (1) { + if (*p == 0) + goto pass; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - p++; -aligned: - while (size & ~(BITS_PER_LONG-1)) { tmp = __reverse_ulong((unsigned char *)p); + + tmp &= ~0UL >> offset; + if (size < BITS_PER_LONG) + tmp &= (~0UL << (BITS_PER_LONG - size)); if (tmp) - goto found_middle; - result += BITS_PER_LONG; + goto found; +pass: + if (size <= BITS_PER_LONG) + break; size -= BITS_PER_LONG; + offset = 0; p++; } - if (!size) - return result; - - tmp = __reverse_ulong((unsigned char *)p); -found_first: - tmp &= (~0UL << (BITS_PER_LONG - size)); - if (!tmp) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __reverse_ffs(tmp); + return result; +found: + return result - size + __reverse_ffs(tmp); } static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, -- GitLab From 845e5e1ecc896b72b2b03f64132dbae4fbf394f4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 13 Nov 2015 18:27:35 +0800 Subject: [PATCH 0010/5498] f2fs: clear page uptodate when dropping cache for atomic write We should clear uptodate flag for all pages atomic written when we drop them, otherwise before these cached pages were reclaimed or invalidated eventually, we will see invalid data when hitting them again. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index efbf6b5f1dc3..ed2c5dec7526 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -247,6 +247,7 @@ int commit_inmem_pages(struct inode *inode, bool abort) submit_bio = true; } } else { + ClearPageUptodate(cur->page); trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP); } set_page_private(cur->page, 0); -- GitLab From 52d4f80c90f52071eefe4237a5fb8dbd0ab23d08 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 19 Nov 2015 16:09:07 +0800 Subject: [PATCH 0011/5498] f2fs: fix to report error in f2fs_readdir get_lock_data_page in f2fs_readdir can fail due to a lot of reasons (i.e. no memory or IO error...), it's better to report this kind of error to user rather than ignoring it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 73ca524a9aaf..45b88bfe1adc 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -855,8 +855,13 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) for (; n < npages; n++) { dentry_page = get_lock_data_page(inode, n, false); - if (IS_ERR(dentry_page)) - continue; + if (IS_ERR(dentry_page)) { + err = PTR_ERR(dentry_page); + if (err == -ENOENT) + continue; + else + goto out; + } dentry_blk = kmap(dentry_page); -- GitLab From 07e9bf4f8bb157de15b46b4add62e29dd97a32ba Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 30 Nov 2015 16:26:44 -0800 Subject: [PATCH 0012/5498] f2fs: avoid deadlock in f2fs_shrink_extent_tree While handling extent trees, we can enter into a reclaiming path anytime. If it tries to release some extent nodes in the same extent tree, write_lock(&et->lock) would be hanged. In order to avoid the deadlock, we can just skip it. Note that, if it is an unreferenced tree, we should get write_lock(&et->lock) successfully and release all of therein nodes. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 7ddba812e11b..de063f2b2384 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -615,9 +615,10 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) for (i = 0; i < found; i++) { struct extent_tree *et = treevec[i]; - write_lock(&et->lock); - node_cnt += __free_extent_tree(sbi, et, false); - write_unlock(&et->lock); + if (write_trylock(&et->lock)) { + node_cnt += __free_extent_tree(sbi, et, false); + write_unlock(&et->lock); + } if (node_cnt + tree_cnt >= nr_shrink) goto unlock_out; -- GitLab From 0adef6ab9e9644236e492e1345c3e2ea7d39f42d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 3 Dec 2015 14:14:40 -0800 Subject: [PATCH 0013/5498] f2fs: do not recover from previous remained wrong dnodes If device does not support discard, some obsolete dnodes can be recovered by roll-forward. This patch enhances the recovery flow. Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 6a3f04fa34e3..fa6f94adf8f0 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -168,6 +168,32 @@ static void recover_inode(struct inode *inode, struct page *page) ino_of_node(page), name); } +static bool is_same_inode(struct inode *inode, struct page *ipage) +{ + struct f2fs_inode *ri = F2FS_INODE(ipage); + struct timespec disk; + + if (!IS_INODE(ipage)) + return true; + + disk.tv_sec = le64_to_cpu(ri->i_ctime); + disk.tv_nsec = le32_to_cpu(ri->i_ctime_nsec); + if (timespec_compare(&inode->i_ctime, &disk) > 0) + return false; + + disk.tv_sec = le64_to_cpu(ri->i_atime); + disk.tv_nsec = le32_to_cpu(ri->i_atime_nsec); + if (timespec_compare(&inode->i_atime, &disk) > 0) + return false; + + disk.tv_sec = le64_to_cpu(ri->i_mtime); + disk.tv_nsec = le32_to_cpu(ri->i_mtime_nsec); + if (timespec_compare(&inode->i_mtime, &disk) > 0) + return false; + + return true; +} + static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) { unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); @@ -197,7 +223,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) goto next; entry = get_fsync_inode(head, ino_of_node(page)); - if (!entry) { + if (entry) { + if (!is_same_inode(entry->inode, page)) + goto next; + } else { if (IS_INODE(page) && is_dent_dnode(page)) { err = recover_inode_page(sbi, page); if (err) -- GitLab From a07cb1907d9a5fd85e6c411a7def2c245ec1b9db Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 1 Dec 2015 11:41:50 +0800 Subject: [PATCH 0014/5498] f2fs: clean up error path in f2fs_readdir No logic changes, just clean up the error path. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 45b88bfe1adc..78eeee09df57 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -867,18 +867,15 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) make_dentry_ptr(inode, &d, (void *)dentry_blk, 1); - if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) - goto stop; + if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) { + kunmap(dentry_page); + f2fs_put_page(dentry_page, 1); + break; + } ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; kunmap(dentry_page); f2fs_put_page(dentry_page, 1); - dentry_page = NULL; - } -stop: - if (dentry_page && !IS_ERR(dentry_page)) { - kunmap(dentry_page); - f2fs_put_page(dentry_page, 1); } out: f2fs_fname_crypto_free_buffer(&fstr); -- GitLab From 74b6eb9509e14b619a12c0395fd705276918f0fc Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 1 Dec 2015 11:42:54 +0800 Subject: [PATCH 0015/5498] f2fs: clean up code with __has_cursum_space Clean up codes in lookup_journal_in_cursum() with __has_cursum_space(). Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ed2c5dec7526..74c474821e5a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1740,13 +1740,13 @@ int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type, if (le32_to_cpu(nid_in_journal(sum, i)) == val) return i; } - if (alloc && nats_in_cursum(sum) < NAT_JOURNAL_ENTRIES) + if (alloc && __has_cursum_space(sum, 1, NAT_JOURNAL)) return update_nats_in_cursum(sum, 1); } else if (type == SIT_JOURNAL) { for (i = 0; i < sits_in_cursum(sum); i++) if (le32_to_cpu(segno_in_journal(sum, i)) == val) return i; - if (alloc && sits_in_cursum(sum) < SIT_JOURNAL_ENTRIES) + if (alloc && __has_cursum_space(sum, 1, SIT_JOURNAL)) return update_sits_in_cursum(sum, 1); } return -1; -- GitLab From 0a174760887969f0341cfef3d05db193b2af8b1f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 1 Dec 2015 11:43:59 +0800 Subject: [PATCH 0016/5498] f2fs: clean up argument of recover_data In recover_data, value of argument 'type' will be CURSEG_WARM_NODE all the time, remove it for cleanup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index fa6f94adf8f0..590f8b3d7d24 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -488,8 +488,7 @@ out: return err; } -static int recover_data(struct f2fs_sb_info *sbi, - struct list_head *head, int type) +static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head) { unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); struct curseg_info *curseg; @@ -498,7 +497,7 @@ static int recover_data(struct f2fs_sb_info *sbi, block_t blkaddr; /* get node pages in the current segment */ - curseg = CURSEG_I(sbi, type); + curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); while (1) { @@ -585,7 +584,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi) need_writecp = true; /* step #2: recover data */ - err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE); + err = recover_data(sbi, &inode_list); if (!err) f2fs_bug_on(sbi, !list_empty(&inode_list)); out: -- GitLab From ea95d5c8e9aa298077da866cfa6f299e5a167fab Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 1 Dec 2015 11:47:33 +0800 Subject: [PATCH 0017/5498] f2fs: kill f2fs_drop_largest_extent For direct IO, f2fs only allocate new address for the block which is not exist in the disk before, its mapping info should not exist in extent cache previously, so here we do not need to call f2fs_drop_largest_extent to drop related cache. Due to no more callers for f2fs_drop_largest_extent now, kill it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ---- fs/f2fs/extent_cache.c | 8 -------- fs/f2fs/f2fs.h | 1 - 3 files changed, 13 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index fca81e88d481..a0d53a38f58a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -495,10 +495,6 @@ alloc: if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)) i_size_write(dn->inode, ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)); - - /* direct IO doesn't use extent cache to maximize the performance */ - f2fs_drop_largest_extent(dn->inode, fofs); - return 0; } diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index de063f2b2384..e86e9f1e0733 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -164,14 +164,6 @@ static void __drop_largest_extent(struct inode *inode, largest->len = 0; } -void f2fs_drop_largest_extent(struct inode *inode, pgoff_t fofs) -{ - if (!f2fs_may_extent_tree(inode)) - return; - - __drop_largest_extent(inode, fofs, 1); -} - void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5a0c63e2dba6..21844c9508a6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2076,7 +2076,6 @@ void f2fs_leave_shrinker(struct f2fs_sb_info *); * extent_cache.c */ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int); -void f2fs_drop_largest_extent(struct inode *, pgoff_t); void f2fs_init_extent_tree(struct inode *, struct f2fs_extent *); unsigned int f2fs_destroy_extent_node(struct inode *); void f2fs_destroy_extent_tree(struct inode *); -- GitLab From d41fe85dbdb156e1d08f3063a946b9bdd156fd3e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 1 Dec 2015 11:56:52 +0800 Subject: [PATCH 0018/5498] f2fs: use sbi->blocks_per_seg to avoid unnecessary calculation Use sbi->blocks_per_seg directly to avoid unnecessary calculation when using 1 << sbi->log_blocks_per_seg. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 2 +- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/file.c | 5 ++--- fs/f2fs/gc.c | 4 ++-- fs/f2fs/node.h | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index b0966f3b1c9a..8ce2fe3f65ab 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -105,7 +105,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi) bimodal = 0; total_vblocks = 0; - blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg); + blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg; hblks_per_sec = blks_per_sec / 2; for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 21844c9508a6..b7a589efccf2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1098,8 +1098,7 @@ static inline int get_dirty_pages(struct inode *inode) static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type) { - unsigned int pages_per_sec = sbi->segs_per_sec * - (1 << sbi->log_blocks_per_seg); + unsigned int pages_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg; return ((get_pages(sbi, block_type) + pages_per_sec - 1) >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; } diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4ef150356d55..44cfdbd44dd3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1657,10 +1657,9 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, struct f2fs_map_blocks map; struct extent_info ei; pgoff_t pg_start, pg_end; - unsigned int blk_per_seg = 1 << sbi->log_blocks_per_seg; + unsigned int blk_per_seg = sbi->blocks_per_seg; unsigned int total = 0, sec_num; - unsigned int pages_per_sec = sbi->segs_per_sec * - (1 << sbi->log_blocks_per_seg); + unsigned int pages_per_sec = sbi->segs_per_sec * blk_per_seg; block_t blk_end = 0; bool fragmented = false; int err; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index fedbf67a0842..ce350c44b5cf 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -173,9 +173,9 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi, { /* SSR allocates in a segment unit */ if (p->alloc_mode == SSR) - return 1 << sbi->log_blocks_per_seg; + return sbi->blocks_per_seg; if (p->gc_mode == GC_GREEDY) - return (1 << sbi->log_blocks_per_seg) * p->ofs_unit; + return sbi->blocks_per_seg * p->ofs_unit; else if (p->gc_mode == GC_CB) return UINT_MAX; else /* No other gc_mode */ diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index e4fffd2d98c4..2de759a7746f 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -183,7 +183,7 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start) block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + - (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + (block_off & (sbi->blocks_per_seg - 1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; -- GitLab From edae2c053eabc30f9fa6f824ce84ec34c987f1ae Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 1 Dec 2015 11:36:16 +0800 Subject: [PATCH 0019/5498] f2fs: fix to convert inline inode in ->setattr In commit 3c4541452748 ("f2fs: do not trim preallocated blocks when truncating after i_size"), in order to follow the regulation: "truncate(x) where x > i_size will not trim all blocks past i_size." like other file systems, in ->setattr we invoked truncate_setsize instead of f2fs_truncate to avoid unneeded block trimming in such case, but forgot to call f2fs_convert_inline_inode keep consistency of inline data conversion rule. This patch fixes to convert inline data if necessary. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 44cfdbd44dd3..de3334895700 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -687,6 +687,14 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) * larger than i_size. */ truncate_setsize(inode, attr->ia_size); + + /* should convert inline inode here */ + if (f2fs_has_inline_data(inode) && + !f2fs_may_inline_data(inode)) { + err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } inode->i_mtime = inode->i_ctime = CURRENT_TIME; } } -- GitLab From a0b3487ab6310fe421261d0a59899be12712060d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 4 Dec 2015 16:51:13 -0800 Subject: [PATCH 0020/5498] f2fs: enhance the bit operation for SSR This patch enhances the existing bit operation when f2fs allocates SSR blocks. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 50 +++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 74c474821e5a..5fa519f02860 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -132,47 +132,37 @@ static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { const unsigned long *p = addr + BIT_WORD(offset); - unsigned long result = offset & ~(BITS_PER_LONG - 1); + unsigned long result = size; unsigned long tmp; if (offset >= size) return size; - size -= result; + size -= (offset & ~(BITS_PER_LONG - 1)); offset %= BITS_PER_LONG; - if (!offset) - goto aligned; - - tmp = __reverse_ulong((unsigned char *)p); - tmp |= ~((~0UL << offset) >> offset); - - if (size < BITS_PER_LONG) - goto found_first; - if (tmp != ~0UL) - goto found_middle; - - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - p++; -aligned: - while (size & ~(BITS_PER_LONG - 1)) { + + while (1) { + if (*p == ~0UL) + goto pass; + tmp = __reverse_ulong((unsigned char *)p); + + if (offset) + tmp |= ~0UL << (BITS_PER_LONG - offset); + if (size < BITS_PER_LONG) + tmp |= ~0UL >> size; if (tmp != ~0UL) - goto found_middle; - result += BITS_PER_LONG; + goto found; +pass: + if (size <= BITS_PER_LONG) + break; size -= BITS_PER_LONG; + offset = 0; p++; } - if (!size) - return result; - - tmp = __reverse_ulong((unsigned char *)p); -found_first: - tmp |= ~(~0UL << (BITS_PER_LONG - size)); - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ -found_middle: - return result + __reverse_ffz(tmp); + return result; +found: + return result - size + __reverse_ffz(tmp); } void register_inmem_page(struct inode *inode, struct page *page) -- GitLab From a8c2c3fc31a1f28db85e84a8864552985ffee9c3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 7 Dec 2015 10:16:58 -0800 Subject: [PATCH 0021/5498] f2fs: refactor f2fs_commit_super Previously, f2fs_commit_super hacks the bh->blocknr to write the broken alternate superblock. Instead of it, we should use the correct logic to retrieve its buffer head with locking it appropriately. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index bd7e9c6c42c8..dbf16ade0e9a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1099,27 +1099,35 @@ out: int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) { struct buffer_head *sbh = sbi->raw_super_buf; - sector_t block = sbh->b_blocknr; + struct buffer_head *bh; int err; /* write back-up superblock first */ - sbh->b_blocknr = block ? 0 : 1; - mark_buffer_dirty(sbh); - err = sync_dirty_buffer(sbh); + bh = sb_getblk(sbi->sb, sbh->b_blocknr ? 0 : 1); + if (!bh) + return -EIO; - sbh->b_blocknr = block; + lock_buffer(bh); + memcpy(bh->b_data, sbh->b_data, sbh->b_size); + WARN_ON(sbh->b_size != F2FS_BLKSIZE); + set_buffer_uptodate(bh); + set_buffer_dirty(bh); + unlock_buffer(bh); + + /* it's rare case, we can do fua all the time */ + err = __sync_dirty_buffer(bh, WRITE_FLUSH_FUA); + brelse(bh); /* if we are in recovery path, skip writing valid superblock */ if (recover || err) - goto out; + return err; /* write current valid superblock */ - mark_buffer_dirty(sbh); - err = sync_dirty_buffer(sbh); -out: - clear_buffer_write_io_error(sbh); - set_buffer_uptodate(sbh); - return err; + lock_buffer(sbh); + set_buffer_dirty(sbh); + unlock_buffer(sbh); + + return __sync_dirty_buffer(sbh, WRITE_FLUSH_FUA); } static int f2fs_fill_super(struct super_block *sb, void *data, int silent) -- GitLab From 27b127485b63997cd7efd9390fafdb7ad9b9812d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 7 Dec 2015 10:18:54 -0800 Subject: [PATCH 0022/5498] f2fs: use lock_buffer when changing superblock When modifying sb contents, we need to use lock its buffer. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index de3334895700..826fc541f0c8 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1594,14 +1594,18 @@ static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) return err; /* update superblock with uuid */ + lock_buffer(sbi->raw_super_buf); generate_random_uuid(sbi->raw_super->encrypt_pw_salt); + unlock_buffer(sbi->raw_super_buf); err = f2fs_commit_super(sbi, false); mnt_drop_write_file(filp); if (err) { /* undo new data */ + lock_buffer(sbi->raw_super_buf); memset(sbi->raw_super->encrypt_pw_salt, 0, 16); + unlock_buffer(sbi->raw_super_buf); return err; } got_it: -- GitLab From bccc413293892005deaee02ee2b81837e8a190d1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 11 Dec 2015 16:08:22 +0800 Subject: [PATCH 0023/5498] f2fs: clean up node page updating flow If read_node_page return LOCKED_PAGE, in its caller it's better a) skip unneeded 'Update' flag and mapping info verfication; b) check nid value stored in footer structure of node page. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/node.c --- fs/f2fs/node.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 413d7724bdf9..f7e2184235cd 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1063,12 +1063,13 @@ repeat: if (err < 0) { f2fs_put_page(page, 1); return ERR_PTR(err); - } else if (err != LOCKED_PAGE) { - lock_page(page); + } else if (err == LOCKED_PAGE) { + goto page_hit; } - if (unlikely(!PageUptodate(page) || nid != nid_of_node(page))) { - ClearPageUptodate(page); + lock_page(page); + + if (unlikely(!PageUptodate(page))) { f2fs_put_page(page, 1); return ERR_PTR(-EIO); } @@ -1076,7 +1077,9 @@ repeat: f2fs_put_page(page, 1); goto repeat; } +page_hit: mark_page_accessed(page); + f2fs_bug_on(sbi, nid != nid_of_node(page)); return page; } @@ -1115,15 +1118,19 @@ repeat: end = start + MAX_RA_NODE; end = min(end, NIDS_PER_BLOCK); for (i = start + 1; i < end; i++) { - nid = get_nid(parent, i, false); - if (!nid) + nid_t tnid = get_nid(parent, i, false); + if (!tnid) continue; - ra_node_page(sbi, nid); + ra_node_page(sbi, tnid); } blk_finish_plug(&plug); lock_page(page); + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 1); + return ERR_PTR(-EIO); + } if (unlikely(page->mapping != NODE_MAPPING(sbi))) { f2fs_put_page(page, 1); goto repeat; @@ -1134,6 +1141,7 @@ page_hit: return ERR_PTR(-EIO); } mark_page_accessed(page); + f2fs_bug_on(sbi, nid != nid_of_node(page)); return page; } -- GitLab From 6b16036dedffe992d18d1fd5475d617d88a5055c Mon Sep 17 00:00:00 2001 From: Fan Li Date: Mon, 14 Dec 2015 13:34:00 +0800 Subject: [PATCH 0024/5498] f2fs: write only the pages in range during defragment @lend of filemap_write_and_wait_range is supposed to be a "offset in bytes where the range ends (inclusive)". Subtract 1 to avoid writing an extra page. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 826fc541f0c8..fbf1cdd66be4 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1689,7 +1689,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, /* writeback all dirty pages in the range */ err = filemap_write_and_wait_range(inode->i_mapping, range->start, - range->start + range->len); + range->start + range->len - 1); if (err) goto out; -- GitLab From f0a925bf223d94228ea86b4a50490e9028afab0a Mon Sep 17 00:00:00 2001 From: Fan Li Date: Mon, 14 Dec 2015 15:26:04 +0800 Subject: [PATCH 0025/5498] f2fs: fix to update variable correctly when skip a unmapped block map.m_len should be reduced after skip a block Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index fbf1cdd66be4..c12164bdf280 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1762,6 +1762,7 @@ do_map: if (!(map.m_flags & F2FS_MAP_FLAGS)) { map.m_lblk++; + map.m_len--; continue; } -- GitLab From 0adcb0ade6632f9a378b64e4d7f15f9c1401e069 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 15 Dec 2015 09:58:18 +0800 Subject: [PATCH 0026/5498] f2fs: do more integrity verification for superblock Do more sanity check for superblock during ->mount. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index dbf16ade0e9a..a6f2beda22b9 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -918,6 +918,79 @@ static loff_t max_file_size(unsigned bits) return result; } +static inline bool sanity_check_area_boundary(struct super_block *sb, + struct f2fs_super_block *raw_super) +{ + u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); + u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr); + u32 sit_blkaddr = le32_to_cpu(raw_super->sit_blkaddr); + u32 nat_blkaddr = le32_to_cpu(raw_super->nat_blkaddr); + u32 ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); + u32 main_blkaddr = le32_to_cpu(raw_super->main_blkaddr); + u32 segment_count_ckpt = le32_to_cpu(raw_super->segment_count_ckpt); + u32 segment_count_sit = le32_to_cpu(raw_super->segment_count_sit); + u32 segment_count_nat = le32_to_cpu(raw_super->segment_count_nat); + u32 segment_count_ssa = le32_to_cpu(raw_super->segment_count_ssa); + u32 segment_count_main = le32_to_cpu(raw_super->segment_count_main); + u32 segment_count = le32_to_cpu(raw_super->segment_count); + u32 log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); + + if (segment0_blkaddr != cp_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Mismatch start address, segment0(%u) cp_blkaddr(%u)", + segment0_blkaddr, cp_blkaddr); + return true; + } + + if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) != + sit_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong CP boundary, start(%u) end(%u) blocks(%u)", + cp_blkaddr, sit_blkaddr, + segment_count_ckpt << log_blocks_per_seg); + return true; + } + + if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) != + nat_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong SIT boundary, start(%u) end(%u) blocks(%u)", + sit_blkaddr, nat_blkaddr, + segment_count_sit << log_blocks_per_seg); + return true; + } + + if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) != + ssa_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong NAT boundary, start(%u) end(%u) blocks(%u)", + nat_blkaddr, ssa_blkaddr, + segment_count_nat << log_blocks_per_seg); + return true; + } + + if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) != + main_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong SSA boundary, start(%u) end(%u) blocks(%u)", + ssa_blkaddr, main_blkaddr, + segment_count_ssa << log_blocks_per_seg); + return true; + } + + if (main_blkaddr + (segment_count_main << log_blocks_per_seg) != + segment0_blkaddr + (segment_count << log_blocks_per_seg)) { + f2fs_msg(sb, KERN_INFO, + "Wrong MAIN_AREA boundary, start(%u) end(%u) blocks(%u)", + main_blkaddr, + segment0_blkaddr + (segment_count << log_blocks_per_seg), + segment_count_main << log_blocks_per_seg); + return true; + } + + return false; +} + static int sanity_check_raw_super(struct super_block *sb, struct f2fs_super_block *raw_super) { @@ -947,6 +1020,14 @@ static int sanity_check_raw_super(struct super_block *sb, return 1; } + /* check log blocks per segment */ + if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) { + f2fs_msg(sb, KERN_INFO, + "Invalid log blocks per segment (%u)\n", + le32_to_cpu(raw_super->log_blocks_per_seg)); + return 1; + } + /* Currently, support 512/1024/2048/4096 bytes sector size */ if (le32_to_cpu(raw_super->log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE || @@ -965,6 +1046,23 @@ static int sanity_check_raw_super(struct super_block *sb, le32_to_cpu(raw_super->log_sectorsize)); return 1; } + + /* check reserved ino info */ + if (le32_to_cpu(raw_super->node_ino) != 1 || + le32_to_cpu(raw_super->meta_ino) != 2 || + le32_to_cpu(raw_super->root_ino) != 3) { + f2fs_msg(sb, KERN_INFO, + "Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)", + le32_to_cpu(raw_super->node_ino), + le32_to_cpu(raw_super->meta_ino), + le32_to_cpu(raw_super->root_ino)); + return 1; + } + + /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */ + if (sanity_check_area_boundary(sb, raw_super)) + return 1; + return 0; } -- GitLab From 3772d99a41c565982269a34efc3279bf858bc946 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 14 Dec 2015 18:58:42 -0800 Subject: [PATCH 0027/5498] f2fs: add symbol to avoid any confusion with tools This patch adds MAX_VOLUME_NAME to sync with f2fs-tools. Signed-off-by: Jaegeuk Kim --- include/linux/f2fs_fs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 25c6324a0dd0..e59c3be92106 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -51,6 +51,7 @@ #define MAX_ACTIVE_DATA_LOGS 8 #define VERSION_LEN 256 +#define MAX_VOLUME_NAME 512 /* * For superblock @@ -84,7 +85,7 @@ struct f2fs_super_block { __le32 node_ino; /* node inode number */ __le32 meta_ino; /* meta inode number */ __u8 uuid[16]; /* 128-bit uuid for volume */ - __le16 volume_name[512]; /* volume name */ + __le16 volume_name[MAX_VOLUME_NAME]; /* volume name */ __le32 extension_count; /* # of extensions below */ __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */ __le32 cp_payload; -- GitLab From 0c6a64ed21a04767c6b155720e89a0f43a76f76a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 15 Dec 2015 13:29:47 +0800 Subject: [PATCH 0028/5498] f2fs: rename {add,remove,release}_dirty_inode to {add,remove,release}_ino_entry remove_dirty_dir_inode will be renamed to remove_dirty_inode as a generic function in following patch for removing directory/regular/symlink inode in global dirty list. Here rename ino management related functions for readability, also in order to avoid name conflict. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 8 ++++---- fs/f2fs/f2fs.h | 6 +++--- fs/f2fs/file.c | 4 ++-- fs/f2fs/inode.c | 4 ++-- fs/f2fs/super.c | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 463a67cc6cdb..914296ee3e18 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -411,13 +411,13 @@ static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) spin_unlock(&im->ino_lock); } -void add_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type) +void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) { /* add new dirty ino entry into list */ __add_ino_entry(sbi, ino, type); } -void remove_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type) +void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) { /* remove dirty ino entry from list */ __remove_ino_entry(sbi, ino, type); @@ -435,7 +435,7 @@ bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode) return e ? true : false; } -void release_dirty_inode(struct f2fs_sb_info *sbi) +void release_ino_entry(struct f2fs_sb_info *sbi) { struct ino_entry *e, *tmp; int i; @@ -1082,7 +1082,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) invalidate_mapping_pages(META_MAPPING(sbi), discard_blk, discard_blk); - release_dirty_inode(sbi); + release_ino_entry(sbi); if (unlikely(f2fs_cp_error(sbi))) return; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b7a589efccf2..7f58739e7156 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1818,9 +1818,9 @@ bool is_valid_blkaddr(struct f2fs_sb_info *, block_t, int); int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int, bool); void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t); long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); -void add_dirty_inode(struct f2fs_sb_info *, nid_t, int type); -void remove_dirty_inode(struct f2fs_sb_info *, nid_t, int type); -void release_dirty_inode(struct f2fs_sb_info *); +void add_ino_entry(struct f2fs_sb_info *, nid_t, int type); +void remove_ino_entry(struct f2fs_sb_info *, nid_t, int type); +void release_ino_entry(struct f2fs_sb_info *); bool exist_written_data(struct f2fs_sb_info *, nid_t, int); int acquire_orphan_inode(struct f2fs_sb_info *); void release_orphan_inode(struct f2fs_sb_info *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c12164bdf280..9b9ef5e81fc6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -276,10 +276,10 @@ sync_nodes: goto out; /* once recovery info is written, don't need to tack this */ - remove_dirty_inode(sbi, ino, APPEND_INO); + remove_ino_entry(sbi, ino, APPEND_INO); clear_inode_flag(fi, FI_APPEND_WRITE); flush_out: - remove_dirty_inode(sbi, ino, UPDATE_INO); + remove_ino_entry(sbi, ino, UPDATE_INO); clear_inode_flag(fi, FI_UPDATE_WRITE); ret = f2fs_issue_flush(sbi); out: diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 54927b62f525..cb1d6ff30959 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -357,9 +357,9 @@ no_delete: if (xnid) invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid); if (is_inode_flag_set(fi, FI_APPEND_WRITE)) - add_dirty_inode(sbi, inode->i_ino, APPEND_INO); + add_ino_entry(sbi, inode->i_ino, APPEND_INO); if (is_inode_flag_set(fi, FI_UPDATE_WRITE)) - add_dirty_inode(sbi, inode->i_ino, UPDATE_INO); + add_ino_entry(sbi, inode->i_ino, UPDATE_INO); if (is_inode_flag_set(fi, FI_FREE_NID)) { if (err && err != -ENOENT) alloc_nid_done(sbi, inode->i_ino); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a6f2beda22b9..9ee7144c5367 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -548,7 +548,7 @@ static void f2fs_put_super(struct super_block *sb) * normally superblock is clean, so we need to release this. * In addition, EIO will skip do checkpoint, we need this as well. */ - release_dirty_inode(sbi); + release_ino_entry(sbi); release_discard_addrs(sbi); f2fs_leave_shrinker(sbi); -- GitLab From 142f41a573128818fa277c8466fe0b52cbe70055 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 15 Dec 2015 13:30:45 +0800 Subject: [PATCH 0029/5498] f2fs: introduce dirty list node in inode info Add a new dirt list node member in inode info for linking the inode to global dirty list in superblock, instead of old implementation which allocate slab cache memory as an entry to inode. It avoids memory pressure due to slab cache allocation, and also makes codes more clean. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 54 ++++++++++++++------------------------------ fs/f2fs/debug.c | 1 - fs/f2fs/f2fs.h | 10 ++------ fs/f2fs/super.c | 1 + 4 files changed, 20 insertions(+), 46 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 914296ee3e18..8f7c59d81130 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -723,25 +723,23 @@ fail_no_cp: return -EINVAL; } -static int __add_dirty_inode(struct inode *inode, struct inode_entry *new) +static void __add_dirty_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); - if (is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR)) - return -EEXIST; + if (is_inode_flag_set(fi, FI_DIRTY_DIR)) + return; - set_inode_flag(F2FS_I(inode), FI_DIRTY_DIR); - F2FS_I(inode)->dirty_dir = new; - list_add_tail(&new->list, &sbi->dir_inode_list); + set_inode_flag(fi, FI_DIRTY_DIR); + list_add_tail(&fi->dirty_list, &sbi->dir_inode_list); stat_inc_dirty_dir(sbi); - return 0; + return; } void update_dirty_page(struct inode *inode, struct page *page) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct inode_entry *new; - int ret = 0; if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) @@ -752,17 +750,11 @@ void update_dirty_page(struct inode *inode, struct page *page) goto out; } - new = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS); - new->inode = inode; - INIT_LIST_HEAD(&new->list); - spin_lock(&sbi->dir_inode_lock); - ret = __add_dirty_inode(inode, new); + __add_dirty_inode(inode); inode_inc_dirty_pages(inode); spin_unlock(&sbi->dir_inode_lock); - if (ret) - kmem_cache_free(inode_entry_slab, new); out: SetPagePrivate(page); f2fs_trace_pid(page); @@ -771,25 +763,16 @@ out: void add_dirty_dir_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct inode_entry *new = - f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS); - int ret = 0; - - new->inode = inode; - INIT_LIST_HEAD(&new->list); spin_lock(&sbi->dir_inode_lock); - ret = __add_dirty_inode(inode, new); + __add_dirty_inode(inode); spin_unlock(&sbi->dir_inode_lock); - - if (ret) - kmem_cache_free(inode_entry_slab, new); } void remove_dirty_dir_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct inode_entry *entry; + struct f2fs_inode_info *fi = F2FS_I(inode); if (!S_ISDIR(inode->i_mode)) return; @@ -801,17 +784,14 @@ void remove_dirty_dir_inode(struct inode *inode) return; } - entry = F2FS_I(inode)->dirty_dir; - list_del(&entry->list); - F2FS_I(inode)->dirty_dir = NULL; - clear_inode_flag(F2FS_I(inode), FI_DIRTY_DIR); + list_del_init(&fi->dirty_list); + clear_inode_flag(fi, FI_DIRTY_DIR); stat_dec_dirty_dir(sbi); spin_unlock(&sbi->dir_inode_lock); - kmem_cache_free(inode_entry_slab, entry); /* Only from the recovery routine */ - if (is_inode_flag_set(F2FS_I(inode), FI_DELAY_IPUT)) { - clear_inode_flag(F2FS_I(inode), FI_DELAY_IPUT); + if (is_inode_flag_set(fi, FI_DELAY_IPUT)) { + clear_inode_flag(fi, FI_DELAY_IPUT); iput(inode); } } @@ -819,8 +799,8 @@ void remove_dirty_dir_inode(struct inode *inode) void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi) { struct list_head *head; - struct inode_entry *entry; struct inode *inode; + struct f2fs_inode_info *fi; retry: if (unlikely(f2fs_cp_error(sbi))) return; @@ -832,8 +812,8 @@ retry: spin_unlock(&sbi->dir_inode_lock); return; } - entry = list_entry(head->next, struct inode_entry, list); - inode = igrab(entry->inode); + fi = list_entry(head->next, struct f2fs_inode_info, dirty_list); + inode = igrab(&fi->vfs_inode); spin_unlock(&sbi->dir_inode_lock); if (inode) { filemap_fdatawrite(inode->i_mapping); diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 8ce2fe3f65ab..f4a7b9e9416d 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -189,7 +189,6 @@ get_cache: si->cache_mem += NM_I(sbi)->dirty_nat_cnt * sizeof(struct nat_entry_set); si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); - si->cache_mem += sbi->n_dirty_dirs * sizeof(struct inode_entry); for (i = 0; i <= UPDATE_INO; i++) si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7f58739e7156..f9081784f739 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -158,13 +158,7 @@ struct ino_entry { nid_t ino; /* inode number */ }; -/* - * for the list of directory inodes or gc inodes. - * NOTE: there are two slab users for this structure, if we add/modify/delete - * fields in structure for one of slab users, it may affect fields or size of - * other one, in this condition, it's better to split both of slab and related - * data structure. - */ +/* for the list of inodes to be GCed */ struct inode_entry { struct list_head list; /* list head */ struct inode *inode; /* vfs inode pointer */ @@ -440,8 +434,8 @@ struct f2fs_inode_info { unsigned int clevel; /* maximum level of given file name */ nid_t i_xattr_nid; /* node id that contains xattrs */ unsigned long long xattr_ver; /* cp version of xattr modification */ - struct inode_entry *dirty_dir; /* the pointer of dirty dir */ + struct list_head dirty_list; /* linked in global dirty list */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct mutex inmem_lock; /* lock for inmemory pages */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9ee7144c5367..4031f8ed9d24 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -432,6 +432,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) fi->i_current_depth = 1; fi->i_advise = 0; init_rwsem(&fi->i_sem); + INIT_LIST_HEAD(&fi->dirty_list); INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); -- GitLab From 37fc3bede4a633de3ac65fa38c6cd9626db6c1a1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 15 Dec 2015 13:31:40 +0800 Subject: [PATCH 0030/5498] f2fs: introduce __remove_dirty_inode Introduce __remove_dirty_inode to clean up codes in remove_dirty_dir_inode. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 8f7c59d81130..df084173e21d 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -737,6 +737,20 @@ static void __add_dirty_inode(struct inode *inode) return; } +static void __remove_dirty_inode(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); + + if (get_dirty_pages(inode) || + !is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR)) + return; + + list_del_init(&fi->dirty_list); + clear_inode_flag(fi, FI_DIRTY_DIR); + stat_dec_dirty_dir(sbi); +} + void update_dirty_page(struct inode *inode, struct page *page) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -778,15 +792,7 @@ void remove_dirty_dir_inode(struct inode *inode) return; spin_lock(&sbi->dir_inode_lock); - if (get_dirty_pages(inode) || - !is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR)) { - spin_unlock(&sbi->dir_inode_lock); - return; - } - - list_del_init(&fi->dirty_list); - clear_inode_flag(fi, FI_DIRTY_DIR); - stat_dec_dirty_dir(sbi); + __remove_dirty_inode(inode); spin_unlock(&sbi->dir_inode_lock); /* Only from the recovery routine */ -- GitLab From 047f75d5768e1cfc5d291d06e950c5daf6514e3d Mon Sep 17 00:00:00 2001 From: Fan Li Date: Tue, 15 Dec 2015 17:02:41 +0800 Subject: [PATCH 0031/5498] f2fs: fix to reset variable correctlly f2fs_map_blocks will set m_flags and m_len to 0, so we don't need to reset m_flags ourselves, but have to reset m_len to correct value before use it again. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 9b9ef5e81fc6..b9f4b745ead2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1703,7 +1703,6 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, } map.m_lblk = pg_start; - map.m_len = pg_end - pg_start; /* * lookup mapping info in dnode page cache, skip defragmenting if all @@ -1711,14 +1710,13 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, * in logical blocks. */ while (map.m_lblk < pg_end) { - map.m_flags = 0; + map.m_len = pg_end - map.m_lblk; err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); if (err) goto out; if (!(map.m_flags & F2FS_MAP_FLAGS)) { map.m_lblk++; - map.m_len--; continue; } @@ -1729,7 +1727,6 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, blk_end = map.m_pblk + map.m_len; map.m_lblk += map.m_len; - map.m_len = pg_end - map.m_lblk; } if (!fragmented) @@ -1755,14 +1752,13 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, int cnt = 0; do_map: - map.m_flags = 0; + map.m_len = pg_end - map.m_lblk; err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); if (err) goto clear_out; if (!(map.m_flags & F2FS_MAP_FLAGS)) { map.m_lblk++; - map.m_len--; continue; } @@ -1787,7 +1783,6 @@ do_map: } map.m_lblk = idx; - map.m_len = pg_end - idx; if (idx < pg_end && cnt < blk_per_seg) goto do_map; -- GitLab From 3bb147095ea51bc6e837428ed963f8c034963f65 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 15 Dec 2015 17:17:20 +0800 Subject: [PATCH 0032/5498] f2fs: backup raw_super in sbi f2fs use fields of f2fs_super_block struct directly in a grabbed buffer. Once the buffer happen to be destroyed (e.g. through dd), it may bring in unpredictable effect on f2fs. This patch fixes to allocate additional buffer to store datas of super block rather than using grabbed block buffer directly. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 4031f8ed9d24..6dfe0d32ad88 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -568,6 +568,7 @@ static void f2fs_put_super(struct super_block *sb) sb->s_fs_info = NULL; brelse(sbi->raw_super_buf); + kfree(sbi->raw_super); kfree(sbi); } @@ -1139,6 +1140,9 @@ static int read_raw_super_block(struct super_block *sb, struct f2fs_super_block *super; int err = 0; + super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL); + if (!super) + return -ENOMEM; retry: buffer = sb_bread(sb, block); if (!buffer) { @@ -1154,8 +1158,7 @@ retry: } } - super = (struct f2fs_super_block *) - ((char *)(buffer)->b_data + F2FS_SUPER_OFFSET); + memcpy(super, buffer->b_data + F2FS_SUPER_OFFSET, sizeof(*super)); /* sanity checking of raw super */ if (sanity_check_raw_super(sb, super)) { @@ -1189,14 +1192,17 @@ retry: out: /* No valid superblock */ - if (!*raw_super) + if (!*raw_super) { + kfree(super); return err; + } return 0; } int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) { + struct f2fs_super_block *super = F2FS_RAW_SUPER(sbi); struct buffer_head *sbh = sbi->raw_super_buf; struct buffer_head *bh; int err; @@ -1207,7 +1213,7 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) return -EIO; lock_buffer(bh); - memcpy(bh->b_data, sbh->b_data, sbh->b_size); + memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); WARN_ON(sbh->b_size != F2FS_BLKSIZE); set_buffer_uptodate(bh); set_buffer_dirty(bh); @@ -1223,6 +1229,10 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) /* write current valid superblock */ lock_buffer(sbh); + if (memcmp(sbh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super))) { + f2fs_msg(sbi->sb, KERN_INFO, "Write modified valid superblock"); + memcpy(sbh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); + } set_buffer_dirty(sbh); unlock_buffer(sbh); @@ -1497,6 +1507,7 @@ free_options: kfree(options); free_sb_buf: brelse(raw_super_buf); + kfree(raw_super); free_sbi: kfree(sbi); -- GitLab From fdb399d5e1287defc9ac42fbd0efde669de4c9a1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 15 Dec 2015 17:19:26 +0800 Subject: [PATCH 0033/5498] f2fs: don't grab super block buffer header all the time We have already got one copy of valid super block in memory, do not grab buffer header of super block all the time. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 8 ++--- fs/f2fs/super.c | 81 +++++++++++++++++++++---------------------------- 3 files changed, 38 insertions(+), 53 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f9081784f739..8cb3f1312b26 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -714,8 +714,8 @@ enum { struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ - struct buffer_head *raw_super_buf; /* buffer head of raw sb */ struct f2fs_super_block *raw_super; /* raw super block pointer */ + int valid_super_block; /* valid super block no */ int s_flag; /* flags for sbi */ /* for node-related operations */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index b9f4b745ead2..762ba042a705 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1594,20 +1594,16 @@ static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) return err; /* update superblock with uuid */ - lock_buffer(sbi->raw_super_buf); generate_random_uuid(sbi->raw_super->encrypt_pw_salt); - unlock_buffer(sbi->raw_super_buf); err = f2fs_commit_super(sbi, false); - - mnt_drop_write_file(filp); if (err) { /* undo new data */ - lock_buffer(sbi->raw_super_buf); memset(sbi->raw_super->encrypt_pw_salt, 0, 16); - unlock_buffer(sbi->raw_super_buf); + mnt_drop_write_file(filp); return err; } + mnt_drop_write_file(filp); got_it: if (copy_to_user((__u8 __user *)arg, sbi->raw_super->encrypt_pw_salt, 16)) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 6dfe0d32ad88..8c8b4673a5db 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -567,7 +567,6 @@ static void f2fs_put_super(struct super_block *sb) wait_for_completion(&sbi->s_kobj_unregister); sb->s_fs_info = NULL; - brelse(sbi->raw_super_buf); kfree(sbi->raw_super); kfree(sbi); } @@ -1132,65 +1131,53 @@ static void init_sb_info(struct f2fs_sb_info *sbi) */ static int read_raw_super_block(struct super_block *sb, struct f2fs_super_block **raw_super, - struct buffer_head **raw_super_buf, - int *recovery) + int *valid_super_block, int *recovery) { int block = 0; - struct buffer_head *buffer; - struct f2fs_super_block *super; + struct buffer_head *bh; + struct f2fs_super_block *super, *buf; int err = 0; super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL); if (!super) return -ENOMEM; retry: - buffer = sb_bread(sb, block); - if (!buffer) { + bh = sb_bread(sb, block); + if (!bh) { *recovery = 1; f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", block + 1); - if (block == 0) { - block++; - goto retry; - } else { - err = -EIO; - goto out; - } + err = -EIO; + goto next; } - memcpy(super, buffer->b_data + F2FS_SUPER_OFFSET, sizeof(*super)); + buf = (struct f2fs_super_block *)(bh->b_data + F2FS_SUPER_OFFSET); /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, super)) { - brelse(buffer); + if (sanity_check_raw_super(sb, buf)) { + brelse(bh); *recovery = 1; f2fs_msg(sb, KERN_ERR, "Can't find valid F2FS filesystem in %dth superblock", block + 1); - if (block == 0) { - block++; - goto retry; - } else { - err = -EINVAL; - goto out; - } + err = -EINVAL; + goto next; } if (!*raw_super) { - *raw_super_buf = buffer; + memcpy(super, buf, sizeof(*super)); + *valid_super_block = block; *raw_super = super; - } else { - /* already have a valid superblock */ - brelse(buffer); } + brelse(bh); +next: /* check the validity of the second superblock */ if (block == 0) { block++; goto retry; } -out: /* No valid superblock */ if (!*raw_super) { kfree(super); @@ -1203,18 +1190,16 @@ out: int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) { struct f2fs_super_block *super = F2FS_RAW_SUPER(sbi); - struct buffer_head *sbh = sbi->raw_super_buf; struct buffer_head *bh; int err; /* write back-up superblock first */ - bh = sb_getblk(sbi->sb, sbh->b_blocknr ? 0 : 1); + bh = sb_getblk(sbi->sb, sbi->valid_super_block ? 0 : 1); if (!bh) return -EIO; lock_buffer(bh); memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); - WARN_ON(sbh->b_size != F2FS_BLKSIZE); set_buffer_uptodate(bh); set_buffer_dirty(bh); unlock_buffer(bh); @@ -1227,33 +1212,37 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) if (recover || err) return err; + bh = sb_getblk(sbi->sb, sbi->valid_super_block); + if (!bh) + return -EIO; + /* write current valid superblock */ - lock_buffer(sbh); - if (memcmp(sbh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super))) { - f2fs_msg(sbi->sb, KERN_INFO, "Write modified valid superblock"); - memcpy(sbh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); - } - set_buffer_dirty(sbh); - unlock_buffer(sbh); + lock_buffer(bh); + memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); + set_buffer_uptodate(bh); + set_buffer_dirty(bh); + unlock_buffer(bh); - return __sync_dirty_buffer(sbh, WRITE_FLUSH_FUA); + err = __sync_dirty_buffer(bh, WRITE_FLUSH_FUA); + brelse(bh); + + return err; } static int f2fs_fill_super(struct super_block *sb, void *data, int silent) { struct f2fs_sb_info *sbi; struct f2fs_super_block *raw_super; - struct buffer_head *raw_super_buf; struct inode *root; long err; bool retry = true, need_fsck = false; char *options = NULL; - int recovery, i; + int recovery, i, valid_super_block; try_onemore: err = -EINVAL; raw_super = NULL; - raw_super_buf = NULL; + valid_super_block = -1; recovery = 0; /* allocate memory for f2fs-specific super block info */ @@ -1267,7 +1256,8 @@ try_onemore: goto free_sbi; } - err = read_raw_super_block(sb, &raw_super, &raw_super_buf, &recovery); + err = read_raw_super_block(sb, &raw_super, &valid_super_block, + &recovery); if (err) goto free_sbi; @@ -1300,7 +1290,7 @@ try_onemore: /* init f2fs-specific super block info */ sbi->sb = sb; sbi->raw_super = raw_super; - sbi->raw_super_buf = raw_super_buf; + sbi->valid_super_block = valid_super_block; mutex_init(&sbi->gc_mutex); mutex_init(&sbi->writepages); mutex_init(&sbi->cp_mutex); @@ -1506,7 +1496,6 @@ free_meta_inode: free_options: kfree(options); free_sb_buf: - brelse(raw_super_buf); kfree(raw_super); free_sbi: kfree(sbi); -- GitLab From 61a307d8a8194187caa0d435f37624a6d9b72b52 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 15 Dec 2015 16:07:14 -0800 Subject: [PATCH 0034/5498] f2fs: relocate tracepoint of write_checkpoint It needs to relocate its location to see exact trace logs. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index df084173e21d..93aa8c88342d 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1131,9 +1131,9 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* do checkpoint periodically */ sbi->cp_expires = round_jiffies_up(jiffies + HZ * sbi->cp_interval); + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint"); out: mutex_unlock(&sbi->cp_mutex); - trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint"); } void init_ino_entry_info(struct f2fs_sb_info *sbi) -- GitLab From 4c1ce43e0c4f744b6c6c288dff41f00dd12ed792 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 16 Dec 2015 13:19:35 +0800 Subject: [PATCH 0035/5498] f2fs: introduce __f2fs_commit_super Introduce __f2fs_commit_super to include duplicated codes in f2fs_commit_super for cleanup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8c8b4673a5db..bc1a8cd38bc8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1187,14 +1187,13 @@ next: return 0; } -int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) +int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block) { struct f2fs_super_block *super = F2FS_RAW_SUPER(sbi); struct buffer_head *bh; int err; - /* write back-up superblock first */ - bh = sb_getblk(sbi->sb, sbi->valid_super_block ? 0 : 1); + bh = sb_getblk(sbi->sb, block); if (!bh) return -EIO; @@ -1208,25 +1207,22 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) err = __sync_dirty_buffer(bh, WRITE_FLUSH_FUA); brelse(bh); + return err; +} + +int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) +{ + int err; + + /* write back-up superblock first */ + err = __f2fs_commit_super(sbi, sbi->valid_super_block ? 0 : 1); + /* if we are in recovery path, skip writing valid superblock */ if (recover || err) return err; - bh = sb_getblk(sbi->sb, sbi->valid_super_block); - if (!bh) - return -EIO; - /* write current valid superblock */ - lock_buffer(bh); - memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); - set_buffer_uptodate(bh); - set_buffer_dirty(bh); - unlock_buffer(bh); - - err = __sync_dirty_buffer(bh, WRITE_FLUSH_FUA); - brelse(bh); - - return err; + return __f2fs_commit_super(sbi, sbi->valid_super_block); } static int f2fs_fill_super(struct super_block *sb, void *data, int silent) -- GitLab From 29264e63b7187693c70a14c8fc2de377ab0b63bf Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 16 Dec 2015 13:09:20 +0800 Subject: [PATCH 0036/5498] f2fs: record dirty status of regular/symlink inode Maintain regular/symlink inode which has dirty pages in global dirty list and record their total dirty pages count like the way of handling directory inode. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 66 ++++++++++++++++++++++---------------------- fs/f2fs/data.c | 4 +-- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 27 +++++++++++------- fs/f2fs/inode.c | 2 +- fs/f2fs/super.c | 6 ++-- 6 files changed, 58 insertions(+), 49 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 93aa8c88342d..16bda926c55b 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -723,53 +723,51 @@ fail_no_cp: return -EINVAL; } -static void __add_dirty_inode(struct inode *inode) +static void __add_dirty_inode(struct inode *inode, enum inode_type type) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); + int flag = (type == DIR_INODE) ? FI_DIRTY_DIR : FI_DIRTY_FILE; - if (is_inode_flag_set(fi, FI_DIRTY_DIR)) + if (is_inode_flag_set(fi, flag)) return; - set_inode_flag(fi, FI_DIRTY_DIR); - list_add_tail(&fi->dirty_list, &sbi->dir_inode_list); - stat_inc_dirty_dir(sbi); - return; + set_inode_flag(fi, flag); + list_add_tail(&fi->dirty_list, &sbi->inode_list[type]); + if (type == DIR_INODE) + stat_inc_dirty_dir(sbi); } -static void __remove_dirty_inode(struct inode *inode) +static void __remove_dirty_inode(struct inode *inode, enum inode_type type) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); + int flag = (type == DIR_INODE) ? FI_DIRTY_DIR : FI_DIRTY_FILE; if (get_dirty_pages(inode) || - !is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR)) + !is_inode_flag_set(F2FS_I(inode), flag)) return; list_del_init(&fi->dirty_list); - clear_inode_flag(fi, FI_DIRTY_DIR); - stat_dec_dirty_dir(sbi); + clear_inode_flag(fi, flag); + if (type == DIR_INODE) + stat_dec_dirty_dir(sbi); } void update_dirty_page(struct inode *inode, struct page *page) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + enum inode_type type = S_ISDIR(inode->i_mode) ? DIR_INODE : FILE_INODE; if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) return; - if (!S_ISDIR(inode->i_mode)) { - inode_inc_dirty_pages(inode); - goto out; - } - - spin_lock(&sbi->dir_inode_lock); - __add_dirty_inode(inode); + spin_lock(&sbi->inode_lock[type]); + __add_dirty_inode(inode, type); inode_inc_dirty_pages(inode); - spin_unlock(&sbi->dir_inode_lock); + spin_unlock(&sbi->inode_lock[type]); -out: SetPagePrivate(page); f2fs_trace_pid(page); } @@ -778,22 +776,24 @@ void add_dirty_dir_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - spin_lock(&sbi->dir_inode_lock); - __add_dirty_inode(inode); - spin_unlock(&sbi->dir_inode_lock); + spin_lock(&sbi->inode_lock[DIR_INODE]); + __add_dirty_inode(inode, DIR_INODE); + spin_unlock(&sbi->inode_lock[DIR_INODE]); } -void remove_dirty_dir_inode(struct inode *inode) +void remove_dirty_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); + enum inode_type type = S_ISDIR(inode->i_mode) ? DIR_INODE : FILE_INODE; - if (!S_ISDIR(inode->i_mode)) + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && + !S_ISLNK(inode->i_mode)) return; - spin_lock(&sbi->dir_inode_lock); - __remove_dirty_inode(inode); - spin_unlock(&sbi->dir_inode_lock); + spin_lock(&sbi->inode_lock[type]); + __remove_dirty_inode(inode, type); + spin_unlock(&sbi->inode_lock[type]); /* Only from the recovery routine */ if (is_inode_flag_set(fi, FI_DELAY_IPUT)) { @@ -802,7 +802,7 @@ void remove_dirty_dir_inode(struct inode *inode) } } -void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi) +void sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type) { struct list_head *head; struct inode *inode; @@ -811,16 +811,16 @@ retry: if (unlikely(f2fs_cp_error(sbi))) return; - spin_lock(&sbi->dir_inode_lock); + spin_lock(&sbi->inode_lock[type]); - head = &sbi->dir_inode_list; + head = &sbi->inode_list[type]; if (list_empty(head)) { - spin_unlock(&sbi->dir_inode_lock); + spin_unlock(&sbi->inode_lock[type]); return; } fi = list_entry(head->next, struct f2fs_inode_info, dirty_list); inode = igrab(&fi->vfs_inode); - spin_unlock(&sbi->dir_inode_lock); + spin_unlock(&sbi->inode_lock[type]); if (inode) { filemap_fdatawrite(inode->i_mapping); iput(inode); @@ -855,7 +855,7 @@ retry_flush_dents: /* write all the dirty dentry pages */ if (get_pages(sbi, F2FS_DIRTY_DENTS)) { f2fs_unlock_all(sbi); - sync_dirty_dir_inodes(sbi); + sync_dirty_inodes(sbi, DIR_INODE); if (unlikely(f2fs_cp_error(sbi))) { err = -EIO; goto out; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a0d53a38f58a..655e03c3f89d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1181,7 +1181,7 @@ out: f2fs_balance_fs(sbi); if (wbc->for_reclaim) { f2fs_submit_merged_bio(sbi, DATA, WRITE); - remove_dirty_dir_inode(inode); + remove_dirty_inode(inode); } return 0; @@ -1373,7 +1373,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, if (locked) mutex_unlock(&sbi->writepages); - remove_dirty_dir_inode(inode); + remove_dirty_inode(inode); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return ret; diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 78eeee09df57..4e71e4166c67 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -444,7 +444,7 @@ error: /* once the failed inode becomes a bad inode, i_mode is S_IFREG */ truncate_inode_pages(&inode->i_data, 0); truncate_blocks(inode, 0, false); - remove_dirty_dir_inode(inode); + remove_dirty_inode(inode); remove_inode_page(inode); return ERR_PTR(err); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8cb3f1312b26..bedfa25c5137 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -647,6 +647,7 @@ struct f2fs_sm_info { enum count_type { F2FS_WRITEBACK, F2FS_DIRTY_DENTS, + F2FS_DIRTY_DATA, F2FS_DIRTY_NODES, F2FS_DIRTY_META, F2FS_INMEM_PAGES, @@ -695,6 +696,12 @@ struct f2fs_bio_info { struct rw_semaphore io_rwsem; /* blocking op for bio */ }; +enum inode_type { + DIR_INODE, /* for dirty dir inode */ + FILE_INODE, /* for dirty regular/symlink inode */ + NR_INODE_TYPE, +}; + /* for inner inode cache management */ struct inode_management { struct radix_tree_root ino_root; /* ino entry array */ @@ -744,9 +751,9 @@ struct f2fs_sb_info { /* for orphan inode, use 0'th array */ unsigned int max_orphans; /* max orphan inodes */ - /* for directory inode management */ - struct list_head dir_inode_list; /* dir inode list */ - spinlock_t dir_inode_lock; /* for dir inode list lock */ + /* for inode management */ + struct list_head inode_list[NR_INODE_TYPE]; /* dirty inode list */ + spinlock_t inode_lock[NR_INODE_TYPE]; /* for dirty inode list lock */ /* for extent tree cache */ struct radix_tree_root extent_tree_root;/* cache extent cache entries */ @@ -1059,8 +1066,8 @@ static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) static inline void inode_inc_dirty_pages(struct inode *inode) { atomic_inc(&F2FS_I(inode)->dirty_pages); - if (S_ISDIR(inode->i_mode)) - inc_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS); + inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? + F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); } static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) @@ -1075,9 +1082,8 @@ static inline void inode_dec_dirty_pages(struct inode *inode) return; atomic_dec(&F2FS_I(inode)->dirty_pages); - - if (S_ISDIR(inode->i_mode)) - dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS); + dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? + F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); } static inline int get_pages(struct f2fs_sb_info *sbi, int count_type) @@ -1416,6 +1422,7 @@ enum { FI_DATA_EXIST, /* indicate data exists */ FI_INLINE_DOTS, /* indicate inline dot dentries */ FI_DO_DEFRAG, /* indicate defragment is running */ + FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ }; static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) @@ -1824,8 +1831,8 @@ int recover_orphan_inodes(struct f2fs_sb_info *); int get_valid_checkpoint(struct f2fs_sb_info *); void update_dirty_page(struct inode *, struct page *); void add_dirty_dir_inode(struct inode *); -void remove_dirty_dir_inode(struct inode *); -void sync_dirty_dir_inodes(struct f2fs_sb_info *); +void remove_dirty_inode(struct inode *); +void sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type); void write_checkpoint(struct f2fs_sb_info *, struct cp_control *); void init_ino_entry_info(struct f2fs_sb_info *); int __init create_checkpoint_caches(void); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index cb1d6ff30959..9705fca793d6 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -327,7 +327,7 @@ void f2fs_evict_inode(struct inode *inode) goto out_clear; f2fs_bug_on(sbi, get_dirty_pages(inode)); - remove_dirty_dir_inode(inode); + remove_dirty_inode(inode); f2fs_destroy_extent_tree(inode); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index bc1a8cd38bc8..5b596d6a8d24 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1339,8 +1339,10 @@ try_onemore: le64_to_cpu(sbi->ckpt->valid_block_count); sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->alloc_valid_block_count = 0; - INIT_LIST_HEAD(&sbi->dir_inode_list); - spin_lock_init(&sbi->dir_inode_lock); + for (i = 0; i < NR_INODE_TYPE; i++) { + INIT_LIST_HEAD(&sbi->inode_list[i]); + spin_lock_init(&sbi->inode_lock[i]); + } init_extent_cache_info(sbi); -- GitLab From b578cd6ad24d5ede2850bd604161ed5fb0881113 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 16 Dec 2015 13:12:16 +0800 Subject: [PATCH 0037/5498] f2fs: introduce new option for controlling data flush Add a new option 'data_flush' to enable data flush functionality. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: Documentation/filesystems/f2fs.txt --- Documentation/filesystems/f2fs.txt | 13 +++++++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 2cca5a25ef89..4090500d27f4 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -131,6 +131,19 @@ nobarrier This option can be used if underlying storage guarantees If this option is set, no cache_flush commands are issued but f2fs still guarantees the write ordering of all the data writes. +fastboot This option is used when a system wants to reduce mount + time as much as possible, even though normal performance + can be sacrificed. +extent_cache Enable an extent cache based on rb-tree, it can cache + as many as extent which map between contiguous logical + address and physical address per inode, resulting in + increasing the cache hit ratio. Set by default. +noextent_cache Disable an extent cache based on rb-tree explicitly, see + the above extent_cache mount option. +noinline_data Disable the inline data feature, inline data feature is + enabled by default. +data_flush Enable data flushing before checkpoint in order to + persist data of regular and symlink. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index bedfa25c5137..f97fce856dc5 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -54,6 +54,7 @@ #define F2FS_MOUNT_FASTBOOT 0x00001000 #define F2FS_MOUNT_EXTENT_CACHE 0x00002000 #define F2FS_MOUNT_FORCE_FG_GC 0x00004000 +#define F2FS_MOUNT_DATA_FLUSH 0x00008000 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5b596d6a8d24..c3070c149c0e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -67,6 +67,7 @@ enum { Opt_extent_cache, Opt_noextent_cache, Opt_noinline_data, + Opt_data_flush, Opt_err, }; @@ -91,6 +92,7 @@ static match_table_t f2fs_tokens = { {Opt_extent_cache, "extent_cache"}, {Opt_noextent_cache, "noextent_cache"}, {Opt_noinline_data, "noinline_data"}, + {Opt_data_flush, "data_flush"}, {Opt_err, NULL}, }; @@ -406,6 +408,9 @@ static int parse_options(struct super_block *sb, char *options) case Opt_noinline_data: clear_opt(sbi, INLINE_DATA); break; + case Opt_data_flush: + set_opt(sbi, DATA_FLUSH); + break; default: f2fs_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" or missing value", @@ -687,6 +692,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",extent_cache"); else seq_puts(seq, ",noextent_cache"); + if (test_opt(sbi, DATA_FLUSH)) + seq_puts(seq, ",data_flush"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); return 0; -- GitLab From c3dbcfabfba07eaff5aec12c3d1db0e593ea7a2f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 17 Dec 2015 17:14:44 +0800 Subject: [PATCH 0038/5498] f2fs: stat dirty regular/symlink inodes Add to stat dirty regular and symlink inode for showing in debugfs. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 ++----- fs/f2fs/debug.c | 6 +++++- fs/f2fs/f2fs.h | 13 +++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 16bda926c55b..92ca1467578f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -734,13 +734,11 @@ static void __add_dirty_inode(struct inode *inode, enum inode_type type) set_inode_flag(fi, flag); list_add_tail(&fi->dirty_list, &sbi->inode_list[type]); - if (type == DIR_INODE) - stat_inc_dirty_dir(sbi); + stat_inc_dirty_inode(sbi, type); } static void __remove_dirty_inode(struct inode *inode, enum inode_type type) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); int flag = (type == DIR_INODE) ? FI_DIRTY_DIR : FI_DIRTY_FILE; @@ -750,8 +748,7 @@ static void __remove_dirty_inode(struct inode *inode, enum inode_type type) list_del_init(&fi->dirty_list); clear_inode_flag(fi, flag); - if (type == DIR_INODE) - stat_dec_dirty_dir(sbi); + stat_dec_dirty_inode(F2FS_I_SB(inode), type); } void update_dirty_page(struct inode *inode, struct page *page) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index f4a7b9e9416d..bb307e642fdd 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -42,8 +42,10 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ext_node = atomic_read(&sbi->total_ext_node); si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES); si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); - si->ndirty_dirs = sbi->n_dirty_dirs; si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); + si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA); + si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; + si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->wb_pages = get_pages(sbi, F2FS_WRITEBACK); si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; @@ -298,6 +300,8 @@ static int stat_show(struct seq_file *s, void *v) si->ndirty_node, si->node_pages); seq_printf(s, " - dents: %4d in dirs:%4d\n", si->ndirty_dent, si->ndirty_dirs); + seq_printf(s, " - datas: %4d in files:%4d\n", + si->ndirty_data, si->ndirty_files); seq_printf(s, " - meta: %4d in %4d\n", si->ndirty_meta, si->meta_pages); seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f97fce856dc5..fa0f47907978 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -817,7 +817,7 @@ struct f2fs_sb_info { atomic_t inline_inode; /* # of inline_data inodes */ atomic_t inline_dir; /* # of inline_dentry inodes */ int bg_gc; /* background gc calls */ - unsigned int n_dirty_dirs; /* # of dir inodes */ + unsigned int ndirty_inode[NR_INODE_TYPE]; /* # of dirty inodes */ #endif unsigned int last_victim[2]; /* last victim segment # */ spinlock_t stat_lock; /* lock for stat operations */ @@ -1886,7 +1886,8 @@ struct f2fs_stat_info { unsigned long long hit_largest, hit_cached, hit_rbtree; unsigned long long hit_total, total_ext; int ext_tree, ext_node; - int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta; + int ndirty_node, ndirty_meta; + int ndirty_dent, ndirty_dirs, ndirty_data, ndirty_files; int nats, dirty_nats, sits, dirty_sits, fnids; int total_count, utilization; int bg_gc, inmem_pages, wb_pages; @@ -1919,8 +1920,8 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) #define stat_inc_cp_count(si) ((si)->cp_count++) #define stat_inc_call_count(si) ((si)->call_count++) #define stat_inc_bggc_count(sbi) ((sbi)->bg_gc++) -#define stat_inc_dirty_dir(sbi) ((sbi)->n_dirty_dirs++) -#define stat_dec_dirty_dir(sbi) ((sbi)->n_dirty_dirs--) +#define stat_inc_dirty_inode(sbi, type) ((sbi)->ndirty_inode[type]++) +#define stat_dec_dirty_inode(sbi, type) ((sbi)->ndirty_inode[type]--) #define stat_inc_total_hit(sbi) (atomic64_inc(&(sbi)->total_hit_ext)) #define stat_inc_rbtree_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_rbtree)) #define stat_inc_largest_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_largest)) @@ -2001,8 +2002,8 @@ void f2fs_destroy_root_stats(void); #define stat_inc_cp_count(si) #define stat_inc_call_count(si) #define stat_inc_bggc_count(si) -#define stat_inc_dirty_dir(sbi) -#define stat_dec_dirty_dir(sbi) +#define stat_inc_dirty_inode(sbi, type) +#define stat_dec_dirty_inode(sbi, type) #define stat_inc_total_hit(sb) #define stat_inc_rbtree_node_hit(sb) #define stat_inc_largest_node_hit(sbi) -- GitLab From 242d59ecb6cdc5d148f62b921ea3c78223022f25 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 17 Dec 2015 17:13:28 +0800 Subject: [PATCH 0039/5498] f2fs: support data flush in background Previously, when finishing a checkpoint, we have persisted all fs meta info including meta inode, node inode, dentry page of directory inode, so, after a sudden power cut, f2fs can recover from last checkpoint with full directory structure. But during checkpoint, we didn't flush dirty pages of regular and symlink inode, so such dirty datas still in memory will be lost in that moment of power off. In order to reduce the chance of lost data, this patch enables f2fs_balance_fs_bg with the ability of data flushing. It will try to flush user data before starting a checkpoint. So user's data written after last checkpoint which may not be fsynced could be saved. When we mount with data_flush option, after every period of cp_interval (could be configured in sysfs: /sys/fs/f2fs/device/cp_interval) seconds user data could be flushed into device once f2fs_balance_fs_bg was called in kworker thread or gc thread. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5fa519f02860..c2474509e5de 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -291,8 +291,11 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) if (!available_free_memory(sbi, NAT_ENTRIES) || excess_prefree_segs(sbi) || !available_free_memory(sbi, INO_ENTRIES) || - jiffies > sbi->cp_expires) + jiffies > sbi->cp_expires) { + if (test_opt(sbi, DATA_FLUSH)) + sync_dirty_inodes(sbi, FILE_INODE); f2fs_sync_fs(sbi->sb, true); + } } static int issue_flush_thread(void *data) -- GitLab From 9f886c1c7c4a974814420f2a5ba3abc7d197cb87 Mon Sep 17 00:00:00 2001 From: Fan Li Date: Thu, 17 Dec 2015 13:20:59 +0800 Subject: [PATCH 0040/5498] f2fs: optimize the flow of f2fs_map_blocks check map->m_len right after it changes to avoid excess call to update dnode_of_data. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 69 ++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 655e03c3f89d..4de9d2fb2cd6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -574,6 +574,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int err = 0, ofs = 1; struct extent_info ei; bool allocated = false; + block_t blkaddr; map->m_len = 0; map->m_flags = 0; @@ -637,6 +638,9 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, pgofs++; get_next: + if (map->m_len >= maxblocks) + goto sync_out; + if (dn.ofs_in_node >= end_offset) { if (allocated) sync_inode_page(&dn); @@ -654,44 +658,43 @@ get_next: end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); } - if (maxblocks > map->m_len) { - block_t blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); - if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { - if (create) { - if (unlikely(f2fs_cp_error(sbi))) { - err = -EIO; - goto sync_out; - } - err = __allocate_data_block(&dn); - if (err) - goto sync_out; - allocated = true; - map->m_flags |= F2FS_MAP_NEW; - blkaddr = dn.data_blkaddr; - } else { - /* - * we only merge preallocated unwritten blocks - * for fiemap. - */ - if (flag != F2FS_GET_BLOCK_FIEMAP || - blkaddr != NEW_ADDR) - goto sync_out; + if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { + if (create) { + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; + goto sync_out; } + err = __allocate_data_block(&dn); + if (err) + goto sync_out; + allocated = true; + map->m_flags |= F2FS_MAP_NEW; + blkaddr = dn.data_blkaddr; + } else { + /* + * we only merge preallocated unwritten blocks + * for fiemap. + */ + if (flag != F2FS_GET_BLOCK_FIEMAP || + blkaddr != NEW_ADDR) + goto sync_out; } + } - /* Give more consecutive addresses for the readahead */ - if ((map->m_pblk != NEW_ADDR && - blkaddr == (map->m_pblk + ofs)) || - (map->m_pblk == NEW_ADDR && - blkaddr == NEW_ADDR)) { - ofs++; - dn.ofs_in_node++; - pgofs++; - map->m_len++; - goto get_next; - } + /* Give more consecutive addresses for the readahead */ + if ((map->m_pblk != NEW_ADDR && + blkaddr == (map->m_pblk + ofs)) || + (map->m_pblk == NEW_ADDR && + blkaddr == NEW_ADDR)) { + ofs++; + dn.ofs_in_node++; + pgofs++; + map->m_len++; + goto get_next; } + sync_out: if (allocated) sync_inode_page(&dn); -- GitLab From 67b414e7f369bb9290dd9ef17afa85e1b10d6100 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 17 Dec 2015 17:17:16 +0800 Subject: [PATCH 0041/5498] f2fs: add a tracepoint for sync_dirty_inodes This patch adds a tracepoint for sync_dirty_inodes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 8 ++++++++ include/trace/events/f2fs.h | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 92ca1467578f..2acee04799cd 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -804,6 +804,11 @@ void sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type) struct list_head *head; struct inode *inode; struct f2fs_inode_info *fi; + bool is_dir = (type == DIR_INODE); + + trace_f2fs_sync_dirty_inodes_enter(sbi->sb, is_dir, + get_pages(sbi, is_dir ? + F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA)); retry: if (unlikely(f2fs_cp_error(sbi))) return; @@ -813,6 +818,9 @@ retry: head = &sbi->inode_list[type]; if (list_empty(head)) { spin_unlock(&sbi->inode_lock[type]); + trace_f2fs_sync_dirty_inodes_exit(sbi->sb, is_dir, + get_pages(sbi, is_dir ? + F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA)); return; } fi = list_entry(head->next, struct f2fs_inode_info, dirty_list); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 78296a10294e..813f0bee4516 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1230,6 +1230,44 @@ TRACE_EVENT(f2fs_destroy_extent_tree, __entry->node_cnt) ); +DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes, + + TP_PROTO(struct super_block *sb, int type, int count), + + TP_ARGS(sb, type, count), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, type) + __field(int, count) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->type = type; + __entry->count = count; + ), + + TP_printk("dev = (%d,%d), %s, dirty count = %d", + show_dev(__entry), + show_file_type(__entry->type), + __entry->count) +); + +DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_enter, + + TP_PROTO(struct super_block *sb, int type, int count), + + TP_ARGS(sb, type, count) +); + +DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_exit, + + TP_PROTO(struct super_block *sb, int type, int count), + + TP_ARGS(sb, type, count) +); + #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ -- GitLab From ebb0ec2257d7e9a69a01f0cd1d426cb353da9862 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 21 Dec 2015 19:20:15 -0800 Subject: [PATCH 0042/5498] f2fs: use atomic variable for total_extent_tree It would be better to use atomic variable for total_extent_tree. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 5 +++-- fs/f2fs/extent_cache.c | 8 ++++---- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 3 ++- fs/f2fs/shrinker.c | 3 ++- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index bb307e642fdd..ed5dfcc8886f 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -38,7 +38,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree); si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree; si->total_ext = atomic64_read(&sbi->total_hit_ext); - si->ext_tree = sbi->total_ext_tree; + si->ext_tree = atomic_read(&sbi->total_ext_tree); si->ext_node = atomic_read(&sbi->total_ext_node); si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES); si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); @@ -193,7 +193,8 @@ get_cache: si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); for (i = 0; i <= UPDATE_INO; i++) si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); - si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree); + si->cache_mem += atomic_read(&sbi->total_ext_tree) * + sizeof(struct extent_tree); si->cache_mem += atomic_read(&sbi->total_ext_node) * sizeof(struct extent_node); diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index e86e9f1e0733..0e97d6af9885 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -70,7 +70,7 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) rwlock_init(&et->lock); atomic_set(&et->refcount, 0); et->count = 0; - sbi->total_ext_tree++; + atomic_inc(&sbi->total_ext_tree); } atomic_inc(&et->refcount); up_write(&sbi->extent_tree_lock); @@ -570,7 +570,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) radix_tree_delete(root, et->ino); kmem_cache_free(extent_tree_slab, et); - sbi->total_ext_tree--; + atomic_dec(&sbi->total_ext_tree); tree_cnt++; if (node_cnt + tree_cnt >= nr_shrink) @@ -663,7 +663,7 @@ void f2fs_destroy_extent_tree(struct inode *inode) f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count); radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); kmem_cache_free(extent_tree_slab, et); - sbi->total_ext_tree--; + atomic_dec(&sbi->total_ext_tree); up_write(&sbi->extent_tree_lock); F2FS_I(inode)->extent_tree = NULL; @@ -715,7 +715,7 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi) init_rwsem(&sbi->extent_tree_lock); INIT_LIST_HEAD(&sbi->extent_list); spin_lock_init(&sbi->extent_lock); - sbi->total_ext_tree = 0; + atomic_set(&sbi->total_ext_tree, 0); atomic_set(&sbi->total_ext_node, 0); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index fa0f47907978..33fef5776a83 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -761,7 +761,7 @@ struct f2fs_sb_info { struct rw_semaphore extent_tree_lock; /* locking extent radix tree */ struct list_head extent_list; /* lru list for shrinker */ spinlock_t extent_lock; /* locking extent lru list */ - int total_ext_tree; /* extent tree count */ + atomic_t total_ext_tree; /* extent tree count */ atomic_t total_ext_node; /* extent info count */ /* basic filesystem units */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index f7e2184235cd..f3d34df89e84 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -65,7 +65,8 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); } else if (type == EXTENT_CACHE) { - mem_size = (sbi->total_ext_tree * sizeof(struct extent_tree) + + mem_size = (atomic_read(&sbi->total_ext_tree) * + sizeof(struct extent_tree) + atomic_read(&sbi->total_ext_node) * sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index da0d8e0b55a5..a11e099cbddc 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -32,7 +32,8 @@ static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi) { - return sbi->total_ext_tree + atomic_read(&sbi->total_ext_node); + return atomic_read(&sbi->total_ext_tree) + + atomic_read(&sbi->total_ext_node); } unsigned long f2fs_shrink_count(struct shrinker *shrink, -- GitLab From 00bca797def7d230cf09dcf1a612377655b2ce64 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 21 Dec 2015 19:25:50 -0800 Subject: [PATCH 0043/5498] f2fs: speed up shrinking extent tree entries If there is no candidates for shrinking slab entries, we don't need to traverse any trees at all. Reviewed-by: Chao Yu [Jaegeuk Kim: fix missing initialization reported by Yunlei He] Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 14 ++++++++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/shrinker.c | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 0e97d6af9885..5305a29f91a3 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -71,6 +71,8 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) atomic_set(&et->refcount, 0); et->count = 0; atomic_inc(&sbi->total_ext_tree); + } else { + atomic_dec(&sbi->total_zombie_tree); } atomic_inc(&et->refcount); up_write(&sbi->extent_tree_lock); @@ -547,10 +549,14 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) unsigned int found; unsigned int node_cnt = 0, tree_cnt = 0; int remained; + bool do_free = false; if (!test_opt(sbi, EXTENT_CACHE)) return 0; + if (!atomic_read(&sbi->total_zombie_tree)) + goto free_node; + if (!down_write_trylock(&sbi->extent_tree_lock)) goto out; @@ -571,6 +577,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) radix_tree_delete(root, et->ino); kmem_cache_free(extent_tree_slab, et); atomic_dec(&sbi->total_ext_tree); + atomic_dec(&sbi->total_zombie_tree); tree_cnt++; if (node_cnt + tree_cnt >= nr_shrink) @@ -580,6 +587,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) } up_write(&sbi->extent_tree_lock); +free_node: /* 2. remove LRU extent entries */ if (!down_write_trylock(&sbi->extent_tree_lock)) goto out; @@ -591,9 +599,13 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) if (!remained--) break; list_del_init(&en->list); + do_free = true; } spin_unlock(&sbi->extent_lock); + if (do_free == false) + goto unlock_out; + /* * reset ino for searching victims from beginning of global extent tree. */ @@ -651,6 +663,7 @@ void f2fs_destroy_extent_tree(struct inode *inode) if (inode->i_nlink && !is_bad_inode(inode) && et->count) { atomic_dec(&et->refcount); + atomic_inc(&sbi->total_zombie_tree); return; } @@ -716,6 +729,7 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sbi->extent_list); spin_lock_init(&sbi->extent_lock); atomic_set(&sbi->total_ext_tree, 0); + atomic_set(&sbi->total_zombie_tree, 0); atomic_set(&sbi->total_ext_node, 0); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 33fef5776a83..6ae93a0164ad 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -762,6 +762,7 @@ struct f2fs_sb_info { struct list_head extent_list; /* lru list for shrinker */ spinlock_t extent_lock; /* locking extent lru list */ atomic_t total_ext_tree; /* extent tree count */ + atomic_t total_zombie_tree; /* extent zombie tree count */ atomic_t total_ext_node; /* extent info count */ /* basic filesystem units */ diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index a11e099cbddc..93606f281bf9 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -32,7 +32,7 @@ static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi) { - return atomic_read(&sbi->total_ext_tree) + + return atomic_read(&sbi->total_zombie_tree) + atomic_read(&sbi->total_ext_node); } -- GitLab From 8356736addd9c3ffbd82d2df58f2ba80fc4d71a6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Dec 2015 11:09:35 -0800 Subject: [PATCH 0044/5498] f2fs: check inline_data flag at converting time We can check inode's inline_data flag when calling to convert it. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 8 +++---- fs/f2fs/file.c | 58 +++++++++++++++++++----------------------------- fs/f2fs/inline.c | 3 +++ 3 files changed, 29 insertions(+), 40 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4de9d2fb2cd6..61131e3971ad 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1574,11 +1574,9 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, int err; /* we don't need to use inline_data strictly */ - if (f2fs_has_inline_data(inode)) { - err = f2fs_convert_inline_inode(inode); - if (err) - return err; - } + err = f2fs_convert_inline_inode(inode); + if (err) + return err; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return 0; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 762ba042a705..263201938cc7 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -419,19 +419,18 @@ static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence) static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file_inode(file); + int err; if (f2fs_encrypted_inode(inode)) { - int err = f2fs_get_encryption_info(inode); + 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; - } + err = f2fs_convert_inline_inode(inode); + if (err) + return err; file_accessed(file); vma->vm_ops = &f2fs_file_vm_ops; @@ -605,7 +604,7 @@ int f2fs_truncate(struct inode *inode, bool lock) trace_f2fs_truncate(inode); /* we should check inline_data size */ - if (f2fs_has_inline_data(inode) && !f2fs_may_inline_data(inode)) { + if (!f2fs_may_inline_data(inode)) { err = f2fs_convert_inline_inode(inode); if (err) return err; @@ -689,8 +688,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) truncate_setsize(inode, attr->ia_size); /* should convert inline inode here */ - if (f2fs_has_inline_data(inode) && - !f2fs_may_inline_data(inode)) { + if (!f2fs_may_inline_data(inode)) { err = f2fs_convert_inline_inode(inode); if (err) return err; @@ -787,13 +785,11 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) { pgoff_t pg_start, pg_end; loff_t off_start, off_end; - int ret = 0; + int ret; - if (f2fs_has_inline_data(inode)) { - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; - } + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; @@ -952,11 +948,9 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(F2FS_I_SB(inode)); - if (f2fs_has_inline_data(inode)) { - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; - } + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; pg_start = offset >> PAGE_CACHE_SHIFT; pg_end = (offset + len) >> PAGE_CACHE_SHIFT; @@ -1002,11 +996,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, f2fs_balance_fs(sbi); - if (f2fs_has_inline_data(inode)) { - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; - } + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1); if (ret) @@ -1115,11 +1107,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(sbi); - if (f2fs_has_inline_data(inode)) { - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; - } + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; ret = truncate_blocks(inode, i_size_read(inode), true); if (ret) @@ -1169,11 +1159,9 @@ static int expand_inode_data(struct inode *inode, loff_t offset, if (ret) return ret; - if (f2fs_has_inline_data(inode)) { - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; - } + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index bda7126466c0..8090854dd29c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -177,6 +177,9 @@ int f2fs_convert_inline_inode(struct inode *inode) struct page *ipage, *page; int err = 0; + if (!f2fs_has_inline_data(inode)) + return 0; + page = grab_cache_page(inode->i_mapping, 0); if (!page) return -ENOMEM; -- GitLab From f055fa88c42b112e364acd89119e716559e4312e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Dec 2015 11:56:08 -0800 Subject: [PATCH 0045/5498] f2fs: avoid unnecessary f2fs_gc for dir operations The f2fs_balance_fs doesn't need to cover f2fs_new_inode or f2fs_find_entry works. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/namei.c --- fs/f2fs/namei.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 191fc2517787..3f8efc37eaa3 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -129,8 +129,6 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, nid_t ino = 0; int err; - f2fs_balance_fs(sbi); - inode = f2fs_new_inode(dir, mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -143,6 +141,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, inode->i_mapping->a_ops = &f2fs_dblock_aops; ino = inode->i_ino; + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -289,12 +289,13 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) int err = -ENOENT; trace_f2fs_unlink_enter(dir, dentry); - f2fs_balance_fs(sbi); de = f2fs_find_entry(dir, &dentry->d_name, &page); if (!de) goto fail; + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = acquire_orphan_inode(sbi); if (err) { @@ -353,8 +354,6 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, if (len > dir->i_sb->s_blocksize) return -ENAMETOOLONG; - f2fs_balance_fs(sbi); - inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -365,6 +364,8 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, inode->i_op = &f2fs_symlink_inode_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -445,8 +446,6 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) struct inode *inode; int err; - f2fs_balance_fs(sbi); - inode = f2fs_new_inode(dir, S_IFDIR | mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -456,6 +455,8 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_mapping->a_ops = &f2fs_dblock_aops; mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); + f2fs_balance_fs(sbi); + set_inode_flag(F2FS_I(inode), FI_INC_LINK); f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); @@ -496,8 +497,6 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, if (!new_valid_dev(rdev)) return -EINVAL; - f2fs_balance_fs(sbi); - inode = f2fs_new_inode(dir, mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -505,6 +504,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, init_special_inode(inode, inode->i_mode, rdev); inode->i_op = &f2fs_special_inode_operations; + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -531,9 +532,6 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, struct inode *inode; int err; - if (!whiteout) - f2fs_balance_fs(sbi); - inode = f2fs_new_inode(dir, mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -547,6 +545,8 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, inode->i_mapping->a_ops = &f2fs_dblock_aops; } + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = acquire_orphan_inode(sbi); if (err) @@ -619,8 +619,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } - f2fs_balance_fs(sbi); - old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) goto out; @@ -650,6 +648,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!new_entry) goto out_whiteout; + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = acquire_orphan_inode(sbi); @@ -681,6 +681,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, update_inode_page(old_inode); update_inode_page(new_inode); } else { + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = f2fs_add_link(new_dentry, old_inode); @@ -778,8 +780,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, new_inode))) return -EPERM; - f2fs_balance_fs(sbi); - old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) goto out; @@ -822,6 +822,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_new_dir; } + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name); -- GitLab From c22f7d4fa512fc51c43985a7a7c505c563271daf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Dec 2015 12:59:54 -0800 Subject: [PATCH 0046/5498] f2fs: record node block allocation in dnode_of_data This patch introduces recording node block allocation in dnode_of_data. This information helps to figure out whether any node block is allocated during specific file operations. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 + fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 61131e3971ad..09d9ba49ac6f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -227,6 +227,7 @@ void set_data_blkaddr(struct dnode_of_data *dn) addr_array = blkaddr_in_node(rn); addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr); set_page_dirty(node_page); + dn->node_changed = true; } int reserve_new_block(struct dnode_of_data *dn) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6ae93a0164ad..3efbdc387c69 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -545,6 +545,7 @@ struct dnode_of_data { nid_t nid; /* node id of the direct node block */ unsigned int ofs_in_node; /* data offset in the node page */ bool inode_page_locked; /* inode page is locked or not */ + bool node_changed; /* is node block changed */ block_t data_blkaddr; /* block address of the node block */ }; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index f3d34df89e84..4842ef6fc025 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -542,6 +542,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) set_nid(parent, offset[i - 1], nids[i], i == 1); alloc_nid_done(sbi, nids[i]); + dn->node_changed = true; done = true; } else if (mode == LOOKUP_NODE_RA && i == level && level > 1) { npage[i] = get_node_page_ra(parent, offset[i - 1]); @@ -678,6 +679,7 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, if (ret < 0) goto out_err; set_nid(page, i, 0, false); + dn->node_changed = true; } } else { child_nofs = nofs + ofs * (NIDS_PER_BLOCK + 1) + 1; @@ -691,6 +693,7 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, ret = truncate_nodes(&rdn, child_nofs, 0, depth - 1); if (ret == (NIDS_PER_BLOCK + 1)) { set_nid(page, i, 0, false); + dn->node_changed = true; child_nofs += ret; } else if (ret < 0 && ret != -ENOENT) { goto out_err; @@ -752,6 +755,7 @@ static int truncate_partial_nodes(struct dnode_of_data *dn, if (err < 0) goto fail; set_nid(pages[idx], i, 0, false); + dn->node_changed = true; } if (offset[idx + 1] == 0) { @@ -1159,6 +1163,7 @@ void sync_inode_page(struct dnode_of_data *dn) } else { update_inode_page(dn->inode); } + dn->node_changed = true; } int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, -- GitLab From c50265e7e5cf2fcf0406c945566c93eeb6512179 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 23 Dec 2015 17:11:43 +0800 Subject: [PATCH 0047/5498] f2fs: reduce covered region of sbi->cp_rwsem in f2fs_map_blocks Only cover sbi->cp_rwsem on one dnode page's allocation and modification instead of multiple's in f2fs_map_blocks, it can reduce the covered region of cp_rwsem, then we can avoid potential long time delay for concurrent checkpointer. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 09d9ba49ac6f..c9ecb1b32f8e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -591,7 +591,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, } if (create) - f2fs_lock_op(F2FS_I_SB(inode)); + f2fs_lock_op(sbi); /* When reading holes, we need its node page */ set_new_dnode(&dn, inode, NULL, NULL, 0); @@ -648,6 +648,11 @@ get_next: allocated = false; f2fs_put_dnode(&dn); + if (create) { + f2fs_unlock_op(sbi); + f2fs_lock_op(sbi); + } + set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, pgofs, mode); if (err) { @@ -703,7 +708,7 @@ put_out: f2fs_put_dnode(&dn); unlock_out: if (create) - f2fs_unlock_op(F2FS_I_SB(inode)); + f2fs_unlock_op(sbi); out: trace_f2fs_map_blocks(inode, map, err); return err; -- GitLab From 92e675dcecfdfadb58cd916fa9caef6bc5efa9bb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Dec 2015 13:23:35 -0800 Subject: [PATCH 0048/5498] f2fs: call f2fs_balance_fs only when node was changed If user tries to update or read data, we don't need to call f2fs_balance_fs which triggers f2fs_gc, which increases unnecessary long latency. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 26 ++++++++++++++++++++++---- fs/f2fs/file.c | 26 +++++++++----------------- fs/f2fs/inline.c | 4 ++++ 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c9ecb1b32f8e..4a74614b38ca 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -510,7 +510,6 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset, u64 end_offset; while (len) { - f2fs_balance_fs(sbi); f2fs_lock_op(sbi); /* When reading holes, we need its node page */ @@ -543,6 +542,9 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset, f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); + + if (dn.node_changed) + f2fs_balance_fs(sbi); } return; @@ -552,6 +554,8 @@ sync_out: f2fs_put_dnode(&dn); out: f2fs_unlock_op(sbi); + if (dn.node_changed) + f2fs_balance_fs(sbi); return; } @@ -650,6 +654,8 @@ get_next: if (create) { f2fs_unlock_op(sbi); + if (dn.node_changed) + f2fs_balance_fs(sbi); f2fs_lock_op(sbi); } @@ -707,8 +713,11 @@ sync_out: put_out: f2fs_put_dnode(&dn); unlock_out: - if (create) + if (create) { f2fs_unlock_op(sbi); + if (dn.node_changed) + f2fs_balance_fs(sbi); + } out: trace_f2fs_map_blocks(inode, map, err); return err; @@ -1416,8 +1425,6 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, trace_f2fs_write_begin(inode, pos, len, flags); - f2fs_balance_fs(sbi); - /* * We should check this at this moment to avoid deadlock on inode page * and #0 page. The locking rule for inline_data conversion should be: @@ -1467,6 +1474,17 @@ put_next: f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); + if (dn.node_changed && has_not_enough_free_secs(sbi, 0)) { + unlock_page(page); + f2fs_balance_fs(sbi); + lock_page(page); + if (page->mapping != mapping) { + /* The page got truncated from under us */ + f2fs_put_page(page, 1); + goto repeat; + } + } + f2fs_wait_on_page_writeback(page, DATA); /* wait for GCed encrypted page writeback */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 263201938cc7..a3f183edf674 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -41,8 +41,6 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, struct dnode_of_data dn; int err; - f2fs_balance_fs(sbi); - sb_start_pagefault(inode->i_sb); f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); @@ -58,6 +56,9 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); + if (dn.node_changed) + f2fs_balance_fs(sbi); + file_update_time(vma->vm_file); lock_page(page); if (unlikely(page->mapping != inode->i_mapping || @@ -234,9 +235,6 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) 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. @@ -268,6 +266,8 @@ sync_nodes: if (need_inode_block_update(sbi, ino)) { mark_inode_dirty_sync(inode); f2fs_write_inode(inode, NULL); + + f2fs_balance_fs(sbi); goto sync_nodes; } @@ -946,8 +946,6 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1)) return -EINVAL; - f2fs_balance_fs(F2FS_I_SB(inode)); - ret = f2fs_convert_inline_inode(inode); if (ret) return ret; @@ -994,8 +992,6 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, if (ret) return ret; - f2fs_balance_fs(sbi); - ret = f2fs_convert_inline_inode(inode); if (ret) return ret; @@ -1105,12 +1101,12 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1)) return -EINVAL; - f2fs_balance_fs(sbi); - ret = f2fs_convert_inline_inode(inode); if (ret) return ret; + f2fs_balance_fs(sbi); + ret = truncate_blocks(inode, i_size_read(inode), true); if (ret) return ret; @@ -1153,8 +1149,6 @@ static int expand_inode_data(struct inode *inode, loff_t offset, loff_t off_start, off_end; int ret = 0; - f2fs_balance_fs(sbi); - ret = inode_newsize_ok(inode, (len + offset)); if (ret) return ret; @@ -1163,6 +1157,8 @@ static int expand_inode_data(struct inode *inode, loff_t offset, if (ret) return ret; + f2fs_balance_fs(sbi); + pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; @@ -1352,8 +1348,6 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) if (!inode_owner_or_capable(inode)) return -EACCES; - f2fs_balance_fs(F2FS_I_SB(inode)); - if (f2fs_is_atomic_file(inode)) return 0; @@ -1440,8 +1434,6 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) if (ret) return ret; - f2fs_balance_fs(F2FS_I_SB(inode)); - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); commit_inmem_pages(inode, true); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 8090854dd29c..c24e5d93720d 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -202,6 +202,10 @@ out: f2fs_unlock_op(sbi); f2fs_put_page(page, 1); + + if (dn.node_changed) + f2fs_balance_fs(sbi); + return err; } -- GitLab From 4dcb623d4e38c51de35d3f8d6f9f578167150a72 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 23 Dec 2015 17:50:30 +0800 Subject: [PATCH 0049/5498] f2fs: report error of do_checkpoint do_checkpoint and write_checkpoint can fail due to reasons like triggering in a readonly fs or encountering IO error of storage device. So it's better to report such error info to user, let user be aware of failure of doing checkpoint. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 29 +++++++++++++++++++---------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 5 +++-- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 5 +++-- fs/f2fs/super.c | 5 +++-- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2acee04799cd..504da00de222 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -911,7 +911,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi) finish_wait(&sbi->cp_wait, &wait); } -static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) +static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); @@ -937,7 +937,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) while (get_pages(sbi, F2FS_DIRTY_META)) { sync_meta_pages(sbi, META, LONG_MAX); if (unlikely(f2fs_cp_error(sbi))) - return; + return -EIO; } next_free_nid(sbi, &last_nid); @@ -1022,7 +1022,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* need to wait for end_io results */ wait_on_all_pages_writeback(sbi); if (unlikely(f2fs_cp_error(sbi))) - return; + return -EIO; /* write out checkpoint buffer at block 0 */ update_meta_page(sbi, ckpt, start_blk++); @@ -1050,7 +1050,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) wait_on_all_pages_writeback(sbi); if (unlikely(f2fs_cp_error(sbi))) - return; + return -EIO; filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LONG_MAX); filemap_fdatawait_range(META_MAPPING(sbi), 0, LONG_MAX); @@ -1076,19 +1076,22 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) release_ino_entry(sbi); if (unlikely(f2fs_cp_error(sbi))) - return; + return -EIO; clear_prefree_segments(sbi, cpc); clear_sbi_flag(sbi, SBI_IS_DIRTY); + + return 0; } /* * We guarantee that this checkpoint procedure will not fail. */ -void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) +int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); unsigned long long ckpt_ver; + int err = 0; mutex_lock(&sbi->cp_mutex); @@ -1096,14 +1099,19 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) (cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC || (cpc->reason == CP_DISCARD && !sbi->discard_blks))) goto out; - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; goto out; - if (f2fs_readonly(sbi->sb)) + } + if (f2fs_readonly(sbi->sb)) { + err = -EROFS; goto out; + } trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops"); - if (block_operations(sbi)) + err = block_operations(sbi); + if (err) goto out; trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops"); @@ -1125,7 +1133,7 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) flush_sit_entries(sbi, cpc); /* unlock all the fs_lock[] in do_checkpoint() */ - do_checkpoint(sbi, cpc); + err = do_checkpoint(sbi, cpc); unblock_operations(sbi); stat_inc_cp_count(sbi->stat_info); @@ -1139,6 +1147,7 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint"); out: mutex_unlock(&sbi->cp_mutex); + return err; } void init_ino_entry_info(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3efbdc387c69..8b0edb0b56f5 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1836,7 +1836,7 @@ void update_dirty_page(struct inode *, struct page *); void add_dirty_dir_inode(struct inode *); void remove_dirty_inode(struct inode *); void sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type); -void write_checkpoint(struct f2fs_sb_info *, struct cp_control *); +int write_checkpoint(struct f2fs_sb_info *, struct cp_control *); void init_ino_entry_info(struct f2fs_sb_info *); int __init create_checkpoint_caches(void); void destroy_checkpoint_caches(void); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a3f183edf674..5fde384260cd 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1621,6 +1621,7 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) struct inode *inode = file_inode(filp); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct cp_control cpc; + int err; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1631,10 +1632,10 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) cpc.reason = __get_cp_reason(sbi); mutex_lock(&sbi->gc_mutex); - write_checkpoint(sbi, &cpc); + err = write_checkpoint(sbi, &cpc); mutex_unlock(&sbi->gc_mutex); - return 0; + return err; } static int f2fs_defragment_range(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 590f8b3d7d24..d24c1a040a13 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -623,7 +623,7 @@ out: .reason = CP_RECOVERY, }; mutex_unlock(&sbi->cp_mutex); - write_checkpoint(sbi, &cpc); + err = write_checkpoint(sbi, &cpc); } else { mutex_unlock(&sbi->cp_mutex); } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c2474509e5de..a3474bad5770 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1118,6 +1118,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; unsigned int start_segno, end_segno; struct cp_control cpc; + int err = 0; if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) return -EINVAL; @@ -1148,12 +1149,12 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) sbi->segs_per_sec) - 1, end_segno); mutex_lock(&sbi->gc_mutex); - write_checkpoint(sbi, &cpc); + err = write_checkpoint(sbi, &cpc); mutex_unlock(&sbi->gc_mutex); } out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); - return 0; + return err; } static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c3070c149c0e..597b533634e0 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -579,6 +579,7 @@ static void f2fs_put_super(struct super_block *sb) int f2fs_sync_fs(struct super_block *sb, int sync) { struct f2fs_sb_info *sbi = F2FS_SB(sb); + int err = 0; trace_f2fs_sync_fs(sb, sync); @@ -588,14 +589,14 @@ int f2fs_sync_fs(struct super_block *sb, int sync) cpc.reason = __get_cp_reason(sbi); mutex_lock(&sbi->gc_mutex); - write_checkpoint(sbi, &cpc); + err = write_checkpoint(sbi, &cpc); mutex_unlock(&sbi->gc_mutex); } else { f2fs_balance_fs(sbi); } f2fs_trace_ios(NULL, 1); - return 0; + return err; } static int f2fs_freeze(struct super_block *sb) -- GitLab From aee891cdbe6fa247db9f69356cc9a68d445654cd Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 23 Dec 2015 17:51:35 +0800 Subject: [PATCH 0050/5498] f2fs: don't convert inline inode when inline_data option is disable If inline_data option is disable, when truncating an inline inode with size which is not exceed maxinum inline size, we should not convert inline inode to regular one to avoid the overhead of synchronizing conversion. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 3 --- fs/f2fs/namei.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index c24e5d93720d..5ffbd169b719 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -16,9 +16,6 @@ bool f2fs_may_inline_data(struct inode *inode) { - if (!test_opt(F2FS_I_SB(inode), INLINE_DATA)) - return false; - if (f2fs_is_atomic_file(inode)) return false; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 3f8efc37eaa3..5e660380f779 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -61,7 +61,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) f2fs_set_encrypted_inode(inode); - if (f2fs_may_inline_data(inode)) + if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode)) set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); if (f2fs_may_inline_dentry(inode)) set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY); -- GitLab From e40afd28b325d74171e2a89380f17ecffa8a5804 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Dec 2015 11:55:18 -0800 Subject: [PATCH 0051/5498] f2fs: introduce prepare_write_begin to clean up This patch adds prepare_write_begin to clean f2fs_write_begin. The major role of this function is to convert any inline_data and allocate or find block address. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 92 +++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4a74614b38ca..347dce82bfcd 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1411,6 +1411,51 @@ static void f2fs_write_failed(struct address_space *mapping, loff_t to) } } +static int prepare_write_begin(struct f2fs_sb_info *sbi, + struct page *page, loff_t pos, unsigned len, + block_t *blk_addr, bool *node_changed) +{ + struct inode *inode = page->mapping->host; + pgoff_t index = page->index; + struct dnode_of_data dn; + struct page *ipage; + int err = 0; + + f2fs_lock_op(sbi); + + /* check inline_data */ + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto unlock_out; + } + + set_new_dnode(&dn, inode, ipage, ipage, 0); + + if (f2fs_has_inline_data(inode)) { + if (pos + len <= MAX_INLINE_DATA) { + read_inline_data(page, ipage); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + sync_inode_page(&dn); + goto done; + } else { + err = f2fs_convert_inline_page(&dn, page); + if (err) + goto err_out; + } + } + err = f2fs_get_block(&dn, index); +done: + /* convert_inline_page can make node_changed */ + *blk_addr = dn.data_blkaddr; + *node_changed = dn.node_changed; +err_out: + f2fs_put_dnode(&dn); +unlock_out: + f2fs_unlock_op(sbi); + return err; +} + static int f2fs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) @@ -1418,9 +1463,9 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *page = NULL; - struct page *ipage; pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; - struct dnode_of_data dn; + bool need_balance = false; + block_t blkaddr = NULL_ADDR; int err = 0; trace_f2fs_write_begin(inode, pos, len, flags); @@ -1444,37 +1489,12 @@ repeat: *pagep = page; - f2fs_lock_op(sbi); - - /* check inline_data */ - ipage = get_node_page(sbi, inode->i_ino); - if (IS_ERR(ipage)) { - err = PTR_ERR(ipage); - goto unlock_fail; - } - - set_new_dnode(&dn, inode, ipage, ipage, 0); - - if (f2fs_has_inline_data(inode)) { - if (pos + len <= MAX_INLINE_DATA) { - read_inline_data(page, ipage); - set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); - sync_inode_page(&dn); - goto put_next; - } - err = f2fs_convert_inline_page(&dn, page); - if (err) - goto put_fail; - } - - err = f2fs_get_block(&dn, index); + err = prepare_write_begin(sbi, page, pos, len, + &blkaddr, &need_balance); if (err) - goto put_fail; -put_next: - f2fs_put_dnode(&dn); - f2fs_unlock_op(sbi); + goto fail; - if (dn.node_changed && has_not_enough_free_secs(sbi, 0)) { + if (need_balance && has_not_enough_free_secs(sbi, 0)) { unlock_page(page); f2fs_balance_fs(sbi); lock_page(page); @@ -1489,7 +1509,7 @@ put_next: /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) - f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); + f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); if (len == PAGE_CACHE_SIZE) goto out_update; @@ -1505,14 +1525,14 @@ put_next: goto out_update; } - if (dn.data_blkaddr == NEW_ADDR) { + if (blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_CACHE_SIZE); } else { struct f2fs_io_info fio = { .sbi = sbi, .type = DATA, .rw = READ_SYNC, - .blk_addr = dn.data_blkaddr, + .blk_addr = blkaddr, .page = page, .encrypted_page = NULL, }; @@ -1543,10 +1563,6 @@ out_clear: clear_cold_data(page); return 0; -put_fail: - f2fs_put_dnode(&dn); -unlock_fail: - f2fs_unlock_op(sbi); fail: f2fs_put_page(page, 1); f2fs_write_failed(mapping, pos + len); -- GitLab From 938d4bc8517b147a82d92625e9fac766bca0faaf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Dec 2015 14:17:47 -0800 Subject: [PATCH 0052/5498] f2fs: return early when trying to read null nid If get_node_page() gets zero nid, we can return early without getting a wrong page. For example, get_dnode_of_data() can try to do that. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4842ef6fc025..7c507a6ed7f1 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1059,6 +1059,10 @@ struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) { struct page *page; int err; + + if (!nid) + return ERR_PTR(-ENOENT); + f2fs_bug_on(sbi, check_nid_range(sbi, nid)); repeat: page = grab_cache_page(NODE_MAPPING(sbi), nid); if (!page) -- GitLab From 1cc83f4410e20f10a079ef7e2c89b3795bf9ca8c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Dec 2015 13:48:58 -0800 Subject: [PATCH 0053/5498] f2fs: avoid f2fs_lock_op in f2fs_write_begin If f2fs_write_begin is to update data, we can bypass calling f2fs_lock_op() in order to avoid the checkpoint latency in the write syscall. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 347dce82bfcd..b86a23bf4c77 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1419,10 +1419,16 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, pgoff_t index = page->index; struct dnode_of_data dn; struct page *ipage; + bool locked = false; + struct extent_info ei; int err = 0; - f2fs_lock_op(sbi); - + if (f2fs_has_inline_data(inode) || + (pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { + f2fs_lock_op(sbi); + locked = true; + } +restart: /* check inline_data */ ipage = get_node_page(sbi, inode->i_ino); if (IS_ERR(ipage)) { @@ -1437,22 +1443,42 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, read_inline_data(page, ipage); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); sync_inode_page(&dn); - goto done; } else { err = f2fs_convert_inline_page(&dn, page); if (err) - goto err_out; + goto out; + if (dn.data_blkaddr == NULL_ADDR) + err = f2fs_get_block(&dn, index); + } + } else if (locked) { + err = f2fs_get_block(&dn, index); + } else { + if (f2fs_lookup_extent_cache(inode, index, &ei)) { + dn.data_blkaddr = ei.blk + index - ei.fofs; + } else { + bool restart = false; + + /* hole case */ + err = get_dnode_of_data(&dn, index, LOOKUP_NODE); + if (err || (!err && dn.data_blkaddr == NULL_ADDR)) + restart = true; + if (restart) { + f2fs_put_dnode(&dn); + f2fs_lock_op(sbi); + locked = true; + goto restart; + } } } - err = f2fs_get_block(&dn, index); -done: + /* convert_inline_page can make node_changed */ *blk_addr = dn.data_blkaddr; *node_changed = dn.node_changed; -err_out: +out: f2fs_put_dnode(&dn); unlock_out: - f2fs_unlock_op(sbi); + if (locked) + f2fs_unlock_op(sbi); return err; } -- GitLab From 335eb94b888a7eec0ae018158b9fd238aa1f0f18 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Dec 2015 14:56:09 -0800 Subject: [PATCH 0054/5498] f2fs: declare static function The __f2fs_commit_super is static. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 597b533634e0..75704d9caae2 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1195,7 +1195,7 @@ next: return 0; } -int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block) +static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block) { struct f2fs_super_block *super = F2FS_RAW_SUPER(sbi); struct buffer_head *bh; -- GitLab From 0a61a838c5b95d2f66f1660c9ebed209e264a536 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 24 Dec 2015 18:03:29 +0800 Subject: [PATCH 0055/5498] f2fs: add missing f2fs_balance_fs in __recover_dot_dentries __recover_do_dentries will try to grab free space in storage, so fix to add missing f2fs_balance_fs here. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 5e660380f779..f7181d1807d6 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -215,6 +215,8 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) struct page *page; int err = 0; + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); de = f2fs_find_entry(dir, &dot, &page); -- GitLab From 34ac785aa9b787cf5867162b35b012a5857d28c6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 24 Dec 2015 18:04:56 +0800 Subject: [PATCH 0056/5498] f2fs: let user being aware of IO error Sometimes we keep dumb when IO error occur in lower layer device, so user will not receive any error return value for some operation, but actually, the operation did not succeed. This sould be avoided, so this patch reports such kind of error to user. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/checkpoint.c | 17 +++++++---------- fs/f2fs/data.c | 23 +++++++++++++---------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 4 +++- fs/f2fs/gc.c | 4 +++- fs/f2fs/node.c | 5 +++++ 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 504da00de222..f04f7052833f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -799,7 +799,7 @@ void remove_dirty_inode(struct inode *inode) } } -void sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type) +int sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type) { struct list_head *head; struct inode *inode; @@ -811,7 +811,7 @@ void sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type) F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA)); retry: if (unlikely(f2fs_cp_error(sbi))) - return; + return -EIO; spin_lock(&sbi->inode_lock[type]); @@ -821,7 +821,7 @@ retry: trace_f2fs_sync_dirty_inodes_exit(sbi->sb, is_dir, get_pages(sbi, is_dir ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA)); - return; + return 0; } fi = list_entry(head->next, struct f2fs_inode_info, dirty_list); inode = igrab(&fi->vfs_inode); @@ -860,11 +860,9 @@ retry_flush_dents: /* write all the dirty dentry pages */ if (get_pages(sbi, F2FS_DIRTY_DENTS)) { f2fs_unlock_all(sbi); - sync_dirty_inodes(sbi, DIR_INODE); - if (unlikely(f2fs_cp_error(sbi))) { - err = -EIO; + err = sync_dirty_inodes(sbi, DIR_INODE); + if (err) goto out; - } goto retry_flush_dents; } @@ -877,10 +875,9 @@ retry_flush_nodes: if (get_pages(sbi, F2FS_DIRTY_NODES)) { up_write(&sbi->node_write); - sync_node_pages(sbi, 0, &wbc); - if (unlikely(f2fs_cp_error(sbi))) { + err = sync_node_pages(sbi, 0, &wbc); + if (err) { f2fs_unlock_all(sbi); - err = -EIO; goto out; } goto retry_flush_nodes; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b86a23bf4c77..c5db36ae14ea 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -499,7 +499,7 @@ alloc: return 0; } -static void __allocate_data_blocks(struct inode *inode, loff_t offset, +static int __allocate_data_blocks(struct inode *inode, loff_t offset, size_t count) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -508,13 +508,15 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset, u64 len = F2FS_BYTES_TO_BLK(count); bool allocated; u64 end_offset; + int err = 0; while (len) { f2fs_lock_op(sbi); /* When reading holes, we need its node page */ set_new_dnode(&dn, inode, NULL, NULL, 0); - if (get_dnode_of_data(&dn, start, ALLOC_NODE)) + err = get_dnode_of_data(&dn, start, ALLOC_NODE); + if (err) goto out; allocated = false; @@ -523,12 +525,15 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset, while (dn.ofs_in_node < end_offset && len) { block_t blkaddr; - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; goto sync_out; + } blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) { - if (__allocate_data_block(&dn)) + err = __allocate_data_block(&dn); + if (err) goto sync_out; allocated = true; } @@ -546,7 +551,7 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset, if (dn.node_changed) f2fs_balance_fs(sbi); } - return; + return err; sync_out: if (allocated) @@ -556,7 +561,7 @@ out: f2fs_unlock_op(sbi); if (dn.node_changed) f2fs_balance_fs(sbi); - return; + return err; } /* @@ -1654,11 +1659,9 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, trace_f2fs_direct_IO_enter(inode, offset, count, rw); if (rw & WRITE) { - __allocate_data_blocks(inode, offset, count); - if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) { - err = -EIO; + err = __allocate_data_blocks(inode, offset, count); + if (err) goto out; - } } err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block_dio); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8b0edb0b56f5..1edf8f0fb02f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1835,7 +1835,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *); void update_dirty_page(struct inode *, struct page *); void add_dirty_dir_inode(struct inode *); void remove_dirty_inode(struct inode *); -void sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type); +int sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type); int write_checkpoint(struct f2fs_sb_info *, struct cp_control *); void init_ino_entry_info(struct f2fs_sb_info *); int __init create_checkpoint_caches(void); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 5fde384260cd..e2db7e039414 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -260,8 +260,10 @@ sync_nodes: sync_node_pages(sbi, ino, &wbc); /* if cp_error was enabled, we should avoid infinite loop */ - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) { + ret = -EIO; goto out; + } if (need_inode_block_update(sbi, ino)) { mark_inode_dirty_sync(inode); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ce350c44b5cf..c09be339569c 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -832,8 +832,10 @@ gc_more: if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) goto stop; - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) { + ret = -EIO; goto stop; + } if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) { gc_type = FG_GC; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7c507a6ed7f1..bb6ea394a649 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1195,6 +1195,11 @@ next_step: for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; + if (unlikely(f2fs_cp_error(sbi))) { + pagevec_release(&pvec); + return -EIO; + } + /* * flushing sequence with step: * 0. indirect nodes -- GitLab From 6134ebd0c74a2171226b76ee9e99f1e9aca43ea1 Mon Sep 17 00:00:00 2001 From: Fan Li Date: Sat, 26 Dec 2015 18:07:41 +0800 Subject: [PATCH 0057/5498] f2fs: fix bugs and simplify codes of f2fs_fiemap fix bugs: 1. len could be updated incorrectly when start+len is beyond isize. 2. If there is a hole consisting of more than two blocks, it could fail to add FIEMAP_EXTENT_LAST flag for the last extent. 3. If there is an extent beyond isize, when we search extents in a range that ends at isize, it will also return the extent beyond isize, which is outside the range. Signed-off-by: Fan li Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 80 +++++++++++++++++--------------------------------- 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c5db36ae14ea..603d3ab84393 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -784,7 +784,6 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, loff_t isize = i_size_read(inode); u64 logical = 0, phys = 0, size = 0; u32 flags = 0; - bool past_eof = false, whole_file = false; int ret = 0; ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); @@ -798,17 +797,18 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, } mutex_lock(&inode->i_mutex); + if (start >= isize) + goto out; - if (len >= isize) { - whole_file = true; - len = isize; - } + if (start + len > isize) + len = isize - start; if (logical_to_blk(inode, len) == 0) len = blk_to_logical(inode, 1); start_blk = logical_to_blk(inode, start); last_blk = logical_to_blk(inode, start + len - 1); + next: memset(&map_bh, 0, sizeof(struct buffer_head)); map_bh.b_size = len; @@ -820,59 +820,33 @@ next: /* HOLE */ if (!buffer_mapped(&map_bh)) { - start_blk++; - - if (!past_eof && blk_to_logical(inode, start_blk) >= isize) - past_eof = 1; - - if (past_eof && size) { - flags |= FIEMAP_EXTENT_LAST; - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, flags); - } else if (size) { - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, flags); - size = 0; - } + /* Go through holes util pass the EOF */ + if (blk_to_logical(inode, start_blk++) < isize) + goto prep_next; + /* Found a hole beyond isize means no more extents. + * Note that the premise is that filesystems don't + * punch holes beyond isize and keep size unchanged. + */ + flags |= FIEMAP_EXTENT_LAST; + } - /* if we have holes up to/past EOF then we're done */ - if (start_blk > last_blk || past_eof || ret) - goto out; - } else { - if (start_blk > last_blk && !whole_file) { - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, flags); - goto out; - } + if (size) + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); - /* - * if size != 0 then we know we already have an extent - * to add, so add it. - */ - if (size) { - ret = fiemap_fill_next_extent(fieinfo, logical, - phys, size, flags); - if (ret) - goto out; - } + if (start_blk > last_blk || ret) + goto out; - logical = blk_to_logical(inode, start_blk); - phys = blk_to_logical(inode, map_bh.b_blocknr); - size = map_bh.b_size; - flags = 0; - if (buffer_unwritten(&map_bh)) - flags = FIEMAP_EXTENT_UNWRITTEN; + logical = blk_to_logical(inode, start_blk); + phys = blk_to_logical(inode, map_bh.b_blocknr); + size = map_bh.b_size; + flags = 0; + if (buffer_unwritten(&map_bh)) + flags = FIEMAP_EXTENT_UNWRITTEN; - start_blk += logical_to_blk(inode, size); + start_blk += logical_to_blk(inode, size); - /* - * If we are past the EOF, then we need to make sure as - * soon as we find a hole that the last extent we found - * is marked with FIEMAP_EXTENT_LAST - */ - if (!past_eof && logical + size >= isize) - past_eof = true; - } +prep_next: cond_resched(); if (fatal_signal_pending(current)) ret = -EINTR; -- GitLab From 7544b52a0d5bb60035c57661049a16daa28fea71 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 28 Dec 2015 21:48:32 +0800 Subject: [PATCH 0058/5498] f2fs: add a max block check for get_data_block_bmap This patch adds a max block check for get_data_block_bmap. Trinity test program will send a block number as parameter into ioctl_fibmap, which will be used in get_node_path(), when the block number large than f2fs max blocks, it will trigger kernel bug. Signed-off-by: Yunlei He Signed-off-by: Xue Liu [Jaegeuk Kim: fix missing condition, pointed by Chao Yu] Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 603d3ab84393..096f5f38a033 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -762,6 +762,10 @@ static int get_data_block_dio(struct inode *inode, sector_t iblock, static int get_data_block_bmap(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { + /* Block number less than F2FS MAX BLOCKS */ + if (unlikely(iblock >= max_file_size(0))) + return -EFBIG; + return __get_data_block(inode, iblock, bh_result, create, F2FS_GET_BLOCK_BMAP); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1edf8f0fb02f..37d38eb278b2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1724,6 +1724,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) * super.c */ int f2fs_commit_super(struct f2fs_sb_info *, bool); +loff_t max_file_size(unsigned bits); int f2fs_sync_fs(struct super_block *, int); extern __printf(3, 4) void f2fs_msg(struct super_block *, const char *, const char *, ...); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 75704d9caae2..a2e3a8f893ed 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -907,7 +907,7 @@ static const struct export_operations f2fs_export_ops = { .get_parent = f2fs_get_parent, }; -static loff_t max_file_size(unsigned bits) +loff_t max_file_size(unsigned bits) { loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS); loff_t leaf_count = ADDRS_PER_BLOCK; -- GitLab From c80417b4bb61a2e6ee135913594b017d49c394a7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 24 Dec 2015 18:11:32 +0800 Subject: [PATCH 0059/5498] f2fs: clean up f2fs_ioc_write_checkpoint Use f2fs_sync_fs to clean up codes in f2fs_ioc_write_checkpoint. Signed-off-by: Chao Yu [Jaegeuk Kim: remove unused err variable] Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e2db7e039414..3f432e2cbdc1 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1622,8 +1622,6 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct cp_control cpc; - int err; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1631,13 +1629,7 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) if (f2fs_readonly(sbi->sb)) return -EROFS; - cpc.reason = __get_cp_reason(sbi); - - mutex_lock(&sbi->gc_mutex); - err = write_checkpoint(sbi, &cpc); - mutex_unlock(&sbi->gc_mutex); - - return err; + return f2fs_sync_fs(sbi->sb, 1); } static int f2fs_defragment_range(struct f2fs_sb_info *sbi, -- GitLab From 25639da0add46f59c726ddcbd13c9e498d625733 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 24 Dec 2015 16:13:09 -0800 Subject: [PATCH 0060/5498] f2fs: early check broken symlink length in the encrypted case If link is broken, its len is zero, and we don't need to move forward. Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index f7181d1807d6..a90b4dbe6754 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -947,7 +947,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, { struct page *cpage = NULL; char *caddr, *paddr = NULL; - struct f2fs_str cstr; + struct f2fs_str cstr = FSTR_INIT(NULL, 0); struct f2fs_str pstr = FSTR_INIT(NULL, 0); struct inode *inode = dentry->d_inode; struct f2fs_encrypted_symlink_data *sd; @@ -968,6 +968,12 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, /* Symlink is encrypted */ sd = (struct f2fs_encrypted_symlink_data *)caddr; cstr.len = le16_to_cpu(sd->len); + + /* this is broken symlink case */ + if (unlikely(cstr.len == 0)) { + res = -ENOENT; + goto errout; + } cstr.name = kmalloc(cstr.len, GFP_NOFS); if (!cstr.name) { res = -ENOMEM; @@ -976,7 +982,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, memcpy(cstr.name, sd->encrypted_path, cstr.len); /* this is broken symlink case */ - if (cstr.name[0] == 0 && cstr.len == 0) { + if (unlikely(cstr.name[0] == 0)) { res = -ENOENT; goto errout; } -- GitLab From b1494c84c3ec1a3c02ad6332ca179dfca5e0d69f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 28 Dec 2015 13:48:11 -0800 Subject: [PATCH 0061/5498] f2fs: use i_size_read to get i_size We need to use i_size_read() to get inode->i_size. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 096f5f38a033..f67b4302b639 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1387,10 +1387,11 @@ skip_write: static void f2fs_write_failed(struct address_space *mapping, loff_t to) { struct inode *inode = mapping->host; + loff_t i_size = i_size_read(inode); - if (to > inode->i_size) { - truncate_pagecache(inode, inode->i_size); - truncate_blocks(inode, inode->i_size, true); + if (to > i_size) { + truncate_pagecache(inode, i_size); + truncate_blocks(inode, i_size, true); } } -- GitLab From 897b7a2e0b2e87895e5187d569b7ac2b057dc2d0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 28 Dec 2015 11:39:06 -0800 Subject: [PATCH 0062/5498] f2fs: load largest extent all the time Otherwise, we can get mismatched largest extent information. One example is: 1. mount f2fs w/ extent_cache 2. make a small extent 3. umount 4. mount f2fs w/o extent_cache 5. update the largest extent 6. umount 7. mount f2fs w/ extent_cache 8. get the old extent made by #2 Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 18 +++++++++++++----- fs/f2fs/f2fs.h | 2 +- fs/f2fs/inode.c | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 5305a29f91a3..b37184f720e8 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -166,20 +166,27 @@ static void __drop_largest_extent(struct inode *inode, largest->len = 0; } -void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) +/* return true, if inode page is changed */ +bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct extent_tree *et; struct extent_node *en; struct extent_info ei; - if (!f2fs_may_extent_tree(inode)) - return; + if (!f2fs_may_extent_tree(inode)) { + /* drop largest extent */ + if (i_ext && i_ext->len) { + i_ext->len = 0; + return true; + } + return false; + } et = __grab_extent_tree(inode); - if (!i_ext || le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN) - return; + if (!i_ext || !i_ext->len) + return false; set_extent_info(&ei, le32_to_cpu(i_ext->fofs), le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); @@ -196,6 +203,7 @@ void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) } out: write_unlock(&et->lock); + return false; } static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 37d38eb278b2..586d43d25747 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2081,7 +2081,7 @@ void f2fs_leave_shrinker(struct f2fs_sb_info *); * extent_cache.c */ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int); -void f2fs_init_extent_tree(struct inode *, struct f2fs_extent *); +bool f2fs_init_extent_tree(struct inode *, struct f2fs_extent *); unsigned int f2fs_destroy_extent_node(struct inode *); void f2fs_destroy_extent_tree(struct inode *); bool f2fs_lookup_extent_cache(struct inode *, pgoff_t, struct extent_info *); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 9705fca793d6..c43b0fac6284 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -138,7 +138,8 @@ static int do_read_inode(struct inode *inode) fi->i_pino = le32_to_cpu(ri->i_pino); fi->i_dir_level = ri->i_dir_level; - f2fs_init_extent_tree(inode, &ri->i_ext); + if (f2fs_init_extent_tree(inode, &ri->i_ext)) + set_page_dirty(node_page); get_inline_info(fi, ri); -- GitLab From 58199b87b5ab6eb5f2fb8ce5f364ac01012af443 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 30 Dec 2015 17:40:31 +0800 Subject: [PATCH 0063/5498] f2fs: fix to skip recovering dot dentries in a readonly fs If filesystem is readonly, leave user message info instead of recovering inline dot inode. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index a90b4dbe6754..c27d3b60f416 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -215,6 +215,13 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) struct page *page; int err = 0; + if (f2fs_readonly(sbi->sb)) { + f2fs_msg(sbi->sb, KERN_INFO, + "skip recovering inline_dots inode (ino:%lu, pino:%u) " + "in readonly mountpoint", dir->i_ino, pino); + return 0; + } + f2fs_balance_fs(sbi); f2fs_lock_op(sbi); -- GitLab From eabbedc232bc96b47b0c1f1fb1c13b7789db6515 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 29 Dec 2015 15:46:33 -0800 Subject: [PATCH 0064/5498] f2fs: fix f2fs_ioc_abort_volatile_write There are two rules to handle aborting volatile or atomic writes. 1. drop atomic writes - we don't need to keep any stale db data. 2. write journal data - we should keep the journal data with fsync for db recovery. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3f432e2cbdc1..4c3b20392839 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1436,9 +1436,14 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) if (ret) return ret; - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); - clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); - commit_inmem_pages(inode, true); + if (f2fs_is_atomic_file(inode)) { + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + commit_inmem_pages(inode, true); + } + if (f2fs_is_volatile_file(inode)) { + clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0); + } mnt_drop_write_file(filp); return ret; -- GitLab From ae189e07977d155e995e813395a6964797f8f62b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Dec 2015 10:28:52 -0800 Subject: [PATCH 0065/5498] f2fs: remove f2fs_bug_on in terms of max_depth There is no report on this bug_on case, but if malicious attacker changed this field intentionally, we can just reset it as a MAX value. Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 4e71e4166c67..2a1c3aef7b8e 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -172,8 +172,6 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, namehash = f2fs_dentry_hash(&name); - f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH); - nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level); nblock = bucket_blocks(level); @@ -238,6 +236,14 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, goto out; max_depth = F2FS_I(dir)->i_current_depth; + if (unlikely(max_depth > MAX_DIR_HASH_DEPTH)) { + f2fs_msg(F2FS_I_SB(dir)->sb, KERN_WARNING, + "Corrupted max_depth of %lu: %u", + dir->i_ino, max_depth); + max_depth = MAX_DIR_HASH_DEPTH; + F2FS_I(dir)->i_current_depth = max_depth; + mark_inode_dirty(dir); + } for (level = 0; level < max_depth; level++) { de = find_in_level(dir, level, &fname, res_page); -- GitLab From feecfb2a4dc6e3b6788fd441bcfbd1ddc540ec86 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Dec 2015 13:08:02 -0800 Subject: [PATCH 0066/5498] f2fs: write pending bios when cp_error is set When testing ioc_shutdown, put_super is able to be hanged by waiting for writebacking pages as follows. INFO: task umount:2723 blocked for more than 120 seconds. Tainted: G O 4.4.0-rc3+ #8 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. umount D ffff88000859f9d8 0 2723 2110 0x00000000 ffff88000859f9d8 0000000000000000 0000000000000000 ffffffff81e11540 ffff880078c225c0 ffff8800085a0000 ffff88007fc17440 7fffffffffffffff ffffffff818239f0 ffff88000859fb48 ffff88000859f9f0 ffffffff8182310c Call Trace: [] ? bit_wait+0x50/0x50 [] schedule+0x3c/0x90 [] schedule_timeout+0x2d9/0x430 [] ? mark_held_locks+0x6f/0xa0 [] ? ktime_get+0x7d/0x140 [] ? bit_wait+0x50/0x50 [] ? kvm_clock_get_cycles+0x25/0x30 [] ? ktime_get+0xac/0x140 [] ? bit_wait+0x50/0x50 [] io_schedule_timeout+0xa4/0x110 [] bit_wait_io+0x35/0x50 [] __wait_on_bit+0x5d/0x90 [] wait_on_page_bit+0xcb/0xf0 [] ? autoremove_wake_function+0x40/0x40 [] truncate_inode_pages_range+0x4bc/0x840 [] truncate_inode_pages_final+0x4d/0x60 [] f2fs_evict_inode+0x75/0x400 [f2fs] [] evict+0xbc/0x190 [] iput+0x229/0x2c0 [] f2fs_put_super+0x105/0x1a0 [f2fs] [] generic_shutdown_super+0x6a/0xf0 [] kill_block_super+0x27/0x70 [] kill_f2fs_super+0x20/0x30 [f2fs] [] deactivate_locked_super+0x43/0x70 [] deactivate_super+0x5c/0x60 [] cleanup_mnt+0x3f/0x90 [] __cleanup_mnt+0x12/0x20 [] task_work_run+0x73/0xa0 [] exit_to_usermode_loop+0xcc/0xd0 [] syscall_return_slowpath+0xcc/0xe0 [] int_ret_from_sys_call+0x25/0x9f Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 2 +- fs/f2fs/node.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index f04f7052833f..f3186684e5a6 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -238,7 +238,7 @@ static int f2fs_write_meta_page(struct page *page, dec_page_count(sbi, F2FS_DIRTY_META); unlock_page(page); - if (wbc->for_reclaim) + if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) f2fs_submit_merged_bio(sbi, META, WRITE); return 0; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f67b4302b639..4ef11526eb3c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1180,7 +1180,7 @@ out: unlock_page(page); if (need_balance_fs) f2fs_balance_fs(sbi); - if (wbc->for_reclaim) { + if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) { f2fs_submit_merged_bio(sbi, DATA, WRITE); remove_dirty_inode(inode); } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index bb6ea394a649..15b5907c4a2c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1374,7 +1374,7 @@ static int f2fs_write_node_page(struct page *page, up_read(&sbi->node_write); unlock_page(page); - if (wbc->for_reclaim) + if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) f2fs_submit_merged_bio(sbi, NODE, WRITE); return 0; -- GitLab From 5a5a230b67a2f210b033de60c294e417daf53680 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Dec 2015 13:49:17 -0800 Subject: [PATCH 0067/5498] f2fs: use IPU for fdatasync This patch fixes missing IPU condition when fdatasync is called. With this patch, fdatasync is able to avoid additional node writes for recovery. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4c3b20392839..63992e185798 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -203,7 +203,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) 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) + if (datasync || 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); -- GitLab From 410c9da33836aa98ffbad4cccda2d13e165dea90 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Dec 2015 15:24:14 -0800 Subject: [PATCH 0068/5498] f2fs: monitor zombie_tree count This patch adds an entry to show the number of zombie extent_tree. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 5 +++-- fs/f2fs/f2fs.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index ed5dfcc8886f..b73e8e133c8b 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -39,6 +39,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree; si->total_ext = atomic64_read(&sbi->total_hit_ext); si->ext_tree = atomic_read(&sbi->total_ext_tree); + si->zombie_tree = atomic_read(&sbi->total_zombie_tree); si->ext_node = atomic_read(&sbi->total_ext_node); si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES); si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); @@ -292,8 +293,8 @@ static int stat_show(struct seq_file *s, void *v) !si->total_ext ? 0 : div64_u64(si->hit_total * 100, si->total_ext), si->hit_total, si->total_ext); - seq_printf(s, " - Inner Struct Count: tree: %d, node: %d\n", - si->ext_tree, si->ext_node); + seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", + si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); seq_printf(s, " - inmem: %4d, wb: %4d\n", si->inmem_pages, si->wb_pages); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 586d43d25747..46449ae4b339 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1888,7 +1888,7 @@ struct f2fs_stat_info { int main_area_segs, main_area_sections, main_area_zones; unsigned long long hit_largest, hit_cached, hit_rbtree; unsigned long long hit_total, total_ext; - int ext_tree, ext_node; + int ext_tree, zombie_tree, ext_node; int ndirty_node, ndirty_meta; int ndirty_dent, ndirty_dirs, ndirty_data, ndirty_files; int nats, dirty_nats, sits, dirty_sits, fnids; -- GitLab From a0296744a2c790afd5f81480c237c824bc51366d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Dec 2015 15:02:16 -0800 Subject: [PATCH 0069/5498] f2fs: introduce zombie list for fast shrinking extent trees This patch removes refcount, and instead, adds zombie_list to shrink directly without radix tree traverse. Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 49 ++++++++++++++++++------------------------ fs/f2fs/f2fs.h | 3 ++- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index b37184f720e8..4dee2be9a648 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -68,13 +68,13 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) et->root = RB_ROOT; et->cached_en = NULL; rwlock_init(&et->lock); - atomic_set(&et->refcount, 0); + INIT_LIST_HEAD(&et->list); et->count = 0; atomic_inc(&sbi->total_ext_tree); } else { atomic_dec(&sbi->total_zombie_tree); + list_del_init(&et->list); } - atomic_inc(&et->refcount); up_write(&sbi->extent_tree_lock); /* never died until evict_inode */ @@ -551,9 +551,9 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) { struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; + struct extent_tree *et, *next; struct extent_node *en, *tmp; unsigned long ino = F2FS_ROOT_INO(sbi); - struct radix_tree_root *root = &sbi->extent_tree_root; unsigned int found; unsigned int node_cnt = 0, tree_cnt = 0; int remained; @@ -569,29 +569,20 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) goto out; /* 1. remove unreferenced extent tree */ - while ((found = radix_tree_gang_lookup(root, - (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { - unsigned i; - - ino = treevec[found - 1]->ino + 1; - for (i = 0; i < found; i++) { - struct extent_tree *et = treevec[i]; - - if (!atomic_read(&et->refcount)) { - write_lock(&et->lock); - node_cnt += __free_extent_tree(sbi, et, true); - write_unlock(&et->lock); + list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { + write_lock(&et->lock); + node_cnt += __free_extent_tree(sbi, et, true); + write_unlock(&et->lock); - radix_tree_delete(root, et->ino); - kmem_cache_free(extent_tree_slab, et); - atomic_dec(&sbi->total_ext_tree); - atomic_dec(&sbi->total_zombie_tree); - tree_cnt++; + list_del_init(&et->list); + radix_tree_delete(&sbi->extent_tree_root, et->ino); + kmem_cache_free(extent_tree_slab, et); + atomic_dec(&sbi->total_ext_tree); + atomic_dec(&sbi->total_zombie_tree); + tree_cnt++; - if (node_cnt + tree_cnt >= nr_shrink) - goto unlock_out; - } - } + if (node_cnt + tree_cnt >= nr_shrink) + goto unlock_out; } up_write(&sbi->extent_tree_lock); @@ -619,7 +610,7 @@ free_node: */ ino = F2FS_ROOT_INO(sbi); - while ((found = radix_tree_gang_lookup(root, + while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root, (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { unsigned i; @@ -670,8 +661,10 @@ void f2fs_destroy_extent_tree(struct inode *inode) return; if (inode->i_nlink && !is_bad_inode(inode) && et->count) { - atomic_dec(&et->refcount); + down_write(&sbi->extent_tree_lock); + list_add_tail(&et->list, &sbi->zombie_list); atomic_inc(&sbi->total_zombie_tree); + up_write(&sbi->extent_tree_lock); return; } @@ -680,8 +673,7 @@ void f2fs_destroy_extent_tree(struct inode *inode) /* delete extent tree entry in radix tree */ down_write(&sbi->extent_tree_lock); - atomic_dec(&et->refcount); - f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count); + f2fs_bug_on(sbi, et->count); radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); kmem_cache_free(extent_tree_slab, et); atomic_dec(&sbi->total_ext_tree); @@ -737,6 +729,7 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sbi->extent_list); spin_lock_init(&sbi->extent_lock); atomic_set(&sbi->total_ext_tree, 0); + INIT_LIST_HEAD(&sbi->zombie_list); atomic_set(&sbi->total_zombie_tree, 0); atomic_set(&sbi->total_ext_node, 0); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 46449ae4b339..6ad26ca03d74 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -358,8 +358,8 @@ struct extent_tree { struct rb_root root; /* root of extent info rb-tree */ struct extent_node *cached_en; /* recently accessed extent node */ struct extent_info largest; /* largested extent info */ + struct list_head list; /* to be used by sbi->zombie_list */ rwlock_t lock; /* protect extent info rb-tree */ - atomic_t refcount; /* reference count of rb-tree */ unsigned int count; /* # of extent node in rb-tree*/ }; @@ -763,6 +763,7 @@ struct f2fs_sb_info { struct list_head extent_list; /* lru list for shrinker */ spinlock_t extent_lock; /* locking extent lru list */ atomic_t total_ext_tree; /* extent tree count */ + struct list_head zombie_list; /* extent zombie tree list */ atomic_t total_zombie_tree; /* extent zombie tree count */ atomic_t total_ext_node; /* extent info count */ -- GitLab From 88c3b9a206c9933fafb55642b2a5f62599d42e71 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 31 Dec 2015 18:20:10 +0800 Subject: [PATCH 0070/5498] f2fs crypto: check CONFIG_F2FS_FS_XATTR for encrypted symlink Add missed CONFIG_F2FS_FS_XATTR for encrypted symlink inode in order to avoid unneeded registry of ->{get,set,remove}xattr. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index c27d3b60f416..d1d38d5c7441 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -1041,10 +1041,12 @@ const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .put_link = kfree_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, +#ifdef CONFIG_F2FS_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = f2fs_listxattr, .removexattr = generic_removexattr, +#endif }; #endif -- GitLab From c333481953338ed2de30ebe52201339d193ead99 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 31 Dec 2015 14:35:37 +0800 Subject: [PATCH 0071/5498] f2fs: introduce max_file_blocks in sbi Introduce max_file_blocks in sbi to store max block index of file in f2fs, it could be used to avoid unneeded calculation of max block index in runtime. Signed-off-by: Chao Yu [Jaegeuk Kim: fix overflow of sbi->max_file_blocks] Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 2 +- fs/f2fs/super.c | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4ef11526eb3c..a6a7c8264ca7 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -763,7 +763,7 @@ static int get_data_block_bmap(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { /* Block number less than F2FS MAX BLOCKS */ - if (unlikely(iblock >= max_file_size(0))) + if (unlikely(iblock >= F2FS_I_SB(inode)->max_file_blocks)) return -EFBIG; return __get_data_block(inode, iblock, bh_result, create, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6ad26ca03d74..6871b3c55530 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -782,6 +782,7 @@ struct f2fs_sb_info { unsigned int total_node_count; /* total node block count */ unsigned int total_valid_node_count; /* valid node block count */ unsigned int total_valid_inode_count; /* valid inode count */ + loff_t max_file_blocks; /* max block index of file */ int active_logs; /* # of active logs */ int dir_level; /* directory level */ @@ -1725,7 +1726,6 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) * super.c */ int f2fs_commit_super(struct f2fs_sb_info *, bool); -loff_t max_file_size(unsigned bits); int f2fs_sync_fs(struct super_block *, int); extern __printf(3, 4) void f2fs_msg(struct super_block *, const char *, const char *, ...); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a2e3a8f893ed..0bbd756821a7 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -907,7 +907,7 @@ static const struct export_operations f2fs_export_ops = { .get_parent = f2fs_get_parent, }; -loff_t max_file_size(unsigned bits) +static loff_t max_file_blocks(void) { loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS); loff_t leaf_count = ADDRS_PER_BLOCK; @@ -923,7 +923,6 @@ loff_t max_file_size(unsigned bits) leaf_count *= NIDS_PER_BLOCK; result += leaf_count; - result <<= bits; return result; } @@ -1278,7 +1277,9 @@ try_onemore: if (err) goto free_options; - sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); + sbi->max_file_blocks = max_file_blocks(); + sb->s_maxbytes = sbi->max_file_blocks << + le32_to_cpu(raw_super->log_blocksize); sb->s_max_links = F2FS_LINK_MAX; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); -- GitLab From 5993c1892c04a282d71462e062cb02b6d0015c62 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 2 Jan 2016 09:19:41 -0800 Subject: [PATCH 0072/5498] f2fs: cover more area with nat_tree_lock There was a subtle bug on nat cache management which incurs wrong nid allocation or wrong block addresses when try_to_free_nats is triggered heavily. This patch enlarges the previous coverage of nat_tree_lock to avoid data race. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 15b5907c4a2c..5f0633ba578e 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -262,13 +262,11 @@ static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, { struct nat_entry *e; - down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); if (!e) { e = grab_nat_entry(nm_i, nid); node_info_from_raw_nat(&e->ni, ne); } - up_write(&nm_i->nat_tree_lock); } static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, @@ -380,6 +378,8 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) memset(&ne, 0, sizeof(struct f2fs_nat_entry)); + down_write(&nm_i->nat_tree_lock); + /* Check current segment summary */ mutex_lock(&curseg->curseg_mutex); i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0); @@ -400,6 +400,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) cache: /* cache nat entry */ cache_nat_entry(NM_I(sbi), nid, &ne); + up_write(&nm_i->nat_tree_lock); } /* @@ -1465,13 +1466,10 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) if (build) { /* do not add allocated nids */ - down_read(&nm_i->nat_tree_lock); ne = __lookup_nat_cache(nm_i, nid); - if (ne && - (!get_nat_flag(ne, IS_CHECKPOINTED) || + if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || nat_get_blkaddr(ne) != NULL_ADDR)) allocated = true; - up_read(&nm_i->nat_tree_lock); if (allocated) return 0; } @@ -1557,6 +1555,8 @@ static void build_free_nids(struct f2fs_sb_info *sbi) ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, META_NAT, true); + down_read(&nm_i->nat_tree_lock); + while (1) { struct page *page = get_current_nat_page(sbi, nid); @@ -1585,6 +1585,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) remove_free_nid(nm_i, nid); } mutex_unlock(&curseg->curseg_mutex); + up_read(&nm_i->nat_tree_lock); ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid), nm_i->ra_nid_pages, META_NAT, false); @@ -1867,14 +1868,12 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) raw_ne = nat_in_journal(sum, i); - down_write(&nm_i->nat_tree_lock); ne = __lookup_nat_cache(nm_i, nid); if (!ne) { ne = grab_nat_entry(nm_i, nid); node_info_from_raw_nat(&ne->ni, &raw_ne); } __set_nat_cache_dirty(nm_i, ne); - up_write(&nm_i->nat_tree_lock); } update_nats_in_cursum(sum, -i); mutex_unlock(&curseg->curseg_mutex); @@ -1908,7 +1907,6 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, struct f2fs_nat_block *nat_blk; struct nat_entry *ne, *cur; struct page *page = NULL; - struct f2fs_nm_info *nm_i = NM_I(sbi); /* * there are two steps to flush nat entries: @@ -1945,12 +1943,8 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, raw_ne = &nat_blk->entries[nid - start_nid]; } raw_nat_from_node_info(raw_ne, &ne->ni); - - down_write(&NM_I(sbi)->nat_tree_lock); nat_reset_flag(ne); __clear_nat_cache_dirty(NM_I(sbi), ne); - up_write(&NM_I(sbi)->nat_tree_lock); - if (nat_get_blkaddr(ne) == NULL_ADDR) add_free_nid(sbi, nid, false); } @@ -1962,9 +1956,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, set->entry_cnt); - down_write(&nm_i->nat_tree_lock); radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set); - up_write(&nm_i->nat_tree_lock); kmem_cache_free(nat_entry_set_slab, set); } @@ -1984,6 +1976,9 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) if (!nm_i->dirty_nat_cnt) return; + + down_write(&nm_i->nat_tree_lock); + /* * if there are no enough space in journal to store dirty nat * entries, remove all entries from journal and merge them @@ -1992,7 +1987,6 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL)) remove_nats_in_journal(sbi); - down_write(&nm_i->nat_tree_lock); while ((found = __gang_lookup_nat_set(nm_i, set_idx, SETVEC_SIZE, setvec))) { unsigned idx; @@ -2001,12 +1995,13 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) __adjust_nat_entry_set(setvec[idx], &sets, MAX_NAT_JENTRIES(sum)); } - up_write(&nm_i->nat_tree_lock); /* flush dirty nats in nat entry set */ list_for_each_entry_safe(set, tmp, &sets, set_list) __flush_nat_entry_set(sbi, set); + up_write(&nm_i->nat_tree_lock); + f2fs_bug_on(sbi, nm_i->dirty_nat_cnt); } -- GitLab From 868c58ba4d310aee20ecd7dcabe960178a84420d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 2 Jan 2016 09:23:27 -0800 Subject: [PATCH 0073/5498] Revert "f2fs: check the node block address of newly allocated nid" Original issue is fixed by: f2fs: cover more area with nat_tree_lock This reverts commit 24928634f81b1592e83b37dcd89ed45c28f12feb. --- fs/f2fs/node.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5f0633ba578e..7cc6cea3b242 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1608,8 +1608,6 @@ retry: /* We should not use stale free nids created by build_free_nids */ if (nm_i->fcnt && !on_build_free_nids(nm_i)) { - struct node_info ni; - f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list)); list_for_each_entry(i, &nm_i->free_nid_list, list) if (i->state == NID_NEW) @@ -1620,13 +1618,6 @@ retry: i->state = NID_ALLOC; nm_i->fcnt--; spin_unlock(&nm_i->free_nid_list_lock); - - /* check nid is allocated already */ - get_node_info(sbi, *nid, &ni); - if (ni.blk_addr != NULL_ADDR) { - alloc_nid_done(sbi, *nid); - goto retry; - } return true; } spin_unlock(&nm_i->free_nid_list_lock); -- GitLab From e9f1b3dfeac9a32c701a28044deefada0c754e75 Mon Sep 17 00:00:00 2001 From: Fan Li Date: Mon, 4 Jan 2016 15:56:50 +0800 Subject: [PATCH 0074/5498] f2fs: read isize while holding i_mutex in fiemap make sure the isize we read doesn't change during the process. Signed-off-by: Fan li Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a6a7c8264ca7..b560348faa95 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -785,7 +785,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, { struct buffer_head map_bh; sector_t start_blk, last_blk; - loff_t isize = i_size_read(inode); + loff_t isize; u64 logical = 0, phys = 0, size = 0; u32 flags = 0; int ret = 0; @@ -801,6 +801,8 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, } mutex_lock(&inode->i_mutex); + + isize = i_size_read(inode); if (start >= isize) goto out; -- GitLab From 7dbf2fc9662749817786bec62a4b5364f511e878 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 8 Jan 2016 20:13:37 +0800 Subject: [PATCH 0075/5498] f2fs: check node id earily when readaheading node page Add node id check in ra_node_page and get_node_page_ra like get_node_page. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7cc6cea3b242..595aacc30656 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1041,6 +1041,10 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) struct page *apage; int err; + if (!nid) + return; + f2fs_bug_on(sbi, check_nid_range(sbi, nid)); + apage = find_get_page(NODE_MAPPING(sbi), nid); if (apage && PageUptodate(apage)) { f2fs_put_page(apage, 0); @@ -1109,6 +1113,7 @@ struct page *get_node_page_ra(struct page *parent, int start) nid = get_nid(parent, start, false); if (!nid) return ERR_PTR(-ENOENT); + f2fs_bug_on(sbi, check_nid_range(sbi, nid)); repeat: page = grab_cache_page(NODE_MAPPING(sbi), nid); if (!page) @@ -1128,9 +1133,9 @@ repeat: end = start + MAX_RA_NODE; end = min(end, NIDS_PER_BLOCK); for (i = start + 1; i < end; i++) { - nid_t tnid = get_nid(parent, i, false); - if (!tnid) - continue; + nid_t tnid; + + tnid = get_nid(parent, i, false); ra_node_page(sbi, tnid); } -- GitLab From 9e8a46551c49c3ced2d9418d4266cc27dc1b5c62 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 5 Jan 2016 16:52:46 +0800 Subject: [PATCH 0076/5498] f2fs: introduce __get_node_page to reuse common code There are duplicated code in between get_node_page and get_node_page_ra, introduce __get_node_page to includes common parts of these two, and export get_node_page and get_node_page_ra by reusing __get_node_page. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/node.c --- fs/f2fs/node.c | 90 ++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 595aacc30656..1a21de03edf4 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1060,57 +1060,34 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) f2fs_put_page(apage, err ? 1 : 0); } -struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) +/* + * readahead MAX_RA_NODE number of node pages. + */ +void ra_node_pages(struct page *parent, int start) { - struct page *page; - int err; - - if (!nid) - return ERR_PTR(-ENOENT); - f2fs_bug_on(sbi, check_nid_range(sbi, nid)); -repeat: - page = grab_cache_page(NODE_MAPPING(sbi), nid); - if (!page) - return ERR_PTR(-ENOMEM); - - err = read_node_page(page, READ_SYNC); - if (err < 0) { - f2fs_put_page(page, 1); - return ERR_PTR(err); - } else if (err == LOCKED_PAGE) { - goto page_hit; - } + struct f2fs_sb_info *sbi = F2FS_P_SB(parent); + struct blk_plug plug; + int i, end; + nid_t nid; - lock_page(page); + blk_start_plug(&plug); - if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 1); - return ERR_PTR(-EIO); - } - if (unlikely(page->mapping != NODE_MAPPING(sbi))) { - f2fs_put_page(page, 1); - goto repeat; + /* Then, try readahead for siblings of the desired node */ + end = start + MAX_RA_NODE; + end = min(end, NIDS_PER_BLOCK); + for (i = start; i < end; i++) { + nid = get_nid(parent, i, false); + ra_node_page(sbi, nid); } -page_hit: - mark_page_accessed(page); - f2fs_bug_on(sbi, nid != nid_of_node(page)); - return page; + blk_finish_plug(&plug); } -/* - * Return a locked page for the desired node page. - * And, readahead MAX_RA_NODE number of node pages. - */ -struct page *get_node_page_ra(struct page *parent, int start) +struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid, + struct page *parent, int start) { - struct f2fs_sb_info *sbi = F2FS_P_SB(parent); - struct blk_plug plug; struct page *page; - int err, i, end; - nid_t nid; + int err; - /* First, try getting the desired direct node. */ - nid = get_nid(parent, start, false); if (!nid) return ERR_PTR(-ENOENT); f2fs_bug_on(sbi, check_nid_range(sbi, nid)); @@ -1127,21 +1104,11 @@ repeat: goto page_hit; } - blk_start_plug(&plug); - - /* Then, try readahead for siblings of the desired node */ - end = start + MAX_RA_NODE; - end = min(end, NIDS_PER_BLOCK); - for (i = start + 1; i < end; i++) { - nid_t tnid; - - tnid = get_nid(parent, i, false); - ra_node_page(sbi, tnid); - } - - blk_finish_plug(&plug); + if (parent) + ra_node_pages(parent, start + 1); lock_page(page); + if (unlikely(!PageUptodate(page))) { f2fs_put_page(page, 1); return ERR_PTR(-EIO); @@ -1160,6 +1127,19 @@ page_hit: return page; } +struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) +{ + return __get_node_page(sbi, nid, NULL, 0); +} + +struct page *get_node_page_ra(struct page *parent, int start) +{ + struct f2fs_sb_info *sbi = F2FS_P_SB(parent); + nid_t nid = get_nid(parent, start, false); + + return __get_node_page(sbi, nid, parent, start); +} + void sync_inode_page(struct dnode_of_data *dn) { if (IS_INODE(dn->node_page) || dn->inode_page == dn->node_page) { -- GitLab From 5ef98ea13e63b998f5cdcbe931f748081f5fbf87 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 1 Jan 2016 22:03:47 -0800 Subject: [PATCH 0077/5498] f2fs: check the page status filled from disk After reading a page, we need to check whether there is any error. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b560348faa95..8b36c8d92c9c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -414,7 +414,7 @@ struct page *get_new_data_page(struct inode *inode, struct page *page; struct dnode_of_data dn; int err; -repeat: + page = f2fs_grab_cache_page(mapping, index, true); if (!page) { /* @@ -443,12 +443,11 @@ repeat: } else { f2fs_put_page(page, 1); - page = get_read_data_page(inode, index, READ_SYNC, true); + /* if ipage exists, blkaddr should be NEW_ADDR */ + f2fs_bug_on(F2FS_I_SB(inode), ipage); + page = get_lock_data_page(inode, index, true); if (IS_ERR(page)) - goto repeat; - - /* wait for read completion */ - lock_page(page); + return page; } got_it: if (new_i_size && i_size_read(inode) < -- GitLab From 8b83eb96e4c39ddb5196a89f0da173e49add6d6e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 7 Jan 2016 13:23:12 -0800 Subject: [PATCH 0078/5498] f2fs: avoid unnecessary f2fs_balance_fs calls Only when node page is newly dirtied, it needs to check whether we need to do f2fs_gc. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 4 ++-- fs/f2fs/inode.c | 19 ++++++++++--------- fs/f2fs/node.c | 26 ++++++++++++++------------ fs/f2fs/node.h | 4 ++-- fs/f2fs/super.c | 2 -- 6 files changed, 30 insertions(+), 29 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8b36c8d92c9c..f0dc1a193f6d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -226,8 +226,8 @@ void set_data_blkaddr(struct dnode_of_data *dn) /* Get physical address of data block */ addr_array = blkaddr_in_node(rn); addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr); - set_page_dirty(node_page); - dn->node_changed = true; + if (set_page_dirty(node_page)) + dn->node_changed = true; } int reserve_new_block(struct dnode_of_data *dn) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6871b3c55530..efc31fd93463 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1673,8 +1673,8 @@ long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); void f2fs_set_inode_flags(struct inode *); struct inode *f2fs_iget(struct super_block *, unsigned long); int try_to_free_nats(struct f2fs_sb_info *, int); -void update_inode(struct inode *, struct page *); -void update_inode_page(struct inode *); +int update_inode(struct inode *, struct page *); +int update_inode_page(struct inode *); int f2fs_write_inode(struct inode *, struct writeback_control *); void f2fs_evict_inode(struct inode *); void handle_failed_inode(struct inode *); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c43b0fac6284..6a44b7202a6d 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -222,7 +222,7 @@ bad_inode: return ERR_PTR(ret); } -void update_inode(struct inode *inode, struct page *node_page) +int update_inode(struct inode *inode, struct page *node_page) { struct f2fs_inode *ri; @@ -260,15 +260,16 @@ void update_inode(struct inode *inode, struct page *node_page) __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); - set_page_dirty(node_page); - clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE); + + return set_page_dirty(node_page); } -void update_inode_page(struct inode *inode) +int update_inode_page(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *node_page; + int ret = 0; retry: node_page = get_node_page(sbi, inode->i_ino); if (IS_ERR(node_page)) { @@ -279,10 +280,11 @@ retry: } else if (err != -ENOENT) { f2fs_stop_checkpoint(sbi); } - return; + return 0; } - update_inode(inode, node_page); + ret = update_inode(inode, node_page); f2fs_put_page(node_page, 1); + return ret; } int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) @@ -300,9 +302,8 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) * We need to balance fs here to prevent from producing dirty node pages * during the urgent cleaning time when runing out of free sections. */ - update_inode_page(inode); - - f2fs_balance_fs(sbi); + if (update_inode_page(inode)) + f2fs_balance_fs(sbi); return 0; } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 1a21de03edf4..272d59e2c40b 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -543,7 +543,6 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) set_nid(parent, offset[i - 1], nids[i], i == 1); alloc_nid_done(sbi, nids[i]); - dn->node_changed = true; done = true; } else if (mode == LOOKUP_NODE_RA && i == level && level > 1) { npage[i] = get_node_page_ra(parent, offset[i - 1]); @@ -679,8 +678,8 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, ret = truncate_dnode(&rdn); if (ret < 0) goto out_err; - set_nid(page, i, 0, false); - dn->node_changed = true; + if (set_nid(page, i, 0, false)) + dn->node_changed = true; } } else { child_nofs = nofs + ofs * (NIDS_PER_BLOCK + 1) + 1; @@ -693,8 +692,8 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, rdn.nid = child_nid; ret = truncate_nodes(&rdn, child_nofs, 0, depth - 1); if (ret == (NIDS_PER_BLOCK + 1)) { - set_nid(page, i, 0, false); - dn->node_changed = true; + if (set_nid(page, i, 0, false)) + dn->node_changed = true; child_nofs += ret; } else if (ret < 0 && ret != -ENOENT) { goto out_err; @@ -755,8 +754,8 @@ static int truncate_partial_nodes(struct dnode_of_data *dn, err = truncate_dnode(dn); if (err < 0) goto fail; - set_nid(pages[idx], i, 0, false); - dn->node_changed = true; + if (set_nid(pages[idx], i, 0, false)) + dn->node_changed = true; } if (offset[idx + 1] == 0) { @@ -981,7 +980,8 @@ struct page *new_node_page(struct dnode_of_data *dn, fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true); set_cold_node(dn->inode, page); SetPageUptodate(page); - set_page_dirty(page); + if (set_page_dirty(page)) + dn->node_changed = true; if (f2fs_has_xattr_block(ofs)) F2FS_I(dn->inode)->i_xattr_nid = dn->nid; @@ -1142,18 +1142,20 @@ struct page *get_node_page_ra(struct page *parent, int start) void sync_inode_page(struct dnode_of_data *dn) { + int ret = 0; + if (IS_INODE(dn->node_page) || dn->inode_page == dn->node_page) { - update_inode(dn->inode, dn->node_page); + ret = update_inode(dn->inode, dn->node_page); } else if (dn->inode_page) { if (!dn->inode_page_locked) lock_page(dn->inode_page); - update_inode(dn->inode, dn->inode_page); + ret = update_inode(dn->inode, dn->inode_page); if (!dn->inode_page_locked) unlock_page(dn->inode_page); } else { - update_inode_page(dn->inode); + ret = update_inode_page(dn->inode); } - dn->node_changed = true; + dn->node_changed = ret ? true: false; } int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 2de759a7746f..d4d1f636fe1c 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -317,7 +317,7 @@ static inline bool IS_DNODE(struct page *node_page) return true; } -static inline void set_nid(struct page *p, int off, nid_t nid, bool i) +static inline int set_nid(struct page *p, int off, nid_t nid, bool i) { struct f2fs_node *rn = F2FS_NODE(p); @@ -327,7 +327,7 @@ static inline void set_nid(struct page *p, int off, nid_t nid, bool i) rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid); else rn->in.nid[off] = cpu_to_le32(nid); - set_page_dirty(p); + return set_page_dirty(p); } static inline nid_t get_nid(struct page *p, int off, bool i) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0bbd756821a7..f5cc790646e2 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -591,8 +591,6 @@ int f2fs_sync_fs(struct super_block *sb, int sync) mutex_lock(&sbi->gc_mutex); err = write_checkpoint(sbi, &cpc); mutex_unlock(&sbi->gc_mutex); - } else { - f2fs_balance_fs(sbi); } f2fs_trace_ios(NULL, 1); -- GitLab From 64fc1c1bfc25e2964e2123ac0b0beab940631566 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 7 Jan 2016 13:52:34 -0800 Subject: [PATCH 0079/5498] f2fs: remove redundant calls This patch removes redundant calls. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 63992e185798..1a167b14d293 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -268,8 +268,6 @@ sync_nodes: if (need_inode_block_update(sbi, ino)) { mark_inode_dirty_sync(inode); f2fs_write_inode(inode, NULL); - - f2fs_balance_fs(sbi); goto sync_nodes; } @@ -485,7 +483,6 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) F2FS_I(dn->inode)) + ofs; f2fs_update_extent_cache_range(dn, fofs, 0, len); dec_valid_block_count(sbi, dn->inode, nr_free); - set_page_dirty(dn->node_page); sync_inode_page(dn); } dn->ofs_in_node = ofs; -- GitLab From 0d48f8700fcbd28e5bcadf96e10d8818c7cba454 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 7 Jan 2016 14:15:04 -0800 Subject: [PATCH 0080/5498] f2fs: clean up f2fs_balance_fs This patch adds one parameter to clean up all the callers of f2fs_balance_fs. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 17 ++++++----------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 17 ++++++++--------- fs/f2fs/inline.c | 3 +-- fs/f2fs/inode.c | 2 +- fs/f2fs/namei.c | 22 +++++++++++----------- fs/f2fs/segment.c | 6 ++++-- fs/f2fs/xattr.c | 2 +- 8 files changed, 33 insertions(+), 38 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f0dc1a193f6d..149ba02757ca 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -547,8 +547,7 @@ static int __allocate_data_blocks(struct inode *inode, loff_t offset, f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); - if (dn.node_changed) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, dn.node_changed); } return err; @@ -558,8 +557,7 @@ sync_out: f2fs_put_dnode(&dn); out: f2fs_unlock_op(sbi); - if (dn.node_changed) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, dn.node_changed); return err; } @@ -658,8 +656,7 @@ get_next: if (create) { f2fs_unlock_op(sbi); - if (dn.node_changed) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, dn.node_changed); f2fs_lock_op(sbi); } @@ -719,8 +716,7 @@ put_out: unlock_out: if (create) { f2fs_unlock_op(sbi); - if (dn.node_changed) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, dn.node_changed); } out: trace_f2fs_map_blocks(inode, map, err); @@ -1179,8 +1175,7 @@ out: if (err) ClearPageUptodate(page); unlock_page(page); - if (need_balance_fs) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, need_balance_fs); if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) { f2fs_submit_merged_bio(sbi, DATA, WRITE); remove_dirty_inode(inode); @@ -1507,7 +1502,7 @@ repeat: if (need_balance && has_not_enough_free_secs(sbi, 0)) { unlock_page(page); - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); lock_page(page); if (page->mapping != mapping) { /* The page got truncated from under us */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index efc31fd93463..4c779e5a53f6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1778,7 +1778,7 @@ void destroy_node_manager_caches(void); */ void register_inmem_page(struct inode *, struct page *); int commit_inmem_pages(struct inode *, bool); -void f2fs_balance_fs(struct f2fs_sb_info *); +void f2fs_balance_fs(struct f2fs_sb_info *, bool); void f2fs_balance_fs_bg(struct f2fs_sb_info *); int f2fs_issue_flush(struct f2fs_sb_info *); int create_flush_cmd_control(struct f2fs_sb_info *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 1a167b14d293..966bce59ea5d 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -56,8 +56,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); - if (dn.node_changed) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, dn.node_changed); file_update_time(vma->vm_file); lock_page(page); @@ -678,7 +677,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) err = f2fs_truncate(inode, true); if (err) return err; - f2fs_balance_fs(F2FS_I_SB(inode)); + f2fs_balance_fs(F2FS_I_SB(inode), true); } else { /* * do not trim all blocks after i_size if target size is @@ -733,7 +732,7 @@ static int fill_zero(struct inode *inode, pgoff_t index, if (!len) return 0; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); page = get_new_data_page(inode, NULL, index, false); @@ -819,7 +818,7 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) loff_t blk_start, blk_end; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); blk_start = (loff_t)pg_start << PAGE_CACHE_SHIFT; blk_end = (loff_t)pg_end << PAGE_CACHE_SHIFT; @@ -922,7 +921,7 @@ static int f2fs_do_collapse(struct inode *inode, pgoff_t start, pgoff_t end) int ret = 0; for (; end < nrpages; start++, end++) { - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); ret = __exchange_data_block(inode, end, start, true); f2fs_unlock_op(sbi); @@ -1104,7 +1103,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (ret) return ret; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); ret = truncate_blocks(inode, i_size_read(inode), true); if (ret) @@ -1156,7 +1155,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset, if (ret) return ret; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; @@ -1656,7 +1655,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, pg_start = range->start >> PAGE_CACHE_SHIFT; pg_end = (range->start + range->len) >> PAGE_CACHE_SHIFT; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); mutex_lock(&inode->i_mutex); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 5ffbd169b719..c3f0b7d4cfca 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -200,8 +200,7 @@ out: f2fs_put_page(page, 1); - if (dn.node_changed) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, dn.node_changed); return err; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 6a44b7202a6d..55773459f58f 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -303,7 +303,7 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) * during the urgent cleaning time when runing out of free sections. */ if (update_inode_page(inode)) - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); return 0; } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index d1d38d5c7441..7c395d515dc9 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -141,7 +141,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, inode->i_mapping->a_ops = &f2fs_dblock_aops; ino = inode->i_ino; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); @@ -173,7 +173,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, !f2fs_is_child_context_consistent_with_parent(dir, inode)) return -EPERM; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); inode->i_ctime = CURRENT_TIME; ihold(inode); @@ -222,7 +222,7 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) return 0; } - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); @@ -303,7 +303,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) if (!de) goto fail; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); err = acquire_orphan_inode(sbi); @@ -373,7 +373,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, inode->i_op = &f2fs_symlink_inode_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); @@ -464,7 +464,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_mapping->a_ops = &f2fs_dblock_aops; mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); set_inode_flag(F2FS_I(inode), FI_INC_LINK); f2fs_lock_op(sbi); @@ -513,7 +513,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, init_special_inode(inode, inode->i_mode, rdev); inode->i_op = &f2fs_special_inode_operations; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); @@ -554,7 +554,7 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, inode->i_mapping->a_ops = &f2fs_dblock_aops; } - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); err = acquire_orphan_inode(sbi); @@ -657,7 +657,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!new_entry) goto out_whiteout; - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); @@ -690,7 +690,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, update_inode_page(old_inode); update_inode_page(new_inode); } else { - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); @@ -831,7 +831,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_new_dir; } - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a3474bad5770..c7bbc915d962 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -213,7 +213,7 @@ int commit_inmem_pages(struct inode *inode, bool abort) * inode becomes free by iget_locked in f2fs_iget. */ if (!abort) { - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); } @@ -262,8 +262,10 @@ int commit_inmem_pages(struct inode *inode, bool abort) * This function balances dirty node and dentry pages. * In addition, it controls garbage collection. */ -void f2fs_balance_fs(struct f2fs_sb_info *sbi) +void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) { + if (!need) + return; /* * We should do GC or end up with checkpoint, if there are so many dirty * dir/node pages without enough free segments. diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 9d44a39b2e29..2ba1f63e837c 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -611,7 +611,7 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, if (ipage) return __f2fs_setxattr(inode, index, name, value, size, ipage, flags); - f2fs_balance_fs(sbi); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); /* protect xattr_ver */ -- GitLab From c5dc5d3b2d6f0a559add3f0b20ae2e59f6d317a9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 8 Jan 2016 20:19:27 +0800 Subject: [PATCH 0081/5498] f2fs: recognize encrypted data in f2fs_fiemap This patch fixes to teach f2fs_fiemap to recognize encrypted data. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 149ba02757ca..975c9b03565a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -831,9 +831,13 @@ next: flags |= FIEMAP_EXTENT_LAST; } - if (size) + if (size) { + if (f2fs_encrypted_inode(inode)) + flags |= FIEMAP_EXTENT_DATA_ENCRYPTED; + ret = fiemap_fill_next_extent(fieinfo, logical, phys, size, flags); + } if (start_blk > last_blk || ret) goto out; -- GitLab From 7205e4711e7098c4ee679d8780c3327327c2f7e0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 8 Jan 2016 20:22:52 +0800 Subject: [PATCH 0082/5498] f2fs: use atomic type for node count in extent tree 1. rename field in struct extent_tree from count to node_cnt for readability. 2. alter to use atomic type for node_cnt. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 17 +++++++++-------- fs/f2fs/f2fs.h | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 4dee2be9a648..9febbc622bf5 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -36,7 +36,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi, rb_link_node(&en->rb_node, parent, p); rb_insert_color(&en->rb_node, &et->root); - et->count++; + atomic_inc(&et->node_cnt); atomic_inc(&sbi->total_ext_node); return en; } @@ -45,7 +45,7 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi, struct extent_tree *et, struct extent_node *en) { rb_erase(&en->rb_node, &et->root); - et->count--; + atomic_dec(&et->node_cnt); atomic_dec(&sbi->total_ext_node); if (et->cached_en == en) @@ -69,7 +69,7 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) et->cached_en = NULL; rwlock_init(&et->lock); INIT_LIST_HEAD(&et->list); - et->count = 0; + atomic_set(&et->node_cnt, 0); atomic_inc(&sbi->total_ext_tree); } else { atomic_dec(&sbi->total_zombie_tree); @@ -133,7 +133,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, { struct rb_node *node, *next; struct extent_node *en; - unsigned int count = et->count; + unsigned int count = atomic_read(&et->node_cnt); node = rb_first(&et->root); while (node) { @@ -154,7 +154,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, node = next; } - return count - et->count; + return count - atomic_read(&et->node_cnt); } static void __drop_largest_extent(struct inode *inode, @@ -192,7 +192,7 @@ bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); write_lock(&et->lock); - if (et->count) + if (atomic_read(&et->node_cnt)) goto out; en = __init_extent_tree(sbi, et, &ei); @@ -660,7 +660,8 @@ void f2fs_destroy_extent_tree(struct inode *inode) if (!et) return; - if (inode->i_nlink && !is_bad_inode(inode) && et->count) { + if (inode->i_nlink && !is_bad_inode(inode) && + atomic_read(&et->node_cnt)) { down_write(&sbi->extent_tree_lock); list_add_tail(&et->list, &sbi->zombie_list); atomic_inc(&sbi->total_zombie_tree); @@ -673,7 +674,7 @@ void f2fs_destroy_extent_tree(struct inode *inode) /* delete extent tree entry in radix tree */ down_write(&sbi->extent_tree_lock); - f2fs_bug_on(sbi, et->count); + f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); kmem_cache_free(extent_tree_slab, et); atomic_dec(&sbi->total_ext_tree); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4c779e5a53f6..067d0decf11c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -360,7 +360,7 @@ struct extent_tree { struct extent_info largest; /* largested extent info */ struct list_head list; /* to be used by sbi->zombie_list */ rwlock_t lock; /* protect extent info rb-tree */ - unsigned int count; /* # of extent node in rb-tree*/ + atomic_t node_cnt; /* # of extent node in rb-tree*/ }; /* -- GitLab From a2687048d229b5cc9bc624be0f8b887256b7223e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 8 Jan 2016 20:24:00 +0800 Subject: [PATCH 0083/5498] f2fs: skip releasing nodes in chindless extent tree If there are no nodes in extent tree, let's skip releasing step to avoid any overhead of grabbing/releasing extent tree lock. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 9febbc622bf5..ccd5c636d3fe 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -570,9 +570,11 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) /* 1. remove unreferenced extent tree */ list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { - write_lock(&et->lock); - node_cnt += __free_extent_tree(sbi, et, true); - write_unlock(&et->lock); + if (atomic_read(&et->node_cnt)) { + write_lock(&et->lock); + node_cnt += __free_extent_tree(sbi, et, true); + write_unlock(&et->lock); + } list_del_init(&et->list); radix_tree_delete(&sbi->extent_tree_root, et->ino); @@ -618,6 +620,9 @@ free_node: for (i = 0; i < found; i++) { struct extent_tree *et = treevec[i]; + if (!atomic_read(&et->node_cnt)) + continue; + if (write_trylock(&et->lock)) { node_cnt += __free_extent_tree(sbi, et, false); write_unlock(&et->lock); @@ -641,7 +646,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode) struct extent_tree *et = F2FS_I(inode)->extent_tree; unsigned int node_cnt = 0; - if (!et) + if (!et || !atomic_read(&et->node_cnt)) return 0; write_lock(&et->lock); -- GitLab From 8f35d7dd9f7853aafa5d080e7fb677ea220c302a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 8 Jan 2016 15:51:50 -0800 Subject: [PATCH 0084/5498] f2fs: introduce time and interval facility This patch adds time and interval arrays to store some timing variables. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/f2fs.h | 21 ++++++++++++++++++++- fs/f2fs/segment.c | 2 +- fs/f2fs/super.c | 7 +++---- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index f3186684e5a6..36b0d044919e 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1140,7 +1140,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) "checkpoint: version = %llx", ckpt_ver); /* do checkpoint periodically */ - sbi->cp_expires = round_jiffies_up(jiffies + HZ * sbi->cp_interval); + f2fs_update_time(sbi, CP_TIME); trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint"); out: mutex_unlock(&sbi->cp_mutex); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 067d0decf11c..984745c6fc79 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -720,6 +720,11 @@ enum { SBI_POR_DOING, /* recovery is doing or not */ }; +enum { + CP_TIME, + MAX_TIME, +}; + struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -746,7 +751,8 @@ struct f2fs_sb_info { struct rw_semaphore node_write; /* locking node writes */ struct mutex writepages; /* mutex for writepages() */ wait_queue_head_t cp_wait; - long cp_expires, cp_interval; /* next expected periodic cp */ + unsigned long last_time[MAX_TIME]; /* to store time in jiffies */ + long interval_time[MAX_TIME]; /* to store thresholds */ struct inode_management im[MAX_INO_ENTRY]; /* manage inode cache */ @@ -836,6 +842,19 @@ struct f2fs_sb_info { unsigned int shrinker_run_no; }; +static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type) +{ + sbi->last_time[type] = jiffies; +} + +static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type) +{ + struct timespec ts = {sbi->interval_time[type], 0}; + unsigned long interval = timespec_to_jiffies(&ts); + + return time_after(jiffies, sbi->last_time[type] + interval); +} + /* * Inline functions */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c7bbc915d962..fed23d5a7b34 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -293,7 +293,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) if (!available_free_memory(sbi, NAT_ENTRIES) || excess_prefree_segs(sbi) || !available_free_memory(sbi, INO_ENTRIES) || - jiffies > sbi->cp_expires) { + f2fs_time_over(sbi, CP_TIME)) { if (test_opt(sbi, DATA_FLUSH)) sync_dirty_inodes(sbi, FILE_INODE); f2fs_sync_fs(sbi->sb, true); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f5cc790646e2..787047f59c00 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -218,7 +218,7 @@ F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); -F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, cp_interval); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -1122,7 +1122,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi) atomic_set(&sbi->nr_pages[i], 0); sbi->dir_level = DEF_DIR_LEVEL; - sbi->cp_interval = DEF_CP_INTERVAL; + sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL; clear_sbi_flag(sbi, SBI_NEED_FSCK); INIT_LIST_HEAD(&sbi->s_list); @@ -1467,8 +1467,7 @@ try_onemore: f2fs_commit_super(sbi, true); } - sbi->cp_expires = round_jiffies_up(jiffies); - + f2fs_update_time(sbi, CP_TIME); return 0; free_kobj: -- GitLab From 75ad848e297dd31afe90fbb8a87998f20268dfc3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 8 Jan 2016 16:57:48 -0800 Subject: [PATCH 0085/5498] f2fs: detect idle time depending on user behavior This patch adds last time that user requested filesystem operations. This information is used to detect whether system is idle or not later. Signed-off-by: Jaegeuk Kim Conflicts: Documentation/ABI/testing/sysfs-fs-f2fs --- Documentation/ABI/testing/sysfs-fs-f2fs | 24 ++++++++++++++++++++++++ fs/f2fs/data.c | 1 + fs/f2fs/dir.c | 4 ++++ fs/f2fs/f2fs.h | 15 +++++++++++++++ fs/f2fs/file.c | 12 ++++++++++++ fs/f2fs/gc.c | 1 - fs/f2fs/gc.h | 8 -------- fs/f2fs/segment.c | 2 +- fs/f2fs/super.c | 4 ++++ fs/f2fs/xattr.c | 1 + 10 files changed, 62 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 6f9157f16725..e5200f354abf 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -74,3 +74,27 @@ Date: March 2014 Contact: "Jaegeuk Kim" Description: Controls the memory footprint used by f2fs. + +What: /sys/fs/f2fs//trim_sections +Date: February 2015 +Contact: "Jaegeuk Kim" +Description: + Controls the trimming rate in batch mode. + +What: /sys/fs/f2fs//cp_interval +Date: October 2015 +Contact: "Jaegeuk Kim" +Description: + Controls the checkpoint timing. + +What: /sys/fs/f2fs//idle_interval +Date: January 2016 +Contact: "Jaegeuk Kim" +Description: + Controls the idle timing. + +What: /sys/fs/f2fs//ra_nid_pages +Date: October 2015 +Contact: "Chao Yu" +Description: + Controls the count of nid pages to be readaheaded. diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 975c9b03565a..38f2b88dd08d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1597,6 +1597,7 @@ static int f2fs_write_end(struct file *file, } f2fs_put_page(page, 1); + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return copied; } diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 2a1c3aef7b8e..83da362d04f6 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -636,6 +636,7 @@ fail: f2fs_put_page(dentry_page, 1); out: f2fs_fname_free_filename(&fname); + f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); return err; } @@ -657,6 +658,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir) clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); fail: up_write(&F2FS_I(inode)->i_sem); + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return err; } @@ -701,6 +703,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); int i; + f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); + if (f2fs_has_inline_dentry(dir)) return f2fs_delete_inline_entry(dentry, page, dir, inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 984745c6fc79..64cc8f7eb14e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) @@ -126,6 +127,7 @@ enum { #define BATCHED_TRIM_BLOCKS(sbi) \ (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) #define DEF_CP_INTERVAL 60 /* 60 secs */ +#define DEF_IDLE_INTERVAL 120 /* 2 mins */ struct cp_control { int reason; @@ -722,6 +724,7 @@ enum { enum { CP_TIME, + REQ_TIME, MAX_TIME, }; @@ -855,6 +858,18 @@ static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type) return time_after(jiffies, sbi->last_time[type] + interval); } +static inline bool is_idle(struct f2fs_sb_info *sbi) +{ + struct block_device *bdev = sbi->sb->s_bdev; + struct request_queue *q = bdev_get_queue(bdev); + struct request_list *rl = &q->root_rl; + + if (rl->count[BLK_RW_SYNC] || rl->count[BLK_RW_ASYNC]) + return 0; + + return f2fs_time_over(sbi, REQ_TIME); +} + /* * Inline functions */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 966bce59ea5d..1488323fb8bf 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -97,6 +97,7 @@ mapped: clear_cold_data(page); out: sb_end_pagefault(inode->i_sb); + f2fs_update_time(sbi, REQ_TIME); return block_page_mkwrite_return(err); } @@ -281,6 +282,7 @@ flush_out: remove_ino_entry(sbi, ino, UPDATE_INO); clear_inode_flag(fi, FI_UPDATE_WRITE); ret = f2fs_issue_flush(sbi); + f2fs_update_time(sbi, REQ_TIME); out: trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); f2fs_trace_ios(NULL, 1); @@ -486,6 +488,7 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) } dn->ofs_in_node = ofs; + f2fs_update_time(sbi, REQ_TIME); trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid, dn->ofs_in_node, nr_free); return nr_free; @@ -1239,6 +1242,7 @@ static long f2fs_fallocate(struct file *file, int mode, if (!ret) { inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } out: @@ -1354,6 +1358,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) return ret; set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); + return 0; } @@ -1401,6 +1407,7 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) return ret; set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return 0; } @@ -1442,6 +1449,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) } mnt_drop_write_file(filp); + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return ret; } @@ -1481,6 +1489,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) default: return -EINVAL; } + f2fs_update_time(sbi, REQ_TIME); return 0; } @@ -1511,6 +1520,7 @@ static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range))) return -EFAULT; + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return 0; } @@ -1534,6 +1544,7 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) sizeof(policy))) return -EFAULT; + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return f2fs_process_policy(&policy, inode); #else return -EOPNOTSUPP; @@ -1810,6 +1821,7 @@ static int f2fs_ioc_defragment(struct file *filp, unsigned long arg) } err = f2fs_defragment_range(sbi, filp, &range); + f2fs_update_time(sbi, REQ_TIME); if (err < 0) goto out; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c09be339569c..f610c2a9bdde 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "f2fs.h" #include "node.h" diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index b4a65be9f7d3..a993967dcdb9 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -100,11 +100,3 @@ static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi) return true; return false; } - -static inline int is_idle(struct f2fs_sb_info *sbi) -{ - struct block_device *bdev = sbi->sb->s_bdev; - struct request_queue *q = bdev_get_queue(bdev); - struct request_list *rl = &q->root_rl; - return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]); -} diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fed23d5a7b34..d8ad1abfa4fd 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -293,7 +293,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) if (!available_free_memory(sbi, NAT_ENTRIES) || excess_prefree_segs(sbi) || !available_free_memory(sbi, INO_ENTRIES) || - f2fs_time_over(sbi, CP_TIME)) { + (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { if (test_opt(sbi, DATA_FLUSH)) sync_dirty_inodes(sbi, FILE_INODE); f2fs_sync_fs(sbi->sb, true); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 787047f59c00..3bf990b80026 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -219,6 +219,7 @@ F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -237,6 +238,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(ram_thresh), ATTR_LIST(ra_nid_pages), ATTR_LIST(cp_interval), + ATTR_LIST(idle_interval), NULL, }; @@ -1123,6 +1125,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi) sbi->dir_level = DEF_DIR_LEVEL; sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL; + sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL; clear_sbi_flag(sbi, SBI_NEED_FSCK); INIT_LIST_HEAD(&sbi->s_list); @@ -1468,6 +1471,7 @@ try_onemore: } f2fs_update_time(sbi, CP_TIME); + f2fs_update_time(sbi, REQ_TIME); return 0; free_kobj: diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 2ba1f63e837c..d59ac2ff2c7c 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -620,5 +620,6 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, up_write(&F2FS_I(inode)->i_sem); f2fs_unlock_op(sbi); + f2fs_update_time(sbi, REQ_TIME); return err; } -- GitLab From a0515c726d7403579e8547c87fdc7bba35d82d62 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 9 Jan 2016 13:45:17 -0800 Subject: [PATCH 0086/5498] f2fs: monitor the number of background checkpoint This patch adds to show the number of background checkpoint. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 3 ++- fs/f2fs/f2fs.h | 4 +++- fs/f2fs/segment.c | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index b73e8e133c8b..48f2ae9452ef 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -272,7 +272,8 @@ static int stat_show(struct seq_file *s, void *v) si->dirty_count); seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n", si->prefree_count, si->free_segs, si->free_secs); - seq_printf(s, "CP calls: %d\n", si->cp_count); + seq_printf(s, "CP calls: %d (BG: %d)\n", + si->cp_count, si->bg_cp_count); seq_printf(s, "GC calls: %d (BG: %d)\n", si->call_count, si->bg_gc); seq_printf(s, " - data segments : %d (%d)\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 64cc8f7eb14e..c43c4c5944cd 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1935,7 +1935,7 @@ struct f2fs_stat_info { int util_free, util_valid, util_invalid; int rsvd_segs, overp_segs; int dirty_count, node_pages, meta_pages; - int prefree_count, call_count, cp_count; + int prefree_count, call_count, cp_count, bg_cp_count; int tot_segs, node_segs, data_segs, free_segs, free_secs; int bg_node_segs, bg_data_segs; int tot_blks, data_blks, node_blks; @@ -1956,6 +1956,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) } #define stat_inc_cp_count(si) ((si)->cp_count++) +#define stat_inc_bg_cp_count(si) ((si)->bg_cp_count++) #define stat_inc_call_count(si) ((si)->call_count++) #define stat_inc_bggc_count(sbi) ((sbi)->bg_gc++) #define stat_inc_dirty_inode(sbi, type) ((sbi)->ndirty_inode[type]++) @@ -2038,6 +2039,7 @@ int __init f2fs_create_root_stats(void); void f2fs_destroy_root_stats(void); #else #define stat_inc_cp_count(si) +#define stat_inc_bg_cp_count(si) #define stat_inc_call_count(si) #define stat_inc_bggc_count(si) #define stat_inc_dirty_inode(sbi, type) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d8ad1abfa4fd..5904a411c86f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -297,6 +297,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) if (test_opt(sbi, DATA_FLUSH)) sync_dirty_inodes(sbi, FILE_INODE); f2fs_sync_fs(sbi->sb, true); + stat_inc_bg_cp_count(sbi->stat_info); } } -- GitLab From 64a0f5ca374b318cfe14ce8ee8c3677d46f002db Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 9 Jan 2016 16:14:08 -0800 Subject: [PATCH 0087/5498] f2fs: fix wrong memory condition check This patch fixes wrong decision for avaliable_free_memory. The return valus is already set as false, so we should consider true condition below only. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/node.c --- fs/f2fs/node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 272d59e2c40b..9ed23e5dc360 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -71,8 +71,8 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); } else { - if (sbi->sb->s_bdi->dirty_exceeded) - return false; + if (!sbi->sb->s_bdi->dirty_exceeded) + return true; } return res; } -- GitLab From b2150e908462f9a7a9465d8717dc3422ee7046b5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 9 Jan 2016 17:08:38 -0800 Subject: [PATCH 0088/5498] f2fs: should unset atomic flag after successful commit If there is an error during commit, we should keep the flag in order to abort it. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 1488323fb8bf..8e68c611437b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1381,8 +1381,10 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) if (f2fs_is_atomic_file(inode)) { clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); ret = commit_inmem_pages(inode, false); - if (ret) + if (ret) { + set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); goto err_out; + } } ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0); -- GitLab From 6342042eecb2b363ac355afc8bf8f480d26c1c6e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 18 Jan 2016 18:24:59 +0800 Subject: [PATCH 0089/5498] f2fs: relocate is_merged_page Operations in is_merged_page is related to inner bio cache, move it to data.c. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 38 ++++++++++++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 38 -------------------------------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 38f2b88dd08d..4260fbaa03cb 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -117,6 +117,44 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) io->bio = NULL; } +bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page, + enum page_type type) +{ + enum page_type btype = PAGE_TYPE_OF_BIO(type); + struct f2fs_bio_info *io = &sbi->write_io[btype]; + struct bio_vec *bvec; + struct page *target; + int i; + + down_read(&io->io_rwsem); + if (!io->bio) { + up_read(&io->io_rwsem); + return false; + } + + bio_for_each_segment_all(bvec, io->bio, i) { + + if (bvec->bv_page->mapping) { + target = bvec->bv_page; + } else { + struct f2fs_crypto_ctx *ctx; + + /* encrypted page */ + ctx = (struct f2fs_crypto_ctx *)page_private( + bvec->bv_page); + target = ctx->w.control_page; + } + + if (page == target) { + up_read(&io->io_rwsem); + return true; + } + } + + up_read(&io->io_rwsem); + return false; +} + void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type, int rw) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c43c4c5944cd..1bca5a57fbee 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1880,6 +1880,7 @@ void destroy_checkpoint_caches(void); /* * data.c */ +bool is_merged_page(struct f2fs_sb_info *, struct page *, enum page_type); void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); int f2fs_submit_page_bio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5904a411c86f..e16235bbdd7d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1415,44 +1415,6 @@ void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, f2fs_update_extent_cache(dn); } -static inline bool is_merged_page(struct f2fs_sb_info *sbi, - struct page *page, enum page_type type) -{ - enum page_type btype = PAGE_TYPE_OF_BIO(type); - struct f2fs_bio_info *io = &sbi->write_io[btype]; - struct bio_vec *bvec; - struct page *target; - int i; - - down_read(&io->io_rwsem); - if (!io->bio) { - up_read(&io->io_rwsem); - return false; - } - - bio_for_each_segment_all(bvec, io->bio, i) { - - if (bvec->bv_page->mapping) { - target = bvec->bv_page; - } else { - struct f2fs_crypto_ctx *ctx; - - /* encrypted page */ - ctx = (struct f2fs_crypto_ctx *)page_private( - bvec->bv_page); - target = ctx->w.control_page; - } - - if (page == target) { - up_read(&io->io_rwsem); - return true; - } - } - - up_read(&io->io_rwsem); - return false; -} - void f2fs_wait_on_page_writeback(struct page *page, enum page_type type) { -- GitLab From 2ed80764c65c70261f9f218447a2a45826963ffd Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 18 Jan 2016 18:31:18 +0800 Subject: [PATCH 0090/5498] f2fs: flush dirty nat entries when exceeding threshold When testing f2fs with xfstest, generic/251 is stuck for long time, the case uses below serials to obtain fresh released space in device, in order to prepare for following fstrim test. 1. rm -rf /mnt/dir 2. mkdir /mnt/dir/ 3. cp -axT `pwd`/ /mnt/dir/ 4. goto 1 During preparing step, all nat entries will be cached in nat cache, most of them are dirty entries with invalid blkaddr, which means nodes related to these entries have been truncated, and they could be reused after the dirty entries been checkpointed. However, there was no checkpoint been triggered, so nid allocators (e.g. mkdir, creat) will run into long journey of iterating all NAT pages, looking for free nids in alloc_nid->build_free_nids. Here, in f2fs_balance_fs_bg we give another chance to do checkpoint to flush nat entries for reusing them in free nid cache when dirty entry count exceeds 10% of max count. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.h | 9 +++++++++ fs/f2fs/segment.c | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index d4d1f636fe1c..bd119c0edf93 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -25,6 +25,9 @@ /* control the memory footprint threshold (10MB per 1GB ram) */ #define DEF_RAM_THRESHOLD 10 +/* control dirty nats ratio threshold (default: 10% over max nid count) */ +#define DEF_DIRTY_NAT_RATIO_THRESHOLD 10 + /* vector size for gang look-up from nat cache that consists of radix tree */ #define NATVEC_SIZE 64 #define SETVEC_SIZE 32 @@ -117,6 +120,12 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne, raw_ne->version = ni->version; } +static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi) +{ + return NM_I(sbi)->dirty_nat_cnt >= NM_I(sbi)->max_nid * + DEF_DIRTY_NAT_RATIO_THRESHOLD / 100; +} + enum mem_type { FREE_NIDS, /* indicates the free nid list */ NAT_ENTRIES, /* indicates the cached nat entry */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e16235bbdd7d..53baf4a420dc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -291,8 +291,9 @@ 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) || + excess_prefree_segs(sbi) || + excess_dirty_nats(sbi) || (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { if (test_opt(sbi, DATA_FLUSH)) sync_dirty_inodes(sbi, FILE_INODE); -- GitLab From 6fb6bf45a018f6bfa93f3e66890de8acc23f65b0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 18 Jan 2016 18:32:58 +0800 Subject: [PATCH 0091/5498] f2fs: export dirty_nats_ratio in sysfs This patch exports a new sysfs entry 'dirty_nat_ratio' to control threshold of dirty nat entries, if current ratio exceeds configured threshold, checkpoint will be triggered in f2fs_balance_fs_bg for flushing dirty nats. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: Documentation/ABI/testing/sysfs-fs-f2fs --- Documentation/ABI/testing/sysfs-fs-f2fs | 8 ++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 1 + fs/f2fs/node.h | 2 +- fs/f2fs/super.c | 2 ++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index e5200f354abf..523cb9d4e272 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -98,3 +98,11 @@ Date: October 2015 Contact: "Chao Yu" Description: Controls the count of nid pages to be readaheaded. + +What: /sys/fs/f2fs//dirty_nats_ratio +Date: January 2016 +Contact: "Chao Yu" +Description: + Controls dirty nat entries ratio threshold, if current + ratio exceeds configured threshold, checkpoint will + be triggered for flushing dirty nat entries. diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1bca5a57fbee..17ba0b7e235c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -514,6 +514,7 @@ struct f2fs_nm_info { nid_t next_scan_nid; /* the next nid to be scanned */ unsigned int ram_thresh; /* control the memory footprint */ unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */ + unsigned int dirty_nats_ratio; /* control dirty nats ratio threshold */ /* NAT cache management */ struct radix_tree_root nat_root;/* root of the nat entry cache */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9ed23e5dc360..49a5cdb5ef0b 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2004,6 +2004,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi) nm_i->nat_cnt = 0; nm_i->ram_thresh = DEF_RAM_THRESHOLD; nm_i->ra_nid_pages = DEF_RA_NID_PAGES; + nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD; INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); INIT_LIST_HEAD(&nm_i->free_nid_list); diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index bd119c0edf93..56c451921522 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -123,7 +123,7 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne, static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi) { return NM_I(sbi)->dirty_nat_cnt >= NM_I(sbi)->max_nid * - DEF_DIRTY_NAT_RATIO_THRESHOLD / 100; + NM_I(sbi)->dirty_nats_ratio / 100; } enum mem_type { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3bf990b80026..be7c40353185 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -216,6 +216,7 @@ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); +F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); @@ -237,6 +238,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(dir_level), ATTR_LIST(ram_thresh), ATTR_LIST(ra_nid_pages), + ATTR_LIST(dirty_nats_ratio), ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), NULL, -- GitLab From d35df7bdef01298c8a481385035367ade4f77db9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 22 Jan 2016 17:42:06 +0800 Subject: [PATCH 0092/5498] f2fs: correct search area in get_new_segment get_new_segment starts from current segment position, tries to search a free segment among its right neighbors locate in same section. But previously our search area was set as [current segment, max segment], which means we have to search to more bits in free_segmap bitmap for some worse cases. So here we correct the search area to [current segment, last segment in section] to avoid unnecessary searching. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 53baf4a420dc..9e3276067526 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -874,9 +874,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi, if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { segno = find_next_zero_bit(free_i->free_segmap, - MAIN_SEGS(sbi), *newseg + 1); - if (segno - *newseg < sbi->segs_per_sec - - (*newseg % sbi->segs_per_sec)) + (hint + 1) * sbi->segs_per_sec, *newseg + 1); + if (segno < (hint + 1) * sbi->segs_per_sec) goto got_it; } find_other_zone: -- GitLab From e4ce81fa5d96798c9a3618c1dcf18d57e3abf438 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 20 Jan 2016 08:27:37 +0800 Subject: [PATCH 0093/5498] f2fs: remove needless condition check This patch removes needless condition variable. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4260fbaa03cb..58a1154a3598 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1478,13 +1478,9 @@ restart: if (f2fs_lookup_extent_cache(inode, index, &ei)) { dn.data_blkaddr = ei.blk + index - ei.fofs; } else { - bool restart = false; - /* hole case */ err = get_dnode_of_data(&dn, index, LOOKUP_NODE); - if (err || (!err && dn.data_blkaddr == NULL_ADDR)) - restart = true; - if (restart) { + if (err || (!err && dn.data_blkaddr == NULL_ADDR)) { f2fs_put_dnode(&dn); f2fs_lock_op(sbi); locked = true; -- GitLab From bce0a689e49e1bb5288d17beb0843b7494d0bf86 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 20 Jan 2016 23:46:05 +0800 Subject: [PATCH 0094/5498] f2fs: use writepages->lock for WB_SYNC_ALL If there are many writepages calls by multiple threads in background, we don't need to serialize to merge all the bios, since it's background. In such the case, it'd better to run writepages concurrently. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 58a1154a3598..ef9fded80c56 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1403,7 +1403,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, DATA, wbc); - if (!S_ISDIR(inode->i_mode)) { + if (!S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_ALL) { mutex_lock(&sbi->writepages); locked = true; } -- GitLab From 128e478621e02150c341cc4bcf747d51b99c9d00 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 23 Jan 2016 22:00:57 +0800 Subject: [PATCH 0095/5498] f2fs: fix to overcome inline_data floods The scenario is: 1. create lots of node blocks 2. sync 3. write lots of inline_data -> got panic due to no free space In that case, we should flush node blocks when writing inline_data in #3, and trigger gc as well. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index f610c2a9bdde..4a704a16bf17 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -838,8 +838,15 @@ gc_more: if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) { gc_type = FG_GC; + /* + * If there is no victim and no prefree segment but still not + * enough free sections, we should flush dent/node blocks and do + * garbage collections. + */ if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi)) write_checkpoint(sbi, &cpc); + else if (has_not_enough_free_secs(sbi, 0)) + write_checkpoint(sbi, &cpc); } if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type)) -- GitLab From 3d53fd2f34b88a93d7db80602a5b57aca06bad84 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 23 Jan 2016 13:35:18 -0800 Subject: [PATCH 0096/5498] f2fs: do f2fs_balance_fs when block is allocated We should consider data block allocation to trigger f2fs_balance_fs. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ef9fded80c56..257dc6b371c1 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -543,7 +543,7 @@ static int __allocate_data_blocks(struct inode *inode, loff_t offset, struct dnode_of_data dn; u64 start = F2FS_BYTES_TO_BLK(offset); u64 len = F2FS_BYTES_TO_BLK(count); - bool allocated; + bool allocated = false; u64 end_offset; int err = 0; @@ -585,7 +585,7 @@ static int __allocate_data_blocks(struct inode *inode, loff_t offset, f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, dn.node_changed); + f2fs_balance_fs(sbi, allocated); } return err; @@ -595,7 +595,7 @@ sync_out: f2fs_put_dnode(&dn); out: f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, dn.node_changed); + f2fs_balance_fs(sbi, allocated); return err; } @@ -689,14 +689,14 @@ get_next: if (dn.ofs_in_node >= end_offset) { if (allocated) sync_inode_page(&dn); - allocated = false; f2fs_put_dnode(&dn); if (create) { f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, dn.node_changed); + f2fs_balance_fs(sbi, allocated); f2fs_lock_op(sbi); } + allocated = false; set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, pgofs, mode); @@ -754,7 +754,7 @@ put_out: unlock_out: if (create) { f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, dn.node_changed); + f2fs_balance_fs(sbi, allocated); } out: trace_f2fs_map_blocks(inode, map, err); -- GitLab From 9c2a8be30b7ac067c949f4fdb3f8b51558813b11 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 25 Jan 2016 05:57:05 -0800 Subject: [PATCH 0097/5498] f2fs: avoid multiple node page writes due to inline_data The sceanrio is: 1. create fully node blocks 2. flush node blocks 3. write inline_data for all the node blocks again 4. flush node blocks redundantly So, this patch tries to flush inline_data when flushing node blocks. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 + fs/f2fs/inline.c | 2 ++ fs/f2fs/inode.c | 4 ++++ fs/f2fs/node.c | 41 +++++++++++++++++++++++++++++++++++++++++ fs/f2fs/node.h | 15 +++++++++++++++ 5 files changed, 63 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 257dc6b371c1..3aef67affce6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1464,6 +1464,7 @@ restart: if (pos + len <= MAX_INLINE_DATA) { read_inline_data(page, ipage); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + set_inline_node(ipage); sync_inode_page(&dn); } else { err = f2fs_convert_inline_page(&dn, page); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index c3f0b7d4cfca..738b761cc1ef 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -159,6 +159,7 @@ no_update: /* clear inline data and flag after data writeback */ truncate_inline_inode(dn->inode_page, 0); + clear_inline_node(dn->inode_page); clear_out: stat_dec_inline_inode(dn->inode); f2fs_clear_inline_inode(dn->inode); @@ -233,6 +234,7 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); sync_inode_page(&dn); + clear_inline_node(dn.inode_page); f2fs_put_dnode(&dn); return 0; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 55773459f58f..fc1918c8a77a 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -262,6 +262,10 @@ int update_inode(struct inode *inode, struct page *node_page) set_cold_node(inode, node_page); clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE); + /* deleted inode */ + if (inode->i_nlink == 0) + clear_inline_node(node_page); + return set_page_dirty(node_page); } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 49a5cdb5ef0b..d3078da6ee81 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1158,6 +1158,39 @@ void sync_inode_page(struct dnode_of_data *dn) dn->node_changed = ret ? true: false; } +static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) +{ + struct inode *inode; + struct page *page; + + /* should flush inline_data before evict_inode */ + inode = ilookup(sbi->sb, ino); + if (!inode) + return; + + page = grab_cache_page_nowait(inode->i_mapping, 0); + if (!page) + goto iput_out; + + if (!PageUptodate(page)) + goto page_out; + + if (!PageDirty(page)) + goto page_out; + + if (!clear_page_dirty_for_io(page)) + goto page_out; + + if (!f2fs_write_inline_data(inode, page)) + inode_dec_dirty_pages(inode); + else + set_page_dirty(page); +page_out: + f2fs_put_page(page, 1); +iput_out: + iput(inode); +} + int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, struct writeback_control *wbc) { @@ -1225,6 +1258,14 @@ continue_unlock: goto continue_unlock; } + /* flush inline_data */ + if (!ino && is_inline_node(page)) { + clear_inline_node(page); + unlock_page(page); + flush_inline_data(sbi, ino_of_node(page)); + continue; + } + if (!clear_page_dirty_for_io(page)) goto continue_unlock; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 56c451921522..8f405d243e49 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -379,6 +379,21 @@ static inline int is_node(struct page *page, int type) #define is_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT) #define is_dent_dnode(page) is_node(page, DENT_BIT_SHIFT) +static inline int is_inline_node(struct page *page) +{ + return PageChecked(page); +} + +static inline void set_inline_node(struct page *page) +{ + SetPageChecked(page); +} + +static inline void clear_inline_node(struct page *page) +{ + ClearPageChecked(page); +} + static inline void set_cold_node(struct inode *inode, struct page *page) { struct f2fs_node *rn = F2FS_NODE(page); -- GitLab From c3d1e04c534181ef8a587377c72f697223da2aa6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 25 Jan 2016 10:46:58 -0800 Subject: [PATCH 0098/5498] f2fs: don't need to sync node page at every time In write_end, we don't need to sync inode page at every time. Instead, we can expect f2fs_write_inode will update later. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3aef67affce6..976b316d65a3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1628,7 +1628,6 @@ static int f2fs_write_end(struct file *file, if (pos + copied > i_size_read(inode)) { i_size_write(inode, pos + copied); mark_inode_dirty(inode); - update_inode_page(inode); } f2fs_put_page(page, 1); -- GitLab From f574adc5b7a52c8f6f0f7de0348484dc875a8e28 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 25 Jan 2016 10:48:50 -0800 Subject: [PATCH 0099/5498] f2fs: avoid needless sync_inode_page when reading inline_data In write_begin, if there is an inline_data, f2fs loads it into 0'th data page. Since it's the read path, we don't need to sync its inode page. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 976b316d65a3..2ac7f4117298 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1465,7 +1465,6 @@ restart: read_inline_data(page, ipage); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); set_inline_node(ipage); - sync_inode_page(&dn); } else { err = f2fs_convert_inline_page(&dn, page); if (err) -- GitLab From 730ed561e90fdad25223716bd845dc2beb4a01f5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 25 Jan 2016 14:31:58 -0800 Subject: [PATCH 0100/5498] f2fs: don't need to call set_page_dirty for io error If end_io gets an error, we don't need to set the page as dirty, since we already set f2fs_stop_checkpoint which will not flush any data. This will resolve the following warning. ====================================================== [ INFO: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected ] 4.4.0+ #9 Tainted: G O ------------------------------------------------------ xfs_io/26773 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire: (&(&sbi->inode_lock[i])->rlock){+.+...}, at: [] update_dirty_page+0x6f/0xd0 [f2fs] and this task is already holding: (&(&q->__queue_lock)->rlock){-.-.-.}, at: [] blk_queue_bio+0x422/0x490 which would create a new lock dependency: (&(&q->__queue_lock)->rlock){-.-.-.} -> (&(&sbi->inode_lock[i])->rlock){+.+...} Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 2ac7f4117298..27a31e0a7c5f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -68,7 +68,6 @@ static void f2fs_write_end_io(struct bio *bio, int err) f2fs_restore_and_release_control_page(&page); if (unlikely(err)) { - set_page_dirty(page); set_bit(AS_EIO, &page->mapping->flags); f2fs_stop_checkpoint(sbi); } -- GitLab From 8d46472d07a5cfa1c373d0571303e856150e2e42 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 23 Jan 2016 16:23:55 +0800 Subject: [PATCH 0101/5498] f2fs: enhance foreground GC If we configure section consist of multiple segments, foreground GC will do the garbage collection with following approach: for each segment in victim section blk_start_plug for each valid block in segment write out by OPU method submit bio cache <--- blk_finish_plug <--- There are two issue: 1) for most of the time, 'submit bio cache' will break the merging in current bio buffer from writes of next segments, making a smaller bio submitting. 2) block plug only cover IO submitting in one segment, which reduce opportunity of merging IOs in plug with multiple segments. So refactor the code as below structure to strive for biggest opportunity of merging IOs: blk_start_plug for each segment in victim section for each valid block in segment write out by OPU method submit bio cache blk_finish_plug Test method: 1. mkfs.f2fs -s 8 /dev/sdX 2. touch 32 files 3. write 2M data into each file 4. punch 1.5M data from offset 0 for each file 5. trigger foreground gc through ioctl Before patch, there are totoally 40 bios submitted. f2fs_submit_write_bio: dev = (8,32), WRITE_SYNC, DATA, sector = 65536, size = 122880 f2fs_submit_write_bio: dev = (8,32), WRITE_SYNC, DATA, sector = 65776, size = 122880 f2fs_submit_write_bio: dev = (8,32), WRITE_SYNC, DATA, sector = 66016, size = 122880 f2fs_submit_write_bio: dev = (8,32), WRITE_SYNC, DATA, sector = 66256, size = 122880 f2fs_submit_write_bio: dev = (8,32), WRITE_SYNC, DATA, sector = 66496, size = 32768 ----repeat for 8 times After patch, there are totally 35 bios submitted. f2fs_submit_write_bio: dev = (8,32), WRITE_SYNC, DATA, sector = 65536, size = 122880 ----repeat 34 times f2fs_submit_write_bio: dev = (8,32), WRITE_SYNC, DATA, sector = 73696, size = 16384 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 143 ++++++++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 71 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 4a704a16bf17..c1f363d2b17c 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -399,7 +399,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, * On validity, copy that node with cold status, otherwise (invalid node) * ignore that. */ -static int gc_node_segment(struct f2fs_sb_info *sbi, +static void gc_node_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, unsigned int segno, int gc_type) { bool initial = true; @@ -419,7 +419,7 @@ next_step: /* stop BG_GC if there is not enough free sections. */ if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) - return 0; + return; if (check_valid_map(sbi, segno, off) == 0) continue; @@ -460,20 +460,6 @@ next_step: initial = false; goto next_step; } - - if (gc_type == FG_GC) { - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = LONG_MAX, - .for_reclaim = 0, - }; - sync_node_pages(sbi, 0, &wbc); - - /* return 1 only if FG_GC succefully reclaimed one */ - if (get_valid_blocks(sbi, segno, 1) == 0) - return 1; - } - return 0; } /* @@ -663,7 +649,7 @@ out: * If the parent node is not valid or the data block address is different, * the victim data block is ignored. */ -static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, +static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, struct gc_inode_list *gc_list, unsigned int segno, int gc_type) { struct super_block *sb = sbi->sb; @@ -686,7 +672,7 @@ next_step: /* stop BG_GC if there is not enough free sections. */ if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) - return 0; + return; if (check_valid_map(sbi, segno, off) == 0) continue; @@ -747,15 +733,6 @@ next_step: if (++phase < 4) goto next_step; - - if (gc_type == FG_GC) { - f2fs_submit_merged_bio(sbi, DATA, WRITE); - - /* return 1 only if FG_GC succefully reclaimed one */ - if (get_valid_blocks(sbi, segno, 1) == 0) - return 1; - } - return 0; } static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, @@ -771,53 +748,90 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, return ret; } -static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, +static int do_garbage_collect(struct f2fs_sb_info *sbi, + unsigned int start_segno, struct gc_inode_list *gc_list, int gc_type) { struct page *sum_page; struct f2fs_summary_block *sum; struct blk_plug plug; - int nfree = 0; + unsigned int segno = start_segno; + unsigned int end_segno = start_segno + sbi->segs_per_sec; + int seg_freed = 0; + unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ? + SUM_TYPE_DATA : SUM_TYPE_NODE; - /* read segment summary of victim */ - sum_page = get_sum_page(sbi, segno); + /* readahead multi ssa blocks those have contiguous address */ + if (sbi->segs_per_sec > 1) + ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), + sbi->segs_per_sec, META_SSA, true); + + /* reference all summary page */ + while (segno < end_segno) { + sum_page = get_sum_page(sbi, segno++); + unlock_page(sum_page); + } blk_start_plug(&plug); - sum = page_address(sum_page); + for (segno = start_segno; segno < end_segno; segno++) { + /* find segment summary of victim */ + sum_page = find_get_page(META_MAPPING(sbi), + GET_SUM_BLOCK(sbi, segno)); + f2fs_bug_on(sbi, !PageUptodate(sum_page)); + f2fs_put_page(sum_page, 0); - /* - * this is to avoid deadlock: - * - lock_page(sum_page) - f2fs_replace_block - * - check_valid_map() - mutex_lock(sentry_lock) - * - mutex_lock(sentry_lock) - change_curseg() - * - lock_page(sum_page) - */ - unlock_page(sum_page); - - switch (GET_SUM_TYPE((&sum->footer))) { - case SUM_TYPE_NODE: - nfree = gc_node_segment(sbi, sum->entries, segno, gc_type); - break; - case SUM_TYPE_DATA: - nfree = gc_data_segment(sbi, sum->entries, gc_list, - segno, gc_type); - break; + sum = page_address(sum_page); + f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer))); + + /* + * this is to avoid deadlock: + * - lock_page(sum_page) - f2fs_replace_block + * - check_valid_map() - mutex_lock(sentry_lock) + * - mutex_lock(sentry_lock) - change_curseg() + * - lock_page(sum_page) + */ + + if (type == SUM_TYPE_NODE) + gc_node_segment(sbi, sum->entries, segno, gc_type); + else + gc_data_segment(sbi, sum->entries, gc_list, segno, + gc_type); + + stat_inc_seg_count(sbi, type, gc_type); + stat_inc_call_count(sbi->stat_info); + + f2fs_put_page(sum_page, 0); + } + + if (gc_type == FG_GC) { + if (type == SUM_TYPE_NODE) { + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = LONG_MAX, + .for_reclaim = 0, + }; + sync_node_pages(sbi, 0, &wbc); + } else { + f2fs_submit_merged_bio(sbi, DATA, WRITE); + } } - blk_finish_plug(&plug); - stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type); - stat_inc_call_count(sbi->stat_info); + blk_finish_plug(&plug); - f2fs_put_page(sum_page, 0); - return nfree; + if (gc_type == FG_GC) { + while (start_segno < end_segno) + if (get_valid_blocks(sbi, start_segno++, 1) == 0) + seg_freed++; + } + return seg_freed; } int f2fs_gc(struct f2fs_sb_info *sbi, bool sync) { - unsigned int segno, i; + unsigned int segno; int gc_type = sync ? FG_GC : BG_GC; - int sec_freed = 0; + int sec_freed = 0, seg_freed; int ret = -EINVAL; struct cp_control cpc; struct gc_inode_list gc_list = { @@ -853,22 +867,9 @@ gc_more: goto stop; ret = 0; - /* readahead multi ssa blocks those have contiguous address */ - if (sbi->segs_per_sec > 1) - ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec, - META_SSA, true); - - for (i = 0; i < sbi->segs_per_sec; i++) { - /* - * for FG_GC case, halt gcing left segments once failed one - * of segments in selected section to avoid long latency. - */ - if (!do_garbage_collect(sbi, segno + i, &gc_list, gc_type) && - gc_type == FG_GC) - break; - } + seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type); - if (i == sbi->segs_per_sec && gc_type == FG_GC) + if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec) sec_freed++; if (gc_type == FG_GC) -- GitLab From b61980b4916e3e3f2d9fa665d3654178f6252ae4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 20 Jan 2016 23:43:51 +0800 Subject: [PATCH 0102/5498] f2fs: use wait_for_stable_page to avoid contention In write_begin, if storage supports stable_page, we don't need to wait for writeback to update its contents. This patch introduces to use wait_for_stable_page instead of wait_on_page_writeback. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 ++-- fs/f2fs/data.c | 7 ++++--- fs/f2fs/dir.c | 8 ++++---- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 6 +++--- fs/f2fs/gc.c | 10 +++++----- fs/f2fs/inline.c | 16 ++++++++-------- fs/f2fs/inode.c | 4 ++-- fs/f2fs/node.c | 10 +++++----- fs/f2fs/node.h | 2 +- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 12 ++++++++---- fs/f2fs/xattr.c | 6 +++--- 13 files changed, 47 insertions(+), 42 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 36b0d044919e..7d7e5d97e2f6 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -39,7 +39,7 @@ repeat: cond_resched(); goto repeat; } - f2fs_wait_on_page_writeback(page, META); + f2fs_wait_on_page_writeback(page, META, true); SetPageUptodate(page); return page; } @@ -233,7 +233,7 @@ static int f2fs_write_meta_page(struct page *page, if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - f2fs_wait_on_page_writeback(page, META); + f2fs_wait_on_page_writeback(page, META, true); write_meta_page(sbi, page); dec_page_count(sbi, F2FS_DIRTY_META); unlock_page(page); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 27a31e0a7c5f..b82d33828afa 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -256,7 +256,7 @@ void set_data_blkaddr(struct dnode_of_data *dn) struct page *node_page = dn->node_page; unsigned int ofs_in_node = dn->ofs_in_node; - f2fs_wait_on_page_writeback(node_page, NODE); + f2fs_wait_on_page_writeback(node_page, NODE, true); rn = F2FS_NODE(node_page); @@ -1320,7 +1320,8 @@ continue_unlock: if (PageWriteback(page)) { if (wbc->sync_mode != WB_SYNC_NONE) - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, + DATA, true); else goto continue_unlock; } @@ -1548,7 +1549,7 @@ repeat: } } - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, false); /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 83da362d04f6..d7d0e783d15e 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -296,7 +296,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, { enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA; lock_page(page); - f2fs_wait_on_page_writeback(page, type); + f2fs_wait_on_page_writeback(page, type, true); de->ino = cpu_to_le32(inode->i_ino); set_de_type(de, inode->i_mode); f2fs_dentry_kunmap(dir, page); @@ -311,7 +311,7 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage) { struct f2fs_inode *ri; - f2fs_wait_on_page_writeback(ipage, NODE); + f2fs_wait_on_page_writeback(ipage, NODE, true); /* copy name info. to this inode page */ ri = F2FS_INODE(ipage); @@ -598,7 +598,7 @@ start: ++level; goto start; add_dentry: - f2fs_wait_on_page_writeback(dentry_page, DATA); + f2fs_wait_on_page_writeback(dentry_page, DATA, true); if (inode) { down_write(&F2FS_I(inode)->i_sem); @@ -709,7 +709,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, return f2fs_delete_inline_entry(dentry, page, dir, inode); lock_page(page); - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); dentry_blk = page_address(page); bit_pos = dentry - dentry_blk->dentry; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 17ba0b7e235c..7fa621a9d229 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1837,7 +1837,7 @@ void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *, block_t, block_t, unsigned char, bool); void allocate_data_block(struct f2fs_sb_info *, struct page *, block_t, block_t *, struct f2fs_summary *, int); -void f2fs_wait_on_page_writeback(struct page *, enum page_type); +void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool); void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t); void write_data_summaries(struct f2fs_sb_info *, block_t); void write_node_summaries(struct f2fs_sb_info *, block_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 8e68c611437b..43164b05b6ce 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -87,7 +87,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, trace_f2fs_vm_page_mkwrite(page, DATA); mapped: /* fill the page */ - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, false); /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) @@ -522,7 +522,7 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, if (IS_ERR(page)) return 0; truncate_out: - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); zero_user(page, offset, PAGE_CACHE_SIZE - offset); if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) set_page_dirty(page); @@ -744,7 +744,7 @@ static int fill_zero(struct inode *inode, pgoff_t index, if (IS_ERR(page)) return PTR_ERR(page); - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); zero_user(page, start, len); set_page_dirty(page); f2fs_put_page(page, 1); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c1f363d2b17c..d57245deca99 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -446,7 +446,7 @@ next_step: /* set page dirty and write it */ if (gc_type == FG_GC) { - f2fs_wait_on_page_writeback(node_page, NODE); + f2fs_wait_on_page_writeback(node_page, NODE, true); set_page_dirty(node_page); } else { if (!PageWriteback(node_page)) @@ -553,7 +553,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) * don't cache encrypted data into meta inode until previous dirty * data were writebacked to avoid racing between GC and flush. */ - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); get_node_info(fio.sbi, dn.nid, &ni); set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); @@ -582,14 +582,14 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) goto put_page_out; set_page_dirty(fio.encrypted_page); - f2fs_wait_on_page_writeback(fio.encrypted_page, DATA); + f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true); if (clear_page_dirty_for_io(fio.encrypted_page)) dec_page_count(fio.sbi, F2FS_DIRTY_META); set_page_writeback(fio.encrypted_page); /* allocate block address */ - f2fs_wait_on_page_writeback(dn.node_page, NODE); + f2fs_wait_on_page_writeback(dn.node_page, NODE, true); allocate_data_block(fio.sbi, NULL, fio.blk_addr, &fio.blk_addr, &sum, CURSEG_COLD_DATA); fio.rw = WRITE_SYNC; @@ -631,7 +631,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type) .encrypted_page = NULL, }; set_page_dirty(page); - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); if (clear_page_dirty_for_io(page)) inode_dec_dirty_pages(inode); set_cold_data(page); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 738b761cc1ef..fc4d29845938 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -71,7 +71,7 @@ bool truncate_inline_inode(struct page *ipage, u64 from) addr = inline_data_addr(ipage); - f2fs_wait_on_page_writeback(ipage, NODE); + f2fs_wait_on_page_writeback(ipage, NODE, true); memset(addr + from, 0, MAX_INLINE_DATA - from); return true; @@ -124,7 +124,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) if (err) return err; - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); if (PageUptodate(page)) goto no_update; @@ -150,7 +150,7 @@ no_update: write_data_page(dn, &fio); set_data_blkaddr(dn); f2fs_update_extent_cache(dn); - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); if (dirty) inode_dec_dirty_pages(dn->inode); @@ -224,7 +224,7 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) f2fs_bug_on(F2FS_I_SB(inode), page->index); - f2fs_wait_on_page_writeback(dn.inode_page, NODE); + f2fs_wait_on_page_writeback(dn.inode_page, NODE, true); src_addr = kmap_atomic(page); dst_addr = inline_data_addr(dn.inode_page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); @@ -263,7 +263,7 @@ process_inline: ipage = get_node_page(sbi, inode->i_ino); f2fs_bug_on(sbi, IS_ERR(ipage)); - f2fs_wait_on_page_writeback(ipage, NODE); + f2fs_wait_on_page_writeback(ipage, NODE, true); src_addr = inline_data_addr(npage); dst_addr = inline_data_addr(ipage); @@ -391,7 +391,7 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, if (err) goto out; - f2fs_wait_on_page_writeback(page, DATA); + f2fs_wait_on_page_writeback(page, DATA, true); zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); dentry_blk = kmap_atomic(page); @@ -471,7 +471,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, } } - f2fs_wait_on_page_writeback(ipage, NODE); + f2fs_wait_on_page_writeback(ipage, NODE, true); name_hash = f2fs_dentry_hash(name); make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); @@ -509,7 +509,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, int i; lock_page(page); - f2fs_wait_on_page_writeback(page, NODE); + f2fs_wait_on_page_writeback(page, NODE, true); inline_dentry = inline_data_addr(page); bit_pos = dentry - inline_dentry->dentry; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index fc1918c8a77a..4bd44f817ff4 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -83,7 +83,7 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage) while (start < end) { if (*start++) { - f2fs_wait_on_page_writeback(ipage, NODE); + f2fs_wait_on_page_writeback(ipage, NODE, true); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage)); @@ -226,7 +226,7 @@ int update_inode(struct inode *inode, struct page *node_page) { struct f2fs_inode *ri; - f2fs_wait_on_page_writeback(node_page, NODE); + f2fs_wait_on_page_writeback(node_page, NODE, true); ri = F2FS_INODE(node_page); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d3078da6ee81..2763ce12e3f2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -861,7 +861,7 @@ skip_partial: f2fs_put_page(page, 1); goto restart; } - f2fs_wait_on_page_writeback(page, NODE); + f2fs_wait_on_page_writeback(page, NODE, true); ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0; set_page_dirty(page); unlock_page(page); @@ -976,7 +976,7 @@ struct page *new_node_page(struct dnode_of_data *dn, new_ni.ino = dn->inode->i_ino; set_node_addr(sbi, &new_ni, NEW_ADDR, false); - f2fs_wait_on_page_writeback(page, NODE); + f2fs_wait_on_page_writeback(page, NODE, true); fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true); set_cold_node(dn->inode, page); SetPageUptodate(page); @@ -1332,7 +1332,7 @@ int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino) continue; if (ino && ino_of_node(page) == ino) { - f2fs_wait_on_page_writeback(page, NODE); + f2fs_wait_on_page_writeback(page, NODE, true); if (TestClearPageError(page)) ret = -EIO; } @@ -1371,7 +1371,7 @@ static int f2fs_write_node_page(struct page *page, if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - f2fs_wait_on_page_writeback(page, NODE); + f2fs_wait_on_page_writeback(page, NODE, true); /* get old block addr of this node page */ nid = nid_of_node(page); @@ -1748,7 +1748,7 @@ void recover_inline_xattr(struct inode *inode, struct page *page) src_addr = inline_xattr_addr(page); inline_size = inline_xattr_size(inode); - f2fs_wait_on_page_writeback(ipage, NODE); + f2fs_wait_on_page_writeback(ipage, NODE, true); memcpy(dst_addr, src_addr, inline_size); update_inode: update_inode(inode, ipage); diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 8f405d243e49..1f4f9d4569d9 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -330,7 +330,7 @@ static inline int set_nid(struct page *p, int off, nid_t nid, bool i) { struct f2fs_node *rn = F2FS_NODE(p); - f2fs_wait_on_page_writeback(p, NODE); + f2fs_wait_on_page_writeback(p, NODE, true); if (i) rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index d24c1a040a13..47324cee685f 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -418,7 +418,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (err) goto out; - f2fs_wait_on_page_writeback(dn.node_page, NODE); + f2fs_wait_on_page_writeback(dn.node_page, NODE, true); get_node_info(sbi, dn.nid, &ni); f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9e3276067526..7cd07d54be4e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -223,7 +223,8 @@ int commit_inmem_pages(struct inode *inode, bool abort) if (!abort) { if (cur->page->mapping == inode->i_mapping) { set_page_dirty(cur->page); - f2fs_wait_on_page_writeback(cur->page, DATA); + f2fs_wait_on_page_writeback(cur->page, DATA, + true); if (clear_page_dirty_for_io(cur->page)) inode_dec_dirty_pages(inode); trace_f2fs_commit_inmem_page(cur->page, INMEM); @@ -1416,14 +1417,17 @@ void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, } void f2fs_wait_on_page_writeback(struct page *page, - enum page_type type) + enum page_type type, bool ordered) { if (PageWriteback(page)) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); if (is_merged_page(sbi, page, type)) f2fs_submit_merged_bio(sbi, type, WRITE); - wait_on_page_writeback(page); + if (ordered) + wait_on_page_writeback(page); + else + wait_for_stable_page(page); } } @@ -1439,7 +1443,7 @@ void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, cpage = find_lock_page(META_MAPPING(sbi), blkaddr); if (cpage) { - f2fs_wait_on_page_writeback(cpage, DATA); + f2fs_wait_on_page_writeback(cpage, DATA, true); f2fs_put_page(cpage, 1); } } diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index d59ac2ff2c7c..d2b15513dca1 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -347,7 +347,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, if (ipage) { inline_addr = inline_xattr_addr(ipage); - f2fs_wait_on_page_writeback(ipage, NODE); + f2fs_wait_on_page_writeback(ipage, NODE, true); } else { page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) { @@ -355,7 +355,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, return PTR_ERR(page); } inline_addr = inline_xattr_addr(page); - f2fs_wait_on_page_writeback(page, NODE); + f2fs_wait_on_page_writeback(page, NODE, true); } memcpy(inline_addr, txattr_addr, inline_size); f2fs_put_page(page, 1); @@ -376,7 +376,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, return PTR_ERR(xpage); } f2fs_bug_on(sbi, new_nid); - f2fs_wait_on_page_writeback(xpage, NODE); + f2fs_wait_on_page_writeback(xpage, NODE, true); } else { struct dnode_of_data dn; set_new_dnode(&dn, inode, NULL, NULL, new_nid); -- GitLab From e98355410d40d24a8231b89590543423055299e3 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 3 Feb 2016 10:26:13 +0800 Subject: [PATCH 0103/5498] f2fs: delete unnecessary wait for page writeback no need to wait inline file page writeback for no one use it, so this patch delete unnecessary wait. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index fc4d29845938..0be4a9b400c6 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -124,8 +124,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) if (err) return err; - f2fs_wait_on_page_writeback(page, DATA, true); - + f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page)); if (PageUptodate(page)) goto no_update; -- GitLab From f25823cece662162b64f1f470d52ebca93888d1d Mon Sep 17 00:00:00 2001 From: Fan Li Date: Wed, 3 Feb 2016 16:21:57 +0800 Subject: [PATCH 0104/5498] f2fs: avoid unnecessary search while finding victim in gc variable nsearched in get_victim_by_default() indicates the number of dirty segments we already checked. There are 2 problems about the way it updates: 1. When p.ofs_unit is greater than 1, the victim we find consists of multiple segments, possibly more than 1 dirty segment. But nsearched always increases by 1. 2. If segments have been found but not been chosen, nsearched won't increase. So even we have checked all dirty segments, nsearched may still less than p.max_search. All these problems could cause unnecessary search after all dirty segments have already been checked. Signed-off-by: Fan li Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index d57245deca99..645273c8b341 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -245,6 +245,18 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, return get_cb_cost(sbi, segno); } +static unsigned int count_bits(const unsigned long *addr, + unsigned int offset, unsigned int len) +{ + unsigned int end = offset + len, sum = 0; + + while (offset < end) { + if (test_bit(offset++, addr)) + ++sum; + } + return sum; +} + /* * This function is called from two paths. * One is garbage collection and the other is SSR segment selection. @@ -260,7 +272,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, struct victim_sel_policy p; unsigned int secno, max_cost; unsigned int last_segment = MAIN_SEGS(sbi); - int nsearched = 0; + unsigned int nsearched = 0; mutex_lock(&dirty_i->seglist_lock); @@ -295,26 +307,31 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, } p.offset = segno + p.ofs_unit; - if (p.ofs_unit > 1) + if (p.ofs_unit > 1) { p.offset -= segno % p.ofs_unit; + nsearched += count_bits(p.dirty_segmap, + p.offset - p.ofs_unit, + p.ofs_unit); + } else { + nsearched++; + } + secno = GET_SECNO(sbi, segno); if (sec_usage_check(sbi, secno)) - continue; + goto next; if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap)) - continue; + goto next; cost = get_gc_cost(sbi, segno, &p); if (p.min_cost > cost) { p.min_segno = segno; p.min_cost = cost; - } else if (unlikely(cost == max_cost)) { - continue; } - - if (nsearched++ >= p.max_search) { +next: + if (nsearched >= p.max_search) { sbi->last_victim[p.gc_mode] = segno; break; } -- GitLab From a69f7134a69b15f3f1d8b761c27594b55cee945b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 26 Jan 2016 11:55:35 -0800 Subject: [PATCH 0105/5498] f2fs: use wq_has_sleeper for cp_wait wait_queue We need to use wq_has_sleeper including smp_mb to consider cp_wait concurrency. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 +-- fs/f2fs/f2fs.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b82d33828afa..b3cb58a22e78 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -75,8 +75,7 @@ static void f2fs_write_end_io(struct bio *bio, int err) dec_page_count(sbi, F2FS_WRITEBACK); } - if (!get_pages(sbi, F2FS_WRITEBACK) && - !list_empty(&sbi->cp_wait.task_list)) + if (!get_pages(sbi, F2FS_WRITEBACK) && wq_has_sleeper(&sbi->cp_wait)) wake_up(&sbi->cp_wait); bio_put(bio); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7fa621a9d229..a649a715bf3b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -105,6 +105,27 @@ static inline bool f2fs_crc_valid(__u32 blk_crc, void *buf, size_t buf_size) return f2fs_crc32(buf, buf_size) == blk_crc; } +/** + * wq_has_sleeper - check if there are any waiting processes + * @wq: wait queue head + * + * Returns true if wq has waiting processes + * + * Please refer to the comment for waitqueue_active. + */ +static inline bool wq_has_sleeper(wait_queue_head_t *wq) +{ + /* + * We need to be sure we are in sync with the + * add_wait_queue modifications to the wait queue. + * + * This memory barrier should be paired with one on the + * waiting side. + */ + smp_mb(); + return waitqueue_active(wq); +} + /* * For checkpoint manager */ -- GitLab From 1b1d244a512cb5d4e95f47bb0b251f1e6c4aeab0 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Tue, 26 Jan 2016 12:56:25 +0000 Subject: [PATCH 0106/5498] f2fs: reconstruct the code to free an extent_node There are three steps to free an extent node: 1) list_del_init, 2)__detach_extent_node, 3) kmem_cache_free In path f2fs_destroy_extent_tree, 1->2->3 to free a node, But in path f2fs_update_extent_tree_range, it is 2->1->3. This patch makes all the order to be: 1->2->3 It makes sense, since in the next patch, we import a victim list in the path shrink_extent_tree, we could check if the extent_node is in the victim list by checking the list_empty(). So it is necessary to put 1) first. Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 55 +++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index ccd5c636d3fe..ecb1f99e1fc8 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -50,6 +50,24 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi, if (et->cached_en == en) et->cached_en = NULL; + kmem_cache_free(extent_node_slab, en); +} + +/* + * Flow to release an extent_node: + * 1. list_del_init + * 2. __detach_extent_node + * 3. kmem_cache_free. + */ +static void __release_extent_node(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_node *en) +{ + spin_lock(&sbi->extent_lock); + if (!list_empty(&en->list)) + list_del_init(&en->list); + spin_unlock(&sbi->extent_lock); + + __detach_extent_node(sbi, et, en); } static struct extent_tree *__grab_extent_tree(struct inode *inode) @@ -140,17 +158,10 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, next = rb_next(node); en = rb_entry(node, struct extent_node, rb_node); - if (free_all) { - spin_lock(&sbi->extent_lock); - if (!list_empty(&en->list)) - list_del_init(&en->list); - spin_unlock(&sbi->extent_lock); - } - - if (free_all || list_empty(&en->list)) { + if (free_all) + __release_extent_node(sbi, et, en); + else if (list_empty(&en->list)) __detach_extent_node(sbi, et, en); - kmem_cache_free(extent_node_slab, en); - } node = next; } @@ -329,7 +340,6 @@ lookup_neighbors: static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, struct extent_tree *et, struct extent_info *ei, - struct extent_node **den, struct extent_node *prev_ex, struct extent_node *next_ex) { @@ -342,10 +352,8 @@ static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, } if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) { - if (en) { - __detach_extent_node(sbi, et, prev_ex); - *den = prev_ex; - } + if (en) + __release_extent_node(sbi, et, prev_ex); next_ex->ei.fofs = ei->fofs; next_ex->ei.blk = ei->blk; next_ex->ei.len += ei->len; @@ -479,7 +487,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, if (parts) __try_update_largest_extent(et, en); else - __detach_extent_node(sbi, et, en); + __release_extent_node(sbi, et, en); /* * if original extent is split into zero or two parts, extent @@ -493,26 +501,18 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, /* update in global extent list */ spin_lock(&sbi->extent_lock); - if (!parts && !list_empty(&en->list)) - list_del(&en->list); if (en1) list_add_tail(&en1->list, &sbi->extent_list); spin_unlock(&sbi->extent_lock); - /* release extent node */ - if (!parts) - kmem_cache_free(extent_node_slab, en); - en = next_en; } /* 3. update extent in extent cache */ if (blkaddr) { - struct extent_node *den = NULL; set_extent_info(&ei, fofs, blkaddr, len); - en1 = __try_merge_extent_node(sbi, et, &ei, &den, - prev_en, next_en); + en1 = __try_merge_extent_node(sbi, et, &ei, prev_en, next_en); if (!en1) en1 = __insert_extent_tree(sbi, et, &ei, insert_p, insert_parent); @@ -532,12 +532,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, else list_move_tail(&en1->list, &sbi->extent_list); } - if (den && !list_empty(&den->list)) - list_del(&den->list); spin_unlock(&sbi->extent_lock); - - if (den) - kmem_cache_free(extent_node_slab, den); } if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) -- GitLab From 70598a551c4fa36fd244bb6ef7b719c8d38e951a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 26 Jan 2016 09:12:50 -0800 Subject: [PATCH 0107/5498] f2fs: move extent_node list operations being coupled with rbtree operation This patch moves extent_node list operations to be handled together with its rbtree operations. Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index ecb1f99e1fc8..995a7b1baa70 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -360,10 +360,16 @@ static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, en = next_ex; } - if (en) { - __try_update_largest_extent(et, en); - et->cached_en = en; - } + if (!en) + return NULL; + + __try_update_largest_extent(et, en); + et->cached_en = en; + + spin_lock(&sbi->extent_lock); + if (!list_empty(&en->list)) + list_move_tail(&en->list, &sbi->extent_list); + spin_unlock(&sbi->extent_lock); return en; } @@ -400,6 +406,11 @@ do_insert: __try_update_largest_extent(et, en); et->cached_en = en; + + /* update in global extent list */ + spin_lock(&sbi->extent_lock); + list_add_tail(&en->list, &sbi->extent_list); + spin_unlock(&sbi->extent_lock); return en; } @@ -498,13 +509,6 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, insert_p = NULL; insert_parent = NULL; } - - /* update in global extent list */ - spin_lock(&sbi->extent_lock); - if (en1) - list_add_tail(&en1->list, &sbi->extent_list); - spin_unlock(&sbi->extent_lock); - en = next_en; } @@ -512,9 +516,8 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, if (blkaddr) { set_extent_info(&ei, fofs, blkaddr, len); - en1 = __try_merge_extent_node(sbi, et, &ei, prev_en, next_en); - if (!en1) - en1 = __insert_extent_tree(sbi, et, &ei, + if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en)) + __insert_extent_tree(sbi, et, &ei, insert_p, insert_parent); /* give up extent_cache, if split and small updates happen */ @@ -524,15 +527,6 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, et->largest.len = 0; set_inode_flag(F2FS_I(inode), FI_NO_EXTENT); } - - spin_lock(&sbi->extent_lock); - if (en1) { - if (list_empty(&en1->list)) - list_add_tail(&en1->list, &sbi->extent_list); - else - list_move_tail(&en1->list, &sbi->extent_list); - } - spin_unlock(&sbi->extent_lock); } if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) -- GitLab From e04642d0e6ec14fc25c8277ae10e9358fc0b0d95 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 26 Jan 2016 09:20:05 -0800 Subject: [PATCH 0108/5498] f2fs: don't set cached_en if it will be freed If en has empty list pointer, it will be freed sooner, so we don't need to set cached_en with it. Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 995a7b1baa70..aae99f2a4345 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -243,9 +243,10 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, if (en) { *ei = en->ei; spin_lock(&sbi->extent_lock); - if (!list_empty(&en->list)) + if (!list_empty(&en->list)) { list_move_tail(&en->list, &sbi->extent_list); - et->cached_en = en; + et->cached_en = en; + } spin_unlock(&sbi->extent_lock); ret = true; } @@ -364,11 +365,12 @@ static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, return NULL; __try_update_largest_extent(et, en); - et->cached_en = en; spin_lock(&sbi->extent_lock); - if (!list_empty(&en->list)) + if (!list_empty(&en->list)) { list_move_tail(&en->list, &sbi->extent_list); + et->cached_en = en; + } spin_unlock(&sbi->extent_lock); return en; } @@ -405,11 +407,11 @@ do_insert: return NULL; __try_update_largest_extent(et, en); - et->cached_en = en; /* update in global extent list */ spin_lock(&sbi->extent_lock); list_add_tail(&en->list, &sbi->extent_list); + et->cached_en = en; spin_unlock(&sbi->extent_lock); return en; } -- GitLab From 5c86f3dd2d9a956138237ccce8d4bf482ebfbe47 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Tue, 26 Jan 2016 12:56:26 +0000 Subject: [PATCH 0109/5498] f2fs: improve shrink performance of extent nodes On the worst case, we need to scan the whole radix tree and every rb-tree to free the victimed extent_nodes when shrinking. Pengyang initially introduced a victim_list to record the victimed extent_nodes, and free these extent_nodes by just scanning a list. Later, Chao Yu enhances the original patch to improve memory footprint by removing victim list. The policy of lru list shrinking becomes: 1) lock lru list's lock 2) trylock extent tree's lock 3) remove extent node from lru list 4) unlock lru list's lock 5) do shrink 6) repeat 1) to 5) Signed-off-by: Hou Pengyang Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 76 ++++++++++++++++-------------------------- fs/f2fs/f2fs.h | 1 + 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index aae99f2a4345..53f49f713323 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -33,6 +33,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi, en->ei = *ei; INIT_LIST_HEAD(&en->list); + en->et = et; rb_link_node(&en->rb_node, parent, p); rb_insert_color(&en->rb_node, &et->root); @@ -63,8 +64,8 @@ static void __release_extent_node(struct f2fs_sb_info *sbi, struct extent_tree *et, struct extent_node *en) { spin_lock(&sbi->extent_lock); - if (!list_empty(&en->list)) - list_del_init(&en->list); + f2fs_bug_on(sbi, list_empty(&en->list)); + list_del_init(&en->list); spin_unlock(&sbi->extent_lock); __detach_extent_node(sbi, et, en); @@ -147,7 +148,7 @@ static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi, } static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, - struct extent_tree *et, bool free_all) + struct extent_tree *et) { struct rb_node *node, *next; struct extent_node *en; @@ -157,11 +158,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, while (node) { next = rb_next(node); en = rb_entry(node, struct extent_node, rb_node); - - if (free_all) - __release_extent_node(sbi, et, en); - else if (list_empty(&en->list)) - __detach_extent_node(sbi, et, en); + __release_extent_node(sbi, et, en); node = next; } @@ -532,7 +529,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, } if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) - __free_extent_tree(sbi, et, true); + __free_extent_tree(sbi, et); write_unlock(&et->lock); @@ -541,14 +538,10 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) { - struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; struct extent_tree *et, *next; - struct extent_node *en, *tmp; - unsigned long ino = F2FS_ROOT_INO(sbi); - unsigned int found; + struct extent_node *en; unsigned int node_cnt = 0, tree_cnt = 0; int remained; - bool do_free = false; if (!test_opt(sbi, EXTENT_CACHE)) return 0; @@ -563,10 +556,10 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) list_for_each_entry_safe(et, next, &sbi->zombie_list, list) { if (atomic_read(&et->node_cnt)) { write_lock(&et->lock); - node_cnt += __free_extent_tree(sbi, et, true); + node_cnt += __free_extent_tree(sbi, et); write_unlock(&et->lock); } - + f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); list_del_init(&et->list); radix_tree_delete(&sbi->extent_tree_root, et->ino); kmem_cache_free(extent_tree_slab, et); @@ -587,42 +580,29 @@ free_node: remained = nr_shrink - (node_cnt + tree_cnt); spin_lock(&sbi->extent_lock); - list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) { - if (!remained--) + for (; remained > 0; remained--) { + if (list_empty(&sbi->extent_list)) break; - list_del_init(&en->list); - do_free = true; - } - spin_unlock(&sbi->extent_lock); - - if (do_free == false) - goto unlock_out; - - /* - * reset ino for searching victims from beginning of global extent tree. - */ - ino = F2FS_ROOT_INO(sbi); - - while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root, - (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { - unsigned i; - - ino = treevec[found - 1]->ino + 1; - for (i = 0; i < found; i++) { - struct extent_tree *et = treevec[i]; + en = list_first_entry(&sbi->extent_list, + struct extent_node, list); + et = en->et; + if (!write_trylock(&et->lock)) { + /* refresh this extent node's position in extent list */ + list_move_tail(&en->list, &sbi->extent_list); + continue; + } - if (!atomic_read(&et->node_cnt)) - continue; + list_del_init(&en->list); + spin_unlock(&sbi->extent_lock); - if (write_trylock(&et->lock)) { - node_cnt += __free_extent_tree(sbi, et, false); - write_unlock(&et->lock); - } + __detach_extent_node(sbi, et, en); - if (node_cnt + tree_cnt >= nr_shrink) - goto unlock_out; - } + write_unlock(&et->lock); + node_cnt++; + spin_lock(&sbi->extent_lock); } + spin_unlock(&sbi->extent_lock); + unlock_out: up_write(&sbi->extent_tree_lock); out: @@ -641,7 +621,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode) return 0; write_lock(&et->lock); - node_cnt = __free_extent_tree(sbi, et, true); + node_cnt = __free_extent_tree(sbi, et); write_unlock(&et->lock); return node_cnt; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a649a715bf3b..49d0ab9bfe77 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -374,6 +374,7 @@ struct extent_node { struct rb_node rb_node; /* rb node located in rb-tree */ struct list_head list; /* node in global extent list of sbi */ struct extent_info ei; /* extent info */ + struct extent_tree *et; /* extent tree pointer */ }; struct extent_tree { -- GitLab From e56fa42d09da5224c913016b4179c9c4c63b981e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 20 Jan 2016 07:31:48 +0800 Subject: [PATCH 0110/5498] f2fs: give scheduling point in shrinking path It needs to give a chance to be rescheduled while shrinking slab entries. Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 53f49f713323..365cf950891b 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -569,6 +569,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) if (node_cnt + tree_cnt >= nr_shrink) goto unlock_out; + cond_resched(); } up_write(&sbi->extent_tree_lock); -- GitLab From 56a66d9932aaf6d36337f78b3160418dd60807a4 Mon Sep 17 00:00:00 2001 From: Shuoran Liu Date: Wed, 27 Jan 2016 09:57:30 +0800 Subject: [PATCH 0111/5498] f2fs: introduce lifetime write IO statistics This patch introduces lifetime IO write statistics exposed to the sysfs interface. The write IO amount is obtained from block layer, accumulated in the file system and stored in the hot node summary of checkpoint. Signed-off-by: Shuoran Liu Signed-off-by: Pengyang Hou [Jaegeuk Kim: add sysfs documentation] Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 6 ++++ fs/f2fs/checkpoint.c | 11 +++++++ fs/f2fs/f2fs.h | 11 +++++++ fs/f2fs/super.c | 39 +++++++++++++++++++++++-- include/linux/f2fs_fs.h | 14 ++++++++- 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 523cb9d4e272..a809f6005f14 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -106,3 +106,9 @@ Description: Controls dirty nat entries ratio threshold, if current ratio exceeds configured threshold, checkpoint will be triggered for flushing dirty nat entries. + +What: /sys/fs/f2fs//lifetime_write_kbytes +Date: January 2016 +Contact: "Shuoran Liu" +Description: + Shows total written kbytes issued to disk. diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 7d7e5d97e2f6..40a7c0ca7e20 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -922,6 +922,9 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) int cp_payload_blks = __cp_payload(sbi); block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg); bool invalidate = false; + struct super_block *sb = sbi->sb; + struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); + u64 kbytes_written; /* * This avoids to conduct wrong roll-forward operations and uses @@ -1035,6 +1038,14 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) write_data_summaries(sbi, start_blk); start_blk += data_sum_blocks; + + /* Record write statistics in the hot node summary */ + kbytes_written = sbi->kbytes_written; + if (sb->s_bdev->bd_part) + kbytes_written += BD_PART_WRITTEN(sbi); + + seg_i->sum_blk->info.kbytes_written = cpu_to_le64(kbytes_written); + if (__remain_node_summaries(cpc->reason)) { write_node_summaries(sbi, start_blk); start_blk += NR_CURSEG_NODE_TYPE; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 49d0ab9bfe77..e9ac3acead11 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -866,8 +866,19 @@ struct f2fs_sb_info { struct list_head s_list; struct mutex umount_mutex; unsigned int shrinker_run_no; + + /* For write statistics */ + u64 sectors_written_start; + u64 kbytes_written; }; +/* For write statistics. Suppose sector size is 512 bytes, + * and the return value is in kbytes. s is of struct f2fs_sb_info. + */ +#define BD_PART_WRITTEN(s) \ +(((u64)part_stat_read(s->sb->s_bdev->bd_part, sectors[1]) - \ + s->sectors_written_start) >> 1) + static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type) { sbi->last_time[type] = jiffies; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index be7c40353185..99620219734d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -126,6 +126,19 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) return NULL; } +static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct super_block *sb = sbi->sb; + + if (!sb->s_bdev->bd_part) + return snprintf(buf, PAGE_SIZE, "0\n"); + + return snprintf(buf, PAGE_SIZE, "%llu\n", + (unsigned long long)(sbi->kbytes_written + + BD_PART_WRITTEN(sbi))); +} + static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { @@ -204,6 +217,9 @@ static struct f2fs_attr f2fs_attr_##_name = { \ f2fs_sbi_show, f2fs_sbi_store, \ offsetof(struct struct_name, elname)) +#define F2FS_GENERAL_RO_ATTR(name) \ +static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) + F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); @@ -221,6 +237,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); +F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -241,6 +258,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(dirty_nats_ratio), ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), + ATTR_LIST(lifetime_write_kbytes), NULL, }; @@ -768,8 +786,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) bool need_stop_gc = false; bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE); - sync_filesystem(sb); - /* * Save the old mount options in case we * need to restore them. @@ -777,6 +793,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) org_mount_opt = sbi->mount_opt; active_logs = sbi->active_logs; + if (*flags & MS_RDONLY) { + set_opt(sbi, FASTBOOT); + set_sbi_flag(sbi, SBI_IS_DIRTY); + } + + sync_filesystem(sb); + sbi->mount_opt.opt = 0; default_options(sbi); @@ -1244,6 +1267,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) bool retry = true, need_fsck = false; char *options = NULL; int recovery, i, valid_super_block; + struct curseg_info *seg_i; try_onemore: err = -EINVAL; @@ -1374,6 +1398,17 @@ try_onemore: goto free_nm; } + /* For write statistics */ + if (sb->s_bdev->bd_part) + sbi->sectors_written_start = + (u64)part_stat_read(sb->s_bdev->bd_part, sectors[1]); + + /* Read accumulated write IO statistics if exists */ + seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); + if (__exist_node_summaries(sbi)) + sbi->kbytes_written = + le64_to_cpu(seg_i->sum_blk->info.kbytes_written); + build_gc_manager(sbi); /* get an inode for node space */ diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index e59c3be92106..67aa01df777d 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -358,6 +358,12 @@ struct summary_footer { sizeof(struct sit_journal_entry)) #define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ sizeof(struct sit_journal_entry)) + +/* Reserved area should make size of f2fs_extra_info equals to + * that of nat_journal and sit_journal. + */ +#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8) + /* * frequently updated NAT/SIT entries can be stored in the spare area in * summary blocks @@ -387,6 +393,11 @@ struct sit_journal { __u8 reserved[SIT_JOURNAL_RESERVED]; } __packed; +struct f2fs_extra_info { + __le64 kbytes_written; + __u8 reserved[EXTRA_INFO_RESERVED]; +} __packed; + /* 4KB-sized summary block structure */ struct f2fs_summary_block { struct f2fs_summary entries[ENTRIES_IN_SUM]; @@ -394,10 +405,11 @@ struct f2fs_summary_block { __le16 n_nats; __le16 n_sits; }; - /* spare area is used by NAT or SIT journals */ + /* spare area is used by NAT or SIT journals or extra info */ union { struct nat_journal nat_j; struct sit_journal sit_j; + struct f2fs_extra_info info; }; struct summary_footer footer; } __packed; -- GitLab From c4019029f21b9f8a7beca67420e66ec098ea95ec Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 26 Jan 2016 15:37:38 +0800 Subject: [PATCH 0112/5498] f2fs: simplify f2fs_map_blocks In f2fs_map_blocks, we use duplicated codes to handle first block mapping and the following blocks mapping, it's unnecessary. This patch simplifies f2fs_map_blocks to avoid using copied codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 101 ++++++++++++++++--------------------------------- 1 file changed, 32 insertions(+), 69 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b3cb58a22e78..6b1dbbd8fd3a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -632,6 +632,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, goto out; } +next_dnode: if (create) f2fs_lock_op(sbi); @@ -644,47 +645,57 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, goto unlock_out; } - if (dn.data_blkaddr == NEW_ADDR || dn.data_blkaddr == NULL_ADDR) { + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + +next_block: + blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + + if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { if (create) { if (unlikely(f2fs_cp_error(sbi))) { err = -EIO; - goto put_out; + goto sync_out; } err = __allocate_data_block(&dn); if (err) - goto put_out; + goto sync_out; allocated = true; map->m_flags = F2FS_MAP_NEW; + blkaddr = dn.data_blkaddr; } else { if (flag != F2FS_GET_BLOCK_FIEMAP || - dn.data_blkaddr != NEW_ADDR) { + blkaddr != NEW_ADDR) { if (flag == F2FS_GET_BLOCK_BMAP) err = -ENOENT; - goto put_out; + goto sync_out; } - - /* - * preallocated unwritten block should be mapped - * for fiemap. - */ - if (dn.data_blkaddr == NEW_ADDR) - map->m_flags = F2FS_MAP_UNWRITTEN; } } - map->m_flags |= F2FS_MAP_MAPPED; - map->m_pblk = dn.data_blkaddr; - map->m_len = 1; + if (map->m_len == 0) { + /* preallocated unwritten block should be mapped for fiemap. */ + if (blkaddr == NEW_ADDR) + map->m_flags |= F2FS_MAP_UNWRITTEN; + map->m_flags |= F2FS_MAP_MAPPED; + + map->m_pblk = blkaddr; + map->m_len = 1; + } else if ((map->m_pblk != NEW_ADDR && + blkaddr == (map->m_pblk + ofs)) || + (map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR)) { + ofs++; + map->m_len++; + } else { + goto sync_out; + } - end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); dn.ofs_in_node++; pgofs++; -get_next: - if (map->m_len >= maxblocks) - goto sync_out; + if (map->m_len < maxblocks) { + if (dn.ofs_in_node < end_offset) + goto next_block; - if (dn.ofs_in_node >= end_offset) { if (allocated) sync_inode_page(&dn); f2fs_put_dnode(&dn); @@ -692,62 +703,14 @@ get_next: if (create) { f2fs_unlock_op(sbi); f2fs_balance_fs(sbi, allocated); - f2fs_lock_op(sbi); } allocated = false; - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, pgofs, mode); - if (err) { - if (err == -ENOENT) - err = 0; - goto unlock_out; - } - - end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); - } - - blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); - - if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { - if (create) { - if (unlikely(f2fs_cp_error(sbi))) { - err = -EIO; - goto sync_out; - } - err = __allocate_data_block(&dn); - if (err) - goto sync_out; - allocated = true; - map->m_flags |= F2FS_MAP_NEW; - blkaddr = dn.data_blkaddr; - } else { - /* - * we only merge preallocated unwritten blocks - * for fiemap. - */ - if (flag != F2FS_GET_BLOCK_FIEMAP || - blkaddr != NEW_ADDR) - goto sync_out; - } - } - - /* Give more consecutive addresses for the readahead */ - if ((map->m_pblk != NEW_ADDR && - blkaddr == (map->m_pblk + ofs)) || - (map->m_pblk == NEW_ADDR && - blkaddr == NEW_ADDR)) { - ofs++; - dn.ofs_in_node++; - pgofs++; - map->m_len++; - goto get_next; + goto next_dnode; } sync_out: if (allocated) sync_inode_page(&dn); -put_out: f2fs_put_dnode(&dn); unlock_out: if (create) { -- GitLab From 6d6ac3c66ccdcbb83b9e19858e0b3073c53d357c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 26 Jan 2016 15:38:29 +0800 Subject: [PATCH 0113/5498] f2fs: simplify __allocate_data_blocks This patch uses existing function f2fs_map_block to simplify implementation of __allocate_data_blocks. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 60 ++++---------------------------------------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6b1dbbd8fd3a..5b88e00c6731 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -537,64 +537,12 @@ alloc: static int __allocate_data_blocks(struct inode *inode, loff_t offset, size_t count) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct dnode_of_data dn; - u64 start = F2FS_BYTES_TO_BLK(offset); - u64 len = F2FS_BYTES_TO_BLK(count); - bool allocated = false; - u64 end_offset; - int err = 0; - - while (len) { - f2fs_lock_op(sbi); - - /* When reading holes, we need its node page */ - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, start, ALLOC_NODE); - if (err) - goto out; - - allocated = false; - end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); - - while (dn.ofs_in_node < end_offset && len) { - block_t blkaddr; - - if (unlikely(f2fs_cp_error(sbi))) { - err = -EIO; - goto sync_out; - } - - blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); - if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) { - err = __allocate_data_block(&dn); - if (err) - goto sync_out; - allocated = true; - } - len--; - start++; - dn.ofs_in_node++; - } - - if (allocated) - sync_inode_page(&dn); - - f2fs_put_dnode(&dn); - f2fs_unlock_op(sbi); + struct f2fs_map_blocks map; - f2fs_balance_fs(sbi, allocated); - } - return err; + map.m_lblk = F2FS_BYTES_TO_BLK(offset); + map.m_len = F2FS_BYTES_TO_BLK(count); -sync_out: - if (allocated) - sync_inode_page(&dn); - f2fs_put_dnode(&dn); -out: - f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, allocated); - return err; + return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_DIO); } /* -- GitLab From de7c346226768892f58f218d78e5ed1358aba422 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 26 Jan 2016 15:39:35 +0800 Subject: [PATCH 0114/5498] f2fs: remove unneeded pointer conversion There are redundant pointer conversion in following call stack: - at position a, inode was been converted to f2fs_file_info. - at position b, f2fs_file_info was been converted to inode again. - truncate_blocks(inode,..) - fi = F2FS_I(inode) ---a - ADDRS_PER_PAGE(node_page, fi) - addrs_per_inode(fi) - inode = &fi->vfs_inode ---b - f2fs_has_inline_xattr(inode) - fi = F2FS_I(inode) - is_inode_flag_set(fi,..) In order to avoid unneeded conversion, alter ADDRS_PER_PAGE and addrs_per_inode to acept parameter with type of inode pointer. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 ++--- fs/f2fs/extent_cache.c | 5 ++--- fs/f2fs/f2fs.h | 14 +++++++------- fs/f2fs/file.c | 11 +++++------ fs/f2fs/gc.c | 8 ++++---- fs/f2fs/node.c | 8 ++++---- fs/f2fs/recovery.c | 10 ++++------ include/linux/f2fs_fs.h | 6 +++--- 8 files changed, 31 insertions(+), 36 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5b88e00c6731..c3cf6b95a91f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -498,7 +498,6 @@ got_it: static int __allocate_data_block(struct dnode_of_data *dn) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); - struct f2fs_inode_info *fi = F2FS_I(dn->inode); struct f2fs_summary sum; struct node_info ni; int seg = CURSEG_WARM_DATA; @@ -526,7 +525,7 @@ alloc: set_data_blkaddr(dn); /* update i_size */ - fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) + dn->ofs_in_node; if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)) i_size_write(dn->inode, @@ -593,7 +592,7 @@ next_dnode: goto unlock_out; } - end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + end_offset = ADDRS_PER_PAGE(dn.node_page, inode); next_block: blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 365cf950891b..071a1b19e5af 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -673,7 +673,6 @@ bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs, void f2fs_update_extent_cache(struct dnode_of_data *dn) { - struct f2fs_inode_info *fi = F2FS_I(dn->inode); pgoff_t fofs; if (!f2fs_may_extent_tree(dn->inode)) @@ -682,8 +681,8 @@ void f2fs_update_extent_cache(struct dnode_of_data *dn) f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR); - fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + - dn->ofs_in_node; + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) + + dn->ofs_in_node; if (f2fs_update_extent_tree_range(dn->inode, fofs, dn->data_blkaddr, 1)) sync_inode_page(dn); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e9ac3acead11..124fd56ea9f5 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1558,9 +1558,9 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) return is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR); } -static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi) +static inline unsigned int addrs_per_inode(struct inode *inode) { - if (f2fs_has_inline_xattr(&fi->vfs_inode)) + if (f2fs_has_inline_xattr(inode)) return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS; return DEF_ADDRS_PER_INODE; } @@ -1716,10 +1716,10 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags) (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) /* get offset of first page in next direct node */ -#define PGOFS_OF_NEXT_DNODE(pgofs, fi) \ - ((pgofs < ADDRS_PER_INODE(fi)) ? ADDRS_PER_INODE(fi) : \ - (pgofs - ADDRS_PER_INODE(fi) + ADDRS_PER_BLOCK) / \ - ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi)) +#define PGOFS_OF_NEXT_DNODE(pgofs, inode) \ + ((pgofs < ADDRS_PER_INODE(inode)) ? ADDRS_PER_INODE(inode) : \ + (pgofs - ADDRS_PER_INODE(inode) + ADDRS_PER_BLOCK) / \ + ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode)) /* * file.c @@ -1937,7 +1937,7 @@ int f2fs_release_page(struct page *, gfp_t); */ int start_gc_thread(struct f2fs_sb_info *); void stop_gc_thread(struct f2fs_sb_info *); -block_t start_bidx_of_node(unsigned int, struct f2fs_inode_info *); +block_t start_bidx_of_node(unsigned int, struct inode *); int f2fs_gc(struct f2fs_sb_info *, bool); void build_gc_manager(struct f2fs_sb_info *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 43164b05b6ce..e85b20bfd011 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -359,15 +359,14 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) } else if (err == -ENOENT) { /* direct node does not exists */ if (whence == SEEK_DATA) { - pgofs = PGOFS_OF_NEXT_DNODE(pgofs, - F2FS_I(inode)); + pgofs = PGOFS_OF_NEXT_DNODE(pgofs, inode); continue; } else { goto found; } } - end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + end_offset = ADDRS_PER_PAGE(dn.node_page, inode); /* find data/hole in dnode block */ for (; dn.ofs_in_node < end_offset; @@ -481,7 +480,7 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) * we will invalidate all blkaddr in the whole range. */ fofs = start_bidx_of_node(ofs_of_node(dn->node_page), - F2FS_I(dn->inode)) + ofs; + dn->inode) + ofs; f2fs_update_extent_cache_range(dn, fofs, 0, len); dec_valid_block_count(sbi, dn->inode, nr_free); sync_inode_page(dn); @@ -569,7 +568,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) goto out; } - count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + count = ADDRS_PER_PAGE(dn.node_page, inode); count -= dn.ofs_in_node; f2fs_bug_on(sbi, count < 0); @@ -769,7 +768,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) return err; } - end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + end_offset = ADDRS_PER_PAGE(dn.node_page, inode); count = min(end_offset - dn.ofs_in_node, pg_end - pg_start); f2fs_bug_on(F2FS_I_SB(inode), count == 0 || count > end_offset); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 645273c8b341..47ade3542fbd 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -486,7 +486,7 @@ next_step: * as indirect or double indirect node blocks, are given, it must be a caller's * bug. */ -block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi) +block_t start_bidx_of_node(unsigned int node_ofs, struct inode *inode) { unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4; unsigned int bidx; @@ -503,7 +503,7 @@ block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi) int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1); bidx = node_ofs - 5 - dec; } - return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi); + return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode); } static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, @@ -722,7 +722,7 @@ next_step: continue; } - start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + start_bidx = start_bidx_of_node(nofs, inode); data_page = get_read_data_page(inode, start_bidx + ofs_in_node, READA, true); if (IS_ERR(data_page)) { @@ -738,7 +738,7 @@ next_step: /* phase 3 */ inode = find_gc_inode(gc_list, dni.ino); if (inode) { - start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)) + start_bidx = start_bidx_of_node(nofs, inode) + ofs_in_node; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) move_encrypted_block(inode, start_bidx); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2763ce12e3f2..d4023c0ed1c5 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -407,10 +407,10 @@ cache: * The maximum depth is four. * Offset[0] will have raw inode offset. */ -static int get_node_path(struct f2fs_inode_info *fi, long block, +static int get_node_path(struct inode *inode, long block, int offset[4], unsigned int noffset[4]) { - const long direct_index = ADDRS_PER_INODE(fi); + const long direct_index = ADDRS_PER_INODE(inode); const long direct_blks = ADDRS_PER_BLOCK; const long dptrs_per_blk = NIDS_PER_BLOCK; const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; @@ -498,7 +498,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) int level, i; int err = 0; - level = get_node_path(F2FS_I(dn->inode), index, offset, noffset); + level = get_node_path(dn->inode, index, offset, noffset); nids[0] = dn->inode->i_ino; npage[0] = dn->inode_page; @@ -792,7 +792,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from) trace_f2fs_truncate_inode_blocks_enter(inode, from); - level = get_node_path(F2FS_I(inode), from, offset, noffset); + level = get_node_path(inode, from, offset, noffset); restart: page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) { diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 47324cee685f..cd100f09ad58 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -350,8 +350,7 @@ got_it: inode = dn->inode; } - bidx = start_bidx_of_node(offset, F2FS_I(inode)) + - le16_to_cpu(sum.ofs_in_node); + bidx = start_bidx_of_node(offset, inode) + le16_to_cpu(sum.ofs_in_node); /* * if inode page is locked, unlock temporarily, but its reference @@ -386,10 +385,9 @@ truncate_out: static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, struct page *page, block_t blkaddr) { - struct f2fs_inode_info *fi = F2FS_I(inode); - unsigned int start, end; struct dnode_of_data dn; struct node_info ni; + unsigned int start, end; int err = 0, recovered = 0; /* step 1: recover xattr */ @@ -409,8 +407,8 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, goto out; /* step 3: recover data indices */ - start = start_bidx_of_node(ofs_of_node(page), fi); - end = start + ADDRS_PER_PAGE(page, fi); + start = start_bidx_of_node(ofs_of_node(page), inode); + end = start + ADDRS_PER_PAGE(page, inode); set_new_dnode(&dn, inode, NULL, NULL, 0); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 67aa01df777d..44ae822e154f 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -170,12 +170,12 @@ struct f2fs_extent { #define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ -#define ADDRS_PER_INODE(fi) addrs_per_inode(fi) +#define ADDRS_PER_INODE(inode) addrs_per_inode(inode) #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ -#define ADDRS_PER_PAGE(page, fi) \ - (IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK) +#define ADDRS_PER_PAGE(page, inode) \ + (IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK) #define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) #define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) -- GitLab From 86e0fa243a9d5dbe08a00c538e8fa404a4c77144 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 26 Jan 2016 15:40:44 +0800 Subject: [PATCH 0115/5498] f2fs: introduce get_next_page_offset to speed up SEEK_DATA When seeking data in ->llseek, if we encounter a big hole which covers several dnode pages, we will try to seek data from index of page which is the first page of next dnode page, at most we could skip searching (ADDRS_PER_BLOCK - 1) pages. However it's still not efficient, because if our indirect/double-indirect pointer are NULL, there are no dnode page locate in the tree indirect/ double-indirect pointer point to, it's not necessary to search the whole region. This patch introduces get_next_page_offset to calculate next page offset based on current searching level and max searching level returned from get_dnode_of_data, with this, we could skip searching the entire area indirect or double-indirect node block is not exist. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +++ fs/f2fs/file.c | 2 +- fs/f2fs/node.c | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 124fd56ea9f5..7ee46b5c61ff 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -571,6 +571,8 @@ struct dnode_of_data { unsigned int ofs_in_node; /* data offset in the node page */ bool inode_page_locked; /* inode page is locked or not */ bool node_changed; /* is node block changed */ + char cur_level; /* level of hole node page */ + char max_level; /* level of current page located */ block_t data_blkaddr; /* block address of the node block */ }; @@ -1814,6 +1816,7 @@ int need_dentry_mark(struct f2fs_sb_info *, nid_t); bool is_checkpointed_node(struct f2fs_sb_info *, nid_t); bool need_inode_block_update(struct f2fs_sb_info *, nid_t); void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); +pgoff_t get_next_page_offset(struct dnode_of_data *, pgoff_t); int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); int truncate_inode_blocks(struct inode *, pgoff_t); int truncate_xattr_node(struct inode *, struct page *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e85b20bfd011..56068701e879 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -359,7 +359,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) } else if (err == -ENOENT) { /* direct node does not exists */ if (whence == SEEK_DATA) { - pgofs = PGOFS_OF_NEXT_DNODE(pgofs, inode); + pgofs = get_next_page_offset(&dn, pgofs); continue; } else { goto found; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d4023c0ed1c5..09b32c9361d9 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -403,6 +403,37 @@ cache: up_write(&nm_i->nat_tree_lock); } +pgoff_t get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs) +{ + const long direct_index = ADDRS_PER_INODE(dn->inode); + const long direct_blks = ADDRS_PER_BLOCK; + const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; + unsigned int skipped_unit = ADDRS_PER_BLOCK; + int cur_level = dn->cur_level; + int max_level = dn->max_level; + pgoff_t base = 0; + + if (!dn->max_level) + return pgofs + 1; + + while (max_level-- > cur_level) + skipped_unit *= NIDS_PER_BLOCK; + + switch (dn->max_level) { + case 3: + base += 2 * indirect_blks; + case 2: + base += 2 * direct_blks; + case 1: + base += direct_index; + break; + default: + f2fs_bug_on(F2FS_I_SB(dn->inode), 1); + } + + return ((pgofs - base) / skipped_unit + 1) * skipped_unit + base; +} + /* * The maximum depth is four. * Offset[0] will have raw inode offset. @@ -495,7 +526,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) int offset[4]; unsigned int noffset[4]; nid_t nids[4]; - int level, i; + int level, i = 0; int err = 0; level = get_node_path(dn->inode, index, offset, noffset); @@ -585,6 +616,10 @@ release_pages: release_out: dn->inode_page = NULL; dn->node_page = NULL; + if (err == -ENOENT) { + dn->cur_level = i; + dn->max_level = level; + } return err; } -- GitLab From f75c239d58fa6b89a6e9761018aac1964c631141 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 26 Jan 2016 15:42:58 +0800 Subject: [PATCH 0116/5498] f2fs: speed up handling holes in fiemap This patch makes f2fs_map_blocks supporting returning next potential page offset which skips hole region in indirect tree of inode, and use it to speed up fiemap in handling big hole case. Test method: xfs_io -f /mnt/f2fs/file -c "pwrite 1099511627776 4096" time xfs_io -f /mnt/f2fs/file -c "fiemap -v" Before: time xfs_io -f /mnt/f2fs/file -c "fiemap -v" /mnt/f2fs/file: EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS 0: [0..2147483647]: hole 2147483648 1: [2147483648..2147483655]: 81920..81927 8 0x1 real 3m3.518s user 0m0.000s sys 3m3.456s After: time xfs_io -f /mnt/f2fs/file -c "fiemap -v" /mnt/f2fs/file: EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS 0: [0..2147483647]: hole 2147483648 1: [2147483648..2147483655]: 81920..81927 8 0x1 real 0m0.008s user 0m0.000s sys 0m0.008s Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 35 ++++++++++++++++++++++++++--------- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c3cf6b95a91f..cdf70b94da15 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -540,6 +540,7 @@ static int __allocate_data_blocks(struct inode *inode, loff_t offset, map.m_lblk = F2FS_BYTES_TO_BLK(offset); map.m_len = F2FS_BYTES_TO_BLK(count); + map.m_next_pgofs = NULL; return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_DIO); } @@ -587,8 +588,12 @@ next_dnode: set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, pgofs, mode); if (err) { - if (err == -ENOENT) + if (err == -ENOENT) { err = 0; + if (map->m_next_pgofs) + *map->m_next_pgofs = + get_next_page_offset(&dn, pgofs); + } goto unlock_out; } @@ -610,6 +615,11 @@ next_block: map->m_flags = F2FS_MAP_NEW; blkaddr = dn.data_blkaddr; } else { + if (flag == F2FS_GET_BLOCK_FIEMAP && + blkaddr == NULL_ADDR) { + if (map->m_next_pgofs) + *map->m_next_pgofs = pgofs + 1; + } if (flag != F2FS_GET_BLOCK_FIEMAP || blkaddr != NEW_ADDR) { if (flag == F2FS_GET_BLOCK_BMAP) @@ -670,13 +680,15 @@ out: } static int __get_data_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh, int create, int flag) + struct buffer_head *bh, int create, int flag, + pgoff_t *next_pgofs) { struct f2fs_map_blocks map; int ret; map.m_lblk = iblock; map.m_len = bh->b_size >> inode->i_blkbits; + map.m_next_pgofs = next_pgofs; ret = f2fs_map_blocks(inode, &map, create, flag); if (!ret) { @@ -688,16 +700,18 @@ static int __get_data_block(struct inode *inode, sector_t iblock, } static int get_data_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create, int flag) + struct buffer_head *bh_result, int create, int flag, + pgoff_t *next_pgofs) { - return __get_data_block(inode, iblock, bh_result, create, flag); + return __get_data_block(inode, iblock, bh_result, create, + flag, next_pgofs); } static int get_data_block_dio(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_DIO); + F2FS_GET_BLOCK_DIO, NULL); } static int get_data_block_bmap(struct inode *inode, sector_t iblock, @@ -708,7 +722,7 @@ static int get_data_block_bmap(struct inode *inode, sector_t iblock, return -EFBIG; return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_BMAP); + F2FS_GET_BLOCK_BMAP, NULL); } static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) @@ -726,6 +740,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, { struct buffer_head map_bh; sector_t start_blk, last_blk; + pgoff_t next_pgofs; loff_t isize; u64 logical = 0, phys = 0, size = 0; u32 flags = 0; @@ -761,14 +776,15 @@ next: map_bh.b_size = len; ret = get_data_block(inode, start_blk, &map_bh, 0, - F2FS_GET_BLOCK_FIEMAP); + F2FS_GET_BLOCK_FIEMAP, &next_pgofs); if (ret) goto out; /* HOLE */ if (!buffer_mapped(&map_bh)) { + start_blk = next_pgofs; /* Go through holes util pass the EOF */ - if (blk_to_logical(inode, start_blk++) < isize) + if (blk_to_logical(inode, start_blk) < isize) goto prep_next; /* Found a hole beyond isize means no more extents. * Note that the premise is that filesystems don't @@ -836,6 +852,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_lblk = 0; map.m_len = 0; map.m_flags = 0; + map.m_next_pgofs = NULL; for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { @@ -874,7 +891,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_len = last_block - block_in_file; if (f2fs_map_blocks(inode, &map, 0, - F2FS_GET_BLOCK_READ)) + F2FS_GET_BLOCK_READ)) goto set_error_page; } got_it: diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7ee46b5c61ff..e133b8a45fbb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -403,6 +403,7 @@ struct f2fs_map_blocks { block_t m_lblk; unsigned int m_len; unsigned int m_flags; + pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */ }; /* for flag in get_data_block */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 56068701e879..77aadec0e70e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1650,7 +1650,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, struct f2fs_defragment *range) { struct inode *inode = file_inode(filp); - struct f2fs_map_blocks map; + struct f2fs_map_blocks map = { .m_next_pgofs = NULL }; struct extent_info ei; pgoff_t pg_start, pg_end; unsigned int blk_per_seg = sbi->blocks_per_seg; -- GitLab From 8df86457af2a4027b4dc762d6ee3d8f41a748628 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Thu, 28 Jan 2016 11:40:26 +0000 Subject: [PATCH 0117/5498] f2fs: fix endianness of on-disk summary_footer Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- include/linux/f2fs_fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 44ae822e154f..ac8040278f69 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -345,7 +345,7 @@ struct f2fs_summary { struct summary_footer { unsigned char entry_type; /* SUM_TYPE_XXX */ - __u32 check_sum; /* summary checksum */ + __le32 check_sum; /* summary checksum */ } __packed; #define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\ -- GitLab From 4405fa179fe752fad86cabdc48ca8dbb74f285ac Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 28 Jan 2016 11:48:52 -0800 Subject: [PATCH 0118/5498] f2fs: wait on page's writeback in writepages path Likewise f2fs_write_cache_pages, let's do for node and meta pages too. Especially, for node blocks, we should do this before marking its fsync and dentry flags. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 +++- fs/f2fs/node.c | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 40a7c0ca7e20..5269ee1610a2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -233,7 +233,6 @@ static int f2fs_write_meta_page(struct page *page, if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - f2fs_wait_on_page_writeback(page, META, true); write_meta_page(sbi, page); dec_page_count(sbi, F2FS_DIRTY_META); unlock_page(page); @@ -316,6 +315,9 @@ continue_unlock: goto continue_unlock; } + f2fs_wait_on_page_writeback(page, META, true); + + BUG_ON(PageWriteback(page)); if (!clear_page_dirty_for_io(page)) goto continue_unlock; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 09b32c9361d9..d0b8ffe1b67d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1301,6 +1301,9 @@ continue_unlock: continue; } + f2fs_wait_on_page_writeback(page, NODE, true); + + BUG_ON(PageWriteback(page)); if (!clear_page_dirty_for_io(page)) goto continue_unlock; @@ -1406,8 +1409,6 @@ static int f2fs_write_node_page(struct page *page, if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - f2fs_wait_on_page_writeback(page, NODE, true); - /* get old block addr of this node page */ nid = nid_of_node(page); f2fs_bug_on(sbi, page->index != nid); -- GitLab From 73254b957d3bc0a12e7d2319f1d24ed949ee1f69 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Jan 2016 08:57:59 -0800 Subject: [PATCH 0119/5498] f2fs: flush bios to handle cp_error in put_super Sometimes, if cp_error is set, there remains under-writeback pages, resulting in kernel hang in put_super. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 99620219734d..aa2d3d9bf12e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -582,6 +582,13 @@ static void f2fs_put_super(struct super_block *sb) f2fs_leave_shrinker(sbi); mutex_unlock(&sbi->umount_mutex); + /* our cp_error case, we can wait for any writeback page */ + if (get_pages(sbi, F2FS_WRITEBACK)) { + f2fs_submit_merged_bio(sbi, DATA, WRITE); + f2fs_submit_merged_bio(sbi, NODE, WRITE); + f2fs_submit_merged_bio(sbi, META, WRITE); + } + iput(sbi->node_inode); iput(sbi->meta_inode); -- GitLab From 1727172f6dbbe2e743f2bcedfb5831824bfcee23 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Jan 2016 16:21:15 -0800 Subject: [PATCH 0120/5498] f2fs: fix conflict on page->private usage This patch fixes confilct on page->private value between f2fs_trace_pid and atomic page. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 3bbeca13f70d..0ba281baa424 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -182,7 +182,7 @@ struct segment_allocation { * this value is set in page as a private data which indicate that * the page is atomically written, and it is in inmem_pages list. */ -#define ATOMIC_WRITTEN_PAGE 0x0000ffff +#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1) #define IS_ATOMIC_WRITTEN_PAGE(page) \ (page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE) -- GitLab From e338de48e77c9bdbf30533696e57345b0e8beb20 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 18 Jan 2016 18:28:11 +0800 Subject: [PATCH 0121/5498] f2fs: introduce f2fs_submit_merged_bio_cond f2fs use single bio buffer per type data (META/NODE/DATA) for caching writes locating in continuous block address as many as possible, after submitting, these writes may be still cached in bio buffer, so we have to flush cached writes in bio buffer by calling f2fs_submit_merged_bio. Unfortunately, in the scenario of high concurrency, bio buffer could be flushed by someone else before we submit it as below reasons: a) there is no space in bio buffer. b) add a request of different type (SYNC, ASYNC). c) add a discontinuous block address. For this condition, f2fs_submit_merged_bio will be devastating, because it could break the following merging of writes in bio buffer, split one big bio into two smaller one. This patch introduces f2fs_submit_merged_bio_cond which can do a conditional submitting with bio buffer, before submitting it will judge whether: - page in DATA type bio buffer is matching with specified page; - page in DATA type bio buffer is belong to specified inode; - page in NODE type bio buffer is belong to specified inode; If there is no eligible page in bio buffer, we will skip submitting step, result in gaining more chance to merge consecutive block IOs in bio cache. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 ++++- fs/f2fs/data.c | 74 +++++++++++++++++++++++++++++++++----------- fs/f2fs/f2fs.h | 3 +- fs/f2fs/node.c | 15 +++++++-- fs/f2fs/segment.c | 6 ++-- 5 files changed, 79 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 5269ee1610a2..d17d75963b88 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -235,10 +235,15 @@ static int f2fs_write_meta_page(struct page *page, write_meta_page(sbi, page); dec_page_count(sbi, F2FS_DIRTY_META); + + if (wbc->for_reclaim) + f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, META, WRITE); + unlock_page(page); - if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) f2fs_submit_merged_bio(sbi, META, WRITE); + return 0; redirty_out: diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index cdf70b94da15..a70714a42f73 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -115,20 +115,18 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) io->bio = NULL; } -bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page, - enum page_type type) +static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, + struct page *page, nid_t ino) { - enum page_type btype = PAGE_TYPE_OF_BIO(type); - struct f2fs_bio_info *io = &sbi->write_io[btype]; struct bio_vec *bvec; struct page *target; int i; - down_read(&io->io_rwsem); - if (!io->bio) { - up_read(&io->io_rwsem); + if (!io->bio) return false; - } + + if (!inode && !page && !ino) + return true; bio_for_each_segment_all(bvec, io->bio, i) { @@ -143,18 +141,34 @@ bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page, target = ctx->w.control_page; } - if (page == target) { - up_read(&io->io_rwsem); + if (inode && inode == target->mapping->host) + return true; + if (page && page == target) + return true; + if (ino && ino == ino_of_node(target)) return true; - } } - up_read(&io->io_rwsem); return false; } -void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, - enum page_type type, int rw) +static bool has_merged_page(struct f2fs_sb_info *sbi, struct inode *inode, + struct page *page, nid_t ino, + enum page_type type) +{ + enum page_type btype = PAGE_TYPE_OF_BIO(type); + struct f2fs_bio_info *io = &sbi->write_io[btype]; + bool ret; + + down_read(&io->io_rwsem); + ret = __has_merged_page(io, inode, page, ino); + up_read(&io->io_rwsem); + return ret; +} + +static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, + struct inode *inode, struct page *page, + nid_t ino, enum page_type type, int rw) { enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io; @@ -163,6 +177,9 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, down_write(&io->io_rwsem); + if (!__has_merged_page(io, inode, page, ino)) + goto out; + /* change META to META_FLUSH in the checkpoint procedure */ if (type >= META_FLUSH) { io->fio.type = META_FLUSH; @@ -172,9 +189,24 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO; } __submit_merged_bio(io); +out: up_write(&io->io_rwsem); } +void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type, + int rw) +{ + __f2fs_submit_merged_bio(sbi, NULL, NULL, 0, type, rw); +} + +void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi, + struct inode *inode, struct page *page, + nid_t ino, enum page_type type, int rw) +{ + if (has_merged_page(sbi, inode, page, ino, type)) + __f2fs_submit_merged_bio(sbi, inode, page, ino, type, rw); +} + /* * Fill the locked page with data located in the block address. * Return unlocked page. @@ -1141,12 +1173,18 @@ out: inode_dec_dirty_pages(inode); if (err) ClearPageUptodate(page); + + if (wbc->for_reclaim) { + f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, DATA, WRITE); + remove_dirty_inode(inode); + } + unlock_page(page); f2fs_balance_fs(sbi, need_balance_fs); - if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) { + + if (unlikely(f2fs_cp_error(sbi))) f2fs_submit_merged_bio(sbi, DATA, WRITE); - remove_dirty_inode(inode); - } + return 0; redirty_out: @@ -1334,7 +1372,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, locked = true; } ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); - f2fs_submit_merged_bio(sbi, DATA, WRITE); + f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE); if (locked) mutex_unlock(&sbi->writepages); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e133b8a45fbb..ce6ab34e4953 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1918,8 +1918,9 @@ void destroy_checkpoint_caches(void); /* * data.c */ -bool is_merged_page(struct f2fs_sb_info *, struct page *, enum page_type); void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); +void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *, + struct page *, nid_t, enum page_type, int); int f2fs_submit_page_bio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *); void set_data_blkaddr(struct dnode_of_data *); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d0b8ffe1b67d..242a79cf1d91 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1341,8 +1341,13 @@ continue_unlock: goto next_step; } - if (wrote) - f2fs_submit_merged_bio(sbi, NODE, WRITE); + if (wrote) { + if (ino) + f2fs_submit_merged_bio_cond(sbi, NULL, NULL, + ino, NODE, WRITE); + else + f2fs_submit_merged_bio(sbi, NODE, WRITE); + } return nwritten; } @@ -1437,9 +1442,13 @@ static int f2fs_write_node_page(struct page *page, set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page)); dec_page_count(sbi, F2FS_DIRTY_NODES); up_read(&sbi->node_write); + + if (wbc->for_reclaim) + f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE); + unlock_page(page); - if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) f2fs_submit_merged_bio(sbi, NODE, WRITE); return 0; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7cd07d54be4e..57a5f7bb275a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -254,7 +254,8 @@ int commit_inmem_pages(struct inode *inode, bool abort) if (!abort) { f2fs_unlock_op(sbi); if (submit_bio) - f2fs_submit_merged_bio(sbi, DATA, WRITE); + f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, + DATA, WRITE); } return err; } @@ -1422,8 +1423,7 @@ void f2fs_wait_on_page_writeback(struct page *page, if (PageWriteback(page)) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); - if (is_merged_page(sbi, page, type)) - f2fs_submit_merged_bio(sbi, type, WRITE); + f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, type, WRITE); if (ordered) wait_on_page_writeback(page); else -- GitLab From ba34e0a49be59f540b303634b6dd531e4ff28a25 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 4 Feb 2016 16:14:00 +0800 Subject: [PATCH 0122/5498] f2fs: fix missing skip pages info fix missing skip pages info in f2fs_writepages trace event. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 5 +++-- fs/f2fs/data.c | 5 +++-- fs/f2fs/node.c | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index d17d75963b88..669f64f6a4d3 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -257,13 +257,13 @@ static int f2fs_write_meta_pages(struct address_space *mapping, struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); long diff, written; - trace_f2fs_writepages(mapping->host, wbc, META); - /* collect a number of dirty meta pages and write together */ if (wbc->for_kupdate || get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META)) goto skip_write; + trace_f2fs_writepages(mapping->host, wbc, META); + /* if mounting is failed, skip writing node pages */ mutex_lock(&sbi->cp_mutex); diff = nr_pages_to_write(sbi, META, wbc); @@ -274,6 +274,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping, skip_write: wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META); + trace_f2fs_writepages(mapping->host, wbc, META); return 0; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a70714a42f73..77e189dc5d00 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1342,8 +1342,6 @@ static int f2fs_write_data_pages(struct address_space *mapping, int ret; long diff; - trace_f2fs_writepages(mapping->host, wbc, DATA); - /* deal with chardevs and other special file */ if (!mapping->a_ops->writepage) return 0; @@ -1365,6 +1363,8 @@ static int f2fs_write_data_pages(struct address_space *mapping, if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto skip_write; + trace_f2fs_writepages(mapping->host, wbc, DATA); + diff = nr_pages_to_write(sbi, DATA, wbc); if (!S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_ALL) { @@ -1383,6 +1383,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, skip_write: wbc->pages_skipped += get_dirty_pages(inode); + trace_f2fs_writepages(mapping->host, wbc, DATA); return 0; } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 242a79cf1d91..0bbc5707d80b 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1464,8 +1464,6 @@ static int f2fs_write_node_pages(struct address_space *mapping, struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); long diff; - trace_f2fs_writepages(mapping->host, wbc, NODE); - /* balancing f2fs's metadata in background */ f2fs_balance_fs_bg(sbi); @@ -1473,6 +1471,8 @@ static int f2fs_write_node_pages(struct address_space *mapping, if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE)) goto skip_write; + trace_f2fs_writepages(mapping->host, wbc, NODE); + diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; sync_node_pages(sbi, 0, wbc); @@ -1481,6 +1481,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, skip_write: wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES); + trace_f2fs_writepages(mapping->host, wbc, NODE); return 0; } -- GitLab From 13bd54b142d5aab539ed7828fc35cb5088fcf23b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 3 Feb 2016 13:09:09 -0800 Subject: [PATCH 0123/5498] f2fs: move dio preallocation into f2fs_file_write_iter This patch moves preallocation code for direct IOs into f2fs_file_write_iter. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 35 +++++++++++++++-------------------- fs/f2fs/f2fs.h | 12 ++++++++++++ fs/f2fs/file.c | 25 +++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 77e189dc5d00..20a1a9d20faa 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -565,16 +565,23 @@ alloc: return 0; } -static int __allocate_data_blocks(struct inode *inode, loff_t offset, - size_t count) +ssize_t f2fs_preallocate_blocks(struct inode *inode, loff_t pos, + size_t count, bool dio) { struct f2fs_map_blocks map; + ssize_t ret = 0; - map.m_lblk = F2FS_BYTES_TO_BLK(offset); + map.m_lblk = F2FS_BYTES_TO_BLK(pos); map.m_len = F2FS_BYTES_TO_BLK(count); map.m_next_pgofs = NULL; - return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_DIO); + if (dio && !(f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + ret = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO); + } + return ret; } /* @@ -671,7 +678,8 @@ next_block: map->m_len = 1; } else if ((map->m_pblk != NEW_ADDR && blkaddr == (map->m_pblk + ofs)) || - (map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR)) { + (map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR) || + flag == F2FS_GET_BLOCK_PRE_DIO) { ofs++; map->m_len++; } else { @@ -1616,34 +1624,21 @@ static int check_direct_IO(struct inode *inode, struct iov_iter *iter, static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t offset) { - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; + struct address_space *mapping = iocb->ki_filp->f_mapping; struct inode *inode = mapping->host; size_t count = iov_iter_count(iter); int err; - /* we don't need to use inline_data strictly */ - err = f2fs_convert_inline_inode(inode); + err = check_direct_IO(inode, iter, offset); if (err) return err; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return 0; - err = check_direct_IO(inode, iter, offset); - if (err) - return err; - trace_f2fs_direct_IO_enter(inode, offset, count, rw); - if (rw & WRITE) { - err = __allocate_data_blocks(inode, offset, count); - if (err) - goto out; - } - err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block_dio); -out: if (err < 0 && (rw & WRITE)) f2fs_write_failed(mapping, offset + count); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ce6ab34e4953..2a5581396928 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -105,6 +105,16 @@ static inline bool f2fs_crc_valid(__u32 blk_crc, void *buf, size_t buf_size) return f2fs_crc32(buf, buf_size) == blk_crc; } +static inline void inode_lock(struct inode *inode) +{ + mutex_lock(&inode->i_mutex); +} + +static inline void inode_unlock(struct inode *inode) +{ + mutex_unlock(&inode->i_mutex); +} + /** * wq_has_sleeper - check if there are any waiting processes * @wq: wait queue head @@ -411,6 +421,7 @@ struct f2fs_map_blocks { #define F2FS_GET_BLOCK_DIO 1 #define F2FS_GET_BLOCK_FIEMAP 2 #define F2FS_GET_BLOCK_BMAP 3 +#define F2FS_GET_BLOCK_PRE_DIO 4 /* * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. @@ -1926,6 +1937,7 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *); void set_data_blkaddr(struct dnode_of_data *); int reserve_new_block(struct dnode_of_data *); int f2fs_get_block(struct dnode_of_data *, pgoff_t); +ssize_t f2fs_preallocate_blocks(struct inode *, loff_t, size_t, bool); int f2fs_reserve_block(struct dnode_of_data *, pgoff_t); struct page *get_read_data_page(struct inode *, pgoff_t, int, bool); struct page *find_data_page(struct inode *, pgoff_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 77aadec0e70e..4c63774b41a9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1876,14 +1876,35 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { - struct inode *inode = file_inode(iocb->ki_filp); + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + size_t count = iov_iter_count(from); + loff_t pos = iocb->ki_pos; + ssize_t ret; if (f2fs_encrypted_inode(inode) && !f2fs_has_encryption_key(inode) && f2fs_get_encryption_info(inode)) return -EACCES; - return generic_file_write_iter(iocb, from); + inode_lock(inode); + ret = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); + if (!ret) { + ret = f2fs_preallocate_blocks(inode, pos, count, + file->f_flags & O_DIRECT); + if (!ret) + ret = __generic_file_write_iter(iocb, from); + } + inode_unlock(inode); + + if (ret > 0) { + ssize_t err; + + err = generic_write_sync(file, iocb->ki_pos - ret, ret); + if (err < 0) + ret = err; + } + return ret; } #ifdef CONFIG_COMPAT -- GitLab From 4216b7389aeeed52672440fe8d739845a10f221c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 3 Feb 2016 13:49:44 -0800 Subject: [PATCH 0124/5498] f2fs: preallocate blocks for buffered aio writes This patch preallocates data blocks for buffered aio writes. With this patch, we can avoid redundant locking and unlocking of node pages given consecutive aio request. [For 3.10] - Add preallocationg for generic_splice_write(sendfile) for xfstests/249, 285 Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c fs/f2fs/file.c --- fs/f2fs/data.c | 36 ++++++++++++++++++++++++++++++------ fs/f2fs/f2fs.h | 1 + include/linux/f2fs_fs.h | 2 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 20a1a9d20faa..7be97d720a82 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -572,15 +572,25 @@ ssize_t f2fs_preallocate_blocks(struct inode *inode, loff_t pos, ssize_t ret = 0; map.m_lblk = F2FS_BYTES_TO_BLK(pos); - map.m_len = F2FS_BYTES_TO_BLK(count); + map.m_len = F2FS_BLK_ALIGN(count); map.m_next_pgofs = NULL; - if (dio && !(f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))) { + if (f2fs_encrypted_inode(inode)) + return 0; + + if (dio) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO); + } + if (pos + count > MAX_INLINE_DATA) { ret = f2fs_convert_inline_inode(inode); if (ret) return ret; - ret = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO); } + if (!f2fs_has_inline_data(inode)) + return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); return ret; } @@ -612,7 +622,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, /* it only supports block size == page size */ pgofs = (pgoff_t)map->m_lblk; - if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) { + if (!create && f2fs_lookup_extent_cache(inode, pgofs, &ei)) { map->m_pblk = ei.blk + pgofs - ei.fofs; map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs); map->m_flags = F2FS_MAP_MAPPED; @@ -647,7 +657,12 @@ next_block: err = -EIO; goto sync_out; } - err = __allocate_data_block(&dn); + if (flag == F2FS_GET_BLOCK_PRE_AIO) { + if (blkaddr == NULL_ADDR) + err = reserve_new_block(&dn); + } else { + err = __allocate_data_block(&dn); + } if (err) goto sync_out; allocated = true; @@ -679,7 +694,8 @@ next_block: } else if ((map->m_pblk != NEW_ADDR && blkaddr == (map->m_pblk + ofs)) || (map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR) || - flag == F2FS_GET_BLOCK_PRE_DIO) { + flag == F2FS_GET_BLOCK_PRE_DIO || + flag == F2FS_GET_BLOCK_PRE_AIO) { ofs++; map->m_len++; } else { @@ -1418,6 +1434,14 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, struct extent_info ei; int err = 0; + /* + * we already allocated all the blocks, so we don't need to get + * the block addresses when there is no need to fill the page. + */ + if (!f2fs_has_inline_data(inode) && !f2fs_encrypted_inode(inode) && + len == PAGE_CACHE_SIZE) + return 0; + if (f2fs_has_inline_data(inode) || (pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { f2fs_lock_op(sbi); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2a5581396928..523dc1723c1f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -422,6 +422,7 @@ struct f2fs_map_blocks { #define F2FS_GET_BLOCK_FIEMAP 2 #define F2FS_GET_BLOCK_BMAP 3 #define F2FS_GET_BLOCK_PRE_DIO 4 +#define F2FS_GET_BLOCK_PRE_AIO 5 /* * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index ac8040278f69..f43e6a01a023 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -21,7 +21,7 @@ #define F2FS_BLKSIZE 4096 /* support only 4KB block */ #define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */ #define F2FS_MAX_EXTENSION 64 /* # of extension entries */ -#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE) +#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS) #define NULL_ADDR ((block_t)0) /* used as block_t addresses */ #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ -- GitLab From eb45172d14fdb5f441385b865b32a1abc2486b41 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 8 Feb 2016 14:17:38 -0800 Subject: [PATCH 0125/5498] f2fs: increase i_size to avoid missing data When finsert is doing with dirting pages, we should increase i_size right away. Otherwise, the moved page is able to be dropped by the following filemap_write_and_wait_range before updating i_size. Especially, it can be done by if ((page->index >= end_index + 1) || !offset) goto out; in f2fs_write_data_page. This should resolve the below xfstests/091 failure reported by Dave. $ diff -u tests/generic/091.out /home/dave/src/xfstests-dev/results//f2fs/generic/091.out.bad --- tests/generic/091.out 2014-01-20 16:57:33.000000000 +1100 +++ /home/dave/src/xfstests-dev/results//f2fs/generic/091.out.bad 2016-02-08 15:21:02.701375087 +1100 @@ -1,7 +1,18 @@ QA output created by 091 fsx -N 10000 -l 500000 -r PSIZE -t BSIZE -w BSIZE -Z -R -W -fsx -N 10000 -o 8192 -l 500000 -r PSIZE -t BSIZE -w BSIZE -Z -R -W -fsx -N 10000 -o 32768 -l 500000 -r PSIZE -t BSIZE -w BSIZE -Z -R -W -fsx -N 10000 -o 8192 -l 500000 -r PSIZE -t BSIZE -w BSIZE -Z -R -W -fsx -N 10000 -o 32768 -l 500000 -r PSIZE -t BSIZE -w BSIZE -Z -R -W -fsx -N 10000 -o 128000 -l 500000 -r PSIZE -t BSIZE -w BSIZE -Z -W +mapped writes DISABLED +skipping insert range behind EOF +skipping insert range behind EOF +truncating to largest ever: 0x11e00 +dowrite: write: Invalid argument +LOG DUMP (7 total operations): +1( 1 mod 256): SKIPPED (no operation) +2( 2 mod 256): SKIPPED (no operation) +3( 3 mod 256): FALLOC 0x2e0f2 thru 0x3134a (0x3258 bytes) PAST_EOF +4( 4 mod 256): SKIPPED (no operation) +5( 5 mod 256): SKIPPED (no operation) +6( 6 mod 256): TRUNCATE UP from 0x0 to 0x11e00 +7( 7 mod 256): WRITE 0x73400 thru 0x79fff (0x6c00 bytes) HOLE +Log of operations saved to "/mnt/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/test/junk" vs "/mnt/test/junk.fsxgood") Reported-by: Dave Chinner Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4c63774b41a9..6121d090fba9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -892,7 +892,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src, psrc = get_lock_data_page(inode, src, true); if (IS_ERR(psrc)) return PTR_ERR(psrc); - pdst = get_new_data_page(inode, NULL, dst, false); + pdst = get_new_data_page(inode, NULL, dst, true); if (IS_ERR(pdst)) { f2fs_put_page(psrc, 1); return PTR_ERR(pdst); -- GitLab From 3b1228452b86f59cd8beefca0e1c33a24b17c34d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 5 Feb 2016 19:19:01 -0800 Subject: [PATCH 0126/5498] f2fs crypto: replace some BUG_ON()'s with error checks This patch adopts: ext4 crypto: replace some BUG_ON()'s with error checks Signed-off-by: Theodore Ts'o Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/crypto_key.c --- fs/f2fs/crypto.c | 1 - fs/f2fs/crypto_fname.c | 2 -- fs/f2fs/crypto_key.c | 15 ++++++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/crypto.c b/fs/f2fs/crypto.c index 4a62ef14e932..d879c6c846b7 100644 --- a/fs/f2fs/crypto.c +++ b/fs/f2fs/crypto.c @@ -362,7 +362,6 @@ static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx, else res = crypto_ablkcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { - BUG_ON(req->base.data != &ecr); wait_for_completion(&ecr.completion); res = ecr.res; } diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c index ab377d496a39..e7aa67c3772e 100644 --- a/fs/f2fs/crypto_fname.c +++ b/fs/f2fs/crypto_fname.c @@ -124,7 +124,6 @@ static int f2fs_fname_encrypt(struct inode *inode, ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); res = crypto_ablkcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { - BUG_ON(req->base.data != &ecr); wait_for_completion(&ecr.completion); res = ecr.res; } @@ -180,7 +179,6 @@ static int f2fs_fname_decrypt(struct inode *inode, ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); res = crypto_ablkcipher_decrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { - BUG_ON(req->base.data != &ecr); wait_for_completion(&ecr.completion); res = ecr.res; } diff --git a/fs/f2fs/crypto_key.c b/fs/f2fs/crypto_key.c index 9f77de2ef317..71cbc7bdfc7f 100644 --- a/fs/f2fs/crypto_key.c +++ b/fs/f2fs/crypto_key.c @@ -75,7 +75,6 @@ static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE], F2FS_AES_256_XTS_KEY_SIZE, NULL); res = crypto_ablkcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { - BUG_ON(req->base.data != &ecr); wait_for_completion(&ecr.completion); res = ecr.res; } @@ -198,7 +197,11 @@ retry: goto out; } crypt_info->ci_keyring_key = keyring_key; - BUG_ON(keyring_key->type != &key_type_logon); + if (keyring_key->type != &key_type_logon) { + printk_once(KERN_WARNING "f2fs: key type must be logon\n"); + res = -ENOKEY; + goto out; + } ukp = ((struct user_key_payload *)keyring_key->payload.data); if (ukp->datalen != sizeof(struct f2fs_encryption_key)) { res = -EINVAL; @@ -207,7 +210,13 @@ retry: master_key = (struct f2fs_encryption_key *)ukp->data; BUILD_BUG_ON(F2FS_AES_128_ECB_KEY_SIZE != F2FS_KEY_DERIVATION_NONCE_SIZE); - BUG_ON(master_key->size != F2FS_AES_256_XTS_KEY_SIZE); + if (master_key->size != F2FS_AES_256_XTS_KEY_SIZE) { + printk_once(KERN_WARNING + "f2fs: key size incorrect: %d\n", + master_key->size); + res = -ENOKEY; + goto out; + } res = f2fs_derive_key_aes(ctx.nonce, master_key->raw, raw_key); if (res) -- GitLab From e5e3341f2bd996dcec2eac32cadcd9bfddc8da87 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 5 Feb 2016 19:21:41 -0800 Subject: [PATCH 0127/5498] f2fs crypto: fix spelling typo in comment This patch adopts: ext4 crypto: fix spelling typo in comment Signed-off-by: Laurent Navet Signed-off-by: Jaegeuk Kim --- fs/f2fs/crypto_key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/crypto_key.c b/fs/f2fs/crypto_key.c index 71cbc7bdfc7f..45f1807fec12 100644 --- a/fs/f2fs/crypto_key.c +++ b/fs/f2fs/crypto_key.c @@ -33,7 +33,7 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) /** * f2fs_derive_key_aes() - Derive a key using AES-128-ECB - * @deriving_key: Encryption key used for derivatio. + * @deriving_key: Encryption key used for derivation. * @source_key: Source key to which to apply derivation. * @derived_key: Derived key. * -- GitLab From c1266d97d501a6d64c52069d408d98539b51e7df Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 5 Feb 2016 19:34:31 -0800 Subject: [PATCH 0128/5498] f2fs crypto: f2fs_page_crypto() doesn't need a encryption context This patch adopts: ext4 crypto: ext4_page_crypto() doesn't need a encryption context Since ext4_page_crypto() doesn't need an encryption context (at least not any more), this allows us to simplify a number function signature and also allows us to avoid needing to allocate a context in ext4_block_write_begin(). It also means we no longer need a separate ext4_decrypt_one() function. Signed-off-by: Theodore Ts'o Signed-off-by: Jaegeuk Kim --- fs/f2fs/crypto.c | 27 +++++---------------------- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 3 +-- 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/fs/f2fs/crypto.c b/fs/f2fs/crypto.c index d879c6c846b7..3ef37868d0c7 100644 --- a/fs/f2fs/crypto.c +++ b/fs/f2fs/crypto.c @@ -156,7 +156,7 @@ static void completion_pages(struct work_struct *work) bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; - int ret = f2fs_decrypt(ctx, page); + int ret = f2fs_decrypt(page); if (ret) { WARN_ON_ONCE(1); @@ -320,8 +320,7 @@ typedef enum { F2FS_ENCRYPT, } f2fs_direction_t; -static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx, - struct inode *inode, +static int f2fs_page_crypto(struct inode *inode, f2fs_direction_t rw, pgoff_t index, struct page *src_page, @@ -418,7 +417,7 @@ struct page *f2fs_encrypt(struct inode *inode, goto err_out; ctx->w.control_page = plaintext_page; - err = f2fs_page_crypto(ctx, inode, F2FS_ENCRYPT, plaintext_page->index, + err = f2fs_page_crypto(inode, F2FS_ENCRYPT, plaintext_page->index, plaintext_page, ciphertext_page); if (err) { ciphertext_page = ERR_PTR(err); @@ -446,30 +445,14 @@ err_out: * * Return: Zero on success, non-zero otherwise. */ -int f2fs_decrypt(struct f2fs_crypto_ctx *ctx, struct page *page) +int f2fs_decrypt(struct page *page) { BUG_ON(!PageLocked(page)); - return f2fs_page_crypto(ctx, page->mapping->host, + return f2fs_page_crypto(page->mapping->host, F2FS_DECRYPT, page->index, page, page); } -/* - * Convenience function which takes care of allocating and - * deallocating the encryption context - */ -int f2fs_decrypt_one(struct inode *inode, struct page *page) -{ - struct f2fs_crypto_ctx *ctx = f2fs_get_crypto_ctx(inode); - int ret; - - if (IS_ERR(ctx)) - return PTR_ERR(ctx); - ret = f2fs_decrypt(ctx, page); - f2fs_release_crypto_ctx(ctx); - return ret; -} - bool f2fs_valid_contents_enc_mode(uint32_t mode) { return (mode == F2FS_ENCRYPTION_MODE_AES_256_XTS); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 7be97d720a82..263739644b14 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1593,7 +1593,7 @@ repeat: /* avoid symlink page */ if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { - err = f2fs_decrypt_one(inode, page); + err = f2fs_decrypt(page); if (err) goto fail; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 523dc1723c1f..475b14c94f41 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2244,8 +2244,7 @@ uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t); struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *); void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *); struct page *f2fs_encrypt(struct inode *, struct page *); -int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *); -int f2fs_decrypt_one(struct inode *, struct page *); +int f2fs_decrypt(struct page *); void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *); /* crypto_key.c */ -- GitLab From 3c8289a1f2a25e2417182f55f246aee72939f91d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 5 Feb 2016 19:37:27 -0800 Subject: [PATCH 0129/5498] f2fs crypto: check for too-short encrypted file names This patch adopts: ext4 crypto: check for too-short encrypted file names An encrypted file name should never be shorter than an 16 bytes, the AES block size. The 3.10 crypto layer will oops and crash the kernel if ciphertext shorter than the block size is passed to it. Fortunately, in modern kernels the crypto layer will not crash the kernel in this scenario, but nevertheless, it represents a corrupted directory, and we should detect it and mark the file system as corrupted so that e2fsck can fix this. Signed-off-by: Theodore Ts'o Signed-off-by: Jaegeuk Kim --- fs/f2fs/crypto_fname.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c index e7aa67c3772e..905c06535c2a 100644 --- a/fs/f2fs/crypto_fname.c +++ b/fs/f2fs/crypto_fname.c @@ -317,7 +317,10 @@ int f2fs_fname_disk_to_usr(struct inode *inode, oname->len = iname->len; return oname->len; } - + if (iname->len < F2FS_CRYPTO_BLOCK_SIZE) { + printk("encrypted inode too small"); + return -EUCLEAN; + } if (F2FS_I(inode)->i_crypt_info) return f2fs_fname_decrypt(inode, iname, oname); -- GitLab From 48e9805201f9d3f6027bdaf55b0939fd041aee95 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 5 Feb 2016 19:38:42 -0800 Subject: [PATCH 0130/5498] f2fs crypto: add missing locking for keyring_key access This patch adopts: ext4 crypto: add missing locking for keyring_key access Signed-off-by: Theodore Ts'o Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/crypto_key.c --- fs/f2fs/crypto_key.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/crypto_key.c b/fs/f2fs/crypto_key.c index 45f1807fec12..4ef6b6ccf319 100644 --- a/fs/f2fs/crypto_key.c +++ b/fs/f2fs/crypto_key.c @@ -202,9 +202,11 @@ retry: res = -ENOKEY; goto out; } + down_read(&keyring_key->sem); ukp = ((struct user_key_payload *)keyring_key->payload.data); if (ukp->datalen != sizeof(struct f2fs_encryption_key)) { res = -EINVAL; + up_read(&keyring_key->sem); goto out; } master_key = (struct f2fs_encryption_key *)ukp->data; @@ -215,10 +217,12 @@ retry: "f2fs: key size incorrect: %d\n", master_key->size); res = -ENOKEY; + up_read(&keyring_key->sem); goto out; } res = f2fs_derive_key_aes(ctx.nonce, master_key->raw, raw_key); + up_read(&keyring_key->sem); if (res) goto out; -- GitLab From f8d2deda578f2637ed25c4f9dbeffdbc113dd4b0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 9 Feb 2016 10:24:31 -0800 Subject: [PATCH 0131/5498] f2fs: use correct errno This patch is to fix misused error number. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 57a5f7bb275a..47fbb72635ef 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -505,7 +505,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr) { - int err = -ENOTSUPP; + int err = -EOPNOTSUPP; if (test_opt(sbi, DISCARD)) { struct seg_entry *se = get_seg_entry(sbi, -- GitLab From 67bc7eebe31f8499cead5bf64c5e2a9f5954511c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 10 Feb 2016 13:23:16 -0800 Subject: [PATCH 0132/5498] f2fs crypto: sync with ext4's fname padding This patch fixes wrong adoption on fname padding. Signed-off-by: Jaegeuk Kim --- fs/f2fs/crypto_fname.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c index 905c06535c2a..73741fb65069 100644 --- a/fs/f2fs/crypto_fname.c +++ b/fs/f2fs/crypto_fname.c @@ -267,13 +267,13 @@ int f2fs_fname_crypto_alloc_buffer(struct inode *inode, u32 ilen, struct f2fs_str *crypto_str) { unsigned int olen; - int padding = 16; + int padding = 32; struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; if (ci) padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); - if (padding < F2FS_CRYPTO_BLOCK_SIZE) - padding = F2FS_CRYPTO_BLOCK_SIZE; + if (ilen < F2FS_CRYPTO_BLOCK_SIZE) + ilen = F2FS_CRYPTO_BLOCK_SIZE; olen = f2fs_fname_crypto_round_up(ilen, padding); crypto_str->len = olen; if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2) -- GitLab From a80211fd29081f863ba9049b36de75932a006d47 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 12 Feb 2016 14:29:28 -0800 Subject: [PATCH 0133/5498] f2fs: avoid garbage lenghs in dentries This patch fixes to eliminate garbage name lengths in dentries in order to provide correct answers of readdir. For example, if a valid dentry consists of: bitmap : 1 1 1 1 len : 32 0 x 0, readdir can start with second bit_pos having len = 0. Or, it can start with third bit_pos having garbage. In both of cases, we should avoid to try filling dentries. So, this patch not only removes any garbage length, but also avoid entering zero length case in readdir. Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index d7d0e783d15e..3fcebee0c620 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -511,8 +511,12 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, memcpy(d->filename[bit_pos], name->name, name->len); de->ino = cpu_to_le32(ino); set_de_type(de, mode); - for (i = 0; i < slots; i++) + for (i = 0; i < slots; i++) { test_and_set_bit_le(bit_pos + i, (void *)d->bitmap); + /* avoid wrong garbage data for readdir */ + if (i) + (de + i)->name_len = 0; + } } /* @@ -792,6 +796,12 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, break; de = &d->dentry[bit_pos]; + if (de->name_len == 0) { + bit_pos++; + ctx->pos = start_pos + bit_pos; + continue; + } + if (de->file_type < F2FS_FT_MAX) d_type = f2fs_filetype_table[de->file_type]; else -- GitLab From 6142d7a42bedd97b1094f63cd10a0385b24cd567 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 6 Feb 2016 14:38:29 +0800 Subject: [PATCH 0134/5498] f2fs: split drop_inmem_pages from commit_inmem_pages Split drop_inmem_pages from commit_inmem_pages for code readability, and prepare for the following modification. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +- fs/f2fs/file.c | 6 +-- fs/f2fs/inode.c | 2 +- fs/f2fs/segment.c | 103 ++++++++++++++++++++++++++++------------------ fs/f2fs/super.c | 2 +- 5 files changed, 70 insertions(+), 46 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 475b14c94f41..2d894e68e5fa 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1861,7 +1861,8 @@ void destroy_node_manager_caches(void); * segment.c */ void register_inmem_page(struct inode *, struct page *); -int commit_inmem_pages(struct inode *, bool); +void drop_inmem_pages(struct inode *); +int commit_inmem_pages(struct inode *); void f2fs_balance_fs(struct f2fs_sb_info *, bool); void f2fs_balance_fs_bg(struct f2fs_sb_info *); int f2fs_issue_flush(struct f2fs_sb_info *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6121d090fba9..f7088a64e5db 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1255,7 +1255,7 @@ static int f2fs_release_file(struct inode *inode, struct file *filp) { /* some remained atomic pages should discarded */ if (f2fs_is_atomic_file(inode)) - commit_inmem_pages(inode, true); + drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { set_inode_flag(F2FS_I(inode), FI_DROP_CACHE); filemap_fdatawrite(inode->i_mapping); @@ -1379,7 +1379,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) if (f2fs_is_atomic_file(inode)) { clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); - ret = commit_inmem_pages(inode, false); + ret = commit_inmem_pages(inode); if (ret) { set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); goto err_out; @@ -1442,7 +1442,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) if (f2fs_is_atomic_file(inode)) { clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); - commit_inmem_pages(inode, true); + drop_inmem_pages(inode); } if (f2fs_is_volatile_file(inode)) { clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 4bd44f817ff4..ea5b581826e6 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -323,7 +323,7 @@ void f2fs_evict_inode(struct inode *inode) /* some remained atomic pages should discarded */ if (f2fs_is_atomic_file(inode)) - commit_inmem_pages(inode, true); + drop_inmem_pages(inode); trace_f2fs_evict_inode(inode); truncate_inode_pages(&inode->i_data, 0); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 47fbb72635ef..5ae66baf6989 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -191,56 +191,67 @@ void register_inmem_page(struct inode *inode, struct page *page) trace_f2fs_register_inmem_page(page, INMEM); } -int commit_inmem_pages(struct inode *inode, bool abort) +static void __revoke_inmem_pages(struct inode *inode, + struct list_head *head) +{ + struct inmem_pages *cur, *tmp; + + list_for_each_entry_safe(cur, tmp, head, list) { + trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP); + + lock_page(cur->page); + ClearPageUptodate(cur->page); + set_page_private(cur->page, 0); + ClearPagePrivate(cur->page); + f2fs_put_page(cur->page, 1); + + list_del(&cur->list); + kmem_cache_free(inmem_entry_slab, cur); + dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); + } +} + +void drop_inmem_pages(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + + mutex_lock(&fi->inmem_lock); + __revoke_inmem_pages(inode, &fi->inmem_pages); + mutex_unlock(&fi->inmem_lock); +} + +static int __commit_inmem_pages(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); struct inmem_pages *cur, *tmp; - bool submit_bio = false; struct f2fs_io_info fio = { .sbi = sbi, .type = DATA, .rw = WRITE_SYNC | REQ_PRIO, .encrypted_page = NULL, }; + bool submit_bio = false; int err = 0; - /* - * The abort is true only when f2fs_evict_inode is called. - * Basically, the f2fs_evict_inode doesn't produce any data writes, so - * that we don't need to call f2fs_balance_fs. - * Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this - * inode becomes free by iget_locked in f2fs_iget. - */ - if (!abort) { - f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); - } - - mutex_lock(&fi->inmem_lock); list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { lock_page(cur->page); - if (!abort) { - if (cur->page->mapping == inode->i_mapping) { - set_page_dirty(cur->page); - f2fs_wait_on_page_writeback(cur->page, DATA, - true); - if (clear_page_dirty_for_io(cur->page)) - inode_dec_dirty_pages(inode); - trace_f2fs_commit_inmem_page(cur->page, INMEM); - fio.page = cur->page; - err = do_write_data_page(&fio); - if (err) { - unlock_page(cur->page); - break; - } - clear_cold_data(cur->page); - submit_bio = true; + if (cur->page->mapping == inode->i_mapping) { + set_page_dirty(cur->page); + f2fs_wait_on_page_writeback(cur->page, DATA, true); + if (clear_page_dirty_for_io(cur->page)) + inode_dec_dirty_pages(inode); + trace_f2fs_commit_inmem_page(cur->page, INMEM); + fio.page = cur->page; + err = do_write_data_page(&fio); + if (err) { + unlock_page(cur->page); + break; } - } else { - ClearPageUptodate(cur->page); - trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP); + clear_cold_data(cur->page); + submit_bio = true; } + set_page_private(cur->page, 0); ClearPagePrivate(cur->page); f2fs_put_page(cur->page, 1); @@ -249,14 +260,26 @@ int commit_inmem_pages(struct inode *inode, bool abort) kmem_cache_free(inmem_entry_slab, cur); dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); } + + if (submit_bio) + f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE); + return err; +} + +int commit_inmem_pages(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); + int err = 0; + + f2fs_balance_fs(sbi, true); + f2fs_lock_op(sbi); + + mutex_lock(&fi->inmem_lock); + err = __commit_inmem_pages(inode); mutex_unlock(&fi->inmem_lock); - if (!abort) { - f2fs_unlock_op(sbi); - if (submit_bio) - f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, - DATA, WRITE); - } + f2fs_unlock_op(sbi); return err; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index aa2d3d9bf12e..92ffb1669d91 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -494,7 +494,7 @@ static int f2fs_drop_inode(struct inode *inode) /* some remained atomic pages should discarded */ if (f2fs_is_atomic_file(inode)) - commit_inmem_pages(inode, true); + drop_inmem_pages(inode); /* should remain fi->extent_tree for writepage */ f2fs_destroy_extent_node(inode); -- GitLab From 784226569ee32d3ed60b92a2c6baa05edc5e85b7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 6 Feb 2016 14:40:34 +0800 Subject: [PATCH 0135/5498] f2fs: support revoking atomic written pages f2fs support atomic write with following semantics: 1. open db file 2. ioctl start atomic write 3. (write db file) * n 4. ioctl commit atomic write 5. close db file With this flow we can avoid file becoming corrupted when abnormal power cut, because we hold data of transaction in referenced pages linked in inmem_pages list of inode, but without setting them dirty, so these data won't be persisted unless we commit them in step 4. But we should still hold journal db file in memory by using volatile write, because our semantics of 'atomic write support' is incomplete, in step 4, we could fail to submit all dirty data of transaction, once partial dirty data was committed in storage, then after a checkpoint & abnormal power-cut, db file will be corrupted forever. So this patch tries to improve atomic write flow by adding a revoking flow, once inner error occurs in committing, this gives another chance to try to revoke these partial submitted data of current transaction, it makes committing operation more like aotmical one. If we're not lucky, once revoking operation was failed, EAGAIN will be reported to user for suggesting doing the recovery with held journal file, or retrying current transaction again. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 + fs/f2fs/f2fs.h | 4 +- fs/f2fs/file.c | 2 +- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 118 ++++++++++++++++++++++++++---------- fs/f2fs/segment.h | 1 + include/trace/events/f2fs.h | 1 + 7 files changed, 93 insertions(+), 36 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 263739644b14..cc8ecd7b8a5f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1073,6 +1073,7 @@ int do_write_data_page(struct f2fs_io_info *fio) return err; fio->blk_addr = dn.data_blkaddr; + fio->old_blkaddr = dn.data_blkaddr; /* This page is already truncated */ if (fio->blk_addr == NULL_ADDR) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2d894e68e5fa..09d3065e23ab 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -716,6 +716,7 @@ enum page_type { META_FLUSH, INMEM, /* the below types are used by tracepoints only. */ INMEM_DROP, + INMEM_REVOKE, IPU, OPU, }; @@ -725,6 +726,7 @@ struct f2fs_io_info { enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */ block_t blk_addr; /* block address to be written */ + block_t old_blkaddr; /* old block address before Cow */ struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ }; @@ -1884,7 +1886,7 @@ void write_node_page(unsigned int, struct f2fs_io_info *); void write_data_page(struct dnode_of_data *, struct f2fs_io_info *); void rewrite_data_page(struct f2fs_io_info *); void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *, - block_t, block_t, unsigned char, bool); + block_t, block_t, unsigned char, bool, bool); void allocate_data_block(struct f2fs_sb_info *, struct page *, block_t, block_t *, struct f2fs_summary *, int); void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f7088a64e5db..906506c58d8c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -884,7 +884,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src, get_node_info(sbi, dn.nid, &ni); f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr, - ni.version, true); + ni.version, true, false); f2fs_put_dnode(&dn); } else { struct page *psrc, *pdst; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index cd100f09ad58..e26ff81ec04c 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -465,7 +465,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, /* write dummy data page */ f2fs_replace_block(sbi, &dn, src, dest, - ni.version, false); + ni.version, false, false); recovered++; } } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5ae66baf6989..ad5da895260a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -191,24 +191,48 @@ void register_inmem_page(struct inode *inode, struct page *page) trace_f2fs_register_inmem_page(page, INMEM); } -static void __revoke_inmem_pages(struct inode *inode, - struct list_head *head) +static int __revoke_inmem_pages(struct inode *inode, + struct list_head *head, bool drop, bool recover) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct inmem_pages *cur, *tmp; + int err = 0; list_for_each_entry_safe(cur, tmp, head, list) { - trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP); + struct page *page = cur->page; + + if (drop) + trace_f2fs_commit_inmem_page(page, INMEM_DROP); + + lock_page(page); - lock_page(cur->page); - ClearPageUptodate(cur->page); - set_page_private(cur->page, 0); - ClearPagePrivate(cur->page); - f2fs_put_page(cur->page, 1); + if (recover) { + struct dnode_of_data dn; + struct node_info ni; + + trace_f2fs_commit_inmem_page(page, INMEM_REVOKE); + + set_new_dnode(&dn, inode, NULL, NULL, 0); + if (get_dnode_of_data(&dn, page->index, LOOKUP_NODE)) { + err = -EAGAIN; + goto next; + } + get_node_info(sbi, dn.nid, &ni); + f2fs_replace_block(sbi, &dn, dn.data_blkaddr, + cur->old_addr, ni.version, true, true); + f2fs_put_dnode(&dn); + } +next: + ClearPageUptodate(page); + set_page_private(page, 0); + ClearPageUptodate(page); + f2fs_put_page(page, 1); list_del(&cur->list); kmem_cache_free(inmem_entry_slab, cur); dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); } + return err; } void drop_inmem_pages(struct inode *inode) @@ -216,11 +240,12 @@ void drop_inmem_pages(struct inode *inode) struct f2fs_inode_info *fi = F2FS_I(inode); mutex_lock(&fi->inmem_lock); - __revoke_inmem_pages(inode, &fi->inmem_pages); + __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); mutex_unlock(&fi->inmem_lock); } -static int __commit_inmem_pages(struct inode *inode) +static int __commit_inmem_pages(struct inode *inode, + struct list_head *revoke_list) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); @@ -235,34 +260,40 @@ static int __commit_inmem_pages(struct inode *inode) int err = 0; list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { - lock_page(cur->page); - if (cur->page->mapping == inode->i_mapping) { - set_page_dirty(cur->page); - f2fs_wait_on_page_writeback(cur->page, DATA, true); - if (clear_page_dirty_for_io(cur->page)) + struct page *page = cur->page; + + lock_page(page); + if (page->mapping == inode->i_mapping) { + trace_f2fs_commit_inmem_page(page, INMEM); + + set_page_dirty(page); + f2fs_wait_on_page_writeback(page, DATA, true); + if (clear_page_dirty_for_io(page)) inode_dec_dirty_pages(inode); - trace_f2fs_commit_inmem_page(cur->page, INMEM); - fio.page = cur->page; + + fio.page = page; err = do_write_data_page(&fio); if (err) { - unlock_page(cur->page); + unlock_page(page); break; } - clear_cold_data(cur->page); - submit_bio = true; - } - set_page_private(cur->page, 0); - ClearPagePrivate(cur->page); - f2fs_put_page(cur->page, 1); + /* record old blkaddr for revoking */ + cur->old_addr = fio.old_blkaddr; - list_del(&cur->list); - kmem_cache_free(inmem_entry_slab, cur); - dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); + clear_cold_data(page); + submit_bio = true; + } + unlock_page(page); + list_move_tail(&cur->list, revoke_list); } if (submit_bio) f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE); + + if (!err) + __revoke_inmem_pages(inode, revoke_list, false, false); + return err; } @@ -270,13 +301,32 @@ int commit_inmem_pages(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); - int err = 0; + struct list_head revoke_list; + int err; + INIT_LIST_HEAD(&revoke_list); f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); mutex_lock(&fi->inmem_lock); - err = __commit_inmem_pages(inode); + err = __commit_inmem_pages(inode, &revoke_list); + if (err) { + int ret; + /* + * try to revoke all committed pages, but still we could fail + * due to no memory or other reason, if that happened, EAGAIN + * will be returned, which means in such case, transaction is + * already not integrity, caller should use journal to do the + * recovery or rewrite & commit last transaction. For other + * error number, revoking was done by filesystem itself. + */ + ret = __revoke_inmem_pages(inode, &revoke_list, false, true); + if (ret) + err = ret; + + /* drop all uncommitted pages */ + __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); + } mutex_unlock(&fi->inmem_lock); f2fs_unlock_op(sbi); @@ -1360,7 +1410,7 @@ void rewrite_data_page(struct f2fs_io_info *fio) static void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, block_t old_blkaddr, block_t new_blkaddr, - bool recover_curseg) + bool recover_curseg, bool recover_newaddr) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg; @@ -1403,7 +1453,7 @@ 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); - if (!recover_curseg) + if (!recover_curseg || recover_newaddr) update_sit_entry(sbi, new_blkaddr, 1); if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) update_sit_entry(sbi, old_blkaddr, -1); @@ -1427,13 +1477,15 @@ static void __f2fs_replace_block(struct f2fs_sb_info *sbi, void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, block_t old_addr, block_t new_addr, - unsigned char version, bool recover_curseg) + unsigned char version, bool recover_curseg, + bool recover_newaddr) { struct f2fs_summary sum; set_summary(&sum, dn->nid, dn->ofs_in_node, version); - __f2fs_replace_block(sbi, &sum, old_addr, new_addr, recover_curseg); + __f2fs_replace_block(sbi, &sum, old_addr, new_addr, + recover_curseg, recover_newaddr); dn->data_blkaddr = new_addr; set_data_blkaddr(dn); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 0ba281baa424..cd3ce37f9ce1 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -190,6 +190,7 @@ struct segment_allocation { struct inmem_pages { struct list_head list; struct page *page; + block_t old_addr; /* for revoking when fail to commit */ }; struct sit_info { diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 813f0bee4516..73750d6cd643 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -17,6 +17,7 @@ { META_FLUSH, "META_FLUSH" }, \ { INMEM, "INMEM" }, \ { INMEM_DROP, "INMEM_DROP" }, \ + { INMEM_REVOKE, "INMEM_REVOKE" }, \ { IPU, "IN-PLACE" }, \ { OPU, "OUT-OF-PLACE" }) -- GitLab From 75899db10f5f637fff164b5370a6edf93c5cfa36 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 14 Feb 2016 18:56:55 +0800 Subject: [PATCH 0136/5498] f2fs crypto: make sure the encryption info is initialized on opendir(2) This patch syncs f2fs with commit 6bc445e0ff44 ("ext4 crypto: make sure the encryption info is initialized on opendir(2)") from ext4. Signed-off-by: Theodore Ts'o Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 3fcebee0c620..ae6424562fa5 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -902,11 +902,19 @@ out: return err; } +static int f2fs_dir_open(struct inode *inode, struct file *filp) +{ + if (f2fs_encrypted_inode(inode)) + return f2fs_get_encryption_info(inode) ? -EACCES : 0; + return 0; +} + const struct file_operations f2fs_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate = f2fs_readdir, .fsync = f2fs_sync_file, + .open = f2fs_dir_open, .unlocked_ioctl = f2fs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = f2fs_compat_ioctl, -- GitLab From 75fb5c54d69886f47da75eda85fb9ec46f4c4c9b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 14 Feb 2016 18:58:35 +0800 Subject: [PATCH 0137/5498] f2fs crypto: handle unexpected lack of encryption keys This patch syncs f2fs with commit abdd438b26b4 ("ext4 crypto: handle unexpected lack of encryption keys") from ext4. Fix up attempts by users to try to write to a file when they don't have access to the encryption key. Signed-off-by: Theodore Ts'o Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/crypto_policy.c | 3 ++- fs/f2fs/file.c | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c index d4a96af513c2..596f02490f27 100644 --- a/fs/f2fs/crypto_policy.c +++ b/fs/f2fs/crypto_policy.c @@ -192,7 +192,8 @@ int f2fs_inherit_context(struct inode *parent, struct inode *child, return res; ci = F2FS_I(parent)->i_crypt_info; - BUG_ON(ci == NULL); + if (ci == NULL) + return -ENOKEY; ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 906506c58d8c..806f7a577c5b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -425,6 +425,8 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) err = f2fs_get_encryption_info(inode); if (err) return 0; + if (!f2fs_encrypted_inode(inode)) + return -ENOKEY; } /* we don't need to use inline_data strictly */ @@ -444,7 +446,9 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) if (!ret && f2fs_encrypted_inode(inode)) { ret = f2fs_get_encryption_info(inode); if (ret) - ret = -EACCES; + return -EACCES; + if (!f2fs_encrypted_inode(inode)) + return -ENOKEY; } return ret; } -- GitLab From 1fda4df30b7b5b724c2067b425d1a93cfc3451e5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 15 Feb 2016 17:54:26 +0800 Subject: [PATCH 0138/5498] f2fs crypto: avoid unneeded memory allocation when {en/de}crypting symlink This patch adopts f2fs with codes of ext4, it removes unneeded memory allocation in creating/accessing path of symlink. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/crypto_fname.c | 21 +++++++----- fs/f2fs/f2fs.h | 1 + fs/f2fs/namei.c | 72 ++++++++++++++++++++---------------------- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c index 73741fb65069..6dfdc978fe45 100644 --- a/fs/f2fs/crypto_fname.c +++ b/fs/f2fs/crypto_fname.c @@ -257,6 +257,18 @@ u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize) return ((size + blksize - 1) / blksize) * blksize; } +unsigned f2fs_fname_encrypted_size(struct inode *inode, u32 ilen) +{ + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + int padding = 32; + + if (ci) + padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); + if (ilen < F2FS_CRYPTO_BLOCK_SIZE) + ilen = F2FS_CRYPTO_BLOCK_SIZE; + return f2fs_fname_crypto_round_up(ilen, padding); +} + /** * f2fs_fname_crypto_alloc_obuff() - * @@ -266,15 +278,8 @@ u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize) int f2fs_fname_crypto_alloc_buffer(struct inode *inode, u32 ilen, struct f2fs_str *crypto_str) { - unsigned int olen; - int padding = 32; - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + unsigned int olen = f2fs_fname_encrypted_size(inode, ilen); - if (ci) - padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); - if (ilen < F2FS_CRYPTO_BLOCK_SIZE) - ilen = F2FS_CRYPTO_BLOCK_SIZE; - olen = f2fs_fname_crypto_round_up(ilen, padding); crypto_str->len = olen; if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2) olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 09d3065e23ab..728133f73d6f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2257,6 +2257,7 @@ int _f2fs_get_encryption_info(struct inode *inode); /* crypto_fname.c */ bool f2fs_valid_filenames_enc_mode(uint32_t); u32 f2fs_fname_crypto_round_up(u32, u32); +unsigned f2fs_fname_encrypted_size(struct inode *, u32); int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *); int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *, const struct f2fs_str *, struct f2fs_str *); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 7c395d515dc9..02d6783aa5ea 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -354,13 +354,23 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct inode *inode; size_t len = strlen(symname); - size_t p_len; - char *p_str; - struct f2fs_str disk_link = FSTR_INIT(NULL, 0); + struct f2fs_str disk_link = FSTR_INIT((char *)symname, len + 1); struct f2fs_encrypted_symlink_data *sd = NULL; int err; - if (len > dir->i_sb->s_blocksize) + if (f2fs_encrypted_inode(dir)) { + err = f2fs_get_encryption_info(dir); + if (err) + return err; + + if (!f2fs_encrypted_inode(dir)) + return -EPERM; + + disk_link.len = (f2fs_fname_encrypted_size(dir, len) + + sizeof(struct f2fs_encrypted_symlink_data)); + } + + if (disk_link.len > dir->i_sb->s_blocksize) return -ENAMETOOLONG; inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); @@ -382,42 +392,36 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, f2fs_unlock_op(sbi); alloc_nid_done(sbi, inode->i_ino); - if (f2fs_encrypted_inode(dir)) { + if (f2fs_encrypted_inode(inode)) { struct qstr istr = QSTR_INIT(symname, len); + struct f2fs_str ostr; - err = f2fs_get_encryption_info(inode); - if (err) + sd = kzalloc(disk_link.len, GFP_NOFS); + if (!sd) { + err = -ENOMEM; goto err_out; + } - err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link); + err = f2fs_get_encryption_info(inode); if (err) goto err_out; - err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link); - if (err < 0) - goto err_out; - - p_len = encrypted_symlink_data_len(disk_link.len) + 1; - - if (p_len > dir->i_sb->s_blocksize) { - err = -ENAMETOOLONG; + if (!f2fs_encrypted_inode(inode)) { + err = -EPERM; goto err_out; } - sd = kzalloc(p_len, GFP_NOFS); - if (!sd) { - err = -ENOMEM; + ostr.name = sd->encrypted_path; + ostr.len = disk_link.len; + err = f2fs_fname_usr_to_disk(inode, &istr, &ostr); + if (err < 0) goto err_out; - } - memcpy(sd->encrypted_path, disk_link.name, disk_link.len); - sd->len = cpu_to_le16(disk_link.len); - p_str = (char *)sd; - } else { - p_len = len + 1; - p_str = (char *)symname; + + sd->len = cpu_to_le16(ostr.len); + disk_link.name = (char *)sd; } - err = page_symlink(inode, p_str, p_len); + err = page_symlink(inode, disk_link.name, disk_link.len); err_out: d_instantiate(dentry, inode); @@ -433,7 +437,8 @@ err_out: * performance regression. */ if (!err) { - filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1); + filemap_write_and_wait_range(inode->i_mapping, 0, + disk_link.len - 1); if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); @@ -442,7 +447,6 @@ err_out: } kfree(sd); - f2fs_fname_crypto_free_buffer(&disk_link); return err; out: handle_failed_inode(inode); @@ -974,6 +978,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, /* Symlink is encrypted */ sd = (struct f2fs_encrypted_symlink_data *)caddr; + cstr.name = sd->encrypted_path; cstr.len = le16_to_cpu(sd->len); /* this is broken symlink case */ @@ -981,12 +986,6 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, res = -ENOENT; goto errout; } - cstr.name = kmalloc(cstr.len, GFP_NOFS); - if (!cstr.name) { - res = -ENOMEM; - goto errout; - } - memcpy(cstr.name, sd->encrypted_path, cstr.len); /* this is broken symlink case */ if (unlikely(cstr.name[0] == 0)) { @@ -1008,8 +1007,6 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, if (res < 0) goto errout; - kfree(cstr.name); - paddr = pstr.name; /* Null-terminate the name */ @@ -1020,7 +1017,6 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, page_cache_release(cpage); return NULL; errout: - kfree(cstr.name); f2fs_fname_crypto_free_buffer(&pstr); kunmap(cpage); page_cache_release(cpage); -- GitLab From ba1e0a0db7249023af8d3e72397549e488581859 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 14 Feb 2016 18:50:40 +0800 Subject: [PATCH 0139/5498] f2fs: introduce f2fs_journal struct to wrap journal info Introduce a new structure f2fs_journal to wrap journal info in struct f2fs_summary_block for readability. struct f2fs_journal { union { __le16 n_nats; __le16 n_sits; }; union { struct nat_journal nat_j; struct sit_journal sit_j; struct f2fs_extra_info info; }; } __packed; struct f2fs_summary_block { struct f2fs_summary entries[ENTRIES_IN_SUM]; struct f2fs_journal journal; struct summary_footer footer; } __packed; Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/f2fs.h | 39 +++++++++++++++-------------- fs/f2fs/node.c | 42 +++++++++++++++++--------------- fs/f2fs/segment.c | 54 ++++++++++++++++++++--------------------- fs/f2fs/super.c | 2 +- include/linux/f2fs_fs.h | 10 +++++--- 6 files changed, 77 insertions(+), 72 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 669f64f6a4d3..01aae09bc62f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1052,7 +1052,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (sb->s_bdev->bd_part) kbytes_written += BD_PART_WRITTEN(sbi); - seg_i->sum_blk->info.kbytes_written = cpu_to_le64(kbytes_written); + seg_i->sum_blk->journal.info.kbytes_written = cpu_to_le64(kbytes_written); if (__remain_node_summaries(cpc->reason)) { write_node_summaries(sbi, start_blk); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 728133f73d6f..e922adea91a1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -214,37 +214,37 @@ struct fsync_inode_entry { block_t last_inode; /* block address locating the last inode */ }; -#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats)) -#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits)) +#define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats)) +#define sits_in_cursum(jnl) (le16_to_cpu(jnl->n_sits)) -#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne) -#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid) -#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se) -#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno) +#define nat_in_journal(jnl, i) (jnl->nat_j.entries[i].ne) +#define nid_in_journal(jnl, i) (jnl->nat_j.entries[i].nid) +#define sit_in_journal(jnl, i) (jnl->sit_j.entries[i].se) +#define segno_in_journal(jnl, i) (jnl->sit_j.entries[i].segno) -#define MAX_NAT_JENTRIES(sum) (NAT_JOURNAL_ENTRIES - nats_in_cursum(sum)) -#define MAX_SIT_JENTRIES(sum) (SIT_JOURNAL_ENTRIES - sits_in_cursum(sum)) +#define MAX_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl)) +#define MAX_SIT_JENTRIES(jnl) (SIT_JOURNAL_ENTRIES - sits_in_cursum(jnl)) -static inline int update_nats_in_cursum(struct f2fs_summary_block *rs, int i) +static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i) { - int before = nats_in_cursum(rs); - rs->n_nats = cpu_to_le16(before + i); + int before = nats_in_cursum(journal); + journal->n_nats = cpu_to_le16(before + i); return before; } -static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i) +static inline int update_sits_in_cursum(struct f2fs_journal *journal, int i) { - int before = sits_in_cursum(rs); - rs->n_sits = cpu_to_le16(before + i); + int before = sits_in_cursum(journal); + journal->n_sits = cpu_to_le16(before + i); return before; } -static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, - int type) +static inline bool __has_cursum_space(struct f2fs_journal *journal, + int size, int type) { if (type == NAT_JOURNAL) - return size <= MAX_NAT_JENTRIES(sum); - return size <= MAX_SIT_JENTRIES(sum); + return size <= MAX_NAT_JENTRIES(journal); + return size <= MAX_SIT_JENTRIES(journal); } /* @@ -1893,8 +1893,7 @@ void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool); void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t); void write_data_summaries(struct f2fs_sb_info *, block_t); void write_node_summaries(struct f2fs_sb_info *, block_t); -int lookup_journal_in_cursum(struct f2fs_summary_block *, - int, unsigned int, int); +int lookup_journal_in_cursum(struct f2fs_journal *, int, unsigned int, int); void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *); int build_segment_manager(struct f2fs_sb_info *); void destroy_segment_manager(struct f2fs_sb_info *); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0bbc5707d80b..0ea1dbe3fb00 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -354,7 +354,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; nid_t start_nid = START_NID(nid); struct f2fs_nat_block *nat_blk; struct page *page = NULL; @@ -382,9 +382,9 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) /* Check current segment summary */ mutex_lock(&curseg->curseg_mutex); - i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0); + i = lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0); if (i >= 0) { - ne = nat_in_journal(sum, i); + ne = nat_in_journal(journal, i); node_info_from_raw_nat(ni, &ne); } mutex_unlock(&curseg->curseg_mutex); @@ -1617,7 +1617,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; int i = 0; nid_t nid = nm_i->next_scan_nid; @@ -1650,9 +1650,11 @@ static void build_free_nids(struct f2fs_sb_info *sbi) /* find free nids from current sum_pages */ mutex_lock(&curseg->curseg_mutex); - for (i = 0; i < nats_in_cursum(sum); i++) { - block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr); - nid = le32_to_cpu(nid_in_journal(sum, i)); + for (i = 0; i < nats_in_cursum(journal); i++) { + block_t addr; + + addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); + nid = le32_to_cpu(nid_in_journal(journal, i)); if (addr == NULL_ADDR) add_free_nid(sbi, nid, true); else @@ -1922,16 +1924,16 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; int i; mutex_lock(&curseg->curseg_mutex); - for (i = 0; i < nats_in_cursum(sum); i++) { + for (i = 0; i < nats_in_cursum(journal); i++) { struct nat_entry *ne; struct f2fs_nat_entry raw_ne; - nid_t nid = le32_to_cpu(nid_in_journal(sum, i)); + nid_t nid = le32_to_cpu(nid_in_journal(journal, i)); - raw_ne = nat_in_journal(sum, i); + raw_ne = nat_in_journal(journal, i); ne = __lookup_nat_cache(nm_i, nid); if (!ne) { @@ -1940,7 +1942,7 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) } __set_nat_cache_dirty(nm_i, ne); } - update_nats_in_cursum(sum, -i); + update_nats_in_cursum(journal, -i); mutex_unlock(&curseg->curseg_mutex); } @@ -1966,7 +1968,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, struct nat_entry_set *set) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; nid_t start_nid = set->set * NAT_ENTRY_PER_BLOCK; bool to_journal = true; struct f2fs_nat_block *nat_blk; @@ -1978,7 +1980,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, * #1, flush nat entries to journal in current hot data summary block. * #2, flush nat entries to nat page. */ - if (!__has_cursum_space(sum, set->entry_cnt, NAT_JOURNAL)) + if (!__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL)) to_journal = false; if (to_journal) { @@ -1999,11 +2001,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, continue; if (to_journal) { - offset = lookup_journal_in_cursum(sum, + offset = lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 1); f2fs_bug_on(sbi, offset < 0); - raw_ne = &nat_in_journal(sum, offset); - nid_in_journal(sum, offset) = cpu_to_le32(nid); + raw_ne = &nat_in_journal(journal, offset); + nid_in_journal(journal, offset) = cpu_to_le32(nid); } else { raw_ne = &nat_blk->entries[nid - start_nid]; } @@ -2032,7 +2034,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; struct nat_entry_set *setvec[SETVEC_SIZE]; struct nat_entry_set *set, *tmp; unsigned int found; @@ -2049,7 +2051,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) * entries, remove all entries from journal and merge them * into nat entry set. */ - if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL)) + if (!__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL)) remove_nats_in_journal(sbi); while ((found = __gang_lookup_nat_set(nm_i, @@ -2058,7 +2060,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) set_idx = setvec[found - 1]->set + 1; for (idx = 0; idx < found; idx++) __adjust_nat_entry_set(setvec[idx], &sets, - MAX_NAT_JENTRIES(sum)); + MAX_NAT_JENTRIES(journal)); } /* flush dirty nats in nat entry set */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ad5da895260a..5902a67c5a1c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1539,11 +1539,11 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) /* Step 1: restore nat cache */ seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(&seg_i->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE); + memcpy(&seg_i->sum_blk->journal.n_nats, kaddr, SUM_JOURNAL_SIZE); /* Step 2: restore sit cache */ seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(&seg_i->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE, + memcpy(&seg_i->sum_blk->journal.n_sits, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); offset = 2 * SUM_JOURNAL_SIZE; @@ -1695,12 +1695,12 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) /* Step 1: write nat cache */ seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(kaddr, &seg_i->sum_blk->n_nats, SUM_JOURNAL_SIZE); + memcpy(kaddr, &seg_i->sum_blk->journal.n_nats, SUM_JOURNAL_SIZE); written_size += SUM_JOURNAL_SIZE; /* Step 2: write sit cache */ seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(kaddr + written_size, &seg_i->sum_blk->n_sits, + memcpy(kaddr + written_size, &seg_i->sum_blk->journal.n_sits, SUM_JOURNAL_SIZE); written_size += SUM_JOURNAL_SIZE; @@ -1768,24 +1768,24 @@ void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); } -int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type, +int lookup_journal_in_cursum(struct f2fs_journal *journal, int type, unsigned int val, int alloc) { int i; if (type == NAT_JOURNAL) { - for (i = 0; i < nats_in_cursum(sum); i++) { - if (le32_to_cpu(nid_in_journal(sum, i)) == val) + for (i = 0; i < nats_in_cursum(journal); i++) { + if (le32_to_cpu(nid_in_journal(journal, i)) == val) return i; } - if (alloc && __has_cursum_space(sum, 1, NAT_JOURNAL)) - return update_nats_in_cursum(sum, 1); + if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL)) + return update_nats_in_cursum(journal, 1); } else if (type == SIT_JOURNAL) { - for (i = 0; i < sits_in_cursum(sum); i++) - if (le32_to_cpu(segno_in_journal(sum, i)) == val) + for (i = 0; i < sits_in_cursum(journal); i++) + if (le32_to_cpu(segno_in_journal(journal, i)) == val) return i; - if (alloc && __has_cursum_space(sum, 1, SIT_JOURNAL)) - return update_sits_in_cursum(sum, 1); + if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL)) + return update_sits_in_cursum(journal, 1); } return -1; } @@ -1889,20 +1889,20 @@ static void add_sits_in_set(struct f2fs_sb_info *sbi) static void remove_sits_in_journal(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; int i; - for (i = sits_in_cursum(sum) - 1; i >= 0; i--) { + for (i = 0; i < sits_in_cursum(journal); i++) { unsigned int segno; bool dirtied; - segno = le32_to_cpu(segno_in_journal(sum, i)); + segno = le32_to_cpu(segno_in_journal(journal, i)); dirtied = __mark_sit_entry_dirty(sbi, segno); if (!dirtied) add_sit_entry(segno, &SM_I(sbi)->sit_entry_set); } - update_sits_in_cursum(sum, -sits_in_cursum(sum)); + update_sits_in_cursum(journal, -i); } /* @@ -1914,7 +1914,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct sit_info *sit_i = SIT_I(sbi); unsigned long *bitmap = sit_i->dirty_sentries_bitmap; struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; struct sit_entry_set *ses, *tmp; struct list_head *head = &SM_I(sbi)->sit_entry_set; bool to_journal = true; @@ -1937,7 +1937,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) * entries, remove all entries from journal and add and account * them in sit entry set. */ - if (!__has_cursum_space(sum, sit_i->dirty_sentries, SIT_JOURNAL)) + if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL)) remove_sits_in_journal(sbi); /* @@ -1954,7 +1954,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) unsigned int segno = start_segno; if (to_journal && - !__has_cursum_space(sum, ses->entry_cnt, SIT_JOURNAL)) + !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL)) to_journal = false; if (!to_journal) { @@ -1975,13 +1975,13 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) } if (to_journal) { - offset = lookup_journal_in_cursum(sum, + offset = lookup_journal_in_cursum(journal, SIT_JOURNAL, segno, 1); f2fs_bug_on(sbi, offset < 0); - segno_in_journal(sum, offset) = + segno_in_journal(journal, offset) = cpu_to_le32(segno); seg_info_to_raw_sit(se, - &sit_in_journal(sum, offset)); + &sit_in_journal(journal, offset)); } else { sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); seg_info_to_raw_sit(se, @@ -2150,7 +2150,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_journal *journal = &curseg->sum_blk->journal; int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, start, end; unsigned int readed, start_blk = 0; @@ -2169,10 +2169,10 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) struct page *page; mutex_lock(&curseg->curseg_mutex); - for (i = 0; i < sits_in_cursum(sum); i++) { - if (le32_to_cpu(segno_in_journal(sum, i)) + for (i = 0; i < sits_in_cursum(journal); i++) { + if (le32_to_cpu(segno_in_journal(journal, i)) == start) { - sit = sit_in_journal(sum, i); + sit = sit_in_journal(journal, i); mutex_unlock(&curseg->curseg_mutex); goto got_it; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 92ffb1669d91..160e1b6f50c4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1414,7 +1414,7 @@ try_onemore: seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); if (__exist_node_summaries(sbi)) sbi->kbytes_written = - le64_to_cpu(seg_i->sum_blk->info.kbytes_written); + le64_to_cpu(seg_i->sum_blk->journal.info.kbytes_written); build_gc_manager(sbi); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index f43e6a01a023..9eb215a155e0 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -398,9 +398,7 @@ struct f2fs_extra_info { __u8 reserved[EXTRA_INFO_RESERVED]; } __packed; -/* 4KB-sized summary block structure */ -struct f2fs_summary_block { - struct f2fs_summary entries[ENTRIES_IN_SUM]; +struct f2fs_journal { union { __le16 n_nats; __le16 n_sits; @@ -411,6 +409,12 @@ struct f2fs_summary_block { struct sit_journal sit_j; struct f2fs_extra_info info; }; +} __packed; + +/* 4KB-sized summary block structure */ +struct f2fs_summary_block { + struct f2fs_summary entries[ENTRIES_IN_SUM]; + struct f2fs_journal journal; struct summary_footer footer; } __packed; -- GitLab From baf66925a7b216837641a7ea819e12974efecf28 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 14 Feb 2016 18:54:33 +0800 Subject: [PATCH 0140/5498] f2fs: enhance IO path with block plug Try to use block plug in more place as below to let process cache bios as much as possbile, in order to reduce lock overhead of queue in IO scheduler. 1) sync_meta_pages 2) ra_meta_pages 3) f2fs_balance_fs_bg Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 12 ++++++++---- fs/f2fs/segment.c | 9 +++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 01aae09bc62f..4b9429cfd999 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -144,7 +144,6 @@ bool is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type) int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type, bool sync) { - block_t prev_blk_addr = 0; struct page *page; block_t blkno = start; struct f2fs_io_info fio = { @@ -153,10 +152,12 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, .rw = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : READA, .encrypted_page = NULL, }; + struct blk_plug plug; if (unlikely(type == META_POR)) fio.rw &= ~REQ_META; + blk_start_plug(&plug); for (; nrpages-- > 0; blkno++) { if (!is_valid_blkaddr(sbi, blkno, type)) @@ -175,9 +176,6 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, /* get sit block addr */ fio.blk_addr = current_sit_addr(sbi, blkno * SIT_ENTRY_PER_BLOCK); - if (blkno != start && prev_blk_addr + 1 != fio.blk_addr) - goto out; - prev_blk_addr = fio.blk_addr; break; case META_SSA: case META_CP: @@ -202,6 +200,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, } out: f2fs_submit_merged_bio(sbi, META, READ); + blk_finish_plug(&plug); return blkno - start; } @@ -288,9 +287,12 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, struct writeback_control wbc = { .for_reclaim = 0, }; + struct blk_plug plug; pagevec_init(&pvec, 0); + blk_start_plug(&plug); + while (index <= end) { int i, nr_pages; nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, @@ -343,6 +345,8 @@ stop: if (nwritten) f2fs_submit_merged_bio(sbi, type, WRITE); + blk_finish_plug(&plug); + return nwritten; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5902a67c5a1c..eb7979d6d1f3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -370,8 +370,13 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) excess_prefree_segs(sbi) || excess_dirty_nats(sbi) || (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { - if (test_opt(sbi, DATA_FLUSH)) + if (test_opt(sbi, DATA_FLUSH)) { + struct blk_plug plug; + + blk_start_plug(&plug); sync_dirty_inodes(sbi, FILE_INODE); + blk_finish_plug(&plug); + } f2fs_sync_fs(sbi->sb, true); stat_inc_bg_cp_count(sbi->stat_info); } @@ -2154,7 +2159,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, start, end; unsigned int readed, start_blk = 0; - int nrpages = MAX_BIO_BLOCKS(sbi); + int nrpages = MAX_BIO_BLOCKS(sbi) * 8; do { readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true); -- GitLab From cec12f843a2fb13a6d7ce539f2d5017c7ddef8a8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 Feb 2016 18:08:46 +0800 Subject: [PATCH 0141/5498] f2fs: split journal cache from curseg cache In curseg cache, f2fs caches two different parts: - datas of current summay block, i.e. summary entries, footer info. - journal info, i.e. sparse nat/sit entries or io stat info. With this approach, 1) it may cause higher lock contention when we access or update both of the parts of cache since we use the same mutex lock curseg_mutex to protect the cache. 2) current summary block with last journal info will be writebacked into device as a normal summary block when flushing, however, we treat journal info as valid one only in current summary, so most normal summary blocks contain junk journal data, it wastes remaining space of summary block. So, in order to fix above issues, we split curseg cache into two parts: a) current summary block, protected by original mutex lock curseg_mutex b) journal cache, protected by newly introduced r/w semaphore journal_rwsem When loading curseg cache during ->mount, we store summary info and journal info into different caches; When doing checkpoint, we combine datas of two cache into current summary block for persisting. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/node.c | 26 +++++++------- fs/f2fs/segment.c | 85 +++++++++++++++++++++++++++++++------------- fs/f2fs/segment.h | 2 ++ 4 files changed, 77 insertions(+), 38 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 4b9429cfd999..8cb05574151a 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1056,7 +1056,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (sb->s_bdev->bd_part) kbytes_written += BD_PART_WRITTEN(sbi); - seg_i->sum_blk->journal.info.kbytes_written = cpu_to_le64(kbytes_written); + seg_i->journal->info.kbytes_written = cpu_to_le64(kbytes_written); if (__remain_node_summaries(cpc->reason)) { write_node_summaries(sbi, start_blk); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0ea1dbe3fb00..d24447a079c7 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -354,7 +354,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; nid_t start_nid = START_NID(nid); struct f2fs_nat_block *nat_blk; struct page *page = NULL; @@ -381,13 +381,13 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) down_write(&nm_i->nat_tree_lock); /* Check current segment summary */ - mutex_lock(&curseg->curseg_mutex); + down_read(&curseg->journal_rwsem); i = lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0); if (i >= 0) { ne = nat_in_journal(journal, i); node_info_from_raw_nat(ni, &ne); } - mutex_unlock(&curseg->curseg_mutex); + up_read(&curseg->journal_rwsem); if (i >= 0) goto cache; @@ -1617,7 +1617,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; int i = 0; nid_t nid = nm_i->next_scan_nid; @@ -1649,7 +1649,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) nm_i->next_scan_nid = nid; /* find free nids from current sum_pages */ - mutex_lock(&curseg->curseg_mutex); + down_read(&curseg->journal_rwsem); for (i = 0; i < nats_in_cursum(journal); i++) { block_t addr; @@ -1660,7 +1660,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) else remove_free_nid(nm_i, nid); } - mutex_unlock(&curseg->curseg_mutex); + up_read(&curseg->journal_rwsem); up_read(&nm_i->nat_tree_lock); ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid), @@ -1924,10 +1924,10 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; int i; - mutex_lock(&curseg->curseg_mutex); + down_write(&curseg->journal_rwsem); for (i = 0; i < nats_in_cursum(journal); i++) { struct nat_entry *ne; struct f2fs_nat_entry raw_ne; @@ -1943,7 +1943,7 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) __set_nat_cache_dirty(nm_i, ne); } update_nats_in_cursum(journal, -i); - mutex_unlock(&curseg->curseg_mutex); + up_write(&curseg->journal_rwsem); } static void __adjust_nat_entry_set(struct nat_entry_set *nes, @@ -1968,7 +1968,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, struct nat_entry_set *set) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; nid_t start_nid = set->set * NAT_ENTRY_PER_BLOCK; bool to_journal = true; struct f2fs_nat_block *nat_blk; @@ -1984,7 +1984,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, to_journal = false; if (to_journal) { - mutex_lock(&curseg->curseg_mutex); + down_write(&curseg->journal_rwsem); } else { page = get_next_nat_page(sbi, start_nid); nat_blk = page_address(page); @@ -2017,7 +2017,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, } if (to_journal) - mutex_unlock(&curseg->curseg_mutex); + up_write(&curseg->journal_rwsem); else f2fs_put_page(page, 1); @@ -2034,7 +2034,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; struct nat_entry_set *setvec[SETVEC_SIZE]; struct nat_entry_set *set, *tmp; unsigned int found; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index eb7979d6d1f3..5d0e6e6f3af7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -922,6 +922,31 @@ static void write_sum_page(struct f2fs_sb_info *sbi, update_meta_page(sbi, (void *)sum_blk, blk_addr); } +static void write_current_sum_page(struct f2fs_sb_info *sbi, + int type, block_t blk_addr) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + struct page *page = grab_meta_page(sbi, blk_addr); + struct f2fs_summary_block *src = curseg->sum_blk; + struct f2fs_summary_block *dst; + + dst = (struct f2fs_summary_block *)page_address(page); + + mutex_lock(&curseg->curseg_mutex); + + down_read(&curseg->journal_rwsem); + memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE); + up_read(&curseg->journal_rwsem); + + memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE); + memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE); + + mutex_unlock(&curseg->curseg_mutex); + + set_page_dirty(page); + f2fs_put_page(page, 1); +} + static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); @@ -1544,12 +1569,11 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) /* Step 1: restore nat cache */ seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(&seg_i->sum_blk->journal.n_nats, kaddr, SUM_JOURNAL_SIZE); + memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE); /* Step 2: restore sit cache */ seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(&seg_i->sum_blk->journal.n_sits, kaddr + SUM_JOURNAL_SIZE, - SUM_JOURNAL_SIZE); + memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); offset = 2 * SUM_JOURNAL_SIZE; /* Step 3: restore summary entries */ @@ -1645,7 +1669,14 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) /* set uncompleted segment to curseg */ curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); - memcpy(curseg->sum_blk, sum, PAGE_CACHE_SIZE); + + /* update journal info */ + down_write(&curseg->journal_rwsem); + memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE); + up_write(&curseg->journal_rwsem); + + memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE); + memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE); curseg->next_segno = segno; reset_curseg(sbi, type, 0); curseg->alloc_type = ckpt->alloc_type[type]; @@ -1700,13 +1731,12 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) /* Step 1: write nat cache */ seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(kaddr, &seg_i->sum_blk->journal.n_nats, SUM_JOURNAL_SIZE); + memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE); written_size += SUM_JOURNAL_SIZE; /* Step 2: write sit cache */ seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(kaddr + written_size, &seg_i->sum_blk->journal.n_sits, - SUM_JOURNAL_SIZE); + memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE); written_size += SUM_JOURNAL_SIZE; /* Step 3: write summary entries */ @@ -1752,12 +1782,8 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi, else end = type + NR_CURSEG_NODE_TYPE; - for (i = type; i < end; i++) { - struct curseg_info *sum = CURSEG_I(sbi, i); - mutex_lock(&sum->curseg_mutex); - write_sum_page(sbi, sum->sum_blk, blkaddr + (i - type)); - mutex_unlock(&sum->curseg_mutex); - } + for (i = type; i < end; i++) + write_current_sum_page(sbi, i, blkaddr + (i - type)); } void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) @@ -1894,9 +1920,10 @@ static void add_sits_in_set(struct f2fs_sb_info *sbi) static void remove_sits_in_journal(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; int i; + down_write(&curseg->journal_rwsem); for (i = 0; i < sits_in_cursum(journal); i++) { unsigned int segno; bool dirtied; @@ -1908,6 +1935,7 @@ static void remove_sits_in_journal(struct f2fs_sb_info *sbi) add_sit_entry(segno, &SM_I(sbi)->sit_entry_set); } update_sits_in_cursum(journal, -i); + up_write(&curseg->journal_rwsem); } /* @@ -1919,13 +1947,12 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct sit_info *sit_i = SIT_I(sbi); unsigned long *bitmap = sit_i->dirty_sentries_bitmap; struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; struct sit_entry_set *ses, *tmp; struct list_head *head = &SM_I(sbi)->sit_entry_set; bool to_journal = true; struct seg_entry *se; - mutex_lock(&curseg->curseg_mutex); mutex_lock(&sit_i->sentry_lock); if (!sit_i->dirty_sentries) @@ -1962,7 +1989,9 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL)) to_journal = false; - if (!to_journal) { + if (to_journal) { + down_write(&curseg->journal_rwsem); + } else { page = get_next_sit_page(sbi, start_segno); raw_sit = page_address(page); } @@ -1998,7 +2027,9 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) ses->entry_cnt--; } - if (!to_journal) + if (to_journal) + up_write(&curseg->journal_rwsem); + else f2fs_put_page(page, 1); f2fs_bug_on(sbi, ses->entry_cnt); @@ -2013,7 +2044,6 @@ out: add_discard_addrs(sbi, cpc); } mutex_unlock(&sit_i->sentry_lock); - mutex_unlock(&curseg->curseg_mutex); set_prefree_as_free_segments(sbi); } @@ -2145,6 +2175,11 @@ static int build_curseg(struct f2fs_sb_info *sbi) array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL); if (!array[i].sum_blk) return -ENOMEM; + init_rwsem(&array[i].journal_rwsem); + array[i].journal = kzalloc(sizeof(struct f2fs_journal), + GFP_KERNEL); + if (!array[i].journal) + return -ENOMEM; array[i].segno = NULL_SEGNO; array[i].next_blkoff = 0; } @@ -2155,7 +2190,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_journal *journal = &curseg->sum_blk->journal; + struct f2fs_journal *journal = curseg->journal; int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, start, end; unsigned int readed, start_blk = 0; @@ -2173,16 +2208,16 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) struct f2fs_sit_entry sit; struct page *page; - mutex_lock(&curseg->curseg_mutex); + down_read(&curseg->journal_rwsem); for (i = 0; i < sits_in_cursum(journal); i++) { if (le32_to_cpu(segno_in_journal(journal, i)) == start) { sit = sit_in_journal(journal, i); - mutex_unlock(&curseg->curseg_mutex); + up_read(&curseg->journal_rwsem); goto got_it; } } - mutex_unlock(&curseg->curseg_mutex); + up_read(&curseg->journal_rwsem); page = get_current_sit_page(sbi, start); sit_blk = (struct f2fs_sit_block *)page_address(page); @@ -2417,8 +2452,10 @@ static void destroy_curseg(struct f2fs_sb_info *sbi) if (!array) return; SM_I(sbi)->curseg_array = NULL; - for (i = 0; i < NR_CURSEG_TYPE; i++) + for (i = 0; i < NR_CURSEG_TYPE; i++) { kfree(array[i].sum_blk); + kfree(array[i].journal); + } kfree(array); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index cd3ce37f9ce1..6f13f72a670c 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -257,6 +257,8 @@ struct victim_selection { struct curseg_info { struct mutex curseg_mutex; /* lock for consistency */ struct f2fs_summary_block *sum_blk; /* cached summary block */ + struct rw_semaphore journal_rwsem; /* protect journal area */ + struct f2fs_journal *journal; /* cached journal info */ unsigned char alloc_type; /* current allocation type */ unsigned int segno; /* current segment number */ unsigned short next_blkoff; /* next block offset to write */ -- GitLab From 13574203d12dfbad69bc561b76998e1e8fb4ec08 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 Feb 2016 18:12:28 +0800 Subject: [PATCH 0142/5498] f2fs: reorder nat cache lock in cache_nat_entry When lookuping nat entry in cache_nat_entry, if we fail to hit nat cache, we try to load nat entries a) from journal of current segment cache or b) from NAT pages for updating, during the process, write lock of nat_tree_lock will be held to avoid inconsistent condition in between nid cache and nat cache caused by racing among nat entry shrinker, checkpointer, nat entry updater. But this way may cause low efficient when updating nat cache, because it serializes accessing in journal cache or reading NAT pages. Here, we reorder lock and update flow as below to enhance accessing concurrency: - get_node_info - down_read(nat_tree_lock) - lookup nat cache --- hit -> unlock & return - lookup journal cache --- hit -> unlock & goto update - up_read(nat_tree_lock) update: - down_write(nat_tree_lock) - cache_nat_entry - lookup nat cache --- nohit -> update - up_write(nat_tree_lock) Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d24447a079c7..9e41a572ad8d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -257,15 +257,20 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) return new; } -static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, +static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry *ne) { + struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; e = __lookup_nat_cache(nm_i, nid); if (!e) { e = grab_nat_entry(nm_i, nid); node_info_from_raw_nat(&e->ni, ne); + } else { + f2fs_bug_on(sbi, nat_get_ino(e) != ne->ino || + nat_get_blkaddr(e) != ne->block_addr || + nat_get_version(e) != ne->version); } } @@ -371,15 +376,12 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) ni->ino = nat_get_ino(e); ni->blk_addr = nat_get_blkaddr(e); ni->version = nat_get_version(e); - } - up_read(&nm_i->nat_tree_lock); - if (e) + up_read(&nm_i->nat_tree_lock); return; + } memset(&ne, 0, sizeof(struct f2fs_nat_entry)); - down_write(&nm_i->nat_tree_lock); - /* Check current segment summary */ down_read(&curseg->journal_rwsem); i = lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0); @@ -398,8 +400,10 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) node_info_from_raw_nat(ni, &ne); f2fs_put_page(page, 1); cache: + up_read(&nm_i->nat_tree_lock); /* cache nat entry */ - cache_nat_entry(NM_I(sbi), nid, &ne); + down_write(&nm_i->nat_tree_lock); + cache_nat_entry(sbi, nid, &ne); up_write(&nm_i->nat_tree_lock); } -- GitLab From 54dddd32b3d7c5d0c66c580b33a5213ac283492a Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 17 Feb 2016 08:59:01 +0800 Subject: [PATCH 0143/5498] f2fs: slightly reorganize read_raw_super_block read_raw_super_block was introduced to help find the first valid superblock. Commit da554e48caab ("f2fs: recovering broken superblock during mount") changed the behaviour to read both of them and check whether need the recovery flag or not. So the comment before this function isn't consistent with what it actually does. Also, the origin code use two tags to round the err cases, which isn't so readable. So this patch amend the comment and slightly reorganize it. Signed-off-by: Shawn Lin Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 73 ++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 160e1b6f50c4..9305ef4adb50 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1166,14 +1166,15 @@ static void init_sb_info(struct f2fs_sb_info *sbi) /* * Read f2fs raw super block. - * Because we have two copies of super block, so read the first one at first, - * if the first one is invalid, move to read the second one. + * Because we have two copies of super block, so read both of them + * to get the first valid one. If any one of them is broken, we pass + * them recovery flag back to the caller. */ static int read_raw_super_block(struct super_block *sb, struct f2fs_super_block **raw_super, int *valid_super_block, int *recovery) { - int block = 0; + int block; struct buffer_head *bh; struct f2fs_super_block *super, *buf; int err = 0; @@ -1181,50 +1182,48 @@ static int read_raw_super_block(struct super_block *sb, super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL); if (!super) return -ENOMEM; -retry: - bh = sb_bread(sb, block); - if (!bh) { - *recovery = 1; - f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", + + for (block = 0; block < 2; block++) { + bh = sb_bread(sb, block); + if (!bh) { + f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", block + 1); - err = -EIO; - goto next; - } + err = -EIO; + continue; + } - buf = (struct f2fs_super_block *)(bh->b_data + F2FS_SUPER_OFFSET); + buf = (struct f2fs_super_block *) + (bh->b_data + F2FS_SUPER_OFFSET); - /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, buf)) { - brelse(bh); - *recovery = 1; - f2fs_msg(sb, KERN_ERR, - "Can't find valid F2FS filesystem in %dth superblock", - block + 1); - err = -EINVAL; - goto next; - } + /* sanity checking of raw super */ + if (sanity_check_raw_super(sb, buf)) { + f2fs_msg(sb, KERN_ERR, + "Can't find valid F2FS filesystem in %dth superblock", + block + 1); + err = -EINVAL; + brelse(bh); + continue; + } - if (!*raw_super) { - memcpy(super, buf, sizeof(*super)); - *valid_super_block = block; - *raw_super = super; + if (!*raw_super) { + memcpy(super, buf, sizeof(*super)); + *valid_super_block = block; + *raw_super = super; + } + brelse(bh); } - brelse(bh); -next: - /* check the validity of the second superblock */ - if (block == 0) { - block++; - goto retry; - } + /* Fail to read any one of the superblocks*/ + if (err < 0) + *recovery = 1; /* No valid superblock */ - if (!*raw_super) { + if (!*raw_super) kfree(super); - return err; - } + else + err = 0; - return 0; + return err; } static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block) -- GitLab From c6eb2936cb944d669cb9d2e695a093bad914185d Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 17 Feb 2016 11:26:32 +0800 Subject: [PATCH 0144/5498] f2fs: move sanity checking of cp into get_valid_checkpoint >From the function name of get_valid_checkpoint, it seems to return the valid cp or NULL for caller to check. If no valid one is found, f2fs_fill_super will print the err log. But if get_valid_checkpoint get one valid(the return value indicate that it's valid, however actually it is invalid after sanity checking), then print another similar err log. That seems strange. Let's keep sanity checking inside the procedure of geting valid cp. Another improvement we gained from this move is that even the large volume is supported, we check the cp in advanced to skip the following procedure if failing the sanity checking. Signed-off-by: Shawn Lin Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 ++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 10 +--------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 8cb05574151a..5dc7e8a521eb 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -709,6 +709,10 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) cp_block = (struct f2fs_checkpoint *)page_address(cur_page); memcpy(sbi->ckpt, cp_block, blk_size); + /* Sanity checking of checkpoint */ + if (sanity_check_ckpt(sbi)) + goto fail_no_cp; + if (cp_blks <= 1) goto done; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e922adea91a1..81d300399fbc 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1814,6 +1814,7 @@ int f2fs_commit_super(struct f2fs_sb_info *, bool); int f2fs_sync_fs(struct super_block *, int); extern __printf(3, 4) void f2fs_msg(struct super_block *, const char *, const char *, ...); +int sanity_check_ckpt(struct f2fs_sb_info *sbi); /* * hash.c diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9305ef4adb50..70036fb30917 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1106,7 +1106,7 @@ static int sanity_check_raw_super(struct super_block *sb, return 0; } -static int sanity_check_ckpt(struct f2fs_sb_info *sbi) +int sanity_check_ckpt(struct f2fs_sb_info *sbi) { unsigned int total, fsmeta; struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); @@ -1365,13 +1365,6 @@ try_onemore: goto free_meta_inode; } - /* sanity checking of checkpoint */ - err = -EINVAL; - if (sanity_check_ckpt(sbi)) { - f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint"); - goto free_cp; - } - sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count); sbi->total_valid_inode_count = @@ -1539,7 +1532,6 @@ free_nm: destroy_node_manager(sbi); free_sm: destroy_segment_manager(sbi); -free_cp: kfree(sbi->ckpt); free_meta_inode: make_bad_inode(sbi->meta_inode); -- GitLab From 8977c7d9a66cfcfe5d4646b3110c173992cf43f1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 17 Feb 2016 16:45:44 +0800 Subject: [PATCH 0145/5498] f2fs: detect error of update_dent_inode in ->rename Should check and show correct return value of update_dent_inode in ->rename. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 02d6783aa5ea..9b99991f9552 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -669,8 +669,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (err) goto put_out_dir; - if (update_dent_inode(old_inode, new_inode, - &new_dentry->d_name)) { + err = update_dent_inode(old_inode, new_inode, + &new_dentry->d_name); + if (err) { release_orphan_inode(sbi); goto put_out_dir; } -- GitLab From 40aeb50be4a1042a4f4dd35b6820836eee183245 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 17 Feb 2016 16:47:05 +0800 Subject: [PATCH 0146/5498] f2fs: fix to delete old dirent in converted inline directory in ->rename When doing test with fstests/generic/068 in inline_dentry enabled f2fs, following oops dmesg will be reported: ------------[ cut here ]------------ WARNING: CPU: 5 PID: 11841 at fs/inode.c:273 drop_nlink+0x49/0x50() Modules linked in: f2fs(O) ip6table_filter ip6_tables ebtable_nat ebtables nf_conntrack_ipv4 nf_defrag_ipv4 xt_state CPU: 5 PID: 11841 Comm: fsstress Tainted: G O 4.5.0-rc1 #45 Hardware name: Hewlett-Packard HP Z220 CMT Workstation/1790, BIOS K51 v01.61 05/16/2013 0000000000000111 ffff88009cdf7ae8 ffffffff813e5944 0000000000002e41 0000000000000000 0000000000000111 0000000000000000 ffff88009cdf7b28 ffffffff8106a587 ffff88009cdf7b58 ffff8804078fe180 ffff880374a64e00 Call Trace: [] dump_stack+0x48/0x64 [] warn_slowpath_common+0x97/0xe0 [] warn_slowpath_null+0x1a/0x20 [] drop_nlink+0x49/0x50 [] f2fs_rename2+0xe04/0x10c0 [f2fs] [] ? lock_two_nondirectories+0x81/0x90 [] ? lockref_get+0x1d/0x30 [] vfs_rename+0x2e0/0x640 [] ? lookup_dcache+0x3b/0xd0 [] ? update_fast_ctr+0x21/0x40 [] ? security_path_rename+0xa2/0xd0 [] SYSC_renameat2+0x4b6/0x540 [] ? trace_hardirqs_off+0xd/0x10 [] ? exit_to_usermode_loop+0x7a/0xd0 [] ? int_ret_from_sys_call+0x52/0x9f [] ? trace_hardirqs_on_caller+0x100/0x1c0 [] SyS_renameat2+0xe/0x10 [] SyS_rename+0x1e/0x20 [] entry_SYSCALL_64_fastpath+0x12/0x6f ---[ end trace 2b31e17995404e42 ]--- This is because: in the same inline directory, when we renaming one file from source name to target name which is not existed, once space of inline dentry is not enough, inline conversion will be triggered, after that all data in inline dentry will be moved to normal dentry page. After attaching the new entry in coverted dentry page, still we try to remove old entry in original inline dentry, since old entry has been moved, so it obviously doesn't make any effect, result in remaining old entry in converted dentry page. Now, we have two valid dentries pointed to the same inode which has nlink value of 1, deleting them both, above warning appears. This issue can be reproduced easily as below steps: 1. mount f2fs with inline_dentry option 2. mkdir dir 3. touch 180 files named [001-180] in dir 4. rename dir/180 dir/181 5. rm dir/180 dir/181 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 9b99991f9552..5d901607322c 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -623,6 +623,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, struct f2fs_dir_entry *old_dir_entry = NULL; struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *new_entry; + bool is_old_inline = f2fs_has_inline_dentry(old_dir); int err = -ENOENT; if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && @@ -709,6 +710,26 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, inc_nlink(new_dir); update_inode_page(new_dir); } + + /* + * old entry and new entry can locate in the same inline + * dentry in inode, when attaching new entry in inline dentry, + * it could force inline dentry conversion, after that, + * old_entry and old_page will point to wrong address, in + * order to avoid this, let's do the check and update here. + */ + if (is_old_inline && !f2fs_has_inline_dentry(old_dir)) { + f2fs_put_page(old_page, 0); + old_page = NULL; + + old_entry = f2fs_find_entry(old_dir, + &old_dentry->d_name, &old_page); + if (!old_entry) { + err = -EIO; + f2fs_unlock_op(sbi); + goto out_dir; + } + } } down_write(&F2FS_I(old_inode)->i_sem); -- GitLab From e46a55927c153edb31963ba35ec9d085f0c7089f Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 19 Feb 2016 16:02:51 +0800 Subject: [PATCH 0147/5498] f2fs: reuse read_inline_data for f2fs_convert_inline_page f2fs_convert_inline_page introduce what read_inline_data already does for copying out the inline data from inode_page. We can use read_inline_data instead to simplify the code. Signed-off-by: Shawn Lin Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 0be4a9b400c6..6caad8bea8aa 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -105,7 +105,6 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) { - void *src_addr, *dst_addr; struct f2fs_io_info fio = { .sbi = F2FS_I_SB(dn->inode), .type = DATA, @@ -115,8 +114,6 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) }; int dirty, err; - f2fs_bug_on(F2FS_I_SB(dn->inode), page->index); - if (!f2fs_exist_data(dn->inode)) goto clear_out; @@ -125,19 +122,8 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) return err; f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page)); - if (PageUptodate(page)) - goto no_update; - zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); - - /* Copy the whole inline data block */ - src_addr = inline_data_addr(dn->inode_page); - dst_addr = kmap_atomic(page); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); - flush_dcache_page(page); - kunmap_atomic(dst_addr); - SetPageUptodate(page); -no_update: + read_inline_data(page, dn->inode_page); set_page_dirty(page); /* clear dirty state */ -- GitLab From 2cab58fd96d6daf1614cf18210a05d6fdf10907e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 18 Feb 2016 16:34:38 -0800 Subject: [PATCH 0148/5498] f2fs: remain last victim segment number ascending order This patch avoids to remain inefficient victim segment number selected by a victim. For example, if all the dirty segments has same valid blocks, we can get the victim segments descending order due to keeping wrong last segment number. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 47ade3542fbd..c01353429ba0 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -270,7 +270,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, { struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct victim_sel_policy p; - unsigned int secno, max_cost; + unsigned int secno, max_cost, last_victim; unsigned int last_segment = MAIN_SEGS(sbi); unsigned int nsearched = 0; @@ -285,6 +285,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, if (p.max_search == 0) goto out; + last_victim = sbi->last_victim[p.gc_mode]; if (p.alloc_mode == LFS && gc_type == FG_GC) { p.min_segno = check_bg_victims(sbi); if (p.min_segno != NULL_SEGNO) @@ -332,7 +333,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, } next: if (nsearched >= p.max_search) { - sbi->last_victim[p.gc_mode] = segno; + if (!sbi->last_victim[p.gc_mode] && segno <= last_victim) + sbi->last_victim[p.gc_mode] = last_victim + 1; + else + sbi->last_victim[p.gc_mode] = segno + 1; break; } } -- GitLab From 188475155138f23c51c7a942ea2927d79037af4b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 22 Feb 2016 18:32:13 +0800 Subject: [PATCH 0149/5498] f2fs: fix the wrong stat count of calling gc With a partition which was formated as multi segments in one section, we stated incorrectly for count of gc operation. e.g., for a partition with segs_per_sec = 4 cat /sys/kernel/debug/f2fs/status GC calls: 208 (BG: 7) - data segments : 104 (52) - node segments : 104 (24) GC called count should be (104 (data segs) + 104 (node segs)) / 4 = 52, rather than 208. Fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c01353429ba0..8d63fc0b84ea 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -820,7 +820,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, gc_type); stat_inc_seg_count(sbi, type, gc_type); - stat_inc_call_count(sbi->stat_info); f2fs_put_page(sum_page, 0); } @@ -845,6 +844,9 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, if (get_valid_blocks(sbi, start_segno++, 1) == 0) seg_freed++; } + + stat_inc_call_count(sbi->stat_info); + return seg_freed; } -- GitLab From e95bd0582687af6f93079c36765db5d22eba5f63 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 22 Feb 2016 18:33:20 +0800 Subject: [PATCH 0150/5498] f2fs: show more info about superblock recovery This patch changes to show more info in message log about the recovery of the corrupted superblock during ->mount, e.g. the index of corrupted superblock and the result of recovery. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 70036fb30917..e8cd70a80e39 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1502,8 +1502,10 @@ try_onemore: /* recover broken superblock */ if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) { - f2fs_msg(sb, KERN_INFO, "Recover invalid superblock"); - f2fs_commit_super(sbi, true); + err = f2fs_commit_super(sbi, true); + f2fs_msg(sb, KERN_INFO, + "Try to recover %dth superblock, ret: %ld", + sbi->valid_super_block ? 1 : 2, err); } f2fs_update_time(sbi, CP_TIME); -- GitLab From c6e431c6f35275b1530d013aa9d63db603af11d5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 22 Feb 2016 18:35:46 +0800 Subject: [PATCH 0151/5498] f2fs: try to flush inode after merging inline data When flushing node pages, if current node page is an inline inode page, we will try to merge inline data from data page into inline inode page, then skip flushing current node page, it will decrease the number of nodes to be flushed in batch in this round, which may lead to worse performance. This patch gives a chance to flush just merged inline inode pages for performance. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9e41a572ad8d..7dad5965af7d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1279,6 +1279,7 @@ next_step: * If an fsync mode, * we should not skip writing node pages. */ +lock_node: if (ino && ino_of_node(page) == ino) lock_page(page); else if (!trylock_page(page)) @@ -1302,7 +1303,7 @@ continue_unlock: clear_inline_node(page); unlock_page(page); flush_inline_data(sbi, ino_of_node(page)); - continue; + goto lock_node; } f2fs_wait_on_page_writeback(page, NODE, true); -- GitLab From 50014d7d6cadfc1b2ebe21808fdd9f9bdfd7cf1d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 22 Feb 2016 18:36:38 +0800 Subject: [PATCH 0152/5498] f2fs: trace old block address for CoWed page This patch enables to trace old block address of CoWed page for better debugging. f2fs_submit_page_mbio: dev = (1,0), ino = 1, page_index = 0x1d4f0, oldaddr = 0xfe8ab, newaddr = 0xfee90 rw = WRITE_SYNC, type = NODE f2fs_submit_page_mbio: dev = (1,0), ino = 1, page_index = 0x1d4f8, oldaddr = 0xfe8b0, newaddr = 0xfee91 rw = WRITE_SYNC, type = NODE f2fs_submit_page_mbio: dev = (1,0), ino = 1, page_index = 0x1d4fa, oldaddr = 0xfe8ae, newaddr = 0xfee92 rw = WRITE_SYNC, type = NODE f2fs_submit_page_mbio: dev = (1,0), ino = 134824, page_index = 0x96, oldaddr = 0xf049b, newaddr = 0x2bbe rw = WRITE, type = DATA f2fs_submit_page_mbio: dev = (1,0), ino = 134824, page_index = 0x97, oldaddr = 0xf049c, newaddr = 0x2bbf rw = WRITE, type = DATA f2fs_submit_page_mbio: dev = (1,0), ino = 134824, page_index = 0x98, oldaddr = 0xf049d, newaddr = 0x2bc0 rw = WRITE, type = DATA f2fs_submit_page_mbio: dev = (1,0), ino = 135260, page_index = 0x47, oldaddr = 0xffffffff, newaddr = 0xf2631 rw = WRITE, type = DATA f2fs_submit_page_mbio: dev = (1,0), ino = 135260, page_index = 0x48, oldaddr = 0xffffffff, newaddr = 0xf2632 rw = WRITE, type = DATA f2fs_submit_page_mbio: dev = (1,0), ino = 135260, page_index = 0x49, oldaddr = 0xffffffff, newaddr = 0xf2633 rw = WRITE, type = DATA Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 12 +++++++----- fs/f2fs/data.c | 25 ++++++++++++++----------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/gc.c | 15 +++++++-------- fs/f2fs/inline.c | 2 +- fs/f2fs/node.c | 6 +++--- fs/f2fs/segment.c | 10 ++++++---- fs/f2fs/trace.c | 6 ++++-- include/trace/events/f2fs.h | 11 +++++++---- 9 files changed, 50 insertions(+), 39 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 5dc7e8a521eb..5e82705599c6 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -56,7 +56,8 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index, .sbi = sbi, .type = META, .rw = READ_SYNC | REQ_META | REQ_PRIO, - .blk_addr = index, + .old_blkaddr = index, + .new_blkaddr = index, .encrypted_page = NULL, }; @@ -169,24 +170,24 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid))) blkno = 0; /* get nat block addr */ - fio.blk_addr = current_nat_addr(sbi, + fio.new_blkaddr = current_nat_addr(sbi, blkno * NAT_ENTRY_PER_BLOCK); break; case META_SIT: /* get sit block addr */ - fio.blk_addr = current_sit_addr(sbi, + fio.new_blkaddr = current_sit_addr(sbi, blkno * SIT_ENTRY_PER_BLOCK); break; case META_SSA: case META_CP: case META_POR: - fio.blk_addr = blkno; + fio.new_blkaddr = blkno; break; default: BUG(); } - page = grab_cache_page(META_MAPPING(sbi), fio.blk_addr); + page = grab_cache_page(META_MAPPING(sbi), fio.new_blkaddr); if (!page) continue; if (PageUptodate(page)) { @@ -195,6 +196,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, } fio.page = page; + fio.old_blkaddr = fio.new_blkaddr; f2fs_submit_page_mbio(&fio); f2fs_put_page(page, 0); } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index cc8ecd7b8a5f..89eeb29d37fc 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -220,7 +220,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) f2fs_trace_ios(fio, 0); /* Allocate a new bio */ - bio = __bio_alloc(fio->sbi, fio->blk_addr, 1, is_read_io(fio->rw)); + bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->rw)); if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { bio_put(bio); @@ -241,21 +241,24 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) io = is_read ? &sbi->read_io : &sbi->write_io[btype]; - verify_block_addr(sbi, fio->blk_addr); + if (fio->old_blkaddr != NEW_ADDR) + verify_block_addr(sbi, fio->old_blkaddr); + verify_block_addr(sbi, fio->new_blkaddr); down_write(&io->io_rwsem); if (!is_read) inc_page_count(sbi, F2FS_WRITEBACK); - if (io->bio && (io->last_block_in_bio != fio->blk_addr - 1 || + if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 || io->fio.rw != fio->rw)) __submit_merged_bio(io); alloc_new: if (io->bio == NULL) { int bio_blocks = MAX_BIO_BLOCKS(sbi); - io->bio = __bio_alloc(sbi, fio->blk_addr, bio_blocks, is_read); + io->bio = __bio_alloc(sbi, fio->new_blkaddr, + bio_blocks, is_read); io->fio = *fio; } @@ -267,7 +270,7 @@ alloc_new: goto alloc_new; } - io->last_block_in_bio = fio->blk_addr; + io->last_block_in_bio = fio->new_blkaddr; f2fs_trace_ios(fio, 0); up_write(&io->io_rwsem); @@ -401,7 +404,7 @@ got_it: return page; } - fio.blk_addr = dn.data_blkaddr; + fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr; fio.page = page; err = f2fs_submit_page_bio(&fio); if (err) @@ -1072,11 +1075,10 @@ int do_write_data_page(struct f2fs_io_info *fio) if (err) return err; - fio->blk_addr = dn.data_blkaddr; fio->old_blkaddr = dn.data_blkaddr; /* This page is already truncated */ - if (fio->blk_addr == NULL_ADDR) { + if (fio->old_blkaddr == NULL_ADDR) { ClearPageUptodate(page); goto out_writepage; } @@ -1085,7 +1087,7 @@ int do_write_data_page(struct f2fs_io_info *fio) /* wait for GCed encrypted page writeback */ f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode), - fio->blk_addr); + fio->old_blkaddr); fio->encrypted_page = f2fs_encrypt(inode, fio->page); if (IS_ERR(fio->encrypted_page)) { @@ -1100,7 +1102,7 @@ int do_write_data_page(struct f2fs_io_info *fio) * If current allocation needs SSR, * it had better in-place writes for updated data. */ - if (unlikely(fio->blk_addr != NEW_ADDR && + if (unlikely(fio->old_blkaddr != NEW_ADDR && !is_cold_data(page) && !IS_ATOMIC_WRITTEN_PAGE(page) && need_inplace_update(inode))) { @@ -1574,7 +1576,8 @@ repeat: .sbi = sbi, .type = DATA, .rw = READ_SYNC, - .blk_addr = blkaddr, + .old_blkaddr = blkaddr, + .new_blkaddr = blkaddr, .page = page, .encrypted_page = NULL, }; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 81d300399fbc..9b40e489f2ea 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -725,7 +725,7 @@ struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */ - block_t blk_addr; /* block address to be written */ + block_t new_blkaddr; /* new block address to be written */ block_t old_blkaddr; /* old block address before Cow */ struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8d63fc0b84ea..09125a5bc4d6 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -581,12 +581,11 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) /* read page */ fio.page = page; - fio.blk_addr = dn.data_blkaddr; + fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr; - fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), - fio.blk_addr, - FGP_LOCK|FGP_CREAT, - GFP_NOFS); + io.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), + fio.new_blkaddr, + FGP_LOCK | FGP_CREAT, GFP_NOFS); if (!fio.encrypted_page) goto put_out; @@ -611,12 +610,12 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) /* allocate block address */ f2fs_wait_on_page_writeback(dn.node_page, NODE, true); - allocate_data_block(fio.sbi, NULL, fio.blk_addr, - &fio.blk_addr, &sum, CURSEG_COLD_DATA); + allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &fio.new_blkaddr, + &sum, CURSEG_COLD_DATA); fio.rw = WRITE_SYNC; f2fs_submit_page_mbio(&fio); - dn.data_blkaddr = fio.blk_addr; + dn.data_blkaddr = fio.new_blkaddr; set_data_blkaddr(&dn); f2fs_update_extent_cache(&dn); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 6caad8bea8aa..d40261ca0f29 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -131,7 +131,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) /* write data page to try to make data consistent */ set_page_writeback(page); - fio.blk_addr = dn->data_blkaddr; + fio.old_blkaddr = dn->data_blkaddr; write_data_page(dn, &fio); set_data_blkaddr(dn); f2fs_update_extent_cache(dn); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7dad5965af7d..4bc22b245a5b 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1068,7 +1068,7 @@ static int read_node_page(struct page *page, int rw) if (PageUptodate(page)) return LOCKED_PAGE; - fio.blk_addr = ni.blk_addr; + fio.new_blkaddr = fio.old_blkaddr = ni.blk_addr; return f2fs_submit_page_bio(&fio); } @@ -1442,9 +1442,9 @@ static int f2fs_write_node_page(struct page *page, } set_page_writeback(page); - fio.blk_addr = ni.blk_addr; + fio.old_blkaddr = ni.blk_addr; write_node_page(nid, &fio); - set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page)); + set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page)); dec_page_count(sbi, F2FS_DIRTY_NODES); up_read(&sbi->node_write); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5d0e6e6f3af7..d8e13b0d4966 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1385,8 +1385,8 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { int type = __get_segment_type(fio->page, fio->type); - allocate_data_block(fio->sbi, fio->page, fio->blk_addr, - &fio->blk_addr, sum, type); + allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, + &fio->new_blkaddr, sum, type); /* writeout dirty page into bdev */ f2fs_submit_page_mbio(fio); @@ -1398,7 +1398,8 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) .sbi = sbi, .type = META, .rw = WRITE_SYNC | REQ_META | REQ_PRIO, - .blk_addr = page->index, + .old_blkaddr = page->index, + .new_blkaddr = page->index, .page = page, .encrypted_page = NULL, }; @@ -1428,11 +1429,12 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); do_write_page(&sum, fio); - dn->data_blkaddr = fio->blk_addr; + dn->data_blkaddr = fio->new_blkaddr; } void rewrite_data_page(struct f2fs_io_info *fio) { + fio->new_blkaddr = fio->old_blkaddr; stat_inc_inplace_blocks(fio->sbi); f2fs_submit_page_mbio(fio); } diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c index 145fb659ad44..562ce0821559 100644 --- a/fs/f2fs/trace.c +++ b/fs/f2fs/trace.c @@ -29,7 +29,8 @@ static inline void __print_last_io(void) last_io.major, last_io.minor, last_io.pid, "----------------", last_io.type, - last_io.fio.rw, last_io.fio.blk_addr, + last_io.fio.rw, + last_io.fio.new_blkaddr, last_io.len); memset(&last_io, 0, sizeof(last_io)); } @@ -101,7 +102,8 @@ void f2fs_trace_ios(struct f2fs_io_info *fio, int flush) last_io.pid == pid && last_io.type == __file_type(inode, pid) && last_io.fio.rw == fio->rw && - last_io.fio.blk_addr + last_io.len == fio->blk_addr) { + last_io.fio.new_blkaddr + last_io.len == + fio->new_blkaddr) { last_io.len++; return; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 73750d6cd643..7732cb71b908 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -693,7 +693,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio, __field(dev_t, dev) __field(ino_t, ino) __field(pgoff_t, index) - __field(block_t, blkaddr) + __field(block_t, old_blkaddr) + __field(block_t, new_blkaddr) __field(int, rw) __field(int, type) ), @@ -702,16 +703,18 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio, __entry->dev = page->mapping->host->i_sb->s_dev; __entry->ino = page->mapping->host->i_ino; __entry->index = page->index; - __entry->blkaddr = fio->blk_addr; + __entry->old_blkaddr = fio->old_blkaddr; + __entry->new_blkaddr = fio->new_blkaddr; __entry->rw = fio->rw; __entry->type = fio->type; ), TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, " - "blkaddr = 0x%llx, rw = %s%s, type = %s", + "oldaddr = 0x%llx, newaddr = 0x%llx rw = %s%s, type = %s", show_dev_ino(__entry), (unsigned long)__entry->index, - (unsigned long long)__entry->blkaddr, + (unsigned long long)__entry->old_blkaddr, + (unsigned long long)__entry->new_blkaddr, show_bio_type(__entry->rw), show_block_type(__entry->type)) ); -- GitLab From 3e1e4df8c8e6f64e409d0641cbc25db83f057e54 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 23 Feb 2016 12:07:56 +0800 Subject: [PATCH 0153/5498] f2fs: avoid hungtask problem caused by losing wake_up The D state of wait_on_all_pages_writeback should be waken by function f2fs_write_end_io when all writeback pages have been succesfully written to device. It's possible that wake_up comes between get_pages and io_schedule. Maybe in this case it will lost wake_up and still in D state even if all pages have been write back to device, and finally, the whole system will be into the hungtask state. if (!get_pages(sbi, F2FS_WRITEBACK)) break; <--------- wake_up io_schedule(); Signed-off-by: Yunlei He Signed-off-by: Biao He Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- kernel/sched/core.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 5e82705599c6..b43999e7ff91 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -921,7 +921,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi) if (!get_pages(sbi, F2FS_WRITEBACK)) break; - io_schedule(); + io_schedule_timeout(5*HZ); } finish_wait(&sbi->cp_wait, &wait); } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d650e1e593b8..84cf9a0a9bd7 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4439,6 +4439,7 @@ long __sched io_schedule_timeout(long timeout) delayacct_blkio_end(); return ret; } +EXPORT_SYMBOL(io_schedule_timeout); /** * sys_sched_get_priority_max - return maximum RT priority. -- GitLab From ff893e511f604c3ad2b123f3dbb603f0f856b8b0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 24 Feb 2016 17:20:44 +0800 Subject: [PATCH 0154/5498] f2fs: fix incorrect upper bound when iterating inode mapping tree 1. Inode mapping tree can index page in range of [0, ULONG_MAX], however, in some places, f2fs only search or iterate page in ragne of [0, LONG_MAX], result in miss hitting in page cache. 2. filemap_fdatawait_range accepts range parameters in unit of bytes, so the max range it covers should be [0, LLONG_MAX], if we use [0, LONG_MAX] as range for waiting on writeback, big number of pages will not be covered. This patch corrects above two issues. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 8 ++++---- fs/f2fs/file.c | 2 +- fs/f2fs/node.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index b43999e7ff91..0f9666452b88 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -283,7 +283,7 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, long nr_to_write) { struct address_space *mapping = META_MAPPING(sbi); - pgoff_t index = 0, end = LONG_MAX, prev = LONG_MAX; + pgoff_t index = 0, end = ULONG_MAX, prev = ULONG_MAX; struct pagevec pvec; long nwritten = 0; struct writeback_control wbc = { @@ -306,7 +306,7 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; - if (prev == LONG_MAX) + if (prev == ULONG_MAX) prev = page->index - 1; if (nr_to_write != LONG_MAX && page->index != prev + 1) { pagevec_release(&pvec); @@ -1078,8 +1078,8 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (unlikely(f2fs_cp_error(sbi))) return -EIO; - filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LONG_MAX); - filemap_fdatawait_range(META_MAPPING(sbi), 0, LONG_MAX); + filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LLONG_MAX); + filemap_fdatawait_range(META_MAPPING(sbi), 0, LLONG_MAX); /* update user_block_counts */ sbi->last_valid_block_count = sbi->total_valid_block_count; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 806f7a577c5b..e77f66c40126 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -302,7 +302,7 @@ static pgoff_t __get_first_dirty_index(struct address_space *mapping, 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; + pgofs = nr_pages ? pvec.pages[0]->index : ULONG_MAX; pagevec_release(&pvec); return pgofs; } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4bc22b245a5b..5773acc2099a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1242,7 +1242,7 @@ int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, next_step: index = 0; - end = LONG_MAX; + end = ULONG_MAX; while (index <= end) { int i, nr_pages; @@ -1358,7 +1358,7 @@ continue_unlock: int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino) { - pgoff_t index = 0, end = LONG_MAX; + pgoff_t index = 0, end = ULONG_MAX; struct pagevec pvec; int ret2 = 0, ret = 0; -- GitLab From 4b3dd969445f689262490d751fb8c5503295030f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 23 Feb 2016 17:52:43 +0800 Subject: [PATCH 0155/5498] f2fs crypto: fix incorrect positioning for GCing encrypted data page For now, flow of GCing an encrypted data page: 1) try to grab meta page in meta inode's mapping with index of old block address of that data page 2) load data of ciphertext into meta page 3) allocate new block address 4) write the meta page into new block address 5) update block address pointer in direct node page. Other reader/writer will use f2fs_wait_on_encrypted_page_writeback to check and wait on GCed encrypted data cached in meta page writebacked in order to avoid inconsistence among data page cache, meta page cache and data on-disk when updating. However, we will use new block address updated in step 5) as an index to lookup meta page in inner bio buffer. That would be wrong, and we will never find the GCing meta page, since we use the old block address as index of that page in step 1). This patch fixes the issue by adjust the order of step 1) and step 3), and in step 1) grab page with index generated in step 3). Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/gc.c --- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/gc.c | 29 +++++++++++++++++++++-------- fs/f2fs/segment.c | 3 +-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9b40e489f2ea..a716aa2a27d5 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1886,6 +1886,8 @@ void write_meta_page(struct f2fs_sb_info *, struct page *); void write_node_page(unsigned int, struct f2fs_io_info *); void write_data_page(struct dnode_of_data *, struct f2fs_io_info *); void rewrite_data_page(struct f2fs_io_info *); +void __f2fs_replace_block(struct f2fs_sb_info *, struct f2fs_summary *, + block_t, block_t, bool, bool); void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *, block_t, block_t, unsigned char, bool, bool); void allocate_data_block(struct f2fs_sb_info *, struct page *, diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 09125a5bc4d6..d4bf60f128cf 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -553,6 +553,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) struct f2fs_summary sum; struct node_info ni; struct page *page; + block_t newaddr; int err; /* do not read out */ @@ -583,11 +584,15 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) fio.page = page; fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr; - io.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), - fio.new_blkaddr, + allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr, + &sum, CURSEG_COLD_DATA); + + fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr, FGP_LOCK | FGP_CREAT, GFP_NOFS); - if (!fio.encrypted_page) - goto put_out; + if (!fio.encrypted_page) { + err = -ENOMEM; + goto recover_block; + } err = f2fs_submit_page_bio(&fio); if (err) @@ -596,10 +601,14 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) /* write page */ lock_page(fio.encrypted_page); - if (unlikely(!PageUptodate(fio.encrypted_page))) + if (unlikely(!PageUptodate(fio.encrypted_page))) { + err = -EIO; goto put_page_out; - if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) + } + if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) { + err = -EIO; goto put_page_out; + } set_page_dirty(fio.encrypted_page); f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true); @@ -610,9 +619,9 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) /* allocate block address */ f2fs_wait_on_page_writeback(dn.node_page, NODE, true); - allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &fio.new_blkaddr, - &sum, CURSEG_COLD_DATA); + fio.rw = WRITE_SYNC; + fio.new_blkaddr = newaddr; f2fs_submit_page_mbio(&fio); dn.data_blkaddr = fio.new_blkaddr; @@ -623,6 +632,10 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); put_page_out: f2fs_put_page(fio.encrypted_page, 1); +recover_block: + if (err) + __f2fs_replace_block(fio.sbi, &sum, newaddr, fio.old_blkaddr, + true, true); put_out: f2fs_put_dnode(&dn); out: diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d8e13b0d4966..639c4ff86502 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1439,8 +1439,7 @@ void rewrite_data_page(struct f2fs_io_info *fio) f2fs_submit_page_mbio(fio); } -static void __f2fs_replace_block(struct f2fs_sb_info *sbi, - struct f2fs_summary *sum, +void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, block_t old_blkaddr, block_t new_blkaddr, bool recover_curseg, bool recover_newaddr) { -- GitLab From 044b8b77b854c37b190b9e0ae8932b8059e6b34d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 24 Feb 2016 17:16:47 +0800 Subject: [PATCH 0156/5498] f2fs: introduce f2fs_update_data_blkaddr for cleanup Add a new help f2fs_update_data_blkaddr to clean up redundant codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 9 +++++++-- fs/f2fs/extent_cache.c | 9 ++++++--- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 15 +++------------ fs/f2fs/gc.c | 4 +--- fs/f2fs/inline.c | 2 -- fs/f2fs/segment.c | 6 ++---- 7 files changed, 20 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 89eeb29d37fc..9a2ddc30d401 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -301,6 +301,13 @@ void set_data_blkaddr(struct dnode_of_data *dn) dn->node_changed = true; } +void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr) +{ + dn->data_blkaddr = blkaddr; + set_data_blkaddr(dn); + f2fs_update_extent_cache(dn); +} + int reserve_new_block(struct dnode_of_data *dn) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); @@ -1111,8 +1118,6 @@ int do_write_data_page(struct f2fs_io_info *fio) trace_f2fs_do_write_data_page(page, IPU); } else { write_data_page(&dn, fio); - set_data_blkaddr(&dn); - f2fs_update_extent_cache(&dn); trace_f2fs_do_write_data_page(page, OPU); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); if (page->index == 0) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 071a1b19e5af..c859bb044728 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -674,17 +674,20 @@ bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs, void f2fs_update_extent_cache(struct dnode_of_data *dn) { pgoff_t fofs; + block_t blkaddr; if (!f2fs_may_extent_tree(dn->inode)) return; - f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR); - + if (dn->data_blkaddr == NEW_ADDR) + blkaddr = NULL_ADDR; + else + blkaddr = dn->data_blkaddr; fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) + dn->ofs_in_node; - if (f2fs_update_extent_tree_range(dn->inode, fofs, dn->data_blkaddr, 1)) + if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1)) sync_inode_page(dn); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a716aa2a27d5..4b449b5d2f6f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1941,6 +1941,7 @@ void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *, int f2fs_submit_page_bio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *); void set_data_blkaddr(struct dnode_of_data *); +void f2fs_update_data_blkaddr(struct dnode_of_data *, block_t); int reserve_new_block(struct dnode_of_data *); int f2fs_get_block(struct dnode_of_data *, pgoff_t); ssize_t f2fs_preallocate_blocks(struct inode *, loff_t, size_t, bool); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e77f66c40126..68f6b0da1e49 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -858,10 +858,8 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src, } else { new_addr = dn.data_blkaddr; if (!is_checkpointed_data(sbi, new_addr)) { - dn.data_blkaddr = NULL_ADDR; /* do not invalidate this block address */ - set_data_blkaddr(&dn); - f2fs_update_extent_cache(&dn); + f2fs_update_data_blkaddr(&dn, NULL_ADDR); do_replace = true; } f2fs_put_dnode(&dn); @@ -912,9 +910,7 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src, err_out: if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) { - dn.data_blkaddr = new_addr; - set_data_blkaddr(&dn); - f2fs_update_extent_cache(&dn); + f2fs_update_data_blkaddr(&dn, new_addr); f2fs_put_dnode(&dn); } return ret; @@ -1054,12 +1050,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, if (dn.data_blkaddr != NEW_ADDR) { invalidate_blocks(sbi, dn.data_blkaddr); - - dn.data_blkaddr = NEW_ADDR; - set_data_blkaddr(&dn); - - dn.data_blkaddr = NULL_ADDR; - f2fs_update_extent_cache(&dn); + f2fs_update_data_blkaddr(&dn, NEW_ADDR); } f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index d4bf60f128cf..b0051a97824c 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -624,9 +624,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) fio.new_blkaddr = newaddr; f2fs_submit_page_mbio(&fio); - dn.data_blkaddr = fio.new_blkaddr; - set_data_blkaddr(&dn); - f2fs_update_extent_cache(&dn); + f2fs_update_data_blkaddr(&dn, newaddr); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); if (page->index == 0) set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index d40261ca0f29..1c00f2c718ae 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -133,8 +133,6 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) set_page_writeback(page); fio.old_blkaddr = dn->data_blkaddr; write_data_page(dn, &fio); - set_data_blkaddr(dn); - f2fs_update_extent_cache(dn); f2fs_wait_on_page_writeback(page, DATA, true); if (dirty) inode_dec_dirty_pages(dn->inode); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 639c4ff86502..6f16b39f0b52 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1429,7 +1429,7 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); do_write_page(&sum, fio); - dn->data_blkaddr = fio->new_blkaddr; + f2fs_update_data_blkaddr(dn, fio->new_blkaddr); } void rewrite_data_page(struct f2fs_io_info *fio) @@ -1518,9 +1518,7 @@ void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, __f2fs_replace_block(sbi, &sum, old_addr, new_addr, recover_curseg, recover_newaddr); - dn->data_blkaddr = new_addr; - set_data_blkaddr(dn); - f2fs_update_extent_cache(dn); + f2fs_update_data_blkaddr(dn, new_addr); } void f2fs_wait_on_page_writeback(struct page *page, -- GitLab From c006ec4dc89983db9bd449b6fcdb4f01971f0322 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 24 Feb 2016 17:17:55 +0800 Subject: [PATCH 0157/5498] f2fs: introduce f2fs_flush_merged_bios for cleanup Add a new helper f2fs_flush_merged_bios to clean up redundant codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 +--- fs/f2fs/data.c | 7 +++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 7 ++----- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 0f9666452b88..b2875911a1e8 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1142,9 +1142,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops"); - f2fs_submit_merged_bio(sbi, DATA, WRITE); - f2fs_submit_merged_bio(sbi, NODE, WRITE); - f2fs_submit_merged_bio(sbi, META, WRITE); + f2fs_flush_merged_bios(sbi); /* * update checkpoint pack index diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 9a2ddc30d401..5005dbf66b70 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -207,6 +207,13 @@ void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi, __f2fs_submit_merged_bio(sbi, inode, page, ino, type, rw); } +void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi) +{ + f2fs_submit_merged_bio(sbi, DATA, WRITE); + f2fs_submit_merged_bio(sbi, NODE, WRITE); + f2fs_submit_merged_bio(sbi, META, WRITE); +} + /* * Fill the locked page with data located in the block address. * Return unlocked page. diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4b449b5d2f6f..a64fd12da440 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1938,6 +1938,7 @@ void destroy_checkpoint_caches(void); void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *, struct page *, nid_t, enum page_type, int); +void f2fs_flush_merged_bios(struct f2fs_sb_info *); int f2fs_submit_page_bio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *); void set_data_blkaddr(struct dnode_of_data *); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index e8cd70a80e39..d565438fba36 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -583,11 +583,8 @@ static void f2fs_put_super(struct super_block *sb) mutex_unlock(&sbi->umount_mutex); /* our cp_error case, we can wait for any writeback page */ - if (get_pages(sbi, F2FS_WRITEBACK)) { - f2fs_submit_merged_bio(sbi, DATA, WRITE); - f2fs_submit_merged_bio(sbi, NODE, WRITE); - f2fs_submit_merged_bio(sbi, META, WRITE); - } + if (get_pages(sbi, F2FS_WRITEBACK)) + f2fs_flush_merged_bios(sbi); iput(sbi->node_inode); iput(sbi->meta_inode); -- GitLab From 6dc408e83d6edadc152447709db61a88bfb25016 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 26 Feb 2016 09:33:04 +0800 Subject: [PATCH 0158/5498] f2fs: fix to avoid deadlock when merging inline data When testing with fsstress, kworker and user threads were both blocked: INFO: task kworker/u16:1:16580 blocked for more than 120 seconds. "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. kworker/u16:1 D ffff8803f2595390 0 16580 2 0x00000000 Workqueue: writeback bdi_writeback_workfn (flush-251:0) ffff8802730e5760 0000000000000046 ffff880274729fc0 0000000000012440 ffff8802730e5fd8 ffff8802730e4010 0000000000012440 0000000000012440 ffff8802730e5fd8 0000000000012440 ffff880274729fc0 ffff88026eb50000 Call Trace: [] schedule+0x29/0x70 [] rwsem_down_read_failed+0xa5/0xf9 [] call_rwsem_down_read_failed+0x14/0x30 [] f2fs_write_data_page+0x31b/0x420 [f2fs] [] __f2fs_writepage+0x1a/0x50 [f2fs] [] f2fs_write_data_pages+0xe0/0x290 [f2fs] [] do_writepages+0x23/0x40 [] __writeback_single_inode+0x4e/0x250 [] writeback_sb_inodes+0x2c1/0x470 [] __writeback_inodes_wb+0x9e/0xd0 [] wb_writeback+0x1fb/0x2d0 [] wb_do_writeback+0x9c/0x220 [] bdi_writeback_workfn+0x72/0x1c0 [] process_one_work+0x1de/0x5b0 [] worker_thread+0x11f/0x3e0 [] kthread+0xde/0xf0 [] ret_from_fork+0x58/0x90 fsstress thread stack: [] sleep_on_page+0xe/0x20 [] __lock_page+0x67/0x70 [] find_lock_page+0x50/0x80 [] find_or_create_page+0x3f/0xb0 [] sync_node_pages+0x259/0x810 [f2fs] [] write_checkpoint+0x1a4/0xce0 [f2fs] [] f2fs_sync_fs+0x7c/0xd0 [f2fs] [] f2fs_sync_file+0x143/0x5f0 [f2fs] [] vfs_fsync_range+0x2b/0x40 [] vfs_fsync+0x1c/0x20 [] do_fsync+0x41/0x70 [] SyS_fdatasync+0x13/0x20 [] system_call_fastpath+0x16/0x1b [] 0xffffffffffffffff The reason of this issue is: CPU0: CPU1: - f2fs_write_data_pages - f2fs_sync_fs - write_checkpoint - block_operations - f2fs_lock_all - down_write(sbi->cp_rwsem) - lock_page(page) - f2fs_write_data_page - sync_node_pages - flush_inline_data - pagecache_get_page(page, GFP_LOCK) - f2fs_lock_op - down_read(sbi->cp_rwsem) This patch alters to use trylock_page in flush_inline_data to fix this ABBA deadlock issue. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5773acc2099a..5ebcba03411e 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1207,10 +1207,13 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) if (!inode) return; - page = grab_cache_page_nowait(inode->i_mapping, 0); + page = find_get_page(inode->i_mapping, 0); if (!page) goto iput_out; + if (!trylock_page(page)) + goto release_out; + if (!PageUptodate(page)) goto page_out; @@ -1225,7 +1228,9 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) else set_page_dirty(page); page_out: - f2fs_put_page(page, 1); + unlock_page(page); +release_out: + f2fs_put_page(page, 0); iput_out: iput(inode); } -- GitLab From d3027c51c1ac26c22705e391c8152218a085ad04 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 18 Sep 2015 13:35:08 -0400 Subject: [PATCH 0159/5498] fs-writeback: unplug before cond_resched in writeback_sb_inodes Commit 505a666ee3fc ("writeback: plug writeback in wb_writeback() and writeback_inodes_wb()") has us holding a plug during writeback_sb_inodes, which increases the merge rate when relatively contiguous small files are written by the filesystem. It helps both on flash and spindles. For an fs_mark workload creating 4K files in parallel across 8 drives, this commit improves performance ~9% more by unplugging before calling cond_resched(). cond_resched() doesn't trigger an implicit unplug, so explicitly getting the IO down to the device before scheduling reduces latencies for anyone waiting on clean pages. It also cuts down on how often we use kblockd to unplug, which means less work bouncing from one workqueue to another. Many more details about how we got here: https://lkml.org/lkml/2015/9/11/570 Signed-off-by: Chris Mason Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 2d609a5fbfea..a07634599cd7 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -705,6 +705,21 @@ static long writeback_sb_inodes(struct super_block *sb, work->nr_pages -= write_chunk - wbc.nr_to_write; wrote += write_chunk - wbc.nr_to_write; + + if (need_resched()) { + /* + * We're trying to balance between building up a nice + * long list of IOs to improve our merge rate, and + * getting those IOs out quickly for anyone throttling + * in balance_dirty_pages(). cond_resched() doesn't + * unplug, so get our IOs out the door before we + * give up the CPU. + */ + blk_flush_plug(current); + cond_resched(); + } + + spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); if (!(inode->i_state & I_DIRTY)) @@ -712,7 +727,7 @@ static long writeback_sb_inodes(struct super_block *sb, requeue_inode(inode, wb, &wbc); inode_sync_complete(inode); spin_unlock(&inode->i_lock); - cond_resched_lock(&wb->list_lock); + /* * bail out to wb_writeback() often enough to check * background threshold and other termination conditions. -- GitLab From e5b470d1fb6ba84318a79a74e495d48963c1eca0 Mon Sep 17 00:00:00 2001 From: Yang Shi Date: Fri, 26 Feb 2016 16:25:25 -0800 Subject: [PATCH 0160/5498] f2fs: mutex can't be used by down_write_nest_lock() f2fs_lock_all() calls down_write_nest_lock() to acquire a rw_sem and check a mutex, but down_write_nest_lock() is designed for two rw_sem accoring to the comment in include/linux/rwsem.h. And, other than f2fs, it is just called in mm/mmap.c with two rwsem. So, it looks it is used wrongly by f2fs. And, it causes the below compile warning on -rt kernel too. In file included from fs/f2fs/xattr.c:25:0: fs/f2fs/f2fs.h: In function 'f2fs_lock_all': fs/f2fs/f2fs.h:962:34: warning: passing argument 2 of 'down_write_nest_lock' from incompatible pointer type [-Wincompatible-pointer-types] f2fs_down_write(&sbi->cp_rwsem, &sbi->cp_mutex); ^ fs/f2fs/f2fs.h:27:55: note: in definition of macro 'f2fs_down_write' #define f2fs_down_write(x, y) down_write_nest_lock(x, y) ^ In file included from include/linux/rwsem.h:22:0, from fs/f2fs/xattr.c:21: include/linux/rwsem_rt.h:138:20: note: expected 'struct rw_semaphore *' but argument is of type 'struct mutex *' static inline void down_write_nest_lock(struct rw_semaphore *sem, Signed-off-by: Yang Shi Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a64fd12da440..de3929ea3e4e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -34,7 +34,6 @@ set_sbi_flag(sbi, SBI_NEED_FSCK); \ } \ } while (0) -#define f2fs_down_write(x, y) down_write(x) #endif /* @@ -1056,7 +1055,7 @@ static inline void f2fs_unlock_op(struct f2fs_sb_info *sbi) static inline void f2fs_lock_all(struct f2fs_sb_info *sbi) { - f2fs_down_write(&sbi->cp_rwsem, &sbi->cp_mutex); + down_write(&sbi->cp_rwsem); } static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi) -- GitLab From c5b20cbcfc509e201f0dd489470f0021e00cc5fe Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 15 May 2015 16:26:10 -0700 Subject: [PATCH 0161/5498] fs crypto: move per-file encryption from f2fs tree to fs/crypto This patch adds the renamed functions moved from the f2fs crypto files. 1. definitions for per-file encryption used by ext4 and f2fs. 2. crypto.c for encrypt/decrypt functions a. IO preparation: - fscrypt_get_ctx / fscrypt_release_ctx b. before IOs: - fscrypt_encrypt_page - fscrypt_decrypt_page - fscrypt_zeroout_range c. after IOs: - fscrypt_decrypt_bio_pages - fscrypt_pullback_bio_page - fscrypt_restore_control_page 3. policy.c supporting context management. a. For ioctls: - fscrypt_process_policy - fscrypt_get_policy b. For context permission - fscrypt_has_permitted_context - fscrypt_inherit_context 4. keyinfo.c to handle permissions - fscrypt_get_encryption_info - fscrypt_free_encryption_info 5. fname.c to support filename encryption a. general wrapper functions - fscrypt_fname_disk_to_usr - fscrypt_fname_usr_to_disk - fscrypt_setup_filename - fscrypt_free_filename b. specific filename handling functions - fscrypt_fname_alloc_buffer - fscrypt_fname_free_buffer 6. Makefile and Kconfig Cc: Al Viro Signed-off-by: Michael Halcrow Signed-off-by: Ildar Muslukhov Signed-off-by: Uday Savagaonkar Signed-off-by: Theodore Ts'o Signed-off-by: Arnd Bergmann Signed-off-by: Jaegeuk Kim --- fs/Kconfig | 2 + fs/Makefile | 1 + fs/crypto/Kconfig | 18 + fs/crypto/Makefile | 3 + fs/crypto/crypto.c | 554 +++++++++++++++++++++ fs/{f2fs/crypto_fname.c => crypto/fname.c} | 245 +++++---- fs/{f2fs/crypto_key.c => crypto/keyinfo.c} | 189 +++---- fs/crypto/policy.c | 229 +++++++++ fs/f2fs/Kconfig | 10 +- fs/f2fs/Makefile | 2 - fs/f2fs/crypto.c | 473 ------------------ fs/f2fs/crypto_policy.c | 210 -------- fs/f2fs/data.c | 31 +- fs/f2fs/dir.c | 44 +- fs/f2fs/f2fs.h | 172 ++----- fs/f2fs/f2fs_crypto.h | 151 ------ fs/f2fs/file.c | 38 +- fs/f2fs/inline.c | 4 +- fs/f2fs/inode.c | 5 +- fs/f2fs/namei.c | 56 +-- fs/f2fs/super.c | 55 +- include/linux/dcache.h | 2 + include/linux/fs.h | 9 + include/linux/fscrypto.h | 433 ++++++++++++++++ include/uapi/linux/fs.h | 18 + 25 files changed, 1632 insertions(+), 1322 deletions(-) create mode 100644 fs/crypto/Kconfig create mode 100644 fs/crypto/Makefile create mode 100644 fs/crypto/crypto.c rename fs/{f2fs/crypto_fname.c => crypto/fname.c} (59%) rename fs/{f2fs/crypto_key.c => crypto/keyinfo.c} (53%) create mode 100644 fs/crypto/policy.c delete mode 100644 fs/f2fs/crypto.c delete mode 100644 fs/f2fs/crypto_policy.c delete mode 100644 fs/f2fs/f2fs_crypto.h create mode 100644 include/linux/fscrypto.h diff --git a/fs/Kconfig b/fs/Kconfig index 664991afe0c0..9d0b7c01115a 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -61,6 +61,8 @@ config FILE_LOCKING for filesystems like NFS and for the flock() system call. Disabling this option saves about 11k. +source "fs/crypto/Kconfig" + source "fs/notify/Kconfig" source "fs/quota/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index da0bbb456d3f..4a4897b35cc9 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SIGNALFD) += signalfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o obj-$(CONFIG_AIO) += aio.o +obj-$(CONFIG_FS_ENCRYPTION) += crypto/ obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig new file mode 100644 index 000000000000..92348faf9865 --- /dev/null +++ b/fs/crypto/Kconfig @@ -0,0 +1,18 @@ +config FS_ENCRYPTION + tristate "FS Encryption (Per-file encryption)" + depends on BLOCK + select CRYPTO + select CRYPTO_AES + select CRYPTO_CBC + select CRYPTO_ECB + select CRYPTO_XTS + select CRYPTO_CTS + select CRYPTO_CTR + select CRYPTO_SHA256 + select KEYS + select ENCRYPTED_KEYS + help + Enable encryption of files and directories. This + feature is similar to ecryptfs, but it is more memory + efficient since it avoids caching the encrypted and + decrypted pages in the page cache. diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile new file mode 100644 index 000000000000..f17684c48739 --- /dev/null +++ b/fs/crypto/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o + +fscrypto-y := crypto.o fname.o policy.o keyinfo.o diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c new file mode 100644 index 000000000000..7c4b92344dcf --- /dev/null +++ b/fs/crypto/crypto.c @@ -0,0 +1,554 @@ +/* + * This contains encryption functions for per-file encryption. + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * Written by Michael Halcrow, 2014. + * + * Filename encryption additions + * Uday Savagaonkar, 2014 + * Encryption policy handling additions + * Ildar Muslukhov, 2014 + * Add fscrypt_pullback_bio_page() + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + * + * The usage of AES-XTS should conform to recommendations in NIST + * Special Publication 800-38E and IEEE P1619/D16. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int num_prealloc_crypto_pages = 32; +static unsigned int num_prealloc_crypto_ctxs = 128; + +module_param(num_prealloc_crypto_pages, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_pages, + "Number of crypto pages to preallocate"); +module_param(num_prealloc_crypto_ctxs, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_ctxs, + "Number of crypto contexts to preallocate"); + +static mempool_t *fscrypt_bounce_page_pool = NULL; + +static LIST_HEAD(fscrypt_free_ctxs); +static DEFINE_SPINLOCK(fscrypt_ctx_lock); + +static struct workqueue_struct *fscrypt_read_workqueue; +static DEFINE_MUTEX(fscrypt_init_mutex); + +static struct kmem_cache *fscrypt_ctx_cachep; +struct kmem_cache *fscrypt_info_cachep; + +/** + * fscrypt_release_ctx() - Releases an encryption context + * @ctx: The encryption context to release. + * + * If the encryption context was allocated from the pre-allocated pool, returns + * it to that pool. Else, frees it. + * + * If there's a bounce page in the context, this frees that. + */ +void fscrypt_release_ctx(struct fscrypt_ctx *ctx) +{ + unsigned long flags; + + if (ctx->flags & FS_WRITE_PATH_FL && ctx->w.bounce_page) { + mempool_free(ctx->w.bounce_page, fscrypt_bounce_page_pool); + ctx->w.bounce_page = NULL; + } + ctx->w.control_page = NULL; + if (ctx->flags & FS_CTX_REQUIRES_FREE_ENCRYPT_FL) { + kmem_cache_free(fscrypt_ctx_cachep, ctx); + } else { + spin_lock_irqsave(&fscrypt_ctx_lock, flags); + list_add(&ctx->free_list, &fscrypt_free_ctxs); + spin_unlock_irqrestore(&fscrypt_ctx_lock, flags); + } +} +EXPORT_SYMBOL(fscrypt_release_ctx); + +/** + * fscrypt_get_ctx() - Gets an encryption context + * @inode: The inode for which we are doing the crypto + * + * Allocates and initializes an encryption context. + * + * Return: An allocated and initialized encryption context on success; error + * value or NULL otherwise. + */ +struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode) +{ + struct fscrypt_ctx *ctx = NULL; + struct fscrypt_info *ci = inode->i_crypt_info; + unsigned long flags; + + if (ci == NULL) + return ERR_PTR(-ENOKEY); + + /* + * We first try getting the ctx from a free list because in + * the common case the ctx will have an allocated and + * initialized crypto tfm, so it's probably a worthwhile + * optimization. For the bounce page, we first try getting it + * from the kernel allocator because that's just about as fast + * as getting it from a list and because a cache of free pages + * should generally be a "last resort" option for a filesystem + * to be able to do its job. + */ + spin_lock_irqsave(&fscrypt_ctx_lock, flags); + ctx = list_first_entry_or_null(&fscrypt_free_ctxs, + struct fscrypt_ctx, free_list); + if (ctx) + list_del(&ctx->free_list); + spin_unlock_irqrestore(&fscrypt_ctx_lock, flags); + if (!ctx) { + ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, GFP_NOFS); + if (!ctx) + return ERR_PTR(-ENOMEM); + ctx->flags |= FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } else { + ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } + ctx->flags &= ~FS_WRITE_PATH_FL; + return ctx; +} +EXPORT_SYMBOL(fscrypt_get_ctx); + +/** + * fscrypt_complete() - The completion callback for page encryption + * @req: The asynchronous encryption request context + * @res: The result of the encryption operation + */ +static void fscrypt_complete(struct crypto_async_request *req, int res) +{ + struct fscrypt_completion_result *ecr = req->data; + + if (res == -EINPROGRESS) + return; + ecr->res = res; + complete(&ecr->completion); +} + +typedef enum { + FS_DECRYPT = 0, + FS_ENCRYPT, +} fscrypt_direction_t; + +static int do_page_crypto(struct inode *inode, + fscrypt_direction_t rw, pgoff_t index, + struct page *src_page, struct page *dest_page) +{ + u8 xts_tweak[FS_XTS_TWEAK_SIZE]; + struct ablkcipher_request *req = NULL; + DECLARE_FS_COMPLETION_RESULT(ecr); + struct scatterlist dst, src; + struct fscrypt_info *ci = inode->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", + __func__); + return -ENOMEM; + } + + ablkcipher_request_set_callback( + req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + fscrypt_complete, &ecr); + + BUILD_BUG_ON(FS_XTS_TWEAK_SIZE < sizeof(index)); + memcpy(xts_tweak, &inode->i_ino, sizeof(index)); + memset(&xts_tweak[sizeof(index)], 0, + FS_XTS_TWEAK_SIZE - sizeof(index)); + + sg_init_table(&dst, 1); + sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0); + sg_init_table(&src, 1); + sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0); + ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE, + xts_tweak); + if (rw == FS_DECRYPT) + res = crypto_ablkcipher_decrypt(req); + else + res = crypto_ablkcipher_encrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } + ablkcipher_request_free(req); + if (res) { + printk_ratelimited(KERN_ERR + "%s: crypto_ablkcipher_encrypt() returned %d\n", + __func__, res); + return res; + } + return 0; +} + +static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx) +{ + ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, + GFP_NOWAIT); + if (ctx->w.bounce_page == NULL) + return ERR_PTR(-ENOMEM); + ctx->flags |= FS_WRITE_PATH_FL; + return ctx->w.bounce_page; +} + +/** + * fscypt_encrypt_page() - Encrypts a page + * @inode: The inode for which the encryption should take place + * @plaintext_page: The page to encrypt. Must be locked. + * + * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx + * encryption context. + * + * Called on the page write path. The caller must call + * fscrypt_restore_control_page() on the returned ciphertext page to + * release the bounce buffer and the encryption context. + * + * Return: An allocated page with the encrypted content on success. Else, an + * error value or NULL. + */ +struct page *fscrypt_encrypt_page(struct inode *inode, + struct page *plaintext_page) +{ + struct fscrypt_ctx *ctx; + struct page *ciphertext_page = NULL; + int err; + + BUG_ON(!PageLocked(plaintext_page)); + + ctx = fscrypt_get_ctx(inode); + if (IS_ERR(ctx)) + return (struct page *)ctx; + + /* The encryption operation will require a bounce page. */ + ciphertext_page = alloc_bounce_page(ctx); + if (IS_ERR(ciphertext_page)) + goto errout; + + ctx->w.control_page = plaintext_page; + err = do_page_crypto(inode, FS_ENCRYPT, plaintext_page->index, + plaintext_page, ciphertext_page); + if (err) { + ciphertext_page = ERR_PTR(err); + goto errout; + } + SetPagePrivate(ciphertext_page); + set_page_private(ciphertext_page, (unsigned long)ctx); + lock_page(ciphertext_page); + return ciphertext_page; + +errout: + fscrypt_release_ctx(ctx); + return ciphertext_page; +} +EXPORT_SYMBOL(fscrypt_encrypt_page); + +/** + * f2crypt_decrypt_page() - Decrypts a page in-place + * @page: The page to decrypt. Must be locked. + * + * Decrypts page in-place using the ctx encryption context. + * + * Called from the read completion callback. + * + * Return: Zero on success, non-zero otherwise. + */ +int fscrypt_decrypt_page(struct page *page) +{ + BUG_ON(!PageLocked(page)); + + return do_page_crypto(page->mapping->host, + FS_DECRYPT, page->index, page, page); +} +EXPORT_SYMBOL(fscrypt_decrypt_page); + +int fscrypt_zeroout_range(struct inode *inode, pgoff_t lblk, + sector_t pblk, unsigned int len) +{ + struct fscrypt_ctx *ctx; + struct page *ciphertext_page = NULL; + struct bio *bio; + int ret, err = 0; + + BUG_ON(inode->i_sb->s_blocksize != PAGE_CACHE_SIZE); + + ctx = fscrypt_get_ctx(inode); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ciphertext_page = alloc_bounce_page(ctx); + if (IS_ERR(ciphertext_page)) { + err = PTR_ERR(ciphertext_page); + goto errout; + } + + while (len--) { + err = do_page_crypto(inode, FS_ENCRYPT, lblk, + ZERO_PAGE(0), ciphertext_page); + if (err) + goto errout; + + bio = bio_alloc(GFP_KERNEL, 1); + if (!bio) { + err = -ENOMEM; + goto errout; + } + bio->bi_bdev = inode->i_sb->s_bdev; + bio->bi_iter.bi_sector = + pblk << (inode->i_sb->s_blocksize_bits - 9); + ret = bio_add_page(bio, ciphertext_page, + inode->i_sb->s_blocksize, 0); + if (ret != inode->i_sb->s_blocksize) { + /* should never happen! */ + WARN_ON(1); + bio_put(bio); + err = -EIO; + goto errout; + } + err = submit_bio_wait(WRITE, bio); + bio_put(bio); + if (err) + goto errout; + lblk++; + pblk++; + } + err = 0; +errout: + fscrypt_release_ctx(ctx); + return err; +} +EXPORT_SYMBOL(fscrypt_zeroout_range); + +/* + * Validate dentries for encrypted directories to make sure we aren't + * potentially caching stale data after a key has been added or + * removed. + */ +static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct inode *dir = d_inode(dentry->d_parent); + struct fscrypt_info *ci = dir->i_crypt_info; + int dir_has_key, cached_with_key; + + if (!dir->i_sb->s_cop->is_encrypted(dir)) + return 0; + + if (ci && ci->ci_keyring_key && + (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD)))) + ci = NULL; + + /* this should eventually be an flag in d_flags */ + spin_lock(&dentry->d_lock); + cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY; + spin_unlock(&dentry->d_lock); + dir_has_key = (ci != NULL); + + /* + * If the dentry was cached without the key, and it is a + * negative dentry, it might be a valid name. We can't check + * if the key has since been made available due to locking + * reasons, so we fail the validation so ext4_lookup() can do + * this check. + * + * We also fail the validation if the dentry was created with + * the key present, but we no longer have the key, or vice versa. + */ + if ((!cached_with_key && d_is_negative(dentry)) || + (!cached_with_key && dir_has_key) || + (cached_with_key && !dir_has_key)) + return 0; + return 1; +} + +const struct dentry_operations fscrypt_d_ops = { + .d_revalidate = fscrypt_d_revalidate, +}; +EXPORT_SYMBOL(fscrypt_d_ops); + +/* + * Call fscrypt_decrypt_page on every single page, reusing the encryption + * context. + */ +static void completion_pages(struct work_struct *work) +{ + struct fscrypt_ctx *ctx = + container_of(work, struct fscrypt_ctx, r.work); + struct bio *bio = ctx->r.bio; + struct bio_vec *bv; + int i; + + bio_for_each_segment_all(bv, bio, i) { + struct page *page = bv->bv_page; + int ret = fscrypt_decrypt_page(page); + + if (ret) { + WARN_ON_ONCE(1); + SetPageError(page); + } else { + SetPageUptodate(page); + } + unlock_page(page); + } + fscrypt_release_ctx(ctx); + bio_put(bio); +} + +void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio) +{ + INIT_WORK(&ctx->r.work, completion_pages); + ctx->r.bio = bio; + queue_work(fscrypt_read_workqueue, &ctx->r.work); +} +EXPORT_SYMBOL(fscrypt_decrypt_bio_pages); + +void fscrypt_pullback_bio_page(struct page **page, bool restore) +{ + struct fscrypt_ctx *ctx; + struct page *bounce_page; + + /* The bounce data pages are unmapped. */ + if ((*page)->mapping) + return; + + /* The bounce data page is unmapped. */ + bounce_page = *page; + ctx = (struct fscrypt_ctx *)page_private(bounce_page); + + /* restore control page */ + *page = ctx->w.control_page; + + if (restore) + fscrypt_restore_control_page(bounce_page); +} +EXPORT_SYMBOL(fscrypt_pullback_bio_page); + +void fscrypt_restore_control_page(struct page *page) +{ + struct fscrypt_ctx *ctx; + + ctx = (struct fscrypt_ctx *)page_private(page); + set_page_private(page, (unsigned long)NULL); + ClearPagePrivate(page); + unlock_page(page); + fscrypt_release_ctx(ctx); +} +EXPORT_SYMBOL(fscrypt_restore_control_page); + +static void fscrypt_destroy(void) +{ + struct fscrypt_ctx *pos, *n; + + list_for_each_entry_safe(pos, n, &fscrypt_free_ctxs, free_list) + kmem_cache_free(fscrypt_ctx_cachep, pos); + INIT_LIST_HEAD(&fscrypt_free_ctxs); + mempool_destroy(fscrypt_bounce_page_pool); + fscrypt_bounce_page_pool = NULL; +} + +/** + * fscrypt_initialize() - allocate major buffers for fs encryption. + * + * We only call this when we start accessing encrypted files, since it + * results in memory getting allocated that wouldn't otherwise be used. + * + * Return: Zero on success, non-zero otherwise. + */ +int fscrypt_initialize(void) +{ + int i, res = -ENOMEM; + + if (fscrypt_bounce_page_pool) + return 0; + + mutex_lock(&fscrypt_init_mutex); + if (fscrypt_bounce_page_pool) + goto already_initialized; + + for (i = 0; i < num_prealloc_crypto_ctxs; i++) { + struct fscrypt_ctx *ctx; + + ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, GFP_NOFS); + if (!ctx) + goto fail; + list_add(&ctx->free_list, &fscrypt_free_ctxs); + } + + fscrypt_bounce_page_pool = + mempool_create_page_pool(num_prealloc_crypto_pages, 0); + if (!fscrypt_bounce_page_pool) + goto fail; + +already_initialized: + mutex_unlock(&fscrypt_init_mutex); + return 0; +fail: + fscrypt_destroy(); + mutex_unlock(&fscrypt_init_mutex); + return res; +} +EXPORT_SYMBOL(fscrypt_initialize); + +/** + * fscrypt_init() - Set up for fs encryption. + */ +static int __init fscrypt_init(void) +{ + fscrypt_read_workqueue = alloc_workqueue("fscrypt_read_queue", + WQ_HIGHPRI, 0); + if (!fscrypt_read_workqueue) + goto fail; + + fscrypt_ctx_cachep = KMEM_CACHE(fscrypt_ctx, SLAB_RECLAIM_ACCOUNT); + if (!fscrypt_ctx_cachep) + goto fail_free_queue; + + fscrypt_info_cachep = KMEM_CACHE(fscrypt_info, SLAB_RECLAIM_ACCOUNT); + if (!fscrypt_info_cachep) + goto fail_free_ctx; + + return 0; + +fail_free_ctx: + kmem_cache_destroy(fscrypt_ctx_cachep); +fail_free_queue: + destroy_workqueue(fscrypt_read_workqueue); +fail: + return -ENOMEM; +} +module_init(fscrypt_init) + +/** + * fscrypt_exit() - Shutdown the fs encryption system + */ +static void __exit fscrypt_exit(void) +{ + fscrypt_destroy(); + + if (fscrypt_read_workqueue) + destroy_workqueue(fscrypt_read_workqueue); + kmem_cache_destroy(fscrypt_ctx_cachep); + kmem_cache_destroy(fscrypt_info_cachep); +} +module_exit(fscrypt_exit); + +MODULE_LICENSE("GPL"); diff --git a/fs/f2fs/crypto_fname.c b/fs/crypto/fname.c similarity index 59% rename from fs/f2fs/crypto_fname.c rename to fs/crypto/fname.c index 6dfdc978fe45..5e4ddeeba267 100644 --- a/fs/f2fs/crypto_fname.c +++ b/fs/crypto/fname.c @@ -1,46 +1,35 @@ /* - * linux/fs/f2fs/crypto_fname.c - * - * Copied from linux/fs/ext4/crypto.c + * This contains functions for filename crypto management * * Copyright (C) 2015, Google, Inc. * Copyright (C) 2015, Motorola Mobility * - * This contains functions for filename crypto management in f2fs - * * Written by Uday Savagaonkar, 2014. - * - * Adjust f2fs dentry structure - * Jaegeuk Kim, 2015. + * Modified by Jaegeuk Kim, 2015. * * This has not yet undergone a rigorous security audit. */ + #include #include #include #include #include -#include -#include -#include -#include -#include -#include #include -#include -#include #include +#include -#include "f2fs.h" -#include "f2fs_crypto.h" -#include "xattr.h" +static u32 size_round_up(size_t size, size_t blksize) +{ + return ((size + blksize - 1) / blksize) * blksize; +} /** - * f2fs_dir_crypt_complete() - + * dir_crypt_complete() - */ -static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res) +static void dir_crypt_complete(struct crypto_async_request *req, int res) { - struct f2fs_completion_result *ecr = req->data; + struct fscrypt_completion_result *ecr = req->data; if (res == -EINPROGRESS) return; @@ -48,45 +37,35 @@ static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res) complete(&ecr->completion); } -bool f2fs_valid_filenames_enc_mode(uint32_t mode) -{ - return (mode == F2FS_ENCRYPTION_MODE_AES_256_CTS); -} - -static unsigned max_name_len(struct inode *inode) -{ - return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize : - F2FS_NAME_LEN; -} - /** - * f2fs_fname_encrypt() - + * fname_encrypt() - * * This function encrypts the input filename, and returns the length of the * ciphertext. Errors are returned as negative numbers. We trust the caller to * allocate sufficient memory to oname string. */ -static int f2fs_fname_encrypt(struct inode *inode, - const struct qstr *iname, struct f2fs_str *oname) +static int fname_encrypt(struct inode *inode, + const struct qstr *iname, struct fscrypt_str *oname) { u32 ciphertext_len; struct ablkcipher_request *req = NULL; - DECLARE_F2FS_COMPLETION_RESULT(ecr); - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + DECLARE_FS_COMPLETION_RESULT(ecr); + struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; - char iv[F2FS_CRYPTO_BLOCK_SIZE]; + char iv[FS_CRYPTO_BLOCK_SIZE]; struct scatterlist src_sg, dst_sg; - int padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); + int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); char *workbuf, buf[32], *alloc_buf = NULL; - unsigned lim = max_name_len(inode); + unsigned lim; + lim = inode->i_sb->s_cop->max_namelen(inode); if (iname->len <= 0 || iname->len > lim) return -EIO; - ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ? - F2FS_CRYPTO_BLOCK_SIZE : iname->len; - ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding); + ciphertext_len = (iname->len < FS_CRYPTO_BLOCK_SIZE) ? + FS_CRYPTO_BLOCK_SIZE : iname->len; + ciphertext_len = size_round_up(ciphertext_len, padding); ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len; if (ciphertext_len <= sizeof(buf)) { @@ -108,7 +87,7 @@ static int f2fs_fname_encrypt(struct inode *inode, } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - f2fs_dir_crypt_complete, &ecr); + dir_crypt_complete, &ecr); /* Copy the input */ memcpy(workbuf, iname->name, iname->len); @@ -116,7 +95,7 @@ static int f2fs_fname_encrypt(struct inode *inode, memset(workbuf + iname->len, 0, ciphertext_len - iname->len); /* Initialize IV */ - memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); + memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); /* Create encryption request */ sg_init_one(&src_sg, workbuf, ciphertext_len); @@ -129,33 +108,35 @@ static int f2fs_fname_encrypt(struct inode *inode, } kfree(alloc_buf); ablkcipher_request_free(req); - if (res < 0) { + if (res < 0) printk_ratelimited(KERN_ERR "%s: Error (error code %d)\n", __func__, res); - } + oname->len = ciphertext_len; return res; } /* - * f2fs_fname_decrypt() + * fname_decrypt() * This function decrypts the input filename, and returns * the length of the plaintext. * Errors are returned as negative numbers. * We trust the caller to allocate sufficient memory to oname string. */ -static int f2fs_fname_decrypt(struct inode *inode, - const struct f2fs_str *iname, struct f2fs_str *oname) +static int fname_decrypt(struct inode *inode, + const struct fscrypt_str *iname, + struct fscrypt_str *oname) { struct ablkcipher_request *req = NULL; - DECLARE_F2FS_COMPLETION_RESULT(ecr); + DECLARE_FS_COMPLETION_RESULT(ecr); struct scatterlist src_sg, dst_sg; - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; - char iv[F2FS_CRYPTO_BLOCK_SIZE]; - unsigned lim = max_name_len(inode); + char iv[FS_CRYPTO_BLOCK_SIZE]; + unsigned lim; + lim = inode->i_sb->s_cop->max_namelen(inode); if (iname->len <= 0 || iname->len > lim) return -EIO; @@ -168,10 +149,10 @@ static int f2fs_fname_decrypt(struct inode *inode, } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - f2fs_dir_crypt_complete, &ecr); + dir_crypt_complete, &ecr); /* Initialize IV */ - memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); + memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); /* Create decryption request */ sg_init_one(&src_sg, iname->name, iname->len); @@ -185,8 +166,7 @@ static int f2fs_fname_decrypt(struct inode *inode, ablkcipher_request_free(req); if (res < 0) { printk_ratelimited(KERN_ERR - "%s: Error in f2fs_fname_decrypt (error code %d)\n", - __func__, res); + "%s: Error (error code %d)\n", __func__, res); return res; } @@ -198,7 +178,7 @@ static const char *lookup_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; /** - * f2fs_fname_encode_digest() - + * digest_encode() - * * Encodes the input digest using characters from the set [a-zA-Z0-9_+]. * The encoded string is roughly 4/3 times the size of the input string. @@ -247,156 +227,152 @@ static int digest_decode(const char *src, int len, char *dst) return cp - dst; } -/** - * f2fs_fname_crypto_round_up() - - * - * Return: The next multiple of block size - */ -u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize) -{ - return ((size + blksize - 1) / blksize) * blksize; -} - -unsigned f2fs_fname_encrypted_size(struct inode *inode, u32 ilen) +u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen) { - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; int padding = 32; + struct fscrypt_info *ci = inode->i_crypt_info; if (ci) - padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); - if (ilen < F2FS_CRYPTO_BLOCK_SIZE) - ilen = F2FS_CRYPTO_BLOCK_SIZE; - return f2fs_fname_crypto_round_up(ilen, padding); + padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); + if (ilen < FS_CRYPTO_BLOCK_SIZE) + ilen = FS_CRYPTO_BLOCK_SIZE; + return size_round_up(ilen, padding); } +EXPORT_SYMBOL(fscrypt_fname_encrypted_size); /** - * f2fs_fname_crypto_alloc_obuff() - + * fscrypt_fname_crypto_alloc_obuff() - * * Allocates an output buffer that is sufficient for the crypto operation * specified by the context and the direction. */ -int f2fs_fname_crypto_alloc_buffer(struct inode *inode, - u32 ilen, struct f2fs_str *crypto_str) +int fscrypt_fname_alloc_buffer(struct inode *inode, + u32 ilen, struct fscrypt_str *crypto_str) { - unsigned int olen = f2fs_fname_encrypted_size(inode, ilen); + unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen); crypto_str->len = olen; - if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2) - olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2; - /* Allocated buffer can hold one more character to null-terminate the - * string */ + if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2) + olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2; + /* + * Allocated buffer can hold one more character to null-terminate the + * string + */ crypto_str->name = kmalloc(olen + 1, GFP_NOFS); if (!(crypto_str->name)) return -ENOMEM; return 0; } +EXPORT_SYMBOL(fscrypt_fname_alloc_buffer); /** - * f2fs_fname_crypto_free_buffer() - + * fscrypt_fname_crypto_free_buffer() - * * Frees the buffer allocated for crypto operation. */ -void f2fs_fname_crypto_free_buffer(struct f2fs_str *crypto_str) +void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str) { if (!crypto_str) return; kfree(crypto_str->name); crypto_str->name = NULL; } +EXPORT_SYMBOL(fscrypt_fname_free_buffer); /** - * f2fs_fname_disk_to_usr() - converts a filename from disk space to user space + * fscrypt_fname_disk_to_usr() - converts a filename from disk space to user + * space */ -int f2fs_fname_disk_to_usr(struct inode *inode, - f2fs_hash_t *hash, - const struct f2fs_str *iname, - struct f2fs_str *oname) +int fscrypt_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + const struct fscrypt_str *iname, + struct fscrypt_str *oname) { const struct qstr qname = FSTR_TO_QSTR(iname); char buf[24]; int ret; - if (is_dot_dotdot(&qname)) { + if (fscrypt_is_dot_dotdot(&qname)) { oname->name[0] = '.'; oname->name[iname->len - 1] = '.'; oname->len = iname->len; return oname->len; } - if (iname->len < F2FS_CRYPTO_BLOCK_SIZE) { - printk("encrypted inode too small"); + + if (iname->len < FS_CRYPTO_BLOCK_SIZE) return -EUCLEAN; - } - if (F2FS_I(inode)->i_crypt_info) - return f2fs_fname_decrypt(inode, iname, oname); - if (iname->len <= F2FS_FNAME_CRYPTO_DIGEST_SIZE) { + if (inode->i_crypt_info) + return fname_decrypt(inode, iname, oname); + + if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) { ret = digest_encode(iname->name, iname->len, oname->name); oname->len = ret; return ret; } if (hash) { - memcpy(buf, hash, 4); - memset(buf + 4, 0, 4); - } else + memcpy(buf, &hash, 4); + memcpy(buf + 4, &minor_hash, 4); + } else { memset(buf, 0, 8); + } memcpy(buf + 8, iname->name + iname->len - 16, 16); oname->name[0] = '_'; ret = digest_encode(buf, 24, oname->name + 1); oname->len = ret + 1; return ret + 1; } +EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); /** - * f2fs_fname_usr_to_disk() - converts a filename from user space to disk space + * fscrypt_fname_usr_to_disk() - converts a filename from user space to disk + * space */ -int f2fs_fname_usr_to_disk(struct inode *inode, +int fscrypt_fname_usr_to_disk(struct inode *inode, const struct qstr *iname, - struct f2fs_str *oname) + struct fscrypt_str *oname) { - int res; - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; - - if (is_dot_dotdot(iname)) { + if (fscrypt_is_dot_dotdot(iname)) { oname->name[0] = '.'; oname->name[iname->len - 1] = '.'; oname->len = iname->len; return oname->len; } - - if (ci) { - res = f2fs_fname_encrypt(inode, iname, oname); - return res; - } - /* Without a proper key, a user is not allowed to modify the filenames + if (inode->i_crypt_info) + return fname_encrypt(inode, iname, oname); + /* + * Without a proper key, a user is not allowed to modify the filenames * in a directory. Consequently, a user space name cannot be mapped to - * a disk-space name */ + * a disk-space name + */ return -EACCES; } +EXPORT_SYMBOL(fscrypt_fname_usr_to_disk); -int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname, - int lookup, struct f2fs_filename *fname) +int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct fscrypt_name *fname) { - struct f2fs_crypt_info *ci; int ret = 0, bigname = 0; - memset(fname, 0, sizeof(struct f2fs_filename)); + memset(fname, 0, sizeof(struct fscrypt_name)); fname->usr_fname = iname; - if (!f2fs_encrypted_inode(dir) || is_dot_dotdot(iname)) { + if (!dir->i_sb->s_cop->is_encrypted(dir) || + fscrypt_is_dot_dotdot(iname)) { fname->disk_name.name = (unsigned char *)iname->name; fname->disk_name.len = iname->len; return 0; } - ret = f2fs_get_encryption_info(dir); - if (ret) + ret = get_crypt_info(dir); + if (ret && ret != -EOPNOTSUPP) return ret; - ci = F2FS_I(dir)->i_crypt_info; - if (ci) { - ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len, - &fname->crypto_buf); + + if (dir->i_crypt_info) { + ret = fscrypt_fname_alloc_buffer(dir, iname->len, + &fname->crypto_buf); if (ret < 0) return ret; - ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf); + ret = fname_encrypt(dir, iname, &fname->crypto_buf); if (ret < 0) goto errout; fname->disk_name.name = fname->crypto_buf.name; @@ -406,18 +382,19 @@ int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname, if (!lookup) return -EACCES; - /* We don't have the key and we are doing a lookup; decode the + /* + * We don't have the key and we are doing a lookup; decode the * user-supplied name */ if (iname->name[0] == '_') bigname = 1; - if ((bigname && (iname->len != 33)) || - (!bigname && (iname->len > 43))) + if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43))) return -ENOENT; fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); if (fname->crypto_buf.name == NULL) return -ENOMEM; + ret = digest_decode(iname->name + bigname, iname->len - bigname, fname->crypto_buf.name); if (ret < 0) { @@ -427,20 +404,24 @@ int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname, fname->crypto_buf.len = ret; if (bigname) { memcpy(&fname->hash, fname->crypto_buf.name, 4); + memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4); } else { fname->disk_name.name = fname->crypto_buf.name; fname->disk_name.len = fname->crypto_buf.len; } return 0; + errout: - f2fs_fname_crypto_free_buffer(&fname->crypto_buf); + fscrypt_fname_free_buffer(&fname->crypto_buf); return ret; } +EXPORT_SYMBOL(fscrypt_setup_filename); -void f2fs_fname_free_filename(struct f2fs_filename *fname) +void fscrypt_free_filename(struct fscrypt_name *fname) { kfree(fname->crypto_buf.name); fname->crypto_buf.name = NULL; fname->usr_fname = NULL; fname->disk_name.name = NULL; } +EXPORT_SYMBOL(fscrypt_free_filename); diff --git a/fs/f2fs/crypto_key.c b/fs/crypto/keyinfo.c similarity index 53% rename from fs/f2fs/crypto_key.c rename to fs/crypto/keyinfo.c index 4ef6b6ccf319..aea1925f4f7a 100644 --- a/fs/f2fs/crypto_key.c +++ b/fs/crypto/keyinfo.c @@ -1,28 +1,24 @@ /* - * linux/fs/f2fs/crypto_key.c - * - * Copied from linux/fs/f2fs/crypto_key.c + * key management facility for FS encryption support. * * Copyright (C) 2015, Google, Inc. * - * This contains encryption key functions for f2fs + * This contains encryption key functions. * * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015. */ + #include #include #include #include #include #include -#include - -#include "f2fs.h" -#include "xattr.h" +#include static void derive_crypt_complete(struct crypto_async_request *req, int rc) { - struct f2fs_completion_result *ecr = req->data; + struct fscrypt_completion_result *ecr = req->data; if (rc == -EINPROGRESS) return; @@ -32,20 +28,20 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) } /** - * f2fs_derive_key_aes() - Derive a key using AES-128-ECB + * derive_key_aes() - Derive a key using AES-128-ECB * @deriving_key: Encryption key used for derivation. * @source_key: Source key to which to apply derivation. * @derived_key: Derived key. * * Return: Zero on success; non-zero otherwise. */ -static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE], - char source_key[F2FS_AES_256_XTS_KEY_SIZE], - char derived_key[F2FS_AES_256_XTS_KEY_SIZE]) +static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], + u8 source_key[FS_AES_256_XTS_KEY_SIZE], + u8 derived_key[FS_AES_256_XTS_KEY_SIZE]) { int res = 0; struct ablkcipher_request *req = NULL; - DECLARE_F2FS_COMPLETION_RESULT(ecr); + DECLARE_FS_COMPLETION_RESULT(ecr); struct scatterlist src_sg, dst_sg; struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, 0); @@ -65,14 +61,14 @@ static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE], CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, derive_crypt_complete, &ecr); res = crypto_ablkcipher_setkey(tfm, deriving_key, - F2FS_AES_128_ECB_KEY_SIZE); + FS_AES_128_ECB_KEY_SIZE); if (res < 0) goto out; - sg_init_one(&src_sg, source_key, F2FS_AES_256_XTS_KEY_SIZE); - sg_init_one(&dst_sg, derived_key, F2FS_AES_256_XTS_KEY_SIZE); + sg_init_one(&src_sg, source_key, FS_AES_256_XTS_KEY_SIZE); + sg_init_one(&dst_sg, derived_key, FS_AES_256_XTS_KEY_SIZE); ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, - F2FS_AES_256_XTS_KEY_SIZE, NULL); + FS_AES_256_XTS_KEY_SIZE, NULL); res = crypto_ablkcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { wait_for_completion(&ecr.completion); @@ -86,71 +82,61 @@ out: return res; } -static void f2fs_free_crypt_info(struct f2fs_crypt_info *ci) +static void put_crypt_info(struct fscrypt_info *ci) { if (!ci) return; - key_put(ci->ci_keyring_key); + if (ci->ci_keyring_key) + key_put(ci->ci_keyring_key); crypto_free_ablkcipher(ci->ci_ctfm); - kmem_cache_free(f2fs_crypt_info_cachep, ci); -} - -void f2fs_free_encryption_info(struct inode *inode, struct f2fs_crypt_info *ci) -{ - struct f2fs_inode_info *fi = F2FS_I(inode); - struct f2fs_crypt_info *prev; - - if (ci == NULL) - ci = ACCESS_ONCE(fi->i_crypt_info); - if (ci == NULL) - return; - prev = cmpxchg(&fi->i_crypt_info, ci, NULL); - if (prev != ci) - return; - - f2fs_free_crypt_info(ci); + kmem_cache_free(fscrypt_info_cachep, ci); } -int _f2fs_get_encryption_info(struct inode *inode) +int get_crypt_info(struct inode *inode) { - struct f2fs_inode_info *fi = F2FS_I(inode); - struct f2fs_crypt_info *crypt_info; - char full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + - (F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1]; + struct fscrypt_info *crypt_info; + u8 full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE + + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1]; struct key *keyring_key = NULL; - struct f2fs_encryption_key *master_key; - struct f2fs_encryption_context ctx; + struct fscrypt_key *master_key; + struct fscrypt_context ctx; struct user_key_payload *ukp; struct crypto_ablkcipher *ctfm; const char *cipher_str; - char raw_key[F2FS_MAX_KEY_SIZE]; - char mode; + u8 raw_key[FS_MAX_KEY_SIZE]; + u8 mode; int res; - res = f2fs_crypto_initialize(); + res = fscrypt_initialize(); if (res) return res; + + if (!inode->i_sb->s_cop->get_context) + return -EOPNOTSUPP; retry: - crypt_info = ACCESS_ONCE(fi->i_crypt_info); + crypt_info = ACCESS_ONCE(inode->i_crypt_info); if (crypt_info) { if (!crypt_info->ci_keyring_key || key_validate(crypt_info->ci_keyring_key) == 0) return 0; - f2fs_free_encryption_info(inode, crypt_info); + fscrypt_put_encryption_info(inode, crypt_info); goto retry; } - res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, - F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, - &ctx, sizeof(ctx), NULL); - if (res < 0) - return res; - else if (res != sizeof(ctx)) + res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); + if (res < 0) { + if (!fscrypt_dummy_context_enabled(inode)) + return res; + ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS; + ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; + ctx.flags = 0; + } else if (res != sizeof(ctx)) { return -EINVAL; + } res = 0; - crypt_info = kmem_cache_alloc(f2fs_crypt_info_cachep, GFP_NOFS); + crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS); if (!crypt_info) return -ENOMEM; @@ -169,27 +155,30 @@ retry: BUG(); switch (mode) { - case F2FS_ENCRYPTION_MODE_AES_256_XTS: + case FS_ENCRYPTION_MODE_AES_256_XTS: cipher_str = "xts(aes)"; break; - case F2FS_ENCRYPTION_MODE_AES_256_CTS: + case FS_ENCRYPTION_MODE_AES_256_CTS: cipher_str = "cts(cbc(aes))"; break; default: printk_once(KERN_WARNING - "f2fs: unsupported key mode %d (ino %u)\n", - mode, (unsigned) inode->i_ino); + "%s: unsupported key mode %d (ino %u)\n", + __func__, mode, (unsigned) inode->i_ino); res = -ENOKEY; goto out; } - - memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX, - F2FS_KEY_DESC_PREFIX_SIZE); - sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE, - "%*phN", F2FS_KEY_DESCRIPTOR_SIZE, + if (fscrypt_dummy_context_enabled(inode)) { + memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE); + goto got_key; + } + memcpy(full_key_descriptor, FS_KEY_DESC_PREFIX, + FS_KEY_DESC_PREFIX_SIZE); + sprintf(full_key_descriptor + FS_KEY_DESC_PREFIX_SIZE, + "%*phN", FS_KEY_DESCRIPTOR_SIZE, ctx.master_key_descriptor); - full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + - (2 * F2FS_KEY_DESCRIPTOR_SIZE)] = '\0'; + full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE + + (2 * FS_KEY_DESCRIPTOR_SIZE)] = '\0'; keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); if (IS_ERR(keyring_key)) { res = PTR_ERR(keyring_key); @@ -198,34 +187,34 @@ retry: } crypt_info->ci_keyring_key = keyring_key; if (keyring_key->type != &key_type_logon) { - printk_once(KERN_WARNING "f2fs: key type must be logon\n"); + printk_once(KERN_WARNING + "%s: key type must be logon\n", __func__); res = -ENOKEY; goto out; } down_read(&keyring_key->sem); ukp = ((struct user_key_payload *)keyring_key->payload.data); - if (ukp->datalen != sizeof(struct f2fs_encryption_key)) { + if (ukp->datalen != sizeof(struct fscrypt_key)) { res = -EINVAL; up_read(&keyring_key->sem); goto out; } - master_key = (struct f2fs_encryption_key *)ukp->data; - BUILD_BUG_ON(F2FS_AES_128_ECB_KEY_SIZE != - F2FS_KEY_DERIVATION_NONCE_SIZE); - if (master_key->size != F2FS_AES_256_XTS_KEY_SIZE) { + master_key = (struct fscrypt_key *)ukp->data; + BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE); + + if (master_key->size != FS_AES_256_XTS_KEY_SIZE) { printk_once(KERN_WARNING - "f2fs: key size incorrect: %d\n", - master_key->size); + "%s: key size incorrect: %d\n", + __func__, master_key->size); res = -ENOKEY; up_read(&keyring_key->sem); goto out; } - res = f2fs_derive_key_aes(ctx.nonce, master_key->raw, - raw_key); + res = derive_key_aes(ctx.nonce, master_key->raw, raw_key); up_read(&keyring_key->sem); if (res) goto out; - +got_key: ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); if (!ctfm || IS_ERR(ctfm)) { res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; @@ -237,31 +226,53 @@ retry: crypt_info->ci_ctfm = ctfm; crypto_ablkcipher_clear_flags(ctfm, ~0); crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), - CRYPTO_TFM_REQ_WEAK_KEY); - res = crypto_ablkcipher_setkey(ctfm, raw_key, - f2fs_encryption_key_size(mode)); + CRYPTO_TFM_REQ_WEAK_KEY); + res = crypto_ablkcipher_setkey(ctfm, raw_key, fscrypt_key_size(mode)); if (res) goto out; memzero_explicit(raw_key, sizeof(raw_key)); - if (cmpxchg(&fi->i_crypt_info, NULL, crypt_info) != NULL) { - f2fs_free_crypt_info(crypt_info); + if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) != NULL) { + put_crypt_info(crypt_info); goto retry; } return 0; out: - if (res == -ENOKEY && !S_ISREG(inode->i_mode)) + if (res == -ENOKEY) res = 0; - - f2fs_free_crypt_info(crypt_info); + put_crypt_info(crypt_info); memzero_explicit(raw_key, sizeof(raw_key)); return res; } -int f2fs_has_encryption_key(struct inode *inode) +void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci) { - struct f2fs_inode_info *fi = F2FS_I(inode); + struct fscrypt_info *prev; + + if (ci == NULL) + ci = ACCESS_ONCE(inode->i_crypt_info); + if (ci == NULL) + return; - return (fi->i_crypt_info != NULL); + prev = cmpxchg(&inode->i_crypt_info, ci, NULL); + if (prev != ci) + return; + + put_crypt_info(ci); +} +EXPORT_SYMBOL(fscrypt_put_encryption_info); + +int fscrypt_get_encryption_info(struct inode *inode) +{ + struct fscrypt_info *ci = inode->i_crypt_info; + + if (!ci || + (ci->ci_keyring_key && + (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD))))) + return get_crypt_info(inode); + return 0; } +EXPORT_SYMBOL(fscrypt_get_encryption_info); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c new file mode 100644 index 000000000000..0f9961eede1e --- /dev/null +++ b/fs/crypto/policy.c @@ -0,0 +1,229 @@ +/* + * Encryption policy functions for per-file encryption support. + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ + +#include +#include +#include + +static int inode_has_encryption_context(struct inode *inode) +{ + if (!inode->i_sb->s_cop->get_context) + return 0; + return (inode->i_sb->s_cop->get_context(inode, NULL, 0L) > 0); +} + +/* + * check whether the policy is consistent with the encryption context + * for the inode + */ +static int is_encryption_context_consistent_with_policy(struct inode *inode, + const struct fscrypt_policy *policy) +{ + struct fscrypt_context ctx; + int res; + + if (!inode->i_sb->s_cop->get_context) + return 0; + + res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); + if (res != sizeof(ctx)) + return 0; + + return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor, + FS_KEY_DESCRIPTOR_SIZE) == 0 && + (ctx.flags == policy->flags) && + (ctx.contents_encryption_mode == + policy->contents_encryption_mode) && + (ctx.filenames_encryption_mode == + policy->filenames_encryption_mode)); +} + +static int create_encryption_context_from_policy(struct inode *inode, + const struct fscrypt_policy *policy) +{ + struct fscrypt_context ctx; + int res; + + if (!inode->i_sb->s_cop->set_context) + return -EOPNOTSUPP; + + if (inode->i_sb->s_cop->prepare_context) { + res = inode->i_sb->s_cop->prepare_context(inode); + if (res) + return res; + } + + ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; + memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, + FS_KEY_DESCRIPTOR_SIZE); + + if (!fscrypt_valid_contents_enc_mode( + policy->contents_encryption_mode)) { + printk(KERN_WARNING + "%s: Invalid contents encryption mode %d\n", __func__, + policy->contents_encryption_mode); + return -EINVAL; + } + + if (!fscrypt_valid_filenames_enc_mode( + policy->filenames_encryption_mode)) { + printk(KERN_WARNING + "%s: Invalid filenames encryption mode %d\n", __func__, + policy->filenames_encryption_mode); + return -EINVAL; + } + + if (policy->flags & ~FS_POLICY_FLAGS_VALID) + return -EINVAL; + + ctx.contents_encryption_mode = policy->contents_encryption_mode; + ctx.filenames_encryption_mode = policy->filenames_encryption_mode; + ctx.flags = policy->flags; + BUILD_BUG_ON(sizeof(ctx.nonce) != FS_KEY_DERIVATION_NONCE_SIZE); + get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); + + return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL); +} + +int fscrypt_process_policy(struct inode *inode, + const struct fscrypt_policy *policy) +{ + if (policy->version != 0) + return -EINVAL; + + if (!inode_has_encryption_context(inode)) { + if (!inode->i_sb->s_cop->empty_dir) + return -EOPNOTSUPP; + if (!inode->i_sb->s_cop->empty_dir(inode)) + return -ENOTEMPTY; + return create_encryption_context_from_policy(inode, policy); + } + + if (is_encryption_context_consistent_with_policy(inode, policy)) + return 0; + + printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n", + __func__); + return -EINVAL; +} +EXPORT_SYMBOL(fscrypt_process_policy); + +int fscrypt_get_policy(struct inode *inode, struct fscrypt_policy *policy) +{ + struct fscrypt_context ctx; + int res; + + if (!inode->i_sb->s_cop->get_context || + !inode->i_sb->s_cop->is_encrypted(inode)) + return -ENODATA; + + res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); + if (res != sizeof(ctx)) + return -ENODATA; + if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) + return -EINVAL; + + policy->version = 0; + policy->contents_encryption_mode = ctx.contents_encryption_mode; + policy->filenames_encryption_mode = ctx.filenames_encryption_mode; + policy->flags = ctx.flags; + memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor, + FS_KEY_DESCRIPTOR_SIZE); + return 0; +} +EXPORT_SYMBOL(fscrypt_get_policy); + +int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) +{ + struct fscrypt_info *parent_ci, *child_ci; + int res; + + if ((parent == NULL) || (child == NULL)) { + printk(KERN_ERR "parent %p child %p\n", parent, child); + BUG_ON(1); + } + + /* no restrictions if the parent directory is not encrypted */ + if (!parent->i_sb->s_cop->is_encrypted(parent)) + return 1; + /* if the child directory is not encrypted, this is always a problem */ + if (!parent->i_sb->s_cop->is_encrypted(child)) + return 0; + res = fscrypt_get_encryption_info(parent); + if (res) + return 0; + res = fscrypt_get_encryption_info(child); + if (res) + return 0; + parent_ci = parent->i_crypt_info; + child_ci = child->i_crypt_info; + if (!parent_ci && !child_ci) + return 1; + if (!parent_ci || !child_ci) + return 0; + + return (memcmp(parent_ci->ci_master_key, + child_ci->ci_master_key, + FS_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ci->ci_data_mode == child_ci->ci_data_mode) && + (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && + (parent_ci->ci_flags == child_ci->ci_flags)); +} +EXPORT_SYMBOL(fscrypt_has_permitted_context); + +/** + * fscrypt_inherit_context() - Sets a child context from its parent + * @parent: Parent inode from which the context is inherited. + * @child: Child inode that inherits the context from @parent. + * @fs_data: private data given by FS. + * @preload: preload child i_crypt_info + * + * Return: Zero on success, non-zero otherwise + */ +int fscrypt_inherit_context(struct inode *parent, struct inode *child, + void *fs_data, bool preload) +{ + struct fscrypt_context ctx; + struct fscrypt_info *ci; + int res; + + if (!parent->i_sb->s_cop->set_context) + return -EOPNOTSUPP; + + res = fscrypt_get_encryption_info(parent); + if (res < 0) + return res; + + ci = parent->i_crypt_info; + if (ci == NULL) + return -ENOKEY; + + ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; + if (fscrypt_dummy_context_enabled(parent)) { + ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS; + ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; + ctx.flags = 0; + memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE); + res = 0; + } else { + ctx.contents_encryption_mode = ci->ci_data_mode; + ctx.filenames_encryption_mode = ci->ci_filename_mode; + ctx.flags = ci->ci_flags; + memcpy(ctx.master_key_descriptor, ci->ci_master_key, + FS_KEY_DESCRIPTOR_SIZE); + } + get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); + res = parent->i_sb->s_cop->set_context(child, &ctx, + sizeof(ctx), fs_data); + if (res) + return res; + return preload ? fscrypt_get_encryption_info(child): 0; +} +EXPORT_SYMBOL(fscrypt_inherit_context); diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig index b0a9dc929f88..402792bae503 100644 --- a/fs/f2fs/Kconfig +++ b/fs/f2fs/Kconfig @@ -76,15 +76,7 @@ config F2FS_FS_ENCRYPTION bool "F2FS Encryption" depends on F2FS_FS depends on F2FS_FS_XATTR - select CRYPTO_AES - select CRYPTO_CBC - select CRYPTO_ECB - select CRYPTO_XTS - select CRYPTO_CTS - select CRYPTO_CTR - select CRYPTO_SHA256 - select KEYS - select ENCRYPTED_KEYS + select FS_ENCRYPTION help Enable encryption of f2fs files and directories. This feature is similar to ecryptfs, but it is more memory diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile index 08e101ed914c..ca949ea7c02f 100644 --- a/fs/f2fs/Makefile +++ b/fs/f2fs/Makefile @@ -7,5 +7,3 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o -f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o \ - crypto_key.o crypto_fname.o diff --git a/fs/f2fs/crypto.c b/fs/f2fs/crypto.c deleted file mode 100644 index 3ef37868d0c7..000000000000 --- a/fs/f2fs/crypto.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * linux/fs/f2fs/crypto.c - * - * Copied from linux/fs/ext4/crypto.c - * - * Copyright (C) 2015, Google, Inc. - * Copyright (C) 2015, Motorola Mobility - * - * This contains encryption functions for f2fs - * - * Written by Michael Halcrow, 2014. - * - * Filename encryption additions - * Uday Savagaonkar, 2014 - * Encryption policy handling additions - * Ildar Muslukhov, 2014 - * Remove ext4_encrypted_zeroout(), - * add f2fs_restore_and_release_control_page() - * Jaegeuk Kim, 2015. - * - * This has not yet undergone a rigorous security audit. - * - * The usage of AES-XTS should conform to recommendations in NIST - * Special Publication 800-38E and IEEE P1619/D16. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "xattr.h" - -/* Encryption added and removed here! (L: */ - -static unsigned int num_prealloc_crypto_pages = 32; -static unsigned int num_prealloc_crypto_ctxs = 128; - -module_param(num_prealloc_crypto_pages, uint, 0444); -MODULE_PARM_DESC(num_prealloc_crypto_pages, - "Number of crypto pages to preallocate"); -module_param(num_prealloc_crypto_ctxs, uint, 0444); -MODULE_PARM_DESC(num_prealloc_crypto_ctxs, - "Number of crypto contexts to preallocate"); - -static mempool_t *f2fs_bounce_page_pool; - -static LIST_HEAD(f2fs_free_crypto_ctxs); -static DEFINE_SPINLOCK(f2fs_crypto_ctx_lock); - -static struct workqueue_struct *f2fs_read_workqueue; -static DEFINE_MUTEX(crypto_init); - -static struct kmem_cache *f2fs_crypto_ctx_cachep; -struct kmem_cache *f2fs_crypt_info_cachep; - -/** - * f2fs_release_crypto_ctx() - Releases an encryption context - * @ctx: The encryption context to release. - * - * If the encryption context was allocated from the pre-allocated pool, returns - * it to that pool. Else, frees it. - * - * If there's a bounce page in the context, this frees that. - */ -void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx) -{ - unsigned long flags; - - if (ctx->flags & F2FS_WRITE_PATH_FL && ctx->w.bounce_page) { - mempool_free(ctx->w.bounce_page, f2fs_bounce_page_pool); - ctx->w.bounce_page = NULL; - } - ctx->w.control_page = NULL; - if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) { - kmem_cache_free(f2fs_crypto_ctx_cachep, ctx); - } else { - spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags); - list_add(&ctx->free_list, &f2fs_free_crypto_ctxs); - spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags); - } -} - -/** - * f2fs_get_crypto_ctx() - Gets an encryption context - * @inode: The inode for which we are doing the crypto - * - * Allocates and initializes an encryption context. - * - * Return: An allocated and initialized encryption context on success; error - * value or NULL otherwise. - */ -struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode) -{ - struct f2fs_crypto_ctx *ctx = NULL; - unsigned long flags; - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; - - if (ci == NULL) - return ERR_PTR(-ENOKEY); - - /* - * We first try getting the ctx from a free list because in - * the common case the ctx will have an allocated and - * initialized crypto tfm, so it's probably a worthwhile - * optimization. For the bounce page, we first try getting it - * from the kernel allocator because that's just about as fast - * as getting it from a list and because a cache of free pages - * should generally be a "last resort" option for a filesystem - * to be able to do its job. - */ - spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags); - ctx = list_first_entry_or_null(&f2fs_free_crypto_ctxs, - struct f2fs_crypto_ctx, free_list); - if (ctx) - list_del(&ctx->free_list); - spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags); - if (!ctx) { - ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_NOFS); - if (!ctx) - return ERR_PTR(-ENOMEM); - ctx->flags |= F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL; - } else { - ctx->flags &= ~F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL; - } - ctx->flags &= ~F2FS_WRITE_PATH_FL; - return ctx; -} - -/* - * Call f2fs_decrypt on every single page, reusing the encryption - * context. - */ -static void completion_pages(struct work_struct *work) -{ - struct f2fs_crypto_ctx *ctx = - container_of(work, struct f2fs_crypto_ctx, r.work); - struct bio *bio = ctx->r.bio; - struct bio_vec *bv; - int i; - - bio_for_each_segment_all(bv, bio, i) { - struct page *page = bv->bv_page; - int ret = f2fs_decrypt(page); - - if (ret) { - WARN_ON_ONCE(1); - SetPageError(page); - } else - SetPageUptodate(page); - unlock_page(page); - } - f2fs_release_crypto_ctx(ctx); - bio_put(bio); -} - -void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *ctx, struct bio *bio) -{ - INIT_WORK(&ctx->r.work, completion_pages); - ctx->r.bio = bio; - queue_work(f2fs_read_workqueue, &ctx->r.work); -} - -static void f2fs_crypto_destroy(void) -{ - struct f2fs_crypto_ctx *pos, *n; - - list_for_each_entry_safe(pos, n, &f2fs_free_crypto_ctxs, free_list) - kmem_cache_free(f2fs_crypto_ctx_cachep, pos); - INIT_LIST_HEAD(&f2fs_free_crypto_ctxs); - if (f2fs_bounce_page_pool) - mempool_destroy(f2fs_bounce_page_pool); - f2fs_bounce_page_pool = NULL; -} - -/** - * f2fs_crypto_initialize() - Set up for f2fs encryption. - * - * We only call this when we start accessing encrypted files, since it - * results in memory getting allocated that wouldn't otherwise be used. - * - * Return: Zero on success, non-zero otherwise. - */ -int f2fs_crypto_initialize(void) -{ - int i, res = -ENOMEM; - - if (f2fs_bounce_page_pool) - return 0; - - mutex_lock(&crypto_init); - if (f2fs_bounce_page_pool) - goto already_initialized; - - for (i = 0; i < num_prealloc_crypto_ctxs; i++) { - struct f2fs_crypto_ctx *ctx; - - ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_KERNEL); - if (!ctx) - goto fail; - list_add(&ctx->free_list, &f2fs_free_crypto_ctxs); - } - - /* must be allocated at the last step to avoid race condition above */ - f2fs_bounce_page_pool = - mempool_create_page_pool(num_prealloc_crypto_pages, 0); - if (!f2fs_bounce_page_pool) - goto fail; - -already_initialized: - mutex_unlock(&crypto_init); - return 0; -fail: - f2fs_crypto_destroy(); - mutex_unlock(&crypto_init); - return res; -} - -/** - * f2fs_exit_crypto() - Shutdown the f2fs encryption system - */ -void f2fs_exit_crypto(void) -{ - f2fs_crypto_destroy(); - - if (f2fs_read_workqueue) - destroy_workqueue(f2fs_read_workqueue); - if (f2fs_crypto_ctx_cachep) - kmem_cache_destroy(f2fs_crypto_ctx_cachep); - if (f2fs_crypt_info_cachep) - kmem_cache_destroy(f2fs_crypt_info_cachep); -} - -int __init f2fs_init_crypto(void) -{ - int res = -ENOMEM; - - f2fs_read_workqueue = alloc_workqueue("f2fs_crypto", WQ_HIGHPRI, 0); - if (!f2fs_read_workqueue) - goto fail; - - f2fs_crypto_ctx_cachep = KMEM_CACHE(f2fs_crypto_ctx, - SLAB_RECLAIM_ACCOUNT); - if (!f2fs_crypto_ctx_cachep) - goto fail; - - f2fs_crypt_info_cachep = KMEM_CACHE(f2fs_crypt_info, - SLAB_RECLAIM_ACCOUNT); - if (!f2fs_crypt_info_cachep) - goto fail; - - return 0; -fail: - f2fs_exit_crypto(); - return res; -} - -void f2fs_restore_and_release_control_page(struct page **page) -{ - struct f2fs_crypto_ctx *ctx; - struct page *bounce_page; - - /* The bounce data pages are unmapped. */ - if ((*page)->mapping) - return; - - /* The bounce data page is unmapped. */ - bounce_page = *page; - ctx = (struct f2fs_crypto_ctx *)page_private(bounce_page); - - /* restore control page */ - *page = ctx->w.control_page; - - f2fs_restore_control_page(bounce_page); -} - -void f2fs_restore_control_page(struct page *data_page) -{ - struct f2fs_crypto_ctx *ctx = - (struct f2fs_crypto_ctx *)page_private(data_page); - - set_page_private(data_page, (unsigned long)NULL); - ClearPagePrivate(data_page); - unlock_page(data_page); - f2fs_release_crypto_ctx(ctx); -} - -/** - * f2fs_crypt_complete() - The completion callback for page encryption - * @req: The asynchronous encryption request context - * @res: The result of the encryption operation - */ -static void f2fs_crypt_complete(struct crypto_async_request *req, int res) -{ - struct f2fs_completion_result *ecr = req->data; - - if (res == -EINPROGRESS) - return; - ecr->res = res; - complete(&ecr->completion); -} - -typedef enum { - F2FS_DECRYPT = 0, - F2FS_ENCRYPT, -} f2fs_direction_t; - -static int f2fs_page_crypto(struct inode *inode, - f2fs_direction_t rw, - pgoff_t index, - struct page *src_page, - struct page *dest_page) -{ - u8 xts_tweak[F2FS_XTS_TWEAK_SIZE]; - struct ablkcipher_request *req = NULL; - DECLARE_F2FS_COMPLETION_RESULT(ecr); - struct scatterlist dst, src; - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; - struct crypto_ablkcipher *tfm = ci->ci_ctfm; - int res = 0; - - req = ablkcipher_request_alloc(tfm, GFP_NOFS); - if (!req) { - printk_ratelimited(KERN_ERR - "%s: crypto_request_alloc() failed\n", - __func__); - return -ENOMEM; - } - ablkcipher_request_set_callback( - req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - f2fs_crypt_complete, &ecr); - - BUILD_BUG_ON(F2FS_XTS_TWEAK_SIZE < sizeof(index)); - memcpy(xts_tweak, &index, sizeof(index)); - memset(&xts_tweak[sizeof(index)], 0, - F2FS_XTS_TWEAK_SIZE - sizeof(index)); - - sg_init_table(&dst, 1); - sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0); - sg_init_table(&src, 1); - sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0); - ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE, - xts_tweak); - if (rw == F2FS_DECRYPT) - res = crypto_ablkcipher_decrypt(req); - else - res = crypto_ablkcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - wait_for_completion(&ecr.completion); - res = ecr.res; - } - ablkcipher_request_free(req); - if (res) { - printk_ratelimited(KERN_ERR - "%s: crypto_ablkcipher_encrypt() returned %d\n", - __func__, res); - return res; - } - return 0; -} - -static struct page *alloc_bounce_page(struct f2fs_crypto_ctx *ctx) -{ - ctx->w.bounce_page = mempool_alloc(f2fs_bounce_page_pool, GFP_NOWAIT); - if (ctx->w.bounce_page == NULL) - return ERR_PTR(-ENOMEM); - ctx->flags |= F2FS_WRITE_PATH_FL; - return ctx->w.bounce_page; -} - -/** - * f2fs_encrypt() - Encrypts a page - * @inode: The inode for which the encryption should take place - * @plaintext_page: The page to encrypt. Must be locked. - * - * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx - * encryption context. - * - * Called on the page write path. The caller must call - * f2fs_restore_control_page() on the returned ciphertext page to - * release the bounce buffer and the encryption context. - * - * Return: An allocated page with the encrypted content on success. Else, an - * error value or NULL. - */ -struct page *f2fs_encrypt(struct inode *inode, - struct page *plaintext_page) -{ - struct f2fs_crypto_ctx *ctx; - struct page *ciphertext_page = NULL; - int err; - - BUG_ON(!PageLocked(plaintext_page)); - - ctx = f2fs_get_crypto_ctx(inode); - if (IS_ERR(ctx)) - return (struct page *)ctx; - - /* The encryption operation will require a bounce page. */ - ciphertext_page = alloc_bounce_page(ctx); - if (IS_ERR(ciphertext_page)) - goto err_out; - - ctx->w.control_page = plaintext_page; - err = f2fs_page_crypto(inode, F2FS_ENCRYPT, plaintext_page->index, - plaintext_page, ciphertext_page); - if (err) { - ciphertext_page = ERR_PTR(err); - goto err_out; - } - - SetPagePrivate(ciphertext_page); - set_page_private(ciphertext_page, (unsigned long)ctx); - lock_page(ciphertext_page); - return ciphertext_page; - -err_out: - f2fs_release_crypto_ctx(ctx); - return ciphertext_page; -} - -/** - * f2fs_decrypt() - Decrypts a page in-place - * @ctx: The encryption context. - * @page: The page to decrypt. Must be locked. - * - * Decrypts page in-place using the ctx encryption context. - * - * Called from the read completion callback. - * - * Return: Zero on success, non-zero otherwise. - */ -int f2fs_decrypt(struct page *page) -{ - BUG_ON(!PageLocked(page)); - - return f2fs_page_crypto(page->mapping->host, - F2FS_DECRYPT, page->index, page, page); -} - -bool f2fs_valid_contents_enc_mode(uint32_t mode) -{ - return (mode == F2FS_ENCRYPTION_MODE_AES_256_XTS); -} - -/** - * f2fs_validate_encryption_key_size() - Validate the encryption key size - * @mode: The key mode. - * @size: The key size to validate. - * - * Return: The validated key size for @mode. Zero if invalid. - */ -uint32_t f2fs_validate_encryption_key_size(uint32_t mode, uint32_t size) -{ - if (size == f2fs_encryption_key_size(mode)) - return size; - return 0; -} diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c deleted file mode 100644 index 596f02490f27..000000000000 --- a/fs/f2fs/crypto_policy.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * copied from linux/fs/ext4/crypto_policy.c - * - * Copyright (C) 2015, Google, Inc. - * Copyright (C) 2015, Motorola Mobility. - * - * This contains encryption policy functions for f2fs with some modifications - * to support f2fs-specific xattr APIs. - * - * Written by Michael Halcrow, 2015. - * Modified by Jaegeuk Kim, 2015. - */ -#include -#include -#include -#include - -#include "f2fs.h" -#include "xattr.h" - -static int f2fs_inode_has_encryption_context(struct inode *inode) -{ - int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, - F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0, NULL); - return (res > 0); -} - -/* - * check whether the policy is consistent with the encryption context - * for the inode - */ -static int f2fs_is_encryption_context_consistent_with_policy( - struct inode *inode, const struct f2fs_encryption_policy *policy) -{ - struct f2fs_encryption_context ctx; - int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, - F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, - sizeof(ctx), NULL); - - if (res != sizeof(ctx)) - return 0; - - return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor, - F2FS_KEY_DESCRIPTOR_SIZE) == 0 && - (ctx.flags == policy->flags) && - (ctx.contents_encryption_mode == - policy->contents_encryption_mode) && - (ctx.filenames_encryption_mode == - policy->filenames_encryption_mode)); -} - -static int f2fs_create_encryption_context_from_policy( - struct inode *inode, const struct f2fs_encryption_policy *policy) -{ - struct f2fs_encryption_context ctx; - - ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1; - memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, - F2FS_KEY_DESCRIPTOR_SIZE); - - if (!f2fs_valid_contents_enc_mode(policy->contents_encryption_mode)) { - printk(KERN_WARNING - "%s: Invalid contents encryption mode %d\n", __func__, - policy->contents_encryption_mode); - return -EINVAL; - } - - if (!f2fs_valid_filenames_enc_mode(policy->filenames_encryption_mode)) { - printk(KERN_WARNING - "%s: Invalid filenames encryption mode %d\n", __func__, - policy->filenames_encryption_mode); - return -EINVAL; - } - - if (policy->flags & ~F2FS_POLICY_FLAGS_VALID) - return -EINVAL; - - ctx.contents_encryption_mode = policy->contents_encryption_mode; - ctx.filenames_encryption_mode = policy->filenames_encryption_mode; - ctx.flags = policy->flags; - BUILD_BUG_ON(sizeof(ctx.nonce) != F2FS_KEY_DERIVATION_NONCE_SIZE); - get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE); - - return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, - F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, - sizeof(ctx), NULL, XATTR_CREATE); -} - -int f2fs_process_policy(const struct f2fs_encryption_policy *policy, - struct inode *inode) -{ - if (policy->version != 0) - return -EINVAL; - - if (!S_ISDIR(inode->i_mode)) - return -EINVAL; - - if (!f2fs_inode_has_encryption_context(inode)) { - if (!f2fs_empty_dir(inode)) - return -ENOTEMPTY; - return f2fs_create_encryption_context_from_policy(inode, - policy); - } - - if (f2fs_is_encryption_context_consistent_with_policy(inode, policy)) - return 0; - - printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n", - __func__); - return -EINVAL; -} - -int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy) -{ - struct f2fs_encryption_context ctx; - int res; - - if (!f2fs_encrypted_inode(inode)) - return -ENODATA; - - res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, - F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, - &ctx, sizeof(ctx), NULL); - if (res != sizeof(ctx)) - return -ENODATA; - if (ctx.format != F2FS_ENCRYPTION_CONTEXT_FORMAT_V1) - return -EINVAL; - - policy->version = 0; - policy->contents_encryption_mode = ctx.contents_encryption_mode; - policy->filenames_encryption_mode = ctx.filenames_encryption_mode; - policy->flags = ctx.flags; - memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor, - F2FS_KEY_DESCRIPTOR_SIZE); - return 0; -} - -int f2fs_is_child_context_consistent_with_parent(struct inode *parent, - struct inode *child) -{ - struct f2fs_crypt_info *parent_ci, *child_ci; - int res; - - if ((parent == NULL) || (child == NULL)) { - pr_err("parent %p child %p\n", parent, child); - BUG_ON(1); - } - - /* no restrictions if the parent directory is not encrypted */ - if (!f2fs_encrypted_inode(parent)) - return 1; - /* if the child directory is not encrypted, this is always a problem */ - if (!f2fs_encrypted_inode(child)) - return 0; - res = f2fs_get_encryption_info(parent); - if (res) - return 0; - res = f2fs_get_encryption_info(child); - if (res) - return 0; - parent_ci = F2FS_I(parent)->i_crypt_info; - child_ci = F2FS_I(child)->i_crypt_info; - if (!parent_ci && !child_ci) - return 1; - if (!parent_ci || !child_ci) - return 0; - - return (memcmp(parent_ci->ci_master_key, - child_ci->ci_master_key, - F2FS_KEY_DESCRIPTOR_SIZE) == 0 && - (parent_ci->ci_data_mode == child_ci->ci_data_mode) && - (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && - (parent_ci->ci_flags == child_ci->ci_flags)); -} - -/** - * f2fs_inherit_context() - Sets a child context from its parent - * @parent: Parent inode from which the context is inherited. - * @child: Child inode that inherits the context from @parent. - * - * Return: Zero on success, non-zero otherwise - */ -int f2fs_inherit_context(struct inode *parent, struct inode *child, - struct page *ipage) -{ - struct f2fs_encryption_context ctx; - struct f2fs_crypt_info *ci; - int res; - - res = f2fs_get_encryption_info(parent); - if (res < 0) - return res; - - ci = F2FS_I(parent)->i_crypt_info; - if (ci == NULL) - return -ENOKEY; - - ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1; - - ctx.contents_encryption_mode = ci->ci_data_mode; - ctx.filenames_encryption_mode = ci->ci_filename_mode; - ctx.flags = ci->ci_flags; - memcpy(ctx.master_key_descriptor, ci->ci_master_key, - F2FS_KEY_DESCRIPTOR_SIZE); - - get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE); - return f2fs_setxattr(child, F2FS_XATTR_INDEX_ENCRYPTION, - F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, - sizeof(ctx), ipage, XATTR_CREATE); -} diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5005dbf66b70..460daafc7117 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -35,9 +35,9 @@ static void f2fs_read_end_io(struct bio *bio, int err) if (f2fs_bio_encrypted(bio)) { if (err) { - f2fs_release_crypto_ctx(bio->bi_private); + fscrypt_release_ctx(bio->bi_private); } else { - f2fs_end_io_crypto_work(bio->bi_private, bio); + fscrypt_decrypt_bio_pages(bio->bi_private, bio); return; } } @@ -65,7 +65,7 @@ static void f2fs_write_end_io(struct bio *bio, int err) bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; - f2fs_restore_and_release_control_page(&page); + fscrypt_pullback_bio_page(&page, true); if (unlikely(err)) { set_bit(AS_EIO, &page->mapping->flags); @@ -130,16 +130,10 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, bio_for_each_segment_all(bvec, io->bio, i) { - if (bvec->bv_page->mapping) { + if (bvec->bv_page->mapping) target = bvec->bv_page; - } else { - struct f2fs_crypto_ctx *ctx; - - /* encrypted page */ - ctx = (struct f2fs_crypto_ctx *)page_private( - bvec->bv_page); - target = ctx->w.control_page; - } + else + target = fscrypt_control_page(bvec->bv_page); if (inode && inode == target->mapping->host) return true; @@ -221,7 +215,8 @@ void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi) int f2fs_submit_page_bio(struct f2fs_io_info *fio) { struct bio *bio; - struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page; + struct page *page = fio->encrypted_page ? + fio->encrypted_page : fio->page; trace_f2fs_submit_page_bio(page, fio); f2fs_trace_ios(fio, 0); @@ -993,12 +988,12 @@ submit_and_realloc: bio = NULL; } if (bio == NULL) { - struct f2fs_crypto_ctx *ctx = NULL; + struct fscrypt_ctx *ctx = NULL; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { - ctx = f2fs_get_crypto_ctx(inode); + ctx = fscrypt_get_ctx(inode); if (IS_ERR(ctx)) goto set_error_page; @@ -1011,7 +1006,7 @@ submit_and_realloc: min_t(int, nr_pages, bio_get_nr_vecs(bdev))); if (!bio) { if (ctx) - f2fs_release_crypto_ctx(ctx); + fscrypt_release_ctx(ctx); goto set_error_page; } bio->bi_bdev = bdev; @@ -1103,7 +1098,7 @@ int do_write_data_page(struct f2fs_io_info *fio) f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode), fio->old_blkaddr); - fio->encrypted_page = f2fs_encrypt(inode, fio->page); + fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page); if (IS_ERR(fio->encrypted_page)) { err = PTR_ERR(fio->encrypted_page); goto out_writepage; @@ -1609,7 +1604,7 @@ repeat: /* avoid symlink page */ if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { - err = f2fs_decrypt(page); + err = fscrypt_decrypt_page(page); if (err) goto fail; } diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index ae6424562fa5..7497b8d750b1 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -77,7 +77,7 @@ static unsigned long dir_block_index(unsigned int level, } static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, - struct f2fs_filename *fname, + struct fscrypt_name *fname, f2fs_hash_t namehash, int *max_slots, struct page **res_page) @@ -103,15 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, return de; } -struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname, +struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, f2fs_hash_t namehash, int *max_slots, struct f2fs_dentry_ptr *d) { struct f2fs_dir_entry *de; unsigned long bit_pos = 0; int max_len = 0; - struct f2fs_str de_name = FSTR_INIT(NULL, 0); - struct f2fs_str *name = &fname->disk_name; + struct fscrypt_str de_name = FSTR_INIT(NULL, 0); + struct fscrypt_str *name = &fname->disk_name; if (max_slots) *max_slots = 0; @@ -157,7 +157,7 @@ found: static struct f2fs_dir_entry *find_in_level(struct inode *dir, unsigned int level, - struct f2fs_filename *fname, + struct fscrypt_name *fname, struct page **res_page) { struct qstr name = FSTR_TO_QSTR(&fname->disk_name); @@ -218,12 +218,12 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, struct f2fs_dir_entry *de = NULL; unsigned int max_depth; unsigned int level; - struct f2fs_filename fname; + struct fscrypt_name fname; int err; *res_page = NULL; - err = f2fs_fname_setup_filename(dir, child, 1, &fname); + err = fscrypt_setup_filename(dir, child, 1, &fname); if (err) return NULL; @@ -251,7 +251,7 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, break; } out: - f2fs_fname_free_filename(&fname); + fscrypt_free_filename(&fname); return de; } @@ -413,7 +413,7 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, goto put_error; if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) { - err = f2fs_inherit_context(dir, inode, page); + err = fscrypt_inherit_context(dir, inode, page, false); if (err) goto put_error; } @@ -536,11 +536,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_ptr d; struct page *page = NULL; - struct f2fs_filename fname; + struct fscrypt_name fname; struct qstr new_name; int slots, err; - err = f2fs_fname_setup_filename(dir, name, 0, &fname); + err = fscrypt_setup_filename(dir, name, 0, &fname); if (err) return err; @@ -639,7 +639,7 @@ fail: kunmap(dentry_page); f2fs_put_page(dentry_page, 1); out: - f2fs_fname_free_filename(&fname); + fscrypt_free_filename(&fname); f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); return err; } @@ -781,12 +781,12 @@ bool f2fs_empty_dir(struct inode *dir) } bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, - unsigned int start_pos, struct f2fs_str *fstr) + unsigned int start_pos, struct fscrypt_str *fstr) { unsigned char d_type = DT_UNKNOWN; unsigned int bit_pos; struct f2fs_dir_entry *de = NULL; - struct f2fs_str de_name = FSTR_INIT(NULL, 0); + struct fscrypt_str de_name = FSTR_INIT(NULL, 0); bit_pos = ((unsigned long)ctx->pos % d->max); @@ -820,8 +820,9 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, memcpy(de_name.name, d->filename[bit_pos], de_name.len); - ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code, - &de_name, fstr); + ret = fscrypt_fname_disk_to_usr(d->inode, + (u32)de->hash_code, 0, + &de_name, fstr); kfree(de_name.name); if (ret < 0) return true; @@ -849,16 +850,15 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) struct file_ra_state *ra = &file->f_ra; unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); struct f2fs_dentry_ptr d; - struct f2fs_str fstr = FSTR_INIT(NULL, 0); + struct fscrypt_str fstr = FSTR_INIT(NULL, 0); int err = 0; if (f2fs_encrypted_inode(inode)) { - err = f2fs_get_encryption_info(inode); + err = fscrypt_get_encryption_info(inode); if (err) return err; - err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN, - &fstr); + err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr); if (err < 0) return err; } @@ -898,14 +898,14 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) f2fs_put_page(dentry_page, 1); } out: - f2fs_fname_crypto_free_buffer(&fstr); + fscrypt_fname_free_buffer(&fstr); return err; } static int f2fs_dir_open(struct inode *inode, struct file *filp) { if (f2fs_encrypted_inode(inode)) - return f2fs_get_encryption_info(inode) ? -EACCES : 0; + return fscrypt_get_encryption_info(inode) ? -EACCES : 0; return 0; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index de3929ea3e4e..cdaace2fc26d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) @@ -272,12 +273,9 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7) #define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8) -#define F2FS_IOC_SET_ENCRYPTION_POLICY \ - _IOR('f', 19, struct f2fs_encryption_policy) -#define F2FS_IOC_GET_ENCRYPTION_PWSALT \ - _IOW('f', 20, __u8[16]) -#define F2FS_IOC_GET_ENCRYPTION_POLICY \ - _IOW('f', 21, struct f2fs_encryption_policy) +#define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY +#define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY +#define F2FS_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* @@ -297,25 +295,6 @@ struct f2fs_defragment { * For INODE and NODE manager */ /* for directory operations */ -struct f2fs_str { - unsigned char *name; - u32 len; -}; - -struct f2fs_filename { - const struct qstr *usr_fname; - struct f2fs_str disk_name; - f2fs_hash_t hash; -#ifdef CONFIG_F2FS_FS_ENCRYPTION - struct f2fs_str crypto_buf; -#endif -}; - -#define FSTR_INIT(n, l) { .name = n, .len = l } -#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) -#define fname_name(p) ((p)->disk_name.name) -#define fname_len(p) ((p)->disk_name.len) - struct f2fs_dentry_ptr { struct inode *inode; const void *bitmap; @@ -443,15 +422,6 @@ struct f2fs_map_blocks { #define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT) #define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT) -/* Encryption algorithms */ -#define F2FS_ENCRYPTION_MODE_INVALID 0 -#define F2FS_ENCRYPTION_MODE_AES_256_XTS 1 -#define F2FS_ENCRYPTION_MODE_AES_256_GCM 2 -#define F2FS_ENCRYPTION_MODE_AES_256_CBC 3 -#define F2FS_ENCRYPTION_MODE_AES_256_CTS 4 - -#include "f2fs_crypto.h" - #define DEF_DIR_LEVEL 0 struct f2fs_inode_info { @@ -475,13 +445,7 @@ struct f2fs_inode_info { struct list_head dirty_list; /* linked in global dirty list */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct mutex inmem_lock; /* lock for inmemory pages */ - struct extent_tree *extent_tree; /* cached extent_tree entry */ - -#ifdef CONFIG_F2FS_FS_ENCRYPTION - /* Encryption params */ - struct f2fs_crypt_info *i_crypt_info; -#endif }; static inline void get_extent_info(struct extent_info *ext, @@ -1773,10 +1737,10 @@ struct dentry *f2fs_get_parent(struct dentry *child); */ extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; void set_de_type(struct f2fs_dir_entry *, umode_t); -struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *, +struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *, f2fs_hash_t, int *, struct f2fs_dentry_ptr *); bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, - unsigned int, struct f2fs_str *); + unsigned int, struct fscrypt_str *); void do_make_empty_dir(struct inode *, struct inode *, struct f2fs_dentry_ptr *); struct page *init_inode_metadata(struct inode *, struct inode *, @@ -2152,7 +2116,7 @@ int f2fs_convert_inline_inode(struct inode *); int f2fs_write_inline_data(struct inode *, struct page *); bool recover_inline_data(struct inode *, struct page *); struct f2fs_dir_entry *find_in_inline_dir(struct inode *, - struct f2fs_filename *, struct page **); + struct fscrypt_name *, struct page **); struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **); int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, @@ -2161,7 +2125,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *, struct inode *, struct inode *); bool f2fs_empty_inline_dir(struct inode *); int f2fs_read_inline_dir(struct file *, struct dir_context *, - struct f2fs_str *); + struct fscrypt_str *); int f2fs_inline_data_fiemap(struct inode *, struct fiemap_extent_info *, __u64, __u64); @@ -2191,13 +2155,9 @@ void destroy_extent_cache(void); /* * crypto support */ -static inline int f2fs_encrypted_inode(struct inode *inode) +static inline bool f2fs_encrypted_inode(struct inode *inode) { -#ifdef CONFIG_F2FS_FS_ENCRYPTION return file_is_encrypt(inode); -#else - return 0; -#endif } static inline void f2fs_set_encrypted_inode(struct inode *inode) @@ -2209,20 +2169,12 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode) static inline bool f2fs_bio_encrypted(struct bio *bio) { -#ifdef CONFIG_F2FS_FS_ENCRYPTION - return unlikely(bio->bi_private != NULL); -#else - return false; -#endif + return bio->bi_private != NULL; } static inline int f2fs_sb_has_crypto(struct super_block *sb) { -#ifdef CONFIG_F2FS_FS_ENCRYPTION return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); -#else - return 0; -#endif } static inline bool f2fs_may_encrypt(struct inode *inode) @@ -2236,86 +2188,28 @@ static inline bool f2fs_may_encrypt(struct inode *inode) #endif } -/* crypto_policy.c */ -int f2fs_is_child_context_consistent_with_parent(struct inode *, - struct inode *); -int f2fs_inherit_context(struct inode *, struct inode *, struct page *); -int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *); -int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *); - -/* crypt.c */ -extern struct kmem_cache *f2fs_crypt_info_cachep; -bool f2fs_valid_contents_enc_mode(uint32_t); -uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t); -struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *); -void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *); -struct page *f2fs_encrypt(struct inode *, struct page *); -int f2fs_decrypt(struct page *); -void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *); - -/* crypto_key.c */ -void f2fs_free_encryption_info(struct inode *, struct f2fs_crypt_info *); -int _f2fs_get_encryption_info(struct inode *inode); - -/* crypto_fname.c */ -bool f2fs_valid_filenames_enc_mode(uint32_t); -u32 f2fs_fname_crypto_round_up(u32, u32); -unsigned f2fs_fname_encrypted_size(struct inode *, u32); -int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *); -int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *, - const struct f2fs_str *, struct f2fs_str *); -int f2fs_fname_usr_to_disk(struct inode *, const struct qstr *, - struct f2fs_str *); - -#ifdef CONFIG_F2FS_FS_ENCRYPTION -void f2fs_restore_and_release_control_page(struct page **); -void f2fs_restore_control_page(struct page *); - -int __init f2fs_init_crypto(void); -int f2fs_crypto_initialize(void); -void f2fs_exit_crypto(void); - -int f2fs_has_encryption_key(struct inode *); - -static inline int f2fs_get_encryption_info(struct inode *inode) -{ - struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; - - if (!ci || - (ci->ci_keyring_key && - (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | - (1 << KEY_FLAG_REVOKED) | - (1 << KEY_FLAG_DEAD))))) - return _f2fs_get_encryption_info(inode); - return 0; -} - -void f2fs_fname_crypto_free_buffer(struct f2fs_str *); -int f2fs_fname_setup_filename(struct inode *, const struct qstr *, - int lookup, struct f2fs_filename *); -void f2fs_fname_free_filename(struct f2fs_filename *); -#else -static inline void f2fs_restore_and_release_control_page(struct page **p) { } -static inline void f2fs_restore_control_page(struct page *p) { } - -static inline int __init f2fs_init_crypto(void) { return 0; } -static inline void f2fs_exit_crypto(void) { } - -static inline int f2fs_has_encryption_key(struct inode *i) { return 0; } -static inline int f2fs_get_encryption_info(struct inode *i) { return 0; } -static inline void f2fs_fname_crypto_free_buffer(struct f2fs_str *p) { } - -static inline int f2fs_fname_setup_filename(struct inode *dir, - const struct qstr *iname, - int lookup, struct f2fs_filename *fname) -{ - memset(fname, 0, sizeof(struct f2fs_filename)); - fname->usr_fname = iname; - fname->disk_name.name = (unsigned char *)iname->name; - fname->disk_name.len = iname->len; - return 0; -} - -static inline void f2fs_fname_free_filename(struct f2fs_filename *fname) { } +#ifndef CONFIG_F2FS_FS_ENCRYPTION +#define fscrypt_set_d_op(i) +#define fscrypt_get_ctx fscrypt_notsupp_get_ctx +#define fscrypt_release_ctx fscrypt_notsupp_release_ctx +#define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page +#define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page +#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages +#define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page +#define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page +#define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range +#define fscrypt_process_policy fscrypt_notsupp_process_policy +#define fscrypt_get_policy fscrypt_notsupp_get_policy +#define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context +#define fscrypt_inherit_context fscrypt_notsupp_inherit_context +#define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info +#define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info +#define fscrypt_setup_filename fscrypt_notsupp_setup_filename +#define fscrypt_free_filename fscrypt_notsupp_free_filename +#define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size +#define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer +#define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer +#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr +#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk #endif #endif diff --git a/fs/f2fs/f2fs_crypto.h b/fs/f2fs/f2fs_crypto.h deleted file mode 100644 index c2c1c2b63b25..000000000000 --- a/fs/f2fs/f2fs_crypto.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * linux/fs/f2fs/f2fs_crypto.h - * - * Copied from linux/fs/ext4/ext4_crypto.h - * - * Copyright (C) 2015, Google, Inc. - * - * This contains encryption header content for f2fs - * - * Written by Michael Halcrow, 2015. - * Modified by Jaegeuk Kim, 2015. - */ -#ifndef _F2FS_CRYPTO_H -#define _F2FS_CRYPTO_H - -#include - -#define F2FS_KEY_DESCRIPTOR_SIZE 8 - -/* Policy provided via an ioctl on the topmost directory */ -struct f2fs_encryption_policy { - char version; - char contents_encryption_mode; - char filenames_encryption_mode; - char flags; - char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE]; -} __attribute__((__packed__)); - -#define F2FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 -#define F2FS_KEY_DERIVATION_NONCE_SIZE 16 - -#define F2FS_POLICY_FLAGS_PAD_4 0x00 -#define F2FS_POLICY_FLAGS_PAD_8 0x01 -#define F2FS_POLICY_FLAGS_PAD_16 0x02 -#define F2FS_POLICY_FLAGS_PAD_32 0x03 -#define F2FS_POLICY_FLAGS_PAD_MASK 0x03 -#define F2FS_POLICY_FLAGS_VALID 0x03 - -/** - * Encryption context for inode - * - * Protector format: - * 1 byte: Protector format (1 = this version) - * 1 byte: File contents encryption mode - * 1 byte: File names encryption mode - * 1 byte: Flags - * 8 bytes: Master Key descriptor - * 16 bytes: Encryption Key derivation nonce - */ -struct f2fs_encryption_context { - char format; - char contents_encryption_mode; - char filenames_encryption_mode; - char flags; - char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE]; - char nonce[F2FS_KEY_DERIVATION_NONCE_SIZE]; -} __attribute__((__packed__)); - -/* Encryption parameters */ -#define F2FS_XTS_TWEAK_SIZE 16 -#define F2FS_AES_128_ECB_KEY_SIZE 16 -#define F2FS_AES_256_GCM_KEY_SIZE 32 -#define F2FS_AES_256_CBC_KEY_SIZE 32 -#define F2FS_AES_256_CTS_KEY_SIZE 32 -#define F2FS_AES_256_XTS_KEY_SIZE 64 -#define F2FS_MAX_KEY_SIZE 64 - -#define F2FS_KEY_DESC_PREFIX "f2fs:" -#define F2FS_KEY_DESC_PREFIX_SIZE 5 - -struct f2fs_encryption_key { - __u32 mode; - char raw[F2FS_MAX_KEY_SIZE]; - __u32 size; -} __attribute__((__packed__)); - -struct f2fs_crypt_info { - char ci_data_mode; - char ci_filename_mode; - char ci_flags; - struct crypto_ablkcipher *ci_ctfm; - struct key *ci_keyring_key; - char ci_master_key[F2FS_KEY_DESCRIPTOR_SIZE]; -}; - -#define F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 -#define F2FS_WRITE_PATH_FL 0x00000002 - -struct f2fs_crypto_ctx { - union { - struct { - struct page *bounce_page; /* Ciphertext page */ - struct page *control_page; /* Original page */ - } w; - struct { - struct bio *bio; - struct work_struct work; - } r; - struct list_head free_list; /* Free list */ - }; - char flags; /* Flags */ -}; - -struct f2fs_completion_result { - struct completion completion; - int res; -}; - -#define DECLARE_F2FS_COMPLETION_RESULT(ecr) \ - struct f2fs_completion_result ecr = { \ - COMPLETION_INITIALIZER((ecr).completion), 0 } - -static inline int f2fs_encryption_key_size(int mode) -{ - switch (mode) { - case F2FS_ENCRYPTION_MODE_AES_256_XTS: - return F2FS_AES_256_XTS_KEY_SIZE; - case F2FS_ENCRYPTION_MODE_AES_256_GCM: - return F2FS_AES_256_GCM_KEY_SIZE; - case F2FS_ENCRYPTION_MODE_AES_256_CBC: - return F2FS_AES_256_CBC_KEY_SIZE; - case F2FS_ENCRYPTION_MODE_AES_256_CTS: - return F2FS_AES_256_CTS_KEY_SIZE; - default: - BUG(); - } - return 0; -} - -#define F2FS_FNAME_NUM_SCATTER_ENTRIES 4 -#define F2FS_CRYPTO_BLOCK_SIZE 16 -#define F2FS_FNAME_CRYPTO_DIGEST_SIZE 32 - -/** - * For encrypted symlinks, the ciphertext length is stored at the beginning - * of the string in little-endian format. - */ -struct f2fs_encrypted_symlink_data { - __le16 len; - char encrypted_path[1]; -} __attribute__((__packed__)); - -/** - * This function is used to calculate the disk space required to - * store a filename of length l in encrypted symlink format. - */ -static inline u32 encrypted_symlink_data_len(u32 l) -{ - return (l + sizeof(struct f2fs_encrypted_symlink_data) - 1); -} -#endif /* _F2FS_CRYPTO_H */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 68f6b0da1e49..78128bd50fb8 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -422,7 +422,7 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) int err; if (f2fs_encrypted_inode(inode)) { - err = f2fs_get_encryption_info(inode); + err = fscrypt_get_encryption_info(inode); if (err) return 0; if (!f2fs_encrypted_inode(inode)) @@ -444,10 +444,10 @@ 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); + ret = fscrypt_get_encryption_info(inode); if (ret) return -EACCES; - if (!f2fs_encrypted_inode(inode)) + if (!fscrypt_has_encryption_key(inode)) return -ENOKEY; } return ret; @@ -527,7 +527,8 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, truncate_out: f2fs_wait_on_page_writeback(page, DATA, true); zero_user(page, offset, PAGE_CACHE_SIZE - offset); - if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) + if (!cache_only || !f2fs_encrypted_inode(inode) || + !S_ISREG(inode->i_mode)) set_page_dirty(page); f2fs_put_page(page, 1); return 0; @@ -675,7 +676,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_valid & ATTR_SIZE) { if (f2fs_encrypted_inode(inode) && - f2fs_get_encryption_info(inode)) + fscrypt_get_encryption_info(inode)) return -EACCES; if (attr->ia_size <= i_size_read(inode)) { @@ -1532,39 +1533,30 @@ static bool uuid_is_nonzero(__u8 u[16]) static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) { -#ifdef CONFIG_F2FS_FS_ENCRYPTION - struct f2fs_encryption_policy policy; + struct fscrypt_policy policy; struct inode *inode = file_inode(filp); - if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg, - sizeof(policy))) + if (copy_from_user(&policy, (struct fscrypt_policy __user *)arg, + sizeof(policy))) return -EFAULT; f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); - return f2fs_process_policy(&policy, inode); -#else - return -EOPNOTSUPP; -#endif + return fscrypt_process_policy(inode, &policy); } static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) { -#ifdef CONFIG_F2FS_FS_ENCRYPTION - struct f2fs_encryption_policy policy; + struct fscrypt_policy policy; struct inode *inode = file_inode(filp); int err; - err = f2fs_get_policy(inode, &policy); + err = fscrypt_get_policy(inode, &policy); if (err) return err; - if (copy_to_user((struct f2fs_encryption_policy __user *)arg, &policy, - sizeof(policy))) + if (copy_to_user((struct fscrypt_policy __user *)arg, &policy, sizeof(policy))) return -EFAULT; return 0; -#else - return -EOPNOTSUPP; -#endif } static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) @@ -1878,8 +1870,8 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ssize_t ret; if (f2fs_encrypted_inode(inode) && - !f2fs_has_encryption_key(inode) && - f2fs_get_encryption_info(inode)) + !fscrypt_has_encryption_key(inode) && + fscrypt_get_encryption_info(inode)) return -EACCES; inode_lock(inode); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 1c00f2c718ae..358214e9f707 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -277,7 +277,7 @@ process_inline: } struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, - struct f2fs_filename *fname, struct page **res_page) + struct fscrypt_name *fname, struct page **res_page) { struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); struct f2fs_inline_dentry *inline_dentry; @@ -535,7 +535,7 @@ bool f2fs_empty_inline_dir(struct inode *dir) } int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, - struct f2fs_str *fstr) + struct fscrypt_str *fstr) { struct inode *inode = file_inode(file); struct f2fs_inline_dentry *inline_dentry = NULL; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index ea5b581826e6..1395f2b71b43 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -388,10 +388,7 @@ no_delete: } } out_clear: -#ifdef CONFIG_F2FS_FS_ENCRYPTION - if (fi->i_crypt_info) - f2fs_free_encryption_info(inode, fi->i_crypt_info); -#endif + fscrypt_put_encryption_info(inode, NULL); clear_inode(inode); } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 5d901607322c..088dc996216c 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -170,7 +170,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, int err; if (f2fs_encrypted_inode(dir) && - !f2fs_is_child_context_consistent_with_parent(dir, inode)) + !fscrypt_has_permitted_context(dir, inode)) return -EPERM; f2fs_balance_fs(sbi, true); @@ -354,20 +354,20 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct inode *inode; size_t len = strlen(symname); - struct f2fs_str disk_link = FSTR_INIT((char *)symname, len + 1); - struct f2fs_encrypted_symlink_data *sd = NULL; + struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1); + struct fscrypt_symlink_data *sd = NULL; int err; if (f2fs_encrypted_inode(dir)) { - err = f2fs_get_encryption_info(dir); + err = fscrypt_get_encryption_info(dir); if (err) return err; - if (!f2fs_encrypted_inode(dir)) + if (!fscrypt_has_encryption_key(dir)) return -EPERM; - disk_link.len = (f2fs_fname_encrypted_size(dir, len) + - sizeof(struct f2fs_encrypted_symlink_data)); + disk_link.len = (fscrypt_fname_encrypted_size(dir, len) + + sizeof(struct fscrypt_symlink_data)); } if (disk_link.len > dir->i_sb->s_blocksize) @@ -394,7 +394,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, if (f2fs_encrypted_inode(inode)) { struct qstr istr = QSTR_INIT(symname, len); - struct f2fs_str ostr; + struct fscrypt_str ostr; sd = kzalloc(disk_link.len, GFP_NOFS); if (!sd) { @@ -402,18 +402,18 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, goto err_out; } - err = f2fs_get_encryption_info(inode); + err = fscrypt_get_encryption_info(inode); if (err) goto err_out; - if (!f2fs_encrypted_inode(inode)) { + if (!fscrypt_has_encryption_key(inode)) { err = -EPERM; goto err_out; } ostr.name = sd->encrypted_path; ostr.len = disk_link.len; - err = f2fs_fname_usr_to_disk(inode, &istr, &ostr); + err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr); if (err < 0) goto err_out; @@ -597,7 +597,7 @@ out: static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) { if (f2fs_encrypted_inode(dir)) { - int err = f2fs_get_encryption_info(dir); + int err = fscrypt_get_encryption_info(dir); if (err) return err; } @@ -627,8 +627,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, int err = -ENOENT; if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && - !f2fs_is_child_context_consistent_with_parent(new_dir, - old_inode)) { + !fscrypt_has_permitted_context(new_dir, old_inode)) { err = -EPERM; goto out; } @@ -808,11 +807,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, int err = -ENOENT; if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) && - (old_dir != new_dir) && - (!f2fs_is_child_context_consistent_with_parent(new_dir, - old_inode) || - !f2fs_is_child_context_consistent_with_parent(old_dir, - new_inode))) + (old_dir != new_dir) && + (!fscrypt_has_permitted_context(new_dir, old_inode) || + !fscrypt_has_permitted_context(old_dir, new_inode))) return -EPERM; old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); @@ -974,21 +971,20 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } -#ifdef CONFIG_F2FS_FS_ENCRYPTION static void *f2fs_encrypted_follow_link(struct dentry *dentry, struct nameidata *nd) { struct page *cpage = NULL; char *caddr, *paddr = NULL; - struct f2fs_str cstr = FSTR_INIT(NULL, 0); - struct f2fs_str pstr = FSTR_INIT(NULL, 0); + struct fscrypt_str cstr = FSTR_INIT(NULL, 0); + struct fscrypt_str pstr = FSTR_INIT(NULL, 0); + struct fscrypt_symlink_data *sd; struct inode *inode = dentry->d_inode; - struct f2fs_encrypted_symlink_data *sd; loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); u32 max_size = inode->i_sb->s_blocksize; int res; - res = f2fs_get_encryption_info(inode); + res = fscrypt_get_encryption_info(inode); if (res) return ERR_PTR(res); @@ -999,7 +995,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, caddr[size] = 0; /* Symlink is encrypted */ - sd = (struct f2fs_encrypted_symlink_data *)caddr; + sd = (struct fscrypt_symlink_data *)caddr; cstr.name = sd->encrypted_path; cstr.len = le16_to_cpu(sd->len); @@ -1015,17 +1011,16 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, goto errout; } - if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) > - max_size) { + if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) { /* Symlink data on the disk is corrupted */ res = -EIO; goto errout; } - res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr); + res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr); if (res) goto errout; - res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr); + res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr); if (res < 0) goto errout; @@ -1039,7 +1034,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, page_cache_release(cpage); return NULL; errout: - f2fs_fname_crypto_free_buffer(&pstr); + fscrypt_fname_free_buffer(&pstr); kunmap(cpage); page_cache_release(cpage); return ERR_PTR(res); @@ -1066,7 +1061,6 @@ const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .removexattr = generic_removexattr, #endif }; -#endif const struct inode_operations f2fs_dir_inode_operations = { .create = f2fs_create, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d565438fba36..0cdb075a6777 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -470,10 +470,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; - -#ifdef CONFIG_F2FS_FS_ENCRYPTION - fi->i_crypt_info = NULL; -#endif return &fi->vfs_inode; } @@ -507,11 +503,7 @@ static int f2fs_drop_inode(struct inode *inode) sb_end_intwrite(inode->i_sb); -#ifdef CONFIG_F2FS_FS_ENCRYPTION - if (F2FS_I(inode)->i_crypt_info) - f2fs_free_encryption_info(inode, - F2FS_I(inode)->i_crypt_info); -#endif + fscrypt_put_encryption_info(inode, NULL); spin_lock(&inode->i_lock); atomic_dec(&inode->i_count); } @@ -891,6 +883,41 @@ static struct super_operations f2fs_sops = { .remount_fs = f2fs_remount, }; +#ifdef CONFIG_F2FS_FS_ENCRYPTION +static int f2fs_get_context(struct inode *inode, void *ctx, size_t len) +{ + return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + ctx, len, NULL); +} + +static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len, + void *fs_data) +{ + return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + ctx, len, fs_data, XATTR_CREATE); +} + +static unsigned f2fs_max_namelen(struct inode *inode) +{ + return S_ISLNK(inode->i_mode) ? + inode->i_sb->s_blocksize : F2FS_NAME_LEN; +} + +static struct fscrypt_operations f2fs_cryptops = { + .get_context = f2fs_get_context, + .set_context = f2fs_set_context, + .is_encrypted = f2fs_encrypted_inode, + .empty_dir = f2fs_empty_dir, + .max_namelen = f2fs_max_namelen, +}; +#else +static struct fscrypt_operations f2fs_cryptops = { + .is_encrypted = f2fs_encrypted_inode, +}; +#endif + static struct inode *f2fs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation) { @@ -1314,6 +1341,7 @@ try_onemore: get_random_bytes(&sbi->s_next_generation, sizeof(u32)); sb->s_op = &f2fs_sops; + sb->s_cop = &f2fs_cryptops; sb->s_xattr = f2fs_xattr_handlers; sb->s_export_op = &f2fs_export_ops; sb->s_magic = F2FS_SUPER_MAGIC; @@ -1618,13 +1646,9 @@ static int __init init_f2fs_fs(void) err = -ENOMEM; goto free_extent_cache; } - err = f2fs_init_crypto(); - if (err) - goto free_kset; - err = register_shrinker(&f2fs_shrinker_info); if (err) - goto free_crypto; + goto free_kset; err = register_filesystem(&f2fs_fs_type); if (err) @@ -1639,8 +1663,6 @@ free_filesystem: unregister_filesystem(&f2fs_fs_type); free_shrinker: unregister_shrinker(&f2fs_shrinker_info); -free_crypto: - f2fs_exit_crypto(); free_kset: kset_unregister(f2fs_kset); free_extent_cache: @@ -1663,7 +1685,6 @@ static void __exit exit_f2fs_fs(void) f2fs_destroy_root_stats(); unregister_shrinker(&f2fs_shrinker_info); unregister_filesystem(&f2fs_fs_type); - f2fs_exit_crypto(); destroy_extent_cache(); destroy_checkpoint_caches(); destroy_segment_manager_caches(); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 340ee0dae93b..d12e5cdbf36f 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -225,6 +225,8 @@ struct dentry_operations { #define DCACHE_MAY_FREE 0x00800000 #define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */ +#define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */ + extern seqlock_t rename_lock; /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 58f6ab319996..7e880c52daaa 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -49,6 +49,8 @@ struct swap_info_struct; struct seq_file; struct workqueue_struct; struct iov_iter; +struct fscrypt_info; +struct fscrypt_operations; extern void __init inode_init(void); extern void __init inode_init_early(void); @@ -623,6 +625,10 @@ struct inode { struct hlist_head i_fsnotify_marks; #endif +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + struct fscrypt_info *i_crypt_info; +#endif + void *i_private; /* fs or device private pointer */ }; @@ -1217,6 +1223,9 @@ struct super_block { const struct xattr_handler **s_xattr; struct list_head s_inodes; /* all inodes */ + + const struct fscrypt_operations *s_cop; + struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */ struct list_head s_mounts; /* list of mounts; _not_ for fs use */ struct block_device *s_bdev; diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h new file mode 100644 index 000000000000..895cdac4fcdd --- /dev/null +++ b/include/linux/fscrypto.h @@ -0,0 +1,433 @@ +/* + * General per-file encryption definition + * + * Copyright (C) 2015, Google, Inc. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ + +#ifndef _LINUX_FSCRYPTO_H +#define _LINUX_FSCRYPTO_H + +#include +#include +#include +#include +#include +#include + +#define FS_KEY_DERIVATION_NONCE_SIZE 16 +#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 + +#define FS_POLICY_FLAGS_PAD_4 0x00 +#define FS_POLICY_FLAGS_PAD_8 0x01 +#define FS_POLICY_FLAGS_PAD_16 0x02 +#define FS_POLICY_FLAGS_PAD_32 0x03 +#define FS_POLICY_FLAGS_PAD_MASK 0x03 +#define FS_POLICY_FLAGS_VALID 0x03 + +/* Encryption algorithms */ +#define FS_ENCRYPTION_MODE_INVALID 0 +#define FS_ENCRYPTION_MODE_AES_256_XTS 1 +#define FS_ENCRYPTION_MODE_AES_256_GCM 2 +#define FS_ENCRYPTION_MODE_AES_256_CBC 3 +#define FS_ENCRYPTION_MODE_AES_256_CTS 4 + +/** + * Encryption context for inode + * + * Protector format: + * 1 byte: Protector format (1 = this version) + * 1 byte: File contents encryption mode + * 1 byte: File names encryption mode + * 1 byte: Flags + * 8 bytes: Master Key descriptor + * 16 bytes: Encryption Key derivation nonce + */ +struct fscrypt_context { + u8 format; + u8 contents_encryption_mode; + u8 filenames_encryption_mode; + u8 flags; + u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; +} __packed; + +/* Encryption parameters */ +#define FS_XTS_TWEAK_SIZE 16 +#define FS_AES_128_ECB_KEY_SIZE 16 +#define FS_AES_256_GCM_KEY_SIZE 32 +#define FS_AES_256_CBC_KEY_SIZE 32 +#define FS_AES_256_CTS_KEY_SIZE 32 +#define FS_AES_256_XTS_KEY_SIZE 64 +#define FS_MAX_KEY_SIZE 64 + +#define FS_KEY_DESC_PREFIX "fscrypt:" +#define FS_KEY_DESC_PREFIX_SIZE 8 + +/* This is passed in from userspace into the kernel keyring */ +struct fscrypt_key { + u32 mode; + u8 raw[FS_MAX_KEY_SIZE]; + u32 size; +} __packed; + +struct fscrypt_info { + u8 ci_data_mode; + u8 ci_filename_mode; + u8 ci_flags; + struct crypto_ablkcipher *ci_ctfm; + struct key *ci_keyring_key; + u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE]; +}; + +#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 +#define FS_WRITE_PATH_FL 0x00000002 + +struct fscrypt_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + u8 flags; /* Flags */ + u8 mode; /* Encryption mode for tfm */ +}; + +struct fscrypt_completion_result { + struct completion completion; + int res; +}; + +#define DECLARE_FS_COMPLETION_RESULT(ecr) \ + struct fscrypt_completion_result ecr = { \ + COMPLETION_INITIALIZER((ecr).completion), 0 } + +static inline int fscrypt_key_size(int mode) +{ + switch (mode) { + case FS_ENCRYPTION_MODE_AES_256_XTS: + return FS_AES_256_XTS_KEY_SIZE; + case FS_ENCRYPTION_MODE_AES_256_GCM: + return FS_AES_256_GCM_KEY_SIZE; + case FS_ENCRYPTION_MODE_AES_256_CBC: + return FS_AES_256_CBC_KEY_SIZE; + case FS_ENCRYPTION_MODE_AES_256_CTS: + return FS_AES_256_CTS_KEY_SIZE; + default: + BUG(); + } + return 0; +} + +#define FS_FNAME_NUM_SCATTER_ENTRIES 4 +#define FS_CRYPTO_BLOCK_SIZE 16 +#define FS_FNAME_CRYPTO_DIGEST_SIZE 32 + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct fscrypt_symlink_data { + __le16 len; + char encrypted_path[1]; +} __packed; + +/** + * This function is used to calculate the disk space required to + * store a filename of length l in encrypted symlink format. + */ +static inline u32 fscrypt_symlink_data_len(u32 l) +{ + if (l < FS_CRYPTO_BLOCK_SIZE) + l = FS_CRYPTO_BLOCK_SIZE; + return (l + sizeof(struct fscrypt_symlink_data) - 1); +} + +struct fscrypt_str { + unsigned char *name; + u32 len; +}; + +struct fscrypt_name { + const struct qstr *usr_fname; + struct fscrypt_str disk_name; + u32 hash; + u32 minor_hash; + struct fscrypt_str crypto_buf; +}; + +#define FSTR_INIT(n, l) { .name = n, .len = l } +#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) + +/* + * crypto opertions for filesystems + */ +struct fscrypt_operations { + int (*get_context)(struct inode *, void *, size_t); + int (*prepare_context)(struct inode *); + int (*set_context)(struct inode *, const void *, size_t, void *); + int (*dummy_context)(struct inode *); + bool (*is_encrypted)(struct inode *); + bool (*empty_dir)(struct inode *); + unsigned (*max_namelen)(struct inode *); +}; + +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + if (inode->i_sb->s_cop->dummy_context && + inode->i_sb->s_cop->dummy_context(inode)) + return true; + return false; +} + +static inline bool fscrypt_valid_contents_enc_mode(u32 mode) +{ + return (mode == FS_ENCRYPTION_MODE_AES_256_XTS); +} + +static inline bool fscrypt_valid_filenames_enc_mode(u32 mode) +{ + return (mode == FS_ENCRYPTION_MODE_AES_256_CTS); +} + +static inline u32 fscrypt_validate_encryption_key_size(u32 mode, u32 size) +{ + if (size == fscrypt_key_size(mode)) + return size; + return 0; +} + +static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) +{ + if (str->len == 1 && str->name[0] == '.') + return true; + + if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') + return true; + + return false; +} + +static inline struct page *fscrypt_control_page(struct page *page) +{ +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + return ((struct fscrypt_ctx *)page_private(page))->w.control_page; +#else + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +#endif +} + +static inline int fscrypt_has_encryption_key(struct inode *inode) +{ +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + return (inode->i_crypt_info != NULL); +#else + return 0; +#endif +} + +static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry) +{ +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY; + spin_unlock(&dentry->d_lock); +#endif +} + +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +extern const struct dentry_operations fscrypt_d_ops; +#endif + +static inline void fscrypt_set_d_op(struct dentry *dentry) +{ +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + d_set_d_op(dentry, &fscrypt_d_ops); +#endif +} + +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +/* crypto.c */ +extern struct kmem_cache *fscrypt_info_cachep; +int fscrypt_initialize(void); + +extern struct fscrypt_ctx *fscrypt_get_ctx(struct inode *); +extern void fscrypt_release_ctx(struct fscrypt_ctx *); +extern struct page *fscrypt_encrypt_page(struct inode *, struct page *); +extern int fscrypt_decrypt_page(struct page *); +extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); +extern void fscrypt_pullback_bio_page(struct page **, bool); +extern void fscrypt_restore_control_page(struct page *); +extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t, + unsigned int); +/* policy.c */ +extern int fscrypt_process_policy(struct inode *, + const struct fscrypt_policy *); +extern int fscrypt_get_policy(struct inode *, struct fscrypt_policy *); +extern int fscrypt_has_permitted_context(struct inode *, struct inode *); +extern int fscrypt_inherit_context(struct inode *, struct inode *, + void *, bool); +/* keyinfo.c */ +extern int get_crypt_info(struct inode *); +extern int fscrypt_get_encryption_info(struct inode *); +extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); + +/* fname.c */ +extern int fscrypt_setup_filename(struct inode *, const struct qstr *, + int lookup, struct fscrypt_name *); +extern void fscrypt_free_filename(struct fscrypt_name *); +extern u32 fscrypt_fname_encrypted_size(struct inode *, u32); +extern int fscrypt_fname_alloc_buffer(struct inode *, u32, + struct fscrypt_str *); +extern void fscrypt_fname_free_buffer(struct fscrypt_str *); +extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, + const struct fscrypt_str *, struct fscrypt_str *); +extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, + struct fscrypt_str *); +#endif + +/* crypto.c */ +static inline struct fscrypt_ctx *fscrypt_notsupp_get_ctx(struct inode *i) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void fscrypt_notsupp_release_ctx(struct fscrypt_ctx *c) +{ + return; +} + +static inline struct page *fscrypt_notsupp_encrypt_page(struct inode *i, + struct page *p) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int fscrypt_notsupp_decrypt_page(struct page *p) +{ + return -EOPNOTSUPP; +} + +static inline void fscrypt_notsupp_decrypt_bio_pages(struct fscrypt_ctx *c, + struct bio *b) +{ + return; +} + +static inline void fscrypt_notsupp_pullback_bio_page(struct page **p, bool b) +{ + return; +} + +static inline void fscrypt_notsupp_restore_control_page(struct page *p) +{ + return; +} + +static inline int fscrypt_notsupp_zeroout_range(struct inode *i, pgoff_t p, + sector_t s, unsigned int f) +{ + return -EOPNOTSUPP; +} + +/* policy.c */ +static inline int fscrypt_notsupp_process_policy(struct inode *i, + const struct fscrypt_policy *p) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_notsupp_get_policy(struct inode *i, + struct fscrypt_policy *p) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_notsupp_has_permitted_context(struct inode *p, + struct inode *i) +{ + return 0; +} + +static inline int fscrypt_notsupp_inherit_context(struct inode *p, + struct inode *i, void *v, bool b) +{ + return -EOPNOTSUPP; +} + +/* keyinfo.c */ +static inline int fscrypt_notsupp_get_encryption_info(struct inode *i) +{ + return -EOPNOTSUPP; +} + +static inline void fscrypt_notsupp_put_encryption_info(struct inode *i, + struct fscrypt_info *f) +{ + return; +} + + /* fname.c */ +static inline int fscrypt_notsupp_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct fscrypt_name *fname) +{ + if (dir->i_sb->s_cop->is_encrypted(dir)) + return -EOPNOTSUPP; + + memset(fname, 0, sizeof(struct fscrypt_name)); + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; +} + +static inline void fscrypt_notsupp_free_filename(struct fscrypt_name *fname) +{ + return; +} + +static inline u32 fscrypt_notsupp_fname_encrypted_size(struct inode *i, u32 s) +{ + /* never happens */ + WARN_ON(1); + return 0; +} + +static inline int fscrypt_notsupp_fname_alloc_buffer(struct inode *inode, + u32 ilen, struct fscrypt_str *crypto_str) +{ + return -EOPNOTSUPP; +} + +static inline void fscrypt_notsupp_fname_free_buffer(struct fscrypt_str *c) +{ + return; +} + +static inline int fscrypt_notsupp_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + const struct fscrypt_str *iname, + struct fscrypt_str *oname) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_notsupp_fname_usr_to_disk(struct inode *inode, + const struct qstr *iname, + struct fscrypt_str *oname) +{ + return -EOPNOTSUPP; +} +#endif /* _LINUX_FSCRYPTO_H */ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 3735fa0a6784..7ab5efce3a58 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -168,6 +168,24 @@ struct inodes_stat_t { #define FS_IOC32_GETVERSION _IOR('v', 1, int) #define FS_IOC32_SETVERSION _IOW('v', 2, int) +/* + * File system encryption support + */ +/* Policy provided via an ioctl on the topmost directory */ +#define FS_KEY_DESCRIPTOR_SIZE 8 + +struct fscrypt_policy { + __u8 version; + __u8 contents_encryption_mode; + __u8 filenames_encryption_mode; + __u8 flags; + __u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; +} __packed; + +#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy) +#define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) +#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) + /* * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS) */ -- GitLab From b21d08f6aca0e147be00a2d567fe8a430f6673d9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 23 Feb 2016 09:21:37 -0800 Subject: [PATCH 0162/5498] f2fs crypto: sync ext4_lookup and ext4_file_open This patch tries to catch up with lookup and open policies in ext4. Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 2 +- fs/f2fs/file.c | 4 ++++ fs/f2fs/namei.c | 23 +++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 7497b8d750b1..a427d76c0688 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -855,7 +855,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) if (f2fs_encrypted_inode(inode)) { err = fscrypt_get_encryption_info(inode); - if (err) + if (err && err != -ENOKEY) return err; err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 78128bd50fb8..7705dff1a95f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -442,6 +442,7 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) static int f2fs_file_open(struct inode *inode, struct file *filp) { int ret = generic_file_open(inode, filp); + struct inode *dir = filp->f_path.dentry->d_parent->d_inode; if (!ret && f2fs_encrypted_inode(inode)) { ret = fscrypt_get_encryption_info(inode); @@ -450,6 +451,9 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) if (!fscrypt_has_encryption_key(inode)) return -ENOKEY; } + if (f2fs_encrypted_inode(dir) && + !fscrypt_has_permitted_context(dir, inode)) + return -EPERM; return ret; } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 088dc996216c..4af8c417aab0 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -262,6 +262,21 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, nid_t ino; int err = 0; + if (f2fs_encrypted_inode(dir)) { + int res = fscrypt_get_encryption_info(dir); + + /* + * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is + * created while the directory was encrypted and we + * don't have access to the key. + */ + if (fscrypt_has_encryption_key(dir)) + fscrypt_set_encrypted_dentry(dentry); + fscrypt_set_d_op(dentry); + if (res && res != -ENOKEY) + return ERR_PTR(res); + } + if (dentry->d_name.len > F2FS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); @@ -282,6 +297,14 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (err) goto err_out; } + if (!IS_ERR(inode) && f2fs_encrypted_inode(dir) && + (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && + !fscrypt_has_permitted_context(dir, inode)) { + bool nokey = f2fs_encrypted_inode(inode) && + !fscrypt_has_encryption_key(inode); + iput(inode); + return nokey ? ERR_PTR(-ENOKEY) : ERR_PTR(-EPERM); + } return d_splice_alias(inode, dentry); err_out: -- GitLab From 16664a32b5c1827787d5a6ca87905f72cfe95c5f Mon Sep 17 00:00:00 2001 From: Fan Li Date: Mon, 29 Feb 2016 14:29:51 +0800 Subject: [PATCH 0163/5498] f2fs: modify the readahead method in ra_node_page() ra_node_page() is used to read ahead one node page. Comparing to regular read, it's faster because it doesn't wait for IO completion. But if it is called twice for reading the same block, and the IO request from the first call hasn't been completed before the second call, the second call will have to wait until the read is over. Here use the code in __do_page_cache_readahead() to solve this problem. It does nothing when someone else already puts the page in mapping. The status of page should be assured by whoever puts it there. This implement also prevents alteration of page reference count. Signed-off-by: Fan li Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5ebcba03411e..46369ede6b9f 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1084,12 +1084,11 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) return; f2fs_bug_on(sbi, check_nid_range(sbi, nid)); - apage = find_get_page(NODE_MAPPING(sbi), nid); - if (apage && PageUptodate(apage)) { - f2fs_put_page(apage, 0); + rcu_read_lock(); + apage = radix_tree_lookup(&NODE_MAPPING(sbi)->page_tree, nid); + rcu_read_unlock(); + if (apage) return; - } - f2fs_put_page(apage, 0); apage = grab_cache_page(NODE_MAPPING(sbi), nid); if (!apage) -- GitLab From 221f91faa16f0397d713aabc1b0deb34acf8e036 Mon Sep 17 00:00:00 2001 From: Keith Mok Date: Wed, 2 Mar 2016 12:04:24 -0800 Subject: [PATCH 0164/5498] f2fs: use cryptoapi crc32 functions The crc function is done bit by bit. Optimize this by use cryptoapi crc32 function which is backed by h/w acceleration. Signed-off-by: Keith Mok Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/f2fs.h --- fs/f2fs/Kconfig | 2 ++ fs/f2fs/checkpoint.c | 6 +++--- fs/f2fs/f2fs.h | 48 +++++++++++++++++++++++++------------------- fs/f2fs/super.c | 13 ++++++++++++ 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig index 402792bae503..1f8982a957f1 100644 --- a/fs/f2fs/Kconfig +++ b/fs/f2fs/Kconfig @@ -1,6 +1,8 @@ config F2FS_FS tristate "F2FS filesystem support" depends on BLOCK + select CRYPTO + select CRYPTO_CRC32 help F2FS is based on Log-structured File System (LFS), which supports versatile "flash-friendly" features. The design has been focused on diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index b2875911a1e8..70fd90b73e27 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -636,7 +636,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, goto invalid_cp1; crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); - if (!f2fs_crc_valid(crc, cp_block, crc_offset)) + if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset)) goto invalid_cp1; pre_version = cur_cp_version(cp_block); @@ -651,7 +651,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, goto invalid_cp2; crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); - if (!f2fs_crc_valid(crc, cp_block, crc_offset)) + if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset)) goto invalid_cp2; cur_version = cur_cp_version(cp_block); @@ -1030,7 +1030,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP)); - crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset)); + crc32 = f2fs_crc32(sbi, ckpt, le32_to_cpu(ckpt->checksum_offset)); *((__le32 *)((unsigned char *)ckpt + le32_to_cpu(ckpt->checksum_offset))) = cpu_to_le32(crc32); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cdaace2fc26d..5838d1844b7c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) @@ -84,27 +85,6 @@ struct f2fs_mount_info { #define F2FS_CLEAR_FEATURE(sb, mask) \ F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask) -#define CRCPOLY_LE 0xedb88320 - -static inline __u32 f2fs_crc32(void *buf, size_t len) -{ - unsigned char *p = (unsigned char *)buf; - __u32 crc = F2FS_SUPER_MAGIC; - int i; - - while (len--) { - crc ^= *p++; - for (i = 0; i < 8; i++) - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); - } - return crc; -} - -static inline bool f2fs_crc_valid(__u32 blk_crc, void *buf, size_t buf_size) -{ - return f2fs_crc32(buf, buf_size) == blk_crc; -} - static inline void inode_lock(struct inode *inode) { mutex_lock(&inode->i_mutex); @@ -850,6 +830,9 @@ struct f2fs_sb_info { /* For write statistics */ u64 sectors_written_start; u64 kbytes_written; + + /* Reference to checksum algorithm driver via cryptoapi */ + struct crypto_shash *s_chksum_driver; }; /* For write statistics. Suppose sector size is 512 bytes, @@ -887,6 +870,29 @@ static inline bool is_idle(struct f2fs_sb_info *sbi) /* * Inline functions */ +static inline u32 f2fs_crc32(struct f2fs_sb_info *sbi, const void *address, + unsigned int length) +{ + SHASH_DESC_ON_STACK(shash, sbi->s_chksum_driver); + u32 *ctx = (u32 *)shash_desc_ctx(shash); + int err; + + shash->tfm = sbi->s_chksum_driver; + shash->flags = 0; + *ctx = F2FS_SUPER_MAGIC; + + err = crypto_shash_update(shash, address, length); + BUG_ON(err); + + return *ctx; +} + +static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc, + void *buf, size_t buf_size) +{ + return f2fs_crc32(sbi, buf, buf_size) == blk_crc; +} + static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) { return container_of(inode, struct f2fs_inode_info, vfs_inode); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0cdb075a6777..ecdb29fbfe23 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -590,6 +590,8 @@ static void f2fs_put_super(struct super_block *sb) wait_for_completion(&sbi->s_kobj_unregister); sb->s_fs_info = NULL; + if (sbi->s_chksum_driver) + crypto_free_shash(sbi->s_chksum_driver); kfree(sbi->raw_super); kfree(sbi); } @@ -1310,6 +1312,15 @@ try_onemore: if (!sbi) return -ENOMEM; + /* Load the checksum driver */ + sbi->s_chksum_driver = crypto_alloc_shash("crc32", 0, 0); + if (IS_ERR(sbi->s_chksum_driver)) { + f2fs_msg(sb, KERN_ERR, "Cannot load crc32 driver."); + err = PTR_ERR(sbi->s_chksum_driver); + sbi->s_chksum_driver = NULL; + goto free_sbi; + } + /* set a block size */ if (unlikely(!sb_set_blocksize(sb, F2FS_BLKSIZE))) { f2fs_msg(sb, KERN_ERR, "unable to set blocksize"); @@ -1568,6 +1579,8 @@ free_options: free_sb_buf: kfree(raw_super); free_sbi: + if (sbi->s_chksum_driver) + crypto_free_shash(sbi->s_chksum_driver); kfree(sbi); /* give only one another chance */ -- GitLab From e205c99bcc575244cc3f52991704dd2abec54f0e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 8 Mar 2016 09:04:35 -0800 Subject: [PATCH 0165/5498] f2fs: declare static functions Just to avoid sparse warnings. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 46369ede6b9f..47e3d14ce3db 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1101,7 +1101,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) /* * readahead MAX_RA_NODE number of node pages. */ -void ra_node_pages(struct page *parent, int start) +static void ra_node_pages(struct page *parent, int start) { struct f2fs_sb_info *sbi = F2FS_P_SB(parent); struct blk_plug plug; @@ -1120,7 +1120,7 @@ void ra_node_pages(struct page *parent, int start) blk_finish_plug(&plug); } -struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid, +static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid, struct page *parent, int start) { struct page *page; -- GitLab From 84a1546fb4d7ff7b87b27240700a01c2e0e8da45 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 9 Mar 2016 22:07:28 +0800 Subject: [PATCH 0166/5498] f2fs: clean up opened code with f2fs_update_dentry Just clean up opened code with existing function, no logic change. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index a427d76c0688..aabc03552149 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -341,24 +341,14 @@ int update_dent_inode(struct inode *inode, struct inode *to, void do_make_empty_dir(struct inode *inode, struct inode *parent, struct f2fs_dentry_ptr *d) { - struct f2fs_dir_entry *de; - - de = &d->dentry[0]; - de->name_len = cpu_to_le16(1); - de->hash_code = 0; - de->ino = cpu_to_le32(inode->i_ino); - memcpy(d->filename[0], ".", 1); - set_de_type(de, inode->i_mode); + struct qstr dot = QSTR_INIT(".", 1); + struct qstr dotdot = QSTR_INIT("..", 2); - de = &d->dentry[1]; - de->hash_code = 0; - de->name_len = cpu_to_le16(2); - de->ino = cpu_to_le32(parent->i_ino); - memcpy(d->filename[1], "..", 2); - set_de_type(de, parent->i_mode); + /* update dirent of "." */ + f2fs_update_dentry(inode->i_ino, inode->i_mode, d, &dot, 0, 0); - test_and_set_bit_le(0, (void *)d->bitmap); - test_and_set_bit_le(1, (void *)d->bitmap); + /* update dirent of ".." */ + f2fs_update_dentry(parent->i_ino, parent->i_mode, d, &dotdot, 0, 1); } static int make_empty_dir(struct inode *inode, -- GitLab From 848d473b582c56803456d687d2bbee1aeadff4f4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 10 Mar 2016 22:24:23 +0800 Subject: [PATCH 0167/5498] f2fs: fix to avoid unneeded unlock_new_inode During ->lookup, I_NEW state of inode was been cleared in f2fs_iget, so in error path, we don't need to clear it again. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 4af8c417aab0..88c034622e07 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -302,13 +302,13 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, !fscrypt_has_permitted_context(dir, inode)) { bool nokey = f2fs_encrypted_inode(inode) && !fscrypt_has_encryption_key(inode); - iput(inode); - return nokey ? ERR_PTR(-ENOKEY) : ERR_PTR(-EPERM); + err = nokey ? -ENOKEY : -EPERM; + goto err_out; } return d_splice_alias(inode, dentry); err_out: - iget_failed(inode); + iput(inode); return ERR_PTR(err); } -- GitLab From deda52e26df1275673dc26a2ed3806fde3aa4734 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 15 Mar 2016 00:07:56 +0100 Subject: [PATCH 0168/5498] f2fs: add missing argument to f2fs_setxattr stub The f2fs_setxattr() prototype for CONFIG_F2FS_FS_XATTR=n has been wrong for a long time, since 8ae8f1627f39 ("f2fs: support xattr security labels"), but there have never been any callers, so it did not matter. Now, the function gets called from f2fs_ioc_keyctl(), which causes a build failure: fs/f2fs/file.c: In function 'f2fs_ioc_keyctl': include/linux/stddef.h:7:14: error: passing argument 6 of 'f2fs_setxattr' makes integer from pointer without a cast [-Werror=int-conversion] #define NULL ((void *)0) ^ fs/f2fs/file.c:1599:27: note: in expansion of macro 'NULL' value, F2FS_KEY_SIZE, NULL, type); ^ In file included from ../fs/f2fs/file.c:29:0: fs/f2fs/xattr.h:129:19: note: expected 'int' but argument is of type 'void *' static inline int f2fs_setxattr(struct inode *inode, int index, ^ fs/f2fs/file.c:1597:9: error: too many arguments to function 'f2fs_setxattr' return f2fs_setxattr(inode, F2FS_XATTR_INDEX_KEY, ^ In file included from ../fs/f2fs/file.c:29:0: fs/f2fs/xattr.h:129:19: note: declared here static inline int f2fs_setxattr(struct inode *inode, int index, Thsi changes the prototype of the empty stub function to match that of the actual implementation. This will not make the key management work when F2FS_FS_XATTR is disabled, but it gets it to build at least. Signed-off-by: Arnd Bergmann Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index 71a7100d5492..d2fd0387a3c7 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -126,7 +126,8 @@ extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t); #define f2fs_xattr_handlers NULL static inline int f2fs_setxattr(struct inode *inode, int index, - const char *name, const void *value, size_t size, int flags) + const char *name, const void *value, size_t size, + struct page *page, int flags) { return -EOPNOTSUPP; } -- GitLab From 0b782c4da105f13514792a6082cc8ea87a631601 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 11 Mar 2016 15:33:22 -0800 Subject: [PATCH 0169/5498] f2fs: submit node page write bios when really required If many threads calls fsync with data writes, we don't need to flush every bios having node page writes. The f2fs_wait_on_page_writeback will flush its bios when the page is really needed. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 47e3d14ce3db..6f5a0e98e140 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1240,7 +1240,7 @@ int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, pgoff_t index, end; struct pagevec pvec; int step = ino ? 2 : 0; - int nwritten = 0, wrote = 0; + int nwritten = 0; pagevec_init(&pvec, 0); @@ -1330,8 +1330,6 @@ continue_unlock: if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) unlock_page(page); - else - wrote++; if (--wbc->nr_to_write == 0) break; @@ -1349,14 +1347,6 @@ continue_unlock: step++; goto next_step; } - - if (wrote) { - if (ino) - f2fs_submit_merged_bio_cond(sbi, NULL, NULL, - ino, NODE, WRITE); - else - f2fs_submit_merged_bio(sbi, NODE, WRITE); - } return nwritten; } -- GitLab From 172d94fd2b8ac46b8da8df97ed65bdd946b9fea5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 26 Mar 2016 10:13:05 -0700 Subject: [PATCH 0170/5498] f2fs/crypto: fix xts_tweak initialization Commit 0b81d07790726 ("fs crypto: move per-file encryption from f2fs tree to fs/crypto") moved the f2fs crypto files to fs/crypto/ and renamed the symbol prefixes from "f2fs_" to "fscrypt_" (and from "F2FS_" to just "FS" for preprocessor symbols). Because of the symbol renaming, it's a bit hard to see it as a file move: use git show -M30 0b81d07790726 to lower the rename detection to just 30% similarity and make git show the files as renamed (the header file won't be shown as a rename even then - since all it contains is symbol definitions, it looks almost completely different). Even with the renames showing as renames, the diffs are not all that easy to read, since so much is just the renames. But Eric Biggers noticed that it's not just all renames: the initialization of the xts_tweak had been broken too, using the inode number rather than the page offset. That's not right - it makes the xfs_tweak the same for all pages of each inode. It _might_ make sense to make the xfs_tweak contain both the offset _and_ the inode number, but not just the inode number. Reported-by: Eric Biggers Cc: Jaegeuk Kim Signed-off-by: Linus Torvalds --- fs/crypto/crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 7c4b92344dcf..8a357ad51e2c 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -171,7 +171,7 @@ static int do_page_crypto(struct inode *inode, fscrypt_complete, &ecr); BUILD_BUG_ON(FS_XTS_TWEAK_SIZE < sizeof(index)); - memcpy(xts_tweak, &inode->i_ino, sizeof(index)); + memcpy(xts_tweak, &index, sizeof(index)); memset(&xts_tweak[sizeof(index)], 0, FS_XTS_TWEAK_SIZE - sizeof(index)); -- GitLab From 7061a9c4b092899a8b2cb84193ce9b71afa5daaf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 20 Mar 2016 15:33:20 -0700 Subject: [PATCH 0171/5498] f2fs: cover large section in sanity check of super This patch fixes the bug which does not cover a large section case when checking the sanity of superblock. If f2fs detects misalignment, it will fix the superblock during the mount time, so it doesn't need to trigger fsck.f2fs further. Reported-by: Matthias Prager Reported-by: David Gnedt Cc: stable 4.5+ Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 102 ++++++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ecdb29fbfe23..5913fe87bc43 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -984,9 +984,25 @@ static loff_t max_file_blocks(void) return result; } +static int __f2fs_commit_super(struct buffer_head *bh, + struct f2fs_super_block *super) +{ + lock_buffer(bh); + if (super) + memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); + set_buffer_uptodate(bh); + set_buffer_dirty(bh); + unlock_buffer(bh); + + /* it's rare case, we can do fua all the time */ + return __sync_dirty_buffer(bh, WRITE_FLUSH_FUA); +} + static inline bool sanity_check_area_boundary(struct super_block *sb, - struct f2fs_super_block *raw_super) + struct buffer_head *bh) { + struct f2fs_super_block *raw_super = (struct f2fs_super_block *) + (bh->b_data + F2FS_SUPER_OFFSET); u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr); u32 sit_blkaddr = le32_to_cpu(raw_super->sit_blkaddr); @@ -1000,6 +1016,10 @@ static inline bool sanity_check_area_boundary(struct super_block *sb, u32 segment_count_main = le32_to_cpu(raw_super->segment_count_main); u32 segment_count = le32_to_cpu(raw_super->segment_count); u32 log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); + u64 main_end_blkaddr = main_blkaddr + + (segment_count_main << log_blocks_per_seg); + u64 seg_end_blkaddr = segment0_blkaddr + + (segment_count << log_blocks_per_seg); if (segment0_blkaddr != cp_blkaddr) { f2fs_msg(sb, KERN_INFO, @@ -1044,22 +1064,45 @@ static inline bool sanity_check_area_boundary(struct super_block *sb, return true; } - if (main_blkaddr + (segment_count_main << log_blocks_per_seg) != - segment0_blkaddr + (segment_count << log_blocks_per_seg)) { + if (main_end_blkaddr > seg_end_blkaddr) { f2fs_msg(sb, KERN_INFO, - "Wrong MAIN_AREA boundary, start(%u) end(%u) blocks(%u)", + "Wrong MAIN_AREA boundary, start(%u) end(%u) block(%u)", main_blkaddr, - segment0_blkaddr + (segment_count << log_blocks_per_seg), + segment0_blkaddr + + (segment_count << log_blocks_per_seg), segment_count_main << log_blocks_per_seg); return true; + } else if (main_end_blkaddr < seg_end_blkaddr) { + int err = 0; + char *res; + + /* fix in-memory information all the time */ + raw_super->segment_count = cpu_to_le32((main_end_blkaddr - + segment0_blkaddr) >> log_blocks_per_seg); + + if (f2fs_readonly(sb) || bdev_read_only(sb->s_bdev)) { + res = "internally"; + } else { + err = __f2fs_commit_super(bh, NULL); + res = err ? "failed" : "done"; + } + f2fs_msg(sb, KERN_INFO, + "Fix alignment : %s, start(%u) end(%u) block(%u)", + res, main_blkaddr, + segment0_blkaddr + + (segment_count << log_blocks_per_seg), + segment_count_main << log_blocks_per_seg); + if (err) + return true; } - return false; } static int sanity_check_raw_super(struct super_block *sb, - struct f2fs_super_block *raw_super) + struct buffer_head *bh) { + struct f2fs_super_block *raw_super = (struct f2fs_super_block *) + (bh->b_data + F2FS_SUPER_OFFSET); unsigned int blocksize; if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) { @@ -1126,7 +1169,7 @@ static int sanity_check_raw_super(struct super_block *sb, } /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */ - if (sanity_check_area_boundary(sb, raw_super)) + if (sanity_check_area_boundary(sb, bh)) return 1; return 0; @@ -1202,7 +1245,7 @@ static int read_raw_super_block(struct super_block *sb, { int block; struct buffer_head *bh; - struct f2fs_super_block *super, *buf; + struct f2fs_super_block *super; int err = 0; super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL); @@ -1218,11 +1261,8 @@ static int read_raw_super_block(struct super_block *sb, continue; } - buf = (struct f2fs_super_block *) - (bh->b_data + F2FS_SUPER_OFFSET); - /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, buf)) { + if (sanity_check_raw_super(sb, bh)) { f2fs_msg(sb, KERN_ERR, "Can't find valid F2FS filesystem in %dth superblock", block + 1); @@ -1232,7 +1272,8 @@ static int read_raw_super_block(struct super_block *sb, } if (!*raw_super) { - memcpy(super, buf, sizeof(*super)); + memcpy(super, bh->b_data + F2FS_SUPER_OFFSET, + sizeof(*super)); *valid_super_block = block; *raw_super = super; } @@ -1252,42 +1293,29 @@ static int read_raw_super_block(struct super_block *sb, return err; } -static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block) +int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) { - struct f2fs_super_block *super = F2FS_RAW_SUPER(sbi); struct buffer_head *bh; int err; - bh = sb_getblk(sbi->sb, block); + /* write back-up superblock first */ + bh = sb_getblk(sbi->sb, sbi->valid_super_block ? 0: 1); if (!bh) return -EIO; - - lock_buffer(bh); - memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super)); - set_buffer_uptodate(bh); - set_buffer_dirty(bh); - unlock_buffer(bh); - - /* it's rare case, we can do fua all the time */ - err = __sync_dirty_buffer(bh, WRITE_FLUSH_FUA); + err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi)); brelse(bh); - return err; -} - -int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) -{ - int err; - - /* write back-up superblock first */ - err = __f2fs_commit_super(sbi, sbi->valid_super_block ? 0 : 1); - /* if we are in recovery path, skip writing valid superblock */ if (recover || err) return err; /* write current valid superblock */ - return __f2fs_commit_super(sbi, sbi->valid_super_block); + bh = sb_getblk(sbi->sb, sbi->valid_super_block); + if (!bh) + return -EIO; + err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi)); + brelse(bh); + return err; } static int f2fs_fill_super(struct super_block *sb, void *data, int silent) -- GitLab From 1a86c0da2e140013072e6f4eaddeea944f4d8e27 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 30 Mar 2016 13:13:16 -0700 Subject: [PATCH 0172/5498] f2fs crypto: fix corrupted symlink in encrypted case In the encrypted symlink case, we should check its corrupted symname after decrypting it. Otherwise, we can report -ENOENT incorrectly, if encrypted symname starts with '\0'. Cc: stable 4.5+ Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 88c034622e07..fc83cf9a0ed6 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -1028,12 +1028,6 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, goto errout; } - /* this is broken symlink case */ - if (unlikely(cstr.name[0] == 0)) { - res = -ENOENT; - goto errout; - } - if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) { /* Symlink data on the disk is corrupted */ res = -EIO; @@ -1047,6 +1041,12 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, if (res < 0) goto errout; + /* this is broken symlink case */ + if (unlikely(pstr.name[0] == 0)) { + res = -ENOENT; + goto errout; + } + paddr = pstr.name; /* Null-terminate the name */ -- GitLab From 88a6985819751341aeec352627d20cde0549fcb8 Mon Sep 17 00:00:00 2001 From: Shuoran Liu Date: Tue, 29 Mar 2016 18:00:15 +0800 Subject: [PATCH 0173/5498] f2fs: retrieve IO write stat from the right place In the following patch, f2fs: split journal cache from curseg cache journal cache is split from curseg cache. So IO write statistics should be retrived from journal cache but not curseg->sum_blk. Otherwise, it will get 0, and the stat is lost. Signed-off-by: Shuoran Liu Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5913fe87bc43..59714dfe46b8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1470,7 +1470,7 @@ try_onemore: seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); if (__exist_node_summaries(sbi)) sbi->kbytes_written = - le64_to_cpu(seg_i->sum_blk->journal.info.kbytes_written); + le64_to_cpu(seg_i->journal->info.kbytes_written); build_gc_manager(sbi); -- GitLab From 8d5512f26b19ffd2a54b937044d410c991d2ddc7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 11 Apr 2016 15:10:11 -0700 Subject: [PATCH 0174/5498] fscrypto: use dget_parent() in fscrypt_d_revalidate() This patch updates fscrypto along with the below ext4 crypto change. Fixes: 3d43bcfef5f0 ("ext4 crypto: use dget_parent() in ext4_d_revalidate()") Cc: Theodore Ts'o Signed-off-by: Jaegeuk Kim --- fs/crypto/crypto.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 8a357ad51e2c..5ee00234a366 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -344,13 +344,17 @@ EXPORT_SYMBOL(fscrypt_zeroout_range); */ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) { - struct inode *dir = d_inode(dentry->d_parent); - struct fscrypt_info *ci = dir->i_crypt_info; + struct dentry *dir; + struct fscrypt_info *ci; int dir_has_key, cached_with_key; - if (!dir->i_sb->s_cop->is_encrypted(dir)) + dir = dget_parent(dentry); + if (!d_inode(dir)->i_sb->s_cop->is_encrypted(d_inode(dir))) { + dput(dir); return 0; + } + ci = d_inode(dir)->i_crypt_info; if (ci && ci->ci_keyring_key && (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | (1 << KEY_FLAG_REVOKED) | @@ -362,6 +366,7 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY; spin_unlock(&dentry->d_lock); dir_has_key = (ci != NULL); + dput(dir); /* * If the dentry was cached without the key, and it is a -- GitLab From c88b8a041e107edb149e1225abfaf4f4350bb045 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 11 Apr 2016 15:15:38 -0700 Subject: [PATCH 0175/5498] f2fs: use dget_parent and file_dentry in f2fs_file_open This patch synced with the below two ext4 crypto fixes together. In 4.6-rc1, f2fs newly introduced accessing f_path.dentry which crashes overlayfs. To fix, now we need to use file_dentry() to access that field. [Backport NOTE] - Over 4.2, it should use file_dentry Fixes: c0a37d487884 ("ext4: use file_dentry()") Fixes: 9dd78d8c9a7b ("ext4: use dget_parent() in ext4_file_open()") Cc: Miklos Szeredi Cc: Theodore Ts'o Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 7705dff1a95f..d638b4f5c0d8 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -442,7 +442,7 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) static int f2fs_file_open(struct inode *inode, struct file *filp) { int ret = generic_file_open(inode, filp); - struct inode *dir = filp->f_path.dentry->d_parent->d_inode; + struct dentry *dir; if (!ret && f2fs_encrypted_inode(inode)) { ret = fscrypt_get_encryption_info(inode); @@ -451,9 +451,13 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) if (!fscrypt_has_encryption_key(inode)) return -ENOKEY; } - if (f2fs_encrypted_inode(dir) && - !fscrypt_has_permitted_context(dir, inode)) + dir = dget_parent(filp->f_path.dentry); + if (f2fs_encrypted_inode(d_inode(dir)) && + !fscrypt_has_permitted_context(d_inode(dir), inode)) { + dput(dir); return -EPERM; + } + dput(dir); return ret; } -- GitLab From ba13f24c46dcb41699a134f7fbbc60aa9be61661 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 11 Apr 2016 15:51:57 -0700 Subject: [PATCH 0176/5498] fscrypto: don't let data integrity writebacks fail with ENOMEM This patch fixes the issue introduced by the ext4 crypto fix in a same manner. For F2FS, however, we flush the pending IOs and wait for a while to acquire free memory. Fixes: c9af28fdd4492 ("ext4 crypto: don't let data integrity writebacks fail with ENOMEM") Cc: Theodore Ts'o Signed-off-by: Jaegeuk Kim Conflicts: fs/crypto/crypto.c --- fs/crypto/crypto.c | 36 ++++++++++++++++++++---------------- fs/f2fs/data.c | 16 +++++++++++++--- include/linux/fscrypto.h | 9 +++++---- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 5ee00234a366..02f69c363bc1 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -82,13 +82,14 @@ EXPORT_SYMBOL(fscrypt_release_ctx); /** * fscrypt_get_ctx() - Gets an encryption context * @inode: The inode for which we are doing the crypto + * @gfp_flags: The gfp flag for memory allocation * * Allocates and initializes an encryption context. * * Return: An allocated and initialized encryption context on success; error * value or NULL otherwise. */ -struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode) +struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags) { struct fscrypt_ctx *ctx = NULL; struct fscrypt_info *ci = inode->i_crypt_info; @@ -114,7 +115,7 @@ struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode) list_del(&ctx->free_list); spin_unlock_irqrestore(&fscrypt_ctx_lock, flags); if (!ctx) { - ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, GFP_NOFS); + ctx = kmem_cache_zalloc(fscrypt_ctx_cachep, gfp_flags); if (!ctx) return ERR_PTR(-ENOMEM); ctx->flags |= FS_CTX_REQUIRES_FREE_ENCRYPT_FL; @@ -148,7 +149,8 @@ typedef enum { static int do_page_crypto(struct inode *inode, fscrypt_direction_t rw, pgoff_t index, - struct page *src_page, struct page *dest_page) + struct page *src_page, struct page *dest_page, + gfp_t gfp_flags) { u8 xts_tweak[FS_XTS_TWEAK_SIZE]; struct ablkcipher_request *req = NULL; @@ -158,7 +160,7 @@ static int do_page_crypto(struct inode *inode, struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; - req = ablkcipher_request_alloc(tfm, GFP_NOFS); + req = ablkcipher_request_alloc(tfm, gfp_flags); if (!req) { printk_ratelimited(KERN_ERR "%s: crypto_request_alloc() failed\n", @@ -200,10 +202,9 @@ static int do_page_crypto(struct inode *inode, return 0; } -static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx) +static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags) { - ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, - GFP_NOWAIT); + ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags); if (ctx->w.bounce_page == NULL) return ERR_PTR(-ENOMEM); ctx->flags |= FS_WRITE_PATH_FL; @@ -214,6 +215,7 @@ static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx) * fscypt_encrypt_page() - Encrypts a page * @inode: The inode for which the encryption should take place * @plaintext_page: The page to encrypt. Must be locked. + * @gfp_flags: The gfp flag for memory allocation * * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx * encryption context. @@ -226,7 +228,7 @@ static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx) * error value or NULL. */ struct page *fscrypt_encrypt_page(struct inode *inode, - struct page *plaintext_page) + struct page *plaintext_page, gfp_t gfp_flags) { struct fscrypt_ctx *ctx; struct page *ciphertext_page = NULL; @@ -234,18 +236,19 @@ struct page *fscrypt_encrypt_page(struct inode *inode, BUG_ON(!PageLocked(plaintext_page)); - ctx = fscrypt_get_ctx(inode); + ctx = fscrypt_get_ctx(inode, gfp_flags); if (IS_ERR(ctx)) return (struct page *)ctx; /* The encryption operation will require a bounce page. */ - ciphertext_page = alloc_bounce_page(ctx); + ciphertext_page = alloc_bounce_page(ctx, gfp_flags); if (IS_ERR(ciphertext_page)) goto errout; ctx->w.control_page = plaintext_page; err = do_page_crypto(inode, FS_ENCRYPT, plaintext_page->index, - plaintext_page, ciphertext_page); + plaintext_page, ciphertext_page, + gfp_flags); if (err) { ciphertext_page = ERR_PTR(err); goto errout; @@ -276,7 +279,7 @@ int fscrypt_decrypt_page(struct page *page) BUG_ON(!PageLocked(page)); return do_page_crypto(page->mapping->host, - FS_DECRYPT, page->index, page, page); + FS_DECRYPT, page->index, page, page, GFP_NOFS); } EXPORT_SYMBOL(fscrypt_decrypt_page); @@ -290,11 +293,11 @@ int fscrypt_zeroout_range(struct inode *inode, pgoff_t lblk, BUG_ON(inode->i_sb->s_blocksize != PAGE_CACHE_SIZE); - ctx = fscrypt_get_ctx(inode); + ctx = fscrypt_get_ctx(inode, GFP_NOFS); if (IS_ERR(ctx)) return PTR_ERR(ctx); - ciphertext_page = alloc_bounce_page(ctx); + ciphertext_page = alloc_bounce_page(ctx, GFP_NOWAIT); if (IS_ERR(ciphertext_page)) { err = PTR_ERR(ciphertext_page); goto errout; @@ -302,11 +305,12 @@ int fscrypt_zeroout_range(struct inode *inode, pgoff_t lblk, while (len--) { err = do_page_crypto(inode, FS_ENCRYPT, lblk, - ZERO_PAGE(0), ciphertext_page); + ZERO_PAGE(0), ciphertext_page, + GFP_NOFS); if (err) goto errout; - bio = bio_alloc(GFP_KERNEL, 1); + bio = bio_alloc(GFP_NOWAIT, 1); if (!bio) { err = -ENOMEM; goto errout; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 460daafc7117..3b460b1cef8f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -993,7 +993,7 @@ submit_and_realloc: if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { - ctx = fscrypt_get_ctx(inode); + ctx = fscrypt_get_ctx(inode, GFP_NOFS); if (IS_ERR(ctx)) goto set_error_page; @@ -1093,14 +1093,24 @@ int do_write_data_page(struct f2fs_io_info *fio) } if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + gfp_t gfp_flags = GFP_NOFS; /* wait for GCed encrypted page writeback */ f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode), fio->old_blkaddr); - - fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page); +retry_encrypt: + fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, + gfp_flags); if (IS_ERR(fio->encrypted_page)) { err = PTR_ERR(fio->encrypted_page); + if (err == -ENOMEM) { + /* flush pending ios and wait for a while */ + f2fs_flush_merged_bios(F2FS_I_SB(inode)); + congestion_wait(BLK_RW_ASYNC, HZ/50); + gfp_flags |= __GFP_NOFAIL; + err = 0; + goto retry_encrypt; + } goto out_writepage; } } diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h index 895cdac4fcdd..4c4bc8c058ab 100644 --- a/include/linux/fscrypto.h +++ b/include/linux/fscrypto.h @@ -262,9 +262,9 @@ static inline void fscrypt_set_d_op(struct dentry *dentry) extern struct kmem_cache *fscrypt_info_cachep; int fscrypt_initialize(void); -extern struct fscrypt_ctx *fscrypt_get_ctx(struct inode *); +extern struct fscrypt_ctx *fscrypt_get_ctx(struct inode *, gfp_t); extern void fscrypt_release_ctx(struct fscrypt_ctx *); -extern struct page *fscrypt_encrypt_page(struct inode *, struct page *); +extern struct page *fscrypt_encrypt_page(struct inode *, struct page *, gfp_t); extern int fscrypt_decrypt_page(struct page *); extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); extern void fscrypt_pullback_bio_page(struct page **, bool); @@ -298,7 +298,8 @@ extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, #endif /* crypto.c */ -static inline struct fscrypt_ctx *fscrypt_notsupp_get_ctx(struct inode *i) +static inline struct fscrypt_ctx *fscrypt_notsupp_get_ctx(struct inode *i, + gfp_t f) { return ERR_PTR(-EOPNOTSUPP); } @@ -309,7 +310,7 @@ static inline void fscrypt_notsupp_release_ctx(struct fscrypt_ctx *c) } static inline struct page *fscrypt_notsupp_encrypt_page(struct inode *i, - struct page *p) + struct page *p, gfp_t f) { return ERR_PTR(-EOPNOTSUPP); } -- GitLab From 05bad66c57fdd15ad9faf59d3ba737fc59e00ff3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Apr 2016 16:05:36 -0700 Subject: [PATCH 0177/5498] ext4/fscrypto: avoid RCU lookup in d_revalidate As Al pointed, d_revalidate should return RCU lookup before using d_inode. This was originally introduced by: commit 34286d666230 ("fs: rcu-walk aware d_revalidate method"). Reported-by: Al Viro Signed-off-by: Jaegeuk Kim Cc: Theodore Ts'o Cc: stable Conflicts: fs/ext4/crypto.c --- fs/crypto/crypto.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 02f69c363bc1..99be22df21fa 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -28,6 +28,7 @@ #include #include #include +#include #include static unsigned int num_prealloc_crypto_pages = 32; @@ -352,6 +353,9 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) struct fscrypt_info *ci; int dir_has_key, cached_with_key; + if (flags & LOOKUP_RCU) + return -ECHILD; + dir = dget_parent(dentry); if (!d_inode(dir)->i_sb->s_cop->is_encrypted(d_inode(dir))) { dput(dir); -- GitLab From 2b8936cf79cbbbd5d7c5a362284bfc459c9b4a8f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Mar 2016 10:42:01 -0700 Subject: [PATCH 0178/5498] f2fs: give RO message when recovering superblock When one of superblocks is missing, f2fs recovers it with the valid one. But, even if f2fs is mounted as RO, we'd better notify that too. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 59714dfe46b8..b37179ff1c19 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1298,6 +1298,9 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) struct buffer_head *bh; int err; + if (f2fs_readonly(sbi->sb) || bdev_read_only(sbi->sb->s_bdev)) + return -EROFS; + /* write back-up superblock first */ bh = sb_getblk(sbi->sb, sbi->valid_super_block ? 0: 1); if (!bh) @@ -1565,7 +1568,7 @@ try_onemore: kfree(options); /* recover broken superblock */ - if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) { + if (recovery) { err = f2fs_commit_super(sbi, true); f2fs_msg(sb, KERN_INFO, "Try to recover %dth superblock, ret: %ld", -- GitLab From 38a5756f7963152a6f147c8640f0b1a6d67857d8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Mar 2016 17:05:27 -0700 Subject: [PATCH 0179/5498] f2fs: recover superblock at RW remounts This patch adds a sbi flag, SBI_NEED_SB_WRITE, which indicates it needs to recover superblock when (re)mounting as RW. This is set only when f2fs is mounted as RO. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 36 +++++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5838d1844b7c..c45730043565 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -703,6 +703,7 @@ enum { SBI_IS_CLOSE, /* specify unmounting */ SBI_NEED_FSCK, /* need fsck.f2fs to fix */ SBI_POR_DOING, /* recovery is doing or not */ + SBI_NEED_SB_WRITE, /* need to recover superblock */ }; enum { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b37179ff1c19..c35b4a4e9255 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -796,6 +796,15 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) set_sbi_flag(sbi, SBI_IS_DIRTY); } + /* recover superblocks we couldn't write due to previous RO mount */ + if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) { + err = f2fs_commit_super(sbi, false); + f2fs_msg(sb, KERN_INFO, + "Try to recover all the superblocks, ret: %d", err); + if (!err) + clear_sbi_flag(sbi, SBI_NEED_SB_WRITE); + } + sync_filesystem(sb); sbi->mount_opt.opt = 0; @@ -852,8 +861,9 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) } skip: /* Update the POSIXACL Flag */ - sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); + return 0; restore_gc: if (need_restart_gc) { @@ -998,11 +1008,12 @@ static int __f2fs_commit_super(struct buffer_head *bh, return __sync_dirty_buffer(bh, WRITE_FLUSH_FUA); } -static inline bool sanity_check_area_boundary(struct super_block *sb, +static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi, struct buffer_head *bh) { struct f2fs_super_block *raw_super = (struct f2fs_super_block *) (bh->b_data + F2FS_SUPER_OFFSET); + struct super_block *sb = sbi->sb; u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr); u32 sit_blkaddr = le32_to_cpu(raw_super->sit_blkaddr); @@ -1081,6 +1092,7 @@ static inline bool sanity_check_area_boundary(struct super_block *sb, segment0_blkaddr) >> log_blocks_per_seg); if (f2fs_readonly(sb) || bdev_read_only(sb->s_bdev)) { + set_sbi_flag(sbi, SBI_NEED_SB_WRITE); res = "internally"; } else { err = __f2fs_commit_super(bh, NULL); @@ -1098,11 +1110,12 @@ static inline bool sanity_check_area_boundary(struct super_block *sb, return false; } -static int sanity_check_raw_super(struct super_block *sb, +static int sanity_check_raw_super(struct f2fs_sb_info *sbi, struct buffer_head *bh) { struct f2fs_super_block *raw_super = (struct f2fs_super_block *) (bh->b_data + F2FS_SUPER_OFFSET); + struct super_block *sb = sbi->sb; unsigned int blocksize; if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) { @@ -1169,7 +1182,7 @@ static int sanity_check_raw_super(struct super_block *sb, } /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */ - if (sanity_check_area_boundary(sb, bh)) + if (sanity_check_area_boundary(sbi, bh)) return 1; return 0; @@ -1239,10 +1252,11 @@ static void init_sb_info(struct f2fs_sb_info *sbi) * to get the first valid one. If any one of them is broken, we pass * them recovery flag back to the caller. */ -static int read_raw_super_block(struct super_block *sb, +static int read_raw_super_block(struct f2fs_sb_info *sbi, struct f2fs_super_block **raw_super, int *valid_super_block, int *recovery) { + struct super_block *sb = sbi->sb; int block; struct buffer_head *bh; struct f2fs_super_block *super; @@ -1262,7 +1276,7 @@ static int read_raw_super_block(struct super_block *sb, } /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, bh)) { + if (sanity_check_raw_super(sbi, bh)) { f2fs_msg(sb, KERN_ERR, "Can't find valid F2FS filesystem in %dth superblock", block + 1); @@ -1298,8 +1312,11 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) struct buffer_head *bh; int err; - if (f2fs_readonly(sbi->sb) || bdev_read_only(sbi->sb->s_bdev)) + if ((recover && f2fs_readonly(sbi->sb)) || + bdev_read_only(sbi->sb->s_bdev)) { + set_sbi_flag(sbi, SBI_NEED_SB_WRITE); return -EROFS; + } /* write back-up superblock first */ bh = sb_getblk(sbi->sb, sbi->valid_super_block ? 0: 1); @@ -1343,6 +1360,8 @@ try_onemore: if (!sbi) return -ENOMEM; + sbi->sb = sb; + /* Load the checksum driver */ sbi->s_chksum_driver = crypto_alloc_shash("crc32", 0, 0); if (IS_ERR(sbi->s_chksum_driver)) { @@ -1358,7 +1377,7 @@ try_onemore: goto free_sbi; } - err = read_raw_super_block(sb, &raw_super, &valid_super_block, + err = read_raw_super_block(sbi, &raw_super, &valid_super_block, &recovery); if (err) goto free_sbi; @@ -1393,7 +1412,6 @@ try_onemore: memcpy(sb->s_uuid, raw_super->uuid, sizeof(raw_super->uuid)); /* init f2fs-specific super block info */ - sbi->sb = sb; sbi->raw_super = raw_super; sbi->valid_super_block = valid_super_block; mutex_init(&sbi->gc_mutex); -- GitLab From 7512796ca5fc08a60a29a8014a07f6b4ddfad2e6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Mar 2016 16:12:58 -0700 Subject: [PATCH 0180/5498] f2fs: give -EINVAL for norecovery and rw mount Once detecting something to recover, f2fs should stop mounting, given norecovery and rw mount options. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/recovery.c | 11 +++++++---- fs/f2fs/super.c | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c45730043565..9eb765f96bc1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1939,7 +1939,7 @@ void build_gc_manager(struct f2fs_sb_info *); /* * recovery.c */ -int recover_fsync_data(struct f2fs_sb_info *); +int recover_fsync_data(struct f2fs_sb_info *, bool); bool space_for_roll_forward(struct f2fs_sb_info *); /* diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index e26ff81ec04c..b1136fba8a98 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -551,12 +551,13 @@ next: return err; } -int recover_fsync_data(struct f2fs_sb_info *sbi) +int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); struct list_head inode_list; block_t blkaddr; int err; + int ret = 0; bool need_writecp = false; fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", @@ -573,11 +574,13 @@ int recover_fsync_data(struct f2fs_sb_info *sbi) /* step #1: find fsynced inode numbers */ err = find_fsync_dnodes(sbi, &inode_list); - if (err) + if (err || list_empty(&inode_list)) goto out; - if (list_empty(&inode_list)) + if (check_only) { + ret = 1; goto out; + } need_writecp = true; @@ -625,5 +628,5 @@ out: } else { mutex_unlock(&sbi->cp_mutex); } - return err; + return ret ? ret: err; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c35b4a4e9255..1612699aede1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1562,14 +1562,24 @@ try_onemore: if (need_fsck) set_sbi_flag(sbi, SBI_NEED_FSCK); - err = recover_fsync_data(sbi); - if (err) { + err = recover_fsync_data(sbi, false); + if (err < 0) { need_fsck = true; f2fs_msg(sb, KERN_ERR, "Cannot recover all fsync data errno=%ld", err); goto free_kobj; } + } else { + err = recover_fsync_data(sbi, true); + + if (!f2fs_readonly(sb) && err > 0) { + err = -EINVAL; + f2fs_msg(sb, KERN_ERR, + "Need to recover fsync data"); + goto free_kobj; + } } + /* recover_fsync_data() cleared this already */ clear_sbi_flag(sbi, SBI_POR_DOING); -- GitLab From 97f3ce9473732061b121b20844de24e5a6588760 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 24 Mar 2016 10:29:39 -0700 Subject: [PATCH 0181/5498] f2fs: treat as a normal umount when remounting ro When user remounts f2fs as read-only, we can mark the checkpoint as umount. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1612699aede1..520cfce90b45 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -791,11 +791,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) org_mount_opt = sbi->mount_opt; active_logs = sbi->active_logs; - if (*flags & MS_RDONLY) { - set_opt(sbi, FASTBOOT); - set_sbi_flag(sbi, SBI_IS_DIRTY); - } - /* recover superblocks we couldn't write due to previous RO mount */ if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) { err = f2fs_commit_super(sbi, false); @@ -805,8 +800,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) clear_sbi_flag(sbi, SBI_NEED_SB_WRITE); } - sync_filesystem(sb); - sbi->mount_opt.opt = 0; default_options(sbi); @@ -838,7 +831,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) 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) { @@ -848,6 +840,16 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) need_stop_gc = true; } + if (*flags & MS_RDONLY) { + writeback_inodes_sb(sb, WB_REASON_SYNC); + sync_inodes_sb(sb); + + set_sbi_flag(sbi, SBI_IS_DIRTY); + set_sbi_flag(sbi, SBI_IS_CLOSE); + f2fs_sync_fs(sb, 1); + clear_sbi_flag(sbi, SBI_IS_CLOSE); + } + /* * We stop issue flush thread if FS is mounted as RO * or if flush_merge is not passed in mount option. -- GitLab From ff7c1650ba28dfc6b01a6d090ad6d8a35eaa56ca Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 18 Mar 2016 09:46:10 -0700 Subject: [PATCH 0182/5498] f2fs: show current mount status This patch remains the current mount status to f2fs status info. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/debug.c --- fs/f2fs/debug.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 48f2ae9452ef..4e4fda2909cb 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -218,8 +218,9 @@ static int stat_show(struct seq_file *s, void *v) update_general_status(si->sbi); - seq_printf(s, "\n=====[ partition info(%s). #%d ]=====\n", - bdevname(si->sbi->sb->s_bdev, devname), i++); + seq_printf(s, "\n=====[ partition info(%s). #%d, %s]=====\n", + bdevname(si->sbi->sb->s_bdev, devname), i++, + f2fs_readonly(si->sbi->sb) ? "RO": "RW"); seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ", si->sit_area_segs, si->nat_area_segs); seq_printf(s, "[SSA: %d] [MAIN: %d", -- GitLab From 521058220193ad88b31a523ac300432ce80c6fe6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 22 Feb 2016 18:29:18 +0800 Subject: [PATCH 0183/5498] f2fs: fix to convert inline directory correctly With below serials, we will lose parts of dirents: 1) mount f2fs with inline_dentry option 2) echo 1 > /sys/fs/f2fs/sdX/dir_level 3) mkdir dir 4) touch 180 files named [1-180] in dir 5) touch 181 in dir 6) echo 3 > /proc/sys/vm/drop_caches 7) ll dir ls: cannot access 2: No such file or directory ls: cannot access 4: No such file or directory ls: cannot access 5: No such file or directory ls: cannot access 6: No such file or directory ls: cannot access 8: No such file or directory ls: cannot access 9: No such file or directory ... total 360 drwxr-xr-x 2 root root 4096 Feb 19 15:12 ./ drwxr-xr-x 3 root root 4096 Feb 19 15:11 ../ -rw-r--r-- 1 root root 0 Feb 19 15:12 1 -rw-r--r-- 1 root root 0 Feb 19 15:12 10 -rw-r--r-- 1 root root 0 Feb 19 15:12 100 -????????? ? ? ? ? ? 101 -????????? ? ? ? ? ? 102 -????????? ? ? ? ? ? 103 ... The reason is: when doing the inline dir conversion, we didn't consider that directory has hierarchical hash structure which can be configured through sysfs interface 'dir_level'. By default, dir_level of directory inode is 0, it means we have one bucket in hash table located in first level, all dirents will be hashed in this bucket, so it has no problem for us to do the duplication simply between inline dentry page and converted normal dentry page. However, if we configured dir_level with the value N (greater than 0), it will expand the bucket number of first level hash table by 2^N - 1, it hashs dirents into different buckets according their hash value, if we still move all dirents to first bucket, it makes incorrent locating for inline dirents, the result is, although we can iterate all dirents through ->readdir, we can't stat some of them in ->lookup which based on hash table searching. This patch fixes this issue by rehashing dirents into correct position when converting inline directory. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 87 ++++++++++++++++++++------------------ fs/f2fs/f2fs.h | 3 ++ fs/f2fs/inline.c | 94 ++++++++++++++++++++++++++++++++++++++++- include/linux/f2fs_fs.h | 2 + 4 files changed, 144 insertions(+), 42 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index aabc03552149..134d5cd39ed8 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -48,7 +48,6 @@ unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { [F2FS_FT_SYMLINK] = DT_LNK, }; -#define S_SHIFT 12 static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFREG >> S_SHIFT] = F2FS_FT_REG_FILE, [S_IFDIR >> S_SHIFT] = F2FS_FT_DIR, @@ -64,6 +63,13 @@ void set_de_type(struct f2fs_dir_entry *de, umode_t mode) de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; } +unsigned char get_de_type(struct f2fs_dir_entry *de) +{ + if (de->file_type < F2FS_FT_MAX) + return f2fs_filetype_table[de->file_type]; + return DT_UNKNOWN; +} + static unsigned long dir_block_index(unsigned int level, int dir_level, unsigned int idx) { @@ -509,11 +515,7 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, } } -/* - * Caller should grab and release a rwsem by calling f2fs_lock_op() and - * f2fs_unlock_op(). - */ -int __f2fs_add_link(struct inode *dir, const struct qstr *name, +int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, struct inode *inode, nid_t ino, umode_t mode) { unsigned int bit_pos; @@ -526,28 +528,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_ptr d; struct page *page = NULL; - struct fscrypt_name fname; - struct qstr new_name; - int slots, err; - - err = fscrypt_setup_filename(dir, name, 0, &fname); - if (err) - return err; - - new_name.name = fname_name(&fname); - new_name.len = fname_len(&fname); - - if (f2fs_has_inline_dentry(dir)) { - err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); - if (!err || err != -EAGAIN) - goto out; - else - err = 0; - } + int slots, err = 0; level = 0; - slots = GET_DENTRY_SLOTS(new_name.len); - dentry_hash = f2fs_dentry_hash(&new_name); + slots = GET_DENTRY_SLOTS(new_name->len); + dentry_hash = f2fs_dentry_hash(new_name); current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { @@ -556,10 +541,8 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, } start: - if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) { - err = -ENOSPC; - goto out; - } + if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) + return -ENOSPC; /* Increase the depth, if required */ if (level == current_depth) @@ -573,10 +556,8 @@ start: for (block = bidx; block <= (bidx + nblock - 1); block++) { dentry_page = get_new_data_page(dir, NULL, block, true); - if (IS_ERR(dentry_page)) { - err = PTR_ERR(dentry_page); - goto out; - } + if (IS_ERR(dentry_page)) + return PTR_ERR(dentry_page); dentry_blk = kmap(dentry_page); bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, @@ -596,7 +577,7 @@ add_dentry: if (inode) { down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, &new_name, NULL); + page = init_inode_metadata(inode, dir, new_name, NULL); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; @@ -606,7 +587,7 @@ add_dentry: } make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); - f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos); + f2fs_update_dentry(ino, mode, &d, new_name, dentry_hash, bit_pos); set_page_dirty(dentry_page); @@ -628,7 +609,34 @@ fail: } kunmap(dentry_page); f2fs_put_page(dentry_page, 1); -out: + + return err; +} + +/* + * Caller should grab and release a rwsem by calling f2fs_lock_op() and + * f2fs_unlock_op(). + */ +int __f2fs_add_link(struct inode *dir, const struct qstr *name, + struct inode *inode, nid_t ino, umode_t mode) +{ + struct fscrypt_name fname; + struct qstr new_name; + int err; + + err = fscrypt_setup_filename(dir, name, 0, &fname); + if (err) + return err; + + new_name.name = fname_name(&fname); + new_name.len = fname_len(&fname); + + err = -EAGAIN; + if (f2fs_has_inline_dentry(dir)) + err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); + if (err == -EAGAIN) + err = f2fs_add_regular_entry(dir, &new_name, inode, ino, mode); + fscrypt_free_filename(&fname); f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); return err; @@ -792,10 +800,7 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, continue; } - if (de->file_type < F2FS_FT_MAX) - d_type = f2fs_filetype_table[de->file_type]; - else - d_type = DT_UNKNOWN; + d_type = get_de_type(de); de_name.name = d->filename[bit_pos]; de_name.len = le16_to_cpu(de->name_len); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9eb765f96bc1..985f6c730799 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1744,6 +1744,7 @@ struct dentry *f2fs_get_parent(struct dentry *child); */ extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; void set_de_type(struct f2fs_dir_entry *, umode_t); +unsigned char get_de_type(struct f2fs_dir_entry *); struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *, f2fs_hash_t, int *, struct f2fs_dentry_ptr *); bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, @@ -1764,6 +1765,8 @@ void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, int update_dent_inode(struct inode *, struct inode *, const struct qstr *); void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, const struct qstr *, f2fs_hash_t , unsigned int); +int f2fs_add_regular_entry(struct inode *, const struct qstr *, + struct inode *, nid_t, umode_t); int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, umode_t); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 358214e9f707..f0c6aca2e147 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -355,7 +355,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, * NOTE: ipage is grabbed by caller, but if any error occurs, we should * release ipage in this function. */ -static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, +static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *inline_dentry) { struct page *page; @@ -416,6 +416,98 @@ out: return err; } +static int f2fs_add_inline_entries(struct inode *dir, + struct f2fs_inline_dentry *inline_dentry) +{ + struct f2fs_dentry_ptr d; + unsigned long bit_pos = 0; + int err = 0; + + make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2); + + while (bit_pos < d.max) { + struct f2fs_dir_entry *de; + struct qstr new_name; + nid_t ino; + umode_t fake_mode; + + if (!test_bit_le(bit_pos, d.bitmap)) { + bit_pos++; + continue; + } + + de = &d.dentry[bit_pos]; + new_name.name = d.filename[bit_pos]; + new_name.len = de->name_len; + + ino = le32_to_cpu(de->ino); + fake_mode = get_de_type(de) << S_SHIFT; + + err = f2fs_add_regular_entry(dir, &new_name, NULL, + ino, fake_mode); + if (err) + goto punch_dentry_pages; + + if (unlikely(!de->name_len)) + d.max = -1; + + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + } + return 0; +punch_dentry_pages: + truncate_inode_pages(&dir->i_data, 0); + truncate_blocks(dir, 0, false); + remove_dirty_inode(dir); + return err; +} + +static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, + struct f2fs_inline_dentry *inline_dentry) +{ + struct f2fs_inline_dentry *backup_dentry; + int err; + + backup_dentry = kmalloc(sizeof(struct f2fs_inline_dentry), + GFP_F2FS_ZERO); + if (!backup_dentry) + return -ENOMEM; + + memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA); + truncate_inline_inode(ipage, 0); + + unlock_page(ipage); + + err = f2fs_add_inline_entries(dir, backup_dentry); + if (err) + goto recover; + + lock_page(ipage); + + stat_dec_inline_dir(dir); + clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); + update_inode(dir, ipage); + kfree(backup_dentry); + return 0; +recover: + lock_page(ipage); + memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); + i_size_write(dir, MAX_INLINE_DATA); + update_inode(dir, ipage); + f2fs_put_page(ipage, 1); + + kfree(backup_dentry); + return err; +} + +static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, + struct f2fs_inline_dentry *inline_dentry) +{ + if (!F2FS_I(dir)->i_dir_level) + return f2fs_move_inline_dirents(dir, ipage, inline_dentry); + else + return f2fs_move_rehashed_dirents(dir, ipage, inline_dentry); +} + int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, struct inode *inode, nid_t ino, umode_t mode) { diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 9eb215a155e0..71a9ba836a1f 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -508,4 +508,6 @@ enum { F2FS_FT_MAX }; +#define S_SHIFT 12 + #endif /* _LINUX_F2FS_FS_H */ -- GitLab From cfe7a03f595ead159e0577307833bf3a926c268f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 29 Mar 2016 16:13:45 -0700 Subject: [PATCH 0184/5498] f2fs: add BUG_ON to avoid unnecessary flow This patch adds BUG_ON instead of retrying loop. In the case of node pages, we already got this inode page, but unlocked it. By the fact that we don't truncate any node pages in operations, the page's mapping should be unchangeable. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 6f5a0e98e140..71728cc9461a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -832,7 +832,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from) trace_f2fs_truncate_inode_blocks_enter(inode, from); level = get_node_path(inode, from, offset, noffset); -restart: + page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) { trace_f2fs_truncate_inode_blocks_exit(inode, PTR_ERR(page)); @@ -896,10 +896,7 @@ skip_partial: if (offset[1] == 0 && ri->i_nid[offset[0] - NODE_DIR1_BLOCK]) { lock_page(page); - if (unlikely(page->mapping != NODE_MAPPING(sbi))) { - f2fs_put_page(page, 1); - goto restart; - } + BUG_ON(page->mapping != NODE_MAPPING(sbi)); f2fs_wait_on_page_writeback(page, NODE, true); ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0; set_page_dirty(page); -- GitLab From fdde6ad100e8a347fa16c1e10781e7b627b0fdd1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 11 Apr 2016 11:51:51 -0700 Subject: [PATCH 0185/5498] f2fs: fix dropping inmemory pages in a wrong time When one reader closes its file while the other writer is doing atomic writes, f2fs_release_file drops atomic data resulting in an empty commit. This patch fixes this wrong commit problem by checking openess of the file. Process0 Process1 open file start atomic write write data read data close file f2fs_release_file() clear atomic data commit atomic write Reported-by: Miao Xie Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d638b4f5c0d8..c802cf31c304 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1257,6 +1257,14 @@ out: static int f2fs_release_file(struct inode *inode, struct file *filp) { + /* + * f2fs_relase_file is called at every close calls. So we should + * not drop any inmemory pages by close called by other process. + */ + if (!(filp->f_mode & FMODE_WRITE) || + atomic_read(&inode->i_writecount) != 1) + return 0; + /* some remained atomic pages should discarded */ if (f2fs_is_atomic_file(inode)) drop_inmem_pages(inode); -- GitLab From 255759b83788d380ab3a2f13b9580116bdff228a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 11 Apr 2016 13:15:10 -0700 Subject: [PATCH 0186/5498] f2fs: unset atomic/volatile flag in f2fs_release_file The atomic/volatile operation should be done in pair of start and commit ioctl. For example, if a killed process remains open-ended atomic operation, we should drop its flag as well as its atomic data. Otherwise, if sqlite initiates another operation which doesn't require atomic writes, it will lose every data, since f2fs still treats with them as atomic writes; nobody will trigger its commit. Reported-by: Miao Xie Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 ++--- fs/f2fs/segment.c | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c802cf31c304..7436786e7fd2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1269,6 +1269,7 @@ static int f2fs_release_file(struct inode *inode, struct file *filp) if (f2fs_is_atomic_file(inode)) drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { + clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); set_inode_flag(F2FS_I(inode), FI_DROP_CACHE); filemap_fdatawrite(inode->i_mapping); clear_inode_flag(F2FS_I(inode), FI_DROP_CACHE); @@ -1452,10 +1453,8 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) if (ret) return ret; - if (f2fs_is_atomic_file(inode)) { - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + if (f2fs_is_atomic_file(inode)) drop_inmem_pages(inode); - } if (f2fs_is_volatile_file(inode)) { clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6f16b39f0b52..e785f0db5d99 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -239,6 +239,8 @@ void drop_inmem_pages(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + mutex_lock(&fi->inmem_lock); __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); mutex_unlock(&fi->inmem_lock); -- GitLab From 066dd80ae02b80fb240a5ad7e1ff45c3399ceae8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 6 Apr 2016 11:27:03 -0700 Subject: [PATCH 0187/5498] f2fs: remove redundant condition check This patch resolves the redundant condition check reported by David. Reported-by: David Binderman Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 70fd90b73e27..2e27b9fdfdf5 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -212,7 +212,7 @@ void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index) bool readahead = false; page = find_get_page(META_MAPPING(sbi), index); - if (!page || (page && !PageUptodate(page))) + if (!page || !PageUptodate(page)) readahead = true; f2fs_put_page(page, 0); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3b460b1cef8f..f147a6c9a15e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1497,7 +1497,7 @@ restart: } else { /* hole case */ err = get_dnode_of_data(&dn, index, LOOKUP_NODE); - if (err || (!err && dn.data_blkaddr == NULL_ADDR)) { + if (err || dn.data_blkaddr == NULL_ADDR) { f2fs_put_dnode(&dn); f2fs_lock_op(sbi); locked = true; -- GitLab From 69e13ca8e8070ef876eb32dc2f4e093db572082a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Apr 2016 11:52:30 -0700 Subject: [PATCH 0188/5498] f2fs: give -E2BIG for no space in xattr This patch returns -E2BIG if there is no space to add an xattr entry. This should fix generic/026 in xfstests as well. Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index d2b15513dca1..a3a5ae4252f7 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -540,7 +540,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, free = free + ENTRY_SIZE(here); if (unlikely(free < newsize)) { - error = -ENOSPC; + error = -E2BIG; goto exit; } } -- GitLab From c3b3acb290fe0f5e0720a418f33abb8955e1e4c4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Apr 2016 14:11:03 -0700 Subject: [PATCH 0189/5498] f2fs: don't invalidate atomic page if successful If we committed atomic write successfully, we don't need to invalidate pages. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e785f0db5d99..fc0d354f35d1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -223,9 +223,10 @@ static int __revoke_inmem_pages(struct inode *inode, f2fs_put_dnode(&dn); } next: - ClearPageUptodate(page); + /* we don't need to invalidate this in the sccessful status */ + if (drop || recover) + ClearPageUptodate(page); set_page_private(page, 0); - ClearPageUptodate(page); f2fs_put_page(page, 1); list_del(&cur->list); -- GitLab From 7b19afe21f2c193fa6b7bce4e56adb2b12346212 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Apr 2016 14:36:11 -0700 Subject: [PATCH 0190/5498] f2fs: flush dirty pages before starting atomic writes If somebody wrote some data before atomic writes, we should flush them in order to handle atomic data in a right period. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 7436786e7fd2..a74833a14baa 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1372,7 +1372,16 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); - return 0; + if (!get_dirty_pages(inode)) + return 0; + + f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, + "Unexpected flush for atomic writes: ino=%lu, npages=%u", + inode->i_ino, get_dirty_pages(inode)); + ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); + if (ret) + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + return ret; } static int f2fs_ioc_commit_atomic_write(struct file *filp) -- GitLab From eb80b43fe96fd8982a9d9207d2715ec7d7b84b74 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 13 Apr 2016 16:14:38 -0700 Subject: [PATCH 0191/5498] f2fs: avoid needless lock for node pages when fsyncing a file When fsync is called, sync_node_pages finds a proper direct node pages to flush. But, it locks unrelated direct node pages together unnecessarily. Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 71728cc9461a..deb3dd92e8c4 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1281,10 +1281,14 @@ next_step: * we should not skip writing node pages. */ lock_node: - if (ino && ino_of_node(page) == ino) - lock_page(page); - else if (!trylock_page(page)) + if (ino) { + if (ino_of_node(page) == ino) + lock_page(page); + else + continue; + } else if (!trylock_page(page)) { continue; + } if (unlikely(page->mapping != NODE_MAPPING(sbi))) { continue_unlock: -- GitLab From 42e11b5beeb9f78507b88c4bcf5fe10279f09a41 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 14 Apr 2016 16:48:52 -0700 Subject: [PATCH 0192/5498] f2fs: avoid writing 0'th page in volatile writes The first page of volatile writes usually contains a sort of header information which will be used for recovery. (e.g., journal header of sqlite) If this is written without other journal data, user needs to handle the stale journal information. Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f147a6c9a15e..f0d1092ceb2c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1178,8 +1178,10 @@ write: goto redirty_out; if (f2fs_is_drop_cache(inode)) goto out; - if (f2fs_is_volatile_file(inode) && !wbc->for_reclaim && - available_free_memory(sbi, BASE_CHECK)) + /* we should not write 0'th page having journal header */ + if (f2fs_is_volatile_file(inode) && (!page->index || + (!wbc->for_reclaim && + available_free_memory(sbi, BASE_CHECK)))) goto redirty_out; /* Dentry blocks are controlled by checkpoint */ -- GitLab From 679443656030e30855a7f8a402d11f5f0d692790 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 13 Apr 2016 16:24:44 -0700 Subject: [PATCH 0193/5498] f2fs: split sync_node_pages with fsync_node_pages This patch splits the existing sync_node_pages into (f)sync_node_pages. The fsync_node_pages is used for f2fs_sync_file only. Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/f2fs.h | 3 +- fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 2 +- fs/f2fs/node.c | 108 +++++++++++++++++++++++++++++++------------ 5 files changed, 84 insertions(+), 33 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2e27b9fdfdf5..3077245d9bd2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -893,7 +893,7 @@ retry_flush_nodes: if (get_pages(sbi, F2FS_DIRTY_NODES)) { up_write(&sbi->node_write); - err = sync_node_pages(sbi, 0, &wbc); + err = sync_node_pages(sbi, &wbc); if (err) { f2fs_unlock_all(sbi); goto out; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 985f6c730799..90348e21ce05 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1817,7 +1817,8 @@ void ra_node_page(struct f2fs_sb_info *, nid_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); struct page *get_node_page_ra(struct page *, int); void sync_inode_page(struct dnode_of_data *); -int sync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *); +int fsync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *); +int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); bool alloc_nid(struct f2fs_sb_info *, nid_t *); void alloc_nid_done(struct f2fs_sb_info *, nid_t); void alloc_nid_failed(struct f2fs_sb_info *, nid_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a74833a14baa..eb31d2cabab2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -257,7 +257,7 @@ go_write: goto out; } sync_nodes: - sync_node_pages(sbi, ino, &wbc); + fsync_node_pages(sbi, ino, &wbc); /* if cp_error was enabled, we should avoid infinite loop */ if (unlikely(f2fs_cp_error(sbi))) { diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index b0051a97824c..e82046523186 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -841,7 +841,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, .nr_to_write = LONG_MAX, .for_reclaim = 0, }; - sync_node_pages(sbi, 0, &wbc); + sync_node_pages(sbi, &wbc); } else { f2fs_submit_merged_bio(sbi, DATA, WRITE); } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index deb3dd92e8c4..978eacc6c0ed 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1231,12 +1231,84 @@ iput_out: iput(inode); } -int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, +int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, struct writeback_control *wbc) { pgoff_t index, end; struct pagevec pvec; - int step = ino ? 2 : 0; + int nwritten = 0; + + pagevec_init(&pvec, 0); + index = 0; + end = ULONG_MAX; + + while (index <= end) { + int i, nr_pages; + nr_pages = pagevec_lookup_tag(&pvec, NODE_MAPPING(sbi), &index, + PAGECACHE_TAG_DIRTY, + min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); + if (nr_pages == 0) + break; + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + + if (unlikely(f2fs_cp_error(sbi))) { + pagevec_release(&pvec); + return -EIO; + } + + if (!IS_DNODE(page) || !is_cold_node(page)) + continue; + if (ino_of_node(page) != ino) + continue; + + lock_page(page); + + if (unlikely(page->mapping != NODE_MAPPING(sbi))) { +continue_unlock: + unlock_page(page); + continue; + } + if (ino_of_node(page) != ino) + goto continue_unlock; + + if (!PageDirty(page)) { + /* someone wrote it for us */ + goto continue_unlock; + } + + f2fs_wait_on_page_writeback(page, NODE, true); + BUG_ON(PageWriteback(page)); + if (!clear_page_dirty_for_io(page)) + goto continue_unlock; + + set_fsync_mark(page, 1); + if (IS_INODE(page)) + set_dentry_mark(page, + need_dentry_mark(sbi, ino)); + nwritten++; + + if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) + unlock_page(page); + + if (--wbc->nr_to_write == 0) + break; + } + pagevec_release(&pvec); + cond_resched(); + + if (wbc->nr_to_write == 0) + break; + } + return nwritten; +} + +int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc) +{ + pgoff_t index, end; + struct pagevec pvec; + int step = 0; int nwritten = 0; pagevec_init(&pvec, 0); @@ -1275,28 +1347,15 @@ next_step: if (step == 2 && (!IS_DNODE(page) || !is_cold_node(page))) continue; - - /* - * If an fsync mode, - * we should not skip writing node pages. - */ lock_node: - if (ino) { - if (ino_of_node(page) == ino) - lock_page(page); - else - continue; - } else if (!trylock_page(page)) { + if (!trylock_page(page)) continue; - } if (unlikely(page->mapping != NODE_MAPPING(sbi))) { continue_unlock: unlock_page(page); continue; } - if (ino && ino_of_node(page) != ino) - goto continue_unlock; if (!PageDirty(page)) { /* someone wrote it for us */ @@ -1304,7 +1363,7 @@ continue_unlock: } /* flush inline_data */ - if (!ino && is_inline_node(page)) { + if (is_inline_node(page)) { clear_inline_node(page); unlock_page(page); flush_inline_data(sbi, ino_of_node(page)); @@ -1317,17 +1376,8 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - /* called by fsync() */ - if (ino && IS_DNODE(page)) { - set_fsync_mark(page, 1); - if (IS_INODE(page)) - set_dentry_mark(page, - need_dentry_mark(sbi, ino)); - nwritten++; - } else { - set_fsync_mark(page, 0); - set_dentry_mark(page, 0); - } + set_fsync_mark(page, 0); + set_dentry_mark(page, 0); if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) unlock_page(page); @@ -1475,7 +1525,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; - sync_node_pages(sbi, 0, wbc); + sync_node_pages(sbi, wbc); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return 0; -- GitLab From b049dd4949774ce071a649781766627bb3a14a30 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 15 Apr 2016 09:25:04 -0700 Subject: [PATCH 0194/5498] f2fs: report unwritten status in fsync_node_pages The fsync_node_pages should return pass or failure so that user could know fsync is completed or not. Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 4 +++- fs/f2fs/node.c | 13 ++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index eb31d2cabab2..37799e0e5719 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -257,7 +257,9 @@ go_write: goto out; } sync_nodes: - fsync_node_pages(sbi, ino, &wbc); + ret = fsync_node_pages(sbi, ino, &wbc); + if (ret) + goto out; /* if cp_error was enabled, we should avoid infinite loop */ if (unlikely(f2fs_cp_error(sbi))) { diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 978eacc6c0ed..b54b287e8ccb 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1236,7 +1236,7 @@ int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, { pgoff_t index, end; struct pagevec pvec; - int nwritten = 0; + int ret = 0; pagevec_init(&pvec, 0); index = 0; @@ -1287,21 +1287,20 @@ continue_unlock: if (IS_INODE(page)) set_dentry_mark(page, need_dentry_mark(sbi, ino)); - nwritten++; - if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) + ret = NODE_MAPPING(sbi)->a_ops->writepage(page, wbc); + if (ret) { unlock_page(page); - - if (--wbc->nr_to_write == 0) break; + } } pagevec_release(&pvec); cond_resched(); - if (wbc->nr_to_write == 0) + if (ret) break; } - return nwritten; + return ret ? -EIO: 0; } int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc) -- GitLab From 898eb34e25360735dc15cc5c1c64e0bca08f0c54 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 15 Apr 2016 09:43:17 -0700 Subject: [PATCH 0195/5498] f2fs: set fsync mark only for the last dnode In order to give atomic writes, we should consider power failure during sync_node_pages in fsync. So, this patch marks fsync flag only in the last dnode block. Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 4 +- fs/f2fs/file.c | 14 ++++-- fs/f2fs/node.c | 106 +++++++++++++++++++++++++++++++++++++++++---- fs/f2fs/recovery.c | 9 ++-- 4 files changed, 113 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 90348e21ce05..d8e56381f81f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -191,7 +191,6 @@ struct fsync_inode_entry { struct inode *inode; /* vfs inode pointer */ block_t blkaddr; /* block address locating the last fsync */ block_t last_dentry; /* block address locating the last dentry */ - block_t last_inode; /* block address locating the last inode */ }; #define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats)) @@ -1817,7 +1816,8 @@ void ra_node_page(struct f2fs_sb_info *, nid_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); struct page *get_node_page_ra(struct page *, int); void sync_inode_page(struct dnode_of_data *); -int fsync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *); +int fsync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *, + bool); int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); bool alloc_nid(struct f2fs_sb_info *, nid_t *); void alloc_nid_done(struct f2fs_sb_info *, nid_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 37799e0e5719..e42620b6b702 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -183,7 +183,8 @@ static void try_to_fix_pino(struct inode *inode) } } -int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) +static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, + int datasync, bool atomic) { struct inode *inode = file->f_mapping->host; struct f2fs_inode_info *fi = F2FS_I(inode); @@ -257,7 +258,7 @@ go_write: goto out; } sync_nodes: - ret = fsync_node_pages(sbi, ino, &wbc); + ret = fsync_node_pages(sbi, ino, &wbc, atomic); if (ret) goto out; @@ -291,6 +292,11 @@ out: return ret; } +int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) +{ + return f2fs_do_sync_file(file, start, end, datasync, false); +} + static pgoff_t __get_first_dirty_index(struct address_space *mapping, pgoff_t pgofs, int whence) { @@ -1410,7 +1416,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) } } - ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0); + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); err_out: mnt_drop_write_file(filp); return ret; @@ -1468,7 +1474,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); - ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0); + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); } mnt_drop_write_file(filp); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b54b287e8ccb..b86a60ff58d3 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1231,13 +1231,81 @@ iput_out: iput(inode); } +static struct page *last_fsync_dnode(struct f2fs_sb_info *sbi, nid_t ino) +{ + pgoff_t index, end; + struct pagevec pvec; + struct page *last_page = NULL; + + pagevec_init(&pvec, 0); + index = 0; + end = ULONG_MAX; + + while (index <= end) { + int i, nr_pages; + nr_pages = pagevec_lookup_tag(&pvec, NODE_MAPPING(sbi), &index, + PAGECACHE_TAG_DIRTY, + min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); + if (nr_pages == 0) + break; + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + + if (unlikely(f2fs_cp_error(sbi))) { + f2fs_put_page(last_page, 0); + pagevec_release(&pvec); + return ERR_PTR(-EIO); + } + + if (!IS_DNODE(page) || !is_cold_node(page)) + continue; + if (ino_of_node(page) != ino) + continue; + + lock_page(page); + + if (unlikely(page->mapping != NODE_MAPPING(sbi))) { +continue_unlock: + unlock_page(page); + continue; + } + if (ino_of_node(page) != ino) + goto continue_unlock; + + if (!PageDirty(page)) { + /* someone wrote it for us */ + goto continue_unlock; + } + + if (last_page) + f2fs_put_page(last_page, 0); + + get_page(page); + last_page = page; + unlock_page(page); + } + pagevec_release(&pvec); + cond_resched(); + } + return last_page; +} + int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, - struct writeback_control *wbc) + struct writeback_control *wbc, bool atomic) { pgoff_t index, end; struct pagevec pvec; int ret = 0; + struct page *last_page = NULL; + bool marked = false; + if (atomic) { + last_page = last_fsync_dnode(sbi, ino); + if (IS_ERR_OR_NULL(last_page)) + return PTR_ERR_OR_ZERO(last_page); + } +retry: pagevec_init(&pvec, 0); index = 0; end = ULONG_MAX; @@ -1254,6 +1322,7 @@ int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, struct page *page = pvec.pages[i]; if (unlikely(f2fs_cp_error(sbi))) { + f2fs_put_page(last_page, 0); pagevec_release(&pvec); return -EIO; } @@ -1273,33 +1342,54 @@ continue_unlock: if (ino_of_node(page) != ino) goto continue_unlock; - if (!PageDirty(page)) { + if (!PageDirty(page) && page != last_page) { /* someone wrote it for us */ goto continue_unlock; } f2fs_wait_on_page_writeback(page, NODE, true); BUG_ON(PageWriteback(page)); - if (!clear_page_dirty_for_io(page)) - goto continue_unlock; - set_fsync_mark(page, 1); - if (IS_INODE(page)) - set_dentry_mark(page, + if (!atomic || page == last_page) { + set_fsync_mark(page, 1); + if (IS_INODE(page)) + set_dentry_mark(page, need_dentry_mark(sbi, ino)); + /* may be written by other thread */ + if (!PageDirty(page)) + set_page_dirty(page); + } + + if (!clear_page_dirty_for_io(page)) + goto continue_unlock; ret = NODE_MAPPING(sbi)->a_ops->writepage(page, wbc); if (ret) { unlock_page(page); + f2fs_put_page(last_page, 0); + break; + } + if (page == last_page) { + f2fs_put_page(page, 0); + marked = true; break; } } pagevec_release(&pvec); cond_resched(); - if (ret) + if (ret || marked) break; } + if (!ret && atomic && !marked) { + f2fs_msg(sbi->sb, KERN_DEBUG, + "Retry to write fsync mark: ino=%u, idx=%lx", + ino, last_page->index); + lock_page(last_page); + set_page_dirty(last_page); + unlock_page(last_page); + goto retry; + } return ret ? -EIO: 0; } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index b1136fba8a98..d2080d82c447 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -257,11 +257,8 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) } entry->blkaddr = blkaddr; - if (IS_INODE(page)) { - entry->last_inode = blkaddr; - if (is_dent_dnode(page)) - entry->last_dentry = blkaddr; - } + if (IS_INODE(page) && is_dent_dnode(page)) + entry->last_dentry = blkaddr; next: /* check next segment */ blkaddr = next_blkaddr_of_node(page); @@ -521,7 +518,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head) * In this case, we can lose the latest inode(x). * So, call recover_inode for the inode update. */ - if (entry->last_inode == blkaddr) + if (IS_INODE(page)) recover_inode(entry->inode, page); if (entry->last_dentry == blkaddr) { err = recover_dentry(entry->inode, page); -- GitLab From 39a58e05f7eb52bcc2130ed6340ebb546a7d75a8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 18 Apr 2016 17:07:44 -0400 Subject: [PATCH 0196/5498] f2fs: issue cache flush on direct IO Under direct IO path with O_(D)SYNC, it needs to set proper APPEND or UPDATE flags, so taht f2fs_sync_file can make its data safe. Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f0d1092ceb2c..1f978b157331 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -674,6 +674,9 @@ next_block: err = reserve_new_block(&dn); } else { err = __allocate_data_block(&dn); + if (!err) + set_inode_flag(F2FS_I(inode), + FI_APPEND_WRITE); } if (err) goto sync_out; @@ -1686,8 +1689,12 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, trace_f2fs_direct_IO_enter(inode, offset, count, rw); err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block_dio); - if (err < 0 && (rw & WRITE)) - f2fs_write_failed(mapping, offset + count); + if (rw & WRITE) { + if (err > 0) + set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE); + else if (err < 0) + f2fs_write_failed(mapping, offset + count); + } trace_f2fs_direct_IO_exit(inode, offset, count, rw, err); -- GitLab From 5377a82d921db9f00c0d9f256a3632ae86e70dbe Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 27 Apr 2016 22:22:20 +0800 Subject: [PATCH 0197/5498] f2fs: be aware of invalid filename length The filename length in dirent of may become zero-sized after random junk data injection, once encounter such dirent, find_target_dentry or f2fs_add_inline_entries will run into an infinite loop. So let f2fs being aware of that to avoid deadloop. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 14 +++++--------- fs/f2fs/inline.c | 14 ++++++-------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 134d5cd39ed8..544643620b79 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -101,11 +101,6 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, else kunmap(dentry_page); - /* - * For the most part, it should be a bug when name_len is zero. - * We stop here for figuring out where the bugs has occurred. - */ - f2fs_bug_on(F2FS_P_SB(dentry_page), d.max < 0); return de; } @@ -130,6 +125,11 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, de = &d->dentry[bit_pos]; + if (unlikely(!de->name_len)) { + bit_pos++; + continue; + } + /* encrypted case */ de_name.name = d->filename[bit_pos]; de_name.len = le16_to_cpu(de->name_len); @@ -147,10 +147,6 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, *max_slots = max_len; max_len = 0; - /* remain bug on condition */ - if (unlikely(!de->name_len)) - d->max = -1; - bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index f0c6aca2e147..7469d66cda9c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -303,11 +303,6 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, else f2fs_put_page(ipage, 0); - /* - * For the most part, it should be a bug when name_len is zero. - * We stop here for figuring out where the bugs has occurred. - */ - f2fs_bug_on(sbi, d.max < 0); return de; } @@ -437,6 +432,12 @@ static int f2fs_add_inline_entries(struct inode *dir, } de = &d.dentry[bit_pos]; + + if (unlikely(!de->name_len)) { + bit_pos++; + continue; + } + new_name.name = d.filename[bit_pos]; new_name.len = de->name_len; @@ -448,9 +449,6 @@ static int f2fs_add_inline_entries(struct inode *dir, if (err) goto punch_dentry_pages; - if (unlikely(!de->name_len)) - d.max = -1; - bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); } return 0; -- GitLab From 91dfe774d1823d4c53e2f4987d039226a455d0e3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 27 Apr 2016 21:40:15 +0800 Subject: [PATCH 0198/5498] f2fs: move node pages only in victim section during GC For foreground GC, we cache node blocks in victim section and set them dirty, then we call sync_node_pages to flush these node pages, but meanwhile, those node pages which does not locate in victim section will be flushed together, so more bandwidth and continuous free space would be occupied. So for this condition, it's better to leave those unrelated node page in cache for further write hit, and let CP or VM to flush them afterward. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/gc.c | 25 ++++--------------------- fs/f2fs/node.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d8e56381f81f..9eb0be6d7c1e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1816,6 +1816,7 @@ void ra_node_page(struct f2fs_sb_info *, nid_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); struct page *get_node_page_ra(struct page *, int); void sync_inode_page(struct dnode_of_data *); +void move_node_page(struct page *, int); int fsync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *, bool); int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e82046523186..19e9fafc5c70 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -465,15 +465,7 @@ next_step: continue; } - /* set page dirty and write it */ - if (gc_type == FG_GC) { - f2fs_wait_on_page_writeback(node_page, NODE, true); - set_page_dirty(node_page); - } else { - if (!PageWriteback(node_page)) - set_page_dirty(node_page); - } - f2fs_put_page(node_page, 1); + move_node_page(node_page, gc_type); stat_inc_node_blk_count(sbi, 1, gc_type); } @@ -834,18 +826,9 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, f2fs_put_page(sum_page, 0); } - if (gc_type == FG_GC) { - if (type == SUM_TYPE_NODE) { - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = LONG_MAX, - .for_reclaim = 0, - }; - sync_node_pages(sbi, &wbc); - } else { - f2fs_submit_merged_bio(sbi, DATA, WRITE); - } - } + if (gc_type == FG_GC) + f2fs_submit_merged_bio(sbi, + (type == SUM_TYPE_NODE) ? NODE : DATA, WRITE); blk_finish_plug(&plug); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b86a60ff58d3..134ddd02b62d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1231,6 +1231,37 @@ iput_out: iput(inode); } +void move_node_page(struct page *node_page, int gc_type) +{ + if (gc_type == FG_GC) { + struct f2fs_sb_info *sbi = F2FS_P_SB(node_page); + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 1, + .for_reclaim = 0, + }; + + set_page_dirty(node_page); + f2fs_wait_on_page_writeback(node_page, NODE, true); + + f2fs_bug_on(sbi, PageWriteback(node_page)); + if (!clear_page_dirty_for_io(node_page)) + goto out_page; + + if (NODE_MAPPING(sbi)->a_ops->writepage(node_page, &wbc)) + unlock_page(node_page); + goto release_page; + } else { + /* set page dirty and write it */ + if (!PageWriteback(node_page)) + set_page_dirty(node_page); + } +out_page: + unlock_page(node_page); +release_page: + f2fs_put_page(node_page, 0); +} + static struct page *last_fsync_dnode(struct f2fs_sb_info *sbi, nid_t ino) { pgoff_t index, end; -- GitLab From cf07c3a274e91a08b214e49e1f52f8c3ffec1bb5 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Wed, 27 Apr 2016 20:32:37 +0800 Subject: [PATCH 0199/5498] f2fs: fix to return 0 if err == -ENOENT in f2fs_readdir Commit 57b62d29ad5b384775974973087d47755a8c6fcc ("f2fs: fix to report error in f2fs_readdir") causes f2fs_readdir to return -ENOENT when get_lock_data_page returns -ENOENT. However, the original logic is to continue when get_lock_data_page returns -ENOENT, but it forgets to reset err to 0. This will cause getdents64 incorretly return -ENOENT when lastdirent is NULL in getdents64. This will lead to a wrong return value for syscall caller. Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 544643620b79..df8f10847a4d 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -888,6 +888,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } + err = 0; out: fscrypt_fname_free_buffer(&fstr); return err; -- GitLab From 0fb9a5c77991cc3c3fbe22b25122a4fb3f90883b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Apr 2016 20:09:15 +0800 Subject: [PATCH 0200/5498] f2fs: fix to clear private data in page Private data in page should be removed during ->releasepage or ->invalidatepage, otherwise garbage data would be remained in that page. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1f978b157331..4936c5bcde6c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1724,6 +1724,7 @@ void f2fs_invalidate_page(struct page *page, unsigned int offset, if (IS_ATOMIC_WRITTEN_PAGE(page)) return; + set_page_private(page, 0); ClearPagePrivate(page); } @@ -1737,6 +1738,7 @@ int f2fs_release_page(struct page *page, gfp_t wait) if (IS_ATOMIC_WRITTEN_PAGE(page)) return 0; + set_page_private(page, 0); ClearPagePrivate(page); return 1; } -- GitLab From c45fdb585a6343e073716c8a9637764d9cc859d4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Apr 2016 20:13:36 +0800 Subject: [PATCH 0201/5498] f2fs: fix to clear page private flag Commit 28bc106b2346 ("f2fs: support revoking atomic written pages") forgot to clear page private flag correctly, fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fc0d354f35d1..c6f47e4b5f80 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -227,6 +227,7 @@ next: if (drop || recover) ClearPageUptodate(page); set_page_private(page, 0); + ClearPagePrivate(page); f2fs_put_page(page, 1); list_del(&cur->list); -- GitLab From bdeae28a79a68b590c32b2c97627a22d8bb24ec3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Apr 2016 20:13:37 +0800 Subject: [PATCH 0202/5498] f2fs: factor out fsync inode entry operations Factor out fsync inode entry operations into {add,del}_fsync_inode. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 59 +++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index d2080d82c447..5349113fac7c 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -67,6 +67,28 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, return NULL; } +static struct fsync_inode_entry *add_fsync_inode(struct list_head *head, + struct inode *inode) +{ + struct fsync_inode_entry *entry; + + entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); + if (!entry) + return NULL; + + entry->inode = inode; + list_add_tail(&entry->list, head); + + return entry; +} + +static void del_fsync_inode(struct fsync_inode_entry *entry) +{ + iput(entry->inode); + list_del(&entry->list); + kmem_cache_free(fsync_entry_slab, entry); +} + static int recover_dentry(struct inode *inode, struct page *ipage) { struct f2fs_inode *raw_inode = F2FS_INODE(ipage); @@ -198,6 +220,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) { unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); struct curseg_info *curseg; + struct inode *inode; struct page *page = NULL; block_t blkaddr; int err = 0; @@ -233,27 +256,27 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) break; } - /* add this fsync inode to the list */ - entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); - if (!entry) { - err = -ENOMEM; - break; - } /* * CP | dnode(F) | inode(DF) * For this case, we should not give up now. */ - entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); - if (IS_ERR(entry->inode)) { - err = PTR_ERR(entry->inode); - kmem_cache_free(fsync_entry_slab, entry); + inode = f2fs_iget(sbi->sb, ino_of_node(page)); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); if (err == -ENOENT) { err = 0; goto next; } break; } - list_add_tail(&entry->list, head); + + /* add this fsync inode to the list */ + entry = add_fsync_inode(head, inode); + if (!entry) { + err = -ENOMEM; + iput(inode); + break; + } } entry->blkaddr = blkaddr; @@ -274,11 +297,8 @@ static void destroy_fsync_dnodes(struct list_head *head) { struct fsync_inode_entry *entry, *tmp; - list_for_each_entry_safe(entry, tmp, head, list) { - iput(entry->inode); - list_del(&entry->list); - kmem_cache_free(fsync_entry_slab, entry); - } + list_for_each_entry_safe(entry, tmp, head, list) + del_fsync_inode(entry); } static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, @@ -533,11 +553,8 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head) break; } - if (entry->blkaddr == blkaddr) { - iput(entry->inode); - list_del(&entry->list); - kmem_cache_free(fsync_entry_slab, entry); - } + if (entry->blkaddr == blkaddr) + del_fsync_inode(entry); next: /* check next segment */ blkaddr = next_blkaddr_of_node(page); -- GitLab From c57691201b1f0e30c389e56f5fc177246dc8f9fa Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 27 Apr 2016 16:07:56 -0700 Subject: [PATCH 0203/5498] f2fs: introduce macros for proc entries This adds macros to be used multiple proc entries. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 520cfce90b45..9c7ae25326e6 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -745,19 +745,22 @@ static int segment_info_seq_show(struct seq_file *seq, void *offset) return 0; } -static int segment_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, segment_info_seq_show, PDE_DATA(inode)); -} - -static const struct file_operations f2fs_seq_segment_info_fops = { - .owner = THIS_MODULE, - .open = segment_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, +#define F2FS_PROC_FILE_DEF(_name) \ +static int _name##_open_fs(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, _name##_seq_show, PDE_DATA(inode)); \ +} \ + \ +static const struct file_operations f2fs_seq_##_name##_fops = { \ + .owner = THIS_MODULE, \ + .open = _name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ }; +F2FS_PROC_FILE_DEF(segment_info); + static void default_options(struct f2fs_sb_info *sbi) { /* init some FS parameters */ -- GitLab From a6c49ad4b521367cbba1b92cedf203ffc7879919 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 27 Apr 2016 16:29:05 -0700 Subject: [PATCH 0204/5498] f2fs: add proc entry to show valid block bitmap This patch adds a new proc entry to show segment information in more detail. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9c7ae25326e6..324e562033b9 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -539,6 +539,7 @@ static void f2fs_put_super(struct super_block *sb) if (sbi->s_proc) { remove_proc_entry("segment_info", sbi->s_proc); + remove_proc_entry("segment_bits", sbi->s_proc); remove_proc_entry(sb->s_id, f2fs_proc_root); } kobject_del(&sbi->s_kobj); @@ -745,6 +746,30 @@ static int segment_info_seq_show(struct seq_file *seq, void *offset) return 0; } +static int segment_bits_seq_show(struct seq_file *seq, void *offset) +{ + struct super_block *sb = seq->private; + struct f2fs_sb_info *sbi = F2FS_SB(sb); + unsigned int total_segs = + le32_to_cpu(sbi->raw_super->segment_count_main); + int i, j; + + seq_puts(seq, "format: segment_type|valid_blocks|bitmaps\n" + "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); + + for (i = 0; i < total_segs; i++) { + struct seg_entry *se = get_seg_entry(sbi, i); + + seq_printf(seq, "%-10d", i); + seq_printf(seq, "%d|%-3u|", se->type, + get_valid_blocks(sbi, i, 1)); + for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) + seq_printf(seq, "%x ", se->cur_valid_map[j]); + seq_putc(seq, '\n'); + } + return 0; +} + #define F2FS_PROC_FILE_DEF(_name) \ static int _name##_open_fs(struct inode *inode, struct file *file) \ { \ @@ -760,6 +785,7 @@ static const struct file_operations f2fs_seq_##_name##_fops = { \ }; F2FS_PROC_FILE_DEF(segment_info); +F2FS_PROC_FILE_DEF(segment_bits); static void default_options(struct f2fs_sb_info *sbi) { @@ -1541,9 +1567,12 @@ try_onemore: if (f2fs_proc_root) sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); - if (sbi->s_proc) + if (sbi->s_proc) { proc_create_data("segment_info", S_IRUGO, sbi->s_proc, &f2fs_seq_segment_info_fops, sb); + proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, + &f2fs_seq_segment_bits_fops, sb); + } sbi->s_kobj.kset = f2fs_kset; init_completion(&sbi->s_kobj_unregister); @@ -1619,6 +1648,7 @@ free_kobj: free_proc: if (sbi->s_proc) { remove_proc_entry("segment_info", sbi->s_proc); + remove_proc_entry("segment_bits", sbi->s_proc); remove_proc_entry(sb->s_id, f2fs_proc_root); } f2fs_destroy_stats(sbi); -- GitLab From af7bc36fddc001784eeb76167ada58f92a933ffb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Apr 2016 15:16:42 -0700 Subject: [PATCH 0205/5498] f2fs: introduce f2fs_kmalloc to wrap kmalloc This patch adds f2fs_kmalloc. Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 4 ++-- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 5 +++++ fs/f2fs/gc.c | 2 +- fs/f2fs/inline.c | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index c8f25f7241f0..d757d79a4d71 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -115,7 +115,7 @@ static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) struct f2fs_acl_entry *entry; int i; - f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * + f2fs_acl = f2fs_kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * sizeof(struct f2fs_acl_entry), GFP_NOFS); if (!f2fs_acl) return ERR_PTR(-ENOMEM); @@ -175,7 +175,7 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type, retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage); if (retval > 0) { - value = kmalloc(retval, GFP_F2FS_ZERO); + value = f2fs_kmalloc(retval, GFP_F2FS_ZERO); if (!value) return ERR_PTR(-ENOMEM); retval = f2fs_getxattr(inode, name_index, "", value, diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index df8f10847a4d..100facc9ced3 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -805,7 +805,7 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, int save_len = fstr->len; int ret; - de_name.name = kmalloc(de_name.len, GFP_NOFS); + de_name.name = f2fs_kmalloc(de_name.len, GFP_NOFS); if (!de_name.name) return false; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9eb0be6d7c1e..82269c11da3b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1677,6 +1677,11 @@ static inline bool f2fs_may_extent_tree(struct inode *inode) return S_ISREG(mode); } +static inline void *f2fs_kmalloc(size_t size, gfp_t flags) +{ + return kmalloc(size, flags); +} + static inline void *f2fs_kvmalloc(size_t size, gfp_t flags) { void *ret; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 19e9fafc5c70..38d56f678912 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -96,7 +96,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) dev_t dev = sbi->sb->s_bdev->bd_dev; int err = 0; - gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); + gc_th = f2fs_kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); if (!gc_th) { err = -ENOMEM; goto out; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 7469d66cda9c..0bb4f9c80fcc 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -465,7 +465,7 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *backup_dentry; int err; - backup_dentry = kmalloc(sizeof(struct f2fs_inline_dentry), + backup_dentry = f2fs_kmalloc(sizeof(struct f2fs_inline_dentry), GFP_F2FS_ZERO); if (!backup_dentry) return -ENOMEM; -- GitLab From 6dc85b4e70062b543c56f717144604d0a3ea98ab Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Apr 2016 16:11:53 -0700 Subject: [PATCH 0206/5498] f2fs: use f2fs_grab_cache_page instead of grab_cache_page This patch converts grab_cache_page to f2fs_grab_cache_page. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 ++++--- fs/f2fs/inline.c | 4 ++-- fs/f2fs/node.c | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 3077245d9bd2..5b5678854ec0 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -34,7 +34,7 @@ struct page *grab_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) struct address_space *mapping = META_MAPPING(sbi); struct page *page = NULL; repeat: - page = grab_cache_page(mapping, index); + page = f2fs_grab_cache_page(mapping, index, false); if (!page) { cond_resched(); goto repeat; @@ -64,7 +64,7 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index, if (unlikely(!is_meta)) fio.rw &= ~REQ_META; repeat: - page = grab_cache_page(mapping, index); + page = f2fs_grab_cache_page(mapping, index, false); if (!page) { cond_resched(); goto repeat; @@ -187,7 +187,8 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, BUG(); } - page = grab_cache_page(META_MAPPING(sbi), fio.new_blkaddr); + page = f2fs_grab_cache_page(META_MAPPING(sbi), + fio.new_blkaddr, false); if (!page) continue; if (PageUptodate(page)) { diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 0bb4f9c80fcc..80cdacf2a27c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -161,7 +161,7 @@ int f2fs_convert_inline_inode(struct inode *inode) if (!f2fs_has_inline_data(inode)) return 0; - page = grab_cache_page(inode->i_mapping, 0); + page = f2fs_grab_cache_page(inode->i_mapping, 0, false); if (!page) return -ENOMEM; @@ -358,7 +358,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, struct f2fs_dentry_block *dentry_blk; int err; - page = grab_cache_page(dir->i_mapping, 0); + page = f2fs_grab_cache_page(dir->i_mapping, 0, false); if (!page) { f2fs_put_page(ipage, 1); return -ENOMEM; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 134ddd02b62d..d3e2cf98a18c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -995,7 +995,7 @@ struct page *new_node_page(struct dnode_of_data *dn, if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) return ERR_PTR(-EPERM); - page = grab_cache_page(NODE_MAPPING(sbi), dn->nid); + page = f2fs_grab_cache_page(NODE_MAPPING(sbi), dn->nid, false); if (!page) return ERR_PTR(-ENOMEM); @@ -1087,7 +1087,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) if (apage) return; - apage = grab_cache_page(NODE_MAPPING(sbi), nid); + apage = f2fs_grab_cache_page(NODE_MAPPING(sbi), nid, false); if (!apage) return; @@ -1127,7 +1127,7 @@ static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid, return ERR_PTR(-ENOENT); f2fs_bug_on(sbi, check_nid_range(sbi, nid)); repeat: - page = grab_cache_page(NODE_MAPPING(sbi), nid); + page = f2fs_grab_cache_page(NODE_MAPPING(sbi), nid, false); if (!page) return ERR_PTR(-ENOMEM); @@ -2021,7 +2021,7 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) if (unlikely(old_ni.blk_addr != NULL_ADDR)) return -EINVAL; - ipage = grab_cache_page(NODE_MAPPING(sbi), ino); + ipage = f2fs_grab_cache_page(NODE_MAPPING(sbi), ino, false); if (!ipage) return -ENOMEM; -- GitLab From 314e1b205d4a9959892464f4d684e83e23bf81a9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Apr 2016 15:34:32 -0700 Subject: [PATCH 0207/5498] f2fs: add mount option to select fault injection ratio This patch adds a mount option to select fault ratio. Signed-off-by: Jaegeuk Kim --- fs/f2fs/Kconfig | 8 ++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig index 1f8982a957f1..378c221d68a9 100644 --- a/fs/f2fs/Kconfig +++ b/fs/f2fs/Kconfig @@ -94,3 +94,11 @@ config F2FS_IO_TRACE information and block IO patterns in the filesystem level. If unsure, say N. + +config F2FS_FAULT_INJECTION + bool "F2FS fault injection facility" + depends on F2FS_FS + help + Test F2FS to inject faults such as ENOMEM, ENOSPC, and so on. + + If unsure, say N. diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 82269c11da3b..b988d0c33527 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -57,6 +57,7 @@ #define F2FS_MOUNT_EXTENT_CACHE 0x00002000 #define F2FS_MOUNT_FORCE_FG_GC 0x00004000 #define F2FS_MOUNT_DATA_FLUSH 0x00008000 +#define F2FS_MOUNT_FAULT_INJECTION 0x00010000 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 324e562033b9..cb4fa9f81d6d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -39,6 +39,10 @@ static struct proc_dir_entry *f2fs_proc_root; static struct kmem_cache *f2fs_inode_cachep; static struct kset *f2fs_kset; +#ifdef CONFIG_F2FS_FAULT_INJECTION +u32 f2fs_fault_rate = 0; +#endif + /* f2fs-wide shrinker description */ static struct shrinker f2fs_shrinker_info = { .scan_objects = f2fs_shrink_scan, @@ -68,6 +72,7 @@ enum { Opt_noextent_cache, Opt_noinline_data, Opt_data_flush, + Opt_fault_injection, Opt_err, }; @@ -93,6 +98,7 @@ static match_table_t f2fs_tokens = { {Opt_noextent_cache, "noextent_cache"}, {Opt_noinline_data, "noinline_data"}, {Opt_data_flush, "data_flush"}, + {Opt_fault_injection, "fault_injection=%u"}, {Opt_err, NULL}, }; @@ -300,6 +306,9 @@ static int parse_options(struct super_block *sb, char *options) char *p, *name; int arg = 0; +#ifdef CONFIG_F2FS_FAULT_INJECTION + f2fs_fault_rate = 0; +#endif if (!options) return 0; @@ -433,6 +442,16 @@ static int parse_options(struct super_block *sb, char *options) case Opt_data_flush: set_opt(sbi, DATA_FLUSH); break; + case Opt_fault_injection: + if (args->from && match_int(args, &arg)) + return -EINVAL; +#ifdef CONFIG_F2FS_FAULT_INJECTION + f2fs_fault_rate = arg; +#else + f2fs_msg(sb, KERN_INFO, + "FAULT_INJECTION was not selected"); +#endif + break; default: f2fs_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" or missing value", -- GitLab From ba2df0792d3d7aeb918adc820072af8b9ee9bc8a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Apr 2016 15:49:56 -0700 Subject: [PATCH 0208/5498] f2fs: inject kmalloc failure This patch injects kmalloc failure given a fault injection rate. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 29 +++++++++++++++++++++++++++++ fs/f2fs/super.c | 6 ++++++ 2 files changed, 35 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b988d0c33527..f8a43a5c6da6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -38,6 +38,31 @@ } while (0) #endif +#ifdef CONFIG_F2FS_FAULT_INJECTION +enum { + FAULT_KMALLOC, + FAULT_MAX, +}; + +extern u32 f2fs_fault_rate; +extern atomic_t f2fs_ops; +extern char *fault_name[FAULT_MAX]; + +static inline bool time_to_inject(int type) +{ + atomic_inc(&f2fs_ops); + if (f2fs_fault_rate && (atomic_read(&f2fs_ops) >= f2fs_fault_rate)) { + atomic_set(&f2fs_ops, 0); + printk("%sF2FS-fs : inject %s in %pF\n", + KERN_INFO, + fault_name[type], + __builtin_return_address(0)); + return true; + } + return false; +} +#endif + /* * For mount options */ @@ -1680,6 +1705,10 @@ static inline bool f2fs_may_extent_tree(struct inode *inode) static inline void *f2fs_kmalloc(size_t size, gfp_t flags) { +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(FAULT_KMALLOC)) + return NULL; +#endif return kmalloc(size, flags); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index cb4fa9f81d6d..6da05b7a20bb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -41,6 +41,11 @@ static struct kset *f2fs_kset; #ifdef CONFIG_F2FS_FAULT_INJECTION u32 f2fs_fault_rate = 0; +atomic_t f2fs_ops; + +char *fault_name[FAULT_MAX] = { + [FAULT_KMALLOC] = "kmalloc", +}; #endif /* f2fs-wide shrinker description */ @@ -447,6 +452,7 @@ static int parse_options(struct super_block *sb, char *options) return -EINVAL; #ifdef CONFIG_F2FS_FAULT_INJECTION f2fs_fault_rate = arg; + atomic_set(&f2fs_ops, 0); #else f2fs_msg(sb, KERN_INFO, "FAULT_INJECTION was not selected"); -- GitLab From b106c372485f651a399c17b4b45f8e612ba3efea Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Apr 2016 16:17:09 -0700 Subject: [PATCH 0209/5498] f2fs: inject page allocation failures This patch adds page allocation failures. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 9 +++++++++ fs/f2fs/super.c | 1 + 2 files changed, 10 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f8a43a5c6da6..acdbd4673a5f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -41,6 +41,7 @@ #ifdef CONFIG_F2FS_FAULT_INJECTION enum { FAULT_KMALLOC, + FAULT_PAGE_ALLOC, FAULT_MAX, }; @@ -1327,6 +1328,14 @@ static inline unsigned int valid_inode_count(struct f2fs_sb_info *sbi) static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, pgoff_t index, bool for_write) { +#ifdef CONFIG_F2FS_FAULT_INJECTION + struct page *page = find_lock_page(mapping, index); + if (page) + return page; + + if (time_to_inject(FAULT_PAGE_ALLOC)) + return NULL; +#endif if (!for_write) return grab_cache_page(mapping, index); return grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 6da05b7a20bb..a902e9f596fe 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -45,6 +45,7 @@ atomic_t f2fs_ops; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", + [FAULT_PAGE_ALLOC] = "page alloc", }; #endif -- GitLab From 8d2360e936b1439bacd76aa0158d50d0e95c8adf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 29 Apr 2016 16:29:22 -0700 Subject: [PATCH 0210/5498] f2fs: inject ENOSPC failures This patch injects ENOSPC failures. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 +++++++ fs/f2fs/dir.c | 4 ++++ fs/f2fs/f2fs.h | 10 ++++++++++ fs/f2fs/node.c | 4 ++++ fs/f2fs/super.c | 4 ++++ 5 files changed, 29 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 5b5678854ec0..cf97f6f51c18 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -475,6 +475,13 @@ int acquire_orphan_inode(struct f2fs_sb_info *sbi) int err = 0; spin_lock(&im->ino_lock); + +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(FAULT_ORPHAN)) { + spin_unlock(&im->ino_lock); + return -ENOSPC; + } +#endif if (unlikely(im->ino_num >= sbi->max_orphans)) err = -ENOSPC; else diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 100facc9ced3..82e54eda64e1 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -537,6 +537,10 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, } start: +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(FAULT_DIR_DEPTH)) + return -ENOSPC; +#endif if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) return -ENOSPC; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index acdbd4673a5f..815b88e2e599 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -42,6 +42,10 @@ enum { FAULT_KMALLOC, FAULT_PAGE_ALLOC, + FAULT_ALLOC_NID, + FAULT_ORPHAN, + FAULT_BLOCK, + FAULT_DIR_DEPTH, FAULT_MAX, }; @@ -1118,6 +1122,12 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, block_t valid_block_count; spin_lock(&sbi->stat_lock); +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(FAULT_BLOCK)) { + spin_unlock(&sbi->stat_lock); + return false; + } +#endif valid_block_count = sbi->total_valid_block_count + (block_t)count; if (unlikely(valid_block_count > sbi->user_block_count)) { diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d3e2cf98a18c..20abb05e32a5 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1847,6 +1847,10 @@ bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid) struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i = NULL; retry: +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(FAULT_ALLOC_NID)) + return false; +#endif if (unlikely(sbi->total_valid_node_count + 1 > nm_i->available_nids)) return false; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a902e9f596fe..b3fcae3166c7 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -46,6 +46,10 @@ atomic_t f2fs_ops; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", [FAULT_PAGE_ALLOC] = "page alloc", + [FAULT_ALLOC_NID] = "alloc nid", + [FAULT_ORPHAN] = "orphan", + [FAULT_BLOCK] = "no more block", + [FAULT_DIR_DEPTH] = "too big dir depth", }; #endif -- GitLab From 32ea66cb286b16a73ac379733fec79599ad31783 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 2 May 2016 12:34:48 -0700 Subject: [PATCH 0211/5498] f2fs: revisit error handling flows This patch fixes a couple of bugs regarding to orphan inodes when handling errors. This tries to - call alloc_nid_done with add_orphan_inode in handle_failed_inode - let truncate blocks in f2fs_evict_inode - not make a bad inode due to i_mode change Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 20 ++++++++++++-------- fs/f2fs/inode.c | 40 ++++++++++++++++------------------------ 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 82e54eda64e1..587ee282e66e 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -391,9 +391,14 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, return page; if (S_ISDIR(inode->i_mode)) { + /* in order to handle error case */ + get_page(page); err = make_empty_dir(inode, dir, page); - if (err) - goto error; + if (err) { + lock_page(page); + goto put_error; + } + put_page(page); } err = f2fs_init_acl(inode, dir, page, dpage); @@ -437,13 +442,12 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, return page; put_error: - f2fs_put_page(page, 1); -error: - /* once the failed inode becomes a bad inode, i_mode is S_IFREG */ + /* truncate empty dir pages */ truncate_inode_pages(&inode->i_data, 0); - truncate_blocks(inode, 0, false); - remove_dirty_inode(inode); - remove_inode_page(inode); + + clear_nlink(inode); + update_inode(inode, page); + f2fs_put_page(page, 1); return ERR_PTR(err); } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 1395f2b71b43..776d65dfb992 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -367,10 +367,7 @@ no_delete: if (is_inode_flag_set(fi, FI_UPDATE_WRITE)) add_ino_entry(sbi, inode->i_ino, UPDATE_INO); if (is_inode_flag_set(fi, FI_FREE_NID)) { - if (err && err != -ENOENT) - alloc_nid_done(sbi, inode->i_ino); - else - alloc_nid_failed(sbi, inode->i_ino); + alloc_nid_failed(sbi, inode->i_ino); clear_inode_flag(fi, FI_FREE_NID); } @@ -396,37 +393,32 @@ out_clear: void handle_failed_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - int err = 0; + struct node_info ni; - clear_nlink(inode); - make_bad_inode(inode); + /* don't make bad inode, since it becomes a regular file. */ unlock_new_inode(inode); - i_size_write(inode, 0); - if (F2FS_HAS_BLOCKS(inode)) - err = f2fs_truncate(inode, false); - - if (!err) - err = remove_inode_page(inode); - /* - * if we skip truncate_node in remove_inode_page bacause we failed - * before, it's better to find another way to release resource of - * this inode (e.g. valid block count, node block or nid). Here we - * choose to add this inode to orphan list, so that we can call iput - * for releasing in orphan recovery flow. - * * Note: we should add inode to orphan list before f2fs_unlock_op() * so we can prevent losing this orphan when encoutering checkpoint * and following suddenly power-off. */ - if (err && err != -ENOENT) { - err = acquire_orphan_inode(sbi); - if (!err) + get_node_info(sbi, inode->i_ino, &ni); + + if (ni.blk_addr != NULL_ADDR) { + int err = acquire_orphan_inode(sbi); + if (err) { + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_msg(sbi->sb, KERN_WARNING, + "Too many orphan inodes, run fsck to fix."); + } else { add_orphan_inode(sbi, inode->i_ino); + } + alloc_nid_done(sbi, inode->i_ino); + } else { + set_inode_flag(F2FS_I(inode), FI_FREE_NID); } - set_inode_flag(F2FS_I(inode), FI_FREE_NID); f2fs_unlock_op(sbi); /* iput will drop the inode object */ -- GitLab From 05faa6fefdb7eef475b0bb5d75653fe1885a3cff Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 2 May 2016 22:09:56 -0700 Subject: [PATCH 0212/5498] f2fs: fix leak of orphan inode objects When unmounting filesystem, we should release all the ino entries. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 6 +++--- fs/f2fs/f2fs.h | 2 +- fs/f2fs/super.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index cf97f6f51c18..f9a7d7f9a7e2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -450,12 +450,12 @@ bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode) return e ? true : false; } -void release_ino_entry(struct f2fs_sb_info *sbi) +void release_ino_entry(struct f2fs_sb_info *sbi, bool all) { struct ino_entry *e, *tmp; int i; - for (i = APPEND_INO; i <= UPDATE_INO; i++) { + for (i = all ? ORPHAN_INO: APPEND_INO; i <= UPDATE_INO; i++) { struct inode_management *im = &sbi->im[i]; spin_lock(&im->ino_lock); @@ -1107,7 +1107,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) invalidate_mapping_pages(META_MAPPING(sbi), discard_blk, discard_blk); - release_ino_entry(sbi); + release_ino_entry(sbi, false); if (unlikely(f2fs_cp_error(sbi))) return -EIO; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 815b88e2e599..42f049f7953e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1944,7 +1944,7 @@ void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t); long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); void add_ino_entry(struct f2fs_sb_info *, nid_t, int type); void remove_ino_entry(struct f2fs_sb_info *, nid_t, int type); -void release_ino_entry(struct f2fs_sb_info *); +void release_ino_entry(struct f2fs_sb_info *, bool); bool exist_written_data(struct f2fs_sb_info *, nid_t, int); int acquire_orphan_inode(struct f2fs_sb_info *); void release_orphan_inode(struct f2fs_sb_info *); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b3fcae3166c7..84d10358b595 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -599,7 +599,7 @@ static void f2fs_put_super(struct super_block *sb) * normally superblock is clean, so we need to release this. * In addition, EIO will skip do checkpoint, we need this as well. */ - release_ino_entry(sbi); + release_ino_entry(sbi, true); release_discard_addrs(sbi); f2fs_leave_shrinker(sbi); -- GitLab From 41c1e27d719b67c0ada516086083621bad301ab4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 3 May 2016 09:22:18 -0700 Subject: [PATCH 0213/5498] f2fs: retry to truncate blocks in -ENOMEM case This patch modifies to retry truncating node blocks in -ENOMEM case. Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 776d65dfb992..c4e7f81ff759 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -343,7 +343,7 @@ void f2fs_evict_inode(struct inode *inode) sb_start_intwrite(inode->i_sb); set_inode_flag(fi, FI_NO_ALLOC); i_size_write(inode, 0); - +retry: if (F2FS_HAS_BLOCKS(inode)) err = f2fs_truncate(inode, true); @@ -353,6 +353,12 @@ void f2fs_evict_inode(struct inode *inode) f2fs_unlock_op(sbi); } + /* give more chances, if ENOMEM case */ + if (err == -ENOMEM) { + err = 0; + goto retry; + } + sb_end_intwrite(inode->i_sb); no_delete: stat_dec_inline_xattr(inode); -- GitLab From cd62676008e910cdb40d0e603fd31c4a3ce73f79 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 May 2016 23:17:00 +0800 Subject: [PATCH 0214/5498] f2fs: remove unneeded readahead in find_fsync_dnodes In find_fsync_dnodes, get_tmp_page will read dnode page synchronously, previously, ra_meta_page did the same work, which is redundant, remove it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 5349113fac7c..d0b939d3e2a3 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -229,8 +229,6 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); - ra_meta_pages(sbi, blkaddr, 1, META_POR, true); - while (1) { struct fsync_inode_entry *entry; -- GitLab From 41a82e5342ec17dac7486653c101e512114c620f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 May 2016 23:19:46 +0800 Subject: [PATCH 0215/5498] f2fs: remove unneeded memset when updating xattr Each of fields in struct f2fs_xattr_entry will be assigned later, so previously we don't need to memset the struct. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index a3a5ae4252f7..e0a2d01ef51b 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -568,7 +568,6 @@ static int __f2fs_setxattr(struct inode *inode, int index, * Before we come here, old entry is removed. * We just write new entry. */ - memset(last, 0, newsize); last->e_name_index = index; last->e_name_len = len; memcpy(last->e_name, name, len); -- GitLab From 0cd067fb0fb24a4b803cb8787789d5c21dd1c66a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 May 2016 23:19:47 +0800 Subject: [PATCH 0216/5498] f2fs: reuse get_extent_info Reuse get_extent_info for readability. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 3 +-- fs/f2fs/f2fs.h | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index c859bb044728..5bfcdb9b69f2 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -196,8 +196,7 @@ bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) if (!i_ext || !i_ext->len) return false; - set_extent_info(&ei, le32_to_cpu(i_ext->fofs), - le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); + get_extent_info(&ei, i_ext); write_lock(&et->lock); if (atomic_read(&et->node_cnt)) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 42f049f7953e..e400f729678f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -459,11 +459,11 @@ struct f2fs_inode_info { }; static inline void get_extent_info(struct extent_info *ext, - struct f2fs_extent i_ext) + struct f2fs_extent *i_ext) { - ext->fofs = le32_to_cpu(i_ext.fofs); - ext->blk = le32_to_cpu(i_ext.blk); - ext->len = le32_to_cpu(i_ext.len); + ext->fofs = le32_to_cpu(i_ext->fofs); + ext->blk = le32_to_cpu(i_ext->blk); + ext->len = le32_to_cpu(i_ext->len); } static inline void set_raw_extent(struct extent_info *ext, -- GitLab From 7ff270fc2a47f94cb50f2ea949eb36453300dec7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 May 2016 23:19:48 +0800 Subject: [PATCH 0217/5498] f2fs: shrink size of struct seg_entry Restructure struct seg_entry to eliminate holes in it, after that, in 32-bits machine, it reduces size from 32 bytes to 24 bytes; in 64-bits machine, it reduces size from 56 bytes to 40 bytes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 6f13f72a670c..dde1f1ef8a22 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -157,16 +157,17 @@ struct victim_sel_policy { }; struct seg_entry { - unsigned short valid_blocks; /* # of valid blocks */ + unsigned int type:6; /* segment type like CURSEG_XXX_TYPE */ + unsigned int valid_blocks:10; /* # of valid blocks */ + unsigned int ckpt_valid_blocks:10; /* # of valid blocks last cp */ + unsigned int padding:6; /* padding */ unsigned char *cur_valid_map; /* validity bitmap of blocks */ /* * # of valid blocks and the validity bitmap stored in the the last * checkpoint pack. This information is used by the SSR mode. */ - unsigned short ckpt_valid_blocks; - unsigned char *ckpt_valid_map; + unsigned char *ckpt_valid_map; /* validity bitmap of blocks last cp */ unsigned char *discard_map; - unsigned char type; /* segment type like CURSEG_XXX_TYPE */ unsigned long long mtime; /* modification time of the segment */ }; -- GitLab From ad5e8071c0ed142d665e405b79b7040fadf8c045 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 4 May 2016 19:48:53 -0700 Subject: [PATCH 0218/5498] f2fs: don't worry about inode leak in evict_inode Even if an inode failed to release its blocks, it should be kept in an orphan inode list, so it will be released later. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c4e7f81ff759..d2ddc0adbf2d 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -376,20 +376,8 @@ no_delete: alloc_nid_failed(sbi, inode->i_ino); clear_inode_flag(fi, FI_FREE_NID); } - - if (err && err != -ENOENT) { - if (!exist_written_data(sbi, inode->i_ino, ORPHAN_INO)) { - /* - * get here because we failed to release resource - * of inode previously, reminder our user to run fsck - * for fixing. - */ - set_sbi_flag(sbi, SBI_NEED_FSCK); - f2fs_msg(sbi->sb, KERN_WARNING, - "inode (ino:%lu) resource leak, run fsck " - "to fix this issue!", inode->i_ino); - } - } + f2fs_bug_on(sbi, err && + !exist_written_data(sbi, inode->i_ino, ORPHAN_INO)); out_clear: fscrypt_put_encryption_info(inode, NULL); clear_inode(inode); -- GitLab From f32ef8910c16364a8e84b180db6c06d840d5fd1d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 4 May 2016 09:58:10 -0700 Subject: [PATCH 0219/5498] f2fs: remove an obsolete variable This patch removes an obsolete variable used in add_free_nid. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 20abb05e32a5..4ef4afd55e0a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1699,7 +1699,6 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i; struct nat_entry *ne; - bool allocated = false; if (!available_free_memory(sbi, FREE_NIDS)) return -1; @@ -1713,8 +1712,6 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) ne = __lookup_nat_cache(nm_i, nid); if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || nat_get_blkaddr(ne) != NULL_ADDR)) - allocated = true; - if (allocated) return 0; } -- GitLab From fa2ff35b3a66bfe7da3e520ac9467a6dabd7ae76 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 5 May 2016 19:13:02 +0800 Subject: [PATCH 0220/5498] f2fs: fix incorrect mapping in ->bmap Currently, generic_block_bmap is used in f2fs_bmap, its semantics is when the mapping is been found, return position of target physical block, otherwise return zero. But, previously, when there is no mapping info for specified logical block, f2fs_bmap will map target physical block to a uninitialized variable, which should be wrong. Fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4936c5bcde6c..311441f994ba 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -649,6 +649,8 @@ next_dnode: set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, pgofs, mode); if (err) { + if (flag == F2FS_GET_BLOCK_BMAP) + map->m_pblk = 0; if (err == -ENOENT) { err = 0; if (map->m_next_pgofs) @@ -684,17 +686,18 @@ next_block: map->m_flags = F2FS_MAP_NEW; blkaddr = dn.data_blkaddr; } else { + if (flag == F2FS_GET_BLOCK_BMAP) { + map->m_pblk = 0; + goto sync_out; + } if (flag == F2FS_GET_BLOCK_FIEMAP && blkaddr == NULL_ADDR) { if (map->m_next_pgofs) *map->m_next_pgofs = pgofs + 1; } if (flag != F2FS_GET_BLOCK_FIEMAP || - blkaddr != NEW_ADDR) { - if (flag == F2FS_GET_BLOCK_BMAP) - err = -ENOENT; + blkaddr != NEW_ADDR) goto sync_out; - } } } -- GitLab From 1fa16870dbde241c052a101047eedc0dc4cc0489 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 5 May 2016 19:13:03 +0800 Subject: [PATCH 0221/5498] f2fs: avoid panic when truncating to max filesize The following panic occurs when truncating inode which has inline xattr to max filesize. [] get_dnode_of_data+0x4e/0x580 [f2fs] [] ? read_node_page+0x51/0x90 [f2fs] [] ? get_node_page.part.34+0xb9/0x170 [f2fs] [] truncate_blocks+0x131/0x3f0 [f2fs] [] f2fs_truncate+0x73/0x100 [f2fs] [] f2fs_setattr+0x62/0x2a0 [f2fs] [] notify_change+0x158/0x300 [] do_truncate+0x6b/0xa0 [] ? __sb_start_write+0x49/0x100 [] do_sys_ftruncate.constprop.12+0x118/0x170 [] SyS_ftruncate+0xe/0x10 [] tracesys+0xe1/0xe6 [] get_node_path+0x210/0x220 [f2fs] --[ end trace 5fea664dfbcc6625 ]--- The reason is truncate_blocks tries to truncate all node and data blocks start from specified block offset with value of (max filesize / block size), but actually, our valid max block offset is (max filesize / block size) - 1, so f2fs detects such invalid block offset with BUG_ON in truncation path. This patch lets f2fs skip truncating data which is exceeding max filesize. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e42620b6b702..e1a6288c9118 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -564,6 +564,9 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) free_from = (pgoff_t)F2FS_BYTES_TO_BLK(from + blocksize - 1); + if (free_from >= sbi->max_file_blocks) + goto free_partial; + if (lock) f2fs_lock_op(sbi); @@ -605,7 +608,7 @@ free_next: out: if (lock) f2fs_unlock_op(sbi); - +free_partial: /* lastly zero out the first data page */ if (!err) err = truncate_partial_data_page(inode, from, truncate_page); -- GitLab From 12b7d171fbf2d90305966c206f73a711c3b4efb0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 4 May 2016 22:05:01 -0700 Subject: [PATCH 0222/5498] fscrypto/f2fs: allow fs-specific key prefix for fs encryption This patch allows fscrypto to handle a second key prefix given by filesystem. The main reason is to provide backward compatibility, since previously f2fs used "f2fs:" as a crypto prefix instead of "fscrypt:". Later, ext4 should also provide key_prefix() to give "ext4:". One concern decribed by Ted would be kinda double check overhead of prefixes. In x86, for example, validate_user_key consumes 8 ms after boot-up, which turns out derive_key_aes() consumed most of the time to load specific crypto module. After such the cold miss, it shows almost zero latencies, which treats as a negligible overhead. Note that request_key() detects wrong prefix in prior to derive_key_aes() even. Cc: Ted Tso Cc: stable@vger.kernel.org # v4.6 Signed-off-by: Jaegeuk Kim Conflicts: fs/crypto/keyinfo.c --- fs/crypto/keyinfo.c | 120 +++++++++++++++++++++++++-------------- fs/f2fs/f2fs.h | 8 +++ fs/f2fs/super.c | 13 +++++ include/linux/fscrypto.h | 1 + 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index aea1925f4f7a..7be4f4312341 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -82,6 +82,67 @@ out: return res; } +static int validate_user_key(struct fscrypt_info *crypt_info, + struct fscrypt_context *ctx, u8 *raw_key, + u8 *prefix, int prefix_size) +{ + u8 *full_key_descriptor; + struct key *keyring_key; + struct fscrypt_key *master_key; + const struct user_key_payload *ukp; + int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1; + int res; + + full_key_descriptor = kmalloc(full_key_len, GFP_NOFS); + if (!full_key_descriptor) + return -ENOMEM; + + memcpy(full_key_descriptor, prefix, prefix_size); + sprintf(full_key_descriptor + prefix_size, + "%*phN", FS_KEY_DESCRIPTOR_SIZE, + ctx->master_key_descriptor); + full_key_descriptor[full_key_len - 1] = '\0'; + keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); + kfree(full_key_descriptor); + if (IS_ERR(keyring_key)) + return PTR_ERR(keyring_key); + + if (keyring_key->type != &key_type_logon) { + printk_once(KERN_WARNING + "%s: key type must be logon\n", __func__); + res = -ENOKEY; + goto out; + } + down_read(&keyring_key->sem); + ukp = ((struct user_key_payload *)keyring_key->payload.data); + if (ukp->datalen != sizeof(struct fscrypt_key)) { + res = -EINVAL; + up_read(&keyring_key->sem); + goto out; + } + master_key = (struct fscrypt_key *)ukp->data; + BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE); + + if (master_key->size != FS_AES_256_XTS_KEY_SIZE) { + printk_once(KERN_WARNING + "%s: key size incorrect: %d\n", + __func__, master_key->size); + res = -ENOKEY; + up_read(&keyring_key->sem); + goto out; + } + res = derive_key_aes(ctx->nonce, master_key->raw, raw_key); + up_read(&keyring_key->sem); + if (res) + goto out; + + crypt_info->ci_keyring_key = keyring_key; + return 0; +out: + key_put(keyring_key); + return res; +} + static void put_crypt_info(struct fscrypt_info *ci) { if (!ci) @@ -96,12 +157,7 @@ static void put_crypt_info(struct fscrypt_info *ci) int get_crypt_info(struct inode *inode) { struct fscrypt_info *crypt_info; - u8 full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE + - (FS_KEY_DESCRIPTOR_SIZE * 2) + 1]; - struct key *keyring_key = NULL; - struct fscrypt_key *master_key; struct fscrypt_context ctx; - struct user_key_payload *ukp; struct crypto_ablkcipher *ctfm; const char *cipher_str; u8 raw_key[FS_MAX_KEY_SIZE]; @@ -172,48 +228,24 @@ retry: memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE); goto got_key; } - memcpy(full_key_descriptor, FS_KEY_DESC_PREFIX, - FS_KEY_DESC_PREFIX_SIZE); - sprintf(full_key_descriptor + FS_KEY_DESC_PREFIX_SIZE, - "%*phN", FS_KEY_DESCRIPTOR_SIZE, - ctx.master_key_descriptor); - full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE + - (2 * FS_KEY_DESCRIPTOR_SIZE)] = '\0'; - keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); - if (IS_ERR(keyring_key)) { - res = PTR_ERR(keyring_key); - keyring_key = NULL; - goto out; - } - crypt_info->ci_keyring_key = keyring_key; - if (keyring_key->type != &key_type_logon) { - printk_once(KERN_WARNING - "%s: key type must be logon\n", __func__); - res = -ENOKEY; - goto out; - } - down_read(&keyring_key->sem); - ukp = ((struct user_key_payload *)keyring_key->payload.data); - if (ukp->datalen != sizeof(struct fscrypt_key)) { - res = -EINVAL; - up_read(&keyring_key->sem); - goto out; - } - master_key = (struct fscrypt_key *)ukp->data; - BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE); - if (master_key->size != FS_AES_256_XTS_KEY_SIZE) { - printk_once(KERN_WARNING - "%s: key size incorrect: %d\n", - __func__, master_key->size); - res = -ENOKEY; - up_read(&keyring_key->sem); + res = validate_user_key(crypt_info, &ctx, raw_key, + FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE); + if (res && inode->i_sb->s_cop->key_prefix) { + u8 *prefix = NULL; + int prefix_size, res2; + + prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix); + res2 = validate_user_key(crypt_info, &ctx, raw_key, + prefix, prefix_size); + if (res2) { + if (res2 == -ENOKEY) + res = -ENOKEY; + goto out; + } + } else if (res) { goto out; } - res = derive_key_aes(ctx.nonce, master_key->raw, raw_key); - up_read(&keyring_key->sem); - if (res) - goto out; got_key: ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); if (!ctfm || IS_ERR(ctfm)) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e400f729678f..cded8910af93 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -742,6 +742,10 @@ enum { MAX_TIME, }; +#ifdef CONFIG_F2FS_FS_ENCRYPTION +#define F2FS_KEY_DESC_PREFIX "f2fs:" +#define F2FS_KEY_DESC_PREFIX_SIZE 5 +#endif struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -749,6 +753,10 @@ struct f2fs_sb_info { int valid_super_block; /* valid super block no */ int s_flag; /* flags for sbi */ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + u8 key_prefix[F2FS_KEY_DESC_PREFIX_SIZE]; + u8 key_prefix_size; +#endif /* for node-related operations */ struct f2fs_nm_info *nm_info; /* node manager */ struct inode *node_inode; /* cache node blocks */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 84d10358b595..7871be11d5f4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -964,6 +964,12 @@ static int f2fs_get_context(struct inode *inode, void *ctx, size_t len) ctx, len, NULL); } +static int f2fs_key_prefix(struct inode *inode, u8 **key) +{ + *key = F2FS_I_SB(inode)->key_prefix; + return F2FS_I_SB(inode)->key_prefix_size; +} + static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len, void *fs_data) { @@ -980,6 +986,7 @@ static unsigned f2fs_max_namelen(struct inode *inode) static struct fscrypt_operations f2fs_cryptops = { .get_context = f2fs_get_context, + .key_prefix = f2fs_key_prefix, .set_context = f2fs_set_context, .is_encrypted = f2fs_encrypted_inode, .empty_dir = f2fs_empty_dir, @@ -1305,6 +1312,12 @@ static void init_sb_info(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sbi->s_list); mutex_init(&sbi->umount_mutex); + +#ifdef CONFIG_F2FS_FS_ENCRYPTION + memcpy(sbi->key_prefix, F2FS_KEY_DESC_PREFIX, + F2FS_KEY_DESC_PREFIX_SIZE); + sbi->key_prefix_size = F2FS_KEY_DESC_PREFIX_SIZE; +#endif } /* diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h index 4c4bc8c058ab..7ddcd5bc849e 100644 --- a/include/linux/fscrypto.h +++ b/include/linux/fscrypto.h @@ -174,6 +174,7 @@ struct fscrypt_name { */ struct fscrypt_operations { int (*get_context)(struct inode *, void *, size_t); + int (*key_prefix)(struct inode *, u8 **); int (*prepare_context)(struct inode *); int (*set_context)(struct inode *, const void *, size_t, void *); int (*dummy_context)(struct inode *); -- GitLab From 718db1837a987f78405c277cacaacae9c7bc4a74 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 7 May 2016 16:15:05 +0800 Subject: [PATCH 0223/5498] f2fs: fix inode cache leak When testing f2fs with inline_dentry option, generic/342 reports: VFS: Busy inodes after unmount of dm-0. Self-destruct in 5 seconds. Have a nice day... After rmmod f2fs module, kenrel shows following dmesg: ============================================================================= BUG f2fs_inode_cache (Tainted: G O ): Objects remaining in f2fs_inode_cache on __kmem_cache_shutdown() ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: Slab 0xf51ca0e0 objects=22 used=1 fp=0xd1e6fc60 flags=0x40004080 CPU: 3 PID: 7455 Comm: rmmod Tainted: G B O 4.6.0-rc4+ #16 Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 00000086 00000086 d062fe18 c13a83a0 f51ca0e0 d062fe38 d062fea4 c11c7276 c1981040 f51ca0e0 00000016 00000001 d1e6fc60 40004080 656a624f 20737463 616d6572 6e696e69 6e692067 66326620 6e695f73 5f65646f 68636163 6e6f2065 Call Trace: [] dump_stack+0x5f/0x8f [] slab_err+0x76/0x80 [] ? __kmem_cache_shutdown+0x100/0x2f0 [] ? __kmem_cache_shutdown+0x100/0x2f0 [] __kmem_cache_shutdown+0x125/0x2f0 [] kmem_cache_destroy+0x158/0x1f0 [] ? mutex_unlock+0xd/0x10 [] exit_f2fs_fs+0x4b/0x5a8 [f2fs] [] SyS_delete_module+0x16c/0x1d0 [] ? do_fast_syscall_32+0x30/0x1c0 [] ? __this_cpu_preempt_check+0xf/0x20 [] ? trace_hardirqs_on_caller+0xdd/0x210 [] ? trace_hardirqs_off+0xb/0x10 [] do_fast_syscall_32+0xa1/0x1c0 [] sysenter_past_esp+0x45/0x74 INFO: Object 0xd1e6d9e0 @offset=6624 kmem_cache_destroy f2fs_inode_cache: Slab cache still has objects CPU: 3 PID: 7455 Comm: rmmod Tainted: G B O 4.6.0-rc4+ #16 Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 00000286 00000286 d062fef4 c13a83a0 f174b000 d062ff14 d062ff28 c1198ac7 c197fe18 f3c5b980 d062ff20 000d04f2 d062ff0c d062ff0c d062ff14 d062ff14 f8f20dc0 fffffff5 d062e000 d062ff30 f8f15aa3 d062ff7c c10f596c 73663266 Call Trace: [] dump_stack+0x5f/0x8f [] kmem_cache_destroy+0x1e7/0x1f0 [] exit_f2fs_fs+0x4b/0x5a8 [f2fs] [] SyS_delete_module+0x16c/0x1d0 [] ? do_fast_syscall_32+0x30/0x1c0 [] ? __this_cpu_preempt_check+0xf/0x20 [] ? trace_hardirqs_on_caller+0xdd/0x210 [] ? trace_hardirqs_off+0xb/0x10 [] do_fast_syscall_32+0xa1/0x1c0 [] sysenter_past_esp+0x45/0x74 The reason is: in recovery flow, we use delayed iput mechanism for directory which has recovered dentry block. It means the reference of inode will be held until last dirty dentry page being writebacked. But when we mount f2fs with inline_dentry option, during recovery, dirent may only be recovered into dir inode page rather than dentry page, so there are no chance for us to release inode reference in ->writepage when writebacking last dentry page. We can call paired iget/iput explicityly for inline_dentry case, but for non-inline_dentry case, iput will call writeback_single_inode to write all data pages synchronously, but during recovery, ->writepages of f2fs skips writing all pages, result in losing dirent. This patch fixes this issue by obsoleting old mechanism, and introduce a new dir_list to hold all directory inodes which has recovered datas until finishing recovery. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 16 ------------- fs/f2fs/f2fs.h | 2 -- fs/f2fs/recovery.c | 56 ++++++++++++++++++++++++-------------------- 3 files changed, 31 insertions(+), 43 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index f9a7d7f9a7e2..6bbd4dbb6e16 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -795,19 +795,9 @@ void update_dirty_page(struct inode *inode, struct page *page) f2fs_trace_pid(page); } -void add_dirty_dir_inode(struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - - spin_lock(&sbi->inode_lock[DIR_INODE]); - __add_dirty_inode(inode, DIR_INODE); - spin_unlock(&sbi->inode_lock[DIR_INODE]); -} - void remove_dirty_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct f2fs_inode_info *fi = F2FS_I(inode); enum inode_type type = S_ISDIR(inode->i_mode) ? DIR_INODE : FILE_INODE; if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && @@ -817,12 +807,6 @@ void remove_dirty_inode(struct inode *inode) spin_lock(&sbi->inode_lock[type]); __remove_dirty_inode(inode, type); spin_unlock(&sbi->inode_lock[type]); - - /* Only from the recovery routine */ - if (is_inode_flag_set(fi, FI_DELAY_IPUT)) { - clear_inode_flag(fi, FI_DELAY_IPUT); - iput(inode); - } } int sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cded8910af93..3eb84f0c6fcd 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1519,7 +1519,6 @@ enum { FI_NO_ALLOC, /* should not allocate any blocks */ FI_FREE_NID, /* free allocated nide */ FI_UPDATE_DIR, /* should update inode block for consistency */ - FI_DELAY_IPUT, /* used for the recovery */ FI_NO_EXTENT, /* not to use the extent cache */ FI_INLINE_XATTR, /* used for inline xattr */ FI_INLINE_DATA, /* used for inline data*/ @@ -1961,7 +1960,6 @@ void remove_orphan_inode(struct f2fs_sb_info *, nid_t); int recover_orphan_inodes(struct f2fs_sb_info *); int get_valid_checkpoint(struct f2fs_sb_info *); void update_dirty_page(struct inode *, struct page *); -void add_dirty_dir_inode(struct inode *); void remove_dirty_inode(struct inode *); int sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type); int write_checkpoint(struct f2fs_sb_info *, struct cp_control *); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index d0b939d3e2a3..95f2d7a95ed8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -89,7 +89,8 @@ static void del_fsync_inode(struct fsync_inode_entry *entry) kmem_cache_free(fsync_entry_slab, entry); } -static int recover_dentry(struct inode *inode, struct page *ipage) +static int recover_dentry(struct inode *inode, struct page *ipage, + struct list_head *dir_list) { struct f2fs_inode *raw_inode = F2FS_INODE(ipage); nid_t pino = le32_to_cpu(raw_inode->i_pino); @@ -97,18 +98,29 @@ static int recover_dentry(struct inode *inode, struct page *ipage) struct qstr name; struct page *page; struct inode *dir, *einode; + struct fsync_inode_entry *entry; int err = 0; - dir = f2fs_iget(inode->i_sb, pino); - if (IS_ERR(dir)) { - err = PTR_ERR(dir); - goto out; + entry = get_fsync_inode(dir_list, pino); + if (!entry) { + dir = f2fs_iget(inode->i_sb, pino); + if (IS_ERR(dir)) { + err = PTR_ERR(dir); + goto out; + } + + entry = add_fsync_inode(dir_list, dir); + if (!entry) { + err = -ENOMEM; + iput(dir); + goto out; + } } - if (file_enc_name(inode)) { - iput(dir); + dir = entry->inode; + + if (file_enc_name(inode)) return 0; - } name.len = le32_to_cpu(raw_inode->i_namelen); name.name = raw_inode->i_name; @@ -116,7 +128,7 @@ static int recover_dentry(struct inode *inode, struct page *ipage) if (unlikely(name.len > F2FS_NAME_LEN)) { WARN_ON(1); err = -ENAMETOOLONG; - goto out_err; + goto out; } retry: de = f2fs_find_entry(dir, &name, &page); @@ -142,23 +154,12 @@ retry: goto retry; } err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode); - if (err) - goto out_err; - - if (is_inode_flag_set(F2FS_I(dir), FI_DELAY_IPUT)) { - iput(dir); - } else { - add_dirty_dir_inode(dir); - set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT); - } goto out; out_unmap_put: f2fs_dentry_kunmap(dir, page); f2fs_put_page(page, 0); -out_err: - iput(dir); out: f2fs_msg(inode->i_sb, KERN_NOTICE, "%s: ino = %x, name = %s, dir = %lx, err = %d", @@ -501,7 +502,8 @@ out: return err; } -static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head) +static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, + struct list_head *dir_list) { unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); struct curseg_info *curseg; @@ -528,7 +530,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head) break; } - entry = get_fsync_inode(head, ino_of_node(page)); + entry = get_fsync_inode(inode_list, ino_of_node(page)); if (!entry) goto next; /* @@ -539,7 +541,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head) if (IS_INODE(page)) recover_inode(entry->inode, page); if (entry->last_dentry == blkaddr) { - err = recover_dentry(entry->inode, page); + err = recover_dentry(entry->inode, page, dir_list); if (err) { f2fs_put_page(page, 1); break; @@ -567,6 +569,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); struct list_head inode_list; + struct list_head dir_list; block_t blkaddr; int err; int ret = 0; @@ -578,6 +581,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) return -ENOMEM; INIT_LIST_HEAD(&inode_list); + INIT_LIST_HEAD(&dir_list); /* prevent checkpoint */ mutex_lock(&sbi->cp_mutex); @@ -597,12 +601,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) need_writecp = true; /* step #2: recover data */ - err = recover_data(sbi, &inode_list); + err = recover_data(sbi, &inode_list, &dir_list); if (!err) f2fs_bug_on(sbi, !list_empty(&inode_list)); out: destroy_fsync_dnodes(&inode_list); - kmem_cache_destroy(fsync_entry_slab); /* truncate meta pages to be used by the recovery */ truncate_inode_pages_range(META_MAPPING(sbi), @@ -640,5 +643,8 @@ out: } else { mutex_unlock(&sbi->cp_mutex); } + + destroy_fsync_dnodes(&dir_list); + kmem_cache_destroy(fsync_entry_slab); return ret ? ret: err; } -- GitLab From 0b74eb8a05701b606ed01a72c805fbc5717c5e10 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 6 May 2016 15:30:38 -0700 Subject: [PATCH 0224/5498] f2fs: fallocate data blocks in single locked node page This patch is to improve the expand_inode speed in fallocate by allocating data blocks as many as possible in single locked node page. In SSD, # time fallocate -l 500G $MNT/testfile Before : 1m 33.410 s After : 24.758 s Reported-by: Stephen Bates Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/file.c --- fs/f2fs/file.c | 51 ++++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e1a6288c9118..1c4e279edb0f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1159,10 +1159,11 @@ static int expand_inode_data(struct inode *inode, loff_t offset, loff_t len, int mode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - pgoff_t index, pg_start, pg_end; + struct f2fs_map_blocks map = { .m_next_pgofs = NULL }; + pgoff_t pg_end; loff_t new_size = i_size_read(inode); - loff_t off_start, off_end; - int ret = 0; + loff_t off_end; + int ret; ret = inode_newsize_ok(inode, (len + offset)); if (ret) @@ -1174,43 +1175,35 @@ static int expand_inode_data(struct inode *inode, loff_t offset, f2fs_balance_fs(sbi, true); - pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; - pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; + pg_end = ((unsigned long long)offset + len) >> PAGE_SHIFT; + off_end = (offset + len) & (PAGE_SIZE - 1); - off_start = offset & (PAGE_CACHE_SIZE - 1); - off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); + map.m_lblk = ((unsigned long long)offset) >> PAGE_SHIFT; + map.m_len = pg_end - map.m_lblk; + if (off_end) + map.m_len++; - f2fs_lock_op(sbi); + ret = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); + if (ret) { + pgoff_t last_off; - for (index = pg_start; index <= pg_end; index++) { - struct dnode_of_data dn; + if (!map.m_len) + return ret; - if (index == pg_end && !off_end) - goto noalloc; + last_off = map.m_lblk + map.m_len - 1; - set_new_dnode(&dn, inode, NULL, NULL, 0); - ret = f2fs_reserve_block(&dn, index); - if (ret) - break; -noalloc: - if (pg_start == pg_end) - new_size = offset + len; - else if (index == pg_start && off_start) - new_size = (loff_t)(index + 1) << PAGE_CACHE_SHIFT; - else if (index == pg_end) - new_size = ((loff_t)index << PAGE_CACHE_SHIFT) + - off_end; - else - new_size += PAGE_CACHE_SIZE; + /* update new size to the failed position */ + new_size = (last_off == pg_end) ? offset + len: + (loff_t)(last_off + 1) << PAGE_SHIFT; + } else { + new_size = ((loff_t)pg_end << PAGE_SHIFT) + off_end; } - if (!(mode & FALLOC_FL_KEEP_SIZE) && - i_size_read(inode) < new_size) { + if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) { i_size_write(inode, new_size); mark_inode_dirty(inode); update_inode_page(inode); } - f2fs_unlock_op(sbi); return ret; } -- GitLab From 5aa4ae1ba752a8eb27625dbd29202b5832a37103 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 6 May 2016 16:19:43 -0700 Subject: [PATCH 0225/5498] f2fs: read node blocks ahead when truncating blocks This patch enables reading node blocks in advance when truncating large data blocks. > time rm $MNT/testfile (500GB) after drop_cachees Before : 9.422 s After : 4.821 s Reported-by: Stephen Bates Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/node.c --- fs/f2fs/file.c | 2 +- fs/f2fs/node.c | 51 +++++++++++++++++++++++++++----------------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 1c4e279edb0f..104104da7597 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -585,7 +585,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) } set_new_dnode(&dn, inode, ipage, NULL, 0); - err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE); + err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE_RA); if (err) { if (err == -ENOENT) goto free_next; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4ef4afd55e0a..3afe0a726368 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -407,6 +407,29 @@ cache: up_write(&nm_i->nat_tree_lock); } +/* + * readahead MAX_RA_NODE number of node pages. + */ +static void ra_node_pages(struct page *parent, int start, int n) +{ + struct f2fs_sb_info *sbi = F2FS_P_SB(parent); + struct blk_plug plug; + int i, end; + nid_t nid; + + blk_start_plug(&plug); + + /* Then, try readahead for siblings of the desired node */ + end = start + n; + end = min(end, NIDS_PER_BLOCK); + for (i = start; i < end; i++) { + nid = get_nid(parent, i, false); + ra_node_page(sbi, nid); + } + + blk_finish_plug(&plug); +} + pgoff_t get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs) { const long direct_index = ADDRS_PER_INODE(dn->inode); @@ -707,6 +730,8 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, return PTR_ERR(page); } + ra_node_pages(page, ofs, NIDS_PER_BLOCK); + rn = F2FS_NODE(page); if (depth < 3) { for (i = ofs; i < NIDS_PER_BLOCK; i++, freed++) { @@ -784,6 +809,8 @@ static int truncate_partial_nodes(struct dnode_of_data *dn, nid[i + 1] = get_nid(pages[i], offset[i + 1], false); } + ra_node_pages(pages[idx], offset[idx + 1], NIDS_PER_BLOCK); + /* free direct nodes linked to a partial indirect node */ for (i = offset[idx + 1]; i < NIDS_PER_BLOCK; i++) { child_nid = get_nid(pages[idx], i, false); @@ -1095,28 +1122,6 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) f2fs_put_page(apage, err ? 1 : 0); } -/* - * readahead MAX_RA_NODE number of node pages. - */ -static void ra_node_pages(struct page *parent, int start) -{ - struct f2fs_sb_info *sbi = F2FS_P_SB(parent); - struct blk_plug plug; - int i, end; - nid_t nid; - - blk_start_plug(&plug); - - /* Then, try readahead for siblings of the desired node */ - end = start + MAX_RA_NODE; - end = min(end, NIDS_PER_BLOCK); - for (i = start; i < end; i++) { - nid = get_nid(parent, i, false); - ra_node_page(sbi, nid); - } - blk_finish_plug(&plug); -} - static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid, struct page *parent, int start) { @@ -1140,7 +1145,7 @@ repeat: } if (parent) - ra_node_pages(parent, start + 1); + ra_node_pages(parent, start + 1, MAX_RA_NODE); lock_page(page); -- GitLab From dd8f4e4e1980cbec730dd18458b33ca8bf45413d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 7 May 2016 08:52:57 -0700 Subject: [PATCH 0226/5498] f2fs: do not preallocate block unaligned to 4KB Previously f2fs_preallocate_blocks() tries to allocate unaligned blocks. In f2fs_write_begin(), however, prepare_write_begin() does not skip its allocation due to (len != 4KB). So, it needs locking node page twice unexpectedly. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 311441f994ba..3f3c2351e0a1 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -583,8 +583,8 @@ ssize_t f2fs_preallocate_blocks(struct inode *inode, loff_t pos, struct f2fs_map_blocks map; ssize_t ret = 0; - map.m_lblk = F2FS_BYTES_TO_BLK(pos); - map.m_len = F2FS_BLK_ALIGN(count); + map.m_lblk = F2FS_BLK_ALIGN(pos); + map.m_len = F2FS_BYTES_TO_BLK(count); map.m_next_pgofs = NULL; if (f2fs_encrypted_inode(inode)) -- GitLab From 0d2b43ac053ff3a91bca8baf6e4502661e830400 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 9 May 2016 19:56:32 +0800 Subject: [PATCH 0227/5498] f2fs: use mnt_{want,drop}_write_file in ioctl In interfaces of ioctl, mnt_{want,drop}_write_file should be used for: - get exclusion against file system freezing which may used by lvm snapshot. - do telling filesystem that a write is about to be performed on it, and make sure that the writes are permitted. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 117 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 104104da7597..d7f27156fc51 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1310,20 +1310,16 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) unsigned int oldflags; int ret; + if (!inode_owner_or_capable(inode)) + return -EACCES; + + if (get_user(flags, (int __user *)arg)) + return -EFAULT; + ret = mnt_want_write_file(filp); if (ret) return ret; - if (!inode_owner_or_capable(inode)) { - ret = -EACCES; - goto out; - } - - if (get_user(flags, (int __user *)arg)) { - ret = -EFAULT; - goto out; - } - flags = f2fs_mask_flags(inode->i_mode, flags); mutex_lock(&inode->i_mutex); @@ -1366,18 +1362,22 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) if (!inode_owner_or_capable(inode)) return -EACCES; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + if (f2fs_is_atomic_file(inode)) - return 0; + goto out; ret = f2fs_convert_inline_inode(inode); if (ret) - return ret; + goto out; set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); if (!get_dirty_pages(inode)) - return 0; + goto out; f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, "Unexpected flush for atomic writes: ino=%lu, npages=%u", @@ -1385,6 +1385,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); if (ret) clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); +out: + mnt_drop_write_file(filp); return ret; } @@ -1396,13 +1398,13 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) if (!inode_owner_or_capable(inode)) return -EACCES; - if (f2fs_is_volatile_file(inode)) - return 0; - ret = mnt_want_write_file(filp); if (ret) return ret; + if (f2fs_is_volatile_file(inode)) + goto err_out; + if (f2fs_is_atomic_file(inode)) { clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); ret = commit_inmem_pages(inode); @@ -1426,32 +1428,48 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) if (!inode_owner_or_capable(inode)) return -EACCES; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + if (f2fs_is_volatile_file(inode)) - return 0; + goto out; ret = f2fs_convert_inline_inode(inode); if (ret) - return ret; + goto out; set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); - return 0; +out: + mnt_drop_write_file(filp); + return ret; } static int f2fs_ioc_release_volatile_write(struct file *filp) { struct inode *inode = file_inode(filp); + int ret; if (!inode_owner_or_capable(inode)) return -EACCES; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + if (!f2fs_is_volatile_file(inode)) - return 0; + goto out; - if (!f2fs_is_first_block_written(inode)) - return truncate_partial_data_page(inode, 0, true); + if (!f2fs_is_first_block_written(inode)) { + ret = truncate_partial_data_page(inode, 0, true); + goto out; + } - return punch_hole(inode, 0, F2FS_BLKSIZE); + ret = punch_hole(inode, 0, F2FS_BLKSIZE); +out: + mnt_drop_write_file(filp); + return ret; } static int f2fs_ioc_abort_volatile_write(struct file *filp) @@ -1484,6 +1502,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct super_block *sb = sbi->sb; __u32 in; + int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1491,6 +1510,10 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) if (get_user(in, (__u32 __user *)arg)) return -EFAULT; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + switch (in) { case FS_GOING_DOWN_FULLSYNC: sb = freeze_bdev(sb->s_bdev); @@ -1512,10 +1535,13 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) f2fs_stop_checkpoint(sbi); break; default: - return -EINVAL; + ret = -EINVAL; + goto out; } f2fs_update_time(sbi, REQ_TIME); - return 0; +out: + mnt_drop_write_file(filp); + return ret; } static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) @@ -1536,9 +1562,14 @@ static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) sizeof(range))) return -EFAULT; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + range.minlen = max((unsigned int)range.minlen, q->limits.discard_granularity); ret = f2fs_trim_fs(F2FS_SB(sb), &range); + mnt_drop_write_file(filp); if (ret < 0) return ret; @@ -1563,13 +1594,21 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) { struct fscrypt_policy policy; struct inode *inode = file_inode(filp); + int ret; if (copy_from_user(&policy, (struct fscrypt_policy __user *)arg, sizeof(policy))) return -EFAULT; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); - return fscrypt_process_policy(inode, &policy); + ret = fscrypt_process_policy(inode, &policy); + + mnt_drop_write_file(filp); + return ret; } static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) @@ -1626,6 +1665,7 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg) struct inode *inode = file_inode(filp); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); __u32 sync; + int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1636,20 +1676,30 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg) if (f2fs_readonly(sbi->sb)) return -EROFS; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + if (!sync) { - if (!mutex_trylock(&sbi->gc_mutex)) - return -EBUSY; + if (!mutex_trylock(&sbi->gc_mutex)) { + ret = -EBUSY; + goto out; + } } else { mutex_lock(&sbi->gc_mutex); } - return f2fs_gc(sbi, sync); + ret = f2fs_gc(sbi, sync); +out: + mnt_drop_write_file(filp); + return ret; } static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1657,7 +1707,14 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) if (f2fs_readonly(sbi->sb)) return -EROFS; - return f2fs_sync_fs(sbi->sb, 1); + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + ret = f2fs_sync_fs(sbi->sb, 1); + + mnt_drop_write_file(filp); + return ret; } static int f2fs_defragment_range(struct f2fs_sb_info *sbi, -- GitLab From 4d0d91600aebc80038509ec3c1a414c2e034e86b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 9 May 2016 19:56:33 +0800 Subject: [PATCH 0228/5498] f2fs: make atomic/volatile operation exclusive atomic/volatile ioctl interfaces are exposed to user like other file operation interface, it needs to make them getting exclusion against to each other to avoid potential conflict among these operations in concurrent scenario. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d7f27156fc51..5ca186a1468a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1366,6 +1366,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) if (ret) return ret; + inode_lock(inode); + if (f2fs_is_atomic_file(inode)) goto out; @@ -1386,6 +1388,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) if (ret) clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); out: + inode_unlock(inode); mnt_drop_write_file(filp); return ret; } @@ -1402,6 +1405,8 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) if (ret) return ret; + inode_lock(inode); + if (f2fs_is_volatile_file(inode)) goto err_out; @@ -1416,6 +1421,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); err_out: + inode_unlock(inode); mnt_drop_write_file(filp); return ret; } @@ -1432,6 +1438,8 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) if (ret) return ret; + inode_lock(inode); + if (f2fs_is_volatile_file(inode)) goto out; @@ -1442,6 +1450,7 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); out: + inode_unlock(inode); mnt_drop_write_file(filp); return ret; } @@ -1458,6 +1467,8 @@ static int f2fs_ioc_release_volatile_write(struct file *filp) if (ret) return ret; + inode_lock(inode); + if (!f2fs_is_volatile_file(inode)) goto out; @@ -1468,6 +1479,7 @@ static int f2fs_ioc_release_volatile_write(struct file *filp) ret = punch_hole(inode, 0, F2FS_BLKSIZE); out: + inode_unlock(inode); mnt_drop_write_file(filp); return ret; } @@ -1484,6 +1496,8 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) if (ret) return ret; + inode_lock(inode); + if (f2fs_is_atomic_file(inode)) drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { @@ -1491,6 +1505,8 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); } + inode_unlock(inode); + mnt_drop_write_file(filp); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return ret; -- GitLab From bd6c696cfc952d3ef5a9510c0349f10addbdbc50 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 9 May 2016 19:56:30 +0800 Subject: [PATCH 0229/5498] f2fs: support in batch multi blocks preallocation This patch introduces reserve_new_blocks to make preallocation of multi blocks as in batch operation, so it can avoid lots of redundant operation, result in better performance. In virtual machine, with rotational device: time fallocate -l 32G /mnt/f2fs/file Before: real 0m4.584s user 0m0.000s sys 0m4.580s After: real 0m0.292s user 0m0.000s sys 0m0.272s In x86, with SSD: time fallocate -l 500G $MNT/testfile Before : 24.758 s After : 1.604 s Signed-off-by: Chao Yu [Jaegeuk Kim: fix bugs and add performance numbers measured in x86.] Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 132 ++++++++++++++++++++++++++---------- fs/f2fs/f2fs.h | 20 ++++-- include/trace/events/f2fs.h | 14 ++-- 3 files changed, 117 insertions(+), 49 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3f3c2351e0a1..67ee59dcbc52 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -279,6 +279,16 @@ alloc_new: trace_f2fs_submit_page_mbio(fio->page, fio); } +static void __set_data_blkaddr(struct dnode_of_data *dn) +{ + struct f2fs_node *rn = F2FS_NODE(dn->node_page); + __le32 *addr_array; + + /* Get physical address of data block */ + addr_array = blkaddr_in_node(rn); + addr_array[dn->ofs_in_node] = cpu_to_le32(dn->data_blkaddr); +} + /* * Lock ordering for the change of data block address: * ->data_page @@ -287,19 +297,9 @@ alloc_new: */ void set_data_blkaddr(struct dnode_of_data *dn) { - struct f2fs_node *rn; - __le32 *addr_array; - struct page *node_page = dn->node_page; - unsigned int ofs_in_node = dn->ofs_in_node; - - f2fs_wait_on_page_writeback(node_page, NODE, true); - - rn = F2FS_NODE(node_page); - - /* Get physical address of data block */ - addr_array = blkaddr_in_node(rn); - addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr); - if (set_page_dirty(node_page)) + f2fs_wait_on_page_writeback(dn->node_page, NODE, true); + __set_data_blkaddr(dn); + if (set_page_dirty(dn->node_page)) dn->node_changed = true; } @@ -310,24 +310,53 @@ void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr) f2fs_update_extent_cache(dn); } -int reserve_new_block(struct dnode_of_data *dn) +/* dn->ofs_in_node will be returned with up-to-date last block pointer */ +int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + if (!count) + return 0; + if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) return -EPERM; - if (unlikely(!inc_valid_block_count(sbi, dn->inode, 1))) + if (unlikely(!inc_valid_block_count(sbi, dn->inode, &count))) return -ENOSPC; - trace_f2fs_reserve_new_block(dn->inode, dn->nid, dn->ofs_in_node); + trace_f2fs_reserve_new_blocks(dn->inode, dn->nid, + dn->ofs_in_node, count); + + f2fs_wait_on_page_writeback(dn->node_page, NODE, true); + + for (; count > 0; dn->ofs_in_node++) { + block_t blkaddr = + datablock_addr(dn->node_page, dn->ofs_in_node); + if (blkaddr == NULL_ADDR) { + dn->data_blkaddr = NEW_ADDR; + __set_data_blkaddr(dn); + count--; + } + } + + if (set_page_dirty(dn->node_page)) + dn->node_changed = true; - dn->data_blkaddr = NEW_ADDR; - set_data_blkaddr(dn); mark_inode_dirty(dn->inode); sync_inode_page(dn); return 0; } +/* Should keep dn->ofs_in_node unchanged */ +int reserve_new_block(struct dnode_of_data *dn) +{ + unsigned int ofs_in_node = dn->ofs_in_node; + int ret; + + ret = reserve_new_blocks(dn, 1); + dn->ofs_in_node = ofs_in_node; + return ret; +} + int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index) { bool need_put = dn->inode_page ? false : true; @@ -546,6 +575,7 @@ static int __allocate_data_block(struct dnode_of_data *dn) struct node_info ni; int seg = CURSEG_WARM_DATA; pgoff_t fofs; + blkcnt_t count = 1; if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) return -EPERM; @@ -554,7 +584,7 @@ static int __allocate_data_block(struct dnode_of_data *dn) if (dn->data_blkaddr == NEW_ADDR) goto alloc; - if (unlikely(!inc_valid_block_count(sbi, dn->inode, 1))) + if (unlikely(!inc_valid_block_count(sbi, dn->inode, &count))) return -ENOSPC; alloc: @@ -622,8 +652,10 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, struct dnode_of_data dn; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA; - pgoff_t pgofs, end_offset; + pgoff_t pgofs, end_offset, end; int err = 0, ofs = 1; + unsigned int ofs_in_node, last_ofs_in_node; + blkcnt_t prealloc; struct extent_info ei; bool allocated = false; block_t blkaddr; @@ -633,6 +665,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, /* it only supports block size == page size */ pgofs = (pgoff_t)map->m_lblk; + end = pgofs + maxblocks; if (!create && f2fs_lookup_extent_cache(inode, pgofs, &ei)) { map->m_pblk = ei.blk + pgofs - ei.fofs; @@ -660,6 +693,8 @@ next_dnode: goto unlock_out; } + prealloc = 0; + ofs_in_node = dn.ofs_in_node; end_offset = ADDRS_PER_PAGE(dn.node_page, inode); next_block: @@ -672,17 +707,20 @@ next_block: goto sync_out; } if (flag == F2FS_GET_BLOCK_PRE_AIO) { - if (blkaddr == NULL_ADDR) - err = reserve_new_block(&dn); + if (blkaddr == NULL_ADDR) { + prealloc++; + last_ofs_in_node = dn.ofs_in_node; + } } else { err = __allocate_data_block(&dn); - if (!err) + if (!err) { set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + allocated = true; + } } if (err) goto sync_out; - allocated = true; map->m_flags = F2FS_MAP_NEW; blkaddr = dn.data_blkaddr; } else { @@ -701,6 +739,9 @@ next_block: } } + if (flag == F2FS_GET_BLOCK_PRE_AIO) + goto skip; + if (map->m_len == 0) { /* preallocated unwritten block should be mapped for fiemap. */ if (blkaddr == NEW_ADDR) @@ -712,32 +753,49 @@ next_block: } else if ((map->m_pblk != NEW_ADDR && blkaddr == (map->m_pblk + ofs)) || (map->m_pblk == NEW_ADDR && blkaddr == NEW_ADDR) || - flag == F2FS_GET_BLOCK_PRE_DIO || - flag == F2FS_GET_BLOCK_PRE_AIO) { + flag == F2FS_GET_BLOCK_PRE_DIO) { ofs++; map->m_len++; } else { goto sync_out; } +skip: dn.ofs_in_node++; pgofs++; - if (map->m_len < maxblocks) { - if (dn.ofs_in_node < end_offset) - goto next_block; + /* preallocate blocks in batch for one dnode page */ + if (flag == F2FS_GET_BLOCK_PRE_AIO && + (pgofs == end || dn.ofs_in_node == end_offset)) { - if (allocated) - sync_inode_page(&dn); - f2fs_put_dnode(&dn); + dn.ofs_in_node = ofs_in_node; + err = reserve_new_blocks(&dn, prealloc); + if (err) + goto sync_out; - if (create) { - f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, allocated); + map->m_len += dn.ofs_in_node - ofs_in_node; + if (prealloc && dn.ofs_in_node != last_ofs_in_node + 1) { + err = -ENOSPC; + goto sync_out; } - allocated = false; - goto next_dnode; + dn.ofs_in_node = end_offset; + } + + if (pgofs >= end) + goto sync_out; + else if (dn.ofs_in_node < end_offset) + goto next_block; + + if (allocated) + sync_inode_page(&dn); + f2fs_put_dnode(&dn); + + if (create) { + f2fs_unlock_op(sbi); + f2fs_balance_fs(sbi, allocated); } + allocated = false; + goto next_dnode; sync_out: if (allocated) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3eb84f0c6fcd..111b2a6b967a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1125,7 +1125,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs) } static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, - struct inode *inode, blkcnt_t count) + struct inode *inode, blkcnt_t *count) { block_t valid_block_count; @@ -1137,14 +1137,19 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, } #endif valid_block_count = - sbi->total_valid_block_count + (block_t)count; + sbi->total_valid_block_count + (block_t)(*count); if (unlikely(valid_block_count > sbi->user_block_count)) { - spin_unlock(&sbi->stat_lock); - return false; + *count = sbi->user_block_count - sbi->total_valid_block_count; + if (!*count) { + spin_unlock(&sbi->stat_lock); + return false; + } } - inode->i_blocks += count; - sbi->total_valid_block_count = valid_block_count; - sbi->alloc_valid_block_count += (block_t)count; + /* *count can be recalculated */ + inode->i_blocks += *count; + sbi->total_valid_block_count = + sbi->total_valid_block_count + (block_t)(*count); + sbi->alloc_valid_block_count += (block_t)(*count); spin_unlock(&sbi->stat_lock); return true; } @@ -1978,6 +1983,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *); void set_data_blkaddr(struct dnode_of_data *); void f2fs_update_data_blkaddr(struct dnode_of_data *, block_t); +int reserve_new_blocks(struct dnode_of_data *, blkcnt_t); int reserve_new_block(struct dnode_of_data *); int f2fs_get_block(struct dnode_of_data *, pgoff_t); ssize_t f2fs_preallocate_blocks(struct inode *, loff_t, size_t, bool); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 7732cb71b908..7e2ed7aa37af 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -659,28 +659,32 @@ TRACE_EVENT(f2fs_direct_IO_exit, __entry->ret) ); -TRACE_EVENT(f2fs_reserve_new_block, +TRACE_EVENT(f2fs_reserve_new_blocks, - TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node), + TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node, + blkcnt_t count), - TP_ARGS(inode, nid, ofs_in_node), + TP_ARGS(inode, nid, ofs_in_node, count), TP_STRUCT__entry( __field(dev_t, dev) __field(nid_t, nid) __field(unsigned int, ofs_in_node) + __field(blkcnt_t, count) ), TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->nid = nid; __entry->ofs_in_node = ofs_in_node; + __entry->count = count; ), - TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u", + TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u, count = %llu", show_dev(__entry), (unsigned int)__entry->nid, - __entry->ofs_in_node) + __entry->ofs_in_node, + (unsigned long long)__entry->count) ); DECLARE_EVENT_CLASS(f2fs__submit_page_bio, -- GitLab From 352a7ed3d8fcec9f4095ddd618f3fa500b5d9b40 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 9 May 2016 19:56:31 +0800 Subject: [PATCH 0230/5498] f2fs: support in batch fzero in dnode page This patch tries to speedup fzero_range by making space preallocation and address removal of blocks in one dnode page as in batch operation. In virtual machine, with zram driver: dd if=/dev/zero of=/mnt/f2fs/file bs=1M count=4096 time xfs_io -f /mnt/f2fs/file -c "fzero 0 4096M" Before: real 0m3.276s user 0m0.008s sys 0m3.260s After: real 0m1.568s user 0m0.000s sys 0m1.564s Signed-off-by: Chao Yu [Jaegeuk Kim: consider ENOSPC case] Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/file.c --- fs/f2fs/file.c | 72 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 5ca186a1468a..2b946983b705 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -998,6 +998,49 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) return ret; } +static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start, + pgoff_t end) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + pgoff_t index = start; + unsigned int ofs_in_node = dn->ofs_in_node; + blkcnt_t count = 0; + int ret; + + for (; index < end; index++, dn->ofs_in_node++) { + if (datablock_addr(dn->node_page, dn->ofs_in_node) == NULL_ADDR) + count++; + } + + dn->ofs_in_node = ofs_in_node; + ret = reserve_new_blocks(dn, count); + if (ret) + return ret; + + dn->ofs_in_node = ofs_in_node; + for (index = start; index < end; index++, dn->ofs_in_node++) { + dn->data_blkaddr = + datablock_addr(dn->node_page, dn->ofs_in_node); + /* + * reserve_new_blocks will not guarantee entire block + * allocation. + */ + if (dn->data_blkaddr == NULL_ADDR) { + ret = -ENOSPC; + break; + } + if (dn->data_blkaddr != NEW_ADDR) { + invalidate_blocks(sbi, dn->data_blkaddr); + dn->data_blkaddr = NEW_ADDR; + set_data_blkaddr(dn); + } + } + + f2fs_update_extent_cache_range(dn, start, 0, index - start); + + return ret; +} + static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, int mode) { @@ -1048,35 +1091,32 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, (loff_t)pg_start << PAGE_CACHE_SHIFT); } - for (index = pg_start; index < pg_end; index++) { + for (index = pg_start; index < pg_end;) { struct dnode_of_data dn; - struct page *ipage; + unsigned int end_offset; + pgoff_t end; f2fs_lock_op(sbi); - ipage = get_node_page(sbi, inode->i_ino); - if (IS_ERR(ipage)) { - ret = PTR_ERR(ipage); - f2fs_unlock_op(sbi); - goto out; - } - - set_new_dnode(&dn, inode, ipage, NULL, 0); - ret = f2fs_reserve_block(&dn, index); + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = get_dnode_of_data(&dn, index, ALLOC_NODE); if (ret) { f2fs_unlock_op(sbi); goto out; } - if (dn.data_blkaddr != NEW_ADDR) { - invalidate_blocks(sbi, dn.data_blkaddr); - f2fs_update_data_blkaddr(&dn, NEW_ADDR); - } + end_offset = ADDRS_PER_PAGE(dn.node_page, inode); + end = min(pg_end, end_offset - dn.ofs_in_node + index); + + ret = f2fs_do_zero_range(&dn, index, end); f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); + if (ret) + goto out; + index = end; new_size = max_t(loff_t, new_size, - (loff_t)(index + 1) << PAGE_CACHE_SHIFT); + (loff_t)(index) << PAGE_SHIFT); } if (off_end) { -- GitLab From 3d4d76445beeb1e35abe042f42bc28da4c9867e4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 10 May 2016 19:13:50 -0700 Subject: [PATCH 0231/5498] f2fs: show # of orphan inodes This adds debug information for # of orphan inodes. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 5 ++++- fs/f2fs/f2fs.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 4e4fda2909cb..f2da6209cf16 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -58,6 +58,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->inline_xattr = atomic_read(&sbi->inline_xattr); si->inline_inode = atomic_read(&sbi->inline_inode); si->inline_dir = atomic_read(&sbi->inline_dir); + si->orphans = sbi->im[ORPHAN_INO].ino_num; si->utilization = utilization(sbi); si->free_segs = free_segments(sbi); @@ -192,7 +193,7 @@ get_cache: si->cache_mem += NM_I(sbi)->dirty_nat_cnt * sizeof(struct nat_entry_set); si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); - for (i = 0; i <= UPDATE_INO; i++) + for (i = 0; i <= ORPHAN_INO; i++) si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); si->cache_mem += atomic_read(&sbi->total_ext_tree) * sizeof(struct extent_tree); @@ -240,6 +241,8 @@ static int stat_show(struct seq_file *s, void *v) si->inline_inode); seq_printf(s, " - Inline_dentry Inode: %u\n", si->inline_dir); + seq_printf(s, " - Orphan Inode: %u\n", + si->orphans); seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", si->main_area_segs, si->main_area_sections, si->main_area_zones); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 111b2a6b967a..992f0bd20694 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2030,7 +2030,7 @@ struct f2fs_stat_info { int nats, dirty_nats, sits, dirty_sits, fnids; int total_count, utilization; int bg_gc, inmem_pages, wb_pages; - int inline_xattr, inline_inode, inline_dir; + int inline_xattr, inline_inode, inline_dir, orphans; unsigned int valid_count, valid_node_count, valid_inode_count; unsigned int bimodal, avg_vblocks; int util_free, util_valid, util_invalid; -- GitLab From 04957cd0428ff345917e79766c0b756d228b97d5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 11 May 2016 09:13:13 -0700 Subject: [PATCH 0232/5498] f2fs: avoid f2fs_bug_on during recovery We don't need to use f2fs_bug_on() to treat with any error case when allocating a block during recovery. Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 95f2d7a95ed8..bd444cd9ec0c 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -460,8 +460,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, */ if (dest == NEW_ADDR) { truncate_data_blocks_range(&dn, 1); - err = reserve_new_block(&dn); - f2fs_bug_on(sbi, err); + reserve_new_block(&dn); continue; } -- GitLab From 615842b2da6e35ec13bba27a73c25c4b1180a8e7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 11 May 2016 19:48:44 +0800 Subject: [PATCH 0233/5498] f2fs: fix deadlock when flush inline data Below backtrace info was reported by Yunlei He: Call Trace: [] schedule+0x35/0x80 [] rwsem_down_read_failed+0xed/0x130 [] call_rwsem_down_read_failed+0x18/0x [] down_read+0x20/0x30 [] f2fs_evict_inode+0x242/0x3a0 [f2fs] [] evict+0xc7/0x1a0 [] iput+0x196/0x200 [] __dentry_kill+0x179/0x1e0 [] dput+0x199/0x1f0 [] __fput+0x18b/0x220 [] ____fput+0xe/0x10 [] task_work_run+0x77/0x90 [] exit_to_usermode_loop+0x73/0xa2 [] do_syscall_64+0xfa/0x110 [] entry_SYSCALL64_slow_path+0x25/0x25 Call Trace: [] schedule+0x35/0x80 [] __wait_on_freeing_inode+0xa3/0xd0 [] ? autoremove_wake_function+0x40/0x4 [] find_inode_fast+0x7d/0xb0 [] ilookup+0x6a/0xd0 [] sync_node_pages+0x210/0x650 [f2fs] [] ? do_fsync+0x70/0x70 [] block_operations+0x9e/0xf0 [f2fs] [] ? bio_endio+0x55/0x60 [] write_checkpoint+0x92/0xba0 [f2fs] [] ? mempool_free_slab+0x17/0x20 [] ? mempool_free+0x2b/0x80 [] ? do_fsync+0x70/0x70 [] f2fs_sync_fs+0x63/0xd0 [f2fs] [] ? ext4_sync_fs+0xbf/0x190 [] sync_fs_one_sb+0x20/0x30 [] iterate_supers+0xb9/0x110 [] sys_sync+0x55/0x90 [] do_syscall_64+0x69/0x110 [] entry_SYSCALL64_slow_path+0x25/0x25 With following excuting serials, we will set inline_node in inode page after inode was unlinked, result in a deadloop described as below: 1. open file 2. write file 3. unlink file 4. write file 5. close file Thread A Thread B - dput - iput_final - inode->i_state |= I_FREEING - evict - f2fs_evict_inode - f2fs_sync_fs - write_checkpoint - block_operations - f2fs_lock_all (down_write(cp_rwsem)) - f2fs_lock_op (down_read(cp_rwsem)) - sync_node_pages - ilookup - find_inode_fast - __wait_on_freeing_inode (wait on I_FREEING clear) Here, we change to set inline_node flag only for linked inode for fixing. Reported-by: Yunlei He Signed-off-by: Chao Yu Tested-by: Jaegeuk Kim Cc: stable@vger.kernel.org # v4.6 Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 67ee59dcbc52..82803e7aaacf 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1547,7 +1547,8 @@ restart: if (pos + len <= MAX_INLINE_DATA) { read_inline_data(page, ipage); set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); - set_inline_node(ipage); + if (inode->i_nlink) + set_inline_node(ipage); } else { err = f2fs_convert_inline_page(&dn, page); if (err) -- GitLab From 79e9df5545d64c3a903c216c3550efbae411d305 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 11 May 2016 17:08:14 +0800 Subject: [PATCH 0234/5498] f2fs: correct return value type of f2fs_fill_super Signed-off-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7871be11d5f4..6c5e42703e75 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1417,7 +1417,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) struct f2fs_sb_info *sbi; struct f2fs_super_block *raw_super; struct inode *root; - long err; + int err; bool retry = true, need_fsck = false; char *options = NULL; int recovery, i, valid_super_block; @@ -1643,7 +1643,7 @@ try_onemore: if (err < 0) { need_fsck = true; f2fs_msg(sb, KERN_ERR, - "Cannot recover all fsync data errno=%ld", err); + "Cannot recover all fsync data errno=%d", err); goto free_kobj; } } else { @@ -1676,7 +1676,7 @@ try_onemore: if (recovery) { err = f2fs_commit_super(sbi, true); f2fs_msg(sb, KERN_INFO, - "Try to recover %dth superblock, ret: %ld", + "Try to recover %dth superblock, ret: %d", sbi->valid_super_block ? 1 : 2, err); } -- GitLab From 0789be8ad2e5d07b8d53253eeeb58bf58e5dc286 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 14 May 2016 19:03:52 +0800 Subject: [PATCH 0235/5498] f2fs: fix i_current_depth during inline dentry conversion With below steps, we will see that dentry page becoming unaccessable later. This is because we forget updating i_current_depth in inode during inline dentry conversion, after that, once we failed at somewhere, it will leave i_current_depth as 0 in non-inline directory. Then, during ->lookup, the current_depth value makes all dentry pages in first level invisible. Fix it. 1) mount f2fs with inline_dentry option 2) mkdir dir 3) touch 180 files named [0-179] in dir 4) touch 180 in dir (fail after inline dir conversion) 5) ll dir ls: cannot access /mnt/f2fs/dir/0: No such file or directory ls: cannot access /mnt/f2fs/dir/1: No such file or directory ls: cannot access /mnt/f2fs/dir/2: No such file or directory ls: cannot access /mnt/f2fs/dir/3: No such file or directory ls: cannot access /mnt/f2fs/dir/4: No such file or directory drwxr-xr-x 2 root root 4096 may 13 21:47 ./ drwxr-xr-x 3 root root 4096 may 13 21:46 ../ -????????? ? ? ? ? ? 0 -????????? ? ? ? ? ? 1 -????????? ? ? ? ? ? 10 -????????? ? ? ? ? ? 100 -????????? ? ? ? ? ? 101 -????????? ? ? ? ? ? 102 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/inline.c --- fs/f2fs/inline.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 80cdacf2a27c..915ab99af648 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -400,8 +400,9 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, stat_dec_inline_dir(dir); clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); - if (i_size_read(dir) < PAGE_CACHE_SIZE) { - i_size_write(dir, PAGE_CACHE_SIZE); + F2FS_I(dir)->i_current_depth = 1; + if (i_size_read(dir) < PAGE_SIZE) { + i_size_write(dir, PAGE_SIZE); set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); } -- GitLab From 530950b35034462a752b1de3050e998c735554e3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 14 May 2016 19:03:53 +0800 Subject: [PATCH 0236/5498] f2fs: fix incorrect error path handling in f2fs_move_rehashed_dirents Fix two bugs in error path of f2fs_move_rehashed_dirents: - release dir's inode page if fail to call kmalloc - recover i_current_depth if fail to converting Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 915ab99af648..43254b7b2a3f 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -464,12 +464,15 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *inline_dentry) { struct f2fs_inline_dentry *backup_dentry; + struct f2fs_inode_info *fi = F2FS_I(dir); int err; backup_dentry = f2fs_kmalloc(sizeof(struct f2fs_inline_dentry), GFP_F2FS_ZERO); - if (!backup_dentry) + if (!backup_dentry) { + f2fs_put_page(ipage, 1); return -ENOMEM; + } memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA); truncate_inline_inode(ipage, 0); @@ -483,13 +486,14 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, lock_page(ipage); stat_dec_inline_dir(dir); - clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); + clear_inode_flag(fi, FI_INLINE_DENTRY); update_inode(dir, ipage); kfree(backup_dentry); return 0; recover: lock_page(ipage); memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); + fi->i_current_depth = 0; i_size_write(dir, MAX_INLINE_DATA); update_inode(dir, ipage); f2fs_put_page(ipage, 1); -- GitLab From f57f066295d024cdacd1886bf05adb71fac67d23 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Fri, 13 May 2016 14:57:43 +0800 Subject: [PATCH 0237/5498] f2fs: no need inc dirty pages under inode lock No need inc dirty pages under inode lock Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 6bbd4dbb6e16..b413b4692063 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -788,9 +788,9 @@ void update_dirty_page(struct inode *inode, struct page *page) spin_lock(&sbi->inode_lock[type]); __add_dirty_inode(inode, type); - inode_inc_dirty_pages(inode); spin_unlock(&sbi->inode_lock[type]); + inode_inc_dirty_pages(inode); SetPagePrivate(page); f2fs_trace_pid(page); } -- GitLab From 4799a04c3aef2bc71fe1c7483389897405f48d86 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Mon, 16 May 2016 12:38:50 +0800 Subject: [PATCH 0238/5498] f2fs: add fault injection to sysfs This patch introduces a new struct f2fs_fault_info and a global f2fs_fault to save fault injection status. Fault injection entries are created in /sys/fs/f2fs/fault_injection/ during initializing f2fs module. Signed-off-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 31 ++++++++++++++++++---- fs/f2fs/super.c | 70 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 992f0bd20694..afc230e6eb57 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -49,15 +49,36 @@ enum { FAULT_MAX, }; -extern u32 f2fs_fault_rate; -extern atomic_t f2fs_ops; +struct f2fs_fault_info { + atomic_t inject_ops; + unsigned int inject_rate; + unsigned int inject_type; +}; + +extern struct f2fs_fault_info f2fs_fault; extern char *fault_name[FAULT_MAX]; +#define IS_FAULT_SET(type) (f2fs_fault.inject_type & (1 << (type))) static inline bool time_to_inject(int type) { - atomic_inc(&f2fs_ops); - if (f2fs_fault_rate && (atomic_read(&f2fs_ops) >= f2fs_fault_rate)) { - atomic_set(&f2fs_ops, 0); + if (!f2fs_fault.inject_rate) + return false; + if (type == FAULT_KMALLOC && !IS_FAULT_SET(type)) + return false; + else if (type == FAULT_PAGE_ALLOC && !IS_FAULT_SET(type)) + return false; + else if (type == FAULT_ALLOC_NID && !IS_FAULT_SET(type)) + return false; + else if (type == FAULT_ORPHAN && !IS_FAULT_SET(type)) + return false; + else if (type == FAULT_BLOCK && !IS_FAULT_SET(type)) + return false; + else if (type == FAULT_DIR_DEPTH && !IS_FAULT_SET(type)) + return false; + + atomic_inc(&f2fs_fault.inject_ops); + if (atomic_read(&f2fs_fault.inject_ops) >= f2fs_fault.inject_rate) { + atomic_set(&f2fs_fault.inject_ops, 0); printk("%sF2FS-fs : inject %s in %pF\n", KERN_INFO, fault_name[type], diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 6c5e42703e75..208e5a4ab18f 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -40,8 +40,7 @@ static struct kmem_cache *f2fs_inode_cachep; static struct kset *f2fs_kset; #ifdef CONFIG_F2FS_FAULT_INJECTION -u32 f2fs_fault_rate = 0; -atomic_t f2fs_ops; +struct f2fs_fault_info f2fs_fault; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", @@ -51,6 +50,17 @@ char *fault_name[FAULT_MAX] = { [FAULT_BLOCK] = "no more block", [FAULT_DIR_DEPTH] = "too big dir depth", }; + +static void f2fs_build_fault_attr(unsigned int rate) +{ + if (rate) { + atomic_set(&f2fs_fault.inject_ops, 0); + f2fs_fault.inject_rate = rate; + f2fs_fault.inject_type = (1 << FAULT_MAX) - 1; + } else { + memset(&f2fs_fault, 0, sizeof(struct f2fs_fault_info)); + } +} #endif /* f2fs-wide shrinker description */ @@ -118,6 +128,10 @@ enum { SM_INFO, /* struct f2fs_sm_info */ NM_INFO, /* struct f2fs_nm_info */ F2FS_SBI, /* struct f2fs_sb_info */ +#ifdef CONFIG_F2FS_FAULT_INJECTION + FAULT_INFO_RATE, /* struct f2fs_fault_info */ + FAULT_INFO_TYPE, /* struct f2fs_fault_info */ +#endif }; struct f2fs_attr { @@ -139,6 +153,11 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) return (unsigned char *)NM_I(sbi); else if (struct_type == F2FS_SBI) return (unsigned char *)sbi; +#ifdef CONFIG_F2FS_FAULT_INJECTION + else if (struct_type == FAULT_INFO_RATE || + struct_type == FAULT_INFO_TYPE) + return (unsigned char *)&f2fs_fault; +#endif return NULL; } @@ -188,6 +207,10 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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 *ui = t; return count; } @@ -253,6 +276,10 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); +#ifdef CONFIG_F2FS_FAULT_INJECTION +F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); +F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); +#endif F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) @@ -289,6 +316,22 @@ static struct kobj_type f2fs_ktype = { .release = f2fs_sb_release, }; +#ifdef CONFIG_F2FS_FAULT_INJECTION +/* sysfs for f2fs fault injection */ +static struct kobject f2fs_fault_inject; + +static struct attribute *f2fs_fault_attrs[] = { + ATTR_LIST(inject_rate), + ATTR_LIST(inject_type), + NULL +}; + +static struct kobj_type f2fs_fault_ktype = { + .default_attrs = f2fs_fault_attrs, + .sysfs_ops = &f2fs_attr_ops, +}; +#endif + void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) { struct va_format vaf; @@ -317,8 +360,9 @@ static int parse_options(struct super_block *sb, char *options) int arg = 0; #ifdef CONFIG_F2FS_FAULT_INJECTION - f2fs_fault_rate = 0; + f2fs_build_fault_attr(0); #endif + if (!options) return 0; @@ -456,8 +500,7 @@ static int parse_options(struct super_block *sb, char *options) if (args->from && match_int(args, &arg)) return -EINVAL; #ifdef CONFIG_F2FS_FAULT_INJECTION - f2fs_fault_rate = arg; - atomic_set(&f2fs_ops, 0); + f2fs_build_fault_attr(arg); #else f2fs_msg(sb, KERN_INFO, "FAULT_INJECTION was not selected"); @@ -1796,6 +1839,16 @@ static int __init init_f2fs_fs(void) err = -ENOMEM; goto free_extent_cache; } +#ifdef CONFIG_F2FS_FAULT_INJECTION + f2fs_fault_inject.kset = f2fs_kset; + f2fs_build_fault_attr(0); + err = kobject_init_and_add(&f2fs_fault_inject, &f2fs_fault_ktype, + NULL, "fault_injection"); + if (err) { + f2fs_fault_inject.kset = NULL; + goto free_kset; + } +#endif err = register_shrinker(&f2fs_shrinker_info); if (err) goto free_kset; @@ -1814,6 +1867,10 @@ free_filesystem: free_shrinker: unregister_shrinker(&f2fs_shrinker_info); free_kset: +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (f2fs_fault_inject.kset) + kobject_put(&f2fs_fault_inject); +#endif kset_unregister(f2fs_kset); free_extent_cache: destroy_extent_cache(); @@ -1840,6 +1897,9 @@ static void __exit exit_f2fs_fs(void) destroy_segment_manager_caches(); destroy_node_manager_caches(); destroy_inodecache(); +#ifdef CONFIG_F2FS_FAULT_INJECTION + kobject_put(&f2fs_fault_inject); +#endif kset_unregister(f2fs_kset); f2fs_destroy_trace_ios(); } -- GitLab From 18ed0549dcb52b9d3ed5e6feef9494bf9f632b20 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 16 May 2016 10:33:40 -0700 Subject: [PATCH 0239/5498] f2fs: manipulate dirty file inodes when DATA_FLUSH is set It needs to maintain dirty file inodes only if DATA_FLUSH is set. Otherwise, let's avoid its overhead. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index b413b4692063..55c6eff369be 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -786,9 +786,11 @@ void update_dirty_page(struct inode *inode, struct page *page) !S_ISLNK(inode->i_mode)) return; - spin_lock(&sbi->inode_lock[type]); - __add_dirty_inode(inode, type); - spin_unlock(&sbi->inode_lock[type]); + if (type != FILE_INODE || test_opt(sbi, DATA_FLUSH)) { + spin_lock(&sbi->inode_lock[type]); + __add_dirty_inode(inode, type); + spin_unlock(&sbi->inode_lock[type]); + } inode_inc_dirty_pages(inode); SetPagePrivate(page); @@ -804,6 +806,9 @@ void remove_dirty_inode(struct inode *inode) !S_ISLNK(inode->i_mode)) return; + if (type == FILE_INODE && !test_opt(sbi, DATA_FLUSH)) + return; + spin_lock(&sbi->inode_lock[type]); __remove_dirty_inode(inode, type); spin_unlock(&sbi->inode_lock[type]); -- GitLab From b4fba71e9d6f55fdda5aee8f7528d8d7ffdfabe4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 17 May 2016 16:23:36 -0700 Subject: [PATCH 0240/5498] f2fs: use bio count instead of F2FS_WRITEBACK page count This can reduce page counting overhead. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 26 +++++++++++++++----------- fs/f2fs/debug.c | 6 +++--- fs/f2fs/f2fs.h | 4 ++-- fs/f2fs/super.c | 2 +- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 55c6eff369be..28ec9864819d 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -915,7 +915,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi) for (;;) { prepare_to_wait(&sbi->cp_wait, &wait, TASK_UNINTERRUPTIBLE); - if (!get_pages(sbi, F2FS_WRITEBACK)) + if (!atomic_read(&sbi->nr_wb_bios)) break; io_schedule_timeout(5*HZ); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 82803e7aaacf..4d6f2cad57a5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -72,10 +72,9 @@ static void f2fs_write_end_io(struct bio *bio, int err) f2fs_stop_checkpoint(sbi); } end_page_writeback(page); - dec_page_count(sbi, F2FS_WRITEBACK); } - - if (!get_pages(sbi, F2FS_WRITEBACK) && wq_has_sleeper(&sbi->cp_wait)) + if (atomic_dec_and_test(&sbi->nr_wb_bios) && + wq_has_sleeper(&sbi->cp_wait)) wake_up(&sbi->cp_wait); bio_put(bio); @@ -99,6 +98,14 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, return bio; } +static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, + struct bio *bio) +{ + if (!is_read_io(rw)) + atomic_inc(&sbi->nr_wb_bios); + submit_bio(rw, bio); +} + static void __submit_merged_bio(struct f2fs_bio_info *io) { struct f2fs_io_info *fio = &io->fio; @@ -111,7 +118,7 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) else trace_f2fs_submit_write_bio(io->sbi->sb, fio, io->bio); - submit_bio(fio->rw, io->bio); + __submit_bio(io->sbi, fio->rw, io->bio); io->bio = NULL; } @@ -229,7 +236,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) return -EFAULT; } - submit_bio(fio->rw, bio); + __submit_bio(fio->sbi, fio->rw, bio); return 0; } @@ -249,9 +256,6 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) down_write(&io->io_rwsem); - if (!is_read) - inc_page_count(sbi, F2FS_WRITEBACK); - if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 || io->fio.rw != fio->rw)) __submit_merged_bio(io); @@ -1048,7 +1052,7 @@ got_it: */ if (bio && (last_block_in_bio != block_nr - 1)) { submit_and_realloc: - submit_bio(READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio); bio = NULL; } if (bio == NULL) { @@ -1091,7 +1095,7 @@ set_error_page: goto next_page; confused: if (bio) { - submit_bio(READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio); bio = NULL; } unlock_page(page); @@ -1101,7 +1105,7 @@ next_page: } BUG_ON(pages && !list_empty(pages)); if (bio) - submit_bio(READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio); return 0; } diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index f2da6209cf16..2de03b220313 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -48,7 +48,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); - si->wb_pages = get_pages(sbi, F2FS_WRITEBACK); + si->wb_bios = atomic_read(&sbi->nr_wb_bios); si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->rsvd_segs = reserved_segments(sbi); si->overp_segs = overprovision_segments(sbi); @@ -301,8 +301,8 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - inmem: %4d, wb: %4d\n", - si->inmem_pages, si->wb_pages); + seq_printf(s, " - inmem: %4d, wb_bios: %4d\n", + si->inmem_pages, si->wb_bios); seq_printf(s, " - nodes: %4d in %4d\n", si->ndirty_node, si->node_pages); seq_printf(s, " - dents: %4d in dirs:%4d\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index afc230e6eb57..e43fd5ec8caa 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -681,7 +681,6 @@ struct f2fs_sm_info { * dirty dentry blocks, dirty node blocks, and dirty meta blocks. */ enum count_type { - F2FS_WRITEBACK, F2FS_DIRTY_DENTS, F2FS_DIRTY_DATA, F2FS_DIRTY_NODES, @@ -844,6 +843,7 @@ struct f2fs_sb_info { block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ u32 s_next_generation; /* for NFS support */ + atomic_t nr_wb_bios; /* # of writeback bios */ atomic_t nr_pages[NR_COUNT_TYPE]; /* # of pages, see count_type */ struct f2fs_mount_info mount_opt; /* mount options */ @@ -2050,7 +2050,7 @@ struct f2fs_stat_info { int ndirty_dent, ndirty_dirs, ndirty_data, ndirty_files; int nats, dirty_nats, sits, dirty_sits, fnids; int total_count, utilization; - int bg_gc, inmem_pages, wb_pages; + int bg_gc, inmem_pages, wb_bios; int inline_xattr, inline_inode, inline_dir, orphans; unsigned int valid_count, valid_node_count, valid_inode_count; unsigned int bimodal, avg_vblocks; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 208e5a4ab18f..93aee93923e3 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -649,7 +649,7 @@ static void f2fs_put_super(struct super_block *sb) mutex_unlock(&sbi->umount_mutex); /* our cp_error case, we can wait for any writeback page */ - if (get_pages(sbi, F2FS_WRITEBACK)) + if (atomic_read(&sbi->nr_wb_bios)) f2fs_flush_merged_bios(sbi); iput(sbi->node_inode); -- GitLab From 78a3b33d031044091ead14076f725a6d074aac56 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 13 May 2016 12:36:58 -0700 Subject: [PATCH 0241/5498] f2fs: use percpu_counter for page counters This patch substitutes percpu_counter for atomic_counter when counting various types of pages. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 11 ++++++----- fs/f2fs/f2fs.h | 24 ++++++++++++++---------- fs/f2fs/super.c | 31 +++++++++++++++++++++++++++---- include/trace/events/f2fs.h | 10 +++++----- 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 2de03b220313..39cb2bc55f5a 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -144,6 +144,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem = sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize; si->base_mem += 2 * sizeof(struct f2fs_inode_info); si->base_mem += sizeof(*sbi->ckpt); + si->base_mem += sizeof(struct percpu_counter) * NR_COUNT_TYPE; /* build sm */ si->base_mem += sizeof(struct f2fs_sm_info); @@ -301,15 +302,15 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - inmem: %4d, wb_bios: %4d\n", + seq_printf(s, " - inmem: %4lld, wb_bios: %4d\n", si->inmem_pages, si->wb_bios); - seq_printf(s, " - nodes: %4d in %4d\n", + seq_printf(s, " - nodes: %4lld in %4d\n", si->ndirty_node, si->node_pages); - seq_printf(s, " - dents: %4d in dirs:%4d\n", + seq_printf(s, " - dents: %4lld in dirs:%4d\n", si->ndirty_dent, si->ndirty_dirs); - seq_printf(s, " - datas: %4d in files:%4d\n", + seq_printf(s, " - datas: %4lld in files:%4d\n", si->ndirty_data, si->ndirty_files); - seq_printf(s, " - meta: %4d in %4d\n", + seq_printf(s, " - meta: %4lld in %4d\n", si->ndirty_meta, si->meta_pages); seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", si->dirty_nats, si->nats, si->dirty_sits, si->sits); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e43fd5ec8caa..8aa9ee4405db 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -844,7 +844,9 @@ struct f2fs_sb_info { block_t last_valid_block_count; /* for recovery */ u32 s_next_generation; /* for NFS support */ atomic_t nr_wb_bios; /* # of writeback bios */ - atomic_t nr_pages[NR_COUNT_TYPE]; /* # of pages, see count_type */ + + /* # of pages, see count_type */ + struct percpu_counter nr_pages[NR_COUNT_TYPE]; struct f2fs_mount_info mount_opt; /* mount options */ @@ -1189,7 +1191,7 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) { - atomic_inc(&sbi->nr_pages[count_type]); + percpu_counter_inc(&sbi->nr_pages[count_type]); set_sbi_flag(sbi, SBI_IS_DIRTY); } @@ -1202,7 +1204,7 @@ static inline void inode_inc_dirty_pages(struct inode *inode) static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) { - atomic_dec(&sbi->nr_pages[count_type]); + percpu_counter_dec(&sbi->nr_pages[count_type]); } static inline void inode_dec_dirty_pages(struct inode *inode) @@ -1216,9 +1218,9 @@ static inline void inode_dec_dirty_pages(struct inode *inode) F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); } -static inline int get_pages(struct f2fs_sb_info *sbi, int count_type) +static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type) { - return atomic_read(&sbi->nr_pages[count_type]); + return percpu_counter_sum_positive(&sbi->nr_pages[count_type]); } static inline int get_dirty_pages(struct inode *inode) @@ -1229,8 +1231,10 @@ static inline int get_dirty_pages(struct inode *inode) static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type) { unsigned int pages_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg; - return ((get_pages(sbi, block_type) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; + unsigned int segs = (get_pages(sbi, block_type) + pages_per_sec - 1) >> + sbi->log_blocks_per_seg; + + return segs / sbi->segs_per_sec; } static inline block_t valid_user_blocks(struct f2fs_sb_info *sbi) @@ -2046,11 +2050,11 @@ struct f2fs_stat_info { unsigned long long hit_largest, hit_cached, hit_rbtree; unsigned long long hit_total, total_ext; int ext_tree, zombie_tree, ext_node; - int ndirty_node, ndirty_meta; - int ndirty_dent, ndirty_dirs, ndirty_data, ndirty_files; + s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, inmem_pages; + unsigned int ndirty_dirs, ndirty_files; int nats, dirty_nats, sits, dirty_sits, fnids; int total_count, utilization; - int bg_gc, inmem_pages, wb_bios; + int bg_gc, wb_bios; int inline_xattr, inline_inode, inline_dir, orphans; unsigned int valid_count, valid_node_count, valid_inode_count; unsigned int bimodal, avg_vblocks; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 93aee93923e3..a6273d4b2a9a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -606,6 +606,14 @@ static void f2fs_destroy_inode(struct inode *inode) call_rcu(&inode->i_rcu, f2fs_i_callback); } +static void destroy_percpu_info(struct f2fs_sb_info *sbi) +{ + int i; + + for (i = 0; i < NR_COUNT_TYPE; i++) + percpu_counter_destroy(&sbi->nr_pages[i]); +} + static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -667,6 +675,8 @@ static void f2fs_put_super(struct super_block *sb) if (sbi->s_chksum_driver) crypto_free_shash(sbi->s_chksum_driver); kfree(sbi->raw_super); + + destroy_percpu_info(sbi); kfree(sbi); } @@ -1325,7 +1335,6 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) static void init_sb_info(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = sbi->raw_super; - int i; sbi->log_sectors_per_block = le32_to_cpu(raw_super->log_sectors_per_block); @@ -1345,9 +1354,6 @@ static void init_sb_info(struct f2fs_sb_info *sbi) sbi->cur_victim_sec = NULL_SECNO; sbi->max_victim_search = DEF_MAX_VICTIM_SEARCH; - for (i = 0; i < NR_COUNT_TYPE; i++) - atomic_set(&sbi->nr_pages[i], 0); - sbi->dir_level = DEF_DIR_LEVEL; sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL; sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL; @@ -1363,6 +1369,18 @@ static void init_sb_info(struct f2fs_sb_info *sbi) #endif } +static int init_percpu_info(struct f2fs_sb_info *sbi) +{ + int i, err; + + for (i = 0; i < NR_COUNT_TYPE; i++) { + err = percpu_counter_init(&sbi->nr_pages[i], 0, GFP_KERNEL); + if (err) + return err; + } + return 0; +} + /* * Read f2fs raw super block. * Because we have two copies of super block, so read both of them @@ -1553,6 +1571,10 @@ try_onemore: init_waitqueue_head(&sbi->cp_wait); init_sb_info(sbi); + err = init_percpu_info(sbi); + if (err) + goto free_options; + /* get an inode for meta space */ sbi->meta_inode = f2fs_iget(sb, F2FS_META_INO(sbi)); if (IS_ERR(sbi->meta_inode)) { @@ -1755,6 +1777,7 @@ free_meta_inode: make_bad_inode(sbi->meta_inode); iput(sbi->meta_inode); free_options: + destroy_percpu_info(sbi); kfree(options); free_sb_buf: kfree(raw_super); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 7e2ed7aa37af..047a5d2f77ee 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1240,14 +1240,14 @@ TRACE_EVENT(f2fs_destroy_extent_tree, DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes, - TP_PROTO(struct super_block *sb, int type, int count), + TP_PROTO(struct super_block *sb, int type, s64 count), TP_ARGS(sb, type, count), TP_STRUCT__entry( __field(dev_t, dev) __field(int, type) - __field(int, count) + __field(s64, count) ), TP_fast_assign( @@ -1256,7 +1256,7 @@ DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes, __entry->count = count; ), - TP_printk("dev = (%d,%d), %s, dirty count = %d", + TP_printk("dev = (%d,%d), %s, dirty count = %lld", show_dev(__entry), show_file_type(__entry->type), __entry->count) @@ -1264,14 +1264,14 @@ DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes, DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_enter, - TP_PROTO(struct super_block *sb, int type, int count), + TP_PROTO(struct super_block *sb, int type, s64 count), TP_ARGS(sb, type, count) ); DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_exit, - TP_PROTO(struct super_block *sb, int type, int count), + TP_PROTO(struct super_block *sb, int type, s64 count), TP_ARGS(sb, type, count) ); -- GitLab From e4ad9aec3d36a293244f1c6cf71d88f17706006f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 13 May 2016 12:47:11 -0700 Subject: [PATCH 0242/5498] f2fs: use percpu_counter for # of dirty pages in inode This patch adds percpu_counter for # of dirty pages in inode. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 10 +++++----- fs/f2fs/file.c | 2 +- fs/f2fs/super.c | 7 ++++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8aa9ee4405db..ac1eeeede8a0 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -467,7 +467,7 @@ struct f2fs_inode_info { /* Use below internally in f2fs*/ unsigned long flags; /* use to pass per-file flags */ struct rw_semaphore i_sem; /* protect fi info */ - atomic_t dirty_pages; /* # of dirty pages */ + struct percpu_counter dirty_pages; /* # of dirty pages */ f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ nid_t i_xattr_nid; /* node id that contains xattrs */ @@ -1197,7 +1197,7 @@ static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) static inline void inode_inc_dirty_pages(struct inode *inode) { - atomic_inc(&F2FS_I(inode)->dirty_pages); + percpu_counter_inc(&F2FS_I(inode)->dirty_pages); inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); } @@ -1213,7 +1213,7 @@ static inline void inode_dec_dirty_pages(struct inode *inode) !S_ISLNK(inode->i_mode)) return; - atomic_dec(&F2FS_I(inode)->dirty_pages); + percpu_counter_dec(&F2FS_I(inode)->dirty_pages); dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); } @@ -1223,9 +1223,9 @@ static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type) return percpu_counter_sum_positive(&sbi->nr_pages[count_type]); } -static inline int get_dirty_pages(struct inode *inode) +static inline s64 get_dirty_pages(struct inode *inode) { - return atomic_read(&F2FS_I(inode)->dirty_pages); + return percpu_counter_sum_positive(&F2FS_I(inode)->dirty_pages); } static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2b946983b705..393c9f7d48f9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1422,7 +1422,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) goto out; f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, - "Unexpected flush for atomic writes: ino=%lu, npages=%u", + "Unexpected flush for atomic writes: ino=%lu, npages=%lld", inode->i_ino, get_dirty_pages(inode)); ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); if (ret) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a6273d4b2a9a..b4d99bede448 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -526,9 +526,13 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_once((void *) fi); + if (percpu_counter_init(&fi->dirty_pages, 0, GFP_NOFS)) { + kmem_cache_free(f2fs_inode_cachep, fi); + return NULL; + } + /* Initialize f2fs-specific inode info */ fi->vfs_inode.i_version = 1; - atomic_set(&fi->dirty_pages, 0); fi->i_current_depth = 1; fi->i_advise = 0; init_rwsem(&fi->i_sem); @@ -603,6 +607,7 @@ static void f2fs_i_callback(struct rcu_head *head) static void f2fs_destroy_inode(struct inode *inode) { + percpu_counter_destroy(&F2FS_I(inode)->dirty_pages); call_rcu(&inode->i_rcu, f2fs_i_callback); } -- GitLab From 743c97ae54e381d62a779523439ee2eb4ece056e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 16 May 2016 11:06:50 -0700 Subject: [PATCH 0243/5498] f2fs: use percpu_counter for alloc_valid_block_count This patch uses percpu_count for sbi->alloc_valid_block_count. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/f2fs.h | 8 +++++--- fs/f2fs/recovery.c | 5 +++-- fs/f2fs/super.c | 7 +++++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 28ec9864819d..e4fe7f191bd2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1080,7 +1080,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* update user_block_counts */ sbi->last_valid_block_count = sbi->total_valid_block_count; - sbi->alloc_valid_block_count = 0; + percpu_counter_set(&sbi->alloc_valid_block_count, 0); /* Here, we only have one bio having CP pack */ sync_meta_pages(sbi, META_FLUSH, LONG_MAX); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ac1eeeede8a0..82e094594ada 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -839,7 +839,6 @@ struct f2fs_sb_info { block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ - block_t alloc_valid_block_count; /* # of allocated blocks */ block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ u32 s_next_generation; /* for NFS support */ @@ -847,6 +846,8 @@ struct f2fs_sb_info { /* # of pages, see count_type */ struct percpu_counter nr_pages[NR_COUNT_TYPE]; + /* # of allocated blocks */ + struct percpu_counter alloc_valid_block_count; struct f2fs_mount_info mount_opt; /* mount options */ @@ -1172,8 +1173,9 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, inode->i_blocks += *count; sbi->total_valid_block_count = sbi->total_valid_block_count + (block_t)(*count); - sbi->alloc_valid_block_count += (block_t)(*count); spin_unlock(&sbi->stat_lock); + + percpu_counter_add(&sbi->alloc_valid_block_count, (*count)); return true; } @@ -1323,11 +1325,11 @@ static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, if (inode) inode->i_blocks++; - sbi->alloc_valid_block_count++; sbi->total_valid_node_count++; sbi->total_valid_block_count++; spin_unlock(&sbi->stat_lock); + percpu_counter_inc(&sbi->alloc_valid_block_count); return true; } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index bd444cd9ec0c..25586bf49485 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -49,8 +49,9 @@ static struct kmem_cache *fsync_entry_slab; bool space_for_roll_forward(struct f2fs_sb_info *sbi) { - if (sbi->last_valid_block_count + sbi->alloc_valid_block_count - > sbi->user_block_count) + s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count); + + if (sbi->last_valid_block_count + nalloc > sbi->user_block_count) return false; return true; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b4d99bede448..b602648337dc 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -617,6 +617,7 @@ static void destroy_percpu_info(struct f2fs_sb_info *sbi) for (i = 0; i < NR_COUNT_TYPE; i++) percpu_counter_destroy(&sbi->nr_pages[i]); + percpu_counter_destroy(&sbi->alloc_valid_block_count); } static void f2fs_put_super(struct super_block *sb) @@ -1383,7 +1384,9 @@ static int init_percpu_info(struct f2fs_sb_info *sbi) if (err) return err; } - return 0; + + return percpu_counter_init(&sbi->alloc_valid_block_count, 0, + GFP_KERNEL); } /* @@ -1602,7 +1605,7 @@ try_onemore: sbi->total_valid_block_count = le64_to_cpu(sbi->ckpt->valid_block_count); sbi->last_valid_block_count = sbi->total_valid_block_count; - sbi->alloc_valid_block_count = 0; + for (i = 0; i < NR_INODE_TYPE; i++) { INIT_LIST_HEAD(&sbi->inode_list[i]); spin_lock_init(&sbi->inode_lock[i]); -- GitLab From de94f885dfba99f12d89440187a3845e854674f3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 16 May 2016 11:42:32 -0700 Subject: [PATCH 0244/5498] f2fs: use percpu_counter for total_valid_inode_count This patch uses percpu_counter to avoid stat_lock. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 18 +++++++----------- fs/f2fs/super.c | 11 ++++++++--- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 82e094594ada..dbd06092a312 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -832,7 +832,6 @@ struct f2fs_sb_info { unsigned int total_sections; /* total section count */ unsigned int total_node_count; /* total node block count */ unsigned int total_valid_node_count; /* valid node block count */ - unsigned int total_valid_inode_count; /* valid inode count */ loff_t max_file_blocks; /* max block index of file */ int active_logs; /* # of active logs */ int dir_level; /* directory level */ @@ -849,6 +848,9 @@ struct f2fs_sb_info { /* # of allocated blocks */ struct percpu_counter alloc_valid_block_count; + /* valid inode count */ + struct percpu_counter total_valid_inode_count; + struct f2fs_mount_info mount_opt; /* mount options */ /* for cleaning operations */ @@ -1356,23 +1358,17 @@ static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi) static inline void inc_valid_inode_count(struct f2fs_sb_info *sbi) { - spin_lock(&sbi->stat_lock); - f2fs_bug_on(sbi, sbi->total_valid_inode_count == sbi->total_node_count); - sbi->total_valid_inode_count++; - spin_unlock(&sbi->stat_lock); + percpu_counter_inc(&sbi->total_valid_inode_count); } static inline void dec_valid_inode_count(struct f2fs_sb_info *sbi) { - spin_lock(&sbi->stat_lock); - f2fs_bug_on(sbi, !sbi->total_valid_inode_count); - sbi->total_valid_inode_count--; - spin_unlock(&sbi->stat_lock); + percpu_counter_dec(&sbi->total_valid_inode_count); } -static inline unsigned int valid_inode_count(struct f2fs_sb_info *sbi) +static inline s64 valid_inode_count(struct f2fs_sb_info *sbi) { - return sbi->total_valid_inode_count; + return percpu_counter_sum_positive(&sbi->total_valid_inode_count); } static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b602648337dc..380c1ef9c0ed 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -618,6 +618,7 @@ static void destroy_percpu_info(struct f2fs_sb_info *sbi) for (i = 0; i < NR_COUNT_TYPE; i++) percpu_counter_destroy(&sbi->nr_pages[i]); percpu_counter_destroy(&sbi->alloc_valid_block_count); + percpu_counter_destroy(&sbi->total_valid_inode_count); } static void f2fs_put_super(struct super_block *sb) @@ -1385,7 +1386,11 @@ static int init_percpu_info(struct f2fs_sb_info *sbi) return err; } - return percpu_counter_init(&sbi->alloc_valid_block_count, 0, + err = percpu_counter_init(&sbi->alloc_valid_block_count, 0, GFP_KERNEL); + if (err) + return err; + + return percpu_counter_init(&sbi->total_valid_inode_count, 0, GFP_KERNEL); } @@ -1599,8 +1604,8 @@ try_onemore: sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count); - sbi->total_valid_inode_count = - le32_to_cpu(sbi->ckpt->valid_inode_count); + percpu_counter_set(&sbi->total_valid_inode_count, + le32_to_cpu(sbi->ckpt->valid_inode_count)); sbi->user_block_count = le64_to_cpu(sbi->ckpt->user_block_count); sbi->total_valid_block_count = le64_to_cpu(sbi->ckpt->valid_block_count); -- GitLab From 0c6d0a70c722546abef6fa54d7d69b6ed88d344a Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Wed, 18 May 2016 08:02:25 +0800 Subject: [PATCH 0245/5498] f2fs: make exit_f2fs_fs more clear init_f2fs_fs does: 1) f2fs_build_trace_ios 2) init_inodecache 3) create_node_manager_caches 4) create_segment_manager_caches 5) create_checkpoint_caches 6) create_extent_cache 7) kset_create_and_add 8) kobject_init_and_add 9) register_shrinker 10) register_filesystem 11) f2fs_create_root_stats 12) proc_mkdir exit_f2fs_fs should do cleanup in the reverse order to make the code more clear. Signed-off-by: Tiezhu Yang Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 380c1ef9c0ed..5263b4aea769 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1926,17 +1926,17 @@ static void __exit exit_f2fs_fs(void) { remove_proc_entry("fs/f2fs", NULL); f2fs_destroy_root_stats(); - unregister_shrinker(&f2fs_shrinker_info); unregister_filesystem(&f2fs_fs_type); + unregister_shrinker(&f2fs_shrinker_info); +#ifdef CONFIG_F2FS_FAULT_INJECTION + kobject_put(&f2fs_fault_inject); +#endif + kset_unregister(f2fs_kset); destroy_extent_cache(); destroy_checkpoint_caches(); destroy_segment_manager_caches(); destroy_node_manager_caches(); destroy_inodecache(); -#ifdef CONFIG_F2FS_FAULT_INJECTION - kobject_put(&f2fs_fault_inject); -#endif - kset_unregister(f2fs_kset); f2fs_destroy_trace_ios(); } -- GitLab From a3c3aec6e3fb0395d1780d79d6fe80052c492cc3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 19 May 2016 11:57:21 -0700 Subject: [PATCH 0246/5498] f2fs: avoid ENOSPC fault in the recovery process This patch avoids impossible error injection, ENOSPC, during recovery process. Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 25586bf49485..b485d02b8a8f 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -470,6 +470,10 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (src == NULL_ADDR) { err = reserve_new_block(&dn); +#ifdef CONFIG_F2FS_FAULT_INJECTION + while (err) + err = reserve_new_block(&dn); +#endif /* We should not get -ENOSPC */ f2fs_bug_on(sbi, err); } -- GitLab From 80f501338aa6df1f34b73c458f10c6fcfd1bd588 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 18 May 2016 14:07:56 -0700 Subject: [PATCH 0247/5498] f2fs: flush pending bios right away when error occurs Given errors, this patch flushes pending bios as soon as possible. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/file.c Conflicts: fs/f2fs/file.c --- fs/f2fs/checkpoint.c | 10 +++++++++- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 7 +------ fs/f2fs/file.c | 8 ++++---- fs/f2fs/inode.c | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index e4fe7f191bd2..934b94d8e4f0 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -26,6 +26,14 @@ static struct kmem_cache *ino_entry_slab; struct kmem_cache *inode_entry_slab; +void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io) +{ + set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); + sbi->sb->s_flags |= MS_RDONLY; + if (!end_io) + f2fs_flush_merged_bios(sbi); +} + /* * We guarantee no failure on the returned page. */ @@ -91,7 +99,7 @@ repeat: * meta page. */ if (unlikely(!PageUptodate(page))) - f2fs_stop_checkpoint(sbi); + f2fs_stop_checkpoint(sbi, false); out: mark_page_accessed(page); return page; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4d6f2cad57a5..469ec9f4bcb5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -69,7 +69,7 @@ static void f2fs_write_end_io(struct bio *bio, int err) if (unlikely(err)) { set_bit(AS_EIO, &page->mapping->flags); - f2fs_stop_checkpoint(sbi); + f2fs_stop_checkpoint(sbi, true); } end_page_writeback(page); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dbd06092a312..36cb3ceba87a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1729,12 +1729,6 @@ static inline bool f2fs_cp_error(struct f2fs_sb_info *sbi) return is_set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); } -static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi) -{ - set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); - sbi->sb->s_flags |= MS_RDONLY; -} - static inline bool is_dot_dotdot(const struct qstr *str) { if (str->len == 1 && str->name[0] == '.') @@ -1970,6 +1964,7 @@ void destroy_segment_manager_caches(void); /* * checkpoint.c */ +void f2fs_stop_checkpoint(struct f2fs_sb_info *, bool); struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); struct page *get_tmp_page(struct f2fs_sb_info *, pgoff_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 393c9f7d48f9..eeae40259fda 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1574,21 +1574,21 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) case FS_GOING_DOWN_FULLSYNC: sb = freeze_bdev(sb->s_bdev); if (sb && !IS_ERR(sb)) { - f2fs_stop_checkpoint(sbi); + f2fs_stop_checkpoint(sbi, false); thaw_bdev(sb->s_bdev, sb); } break; case FS_GOING_DOWN_METASYNC: /* do checkpoint only */ f2fs_sync_fs(sb, 1); - f2fs_stop_checkpoint(sbi); + f2fs_stop_checkpoint(sbi, false); break; case FS_GOING_DOWN_NOSYNC: - f2fs_stop_checkpoint(sbi); + f2fs_stop_checkpoint(sbi, false); break; case FS_GOING_DOWN_METAFLUSH: sync_meta_pages(sbi, META, LONG_MAX); - f2fs_stop_checkpoint(sbi); + f2fs_stop_checkpoint(sbi, false); break; default: ret = -EINVAL; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index d2ddc0adbf2d..0217b94f971e 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -282,7 +282,7 @@ retry: cond_resched(); goto retry; } else if (err != -ENOENT) { - f2fs_stop_checkpoint(sbi); + f2fs_stop_checkpoint(sbi, false); } return 0; } -- GitLab From e4a400086a0e83ea979af23c614fa72a7252803a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 21 May 2016 00:11:09 +0800 Subject: [PATCH 0248/5498] f2fs: fix to update dirty page count correctly Once we failed to merge inline data into inode page during flushing inline inode, we will skip invoking inode_dec_dirty_pages, which makes dirty page count incorrect, result in panic in ->evict_inode, Fix it. ------------[ cut here ]------------ kernel BUG at /home/yuchao/git/devf2fs/inode.c:336! invalid opcode: 0000 [#1] PREEMPT SMP CPU: 3 PID: 10004 Comm: umount Tainted: G O 4.6.0-rc5+ #17 Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 task: f0c33000 ti: c5212000 task.ti: c5212000 EIP: 0060:[] EFLAGS: 00010202 CPU: 3 EIP is at f2fs_evict_inode+0x85/0x490 [f2fs] EAX: 00000001 EBX: c4529ea0 ECX: 00000001 EDX: 00000000 ESI: c0131000 EDI: f89dd0a0 EBP: c5213e9c ESP: c5213e78 DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 CR0: 80050033 CR2: b75878c0 CR3: 1a36a700 CR4: 000406f0 Stack: c4529ea0 c4529ef4 c5213e8c c176d45c c4529ef4 00000000 c4529ea0 c4529fac f89dd0a0 c5213eb0 c1204a68 c5213ed8 c452a2b4 c6680930 c5213ec0 c1204b64 c6680d44 c6680620 c5213eec c120588d ee84b000 ee84b5c0 c5214000 ee84b5e0 Call Trace: [] ? _raw_spin_unlock+0x2c/0x50 [] evict+0xa8/0x170 [] dispose_list+0x34/0x50 [] evict_inodes+0x10d/0x130 [] generic_shutdown_super+0x41/0xe0 [] ? unregister_shrinker+0x40/0x50 [] ? unregister_shrinker+0x40/0x50 [] kill_block_super+0x22/0x70 [] kill_f2fs_super+0x1e/0x20 [f2fs] [] deactivate_locked_super+0x3d/0x70 [] deactivate_super+0x43/0x60 [] cleanup_mnt+0x39/0x80 [] __cleanup_mnt+0x10/0x20 [] task_work_run+0x71/0x90 [] exit_to_usermode_loop+0x72/0x9e [] do_fast_syscall_32+0x19c/0x1c0 [] sysenter_past_esp+0x45/0x74 EIP: [] f2fs_evict_inode+0x85/0x490 [f2fs] SS:ESP 0068:c5213e78 ---[ end trace d30536330b7fdc58 ]--- Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 3afe0a726368..8660ebd189a6 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1202,6 +1202,7 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) { struct inode *inode; struct page *page; + int ret; /* should flush inline_data before evict_inode */ inode = ilookup(sbi->sb, ino); @@ -1224,9 +1225,9 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) if (!clear_page_dirty_for_io(page)) goto page_out; - if (!f2fs_write_inline_data(inode, page)) - inode_dec_dirty_pages(inode); - else + ret = f2fs_write_inline_data(inode, page); + inode_dec_dirty_pages(inode); + if (ret) set_page_dirty(page); page_out: unlock_page(page); -- GitLab From 6cde131921402a45c4e6ff81337d6762aebe8c82 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Jun 2016 16:45:12 -0700 Subject: [PATCH 0249/5498] f2fs: adjust other changes This patch changes: - PAGE_CACHE_* - inode_lock Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 56 ++++++++++++++++---------------- fs/f2fs/debug.c | 6 ++-- fs/f2fs/dir.c | 4 +-- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 80 +++++++++++++++++++++++----------------------- fs/f2fs/inline.c | 6 ++-- fs/f2fs/node.c | 10 +++--- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 16 +++++----- fs/f2fs/super.c | 7 ++-- 10 files changed, 94 insertions(+), 95 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 469ec9f4bcb5..92f7472eff63 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -231,7 +231,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) /* Allocate a new bio */ bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->rw)); - if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { + if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { bio_put(bio); return -EFAULT; } @@ -270,8 +270,8 @@ alloc_new: bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; - if (bio_add_page(io->bio, bio_page, PAGE_CACHE_SIZE, 0) < - PAGE_CACHE_SIZE) { + if (bio_add_page(io->bio, bio_page, PAGE_SIZE, 0) < + PAGE_SIZE) { __submit_merged_bio(io); goto alloc_new; } @@ -440,7 +440,7 @@ got_it: * see, f2fs_add_link -> get_new_data_page -> init_inode_metadata. */ if (dn.data_blkaddr == NEW_ADDR) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); + zero_user_segment(page, 0, PAGE_SIZE); SetPageUptodate(page); unlock_page(page); return page; @@ -551,7 +551,7 @@ struct page *get_new_data_page(struct inode *inode, goto got_it; if (dn.data_blkaddr == NEW_ADDR) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); + zero_user_segment(page, 0, PAGE_SIZE); SetPageUptodate(page); } else { f2fs_put_page(page, 1); @@ -564,8 +564,8 @@ struct page *get_new_data_page(struct inode *inode, } got_it: if (new_i_size && i_size_read(inode) < - ((loff_t)(index + 1) << PAGE_CACHE_SHIFT)) { - i_size_write(inode, ((loff_t)(index + 1) << PAGE_CACHE_SHIFT)); + ((loff_t)(index + 1) << PAGE_SHIFT)) { + i_size_write(inode, ((loff_t)(index + 1) << PAGE_SHIFT)); /* Only the directory inode sets new_i_size */ set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); } @@ -605,9 +605,9 @@ alloc: /* update i_size */ fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) + dn->ofs_in_node; - if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)) + if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_SHIFT)) i_size_write(dn->inode, - ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT)); + ((loff_t)(fofs + 1) << PAGE_SHIFT)); return 0; } @@ -892,7 +892,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, return ret; } - mutex_lock(&inode->i_mutex); + inode_lock(inode); isize = i_size_read(inode); if (start >= isize) @@ -959,7 +959,7 @@ out: if (ret == 1) ret = 0; - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); return ret; } @@ -1040,7 +1040,7 @@ got_it: goto confused; } } else { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); + zero_user_segment(page, 0, PAGE_SIZE); SetPageUptodate(page); unlock_page(page); goto next_page; @@ -1090,7 +1090,7 @@ submit_and_realloc: goto next_page; set_error_page: SetPageError(page); - zero_user_segment(page, 0, PAGE_CACHE_SIZE); + zero_user_segment(page, 0, PAGE_SIZE); unlock_page(page); goto next_page; confused: @@ -1101,7 +1101,7 @@ confused: unlock_page(page); next_page: if (pages) - page_cache_release(page); + put_page(page); } BUG_ON(pages && !list_empty(pages)); if (bio) @@ -1215,7 +1215,7 @@ static int f2fs_write_data_page(struct page *page, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); loff_t i_size = i_size_read(inode); const pgoff_t end_index = ((unsigned long long) i_size) - >> PAGE_CACHE_SHIFT; + >> PAGE_SHIFT; unsigned offset = 0; bool need_balance_fs = false; int err = 0; @@ -1236,11 +1236,11 @@ static int f2fs_write_data_page(struct page *page, * If the offset is out-of-range of file size, * this page does not have to be written to disk. */ - offset = i_size & (PAGE_CACHE_SIZE - 1); + offset = i_size & (PAGE_SIZE - 1); if ((page->index >= end_index + 1) || !offset) goto out; - zero_user_segment(page, offset, PAGE_CACHE_SIZE); + zero_user_segment(page, offset, PAGE_SIZE); write: if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; @@ -1348,8 +1348,8 @@ next: cycled = 0; end = -1; } else { - index = wbc->range_start >> PAGE_CACHE_SHIFT; - end = wbc->range_end >> PAGE_CACHE_SHIFT; + index = wbc->range_start >> PAGE_SHIFT; + end = wbc->range_end >> PAGE_SHIFT; if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) range_whole = 1; cycled = 1; /* ignore range_cyclic tests */ @@ -1529,11 +1529,11 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, * the block addresses when there is no need to fill the page. */ if (!f2fs_has_inline_data(inode) && !f2fs_encrypted_inode(inode) && - len == PAGE_CACHE_SIZE) + len == PAGE_SIZE) return 0; if (f2fs_has_inline_data(inode) || - (pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { + (pos & PAGE_MASK) >= i_size_read(inode)) { f2fs_lock_op(sbi); locked = true; } @@ -1595,7 +1595,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *page = NULL; - pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; + pgoff_t index = ((unsigned long long) pos) >> PAGE_SHIFT; bool need_balance = false; block_t blkaddr = NULL_ADDR; int err = 0; @@ -1643,22 +1643,22 @@ repeat: if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); - if (len == PAGE_CACHE_SIZE) + if (len == PAGE_SIZE) goto out_update; if (PageUptodate(page)) goto out_clear; - if ((pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { - unsigned start = pos & (PAGE_CACHE_SIZE - 1); + if ((pos & PAGE_MASK) >= i_size_read(inode)) { + unsigned start = pos & (PAGE_SIZE - 1); unsigned end = start + len; /* Reading beyond i_size is simple: memset to zero */ - zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); + zero_user_segments(page, 0, start, end, PAGE_SIZE); goto out_update; } if (blkaddr == NEW_ADDR) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); + zero_user_segment(page, 0, PAGE_SIZE); } else { struct f2fs_io_info fio = { .sbi = sbi, @@ -1774,7 +1774,7 @@ void f2fs_invalidate_page(struct page *page, unsigned int offset, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); if (inode->i_ino >= F2FS_ROOT_INO(sbi) && - (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE)) + (offset % PAGE_SIZE || length != PAGE_SIZE)) return; if (PageDirty(page)) { diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 39cb2bc55f5a..4e87a67c0d2c 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -166,7 +166,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi) /* build curseg */ si->base_mem += sizeof(struct curseg_info) * NR_CURSEG_TYPE; - si->base_mem += PAGE_CACHE_SIZE * NR_CURSEG_TYPE; + si->base_mem += PAGE_SIZE * NR_CURSEG_TYPE; /* build dirty segmap */ si->base_mem += sizeof(struct dirty_seglist_info); @@ -203,9 +203,9 @@ get_cache: si->page_mem = 0; npages = NODE_MAPPING(sbi)->nrpages; - si->page_mem += (unsigned long long)npages << PAGE_CACHE_SHIFT; + si->page_mem += (unsigned long long)npages << PAGE_SHIFT; npages = META_MAPPING(sbi)->nrpages; - si->page_mem += (unsigned long long)npages << PAGE_CACHE_SHIFT; + si->page_mem += (unsigned long long)npages << PAGE_SHIFT; } static int stat_show(struct seq_file *s, void *v) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 587ee282e66e..b93d755cb9e4 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -17,8 +17,8 @@ static unsigned long dir_blocks(struct inode *inode) { - return ((unsigned long long) (i_size_read(inode) + PAGE_CACHE_SIZE - 1)) - >> PAGE_CACHE_SHIFT; + return ((unsigned long long) (i_size_read(inode) + PAGE_SIZE - 1)) + >> PAGE_SHIFT; } static unsigned int dir_buckets(unsigned int level, int dir_level) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 36cb3ceba87a..3898140efac9 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1406,7 +1406,7 @@ static inline void f2fs_put_page(struct page *page, int unlock) f2fs_bug_on(F2FS_P_SB(page), !PageLocked(page)); unlock_page(page); } - page_cache_release(page); + put_page(page); } static inline void f2fs_put_dnode(struct dnode_of_data *dn) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index eeae40259fda..53e88991415b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -75,11 +75,11 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, goto mapped; /* page is wholly or partially inside EOF */ - if (((loff_t)(page->index + 1) << PAGE_CACHE_SHIFT) > + if (((loff_t)(page->index + 1) << PAGE_SHIFT) > i_size_read(inode)) { unsigned offset; - offset = i_size_read(inode) & ~PAGE_CACHE_MASK; - zero_user_segment(page, offset, PAGE_CACHE_SIZE); + offset = i_size_read(inode) & ~PAGE_MASK; + zero_user_segment(page, offset, PAGE_SIZE); } set_page_dirty(page); SetPageUptodate(page); @@ -342,7 +342,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) loff_t isize; int err = 0; - mutex_lock(&inode->i_mutex); + inode_lock(inode); isize = i_size_read(inode); if (offset >= isize) @@ -355,11 +355,11 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) goto found; } - pgofs = (pgoff_t)(offset >> PAGE_CACHE_SHIFT); + pgofs = (pgoff_t)(offset >> PAGE_SHIFT); dirty = __get_first_dirty_index(inode->i_mapping, pgofs, whence); - for (; data_ofs < isize; data_ofs = (loff_t)pgofs << PAGE_CACHE_SHIFT) { + for (; data_ofs < isize; data_ofs = (loff_t)pgofs << PAGE_SHIFT) { set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA); if (err && err != -ENOENT) { @@ -379,7 +379,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) /* 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) { + data_ofs = (loff_t)pgofs << PAGE_SHIFT) { block_t blkaddr; blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); @@ -396,10 +396,10 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) found: if (whence == SEEK_HOLE && data_ofs > isize) data_ofs = isize; - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); return vfs_setpos(file, data_ofs, maxbytes); fail: - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); return -ENXIO; } @@ -521,8 +521,8 @@ void truncate_data_blocks(struct dnode_of_data *dn) static int truncate_partial_data_page(struct inode *inode, u64 from, bool cache_only) { - unsigned offset = from & (PAGE_CACHE_SIZE - 1); - pgoff_t index = from >> PAGE_CACHE_SHIFT; + unsigned offset = from & (PAGE_SIZE - 1); + pgoff_t index = from >> PAGE_SHIFT; struct address_space *mapping = inode->i_mapping; struct page *page; @@ -542,7 +542,7 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, return 0; truncate_out: f2fs_wait_on_page_writeback(page, DATA, true); - zero_user(page, offset, PAGE_CACHE_SIZE - offset); + zero_user(page, offset, PAGE_SIZE - offset); if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) set_page_dirty(page); @@ -815,11 +815,11 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) if (ret) return ret; - pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; - pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; + pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; + pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; - off_start = offset & (PAGE_CACHE_SIZE - 1); - off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); + off_start = offset & (PAGE_SIZE - 1); + off_end = (offset + len) & (PAGE_SIZE - 1); if (pg_start == pg_end) { ret = fill_zero(inode, pg_start, off_start, @@ -829,7 +829,7 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) } else { if (off_start) { ret = fill_zero(inode, pg_start++, off_start, - PAGE_CACHE_SIZE - off_start); + PAGE_SIZE - off_start); if (ret) return ret; } @@ -846,8 +846,8 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(sbi, true); - blk_start = (loff_t)pg_start << PAGE_CACHE_SHIFT; - blk_end = (loff_t)pg_end << PAGE_CACHE_SHIFT; + blk_start = (loff_t)pg_start << PAGE_SHIFT; + blk_end = (loff_t)pg_end << PAGE_SHIFT; truncate_inode_pages_range(mapping, blk_start, blk_end - 1); @@ -970,8 +970,8 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) if (ret) return ret; - pg_start = offset >> PAGE_CACHE_SHIFT; - pg_end = (offset + len) >> PAGE_CACHE_SHIFT; + pg_start = offset >> PAGE_SHIFT; + pg_end = (offset + len) >> PAGE_SHIFT; /* write out all dirty pages from offset */ ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); @@ -1065,11 +1065,11 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, truncate_pagecache_range(inode, offset, offset + len - 1); - pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; - pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; + pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; + pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; - off_start = offset & (PAGE_CACHE_SIZE - 1); - off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); + off_start = offset & (PAGE_SIZE - 1); + off_end = (offset + len) & (PAGE_SIZE - 1); if (pg_start == pg_end) { ret = fill_zero(inode, pg_start, off_start, @@ -1083,12 +1083,12 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, } else { if (off_start) { ret = fill_zero(inode, pg_start++, off_start, - PAGE_CACHE_SIZE - off_start); + PAGE_SIZE - off_start); if (ret) return ret; new_size = max_t(loff_t, new_size, - (loff_t)pg_start << PAGE_CACHE_SHIFT); + (loff_t)pg_start << PAGE_SHIFT); } for (index = pg_start; index < pg_end;) { @@ -1116,7 +1116,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, index = end; new_size = max_t(loff_t, new_size, - (loff_t)(index) << PAGE_SHIFT); + (loff_t)index << PAGE_SHIFT); } if (off_end) { @@ -1173,8 +1173,8 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) truncate_pagecache(inode, offset); - pg_start = offset >> PAGE_CACHE_SHIFT; - pg_end = (offset + len) >> PAGE_CACHE_SHIFT; + pg_start = offset >> PAGE_SHIFT; + pg_end = (offset + len) >> PAGE_SHIFT; delta = pg_end - pg_start; nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; @@ -1269,7 +1269,7 @@ static long f2fs_fallocate(struct file *file, int mode, FALLOC_FL_INSERT_RANGE)) return -EOPNOTSUPP; - mutex_lock(&inode->i_mutex); + inode_lock(inode); if (mode & FALLOC_FL_PUNCH_HOLE) { if (offset >= inode->i_size) @@ -1293,7 +1293,7 @@ static long f2fs_fallocate(struct file *file, int mode, } out: - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); trace_f2fs_fallocate(inode, mode, offset, len, ret); return ret; @@ -1362,13 +1362,13 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) flags = f2fs_mask_flags(inode->i_mode, flags); - mutex_lock(&inode->i_mutex); + inode_lock(inode); oldflags = fi->i_flags; if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); ret = -EPERM; goto out; } @@ -1377,7 +1377,7 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) flags = flags & FS_FL_USER_MODIFIABLE; flags |= oldflags & ~FS_FL_USER_MODIFIABLE; fi->i_flags = flags; - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); f2fs_set_inode_flags(inode); inode->i_ctime = CURRENT_TIME; @@ -1792,12 +1792,12 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, if (need_inplace_update(inode)) return -EINVAL; - pg_start = range->start >> PAGE_CACHE_SHIFT; - pg_end = (range->start + range->len) >> PAGE_CACHE_SHIFT; + pg_start = range->start >> PAGE_SHIFT; + pg_end = (range->start + range->len) >> PAGE_SHIFT; f2fs_balance_fs(sbi, true); - mutex_lock(&inode->i_mutex); + inode_lock(inode); /* writeback all dirty pages in the range */ err = filemap_write_and_wait_range(inode->i_mapping, range->start, @@ -1908,9 +1908,9 @@ do_map: clear_out: clear_inode_flag(F2FS_I(inode), FI_DO_DEFRAG); out: - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); if (!err) - range->len = (u64)total << PAGE_CACHE_SHIFT; + range->len = (u64)total << PAGE_SHIFT; return err; } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 43254b7b2a3f..a4bb155dd00a 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -51,7 +51,7 @@ void read_inline_data(struct page *page, struct page *ipage) f2fs_bug_on(F2FS_P_SB(page), page->index); - zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); + zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE); /* Copy the whole inline data block */ src_addr = inline_data_addr(ipage); @@ -93,7 +93,7 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) } if (page->index) - zero_user_segment(page, 0, PAGE_CACHE_SIZE); + zero_user_segment(page, 0, PAGE_SIZE); else read_inline_data(page, ipage); @@ -370,7 +370,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, goto out; f2fs_wait_on_page_writeback(page, DATA, true); - zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); + zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE); dentry_blk = kmap_atomic(page); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 8660ebd189a6..713bb641cfd5 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -46,11 +46,11 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) */ if (type == FREE_NIDS) { mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >> - PAGE_CACHE_SHIFT; + PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == NAT_ENTRIES) { mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> - PAGE_CACHE_SHIFT; + PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == DIRTY_DENTS) { if (sbi->sb->s_bdi->dirty_exceeded) @@ -62,13 +62,13 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) for (i = 0; i <= UPDATE_INO; i++) mem_size += (sbi->im[i].ino_num * - sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT; + sizeof(struct ino_entry)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); } else if (type == EXTENT_CACHE) { mem_size = (atomic_read(&sbi->total_ext_tree) * sizeof(struct extent_tree) + atomic_read(&sbi->total_ext_node) * - sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT; + sizeof(struct extent_node)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); } else { if (!sbi->sb->s_bdi->dirty_exceeded) @@ -121,7 +121,7 @@ static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid) src_addr = page_address(src_page); dst_addr = page_address(dst_page); - memcpy(dst_addr, src_addr, PAGE_CACHE_SIZE); + memcpy(dst_addr, src_addr, PAGE_SIZE); set_page_dirty(dst_page); f2fs_put_page(src_page, 1); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index b485d02b8a8f..1e1c85ace0d4 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -613,7 +613,7 @@ out: /* truncate meta pages to be used by the recovery */ truncate_inode_pages_range(META_MAPPING(sbi), - (loff_t)MAIN_BLKADDR(sbi) << PAGE_CACHE_SHIFT, -1); + (loff_t)MAIN_BLKADDR(sbi) << PAGE_SHIFT, -1); if (err) { truncate_inode_pages(NODE_MAPPING(sbi), 0); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c6f47e4b5f80..2e6f537a0e7d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -889,12 +889,12 @@ int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) } } - sum_in_page = (PAGE_CACHE_SIZE - 2 * SUM_JOURNAL_SIZE - + sum_in_page = (PAGE_SIZE - 2 * SUM_JOURNAL_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE; if (valid_sum_count <= sum_in_page) return 1; else if ((valid_sum_count - sum_in_page) <= - (PAGE_CACHE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) + (PAGE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) return 2; return 3; } @@ -913,9 +913,9 @@ void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr) void *dst = page_address(page); if (src) - memcpy(dst, src, PAGE_CACHE_SIZE); + memcpy(dst, src, PAGE_SIZE); else - memset(dst, 0, PAGE_CACHE_SIZE); + memset(dst, 0, PAGE_SIZE); set_page_dirty(page); f2fs_put_page(page, 1); } @@ -1600,7 +1600,7 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) s = (struct f2fs_summary *)(kaddr + offset); seg_i->sum_blk->entries[j] = *s; offset += SUMMARY_SIZE; - if (offset + SUMMARY_SIZE <= PAGE_CACHE_SIZE - + if (offset + SUMMARY_SIZE <= PAGE_SIZE - SUM_FOOTER_SIZE) continue; @@ -1761,7 +1761,7 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) *summary = seg_i->sum_blk->entries[j]; written_size += SUMMARY_SIZE; - if (written_size + SUMMARY_SIZE <= PAGE_CACHE_SIZE - + if (written_size + SUMMARY_SIZE <= PAGE_SIZE - SUM_FOOTER_SIZE) continue; @@ -1848,7 +1848,7 @@ static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, src_addr = page_address(src_page); dst_addr = page_address(dst_page); - memcpy(dst_addr, src_addr, PAGE_CACHE_SIZE); + memcpy(dst_addr, src_addr, PAGE_SIZE); set_page_dirty(dst_page); f2fs_put_page(src_page, 1); @@ -2175,7 +2175,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) for (i = 0; i < NR_CURSEG_TYPE; i++) { mutex_init(&array[i].curseg_mutex); - array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL); + array[i].sum_blk = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!array[i].sum_blk) return -ENOMEM; init_rwsem(&array[i].journal_rwsem); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5263b4aea769..ebe821f1e5b7 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -664,8 +664,7 @@ static void f2fs_put_super(struct super_block *sb) mutex_unlock(&sbi->umount_mutex); /* our cp_error case, we can wait for any writeback page */ - if (atomic_read(&sbi->nr_wb_bios)) - f2fs_flush_merged_bios(sbi); + f2fs_flush_merged_bios(sbi); iput(sbi->node_inode); iput(sbi->meta_inode); @@ -1254,10 +1253,10 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, } /* Currently, support only 4KB page cache size */ - if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) { + if (F2FS_BLKSIZE != PAGE_SIZE) { f2fs_msg(sb, KERN_INFO, "Invalid page_cache_size (%lu), supports only 4KB\n", - PAGE_CACHE_SIZE); + PAGE_SIZE); return 1; } -- GitLab From 25285674e370b7c0e4b59669486ba359d606e5a0 Mon Sep 17 00:00:00 2001 From: Harshdeep Dhatt Date: Fri, 27 May 2016 12:40:43 -0600 Subject: [PATCH 0250/5498] msm: kgsl: Ignore EAGAIN when programming perfcounter When programming perfcounter via gpu commands, we may encounter -EAGAIN because of cancelling rb events either due to soft reset or when powering down the device. Ignore this error because we have already set up the perfcounter in software and it will be programmed in hardware by adreno_perfcounter_restore when gpu comes back up. CRs-Fixed: 1024199 Change-Id: I5dc3561d15fa50ac58646f96559cfd262020dda9 Signed-off-by: Harshdeep Dhatt --- drivers/gpu/msm/adreno.c | 5 +++-- drivers/gpu/msm/adreno_perfcounter.c | 32 ++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 60b2c41b0cab..b3e4c87a7004 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -2153,8 +2153,6 @@ static int adreno_soft_reset(struct kgsl_device *device) adreno_support_64bit(adreno_dev)) gpudev->enable_64bit(adreno_dev); - /* Restore physical performance counter values after soft reset */ - adreno_perfcounter_restore(adreno_dev); /* Reinitialize the GPU */ gpudev->start(adreno_dev); @@ -2181,6 +2179,9 @@ static int adreno_soft_reset(struct kgsl_device *device) set_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv); } + /* Restore physical performance counter values after soft reset */ + adreno_perfcounter_restore(adreno_dev); + return ret; } diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c index 96922972200f..58e04495259a 100644 --- a/drivers/gpu/msm/adreno_perfcounter.c +++ b/drivers/gpu/msm/adreno_perfcounter.c @@ -522,12 +522,18 @@ int adreno_perfcounter_get(struct adreno_device *adreno_dev, if (empty == -1) return -EBUSY; + /* initialize the new counter */ + group->regs[empty].countable = countable; + /* enable the new counter */ ret = adreno_perfcounter_enable(adreno_dev, groupid, empty, countable); - if (ret) + if (ret) { + /* Put back the perfcounter */ + if (!(group->flags & ADRENO_PERFCOUNTER_GROUP_FIXED)) + group->regs[empty].countable = + KGSL_PERFCOUNTER_NOT_USED; return ret; - /* initialize the new counter */ - group->regs[empty].countable = countable; + } /* set initial kernel and user count */ if (flags & PERFCOUNTER_FLAG_KERNEL) { @@ -746,10 +752,22 @@ static int _perfcounter_enable_default(struct adreno_device *adreno_dev, /* wait for the above commands submitted to complete */ ret = adreno_ringbuffer_waittimestamp(rb, rb->timestamp, ADRENO_IDLE_TIMEOUT); - if (ret) - KGSL_DRV_ERR(device, - "Perfcounter %u/%u/%u start via commands failed %d\n", - group, counter, countable, ret); + if (ret) { + /* + * If we were woken up because of cancelling rb events + * either due to soft reset or adreno_stop, ignore the + * error and return 0 here. The perfcounter is already + * set up in software and it will be programmed in + * hardware when we wake up or come up after soft reset, + * by adreno_perfcounter_restore. + */ + if (ret == -EAGAIN) + ret = 0; + else + KGSL_DRV_ERR(device, + "Perfcounter %u/%u/%u start via commands failed %d\n", + group, counter, countable, ret); + } } else { /* Select the desired perfcounter */ kgsl_regwrite(device, reg->select, countable); -- GitLab From b91a6d3d5b0980661817bdaedd6b3d1d675134eb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 1 Jun 2016 20:55:51 -0700 Subject: [PATCH 0251/5498] Revert "f2fs: no need inc dirty pages under inode lock" This reverts commit b951a4ec165af4973b2bd9c80fb5845fbd840435. Conflicts: fs/f2fs/checkpoint.c --- fs/f2fs/checkpoint.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 934b94d8e4f0..eb1629ab3d81 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -794,13 +794,12 @@ void update_dirty_page(struct inode *inode, struct page *page) !S_ISLNK(inode->i_mode)) return; - if (type != FILE_INODE || test_opt(sbi, DATA_FLUSH)) { - spin_lock(&sbi->inode_lock[type]); + spin_lock(&sbi->inode_lock[type]); + if (type != FILE_INODE || test_opt(sbi, DATA_FLUSH)) __add_dirty_inode(inode, type); - spin_unlock(&sbi->inode_lock[type]); - } - inode_inc_dirty_pages(inode); + spin_unlock(&sbi->inode_lock[type]); + SetPagePrivate(page); f2fs_trace_pid(page); } -- GitLab From 665363e9bca585d06bec0645aca3ce0db5fa0b4e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 10:13:22 -0700 Subject: [PATCH 0252/5498] f2fs: use inode pointer for {set, clear}_inode_flag This patch refactors to use inode pointer for set_inode_flag and clear_inode_flag. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/file.c --- fs/f2fs/acl.c | 7 ++-- fs/f2fs/checkpoint.c | 15 ++++----- fs/f2fs/data.c | 21 ++++++------ fs/f2fs/dir.c | 22 ++++++------- fs/f2fs/extent_cache.c | 6 ++-- fs/f2fs/f2fs.h | 74 ++++++++++++++++++++---------------------- fs/f2fs/file.c | 58 +++++++++++++++------------------ fs/f2fs/gc.c | 4 +-- fs/f2fs/inline.c | 25 +++++++------- fs/f2fs/inode.c | 29 ++++++++--------- fs/f2fs/namei.c | 22 ++++++++----- fs/f2fs/node.c | 4 +-- fs/f2fs/segment.c | 2 +- fs/f2fs/segment.h | 2 +- fs/f2fs/super.c | 7 +--- fs/f2fs/xattr.c | 7 ++-- 16 files changed, 145 insertions(+), 160 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index d757d79a4d71..5111b2021926 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -204,7 +204,6 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type) static int __f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl, struct page *ipage) { - struct f2fs_inode_info *fi = F2FS_I(inode); int name_index; void *value = NULL; size_t size = 0; @@ -217,7 +216,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, error = posix_acl_equiv_mode(acl, &inode->i_mode); if (error < 0) return error; - set_acl_inode(fi, inode->i_mode); + set_acl_inode(inode, inode->i_mode); if (error == 0) acl = NULL; } @@ -236,7 +235,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, if (acl) { value = f2fs_acl_to_disk(acl, &size); if (IS_ERR(value)) { - clear_inode_flag(fi, FI_ACL_MODE); + clear_inode_flag(inode, FI_ACL_MODE); return (int)PTR_ERR(value); } } @@ -247,7 +246,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, if (!error) set_cached_acl(inode, type, acl); - clear_inode_flag(fi, FI_ACL_MODE); + clear_inode_flag(inode, FI_ACL_MODE); return error; } diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index eb1629ab3d81..0fcba9f84836 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -760,28 +760,25 @@ fail_no_cp: static void __add_dirty_inode(struct inode *inode, enum inode_type type) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct f2fs_inode_info *fi = F2FS_I(inode); int flag = (type == DIR_INODE) ? FI_DIRTY_DIR : FI_DIRTY_FILE; - if (is_inode_flag_set(fi, flag)) + if (is_inode_flag_set(inode, flag)) return; - set_inode_flag(fi, flag); - list_add_tail(&fi->dirty_list, &sbi->inode_list[type]); + set_inode_flag(inode, flag); + list_add_tail(&F2FS_I(inode)->dirty_list, &sbi->inode_list[type]); stat_inc_dirty_inode(sbi, type); } static void __remove_dirty_inode(struct inode *inode, enum inode_type type) { - struct f2fs_inode_info *fi = F2FS_I(inode); int flag = (type == DIR_INODE) ? FI_DIRTY_DIR : FI_DIRTY_FILE; - if (get_dirty_pages(inode) || - !is_inode_flag_set(F2FS_I(inode), flag)) + if (get_dirty_pages(inode) || !is_inode_flag_set(inode, flag)) return; - list_del_init(&fi->dirty_list); - clear_inode_flag(fi, flag); + list_del_init(&F2FS_I(inode)->dirty_list); + clear_inode_flag(inode, flag); stat_dec_dirty_inode(F2FS_I_SB(inode), type); } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 92f7472eff63..dd3b6dbb1a67 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -322,7 +322,7 @@ int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count) if (!count) return 0; - if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) + if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC))) return -EPERM; if (unlikely(!inc_valid_block_count(sbi, dn->inode, &count))) return -ENOSPC; @@ -567,7 +567,7 @@ got_it: ((loff_t)(index + 1) << PAGE_SHIFT)) { i_size_write(inode, ((loff_t)(index + 1) << PAGE_SHIFT)); /* Only the directory inode sets new_i_size */ - set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); + set_inode_flag(inode, FI_UPDATE_DIR); } return page; } @@ -581,7 +581,7 @@ static int __allocate_data_block(struct dnode_of_data *dn) pgoff_t fofs; blkcnt_t count = 1; - if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) + if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC))) return -EPERM; dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); @@ -718,8 +718,7 @@ next_block: } else { err = __allocate_data_block(&dn); if (!err) { - set_inode_flag(F2FS_I(inode), - FI_APPEND_WRITE); + set_inode_flag(inode, FI_APPEND_WRITE); allocated = true; } } @@ -1194,14 +1193,14 @@ retry_encrypt: !IS_ATOMIC_WRITTEN_PAGE(page) && need_inplace_update(inode))) { rewrite_data_page(fio); - set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE); + set_inode_flag(inode, FI_UPDATE_WRITE); trace_f2fs_do_write_data_page(page, IPU); } else { write_data_page(&dn, fio); trace_f2fs_do_write_data_page(page, OPU); - set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + set_inode_flag(inode, FI_APPEND_WRITE); if (page->index == 0) - set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); + set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); } out_writepage: f2fs_put_dnode(&dn); @@ -1470,7 +1469,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, goto skip_write; /* skip writing during file defragment */ - if (is_inode_flag_set(F2FS_I(inode), FI_DO_DEFRAG)) + if (is_inode_flag_set(inode, FI_DO_DEFRAG)) goto skip_write; /* during POR, we don't need to trigger writepage at all. */ @@ -1550,7 +1549,7 @@ restart: if (f2fs_has_inline_data(inode)) { if (pos + len <= MAX_INLINE_DATA) { read_inline_data(page, ipage); - set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + set_inode_flag(inode, FI_DATA_EXIST); if (inode->i_nlink) set_inline_node(ipage); } else { @@ -1757,7 +1756,7 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block_dio); if (rw & WRITE) { if (err > 0) - set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE); + set_inode_flag(inode, FI_UPDATE_WRITE); else if (err < 0) f2fs_write_failed(mapping, offset + count); } diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index b93d755cb9e4..84490ce8fa0b 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -385,7 +385,7 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, struct page *page; int err; - if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { + if (is_inode_flag_set(inode, FI_NEW_INODE)) { page = new_inode_page(inode); if (IS_ERR(page)) return page; @@ -429,7 +429,7 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, * This file should be checkpointed during fsync. * We lost i_pino from now on. */ - if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { + if (is_inode_flag_set(inode, FI_INC_LINK)) { file_lost_pino(inode); /* * If link the tmpfile to alias through linkat path, @@ -454,23 +454,23 @@ put_error: void update_parent_metadata(struct inode *dir, struct inode *inode, unsigned int current_depth) { - if (inode && is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { + if (inode && is_inode_flag_set(inode, FI_NEW_INODE)) { if (S_ISDIR(inode->i_mode)) { inc_nlink(dir); - set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + set_inode_flag(dir, FI_UPDATE_DIR); } - clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); + clear_inode_flag(inode, FI_NEW_INODE); } dir->i_mtime = dir->i_ctime = CURRENT_TIME; mark_inode_dirty(dir); if (F2FS_I(dir)->i_current_depth != current_depth) { F2FS_I(dir)->i_current_depth = current_depth; - set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + set_inode_flag(dir, FI_UPDATE_DIR); } - if (inode && is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) - clear_inode_flag(F2FS_I(inode), FI_INC_LINK); + if (inode && is_inode_flag_set(inode, FI_INC_LINK)) + clear_inode_flag(inode, FI_INC_LINK); } int room_for_filename(const void *bitmap, int slots, int max_slots) @@ -607,9 +607,9 @@ fail: if (inode) up_write(&F2FS_I(inode)->i_sem); - if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) { + if (is_inode_flag_set(dir, FI_UPDATE_DIR)) { update_inode_page(dir); - clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + clear_inode_flag(dir, FI_UPDATE_DIR); } kunmap(dentry_page); f2fs_put_page(dentry_page, 1); @@ -661,7 +661,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir) update_inode(inode, page); f2fs_put_page(page, 1); - clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); + clear_inode_flag(inode, FI_NEW_INODE); fail: up_write(&F2FS_I(inode)->i_sem); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 5bfcdb9b69f2..852a0b6f0600 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -431,7 +431,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, write_lock(&et->lock); - if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) { + if (is_inode_flag_set(inode, FI_NO_EXTENT)) { write_unlock(&et->lock); return false; } @@ -523,11 +523,11 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, prev.len < F2FS_MIN_EXTENT_LEN && et->largest.len < F2FS_MIN_EXTENT_LEN) { et->largest.len = 0; - set_inode_flag(F2FS_I(inode), FI_NO_EXTENT); + set_inode_flag(inode, FI_NO_EXTENT); } } - if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) + if (is_inode_flag_set(inode, FI_NO_EXTENT)) __free_extent_tree(sbi, et); write_unlock(&et->lock); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3898140efac9..112ff3b934df 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1564,64 +1564,62 @@ enum { FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ }; -static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) +static inline void set_inode_flag(struct inode *inode, int flag) { - if (!test_bit(flag, &fi->flags)) - set_bit(flag, &fi->flags); + if (!test_bit(flag, &F2FS_I(inode)->flags)) + set_bit(flag, &F2FS_I(inode)->flags); } -static inline int is_inode_flag_set(struct f2fs_inode_info *fi, int flag) +static inline int is_inode_flag_set(struct inode *inode, int flag) { - return test_bit(flag, &fi->flags); + return test_bit(flag, &F2FS_I(inode)->flags); } -static inline void clear_inode_flag(struct f2fs_inode_info *fi, int flag) +static inline void clear_inode_flag(struct inode *inode, int flag) { - if (test_bit(flag, &fi->flags)) - clear_bit(flag, &fi->flags); + if (test_bit(flag, &F2FS_I(inode)->flags)) + clear_bit(flag, &F2FS_I(inode)->flags); } -static inline void set_acl_inode(struct f2fs_inode_info *fi, umode_t mode) +static inline void set_acl_inode(struct inode *inode, umode_t mode) { - fi->i_acl_mode = mode; - set_inode_flag(fi, FI_ACL_MODE); + F2FS_I(inode)->i_acl_mode = mode; + set_inode_flag(inode, FI_ACL_MODE); } -static inline void get_inline_info(struct f2fs_inode_info *fi, - struct f2fs_inode *ri) +static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) { if (ri->i_inline & F2FS_INLINE_XATTR) - set_inode_flag(fi, FI_INLINE_XATTR); + set_inode_flag(inode, FI_INLINE_XATTR); if (ri->i_inline & F2FS_INLINE_DATA) - set_inode_flag(fi, FI_INLINE_DATA); + set_inode_flag(inode, FI_INLINE_DATA); if (ri->i_inline & F2FS_INLINE_DENTRY) - set_inode_flag(fi, FI_INLINE_DENTRY); + set_inode_flag(inode, FI_INLINE_DENTRY); if (ri->i_inline & F2FS_DATA_EXIST) - set_inode_flag(fi, FI_DATA_EXIST); + set_inode_flag(inode, FI_DATA_EXIST); if (ri->i_inline & F2FS_INLINE_DOTS) - set_inode_flag(fi, FI_INLINE_DOTS); + set_inode_flag(inode, FI_INLINE_DOTS); } -static inline void set_raw_inline(struct f2fs_inode_info *fi, - struct f2fs_inode *ri) +static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) { ri->i_inline = 0; - if (is_inode_flag_set(fi, FI_INLINE_XATTR)) + if (is_inode_flag_set(inode, FI_INLINE_XATTR)) ri->i_inline |= F2FS_INLINE_XATTR; - if (is_inode_flag_set(fi, FI_INLINE_DATA)) + if (is_inode_flag_set(inode, FI_INLINE_DATA)) ri->i_inline |= F2FS_INLINE_DATA; - if (is_inode_flag_set(fi, FI_INLINE_DENTRY)) + if (is_inode_flag_set(inode, FI_INLINE_DENTRY)) ri->i_inline |= F2FS_INLINE_DENTRY; - if (is_inode_flag_set(fi, FI_DATA_EXIST)) + if (is_inode_flag_set(inode, FI_DATA_EXIST)) ri->i_inline |= F2FS_DATA_EXIST; - if (is_inode_flag_set(fi, FI_INLINE_DOTS)) + if (is_inode_flag_set(inode, FI_INLINE_DOTS)) ri->i_inline |= F2FS_INLINE_DOTS; } static inline int f2fs_has_inline_xattr(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR); + return is_inode_flag_set(inode, FI_INLINE_XATTR); } static inline unsigned int addrs_per_inode(struct inode *inode) @@ -1648,43 +1646,43 @@ static inline int inline_xattr_size(struct inode *inode) static inline int f2fs_has_inline_data(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA); + return is_inode_flag_set(inode, FI_INLINE_DATA); } static inline void f2fs_clear_inline_inode(struct inode *inode) { - clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); - clear_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + clear_inode_flag(inode, FI_INLINE_DATA); + clear_inode_flag(inode, FI_DATA_EXIST); } static inline int f2fs_exist_data(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_DATA_EXIST); + return is_inode_flag_set(inode, FI_DATA_EXIST); } static inline int f2fs_has_inline_dots(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DOTS); + return is_inode_flag_set(inode, FI_INLINE_DOTS); } static inline bool f2fs_is_atomic_file(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE); + return is_inode_flag_set(inode, FI_ATOMIC_FILE); } static inline bool f2fs_is_volatile_file(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_VOLATILE_FILE); + return is_inode_flag_set(inode, FI_VOLATILE_FILE); } static inline bool f2fs_is_first_block_written(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); + return is_inode_flag_set(inode, FI_FIRST_BLOCK_WRITTEN); } static inline bool f2fs_is_drop_cache(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_DROP_CACHE); + return is_inode_flag_set(inode, FI_DROP_CACHE); } static inline void *inline_data_addr(struct page *page) @@ -1695,7 +1693,7 @@ static inline void *inline_data_addr(struct page *page) static inline int f2fs_has_inline_dentry(struct inode *inode) { - return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DENTRY); + return is_inode_flag_set(inode, FI_INLINE_DENTRY); } static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page) @@ -1745,7 +1743,7 @@ static inline bool f2fs_may_extent_tree(struct inode *inode) mode_t mode = inode->i_mode; if (!test_opt(F2FS_I_SB(inode), EXTENT_CACHE) || - is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) + is_inode_flag_set(inode, FI_NO_EXTENT)) return false; return S_ISREG(mode); @@ -1781,7 +1779,7 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags) } #define get_inode_mode(i) \ - ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ + ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) /* get offset of first page in next direct node */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 53e88991415b..4da4f3777740 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -187,7 +187,6 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, int datasync, bool atomic) { 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; @@ -205,9 +204,9 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, /* if fdatasync is triggered, let's do in-place-update */ if (datasync || get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks) - set_inode_flag(fi, FI_NEED_IPU); + set_inode_flag(inode, FI_NEED_IPU); ret = filemap_write_and_wait_range(inode->i_mapping, start, end); - clear_inode_flag(fi, FI_NEED_IPU); + clear_inode_flag(inode, FI_NEED_IPU); if (ret) { trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); @@ -223,14 +222,14 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, /* * if there is no written data, don't waste time to write recovery info. */ - if (!is_inode_flag_set(fi, FI_APPEND_WRITE) && + if (!is_inode_flag_set(inode, 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) || + if (is_inode_flag_set(inode, FI_UPDATE_WRITE) || exist_written_data(sbi, ino, UPDATE_INO)) goto flush_out; goto out; @@ -240,9 +239,9 @@ go_write: * Both of fdatasync() and fsync() are able to be recovered from * sudden-power-off. */ - down_read(&fi->i_sem); + down_read(&F2FS_I(inode)->i_sem); need_cp = need_do_checkpoint(inode); - up_read(&fi->i_sem); + up_read(&F2FS_I(inode)->i_sem); if (need_cp) { /* all the dirty node pages should be flushed for POR */ @@ -253,8 +252,8 @@ go_write: * 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); + clear_inode_flag(inode, FI_APPEND_WRITE); + clear_inode_flag(inode, FI_UPDATE_WRITE); goto out; } sync_nodes: @@ -280,10 +279,10 @@ sync_nodes: /* once recovery info is written, don't need to tack this */ remove_ino_entry(sbi, ino, APPEND_INO); - clear_inode_flag(fi, FI_APPEND_WRITE); + clear_inode_flag(inode, FI_APPEND_WRITE); flush_out: remove_ino_entry(sbi, ino, UPDATE_INO); - clear_inode_flag(fi, FI_UPDATE_WRITE); + clear_inode_flag(inode, FI_UPDATE_WRITE); ret = f2fs_issue_flush(sbi); f2fs_update_time(sbi, REQ_TIME); out: @@ -488,8 +487,7 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) set_data_blkaddr(dn); invalidate_blocks(sbi, blkaddr); if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page)) - clear_inode_flag(F2FS_I(dn->inode), - FI_FIRST_BLOCK_WRITTEN); + clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN); nr_free++; } @@ -655,7 +653,6 @@ int f2fs_getattr(struct vfsmount *mnt, #ifdef CONFIG_F2FS_FS_POSIX_ACL static void __setattr_copy(struct inode *inode, const struct iattr *attr) { - struct f2fs_inode_info *fi = F2FS_I(inode); unsigned int ia_valid = attr->ia_valid; if (ia_valid & ATTR_UID) @@ -676,7 +673,7 @@ static void __setattr_copy(struct inode *inode, const struct iattr *attr) if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) mode &= ~S_ISGID; - set_acl_inode(fi, mode); + set_acl_inode(inode, mode); } } #else @@ -686,7 +683,6 @@ static void __setattr_copy(struct inode *inode, const struct iattr *attr) int f2fs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; - struct f2fs_inode_info *fi = F2FS_I(inode); int err; err = inode_change_ok(inode, attr); @@ -725,9 +721,9 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_valid & ATTR_MODE) { err = posix_acl_chmod(inode, get_inode_mode(inode)); - if (err || is_inode_flag_set(fi, FI_ACL_MODE)) { - inode->i_mode = fi->i_acl_mode; - clear_inode_flag(fi, FI_ACL_MODE); + if (err || is_inode_flag_set(inode, FI_ACL_MODE)) { + inode->i_mode = F2FS_I(inode)->i_acl_mode; + clear_inode_flag(inode, FI_ACL_MODE); } } @@ -1313,10 +1309,10 @@ static int f2fs_release_file(struct inode *inode, struct file *filp) if (f2fs_is_atomic_file(inode)) drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { - clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); - set_inode_flag(F2FS_I(inode), FI_DROP_CACHE); + clear_inode_flag(inode, FI_VOLATILE_FILE); + set_inode_flag(inode, FI_DROP_CACHE); filemap_fdatawrite(inode->i_mapping); - clear_inode_flag(F2FS_I(inode), FI_DROP_CACHE); + clear_inode_flag(inode, FI_DROP_CACHE); } return 0; } @@ -1415,7 +1411,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) if (ret) goto out; - set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + set_inode_flag(inode, FI_ATOMIC_FILE); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); if (!get_dirty_pages(inode)) @@ -1426,7 +1422,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) inode->i_ino, get_dirty_pages(inode)); ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); if (ret) - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_ATOMIC_FILE); out: inode_unlock(inode); mnt_drop_write_file(filp); @@ -1451,10 +1447,10 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) goto err_out; if (f2fs_is_atomic_file(inode)) { - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_ATOMIC_FILE); ret = commit_inmem_pages(inode); if (ret) { - set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + set_inode_flag(inode, FI_ATOMIC_FILE); goto err_out; } } @@ -1487,7 +1483,7 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) if (ret) goto out; - set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + set_inode_flag(inode, FI_VOLATILE_FILE); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); out: inode_unlock(inode); @@ -1541,7 +1537,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) if (f2fs_is_atomic_file(inode)) drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { - clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + clear_inode_flag(inode, FI_VOLATILE_FILE); ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); } @@ -1874,7 +1870,7 @@ do_map: continue; } - set_inode_flag(F2FS_I(inode), FI_DO_DEFRAG); + set_inode_flag(inode, FI_DO_DEFRAG); idx = map.m_lblk; while (idx < map.m_lblk + map.m_len && cnt < blk_per_seg) { @@ -1899,14 +1895,14 @@ do_map: if (idx < pg_end && cnt < blk_per_seg) goto do_map; - clear_inode_flag(F2FS_I(inode), FI_DO_DEFRAG); + clear_inode_flag(inode, FI_DO_DEFRAG); err = filemap_fdatawrite(inode->i_mapping); if (err) goto out; } clear_out: - clear_inode_flag(F2FS_I(inode), FI_DO_DEFRAG); + clear_inode_flag(inode, FI_DO_DEFRAG); out: inode_unlock(inode); if (!err) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 38d56f678912..4a03076074af 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -617,9 +617,9 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) f2fs_submit_page_mbio(&fio); f2fs_update_data_blkaddr(&dn, newaddr); - set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + set_inode_flag(inode, FI_APPEND_WRITE); if (page->index == 0) - set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); + set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); put_page_out: f2fs_put_page(fio.encrypted_page, 1); recover_block: diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index a4bb155dd00a..c50dee937c1b 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -138,7 +138,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) inode_dec_dirty_pages(dn->inode); /* this converted inline_data should be recovered. */ - set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE); + set_inode_flag(dn->inode, FI_APPEND_WRITE); /* clear inline data and flag after data writeback */ truncate_inline_inode(dn->inode_page, 0); @@ -213,8 +213,8 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) memcpy(dst_addr, src_addr, MAX_INLINE_DATA); kunmap_atomic(src_addr); - set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); - set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + set_inode_flag(inode, FI_APPEND_WRITE); + set_inode_flag(inode, FI_DATA_EXIST); sync_inode_page(&dn); clear_inline_node(dn.inode_page); @@ -252,8 +252,8 @@ process_inline: dst_addr = inline_data_addr(ipage); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); - set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); - set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + set_inode_flag(inode, FI_INLINE_DATA); + set_inode_flag(inode, FI_DATA_EXIST); update_inode(inode, ipage); f2fs_put_page(ipage, 1); @@ -341,7 +341,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, /* update i_size to MAX_INLINE_DATA */ if (i_size_read(inode) < MAX_INLINE_DATA) { i_size_write(inode, MAX_INLINE_DATA); - set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); + set_inode_flag(inode, FI_UPDATE_DIR); } return 0; } @@ -398,12 +398,12 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, truncate_inline_inode(ipage, 0); stat_dec_inline_dir(dir); - clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); + clear_inode_flag(dir, FI_INLINE_DENTRY); F2FS_I(dir)->i_current_depth = 1; if (i_size_read(dir) < PAGE_SIZE) { i_size_write(dir, PAGE_SIZE); - set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + set_inode_flag(dir, FI_UPDATE_DIR); } sync_inode_page(&dn); @@ -464,7 +464,6 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *inline_dentry) { struct f2fs_inline_dentry *backup_dentry; - struct f2fs_inode_info *fi = F2FS_I(dir); int err; backup_dentry = f2fs_kmalloc(sizeof(struct f2fs_inline_dentry), @@ -486,14 +485,14 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, lock_page(ipage); stat_dec_inline_dir(dir); - clear_inode_flag(fi, FI_INLINE_DENTRY); + clear_inode_flag(dir, FI_INLINE_DENTRY); update_inode(dir, ipage); kfree(backup_dentry); return 0; recover: lock_page(ipage); memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); - fi->i_current_depth = 0; + F2FS_I(dir)->i_current_depth = 0; i_size_write(dir, MAX_INLINE_DATA); update_inode(dir, ipage); f2fs_put_page(ipage, 1); @@ -569,9 +568,9 @@ fail: if (inode) up_write(&F2FS_I(inode)->i_sem); - if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) { + if (is_inode_flag_set(dir, FI_UPDATE_DIR)) { update_inode(dir, ipage); - clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + clear_inode_flag(dir, FI_UPDATE_DIR); } out: f2fs_put_page(ipage, 1); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 0217b94f971e..24749da28f7d 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -85,8 +85,8 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage) if (*start++) { f2fs_wait_on_page_writeback(ipage, NODE, true); - set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); - set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage)); + set_inode_flag(inode, FI_DATA_EXIST); + set_raw_inline(inode, F2FS_INODE(ipage)); set_page_dirty(ipage); return; } @@ -141,7 +141,7 @@ static int do_read_inode(struct inode *inode) if (f2fs_init_extent_tree(inode, &ri->i_ext)) set_page_dirty(node_page); - get_inline_info(fi, ri); + get_inline_info(inode, ri); /* check data exist */ if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) @@ -151,7 +151,7 @@ static int do_read_inode(struct inode *inode) __get_inode_rdev(inode, ri); if (__written_first_block(ri)) - set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); + set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); f2fs_put_page(node_page, 1); @@ -243,7 +243,7 @@ int update_inode(struct inode *inode, struct page *node_page) &ri->i_ext); else memset(&ri->i_ext, 0, sizeof(ri->i_ext)); - set_raw_inline(F2FS_I(inode), ri); + set_raw_inline(inode, ri); ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec); ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); @@ -260,7 +260,7 @@ int update_inode(struct inode *inode, struct page *node_page) __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); - clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE); + clear_inode_flag(inode, FI_DIRTY_INODE); /* deleted inode */ if (inode->i_nlink == 0) @@ -299,7 +299,7 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) inode->i_ino == F2FS_META_INO(sbi)) return 0; - if (!is_inode_flag_set(F2FS_I(inode), FI_DIRTY_INODE)) + if (!is_inode_flag_set(inode, FI_DIRTY_INODE)) return 0; /* @@ -317,8 +317,7 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) void f2fs_evict_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct f2fs_inode_info *fi = F2FS_I(inode); - nid_t xnid = fi->i_xattr_nid; + nid_t xnid = F2FS_I(inode)->i_xattr_nid; int err = 0; /* some remained atomic pages should discarded */ @@ -341,7 +340,7 @@ void f2fs_evict_inode(struct inode *inode) goto no_delete; sb_start_intwrite(inode->i_sb); - set_inode_flag(fi, FI_NO_ALLOC); + set_inode_flag(inode, FI_NO_ALLOC); i_size_write(inode, 0); retry: if (F2FS_HAS_BLOCKS(inode)) @@ -368,13 +367,13 @@ no_delete: invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino); if (xnid) invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid); - if (is_inode_flag_set(fi, FI_APPEND_WRITE)) + if (is_inode_flag_set(inode, FI_APPEND_WRITE)) add_ino_entry(sbi, inode->i_ino, APPEND_INO); - if (is_inode_flag_set(fi, FI_UPDATE_WRITE)) + if (is_inode_flag_set(inode, FI_UPDATE_WRITE)) add_ino_entry(sbi, inode->i_ino, UPDATE_INO); - if (is_inode_flag_set(fi, FI_FREE_NID)) { + if (is_inode_flag_set(inode, FI_FREE_NID)) { alloc_nid_failed(sbi, inode->i_ino); - clear_inode_flag(fi, FI_FREE_NID); + clear_inode_flag(inode, FI_FREE_NID); } f2fs_bug_on(sbi, err && !exist_written_data(sbi, inode->i_ino, ORPHAN_INO)); @@ -410,7 +409,7 @@ void handle_failed_inode(struct inode *inode) } alloc_nid_done(sbi, inode->i_ino); } else { - set_inode_flag(F2FS_I(inode), FI_FREE_NID); + set_inode_flag(inode, FI_FREE_NID); } f2fs_unlock_op(sbi); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index fc83cf9a0ed6..8dede8932565 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -61,10 +61,14 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) f2fs_set_encrypted_inode(inode); + set_inode_flag(inode, FI_NEW_INODE); + + if (test_opt(sbi, INLINE_XATTR)) + set_inode_flag(inode, FI_INLINE_XATTR); if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode)) - set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + set_inode_flag(inode, FI_INLINE_DATA); if (f2fs_may_inline_dentry(inode)) - set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY); + set_inode_flag(inode, FI_INLINE_DENTRY); f2fs_init_extent_tree(inode, NULL); @@ -80,7 +84,7 @@ fail: trace_f2fs_new_inode(inode, err); make_bad_inode(inode); if (nid_free) - set_inode_flag(F2FS_I(inode), FI_FREE_NID); + set_inode_flag(inode, FI_FREE_NID); iput(inode); return ERR_PTR(err); } @@ -178,7 +182,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, inode->i_ctime = CURRENT_TIME; ihold(inode); - set_inode_flag(F2FS_I(inode), FI_INC_LINK); + set_inode_flag(inode, FI_INC_LINK); f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -191,7 +195,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, f2fs_sync_fs(sbi->sb, 1); return 0; out: - clear_inode_flag(F2FS_I(inode), FI_INC_LINK); + clear_inode_flag(inode, FI_INC_LINK); iput(inode); f2fs_unlock_op(sbi); return err; @@ -245,7 +249,7 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) } out: if (!err) { - clear_inode_flag(F2FS_I(dir), FI_INLINE_DOTS); + clear_inode_flag(dir, FI_INLINE_DOTS); mark_inode_dirty(dir); } @@ -493,7 +497,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) f2fs_balance_fs(sbi, true); - set_inode_flag(F2FS_I(inode), FI_INC_LINK); + set_inode_flag(inode, FI_INC_LINK); f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -510,7 +514,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) return 0; out_fail: - clear_inode_flag(F2FS_I(inode), FI_INC_LINK); + clear_inode_flag(inode, FI_INC_LINK); handle_failed_inode(inode); return err; } @@ -767,7 +771,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (whiteout) { whiteout->i_state |= I_LINKABLE; - set_inode_flag(F2FS_I(whiteout), FI_INC_LINK); + set_inode_flag(whiteout, FI_INC_LINK); err = f2fs_add_link(old_dentry, whiteout); if (err) goto put_out_dir; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 713bb641cfd5..52a0054d3a95 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1019,7 +1019,7 @@ struct page *new_node_page(struct dnode_of_data *dn, struct page *page; int err; - if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) + if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC))) return ERR_PTR(-EPERM); page = f2fs_grab_cache_page(NODE_MAPPING(sbi), dn->nid, false); @@ -1965,7 +1965,7 @@ void recover_inline_xattr(struct inode *inode, struct page *page) ri = F2FS_INODE(page); if (!(ri->i_inline & F2FS_INLINE_XATTR)) { - clear_inode_flag(F2FS_I(inode), FI_INLINE_XATTR); + clear_inode_flag(inode, FI_INLINE_XATTR); goto update_inode; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2e6f537a0e7d..77dc929715cf 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -241,7 +241,7 @@ void drop_inmem_pages(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_ATOMIC_FILE); mutex_lock(&fi->inmem_lock); __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index dde1f1ef8a22..f0562b13e2c2 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -543,7 +543,7 @@ static inline bool need_inplace_update(struct inode *inode) /* this is only set during fdatasync */ if (policy & (0x1 << F2FS_IPU_FSYNC) && - is_inode_flag_set(F2FS_I(inode), FI_NEED_IPU)) + is_inode_flag_set(inode, FI_NEED_IPU)) return true; return false; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ebe821f1e5b7..4f8f4ea82bf6 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -540,11 +540,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); - set_inode_flag(fi, FI_NEW_INODE); - - if (test_opt(F2FS_SB(sb), INLINE_XATTR)) - set_inode_flag(fi, FI_INLINE_XATTR); - /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; return &fi->vfs_inode; @@ -596,7 +591,7 @@ static int f2fs_drop_inode(struct inode *inode) */ static void f2fs_dirty_inode(struct inode *inode, int flags) { - set_inode_flag(F2FS_I(inode), FI_DIRTY_INODE); + set_inode_flag(inode, FI_DIRTY_INODE); } static void f2fs_i_callback(struct rcu_head *head) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index e0a2d01ef51b..71bb415c4dd3 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -483,7 +483,6 @@ static int __f2fs_setxattr(struct inode *inode, int index, const char *name, const void *value, size_t size, struct page *ipage, int flags) { - struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_xattr_entry *here, *last; void *base_addr; int found, newsize; @@ -581,10 +580,10 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (error) goto exit; - if (is_inode_flag_set(fi, FI_ACL_MODE)) { - inode->i_mode = fi->i_acl_mode; + if (is_inode_flag_set(inode, FI_ACL_MODE)) { + inode->i_mode = F2FS_I(inode)->i_acl_mode; inode->i_ctime = CURRENT_TIME; - clear_inode_flag(fi, FI_ACL_MODE); + clear_inode_flag(inode, FI_ACL_MODE); } if (index == F2FS_XATTR_INDEX_ENCRYPTION && !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) -- GitLab From fbc5d0da1f4518bb7d513f758dc9c14b8e0892e2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 09:22:03 -0700 Subject: [PATCH 0253/5498] f2fs: introduce f2fs_i_size_write with mark_inode_dirty_sync This patch introduces f2fs_i_size_write() to call mark_inode_dirty_sync() with i_size_write(). Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 10 ++++------ fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 9 +++++++++ fs/f2fs/file.c | 10 ++++------ fs/f2fs/inline.c | 6 +++--- fs/f2fs/recovery.c | 2 +- fs/f2fs/super.c | 2 +- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index dd3b6dbb1a67..b9d9256a1d63 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -565,7 +565,7 @@ struct page *get_new_data_page(struct inode *inode, got_it: if (new_i_size && i_size_read(inode) < ((loff_t)(index + 1) << PAGE_SHIFT)) { - i_size_write(inode, ((loff_t)(index + 1) << PAGE_SHIFT)); + f2fs_i_size_write(inode, ((loff_t)(index + 1) << PAGE_SHIFT)); /* Only the directory inode sets new_i_size */ set_inode_flag(inode, FI_UPDATE_DIR); } @@ -606,7 +606,7 @@ alloc: fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) + dn->ofs_in_node; if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_SHIFT)) - i_size_write(dn->inode, + f2fs_i_size_write(dn->inode, ((loff_t)(fofs + 1) << PAGE_SHIFT)); return 0; } @@ -1712,10 +1712,8 @@ static int f2fs_write_end(struct file *file, set_page_dirty(page); - if (pos + copied > i_size_read(inode)) { - i_size_write(inode, pos + copied); - mark_inode_dirty(inode); - } + if (pos + copied > i_size_read(inode)) + f2fs_i_size_write(inode, pos + copied); f2fs_put_page(page, 1); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 84490ce8fa0b..f30eb5f7725e 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -686,7 +686,7 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) drop_nlink(inode); if (S_ISDIR(inode->i_mode)) { drop_nlink(inode); - i_size_write(inode, 0); + f2fs_i_size_write(inode, 0); } up_write(&F2FS_I(inode)->i_sem); update_inode_page(inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 112ff3b934df..8c3d3bebea79 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1587,6 +1587,15 @@ static inline void set_acl_inode(struct inode *inode, umode_t mode) set_inode_flag(inode, FI_ACL_MODE); } +static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) +{ + if (i_size_read(inode) == i_size) + return; + + i_size_write(inode, i_size); + mark_inode_dirty_sync(inode); +} + static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) { if (ri->i_inline & F2FS_INLINE_XATTR) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4da4f3777740..378da8fe4d41 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -989,7 +989,7 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) ret = truncate_blocks(inode, new_size, true); if (!ret) - i_size_write(inode, new_size); + f2fs_i_size_write(inode, new_size); return ret; } @@ -1126,8 +1126,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, out: if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) { - i_size_write(inode, new_size); - mark_inode_dirty(inode); + f2fs_i_size_write(inode, new_size); update_inode_page(inode); } @@ -1187,7 +1186,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) truncate_pagecache(inode, offset); if (!ret) - i_size_write(inode, new_size); + f2fs_i_size_write(inode, new_size); return ret; } @@ -1236,8 +1235,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset, } if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) { - i_size_write(inode, new_size); - mark_inode_dirty(inode); + f2fs_i_size_write(inode, new_size); update_inode_page(inode); } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index c50dee937c1b..7a9bc442dd77 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -340,7 +340,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, /* update i_size to MAX_INLINE_DATA */ if (i_size_read(inode) < MAX_INLINE_DATA) { - i_size_write(inode, MAX_INLINE_DATA); + f2fs_i_size_write(inode, MAX_INLINE_DATA); set_inode_flag(inode, FI_UPDATE_DIR); } return 0; @@ -402,7 +402,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, F2FS_I(dir)->i_current_depth = 1; if (i_size_read(dir) < PAGE_SIZE) { - i_size_write(dir, PAGE_SIZE); + f2fs_i_size_write(dir, PAGE_SIZE); set_inode_flag(dir, FI_UPDATE_DIR); } @@ -493,7 +493,7 @@ recover: lock_page(ipage); memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); F2FS_I(dir)->i_current_depth = 0; - i_size_write(dir, MAX_INLINE_DATA); + f2fs_i_size_write(dir, MAX_INLINE_DATA); update_inode(dir, ipage); f2fs_put_page(ipage, 1); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 1e1c85ace0d4..35c5b60084f9 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -175,7 +175,7 @@ static void recover_inode(struct inode *inode, struct page *page) char *name; inode->i_mode = le16_to_cpu(raw->i_mode); - i_size_write(inode, le64_to_cpu(raw->i_size)); + f2fs_i_size_write(inode, le64_to_cpu(raw->i_size)); inode->i_atime.tv_sec = le64_to_cpu(raw->i_mtime); inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime); inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 4f8f4ea82bf6..bddef1f61b91 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -568,7 +568,7 @@ static int f2fs_drop_inode(struct inode *inode) f2fs_destroy_extent_node(inode); sb_start_intwrite(inode->i_sb); - i_size_write(inode, 0); + f2fs_i_size_write(inode, 0); if (F2FS_HAS_BLOCKS(inode)) f2fs_truncate(inode, true); -- GitLab From 84d8dceaeffe42d49cbf39746e4121d7277626ac Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 09:26:06 -0700 Subject: [PATCH 0254/5498] f2fs: introduce f2fs_i_blocks_write with mark_inode_dirty_sync This patch introduces f2fs_i_blocks_write() to call mark_inode_dirty_sync() when changing inode->i_blocks. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 - fs/f2fs/f2fs.h | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b9d9256a1d63..a8cd955d31ec 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -345,7 +345,6 @@ int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count) if (set_page_dirty(dn->node_page)) dn->node_changed = true; - mark_inode_dirty(dn->inode); sync_inode_page(dn); return 0; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8c3d3bebea79..550a24809b0e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1150,6 +1150,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs) return ofs == XATTR_NODE_OFFSET; } +static inline void f2fs_i_blocks_write(struct inode *, blkcnt_t, bool); static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) { @@ -1172,7 +1173,7 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, } } /* *count can be recalculated */ - inode->i_blocks += *count; + f2fs_i_blocks_write(inode, *count, true); sbi->total_valid_block_count = sbi->total_valid_block_count + (block_t)(*count); spin_unlock(&sbi->stat_lock); @@ -1188,7 +1189,7 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count); f2fs_bug_on(sbi, inode->i_blocks < count); - inode->i_blocks -= count; + f2fs_i_blocks_write(inode, count, false); sbi->total_valid_block_count -= (block_t)count; spin_unlock(&sbi->stat_lock); } @@ -1325,7 +1326,7 @@ static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, } if (inode) - inode->i_blocks++; + f2fs_i_blocks_write(inode, 1, true); sbi->total_valid_node_count++; sbi->total_valid_block_count++; @@ -1344,7 +1345,7 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, !sbi->total_valid_node_count); f2fs_bug_on(sbi, !inode->i_blocks); - inode->i_blocks--; + f2fs_i_blocks_write(inode, 1, false); sbi->total_valid_node_count--; sbi->total_valid_block_count--; @@ -1587,6 +1588,14 @@ static inline void set_acl_inode(struct inode *inode, umode_t mode) set_inode_flag(inode, FI_ACL_MODE); } +static inline void f2fs_i_blocks_write(struct inode *inode, + blkcnt_t diff, bool add) +{ + inode->i_blocks = add ? inode->i_blocks + diff : + inode->i_blocks - diff; + mark_inode_dirty_sync(inode); +} + static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) { if (i_size_read(inode) == i_size) -- GitLab From 662cfaaed642f3a0e9764b08fa95e3f4278d13d4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 09:43:20 -0700 Subject: [PATCH 0255/5498] f2fs: introduce f2fs_i_links_write with mark_inode_dirty_sync This patch introduces f2fs_i_links_write() to call mark_inode_dirty_sync() when changing inode->i_links. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 + fs/f2fs/dir.c | 10 +++++----- fs/f2fs/f2fs.h | 9 +++++++++ fs/f2fs/namei.c | 30 +++++++++--------------------- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 0fcba9f84836..3e6a8ee25649 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -536,6 +536,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) } clear_nlink(inode); + mark_inode_dirty_sync(inode); /* truncate all the data during iput */ iput(inode); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index f30eb5f7725e..e8cbb02c4c04 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -437,7 +437,7 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, */ if (inode->i_nlink == 0) remove_orphan_inode(F2FS_I_SB(dir), inode->i_ino); - inc_nlink(inode); + f2fs_i_links_write(inode, true); } return page; @@ -456,7 +456,7 @@ void update_parent_metadata(struct inode *dir, struct inode *inode, { if (inode && is_inode_flag_set(inode, FI_NEW_INODE)) { if (S_ISDIR(inode->i_mode)) { - inc_nlink(dir); + f2fs_i_links_write(dir, true); set_inode_flag(dir, FI_UPDATE_DIR); } clear_inode_flag(inode, FI_NEW_INODE); @@ -675,7 +675,7 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) down_write(&F2FS_I(inode)->i_sem); if (S_ISDIR(inode->i_mode)) { - drop_nlink(dir); + f2fs_i_links_write(dir, false); if (page) update_inode(dir, page); else @@ -683,9 +683,9 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) } inode->i_ctime = CURRENT_TIME; - drop_nlink(inode); + f2fs_i_links_write(inode, false); if (S_ISDIR(inode->i_mode)) { - drop_nlink(inode); + f2fs_i_links_write(inode, false); f2fs_i_size_write(inode, 0); } up_write(&F2FS_I(inode)->i_sem); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 550a24809b0e..1629938db457 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1588,6 +1588,15 @@ static inline void set_acl_inode(struct inode *inode, umode_t mode) set_inode_flag(inode, FI_ACL_MODE); } +static inline void f2fs_i_links_write(struct inode *inode, bool inc) +{ + if (inc) + inc_nlink(inode); + else + drop_nlink(inode); + mark_inode_dirty_sync(inode); +} + static inline void f2fs_i_blocks_write(struct inode *inode, blkcnt_t diff, bool add) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 8dede8932565..1a94a5df31b3 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -343,9 +343,6 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) f2fs_delete_entry(de, page, dir, inode); f2fs_unlock_op(sbi); - /* In order to evict this inode, we set it dirty */ - mark_inode_dirty(inode); - if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); fail: @@ -601,16 +598,16 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, * remove all unused data of tmpfile after abnormal power-off. */ add_orphan_inode(sbi, inode->i_ino); - f2fs_unlock_op(sbi); - alloc_nid_done(sbi, inode->i_ino); if (whiteout) { - inode_dec_link_count(inode); + f2fs_i_links_write(inode, false); *whiteout = inode; } else { d_tmpfile(dentry, inode); } + /* link_count was changed by d_tmpfile as well. */ + f2fs_unlock_op(sbi); unlock_new_inode(inode); return 0; @@ -708,12 +705,10 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, new_inode->i_ctime = CURRENT_TIME; down_write(&F2FS_I(new_inode)->i_sem); if (old_dir_entry) - drop_nlink(new_inode); - drop_nlink(new_inode); + f2fs_i_links_write(new_inode, false); + f2fs_i_links_write(new_inode, false); up_write(&F2FS_I(new_inode)->i_sem); - mark_inode_dirty(new_inode); - if (!new_inode->i_nlink) add_orphan_inode(sbi, new_inode->i_ino); else @@ -733,7 +728,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } if (old_dir_entry) { - inc_nlink(new_dir); + f2fs_i_links_write(new_dir, true); update_inode_page(new_dir); } @@ -788,8 +783,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_dentry_kunmap(old_inode, old_dir_page); f2fs_put_page(old_dir_page, 0); } - drop_nlink(old_dir); - mark_inode_dirty(old_dir); + f2fs_i_links_write(old_dir, false); update_inode_page(old_dir); } @@ -917,10 +911,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, old_dir->i_ctime = CURRENT_TIME; if (old_nlink) { down_write(&F2FS_I(old_dir)->i_sem); - if (old_nlink < 0) - drop_nlink(old_dir); - else - inc_nlink(old_dir); + f2fs_i_links_write(old_dir, old_nlink > 0); up_write(&F2FS_I(old_dir)->i_sem); } mark_inode_dirty(old_dir); @@ -938,10 +929,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, new_dir->i_ctime = CURRENT_TIME; if (new_nlink) { down_write(&F2FS_I(new_dir)->i_sem); - if (new_nlink < 0) - drop_nlink(new_dir); - else - inc_nlink(new_dir); + f2fs_i_links_write(new_dir, new_nlink > 0); up_write(&F2FS_I(new_dir)->i_sem); } mark_inode_dirty(new_dir); -- GitLab From 68665f66f92511ba45d6f0f645acad7cfae44dc2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 09:52:20 -0700 Subject: [PATCH 0256/5498] f2fs: call mark_inode_dirty_sync for i_field changes This patch calls mark_inode_dirty_sync() for the following on-disk inode changes. -> largest -> ctime/mtime/atime -> i_current_depth -> i_xattr_nid -> i_pino -> i_advise -> i_flags -> i_mode Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 2 ++ fs/f2fs/dir.c | 14 +++++----- fs/f2fs/extent_cache.c | 24 +++++++++-------- fs/f2fs/f2fs.h | 58 ++++++++++++++++++++++++++++++++++++------ fs/f2fs/file.c | 12 ++++----- fs/f2fs/inline.c | 7 ++--- fs/f2fs/inode.c | 1 + fs/f2fs/namei.c | 11 +++----- fs/f2fs/node.c | 6 ++--- fs/f2fs/xattr.c | 3 ++- 10 files changed, 92 insertions(+), 46 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 5111b2021926..d3160760c9a4 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -387,6 +387,8 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage, if (error) return error; + mark_inode_dirty_sync(inode); + if (default_acl) { error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl, ipage); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index e8cbb02c4c04..4a6c4a716b5a 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -243,8 +243,7 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, "Corrupted max_depth of %lu: %u", dir->i_ino, max_depth); max_depth = MAX_DIR_HASH_DEPTH; - F2FS_I(dir)->i_current_depth = max_depth; - mark_inode_dirty(dir); + f2fs_i_depth_write(dir, max_depth); } for (level = 0; level < max_depth; level++) { @@ -303,9 +302,9 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, set_de_type(de, inode->i_mode); f2fs_dentry_kunmap(dir, page); set_page_dirty(page); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; - mark_inode_dirty(dir); + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty_sync(dir); f2fs_put_page(page, 1); } @@ -462,10 +461,10 @@ void update_parent_metadata(struct inode *dir, struct inode *inode, clear_inode_flag(inode, FI_NEW_INODE); } dir->i_mtime = dir->i_ctime = CURRENT_TIME; - mark_inode_dirty(dir); + mark_inode_dirty_sync(dir); if (F2FS_I(dir)->i_current_depth != current_depth) { - F2FS_I(dir)->i_current_depth = current_depth; + f2fs_i_depth_write(dir, current_depth); set_inode_flag(dir, FI_UPDATE_DIR); } @@ -597,7 +596,7 @@ add_dentry: if (inode) { /* we don't need to mark_inode_dirty now */ - F2FS_I(inode)->i_pino = dir->i_ino; + f2fs_i_pino_write(inode, dir->i_ino); update_inode(inode, page); f2fs_put_page(page, 1); } @@ -730,6 +729,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, set_page_dirty(page); dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty_sync(dir); if (inode) f2fs_drop_nlink(dir, inode, NULL); diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 852a0b6f0600..d21dda607bf2 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -170,8 +170,10 @@ static void __drop_largest_extent(struct inode *inode, { struct extent_info *largest = &F2FS_I(inode)->extent_tree->largest; - if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs) + if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs) { largest->len = 0; + mark_inode_dirty_sync(inode); + } } /* return true, if inode page is changed */ @@ -335,11 +337,12 @@ lookup_neighbors: return en; } -static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, +static struct extent_node *__try_merge_extent_node(struct inode *inode, struct extent_tree *et, struct extent_info *ei, struct extent_node *prev_ex, struct extent_node *next_ex) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct extent_node *en = NULL; if (prev_ex && __is_back_mergeable(ei, &prev_ex->ei)) { @@ -360,7 +363,7 @@ static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, if (!en) return NULL; - __try_update_largest_extent(et, en); + __try_update_largest_extent(inode, et, en); spin_lock(&sbi->extent_lock); if (!list_empty(&en->list)) { @@ -371,11 +374,12 @@ static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi, return en; } -static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi, +static struct extent_node *__insert_extent_tree(struct inode *inode, struct extent_tree *et, struct extent_info *ei, struct rb_node **insert_p, struct rb_node *insert_parent) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct rb_node **p = &et->root.rb_node; struct rb_node *parent = NULL; struct extent_node *en = NULL; @@ -402,7 +406,7 @@ do_insert: if (!en) return NULL; - __try_update_largest_extent(et, en); + __try_update_largest_extent(inode, et, en); /* update in global extent list */ spin_lock(&sbi->extent_lock); @@ -473,7 +477,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, set_extent_info(&ei, end, end - dei.fofs + dei.blk, org_end - end); - en1 = __insert_extent_tree(sbi, et, &ei, + en1 = __insert_extent_tree(inode, et, &ei, NULL, NULL); next_en = en1; } else { @@ -494,7 +498,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, } if (parts) - __try_update_largest_extent(et, en); + __try_update_largest_extent(inode, et, en); else __release_extent_node(sbi, et, en); @@ -514,15 +518,15 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, if (blkaddr) { set_extent_info(&ei, fofs, blkaddr, len); - if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en)) - __insert_extent_tree(sbi, et, &ei, + if (!__try_merge_extent_node(inode, et, &ei, prev_en, next_en)) + __insert_extent_tree(inode, et, &ei, insert_p, insert_parent); /* give up extent_cache, if split and small updates happen */ if (dei.len >= 1 && prev.len < F2FS_MIN_EXTENT_LEN && et->largest.len < F2FS_MIN_EXTENT_LEN) { - et->largest.len = 0; + __drop_largest_extent(inode, 0, UINT_MAX); set_inode_flag(inode, FI_NO_EXTENT); } } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1629938db457..6b44d6eb700a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -529,11 +529,13 @@ static inline bool __is_front_mergeable(struct extent_info *cur, return __is_extent_mergeable(cur, front); } -static inline void __try_update_largest_extent(struct extent_tree *et, - struct extent_node *en) +static inline void __try_update_largest_extent(struct inode *inode, + struct extent_tree *et, struct extent_node *en) { - if (en->ei.len > et->largest.len) + if (en->ei.len > et->largest.len) { et->largest = en->ei; + mark_inode_dirty_sync(inode); + } } struct f2fs_nm_info { @@ -1565,10 +1567,26 @@ enum { FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ }; +static inline void __mark_inode_dirty_flag(struct inode *inode, + int flag, bool set) +{ + switch (flag) { + case FI_INLINE_XATTR: + case FI_INLINE_DATA: + case FI_INLINE_DENTRY: + if (set) + return; + case FI_DATA_EXIST: + case FI_INLINE_DOTS: + mark_inode_dirty_sync(inode); + } +} + static inline void set_inode_flag(struct inode *inode, int flag) { if (!test_bit(flag, &F2FS_I(inode)->flags)) set_bit(flag, &F2FS_I(inode)->flags); + __mark_inode_dirty_flag(inode, flag, true); } static inline int is_inode_flag_set(struct inode *inode, int flag) @@ -1580,12 +1598,14 @@ static inline void clear_inode_flag(struct inode *inode, int flag) { if (test_bit(flag, &F2FS_I(inode)->flags)) clear_bit(flag, &F2FS_I(inode)->flags); + __mark_inode_dirty_flag(inode, flag, false); } static inline void set_acl_inode(struct inode *inode, umode_t mode) { F2FS_I(inode)->i_acl_mode = mode; set_inode_flag(inode, FI_ACL_MODE); + mark_inode_dirty_sync(inode); } static inline void f2fs_i_links_write(struct inode *inode, bool inc) @@ -1614,18 +1634,38 @@ static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) mark_inode_dirty_sync(inode); } +static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth) +{ + F2FS_I(inode)->i_current_depth = depth; + mark_inode_dirty_sync(inode); +} + +static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid) +{ + F2FS_I(inode)->i_xattr_nid = xnid; + mark_inode_dirty_sync(inode); +} + +static inline void f2fs_i_pino_write(struct inode *inode, nid_t pino) +{ + F2FS_I(inode)->i_pino = pino; + mark_inode_dirty_sync(inode); +} + static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) { + struct f2fs_inode_info *fi = F2FS_I(inode); + if (ri->i_inline & F2FS_INLINE_XATTR) - set_inode_flag(inode, FI_INLINE_XATTR); + set_bit(FI_INLINE_XATTR, &fi->flags); if (ri->i_inline & F2FS_INLINE_DATA) - set_inode_flag(inode, FI_INLINE_DATA); + set_bit(FI_INLINE_DATA, &fi->flags); if (ri->i_inline & F2FS_INLINE_DENTRY) - set_inode_flag(inode, FI_INLINE_DENTRY); + set_bit(FI_INLINE_DENTRY, &fi->flags); if (ri->i_inline & F2FS_DATA_EXIST) - set_inode_flag(inode, FI_DATA_EXIST); + set_bit(FI_DATA_EXIST, &fi->flags); if (ri->i_inline & F2FS_INLINE_DOTS) - set_inode_flag(inode, FI_INLINE_DOTS); + set_bit(FI_INLINE_DOTS, &fi->flags); } static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) @@ -1737,11 +1777,13 @@ static inline int is_file(struct inode *inode, int type) static inline void set_file(struct inode *inode, int type) { F2FS_I(inode)->i_advise |= type; + mark_inode_dirty_sync(inode); } static inline void clear_file(struct inode *inode, int type) { F2FS_I(inode)->i_advise &= ~type; + mark_inode_dirty_sync(inode); } static inline int f2fs_readonly(struct super_block *sb) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 378da8fe4d41..df368ae0b38c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -172,11 +172,10 @@ static void try_to_fix_pino(struct inode *inode) fi->xattr_ver = 0; if (file_wrong_pino(inode) && inode->i_nlink == 1 && get_parent_ino(inode, &pino)) { - fi->i_pino = pino; + f2fs_i_pino_write(inode, pino); file_got_pino(inode); up_write(&fi->i_sem); - mark_inode_dirty_sync(inode); f2fs_write_inode(inode, NULL); } else { up_write(&fi->i_sem); @@ -637,7 +636,7 @@ int f2fs_truncate(struct inode *inode, bool lock) return err; inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); return 0; } @@ -727,7 +726,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } } - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); return err; } @@ -1282,7 +1281,7 @@ static long f2fs_fallocate(struct file *file, int mode, if (!ret) { inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } @@ -1373,9 +1372,8 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) fi->i_flags = flags; inode_unlock(inode); - f2fs_set_inode_flags(inode); inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); + f2fs_set_inode_flags(inode); out: mnt_drop_write_file(filp); return ret; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 7a9bc442dd77..4bc025c29f82 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -400,7 +400,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, stat_dec_inline_dir(dir); clear_inode_flag(dir, FI_INLINE_DENTRY); - F2FS_I(dir)->i_current_depth = 1; + f2fs_i_depth_write(dir, 1); if (i_size_read(dir) < PAGE_SIZE) { f2fs_i_size_write(dir, PAGE_SIZE); set_inode_flag(dir, FI_UPDATE_DIR); @@ -492,7 +492,7 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, recover: lock_page(ipage); memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); - F2FS_I(dir)->i_current_depth = 0; + f2fs_i_depth_write(dir, 0); f2fs_i_size_write(dir, MAX_INLINE_DATA); update_inode(dir, ipage); f2fs_put_page(ipage, 1); @@ -558,7 +558,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, /* we don't need to mark_inode_dirty now */ if (inode) { - F2FS_I(inode)->i_pino = dir->i_ino; + f2fs_i_pino_write(inode, dir->i_ino); update_inode(inode, page); f2fs_put_page(page, 1); } @@ -597,6 +597,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, set_page_dirty(page); dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty_sync(dir); if (inode) f2fs_drop_nlink(dir, inode, page); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 24749da28f7d..112217985d06 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -35,6 +35,7 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_DIRSYNC; inode_set_flags(inode, new_fl, S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); + mark_inode_dirty_sync(inode); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 1a94a5df31b3..6f7e6124a9df 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -77,7 +77,6 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) stat_inc_inline_dir(inode); trace_f2fs_new_inode(inode, 0); - mark_inode_dirty(inode); return inode; fail: @@ -248,10 +247,8 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR); } out: - if (!err) { + if (!err) clear_inode_flag(dir, FI_INLINE_DOTS); - mark_inode_dirty(dir); - } f2fs_unlock_op(sbi); return err; @@ -760,7 +757,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, up_write(&F2FS_I(old_inode)->i_sem); old_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(old_inode); + mark_inode_dirty_sync(old_inode); f2fs_delete_entry(old_entry, old_page, old_dir, NULL); @@ -914,7 +911,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(old_dir, old_nlink > 0); up_write(&F2FS_I(old_dir)->i_sem); } - mark_inode_dirty(old_dir); + mark_inode_dirty_sync(old_dir); update_inode_page(old_dir); /* update directory entry info of new dir inode */ @@ -932,7 +929,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(new_dir, new_nlink > 0); up_write(&F2FS_I(new_dir)->i_sem); } - mark_inode_dirty(new_dir); + mark_inode_dirty_sync(new_dir); update_inode_page(new_dir); f2fs_unlock_op(sbi); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 52a0054d3a95..b87d7c16a3dc 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -953,7 +953,7 @@ int truncate_xattr_node(struct inode *inode, struct page *page) if (IS_ERR(npage)) return PTR_ERR(npage); - F2FS_I(inode)->i_xattr_nid = 0; + f2fs_i_xnid_write(inode, 0); /* need to do checkpoint during fsync */ F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); @@ -1047,7 +1047,7 @@ struct page *new_node_page(struct dnode_of_data *dn, dn->node_changed = true; if (f2fs_has_xattr_block(ofs)) - F2FS_I(dn->inode)->i_xattr_nid = dn->nid; + f2fs_i_xnid_write(dn->inode, dn->nid); dn->node_page = page; if (ipage) @@ -2007,7 +2007,7 @@ recover_xnid: get_node_info(sbi, new_xnid, &ni); ni.ino = inode->i_ino; set_node_addr(sbi, &ni, NEW_ADDR, false); - F2FS_I(inode)->i_xattr_nid = new_xnid; + f2fs_i_xnid_write(inode, new_xnid); /* 3: update xattr blkaddr */ refresh_sit_entry(sbi, NEW_ADDR, blkaddr); diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 71bb415c4dd3..1a73a4bfc8f7 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -153,7 +153,7 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, return -EINVAL; F2FS_I(inode)->i_advise |= *(char *)value; - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); return 0; } @@ -593,6 +593,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, update_inode(inode, ipage); else update_inode_page(inode); + mark_inode_dirty_sync(inode); exit: kzfree(base_addr); return error; -- GitLab From c28213bd645f6f776bfef49e02bc3421014641e6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 11:10:10 -0700 Subject: [PATCH 0257/5498] f2fs: flush inode metadata when checkpoint is doing This patch registers all the inodes which have dirty metadata to sync when checkpoint is doing. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 36 +++++++++++++++++++++++++++++ fs/f2fs/debug.c | 5 ++-- fs/f2fs/f2fs.h | 9 ++++++-- fs/f2fs/inode.c | 7 +++++- fs/f2fs/node.c | 1 + fs/f2fs/segment.h | 2 ++ fs/f2fs/super.c | 54 ++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 107 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 3e6a8ee25649..a5bd3ee4cfe2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -860,6 +860,34 @@ retry: goto retry; } +int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi) +{ + struct list_head *head = &sbi->inode_list[DIRTY_META]; + struct inode *inode; + struct f2fs_inode_info *fi; + s64 total = get_pages(sbi, F2FS_DIRTY_IMETA); + + while (total--) { + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + + spin_lock(&sbi->inode_lock[DIRTY_META]); + if (list_empty(head)) { + spin_unlock(&sbi->inode_lock[DIRTY_META]); + return 0; + } + fi = list_entry(head->next, struct f2fs_inode_info, + gdirty_list); + inode = igrab(&fi->vfs_inode); + spin_unlock(&sbi->inode_lock[DIRTY_META]); + if (inode) { + update_inode_page(inode); + iput(inode); + } + }; + return 0; +} + /* * Freeze all the FS-operations for checkpoint. */ @@ -886,6 +914,14 @@ retry_flush_dents: goto retry_flush_dents; } + if (get_pages(sbi, F2FS_DIRTY_IMETA)) { + f2fs_unlock_all(sbi); + err = f2fs_sync_inode_meta(sbi); + if (err) + goto out; + goto retry_flush_dents; + } + /* * POR: we should ensure that there are no dirty node pages * until finishing nat/sit flush. diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 4e87a67c0d2c..9197c8d87634 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -47,6 +47,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA); si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; + si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->wb_bios = atomic_read(&sbi->nr_wb_bios); si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; @@ -306,8 +307,8 @@ static int stat_show(struct seq_file *s, void *v) si->inmem_pages, si->wb_bios); seq_printf(s, " - nodes: %4lld in %4d\n", si->ndirty_node, si->node_pages); - seq_printf(s, " - dents: %4lld in dirs:%4d\n", - si->ndirty_dent, si->ndirty_dirs); + seq_printf(s, " - dents: %4lld in dirs:%4d (%4d)\n", + si->ndirty_dent, si->ndirty_dirs, si->ndirty_all); seq_printf(s, " - datas: %4lld in files:%4d\n", si->ndirty_data, si->ndirty_files); seq_printf(s, " - meta: %4lld in %4d\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6b44d6eb700a..1c224057def2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -473,7 +473,8 @@ struct f2fs_inode_info { nid_t i_xattr_nid; /* node id that contains xattrs */ unsigned long long xattr_ver; /* cp version of xattr modification */ - struct list_head dirty_list; /* linked in global dirty list */ + struct list_head dirty_list; /* dirty list for dirs and files */ + struct list_head gdirty_list; /* linked in global dirty list */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct mutex inmem_lock; /* lock for inmemory pages */ struct extent_tree *extent_tree; /* cached extent_tree entry */ @@ -688,6 +689,7 @@ enum count_type { F2FS_DIRTY_NODES, F2FS_DIRTY_META, F2FS_INMEM_PAGES, + F2FS_DIRTY_IMETA, NR_COUNT_TYPE, }; @@ -738,6 +740,7 @@ struct f2fs_bio_info { enum inode_type { DIR_INODE, /* for dirty dir inode */ FILE_INODE, /* for dirty regular/symlink inode */ + DIRTY_META, /* for all dirtied inode metadata */ NR_INODE_TYPE, }; @@ -1932,6 +1935,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) /* * super.c */ +void f2fs_inode_synced(struct inode *); int f2fs_commit_super(struct f2fs_sb_info *, bool); int f2fs_sync_fs(struct super_block *, int); extern __printf(3, 4) @@ -2043,6 +2047,7 @@ void add_ino_entry(struct f2fs_sb_info *, nid_t, int type); void remove_ino_entry(struct f2fs_sb_info *, nid_t, int type); void release_ino_entry(struct f2fs_sb_info *, bool); bool exist_written_data(struct f2fs_sb_info *, nid_t, int); +int f2fs_sync_inode_meta(struct f2fs_sb_info *); int acquire_orphan_inode(struct f2fs_sb_info *); void release_orphan_inode(struct f2fs_sb_info *); void add_orphan_inode(struct f2fs_sb_info *, nid_t); @@ -2111,7 +2116,7 @@ struct f2fs_stat_info { unsigned long long hit_total, total_ext; int ext_tree, zombie_tree, ext_node; s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, inmem_pages; - unsigned int ndirty_dirs, ndirty_files; + unsigned int ndirty_dirs, ndirty_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits, fnids; int total_count, utilization; int bg_gc, wb_bios; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 112217985d06..797058ea72d5 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -261,7 +261,7 @@ int update_inode(struct inode *inode, struct page *node_page) __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); - clear_inode_flag(inode, FI_DIRTY_INODE); + f2fs_inode_synced(inode); /* deleted inode */ if (inode->i_nlink == 0) @@ -285,6 +285,7 @@ retry: } else if (err != -ENOENT) { f2fs_stop_checkpoint(sbi, false); } + f2fs_inode_synced(inode); return 0; } ret = update_inode(inode, node_page); @@ -359,6 +360,8 @@ retry: goto retry; } + if (err) + update_inode_page(inode); sb_end_intwrite(inode->i_sb); no_delete: stat_dec_inline_xattr(inode); @@ -380,6 +383,8 @@ no_delete: !exist_written_data(sbi, inode->i_ino, ORPHAN_INO)); out_clear: fscrypt_put_encryption_info(inode, NULL); + + f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE)); clear_inode(inode); } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b87d7c16a3dc..a878b7b73e39 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -670,6 +670,7 @@ static void truncate_node(struct dnode_of_data *dn) if (dn->nid == dn->inode->i_ino) { remove_orphan_inode(sbi, dn->nid); dec_valid_inode_count(sbi); + f2fs_inode_synced(dn->inode); } else { sync_inode_page(dn); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index f0562b13e2c2..06cb4376f34f 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -478,6 +478,8 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + node_secs += get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return false; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index bddef1f61b91..3146d3491379 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -537,6 +537,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) fi->i_advise = 0; init_rwsem(&fi->i_sem); INIT_LIST_HEAD(&fi->dirty_list); + INIT_LIST_HEAD(&fi->gdirty_list); INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); @@ -547,6 +548,8 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) static int f2fs_drop_inode(struct inode *inode) { + int ret; + /* * This is to avoid a deadlock condition like below. * writeback_single_inode(inode) @@ -554,7 +557,7 @@ static int f2fs_drop_inode(struct inode *inode) * - f2fs_gc -> iput -> evict * - inode_wait_for_writeback(inode) */ - if (!inode_unhashed(inode) && inode->i_state & I_SYNC) { + if ((!inode_unhashed(inode) && inode->i_state & I_SYNC)) { if (!inode->i_nlink && !is_bad_inode(inode)) { /* to avoid evict_inode call simultaneously */ atomic_inc(&inode->i_count); @@ -581,7 +584,20 @@ static int f2fs_drop_inode(struct inode *inode) } return 0; } - return generic_drop_inode(inode); + + ret = generic_drop_inode(inode); + if (is_inode_flag_set(inode, FI_DIRTY_INODE)) { + if (ret) + inode->i_state |= I_WILL_FREE; + spin_unlock(&inode->i_lock); + + update_inode_page(inode); + + spin_lock(&inode->i_lock); + if (ret) + inode->i_state &= ~I_WILL_FREE; + } + return ret; } /* @@ -591,7 +607,40 @@ static int f2fs_drop_inode(struct inode *inode) */ static void f2fs_dirty_inode(struct inode *inode, int flags) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + if (inode->i_ino == F2FS_NODE_INO(sbi) || + inode->i_ino == F2FS_META_INO(sbi)) + return; + + spin_lock(&sbi->inode_lock[DIRTY_META]); + if (is_inode_flag_set(inode, FI_DIRTY_INODE)) { + spin_unlock(&sbi->inode_lock[DIRTY_META]); + return; + } + set_inode_flag(inode, FI_DIRTY_INODE); + list_add_tail(&F2FS_I(inode)->gdirty_list, + &sbi->inode_list[DIRTY_META]); + inc_page_count(sbi, F2FS_DIRTY_IMETA); + spin_unlock(&sbi->inode_lock[DIRTY_META]); + stat_inc_dirty_inode(sbi, DIRTY_META); +} + +void f2fs_inode_synced(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + spin_lock(&sbi->inode_lock[DIRTY_META]); + if (!is_inode_flag_set(inode, FI_DIRTY_INODE)) { + spin_unlock(&sbi->inode_lock[DIRTY_META]); + return; + } + list_del_init(&F2FS_I(inode)->gdirty_list); + clear_inode_flag(inode, FI_DIRTY_INODE); + dec_page_count(sbi, F2FS_DIRTY_IMETA); + spin_unlock(&sbi->inode_lock[DIRTY_META]); + stat_dec_dirty_inode(F2FS_I_SB(inode), DIRTY_META); } static void f2fs_i_callback(struct rcu_head *head) @@ -1757,6 +1806,7 @@ try_onemore: return 0; free_kobj: + f2fs_sync_inode_meta(sbi); kobject_del(&sbi->s_kobj); kobject_put(&sbi->s_kobj); wait_for_completion(&sbi->s_kobj_unregister); -- GitLab From edffd841d1c037befb98b0827ce9b05f1f646d45 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 16:32:49 -0700 Subject: [PATCH 0258/5498] f2fs: remove syncing inode page in all the cases This patch reduces to call them across the whole tree. - sync_inode_page() - update_inode_page() - update_inode() - f2fs_write_inode() Instead, checkpoint will flush all the dirty inode metadata before syncing node pages. Note that, this is doable, since we call mark_inode_dirty_sync() for all inode's field change which needs to update on-disk inode as well. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +---------- fs/f2fs/dir.c | 24 +++--------------------- fs/f2fs/extent_cache.c | 7 ++----- fs/f2fs/f2fs.h | 2 -- fs/f2fs/file.c | 15 +++------------ fs/f2fs/inline.c | 27 ++++++--------------------- fs/f2fs/namei.c | 15 +-------------- fs/f2fs/node.c | 28 ---------------------------- fs/f2fs/recovery.c | 3 --- fs/f2fs/xattr.c | 6 +----- 10 files changed, 17 insertions(+), 121 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a8cd955d31ec..3f135ba7bfc1 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -344,8 +344,6 @@ int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count) if (set_page_dirty(dn->node_page)) dn->node_changed = true; - - sync_inode_page(dn); return 0; } @@ -563,11 +561,8 @@ struct page *get_new_data_page(struct inode *inode, } got_it: if (new_i_size && i_size_read(inode) < - ((loff_t)(index + 1) << PAGE_SHIFT)) { + ((loff_t)(index + 1) << PAGE_SHIFT)) f2fs_i_size_write(inode, ((loff_t)(index + 1) << PAGE_SHIFT)); - /* Only the directory inode sets new_i_size */ - set_inode_flag(inode, FI_UPDATE_DIR); - } return page; } @@ -788,8 +783,6 @@ skip: else if (dn.ofs_in_node < end_offset) goto next_block; - if (allocated) - sync_inode_page(&dn); f2fs_put_dnode(&dn); if (create) { @@ -800,8 +793,6 @@ skip: goto next_dnode; sync_out: - if (allocated) - sync_inode_page(&dn); f2fs_put_dnode(&dn); unlock_out: if (create) { diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 4a6c4a716b5a..69c76b5bba8e 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -454,19 +454,15 @@ void update_parent_metadata(struct inode *dir, struct inode *inode, unsigned int current_depth) { if (inode && is_inode_flag_set(inode, FI_NEW_INODE)) { - if (S_ISDIR(inode->i_mode)) { + if (S_ISDIR(inode->i_mode)) f2fs_i_links_write(dir, true); - set_inode_flag(dir, FI_UPDATE_DIR); - } clear_inode_flag(inode, FI_NEW_INODE); } dir->i_mtime = dir->i_ctime = CURRENT_TIME; mark_inode_dirty_sync(dir); - if (F2FS_I(dir)->i_current_depth != current_depth) { + if (F2FS_I(dir)->i_current_depth != current_depth) f2fs_i_depth_write(dir, current_depth); - set_inode_flag(dir, FI_UPDATE_DIR); - } if (inode && is_inode_flag_set(inode, FI_INC_LINK)) clear_inode_flag(inode, FI_INC_LINK); @@ -595,9 +591,7 @@ add_dentry: set_page_dirty(dentry_page); if (inode) { - /* we don't need to mark_inode_dirty now */ f2fs_i_pino_write(inode, dir->i_ino); - update_inode(inode, page); f2fs_put_page(page, 1); } @@ -606,10 +600,6 @@ fail: if (inode) up_write(&F2FS_I(inode)->i_sem); - if (is_inode_flag_set(dir, FI_UPDATE_DIR)) { - update_inode_page(dir); - clear_inode_flag(dir, FI_UPDATE_DIR); - } kunmap(dentry_page); f2fs_put_page(dentry_page, 1); @@ -656,8 +646,6 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir) err = PTR_ERR(page); goto fail; } - /* we don't need to mark_inode_dirty now */ - update_inode(inode, page); f2fs_put_page(page, 1); clear_inode_flag(inode, FI_NEW_INODE); @@ -673,13 +661,8 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) down_write(&F2FS_I(inode)->i_sem); - if (S_ISDIR(inode->i_mode)) { + if (S_ISDIR(inode->i_mode)) f2fs_i_links_write(dir, false); - if (page) - update_inode(dir, page); - else - update_inode_page(dir); - } inode->i_ctime = CURRENT_TIME; f2fs_i_links_write(inode, false); @@ -688,7 +671,6 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) f2fs_i_size_write(inode, 0); } up_write(&F2FS_I(inode)->i_sem); - update_inode_page(inode); if (inode->i_nlink == 0) add_orphan_inode(sbi, inode->i_ino); diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index d21dda607bf2..e858869d76cb 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -689,9 +689,7 @@ void f2fs_update_extent_cache(struct dnode_of_data *dn) fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) + dn->ofs_in_node; - - if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1)) - sync_inode_page(dn); + f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1); } void f2fs_update_extent_cache_range(struct dnode_of_data *dn, @@ -701,8 +699,7 @@ void f2fs_update_extent_cache_range(struct dnode_of_data *dn, if (!f2fs_may_extent_tree(dn->inode)) return; - if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, len)) - sync_inode_page(dn); + f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, len); } void init_extent_cache_info(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1c224057def2..86aaa80b2e7e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1552,7 +1552,6 @@ enum { FI_ACL_MODE, /* indicate acl mode */ FI_NO_ALLOC, /* should not allocate any blocks */ FI_FREE_NID, /* free allocated nide */ - FI_UPDATE_DIR, /* should update inode block for consistency */ FI_NO_EXTENT, /* not to use the extent cache */ FI_INLINE_XATTR, /* used for inline xattr */ FI_INLINE_DATA, /* used for inline data*/ @@ -1969,7 +1968,6 @@ struct page *new_node_page(struct dnode_of_data *, unsigned int, struct page *); void ra_node_page(struct f2fs_sb_info *, nid_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); struct page *get_node_page_ra(struct page *, int); -void sync_inode_page(struct dnode_of_data *); void move_node_page(struct page *, int); int fsync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *, bool); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index df368ae0b38c..c20623a94611 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -174,12 +174,8 @@ static void try_to_fix_pino(struct inode *inode) get_parent_ino(inode, &pino)) { f2fs_i_pino_write(inode, pino); file_got_pino(inode); - up_write(&fi->i_sem); - - f2fs_write_inode(inode, NULL); - } else { - up_write(&fi->i_sem); } + up_write(&fi->i_sem); } static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, @@ -500,7 +496,6 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) dn->inode) + ofs; f2fs_update_extent_cache_range(dn, fofs, 0, len); dec_valid_block_count(sbi, dn->inode, nr_free); - sync_inode_page(dn); } dn->ofs_in_node = ofs; @@ -1124,10 +1119,8 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, } out: - if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) { + if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) f2fs_i_size_write(inode, new_size); - update_inode_page(inode); - } return ret; } @@ -1233,10 +1226,8 @@ static int expand_inode_data(struct inode *inode, loff_t offset, new_size = ((loff_t)pg_end << PAGE_SHIFT) + off_end; } - if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) { + if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) f2fs_i_size_write(inode, new_size); - update_inode_page(inode); - } return ret; } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 4bc025c29f82..77c9c2439993 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -73,7 +73,7 @@ bool truncate_inline_inode(struct page *ipage, u64 from) f2fs_wait_on_page_writeback(ipage, NODE, true); memset(addr + from, 0, MAX_INLINE_DATA - from); - + set_page_dirty(ipage); return true; } @@ -146,7 +146,6 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) clear_out: stat_dec_inline_inode(dn->inode); f2fs_clear_inline_inode(dn->inode); - sync_inode_page(dn); f2fs_put_dnode(dn); return 0; } @@ -212,11 +211,11 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) dst_addr = inline_data_addr(dn.inode_page); memcpy(dst_addr, src_addr, MAX_INLINE_DATA); kunmap_atomic(src_addr); + set_page_dirty(dn.inode_page); set_inode_flag(inode, FI_APPEND_WRITE); set_inode_flag(inode, FI_DATA_EXIST); - sync_inode_page(&dn); clear_inline_node(dn.inode_page); f2fs_put_dnode(&dn); return 0; @@ -255,7 +254,7 @@ process_inline: set_inode_flag(inode, FI_INLINE_DATA); set_inode_flag(inode, FI_DATA_EXIST); - update_inode(inode, ipage); + set_page_dirty(ipage); f2fs_put_page(ipage, 1); return true; } @@ -266,7 +265,6 @@ process_inline: if (!truncate_inline_inode(ipage, 0)) return false; f2fs_clear_inline_inode(inode); - update_inode(inode, ipage); f2fs_put_page(ipage, 1); } else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) { if (truncate_blocks(inode, 0, false)) @@ -339,10 +337,8 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, set_page_dirty(ipage); /* update i_size to MAX_INLINE_DATA */ - if (i_size_read(inode) < MAX_INLINE_DATA) { + if (i_size_read(inode) < MAX_INLINE_DATA) f2fs_i_size_write(inode, MAX_INLINE_DATA); - set_inode_flag(inode, FI_UPDATE_DIR); - } return 0; } @@ -401,12 +397,8 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, clear_inode_flag(dir, FI_INLINE_DENTRY); f2fs_i_depth_write(dir, 1); - if (i_size_read(dir) < PAGE_SIZE) { + if (i_size_read(dir) < PAGE_SIZE) f2fs_i_size_write(dir, PAGE_SIZE); - set_inode_flag(dir, FI_UPDATE_DIR); - } - - sync_inode_page(&dn); out: f2fs_put_page(page, 1); return err; @@ -486,7 +478,6 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, stat_dec_inline_dir(dir); clear_inode_flag(dir, FI_INLINE_DENTRY); - update_inode(dir, ipage); kfree(backup_dentry); return 0; recover: @@ -494,7 +485,7 @@ recover: memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); f2fs_i_depth_write(dir, 0); f2fs_i_size_write(dir, MAX_INLINE_DATA); - update_inode(dir, ipage); + set_page_dirty(ipage); f2fs_put_page(ipage, 1); kfree(backup_dentry); @@ -559,7 +550,6 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, /* we don't need to mark_inode_dirty now */ if (inode) { f2fs_i_pino_write(inode, dir->i_ino); - update_inode(inode, page); f2fs_put_page(page, 1); } @@ -567,11 +557,6 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, fail: if (inode) up_write(&F2FS_I(inode)->i_sem); - - if (is_inode_flag_set(dir, FI_UPDATE_DIR)) { - update_inode(dir, ipage); - clear_inode_flag(dir, FI_UPDATE_DIR); - } out: f2fs_put_page(ipage, 1); return err; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 6f7e6124a9df..9b003a477bb4 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -710,9 +710,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, add_orphan_inode(sbi, new_inode->i_ino); else release_orphan_inode(sbi); - - update_inode_page(old_inode); - update_inode_page(new_inode); } else { f2fs_balance_fs(sbi, true); @@ -724,10 +721,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_whiteout; } - if (old_dir_entry) { + if (old_dir_entry) f2fs_i_links_write(new_dir, true); - update_inode_page(new_dir); - } /* * old entry and new entry can locate in the same inline @@ -775,13 +770,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (old_dir != new_dir && !whiteout) { f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir); - update_inode_page(old_inode); } else { f2fs_dentry_kunmap(old_inode, old_dir_page); f2fs_put_page(old_dir_page, 0); } f2fs_i_links_write(old_dir, false); - update_inode_page(old_dir); } f2fs_unlock_op(sbi); @@ -903,8 +896,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, file_lost_pino(old_inode); up_write(&F2FS_I(old_inode)->i_sem); - update_inode_page(old_inode); - old_dir->i_ctime = CURRENT_TIME; if (old_nlink) { down_write(&F2FS_I(old_dir)->i_sem); @@ -912,7 +903,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, up_write(&F2FS_I(old_dir)->i_sem); } mark_inode_dirty_sync(old_dir); - update_inode_page(old_dir); /* update directory entry info of new dir inode */ f2fs_set_link(new_dir, new_entry, new_page, old_inode); @@ -921,8 +911,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, file_lost_pino(new_inode); up_write(&F2FS_I(new_inode)->i_sem); - update_inode_page(new_inode); - new_dir->i_ctime = CURRENT_TIME; if (new_nlink) { down_write(&F2FS_I(new_dir)->i_sem); @@ -930,7 +918,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, up_write(&F2FS_I(new_dir)->i_sem); } mark_inode_dirty_sync(new_dir); - update_inode_page(new_dir); f2fs_unlock_op(sbi); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index a878b7b73e39..9ae41055134d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -671,8 +671,6 @@ static void truncate_node(struct dnode_of_data *dn) remove_orphan_inode(sbi, dn->nid); dec_valid_inode_count(sbi); f2fs_inode_synced(dn->inode); - } else { - sync_inode_page(dn); } invalidate: clear_node_page_dirty(dn->node_page); @@ -1050,14 +1048,8 @@ struct page *new_node_page(struct dnode_of_data *dn, if (f2fs_has_xattr_block(ofs)) f2fs_i_xnid_write(dn->inode, dn->nid); - dn->node_page = page; - if (ipage) - update_inode(dn->inode, ipage); - else - sync_inode_page(dn); if (ofs == 0) inc_valid_inode_count(sbi); - return page; fail: @@ -1181,24 +1173,6 @@ struct page *get_node_page_ra(struct page *parent, int start) return __get_node_page(sbi, nid, parent, start); } -void sync_inode_page(struct dnode_of_data *dn) -{ - int ret = 0; - - if (IS_INODE(dn->node_page) || dn->inode_page == dn->node_page) { - ret = update_inode(dn->inode, dn->node_page); - } else if (dn->inode_page) { - if (!dn->inode_page_locked) - lock_page(dn->inode_page); - ret = update_inode(dn->inode, dn->inode_page); - if (!dn->inode_page_locked) - unlock_page(dn->inode_page); - } else { - ret = update_inode_page(dn->inode); - } - dn->node_changed = ret ? true: false; -} - static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) { struct inode *inode; @@ -2013,8 +1987,6 @@ recover_xnid: /* 3: update xattr blkaddr */ refresh_sit_entry(sbi, NEW_ADDR, blkaddr); set_node_addr(sbi, &ni, blkaddr, false); - - update_inode_page(inode); } int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 35c5b60084f9..11053c279ff0 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -490,9 +490,6 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, } } - if (IS_INODE(dn.node_page)) - sync_inode_page(&dn); - copy_node_footer(dn.node_page, page); fill_node_footer(dn.node_page, dn.nid, ni.ino, ofs_of_node(page), false); diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 1a73a4bfc8f7..bdd78436eae1 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -348,6 +348,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, if (ipage) { inline_addr = inline_xattr_addr(ipage); f2fs_wait_on_page_writeback(ipage, NODE, true); + set_page_dirty(ipage); } else { page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) { @@ -588,11 +589,6 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (index == F2FS_XATTR_INDEX_ENCRYPTION && !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) f2fs_set_encrypted_inode(inode); - - if (ipage) - update_inode(inode, ipage); - else - update_inode_page(inode); mark_inode_dirty_sync(inode); exit: kzfree(base_addr); -- GitLab From de8b65f1840d6b3ab72b0726c91f55198e532913 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 20:42:37 -0700 Subject: [PATCH 0259/5498] f2fs: avoid unnecessary updating inode during fsync If roll-forward recovery can recover i_size, we don't need to update inode's metadata during fsync. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 +++ fs/f2fs/f2fs.h | 23 +++++++++++++++++++++-- fs/f2fs/file.c | 4 ++-- fs/f2fs/inode.c | 3 +++ fs/f2fs/node.c | 9 +++++++-- fs/f2fs/recovery.c | 3 +++ fs/f2fs/super.c | 4 ++++ 7 files changed, 43 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3f135ba7bfc1..d6be32adc8d1 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1205,6 +1205,7 @@ static int f2fs_write_data_page(struct page *page, loff_t i_size = i_size_read(inode); const pgoff_t end_index = ((unsigned long long) i_size) >> PAGE_SHIFT; + loff_t psize = (page->index + 1) << PAGE_SHIFT; unsigned offset = 0; bool need_balance_fs = false; int err = 0; @@ -1266,6 +1267,8 @@ write: err = f2fs_write_inline_data(inode, page); if (err == -EAGAIN) err = do_write_data_page(&fio); + if (F2FS_I(inode)->last_disk_size < psize) + F2FS_I(inode)->last_disk_size = psize; f2fs_unlock_op(sbi); done: if (err && err != -ENOENT) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 86aaa80b2e7e..7a49c280aae6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -472,6 +472,7 @@ struct f2fs_inode_info { unsigned int clevel; /* maximum level of given file name */ nid_t i_xattr_nid; /* node id that contains xattrs */ unsigned long long xattr_ver; /* cp version of xattr modification */ + loff_t last_disk_size; /* lastly written file size */ struct list_head dirty_list; /* dirty list for dirs and files */ struct list_head gdirty_list; /* linked in global dirty list */ @@ -1547,6 +1548,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) enum { FI_NEW_INODE, /* indicate newly allocated inode */ FI_DIRTY_INODE, /* indicate inode is dirty or not */ + FI_AUTO_RECOVER, /* indicate inode is recoverable */ FI_DIRTY_DIR, /* indicate directory has dirty pages */ FI_INC_LINK, /* need to increment i_nlink */ FI_ACL_MODE, /* indicate acl mode */ @@ -1622,18 +1624,35 @@ static inline void f2fs_i_links_write(struct inode *inode, bool inc) static inline void f2fs_i_blocks_write(struct inode *inode, blkcnt_t diff, bool add) { + bool clean = !is_inode_flag_set(inode, FI_DIRTY_INODE); + bool recover = is_inode_flag_set(inode, FI_AUTO_RECOVER); + inode->i_blocks = add ? inode->i_blocks + diff : inode->i_blocks - diff; mark_inode_dirty_sync(inode); + if (clean || recover) + set_inode_flag(inode, FI_AUTO_RECOVER); } static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) { + bool clean = !is_inode_flag_set(inode, FI_DIRTY_INODE); + bool recover = is_inode_flag_set(inode, FI_AUTO_RECOVER); + if (i_size_read(inode) == i_size) return; i_size_write(inode, i_size); mark_inode_dirty_sync(inode); + if (clean || recover) + set_inode_flag(inode, FI_AUTO_RECOVER); +} + +static inline bool f2fs_skip_inode_update(struct inode *inode) +{ + if (!is_inode_flag_set(inode, FI_AUTO_RECOVER)) + return false; + return F2FS_I(inode)->last_disk_size == i_size_read(inode); } static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth) @@ -1969,8 +1988,8 @@ void ra_node_page(struct f2fs_sb_info *, nid_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); struct page *get_node_page_ra(struct page *, int); void move_node_page(struct page *, int); -int fsync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *, - bool); +int fsync_node_pages(struct f2fs_sb_info *, struct inode *, + struct writeback_control *, bool); int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); bool alloc_nid(struct f2fs_sb_info *, nid_t *); void alloc_nid_done(struct f2fs_sb_info *, nid_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c20623a94611..f63406ec88da 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -209,7 +209,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, } /* if the inode is dirty, let's recover all the time */ - if (!datasync) { + if (!datasync && !f2fs_skip_inode_update(inode)) { f2fs_write_inode(inode, NULL); goto go_write; } @@ -252,7 +252,7 @@ go_write: goto out; } sync_nodes: - ret = fsync_node_pages(sbi, ino, &wbc, atomic); + ret = fsync_node_pages(sbi, inode, &wbc, atomic); if (ret) goto out; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 797058ea72d5..f228dff23c76 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -154,6 +154,9 @@ static int do_read_inode(struct inode *inode) if (__written_first_block(ri)) set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); + if (!need_inode_block_update(sbi, inode->i_ino)) + fi->last_disk_size = inode->i_size; + f2fs_put_page(node_page, 1); stat_inc_inline_xattr(inode); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9ae41055134d..208af067b714 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1303,7 +1303,7 @@ continue_unlock: return last_page; } -int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, +int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic) { pgoff_t index, end; @@ -1311,6 +1311,7 @@ int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, int ret = 0; struct page *last_page = NULL; bool marked = false; + nid_t ino = inode->i_ino; if (atomic) { last_page = last_fsync_dnode(sbi, ino); @@ -1364,9 +1365,13 @@ continue_unlock: if (!atomic || page == last_page) { set_fsync_mark(page, 1); - if (IS_INODE(page)) + if (IS_INODE(page)) { + if (is_inode_flag_set(inode, + FI_DIRTY_INODE)) + update_inode(inode, page); set_dentry_mark(page, need_dentry_mark(sbi, ino)); + } /* may be written by other thread */ if (!PageDirty(page)) set_page_dirty(page); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 11053c279ff0..3c6425026c23 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -455,6 +455,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, continue; } + if ((start + 1) << PAGE_SHIFT > i_size_read(inode)) + f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT); + /* * dest is reserved block, invalidate src block * and then reserve one new block in dnode page. diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3146d3491379..56bf08d436fd 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -613,6 +613,9 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) inode->i_ino == F2FS_META_INO(sbi)) return; + if (is_inode_flag_set(inode, FI_AUTO_RECOVER)) + clear_inode_flag(inode, FI_AUTO_RECOVER); + spin_lock(&sbi->inode_lock[DIRTY_META]); if (is_inode_flag_set(inode, FI_DIRTY_INODE)) { spin_unlock(&sbi->inode_lock[DIRTY_META]); @@ -638,6 +641,7 @@ void f2fs_inode_synced(struct inode *inode) } list_del_init(&F2FS_I(inode)->gdirty_list); clear_inode_flag(inode, FI_DIRTY_INODE); + clear_inode_flag(inode, FI_AUTO_RECOVER); dec_page_count(sbi, F2FS_DIRTY_IMETA); spin_unlock(&sbi->inode_lock[DIRTY_META]); stat_dec_dirty_inode(F2FS_I_SB(inode), DIRTY_META); -- GitLab From 4318168def264af9d571624bc9006b4dcef314ab Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 23 May 2016 12:04:56 -0700 Subject: [PATCH 0260/5498] f2fs: detect congestion of flush command issues If flush commands do not incur any congestion, we don't need to throw that to dispatching queue which causes unnecessary latency. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7a49c280aae6..ad019c01c07f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -634,6 +634,7 @@ struct flush_cmd { struct flush_cmd_control { struct task_struct *f2fs_issue_flush; /* flush thread */ wait_queue_head_t flush_wait_queue; /* waiting queue for wake-up */ + atomic_t submit_flush; /* # of issued flushes */ struct llist_head issue_list; /* list for command issue */ struct llist_node *dispatch_list; /* list for command dispatch */ }; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 77dc929715cf..34a9159cf5ac 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -433,24 +433,28 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) if (test_opt(sbi, NOBARRIER)) return 0; - if (!test_opt(sbi, FLUSH_MERGE)) { + if (!test_opt(sbi, FLUSH_MERGE) || !atomic_read(&fcc->submit_flush)) { struct bio *bio = f2fs_bio_alloc(0); int ret; + atomic_inc(&fcc->submit_flush); bio->bi_bdev = sbi->sb->s_bdev; ret = submit_bio_wait(WRITE_FLUSH, bio); + atomic_dec(&fcc->submit_flush); bio_put(bio); return ret; } init_completion(&cmd.wait); + atomic_inc(&fcc->submit_flush); llist_add(&cmd.llnode, &fcc->issue_list); if (!fcc->dispatch_list) wake_up(&fcc->flush_wait_queue); wait_for_completion(&cmd.wait); + atomic_dec(&fcc->submit_flush); return cmd.ret; } @@ -464,6 +468,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); if (!fcc) return -ENOMEM; + atomic_set(&fcc->submit_flush, 0); init_waitqueue_head(&fcc->flush_wait_queue); init_llist_head(&fcc->issue_list); SM_I(sbi)->cmd_control_info = fcc; -- GitLab From ef0ea9dcb3c31daf4fb553a4fb804cf5cb7aab7c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 22:39:20 -0700 Subject: [PATCH 0261/5498] f2fs: set flush_merge by default This patch sets flush_merge by default. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 56bf08d436fd..0f898f1e274e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -86,6 +86,7 @@ enum { Opt_inline_data, Opt_inline_dentry, Opt_flush_merge, + Opt_noflush_merge, Opt_nobarrier, Opt_fastboot, Opt_extent_cache, @@ -112,6 +113,7 @@ static match_table_t f2fs_tokens = { {Opt_inline_data, "inline_data"}, {Opt_inline_dentry, "inline_dentry"}, {Opt_flush_merge, "flush_merge"}, + {Opt_noflush_merge, "noflush_merge"}, {Opt_nobarrier, "nobarrier"}, {Opt_fastboot, "fastboot"}, {Opt_extent_cache, "extent_cache"}, @@ -478,6 +480,9 @@ static int parse_options(struct super_block *sb, char *options) case Opt_flush_merge: set_opt(sbi, FLUSH_MERGE); break; + case Opt_noflush_merge: + clear_opt(sbi, FLUSH_MERGE); + break; case Opt_nobarrier: set_opt(sbi, NOBARRIER); break; @@ -932,6 +937,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, BG_GC); set_opt(sbi, INLINE_DATA); set_opt(sbi, EXTENT_CACHE); + set_opt(sbi, FLUSH_MERGE); #ifdef CONFIG_F2FS_FS_XATTR set_opt(sbi, XATTR_USER); -- GitLab From 768464c3a70d2c77f7af32a56e9c1c95646b1712 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 22:50:29 -0700 Subject: [PATCH 0262/5498] f2fs: remove writepages lock This patch removes writepages lock. We can improve multi-threading performance. tiobench, 32 threads, 4KB write per fsync on SSD Before: 25.88 MB/s After: 28.03 MB/s Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 7 ------- fs/f2fs/f2fs.h | 1 - fs/f2fs/super.c | 1 - 3 files changed, 9 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d6be32adc8d1..23c41302028e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1444,7 +1444,6 @@ static int f2fs_write_data_pages(struct address_space *mapping, { struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - bool locked = false; int ret; long diff; @@ -1473,14 +1472,8 @@ static int f2fs_write_data_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, DATA, wbc); - if (!S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_ALL) { - mutex_lock(&sbi->writepages); - locked = true; - } ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE); - if (locked) - mutex_unlock(&sbi->writepages); remove_dirty_inode(inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ad019c01c07f..f4c2fd76e884 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -801,7 +801,6 @@ struct f2fs_sb_info { struct mutex cp_mutex; /* checkpoint procedure lock */ struct rw_semaphore cp_rwsem; /* blocking FS operations */ struct rw_semaphore node_write; /* locking node writes */ - struct mutex writepages; /* mutex for writepages() */ wait_queue_head_t cp_wait; unsigned long last_time[MAX_TIME]; /* to store time in jiffies */ long interval_time[MAX_TIME]; /* to store thresholds */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0f898f1e274e..b094f675f343 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1616,7 +1616,6 @@ try_onemore: sbi->raw_super = raw_super; sbi->valid_super_block = valid_super_block; mutex_init(&sbi->gc_mutex); - mutex_init(&sbi->writepages); mutex_init(&sbi->cp_mutex); init_rwsem(&sbi->node_write); -- GitLab From 2fddfc7d81a51b383d51c1f39b213953779d22a0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 25 May 2016 14:29:11 -0700 Subject: [PATCH 0263/5498] f2fs: propagate error given by f2fs_find_entry If we get ENOMEM or EIO in f2fs_find_entry, we should stop right away. Otherwise, for example, we can get duplicate directory entry by ->chash and ->clevel. Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 23 ++++++++++++++++------- fs/f2fs/inline.c | 4 +++- fs/f2fs/namei.c | 5 +++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 69c76b5bba8e..1ae08882c421 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -185,8 +185,13 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, /* no need to allocate new dentry pages to all the indices */ dentry_page = find_data_page(dir, bidx); if (IS_ERR(dentry_page)) { - room = true; - continue; + if (PTR_ERR(dentry_page) == -ENOENT) { + room = true; + continue; + } else { + *res_page = dentry_page; + break; + } } de = find_in_block(dentry_page, fname, namehash, &max_slots, @@ -223,19 +228,22 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, struct fscrypt_name fname; int err; - *res_page = NULL; - err = fscrypt_setup_filename(dir, child, 1, &fname); - if (err) + if (err) { + *res_page = ERR_PTR(-ENOMEM); return NULL; + } if (f2fs_has_inline_dentry(dir)) { + *res_page = NULL; de = find_in_inline_dir(dir, &fname, res_page); goto out; } - if (npages == 0) + if (npages == 0) { + *res_page = NULL; goto out; + } max_depth = F2FS_I(dir)->i_current_depth; if (unlikely(max_depth > MAX_DIR_HASH_DEPTH)) { @@ -247,8 +255,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, } for (level = 0; level < max_depth; level++) { + *res_page = NULL; de = find_in_level(dir, level, &fname, res_page); - if (de) + if (de || IS_ERR(*res_page)) break; } out: diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 77c9c2439993..1eb30431bf44 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -286,8 +286,10 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, f2fs_hash_t namehash; ipage = get_node_page(sbi, dir->i_ino); - if (IS_ERR(ipage)) + if (IS_ERR(ipage)) { + *res_page = ipage; return NULL; + } namehash = f2fs_dentry_hash(&name); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 9b003a477bb4..4e9b83e19b4c 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -233,6 +233,9 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) if (de) { f2fs_dentry_kunmap(dir, page); f2fs_put_page(page, 0); + } else if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; } else { err = __f2fs_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR); if (err) @@ -243,6 +246,8 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) if (de) { f2fs_dentry_kunmap(dir, page); f2fs_put_page(page, 0); + } else if (IS_ERR(page)) { + err = PTR_ERR(page); } else { err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR); } -- GitLab From 6dc407600ad5c2dc5b9d9f47563cf9a74f4eccd3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 25 May 2016 15:24:18 -0700 Subject: [PATCH 0264/5498] f2fs: inject to produce some orphan inodes Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +++ fs/f2fs/inode.c | 5 +++++ fs/f2fs/super.c | 1 + 3 files changed, 9 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f4c2fd76e884..9c365f9e9e3c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -46,6 +46,7 @@ enum { FAULT_ORPHAN, FAULT_BLOCK, FAULT_DIR_DEPTH, + FAULT_EVICT_INODE, FAULT_MAX, }; @@ -75,6 +76,8 @@ static inline bool time_to_inject(int type) return false; else if (type == FAULT_DIR_DEPTH && !IS_FAULT_SET(type)) return false; + else if (type == FAULT_EVICT_INODE && !IS_FAULT_SET(type)) + return false; atomic_inc(&f2fs_fault.inject_ops); if (atomic_read(&f2fs_fault.inject_ops) >= f2fs_fault.inject_rate) { diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index f228dff23c76..1d473e8b0ca1 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -344,6 +344,11 @@ void f2fs_evict_inode(struct inode *inode) if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(FAULT_EVICT_INODE)) + goto no_delete; +#endif + sb_start_intwrite(inode->i_sb); set_inode_flag(inode, FI_NO_ALLOC); i_size_write(inode, 0); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b094f675f343..604465318ecb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -49,6 +49,7 @@ char *fault_name[FAULT_MAX] = { [FAULT_ORPHAN] = "orphan", [FAULT_BLOCK] = "no more block", [FAULT_DIR_DEPTH] = "too big dir depth", + [FAULT_EVICT_INODE] = "evict_inode fail", }; static void f2fs_build_fault_attr(unsigned int rate) -- GitLab From c88306b80136e744844f8260985793174965ddbb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 25 May 2016 17:17:56 -0700 Subject: [PATCH 0265/5498] f2fs: do not skip writing data pages For data pages, let's try to flush as much as possible in background. On /dev/pmem0, 1. dd if=/dev/zero of=/mnt/test/testfile bs=1M count=2048 conv=fsync Before : 800 MB/s After : 1.1 GB/s 2. dd if=/dev/zero of=/mnt/test/testfile bs=1M count=2048 Before : 1.3 GB/s After : 2.2 GB/s Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +++++------ fs/f2fs/segment.h | 4 +--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 23c41302028e..e73fa41cd055 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1445,7 +1445,6 @@ static int f2fs_write_data_pages(struct address_space *mapping, struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); int ret; - long diff; /* deal with chardevs and other special file */ if (!mapping->a_ops->writepage) @@ -1470,14 +1469,14 @@ static int f2fs_write_data_pages(struct address_space *mapping, trace_f2fs_writepages(mapping->host, wbc, DATA); - diff = nr_pages_to_write(sbi, DATA, wbc); - ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); - f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE); + /* + * if some pages were truncated, we cannot guarantee its mapping->host + * to detect pending bios. + */ + f2fs_submit_merged_bio(sbi, DATA, WRITE); remove_dirty_inode(inode); - - wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return ret; skip_write: diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 06cb4376f34f..43c6d0bab247 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -727,9 +727,7 @@ static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type, nr_to_write = wbc->nr_to_write; - if (type == DATA) - desired = 4096; - else if (type == NODE) + if (type == NODE) desired = 3 * max_hw_blocks(sbi); else desired = MAX_BIO_BLOCKS(sbi); -- GitLab From 58deeab7b2c865f72a4e1a598b3805631f9af8bf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 25 May 2016 20:57:16 -0700 Subject: [PATCH 0266/5498] f2fs: remove two steps to flush dirty data pages If there is no cold page, we don't need to do a loop to flush dirty data pages. On /dev/pmem0, 1. dd if=/dev/zero of=/mnt/test/testfile bs=1M count=2048 conv=fsync Before : 1.1 GB/s After : 1.2 GB/s 2. dd if=/dev/zero of=/mnt/test/testfile bs=1M count=2048 Before : 2.2 GB/s After : 2.3 GB/s Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index e73fa41cd055..016576249f09 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1327,10 +1327,9 @@ static int f2fs_write_cache_pages(struct address_space *mapping, int cycled; int range_whole = 0; int tag; - int step = 0; pagevec_init(&pvec, 0); -next: + if (wbc->range_cyclic) { writeback_index = mapping->writeback_index; /* prev offset */ index = writeback_index; @@ -1385,9 +1384,6 @@ continue_unlock: goto continue_unlock; } - if (step == is_cold_data(page)) - goto continue_unlock; - if (PageWriteback(page)) { if (wbc->sync_mode != WB_SYNC_NONE) f2fs_wait_on_page_writeback(page, @@ -1422,11 +1418,6 @@ continue_unlock: cond_resched(); } - if (step < 1) { - step++; - goto next; - } - if (!cycled && !done) { cycled = 1; index = 0; -- GitLab From 15975616d2731c4583e8e9eda57d5975436ec772 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Thu, 26 May 2016 19:40:29 +0800 Subject: [PATCH 0267/5498] f2fs: return the errno to the caller to avoid using a wrong page Commit aaf9607516ed38825268515ef4d773289a44f429 ("f2fs: check node page contents all the time") pointed out that "sometimes it was reported that its contents was missing", so it checks the page's mapping and contents. When "nid != nid_of_node(page)", ERR_PTR(-EIO) will be returned to the caller. However, commit e1c51b9f1df2f9efc2ec11488717e40cd12015f9 ("f2fs: clean up node page updating flow") moves "nid != nid_of_node(page)" test to "f2fs_bug_on(sbi, nid != nid_of_node(page))", this will return a wrong page to the caller when F2FS_CHECK_FS is off when "sometimes it was reported that its contents was missing" happens. This patch restores to check node page contents all the time, and returns the errno to make the caller known something is wrong and avoid to use the page. This patch also moves f2fs_bug_on to its proper location. Signed-off-by: Yunlong Song Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/node.c --- fs/f2fs/node.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 208af067b714..7062d25570ac 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1142,21 +1142,22 @@ repeat: lock_page(page); - if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 1); - return ERR_PTR(-EIO); - } if (unlikely(page->mapping != NODE_MAPPING(sbi))) { f2fs_put_page(page, 1); goto repeat; } + + if (unlikely(!PageUptodate(page))) + goto out_err; page_hit: - if (unlikely(!PageUptodate(page))) { + if(unlikely(nid != nid_of_node(page))) { + f2fs_bug_on(sbi, 1); + ClearPageUptodate(page); +out_err: f2fs_put_page(page, 1); return ERR_PTR(-EIO); } mark_page_accessed(page); - f2fs_bug_on(sbi, nid != nid_of_node(page)); return page; } -- GitLab From 2cf2e73d7b828be43389a11a17959aad757fc0e0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 27 May 2016 10:10:41 -0700 Subject: [PATCH 0268/5498] f2fs: return error of f2fs_lookup Now we can report an error to f2fs_lookup given by f2fs_find_entry. Suggested-by: He YunLei Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 2 +- fs/f2fs/namei.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 1ae08882c421..d13df3c7e22a 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -230,7 +230,7 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, err = fscrypt_setup_filename(dir, child, 1, &fname); if (err) { - *res_page = ERR_PTR(-ENOMEM); + *res_page = ERR_PTR(err); return NULL; } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 4e9b83e19b4c..a630b1be3cc9 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -287,8 +287,11 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, return ERR_PTR(-ENAMETOOLONG); de = f2fs_find_entry(dir, &dentry->d_name, &page); - if (!de) + if (!de) { + if (IS_ERR(page)) + return (struct dentry *)page; return d_splice_alias(inode, dentry); + } ino = le32_to_cpu(de->ino); f2fs_dentry_kunmap(dir, page); -- GitLab From 3f79ab761e392104bafaf84a2a19b783a9448dee Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 29 May 2016 21:18:23 -0700 Subject: [PATCH 0269/5498] f2fs: handle writepage correctly Previously, f2fs_write_data_pages() calls __f2fs_writepage() which calls f2fs_write_data_page(). If f2fs_write_data_page() returns AOP_WRITEPAGE_ACTIVATE, __f2fs_writepage() calls mapping_set_error(). But, this should not happen at every time, since sometimes f2fs_write_data_page() tries to skip writing pages without error. For example, volatile_write() gives EIO all the time, as Shuoran Liu pointed out. Reported-by: Shuoran Liu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 016576249f09..de586abae4fc 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1242,20 +1242,18 @@ write: available_free_memory(sbi, BASE_CHECK)))) goto redirty_out; - /* Dentry blocks are controlled by checkpoint */ - if (S_ISDIR(inode->i_mode)) { - if (unlikely(f2fs_cp_error(sbi))) - goto redirty_out; - err = do_write_data_page(&fio); - goto done; - } - /* we should bypass data pages to proceed the kworkder jobs */ if (unlikely(f2fs_cp_error(sbi))) { SetPageError(page); goto out; } + /* Dentry blocks are controlled by checkpoint */ + if (S_ISDIR(inode->i_mode)) { + err = do_write_data_page(&fio); + goto done; + } + if (!wbc->for_reclaim) need_balance_fs = true; else if (has_not_enough_free_secs(sbi, 0)) @@ -1295,16 +1293,8 @@ out: redirty_out: redirty_page_for_writepage(wbc, page); - return AOP_WRITEPAGE_ACTIVATE; -} - -static int __f2fs_writepage(struct page *page, struct writeback_control *wbc, - void *data) -{ - struct address_space *mapping = data; - int ret = mapping->a_ops->writepage(page, wbc); - mapping_set_error(mapping, ret); - return ret; + unlock_page(page); + return err; } /* @@ -1313,8 +1303,7 @@ static int __f2fs_writepage(struct page *page, struct writeback_control *wbc, * warm/hot data page. */ static int f2fs_write_cache_pages(struct address_space *mapping, - struct writeback_control *wbc, writepage_t writepage, - void *data) + struct writeback_control *wbc) { int ret = 0; int done = 0; @@ -1396,16 +1385,11 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = (*writepage)(page, wbc, data); + ret = mapping->a_ops->writepage(page, wbc); if (unlikely(ret)) { - if (ret == AOP_WRITEPAGE_ACTIVATE) { - unlock_page(page); - ret = 0; - } else { - done_index = page->index + 1; - done = 1; - break; - } + done_index = page->index + 1; + done = 1; + break; } if (--wbc->nr_to_write <= 0 && @@ -1460,7 +1444,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, trace_f2fs_writepages(mapping->host, wbc, DATA); - ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); + ret = f2fs_write_cache_pages(mapping, wbc); /* * if some pages were truncated, we cannot guarantee its mapping->host * to detect pending bios. -- GitLab From 2b17d5e6fe930d742022b49287bb012359b8d3b8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 1 Jun 2016 21:18:25 -0700 Subject: [PATCH 0270/5498] f2fs: remove deprecated parameter Remove deprecated paramter. Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 4 ++-- fs/f2fs/f2fs.h | 2 +- fs/f2fs/inline.c | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index d13df3c7e22a..dfa9d29e51de 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -664,7 +664,7 @@ fail: return err; } -void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) +void f2fs_drop_nlink(struct inode *dir, struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); @@ -723,7 +723,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, mark_inode_dirty_sync(dir); if (inode) - f2fs_drop_nlink(dir, inode, NULL); + f2fs_drop_nlink(dir, inode); if (bit_pos == NR_DENTRY_IN_BLOCK && !truncate_hole(dir, page->index, page->index + 1)) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9c365f9e9e3c..dde46d83bf4a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1928,7 +1928,7 @@ struct page *init_inode_metadata(struct inode *, struct inode *, const struct qstr *, struct page *); void update_parent_metadata(struct inode *, struct inode *, unsigned int); int room_for_filename(const void *, int, int); -void f2fs_drop_nlink(struct inode *, struct inode *, struct page *); +void f2fs_drop_nlink(struct inode *, struct inode *); struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, struct page **); struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 1eb30431bf44..669f92ffd111 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -582,14 +582,13 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, &inline_dentry->dentry_bitmap); set_page_dirty(page); + f2fs_put_page(page, 1); dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty_sync(dir); if (inode) - f2fs_drop_nlink(dir, inode, page); - - f2fs_put_page(page, 1); + f2fs_drop_nlink(dir, inode); } bool f2fs_empty_inline_dir(struct inode *dir) -- GitLab From b5a1aae28b46a26fee8bf76c5cdde6aef5e568a1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Jun 2016 11:08:56 -0700 Subject: [PATCH 0271/5498] f2fs: avoid wrong count on dirty inodes The number should be covered by spin_lock. Otherwise we can see wrong count in f2fs_stat. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 604465318ecb..7261a7d9e57a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -632,8 +632,8 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) list_add_tail(&F2FS_I(inode)->gdirty_list, &sbi->inode_list[DIRTY_META]); inc_page_count(sbi, F2FS_DIRTY_IMETA); - spin_unlock(&sbi->inode_lock[DIRTY_META]); stat_inc_dirty_inode(sbi, DIRTY_META); + spin_unlock(&sbi->inode_lock[DIRTY_META]); } void f2fs_inode_synced(struct inode *inode) @@ -649,8 +649,8 @@ void f2fs_inode_synced(struct inode *inode) clear_inode_flag(inode, FI_DIRTY_INODE); clear_inode_flag(inode, FI_AUTO_RECOVER); dec_page_count(sbi, F2FS_DIRTY_IMETA); - spin_unlock(&sbi->inode_lock[DIRTY_META]); stat_dec_dirty_inode(F2FS_I_SB(inode), DIRTY_META); + spin_unlock(&sbi->inode_lock[DIRTY_META]); } static void f2fs_i_callback(struct rcu_head *head) -- GitLab From 9409b915f30c24757bddc3e3a17502840128f199 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Jun 2016 13:49:38 -0700 Subject: [PATCH 0272/5498] f2fs: remove obsolete parameter in f2fs_truncate We don't need lock parameter, which is always true. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 6 +++--- fs/f2fs/inode.c | 2 +- fs/f2fs/super.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dde46d83bf4a..7ec4b56e05ab 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1887,7 +1887,7 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags) int f2fs_sync_file(struct file *, loff_t, loff_t, int); void truncate_data_blocks(struct dnode_of_data *); int truncate_blocks(struct inode *, u64, bool); -int f2fs_truncate(struct inode *, bool); +int f2fs_truncate(struct inode *); int f2fs_getattr(struct vfsmount *, struct dentry *, struct kstat *); int f2fs_setattr(struct dentry *, struct iattr *); int truncate_hole(struct inode *, pgoff_t, pgoff_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f63406ec88da..79bdbb1882f5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -609,7 +609,7 @@ free_partial: return err; } -int f2fs_truncate(struct inode *inode, bool lock) +int f2fs_truncate(struct inode *inode) { int err; @@ -626,7 +626,7 @@ int f2fs_truncate(struct inode *inode, bool lock) return err; } - err = truncate_blocks(inode, i_size_read(inode), lock); + err = truncate_blocks(inode, i_size_read(inode), true); if (err) return err; @@ -690,7 +690,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_size <= i_size_read(inode)) { truncate_setsize(inode, attr->ia_size); - err = f2fs_truncate(inode, true); + err = f2fs_truncate(inode); if (err) return err; f2fs_balance_fs(F2FS_I_SB(inode), true); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 1d473e8b0ca1..7a6cfcfb4f99 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -354,7 +354,7 @@ void f2fs_evict_inode(struct inode *inode) i_size_write(inode, 0); retry: if (F2FS_HAS_BLOCKS(inode)) - err = f2fs_truncate(inode, true); + err = f2fs_truncate(inode); if (!err) { f2fs_lock_op(sbi); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7261a7d9e57a..2e5630493ff8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -580,7 +580,7 @@ static int f2fs_drop_inode(struct inode *inode) f2fs_i_size_write(inode, 0); if (F2FS_HAS_BLOCKS(inode)) - f2fs_truncate(inode, true); + f2fs_truncate(inode); sb_end_intwrite(inode->i_sb); -- GitLab From e0f23b92ffa4627c4ceed9f553aebde6d06ab7b3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Jun 2016 14:15:56 -0700 Subject: [PATCH 0273/5498] f2fs: avoid data race between FI_DIRTY_INODE flag and update_inode FI_DIRTY_INODE flag is not covered by inode page lock, so it can be unset at any time like below. Thread #1 Thread #2 - lock_page(ipage) - update i_fields - update i_size/i_blocks/and so on - set FI_DIRTY_INODE - reset FI_DIRTY_INODE - set_page_dirty(ipage) In this case, we can lose the latest i_field information. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 7a6cfcfb4f99..97de5b3d9d5d 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -230,6 +230,8 @@ int update_inode(struct inode *inode, struct page *node_page) { struct f2fs_inode *ri; + f2fs_inode_synced(inode); + f2fs_wait_on_page_writeback(node_page, NODE, true); ri = F2FS_INODE(node_page); @@ -264,7 +266,6 @@ int update_inode(struct inode *inode, struct page *node_page) __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); - f2fs_inode_synced(inode); /* deleted inode */ if (inode->i_nlink == 0) -- GitLab From 7fca598d2c2c5e44b4f18c6f3e3d481f4cc1f19d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Jun 2016 15:26:27 -0700 Subject: [PATCH 0274/5498] f2fs: fix wrong percentage This should be 1%, 10MB / 1GB. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 1f4f9d4569d9..2c2a797e18a8 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -23,7 +23,7 @@ #define MAX_RA_NODE 128 /* control the memory footprint threshold (10MB per 1GB ram) */ -#define DEF_RAM_THRESHOLD 10 +#define DEF_RAM_THRESHOLD 1 /* control dirty nats ratio threshold (default: 10% over max nid count) */ #define DEF_DIRTY_NAT_RATIO_THRESHOLD 10 -- GitLab From 8077e4efd4dfca687765ac0b7710a547c8bc728f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Jun 2016 15:24:24 -0700 Subject: [PATCH 0275/5498] f2fs: control not to exceed # of cached nat entries This is to avoid cache entry management overhead including radix tree. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ++++ fs/f2fs/node.h | 7 +++++++ fs/f2fs/segment.c | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7062d25570ac..e2814f389460 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -52,6 +52,10 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); + if (excess_cached_nats(sbi)) + res = false; + if (nm_i->nat_cnt > DEF_NAT_CACHE_THRESHOLD) + res = false; } else if (type == DIRTY_DENTS) { if (sbi->sb->s_bdi->dirty_exceeded) return false; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 2c2a797e18a8..673ce926cf09 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -27,6 +27,8 @@ /* control dirty nats ratio threshold (default: 10% over max nid count) */ #define DEF_DIRTY_NAT_RATIO_THRESHOLD 10 +/* control total # of nats */ +#define DEF_NAT_CACHE_THRESHOLD 100000 /* vector size for gang look-up from nat cache that consists of radix tree */ #define NATVEC_SIZE 64 @@ -126,6 +128,11 @@ static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi) NM_I(sbi)->dirty_nats_ratio / 100; } +static inline bool excess_cached_nats(struct f2fs_sb_info *sbi) +{ + return NM_I(sbi)->nat_cnt >= DEF_NAT_CACHE_THRESHOLD; +} + enum mem_type { FREE_NIDS, /* indicates the free nid list */ NAT_ENTRIES, /* indicates the cached nat entry */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 34a9159cf5ac..9011bffd1dd0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -345,6 +345,11 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) { if (!need) return; + + /* balance_fs_bg is able to be pending */ + if (excess_cached_nats(sbi)) + f2fs_balance_fs_bg(sbi); + /* * We should do GC or end up with checkpoint, if there are so many dirty * dir/node pages without enough free segments. -- GitLab From e4efc24cc753bd05ee0968663b0d172a09869459 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 3 Jun 2016 12:28:26 -0700 Subject: [PATCH 0276/5498] f2fs: set mapping error for EIO If EIO occurred, we need to set all the mapping to avoid any further IOs. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index de586abae4fc..6dd5c55da1c8 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1244,7 +1244,7 @@ write: /* we should bypass data pages to proceed the kworkder jobs */ if (unlikely(f2fs_cp_error(sbi))) { - SetPageError(page); + mapping_set_error(page->mapping, -EIO); goto out; } -- GitLab From eee6fcaed0baac9318c489844fe881d66d206b3a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 4 Jun 2016 14:21:28 -0700 Subject: [PATCH 0277/5498] f2fs: avoid reverse IO order for NODE and DATA There is a data race between allocate_data_block() and f2fs_sbumit_page_mbio(), which incur unnecessary reversed bio submission. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 6 ++++++ fs/f2fs/super.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7ec4b56e05ab..eb2303096985 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -797,6 +797,7 @@ struct f2fs_sb_info { /* for bio operations */ struct f2fs_bio_info read_io; /* for read bios */ struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ + struct mutex wio_mutex[NODE + 1]; /* bio ordering for NODE/DATA */ /* for checkpoint */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9011bffd1dd0..7b58bfbd84a3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1399,11 +1399,17 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { int type = __get_segment_type(fio->page, fio->type); + if (fio->type == NODE || fio->type == DATA) + mutex_lock(&fio->sbi->wio_mutex[fio->type]); + allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, &fio->new_blkaddr, sum, type); /* writeout dirty page into bdev */ f2fs_submit_page_mbio(fio); + + if (fio->type == NODE || fio->type == DATA) + mutex_unlock(&fio->sbi->wio_mutex[fio->type]); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2e5630493ff8..da2db958c270 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1422,6 +1422,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sbi->s_list); mutex_init(&sbi->umount_mutex); + mutex_init(&sbi->wio_mutex[NODE]); + mutex_init(&sbi->wio_mutex[DATA]); #ifdef CONFIG_F2FS_FS_ENCRYPTION memcpy(sbi->key_prefix, F2FS_KEY_DESC_PREFIX, -- GitLab From 04b9a4daf5fef2b5310ad84385b254de98efad58 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 4 Jun 2016 14:25:24 -0700 Subject: [PATCH 0278/5498] f2fs: drop any block plugging In f2fs, we don't need to keep block plugging for NODE and DATA writes, since we already merged bios as much as possible. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 ---- fs/f2fs/data.c | 17 ++++++++++------- fs/f2fs/gc.c | 5 ----- fs/f2fs/segment.c | 7 +------ 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a5bd3ee4cfe2..99e75e5b9fd2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -898,11 +898,8 @@ static int block_operations(struct f2fs_sb_info *sbi) .nr_to_write = LONG_MAX, .for_reclaim = 0, }; - struct blk_plug plug; int err = 0; - blk_start_plug(&plug); - retry_flush_dents: f2fs_lock_all(sbi); /* write all the dirty dentry pages */ @@ -939,7 +936,6 @@ retry_flush_nodes: goto retry_flush_nodes; } out: - blk_finish_plug(&plug); return err; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6dd5c55da1c8..5ce0eeaa88b3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -99,10 +99,13 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, } static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, - struct bio *bio) + struct bio *bio, enum page_type type) { - if (!is_read_io(rw)) + if (!is_read_io(rw)) { atomic_inc(&sbi->nr_wb_bios); + if (current->plug && (type == DATA || type == NODE)) + blk_finish_plug(current->plug); + } submit_bio(rw, bio); } @@ -118,7 +121,7 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) else trace_f2fs_submit_write_bio(io->sbi->sb, fio, io->bio); - __submit_bio(io->sbi, fio->rw, io->bio); + __submit_bio(io->sbi, fio->rw, io->bio, fio->type); io->bio = NULL; } @@ -236,7 +239,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) return -EFAULT; } - __submit_bio(fio->sbi, fio->rw, bio); + __submit_bio(fio->sbi, fio->rw, bio, fio->type); return 0; } @@ -1041,7 +1044,7 @@ got_it: */ if (bio && (last_block_in_bio != block_nr - 1)) { submit_and_realloc: - __submit_bio(F2FS_I_SB(inode), READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); bio = NULL; } if (bio == NULL) { @@ -1084,7 +1087,7 @@ set_error_page: goto next_page; confused: if (bio) { - __submit_bio(F2FS_I_SB(inode), READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); bio = NULL; } unlock_page(page); @@ -1094,7 +1097,7 @@ next_page: } BUG_ON(pages && !list_empty(pages)); if (bio) - __submit_bio(F2FS_I_SB(inode), READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); return 0; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 4a03076074af..67fd2855ccc9 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -777,7 +777,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, { struct page *sum_page; struct f2fs_summary_block *sum; - struct blk_plug plug; unsigned int segno = start_segno; unsigned int end_segno = start_segno + sbi->segs_per_sec; int seg_freed = 0; @@ -795,8 +794,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unlock_page(sum_page); } - blk_start_plug(&plug); - for (segno = start_segno; segno < end_segno; segno++) { /* find segment summary of victim */ sum_page = find_get_page(META_MAPPING(sbi), @@ -830,8 +827,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, f2fs_submit_merged_bio(sbi, (type == SUM_TYPE_NODE) ? NODE : DATA, WRITE); - blk_finish_plug(&plug); - if (gc_type == FG_GC) { while (start_segno < end_segno) if (get_valid_blocks(sbi, start_segno++, 1) == 0) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7b58bfbd84a3..eff046a792ad 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -379,13 +379,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) excess_prefree_segs(sbi) || excess_dirty_nats(sbi) || (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { - if (test_opt(sbi, DATA_FLUSH)) { - struct blk_plug plug; - - blk_start_plug(&plug); + if (test_opt(sbi, DATA_FLUSH)) sync_dirty_inodes(sbi, FILE_INODE); - blk_finish_plug(&plug); - } f2fs_sync_fs(sbi->sb, true); stat_inc_bg_cp_count(sbi->stat_info); } -- GitLab From dc93a72a0bd907b5001785950433af6ac2e50a74 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 6 Jun 2016 18:49:54 -0700 Subject: [PATCH 0279/5498] f2fs: skip clean segment for gc If a segment in a section is clean or prefreed, we don't need to get its summary and do gc. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 67fd2855ccc9..e1d274cdecb8 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -795,6 +795,10 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, } for (segno = start_segno; segno < end_segno; segno++) { + + if (get_valid_blocks(sbi, segno, 1) == 0) + continue; + /* find segment summary of victim */ sum_page = find_get_page(META_MAPPING(sbi), GET_SUM_BLOCK(sbi, segno)); -- GitLab From 00142a9069502356a2f70f8b3b9a6a9e5bd14aea Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 3 Jun 2016 19:29:38 -0700 Subject: [PATCH 0280/5498] f2fs: introduce mode=lfs mount option This mount option is to enable original log-structured filesystem forcefully. So, there should be no random writes for main area. Especially, this supports host-managed SMR device. Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 3 +++ fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 2 ++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 8 +++++++- fs/f2fs/recovery.c | 6 +++++- fs/f2fs/segment.c | 20 +++++++++++++++++++- fs/f2fs/segment.h | 7 +++++++ fs/f2fs/super.c | 28 ++++++++++++++++++++++++++++ 9 files changed, 74 insertions(+), 4 deletions(-) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 4090500d27f4..028ff62e5467 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -144,6 +144,9 @@ noinline_data Disable the inline data feature, inline data feature is enabled by default. data_flush Enable data flushing before checkpoint in order to persist data of regular and symlink. +mode=%s Control block allocation mode which supports "adaptive" + and "lfs". In "lfs" mode, there should be no random + writes towards main area. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 99e75e5b9fd2..fe540de91059 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -982,7 +982,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) * This avoids to conduct wrong roll-forward operations and uses * metapages, so should be called prior to sync_meta_pages below. */ - if (discard_next_dnode(sbi, discard_blk)) + if (!test_opt(sbi, LFS) && discard_next_dnode(sbi, discard_blk)) invalidate = true; /* Flush all the NAT/SIT pages */ diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5ce0eeaa88b3..7d4772f24f9d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1711,6 +1711,8 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return 0; + if (test_opt(F2FS_I_SB(inode), LFS)) + return 0; trace_f2fs_direct_IO_enter(inode, offset, count, rw); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index eb2303096985..415c6626f227 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -112,6 +112,8 @@ static inline bool time_to_inject(int type) #define F2FS_MOUNT_FORCE_FG_GC 0x00004000 #define F2FS_MOUNT_DATA_FLUSH 0x00008000 #define F2FS_MOUNT_FAULT_INJECTION 0x00010000 +#define F2FS_MOUNT_ADAPTIVE 0x00020000 +#define F2FS_MOUNT_LFS 0x00040000 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 79bdbb1882f5..d49c571c91ad 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -879,9 +879,15 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src, return full ? truncate_hole(inode, dst, dst + 1) : 0; if (do_replace) { - struct page *ipage = get_node_page(sbi, inode->i_ino); + struct page *ipage; struct node_info ni; + if (test_opt(sbi, LFS)) { + ret = -ENOTSUPP; + goto err_out; + } + + ipage = get_node_page(sbi, inode->i_ino); if (IS_ERR(ipage)) { ret = PTR_ERR(ipage); goto err_out; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 3c6425026c23..a214c365a959 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -624,8 +624,12 @@ out: if (err) { bool invalidate = false; - if (discard_next_dnode(sbi, blkaddr)) + if (test_opt(sbi, LFS)) { + update_meta_page(sbi, NULL, blkaddr); invalidate = true; + } else if (discard_next_dnode(sbi, blkaddr)) { + invalidate = true; + } /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index eff046a792ad..4792f94089f7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -707,6 +707,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int start = 0, end = -1; + unsigned int secno, start_segno; mutex_lock(&dirty_i->seglist_lock); @@ -726,8 +727,22 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (!test_opt(sbi, DISCARD)) continue; - f2fs_issue_discard(sbi, START_BLOCK(sbi, start), + if (!test_opt(sbi, LFS) || sbi->segs_per_sec == 1) { + f2fs_issue_discard(sbi, START_BLOCK(sbi, start), (end - start) << sbi->log_blocks_per_seg); + continue; + } +next: + secno = GET_SECNO(sbi, start); + start_segno = secno * sbi->segs_per_sec; + if (!IS_CURSEC(sbi, secno) && + !get_valid_blocks(sbi, start, sbi->segs_per_sec)) + f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno), + sbi->segs_per_sec << sbi->log_blocks_per_seg); + + start = start_segno + sbi->segs_per_sec; + if (start < end) + goto next; } mutex_unlock(&dirty_i->seglist_lock); @@ -1221,6 +1236,9 @@ void allocate_new_segments(struct f2fs_sb_info *sbi) { int i; + if (test_opt(sbi, LFS)) + return; + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) __allocate_new_segments(sbi, i); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 43c6d0bab247..1e71488975d1 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -469,6 +469,10 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + + if (test_opt(sbi, LFS)) + return false; + return free_sections(sbi) <= (node_secs + 2 * dent_secs + reserved_sections(sbi) + 1); } @@ -532,6 +536,9 @@ static inline bool need_inplace_update(struct inode *inode) if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) return false; + if (test_opt(sbi, LFS)) + return false; + if (policy & (0x1 << F2FS_IPU_FORCE)) return true; if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index da2db958c270..b5ec324d95bb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -94,6 +94,7 @@ enum { Opt_noextent_cache, Opt_noinline_data, Opt_data_flush, + Opt_mode, Opt_fault_injection, Opt_err, }; @@ -121,6 +122,7 @@ static match_table_t f2fs_tokens = { {Opt_noextent_cache, "noextent_cache"}, {Opt_noinline_data, "noinline_data"}, {Opt_data_flush, "data_flush"}, + {Opt_mode, "mode=%s"}, {Opt_fault_injection, "fault_injection=%u"}, {Opt_err, NULL}, }; @@ -502,6 +504,25 @@ static int parse_options(struct super_block *sb, char *options) case Opt_data_flush: set_opt(sbi, DATA_FLUSH); break; + case Opt_mode: + name = match_strdup(&args[0]); + + if (!name) + return -ENOMEM; + if (strlen(name) == 8 && + !strncmp(name, "adaptive", 8)) { + set_opt(sbi, ADAPTIVE); + clear_opt(sbi, LFS); + } else if (strlen(name) == 3 && + !strncmp(name, "lfs", 3)) { + clear_opt(sbi, ADAPTIVE); + set_opt(sbi, LFS); + } else { + kfree(name); + return -EINVAL; + } + kfree(name); + break; case Opt_fault_injection: if (args->from && match_int(args, &arg)) return -EINVAL; @@ -857,6 +878,12 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",noextent_cache"); if (test_opt(sbi, DATA_FLUSH)) seq_puts(seq, ",data_flush"); + + seq_puts(seq, ",mode="); + if (test_opt(sbi, ADAPTIVE)) + seq_puts(seq, "adaptive"); + else if (test_opt(sbi, LFS)) + seq_puts(seq, "lfs"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); return 0; @@ -939,6 +966,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, INLINE_DATA); set_opt(sbi, EXTENT_CACHE); set_opt(sbi, FLUSH_MERGE); + set_opt(sbi, ADAPTIVE); #ifdef CONFIG_F2FS_FS_XATTR set_opt(sbi, XATTR_USER); -- GitLab From e99ed6905552429eb6c9996cd5483c9dbb6f9c57 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Jun 2016 14:34:22 -0700 Subject: [PATCH 0281/5498] f2fs: fix deadlock in add_link failure mkdir sync_dirty_inode - init_inode_metadata - lock_page(node) - make_empty_dir - filemap_fdatawrite() - do_writepages - lock_page(data) - write_page(data) - lock_page(node) - f2fs_init_acl - error - truncate_inode_pages - lock_page(data) So, we don't need to truncate data pages in this error case, which will be done by f2fs_evict_inode. Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index dfa9d29e51de..5a4acf2dd22e 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -450,9 +450,6 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, return page; put_error: - /* truncate empty dir pages */ - truncate_inode_pages(&inode->i_data, 0); - clear_nlink(inode); update_inode(inode, page); f2fs_put_page(page, 1); -- GitLab From 02c67c38bd8530db8fdea2713eabbfeddda2750f Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Sat, 4 Jun 2016 22:01:28 +0800 Subject: [PATCH 0282/5498] f2fs: find parent dentry correctly If dotdot directory is corrupted, its slot may be ocupied by another file. In this case, dentry[1] is not the parent directory. Rename and cross-rename will update the inode in dentry[1] incorrectly. This patch finds dotdot dentry by name. Signed-off-by: Sheng Yong [Jaegeuk Kim: remove wron bug_on] Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 17 ++--------------- fs/f2fs/f2fs.h | 1 - fs/f2fs/inline.c | 19 ------------------- 3 files changed, 2 insertions(+), 35 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 5a4acf2dd22e..d7117192e973 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -267,22 +267,9 @@ out: struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p) { - struct page *page; - struct f2fs_dir_entry *de; - struct f2fs_dentry_block *dentry_blk; - - if (f2fs_has_inline_dentry(dir)) - return f2fs_parent_inline_dir(dir, p); - - page = get_lock_data_page(dir, 0, false); - if (IS_ERR(page)) - return NULL; + struct qstr dotdot = QSTR_INIT("..", 2); - dentry_blk = kmap(page); - de = &dentry_blk->dentry[1]; - *p = page; - unlock_page(page); - return de; + return f2fs_find_entry(dir, &dotdot, p); } ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 415c6626f227..02bf49983c2a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2308,7 +2308,6 @@ int f2fs_write_inline_data(struct inode *, struct page *); bool recover_inline_data(struct inode *, struct page *); struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct fscrypt_name *, struct page **); -struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **); int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, nid_t, umode_t); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 669f92ffd111..e10e958250ff 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -306,25 +306,6 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, return de; } -struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *dir, - struct page **p) -{ - struct f2fs_sb_info *sbi = F2FS_I_SB(dir); - struct page *ipage; - struct f2fs_dir_entry *de; - struct f2fs_inline_dentry *dentry_blk; - - ipage = get_node_page(sbi, dir->i_ino); - if (IS_ERR(ipage)) - return NULL; - - dentry_blk = inline_data_addr(ipage); - de = &dentry_blk->dentry[1]; - *p = ipage; - unlock_page(ipage); - return de; -} - int make_empty_inline_dir(struct inode *inode, struct inode *parent, struct page *ipage) { -- GitLab From 47241cc6a6c2182a51b681bbfe9296e6cd690be0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 9 Jun 2016 14:57:19 -0700 Subject: [PATCH 0283/5498] f2fs: report error for f2fs_parent_dir If there is no dentry, we can report its error correctly. Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index a630b1be3cc9..c46099cbde45 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -666,10 +666,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; if (S_ISDIR(old_inode->i_mode)) { - err = -EIO; old_dir_entry = f2fs_parent_dir(old_inode, &old_dir_page); - if (!old_dir_entry) + if (!old_dir_entry) { + err = PTR_ERR(old_dir_page); goto out_old; + } } if (flags & RENAME_WHITEOUT) { @@ -842,19 +843,21 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, /* prepare for updating ".." directory entry info later */ if (old_dir != new_dir) { if (S_ISDIR(old_inode->i_mode)) { - err = -EIO; old_dir_entry = f2fs_parent_dir(old_inode, &old_dir_page); - if (!old_dir_entry) + if (!old_dir_entry) { + err = PTR_ERR(old_dir_page); goto out_new; + } } if (S_ISDIR(new_inode->i_mode)) { - err = -EIO; new_dir_entry = f2fs_parent_dir(new_inode, &new_dir_page); - if (!new_dir_entry) + if (!new_dir_entry) { + err = PTR_ERR(new_dir_page); goto out_old_dir; + } } } -- GitLab From 5ad0519080b14ad809a52a6447fcdb99551da8e9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Jun 2016 18:27:02 -0700 Subject: [PATCH 0284/5498] f2fs: call update_inode_page for orphan inodes Let's store orphan inode pages right away. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 6 +++--- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 2 +- fs/f2fs/inode.c | 4 +--- fs/f2fs/namei.c | 4 ++-- fs/f2fs/super.c | 16 +--------------- 6 files changed, 9 insertions(+), 25 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index fe540de91059..c5501b517553 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -509,10 +509,11 @@ void release_orphan_inode(struct f2fs_sb_info *sbi) spin_unlock(&im->ino_lock); } -void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) +void add_orphan_inode(struct inode *inode) { /* add new orphan ino entry into list */ - __add_ino_entry(sbi, ino, ORPHAN_INO); + __add_ino_entry(F2FS_I_SB(inode), inode->i_ino, ORPHAN_INO); + update_inode_page(inode); } void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) @@ -536,7 +537,6 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) } clear_nlink(inode); - mark_inode_dirty_sync(inode); /* truncate all the data during iput */ iput(inode); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index d7117192e973..8ed270c2201f 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -666,7 +666,7 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode) up_write(&F2FS_I(inode)->i_sem); if (inode->i_nlink == 0) - add_orphan_inode(sbi, inode->i_ino); + add_orphan_inode(inode); else release_orphan_inode(sbi); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 02bf49983c2a..7ab302f81cd4 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2073,7 +2073,7 @@ bool exist_written_data(struct f2fs_sb_info *, nid_t, int); int f2fs_sync_inode_meta(struct f2fs_sb_info *); int acquire_orphan_inode(struct f2fs_sb_info *); void release_orphan_inode(struct f2fs_sb_info *); -void add_orphan_inode(struct f2fs_sb_info *, nid_t); +void add_orphan_inode(struct inode *); void remove_orphan_inode(struct f2fs_sb_info *, nid_t); int recover_orphan_inodes(struct f2fs_sb_info *); int get_valid_checkpoint(struct f2fs_sb_info *); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 97de5b3d9d5d..50e1708ed65e 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -392,8 +392,6 @@ no_delete: !exist_written_data(sbi, inode->i_ino, ORPHAN_INO)); out_clear: fscrypt_put_encryption_info(inode, NULL); - - f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE)); clear_inode(inode); } @@ -420,7 +418,7 @@ void handle_failed_inode(struct inode *inode) f2fs_msg(sbi->sb, KERN_WARNING, "Too many orphan inodes, run fsck to fix."); } else { - add_orphan_inode(sbi, inode->i_ino); + add_orphan_inode(inode); } alloc_nid_done(sbi, inode->i_ino); } else { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index c46099cbde45..1d6435a511f7 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -602,7 +602,7 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, * add this non-linked tmpfile to orphan list, in this way we could * remove all unused data of tmpfile after abnormal power-off. */ - add_orphan_inode(sbi, inode->i_ino); + add_orphan_inode(inode); alloc_nid_done(sbi, inode->i_ino); if (whiteout) { @@ -716,7 +716,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, up_write(&F2FS_I(new_inode)->i_sem); if (!new_inode->i_nlink) - add_orphan_inode(sbi, new_inode->i_ino); + add_orphan_inode(new_inode); else release_orphan_inode(sbi); } else { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b5ec324d95bb..7a0a890c4f81 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -575,8 +575,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) static int f2fs_drop_inode(struct inode *inode) { - int ret; - /* * This is to avoid a deadlock condition like below. * writeback_single_inode(inode) @@ -612,19 +610,7 @@ static int f2fs_drop_inode(struct inode *inode) return 0; } - ret = generic_drop_inode(inode); - if (is_inode_flag_set(inode, FI_DIRTY_INODE)) { - if (ret) - inode->i_state |= I_WILL_FREE; - spin_unlock(&inode->i_lock); - - update_inode_page(inode); - - spin_lock(&inode->i_lock); - if (ret) - inode->i_state &= ~I_WILL_FREE; - } - return ret; + return generic_drop_inode(inode); } /* -- GitLab From 4a0896b190728bdb7cfeb68592243a4a1ac70e9b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Jun 2016 09:47:48 -0700 Subject: [PATCH 0285/5498] f2fs: detect host-managed SMR by feature flag If mkfs.f2fs gives a feature flag for host-managed SMR, we can set mode=lfs by default. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- fs/f2fs/f2fs.h | 21 +++++++++++++++++++++ fs/f2fs/segment.c | 3 ++- fs/f2fs/super.c | 16 ++++++++++------ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 7d4772f24f9d..33564c22e40f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -103,7 +103,8 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, { if (!is_read_io(rw)) { atomic_inc(&sbi->nr_wb_bios); - if (current->plug && (type == DATA || type == NODE)) + if (f2fs_sb_mounted_hmsmr(sbi->sb) && + current->plug && (type == DATA || type == NODE)) blk_finish_plug(current->plug); } submit_bio(rw, bio); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7ab302f81cd4..c05a8aa651aa 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -134,6 +134,7 @@ struct f2fs_mount_info { }; #define F2FS_FEATURE_ENCRYPT 0x0001 +#define F2FS_FEATURE_HMSMR 0x0002 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -2367,6 +2368,26 @@ static inline int f2fs_sb_has_crypto(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); } +static inline int f2fs_sb_mounted_hmsmr(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_HMSMR); +} + +static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt) +{ + clear_opt(sbi, ADAPTIVE); + clear_opt(sbi, LFS); + + switch (mt) { + case F2FS_MOUNT_ADAPTIVE: + set_opt(sbi, ADAPTIVE); + break; + case F2FS_MOUNT_LFS: + set_opt(sbi, LFS); + break; + } +} + static inline bool f2fs_may_encrypt(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4792f94089f7..782975e791f1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2402,7 +2402,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); sm_info->rec_prefree_segments = sm_info->main_segments * DEF_RECLAIM_PREFREE_SEGMENTS / 100; - sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; + if (!test_opt(sbi, LFS)) + sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7a0a890c4f81..3d6cdb1a3a06 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -511,12 +511,10 @@ static int parse_options(struct super_block *sb, char *options) return -ENOMEM; if (strlen(name) == 8 && !strncmp(name, "adaptive", 8)) { - set_opt(sbi, ADAPTIVE); - clear_opt(sbi, LFS); + set_opt_mode(sbi, F2FS_MOUNT_ADAPTIVE); } else if (strlen(name) == 3 && !strncmp(name, "lfs", 3)) { - clear_opt(sbi, ADAPTIVE); - set_opt(sbi, LFS); + set_opt_mode(sbi, F2FS_MOUNT_LFS); } else { kfree(name); return -EINVAL; @@ -952,7 +950,12 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, INLINE_DATA); set_opt(sbi, EXTENT_CACHE); set_opt(sbi, FLUSH_MERGE); - set_opt(sbi, ADAPTIVE); + if (f2fs_sb_mounted_hmsmr(sbi->sb)) { + set_opt_mode(sbi, F2FS_MOUNT_LFS); + set_opt(sbi, DISCARD); + } else { + set_opt_mode(sbi, F2FS_MOUNT_ADAPTIVE); + } #ifdef CONFIG_F2FS_FS_XATTR set_opt(sbi, XATTR_USER); @@ -1601,6 +1604,8 @@ try_onemore: goto free_sbi; sb->s_fs_info = sbi; + sbi->raw_super = raw_super; + default_options(sbi); /* parse mount options */ options = kstrdup((const char *)data, GFP_KERNEL); @@ -1630,7 +1635,6 @@ try_onemore: memcpy(sb->s_uuid, raw_super->uuid, sizeof(raw_super->uuid)); /* init f2fs-specific super block info */ - sbi->raw_super = raw_super; sbi->valid_super_block = valid_super_block; mutex_init(&sbi->gc_mutex); mutex_init(&sbi->cp_mutex); -- GitLab From d7123feaa429821318b52a3c54d664e3c1134f60 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 16 Jun 2016 16:41:49 -0700 Subject: [PATCH 0286/5498] f2fs: produce more nids and reduce readahead nats The readahead nat pages are more likely to be reclaimed quickly, so it'd better to gather more free nids in advance. And, let's keep some free nids as much as possible. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 ++ fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 9 ++++++--- fs/f2fs/node.h | 5 +++-- fs/f2fs/segment.c | 4 +++- fs/f2fs/shrinker.c | 5 +++-- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c5501b517553..90aeb3ac6649 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -942,6 +942,8 @@ out: static void unblock_operations(struct f2fs_sb_info *sbi) { up_write(&sbi->node_write); + + build_free_nids(sbi); f2fs_unlock_all(sbi); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c05a8aa651aa..126dea3edb10 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1998,6 +1998,7 @@ void move_node_page(struct page *, int); int fsync_node_pages(struct f2fs_sb_info *, struct inode *, struct writeback_control *, bool); int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); +void build_free_nids(struct f2fs_sb_info *); bool alloc_nid(struct f2fs_sb_info *, nid_t *); void alloc_nid_done(struct f2fs_sb_info *, nid_t); void alloc_nid_failed(struct f2fs_sb_info *, nid_t); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index e2814f389460..78948b4a2032 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1771,7 +1771,7 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, } } -static void build_free_nids(struct f2fs_sb_info *sbi) +void build_free_nids(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); @@ -1780,7 +1780,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) nid_t nid = nm_i->next_scan_nid; /* Enough entries */ - if (nm_i->fcnt > NAT_ENTRY_PER_BLOCK) + if (nm_i->fcnt >= NAT_ENTRY_PER_BLOCK) return; /* readahead nat pages to be scanned */ @@ -1918,12 +1918,15 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) struct free_nid *i, *next; int nr = nr_shrink; + if (nm_i->fcnt <= MAX_FREE_NIDS) + return 0; + if (!mutex_trylock(&nm_i->build_lock)) return 0; spin_lock(&nm_i->free_nid_list_lock); list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) { - if (nr_shrink <= 0 || nm_i->fcnt <= NAT_ENTRY_PER_BLOCK) + if (nr_shrink <= 0 || nm_i->fcnt <= MAX_FREE_NIDS) break; if (i->state == NID_ALLOC) continue; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 673ce926cf09..fc7684554b1a 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -15,9 +15,10 @@ #define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK) /* # of pages to perform synchronous readahead before building free nids */ -#define FREE_NID_PAGES 4 +#define FREE_NID_PAGES 8 +#define MAX_FREE_NIDS (NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES) -#define DEF_RA_NID_PAGES 4 /* # of nid pages to be readaheaded */ +#define DEF_RA_NID_PAGES 0 /* # of nid pages to be readaheaded */ /* maximum readahead size for node during getting data blocks */ #define MAX_RA_NODE 128 diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 782975e791f1..6d16ecf9d29e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -371,7 +371,9 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK); if (!available_free_memory(sbi, FREE_NIDS)) - try_to_free_nids(sbi, NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES); + try_to_free_nids(sbi, MAX_FREE_NIDS); + else + build_free_nids(sbi); /* checkpoint is the only way to shrink partial cached entries */ if (!available_free_memory(sbi, NAT_ENTRIES) || diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index 93606f281bf9..46c915425923 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -13,6 +13,7 @@ #include #include "f2fs.h" +#include "node.h" static LIST_HEAD(f2fs_list); static DEFINE_SPINLOCK(f2fs_list_lock); @@ -25,8 +26,8 @@ static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi) static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) { - if (NM_I(sbi)->fcnt > NAT_ENTRY_PER_BLOCK) - return NM_I(sbi)->fcnt - NAT_ENTRY_PER_BLOCK; + if (NM_I(sbi)->fcnt > MAX_FREE_NIDS) + return NM_I(sbi)->fcnt - MAX_FREE_NIDS; return 0; } -- GitLab From 5e057668f7cd103d8f081e016071cfd0fd846184 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 16 Jun 2016 16:44:11 -0700 Subject: [PATCH 0287/5498] f2fs: avoid writing node/metapages during writes Let's keep more node/meta pages in run time. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 1e71488975d1..675418619554 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -714,9 +714,9 @@ static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type) if (type == DATA) return sbi->blocks_per_seg; else if (type == NODE) - return 3 * sbi->blocks_per_seg; + return 8 * sbi->blocks_per_seg; else if (type == META) - return MAX_BIO_BLOCKS(sbi); + return 8 * MAX_BIO_BLOCKS(sbi); else return 0; } @@ -735,7 +735,7 @@ static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type, nr_to_write = wbc->nr_to_write; if (type == NODE) - desired = 3 * max_hw_blocks(sbi); + desired = 2 * max_hw_blocks(sbi); else desired = MAX_BIO_BLOCKS(sbi); -- GitLab From 61ac01d79d2a217ffb25983d9049e4befd2e850b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 16 Jun 2016 17:03:23 -0700 Subject: [PATCH 0288/5498] f2fs: avoid latency-critical readahead of node pages The f2fs_map_blocks is very related to the performance, so let's avoid any latency to read ahead node pages. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- fs/f2fs/file.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 33564c22e40f..65ad7b1a3d54 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -653,7 +653,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, unsigned int maxblocks = map->m_len; struct dnode_of_data dn; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA; + int mode = create ? ALLOC_NODE : LOOKUP_NODE; pgoff_t pgofs, end_offset, end; int err = 0, ofs = 1; unsigned int ofs_in_node, last_ofs_in_node; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d49c571c91ad..aa9016d0b0aa 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -355,7 +355,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) for (; data_ofs < isize; data_ofs = (loff_t)pgofs << PAGE_SHIFT) { set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA); + err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE); if (err && err != -ENOENT) { goto fail; } else if (err == -ENOENT) { -- GitLab From f410b30a5f1012aa439ad81c0ab16eaa5987c44a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 3 Jul 2016 22:05:11 +0800 Subject: [PATCH 0289/5498] f2fs: fix to avoid reading out encrypted data in page cache For encrypted inode, if user overwrites data of the inode, f2fs will read encrypted data into page cache, and then do the decryption. However reader can race with overwriter, and it will see encrypted data which has not been decrypted by overwriter yet. Fix it by moving decrypting work to background and keep page non-uptodated until data is decrypted. Thread A Thread B - f2fs_file_write_iter - __generic_file_write_iter - generic_perform_write - f2fs_write_begin - f2fs_submit_page_bio - generic_file_read_iter - do_generic_file_read - lock_page_killable - unlock_page - copy_page_to_iter hit the encrypted data in updated page - lock_page - fscrypt_decrypt_page Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 90 ++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 65ad7b1a3d54..640cd19bc566 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -956,6 +956,37 @@ out: return ret; } +struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, + unsigned nr_pages) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct fscrypt_ctx *ctx = NULL; + struct block_device *bdev = sbi->sb->s_bdev; + struct bio *bio; + + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + ctx = fscrypt_get_ctx(inode, GFP_NOFS); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + /* wait the page to be moved by cleaning */ + f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); + } + + bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); + if (!bio) { + if (ctx) + fscrypt_release_ctx(ctx); + return ERR_PTR(-ENOMEM); + } + bio->bi_bdev = bdev; + bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(blkaddr); + bio->bi_end_io = f2fs_read_end_io; + bio->bi_private = ctx; + + return bio; +} + /* * This function was originally taken from fs/mpage.c, and customized for f2fs. * Major change was from block_size == page_size in f2fs by default. @@ -974,7 +1005,6 @@ static int f2fs_mpage_readpages(struct address_space *mapping, sector_t last_block; sector_t last_block_in_file; sector_t block_nr; - struct block_device *bdev = inode->i_sb->s_bdev; struct f2fs_map_blocks map; map.m_pblk = 0; @@ -1049,31 +1079,9 @@ submit_and_realloc: bio = NULL; } if (bio == NULL) { - struct fscrypt_ctx *ctx = NULL; - - if (f2fs_encrypted_inode(inode) && - S_ISREG(inode->i_mode)) { - - ctx = fscrypt_get_ctx(inode, GFP_NOFS); - if (IS_ERR(ctx)) - goto set_error_page; - - /* wait the page to be moved by cleaning */ - f2fs_wait_on_encrypted_page_writeback( - F2FS_I_SB(inode), block_nr); - } - - bio = bio_alloc(GFP_KERNEL, - min_t(int, nr_pages, bio_get_nr_vecs(bdev))); - if (!bio) { - if (ctx) - fscrypt_release_ctx(ctx); + bio = f2fs_grab_bio(inode, block_nr, nr_pages); + if (IS_ERR(bio)) goto set_error_page; - } - bio->bi_bdev = bdev; - bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(block_nr); - bio->bi_end_io = f2fs_read_end_io; - bio->bi_private = ctx; } if (bio_add_page(bio, page, blocksize, 0) < blocksize) @@ -1623,18 +1631,21 @@ repeat: if (blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_SIZE); } else { - struct f2fs_io_info fio = { - .sbi = sbi, - .type = DATA, - .rw = READ_SYNC, - .old_blkaddr = blkaddr, - .new_blkaddr = blkaddr, - .page = page, - .encrypted_page = NULL, - }; - err = f2fs_submit_page_bio(&fio); - if (err) + struct bio *bio; + + bio = f2fs_grab_bio(inode, blkaddr, 1); + if (IS_ERR(bio)) { + err = PTR_ERR(bio); goto fail; + } + + if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { + bio_put(bio); + err = -EFAULT; + goto fail; + } + + __submit_bio(sbi, READ_SYNC, bio, DATA); lock_page(page); if (unlikely(!PageUptodate(page))) { @@ -1645,13 +1656,6 @@ repeat: f2fs_put_page(page, 1); goto repeat; } - - /* avoid symlink page */ - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { - err = fscrypt_decrypt_page(page); - if (err) - goto fail; - } } out_update: SetPageUptodate(page); -- GitLab From 717a9e24c446ad01364c16fde0ec9004422618c7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 3 Jul 2016 22:05:12 +0800 Subject: [PATCH 0290/5498] f2fs: fix to detect truncation prior rather than EIO during read In procedure of synchonized read, after sending out the read request, reader will try to lock the page for waiting device to finish the read jobs and unlock the page, but meanwhile, truncater will race with reader, so after reader get lock of the page, it should check page's mapping to detect whether someone has truncated the page in advance, then reader has the chance to do the retry if truncation was done, otherwise read can be failed due to previous condition check. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 16 ++++++++-------- fs/f2fs/gc.c | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 640cd19bc566..3db275b2d2da 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -501,14 +501,14 @@ repeat: /* wait for read completion */ lock_page(page); - if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 1); - return ERR_PTR(-EIO); - } if (unlikely(page->mapping != mapping)) { f2fs_put_page(page, 1); goto repeat; } + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 1); + return ERR_PTR(-EIO); + } return page; } @@ -1648,14 +1648,14 @@ repeat: __submit_bio(sbi, READ_SYNC, bio, DATA); lock_page(page); - if (unlikely(!PageUptodate(page))) { - err = -EIO; - goto fail; - } if (unlikely(page->mapping != mapping)) { f2fs_put_page(page, 1); goto repeat; } + if (unlikely(!PageUptodate(page))) { + err = -EIO; + goto fail; + } } out_update: SetPageUptodate(page); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e1d274cdecb8..c9602d0dc57a 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -593,11 +593,11 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) /* write page */ lock_page(fio.encrypted_page); - if (unlikely(!PageUptodate(fio.encrypted_page))) { + if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) { err = -EIO; goto put_page_out; } - if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) { + if (unlikely(!PageUptodate(fio.encrypted_page))) { err = -EIO; goto put_page_out; } -- GitLab From ac541137e8f4cb59c6e46e27def31f031d6b5abb Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 3 Jul 2016 22:05:13 +0800 Subject: [PATCH 0291/5498] f2fs: fix to redirty page if fail to gc data page If we fail to move data page during foreground GC, we should give another chance to writeback that page which was set dirty previously by writer. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c9602d0dc57a..c61213785914 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -653,12 +653,23 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type) .page = page, .encrypted_page = NULL, }; + bool is_dirty = PageDirty(page); + int err; + +retry: set_page_dirty(page); f2fs_wait_on_page_writeback(page, DATA, true); if (clear_page_dirty_for_io(page)) inode_dec_dirty_pages(inode); + set_cold_data(page); - do_write_data_page(&fio); + + err = do_write_data_page(&fio); + if (err == -ENOMEM && is_dirty) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry; + } + clear_cold_data(page); } out: -- GitLab From bb534f1e5b1917b864e7294cec09561055159748 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 3 Jul 2016 22:05:14 +0800 Subject: [PATCH 0292/5498] f2fs: add nodiscard mount option This patch adds 'nodiscard' mount option. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: Documentation/filesystems/f2fs.txt --- Documentation/filesystems/f2fs.txt | 6 +++++- fs/f2fs/super.c | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 028ff62e5467..3e9ba9718dc1 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -106,7 +106,11 @@ background_gc=%s Turn on/off cleaning operations, namely garbage Default value for this option is on. So garbage collection is on by default. disable_roll_forward Disable the roll-forward recovery routine -discard Issue discard/TRIM commands when a segment is cleaned. +norecovery Disable the roll-forward recovery routine, mounted read- + only (i.e., -o ro,disable_roll_forward) +discard/nodiscard Enable/disable real-time discard in f2fs, if discard is + enabled, f2fs will issue discard/TRIM commands when a + segment is cleaned. no_heap Disable heap-style segment allocation which finds free segments for data from the beginning of main area, while for node from the end of main area. diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3d6cdb1a3a06..3d45ae2d8449 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -76,6 +76,7 @@ enum { Opt_disable_roll_forward, Opt_norecovery, Opt_discard, + Opt_nodiscard, Opt_noheap, Opt_user_xattr, Opt_nouser_xattr, @@ -104,6 +105,7 @@ static match_table_t f2fs_tokens = { {Opt_disable_roll_forward, "disable_roll_forward"}, {Opt_norecovery, "norecovery"}, {Opt_discard, "discard"}, + {Opt_nodiscard, "nodiscard"}, {Opt_noheap, "no_heap"}, {Opt_user_xattr, "user_xattr"}, {Opt_nouser_xattr, "nouser_xattr"}, @@ -422,6 +424,8 @@ static int parse_options(struct super_block *sb, char *options) "the device does not support discard"); } break; + case Opt_nodiscard: + clear_opt(sbi, DISCARD); case Opt_noheap: set_opt(sbi, NOHEAP); break; -- GitLab From 152e805bae48a68e6cf59f3fb58010985b549ebf Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 28 Jun 2016 07:27:59 +0800 Subject: [PATCH 0293/5498] f2fs: remove unnecessary goto statement When base_addr is NULL, there is no need to call kzfree, it should return -ENOMEM directly. Additionally, it is better to initialize variable 'error' with 0. Signed-off-by: Tiezhu Yang Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index bdd78436eae1..8961f8884d46 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -489,7 +489,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, int found, newsize; size_t len; __u32 new_hsize; - int error = -ENOMEM; + int error = 0; if (name == NULL) return -EINVAL; @@ -507,7 +507,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, base_addr = read_all_xattrs(inode, ipage); if (!base_addr) - goto exit; + return -ENOMEM; /* find entry with wanted name. */ here = __find_xattr(base_addr, index, len, name); -- GitLab From 657226fb62aa001d25e650f1ee98e6fde42b2c17 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 30 Jun 2016 18:40:10 -0700 Subject: [PATCH 0294/5498] f2fs: introduce f2fs_set_page_dirty_nobuffer This patch adds f2fs_set_page_dirty_nobuffer() copied from __set_page_dirty_buffer. When appending 4KB blocks in f2fs on pmem with multiple cores, this improves the overall performance. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 31 ++++++++++++++++++++++++++++++- fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 90aeb3ac6649..640cafe1752c 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -367,7 +367,7 @@ static int f2fs_set_meta_page_dirty(struct page *page) SetPageUptodate(page); if (!PageDirty(page)) { - __set_page_dirty_nobuffers(page); + f2fs_set_page_dirty_nobuffers(page); inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_META); SetPagePrivate(page); f2fs_trace_pid(page); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3db275b2d2da..380a3e506e4a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include "f2fs.h" @@ -1776,6 +1778,33 @@ int f2fs_release_page(struct page *page, gfp_t wait) return 1; } +/* + * This was copied from __set_page_dirty_buffers which gives higher performance + * in very high speed storages. (e.g., pmem) + */ +void f2fs_set_page_dirty_nobuffers(struct page *page) +{ + struct address_space *mapping = page->mapping; + unsigned long flags; + + if (unlikely(!mapping)) + return; + + spin_lock(&mapping->private_lock); + SetPageDirty(page); + spin_unlock(&mapping->private_lock); + + spin_lock_irqsave(&mapping->tree_lock, flags); + WARN_ON_ONCE(!PageUptodate(page)); + account_page_dirtied(page, mapping); + radix_tree_tag_set(&mapping->page_tree, + page_index(page), PAGECACHE_TAG_DIRTY); + spin_unlock_irqrestore(&mapping->tree_lock, flags); + + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); + return; +} + static int f2fs_set_data_page_dirty(struct page *page) { struct address_space *mapping = page->mapping; @@ -1798,7 +1827,7 @@ static int f2fs_set_data_page_dirty(struct page *page) } if (!PageDirty(page)) { - __set_page_dirty_nobuffers(page); + f2fs_set_page_dirty_nobuffers(page); update_dirty_page(inode, page); return 1; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 126dea3edb10..4637f2a0930f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2110,6 +2110,7 @@ struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool); int do_write_data_page(struct f2fs_io_info *); int f2fs_map_blocks(struct inode *, struct f2fs_map_blocks *, int, int); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); +void f2fs_set_page_dirty_nobuffers(struct page *); void f2fs_invalidate_page(struct page *, unsigned int, unsigned int); int f2fs_release_page(struct page *, gfp_t); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 78948b4a2032..a6f58dd80ea4 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1652,7 +1652,7 @@ static int f2fs_set_node_page_dirty(struct page *page) SetPageUptodate(page); if (!PageDirty(page)) { - __set_page_dirty_nobuffers(page); + f2fs_set_page_dirty_nobuffers(page); inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES); SetPagePrivate(page); f2fs_trace_pid(page); -- GitLab From 19e5babdc1f4ffeea9e9d3ae35d9e90ab6c59464 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 30 Jun 2016 18:49:15 -0700 Subject: [PATCH 0295/5498] f2fs: call SetPageUptodate if needed SetPageUptodate() issues memory barrier, resulting in performance degrdation. Let's avoid that. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/checkpoint.c | 6 ++++-- fs/f2fs/data.c | 18 ++++++++++++------ fs/f2fs/file.c | 3 ++- fs/f2fs/inline.c | 9 ++++++--- fs/f2fs/node.c | 9 ++++++--- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 640cafe1752c..4ccfa55c875c 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -48,7 +48,8 @@ repeat: goto repeat; } f2fs_wait_on_page_writeback(page, META, true); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); return page; } @@ -365,7 +366,8 @@ static int f2fs_set_meta_page_dirty(struct page *page) { trace_f2fs_set_page_dirty(page, META); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); if (!PageDirty(page)) { f2fs_set_page_dirty_nobuffers(page); inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_META); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 380a3e506e4a..f0bc19512186 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -48,7 +48,8 @@ static void f2fs_read_end_io(struct bio *bio, int err) struct page *page = bvec->bv_page; if (!err) { - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); } else { ClearPageUptodate(page); SetPageError(page); @@ -444,7 +445,8 @@ got_it: */ if (dn.data_blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_SIZE); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); unlock_page(page); return page; } @@ -555,7 +557,8 @@ struct page *get_new_data_page(struct inode *inode, if (dn.data_blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_SIZE); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); } else { f2fs_put_page(page, 1); @@ -1066,7 +1069,8 @@ got_it: } } else { zero_user_segment(page, 0, PAGE_SIZE); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); unlock_page(page); goto next_page; } @@ -1660,7 +1664,8 @@ repeat: } } out_update: - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); out_clear: clear_cold_data(page); return 0; @@ -1812,7 +1817,8 @@ static int f2fs_set_data_page_dirty(struct page *page) trace_f2fs_set_page_dirty(page, DATA); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); if (f2fs_is_atomic_file(inode)) { if (!IS_ATOMIC_WRITTEN_PAGE(page)) { diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index aa9016d0b0aa..cd2843e17973 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -82,7 +82,8 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, zero_user_segment(page, offset, PAGE_SIZE); } set_page_dirty(page); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); trace_f2fs_vm_page_mkwrite(page, DATA); mapped: diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e10e958250ff..2cd0edcc4ebc 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -59,7 +59,8 @@ void read_inline_data(struct page *page, struct page *ipage) memcpy(dst_addr, src_addr, MAX_INLINE_DATA); flush_dcache_page(page); kunmap_atomic(dst_addr); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); } bool truncate_inline_inode(struct page *ipage, u64 from) @@ -97,7 +98,8 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) else read_inline_data(page, ipage); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); f2fs_put_page(ipage, 1); unlock_page(page); return 0; @@ -370,7 +372,8 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, NR_INLINE_DENTRY * F2FS_SLOT_LEN); kunmap_atomic(dentry_blk); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); set_page_dirty(page); /* clear inline dir and flag after data writeback */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index a6f58dd80ea4..841e37f57299 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1045,7 +1045,8 @@ struct page *new_node_page(struct dnode_of_data *dn, f2fs_wait_on_page_writeback(page, NODE, true); fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true); set_cold_node(dn->inode, page); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); if (set_page_dirty(page)) dn->node_changed = true; @@ -1650,7 +1651,8 @@ static int f2fs_set_node_page_dirty(struct page *page) { trace_f2fs_set_page_dirty(page, NODE); - SetPageUptodate(page); + if (!PageUptodate(page)) + SetPageUptodate(page); if (!PageDirty(page)) { f2fs_set_page_dirty_nobuffers(page); inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES); @@ -2021,7 +2023,8 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) /* Should not use this inode from free nid list */ remove_free_nid(NM_I(sbi), ino); - SetPageUptodate(ipage); + if (!PageUptodate(ipage)) + SetPageUptodate(ipage); fill_node_footer(ipage, ino, ino, 0, true); src = F2FS_INODE(page); -- GitLab From 510dcbc5e29499a23df2611090295c33b59a8adc Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 30 Jun 2016 19:02:06 -0700 Subject: [PATCH 0296/5498] f2fs: shrink critical region in spin_lock This patch shrinks the critical region in spin_lock. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4637f2a0930f..2bf16ea81b12 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1167,30 +1167,23 @@ static inline void f2fs_i_blocks_write(struct inode *, blkcnt_t, bool); static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) { - block_t valid_block_count; - - spin_lock(&sbi->stat_lock); #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_BLOCK)) { - spin_unlock(&sbi->stat_lock); + if (time_to_inject(FAULT_BLOCK)) return false; - } #endif - valid_block_count = - sbi->total_valid_block_count + (block_t)(*count); - if (unlikely(valid_block_count > sbi->user_block_count)) { - *count = sbi->user_block_count - sbi->total_valid_block_count; + spin_lock(&sbi->stat_lock); + sbi->total_valid_block_count += (block_t)(*count); + if (unlikely(sbi->total_valid_block_count > sbi->user_block_count)) { + *count -= sbi->total_valid_block_count - sbi->user_block_count; + sbi->total_valid_block_count = sbi->user_block_count; if (!*count) { spin_unlock(&sbi->stat_lock); return false; } } - /* *count can be recalculated */ - f2fs_i_blocks_write(inode, *count, true); - sbi->total_valid_block_count = - sbi->total_valid_block_count + (block_t)(*count); spin_unlock(&sbi->stat_lock); + f2fs_i_blocks_write(inode, *count, true); percpu_counter_add(&sbi->alloc_valid_block_count, (*count)); return true; } @@ -1202,9 +1195,9 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count); f2fs_bug_on(sbi, inode->i_blocks < count); - f2fs_i_blocks_write(inode, count, false); sbi->total_valid_block_count -= (block_t)count; spin_unlock(&sbi->stat_lock); + f2fs_i_blocks_write(inode, count, false); } static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) -- GitLab From 6ae13915efa6bff5c877e812b2da3d98816c36ca Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 30 Jun 2016 19:04:16 -0700 Subject: [PATCH 0297/5498] f2fs: skip to check the block address of node page If the node page is up-to-date, it should be alive. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 841e37f57299..06d02db9b52f 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1080,6 +1080,9 @@ static int read_node_page(struct page *page, int rw) .encrypted_page = NULL, }; + if (PageUptodate(page)) + return LOCKED_PAGE; + get_node_info(sbi, page->index, &ni); if (unlikely(ni.blk_addr == NULL_ADDR)) { @@ -1087,9 +1090,6 @@ static int read_node_page(struct page *page, int rw) return -ENOENT; } - if (PageUptodate(page)) - return LOCKED_PAGE; - fio.new_blkaddr = fio.old_blkaddr = ni.blk_addr; return f2fs_submit_page_bio(&fio); } -- GitLab From 2b8fd3a6e654c5c301d425dfa5ad9a828085cee8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 6 Jul 2016 14:13:07 +0800 Subject: [PATCH 0298/5498] f2fs: fix incorrect f_bfree calculation in ->statfs As manual described, f_bfree indicates total free blocks in fs, in f2fs, it includes two parts: visible free blocks and over-provision blocks. This patch corrrects the calculation. fsblkcnt_t f_bfree; /* free blocks in fs */ Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3d45ae2d8449..2287d8d079a0 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -801,7 +801,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) 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_bfree = user_block_count - 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; -- GitLab From 9d7a8619bdfc294924dac9fea4154f1fe8851989 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 7 Jul 2016 12:13:33 +0800 Subject: [PATCH 0299/5498] f2fs: avoid mismatching block range for discard This patch skip discard block range smaller than trim_minlen, and can not be merged by neighbour Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6d16ecf9d29e..9e13db05e3f0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -672,6 +672,10 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) break; end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); + if (force && start && end != max_blocks + && (end - start) < cpc->trim_minlen) + continue; + __add_discard_entry(sbi, cpc, se, start, end); } } -- GitLab From 2c64eef15173bcb37ba864ea1747a05a8fa2c2b0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 7 Jul 2016 22:46:55 +0800 Subject: [PATCH 0300/5498] f2fs: fix to avoid redundant discard during fstrim With below test steps, f2fs will issue redundant discard when doing fstrim, the reason is that we issue discards for both prefree segments and consecutive freed region user wants to trim, part regions they covered are overlapped, here, we change to do not to issue any discards for prefree segments in trimmed range. 1. mount -t f2fs -o discard /dev/zram0 /mnt/f2fs 2. fstrim -o 0 -l 3221225472 -m 2097152 -v /mnt/f2fs/ 3. dd if=/dev/zero of=/mnt/f2fs/a bs=2M count=1 4. dd if=/dev/zero of=/mnt/f2fs/b bs=1M count=1 5. sync 6. rm /mnt/f2fs/a /mnt/f2fs/b 7. fstrim -o 0 -l 3221225472 -m 2097152 -v /mnt/f2fs/ Before: <...>-5428 [001] ...1 9511.052125: f2fs_issue_discard: dev = (251,0), blkstart = 0x2200, blklen = 0x200 <...>-5428 [001] ...1 9511.052787: f2fs_issue_discard: dev = (251,0), blkstart = 0x2200, blklen = 0x300 After: <...>-6764 [000] ...1 9720.382504: f2fs_issue_discard: dev = (251,0), blkstart = 0x2200, blklen = 0x300 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9e13db05e3f0..08f6c0be20cf 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -714,6 +714,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int start = 0, end = -1; unsigned int secno, start_segno; + bool force = (cpc->reason == CP_DISCARD); mutex_lock(&dirty_i->seglist_lock); @@ -730,7 +731,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) dirty_i->nr_dirty[PRE] -= end - start; - if (!test_opt(sbi, DISCARD)) + if (force || !test_opt(sbi, DISCARD)) continue; if (!test_opt(sbi, LFS) || sbi->segs_per_sec == 1) { @@ -754,7 +755,7 @@ next: /* send small discards */ list_for_each_entry_safe(entry, this, head, list) { - if (cpc->reason == CP_DISCARD && entry->len < cpc->trim_minlen) + if (force && entry->len < cpc->trim_minlen) goto skip; f2fs_issue_discard(sbi, entry->blkaddr, entry->len); cpc->trimmed += entry->len; -- GitLab From 15bb3ebf20109fff1a8d68cf8f772a9cac895a42 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 7 Jul 2016 10:06:23 -0700 Subject: [PATCH 0301/5498] f2fs: move i_size_write in f2fs_write_end We don't need to do i_size_write under page lock. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f0bc19512186..0e01005b5c03 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1686,11 +1686,11 @@ static int f2fs_write_end(struct file *file, trace_f2fs_write_end(inode, pos, len, copied); set_page_dirty(page); + f2fs_put_page(page, 1); if (pos + copied > i_size_read(inode)) f2fs_i_size_write(inode, pos + copied); - f2fs_put_page(page, 1); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return copied; } -- GitLab From d0a42a153035077738e6e02410c7a8545a216ed2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 30 Jun 2016 19:09:37 -0700 Subject: [PATCH 0302/5498] f2fs: avoid mark_inode_dirty Let's check inode's dirtiness before calling mark_inode_dirty. Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 2 +- fs/f2fs/dir.c | 6 +++--- fs/f2fs/extent_cache.c | 2 +- fs/f2fs/f2fs.h | 24 +++++++++++++----------- fs/f2fs/file.c | 8 ++++---- fs/f2fs/inline.c | 2 +- fs/f2fs/inode.c | 9 ++++++++- fs/f2fs/namei.c | 6 +++--- fs/f2fs/super.c | 37 +++++++++++++++++++++++-------------- fs/f2fs/xattr.c | 4 ++-- 10 files changed, 59 insertions(+), 41 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index d3160760c9a4..99f0530116e6 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -387,7 +387,7 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage, if (error) return error; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); if (default_acl) { error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl, diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 8ed270c2201f..67b386c98fd9 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -300,7 +300,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, set_page_dirty(page); dir->i_mtime = dir->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir); f2fs_put_page(page, 1); } @@ -452,7 +452,7 @@ void update_parent_metadata(struct inode *dir, struct inode *inode, clear_inode_flag(inode, FI_NEW_INODE); } dir->i_mtime = dir->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir); if (F2FS_I(dir)->i_current_depth != current_depth) f2fs_i_depth_write(dir, current_depth); @@ -704,7 +704,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, set_page_dirty(page); dir->i_ctime = dir->i_mtime = CURRENT_TIME; - mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir); if (inode) f2fs_drop_nlink(dir, inode); diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index e858869d76cb..5b4b6d426ebb 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -172,7 +172,7 @@ static void __drop_largest_extent(struct inode *inode, if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs) { largest->len = 0; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2bf16ea81b12..7016d01b4d1d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -537,12 +537,13 @@ static inline bool __is_front_mergeable(struct extent_info *cur, return __is_extent_mergeable(cur, front); } +extern void f2fs_mark_inode_dirty_sync(struct inode *); static inline void __try_update_largest_extent(struct inode *inode, struct extent_tree *et, struct extent_node *en) { if (en->ei.len > et->largest.len) { et->largest = en->ei; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } } @@ -1582,7 +1583,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, return; case FI_DATA_EXIST: case FI_INLINE_DOTS: - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } } @@ -1609,7 +1610,7 @@ static inline void set_acl_inode(struct inode *inode, umode_t mode) { F2FS_I(inode)->i_acl_mode = mode; set_inode_flag(inode, FI_ACL_MODE); - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static inline void f2fs_i_links_write(struct inode *inode, bool inc) @@ -1618,7 +1619,7 @@ static inline void f2fs_i_links_write(struct inode *inode, bool inc) inc_nlink(inode); else drop_nlink(inode); - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static inline void f2fs_i_blocks_write(struct inode *inode, @@ -1629,7 +1630,7 @@ static inline void f2fs_i_blocks_write(struct inode *inode, inode->i_blocks = add ? inode->i_blocks + diff : inode->i_blocks - diff; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); if (clean || recover) set_inode_flag(inode, FI_AUTO_RECOVER); } @@ -1643,7 +1644,7 @@ static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) return; i_size_write(inode, i_size); - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); if (clean || recover) set_inode_flag(inode, FI_AUTO_RECOVER); } @@ -1658,19 +1659,19 @@ static inline bool f2fs_skip_inode_update(struct inode *inode) static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth) { F2FS_I(inode)->i_current_depth = depth; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid) { F2FS_I(inode)->i_xattr_nid = xnid; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static inline void f2fs_i_pino_write(struct inode *inode, nid_t pino) { F2FS_I(inode)->i_pino = pino; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) @@ -1798,13 +1799,13 @@ static inline int is_file(struct inode *inode, int type) static inline void set_file(struct inode *inode, int type) { F2FS_I(inode)->i_advise |= type; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static inline void clear_file(struct inode *inode, int type) { F2FS_I(inode)->i_advise &= ~type; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static inline int f2fs_readonly(struct super_block *sb) @@ -1953,6 +1954,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) /* * super.c */ +int f2fs_inode_dirtied(struct inode *); void f2fs_inode_synced(struct inode *); int f2fs_commit_super(struct f2fs_sb_info *, bool); int f2fs_sync_fs(struct super_block *, int); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index cd2843e17973..eb4a1c40a50b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -264,7 +264,7 @@ sync_nodes: } if (need_inode_block_update(sbi, ino)) { - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); f2fs_write_inode(inode, NULL); goto sync_nodes; } @@ -632,7 +632,7 @@ int f2fs_truncate(struct inode *inode) return err; inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); return 0; } @@ -722,7 +722,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } } - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); return err; } @@ -1279,7 +1279,7 @@ static long f2fs_fallocate(struct file *file, int mode, if (!ret) { inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 2cd0edcc4ebc..d411ab6c7483 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -569,7 +569,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, f2fs_put_page(page, 1); dir->i_ctime = dir->i_mtime = CURRENT_TIME; - mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir); if (inode) f2fs_drop_nlink(dir, inode); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 50e1708ed65e..762f167fe014 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -18,6 +18,13 @@ #include +void f2fs_mark_inode_dirty_sync(struct inode *inode) +{ + if (f2fs_inode_dirtied(inode)) + return; + mark_inode_dirty_sync(inode); +} + void f2fs_set_inode_flags(struct inode *inode) { unsigned int flags = F2FS_I(inode)->i_flags; @@ -35,7 +42,7 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_DIRSYNC; inode_set_flags(inode, new_fl, S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 1d6435a511f7..b8d2a03260b2 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -761,7 +761,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, up_write(&F2FS_I(old_inode)->i_sem); old_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(old_inode); + f2fs_mark_inode_dirty_sync(old_inode); f2fs_delete_entry(old_entry, old_page, old_dir, NULL); @@ -913,7 +913,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(old_dir, old_nlink > 0); up_write(&F2FS_I(old_dir)->i_sem); } - mark_inode_dirty_sync(old_dir); + f2fs_mark_inode_dirty_sync(old_dir); /* update directory entry info of new dir inode */ f2fs_set_link(new_dir, new_entry, new_page, old_inode); @@ -928,7 +928,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(new_dir, new_nlink > 0); up_write(&F2FS_I(new_dir)->i_sem); } - mark_inode_dirty_sync(new_dir); + f2fs_mark_inode_dirty_sync(new_dir); f2fs_unlock_op(sbi); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2287d8d079a0..f245bea5479c 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -615,26 +615,14 @@ static int f2fs_drop_inode(struct inode *inode) return generic_drop_inode(inode); } -/* - * f2fs_dirty_inode() is called from __mark_inode_dirty() - * - * We should call set_dirty_inode to write the dirty inode through write_inode. - */ -static void f2fs_dirty_inode(struct inode *inode, int flags) +int f2fs_inode_dirtied(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - if (inode->i_ino == F2FS_NODE_INO(sbi) || - inode->i_ino == F2FS_META_INO(sbi)) - return; - - if (is_inode_flag_set(inode, FI_AUTO_RECOVER)) - clear_inode_flag(inode, FI_AUTO_RECOVER); - spin_lock(&sbi->inode_lock[DIRTY_META]); if (is_inode_flag_set(inode, FI_DIRTY_INODE)) { spin_unlock(&sbi->inode_lock[DIRTY_META]); - return; + return 1; } set_inode_flag(inode, FI_DIRTY_INODE); @@ -643,6 +631,8 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) inc_page_count(sbi, F2FS_DIRTY_IMETA); stat_inc_dirty_inode(sbi, DIRTY_META); spin_unlock(&sbi->inode_lock[DIRTY_META]); + + return 0; } void f2fs_inode_synced(struct inode *inode) @@ -662,6 +652,25 @@ void f2fs_inode_synced(struct inode *inode) spin_unlock(&sbi->inode_lock[DIRTY_META]); } +/* + * f2fs_dirty_inode() is called from __mark_inode_dirty() + * + * We should call set_dirty_inode to write the dirty inode through write_inode. + */ +static void f2fs_dirty_inode(struct inode *inode, int flags) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + if (inode->i_ino == F2FS_NODE_INO(sbi) || + inode->i_ino == F2FS_META_INO(sbi)) + return; + + if (is_inode_flag_set(inode, FI_AUTO_RECOVER)) + clear_inode_flag(inode, FI_AUTO_RECOVER); + + f2fs_inode_dirtied(inode); +} + static void f2fs_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 8961f8884d46..a0f16da9472e 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -153,7 +153,7 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, return -EINVAL; F2FS_I(inode)->i_advise |= *(char *)value; - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); return 0; } @@ -589,7 +589,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (index == F2FS_XATTR_INDEX_ENCRYPTION && !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) f2fs_set_encrypted_inode(inode); - mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode); exit: kzfree(base_addr); return error; -- GitLab From d6c78617fcb2f94814cfc6731c2ce6f329f8ed29 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Jul 2016 09:38:48 -0700 Subject: [PATCH 0303/5498] f2fs: fix ERR_PTR returned by bio This is to fix wrong error pointer handling flow reported by Dan. Reported-by: Dan Carpenter Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0e01005b5c03..23397054e6a6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1086,8 +1086,10 @@ submit_and_realloc: } if (bio == NULL) { bio = f2fs_grab_bio(inode, block_nr, nr_pages); - if (IS_ERR(bio)) + if (IS_ERR(bio)) { + bio = NULL; goto set_error_page; + } } if (bio_add_page(bio, page, blocksize, 0) < blocksize) -- GitLab From 7ea203836a2e4320f12b1ebcc5f43ce235f5b023 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 8 Jul 2016 17:42:21 -0700 Subject: [PATCH 0304/5498] f2fs: refactor __exchange_data_block for speed up This reduces the elapsed time to do xfstests/generic/017. Before: 715 s After: 458 s Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 236 +++++++++++++++++++++++++++++++++++-------------- fs/f2fs/node.c | 1 + 2 files changed, 171 insertions(+), 66 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index eb4a1c40a50b..e04aad504b4c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -851,85 +851,189 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) return ret; } -static int __exchange_data_block(struct inode *inode, pgoff_t src, - pgoff_t dst, bool full) +static int __read_out_blkaddrs(struct inode *inode, block_t *blkaddr, + int *do_replace, pgoff_t off, pgoff_t len) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct dnode_of_data dn; - block_t new_addr; - bool do_replace = false; - int ret; + int ret, done, i; +next_dnode: set_new_dnode(&dn, inode, NULL, NULL, 0); - ret = get_dnode_of_data(&dn, src, LOOKUP_NODE_RA); + ret = get_dnode_of_data(&dn, off, LOOKUP_NODE_RA); if (ret && ret != -ENOENT) { return ret; } else if (ret == -ENOENT) { - new_addr = NULL_ADDR; - } else { - new_addr = dn.data_blkaddr; - if (!is_checkpointed_data(sbi, new_addr)) { + if (dn.max_level == 0) + return -ENOENT; + done = min((pgoff_t)ADDRS_PER_BLOCK - dn.ofs_in_node, len); + blkaddr += done; + do_replace += done; + goto next; + } + + done = min((pgoff_t)ADDRS_PER_PAGE(dn.node_page, inode) - + dn.ofs_in_node, len); + for (i = 0; i < done; i++, blkaddr++, do_replace++, dn.ofs_in_node++) { + *blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + if (!is_checkpointed_data(sbi, *blkaddr)) { + + if (test_opt(sbi, LFS)) { + f2fs_put_dnode(&dn); + return -ENOTSUPP; + } + /* do not invalidate this block address */ f2fs_update_data_blkaddr(&dn, NULL_ADDR); - do_replace = true; + *do_replace = 1; } - f2fs_put_dnode(&dn); } + f2fs_put_dnode(&dn); +next: + len -= done; + off += done; + if (len) + goto next_dnode; + return 0; +} - if (new_addr == NULL_ADDR) - return full ? truncate_hole(inode, dst, dst + 1) : 0; +static int __roll_back_blkaddrs(struct inode *inode, block_t *blkaddr, + int *do_replace, pgoff_t off, int len) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + int ret, i; - if (do_replace) { - struct page *ipage; - struct node_info ni; + for (i = 0; i < len; i++, do_replace++, blkaddr++) { + if (*do_replace == 0) + continue; - if (test_opt(sbi, LFS)) { - ret = -ENOTSUPP; - goto err_out; + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = get_dnode_of_data(&dn, off + i, LOOKUP_NODE_RA); + if (ret) { + dec_valid_block_count(sbi, inode, 1); + invalidate_blocks(sbi, *blkaddr); + } else { + f2fs_update_data_blkaddr(&dn, *blkaddr); } + f2fs_put_dnode(&dn); + } + return 0; +} - ipage = get_node_page(sbi, inode->i_ino); - if (IS_ERR(ipage)) { - ret = PTR_ERR(ipage); - goto err_out; +static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode, + block_t *blkaddr, int *do_replace, + pgoff_t src, pgoff_t dst, pgoff_t len, bool full) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(src_inode); + pgoff_t i = 0; + int ret; + + while (i < len) { + if (blkaddr[i] == NULL_ADDR && !full) { + i++; + continue; } - set_new_dnode(&dn, inode, ipage, NULL, 0); - ret = f2fs_reserve_block(&dn, dst); - if (ret) - goto err_out; + if (do_replace[i] || blkaddr[i] == NULL_ADDR) { + struct dnode_of_data dn; + struct node_info ni; + size_t new_size; + pgoff_t ilen; - truncate_data_blocks_range(&dn, 1); + set_new_dnode(&dn, dst_inode, NULL, NULL, 0); + ret = get_dnode_of_data(&dn, dst + i, ALLOC_NODE); + if (ret) + return ret; - get_node_info(sbi, dn.nid, &ni); - f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr, - ni.version, true, false); - f2fs_put_dnode(&dn); - } else { - struct page *psrc, *pdst; + get_node_info(sbi, dn.nid, &ni); + ilen = min((pgoff_t) + ADDRS_PER_PAGE(dn.node_page, dst_inode) - + dn.ofs_in_node, len - i); + do { + dn.data_blkaddr = datablock_addr(dn.node_page, + dn.ofs_in_node); + truncate_data_blocks_range(&dn, 1); + + if (do_replace[i]) { + f2fs_i_blocks_write(src_inode, + 1, false); + f2fs_i_blocks_write(dst_inode, + 1, true); + f2fs_replace_block(sbi, &dn, dn.data_blkaddr, + blkaddr[i], ni.version, true, false); + + do_replace[i] = 0; + } + dn.ofs_in_node++; + i++; + new_size = (dst + i) << PAGE_SHIFT; + if (dst_inode->i_size < new_size) + f2fs_i_size_write(dst_inode, new_size); + } while ((do_replace[i] || blkaddr[i] == NULL_ADDR) && --ilen); - psrc = get_lock_data_page(inode, src, true); - if (IS_ERR(psrc)) - return PTR_ERR(psrc); - pdst = get_new_data_page(inode, NULL, dst, true); - if (IS_ERR(pdst)) { + f2fs_put_dnode(&dn); + } else { + struct page *psrc, *pdst; + + psrc = get_lock_data_page(src_inode, src + i, true); + if (IS_ERR(psrc)) + return PTR_ERR(psrc); + pdst = get_new_data_page(dst_inode, NULL, dst + i, + true); + if (IS_ERR(pdst)) { + f2fs_put_page(psrc, 1); + return PTR_ERR(pdst); + } + f2fs_copy_page(psrc, pdst); + set_page_dirty(pdst); + f2fs_put_page(pdst, 1); f2fs_put_page(psrc, 1); - return PTR_ERR(pdst); - } - f2fs_copy_page(psrc, pdst); - set_page_dirty(pdst); - f2fs_put_page(pdst, 1); - f2fs_put_page(psrc, 1); - return truncate_hole(inode, src, src + 1); + ret = truncate_hole(src_inode, src + i, src + i + 1); + if (ret) + return ret; + i++; + } } return 0; +} -err_out: - if (!get_dnode_of_data(&dn, src, LOOKUP_NODE)) { - f2fs_update_data_blkaddr(&dn, new_addr); - f2fs_put_dnode(&dn); +static int __exchange_data_block(struct inode *src_inode, + struct inode *dst_inode, pgoff_t src, pgoff_t dst, + int len, bool full) +{ + block_t *src_blkaddr; + int *do_replace; + int ret; + + src_blkaddr = f2fs_kvzalloc(sizeof(block_t) * len, GFP_KERNEL); + if (!src_blkaddr) + return -ENOMEM; + + do_replace = f2fs_kvzalloc(sizeof(int) * len, GFP_KERNEL); + if (!do_replace) { + kvfree(src_blkaddr); + return -ENOMEM; } + + ret = __read_out_blkaddrs(src_inode, src_blkaddr, do_replace, src, len); + if (ret) + goto roll_back; + + ret = __clone_blkaddrs(src_inode, dst_inode, src_blkaddr, + do_replace, src, dst, len, full); + if (ret) + goto roll_back; + + kvfree(src_blkaddr); + kvfree(do_replace); + return 0; + +roll_back: + __roll_back_blkaddrs(src_inode, src_blkaddr, do_replace, src, len); + kvfree(src_blkaddr); + kvfree(do_replace); return ret; } @@ -937,16 +1041,12 @@ static int f2fs_do_collapse(struct inode *inode, pgoff_t start, pgoff_t end) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); pgoff_t nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; - int ret = 0; + int ret; - for (; end < nrpages; start++, end++) { - f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); - ret = __exchange_data_block(inode, end, start, true); - f2fs_unlock_op(sbi); - if (ret) - break; - } + f2fs_balance_fs(sbi, true); + f2fs_lock_op(sbi); + ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true); + f2fs_unlock_op(sbi); return ret; } @@ -1135,7 +1235,7 @@ out: static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - pgoff_t pg_start, pg_end, delta, nrpages, idx; + pgoff_t nr, pg_start, pg_end, delta, idx; loff_t new_size; int ret = 0; @@ -1170,14 +1270,18 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) pg_start = offset >> PAGE_SHIFT; pg_end = (offset + len) >> PAGE_SHIFT; delta = pg_end - pg_start; - nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; + idx = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; + + while (!ret && idx > pg_start) { + nr = idx - pg_start; + if (nr > delta) + nr = delta; + idx -= nr; - for (idx = nrpages - 1; idx >= pg_start && idx != -1; idx--) { f2fs_lock_op(sbi); - ret = __exchange_data_block(inode, idx, idx + delta, false); + ret = __exchange_data_block(inode, inode, idx, + idx + delta, nr, false); f2fs_unlock_op(sbi); - if (ret) - break; } /* write out all moved pages, if possible */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 06d02db9b52f..2e813b84c905 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -650,6 +650,7 @@ release_out: if (err == -ENOENT) { dn->cur_level = i; dn->max_level = level; + dn->ofs_in_node = offset[level]; } return err; } -- GitLab From bd1920c34ae552f5733a8269599b89984b39a7a7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Jul 2016 11:07:52 -0700 Subject: [PATCH 0305/5498] f2fs: disable extent_cache for fcollapse/finsert inodes This reduces the elapsed time to do xfstests/generic/017. Before: 458 s After: 390 s Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 13 +++++++++++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 5 +++++ 3 files changed, 19 insertions(+) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 5b4b6d426ebb..2b06d4fcd954 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -631,6 +631,19 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode) return node_cnt; } +void f2fs_drop_extent_tree(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et = F2FS_I(inode)->extent_tree; + + set_inode_flag(inode, FI_NO_EXTENT); + + write_lock(&et->lock); + __free_extent_tree(sbi, et); + __drop_largest_extent(inode, 0, UINT_MAX); + write_unlock(&et->lock); +} + void f2fs_destroy_extent_tree(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7016d01b4d1d..da414c8ae678 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2330,6 +2330,7 @@ void f2fs_leave_shrinker(struct f2fs_sb_info *); */ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int); bool f2fs_init_extent_tree(struct inode *, struct f2fs_extent *); +void f2fs_drop_extent_tree(struct inode *); unsigned int f2fs_destroy_extent_node(struct inode *); void f2fs_destroy_extent_tree(struct inode *); bool f2fs_lookup_extent_cache(struct inode *, pgoff_t, struct extent_info *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e04aad504b4c..32f30f27bec3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1045,6 +1045,9 @@ static int f2fs_do_collapse(struct inode *inode, pgoff_t start, pgoff_t end) f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); + + f2fs_drop_extent_tree(inode); + ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true); f2fs_unlock_op(sbi); return ret; @@ -1279,6 +1282,8 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) idx -= nr; f2fs_lock_op(sbi); + f2fs_drop_extent_tree(inode); + ret = __exchange_data_block(inode, inode, idx, idx + delta, nr, false); f2fs_unlock_op(sbi); -- GitLab From 79ca62e5f1265bc0001324d0dcc3b5eb9e937b62 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 13 Jul 2016 18:23:35 -0700 Subject: [PATCH 0306/5498] f2fs: add maximum prefree segments In 1TB storage, we need to admit 22841 prefree segments, which can consume too much segments. This patch sets 8GB in max. prefree segments in that case. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 +++ fs/f2fs/segment.h | 1 + 2 files changed, 4 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 08f6c0be20cf..e87aa058f57a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2409,6 +2409,9 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); sm_info->rec_prefree_segments = sm_info->main_segments * DEF_RECLAIM_PREFREE_SEGMENTS / 100; + if (sm_info->rec_prefree_segments > DEF_MAX_RECLAIM_PREFREE_SEGMENTS) + sm_info->rec_prefree_segments = DEF_MAX_RECLAIM_PREFREE_SEGMENTS; + if (!test_opt(sbi, LFS)) sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 675418619554..7073bd1549ff 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -15,6 +15,7 @@ #define NULL_SECNO ((unsigned int)(~0)) #define DEF_RECLAIM_PREFREE_SEGMENTS 5 /* 5% over total segments */ +#define DEF_MAX_RECLAIM_PREFREE_SEGMENTS 4096 /* 8GB in maximum */ /* L: Logical segment # in volume, R: Relative segment # in main area */ #define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno) -- GitLab From 5b0030f5334a2a4f79fede26b2036fcc7066b971 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 13 Jul 2016 09:18:29 +0800 Subject: [PATCH 0307/5498] f2fs: fix to avoid data update racing between GC and DIO Datas in file can be operated by GC and DIO simultaneously, so we will face race case as below: For write case: Thread A Thread B - generic_file_direct_write - invalidate_inode_pages2_range - f2fs_direct_IO - do_blockdev_direct_IO - do_direct_IO - get_more_blocks - f2fs_gc - do_garbage_collect - gc_data_segment - move_data_page - do_write_data_page migrate data block to new block address - dio_bio_submit update user data to old block address For read case: Thread A Thread B - generic_file_direct_write - invalidate_inode_pages2_range - f2fs_direct_IO - do_blockdev_direct_IO - do_direct_IO - get_more_blocks - f2fs_balance_fs - f2fs_gc - do_garbage_collect - gc_data_segment - move_data_page - do_write_data_page migrate data block to new block address - write_checkpoint - do_checkpoint - clear_prefree_segments - f2fs_issue_discard discard old block adress - dio_bio_submit update user buffer from obsolete block address In order to fix this, for one file, we should let DIO and GC getting exclusion against with each other. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 3 +++ fs/f2fs/f2fs.h | 1 + fs/f2fs/gc.c | 20 ++++++++++++++++++++ fs/f2fs/super.c | 2 ++ 4 files changed, 26 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 23397054e6a6..82fdc0f2e773 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1730,7 +1730,10 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, trace_f2fs_direct_IO_enter(inode, offset, count, rw); + down_read(&F2FS_I(inode)->dio_rwsem[rw]); err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block_dio); + up_read(&F2FS_I(inode)->dio_rwsem[rw]); + if (rw & WRITE) { if (err > 0) set_inode_flag(inode, FI_UPDATE_WRITE); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index da414c8ae678..93afc8995145 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -485,6 +485,7 @@ struct f2fs_inode_info { struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct mutex inmem_lock; /* lock for inmemory pages */ struct extent_tree *extent_tree; /* cached extent_tree entry */ + struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ }; static inline void get_extent_info(struct extent_info *ext, diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c61213785914..5c8acf754513 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -755,12 +755,32 @@ next_step: /* phase 3 */ inode = find_gc_inode(gc_list, dni.ino); if (inode) { + struct f2fs_inode_info *fi = F2FS_I(inode); + bool locked = false; + + if (S_ISREG(inode->i_mode)) { + if (!down_write_trylock(&fi->dio_rwsem[READ])) + continue; + if (!down_write_trylock( + &fi->dio_rwsem[WRITE])) { + up_write(&fi->dio_rwsem[READ]); + continue; + } + locked = true; + } + start_bidx = start_bidx_of_node(nofs, inode) + ofs_in_node; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) move_encrypted_block(inode, start_bidx); else move_data_page(inode, start_bidx, gc_type); + + if (locked) { + up_write(&fi->dio_rwsem[WRITE]); + up_write(&fi->dio_rwsem[READ]); + } + stat_inc_data_blk_count(sbi, 1, gc_type); } } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f245bea5479c..8aabce4f9e5b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -569,6 +569,8 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) INIT_LIST_HEAD(&fi->gdirty_list); INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); + init_rwsem(&fi->dio_rwsem[READ]); + init_rwsem(&fi->dio_rwsem[WRITE]); /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; -- GitLab From 49fff39c7972e9b5fc297adcac1ac30f3d19d312 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 13 Jul 2016 19:33:19 -0700 Subject: [PATCH 0308/5498] f2fs: use blk_plug in all the possible paths This patch reverts 19a5f5e2ef37 (f2fs: drop any block plugging), and adds blk_plug in write paths additionally. The main reason is that blk_start_plug can be used to wake up from low-power mode before submitting further bios. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/file.c --- fs/f2fs/checkpoint.c | 7 +++++++ fs/f2fs/data.c | 3 +++ fs/f2fs/file.c | 6 +++++- fs/f2fs/gc.c | 5 +++++ fs/f2fs/node.c | 3 +++ fs/f2fs/segment.c | 7 ++++++- 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 4ccfa55c875c..5a3ee5e56519 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -266,6 +266,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + struct blk_plug plug; long diff, written; /* collect a number of dirty meta pages and write together */ @@ -278,7 +279,9 @@ static int f2fs_write_meta_pages(struct address_space *mapping, /* if mounting is failed, skip writing node pages */ mutex_lock(&sbi->cp_mutex); diff = nr_pages_to_write(sbi, META, wbc); + blk_start_plug(&plug); written = sync_meta_pages(sbi, META, wbc->nr_to_write); + blk_finish_plug(&plug); mutex_unlock(&sbi->cp_mutex); wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff); return 0; @@ -900,8 +903,11 @@ static int block_operations(struct f2fs_sb_info *sbi) .nr_to_write = LONG_MAX, .for_reclaim = 0, }; + struct blk_plug plug; int err = 0; + blk_start_plug(&plug); + retry_flush_dents: f2fs_lock_all(sbi); /* write all the dirty dentry pages */ @@ -938,6 +944,7 @@ retry_flush_nodes: goto retry_flush_nodes; } out: + blk_finish_plug(&plug); return err; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 82fdc0f2e773..93c82fca8c6f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1439,6 +1439,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, { struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct blk_plug plug; int ret; /* deal with chardevs and other special file */ @@ -1464,7 +1465,9 @@ static int f2fs_write_data_pages(struct address_space *mapping, trace_f2fs_writepages(mapping->host, wbc, DATA); + blk_start_plug(&plug); ret = f2fs_write_cache_pages(mapping, wbc); + blk_finish_plug(&plug); /* * if some pages were truncated, we cannot guarantee its mapping->host * to detect pending bios. diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 32f30f27bec3..1250f29cc1e2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2107,6 +2107,7 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = file_inode(file); size_t count = iov_iter_count(from); loff_t pos = iocb->ki_pos; + struct blk_plug plug; ssize_t ret; if (f2fs_encrypted_inode(inode) && @@ -2119,8 +2120,11 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (!ret) { ret = f2fs_preallocate_blocks(inode, pos, count, file->f_flags & O_DIRECT); - if (!ret) + if (!ret) { + blk_start_plug(&plug); ret = __generic_file_write_iter(iocb, from); + blk_finish_plug(&plug); + } } inode_unlock(inode); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 5c8acf754513..de6c41c32c62 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -808,6 +808,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, { struct page *sum_page; struct f2fs_summary_block *sum; + struct blk_plug plug; unsigned int segno = start_segno; unsigned int end_segno = start_segno + sbi->segs_per_sec; int seg_freed = 0; @@ -825,6 +826,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unlock_page(sum_page); } + blk_start_plug(&plug); + for (segno = start_segno; segno < end_segno; segno++) { if (get_valid_blocks(sbi, segno, 1) == 0) @@ -862,6 +865,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, f2fs_submit_merged_bio(sbi, (type == SUM_TYPE_NODE) ? NODE : DATA, WRITE); + blk_finish_plug(&plug); + if (gc_type == FG_GC) { while (start_segno < end_segno) if (get_valid_blocks(sbi, start_segno++, 1) == 0) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2e813b84c905..4070a55e7266 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1625,6 +1625,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + struct blk_plug plug; long diff; /* balancing f2fs's metadata in background */ @@ -1638,7 +1639,9 @@ static int f2fs_write_node_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; + blk_start_plug(&plug); sync_node_pages(sbi, wbc); + blk_finish_plug(&plug); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return 0; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e87aa058f57a..d45e6bbf8493 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -381,8 +381,13 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) excess_prefree_segs(sbi) || excess_dirty_nats(sbi) || (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { - if (test_opt(sbi, DATA_FLUSH)) + if (test_opt(sbi, DATA_FLUSH)) { + struct blk_plug plug; + + blk_start_plug(&plug); sync_dirty_inodes(sbi, FILE_INODE); + blk_finish_plug(&plug); + } f2fs_sync_fs(sbi->sb, true); stat_inc_bg_cp_count(sbi->stat_info); } -- GitLab From de62dad6f502c67fd01b5c0b1f7f242836ce90d0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 15 Jul 2016 19:25:47 +0800 Subject: [PATCH 0309/5498] f2fs: reset default idle interval value The default value of idle interval is 2 mins, but for most time when screen shutdown, there are still operations during the 2 mins interval, and gc's sleep time is about 30 secs to 60 secs, so there is almost no chance for GC thread to do garbage collecting. Set default value of idle interval value from 2 mins to 5 secs for fixing. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 93afc8995145..ce4c49e6a5b0 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -196,7 +196,7 @@ enum { #define BATCHED_TRIM_BLOCKS(sbi) \ (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) #define DEF_CP_INTERVAL 60 /* 60 secs */ -#define DEF_IDLE_INTERVAL 120 /* 2 mins */ +#define DEF_IDLE_INTERVAL 5 /* 5 secs */ struct cp_control { int reason; -- GitLab From 8f380dcfd29be4af415a012c472c9c0453b9dddd Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 16 Jul 2016 21:59:22 -0700 Subject: [PATCH 0310/5498] f2fs: avoid memory allocation failure due to a long length We need to avoid ENOMEM due to unexpected long length. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 1250f29cc1e2..16504fee051b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1001,33 +1001,43 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode, static int __exchange_data_block(struct inode *src_inode, struct inode *dst_inode, pgoff_t src, pgoff_t dst, - int len, bool full) + pgoff_t len, bool full) { block_t *src_blkaddr; int *do_replace; + pgoff_t olen; int ret; - src_blkaddr = f2fs_kvzalloc(sizeof(block_t) * len, GFP_KERNEL); - if (!src_blkaddr) - return -ENOMEM; + while (len) { + olen = min((pgoff_t)4 * ADDRS_PER_BLOCK, len); - do_replace = f2fs_kvzalloc(sizeof(int) * len, GFP_KERNEL); - if (!do_replace) { - kvfree(src_blkaddr); - return -ENOMEM; - } + src_blkaddr = f2fs_kvzalloc(sizeof(block_t) * olen, GFP_KERNEL); + if (!src_blkaddr) + return -ENOMEM; - ret = __read_out_blkaddrs(src_inode, src_blkaddr, do_replace, src, len); - if (ret) - goto roll_back; + do_replace = f2fs_kvzalloc(sizeof(int) * olen, GFP_KERNEL); + if (!do_replace) { + kvfree(src_blkaddr); + return -ENOMEM; + } - ret = __clone_blkaddrs(src_inode, dst_inode, src_blkaddr, - do_replace, src, dst, len, full); - if (ret) - goto roll_back; + ret = __read_out_blkaddrs(src_inode, src_blkaddr, + do_replace, src, olen); + if (ret) + goto roll_back; - kvfree(src_blkaddr); - kvfree(do_replace); + ret = __clone_blkaddrs(src_inode, dst_inode, src_blkaddr, + do_replace, src, dst, olen, full); + if (ret) + goto roll_back; + + src += olen; + dst += olen; + len -= olen; + + kvfree(src_blkaddr); + kvfree(do_replace); + } return 0; roll_back: -- GitLab From 823421f65794109a83b65707105463cae7a232e2 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 19 Jul 2016 08:27:47 +0800 Subject: [PATCH 0311/5498] f2fs: fix to report error number of f2fs_find_entry This patch fixes to report the right error number of f2fs_find_entry to its caller. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/namei.c --- fs/f2fs/dir.c | 10 +++++----- fs/f2fs/f2fs.h | 2 +- fs/f2fs/namei.c | 46 +++++++++++++++++++++++++++++++++++----------- fs/f2fs/recovery.c | 7 +++++-- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 67b386c98fd9..dc6dde9de62f 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -272,17 +272,17 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p) return f2fs_find_entry(dir, &dotdot, p); } -ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr) +ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr, + struct page **page) { ino_t res = 0; struct f2fs_dir_entry *de; - struct page *page; - de = f2fs_find_entry(dir, qstr, &page); + de = f2fs_find_entry(dir, qstr, page); if (de) { res = le32_to_cpu(de->ino); - f2fs_dentry_kunmap(dir, page); - f2fs_put_page(page, 0); + f2fs_dentry_kunmap(dir, *page); + f2fs_put_page(*page, 0); } return res; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ce4c49e6a5b0..5d17ee280d9f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1931,7 +1931,7 @@ void f2fs_drop_nlink(struct inode *, struct inode *); struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, struct page **); struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); -ino_t f2fs_inode_by_name(struct inode *, struct qstr *); +ino_t f2fs_inode_by_name(struct inode *, struct qstr *, struct page **); void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); int update_dent_inode(struct inode *, struct inode *, const struct qstr *); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index b8d2a03260b2..e080bbb0bda4 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -203,9 +203,13 @@ out: struct dentry *f2fs_get_parent(struct dentry *child) { struct qstr dotdot = QSTR_INIT("..", 2); - unsigned long ino = f2fs_inode_by_name(child->d_inode, &dotdot); - if (!ino) + struct page *page; + unsigned long ino = f2fs_inode_by_name(child->d_inode, &dotdot, &page); + if (!ino) { + if (IS_ERR(page)) + return ERR_CAST(page); return ERR_PTR(-ENOENT); + } return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino)); } @@ -332,8 +336,11 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) trace_f2fs_unlink_enter(dir, dentry); de = f2fs_find_entry(dir, &dentry->d_name, &page); - if (!de) + if (!de) { + if (IS_ERR(page)) + err = PTR_ERR(page); goto fail; + } f2fs_balance_fs(sbi, true); @@ -662,13 +669,17 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); - if (!old_entry) + if (!old_entry) { + if (IS_ERR(old_page)) + err = PTR_ERR(old_page); goto out; + } if (S_ISDIR(old_inode->i_mode)) { old_dir_entry = f2fs_parent_dir(old_inode, &old_dir_page); if (!old_dir_entry) { - err = PTR_ERR(old_dir_page); + if (IS_ERR(old_dir_page)) + err = PTR_ERR(old_dir_page); goto out_old; } } @@ -688,8 +699,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, err = -ENOENT; new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, &new_page); - if (!new_entry) + if (!new_entry) { + if (IS_ERR(new_page)) + err = PTR_ERR(new_page); goto out_whiteout; + } f2fs_balance_fs(sbi, true); @@ -747,7 +761,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) { - err = -EIO; + err = -ENOENT; + if (IS_ERR(old_page)) + err = PTR_ERR(old_page); f2fs_unlock_op(sbi); goto out_dir; } @@ -833,12 +849,18 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, return -EPERM; old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); - if (!old_entry) + if (!old_entry) { + if (IS_ERR(old_page)) + err = PTR_ERR(old_page); goto out; + } new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, &new_page); - if (!new_entry) + if (!new_entry) { + if (IS_ERR(new_page)) + err = PTR_ERR(new_page); goto out_old; + } /* prepare for updating ".." directory entry info later */ if (old_dir != new_dir) { @@ -846,7 +868,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, old_dir_entry = f2fs_parent_dir(old_inode, &old_dir_page); if (!old_dir_entry) { - err = PTR_ERR(old_dir_page); + if (IS_ERR(old_dir_page)) + err = PTR_ERR(old_dir_page); goto out_new; } } @@ -855,7 +878,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, new_dir_entry = f2fs_parent_dir(new_inode, &new_dir_page); if (!new_dir_entry) { - err = PTR_ERR(new_dir_page); + if (IS_ERR(new_dir_page)) + err = PTR_ERR(new_dir_page); goto out_old_dir; } } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index a214c365a959..648ce62a6b02 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -153,9 +153,12 @@ retry: f2fs_delete_entry(de, page, dir, einode); iput(einode); goto retry; + } else if (IS_ERR(page)) { + err = PTR_ERR(page); + } else { + err = __f2fs_add_link(dir, &name, inode, + inode->i_ino, inode->i_mode); } - err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode); - goto out; out_unmap_put: -- GitLab From 411b1f99aa55d2afebd251a1bcdd854db8c8a08f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 8 Jul 2016 15:16:47 -0700 Subject: [PATCH 0312/5498] f2fs: support an ioctl to move a range of data blocks This patch implements moving a range of data blocks from source file to destination file. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/file.c --- fs/f2fs/f2fs.h | 9 ++++ fs/f2fs/file.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5d17ee280d9f..2538b7954416 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -309,6 +309,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_GARBAGE_COLLECT _IO(F2FS_IOCTL_MAGIC, 6) #define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7) #define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8) +#define F2FS_IOC_MOVE_RANGE _IOWR(F2FS_IOCTL_MAGIC, 9, \ + struct f2fs_move_range) #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY @@ -328,6 +330,13 @@ struct f2fs_defragment { u64 len; }; +struct f2fs_move_range { + u32 dst_fd; /* destination fd */ + u64 pos_in; /* start position in src_fd */ + u64 pos_out; /* start position in dst_fd */ + u64 len; /* size to move */ +}; + /* * For INODE and NODE manager */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 16504fee051b..a363dc2de8ff 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "f2fs.h" #include "node.h" @@ -2071,6 +2073,133 @@ out: return err; } +static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, size_t len) +{ + struct inode *src = file_inode(file_in); + struct inode *dst = file_inode(file_out); + struct f2fs_sb_info *sbi = F2FS_I_SB(src); + size_t olen = len, dst_max_i_size = 0; + size_t dst_osize; + int ret; + + if (file_in->f_path.mnt != file_out->f_path.mnt || + src->i_sb != dst->i_sb) + return -EXDEV; + + if (unlikely(f2fs_readonly(src->i_sb))) + return -EROFS; + + if (S_ISDIR(src->i_mode) || S_ISDIR(dst->i_mode)) + return -EISDIR; + + if (f2fs_encrypted_inode(src) || f2fs_encrypted_inode(dst)) + return -EOPNOTSUPP; + + inode_lock(src); + if (src != dst) + inode_lock(dst); + + ret = -EINVAL; + if (pos_in + len > src->i_size || pos_in + len < pos_in) + goto out_unlock; + if (len == 0) + olen = len = src->i_size - pos_in; + if (pos_in + len == src->i_size) + len = ALIGN(src->i_size, F2FS_BLKSIZE) - pos_in; + if (len == 0) { + ret = 0; + goto out_unlock; + } + + dst_osize = dst->i_size; + if (pos_out + olen > dst->i_size) + dst_max_i_size = pos_out + olen; + + /* verify the end result is block aligned */ + if (!IS_ALIGNED(pos_in, F2FS_BLKSIZE) || + !IS_ALIGNED(pos_in + len, F2FS_BLKSIZE) || + !IS_ALIGNED(pos_out, F2FS_BLKSIZE)) + goto out_unlock; + + ret = f2fs_convert_inline_inode(src); + if (ret) + goto out_unlock; + + ret = f2fs_convert_inline_inode(dst); + if (ret) + goto out_unlock; + + /* write out all dirty pages from offset */ + ret = filemap_write_and_wait_range(src->i_mapping, + pos_in, pos_in + len); + if (ret) + goto out_unlock; + + ret = filemap_write_and_wait_range(dst->i_mapping, + pos_out, pos_out + len); + if (ret) + goto out_unlock; + + f2fs_balance_fs(sbi, true); + f2fs_lock_op(sbi); + ret = __exchange_data_block(src, dst, pos_in, + pos_out, len >> F2FS_BLKSIZE_BITS, false); + + if (!ret) { + if (dst_max_i_size) + f2fs_i_size_write(dst, dst_max_i_size); + else if (dst_osize != dst->i_size) + f2fs_i_size_write(dst, dst_osize); + } + f2fs_unlock_op(sbi); +out_unlock: + if (src != dst) + inode_unlock(dst); + inode_unlock(src); + return ret; +} + +static int f2fs_ioc_move_range(struct file *filp, unsigned long arg) +{ + struct f2fs_move_range range; + struct fd dst; + int err; + + if (!(filp->f_mode & FMODE_READ) || + !(filp->f_mode & FMODE_WRITE)) + return -EBADF; + + if (copy_from_user(&range, (struct f2fs_move_range __user *)arg, + sizeof(range))) + return -EFAULT; + + dst = fdget(range.dst_fd); + if (!dst.file) + return -EBADF; + + if (!(dst.file->f_mode & FMODE_WRITE)) { + err = -EBADF; + goto err_out; + } + + err = mnt_want_write_file(filp); + if (err) + goto err_out; + + err = f2fs_move_file_range(filp, range.pos_in, dst.file, + range.pos_out, range.len); + + mnt_drop_write_file(filp); + + if (copy_to_user((struct f2fs_move_range __user *)arg, + &range, sizeof(range))) + err = -EFAULT; +err_out: + fdput(dst); + return err; +} + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -2106,6 +2235,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_write_checkpoint(filp, arg); case F2FS_IOC_DEFRAGMENT: return f2fs_ioc_defragment(filp, arg); + case F2FS_IOC_MOVE_RANGE: + return f2fs_ioc_move_range(filp, arg); default: return -ENOTTY; } @@ -2174,6 +2305,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_WRITE_CHECKPOINT: case F2FS_IOC_DEFRAGMENT: break; + case F2FS_IOC_MOVE_RANGE: + break; default: return -ENOIOCTLCMD; } -- GitLab From e081f7224bf1787e45e59c41c8301a6d3d59bfb6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 19 Jul 2016 19:20:11 -0700 Subject: [PATCH 0313/5498] f2fs: avoid data race when deciding checkpoin in f2fs_sync_file When fs utilization is almost full, f2fs_sync_file should do checkpoint if there is not enough space for roll-forward later. (i.e. space_for_roll_forward) So, currently we have no lock for sbi->alloc_valid_block_count, resulting in race condition. In rare case, we can get -ENOSPC when doing roll-forward which triggers if (is_valid_blkaddr(sbi, dest, META_POR)) { if (src == NULL_ADDR) { err = reserve_new_block(&dn); f2fs_bug_on(sbi, err); ... } ... } in do_recover_data. So, this patch avoids that situation in advance. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2538b7954416..ed3462bcfe74 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1178,24 +1178,33 @@ static inline void f2fs_i_blocks_write(struct inode *, blkcnt_t, bool); static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) { + blkcnt_t diff; + #ifdef CONFIG_F2FS_FAULT_INJECTION if (time_to_inject(FAULT_BLOCK)) return false; #endif + /* + * let's increase this in prior to actual block count change in order + * for f2fs_sync_file to avoid data races when deciding checkpoint. + */ + percpu_counter_add(&sbi->alloc_valid_block_count, (*count)); + spin_lock(&sbi->stat_lock); sbi->total_valid_block_count += (block_t)(*count); if (unlikely(sbi->total_valid_block_count > sbi->user_block_count)) { - *count -= sbi->total_valid_block_count - sbi->user_block_count; + diff = sbi->total_valid_block_count - sbi->user_block_count; + *count -= diff; sbi->total_valid_block_count = sbi->user_block_count; if (!*count) { spin_unlock(&sbi->stat_lock); + percpu_counter_sub(&sbi->alloc_valid_block_count, diff); return false; } } spin_unlock(&sbi->stat_lock); f2fs_i_blocks_write(inode, *count, true); - percpu_counter_add(&sbi->alloc_valid_block_count, (*count)); return true; } -- GitLab From 15d7e7a527e871c2439d75a7ed42f9499335dc1f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 19 Jul 2016 19:30:06 -0700 Subject: [PATCH 0314/5498] f2fs: handle error case with f2fs_bug_on It's enough to show BUG or WARN by f2fs_bug_on for error case. Then, we don't need to remain corrupted filesystem. Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 648ce62a6b02..cb591900257a 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -482,6 +482,8 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, #endif /* We should not get -ENOSPC */ f2fs_bug_on(sbi, err); + if (err) + goto err; } /* Check the previous node page having this index */ -- GitLab From ae3dde6f76263df4a5b1981ec35db83ec167ed17 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Fri, 22 Jul 2016 19:08:31 +0800 Subject: [PATCH 0315/5498] f2fs: get victim segment again after new cp Previous selected segment may become free after write_checkpoint, if we do garbage collect on this segment, and then new_curseg happen to reuse it, it may cause f2fs_bug_on as below. panic+0x154/0x29c do_garbage_collect+0x15c/0xaf4 f2fs_gc+0x2dc/0x444 f2fs_balance_fs.part.22+0xcc/0x14c f2fs_balance_fs+0x28/0x34 f2fs_map_blocks+0x5ec/0x790 f2fs_preallocate_blocks+0xe0/0x100 f2fs_file_write_iter+0x64/0x11c new_sync_write+0xac/0x11c vfs_write+0x144/0x1e4 SyS_write+0x60/0xc0 Here, maybe we check sit and ssa type during reset_curseg. So, we check segment is stale or not, and select a new victim to avoid this. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index de6c41c32c62..06cfb94cc3db 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -908,10 +908,13 @@ gc_more: * enough free sections, we should flush dent/node blocks and do * garbage collections. */ - if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi)) + if (__get_victim(sbi, &segno, gc_type) || + prefree_segments(sbi)) { write_checkpoint(sbi, &cpc); - else if (has_not_enough_free_secs(sbi, 0)) + segno = NULL_SEGNO; + } else if (has_not_enough_free_secs(sbi, 0)) { write_checkpoint(sbi, &cpc); + } } if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type)) -- GitLab From 4d07654888e1ceb46be9324b5160dfb2a4f5ba82 Mon Sep 17 00:00:00 2001 From: Liu Xue Date: Fri, 26 Feb 2016 06:39:23 +0000 Subject: [PATCH 0316/5498] f2fs: recovery missing dot dentries in root directory If f2fs was corrupted with missing dot dentries in root dirctory, it needs to recover them after fsck.f2fs set F2FS_INLINE_DOTS flag in directory inode when fsck.f2fs detects missing dot dentries. Signed-off-by: Xue Liu Signed-off-by: Yong Sheng Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index e080bbb0bda4..2f5d58321212 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -271,6 +271,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, struct page *page; nid_t ino; int err = 0; + unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir)); if (f2fs_encrypted_inode(dir)) { int res = fscrypt_get_encryption_info(dir); @@ -305,6 +306,12 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (IS_ERR(inode)) return ERR_CAST(inode); + if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) { + err = __recover_dot_dentries(dir, root_ino); + if (err) + goto err_out; + } + if (f2fs_has_inline_dots(inode)) { err = __recover_dot_dentries(inode, dir->i_ino); if (err) -- GitLab From aa82c36a0aa69e8a63ddd7a7cef48913696484d6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 22 Jul 2016 16:48:19 -0700 Subject: [PATCH 0317/5498] f2fs: adjust other changes This patch changes: - d_inode - file_dentry - inode_nohighmem - ... Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 3 --- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 31 +++++++++++++++++++++---------- fs/f2fs/file.c | 18 +++++++++--------- fs/f2fs/inode.c | 1 + fs/f2fs/namei.c | 25 +++++++++++++------------ fs/f2fs/xattr.c | 10 +++++----- 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 99f0530116e6..4dcc9e28dc5c 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -190,9 +190,6 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type, acl = ERR_PTR(retval); kfree(value); - if (!IS_ERR(acl)) - set_cached_acl(inode, type, acl); - return acl; } diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index dc6dde9de62f..e6abd31ccd4f 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -694,7 +694,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, dentry_blk = page_address(page); bit_pos = dentry - dentry_blk->dentry; for (i = 0; i < slots; i++) - test_and_clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); + clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); /* Let's check and deallocate this dentry page */ bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ed3462bcfe74..9b2af5c57f92 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -174,6 +174,16 @@ static inline bool wq_has_sleeper(wait_queue_head_t *wq) return waitqueue_active(wq); } +static inline struct dentry *file_dentry(const struct file *file) +{ + return file->f_path.dentry; +} + +static inline void inode_nohighmem(struct inode *inode) +{ + mapping_set_gfp_mask(inode->i_mapping, GFP_USER); +} + /* * For checkpoint manager */ @@ -290,15 +300,6 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_GETFLAGS FS_IOC_GETFLAGS #define F2FS_IOC_SETFLAGS FS_IOC_SETFLAGS #define F2FS_IOC_GETVERSION FS_IOC_GETVERSION -#define FS_IOC_SHUTDOWN _IOR('X', 125, __u32) /* Shutdown */ - -/* - * Flags for going down operation used by FS_IOC_GOINGDOWN - */ -#define FS_GOING_DOWN_FULLSYNC 0x0 /* going down with full sync */ -#define FS_GOING_DOWN_METASYNC 0x1 /* going down with metadata */ -#define FS_GOING_DOWN_NOSYNC 0x2 /* going down */ -#define FS_GOING_DOWN_METAFLUSH 0x3 /* going down with meta flush */ #define F2FS_IOCTL_MAGIC 0xf5 #define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) @@ -316,6 +317,16 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT +/* + * should be same as XFS_IOC_GOINGDOWN. + * Flags for going down operation used by FS_IOC_GOINGDOWN + */ +#define F2FS_IOC_SHUTDOWN _IOR('X', 125, __u32) /* Shutdown */ +#define F2FS_GOING_DOWN_FULLSYNC 0x0 /* going down with full sync */ +#define F2FS_GOING_DOWN_METASYNC 0x1 /* going down with metadata */ +#define F2FS_GOING_DOWN_NOSYNC 0x2 /* going down */ +#define F2FS_GOING_DOWN_METAFLUSH 0x3 /* going down with meta flush */ + #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* * ioctl commands in 32 bit emulation @@ -1966,7 +1977,7 @@ bool f2fs_empty_dir(struct inode *); static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) { - return __f2fs_add_link(dentry->d_parent->d_inode, &dentry->d_name, + return __f2fs_add_link(d_inode(dentry->d_parent), &dentry->d_name, inode, inode->i_ino, inode->i_mode); } diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a363dc2de8ff..cfef80d2e115 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -456,7 +456,7 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) if (!fscrypt_has_encryption_key(inode)) return -ENOKEY; } - dir = dget_parent(filp->f_path.dentry); + dir = dget_parent(file_dentry(filp)); if (f2fs_encrypted_inode(d_inode(dir)) && !fscrypt_has_permitted_context(d_inode(dir), inode)) { dput(dir); @@ -641,7 +641,7 @@ int f2fs_truncate(struct inode *inode) int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); generic_fillattr(inode, stat); stat->blocks <<= 3; return 0; @@ -679,7 +679,7 @@ static void __setattr_copy(struct inode *inode, const struct iattr *attr) int f2fs_setattr(struct dentry *dentry, struct iattr *attr) { - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); int err; err = inode_change_ok(inode, attr); @@ -1682,22 +1682,22 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) return ret; switch (in) { - case FS_GOING_DOWN_FULLSYNC: + case F2FS_GOING_DOWN_FULLSYNC: sb = freeze_bdev(sb->s_bdev); if (sb && !IS_ERR(sb)) { f2fs_stop_checkpoint(sbi, false); thaw_bdev(sb->s_bdev, sb); } break; - case FS_GOING_DOWN_METASYNC: + case F2FS_GOING_DOWN_METASYNC: /* do checkpoint only */ f2fs_sync_fs(sb, 1); f2fs_stop_checkpoint(sbi, false); break; - case FS_GOING_DOWN_NOSYNC: + case F2FS_GOING_DOWN_NOSYNC: f2fs_stop_checkpoint(sbi, false); break; - case FS_GOING_DOWN_METAFLUSH: + case F2FS_GOING_DOWN_METAFLUSH: sync_meta_pages(sbi, META, LONG_MAX); f2fs_stop_checkpoint(sbi, false); break; @@ -2219,7 +2219,7 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_release_volatile_write(filp); case F2FS_IOC_ABORT_VOLATILE_WRITE: return f2fs_ioc_abort_volatile_write(filp); - case FS_IOC_SHUTDOWN: + case F2FS_IOC_SHUTDOWN: return f2fs_ioc_shutdown(filp, arg); case FITRIM: return f2fs_ioc_fitrim(filp, arg); @@ -2297,7 +2297,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_START_VOLATILE_WRITE: case F2FS_IOC_RELEASE_VOLATILE_WRITE: case F2FS_IOC_ABORT_VOLATILE_WRITE: - case FS_IOC_SHUTDOWN: + case F2FS_IOC_SHUTDOWN: case F2FS_IOC_SET_ENCRYPTION_POLICY: case F2FS_IOC_GET_ENCRYPTION_PWSALT: case F2FS_IOC_GET_ENCRYPTION_POLICY: diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 762f167fe014..9604e4d3d120 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -214,6 +214,7 @@ make_now: inode->i_op = &f2fs_encrypted_symlink_inode_operations; else inode->i_op = &f2fs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &f2fs_dblock_aops; } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 2f5d58321212..37a9092c84e4 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -168,7 +168,7 @@ out: static int f2fs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { - struct inode *inode = old_dentry->d_inode; + struct inode *inode = d_inode(old_dentry); struct f2fs_sb_info *sbi = F2FS_I_SB(dir); int err; @@ -204,20 +204,20 @@ struct dentry *f2fs_get_parent(struct dentry *child) { struct qstr dotdot = QSTR_INIT("..", 2); struct page *page; - unsigned long ino = f2fs_inode_by_name(child->d_inode, &dotdot, &page); + unsigned long ino = f2fs_inode_by_name(d_inode(child), &dotdot, &page); if (!ino) { if (IS_ERR(page)) return ERR_CAST(page); return ERR_PTR(-ENOENT); } - return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino)); + return d_obtain_alias(f2fs_iget(child->d_sb, ino)); } static int __recover_dot_dentries(struct inode *dir, nid_t pino) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); - struct qstr dot = {.len = 1, .name = "."}; - struct qstr dotdot = {.len = 2, .name = ".."}; + struct qstr dot = QSTR_INIT(".", 1); + struct qstr dotdot = QSTR_INIT("..", 2); struct f2fs_dir_entry *de; struct page *page; int err = 0; @@ -335,7 +335,7 @@ err_out: static int f2fs_unlink(struct inode *dir, struct dentry *dentry) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); struct f2fs_dir_entry *de; struct page *page; int err = -ENOENT; @@ -424,6 +424,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, inode->i_op = &f2fs_encrypted_symlink_inode_operations; else inode->i_op = &f2fs_symlink_inode_operations; + inode_nohighmem(inode); inode->i_mapping->a_ops = &f2fs_dblock_aops; f2fs_balance_fs(sbi, true); @@ -537,7 +538,7 @@ out_fail: static int f2fs_rmdir(struct inode *dir, struct dentry *dentry) { - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); if (f2fs_empty_dir(inode)) return f2fs_unlink(dir, dentry); return -ENOTEMPTY; @@ -658,8 +659,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, unsigned int flags) { struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir); - struct inode *old_inode = old_dentry->d_inode; - struct inode *new_inode = new_dentry->d_inode; + struct inode *old_inode = d_inode(old_dentry); + struct inode *new_inode = d_inode(new_dentry); struct inode *whiteout = NULL; struct page *old_dir_page; struct page *old_page, *new_page = NULL; @@ -840,8 +841,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir); - struct inode *old_inode = old_dentry->d_inode; - struct inode *new_inode = new_dentry->d_inode; + struct inode *old_inode = d_inode(old_dentry); + struct inode *new_inode = d_inode(new_dentry); struct page *old_dir_page, *new_dir_page; struct page *old_page, *new_page; struct f2fs_dir_entry *old_dir_entry = NULL, *new_dir_entry = NULL; @@ -1020,7 +1021,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, struct fscrypt_str cstr = FSTR_INIT(NULL, 0); struct fscrypt_str pstr = FSTR_INIT(NULL, 0); struct fscrypt_symlink_data *sd; - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); u32 max_size = inode->i_sb->s_blocksize; int res; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index a0f16da9472e..8191ce6a6260 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -83,7 +83,7 @@ static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name, } if (strcmp(name, "") == 0) return -EINVAL; - return f2fs_getxattr(dentry->d_inode, type, name, buffer, size, NULL); + return f2fs_getxattr(d_inode(dentry), type, name, buffer, size, NULL); } static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, @@ -108,7 +108,7 @@ static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, if (strcmp(name, "") == 0) return -EINVAL; - return f2fs_setxattr(dentry->d_inode, type, name, + return f2fs_setxattr(d_inode(dentry), type, name, value, size, NULL, flags); } @@ -130,7 +130,7 @@ static size_t f2fs_xattr_advise_list(struct dentry *dentry, char *list, static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name, void *buffer, size_t size, int type) { - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); if (strcmp(name, "") != 0) return -EINVAL; @@ -143,7 +143,7 @@ static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name, static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, const void *value, size_t size, int flags, int type) { - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); if (strcmp(name, "") != 0) return -EINVAL; @@ -445,7 +445,7 @@ cleanup: ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) { - struct inode *inode = dentry->d_inode; + struct inode *inode = d_inode(dentry); struct f2fs_xattr_entry *entry; void *base_addr; int error = 0; -- GitLab From 248b7984fe158c44e84696b79a4c32125b133f6e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 6 Aug 2016 21:09:41 +0800 Subject: [PATCH 0318/5498] Revert "f2fs: move i_size_write in f2fs_write_end" This reverts commit a2ee0a300344a6da76186129b078113354fe13d2. When testing with generic/032 of xfstest suit, failure message will be reported as below: generic/032 8s ... [failed, exit status 1] - output mismatch (see results/generic/032.out.bad) --- tests/generic/032.out 2015-01-11 16:52:27.643681072 +0800 +++ results/generic/032.out.bad 2016-08-06 13:44:43.861330500 +0800 @@ -1,5 +1,5 @@ QA output created by 032 -100 iterations -0000000 cdcd cdcd cdcd cdcd cdcd cdcd cdcd cdcd -* -0100000 +1: [768..775]: unwritten +Unwritten extents found! ... (Run 'diff -u tests/generic/032.out results/generic/032.out.bad' to see the entire diff) Ran: generic/032 Failures: generic/032 Failed 1 of 1 tests In write_end(), we should update i_size of inode before unlock page, otherwise, we will lose newly updated data in following race condition. Thread A Thread B - write_end - unlock page - writepages - lock_page - writepage if page is out-of-range of file size, we will skip writting the page. - update i_size Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 93c82fca8c6f..47a114ad967d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1691,11 +1691,11 @@ static int f2fs_write_end(struct file *file, trace_f2fs_write_end(inode, pos, len, copied); set_page_dirty(page); - f2fs_put_page(page, 1); if (pos + copied > i_size_read(inode)) f2fs_i_size_write(inode, pos + copied); + f2fs_put_page(page, 1); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return copied; } -- GitLab From a638f3f892622a9f85e887b374f0fc36d91e182f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 4 Aug 2016 20:13:02 +0800 Subject: [PATCH 0319/5498] f2fs: allow copying file range only in between regular files Only if two input files are regular files, we allow copying data in range of them, otherwise, deny it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index cfef80d2e115..6686d9969d35 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2090,8 +2090,8 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, if (unlikely(f2fs_readonly(src->i_sb))) return -EROFS; - if (S_ISDIR(src->i_mode) || S_ISDIR(dst->i_mode)) - return -EISDIR; + if (!S_ISREG(src->i_mode) || !S_ISREG(dst->i_mode)) + return -EINVAL; if (f2fs_encrypted_inode(src) || f2fs_encrypted_inode(dst)) return -EOPNOTSUPP; -- GitLab From 536e039e1c948da502888d060ee7ebc228147e2c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 4 Aug 2016 20:13:03 +0800 Subject: [PATCH 0320/5498] f2fs: avoid potential deadlock in f2fs_move_file_range Thread A Thread B - inode_lock fileA - inode_lock fileB - inode_lock fileA - inode_lock fileB We may encounter above potential deadlock during moving file range in concurrent scenario. This patch fixes the issue by using inode_trylock instead. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 5 +++++ fs/f2fs/file.c | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9b2af5c57f92..73cfe0af9575 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -148,6 +148,11 @@ static inline void inode_lock(struct inode *inode) mutex_lock(&inode->i_mutex); } +static inline int inode_trylock(struct inode *inode) +{ + return mutex_trylock(&inode->i_mutex); +} + static inline void inode_unlock(struct inode *inode) { mutex_unlock(&inode->i_mutex); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6686d9969d35..cb6e44c3601a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2097,8 +2097,12 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, return -EOPNOTSUPP; inode_lock(src); - if (src != dst) - inode_lock(dst); + if (src != dst) { + if (!inode_trylock(dst)) { + ret = -EBUSY; + goto out; + } + } ret = -EINVAL; if (pos_in + len > src->i_size || pos_in + len < pos_in) @@ -2156,6 +2160,7 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, out_unlock: if (src != dst) inode_unlock(dst); +out: inode_unlock(src); return ret; } -- GitLab From aed2971d9f00907f54038777f32da273d0280afa Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 28 Jul 2016 12:12:38 +0800 Subject: [PATCH 0321/5498] f2fs: not allow to write illegal blkaddr we came across an error as below: [build_nat_area_bitmap:1710] nid[0x 1718] addr[0x 1c18ddc] ino[0x 1718] [build_nat_area_bitmap:1710] nid[0x 1719] addr[0x 1c193d5] ino[0x 1719] [build_nat_area_bitmap:1710] nid[0x 171a] addr[0x 1c1736e] ino[0x 171a] [build_nat_area_bitmap:1710] nid[0x 171b] addr[0x 58b3ee8f] ino[0x815f92ed] [build_nat_area_bitmap:1710] nid[0x 171c] addr[0x fcdc94b] ino[0x49366377] [build_nat_area_bitmap:1710] nid[0x 171d] addr[0x 7cd2facf] ino[0xb3c55300] [build_nat_area_bitmap:1710] nid[0x 171e] addr[0x bd4e25d0] ino[0x77c34c09] ... ... [build_nat_area_bitmap:1710] nid[0x 1718] addr[0x 1c18ddc] ino[0x 1718] [build_nat_area_bitmap:1710] nid[0x 1719] addr[0x 1c193d5] ino[0x 1719] [build_nat_area_bitmap:1710] nid[0x 171a] addr[0x 1c1736e] ino[0x 171a] [build_nat_area_bitmap:1710] nid[0x 171b] addr[0x 58b3ee8f] ino[0x815f92ed] [build_nat_area_bitmap:1710] nid[0x 171c] addr[0x fcdc94b] ino[0x49366377] [build_nat_area_bitmap:1710] nid[0x 171d] addr[0x 7cd2facf] ino[0xb3c55300] [build_nat_area_bitmap:1710] nid[0x 171e] addr[0x bd4e25d0] ino[0x77c34c09] One nat block may be stepped by a data block, so this patch forbid to write if the blkaddr is illegal Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 7073bd1549ff..db9ae489a469 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -586,8 +586,8 @@ static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) { - f2fs_bug_on(sbi, blk_addr < SEG0_BLKADDR(sbi) - || blk_addr >= MAX_BLKADDR(sbi)); + BUG_ON(blk_addr < SEG0_BLKADDR(sbi) + || blk_addr >= MAX_BLKADDR(sbi)); } /* -- GitLab From 038f193869564b17dcb5e5f68693d56ea4913dd3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 2 Aug 2016 10:56:40 -0700 Subject: [PATCH 0322/5498] f2fs: do not use discard_map for hard disks We don't need to keep discard_map, if disk does not support discard command. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 4 +++- fs/f2fs/f2fs.h | 7 +++++++ fs/f2fs/segment.c | 28 +++++++++++++++++++--------- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 9197c8d87634..42e218b663af 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -154,7 +154,9 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct sit_info); si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry); si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi)); - si->base_mem += 3 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + if (f2fs_discard_en(sbi)) + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); si->base_mem += SIT_VBLOCK_MAP_SIZE; if (sbi->segs_per_sec > 1) si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 73cfe0af9575..e4725e3593a3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1118,6 +1118,13 @@ static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) cp->ckpt_flags = cpu_to_le32(ckpt_flags); } +static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) +{ + struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); + + return blk_queue_discard(q); +} + static inline void f2fs_lock_op(struct f2fs_sb_info *sbi) { down_read(&sbi->cp_rwsem); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d45e6bbf8493..fe1cd95f8d8b 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -657,7 +657,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) bool force = (cpc->reason == CP_DISCARD); int i; - if (se->valid_blocks == max_blocks) + if (se->valid_blocks == max_blocks || !f2fs_discard_en(sbi)) return; if (!force) { @@ -815,12 +815,14 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) if (del > 0) { if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) f2fs_bug_on(sbi, 1); - if (!f2fs_test_and_set_bit(offset, se->discard_map)) + if (f2fs_discard_en(sbi) && + !f2fs_test_and_set_bit(offset, se->discard_map)) sbi->discard_blks--; } else { if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) f2fs_bug_on(sbi, 1); - if (f2fs_test_and_clear_bit(offset, se->discard_map)) + if (f2fs_discard_en(sbi) && + f2fs_test_and_clear_bit(offset, se->discard_map)) sbi->discard_blks++; } if (!f2fs_test_bit(offset, se->ckpt_valid_map)) @@ -2123,12 +2125,16 @@ static int build_sit_info(struct f2fs_sb_info *sbi) = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); sit_i->sentries[start].ckpt_valid_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); - sit_i->sentries[start].discard_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); if (!sit_i->sentries[start].cur_valid_map || - !sit_i->sentries[start].ckpt_valid_map || - !sit_i->sentries[start].discard_map) + !sit_i->sentries[start].ckpt_valid_map) return -ENOMEM; + + if (f2fs_discard_en(sbi)) { + sit_i->sentries[start].discard_map + = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->sentries[start].discard_map) + return -ENOMEM; + } } sit_i->tmp_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); @@ -2272,8 +2278,12 @@ got_it: seg_info_from_raw_sit(se, &sit); /* build discard map only one time */ - memcpy(se->discard_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); - sbi->discard_blks += sbi->blocks_per_seg - se->valid_blocks; + if (f2fs_discard_en(sbi)) { + memcpy(se->discard_map, se->cur_valid_map, + SIT_VBLOCK_MAP_SIZE); + sbi->discard_blks += sbi->blocks_per_seg - + se->valid_blocks; + } if (sbi->segs_per_sec > 1) { struct sec_entry *e = get_sec_entry(sbi, start); -- GitLab From 0ec2018d48b8ba3b0a41368c597ffa883b3e8979 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 11 Aug 2016 10:18:38 +0800 Subject: [PATCH 0323/5498] f2fs: reduce batch size of fstrim This is to reduce the batch size of fstrim to avoid long latency. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e4725e3593a3..23f8f9556959 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -205,7 +205,7 @@ enum { CP_DISCARD, }; -#define DEF_BATCHED_TRIM_SECTIONS 32 +#define DEF_BATCHED_TRIM_SECTIONS 2 #define BATCHED_TRIM_SEGMENTS(sbi) \ (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) #define BATCHED_TRIM_BLOCKS(sbi) \ -- GitLab From 8e8e60207879d626d720c94ffd04d7df722f0c5c Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 18 Aug 2016 21:01:18 +0800 Subject: [PATCH 0324/5498] f2fs: add discard info to sys entry of f2fs status This patch add discard block count to sys entry of f2fs status Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 10 ++++++++-- fs/f2fs/f2fs.h | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 42e218b663af..481c92dce61e 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -54,6 +54,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->rsvd_segs = reserved_segments(sbi); si->overp_segs = overprovision_segments(sbi); si->valid_count = valid_user_blocks(sbi); + si->discard_blks = discard_blocks(sbi); si->valid_node_count = valid_node_count(sbi); si->valid_inode_count = valid_inode_count(sbi); si->inline_xattr = atomic_read(&sbi->inline_xattr); @@ -232,8 +233,13 @@ static int stat_show(struct seq_file *s, void *v) si->ssa_area_segs, si->main_area_segs); seq_printf(s, "(OverProv:%d Resv:%d)]\n\n", si->overp_segs, si->rsvd_segs); - seq_printf(s, "Utilization: %d%% (%d valid blocks)\n", - si->utilization, si->valid_count); + if (test_opt(si->sbi, DISCARD)) + seq_printf(s, "Utilization: %u%% (%u valid blocks, %u discard blocks)\n", + si->utilization, si->valid_count, si->discard_blks); + else + seq_printf(s, "Utilization: %u%% (%u valid blocks)\n", + si->utilization, si->valid_count); + seq_printf(s, " - Node: %u (Inode: %u, ", si->valid_node_count, si->valid_inode_count); seq_printf(s, "Other: %u)\n - Data: %u\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 23f8f9556959..e984520e163b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1296,6 +1296,11 @@ static inline block_t valid_user_blocks(struct f2fs_sb_info *sbi) return sbi->total_valid_block_count; } +static inline block_t discard_blocks(struct f2fs_sb_info *sbi) +{ + return sbi->discard_blks; +} + static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); @@ -2184,7 +2189,7 @@ struct f2fs_stat_info { int total_count, utilization; int bg_gc, wb_bios; int inline_xattr, inline_inode, inline_dir, orphans; - unsigned int valid_count, valid_node_count, valid_inode_count; + unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; unsigned int bimodal, avg_vblocks; int util_free, util_valid, util_invalid; int rsvd_segs, overp_segs; -- GitLab From 945d34fbeda64673bbe2d818af308028b0038967 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 18 Aug 2016 21:01:19 +0800 Subject: [PATCH 0325/5498] f2fs: skip new checkpoint when doing fstrim without fs change This patch enables to do fstrim without checkpoint, if there is no fs change. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 5a3ee5e56519..d4b6c1478ec5 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1189,6 +1189,17 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_flush_merged_bios(sbi); + /* this is the case of multiple fstrims without any changes */ + if (cpc->reason == CP_DISCARD && !is_sbi_flag_set(sbi, SBI_IS_DIRTY)) { + f2fs_bug_on(sbi, NM_I(sbi)->dirty_nat_cnt); + f2fs_bug_on(sbi, SIT_I(sbi)->dirty_sentries); + f2fs_bug_on(sbi, prefree_segments(sbi)); + flush_sit_entries(sbi, cpc); + clear_prefree_segments(sbi, cpc); + unblock_operations(sbi); + goto out; + } + /* * update checkpoint pack index * Increase the version number so that -- GitLab From 09ece2ceda2e466b8a4ddd06387e9da70d231008 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 18 Aug 2016 17:46:14 +0800 Subject: [PATCH 0326/5498] f2fs: set dirty state for filesystem only when updating meta data We don't guarantee integrity of user data after checkpoint, since we only guarantee meta data integrity for data consistency of filesystem. Due to above reason, we only need to set fs as dirty when meta data is updated, so that we can skip writing checkpoint in some case of non-meta data is updated. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e984520e163b..a09a42437eed 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1246,6 +1246,10 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) { percpu_counter_inc(&sbi->nr_pages[count_type]); + + if (count_type == F2FS_DIRTY_DATA || count_type == F2FS_INMEM_PAGES) + return; + set_sbi_flag(sbi, SBI_IS_DIRTY); } -- GitLab From 66402aa419dd2307344b3cb98ecc4dfede93e2e8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 Aug 2016 23:13:46 +0800 Subject: [PATCH 0327/5498] f2fs: clean up foreground GC flow This patch changes to check valid block number of one GCed section directly instead of checking the number in all segments of section one by one in order to clean up codes of foreground GC. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 06cfb94cc3db..881a15ed2b10 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -811,7 +811,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, struct blk_plug plug; unsigned int segno = start_segno; unsigned int end_segno = start_segno + sbi->segs_per_sec; - int seg_freed = 0; + int sec_freed = 0; unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ? SUM_TYPE_DATA : SUM_TYPE_NODE; @@ -867,22 +867,20 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, blk_finish_plug(&plug); - if (gc_type == FG_GC) { - while (start_segno < end_segno) - if (get_valid_blocks(sbi, start_segno++, 1) == 0) - seg_freed++; - } + if (gc_type == FG_GC && + get_valid_blocks(sbi, start_segno, sbi->segs_per_sec) == 0) + sec_freed = 1; stat_inc_call_count(sbi->stat_info); - return seg_freed; + return sec_freed; } int f2fs_gc(struct f2fs_sb_info *sbi, bool sync) { unsigned int segno; int gc_type = sync ? FG_GC : BG_GC; - int sec_freed = 0, seg_freed; + int sec_freed = 0; int ret = -EINVAL; struct cp_control cpc; struct gc_inode_list gc_list = { @@ -921,9 +919,8 @@ gc_more: goto stop; ret = 0; - seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type); - - if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec) + if (do_garbage_collect(sbi, segno, &gc_list, gc_type) && + gc_type == FG_GC) sec_freed++; if (gc_type == FG_GC) -- GitLab From eff4c8debf7aed1527ffcfb998f25874ded8a36a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 Aug 2016 23:13:47 +0800 Subject: [PATCH 0328/5498] f2fs: avoid unneeded loop in build_sit_entries When building each sit entry in cache, firstly, we will load it from sit page, and then check all entries in sit journal, if there is one updated entry in journal, cover cached entry with the journaled one. Actually, most of check operation is unneeded since we only need to update cached entries with journaled entries in batch, so changing the flow as below for more efficient: 1. load all sit entries into cache from sit pages; 2. update sit entries with journal. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 48 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fe1cd95f8d8b..b4bee9c0d26d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2258,22 +2258,11 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) struct f2fs_sit_entry sit; struct page *page; - down_read(&curseg->journal_rwsem); - for (i = 0; i < sits_in_cursum(journal); i++) { - if (le32_to_cpu(segno_in_journal(journal, i)) - == start) { - sit = sit_in_journal(journal, i); - up_read(&curseg->journal_rwsem); - goto got_it; - } - } - up_read(&curseg->journal_rwsem); - page = get_current_sit_page(sbi, start); sit_blk = (struct f2fs_sit_block *)page_address(page); sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; f2fs_put_page(page, 1); -got_it: + check_block_count(sbi, start, &sit); seg_info_from_raw_sit(se, &sit); @@ -2285,13 +2274,40 @@ got_it: se->valid_blocks; } - if (sbi->segs_per_sec > 1) { - struct sec_entry *e = get_sec_entry(sbi, start); - e->valid_blocks += se->valid_blocks; - } + if (sbi->segs_per_sec > 1) + get_sec_entry(sbi, start)->valid_blocks += + se->valid_blocks; } start_blk += readed; } while (start_blk < sit_blk_cnt); + + down_read(&curseg->journal_rwsem); + for (i = 0; i < sits_in_cursum(journal); i++) { + struct f2fs_sit_entry sit; + struct seg_entry *se; + unsigned int old_valid_blocks; + + start = le32_to_cpu(segno_in_journal(journal, i)); + se = &sit_i->sentries[start]; + sit = sit_in_journal(journal, i); + + old_valid_blocks = se->valid_blocks; + + check_block_count(sbi, start, &sit); + seg_info_from_raw_sit(se, &sit); + + if (f2fs_discard_en(sbi)) { + memcpy(se->discard_map, se->cur_valid_map, + SIT_VBLOCK_MAP_SIZE); + sbi->discard_blks += old_valid_blocks - + se->valid_blocks; + } + + if (sbi->segs_per_sec > 1) + get_sec_entry(sbi, start)->valid_blocks += + se->valid_blocks - old_valid_blocks; + } + up_read(&curseg->journal_rwsem); } static void init_free_segmap(struct f2fs_sb_info *sbi) -- GitLab From 1875874a10b1cd64fce31c18943a0b581d46d17f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 20 Aug 2016 15:12:02 +0800 Subject: [PATCH 0329/5498] f2fs: fix to do f2fs_balance_fs in f2fs_map_blocks correctly If we preallocate blocks with f2fs_reserve_blocks in f2fs_map_blocks, we should call f2fs_balance_fs for checking and reclaiming space, fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 47a114ad967d..298e49337b4a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -778,6 +778,7 @@ skip: err = reserve_new_blocks(&dn, prealloc); if (err) goto sync_out; + allocated = dn.node_changed; map->m_len += dn.ofs_in_node - ofs_in_node; if (prealloc && dn.ofs_in_node != last_ofs_in_node + 1) { -- GitLab From e907d6aa893e82c45db1ed03e1d8fe0eb68038e1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 21 Aug 2016 23:21:29 +0800 Subject: [PATCH 0330/5498] f2fs: check return value of write_checkpoint during fstrim During fstrim, if one of multiple write_checkpoint failed, break off and return error number to caller. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index b4bee9c0d26d..0cdca5aadf26 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1300,6 +1300,8 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) mutex_lock(&sbi->gc_mutex); err = write_checkpoint(sbi, &cpc); mutex_unlock(&sbi->gc_mutex); + if (err) + break; } out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); -- GitLab From 0000dfa4d7e1945d7712cc1fb4266a9c3e4daac6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 21 Aug 2016 23:21:31 +0800 Subject: [PATCH 0331/5498] f2fs: remove redundant judgement condition in available_free_memory In available_free_memory, there are two same judgement conditions which is used for checking NAT excess, remove one of them. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4070a55e7266..529807718744 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -54,8 +54,6 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); if (excess_cached_nats(sbi)) res = false; - if (nm_i->nat_cnt > DEF_NAT_CACHE_THRESHOLD) - res = false; } else if (type == DIRTY_DENTS) { if (sbi->sb->s_bdi->dirty_exceeded) return false; -- GitLab From 264bf18111ffa0e0fd9f2ea58516d2ecf1b9d082 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Tue, 23 Aug 2016 20:10:47 +0800 Subject: [PATCH 0332/5498] f2fs: remove unnecessary initialization `flags' is used to save value from userspace, there is no need to initialize it, and FS_FL_USER_VISIBLE is the mask for getflags. Signed-off-by: Sheng Yong Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index cb6e44c3601a..30880d0329f5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1458,7 +1458,7 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); struct f2fs_inode_info *fi = F2FS_I(inode); - unsigned int flags = fi->i_flags & FS_FL_USER_VISIBLE; + unsigned int flags; unsigned int oldflags; int ret; -- GitLab From 512424c7fbd6fa4ab810b495268074a0063d0802 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 23 Aug 2016 15:23:59 +0000 Subject: [PATCH 0333/5498] f2fs: fix non static symbol warning Fixes the following sparse warning: fs/f2fs/data.c:969:12: warning: symbol 'f2fs_grab_bio' was not declared. Should it be static? Signed-off-by: Wei Yongjun Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 298e49337b4a..d440816ab62c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -962,8 +962,8 @@ out: return ret; } -struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, - unsigned nr_pages) +static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, + unsigned nr_pages) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct fscrypt_ctx *ctx = NULL; -- GitLab From 5ae274db9c2b40ed557efccc5340afc5d1bf4b92 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 20 Aug 2016 15:12:01 +0800 Subject: [PATCH 0334/5498] f2fs: fix to preallocate block only aligned to 4K In write_begin(), we skip checking dnode block for preallocating block when whole block needs to be updated since we preallocated its block in f2fs_preallocate_blocks, for partial updated block, we will still try to lock its node and do preallocation in write_begin(), so in f2fs_preallocate_blocks we should not preallocate its block. But previously, the calculation of preallocating block number is incorrect, fix it. Signed-off-by: Chao Yu [Jaegeuk Kim: fix a bug] Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d440816ab62c..94b74278e382 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -621,7 +621,12 @@ ssize_t f2fs_preallocate_blocks(struct inode *inode, loff_t pos, ssize_t ret = 0; map.m_lblk = F2FS_BLK_ALIGN(pos); - map.m_len = F2FS_BYTES_TO_BLK(count); + map.m_len = F2FS_BYTES_TO_BLK(pos + count); + if (map.m_len > map.m_lblk) + map.m_len -= map.m_lblk; + else + map.m_len = 0; + map.m_next_pgofs = NULL; if (f2fs_encrypted_inode(inode)) @@ -667,6 +672,9 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, bool allocated = false; block_t blkaddr; + if (!maxblocks) + return 0; + map->m_len = 0; map->m_flags = 0; -- GitLab From 609f19abeba88f81780f52307792d0c55dd296d1 Mon Sep 17 00:00:00 2001 From: Shuoran Liu Date: Thu, 25 Aug 2016 20:42:09 +0800 Subject: [PATCH 0335/5498] f2fs: fix a bug when using namehash to locate dentry bucket In the following scenario, 1) we don't have the key and doing a lookup for encrypted file, 2) and the encrypted filename is big name we should use fname->hash as name hash value instead of what is calculated by fname->disk_name. Because in such case, fname->disk_name is empty. Signed-off-by: Shuoran Liu Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index e6abd31ccd4f..8bd37afe2add 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -172,7 +172,10 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, int max_slots; f2fs_hash_t namehash; - namehash = f2fs_dentry_hash(&name); + if(fname->hash) + namehash = cpu_to_le32(fname->hash); + else + namehash = f2fs_dentry_hash(&name); nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level); nblock = bucket_blocks(level); -- GitLab From 7a6ed7f4731519e7c732aa3c40e3cbf0d07cf945 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 9 May 2016 19:56:34 +0800 Subject: [PATCH 0336/5498] f2fs: enable inline_dentry by default and add noinline_dentry option Make inline_dentry as default mount option to improve space usage and IO performance in scenario of numerous small directory. It adds noinline_dentry mount option, instead. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: Documentation/filesystems/f2fs.txt --- Documentation/filesystems/f2fs.txt | 5 +++++ fs/f2fs/super.c | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 3e9ba9718dc1..c809792186b2 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -126,6 +126,11 @@ disable_ext_identify Disable the extension list configured by mkfs, so f2fs inline_xattr Enable the inline xattrs feature. inline_data Enable the inline data feature: New created small(<~3.4k) files can be written into inode block. +inline_dentry Enable the inline dir feature: data in new created + directory entries can be written into inode block. The + space of inode block which is used to store inline + dentries is limited to ~3.4k. +noinline_dentry Diable the inline dentry feature. flush_merge Merge concurrent cache_flush commands as much as possible to eliminate redundant command issues. If the underlying device handles the cache_flush command relatively slowly, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8aabce4f9e5b..0e578f04abfc 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -87,6 +87,7 @@ enum { Opt_inline_xattr, Opt_inline_data, Opt_inline_dentry, + Opt_noinline_dentry, Opt_flush_merge, Opt_noflush_merge, Opt_nobarrier, @@ -116,6 +117,7 @@ static match_table_t f2fs_tokens = { {Opt_inline_xattr, "inline_xattr"}, {Opt_inline_data, "inline_data"}, {Opt_inline_dentry, "inline_dentry"}, + {Opt_noinline_dentry, "noinline_dentry"}, {Opt_flush_merge, "flush_merge"}, {Opt_noflush_merge, "noflush_merge"}, {Opt_nobarrier, "nobarrier"}, @@ -484,6 +486,9 @@ static int parse_options(struct super_block *sb, char *options) case Opt_inline_dentry: set_opt(sbi, INLINE_DENTRY); break; + case Opt_noinline_dentry: + clear_opt(sbi, INLINE_DENTRY); + break; case Opt_flush_merge: set_opt(sbi, FLUSH_MERGE); break; @@ -865,6 +870,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",noinline_data"); if (test_opt(sbi, INLINE_DENTRY)) seq_puts(seq, ",inline_dentry"); + else + seq_puts(seq, ",noinline_dentry"); if (!f2fs_readonly(sbi->sb) && test_opt(sbi, FLUSH_MERGE)) seq_puts(seq, ",flush_merge"); if (test_opt(sbi, NOBARRIER)) @@ -963,6 +970,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, BG_GC); set_opt(sbi, INLINE_DATA); + set_opt(sbi, INLINE_DENTRY); set_opt(sbi, EXTENT_CACHE); set_opt(sbi, FLUSH_MERGE); if (f2fs_sb_mounted_hmsmr(sbi->sb)) { -- GitLab From 0ea607c84624d4aa7ba348cc5cc8fcbd673f416c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 21 Aug 2016 23:21:30 +0800 Subject: [PATCH 0337/5498] f2fs: schedule in between two continous batch discards In batch discard approach of fstrim will grab/release gc_mutex lock repeatly, it makes contention of the lock becoming more intensive. So after one batch discards were issued in checkpoint and the lock was released, it's better to do schedule() to increase opportunity of grabbing gc_mutex lock for other competitors. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0cdca5aadf26..143d6eef26b8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1302,6 +1302,8 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) mutex_unlock(&sbi->gc_mutex); if (err) break; + + schedule(); } out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); -- GitLab From afaa58aff0411343193f645157a61b4ff7468a45 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 27 Aug 2016 00:14:31 +0800 Subject: [PATCH 0338/5498] f2fs: do in batch synchronously readahead during GC In order to enhance performance, we try to readahead node page during GC, but before loading node page we should get block address of node page which is stored in NAT table, so synchronously read of single NAT page block our readahead flow. f2fs_submit_page_bio: dev = (251,0), ino = 2, page_index = 0xa1e, oldaddr = 0xa1e, newaddr = 0xa1e, rw = READ_SYNC(MP), type = META f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x35e9, oldaddr = 0x72d7a, newaddr = 0x72d7a, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 2, page_index = 0xc1f, oldaddr = 0xc1f, newaddr = 0xc1f, rw = READ_SYNC(MP), type = META f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x389d, oldaddr = 0x72d7d, newaddr = 0x72d7d, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x3a82, oldaddr = 0x72d7f, newaddr = 0x72d7f, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x3bfa, oldaddr = 0x72d86, newaddr = 0x72d86, rw = READAHEAD ^H, type = NODE This patch adds one phase that do readahead NAT pages in batch before readahead node page for more effeciently. f2fs_submit_page_bio: dev = (251,0), ino = 2, page_index = 0x1952, oldaddr = 0x1952, newaddr = 0x1952, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xc34, oldaddr = 0xc34, newaddr = 0xc34, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xa33, oldaddr = 0xa33, newaddr = 0xa33, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xc30, oldaddr = 0xc30, newaddr = 0xc30, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xc32, oldaddr = 0xc32, newaddr = 0xc32, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xc26, oldaddr = 0xc26, newaddr = 0xc26, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xa2b, oldaddr = 0xa2b, newaddr = 0xa2b, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xc23, oldaddr = 0xc23, newaddr = 0xc23, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xc24, oldaddr = 0xc24, newaddr = 0xc24, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xa10, oldaddr = 0xa10, newaddr = 0xa10, rw = READ_SYNC(MP), type = META f2fs_submit_page_mbio: dev = (251,0), ino = 2, page_index = 0xc2c, oldaddr = 0xc2c, newaddr = 0xc2c, rw = READ_SYNC(MP), type = META f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x5db7, oldaddr = 0x6be00, newaddr = 0x6be00, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x5db9, oldaddr = 0x6be17, newaddr = 0x6be17, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x5dbc, oldaddr = 0x6be1a, newaddr = 0x6be1a, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x5dc3, oldaddr = 0x6be20, newaddr = 0x6be20, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x5dc7, oldaddr = 0x6be24, newaddr = 0x6be24, rw = READAHEAD ^H, type = NODE f2fs_submit_page_bio: dev = (251,0), ino = 1, page_index = 0x5dc9, oldaddr = 0x6be25, newaddr = 0x6be25, rw = READAHEAD ^H, type = NODE Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 881a15ed2b10..c83cc77423ea 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -423,10 +423,10 @@ static int check_valid_map(struct f2fs_sb_info *sbi, static void gc_node_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, unsigned int segno, int gc_type) { - bool initial = true; struct f2fs_summary *entry; block_t start_addr; int off; + int phase = 0; start_addr = START_BLOCK(sbi, segno); @@ -445,10 +445,18 @@ next_step: if (check_valid_map(sbi, segno, off) == 0) continue; - if (initial) { + if (phase == 0) { + ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), 1, + META_NAT, true); + continue; + } + + if (phase == 1) { ra_node_page(sbi, nid); continue; } + + /* phase == 2 */ node_page = get_node_page(sbi, nid); if (IS_ERR(node_page)) continue; @@ -469,10 +477,8 @@ next_step: stat_inc_node_blk_count(sbi, 1, gc_type); } - if (initial) { - initial = false; + if (++phase < 3) goto next_step; - } } /* @@ -703,6 +709,7 @@ next_step: struct node_info dni; /* dnode info for the data */ unsigned int ofs_in_node, nofs; block_t start_bidx; + nid_t nid = le32_to_cpu(entry->nid); /* stop BG_GC if there is not enough free sections. */ if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) @@ -712,7 +719,13 @@ next_step: continue; if (phase == 0) { - ra_node_page(sbi, le32_to_cpu(entry->nid)); + ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), 1, + META_NAT, true); + continue; + } + + if (phase == 1) { + ra_node_page(sbi, nid); continue; } @@ -720,14 +733,14 @@ next_step: if (!is_alive(sbi, entry, &dni, start_addr + off, &nofs)) continue; - if (phase == 1) { + if (phase == 2) { ra_node_page(sbi, dni.ino); continue; } ofs_in_node = le16_to_cpu(entry->ofs_in_node); - if (phase == 2) { + if (phase == 3) { inode = f2fs_iget(sb, dni.ino); if (IS_ERR(inode) || is_bad_inode(inode)) continue; @@ -752,7 +765,7 @@ next_step: continue; } - /* phase 3 */ + /* phase 4 */ inode = find_gc_inode(gc_list, dni.ino); if (inode) { struct f2fs_inode_info *fi = F2FS_I(inode); @@ -785,7 +798,7 @@ next_step: } } - if (++phase < 4) + if (++phase < 5) goto next_step; } -- GitLab From 5421886201f1bf7bf73389656e84d1abc9e6b517 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 28 Aug 2016 18:57:55 +0800 Subject: [PATCH 0339/5498] f2fs: fix to do security initialization of encrypted inode with original filename When creating new inode, security_inode_init_security will be called for initializing security info related to the inode, and filename is passed to security module, it helps security module such as SElinux to know which rule or label could be applied for the inode with specified name. Previously, if new inode is created as an encrypted one, f2fs will transfer encrypted filename to security module which may fail the check of security policy belong to the inode. So in order to this issue, alter to transfer original unencrypted filename instead. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 21 +++++++++++++-------- fs/f2fs/f2fs.h | 8 ++++---- fs/f2fs/inline.c | 17 +++++++++-------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 8bd37afe2add..e4c58665fb6f 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -378,7 +378,8 @@ static int make_empty_dir(struct inode *inode, } struct page *init_inode_metadata(struct inode *inode, struct inode *dir, - const struct qstr *name, struct page *dpage) + const struct qstr *new_name, const struct qstr *orig_name, + struct page *dpage) { struct page *page; int err; @@ -403,7 +404,7 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, if (err) goto put_error; - err = f2fs_init_security(inode, dir, name, page); + err = f2fs_init_security(inode, dir, orig_name, page); if (err) goto put_error; @@ -420,8 +421,8 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, set_cold_node(inode, page); } - if (name) - init_dent_inode(name, page); + if (new_name) + init_dent_inode(new_name, page); /* * This file should be checkpointed during fsync. @@ -507,6 +508,7 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, } int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, + const struct qstr *orig_name, struct inode *inode, nid_t ino, umode_t mode) { unsigned int bit_pos; @@ -572,7 +574,8 @@ add_dentry: if (inode) { down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, new_name, NULL); + page = init_inode_metadata(inode, dir, new_name, + orig_name, NULL); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; @@ -622,9 +625,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, err = -EAGAIN; if (f2fs_has_inline_dentry(dir)) - err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); + err = f2fs_add_inline_entry(dir, &new_name, fname.usr_fname, + inode, ino, mode); if (err == -EAGAIN) - err = f2fs_add_regular_entry(dir, &new_name, inode, ino, mode); + err = f2fs_add_regular_entry(dir, &new_name, fname.usr_fname, + inode, ino, mode); fscrypt_free_filename(&fname); f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); @@ -637,7 +642,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir) int err = 0; down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, NULL, NULL); + page = init_inode_metadata(inode, dir, NULL, NULL, NULL); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a09a42437eed..369c7b926dab 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1974,7 +1974,7 @@ bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, void do_make_empty_dir(struct inode *, struct inode *, struct f2fs_dentry_ptr *); struct page *init_inode_metadata(struct inode *, struct inode *, - const struct qstr *, struct page *); + const struct qstr *, const struct qstr *, struct page *); void update_parent_metadata(struct inode *, struct inode *, unsigned int); int room_for_filename(const void *, int, int); void f2fs_drop_nlink(struct inode *, struct inode *); @@ -1988,7 +1988,7 @@ int update_dent_inode(struct inode *, struct inode *, const struct qstr *); void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, const struct qstr *, f2fs_hash_t , unsigned int); int f2fs_add_regular_entry(struct inode *, const struct qstr *, - struct inode *, nid_t, umode_t); + const struct qstr *, struct inode *, nid_t, umode_t); int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, umode_t); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, @@ -2358,8 +2358,8 @@ bool recover_inline_data(struct inode *, struct page *); struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct fscrypt_name *, struct page **); int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); -int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, - nid_t, umode_t); +int f2fs_add_inline_entry(struct inode *, const struct qstr *, + const struct qstr *, struct inode *, nid_t, umode_t); void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *, struct inode *, struct inode *); bool f2fs_empty_inline_dir(struct inode *); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index d411ab6c7483..34ef892d8d9e 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -423,7 +423,7 @@ static int f2fs_add_inline_entries(struct inode *dir, ino = le32_to_cpu(de->ino); fake_mode = get_de_type(de) << S_SHIFT; - err = f2fs_add_regular_entry(dir, &new_name, NULL, + err = f2fs_add_regular_entry(dir, &new_name, NULL, NULL, ino, fake_mode); if (err) goto punch_dentry_pages; @@ -487,17 +487,17 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, return f2fs_move_rehashed_dirents(dir, ipage, inline_dentry); } -int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, - struct inode *inode, nid_t ino, umode_t mode) +int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, + const struct qstr *orig_name, + struct inode *inode, nid_t ino, umode_t mode) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct page *ipage; unsigned int bit_pos; f2fs_hash_t name_hash; - size_t namelen = name->len; struct f2fs_inline_dentry *dentry_blk = NULL; struct f2fs_dentry_ptr d; - int slots = GET_DENTRY_SLOTS(namelen); + int slots = GET_DENTRY_SLOTS(new_name->len); struct page *page = NULL; int err = 0; @@ -518,7 +518,8 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, if (inode) { down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, name, ipage); + page = init_inode_metadata(inode, dir, new_name, + orig_name, ipage); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; @@ -527,9 +528,9 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, f2fs_wait_on_page_writeback(ipage, NODE, true); - name_hash = f2fs_dentry_hash(name); + name_hash = f2fs_dentry_hash(new_name); make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); - f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos); + f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); set_page_dirty(ipage); -- GitLab From a1ba53aa589f80f1fc1a92bf21ad719858511f59 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 28 Aug 2016 22:00:12 +0800 Subject: [PATCH 0340/5498] f2fs crypto: avoid unneeded memory allocation in ->readdir When decrypting dirents in ->readdir, fscrypt_fname_disk_to_usr won't change content of original encrypted dirent, we don't need to allocate additional buffer for storing mirror of it, so get rid of it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index e4c58665fb6f..ba4c8458d034 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -796,16 +796,9 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, int save_len = fstr->len; int ret; - de_name.name = f2fs_kmalloc(de_name.len, GFP_NOFS); - if (!de_name.name) - return false; - - memcpy(de_name.name, d->filename[bit_pos], de_name.len); - ret = fscrypt_fname_disk_to_usr(d->inode, (u32)de->hash_code, 0, &de_name, fstr); - kfree(de_name.name); if (ret < 0) return true; -- GitLab From e0521e6eea8732a738b33396d6be571be25b6b36 Mon Sep 17 00:00:00 2001 From: Shuoran Liu Date: Mon, 29 Aug 2016 11:27:55 +0800 Subject: [PATCH 0341/5498] f2fs: set encryption name flag in add inline entry path This patch sets encryption name flag in the add inline entry path if filename is encrypted. Signed-off-by: Shuoran Liu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 34ef892d8d9e..66a0674846ef 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -524,6 +524,8 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, err = PTR_ERR(page); goto fail; } + if (f2fs_encrypted_inode(dir)) + file_set_enc_name(inode); } f2fs_wait_on_page_writeback(ipage, NODE, true); -- GitLab From 2420c9c708050b16232b4ea56f3bc9afe1b1cb7c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 29 Aug 2016 18:23:45 -0700 Subject: [PATCH 0342/5498] f2fs: fix lost xattrs of directories This patch enhances the xattr consistency of dirs from suddern power-cuts. Possible scenario would be: 1. dir->setxattr used by per-file encryption 2. file->setxattr goes into inline_xattr 3. file->fsync In that case, we should do checkpoint for #1. Otherwise we'd lose dir's key information for the file given #2. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 + fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 2 ++ fs/f2fs/xattr.c | 2 ++ 4 files changed, 6 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index d4b6c1478ec5..bae2a61fca27 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1151,6 +1151,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) clear_prefree_segments(sbi, cpc); clear_sbi_flag(sbi, SBI_IS_DIRTY); + clear_sbi_flag(sbi, SBI_NEED_CP); return 0; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 369c7b926dab..9c2371eead54 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -794,6 +794,7 @@ enum { SBI_NEED_FSCK, /* need fsck.f2fs to fix */ SBI_POR_DOING, /* recovery is doing or not */ SBI_NEED_SB_WRITE, /* need to recover superblock */ + SBI_NEED_CP, /* need to checkpoint */ }; enum { diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 30880d0329f5..077310f6659d 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -137,6 +137,8 @@ static inline bool need_do_checkpoint(struct inode *inode) if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) need_cp = true; + else if (is_sbi_flag_set(sbi, SBI_NEED_CP)) + need_cp = true; else if (file_enc_name(inode) && need_dentry_mark(sbi, inode->i_ino)) need_cp = true; else if (file_wrong_pino(inode)) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 8191ce6a6260..45e77e8ed93c 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -590,6 +590,8 @@ static int __f2fs_setxattr(struct inode *inode, int index, !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) f2fs_set_encrypted_inode(inode); f2fs_mark_inode_dirty_sync(inode); + if (!error && S_ISDIR(inode->i_mode)) + set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP); exit: kzfree(base_addr); return error; -- GitLab From 6e70bd1c1446bcaa69fdff6b60f9df271fdf7222 Mon Sep 17 00:00:00 2001 From: Shuoran Liu Date: Mon, 29 Aug 2016 11:27:56 +0800 Subject: [PATCH 0343/5498] f2fs: add roll-forward recovery process for encrypted dentry Add roll-forward recovery process for encrypted dentry, so the first fsync issued to an encrypted file does not need writing checkpoint. This improves the performance of the following test at thousands of small files: open -> write -> fsync -> close Signed-off-by: Shuoran Liu Acked-by: Chao Yu [Jaegeuk Kim: modify kernel message to show encrypted names] Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/dir.c --- fs/f2fs/dir.c | 79 ++++++++++++++++++++++++++++------------------ fs/f2fs/f2fs.h | 4 +++ fs/f2fs/file.c | 2 -- fs/f2fs/recovery.c | 23 ++++++++------ 4 files changed, 66 insertions(+), 42 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index ba4c8458d034..695006b99a0d 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -215,31 +215,17 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, return de; } -/* - * Find an entry in the specified directory with the wanted name. - * It returns the page where the entry was found (as a parameter - res_page), - * and the entry itself. Page is returned mapped and unlocked. - * Entry is guaranteed to be valid. - */ -struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, - struct qstr *child, struct page **res_page) +struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir, + struct fscrypt_name *fname, struct page **res_page) { unsigned long npages = dir_blocks(dir); struct f2fs_dir_entry *de = NULL; unsigned int max_depth; unsigned int level; - struct fscrypt_name fname; - int err; - - err = fscrypt_setup_filename(dir, child, 1, &fname); - if (err) { - *res_page = ERR_PTR(err); - return NULL; - } if (f2fs_has_inline_dentry(dir)) { *res_page = NULL; - de = find_in_inline_dir(dir, &fname, res_page); + de = find_in_inline_dir(dir, fname, res_page); goto out; } @@ -259,11 +245,35 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, for (level = 0; level < max_depth; level++) { *res_page = NULL; - de = find_in_level(dir, level, &fname, res_page); + de = find_in_level(dir, level, fname, res_page); if (de || IS_ERR(*res_page)) break; } out: + return de; +} + +/* + * Find an entry in the specified directory with the wanted name. + * It returns the page where the entry was found (as a parameter - res_page), + * and the entry itself. Page is returned mapped and unlocked. + * Entry is guaranteed to be valid. + */ +struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, + struct qstr *child, struct page **res_page) +{ + struct f2fs_dir_entry *de = NULL; + struct fscrypt_name fname; + int err; + + err = fscrypt_setup_filename(dir, child, 1, &fname); + if (err) { + *res_page = ERR_PTR(err); + return NULL; + } + + de = __f2fs_find_entry(dir, &fname, res_page); + fscrypt_free_filename(&fname); return de; } @@ -605,6 +615,26 @@ fail: return err; } +int __f2fs_do_add_link(struct inode *dir, struct fscrypt_name *fname, + struct inode *inode, nid_t ino, umode_t mode) +{ + struct qstr new_name; + int err = -EAGAIN; + + new_name.name = fname_name(fname); + new_name.len = fname_len(fname); + + if (f2fs_has_inline_dentry(dir)) + err = f2fs_add_inline_entry(dir, &new_name, fname->usr_fname, + inode, ino, mode); + if (err == -EAGAIN) + err = f2fs_add_regular_entry(dir, &new_name, fname->usr_fname, + inode, ino, mode); + + f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); + return err; +} + /* * Caller should grab and release a rwsem by calling f2fs_lock_op() and * f2fs_unlock_op(). @@ -613,26 +643,15 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode, nid_t ino, umode_t mode) { struct fscrypt_name fname; - struct qstr new_name; int err; err = fscrypt_setup_filename(dir, name, 0, &fname); if (err) return err; - new_name.name = fname_name(&fname); - new_name.len = fname_len(&fname); - - err = -EAGAIN; - if (f2fs_has_inline_dentry(dir)) - err = f2fs_add_inline_entry(dir, &new_name, fname.usr_fname, - inode, ino, mode); - if (err == -EAGAIN) - err = f2fs_add_regular_entry(dir, &new_name, fname.usr_fname, - inode, ino, mode); + err = __f2fs_do_add_link(dir, &fname, inode, ino, mode); fscrypt_free_filename(&fname); - f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); return err; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9c2371eead54..901ae72891ed 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1979,6 +1979,8 @@ struct page *init_inode_metadata(struct inode *, struct inode *, void update_parent_metadata(struct inode *, struct inode *, unsigned int); int room_for_filename(const void *, int, int); void f2fs_drop_nlink(struct inode *, struct inode *); +struct f2fs_dir_entry *__f2fs_find_entry(struct inode *, struct fscrypt_name *, + struct page **); struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, struct page **); struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); @@ -1990,6 +1992,8 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, const struct qstr *, f2fs_hash_t , unsigned int); int f2fs_add_regular_entry(struct inode *, const struct qstr *, const struct qstr *, struct inode *, nid_t, umode_t); +int __f2fs_do_add_link(struct inode *, struct fscrypt_name*, struct inode *, + nid_t, umode_t); int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, umode_t); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 077310f6659d..88b0975b2882 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -139,8 +139,6 @@ static inline bool need_do_checkpoint(struct inode *inode) need_cp = true; else if (is_sbi_flag_set(sbi, SBI_NEED_CP)) need_cp = true; - else if (file_enc_name(inode) && need_dentry_mark(sbi, inode->i_ino)) - need_cp = true; else if (file_wrong_pino(inode)) need_cp = true; else if (!space_for_roll_forward(sbi)) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index cb591900257a..6f2f95181bb7 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -96,11 +96,12 @@ static int recover_dentry(struct inode *inode, struct page *ipage, struct f2fs_inode *raw_inode = F2FS_INODE(ipage); nid_t pino = le32_to_cpu(raw_inode->i_pino); struct f2fs_dir_entry *de; - struct qstr name; + struct fscrypt_name fname; struct page *page; struct inode *dir, *einode; struct fsync_inode_entry *entry; int err = 0; + char *name; entry = get_fsync_inode(dir_list, pino); if (!entry) { @@ -120,19 +121,17 @@ static int recover_dentry(struct inode *inode, struct page *ipage, dir = entry->inode; - if (file_enc_name(inode)) - return 0; - - name.len = le32_to_cpu(raw_inode->i_namelen); - name.name = raw_inode->i_name; + memset(&fname, 0, sizeof(struct fscrypt_name)); + fname.disk_name.len = le32_to_cpu(raw_inode->i_namelen); + fname.disk_name.name = raw_inode->i_name; - if (unlikely(name.len > F2FS_NAME_LEN)) { + if (unlikely(fname.disk_name.len > F2FS_NAME_LEN)) { WARN_ON(1); err = -ENAMETOOLONG; goto out; } retry: - de = f2fs_find_entry(dir, &name, &page); + de = __f2fs_find_entry(dir, &fname, &page); if (de && inode->i_ino == le32_to_cpu(de->ino)) goto out_unmap_put; @@ -156,7 +155,7 @@ retry: } else if (IS_ERR(page)) { err = PTR_ERR(page); } else { - err = __f2fs_add_link(dir, &name, inode, + err = __f2fs_do_add_link(dir, &fname, inode, inode->i_ino, inode->i_mode); } goto out; @@ -165,9 +164,13 @@ out_unmap_put: f2fs_dentry_kunmap(dir, page); f2fs_put_page(page, 0); out: + if (file_enc_name(inode)) + name = ""; + else + name = raw_inode->i_name; f2fs_msg(inode->i_sb, KERN_NOTICE, "%s: ino = %x, name = %s, dir = %lx, err = %d", - __func__, ino_of_node(ipage), raw_inode->i_name, + __func__, ino_of_node(ipage), name, IS_ERR(dir) ? 0 : dir->i_ino, err); return err; } -- GitLab From 2146510e986b757373fbae49717880b60b8b5120 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 31 Aug 2016 10:43:19 +0800 Subject: [PATCH 0344/5498] f2fs: fix to set superblock dirty correctly tests/generic/251 of fstest suit complains us with below message: ------------[ cut here ]------------ invalid opcode: 0000 [#1] PREEMPT SMP CPU: 2 PID: 7698 Comm: fstrim Tainted: G O 4.7.0+ #21 task: e9f4e000 task.stack: e7262000 EIP: 0060:[] EFLAGS: 00010202 CPU: 2 EIP is at write_checkpoint+0xfde/0x1020 [f2fs] EAX: f33eb300 EBX: eecac310 ECX: 00000001 EDX: ffff0001 ESI: eecac000 EDI: eecac5f0 EBP: e7263dec ESP: e7263d18 DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 CR0: 80050033 CR2: b76ab01c CR3: 2eb89de0 CR4: 000406f0 Stack: 00000001 a220fb7b e9f4e000 00000002 419ff2d3 b3a05151 00000002 e9f4e5d8 e9f4e000 419ff2d3 b3a05151 eecac310 c10b8154 b3a05151 419ff2d3 c10b78bd e9f4e000 e9f4e000 e9f4e5d8 00000001 e9f4e000 ec409000 eecac2cc eecac288 Call Trace: [] ? __lock_acquire+0x3c4/0x760 [] ? mark_held_locks+0x5d/0x80 [] f2fs_trim_fs+0x1c2/0x2e0 [f2fs] [] f2fs_ioctl+0x6b6/0x10b0 [f2fs] [] ? __this_cpu_preempt_check+0xf/0x20 [] ? trace_hardirqs_off_caller+0x91/0x120 [] ? __exchange_data_block+0xd30/0xd30 [f2fs] [] do_vfs_ioctl+0x81/0x7f0 [] ? kmem_cache_free+0x245/0x2e0 [] ? get_unused_fd_flags+0x40/0x40 [] ? putname+0x4c/0x50 [] ? do_sys_open+0x16e/0x1d0 [] ? do_fast_syscall_32+0x30/0x1c0 [] ? __this_cpu_preempt_check+0xf/0x20 [] SyS_ioctl+0x58/0x80 [] do_fast_syscall_32+0xa1/0x1c0 [] sysenter_past_esp+0x45/0x74 EIP: [] write_checkpoint+0xfde/0x1020 [f2fs] SS:ESP 0068:e7263d18 ---[ end trace 4de95d7e6b3aa7c6 ]--- The reason is: with below call stack, we will encounter BUG_ON during doing fstrim. Thread A Thread B - write_checkpoint - do_checkpoint - f2fs_write_inode - update_inode_page - update_inode - set_page_dirty - f2fs_set_node_page_dirty - inc_page_count - percpu_counter_inc - set_sbi_flag(SBI_IS_DIRTY) - clear_sbi_flag(SBI_IS_DIRTY) Thread C Thread D - f2fs_write_node_page - set_node_addr - __set_nat_cache_dirty - nm_i->dirty_nat_cnt++ - do_vfs_ioctl - f2fs_ioctl - f2fs_trim_fs - write_checkpoint - f2fs_bug_on(nm_i->dirty_nat_cnt) Fix it by setting superblock dirty correctly in do_checkpoint and f2fs_write_node_page. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index bae2a61fca27..cdd994904dc6 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1153,6 +1153,16 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) clear_sbi_flag(sbi, SBI_IS_DIRTY); clear_sbi_flag(sbi, SBI_NEED_CP); + /* + * redirty superblock if metadata like node page or inode cache is + * updated during writing checkpoint. + */ + if (get_pages(sbi, F2FS_DIRTY_NODES) || + get_pages(sbi, F2FS_DIRTY_IMETA)) + set_sbi_flag(sbi, SBI_IS_DIRTY); + + f2fs_bug_on(sbi, get_pages(sbi, F2FS_DIRTY_DENTS)); + return 0; } -- GitLab From 7a895023cc2c06c08a6cccb71e112255eb0837ee Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 31 Aug 2016 16:20:37 -0700 Subject: [PATCH 0345/5498] f2fs: set dentry bits on random location in memory This fixes pointer panic when using inline_dentry, which was triggered when backporting to 3.10. Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 2 +- fs/f2fs/inline.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 695006b99a0d..bb3957289a44 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -510,7 +510,7 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, de->ino = cpu_to_le32(ino); set_de_type(de, mode); for (i = 0; i < slots; i++) { - test_and_set_bit_le(bit_pos + i, (void *)d->bitmap); + __set_bit_le(bit_pos + i, (void *)d->bitmap); /* avoid wrong garbage data for readdir */ if (i) (de + i)->name_len = 0; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 66a0674846ef..f7f61abe1a79 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -565,7 +565,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, inline_dentry = inline_data_addr(page); bit_pos = dentry - inline_dentry->dentry; for (i = 0; i < slots; i++) - test_and_clear_bit_le(bit_pos + i, + __clear_bit_le(bit_pos + i, &inline_dentry->dentry_bitmap); set_page_dirty(page); -- GitLab From cf58c169f888158de50944202d02eb815f31bd56 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 5 Sep 2016 12:28:26 +0800 Subject: [PATCH 0346/5498] f2fs: fix minor typo Correct typo from 'destory' to 'destroy'. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 143d6eef26b8..cf9baa795d60 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2599,7 +2599,7 @@ int __init create_segment_manager_caches(void) sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set", sizeof(struct sit_entry_set)); if (!sit_entry_set_slab) - goto destory_discard_entry; + goto destroy_discard_entry; inmem_entry_slab = f2fs_kmem_cache_create("inmem_page_entry", sizeof(struct inmem_pages)); @@ -2609,7 +2609,7 @@ int __init create_segment_manager_caches(void) destroy_sit_entry_set: kmem_cache_destroy(sit_entry_set_slab); -destory_discard_entry: +destroy_discard_entry: kmem_cache_destroy(discard_entry_slab); fail: return -ENOMEM; -- GitLab From 0cfcd2297f86c9249f43f0bedc35606a5f630038 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 5 Sep 2016 12:28:27 +0800 Subject: [PATCH 0347/5498] f2fs: fix to detect temporary name of multimedia file Some applications may create multimeida file with temporary name like '*.jpg.tmp' or '*.mp4.tmp', then rename to '*.jpg' or '*.mp4'. Now, f2fs can only detect multimedia filename with specified format: "filename + '.' + extension", so it will make f2fs missing to detect multimedia file with special temporary name, result in failing to set cold flag on file. This patch enhances detection flow for enabling lookup extension in the middle of temporary filename. Reported-by: Xue Liu Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 37a9092c84e4..748d7b5c9ce9 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -92,18 +92,23 @@ static int is_multimedia_file(const unsigned char *s, const char *sub) { size_t slen = strlen(s); size_t sublen = strlen(sub); + int i; /* * filename format of multimedia file should be defined as: - * "filename + '.' + extension". + * "filename + '.' + extension + (optional: '.' + temp extension)". */ if (slen < sublen + 2) return 0; - if (s[slen - sublen - 1] != '.') - return 0; + for (i = 1; i < slen - sublen; i++) { + if (s[i] != '.') + continue; + if (!strncasecmp(s + i + 1, sub, sublen)) + return 1; + } - return !strncasecmp(s + slen - sublen, sub, sublen); + return 0; } /* -- GitLab From 1455c837f28c783f2ece051aa2df8fa4ac31f4bc Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 6 Sep 2016 13:31:56 -0700 Subject: [PATCH 0348/5498] f2fs: no need to make zeros beyond i_size We don't need to make zeros beyond i_size, since we already wrote that through NEW_ADDR case. Reported-by: Al Viro Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 94b74278e382..18fe8082c2b4 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1639,15 +1639,6 @@ repeat: if (PageUptodate(page)) goto out_clear; - if ((pos & PAGE_MASK) >= i_size_read(inode)) { - unsigned start = pos & (PAGE_SIZE - 1); - unsigned end = start + len; - - /* Reading beyond i_size is simple: memset to zero */ - zero_user_segments(page, 0, start, end, PAGE_SIZE); - goto out_update; - } - if (blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_SIZE); } else { -- GitLab From 4cc278f9bb5b4e3fe395c47b6ed36897bdb56150 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 6 Sep 2016 15:55:54 -0700 Subject: [PATCH 0349/5498] f2fs: avoid page allocation for truncating partial inline_data When truncating cached inline_data, we don't need to allocate a new page all the time. Instead, it must check its page cache only. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 88b0975b2882..981c0e437029 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -525,7 +525,7 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, return 0; if (cache_only) { - page = f2fs_grab_cache_page(mapping, index, false); + page = find_lock_page(mapping, index); if (page && PageUptodate(page)) goto truncate_out; f2fs_put_page(page, 1); -- GitLab From 0ac5f39748607ff68753c401b8409e0ecb13e989 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 1 Sep 2016 10:14:39 +0800 Subject: [PATCH 0350/5498] f2fs: forbid to do fstrim if fs has some error This patch skip fstrim if sbi set SBI_NEED_FSCK flag Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index cf9baa795d60..24f1b3dee0cf 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1276,6 +1276,12 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) if (end <= MAIN_BLKADDR(sbi)) goto out; + if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) { + f2fs_msg(sbi->sb, KERN_WARNING, + "Found FS corruption, run fsck to fix."); + goto out; + } + /* start/end segment number in main_area */ start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start); end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : -- GitLab From 447db2cb791600ef66327bd6990b3f7088347d4e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 1 Sep 2016 12:02:51 -0700 Subject: [PATCH 0351/5498] f2fs: check free_sections for defragmentation Fix wrong condition check for defragmentation of a file. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 10 +++++----- fs/f2fs/segment.c | 6 +++--- fs/f2fs/segment.h | 7 ++++--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 18fe8082c2b4..6482c129f509 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1285,7 +1285,7 @@ write: if (!wbc->for_reclaim) need_balance_fs = true; - else if (has_not_enough_free_secs(sbi, 0)) + else if (has_not_enough_free_secs(sbi, 0, 0)) goto redirty_out; err = -EAGAIN; @@ -1617,7 +1617,7 @@ repeat: if (err) goto fail; - if (need_balance && has_not_enough_free_secs(sbi, 0)) { + if (need_balance && has_not_enough_free_secs(sbi, 0, 0)) { unlock_page(page); f2fs_balance_fs(sbi, true); lock_page(page); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 981c0e437029..dff378b26e31 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1965,7 +1965,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, * avoid defragment running in SSR mode when free section are allocated * intensively */ - if (has_not_enough_free_secs(sbi, sec_num)) { + if (has_not_enough_free_secs(sbi, 0, sec_num)) { err = -EAGAIN; goto out; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c83cc77423ea..4c5b8650b3cc 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -439,7 +439,7 @@ next_step: struct node_info ni; /* stop BG_GC if there is not enough free sections. */ - if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) return; if (check_valid_map(sbi, segno, off) == 0) @@ -712,7 +712,7 @@ next_step: nid_t nid = le32_to_cpu(entry->nid); /* stop BG_GC if there is not enough free sections. */ - if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) return; if (check_valid_map(sbi, segno, off) == 0) @@ -912,7 +912,7 @@ gc_more: goto stop; } - if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) { + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed, 0)) { gc_type = FG_GC; /* * If there is no victim and no prefree segment but still not @@ -923,7 +923,7 @@ gc_more: prefree_segments(sbi)) { write_checkpoint(sbi, &cpc); segno = NULL_SEGNO; - } else if (has_not_enough_free_secs(sbi, 0)) { + } else if (has_not_enough_free_secs(sbi, 0, 0)) { write_checkpoint(sbi, &cpc); } } @@ -940,7 +940,7 @@ gc_more: sbi->cur_victim_sec = NULL_SEGNO; if (!sync) { - if (has_not_enough_free_secs(sbi, sec_freed)) + if (has_not_enough_free_secs(sbi, sec_freed, 0)) goto gc_more; if (gc_type == FG_GC) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 24f1b3dee0cf..28e44c4d6fcb 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -354,7 +354,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) * We should do GC or end up with checkpoint, if there are so many dirty * dir/node pages without enough free segments. */ - if (has_not_enough_free_secs(sbi, 0)) { + if (has_not_enough_free_secs(sbi, 0, 0)) { mutex_lock(&sbi->gc_mutex); f2fs_gc(sbi, false); } @@ -1201,7 +1201,7 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) struct curseg_info *curseg = CURSEG_I(sbi, type); const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; - if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0)) + if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0, 0)) return v_ops->get_victim(sbi, &(curseg)->next_segno, BG_GC, type, SSR); @@ -1400,7 +1400,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, /* direct_io'ed data is aligned to the segment for better performance */ if (direct_io && curseg->next_blkoff && - !has_not_enough_free_secs(sbi, 0)) + !has_not_enough_free_secs(sbi, 0, 0)) __allocate_new_segments(sbi, type); *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index db9ae489a469..59f075f79f85 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -478,7 +478,8 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) reserved_sections(sbi) + 1); } -static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) +static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, + int freed, int needed) { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); @@ -488,8 +489,8 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return false; - return (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + - reserved_sections(sbi)); + return (free_sections(sbi) + freed) <= + (node_secs + 2 * dent_secs + reserved_sections(sbi) + needed); } static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi) -- GitLab From 791641a4002e3c147756ec32b73261a2ce24cdb2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 9 Sep 2016 16:48:15 -0700 Subject: [PATCH 0352/5498] f2fs: add common iget in add_fsync_inode There is no functional change. Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 6f2f95181bb7..427a7bd60a64 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -68,14 +68,20 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, return NULL; } -static struct fsync_inode_entry *add_fsync_inode(struct list_head *head, - struct inode *inode) +static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, + struct list_head *head, nid_t ino) { + struct inode *inode = f2fs_iget(sbi->sb, ino); struct fsync_inode_entry *entry; + if (IS_ERR(inode)) + return ERR_CAST(inode); + entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); - if (!entry) - return NULL; + if (!entry) { + iput(inode); + return ERR_PTR(-ENOMEM); + } entry->inode = inode; list_add_tail(&entry->list, head); @@ -105,16 +111,10 @@ static int recover_dentry(struct inode *inode, struct page *ipage, entry = get_fsync_inode(dir_list, pino); if (!entry) { - dir = f2fs_iget(inode->i_sb, pino); - if (IS_ERR(dir)) { - err = PTR_ERR(dir); - goto out; - } - - entry = add_fsync_inode(dir_list, dir); - if (!entry) { - err = -ENOMEM; - iput(dir); + entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino); + if (IS_ERR(entry)) { + dir = ERR_CAST(entry); + err = PTR_ERR(entry); goto out; } } @@ -228,7 +228,6 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) { unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); struct curseg_info *curseg; - struct inode *inode; struct page *page = NULL; block_t blkaddr; int err = 0; @@ -266,23 +265,15 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) * CP | dnode(F) | inode(DF) * For this case, we should not give up now. */ - inode = f2fs_iget(sbi->sb, ino_of_node(page)); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); + entry = add_fsync_inode(sbi, head, ino_of_node(page)); + if (IS_ERR(entry)) { + err = PTR_ERR(entry); if (err == -ENOENT) { err = 0; goto next; } break; } - - /* add this fsync inode to the list */ - entry = add_fsync_inode(head, inode); - if (!entry) { - err = -ENOMEM; - iput(inode); - break; - } } entry->blkaddr = blkaddr; -- GitLab From b499c16e65244f689df07ff47b2b72f1c2fb83f4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 9 Sep 2016 16:59:39 -0700 Subject: [PATCH 0353/5498] f2fs: avoid ENOMEM during roll-forward recovery This patch gives another chances during roll-forward recovery regarding to -ENOMEM. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/inode.c | 15 +++++++++++++++ fs/f2fs/node.c | 8 +++++--- fs/f2fs/recovery.c | 34 +++++++++++++++++++++------------- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 901ae72891ed..a299af3af78b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1950,6 +1950,7 @@ long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); */ void f2fs_set_inode_flags(struct inode *); struct inode *f2fs_iget(struct super_block *, unsigned long); +struct inode *f2fs_iget_retry(struct super_block *, unsigned long); int try_to_free_nats(struct f2fs_sb_info *, int); int update_inode(struct inode *, struct page *); int update_inode_page(struct inode *); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 9604e4d3d120..739341e83ace 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "f2fs.h" @@ -234,6 +235,20 @@ bad_inode: return ERR_PTR(ret); } +struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino) +{ + struct inode *inode; +retry: + inode = f2fs_iget(sb, ino); + if (IS_ERR(inode)) { + if (PTR_ERR(inode) == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry; + } + } + return inode; +} + int update_inode(struct inode *inode, struct page *node_page) { struct f2fs_inode *ri; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 529807718744..d8f3424776ab 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2017,10 +2017,12 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) if (unlikely(old_ni.blk_addr != NULL_ADDR)) return -EINVAL; - +retry: ipage = f2fs_grab_cache_page(NODE_MAPPING(sbi), ino, false); - if (!ipage) - return -ENOMEM; + if (!ipage) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry; + } /* Should not use this inode from free nid list */ remove_free_nid(NM_I(sbi), ino); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 427a7bd60a64..d10041339a1d 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -71,18 +71,14 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, struct list_head *head, nid_t ino) { - struct inode *inode = f2fs_iget(sbi->sb, ino); + struct inode *inode; struct fsync_inode_entry *entry; + inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) return ERR_CAST(inode); - entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); - if (!entry) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - + entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); entry->inode = inode; list_add_tail(&entry->list, head); @@ -136,7 +132,7 @@ retry: goto out_unmap_put; if (de) { - einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino)); + einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino)); if (IS_ERR(einode)) { WARN_ON(1); err = PTR_ERR(einode); @@ -158,6 +154,8 @@ retry: err = __f2fs_do_add_link(dir, &fname, inode, inode->i_ino, inode->i_mode); } + if (err == -ENOMEM) + goto retry; goto out; out_unmap_put: @@ -357,7 +355,7 @@ got_it: if (ino != dn->inode->i_ino) { /* Deallocate previous index in the node page */ - inode = f2fs_iget(sbi->sb, ino); + inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) return PTR_ERR(inode); } else { @@ -425,10 +423,15 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, end = start + ADDRS_PER_PAGE(page, inode); set_new_dnode(&dn, inode, NULL, NULL, 0); - +retry_dn: err = get_dnode_of_data(&dn, start, ALLOC_NODE); - if (err) + if (err) { + if (err == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry_dn; + } goto out; + } f2fs_wait_on_page_writeback(dn.node_page, NODE, true); @@ -479,11 +482,16 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (err) goto err; } - +retry_prev: /* Check the previous node page having this index */ err = check_index_in_prev_nodes(sbi, dest, &dn); - if (err) + if (err) { + if (err == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry_prev; + } goto err; + } /* write dummy data page */ f2fs_replace_block(sbi, &dn, src, dest, -- GitLab From 29e51102e8d23a10433d6db356d9d0b61c63458f Mon Sep 17 00:00:00 2001 From: Fan Li Date: Sat, 10 Sep 2016 11:19:37 +0800 Subject: [PATCH 0354/5498] f2fs: fix parameters of __exchange_data_block __exchange_data_block should take block indexes as parameters instead of offsets in bytes. Signed-off-by: Fan li Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index dff378b26e31..dfd26722dcfd 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2147,8 +2147,9 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); - ret = __exchange_data_block(src, dst, pos_in, - pos_out, len >> F2FS_BLKSIZE_BITS, false); + ret = __exchange_data_block(src, dst, pos_in >> F2FS_BLKSIZE_BITS, + pos_out >> F2FS_BLKSIZE_BITS, + len >> F2FS_BLKSIZE_BITS, false); if (!ret) { if (dst_max_i_size) -- GitLab From dde2ac3130adb01fcc1f3d9401cb25dfa0d5ebc4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 6 Sep 2016 11:02:03 -0700 Subject: [PATCH 0355/5498] f2fs: fix to set PageUptodate in f2fs_write_end correctly Previously, f2fs_write_begin sets PageUptodate all the time. But, when user tries to update the entire page (i.e., len == PAGE_SIZE), we need to consider that the page is able to be copied partially afterwards. In such the case, we will lose the remaing region in the page. This patch fixes this by setting PageUptodate in f2fs_write_end as given copied result. In the short copy case, it returns zero to let generic_perform_write retry copying user data again. As a result, f2fs_write_end() works: PageUptodate len copied return retry 1. no 4096 4096 4096 false -> return 4096 2. no 4096 1024 0 true -> goto #1 case 3. yes 2048 2048 2048 false -> return 2048 4. yes 2048 1024 1024 false -> return 1024 Suggested-by: Al Viro Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6482c129f509..53e657808f83 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1634,13 +1634,12 @@ repeat: if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); - if (len == PAGE_SIZE) - goto out_update; - if (PageUptodate(page)) - goto out_clear; + if (len == PAGE_SIZE || PageUptodate(page)) + return 0; if (blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_SIZE); + SetPageUptodate(page); } else { struct bio *bio; @@ -1668,11 +1667,6 @@ repeat: goto fail; } } -out_update: - if (!PageUptodate(page)) - SetPageUptodate(page); -out_clear: - clear_cold_data(page); return 0; fail: @@ -1690,11 +1684,26 @@ static int f2fs_write_end(struct file *file, trace_f2fs_write_end(inode, pos, len, copied); + /* + * This should be come from len == PAGE_SIZE, and we expect copied + * should be PAGE_SIZE. Otherwise, we treat it with zero copied and + * let generic_perform_write() try to copy data again through copied=0. + */ + if (!PageUptodate(page)) { + if (unlikely(copied != PAGE_SIZE)) + copied = 0; + else + SetPageUptodate(page); + } + if (!copied) + goto unlock_out; + set_page_dirty(page); + clear_cold_data(page); if (pos + copied > i_size_read(inode)) f2fs_i_size_write(inode, pos + copied); - +unlock_out: f2fs_put_page(page, 1); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return copied; -- GitLab From c7c2c3a276d2bf0a5ada026db1f9e152312b3c2f Mon Sep 17 00:00:00 2001 From: Fan Li Date: Tue, 13 Sep 2016 11:35:42 +0800 Subject: [PATCH 0356/5498] f2fs: exclude special cases for f2fs_move_file_range When src and dst is the same file, and the latter part of source region overlaps with the former part of destination region, current implement will overwrite data which hasn't been moved yet and truncate data in overlapped region. This patch return -EINVAL when such cases occur and return 0 when source region and destination region is actually the same part of the same file. Signed-off-by: Fan li Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index dfd26722dcfd..bbcfced32a02 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2096,6 +2096,13 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, if (f2fs_encrypted_inode(src) || f2fs_encrypted_inode(dst)) return -EOPNOTSUPP; + if (src == dst) { + if (pos_in == pos_out) + return 0; + if (pos_out > pos_in && pos_out < pos_in + len) + return -EINVAL; + } + inode_lock(src); if (src != dst) { if (!inode_trylock(dst)) { -- GitLab From a46bb73c756370eb354087d40f81e14ea68f3dda Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Wed, 14 Sep 2016 03:00:02 +0800 Subject: [PATCH 0357/5498] f2fs: remove dead code f2fs_check_acl The macro f2fs_check_acl is defined but never used since the initial commit, this patch removes the code that has been dead for several years. Signed-off-by: Tiezhu Yang Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/acl.h b/fs/f2fs/acl.h index b2334d11dae8..2c685185c24d 100644 --- a/fs/f2fs/acl.h +++ b/fs/f2fs/acl.h @@ -41,7 +41,6 @@ extern int f2fs_set_acl(struct inode *, struct posix_acl *, int); extern int f2fs_init_acl(struct inode *, struct inode *, struct page *, struct page *); #else -#define f2fs_check_acl NULL #define f2fs_get_acl NULL #define f2fs_set_acl NULL -- GitLab From 0aeecf1f794b3e5e47da786d80ed6fa4f593f020 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 12 Sep 2016 15:08:37 -0700 Subject: [PATCH 0358/5498] f2fs: handle error in recover_orphan_inode This patch enhances the error path in recover_orphan_inode. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index cdd994904dc6..593e3719550a 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -530,8 +530,9 @@ void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) { struct inode *inode; + struct node_info ni; - inode = f2fs_iget(sbi->sb, ino); + inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) { /* * there should be a bug that we can't find the entry @@ -545,6 +546,22 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) /* truncate all the data during iput */ iput(inode); + + get_node_info(sbi, ino, &ni); + + /* ENOMEM was fully retried in f2fs_evict_inode. */ + if (ni.blk_addr != NULL_ADDR) { + int err = acquire_orphan_inode(sbi); + + if (err) { + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_msg(sbi->sb, KERN_WARNING, + "%s: orphan failed (ino=%x), run fsck to fix.", + __func__, ino); + return err; + } + __add_ino_entry(sbi, ino, ORPHAN_INO); + } return 0; } -- GitLab From 2c57f47175a7940ad42575f9d55ad2668c0fc3c6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 18 Sep 2016 23:30:03 +0800 Subject: [PATCH 0359/5498] f2fs: make f2fs_filetype_table static There is no more user of f2fs_filetype_table outside of dir.c, make it static. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index bb3957289a44..699b043de007 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -37,7 +37,7 @@ static unsigned int bucket_blocks(unsigned int level) return 4; } -unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { +static unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { [F2FS_FT_UNKNOWN] = DT_UNKNOWN, [F2FS_FT_REG_FILE] = DT_REG, [F2FS_FT_DIR] = DT_DIR, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a299af3af78b..70088786b85a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1966,7 +1966,6 @@ struct dentry *f2fs_get_parent(struct dentry *child); /* * dir.c */ -extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; void set_de_type(struct f2fs_dir_entry *, umode_t); unsigned char get_de_type(struct f2fs_dir_entry *); struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *, -- GitLab From 5f3d646a5c154e66669791fba6aa7bbf9526e019 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 18 Sep 2016 23:30:04 +0800 Subject: [PATCH 0360/5498] f2fs: fix to return error number of read_all_xattrs correctly We treat all error in read_all_xattrs as a no memory error, which covers the real reason of failure in it. Fix it by return correct errno in order to reflect the real cause. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 45e77e8ed93c..4f9bd6bd8e3f 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -266,18 +266,20 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, return entry; } -static void *read_all_xattrs(struct inode *inode, struct page *ipage) +static int read_all_xattrs(struct inode *inode, struct page *ipage, + void **base_addr) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_xattr_header *header; size_t size = PAGE_SIZE, inline_size = 0; void *txattr_addr; + int err; inline_size = inline_xattr_size(inode); txattr_addr = kzalloc(inline_size + size, GFP_F2FS_ZERO); if (!txattr_addr) - return NULL; + return -ENOMEM; /* read from inline xattr */ if (inline_size) { @@ -288,8 +290,10 @@ static void *read_all_xattrs(struct inode *inode, struct page *ipage) inline_addr = inline_xattr_addr(ipage); } else { page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) + if (IS_ERR(page)) { + err = PTR_ERR(page); goto fail; + } inline_addr = inline_xattr_addr(page); } memcpy(txattr_addr, inline_addr, inline_size); @@ -303,8 +307,10 @@ static void *read_all_xattrs(struct inode *inode, struct page *ipage) /* The inode already has an extended attribute block. */ xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); - if (IS_ERR(xpage)) + if (IS_ERR(xpage)) { + err = PTR_ERR(xpage); goto fail; + } xattr_addr = page_address(xpage); memcpy(txattr_addr + inline_size, xattr_addr, PAGE_SIZE); @@ -318,10 +324,11 @@ static void *read_all_xattrs(struct inode *inode, struct page *ipage) header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC); header->h_refcount = cpu_to_le32(1); } - return txattr_addr; + *base_addr = txattr_addr; + return 0; fail: kzfree(txattr_addr); - return NULL; + return err; } static inline int write_all_xattrs(struct inode *inode, __u32 hsize, @@ -415,9 +422,9 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name, if (len > F2FS_NAME_LEN) return -ERANGE; - base_addr = read_all_xattrs(inode, ipage); - if (!base_addr) - return -ENOMEM; + error = read_all_xattrs(inode, ipage, &base_addr); + if (error) + return error; entry = __find_xattr(base_addr, index, len, name); if (IS_XATTR_LAST_ENTRY(entry)) { @@ -451,9 +458,9 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) int error = 0; size_t rest = buffer_size; - base_addr = read_all_xattrs(inode, NULL); - if (!base_addr) - return -ENOMEM; + error = read_all_xattrs(inode, NULL, &base_addr); + if (error) + return error; list_for_each_xattr(entry, base_addr) { const struct xattr_handler *handler = @@ -505,9 +512,9 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (size > MAX_VALUE_LEN(inode)) return -E2BIG; - base_addr = read_all_xattrs(inode, ipage); - if (!base_addr) - return -ENOMEM; + error = read_all_xattrs(inode, ipage, &base_addr); + if (error) + return error; /* find entry with wanted name. */ here = __find_xattr(base_addr, index, len, name); -- GitLab From 3801200fddf9f2bbfffc22ecc1e7275113a6a271 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 18 Sep 2016 23:30:07 +0800 Subject: [PATCH 0361/5498] f2fs: support IO error injection This patch adds to support IO error injection for testing IO error tolerance of f2fs. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 +++++ fs/f2fs/f2fs.h | 3 +++ fs/f2fs/super.c | 1 + 3 files changed, 9 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 53e657808f83..4f5b55801285 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -35,6 +35,11 @@ static void f2fs_read_end_io(struct bio *bio, int err) struct bio_vec *bvec; int i; +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(FAULT_IO)) + err = -EIO; +#endif + if (f2fs_bio_encrypted(bio)) { if (err) { fscrypt_release_ctx(bio->bi_private); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 70088786b85a..0c02b3b5efd1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -47,6 +47,7 @@ enum { FAULT_BLOCK, FAULT_DIR_DEPTH, FAULT_EVICT_INODE, + FAULT_IO, FAULT_MAX, }; @@ -78,6 +79,8 @@ static inline bool time_to_inject(int type) return false; else if (type == FAULT_EVICT_INODE && !IS_FAULT_SET(type)) return false; + else if (type == FAULT_IO && !IS_FAULT_SET(type)) + return false; atomic_inc(&f2fs_fault.inject_ops); if (atomic_read(&f2fs_fault.inject_ops) >= f2fs_fault.inject_rate) { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0e578f04abfc..daeeee8753f8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -50,6 +50,7 @@ char *fault_name[FAULT_MAX] = { [FAULT_BLOCK] = "no more block", [FAULT_DIR_DEPTH] = "too big dir depth", [FAULT_EVICT_INODE] = "evict_inode fail", + [FAULT_IO] = "IO error", }; static void f2fs_build_fault_attr(unsigned int rate) -- GitLab From 806e871a4b689668680ecb8ab337de3c483ec6b5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 18 Sep 2016 23:30:08 +0800 Subject: [PATCH 0362/5498] f2fs: show dirty inode number This patch enables showing dirty inode number in procfs. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 3 +++ fs/f2fs/f2fs.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 481c92dce61e..8d8624864e96 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -45,6 +45,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA); + si->ndirty_imeta = get_pages(sbi, F2FS_DIRTY_IMETA); si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; @@ -321,6 +322,8 @@ static int stat_show(struct seq_file *s, void *v) si->ndirty_data, si->ndirty_files); seq_printf(s, " - meta: %4lld in %4d\n", si->ndirty_meta, si->meta_pages); + seq_printf(s, " - imeta: %4lld\n", + si->ndirty_imeta); seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", si->dirty_nats, si->nats, si->dirty_sits, si->sits); seq_printf(s, " - free_nids: %9d\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0c02b3b5efd1..f2693e5d952c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2195,7 +2195,8 @@ struct f2fs_stat_info { unsigned long long hit_largest, hit_cached, hit_rbtree; unsigned long long hit_total, total_ext; int ext_tree, zombie_tree, ext_node; - s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, inmem_pages; + s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta; + s64 inmem_pages; unsigned int ndirty_dirs, ndirty_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits, fnids; int total_count, utilization; -- GitLab From 12d8feda5e928984fee87dd172ea91825b5cbc90 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Sun, 18 Sep 2016 08:16:56 +0800 Subject: [PATCH 0363/5498] f2fs: preallocate blocks for encrypted file This patch allow preallocates data blocks for buffered aio writes in encrypted file. Signed-off-by: Yunlei He Reviewed-by: Chao Yu [Jaegeuk Kim: fix to avoid BUG_ON] Signed-off-by: Jaegeuk Kim fs/f2fs/data.c --- fs/f2fs/data.c | 6 +----- fs/f2fs/segment.c | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4f5b55801285..5852b633375c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -634,9 +634,6 @@ ssize_t f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_next_pgofs = NULL; - if (f2fs_encrypted_inode(inode)) - return 0; - if (dio) { ret = f2fs_convert_inline_inode(inode); if (ret) @@ -1524,8 +1521,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, * we already allocated all the blocks, so we don't need to get * the block addresses when there is no need to fill the page. */ - if (!f2fs_has_inline_data(inode) && !f2fs_encrypted_inode(inode) && - len == PAGE_SIZE) + if (!f2fs_has_inline_data(inode) && len == PAGE_SIZE) return 0; if (f2fs_has_inline_data(inode) || diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 28e44c4d6fcb..5610fe2b49a5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1597,11 +1597,9 @@ void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, { struct page *cpage; - if (blkaddr == NEW_ADDR) + if (blkaddr == NEW_ADDR || blkaddr == NULL_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, true); -- GitLab From aa88a60c9dac98b6f1b071407d101706476dd916 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 19 Sep 2016 17:55:10 -0700 Subject: [PATCH 0364/5498] f2fs: use crc and cp version to determine roll-forward recovery Previously, we used cp_version only to detect recoverable dnodes. In order to avoid same garbage cp_version, we needed to truncate the next dnode during checkpoint, resulting in additional discard or data write. If we can distinguish this by using crc in addition to cp_version, we can remove this overhead. There is backward compatibility concern where it changes node_footer layout. So, this patch introduces a new checkpoint flag, CP_CRC_RECOVERY_FLAG, to detect new layout. New layout will be activated only when this flag is set. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 21 ++--------- fs/f2fs/f2fs.h | 1 - fs/f2fs/node.h | 77 ++++++++++++++++++++++++++--------------- fs/f2fs/recovery.c | 34 ++++-------------- fs/f2fs/segment.c | 22 ------------ fs/f2fs/super.c | 5 ++- include/linux/f2fs_fs.h | 1 + 7 files changed, 63 insertions(+), 98 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 593e3719550a..0684ded873d3 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -991,7 +991,6 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi) static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; nid_t last_nid = nm_i->next_scan_nid; @@ -1000,19 +999,10 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) __u32 crc32 = 0; int i; int cp_payload_blks = __cp_payload(sbi); - block_t discard_blk = NEXT_FREE_BLKADDR(sbi, curseg); - bool invalidate = false; struct super_block *sb = sbi->sb; struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); u64 kbytes_written; - /* - * This avoids to conduct wrong roll-forward operations and uses - * metapages, so should be called prior to sync_meta_pages below. - */ - if (!test_opt(sbi, LFS) && discard_next_dnode(sbi, discard_blk)) - invalidate = true; - /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { sync_meta_pages(sbi, META, LONG_MAX); @@ -1088,6 +1078,9 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) set_ckpt_flags(ckpt, CP_FSCK_FLAG); + /* set this flag to activate crc|cp_ver for recovery */ + set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); + /* update SIT/NAT bitmap */ get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP)); @@ -1153,14 +1146,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* wait for previous submitted meta pages writeback */ wait_on_all_pages_writeback(sbi); - /* - * invalidate meta page which is used temporarily for zeroing out - * block at the end of warm node chain. - */ - if (invalidate) - invalidate_mapping_pages(META_MAPPING(sbi), discard_blk, - discard_blk); - release_ino_entry(sbi, false); if (unlikely(f2fs_cp_error(sbi))) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f2693e5d952c..44cfaa87deec 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2084,7 +2084,6 @@ bool is_checkpointed_data(struct f2fs_sb_info *, block_t); void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *); void release_discard_addrs(struct f2fs_sb_info *); -bool discard_next_dnode(struct f2fs_sb_info *, block_t); int npages_for_summary_flush(struct f2fs_sb_info *, bool); void allocate_new_segments(struct f2fs_sb_info *); int f2fs_trim_fs(struct f2fs_sb_info *, struct fstrim_range *); diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index fc7684554b1a..e8ca64a70de0 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -229,6 +229,37 @@ static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid) f2fs_change_bit(block_off, nm_i->nat_bitmap); } +static inline nid_t ino_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le32_to_cpu(rn->footer.ino); +} + +static inline nid_t nid_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le32_to_cpu(rn->footer.nid); +} + +static inline unsigned int ofs_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + unsigned flag = le32_to_cpu(rn->footer.flag); + return flag >> OFFSET_BIT_SHIFT; +} + +static inline __u64 cpver_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le64_to_cpu(rn->footer.cp_ver); +} + +static inline block_t next_blkaddr_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le32_to_cpu(rn->footer.next_blkaddr); +} + static inline void fill_node_footer(struct page *page, nid_t nid, nid_t ino, unsigned int ofs, bool reset) { @@ -259,40 +290,30 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page)); struct f2fs_node *rn = F2FS_NODE(page); + size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); + __u64 cp_ver = le64_to_cpu(ckpt->checkpoint_ver); - rn->footer.cp_ver = ckpt->checkpoint_ver; + if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { + __u64 crc = le32_to_cpu(*((__le32 *) + ((unsigned char *)ckpt + crc_offset))); + cp_ver |= (crc << 32); + } + rn->footer.cp_ver = cpu_to_le64(cp_ver); rn->footer.next_blkaddr = cpu_to_le32(blkaddr); } -static inline nid_t ino_of_node(struct page *node_page) -{ - struct f2fs_node *rn = F2FS_NODE(node_page); - return le32_to_cpu(rn->footer.ino); -} - -static inline nid_t nid_of_node(struct page *node_page) +static inline bool is_recoverable_dnode(struct page *page) { - struct f2fs_node *rn = F2FS_NODE(node_page); - return le32_to_cpu(rn->footer.nid); -} - -static inline unsigned int ofs_of_node(struct page *node_page) -{ - struct f2fs_node *rn = F2FS_NODE(node_page); - unsigned flag = le32_to_cpu(rn->footer.flag); - return flag >> OFFSET_BIT_SHIFT; -} - -static inline unsigned long long cpver_of_node(struct page *node_page) -{ - struct f2fs_node *rn = F2FS_NODE(node_page); - return le64_to_cpu(rn->footer.cp_ver); -} + struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page)); + size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); + __u64 cp_ver = cur_cp_version(ckpt); -static inline block_t next_blkaddr_of_node(struct page *node_page) -{ - struct f2fs_node *rn = F2FS_NODE(node_page); - return le32_to_cpu(rn->footer.next_blkaddr); + if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { + __u64 crc = le32_to_cpu(*((__le32 *) + ((unsigned char *)ckpt + crc_offset))); + cp_ver |= (crc << 32); + } + return cpu_to_le64(cp_ver) == cpver_of_node(page); } /* diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index d10041339a1d..6b4ee8a52603 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -224,7 +224,6 @@ static bool is_same_inode(struct inode *inode, struct page *ipage) static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) { - unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); struct curseg_info *curseg; struct page *page = NULL; block_t blkaddr; @@ -242,7 +241,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) page = get_tmp_page(sbi, blkaddr); - if (cp_ver != cpver_of_node(page)) + if (!is_recoverable_dnode(page)) break; if (!is_fsync_dnode(page)) @@ -516,7 +515,6 @@ out: static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, struct list_head *dir_list) { - unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); struct curseg_info *curseg; struct page *page = NULL; int err = 0; @@ -536,7 +534,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, page = get_tmp_page(sbi, blkaddr); - if (cp_ver != cpver_of_node(page)) { + if (!is_recoverable_dnode(page)) { f2fs_put_page(page, 1); break; } @@ -628,35 +626,15 @@ out: } clear_sbi_flag(sbi, SBI_POR_DOING); - if (err) { - bool invalidate = false; - - if (test_opt(sbi, LFS)) { - update_meta_page(sbi, NULL, blkaddr); - invalidate = true; - } else if (discard_next_dnode(sbi, blkaddr)) { - invalidate = true; - } - - /* Flush all the NAT/SIT pages */ - while (get_pages(sbi, F2FS_DIRTY_META)) - sync_meta_pages(sbi, META, LONG_MAX); - - /* invalidate temporary meta page */ - if (invalidate) - invalidate_mapping_pages(META_MAPPING(sbi), - blkaddr, blkaddr); - + if (err) set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); - mutex_unlock(&sbi->cp_mutex); - } else if (need_writecp) { + mutex_unlock(&sbi->cp_mutex); + + if (!err && need_writecp) { struct cp_control cpc = { .reason = CP_RECOVERY, }; - mutex_unlock(&sbi->cp_mutex); err = write_checkpoint(sbi, &cpc); - } else { - mutex_unlock(&sbi->cp_mutex); } destroy_fsync_dnodes(&dir_list); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5610fe2b49a5..7085d7e7fcf8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -597,28 +597,6 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); } -bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr) -{ - int err = -EOPNOTSUPP; - - if (test_opt(sbi, DISCARD)) { - struct seg_entry *se = get_seg_entry(sbi, - GET_SEGNO(sbi, blkaddr)); - unsigned int offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); - - if (f2fs_test_bit(offset, se->discard_map)) - return false; - - err = f2fs_issue_discard(sbi, blkaddr, 1); - } - - if (err) { - update_meta_page(sbi, NULL, blkaddr); - return true; - } - return false; -} - static void __add_discard_entry(struct f2fs_sb_info *sbi, struct cp_control *cpc, struct seg_entry *se, unsigned int start, unsigned int end) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index daeeee8753f8..14cc42b54bf7 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1814,6 +1814,9 @@ try_onemore: if (need_fsck) set_sbi_flag(sbi, SBI_NEED_FSCK); + if (!retry) + goto skip_recovery; + err = recover_fsync_data(sbi, false); if (err < 0) { need_fsck = true; @@ -1831,7 +1834,7 @@ try_onemore: goto free_kobj; } } - +skip_recovery: /* recover_fsync_data() cleared this already */ clear_sbi_flag(sbi, SBI_POR_DOING); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 71a9ba836a1f..5675ff23e988 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -100,6 +100,7 @@ struct f2fs_super_block { /* * For checkpoint */ +#define CP_CRC_RECOVERY_FLAG 0x00000040 #define CP_FASTBOOT_FLAG 0x00000020 #define CP_FSCK_FLAG 0x00000010 #define CP_ERROR_FLAG 0x00000008 -- GitLab From eafba989779137e5dedd998caf990c19b5014170 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 19 Sep 2016 18:13:54 -0700 Subject: [PATCH 0365/5498] f2fs: put directory inodes before checkpoint in roll-forward recovery Before checkpoint, we'd be better drop any inodes. Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 6b4ee8a52603..72afe7993c5a 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -630,6 +630,9 @@ out: set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); mutex_unlock(&sbi->cp_mutex); + /* let's drop all the directory inodes for clean checkpoint */ + destroy_fsync_dnodes(&dir_list); + if (!err && need_writecp) { struct cp_control cpc = { .reason = CP_RECOVERY, @@ -637,7 +640,6 @@ out: err = write_checkpoint(sbi, &cpc); } - destroy_fsync_dnodes(&dir_list); kmem_cache_destroy(fsync_entry_slab); return ret ? ret: err; } -- GitLab From 3c608f2b4fe6558f4414d4982f527261f790766e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 20 Sep 2016 10:29:47 +0800 Subject: [PATCH 0366/5498] f2fs: fix to avoid race condition when updating sbi flag Making updating of sbi flag atomic by using {test,set,clear}_bit, otherwise in concurrency scenario, the flag could be updated incorrectly. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 44cfaa87deec..c044bc9e8f5c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -815,7 +815,7 @@ struct f2fs_sb_info { struct proc_dir_entry *s_proc; /* proc entry */ struct f2fs_super_block *raw_super; /* raw super block pointer */ int valid_super_block; /* valid super block no */ - int s_flag; /* flags for sbi */ + unsigned long s_flag; /* flags for sbi */ #ifdef CONFIG_F2FS_FS_ENCRYPTION u8 key_prefix[F2FS_KEY_DESC_PREFIX_SIZE]; @@ -1084,17 +1084,17 @@ static inline struct address_space *NODE_MAPPING(struct f2fs_sb_info *sbi) static inline bool is_sbi_flag_set(struct f2fs_sb_info *sbi, unsigned int type) { - return sbi->s_flag & (0x01 << type); + return test_bit(type, &sbi->s_flag); } static inline void set_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) { - sbi->s_flag |= (0x01 << type); + set_bit(type, &sbi->s_flag); } static inline void clear_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) { - sbi->s_flag &= ~(0x01 << type); + clear_bit(type, &sbi->s_flag); } static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) -- GitLab From 55c58b1f361144daba5ac0193bc966c621a75adc Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 20 Sep 2016 11:04:18 +0800 Subject: [PATCH 0367/5498] f2fs: introduce cp_lock to protect updating of ckpt_flags This patch introduces spinlock to protect updating process of ckpt_flags field in struct f2fs_checkpoint, it avoids incorrectly updating in race condition. Signed-off-by: Chao Yu [Jaegeuk Kim: add __is_set_ckpt_flags likewise __set_ckpt_flags] Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 31 ++++++++++++++++++------------- fs/f2fs/f2fs.h | 41 +++++++++++++++++++++++++++++++++-------- fs/f2fs/node.h | 4 ++-- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 4 ++-- fs/f2fs/super.c | 5 +++-- 6 files changed, 59 insertions(+), 28 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 0684ded873d3..9accdd1179c3 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -28,7 +28,7 @@ struct kmem_cache *inode_entry_slab; void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io) { - set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); + set_ckpt_flags(sbi, CP_ERROR_FLAG); sbi->sb->s_flags |= MS_RDONLY; if (!end_io) f2fs_flush_merged_bios(sbi); @@ -570,7 +570,7 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) block_t start_blk, orphan_blocks, i, j; int err; - if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG)) + if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) return 0; start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); @@ -594,7 +594,7 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) f2fs_put_page(page, 1); } /* clear Orphan Flag */ - clear_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG); + clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG); return 0; } @@ -1042,10 +1042,12 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* 2 cp + n data seg summary + orphan inode blocks */ data_sum_blocks = npages_for_summary_flush(sbi, false); + spin_lock(&sbi->cp_lock); if (data_sum_blocks < NR_CURSEG_DATA_TYPE) - set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); + __set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); else - clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); + __clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); + spin_unlock(&sbi->cp_lock); orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num); ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + @@ -1060,26 +1062,29 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) cp_payload_blks + data_sum_blocks + orphan_blocks); + spin_lock(&sbi->cp_lock); if (cpc->reason == CP_UMOUNT) - set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + __set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); else - clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + __clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); if (cpc->reason == CP_FASTBOOT) - set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + __set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); else - clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + __clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); if (orphan_num) - set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); + __set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); else - clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); + __clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) - set_ckpt_flags(ckpt, CP_FSCK_FLAG); + __set_ckpt_flags(ckpt, CP_FSCK_FLAG); /* set this flag to activate crc|cp_ver for recovery */ - set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); + __set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); + + spin_unlock(&sbi->cp_lock); /* update SIT/NAT bitmap */ get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c044bc9e8f5c..22f146da1569 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -835,6 +835,7 @@ struct f2fs_sb_info { /* for checkpoint */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ + spinlock_t cp_lock; /* for flag in ckpt */ struct inode *meta_inode; /* cache meta blocks */ struct mutex cp_mutex; /* checkpoint procedure lock */ struct rw_semaphore cp_rwsem; /* blocking FS operations */ @@ -1102,26 +1103,50 @@ static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) return le64_to_cpu(cp->checkpoint_ver); } -static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +static inline bool __is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) { unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); + return ckpt_flags & f; } -static inline void set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +static inline bool is_set_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) { - unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); + return __is_set_ckpt_flags(F2FS_CKPT(sbi), f); +} + +static inline void __set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +{ + unsigned int ckpt_flags; + + ckpt_flags = le32_to_cpu(cp->ckpt_flags); ckpt_flags |= f; cp->ckpt_flags = cpu_to_le32(ckpt_flags); } -static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +static inline void set_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) { - unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); + spin_lock(&sbi->cp_lock); + __set_ckpt_flags(F2FS_CKPT(sbi), f); + spin_unlock(&sbi->cp_lock); +} + +static inline void __clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +{ + unsigned int ckpt_flags; + + ckpt_flags = le32_to_cpu(cp->ckpt_flags); ckpt_flags &= (~f); cp->ckpt_flags = cpu_to_le32(ckpt_flags); } +static inline void clear_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) +{ + spin_lock(&sbi->cp_lock); + __clear_ckpt_flags(F2FS_CKPT(sbi), f); + spin_unlock(&sbi->cp_lock); +} + static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) { struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); @@ -1167,8 +1192,8 @@ static inline bool __remain_node_summaries(int reason) static inline bool __exist_node_summaries(struct f2fs_sb_info *sbi) { - return (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG) || - is_set_ckpt_flags(F2FS_CKPT(sbi), CP_FASTBOOT_FLAG)); + return (is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG) || + is_set_ckpt_flags(sbi, CP_FASTBOOT_FLAG)); } /* @@ -1870,7 +1895,7 @@ static inline int f2fs_readonly(struct super_block *sb) static inline bool f2fs_cp_error(struct f2fs_sb_info *sbi) { - return is_set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); + return is_set_ckpt_flags(sbi, CP_ERROR_FLAG); } static inline bool is_dot_dotdot(const struct qstr *str) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index e8ca64a70de0..868bec65e51c 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -293,7 +293,7 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr) size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); __u64 cp_ver = le64_to_cpu(ckpt->checkpoint_ver); - if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { + if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { __u64 crc = le32_to_cpu(*((__le32 *) ((unsigned char *)ckpt + crc_offset))); cp_ver |= (crc << 32); @@ -308,7 +308,7 @@ static inline bool is_recoverable_dnode(struct page *page) size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); __u64 cp_ver = cur_cp_version(ckpt); - if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { + if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { __u64 crc = le32_to_cpu(*((__le32 *) ((unsigned char *)ckpt + crc_offset))); cp_ver |= (crc << 32); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 72afe7993c5a..8011cbb5500c 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -627,7 +627,7 @@ out: clear_sbi_flag(sbi, SBI_POR_DOING); if (err) - set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); + set_ckpt_flags(sbi, CP_ERROR_FLAG); mutex_unlock(&sbi->cp_mutex); /* let's drop all the directory inodes for clean checkpoint */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7085d7e7fcf8..3f8f8eb8aa79 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1723,7 +1723,7 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) int type = CURSEG_HOT_DATA; int err; - if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) { + if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) { int npages = npages_for_summary_flush(sbi, true); if (npages >= 2) @@ -1820,7 +1820,7 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi, void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) { - if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) + if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) write_compacted_summaries(sbi, start_blk); else write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 14cc42b54bf7..1efcbc98c4b1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -723,7 +723,7 @@ static void f2fs_put_super(struct super_block *sb) * clean checkpoint again. */ if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) || - !is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG)) { + !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { struct cp_control cpc = { .reason = CP_UMOUNT, }; @@ -1465,6 +1465,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi) mutex_init(&sbi->umount_mutex); mutex_init(&sbi->wio_mutex[NODE]); mutex_init(&sbi->wio_mutex[DATA]); + spin_lock_init(&sbi->cp_lock); #ifdef CONFIG_F2FS_FS_ENCRYPTION memcpy(sbi->key_prefix, F2FS_KEY_DESC_PREFIX, @@ -1806,7 +1807,7 @@ try_onemore: * previous checkpoint was not done by clean system shutdown. */ if (bdev_read_only(sb->s_bdev) && - !is_set_ckpt_flags(sbi->ckpt, CP_UMOUNT_FLAG)) { + !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { err = -EROFS; goto free_kobj; } -- GitLab From c6d1d0402b6dc80a182903e5e2b949dbfb8ed783 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Sep 2016 09:28:06 -0700 Subject: [PATCH 0368/5498] f2fs: assign return value in f2fs_gc This patch adds a return value of write_checkpoint for f2fs_gc. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 4c5b8650b3cc..618b00195216 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -921,10 +921,14 @@ gc_more: */ if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi)) { - write_checkpoint(sbi, &cpc); + ret = write_checkpoint(sbi, &cpc); + if (ret) + goto stop; segno = NULL_SEGNO; } else if (has_not_enough_free_secs(sbi, 0, 0)) { - write_checkpoint(sbi, &cpc); + ret = write_checkpoint(sbi, &cpc); + if (ret) + goto stop; } } @@ -944,7 +948,7 @@ gc_more: goto gc_more; if (gc_type == FG_GC) - write_checkpoint(sbi, &cpc); + ret = write_checkpoint(sbi, &cpc); } stop: mutex_unlock(&sbi->gc_mutex); -- GitLab From fcbc0c6ba0a974471a83a7d4c6dbd7266bce01f4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Sep 2016 09:34:48 -0700 Subject: [PATCH 0369/5498] f2fs: should put_page for summary page We should call put_page for preloaded summary pages in do_garbage_collect. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 618b00195216..28c685e5b763 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -844,7 +844,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, for (segno = start_segno; segno < end_segno; segno++) { if (get_valid_blocks(sbi, segno, 1) == 0) - continue; + goto next; /* find segment summary of victim */ sum_page = find_get_page(META_MAPPING(sbi), @@ -870,7 +870,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, gc_type); stat_inc_seg_count(sbi, type, gc_type); - +next: f2fs_put_page(sum_page, 0); } -- GitLab From 3f1d92f70927ee24b54a3202f76289f2d175d3ba Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Sep 2016 09:37:23 -0700 Subject: [PATCH 0370/5498] f2fs: avoid gc in cp_error case Otherwise, we can hit f2fs_bug_on(sbi, !PageUptodate(sum_page)); Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 28c685e5b763..8b7021fc661a 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -843,7 +843,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, for (segno = start_segno; segno < end_segno; segno++) { - if (get_valid_blocks(sbi, segno, 1) == 0) + if (get_valid_blocks(sbi, segno, 1) == 0 || + unlikely(f2fs_cp_error(sbi))) goto next; /* find segment summary of victim */ -- GitLab From 2b0387bf73667b8461c1e9097cc80d61fbd0533b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Sep 2016 11:39:42 -0700 Subject: [PATCH 0371/5498] f2fs: handle errors during recover_orphan_inodes This patch fixes to handle EIO during recover_orphan_inode() given the below panic. F2FS-fs : inject IO error in f2fs_read_end_io+0xe6/0x100 [f2fs] ------------[ cut here ]------------ RIP: 0010:[] [] f2fs_evict_inode+0x433/0x470 [f2fs] RSP: 0018:ffff92f8b7fb7c30 EFLAGS: 00010246 RAX: ffff92fb88a13500 RBX: ffff92f890566ea0 RCX: 00000000fd3c255c RDX: 0000000000000001 RSI: ffff92fb88a13d90 RDI: ffff92fb8ee127e8 RBP: ffff92f8b7fb7c58 R08: 0000000000000001 R09: ffff92fb88a13d58 R10: 000000005a6a9373 R11: 0000000000000001 R12: 00000000fffffffb R13: ffff92fb8ee12000 R14: 00000000000034ca R15: ffff92fb8ee12620 FS: 00007f1fefd8e880(0000) GS:ffff92fb95600000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fc211d34cdb CR3: 000000012d43a000 CR4: 00000000001406e0 Stack: ffff92f890566ea0 ffff92f890567078 ffffffffc0b5a0c0 ffff92f890566f28 ffff92fb888b2000 ffff92f8b7fb7c80 ffffffffbc27ff55 ffff92f890566ea0 ffff92fb8bf10000 ffffffffc0b5a0c0 ffff92f8b7fb7cb0 ffffffffbc28090d Call Trace: [] evict+0xc5/0x1a0 [] iput+0x1ad/0x2c0 [] recover_orphan_inodes+0x10c/0x2e0 [f2fs] [] f2fs_fill_super+0x884/0x1150 [f2fs] [] mount_bdev+0x18c/0x1c0 [] ? f2fs_commit_super+0x100/0x100 [f2fs] [] f2fs_mount+0x15/0x20 [f2fs] [] mount_fs+0x39/0x170 [] vfs_kern_mount+0x6b/0x160 [] do_mount+0x1cf/0xd00 [] ? copy_mount_options+0xac/0x170 [] SyS_mount+0x83/0xd0 [] entry_SYSCALL_64_fastpath+0x23/0xc1 Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 27 +++++++++++++++++---------- fs/f2fs/super.c | 1 + 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 9accdd1179c3..db032bdb6bb5 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -531,6 +531,17 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) { struct inode *inode; struct node_info ni; + int err = acquire_orphan_inode(sbi); + + if (err) { + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_msg(sbi->sb, KERN_WARNING, + "%s: orphan failed (ino=%x), run fsck to fix.", + __func__, ino); + return err; + } + + __add_ino_entry(sbi, ino, ORPHAN_INO); inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) { @@ -551,17 +562,13 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) /* ENOMEM was fully retried in f2fs_evict_inode. */ if (ni.blk_addr != NULL_ADDR) { - int err = acquire_orphan_inode(sbi); - - if (err) { - set_sbi_flag(sbi, SBI_NEED_FSCK); - f2fs_msg(sbi->sb, KERN_WARNING, - "%s: orphan failed (ino=%x), run fsck to fix.", - __func__, ino); - return err; - } - __add_ino_entry(sbi, ino, ORPHAN_INO); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_msg(sbi->sb, KERN_WARNING, + "%s: orphan failed (ino=%x), run fsck to fix.", + __func__, ino); + return -EIO; } + __remove_ino_entry(sbi, ino, ORPHAN_INO); return 0; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1efcbc98c4b1..4b0d87f13fd4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1880,6 +1880,7 @@ free_root_inode: sb->s_root = NULL; free_node_inode: mutex_lock(&sbi->umount_mutex); + release_ino_entry(sbi, true); f2fs_leave_shrinker(sbi); iput(sbi->node_inode); mutex_unlock(&sbi->umount_mutex); -- GitLab From 0b15e3c718ad59d365c787cdd49f208cb2fe374f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 22 Sep 2016 13:31:48 -0700 Subject: [PATCH 0372/5498] f2fs: do not unnecessarily null-terminate encrypted symlink data Null-terminating the fscrypt_symlink_data on read is unnecessary because it is not string data --- it contains binary ciphertext. Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/namei.c --- fs/f2fs/namei.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 748d7b5c9ce9..64e050df92db 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -1027,7 +1027,6 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, struct fscrypt_str pstr = FSTR_INIT(NULL, 0); struct fscrypt_symlink_data *sd; struct inode *inode = d_inode(dentry); - loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); u32 max_size = inode->i_sb->s_blocksize; int res; @@ -1037,9 +1036,8 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, cpage = read_mapping_page(inode->i_mapping, 0, NULL); if (IS_ERR(cpage)) - return cpage; + return ERR_CAST(cpage); caddr = kmap(cpage); - caddr[size] = 0; /* Symlink is encrypted */ sd = (struct fscrypt_symlink_data *)caddr; -- GitLab From f23703646f7a99243da280483235689aad80bca1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 23 Sep 2016 11:29:00 -0700 Subject: [PATCH 0373/5498] f2fs: remove dirty inode pages in error path When getting EIO while handling orphan inodes, we can get some dirty node pages. Then, f2fs_write_node_pages() called by iput(node_inode) will try to flush node pages. But in this case, we should prevent to do that, since we will try again from the start. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 4b0d87f13fd4..927f202c2212 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1879,6 +1879,7 @@ free_root_inode: dput(sb->s_root); sb->s_root = NULL; free_node_inode: + truncate_inode_pages_final(NODE_MAPPING(sbi)); mutex_lock(&sbi->umount_mutex); release_ino_entry(sbi, true); f2fs_leave_shrinker(sbi); -- GitLab From 9a5f83d9f1d901a5054061abcf168be0acf0920e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 23 Sep 2016 13:24:56 +0800 Subject: [PATCH 0374/5498] f2fs: adjust display format of segment bit Just adjust segment bit info printed in procfs. Before: 1008 5|0 |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 3|183|0 0 61 20 20 0 0 21 80 c0 2 e4 e 54 0 21 21 17 a 44 d0 28 e4 50 40 30 8 0 2d 32 0 5 b0 80 1 43 2 8e f8 7b 2 25 93 bf e0 73 8e 9a 19 44 60 ff e4 cc e6 8e bf f9 ff 5 3d 31 3d 13 1010 3|1 |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 After: 1008 5|0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1009 4|434| ff 7d ff bf d9 3f ff e7 ff bf d7 bf ff bb be ff fb df f7 fb fa bf fb fe bb df dd ff fe ef ff fe ef e2 27 bf ab bf fb df fd bd bf fb db fc ff ff 3f ff ff bf ff 5f db 3f fb fb bf fb bf 4f ff ef 1010 4|422| ff bb fe ff ef d7 ee ff ff fc bf ef 7d eb ec fd fb 3f 97 7f ef ff af ff db ff ff 69 bf ff f6 e7 ff fb f7 7b fb df be ff ff ef f3 fe ff ff df fe f7 fa ff b7 77 be fe fb a9 7f 87 a2 ac c7 ff 75 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 927f202c2212..48921cdb51b6 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -941,7 +941,7 @@ static int segment_bits_seq_show(struct seq_file *seq, void *offset) seq_printf(seq, "%d|%-3u|", se->type, get_valid_blocks(sbi, i, 1)); for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) - seq_printf(seq, "%x ", se->cur_valid_map[j]); + seq_printf(seq, " %.2x", se->cur_valid_map[j]); seq_putc(seq, '\n'); } return 0; -- GitLab From 0bcf5f5981c49fa3b7bb68c0f0a8d1c78bebca82 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 23 Sep 2016 21:30:09 +0800 Subject: [PATCH 0375/5498] f2fs: support configuring fault injection per superblock Previously, we only support global fault injection configuration, so that when we configure type/rate of fault injection through sysfs, mount option, it will influence all f2fs partition which is being used. It is not make sence, since it will be not convenient if developer want to test separated partitions with different fault injection rate/type simultaneously, also it's not possible to enable fault injection in one partition and disable fault injection in other one. >From now on, we move global configuration of fault injection in module into per-superblock, hence injection testing can be more flexible. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 12 ++++--- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 2 +- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 74 +++++++++++++++++++++----------------------- fs/f2fs/gc.c | 2 +- fs/f2fs/inline.c | 4 +-- fs/f2fs/inode.c | 2 +- fs/f2fs/node.c | 2 +- fs/f2fs/super.c | 57 +++++++++------------------------- 10 files changed, 65 insertions(+), 94 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 4dcc9e28dc5c..1e29630f49c1 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -109,14 +109,16 @@ fail: return ERR_PTR(-EINVAL); } -static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) +static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi, + const struct posix_acl *acl, size_t *size) { struct f2fs_acl_header *f2fs_acl; struct f2fs_acl_entry *entry; int i; - f2fs_acl = f2fs_kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * - sizeof(struct f2fs_acl_entry), GFP_NOFS); + f2fs_acl = f2fs_kmalloc(sbi, sizeof(struct f2fs_acl_header) + + acl->a_count * sizeof(struct f2fs_acl_entry), + GFP_NOFS); if (!f2fs_acl) return ERR_PTR(-ENOMEM); @@ -175,7 +177,7 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type, retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage); if (retval > 0) { - value = f2fs_kmalloc(retval, GFP_F2FS_ZERO); + value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO); if (!value) return ERR_PTR(-ENOMEM); retval = f2fs_getxattr(inode, name_index, "", value, @@ -230,7 +232,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, } if (acl) { - value = f2fs_acl_to_disk(acl, &size); + value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size); if (IS_ERR(value)) { clear_inode_flag(inode, FI_ACL_MODE); return (int)PTR_ERR(value); diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index db032bdb6bb5..23c87523da45 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -490,7 +490,7 @@ int acquire_orphan_inode(struct f2fs_sb_info *sbi) spin_lock(&im->ino_lock); #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_ORPHAN)) { + if (time_to_inject(sbi, FAULT_ORPHAN)) { spin_unlock(&im->ino_lock); return -ENOSPC; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5852b633375c..631011132928 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -36,7 +36,7 @@ static void f2fs_read_end_io(struct bio *bio, int err) int i; #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_IO)) + if (time_to_inject(F2FS_P_SB(bio->bi_io_vec->bv_page), FAULT_IO)) err = -EIO; #endif diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 699b043de007..b200a4d2c5b0 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -545,7 +545,7 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, start: #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_DIR_DEPTH)) + if (time_to_inject(F2FS_I_SB(dir), FAULT_DIR_DEPTH)) return -ENOSPC; #endif if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 22f146da1569..4cd398e9cf39 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -57,42 +57,8 @@ struct f2fs_fault_info { unsigned int inject_type; }; -extern struct f2fs_fault_info f2fs_fault; extern char *fault_name[FAULT_MAX]; -#define IS_FAULT_SET(type) (f2fs_fault.inject_type & (1 << (type))) - -static inline bool time_to_inject(int type) -{ - if (!f2fs_fault.inject_rate) - return false; - if (type == FAULT_KMALLOC && !IS_FAULT_SET(type)) - return false; - else if (type == FAULT_PAGE_ALLOC && !IS_FAULT_SET(type)) - return false; - else if (type == FAULT_ALLOC_NID && !IS_FAULT_SET(type)) - return false; - else if (type == FAULT_ORPHAN && !IS_FAULT_SET(type)) - return false; - else if (type == FAULT_BLOCK && !IS_FAULT_SET(type)) - return false; - else if (type == FAULT_DIR_DEPTH && !IS_FAULT_SET(type)) - return false; - else if (type == FAULT_EVICT_INODE && !IS_FAULT_SET(type)) - return false; - else if (type == FAULT_IO && !IS_FAULT_SET(type)) - return false; - - atomic_inc(&f2fs_fault.inject_ops); - if (atomic_read(&f2fs_fault.inject_ops) >= f2fs_fault.inject_rate) { - atomic_set(&f2fs_fault.inject_ops, 0); - printk("%sF2FS-fs : inject %s in %pF\n", - KERN_INFO, - fault_name[type], - __builtin_return_address(0)); - return true; - } - return false; -} +#define IS_FAULT_SET(fi, type) (fi->inject_type & (1 << (type))) #endif /* @@ -943,8 +909,37 @@ struct f2fs_sb_info { /* Reference to checksum algorithm driver via cryptoapi */ struct crypto_shash *s_chksum_driver; + + /* For fault injection */ +#ifdef CONFIG_F2FS_FAULT_INJECTION + struct f2fs_fault_info fault_info; +#endif }; +#ifdef CONFIG_F2FS_FAULT_INJECTION +static inline bool time_to_inject(struct f2fs_sb_info *sbi, int type) +{ + struct f2fs_fault_info *ffi = &sbi->fault_info; + + if (!ffi->inject_rate) + return false; + + if (!IS_FAULT_SET(ffi, type)) + return false; + + atomic_inc(&ffi->inject_ops); + if (atomic_read(&ffi->inject_ops) >= ffi->inject_rate) { + atomic_set(&ffi->inject_ops, 0); + printk("%sF2FS-fs : inject %s in %pF\n", + KERN_INFO, + fault_name[type], + __builtin_return_address(0)); + return true; + } + return false; +} +#endif + /* For write statistics. Suppose sector size is 512 bytes, * and the return value is in kbytes. s is of struct f2fs_sb_info. */ @@ -1233,7 +1228,7 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, blkcnt_t diff; #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_BLOCK)) + if (time_to_inject(sbi, FAULT_BLOCK)) return false; #endif /* @@ -1467,7 +1462,7 @@ static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, if (page) return page; - if (time_to_inject(FAULT_PAGE_ALLOC)) + if (time_to_inject(F2FS_M_SB(mapping), FAULT_PAGE_ALLOC)) return NULL; #endif if (!for_write) @@ -1920,10 +1915,11 @@ static inline bool f2fs_may_extent_tree(struct inode *inode) return S_ISREG(mode); } -static inline void *f2fs_kmalloc(size_t size, gfp_t flags) +static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi, + size_t size, gfp_t flags) { #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_KMALLOC)) + if (time_to_inject(sbi, FAULT_KMALLOC)) return NULL; #endif return kmalloc(size, flags); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8b7021fc661a..bc77eab49a15 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -96,7 +96,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) dev_t dev = sbi->sb->s_bdev->bd_dev; int err = 0; - gc_th = f2fs_kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); + gc_th = f2fs_kmalloc(sbi, sizeof(struct f2fs_gc_kthread), GFP_KERNEL); if (!gc_th) { err = -ENOMEM; goto out; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index f7f61abe1a79..a04c1016d511 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -444,8 +444,8 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, struct f2fs_inline_dentry *backup_dentry; int err; - backup_dentry = f2fs_kmalloc(sizeof(struct f2fs_inline_dentry), - GFP_F2FS_ZERO); + backup_dentry = f2fs_kmalloc(F2FS_I_SB(dir), + sizeof(struct f2fs_inline_dentry), GFP_F2FS_ZERO); if (!backup_dentry) { f2fs_put_page(ipage, 1); return -ENOMEM; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 739341e83ace..6d799f5b78bb 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -369,7 +369,7 @@ void f2fs_evict_inode(struct inode *inode) goto no_delete; #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_EVICT_INODE)) + if (time_to_inject(sbi, FAULT_EVICT_INODE)) goto no_delete; #endif diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d8f3424776ab..3c8b2243dba9 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1840,7 +1840,7 @@ bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid) struct free_nid *i = NULL; retry: #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(FAULT_ALLOC_NID)) + if (time_to_inject(sbi, FAULT_ALLOC_NID)) return false; #endif if (unlikely(sbi->total_valid_node_count + 1 > nm_i->available_nids)) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 48921cdb51b6..335f8b42a9d4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -40,7 +40,6 @@ static struct kmem_cache *f2fs_inode_cachep; static struct kset *f2fs_kset; #ifdef CONFIG_F2FS_FAULT_INJECTION -struct f2fs_fault_info f2fs_fault; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", @@ -53,14 +52,17 @@ char *fault_name[FAULT_MAX] = { [FAULT_IO] = "IO error", }; -static void f2fs_build_fault_attr(unsigned int rate) +static void f2fs_build_fault_attr(struct f2fs_sb_info *sbi, + unsigned int rate) { + struct f2fs_fault_info *ffi = &sbi->fault_info; + if (rate) { - atomic_set(&f2fs_fault.inject_ops, 0); - f2fs_fault.inject_rate = rate; - f2fs_fault.inject_type = (1 << FAULT_MAX) - 1; + atomic_set(&ffi->inject_ops, 0); + ffi->inject_rate = rate; + ffi->inject_type = (1 << FAULT_MAX) - 1; } else { - memset(&f2fs_fault, 0, sizeof(struct f2fs_fault_info)); + memset(ffi, 0, sizeof(struct f2fs_fault_info)); } } #endif @@ -166,7 +168,7 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) #ifdef CONFIG_F2FS_FAULT_INJECTION else if (struct_type == FAULT_INFO_RATE || struct_type == FAULT_INFO_TYPE) - return (unsigned char *)&f2fs_fault; + return (unsigned char *)&sbi->fault_info; #endif return NULL; } @@ -311,6 +313,10 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(dirty_nats_ratio), ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), +#ifdef CONFIG_F2FS_FAULT_INJECTION + ATTR_LIST(inject_rate), + ATTR_LIST(inject_type), +#endif ATTR_LIST(lifetime_write_kbytes), NULL, }; @@ -326,22 +332,6 @@ static struct kobj_type f2fs_ktype = { .release = f2fs_sb_release, }; -#ifdef CONFIG_F2FS_FAULT_INJECTION -/* sysfs for f2fs fault injection */ -static struct kobject f2fs_fault_inject; - -static struct attribute *f2fs_fault_attrs[] = { - ATTR_LIST(inject_rate), - ATTR_LIST(inject_type), - NULL -}; - -static struct kobj_type f2fs_fault_ktype = { - .default_attrs = f2fs_fault_attrs, - .sysfs_ops = &f2fs_attr_ops, -}; -#endif - void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) { struct va_format vaf; @@ -370,7 +360,7 @@ static int parse_options(struct super_block *sb, char *options) int arg = 0; #ifdef CONFIG_F2FS_FAULT_INJECTION - f2fs_build_fault_attr(0); + f2fs_build_fault_attr(sbi, 0); #endif if (!options) @@ -535,7 +525,7 @@ static int parse_options(struct super_block *sb, char *options) if (args->from && match_int(args, &arg)) return -EINVAL; #ifdef CONFIG_F2FS_FAULT_INJECTION - f2fs_build_fault_attr(arg); + f2fs_build_fault_attr(sbi, arg); #else f2fs_msg(sb, KERN_INFO, "FAULT_INJECTION was not selected"); @@ -1979,16 +1969,6 @@ static int __init init_f2fs_fs(void) err = -ENOMEM; goto free_extent_cache; } -#ifdef CONFIG_F2FS_FAULT_INJECTION - f2fs_fault_inject.kset = f2fs_kset; - f2fs_build_fault_attr(0); - err = kobject_init_and_add(&f2fs_fault_inject, &f2fs_fault_ktype, - NULL, "fault_injection"); - if (err) { - f2fs_fault_inject.kset = NULL; - goto free_kset; - } -#endif err = register_shrinker(&f2fs_shrinker_info); if (err) goto free_kset; @@ -2007,10 +1987,6 @@ free_filesystem: free_shrinker: unregister_shrinker(&f2fs_shrinker_info); free_kset: -#ifdef CONFIG_F2FS_FAULT_INJECTION - if (f2fs_fault_inject.kset) - kobject_put(&f2fs_fault_inject); -#endif kset_unregister(f2fs_kset); free_extent_cache: destroy_extent_cache(); @@ -2032,9 +2008,6 @@ static void __exit exit_f2fs_fs(void) f2fs_destroy_root_stats(); unregister_filesystem(&f2fs_fs_type); unregister_shrinker(&f2fs_shrinker_info); -#ifdef CONFIG_F2FS_FAULT_INJECTION - kobject_put(&f2fs_fault_inject); -#endif kset_unregister(f2fs_kset); destroy_extent_cache(); destroy_checkpoint_caches(); -- GitLab From ba122fbea98f13ce10bdd6574a77ba80e82171be Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Sat, 24 Sep 2016 12:29:18 +0800 Subject: [PATCH 0376/5498] f2fs: remove redundant value definition This patch remove redundant value definition in build_sit_entries Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3f8f8eb8aa79..0172804f6523 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2227,6 +2227,8 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct f2fs_journal *journal = curseg->journal; + struct seg_entry *se; + struct f2fs_sit_entry sit; int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, start, end; unsigned int readed, start_blk = 0; @@ -2239,11 +2241,10 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) end = (start_blk + readed) * sit_i->sents_per_block; for (; start < end && start < MAIN_SEGS(sbi); start++) { - struct seg_entry *se = &sit_i->sentries[start]; struct f2fs_sit_block *sit_blk; - struct f2fs_sit_entry sit; struct page *page; + se = &sit_i->sentries[start]; page = get_current_sit_page(sbi, start); sit_blk = (struct f2fs_sit_block *)page_address(page); sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; @@ -2269,8 +2270,6 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) down_read(&curseg->journal_rwsem); for (i = 0; i < sits_in_cursum(journal); i++) { - struct f2fs_sit_entry sit; - struct seg_entry *se; unsigned int old_valid_blocks; start = le32_to_cpu(segno_in_journal(journal, i)); -- GitLab From 54358f7fa76829ed344a9866f3ec92d05db3387a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 26 Sep 2016 19:45:05 +0800 Subject: [PATCH 0377/5498] f2fs: do fault injection initialization in default_options Do fault injection initialization in default_options to keep consistent with other default option configurating. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 335f8b42a9d4..1f98cce74e9e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -359,10 +359,6 @@ static int parse_options(struct super_block *sb, char *options) char *p, *name; int arg = 0; -#ifdef CONFIG_F2FS_FAULT_INJECTION - f2fs_build_fault_attr(sbi, 0); -#endif - if (!options) return 0; @@ -977,6 +973,10 @@ static void default_options(struct f2fs_sb_info *sbi) #ifdef CONFIG_F2FS_FS_POSIX_ACL set_opt(sbi, POSIX_ACL); #endif + +#ifdef CONFIG_F2FS_FAULT_INJECTION + f2fs_build_fault_attr(sbi, 0); +#endif } static int f2fs_remount(struct super_block *sb, int *flags, char *data) -- GitLab From ec488fe8bb0a1c489fd25abbbf4516ee94ba3e2d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 26 Sep 2016 19:45:06 +0800 Subject: [PATCH 0378/5498] f2fs: fix to recover old fault injection config in ->remount_fs In ->remount_fs, we didn't recover original fault injection config if we encounter error, fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1f98cce74e9e..72c68ef66cbe 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -987,6 +987,9 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) bool need_restart_gc = false; bool need_stop_gc = false; bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE); +#ifdef CONFIG_F2FS_FAULT_INJECTION + struct f2fs_fault_info ffi = sbi->fault_info; +#endif /* * Save the old mount options in case we @@ -1082,6 +1085,9 @@ restore_gc: restore_opts: sbi->mount_opt = org_mount_opt; sbi->active_logs = active_logs; +#ifdef CONFIG_F2FS_FAULT_INJECTION + sbi->fault_info = ffi; +#endif return err; } -- GitLab From 6f464a544b6ef0243b821d88e2ca23b66a29a967 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 26 Sep 2016 19:45:55 +0800 Subject: [PATCH 0379/5498] f2fs: support checkpoint error injection This patch adds to support checkpoint error injection in f2fs for testing fatal error tolerance, it will be useful that it can simulate abnormal power off by f2fs itself instead of calling godown ioctl by running apps. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/gc.c | 5 +++++ fs/f2fs/segment.c | 5 +++++ fs/f2fs/super.c | 1 + 4 files changed, 12 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4cd398e9cf39..dcbaf177a94f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -48,6 +48,7 @@ enum { FAULT_DIR_DEPTH, FAULT_EVICT_INODE, FAULT_IO, + FAULT_CHECKPOINT, FAULT_MAX, }; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index bc77eab49a15..d6b6c264f3d8 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -47,6 +47,11 @@ static int gc_thread_func(void *data) continue; } +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_CHECKPOINT)) + f2fs_stop_checkpoint(sbi, false); +#endif + /* * [GC triggering condition] * 0. GC is not conducted currently. diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0172804f6523..b3c61ae37f92 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -343,6 +343,11 @@ int commit_inmem_pages(struct inode *inode) */ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) { +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_CHECKPOINT)) + f2fs_stop_checkpoint(sbi, false); +#endif + if (!need) return; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 72c68ef66cbe..548ee5ab5091 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -50,6 +50,7 @@ char *fault_name[FAULT_MAX] = { [FAULT_DIR_DEPTH] = "too big dir depth", [FAULT_EVICT_INODE] = "evict_inode fail", [FAULT_IO] = "IO error", + [FAULT_CHECKPOINT] = "checkpoint error", }; static void f2fs_build_fault_attr(struct f2fs_sb_info *sbi, -- GitLab From 963d55e25d66c0b0b4302433c00dd6fc1e8231ff Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 27 Sep 2016 00:09:53 +0800 Subject: [PATCH 0380/5498] f2fs: remove redundant io plug Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 23c87523da45..efc07751b014 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -266,7 +266,6 @@ static int f2fs_write_meta_pages(struct address_space *mapping, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); - struct blk_plug plug; long diff, written; /* collect a number of dirty meta pages and write together */ @@ -279,9 +278,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping, /* if mounting is failed, skip writing node pages */ mutex_lock(&sbi->cp_mutex); diff = nr_pages_to_write(sbi, META, wbc); - blk_start_plug(&plug); written = sync_meta_pages(sbi, META, wbc->nr_to_write); - blk_finish_plug(&plug); mutex_unlock(&sbi->cp_mutex); wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff); return 0; -- GitLab From 71863d04d646c535f3406d1a9f3c62518b143a90 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Thu, 29 Sep 2016 18:37:31 +0800 Subject: [PATCH 0381/5498] f2fs: remove dead variable Signed-off-by: Sheng Yong Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index d6b6c264f3d8..0a0a1ad1fe1f 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -275,7 +275,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, { struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct victim_sel_policy p; - unsigned int secno, max_cost, last_victim; + unsigned int secno, last_victim; unsigned int last_segment = MAIN_SEGS(sbi); unsigned int nsearched = 0; @@ -285,7 +285,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, select_policy(sbi, gc_type, type, &p); p.min_segno = NULL_SEGNO; - p.min_cost = max_cost = get_max_cost(sbi, &p); + p.min_cost = get_max_cost(sbi, &p); if (p.max_search == 0) goto out; -- GitLab From 3574ef585e86ac2adebcae5f3d2caed48f4bbcb6 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Fri, 30 Sep 2016 08:24:53 +0800 Subject: [PATCH 0382/5498] f2fs: introduce get_checkpoint_version for cleanup There exists almost same codes when get the value of pre_version and cur_version in function validate_checkpoint, this patch adds get_checkpoint_version to clean up redundant codes. Signed-off-by: Tiezhu Yang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 66 +++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index efc07751b014..d9ce020e0f86 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -659,45 +659,55 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) } } -static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, - block_t cp_addr, unsigned long long *version) +static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr, + struct f2fs_checkpoint **cp_block, struct page **cp_page, + unsigned long long *version) { - struct page *cp_page_1, *cp_page_2 = NULL; unsigned long blk_size = sbi->blocksize; - struct f2fs_checkpoint *cp_block; - unsigned long long cur_version = 0, pre_version = 0; - size_t crc_offset; + size_t crc_offset = 0; __u32 crc = 0; - /* Read the 1st cp block in this CP pack */ - cp_page_1 = get_meta_page(sbi, cp_addr); + *cp_page = get_meta_page(sbi, cp_addr); + *cp_block = (struct f2fs_checkpoint *)page_address(*cp_page); - /* get the version number */ - cp_block = (struct f2fs_checkpoint *)page_address(cp_page_1); - crc_offset = le32_to_cpu(cp_block->checksum_offset); - if (crc_offset >= blk_size) - goto invalid_cp1; + crc_offset = le32_to_cpu((*cp_block)->checksum_offset); + if (crc_offset >= blk_size) { + f2fs_msg(sbi->sb, KERN_WARNING, + "invalid crc_offset: %zu", crc_offset); + return -EINVAL; + } - crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); - if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset)) - goto invalid_cp1; + crc = le32_to_cpu(*((__le32 *)((unsigned char *)*cp_block + + crc_offset))); + if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) { + f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value"); + return -EINVAL; + } - pre_version = cur_cp_version(cp_block); + *version = cur_cp_version(*cp_block); + return 0; +} - /* Read the 2nd cp block in this CP pack */ - cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1; - cp_page_2 = get_meta_page(sbi, cp_addr); +static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, + block_t cp_addr, unsigned long long *version) +{ + struct page *cp_page_1 = NULL, *cp_page_2 = NULL; + struct f2fs_checkpoint *cp_block = NULL; + unsigned long long cur_version = 0, pre_version = 0; + int err; - cp_block = (struct f2fs_checkpoint *)page_address(cp_page_2); - crc_offset = le32_to_cpu(cp_block->checksum_offset); - if (crc_offset >= blk_size) - goto invalid_cp2; + err = get_checkpoint_version(sbi, cp_addr, &cp_block, + &cp_page_1, version); + if (err) + goto invalid_cp1; + pre_version = *version; - crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); - if (!f2fs_crc_valid(sbi, crc, cp_block, crc_offset)) + cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1; + err = get_checkpoint_version(sbi, cp_addr, &cp_block, + &cp_page_2, version); + if (err) goto invalid_cp2; - - cur_version = cur_cp_version(cp_block); + cur_version = *version; if (cur_version == pre_version) { *version = cur_version; -- GitLab From d2f3c60fced2945ea8b06deb98dc788f6adac52f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 29 Sep 2016 18:50:10 +0800 Subject: [PATCH 0383/5498] f2fs: fix to commit bio cache after flushing node pages In sync_node_pages, we won't check and commit last merged pages in private bio cache of f2fs, as these pages were taged as writeback, someone who is waiting for writebacking of the page will be blocked until the cache was committed by someone else. We need to commit node type bio cache to avoid potential deadlock or long delay of waiting writeback. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 3c8b2243dba9..5f2180625281 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1317,6 +1317,7 @@ int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct page *last_page = NULL; bool marked = false; nid_t ino = inode->i_ino; + int nwritten = 0; if (atomic) { last_page = last_fsync_dnode(sbi, ino); @@ -1390,7 +1391,10 @@ continue_unlock: unlock_page(page); f2fs_put_page(last_page, 0); break; + } else { + nwritten++; } + if (page == last_page) { f2fs_put_page(page, 0); marked = true; @@ -1412,6 +1416,9 @@ continue_unlock: unlock_page(last_page); goto retry; } + + if (nwritten) + f2fs_submit_merged_bio_cond(sbi, NULL, NULL, ino, NODE, WRITE); return ret ? -EIO: 0; } @@ -1421,6 +1428,7 @@ int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc) struct pagevec pvec; int step = 0; int nwritten = 0; + int ret = 0; pagevec_init(&pvec, 0); @@ -1441,7 +1449,8 @@ next_step: if (unlikely(f2fs_cp_error(sbi))) { pagevec_release(&pvec); - return -EIO; + ret = -EIO; + goto out; } /* @@ -1492,6 +1501,8 @@ continue_unlock: if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) unlock_page(page); + else + nwritten++; if (--wbc->nr_to_write == 0) break; @@ -1509,7 +1520,10 @@ continue_unlock: step++; goto next_step; } - return nwritten; +out: + if (nwritten) + f2fs_submit_merged_bio(sbi, NODE, WRITE); + return ret; } int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino) -- GitLab From 176efe65680ee6e2929219f75a79e5519a2f5284 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 29 Sep 2016 18:50:11 +0800 Subject: [PATCH 0384/5498] f2fs: don't submit irrelevant page While we call ->writepages, there are two cases: a. we didn't writeout any dirty pages, since they are writebacked by other thread concurrently. b. we writeout dirty pages, and have already submitted bio to block layer. In these cases, we don't need to do additional bio flushing unnecessarily, it may split bio in cache into smaller one. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 631011132928..3abfb6720f94 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1347,6 +1347,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping, int cycled; int range_whole = 0; int tag; + int nwritten = 0; pagevec_init(&pvec, 0); @@ -1421,6 +1422,8 @@ continue_unlock: done_index = page->index + 1; done = 1; break; + } else { + nwritten++; } if (--wbc->nr_to_write <= 0 && @@ -1442,6 +1445,10 @@ continue_unlock: if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = done_index; + if (nwritten) + f2fs_submit_merged_bio_cond(F2FS_M_SB(mapping), mapping->host, + NULL, 0, DATA, WRITE); + return ret; } @@ -1483,7 +1490,6 @@ static int f2fs_write_data_pages(struct address_space *mapping, * if some pages were truncated, we cannot guarantee its mapping->host * to detect pending bios. */ - f2fs_submit_merged_bio(sbi, DATA, WRITE); remove_dirty_inode(inode); return ret; -- GitLab From 90e0744d6d4075e3be29f69d807c6700ebdeb2e0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 30 Sep 2016 17:37:43 -0700 Subject: [PATCH 0385/5498] f2fs: introduce update_ckpt_flags to clean up This patch add update_ckpt_flags() to clean up the flow. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 56 ++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index d9ce020e0f86..3083a4c02519 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1002,6 +1002,37 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi) finish_wait(&sbi->cp_wait, &wait); } +static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{ + unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + + spin_lock(&sbi->cp_lock); + + if (cpc->reason == CP_UMOUNT) + __set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + else + __clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + + if (cpc->reason == CP_FASTBOOT) + __set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + else + __clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + + if (orphan_num) + __set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); + else + __clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); + + if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) + __set_ckpt_flags(ckpt, CP_FSCK_FLAG); + + /* set this flag to activate crc|cp_ver for recovery */ + __set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); + + spin_unlock(&sbi->cp_lock); +} + static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); @@ -1076,29 +1107,8 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) cp_payload_blks + data_sum_blocks + orphan_blocks); - spin_lock(&sbi->cp_lock); - if (cpc->reason == CP_UMOUNT) - __set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); - else - __clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); - - if (cpc->reason == CP_FASTBOOT) - __set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); - else - __clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); - - if (orphan_num) - __set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); - else - __clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); - - if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) - __set_ckpt_flags(ckpt, CP_FSCK_FLAG); - - /* set this flag to activate crc|cp_ver for recovery */ - __set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); - - spin_unlock(&sbi->cp_lock); + /* update ckpt flag for checkpoint */ + update_ckpt_flags(sbi, cpc); /* update SIT/NAT bitmap */ get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); -- GitLab From a81a601e7faeb5955d4952e15299429ecffc578c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 13 Oct 2016 09:32:27 -0700 Subject: [PATCH 0386/5498] f2fs: remove existing kfree_put_link This patch removes existing kfree_put_link. Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 64e050df92db..a8f9368b38df 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -1085,14 +1085,6 @@ errout: return ERR_PTR(res); } -void kfree_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - kfree(s); -} - const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = f2fs_encrypted_follow_link, -- GitLab From dee82ee2261173324a18e67465885fea9e254385 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 12 Oct 2016 13:38:41 -0700 Subject: [PATCH 0387/5498] f2fs: fix wrong sum_page pointer in f2fs_gc This patch fixes using a wrong pointer for sum_page in f2fs_gc. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 0a0a1ad1fe1f..4336807cc690 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -848,16 +848,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, for (segno = start_segno; segno < end_segno; segno++) { - if (get_valid_blocks(sbi, segno, 1) == 0 || - unlikely(f2fs_cp_error(sbi))) - goto next; - /* find segment summary of victim */ sum_page = find_get_page(META_MAPPING(sbi), GET_SUM_BLOCK(sbi, segno)); - f2fs_bug_on(sbi, !PageUptodate(sum_page)); f2fs_put_page(sum_page, 0); + if (get_valid_blocks(sbi, segno, 1) == 0 || + !PageUptodate(sum_page) || + unlikely(f2fs_cp_error(sbi))) + goto next; + sum = page_address(sum_page); f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer))); -- GitLab From e0cc5b4e5178333124cbb02b9ae413df1c9f5dc8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Nov 2016 14:06:03 -0800 Subject: [PATCH 0388/5498] posix_acl: Clear SGID bit when setting file permissions Cherry-pick to f2fs only for generic/375 from: (073931017: posix_acl: Clear SGID bit when setting file permissions) Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 6 ++---- fs/posix_acl.c | 31 +++++++++++++++++++++++++++++++ include/linux/posix_acl.h | 1 + 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 1e29630f49c1..6fe23af509e1 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -212,12 +212,10 @@ static int __f2fs_set_acl(struct inode *inode, int type, case ACL_TYPE_ACCESS: name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { - error = posix_acl_equiv_mode(acl, &inode->i_mode); - if (error < 0) + error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (error) return error; set_acl_inode(inode, inode->i_mode); - if (error == 0) - acl = NULL; } break; diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 0855f772cd41..73aad912314e 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -594,6 +594,37 @@ no_acl: } EXPORT_SYMBOL_GPL(posix_acl_create); +/** + * posix_acl_update_mode - update mode in set_acl + * + * Update the file mode when setting an ACL: compute the new file permission + * bits based on the ACL. In addition, if the ACL is equivalent to the new + * file mode, set *acl to NULL to indicate that no ACL should be set. + * + * As with chmod, clear the setgit bit if the caller is not in the owning group + * or capable of CAP_FSETID (see inode_change_ok). + * + * Called from set_acl inode operations. + */ +int posix_acl_update_mode(struct inode *inode, umode_t *mode_p, + struct posix_acl **acl) +{ + umode_t mode = inode->i_mode; + int error; + + error = posix_acl_equiv_mode(*acl, &mode); + if (error < 0) + return error; + if (error == 0) + *acl = NULL; + if (!in_group_p(inode->i_gid) && + !capable_wrt_inode_uidgid(inode, CAP_FSETID)) + mode &= ~S_ISGID; + *mode_p = mode; + return 0; +} +EXPORT_SYMBOL(posix_acl_update_mode); + /* * Fix up the uids and gids in posix acl extended attributes in place. */ diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h index 3e96a6a76103..d1a8ad7e5ae4 100644 --- a/include/linux/posix_acl.h +++ b/include/linux/posix_acl.h @@ -95,6 +95,7 @@ extern int set_posix_acl(struct inode *, int, struct posix_acl *); extern int posix_acl_chmod(struct inode *, umode_t); extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **, struct posix_acl **); +extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **); extern int simple_set_acl(struct inode *, struct posix_acl *, int); extern int simple_acl_create(struct inode *, struct inode *); -- GitLab From b92da4fc4cd79bc59daa71d4d8491c5332bd290f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 23 Nov 2016 10:51:17 -0800 Subject: [PATCH 0389/5498] f2fs: fix overflow due to condition check order In the last ilen case, i was already increased, resulting in accessing out- of-boundary entry of do_replace and blkaddr. Fix to check ilen first to exit the loop. Fixes: 2aa8fbb9693020 ("f2fs: refactor __exchange_data_block for speed up") Cc: stable@vger.kernel.org # 4.8+ Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index bbcfced32a02..541e61a14583 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -972,7 +972,7 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode, new_size = (dst + i) << PAGE_SHIFT; if (dst_inode->i_size < new_size) f2fs_i_size_write(dst_inode, new_size); - } while ((do_replace[i] || blkaddr[i] == NULL_ADDR) && --ilen); + } while (--ilen && (do_replace[i] || blkaddr[i] == NULL_ADDR)); f2fs_put_dnode(&dn); } else { -- GitLab From e91537a12cfd5fb955f352c053d8ac7034d7e326 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 24 Nov 2016 12:45:15 -0800 Subject: [PATCH 0390/5498] f2fs: fix to determine start_cp_addr by sbi->cur_cp_pack We don't guarantee cp_addr is fixed by cp_version. This is to sync with f2fs-tools. Cc: stable@vger.kernel.org Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 8 +++++++- fs/f2fs/f2fs.h | 26 ++++++++++++++++---------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 3083a4c02519..565a4bd8c586 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -768,6 +768,11 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) if (sanity_check_ckpt(sbi)) goto fail_no_cp; + if (cur_page == cp1) + sbi->cur_cp_pack = 1; + else + sbi->cur_cp_pack = 2; + if (cp_blks <= 1) goto done; @@ -1119,7 +1124,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) le32_to_cpu(ckpt->checksum_offset))) = cpu_to_le32(crc32); - start_blk = __start_cp_addr(sbi); + start_blk = __start_cp_next_addr(sbi); /* need to wait for end_io results */ wait_on_all_pages_writeback(sbi); @@ -1183,6 +1188,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) clear_prefree_segments(sbi, cpc); clear_sbi_flag(sbi, SBI_IS_DIRTY); clear_sbi_flag(sbi, SBI_NEED_CP); + __set_cp_next_pack(sbi); /* * redirty superblock if metadata like node page or inode cache is diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dcbaf177a94f..5f822e8596e0 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -802,6 +802,7 @@ struct f2fs_sb_info { /* for checkpoint */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ + int cur_cp_pack; /* remain current cp pack */ spinlock_t cp_lock; /* for flag in ckpt */ struct inode *meta_inode; /* cache meta blocks */ struct mutex cp_mutex; /* checkpoint procedure lock */ @@ -1367,22 +1368,27 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag) static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi) { - block_t start_addr; - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - unsigned long long ckpt_version = cur_cp_version(ckpt); - - start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr); + block_t start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr); - /* - * odd numbered checkpoint should at cp segment 0 - * and even segment must be at cp segment 1 - */ - if (!(ckpt_version & 1)) + if (sbi->cur_cp_pack == 2) start_addr += sbi->blocks_per_seg; + return start_addr; +} +static inline block_t __start_cp_next_addr(struct f2fs_sb_info *sbi) +{ + block_t start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr); + + if (sbi->cur_cp_pack == 1) + start_addr += sbi->blocks_per_seg; return start_addr; } +static inline void __set_cp_next_pack(struct f2fs_sb_info *sbi) +{ + sbi->cur_cp_pack = (sbi->cur_cp_pack == 1) ? 2 : 1; +} + static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) { return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); -- GitLab From 0d5ed69805ee57d7b416c4a6b47c3b67284db105 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 29 Nov 2016 10:36:21 -0800 Subject: [PATCH 0391/5498] f2fs: fix missing read/write function pointers Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 541e61a14583..eb788b208136 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2329,6 +2329,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) const struct file_operations f2fs_file_operations = { .llseek = f2fs_llseek, + .read = new_sync_read, + .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = f2fs_file_write_iter, .open = f2fs_file_open, -- GitLab From 09ce36ff2db8abee8ebf636f7c1650101e4f489b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:31:34 +0800 Subject: [PATCH 0392/5498] f2fs: exclude free nids building and allocation During nid allocation, it needs to exclude building and allocating flow of free nids, this is because while building free nid cache, there are two steps: a) load free nids from unused nat entries in NAT pages, b) update free nid cache by checking nat journal. The two steps should be atomical, otherwise an used nid can be allocated as free one after a) and before b). This patch adds missing lock which covers build_free_nids in unlock_operation and f2fs_balance_fs_bg to avoid that. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5f2180625281..0eeaeb8b4f4f 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1789,7 +1789,7 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, } } -void build_free_nids(struct f2fs_sb_info *sbi) +void __build_free_nids(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); @@ -1843,6 +1843,13 @@ void build_free_nids(struct f2fs_sb_info *sbi) nm_i->ra_nid_pages, META_NAT, false); } +void build_free_nids(struct f2fs_sb_info *sbi) +{ + mutex_lock(&NM_I(sbi)->build_lock); + __build_free_nids(sbi); + mutex_unlock(&NM_I(sbi)->build_lock); +} + /* * If this function returns success, caller can obtain a new nid * from second parameter of this function. @@ -1879,9 +1886,7 @@ retry: spin_unlock(&nm_i->free_nid_list_lock); /* Let's scan nat pages and its caches to get free nids */ - mutex_lock(&nm_i->build_lock); build_free_nids(sbi); - mutex_unlock(&nm_i->build_lock); goto retry; } -- GitLab From a18ac82b3c02fbdd3b95d98eb629a4627b30e093 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:57:00 +0800 Subject: [PATCH 0393/5498] f2fs: fix to release discard entries during checkpoint In f2fs_fill_super, if there is any IO error occurs during recovery, cached discard entries will be leaked, in order to avoid this, make write_checkpoint() handle memory release by itself, besides, move clear_prefree_segments to write_checkpoint for readability. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/checkpoint.c --- fs/f2fs/checkpoint.c | 5 ++++- fs/f2fs/super.c | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 565a4bd8c586..74f9bca6bd8a 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1185,7 +1185,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (unlikely(f2fs_cp_error(sbi))) return -EIO; - clear_prefree_segments(sbi, cpc); clear_sbi_flag(sbi, SBI_IS_DIRTY); clear_sbi_flag(sbi, SBI_NEED_CP); __set_cp_next_pack(sbi); @@ -1262,6 +1261,10 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* unlock all the fs_lock[] in do_checkpoint() */ err = do_checkpoint(sbi, cpc); + if (err) + release_discard_addrs(sbi); + else + clear_prefree_segments(sbi, cpc); unblock_operations(sbi); stat_inc_cp_count(sbi->stat_info); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 548ee5ab5091..62a6aa3fe147 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -725,7 +725,6 @@ static void f2fs_put_super(struct super_block *sb) * In addition, EIO will skip do checkpoint, we need this as well. */ release_ino_entry(sbi, true); - release_discard_addrs(sbi); f2fs_leave_shrinker(sbi); mutex_unlock(&sbi->umount_mutex); -- GitLab From bfb26ecc208d849fe7591414a56c58d35ec07625 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:57:01 +0800 Subject: [PATCH 0394/5498] f2fs: give a chance to detach from dirty list If there is no dirty pages in inode, we should give a chance to detach the inode from global dirty list, otherwise it needs to call another unnecessary .writepages for detaching. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 8 +++++--- fs/f2fs/dir.c | 1 + fs/f2fs/gc.c | 4 +++- fs/f2fs/inline.c | 4 +++- fs/f2fs/node.c | 1 + fs/f2fs/segment.c | 4 +++- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3abfb6720f94..16103b30fdec 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1776,12 +1776,14 @@ void f2fs_invalidate_page(struct page *page, unsigned int offset, return; if (PageDirty(page)) { - if (inode->i_ino == F2FS_META_INO(sbi)) + if (inode->i_ino == F2FS_META_INO(sbi)) { dec_page_count(sbi, F2FS_DIRTY_META); - else if (inode->i_ino == F2FS_NODE_INO(sbi)) + } else if (inode->i_ino == F2FS_NODE_INO(sbi)) { dec_page_count(sbi, F2FS_DIRTY_NODES); - else + } else { inode_dec_dirty_pages(inode); + remove_dirty_inode(inode); + } } /* This is atomic written page, keep Private */ diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index b200a4d2c5b0..ffdb2897accb 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -742,6 +742,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, ClearPagePrivate(page); ClearPageUptodate(page); inode_dec_dirty_pages(dir); + remove_dirty_inode(dir); } f2fs_put_page(page, 1); } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 4336807cc690..72a0ca08f901 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -670,8 +670,10 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type) retry: set_page_dirty(page); f2fs_wait_on_page_writeback(page, DATA, true); - if (clear_page_dirty_for_io(page)) + if (clear_page_dirty_for_io(page)) { inode_dec_dirty_pages(inode); + remove_dirty_inode(inode); + } set_cold_data(page); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index a04c1016d511..b21a0788f2cd 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -136,8 +136,10 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) fio.old_blkaddr = dn->data_blkaddr; write_data_page(dn, &fio); f2fs_wait_on_page_writeback(page, DATA, true); - if (dirty) + if (dirty) { inode_dec_dirty_pages(dn->inode); + remove_dirty_inode(dn->inode); + } /* this converted inline_data should be recovered. */ set_inode_flag(dn->inode, FI_APPEND_WRITE); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0eeaeb8b4f4f..5bbb7371698a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1207,6 +1207,7 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) ret = f2fs_write_inline_data(inode, page); inode_dec_dirty_pages(inode); + remove_dirty_inode(inode); if (ret) set_page_dirty(page); page_out: diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index b3c61ae37f92..75477ec6c535 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -272,8 +272,10 @@ static int __commit_inmem_pages(struct inode *inode, set_page_dirty(page); f2fs_wait_on_page_writeback(page, DATA, true); - if (clear_page_dirty_for_io(page)) + if (clear_page_dirty_for_io(page)) { inode_dec_dirty_pages(inode); + remove_dirty_inode(inode); + } fio.page = page; err = do_write_data_page(&fio); -- GitLab From 53999e39ab99cac75859076bc7c168c0c98fed6d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:57:02 +0800 Subject: [PATCH 0395/5498] f2fs: add missing f2fs_balance_fs in f2fs_zero_range f2fs_balance_fs should be called in between node page updating, otherwise node page count will exceeded far beyond watermark of triggering foreground garbage collection, result in facing high risk of hitting LFS allocation failure. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index eb788b208136..e673756bbc14 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1223,6 +1223,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, ret = f2fs_do_zero_range(&dn, index, end); f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); + + f2fs_balance_fs(sbi, dn.node_changed); + if (ret) goto out; -- GitLab From ee6a1b185efc5a9e97be55d0cc7c0af47623e3d3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:57:03 +0800 Subject: [PATCH 0396/5498] f2fs: don't miss any f2fs_balance_fs cases In f2fs_map_blocks, let f2fs_balance_fs detects node page modification with dn.node_changed to avoid miss some corner cases. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 16103b30fdec..bb5175295a2f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -671,7 +671,6 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, unsigned int ofs_in_node, last_ofs_in_node; blkcnt_t prealloc; struct extent_info ei; - bool allocated = false; block_t blkaddr; if (!maxblocks) @@ -730,10 +729,8 @@ next_block: } } else { err = __allocate_data_block(&dn); - if (!err) { + if (!err) set_inode_flag(inode, FI_APPEND_WRITE); - allocated = true; - } } if (err) goto sync_out; @@ -788,7 +785,6 @@ skip: err = reserve_new_blocks(&dn, prealloc); if (err) goto sync_out; - allocated = dn.node_changed; map->m_len += dn.ofs_in_node - ofs_in_node; if (prealloc && dn.ofs_in_node != last_ofs_in_node + 1) { @@ -807,9 +803,8 @@ skip: if (create) { f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, allocated); + f2fs_balance_fs(sbi, dn.node_changed); } - allocated = false; goto next_dnode; sync_out: @@ -817,7 +812,7 @@ sync_out: unlock_out: if (create) { f2fs_unlock_op(sbi); - f2fs_balance_fs(sbi, allocated); + f2fs_balance_fs(sbi, dn.node_changed); } out: trace_f2fs_map_blocks(inode, map, err); -- GitLab From f4cab3cc8a76e4d5eabcd151db486f48dcb7bad2 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:57:04 +0800 Subject: [PATCH 0397/5498] f2fs: be aware of extent beyond EOF in fiemap f2fs can support fallocating blocks beyond file size without changing the size, but ->fiemap of f2fs was restricted and can't detect these extents fallocated past EOF, now relieve the restriction. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index bb5175295a2f..ca8ed873e58b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -881,7 +881,6 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, struct buffer_head map_bh; sector_t start_blk, last_blk; pgoff_t next_pgofs; - loff_t isize; u64 logical = 0, phys = 0, size = 0; u32 flags = 0; int ret = 0; @@ -898,13 +897,6 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, inode_lock(inode); - isize = i_size_read(inode); - if (start >= isize) - goto out; - - if (start + len > isize) - len = isize - start; - if (logical_to_blk(inode, len) == 0) len = blk_to_logical(inode, 1); @@ -923,13 +915,11 @@ next: /* HOLE */ if (!buffer_mapped(&map_bh)) { start_blk = next_pgofs; - /* Go through holes util pass the EOF */ - if (blk_to_logical(inode, start_blk) < isize) + + if (blk_to_logical(inode, start_blk) < blk_to_logical(inode, + F2FS_I_SB(inode)->max_file_blocks)) goto prep_next; - /* Found a hole beyond isize means no more extents. - * Note that the premise is that filesystems don't - * punch holes beyond isize and keep size unchanged. - */ + flags |= FIEMAP_EXTENT_LAST; } -- GitLab From 17b064c7104431f6a88748f8d774f08c0aadfdf0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:57:05 +0800 Subject: [PATCH 0398/5498] f2fs: fix to update largest extent under lock In order to avoid racing problem, make largest extent cache being updated under lock. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 6d799f5b78bb..b5092dd35fc5 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -252,6 +252,7 @@ retry: int update_inode(struct inode *inode, struct page *node_page) { struct f2fs_inode *ri; + struct extent_tree *et = F2FS_I(inode)->extent_tree; f2fs_inode_synced(inode); @@ -267,11 +268,13 @@ int update_inode(struct inode *inode, struct page *node_page) ri->i_size = cpu_to_le64(i_size_read(inode)); ri->i_blocks = cpu_to_le64(inode->i_blocks); - if (F2FS_I(inode)->extent_tree) - set_raw_extent(&F2FS_I(inode)->extent_tree->largest, - &ri->i_ext); - else + if (et) { + read_lock(&et->lock); + set_raw_extent(&et->largest, &ri->i_ext); + read_unlock(&et->lock); + } else { memset(&ri->i_ext, 0, sizeof(ri->i_ext)); + } set_raw_inline(inode, ri); ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec); -- GitLab From 9f88e9b88a3eafdb20b411b89360f8587b4ad1da Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:57:06 +0800 Subject: [PATCH 0399/5498] f2fs: fix error handling in fsync_node_pages In fsync_node_pages, if f2fs was taged with CP_ERROR_FLAG, make sure bio cache was flushed before return. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5bbb7371698a..57d22ddb89e9 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1344,7 +1344,8 @@ retry: if (unlikely(f2fs_cp_error(sbi))) { f2fs_put_page(last_page, 0); pagevec_release(&pvec); - return -EIO; + ret = -EIO; + goto out; } if (!IS_DNODE(page) || !is_cold_node(page)) @@ -1417,7 +1418,7 @@ continue_unlock: unlock_page(last_page); goto retry; } - +out: if (nwritten) f2fs_submit_merged_bio_cond(sbi, NULL, NULL, ino, NODE, WRITE); return ret ? -EIO: 0; -- GitLab From 5d80603c33053c64c1e6b9014c192394e6be63d1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 11 Oct 2016 10:36:12 -0700 Subject: [PATCH 0400/5498] f2fs: fix sparse warnings f2fs contained a number of endianness conversion bugs. Also, one function should have been 'static'. Found with sparse by running 'make C=2 CF=-D__CHECK_ENDIAN__ fs/f2fs/' Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/dir.c | 2 +- fs/f2fs/inline.c | 2 +- fs/f2fs/node.c | 5 +++-- fs/f2fs/node.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index ffdb2897accb..864bdddb96b7 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -136,7 +136,7 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, /* show encrypted name */ if (fname->hash) { - if (de->hash_code == fname->hash) + if (de->hash_code == cpu_to_le32(fname->hash)) goto found; } else if (de_name.len == name->len && de->hash_code == namehash && diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index b21a0788f2cd..06d20489d532 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -420,7 +420,7 @@ static int f2fs_add_inline_entries(struct inode *dir, } new_name.name = d.filename[bit_pos]; - new_name.len = de->name_len; + new_name.len = le16_to_cpu(de->name_len); ino = le32_to_cpu(de->ino); fake_mode = get_de_type(de) << S_SHIFT; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 57d22ddb89e9..b4354d6ca40d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -270,8 +270,9 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, e = grab_nat_entry(nm_i, nid); node_info_from_raw_nat(&e->ni, ne); } else { - f2fs_bug_on(sbi, nat_get_ino(e) != ne->ino || - nat_get_blkaddr(e) != ne->block_addr || + f2fs_bug_on(sbi, nat_get_ino(e) != le32_to_cpu(ne->ino) || + nat_get_blkaddr(e) != + le32_to_cpu(ne->block_addr) || nat_get_version(e) != ne->version); } } diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 868bec65e51c..cfdcf98516a1 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -313,7 +313,7 @@ static inline bool is_recoverable_dnode(struct page *page) ((unsigned char *)ckpt + crc_offset))); cp_ver |= (crc << 32); } - return cpu_to_le64(cp_ver) == cpver_of_node(page); + return cp_ver == cpver_of_node(page); } /* -- GitLab From 1d13aee8cd3987953951f09934d93964b6f86452 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:56:59 +0800 Subject: [PATCH 0401/5498] f2fs: clear nlink if fail to add_link We don't need to keep incomplete created inode in cache, so if we fail to add link into directory during new inode creation, it's better to set nlink of inode to zero, then we can evict inode immediately. Otherwise release of nid belong to inode will be delayed until inode cache is being shrunk, it may cause a seemingly endless loop while allocating free nids in time of testing generic/269 case of fstest suit. Signed-off-by: Chao Yu [Jaegeuk Kim: add update_inode_page to fix kernel panic] Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index b5092dd35fc5..0fbe947aa856 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -387,6 +387,8 @@ retry: f2fs_lock_op(sbi); err = remove_inode_page(inode); f2fs_unlock_op(sbi); + if (err == -ENOENT) + err = 0; } /* give more chances, if ENOMEM case */ @@ -427,6 +429,18 @@ void handle_failed_inode(struct inode *inode) struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct node_info ni; + /* + * clear nlink of inode in order to release resource of inode + * immediately. + */ + clear_nlink(inode); + + /* + * we must call this to avoid inode being remained as dirty, resulting + * in a panic when flushing dirty inodes in gdirty_list. + */ + update_inode_page(inode); + /* don't make bad inode, since it becomes a regular file. */ unlock_new_inode(inode); -- GitLab From 20e859977601f117d756902e59b00645c3d7f5b8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 12 Oct 2016 19:28:29 +0800 Subject: [PATCH 0402/5498] f2fs: split free nid list During free nid allocation, in order to do preallocation, we will tag free nid entry as allocated one and still leave it in free nid list, for other allocators who want to grab free nids, it needs to traverse the free nid list for lookup. It becomes overhead in scenario of allocating free nid intensively by multithreads. This patch splits free nid list to two list: {free,alloc}_nid_list, to keep free nids and preallocated free nids separately, after that, traverse latency will be gone, besides split nid_cnt for separate statistic. Additionally, introduce __insert_nid_to_list and __remove_nid_from_list for cleanup. Signed-off-by: Chao Yu [Jaegeuk Kim: modify f2fs_bug_on to avoid needless branches] Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 11 ++-- fs/f2fs/f2fs.h | 14 +++-- fs/f2fs/node.c | 136 +++++++++++++++++++++++++++------------------ fs/f2fs/node.h | 11 ++-- fs/f2fs/shrinker.c | 4 +- 5 files changed, 108 insertions(+), 68 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 8d8624864e96..31d6fb98b4a2 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -74,7 +74,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->dirty_nats = NM_I(sbi)->dirty_nat_cnt; si->sits = MAIN_SEGS(sbi); si->dirty_sits = SIT_I(sbi)->dirty_sentries; - si->fnids = NM_I(sbi)->fcnt; + si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID_LIST]; + si->alloc_nids = NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]; si->bg_gc = sbi->bg_gc; si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg) * 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg) @@ -194,7 +195,9 @@ get_cache: si->cache_mem += sizeof(struct flush_cmd_control); /* free nids */ - si->cache_mem += NM_I(sbi)->fcnt * sizeof(struct free_nid); + si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID_LIST] + + NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]) * + sizeof(struct free_nid); si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry); si->cache_mem += NM_I(sbi)->dirty_nat_cnt * sizeof(struct nat_entry_set); @@ -326,8 +329,8 @@ static int stat_show(struct seq_file *s, void *v) si->ndirty_imeta); seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", si->dirty_nats, si->nats, si->dirty_sits, si->sits); - seq_printf(s, " - free_nids: %9d\n", - si->fnids); + seq_printf(s, " - free_nids: %9d, alloc_nids: %9d\n", + si->free_nids, si->alloc_nids); seq_puts(s, "\nDistribution of User Blocks:"); seq_puts(s, " [ valid | invalid | free ]\n"); seq_puts(s, " ["); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5f822e8596e0..f3bdc7a88421 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -543,6 +543,12 @@ static inline void __try_update_largest_extent(struct inode *inode, } } +enum nid_list { + FREE_NID_LIST, + ALLOC_NID_LIST, + MAX_NID_LIST, +}; + struct f2fs_nm_info { block_t nat_blkaddr; /* base disk address of NAT */ nid_t max_nid; /* maximum possible node ids */ @@ -562,9 +568,9 @@ struct f2fs_nm_info { /* free node ids management */ struct radix_tree_root free_nid_root;/* root of the free_nid cache */ - struct list_head free_nid_list; /* a list for free nids */ - spinlock_t free_nid_list_lock; /* protect free nid list */ - unsigned int fcnt; /* the number of free node id */ + struct list_head nid_list[MAX_NID_LIST];/* lists for free nids */ + unsigned int nid_cnt[MAX_NID_LIST]; /* the number of free node id */ + spinlock_t nid_list_lock; /* protect nid lists ops */ struct mutex build_lock; /* lock for build free nids */ /* for checkpoint */ @@ -2225,7 +2231,7 @@ struct f2fs_stat_info { s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta; s64 inmem_pages; unsigned int ndirty_dirs, ndirty_files, ndirty_all; - int nats, dirty_nats, sits, dirty_sits, fnids; + int nats, dirty_nats, sits, dirty_sits, free_nids, alloc_nids; int total_count, utilization; int bg_gc, wb_bios; int inline_xattr, inline_inode, inline_dir, orphans; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b4354d6ca40d..12577b917bf7 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -45,8 +45,8 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) * give 25%, 25%, 50%, 50%, 50% memory for each components respectively */ if (type == FREE_NIDS) { - mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >> - PAGE_SHIFT; + mem_size = (nm_i->nid_cnt[FREE_NID_LIST] * + sizeof(struct free_nid)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == NAT_ENTRIES) { mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> @@ -1702,10 +1702,31 @@ static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, static void __del_from_free_nid_list(struct f2fs_nm_info *nm_i, struct free_nid *i) { - list_del(&i->list); radix_tree_delete(&nm_i->free_nid_root, i->nid); } +static void __insert_nid_to_list(struct f2fs_sb_info *sbi, + struct free_nid *i, enum nid_list list) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + + f2fs_bug_on(sbi, list == FREE_NID_LIST ? i->state != NID_NEW : + i->state != NID_ALLOC); + nm_i->nid_cnt[list]++; + list_add_tail(&i->list, &nm_i->nid_list[list]); +} + +static void __remove_nid_from_list(struct f2fs_sb_info *sbi, + struct free_nid *i, enum nid_list list) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + + f2fs_bug_on(sbi, list == FREE_NID_LIST ? i->state != NID_NEW : + i->state != NID_ALLOC); + nm_i->nid_cnt[list]--; + list_del(&i->list); +} + static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) { struct f2fs_nm_info *nm_i = NM_I(sbi); @@ -1736,33 +1757,33 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) return 0; } - spin_lock(&nm_i->free_nid_list_lock); + spin_lock(&nm_i->nid_list_lock); if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) { - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); kmem_cache_free(free_nid_slab, i); return 0; } - list_add_tail(&i->list, &nm_i->free_nid_list); - nm_i->fcnt++; - spin_unlock(&nm_i->free_nid_list_lock); + __insert_nid_to_list(sbi, i, FREE_NID_LIST); + spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); return 1; } -static void remove_free_nid(struct f2fs_nm_info *nm_i, nid_t nid) +static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) { + struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i; bool need_free = false; - spin_lock(&nm_i->free_nid_list_lock); + spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); if (i && i->state == NID_NEW) { + __remove_nid_from_list(sbi, i, FREE_NID_LIST); __del_from_free_nid_list(nm_i, i); - nm_i->fcnt--; need_free = true; } - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); if (need_free) kmem_cache_free(free_nid_slab, i); @@ -1801,7 +1822,7 @@ void __build_free_nids(struct f2fs_sb_info *sbi) nid_t nid = nm_i->next_scan_nid; /* Enough entries */ - if (nm_i->fcnt >= NAT_ENTRY_PER_BLOCK) + if (nm_i->nid_cnt[FREE_NID_LIST] >= NAT_ENTRY_PER_BLOCK) return; /* readahead nat pages to be scanned */ @@ -1837,7 +1858,7 @@ void __build_free_nids(struct f2fs_sb_info *sbi) if (addr == NULL_ADDR) add_free_nid(sbi, nid, true); else - remove_free_nid(nm_i, nid); + remove_free_nid(sbi, nid); } up_read(&curseg->journal_rwsem); up_read(&nm_i->nat_tree_lock); @@ -1870,23 +1891,22 @@ retry: if (unlikely(sbi->total_valid_node_count + 1 > nm_i->available_nids)) return false; - spin_lock(&nm_i->free_nid_list_lock); + spin_lock(&nm_i->nid_list_lock); /* We should not use stale free nids created by build_free_nids */ - if (nm_i->fcnt && !on_build_free_nids(nm_i)) { - f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list)); - list_for_each_entry(i, &nm_i->free_nid_list, list) - if (i->state == NID_NEW) - break; - - f2fs_bug_on(sbi, i->state != NID_NEW); + if (nm_i->nid_cnt[FREE_NID_LIST] && !on_build_free_nids(nm_i)) { + f2fs_bug_on(sbi, list_empty(&nm_i->nid_list[FREE_NID_LIST])); + i = list_first_entry(&nm_i->nid_list[FREE_NID_LIST], + struct free_nid, list); *nid = i->nid; + + __remove_nid_from_list(sbi, i, FREE_NID_LIST); i->state = NID_ALLOC; - nm_i->fcnt--; - spin_unlock(&nm_i->free_nid_list_lock); + __insert_nid_to_list(sbi, i, ALLOC_NID_LIST); + spin_unlock(&nm_i->nid_list_lock); return true; } - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); /* Let's scan nat pages and its caches to get free nids */ build_free_nids(sbi); @@ -1901,11 +1921,12 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid) struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i; - spin_lock(&nm_i->free_nid_list_lock); + spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); - f2fs_bug_on(sbi, !i || i->state != NID_ALLOC); + f2fs_bug_on(sbi, !i); + __remove_nid_from_list(sbi, i, ALLOC_NID_LIST); __del_from_free_nid_list(nm_i, i); - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); } @@ -1922,17 +1943,20 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) if (!nid) return; - spin_lock(&nm_i->free_nid_list_lock); + spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); - f2fs_bug_on(sbi, !i || i->state != NID_ALLOC); + f2fs_bug_on(sbi, !i); + + __remove_nid_from_list(sbi, i, ALLOC_NID_LIST); + if (!available_free_memory(sbi, FREE_NIDS)) { __del_from_free_nid_list(nm_i, i); need_free = true; } else { i->state = NID_NEW; - nm_i->fcnt++; + __insert_nid_to_list(sbi, i, FREE_NID_LIST); } - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); if (need_free) kmem_cache_free(free_nid_slab, i); @@ -1944,24 +1968,26 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) struct free_nid *i, *next; int nr = nr_shrink; - if (nm_i->fcnt <= MAX_FREE_NIDS) + if (nm_i->nid_cnt[FREE_NID_LIST] <= MAX_FREE_NIDS) return 0; if (!mutex_trylock(&nm_i->build_lock)) return 0; - spin_lock(&nm_i->free_nid_list_lock); - list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) { - if (nr_shrink <= 0 || nm_i->fcnt <= MAX_FREE_NIDS) + spin_lock(&nm_i->nid_list_lock); + list_for_each_entry_safe(i, next, &nm_i->nid_list[FREE_NID_LIST], + list) { + if (nr_shrink <= 0 || + nm_i->nid_cnt[FREE_NID_LIST] <= MAX_FREE_NIDS) break; - if (i->state == NID_ALLOC) - continue; + + __remove_nid_from_list(sbi, i, FREE_NID_LIST); __del_from_free_nid_list(nm_i, i); + kmem_cache_free(free_nid_slab, i); - nm_i->fcnt--; nr_shrink--; } - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); mutex_unlock(&nm_i->build_lock); return nr - nr_shrink; @@ -2017,7 +2043,7 @@ recover_xnid: if (unlikely(!inc_valid_node_count(sbi, inode))) f2fs_bug_on(sbi, 1); - remove_free_nid(NM_I(sbi), new_xnid); + remove_free_nid(sbi, new_xnid); get_node_info(sbi, new_xnid, &ni); ni.ino = inode->i_ino; set_node_addr(sbi, &ni, NEW_ADDR, false); @@ -2047,7 +2073,7 @@ retry: } /* Should not use this inode from free nid list */ - remove_free_nid(NM_I(sbi), ino); + remove_free_nid(sbi, ino); if (!PageUptodate(ipage)) SetPageUptodate(ipage); @@ -2281,20 +2307,22 @@ static int init_node_manager(struct f2fs_sb_info *sbi) /* not used nids: 0, node, meta, (and root counted as valid node) */ nm_i->available_nids = nm_i->max_nid - F2FS_RESERVED_NODE_NUM; - nm_i->fcnt = 0; + nm_i->nid_cnt[FREE_NID_LIST] = 0; + nm_i->nid_cnt[ALLOC_NID_LIST] = 0; nm_i->nat_cnt = 0; nm_i->ram_thresh = DEF_RAM_THRESHOLD; nm_i->ra_nid_pages = DEF_RA_NID_PAGES; nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD; INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); - INIT_LIST_HEAD(&nm_i->free_nid_list); + INIT_LIST_HEAD(&nm_i->nid_list[FREE_NID_LIST]); + INIT_LIST_HEAD(&nm_i->nid_list[ALLOC_NID_LIST]); INIT_RADIX_TREE(&nm_i->nat_root, GFP_NOIO); INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_NOIO); INIT_LIST_HEAD(&nm_i->nat_entries); mutex_init(&nm_i->build_lock); - spin_lock_init(&nm_i->free_nid_list_lock); + spin_lock_init(&nm_i->nid_list_lock); init_rwsem(&nm_i->nat_tree_lock); nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); @@ -2339,17 +2367,19 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) return; /* destroy free nid list */ - spin_lock(&nm_i->free_nid_list_lock); - list_for_each_entry_safe(i, next_i, &nm_i->free_nid_list, list) { - f2fs_bug_on(sbi, i->state == NID_ALLOC); + spin_lock(&nm_i->nid_list_lock); + list_for_each_entry_safe(i, next_i, &nm_i->nid_list[FREE_NID_LIST], + list) { + __remove_nid_from_list(sbi, i, FREE_NID_LIST); __del_from_free_nid_list(nm_i, i); - nm_i->fcnt--; - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); - spin_lock(&nm_i->free_nid_list_lock); + spin_lock(&nm_i->nid_list_lock); } - f2fs_bug_on(sbi, nm_i->fcnt); - spin_unlock(&nm_i->free_nid_list_lock); + f2fs_bug_on(sbi, nm_i->nid_cnt[FREE_NID_LIST]); + f2fs_bug_on(sbi, nm_i->nid_cnt[ALLOC_NID_LIST]); + f2fs_bug_on(sbi, !list_empty(&nm_i->nid_list[ALLOC_NID_LIST])); + spin_unlock(&nm_i->nid_list_lock); /* destroy nat cache */ down_write(&nm_i->nat_tree_lock); diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index cfdcf98516a1..e7997e240366 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -169,14 +169,15 @@ static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *fnid; - spin_lock(&nm_i->free_nid_list_lock); - if (nm_i->fcnt <= 0) { - spin_unlock(&nm_i->free_nid_list_lock); + spin_lock(&nm_i->nid_list_lock); + if (nm_i->nid_cnt[FREE_NID_LIST] <= 0) { + spin_unlock(&nm_i->nid_list_lock); return; } - fnid = list_entry(nm_i->free_nid_list.next, struct free_nid, list); + fnid = list_entry(nm_i->nid_list[FREE_NID_LIST].next, + struct free_nid, list); *nid = fnid->nid; - spin_unlock(&nm_i->free_nid_list_lock); + spin_unlock(&nm_i->nid_list_lock); } /* diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index 46c915425923..ec539f407cc4 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -26,8 +26,8 @@ static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi) static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) { - if (NM_I(sbi)->fcnt > MAX_FREE_NIDS) - return NM_I(sbi)->fcnt - MAX_FREE_NIDS; + if (NM_I(sbi)->nid_cnt[FREE_NID_LIST] > MAX_FREE_NIDS) + return NM_I(sbi)->nid_cnt[FREE_NID_LIST] - MAX_FREE_NIDS; return 0; } -- GitLab From 997c9ede879e560405dca538171edd7b0fe6dfac Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 12 Oct 2016 10:09:59 -0700 Subject: [PATCH 0403/5498] f2fs: clean up free nid list operations This patch cleans up to use consistent free nid list ops. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 56 +++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 12577b917bf7..c30cfc4fef37 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1699,25 +1699,26 @@ static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, return radix_tree_lookup(&nm_i->free_nid_root, n); } -static void __del_from_free_nid_list(struct f2fs_nm_info *nm_i, - struct free_nid *i) -{ - radix_tree_delete(&nm_i->free_nid_root, i->nid); -} - -static void __insert_nid_to_list(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_list list) +static int __insert_nid_to_list(struct f2fs_sb_info *sbi, + struct free_nid *i, enum nid_list list, bool new) { struct f2fs_nm_info *nm_i = NM_I(sbi); + if (new) { + int err = radix_tree_insert(&nm_i->free_nid_root, i->nid, i); + if (err) + return err; + } + f2fs_bug_on(sbi, list == FREE_NID_LIST ? i->state != NID_NEW : i->state != NID_ALLOC); nm_i->nid_cnt[list]++; list_add_tail(&i->list, &nm_i->nid_list[list]); + return 0; } static void __remove_nid_from_list(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_list list) + struct free_nid *i, enum nid_list list, bool reuse) { struct f2fs_nm_info *nm_i = NM_I(sbi); @@ -1725,6 +1726,8 @@ static void __remove_nid_from_list(struct f2fs_sb_info *sbi, i->state != NID_ALLOC); nm_i->nid_cnt[list]--; list_del(&i->list); + if (!reuse) + radix_tree_delete(&nm_i->free_nid_root, i->nid); } static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) @@ -1732,6 +1735,7 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i; struct nat_entry *ne; + int err; if (!available_free_memory(sbi, FREE_NIDS)) return -1; @@ -1758,15 +1762,13 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) } spin_lock(&nm_i->nid_list_lock); - if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) { - spin_unlock(&nm_i->nid_list_lock); - radix_tree_preload_end(); + err = __insert_nid_to_list(sbi, i, FREE_NID_LIST, true); + spin_unlock(&nm_i->nid_list_lock); + radix_tree_preload_end(); + if (err) { kmem_cache_free(free_nid_slab, i); return 0; } - __insert_nid_to_list(sbi, i, FREE_NID_LIST); - spin_unlock(&nm_i->nid_list_lock); - radix_tree_preload_end(); return 1; } @@ -1779,8 +1781,7 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); if (i && i->state == NID_NEW) { - __remove_nid_from_list(sbi, i, FREE_NID_LIST); - __del_from_free_nid_list(nm_i, i); + __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); need_free = true; } spin_unlock(&nm_i->nid_list_lock); @@ -1900,9 +1901,9 @@ retry: struct free_nid, list); *nid = i->nid; - __remove_nid_from_list(sbi, i, FREE_NID_LIST); + __remove_nid_from_list(sbi, i, FREE_NID_LIST, true); i->state = NID_ALLOC; - __insert_nid_to_list(sbi, i, ALLOC_NID_LIST); + __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); spin_unlock(&nm_i->nid_list_lock); return true; } @@ -1924,8 +1925,7 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); f2fs_bug_on(sbi, !i); - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST); - __del_from_free_nid_list(nm_i, i); + __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, false); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); @@ -1947,14 +1947,13 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) i = __lookup_free_nid_list(nm_i, nid); f2fs_bug_on(sbi, !i); - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST); - if (!available_free_memory(sbi, FREE_NIDS)) { - __del_from_free_nid_list(nm_i, i); + __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, false); need_free = true; } else { + __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, true); i->state = NID_NEW; - __insert_nid_to_list(sbi, i, FREE_NID_LIST); + __insert_nid_to_list(sbi, i, FREE_NID_LIST, false); } spin_unlock(&nm_i->nid_list_lock); @@ -1981,9 +1980,7 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) nm_i->nid_cnt[FREE_NID_LIST] <= MAX_FREE_NIDS) break; - __remove_nid_from_list(sbi, i, FREE_NID_LIST); - __del_from_free_nid_list(nm_i, i); - + __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); kmem_cache_free(free_nid_slab, i); nr_shrink--; } @@ -2370,8 +2367,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) spin_lock(&nm_i->nid_list_lock); list_for_each_entry_safe(i, next_i, &nm_i->nid_list[FREE_NID_LIST], list) { - __remove_nid_from_list(sbi, i, FREE_NID_LIST); - __del_from_free_nid_list(nm_i, i); + __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); spin_lock(&nm_i->nid_list_lock); -- GitLab From 89ad3012420d096fa6e03f14741e89e795663c78 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:31:35 +0800 Subject: [PATCH 0404/5498] f2fs: don't interrupt free nids building during nid allocation Let build_free_nids support sync/async methods, in allocation flow of nids, we use synchronuous method, so that we can avoid looping in alloc_nid when free memory is low; in unblock_operations and f2fs_balance_fs_bg we use asynchronuous method in where low memory condition can interrupt us. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 22 ++++++++++------------ fs/f2fs/segment.c | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 74f9bca6bd8a..eabc5b868b54 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -988,7 +988,7 @@ static void unblock_operations(struct f2fs_sb_info *sbi) { up_write(&sbi->node_write); - build_free_nids(sbi); + build_free_nids(sbi, false); f2fs_unlock_all(sbi); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f3bdc7a88421..db74e2f7ac61 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2086,7 +2086,7 @@ void move_node_page(struct page *, int); int fsync_node_pages(struct f2fs_sb_info *, struct inode *, struct writeback_control *, bool); int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); -void build_free_nids(struct f2fs_sb_info *); +void build_free_nids(struct f2fs_sb_info *, bool); bool alloc_nid(struct f2fs_sb_info *, nid_t *); void alloc_nid_done(struct f2fs_sb_info *, nid_t); void alloc_nid_failed(struct f2fs_sb_info *, nid_t); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index c30cfc4fef37..b1454b401a2a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1737,9 +1737,6 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) struct nat_entry *ne; int err; - if (!available_free_memory(sbi, FREE_NIDS)) - return -1; - /* 0 nid should not be used */ if (unlikely(nid == 0)) return 0; @@ -1807,14 +1804,12 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr); f2fs_bug_on(sbi, blk_addr == NEW_ADDR); - if (blk_addr == NULL_ADDR) { - if (add_free_nid(sbi, start_nid, true) < 0) - break; - } + if (blk_addr == NULL_ADDR) + add_free_nid(sbi, start_nid, true); } } -void __build_free_nids(struct f2fs_sb_info *sbi) +void __build_free_nids(struct f2fs_sb_info *sbi, bool sync) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); @@ -1826,6 +1821,9 @@ void __build_free_nids(struct f2fs_sb_info *sbi) if (nm_i->nid_cnt[FREE_NID_LIST] >= NAT_ENTRY_PER_BLOCK) return; + if (!sync && !available_free_memory(sbi, FREE_NIDS)) + return; + /* readahead nat pages to be scanned */ ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, META_NAT, true); @@ -1868,10 +1866,10 @@ void __build_free_nids(struct f2fs_sb_info *sbi) nm_i->ra_nid_pages, META_NAT, false); } -void build_free_nids(struct f2fs_sb_info *sbi) +void build_free_nids(struct f2fs_sb_info *sbi, bool sync) { mutex_lock(&NM_I(sbi)->build_lock); - __build_free_nids(sbi); + __build_free_nids(sbi, sync); mutex_unlock(&NM_I(sbi)->build_lock); } @@ -1910,7 +1908,7 @@ retry: spin_unlock(&nm_i->nid_list_lock); /* Let's scan nat pages and its caches to get free nids */ - build_free_nids(sbi); + build_free_nids(sbi, true); goto retry; } @@ -2347,7 +2345,7 @@ int build_node_manager(struct f2fs_sb_info *sbi) if (err) return err; - build_free_nids(sbi); + build_free_nids(sbi, true); return 0; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 75477ec6c535..48903702de27 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -380,7 +380,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) if (!available_free_memory(sbi, FREE_NIDS)) try_to_free_nids(sbi, MAX_FREE_NIDS); else - build_free_nids(sbi); + build_free_nids(sbi, false); /* checkpoint is the only way to shrink partial cached entries */ if (!available_free_memory(sbi, NAT_ENTRIES) || -- GitLab From f7cc090c9d102bdbbced3ad150edccda5d221316 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Oct 2016 22:31:36 +0800 Subject: [PATCH 0405/5498] f2fs: avoid casted negative value as shrink count This patch makes sure it returns a positive value instead of a probable casted negative value as shrink count. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/shrinker.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index ec539f407cc4..5c60fc28ec75 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -21,14 +21,16 @@ static unsigned int shrinker_run_no; static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi) { - return NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt; + long count = NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt; + + return count > 0 ? count : 0; } static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) { - if (NM_I(sbi)->nid_cnt[FREE_NID_LIST] > MAX_FREE_NIDS) - return NM_I(sbi)->nid_cnt[FREE_NID_LIST] - MAX_FREE_NIDS; - return 0; + long count = NM_I(sbi)->nid_cnt[FREE_NID_LIST] - MAX_FREE_NIDS; + + return count > 0 ? count : 0; } static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi) -- GitLab From 31a99f9e14a6f1f6c94ce3f8e8dfbab006c0cb05 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 14 Oct 2016 13:28:05 -0700 Subject: [PATCH 0406/5498] f2fs: count dirty inodes to flush node pages during checkpoint If there are a lot of dirty inodes, we need to flush all of them when doing checkpoint. So, we need to count this for enough free space. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 59f075f79f85..1a161b7052f0 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -470,11 +470,12 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); if (test_opt(sbi, LFS)) return false; - return free_sections(sbi) <= (node_secs + 2 * dent_secs + + return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + reserved_sections(sbi) + 1); } @@ -483,14 +484,14 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); - - node_secs += get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); + int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return false; return (free_sections(sbi) + freed) <= - (node_secs + 2 * dent_secs + reserved_sections(sbi) + needed); + (node_secs + 2 * dent_secs + imeta_secs + + reserved_sections(sbi) + needed); } static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi) -- GitLab From e4f0165ebb63bc77312fbb76bcdde1f69258040e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 14 Oct 2016 13:30:31 -0700 Subject: [PATCH 0407/5498] f2fs: call f2fs_balance_fs for setattr If inode becomes dirty, we need to check the # of dirty inodes whether or not further checkpoint would be required. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e673756bbc14..aea33be5cfb9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -696,7 +696,6 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) err = f2fs_truncate(inode); if (err) return err; - f2fs_balance_fs(F2FS_I_SB(inode), true); } else { /* * do not trim all blocks after i_size if target size is @@ -725,6 +724,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } f2fs_mark_inode_dirty_sync(inode); + + /* inode change will produce dirty node pages flushed by checkpoint */ + f2fs_balance_fs(F2FS_I_SB(inode), true); + return err; } -- GitLab From da5ab8a87469c208078704ed043a18129651c19e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 17 Oct 2016 15:36:31 -0700 Subject: [PATCH 0408/5498] f2fs: declare static function for __build_free_nids This patch avoids build warning. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b1454b401a2a..3e4c6ef10047 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1809,7 +1809,7 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, } } -void __build_free_nids(struct f2fs_sb_info *sbi, bool sync) +static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); -- GitLab From 660bbf60d79cd633ebe505421ecfafdeb2981f3e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 18 Oct 2016 11:07:45 -0700 Subject: [PATCH 0409/5498] f2fs: use BIO_MAX_PAGES for bio allocation We don't need to allocate bio partially in order to maximize sequential writes. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 4 +--- fs/f2fs/node.c | 3 +-- fs/f2fs/segment.c | 4 ++-- fs/f2fs/segment.h | 17 +++-------------- 5 files changed, 8 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index eabc5b868b54..7b7cc39888fe 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -227,7 +227,7 @@ void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index) f2fs_put_page(page, 0); if (readahead) - ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR, true); + ra_meta_pages(sbi, index, BIO_MAX_PAGES, META_POR, true); } static int f2fs_write_meta_page(struct page *page, diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ca8ed873e58b..436cabe5bcdc 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -273,10 +273,8 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) __submit_merged_bio(io); alloc_new: if (io->bio == NULL) { - int bio_blocks = MAX_BIO_BLOCKS(sbi); - io->bio = __bio_alloc(sbi, fio->new_blkaddr, - bio_blocks, is_read); + BIO_MAX_PAGES, is_read); io->fio = *fio; } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 3e4c6ef10047..d3b5aa81fde2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2102,7 +2102,6 @@ int restore_node_summary(struct f2fs_sb_info *sbi, struct f2fs_node *rn; struct f2fs_summary *sum_entry; block_t addr; - int bio_blocks = MAX_BIO_BLOCKS(sbi); int i, idx, last_offset, nrpages; /* scan the node segment */ @@ -2111,7 +2110,7 @@ int restore_node_summary(struct f2fs_sb_info *sbi, sum_entry = &sum->entries[0]; for (i = 0; i < last_offset; i += nrpages, addr += nrpages) { - nrpages = min(last_offset - i, bio_blocks); + nrpages = min(last_offset - i, BIO_MAX_PAGES); /* readahead node pages */ ra_meta_pages(sbi, addr, nrpages, META_POR, true); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 48903702de27..ec4d74c26067 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2239,10 +2239,10 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, start, end; unsigned int readed, start_blk = 0; - int nrpages = MAX_BIO_BLOCKS(sbi) * 8; do { - readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true); + readed = ra_meta_pages(sbi, start_blk, BIO_MAX_PAGES, + META_SIT, true); start = start_blk * sit_i->sents_per_block; end = (start_blk + readed) * sit_i->sents_per_block; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 1a161b7052f0..59c4673244eb 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -101,8 +101,6 @@ (((sector_t)blk_addr) << F2FS_LOG_SECTORS_PER_BLOCK) #define SECTOR_TO_BLOCK(sectors) \ (sectors >> F2FS_LOG_SECTORS_PER_BLOCK) -#define MAX_BIO_BLOCKS(sbi) \ - ((int)min((int)max_hw_blocks(sbi), BIO_MAX_PAGES)) /* * indicate a block allocation direction: RIGHT and LEFT. @@ -695,13 +693,6 @@ static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno) return false; } -static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi) -{ - struct block_device *bdev = sbi->sb->s_bdev; - struct request_queue *q = bdev_get_queue(bdev); - return SECTOR_TO_BLOCK(queue_max_sectors(q)); -} - /* * It is very important to gather dirty pages and write at once, so that we can * submit a big bio without interfering other data writes. @@ -719,7 +710,7 @@ static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type) else if (type == NODE) return 8 * sbi->blocks_per_seg; else if (type == META) - return 8 * MAX_BIO_BLOCKS(sbi); + return 8 * BIO_MAX_PAGES; else return 0; } @@ -736,11 +727,9 @@ static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type, return 0; nr_to_write = wbc->nr_to_write; - + desired = BIO_MAX_PAGES; if (type == NODE) - desired = 2 * max_hw_blocks(sbi); - else - desired = MAX_BIO_BLOCKS(sbi); + desired <<= 1; wbc->nr_to_write = desired; return desired - nr_to_write; -- GitLab From 3212839cc8e80201fd2c216a8c1402cae502249a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 10 Nov 2016 18:04:05 -0800 Subject: [PATCH 0410/5498] f2fs: Replace CURRENT_TIME_SEC with current_time() for inode timestamps This is for backport only. fs: Replace CURRENT_TIME_SEC with current_time() for inode timestamps Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 8 ++++---- fs/f2fs/f2fs.h | 22 ++++++++++++++++++++++ fs/f2fs/file.c | 8 ++++---- fs/f2fs/inline.c | 2 +- fs/f2fs/namei.c | 8 ++++---- fs/f2fs/xattr.c | 2 +- 6 files changed, 36 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 864bdddb96b7..c0224dbbefba 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -312,7 +312,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, f2fs_dentry_kunmap(dir, page); set_page_dirty(page); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = current_time(dir); f2fs_mark_inode_dirty_sync(dir); f2fs_put_page(page, 1); } @@ -465,7 +465,7 @@ void update_parent_metadata(struct inode *dir, struct inode *inode, f2fs_i_links_write(dir, true); clear_inode_flag(inode, FI_NEW_INODE); } - dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = current_time(dir); f2fs_mark_inode_dirty_sync(dir); if (F2FS_I(dir)->i_current_depth != current_depth) @@ -683,7 +683,7 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode) if (S_ISDIR(inode->i_mode)) f2fs_i_links_write(dir, false); - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = current_time(inode); f2fs_i_links_write(inode, false); if (S_ISDIR(inode->i_mode)) { @@ -730,7 +730,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, kunmap(page); /* kunmap - pair of f2fs_find_entry */ set_page_dirty(page); - dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_ctime = dir->i_mtime = current_time(dir); f2fs_mark_inode_dirty_sync(dir); if (inode) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index db74e2f7ac61..7fcc8c536c75 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -159,6 +159,28 @@ static inline void inode_nohighmem(struct inode *inode) mapping_set_gfp_mask(inode->i_mapping, GFP_USER); } +/** + * current_time - Return FS time + * @inode: inode. + * + * Return the current time truncated to the time granularity supported by + * the fs. + * + * Note that inode and inode->sb cannot be NULL. + * Otherwise, the function warns and returns time without truncation. + */ +static inline struct timespec current_time(struct inode *inode) +{ + struct timespec now = current_kernel_time(); + + if (unlikely(!inode->i_sb)) { + WARN(1, "current_time() called with uninitialized super_block in the inode"); + return now; + } + + return timespec_trunc(now, inode->i_sb->s_time_gran); +} + /* * For checkpoint manager */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index aea33be5cfb9..2b24b45e184a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -633,7 +633,7 @@ int f2fs_truncate(struct inode *inode) if (err) return err; - inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode); return 0; } @@ -709,7 +709,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_mtime = inode->i_ctime = current_time(inode); } } @@ -1405,7 +1405,7 @@ static long f2fs_fallocate(struct file *file, int mode, } if (!ret) { - inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } @@ -1497,7 +1497,7 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) fi->i_flags = flags; inode_unlock(inode); - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = current_time(inode); f2fs_set_inode_flags(inode); out: mnt_drop_write_file(filp); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 06d20489d532..3106155994b4 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -573,7 +573,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, set_page_dirty(page); f2fs_put_page(page, 1); - dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_ctime = dir->i_mtime = current_time(dir); f2fs_mark_inode_dirty_sync(dir); if (inode) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index a8f9368b38df..5996bee354d0 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -47,7 +47,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) inode->i_ino = ino; inode->i_blocks = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); inode->i_generation = sbi->s_next_generation++; err = insert_inode_locked(inode); @@ -183,7 +183,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, f2fs_balance_fs(sbi, true); - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = current_time(inode); ihold(inode); set_inode_flag(inode, FI_INC_LINK); @@ -735,7 +735,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_set_link(new_dir, new_entry, new_page, old_inode); - new_inode->i_ctime = CURRENT_TIME; + new_inode->i_ctime = current_time(new_inode); down_write(&F2FS_I(new_inode)->i_sem); if (old_dir_entry) f2fs_i_links_write(new_inode, false); @@ -789,7 +789,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, file_set_enc_name(old_inode); up_write(&F2FS_I(old_inode)->i_sem); - old_inode->i_ctime = CURRENT_TIME; + old_inode->i_ctime = current_time(old_inode); f2fs_mark_inode_dirty_sync(old_inode); f2fs_delete_entry(old_entry, old_page, old_dir, NULL); diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 4f9bd6bd8e3f..ccfe9f3893a2 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -590,7 +590,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (is_inode_flag_set(inode, FI_ACL_MODE)) { inode->i_mode = F2FS_I(inode)->i_acl_mode; - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = current_time(inode); clear_inode_flag(inode, FI_ACL_MODE); } if (index == F2FS_XATTR_INDEX_ENCRYPTION && -- GitLab From dca9665533141ddf8b2f2f9ec546efa24cac3a4e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 14 Oct 2016 11:51:23 -0700 Subject: [PATCH 0411/5498] f2fs: keep dirty inodes selectively for checkpoint This is to avoid no free segment bug during checkpoint caused by a number of dirty inodes. The case was reported by Chao like this. 1. mount with lazytime option 2. fill 4k file until disk is full 3. sync filesystem 4. read all files in the image 5. umount In this case, we actually don't need to flush dirty inode to inode page during checkpoint. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 2 +- fs/f2fs/dir.c | 6 +++--- fs/f2fs/extent_cache.c | 2 +- fs/f2fs/f2fs.h | 26 +++++++++++++------------- fs/f2fs/file.c | 9 +++++---- fs/f2fs/inline.c | 2 +- fs/f2fs/inode.c | 7 ++++--- fs/f2fs/namei.c | 6 +++--- fs/f2fs/super.c | 29 ++++++++++++++++------------- fs/f2fs/xattr.c | 4 ++-- 10 files changed, 49 insertions(+), 44 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 6fe23af509e1..8f487692c21f 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -384,7 +384,7 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage, if (error) return error; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); if (default_acl) { error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl, diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index c0224dbbefba..08490fb5c62f 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -313,7 +313,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, set_page_dirty(page); dir->i_mtime = dir->i_ctime = current_time(dir); - f2fs_mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir, false); f2fs_put_page(page, 1); } @@ -466,7 +466,7 @@ void update_parent_metadata(struct inode *dir, struct inode *inode, clear_inode_flag(inode, FI_NEW_INODE); } dir->i_mtime = dir->i_ctime = current_time(dir); - f2fs_mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir, false); if (F2FS_I(dir)->i_current_depth != current_depth) f2fs_i_depth_write(dir, current_depth); @@ -731,7 +731,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, set_page_dirty(page); dir->i_ctime = dir->i_mtime = current_time(dir); - f2fs_mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir, false); if (inode) f2fs_drop_nlink(dir, inode); diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 2b06d4fcd954..4db44da7ef69 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -172,7 +172,7 @@ static void __drop_largest_extent(struct inode *inode, if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs) { largest->len = 0; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7fcc8c536c75..9935b7663aab 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -555,13 +555,13 @@ static inline bool __is_front_mergeable(struct extent_info *cur, return __is_extent_mergeable(cur, front); } -extern void f2fs_mark_inode_dirty_sync(struct inode *); +extern void f2fs_mark_inode_dirty_sync(struct inode *, bool); static inline void __try_update_largest_extent(struct inode *inode, struct extent_tree *et, struct extent_node *en) { if (en->ei.len > et->largest.len) { et->largest = en->ei; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } } @@ -1693,7 +1693,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, return; case FI_DATA_EXIST: case FI_INLINE_DOTS: - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } } @@ -1720,7 +1720,7 @@ static inline void set_acl_inode(struct inode *inode, umode_t mode) { F2FS_I(inode)->i_acl_mode = mode; set_inode_flag(inode, FI_ACL_MODE); - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, false); } static inline void f2fs_i_links_write(struct inode *inode, bool inc) @@ -1729,7 +1729,7 @@ static inline void f2fs_i_links_write(struct inode *inode, bool inc) inc_nlink(inode); else drop_nlink(inode); - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } static inline void f2fs_i_blocks_write(struct inode *inode, @@ -1740,7 +1740,7 @@ static inline void f2fs_i_blocks_write(struct inode *inode, inode->i_blocks = add ? inode->i_blocks + diff : inode->i_blocks - diff; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); if (clean || recover) set_inode_flag(inode, FI_AUTO_RECOVER); } @@ -1754,7 +1754,7 @@ static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) return; i_size_write(inode, i_size); - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); if (clean || recover) set_inode_flag(inode, FI_AUTO_RECOVER); } @@ -1769,19 +1769,19 @@ static inline bool f2fs_skip_inode_update(struct inode *inode) static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth) { F2FS_I(inode)->i_current_depth = depth; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid) { F2FS_I(inode)->i_xattr_nid = xnid; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } static inline void f2fs_i_pino_write(struct inode *inode, nid_t pino) { F2FS_I(inode)->i_pino = pino; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) @@ -1909,13 +1909,13 @@ static inline int is_file(struct inode *inode, int type) static inline void set_file(struct inode *inode, int type) { F2FS_I(inode)->i_advise |= type; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } static inline void clear_file(struct inode *inode, int type) { F2FS_I(inode)->i_advise &= ~type; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); } static inline int f2fs_readonly(struct super_block *sb) @@ -2069,7 +2069,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) /* * super.c */ -int f2fs_inode_dirtied(struct inode *); +int f2fs_inode_dirtied(struct inode *, bool); void f2fs_inode_synced(struct inode *); int f2fs_commit_super(struct f2fs_sb_info *, bool); int f2fs_sync_fs(struct super_block *, int); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2b24b45e184a..c31549cf5ad2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -266,7 +266,7 @@ sync_nodes: } if (need_inode_block_update(sbi, ino)) { - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); f2fs_write_inode(inode, NULL); goto sync_nodes; } @@ -634,7 +634,7 @@ int f2fs_truncate(struct inode *inode) return err; inode->i_mtime = inode->i_ctime = current_time(inode); - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, false); return 0; } @@ -723,7 +723,8 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } } - f2fs_mark_inode_dirty_sync(inode); + /* update attributes only */ + f2fs_mark_inode_dirty_sync(inode, false); /* inode change will produce dirty node pages flushed by checkpoint */ f2fs_balance_fs(F2FS_I_SB(inode), true); @@ -1406,7 +1407,7 @@ static long f2fs_fallocate(struct file *file, int mode, if (!ret) { inode->i_mtime = inode->i_ctime = current_time(inode); - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, false); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 3106155994b4..841aa13d9f4e 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -574,7 +574,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, f2fs_put_page(page, 1); dir->i_ctime = dir->i_mtime = current_time(dir); - f2fs_mark_inode_dirty_sync(dir); + f2fs_mark_inode_dirty_sync(dir, false); if (inode) f2fs_drop_nlink(dir, inode); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 0fbe947aa856..cc07530a3ba9 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -19,10 +19,11 @@ #include -void f2fs_mark_inode_dirty_sync(struct inode *inode) +void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync) { - if (f2fs_inode_dirtied(inode)) + if (f2fs_inode_dirtied(inode, sync)) return; + mark_inode_dirty_sync(inode); } @@ -43,7 +44,7 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_DIRSYNC; inode_set_flags(inode, new_fl, S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, false); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 5996bee354d0..5316d455e120 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -790,7 +790,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, up_write(&F2FS_I(old_inode)->i_sem); old_inode->i_ctime = current_time(old_inode); - f2fs_mark_inode_dirty_sync(old_inode); + f2fs_mark_inode_dirty_sync(old_inode, false); f2fs_delete_entry(old_entry, old_page, old_dir, NULL); @@ -950,7 +950,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(old_dir, old_nlink > 0); up_write(&F2FS_I(old_dir)->i_sem); } - f2fs_mark_inode_dirty_sync(old_dir); + f2fs_mark_inode_dirty_sync(old_dir, false); /* update directory entry info of new dir inode */ f2fs_set_link(new_dir, new_entry, new_page, old_inode); @@ -965,7 +965,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(new_dir, new_nlink > 0); up_write(&F2FS_I(new_dir)->i_sem); } - f2fs_mark_inode_dirty_sync(new_dir); + f2fs_mark_inode_dirty_sync(new_dir, false); f2fs_unlock_op(sbi); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 62a6aa3fe147..011f5cbdf12d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -610,24 +610,25 @@ static int f2fs_drop_inode(struct inode *inode) return generic_drop_inode(inode); } -int f2fs_inode_dirtied(struct inode *inode) +int f2fs_inode_dirtied(struct inode *inode, bool sync) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int ret = 0; spin_lock(&sbi->inode_lock[DIRTY_META]); if (is_inode_flag_set(inode, FI_DIRTY_INODE)) { - spin_unlock(&sbi->inode_lock[DIRTY_META]); - return 1; + ret = 1; + } else { + set_inode_flag(inode, FI_DIRTY_INODE); + stat_inc_dirty_inode(sbi, DIRTY_META); } - - set_inode_flag(inode, FI_DIRTY_INODE); - list_add_tail(&F2FS_I(inode)->gdirty_list, + if (sync && list_empty(&F2FS_I(inode)->gdirty_list)) { + list_add_tail(&F2FS_I(inode)->gdirty_list, &sbi->inode_list[DIRTY_META]); - inc_page_count(sbi, F2FS_DIRTY_IMETA); - stat_inc_dirty_inode(sbi, DIRTY_META); + inc_page_count(sbi, F2FS_DIRTY_IMETA); + } spin_unlock(&sbi->inode_lock[DIRTY_META]); - - return 0; + return ret; } void f2fs_inode_synced(struct inode *inode) @@ -639,10 +640,12 @@ void f2fs_inode_synced(struct inode *inode) spin_unlock(&sbi->inode_lock[DIRTY_META]); return; } - list_del_init(&F2FS_I(inode)->gdirty_list); + if (!list_empty(&F2FS_I(inode)->gdirty_list)) { + list_del_init(&F2FS_I(inode)->gdirty_list); + dec_page_count(sbi, F2FS_DIRTY_IMETA); + } clear_inode_flag(inode, FI_DIRTY_INODE); clear_inode_flag(inode, FI_AUTO_RECOVER); - dec_page_count(sbi, F2FS_DIRTY_IMETA); stat_dec_dirty_inode(F2FS_I_SB(inode), DIRTY_META); spin_unlock(&sbi->inode_lock[DIRTY_META]); } @@ -663,7 +666,7 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) if (is_inode_flag_set(inode, FI_AUTO_RECOVER)) clear_inode_flag(inode, FI_AUTO_RECOVER); - f2fs_inode_dirtied(inode); + f2fs_inode_dirtied(inode, false); } static void f2fs_i_callback(struct rcu_head *head) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index ccfe9f3893a2..f7d043803947 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -153,7 +153,7 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, return -EINVAL; F2FS_I(inode)->i_advise |= *(char *)value; - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); return 0; } @@ -596,7 +596,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (index == F2FS_XATTR_INDEX_ENCRYPTION && !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) f2fs_set_encrypted_inode(inode); - f2fs_mark_inode_dirty_sync(inode); + f2fs_mark_inode_dirty_sync(inode, true); if (!error && S_ISDIR(inode->i_mode)) set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP); exit: -- GitLab From 85b276dafabddce4c1b282d136288552cc6882cb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 19 Oct 2016 18:27:56 -0700 Subject: [PATCH 0412/5498] f2fs: make clean inodes when flushing inode page This patch tries to make more clean inodes when flushing dirty inodes in checkpoint. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 6 +++++- fs/f2fs/inode.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 7b7cc39888fe..37b558acccd4 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -922,7 +922,11 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi) inode = igrab(&fi->vfs_inode); spin_unlock(&sbi->inode_lock[DIRTY_META]); if (inode) { - update_inode_page(inode); + sync_inode_metadata(inode, 0); + + /* it's on eviction */ + if (is_inode_flag_set(inode, FI_DIRTY_INODE)) + update_inode_page(inode); iput(inode); } }; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index cc07530a3ba9..7a2b3cc964a5 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -339,7 +339,7 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) * We need to balance fs here to prevent from producing dirty node pages * during the urgent cleaning time when runing out of free sections. */ - if (update_inode_page(inode)) + if (update_inode_page(inode) && wbc && wbc->nr_to_write) f2fs_balance_fs(sbi, true); return 0; } -- GitLab From 7a5791722ee5b6699efe286b72ba1c1c30019e16 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 20 Oct 2016 19:09:57 -0700 Subject: [PATCH 0413/5498] f2fs: remove percpu_count due to performance regression This patch removes percpu_count usage due to performance regression in iozone. Fixes: 523be8a6b3 ("f2fs: use percpu_counter for page counters") Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 12 ++++++------ fs/f2fs/f2fs.h | 12 ++++++------ fs/f2fs/super.c | 16 +++++----------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 31d6fb98b4a2..79f5b5a4e428 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -315,17 +315,17 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - inmem: %4lld, wb_bios: %4d\n", + seq_printf(s, " - inmem: %4d, wb_bios: %4d\n", si->inmem_pages, si->wb_bios); - seq_printf(s, " - nodes: %4lld in %4d\n", + seq_printf(s, " - nodes: %4d in %4d\n", si->ndirty_node, si->node_pages); - seq_printf(s, " - dents: %4lld in dirs:%4d (%4d)\n", + seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n", si->ndirty_dent, si->ndirty_dirs, si->ndirty_all); - seq_printf(s, " - datas: %4lld in files:%4d\n", + seq_printf(s, " - datas: %4d in files:%4d\n", si->ndirty_data, si->ndirty_files); - seq_printf(s, " - meta: %4lld in %4d\n", + seq_printf(s, " - meta: %4d in %4d\n", si->ndirty_meta, si->meta_pages); - seq_printf(s, " - imeta: %4lld\n", + seq_printf(s, " - imeta: %4d\n", si->ndirty_imeta); seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", si->dirty_nats, si->nats, si->dirty_sits, si->sits); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9935b7663aab..db0a5a9f080e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -885,7 +885,7 @@ struct f2fs_sb_info { atomic_t nr_wb_bios; /* # of writeback bios */ /* # of pages, see count_type */ - struct percpu_counter nr_pages[NR_COUNT_TYPE]; + atomic_t nr_pages[NR_COUNT_TYPE]; /* # of allocated blocks */ struct percpu_counter alloc_valid_block_count; @@ -1299,7 +1299,7 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) { - percpu_counter_inc(&sbi->nr_pages[count_type]); + atomic_inc(&sbi->nr_pages[count_type]); if (count_type == F2FS_DIRTY_DATA || count_type == F2FS_INMEM_PAGES) return; @@ -1316,7 +1316,7 @@ static inline void inode_inc_dirty_pages(struct inode *inode) static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) { - percpu_counter_dec(&sbi->nr_pages[count_type]); + atomic_dec(&sbi->nr_pages[count_type]); } static inline void inode_dec_dirty_pages(struct inode *inode) @@ -1332,7 +1332,7 @@ static inline void inode_dec_dirty_pages(struct inode *inode) static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type) { - return percpu_counter_sum_positive(&sbi->nr_pages[count_type]); + return atomic_read(&sbi->nr_pages[count_type]); } static inline s64 get_dirty_pages(struct inode *inode) @@ -2250,8 +2250,8 @@ struct f2fs_stat_info { unsigned long long hit_largest, hit_cached, hit_rbtree; unsigned long long hit_total, total_ext; int ext_tree, zombie_tree, ext_node; - s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta; - s64 inmem_pages; + int ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta; + int inmem_pages; unsigned int ndirty_dirs, ndirty_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits, free_nids, alloc_nids; int total_count, utilization; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 011f5cbdf12d..574a28789490 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -683,10 +683,6 @@ static void f2fs_destroy_inode(struct inode *inode) static void destroy_percpu_info(struct f2fs_sb_info *sbi) { - int i; - - for (i = 0; i < NR_COUNT_TYPE; i++) - percpu_counter_destroy(&sbi->nr_pages[i]); percpu_counter_destroy(&sbi->alloc_valid_block_count); percpu_counter_destroy(&sbi->total_valid_inode_count); } @@ -1436,6 +1432,7 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) static void init_sb_info(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = sbi->raw_super; + int i; sbi->log_sectors_per_block = le32_to_cpu(raw_super->log_sectors_per_block); @@ -1460,6 +1457,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi) sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL; clear_sbi_flag(sbi, SBI_NEED_FSCK); + for (i = 0; i < NR_COUNT_TYPE; i++) + atomic_set(&sbi->nr_pages[i], 0); + INIT_LIST_HEAD(&sbi->s_list); mutex_init(&sbi->umount_mutex); mutex_init(&sbi->wio_mutex[NODE]); @@ -1475,13 +1475,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi) static int init_percpu_info(struct f2fs_sb_info *sbi) { - int i, err; - - for (i = 0; i < NR_COUNT_TYPE; i++) { - err = percpu_counter_init(&sbi->nr_pages[i], 0, GFP_KERNEL); - if (err) - return err; - } + int err; err = percpu_counter_init(&sbi->alloc_valid_block_count, 0, GFP_KERNEL); if (err) -- GitLab From b63fd7b2ca21ca52509a7ae8737c8b9e816a5f4b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 2 Nov 2016 14:52:15 +0100 Subject: [PATCH 0414/5498] f2fs: hide a maybe-uninitialized warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc is unsure about the use of last_ofs_in_node, which might happen without a prior initialization: fs/f2fs//git/arm-soc/fs/f2fs/data.c: In function ‘f2fs_map_blocks’: fs/f2fs/data.c:799:54: warning: ‘last_ofs_in_node’ may be used uninitialized in this function [-Wmaybe-uninitialized] if (prealloc && dn.ofs_in_node != last_ofs_in_node + 1) { As pointed out by Chao Yu, the code is actually correct as 'prealloc' is only set if the last_ofs_in_node has been set, the two always get updated together. This initializes last_ofs_in_node to dn.ofs_in_node for each new dnode at the start of the 'next_block' loop, which at that point is a correct initialization as well. I assume that compilers that correctly track the contents of the variables and do not warn about the condition also figure out that they can eliminate the extra assignment here. Fixes: 46008c6d4232 ("f2fs: support in batch multi blocks preallocation") Signed-off-by: Arnd Bergmann Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 436cabe5bcdc..6388d2d5d24e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -708,7 +708,7 @@ next_dnode: } prealloc = 0; - ofs_in_node = dn.ofs_in_node; + last_ofs_in_node = ofs_in_node = dn.ofs_in_node; end_offset = ADDRS_PER_PAGE(dn.node_page, inode); next_block: -- GitLab From 2a1690c39c9dfc7555550093bdf48cb1c66b1c01 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 1 Dec 2016 10:44:44 -0800 Subject: [PATCH 0415/5498] fs/crypto: catch up 4.9-rc6 Signed-off-by: Jaegeuk Kim --- fs/crypto/crypto.c | 24 ++++---- fs/crypto/fname.c | 127 ++++++++++++++++++--------------------- fs/crypto/keyinfo.c | 89 ++++++++++++++++++--------- fs/crypto/policy.c | 53 ++++++++++++---- fs/f2fs/dir.c | 6 +- fs/f2fs/file.c | 2 +- fs/f2fs/namei.c | 6 +- include/linux/fscrypto.h | 29 +-------- 8 files changed, 183 insertions(+), 153 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 99be22df21fa..83ece4d31d54 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -129,11 +129,11 @@ struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags) EXPORT_SYMBOL(fscrypt_get_ctx); /** - * fscrypt_complete() - The completion callback for page encryption - * @req: The asynchronous encryption request context - * @res: The result of the encryption operation + * page_crypt_complete() - completion callback for page crypto + * @req: The asynchronous cipher request context + * @res: The result of the cipher operation */ -static void fscrypt_complete(struct crypto_async_request *req, int res) +static void page_crypt_complete(struct crypto_async_request *req, int res) { struct fscrypt_completion_result *ecr = req->data; @@ -153,7 +153,10 @@ static int do_page_crypto(struct inode *inode, struct page *src_page, struct page *dest_page, gfp_t gfp_flags) { - u8 xts_tweak[FS_XTS_TWEAK_SIZE]; + struct { + __le64 index; + u8 padding[FS_XTS_TWEAK_SIZE - sizeof(__le64)]; + } xts_tweak; struct ablkcipher_request *req = NULL; DECLARE_FS_COMPLETION_RESULT(ecr); struct scatterlist dst, src; @@ -171,19 +174,18 @@ static int do_page_crypto(struct inode *inode, ablkcipher_request_set_callback( req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - fscrypt_complete, &ecr); + page_crypt_complete, &ecr); - BUILD_BUG_ON(FS_XTS_TWEAK_SIZE < sizeof(index)); - memcpy(xts_tweak, &index, sizeof(index)); - memset(&xts_tweak[sizeof(index)], 0, - FS_XTS_TWEAK_SIZE - sizeof(index)); + BUILD_BUG_ON(sizeof(xts_tweak) != FS_XTS_TWEAK_SIZE); + xts_tweak.index = cpu_to_le64(index); + memset(xts_tweak.padding, 0, sizeof(xts_tweak.padding)); sg_init_table(&dst, 1); sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0); sg_init_table(&src, 1); sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0); ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE, - xts_tweak); + &xts_tweak); if (rw == FS_DECRYPT) res = crypto_ablkcipher_decrypt(req); else diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 5e4ddeeba267..3e907874840b 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -19,15 +19,12 @@ #include #include -static u32 size_round_up(size_t size, size_t blksize) -{ - return ((size + blksize - 1) / blksize) * blksize; -} - /** - * dir_crypt_complete() - + * fname_crypt_complete() - completion callback for filename crypto + * @req: The asynchronous cipher request context + * @res: The result of the cipher operation */ -static void dir_crypt_complete(struct crypto_async_request *req, int res) +static void fname_crypt_complete(struct crypto_async_request *req, int res) { struct fscrypt_completion_result *ecr = req->data; @@ -38,90 +35,81 @@ static void dir_crypt_complete(struct crypto_async_request *req, int res) } /** - * fname_encrypt() - + * fname_encrypt() - encrypt a filename + * + * The caller must have allocated sufficient memory for the @oname string. * - * This function encrypts the input filename, and returns the length of the - * ciphertext. Errors are returned as negative numbers. We trust the caller to - * allocate sufficient memory to oname string. + * Return: 0 on success, -errno on failure */ static int fname_encrypt(struct inode *inode, const struct qstr *iname, struct fscrypt_str *oname) { - u32 ciphertext_len; struct ablkcipher_request *req = NULL; DECLARE_FS_COMPLETION_RESULT(ecr); struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; char iv[FS_CRYPTO_BLOCK_SIZE]; - struct scatterlist src_sg, dst_sg; + struct scatterlist sg; int padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); - char *workbuf, buf[32], *alloc_buf = NULL; - unsigned lim; + unsigned int lim; + unsigned int cryptlen; lim = inode->i_sb->s_cop->max_namelen(inode); if (iname->len <= 0 || iname->len > lim) return -EIO; - ciphertext_len = (iname->len < FS_CRYPTO_BLOCK_SIZE) ? - FS_CRYPTO_BLOCK_SIZE : iname->len; - ciphertext_len = size_round_up(ciphertext_len, padding); - ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len; + /* + * Copy the filename to the output buffer for encrypting in-place and + * pad it with the needed number of NUL bytes. + */ + cryptlen = max_t(unsigned int, iname->len, FS_CRYPTO_BLOCK_SIZE); + cryptlen = round_up(cryptlen, padding); + cryptlen = min(cryptlen, lim); + memcpy(oname->name, iname->name, iname->len); + memset(oname->name + iname->len, 0, cryptlen - iname->len); - if (ciphertext_len <= sizeof(buf)) { - workbuf = buf; - } else { - alloc_buf = kmalloc(ciphertext_len, GFP_NOFS); - if (!alloc_buf) - return -ENOMEM; - workbuf = alloc_buf; - } + /* Initialize the IV */ + memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); /* Allocate request */ req = ablkcipher_request_alloc(tfm, GFP_NOFS); if (!req) { printk_ratelimited(KERN_ERR - "%s: crypto_request_alloc() failed\n", __func__); - kfree(alloc_buf); + "%s: ablkcipher_request_alloc() failed\n", __func__); return -ENOMEM; } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - dir_crypt_complete, &ecr); - - /* Copy the input */ - memcpy(workbuf, iname->name, iname->len); - if (iname->len < ciphertext_len) - memset(workbuf + iname->len, 0, ciphertext_len - iname->len); + fname_crypt_complete, &ecr); - /* Initialize IV */ - memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); + sg_init_one(&sg, oname->name, cryptlen); + ablkcipher_request_set_crypt(req, &sg, &sg, cryptlen, iv); - /* Create encryption request */ - sg_init_one(&src_sg, workbuf, ciphertext_len); - sg_init_one(&dst_sg, oname->name, ciphertext_len); - ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); + /* Do the encryption */ res = crypto_ablkcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { + /* Request is being completed asynchronously; wait for it */ wait_for_completion(&ecr.completion); res = ecr.res; } - kfree(alloc_buf); ablkcipher_request_free(req); - if (res < 0) + if (res < 0) { printk_ratelimited(KERN_ERR "%s: Error (error code %d)\n", __func__, res); + return res; + } - oname->len = ciphertext_len; - return res; + oname->len = cryptlen; + return 0; } -/* - * fname_decrypt() - * This function decrypts the input filename, and returns - * the length of the plaintext. - * Errors are returned as negative numbers. - * We trust the caller to allocate sufficient memory to oname string. +/** + * fname_decrypt() - decrypt a filename + * + * The caller must have allocated sufficient memory for the @oname string. + * + * Return: 0 on success, -errno on failure */ static int fname_decrypt(struct inode *inode, const struct fscrypt_str *iname, @@ -149,7 +137,7 @@ static int fname_decrypt(struct inode *inode, } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - dir_crypt_complete, &ecr); + fname_crypt_complete, &ecr); /* Initialize IV */ memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); @@ -171,7 +159,7 @@ static int fname_decrypt(struct inode *inode, } oname->len = strnlen(oname->name, iname->len); - return oname->len; + return 0; } static const char *lookup_table = @@ -234,9 +222,8 @@ u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen) if (ci) padding = 4 << (ci->ci_flags & FS_POLICY_FLAGS_PAD_MASK); - if (ilen < FS_CRYPTO_BLOCK_SIZE) - ilen = FS_CRYPTO_BLOCK_SIZE; - return size_round_up(ilen, padding); + ilen = max(ilen, (u32)FS_CRYPTO_BLOCK_SIZE); + return round_up(ilen, padding); } EXPORT_SYMBOL(fscrypt_fname_encrypted_size); @@ -282,6 +269,10 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer); /** * fscrypt_fname_disk_to_usr() - converts a filename from disk space to user * space + * + * The caller must have allocated sufficient memory for the @oname string. + * + * Return: 0 on success, -errno on failure */ int fscrypt_fname_disk_to_usr(struct inode *inode, u32 hash, u32 minor_hash, @@ -290,13 +281,12 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, { const struct qstr qname = FSTR_TO_QSTR(iname); char buf[24]; - int ret; if (fscrypt_is_dot_dotdot(&qname)) { oname->name[0] = '.'; oname->name[iname->len - 1] = '.'; oname->len = iname->len; - return oname->len; + return 0; } if (iname->len < FS_CRYPTO_BLOCK_SIZE) @@ -306,9 +296,9 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, return fname_decrypt(inode, iname, oname); if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) { - ret = digest_encode(iname->name, iname->len, oname->name); - oname->len = ret; - return ret; + oname->len = digest_encode(iname->name, iname->len, + oname->name); + return 0; } if (hash) { memcpy(buf, &hash, 4); @@ -318,15 +308,18 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, } memcpy(buf + 8, iname->name + iname->len - 16, 16); oname->name[0] = '_'; - ret = digest_encode(buf, 24, oname->name + 1); - oname->len = ret + 1; - return ret + 1; + oname->len = 1 + digest_encode(buf, 24, oname->name + 1); + return 0; } EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); /** * fscrypt_fname_usr_to_disk() - converts a filename from user space to disk * space + * + * The caller must have allocated sufficient memory for the @oname string. + * + * Return: 0 on success, -errno on failure */ int fscrypt_fname_usr_to_disk(struct inode *inode, const struct qstr *iname, @@ -336,7 +329,7 @@ int fscrypt_fname_usr_to_disk(struct inode *inode, oname->name[0] = '.'; oname->name[iname->len - 1] = '.'; oname->len = iname->len; - return oname->len; + return 0; } if (inode->i_crypt_info) return fname_encrypt(inode, iname, oname); @@ -370,10 +363,10 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, if (dir->i_crypt_info) { ret = fscrypt_fname_alloc_buffer(dir, iname->len, &fname->crypto_buf); - if (ret < 0) + if (ret) return ret; ret = fname_encrypt(dir, iname, &fname->crypto_buf); - if (ret < 0) + if (ret) goto errout; fname->disk_name.name = fname->crypto_buf.name; fname->disk_name.len = fname->crypto_buf.len; diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 7be4f4312341..cff4641941fb 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -8,9 +8,7 @@ * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015. */ -#include #include -#include #include #include #include @@ -143,6 +141,38 @@ out: return res; } +static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode, + const char **cipher_str_ret, int *keysize_ret) +{ + if (S_ISREG(inode->i_mode)) { + if (ci->ci_data_mode == FS_ENCRYPTION_MODE_AES_256_XTS) { + *cipher_str_ret = "xts(aes)"; + *keysize_ret = FS_AES_256_XTS_KEY_SIZE; + return 0; + } + pr_warn_once("fscrypto: unsupported contents encryption mode " + "%d for inode %lu\n", + ci->ci_data_mode, inode->i_ino); + return -ENOKEY; + } + + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) { + if (ci->ci_filename_mode == FS_ENCRYPTION_MODE_AES_256_CTS) { + *cipher_str_ret = "cts(cbc(aes))"; + *keysize_ret = FS_AES_256_CTS_KEY_SIZE; + return 0; + } + pr_warn_once("fscrypto: unsupported filenames encryption mode " + "%d for inode %lu\n", + ci->ci_filename_mode, inode->i_ino); + return -ENOKEY; + } + + pr_warn_once("fscrypto: unsupported file type %d for inode %lu\n", + (inode->i_mode & S_IFMT), inode->i_ino); + return -ENOKEY; +} + static void put_crypt_info(struct fscrypt_info *ci) { if (!ci) @@ -160,8 +190,8 @@ int get_crypt_info(struct inode *inode) struct fscrypt_context ctx; struct crypto_ablkcipher *ctfm; const char *cipher_str; - u8 raw_key[FS_MAX_KEY_SIZE]; - u8 mode; + int keysize; + u8 *raw_key = NULL; int res; res = fscrypt_initialize(); @@ -184,13 +214,19 @@ retry: if (res < 0) { if (!fscrypt_dummy_context_enabled(inode)) return res; + ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS; ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; ctx.flags = 0; } else if (res != sizeof(ctx)) { return -EINVAL; } - res = 0; + + if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) + return -EINVAL; + + if (ctx.flags & ~FS_POLICY_FLAGS_VALID) + return -EINVAL; crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS); if (!crypt_info) @@ -203,27 +239,20 @@ retry: crypt_info->ci_keyring_key = NULL; memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, sizeof(crypt_info->ci_master_key)); - if (S_ISREG(inode->i_mode)) - mode = crypt_info->ci_data_mode; - else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) - mode = crypt_info->ci_filename_mode; - else - BUG(); - - switch (mode) { - case FS_ENCRYPTION_MODE_AES_256_XTS: - cipher_str = "xts(aes)"; - break; - case FS_ENCRYPTION_MODE_AES_256_CTS: - cipher_str = "cts(cbc(aes))"; - break; - default: - printk_once(KERN_WARNING - "%s: unsupported key mode %d (ino %u)\n", - __func__, mode, (unsigned) inode->i_ino); - res = -ENOKEY; + + res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize); + if (res) goto out; - } + + /* + * This cannot be a stack buffer because it is passed to the scatterlist + * crypto API as part of key derivation. + */ + res = -ENOMEM; + raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS); + if (!raw_key) + goto out; + if (fscrypt_dummy_context_enabled(inode)) { memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE); goto got_key; @@ -257,13 +286,13 @@ got_key: } crypt_info->ci_ctfm = ctfm; crypto_ablkcipher_clear_flags(ctfm, ~0); - crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), - CRYPTO_TFM_REQ_WEAK_KEY); - res = crypto_ablkcipher_setkey(ctfm, raw_key, fscrypt_key_size(mode)); + crypto_ablkcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY); + res = crypto_ablkcipher_setkey(ctfm, raw_key, keysize); if (res) goto out; - memzero_explicit(raw_key, sizeof(raw_key)); + kzfree(raw_key); + raw_key = NULL; if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) != NULL) { put_crypt_info(crypt_info); goto retry; @@ -274,7 +303,7 @@ out: if (res == -ENOKEY) res = 0; put_crypt_info(crypt_info); - memzero_explicit(raw_key, sizeof(raw_key)); + kzfree(raw_key); return res; } diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 0f9961eede1e..77338d91b3a3 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -11,6 +11,7 @@ #include #include #include +#include static int inode_has_encryption_context(struct inode *inode) { @@ -92,26 +93,56 @@ static int create_encryption_context_from_policy(struct inode *inode, return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL); } -int fscrypt_process_policy(struct inode *inode, +static inline void inode_lock(struct inode *inode) +{ + mutex_lock(&inode->i_mutex); +} + +static inline void inode_unlock(struct inode *inode) +{ + mutex_unlock(&inode->i_mutex); +} + +int fscrypt_process_policy(struct file *filp, const struct fscrypt_policy *policy) { + struct inode *inode = file_inode(filp); + int ret; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + if (policy->version != 0) return -EINVAL; + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + inode_lock(inode); + if (!inode_has_encryption_context(inode)) { - if (!inode->i_sb->s_cop->empty_dir) - return -EOPNOTSUPP; - if (!inode->i_sb->s_cop->empty_dir(inode)) - return -ENOTEMPTY; - return create_encryption_context_from_policy(inode, policy); + if (!S_ISDIR(inode->i_mode)) + ret = -EINVAL; + else if (!inode->i_sb->s_cop->empty_dir) + ret = -EOPNOTSUPP; + else if (!inode->i_sb->s_cop->empty_dir(inode)) + ret = -ENOTEMPTY; + else + ret = create_encryption_context_from_policy(inode, + policy); + } else if (!is_encryption_context_consistent_with_policy(inode, + policy)) { + printk(KERN_WARNING + "%s: Policy inconsistent with encryption context\n", + __func__); + ret = -EINVAL; } - if (is_encryption_context_consistent_with_policy(inode, policy)) - return 0; + inode_unlock(inode); - printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n", - __func__); - return -EINVAL; + mnt_drop_write_file(filp); + return ret; } EXPORT_SYMBOL(fscrypt_process_policy); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 08490fb5c62f..e160440e84cc 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -814,12 +814,12 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, if (f2fs_encrypted_inode(d->inode)) { int save_len = fstr->len; - int ret; + int err; - ret = fscrypt_fname_disk_to_usr(d->inode, + err = fscrypt_fname_disk_to_usr(d->inode, (u32)de->hash_code, 0, &de_name, fstr); - if (ret < 0) + if (err) return true; de_name = *fstr; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c31549cf5ad2..77e9af2ed341 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1779,7 +1779,7 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) return ret; f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); - ret = fscrypt_process_policy(inode, &policy); + ret = fscrypt_process_policy(filp, &policy); mnt_drop_write_file(filp); return ret; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 5316d455e120..60920dc84f29 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -463,7 +463,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, ostr.name = sd->encrypted_path; ostr.len = disk_link.len; err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr); - if (err < 0) + if (err) goto err_out; sd->len = cpu_to_le16(ostr.len); @@ -1060,7 +1060,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, goto errout; res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr); - if (res < 0) + if (res) goto errout; /* this is broken symlink case */ @@ -1072,7 +1072,7 @@ static void *f2fs_encrypted_follow_link(struct dentry *dentry, paddr = pstr.name; /* Null-terminate the name */ - paddr[res] = '\0'; + paddr[pstr.len] = '\0'; nd_set_link(nd, paddr); kunmap(cpage); diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h index 7ddcd5bc849e..181937edadad 100644 --- a/include/linux/fscrypto.h +++ b/include/linux/fscrypto.h @@ -110,23 +110,6 @@ struct fscrypt_completion_result { struct fscrypt_completion_result ecr = { \ COMPLETION_INITIALIZER((ecr).completion), 0 } -static inline int fscrypt_key_size(int mode) -{ - switch (mode) { - case FS_ENCRYPTION_MODE_AES_256_XTS: - return FS_AES_256_XTS_KEY_SIZE; - case FS_ENCRYPTION_MODE_AES_256_GCM: - return FS_AES_256_GCM_KEY_SIZE; - case FS_ENCRYPTION_MODE_AES_256_CBC: - return FS_AES_256_CBC_KEY_SIZE; - case FS_ENCRYPTION_MODE_AES_256_CTS: - return FS_AES_256_CTS_KEY_SIZE; - default: - BUG(); - } - return 0; -} - #define FS_FNAME_NUM_SCATTER_ENTRIES 4 #define FS_CRYPTO_BLOCK_SIZE 16 #define FS_FNAME_CRYPTO_DIGEST_SIZE 32 @@ -201,13 +184,6 @@ static inline bool fscrypt_valid_filenames_enc_mode(u32 mode) return (mode == FS_ENCRYPTION_MODE_AES_256_CTS); } -static inline u32 fscrypt_validate_encryption_key_size(u32 mode, u32 size) -{ - if (size == fscrypt_key_size(mode)) - return size; - return 0; -} - static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) { if (str->len == 1 && str->name[0] == '.') @@ -273,8 +249,7 @@ extern void fscrypt_restore_control_page(struct page *); extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t, unsigned int); /* policy.c */ -extern int fscrypt_process_policy(struct inode *, - const struct fscrypt_policy *); +extern int fscrypt_process_policy(struct file *, const struct fscrypt_policy *); extern int fscrypt_get_policy(struct inode *, struct fscrypt_policy *); extern int fscrypt_has_permitted_context(struct inode *, struct inode *); extern int fscrypt_inherit_context(struct inode *, struct inode *, @@ -344,7 +319,7 @@ static inline int fscrypt_notsupp_zeroout_range(struct inode *i, pgoff_t p, } /* policy.c */ -static inline int fscrypt_notsupp_process_policy(struct inode *i, +static inline int fscrypt_notsupp_process_policy(struct file *f, const struct fscrypt_policy *p) { return -EOPNOTSUPP; -- GitLab From 6142866eb99267fe14880df7566b4259db96ef1a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 29 Oct 2016 18:46:34 +0800 Subject: [PATCH 0416/5498] f2fs: report error of f2fs_fill_dentries Report error of f2fs_fill_dentries to ->iterate_shared, otherwise when error ocurrs, user may just list part of dirents in target directory without any hints. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 21 ++++++++++++--------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/inline.c | 6 ++++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index e160440e84cc..05843b0ace56 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -785,7 +785,7 @@ bool f2fs_empty_dir(struct inode *dir) return true; } -bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, +int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, unsigned int start_pos, struct fscrypt_str *fstr) { unsigned char d_type = DT_UNKNOWN; @@ -820,7 +820,7 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, (u32)de->hash_code, 0, &de_name, fstr); if (err) - return true; + return err; de_name = *fstr; fstr->len = save_len; @@ -828,12 +828,12 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, if (!dir_emit(ctx, de_name.name, de_name.len, le32_to_cpu(de->ino), d_type)) - return true; + return 1; bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); ctx->pos = start_pos + bit_pos; } - return false; + return 0; } static int f2fs_readdir(struct file *file, struct dir_context *ctx) @@ -872,17 +872,21 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) dentry_page = get_lock_data_page(inode, n, false); if (IS_ERR(dentry_page)) { err = PTR_ERR(dentry_page); - if (err == -ENOENT) + if (err == -ENOENT) { + err = 0; continue; - else + } else { goto out; + } } dentry_blk = kmap(dentry_page); make_dentry_ptr(inode, &d, (void *)dentry_blk, 1); - if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) { + err = f2fs_fill_dentries(ctx, &d, + n * NR_DENTRY_IN_BLOCK, &fstr); + if (err) { kunmap(dentry_page); f2fs_put_page(dentry_page, 1); break; @@ -892,10 +896,9 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } - err = 0; out: fscrypt_fname_free_buffer(&fstr); - return err; + return err < 0 ? err : 0; } static int f2fs_dir_open(struct inode *inode, struct file *filp) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index db0a5a9f080e..fd28a1f57027 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2029,7 +2029,7 @@ void set_de_type(struct f2fs_dir_entry *, umode_t); unsigned char get_de_type(struct f2fs_dir_entry *); struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *, f2fs_hash_t, int *, struct f2fs_dentry_ptr *); -bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, +int f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, unsigned int, struct fscrypt_str *); void do_make_empty_dir(struct inode *, struct inode *, struct f2fs_dentry_ptr *); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 841aa13d9f4e..3f8bfc87c6dc 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -611,6 +611,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, struct f2fs_inline_dentry *inline_dentry = NULL; struct page *ipage = NULL; struct f2fs_dentry_ptr d; + int err; if (ctx->pos == NR_INLINE_DENTRY) return 0; @@ -623,11 +624,12 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, make_dentry_ptr(inode, &d, (void *)inline_dentry, 2); - if (!f2fs_fill_dentries(ctx, &d, 0, fstr)) + err = f2fs_fill_dentries(ctx, &d, 0, fstr); + if (!err) ctx->pos = NR_INLINE_DENTRY; f2fs_put_page(ipage, 1); - return 0; + return err < 0 ? err : 0; } int f2fs_inline_data_fiemap(struct inode *inode, -- GitLab From bfa4fc61a43b1ff241325024b85c4ba2217efd05 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 31 Oct 2016 14:01:41 -0700 Subject: [PATCH 0417/5498] f2fs: avoid infinite loop in the EIO case on recover_orphan_inodes This patch should fix an infinite loop case below. F2FS-fs : inject IO error in f2fs_read_end_io+0xf3/0x120 [f2fs] F2FS-fs (nvme0n1p1): recover_orphan_inode: orphan failed (ino=39ac1a), run fsck to fix. ... [] sync_meta_pages+0xae/0x270 [f2fs] [] ? flush_sit_entries+0x8d/0x960 [f2fs] [] write_checkpoint+0x361/0xf20 [f2fs] [] ? trace_hardirqs_on+0xd/0x10 [] ? f2fs_sync_fs+0x79/0x190 [f2fs] [] f2fs_sync_fs+0x85/0x190 [f2fs] [] f2fs_balance_fs_bg+0x7e/0x1c0 [f2fs] [] f2fs_write_node_pages+0x34/0x320 [f2fs] [] do_writepages+0x21/0x30 [] __writeback_single_inode+0x61/0x760 [] ? _raw_spin_unlock+0x27/0x40 [] writeback_single_inode+0xd5/0x190 [] write_inode_now+0x99/0xc0 [] iput+0x1f6/0x2c0 [] f2fs_fill_super+0xe0e/0x1300 [f2fs] [] ? sget_userns+0x4f4/0x530 [] mount_bdev+0x182/0x1b0 [] ? f2fs_commit_super+0x100/0x100 [f2fs] [] f2fs_mount+0x15/0x20 [f2fs] [] mount_fs+0x38/0x170 [] vfs_kern_mount+0x6b/0x160 [] do_mount+0x1be/0xd60 [] ? copy_mount_options+0xb7/0x220 [] SyS_mount+0x94/0xd0 [] entry_SYSCALL_64_fastpath+0x23/0xc6 Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 574a28789490..ea8166b6c09d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1876,6 +1876,13 @@ free_node_inode: mutex_lock(&sbi->umount_mutex); release_ino_entry(sbi, true); f2fs_leave_shrinker(sbi); + /* + * Some dirty meta pages can be produced by recover_orphan_inodes() + * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg() + * followed by write_checkpoint() through f2fs_write_node_pages(), which + * falls into an infinite loop in sync_meta_pages(). + */ + truncate_inode_pages_final(META_MAPPING(sbi)); iput(sbi->node_inode); mutex_unlock(&sbi->umount_mutex); free_nm: -- GitLab From dc48c6295b62c9f019fb71d14e7ea66999697b29 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:44:59 +0900 Subject: [PATCH 0418/5498] f2fs: Add missing break in switch-case Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ea8166b6c09d..35eb6e7b1be3 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -416,6 +416,7 @@ static int parse_options(struct super_block *sb, char *options) break; case Opt_nodiscard: clear_opt(sbi, DISCARD); + break; case Opt_noheap: set_opt(sbi, NOHEAP); break; -- GitLab From 88ad58a2186976f5363908496fd00cdfb4137490 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:00 +0900 Subject: [PATCH 0419/5498] f2fs: Use generic zoned block device terminology SMR stands for "Shingled Magnetic Recording" which makes sense only for hard disk drives (spinning rust). The ZBC/ZAC standards enable management of SMR disks, but solid state drives may also support those standards. So rename the HMSMR feature to BLKZONED to avoid a HDD centric terminology. For the same reason, rename f2fs_sb_mounted_hmsmr to f2fs_sb_mounted_blkzoned. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 6 +++--- fs/f2fs/super.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6388d2d5d24e..b2793d043e35 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -111,7 +111,7 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, { if (!is_read_io(rw)) { atomic_inc(&sbi->nr_wb_bios); - if (f2fs_sb_mounted_hmsmr(sbi->sb) && + if (f2fs_sb_mounted_blkzoned(sbi->sb) && current->plug && (type == DATA || type == NODE)) blk_finish_plug(current->plug); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index fd28a1f57027..3ba1f6703b63 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -104,7 +104,7 @@ struct f2fs_mount_info { }; #define F2FS_FEATURE_ENCRYPT 0x0001 -#define F2FS_FEATURE_HMSMR 0x0002 +#define F2FS_FEATURE_BLKZONED 0x0002 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -2481,9 +2481,9 @@ static inline int f2fs_sb_has_crypto(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); } -static inline int f2fs_sb_mounted_hmsmr(struct super_block *sb) +static inline int f2fs_sb_mounted_blkzoned(struct super_block *sb) { - return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_HMSMR); + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_BLKZONED); } static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 35eb6e7b1be3..e80c16861903 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -960,7 +960,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, INLINE_DENTRY); set_opt(sbi, EXTENT_CACHE); set_opt(sbi, FLUSH_MERGE); - if (f2fs_sb_mounted_hmsmr(sbi->sb)) { + if (f2fs_sb_mounted_blkzoned(sbi->sb)) { set_opt_mode(sbi, F2FS_MOUNT_LFS); set_opt(sbi, DISCARD); } else { -- GitLab From 02d05e2e566c25d50ba2e29e94c9c18e08f410d9 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:01 +0900 Subject: [PATCH 0420/5498] f2fs: Check zoned block feature for host-managed zoned block devices The F2FS_FEATURE_BLKZONED feature indicates that the drive was formatted with zone alignment optimization. This is optional for host-aware devices, but mandatory for host-managed zoned block devices. So check that the feature is set in this latter case. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index e80c16861903..5432669ca1c7 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1625,6 +1625,26 @@ try_onemore: sb->s_fs_info = sbi; sbi->raw_super = raw_super; + /* + * The BLKZONED feature indicates that the drive was formatted with + * zone alignment optimization. This is optional for host-aware + * devices, but mandatory for host-managed zoned block devices. + */ +#ifndef CONFIG_BLK_DEV_ZONED + if (f2fs_sb_mounted_blkzoned(sb)) { + f2fs_msg(sb, KERN_ERR, + "Zoned block device support is not enabled\n"); + goto free_sb_buf; + } +#else + if (bdev_zoned_model(sb->s_bdev) == BLK_ZONED_HM && + !f2fs_sb_mounted_blkzoned(sb)) { + f2fs_msg(sb, KERN_ERR, + "Zoned block device feature not enabled\n"); + goto free_sb_buf; + } +#endif + default_options(sbi); /* parse mount options */ options = kstrdup((const char *)data, GFP_KERNEL); -- GitLab From 8e59e45690171e4483890a0dda2e309d3e71ff7f Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:02 +0900 Subject: [PATCH 0421/5498] f2fs: Suppress discard warning message for zoned block devices For zoned block devices, discard is replaced by zone reset. So do not warn if the device does not supports discard. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5432669ca1c7..2ac30731aa5a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -408,7 +408,7 @@ static int parse_options(struct super_block *sb, char *options) q = bdev_get_queue(sb->s_bdev); if (blk_queue_discard(q)) { set_opt(sbi, DISCARD); - } else { + } else if (!f2fs_sb_mounted_blkzoned(sb)) { f2fs_msg(sb, KERN_WARNING, "mounting with \"discard\" option, but " "the device does not support discard"); -- GitLab From eeb1cbaf8c3e678402a0dfbc7ee1550771a77e7a Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:03 +0900 Subject: [PATCH 0422/5498] f2fs: Always enable discard for zoned blocks devices Zone write pointer reset acts as discard for zoned block devices. So if the zoned block device feature is enabled, always declare that discard is enabled, even if the device does not actually support the command. For the same reason, prevent the use the "nodicard" mount option. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 14 +++++++------- fs/f2fs/super.c | 5 +++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3ba1f6703b63..12daea54bdc1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1172,13 +1172,6 @@ static inline void clear_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) spin_unlock(&sbi->cp_lock); } -static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) -{ - struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); - - return blk_queue_discard(q); -} - static inline void f2fs_lock_op(struct f2fs_sb_info *sbi) { down_read(&sbi->cp_rwsem); @@ -2486,6 +2479,13 @@ static inline int f2fs_sb_mounted_blkzoned(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_BLKZONED); } +static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) +{ + struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); + + return blk_queue_discard(q) || f2fs_sb_mounted_blkzoned(sbi->sb); +} + static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt) { clear_opt(sbi, ADAPTIVE); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2ac30731aa5a..10318822605b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -415,6 +415,11 @@ static int parse_options(struct super_block *sb, char *options) } break; case Opt_nodiscard: + if (f2fs_sb_mounted_blkzoned(sb)) { + f2fs_msg(sb, KERN_WARNING, + "discard is required for zoned block devices"); + return -EINVAL; + } clear_opt(sbi, DISCARD); break; case Opt_noheap: -- GitLab From 8eda4a4abcd03b44727d5bddca88df4b962fcffb Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:04 +0900 Subject: [PATCH 0423/5498] f2fs: Do not allow adaptive mode for host-managed zoned block devices The LFS mode is mandatory for host-managed zoned block devices as update in place optimizations are not possible for segments in sequential zones. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 10318822605b..882ee5b5d3d2 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -514,6 +514,13 @@ static int parse_options(struct super_block *sb, char *options) return -ENOMEM; if (strlen(name) == 8 && !strncmp(name, "adaptive", 8)) { + if (f2fs_sb_mounted_blkzoned(sb)) { + f2fs_msg(sb, KERN_WARNING, + "adaptive mode is not allowed with " + "zoned block device feature"); + kfree(name); + return -EINVAL; + } set_opt_mode(sbi, F2FS_MOUNT_ADAPTIVE); } else if (strlen(name) == 3 && !strncmp(name, "lfs", 3)) { -- GitLab From fc40ec5daf850d16647750973c4706376bbcd1d5 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:05 +0900 Subject: [PATCH 0424/5498] f2fs: Cache zoned block devices zone type With the zoned block device feature enabled, section discard need to do a zone reset for sections contained in sequential zones, and a regular discard (if supported) for sections stored in conventional zones. Avoid the need for a costly report zones to obtain a section zone type when discarding it by caching the types of the device zones in the super block information. This cache is initialized at mount time for mounts with the zoned block device feature enabled. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 18 +++++++++++++ fs/f2fs/super.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 12daea54bdc1..4c896555397f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -816,6 +816,14 @@ struct f2fs_sb_info { u8 key_prefix[F2FS_KEY_DESC_PREFIX_SIZE]; u8 key_prefix_size; #endif + +#ifdef CONFIG_BLK_DEV_ZONED + unsigned int nr_blkz; /* Total number of zones */ + unsigned int blocks_per_blkz; /* F2FS blocks per zone */ + unsigned int log_blocks_per_blkz; /* log2 F2FS blocks per zone */ + u8 *blkz_type; /* Array of zones type */ +#endif + /* for node-related operations */ struct f2fs_nm_info *nm_info; /* node manager */ struct inode *node_inode; /* cache node blocks */ @@ -2479,6 +2487,16 @@ static inline int f2fs_sb_mounted_blkzoned(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_BLKZONED); } +#ifdef CONFIG_BLK_DEV_ZONED +static inline int get_blkz_type(struct f2fs_sb_info *sbi, + block_t blkaddr) +{ + unsigned int zno = blkaddr >> sbi->log_blocks_per_blkz; + + return sbi->blkz_type[zno]; +} +#endif + static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) { struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 882ee5b5d3d2..160fee9e7074 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1498,6 +1498,65 @@ static int init_percpu_info(struct f2fs_sb_info *sbi) GFP_KERNEL); } +#ifdef CONFIG_BLK_DEV_ZONED +static int init_blkz_info(struct f2fs_sb_info *sbi) +{ + struct block_device *bdev = sbi->sb->s_bdev; + sector_t nr_sectors = bdev->bd_part->nr_sects; + sector_t sector = 0; + struct blk_zone *zones; + unsigned int i, nr_zones; + unsigned int n = 0; + int err = -EIO; + + if (!f2fs_sb_mounted_blkzoned(sbi->sb)) + return 0; + + sbi->blocks_per_blkz = SECTOR_TO_BLOCK(bdev_zone_size(bdev)); + sbi->log_blocks_per_blkz = __ilog2_u32(sbi->blocks_per_blkz); + sbi->nr_blkz = SECTOR_TO_BLOCK(nr_sectors) >> + sbi->log_blocks_per_blkz; + if (nr_sectors & (bdev_zone_size(bdev) - 1)) + sbi->nr_blkz++; + + sbi->blkz_type = kmalloc(sbi->nr_blkz, GFP_KERNEL); + if (!sbi->blkz_type) + return -ENOMEM; + +#define F2FS_REPORT_NR_ZONES 4096 + + zones = kcalloc(F2FS_REPORT_NR_ZONES, sizeof(struct blk_zone), + GFP_KERNEL); + if (!zones) + return -ENOMEM; + + /* Get block zones type */ + while (zones && sector < nr_sectors) { + + nr_zones = F2FS_REPORT_NR_ZONES; + err = blkdev_report_zones(bdev, sector, + zones, &nr_zones, + GFP_KERNEL); + if (err) + break; + if (!nr_zones) { + err = -EIO; + break; + } + + for (i = 0; i < nr_zones; i++) { + sbi->blkz_type[n] = zones[i].type; + sector += zones[i].len; + n++; + } + } + + kfree(zones); + + return err; +} +#endif + /* * Read f2fs raw super block. * Because we have two copies of super block, so read both of them @@ -1744,6 +1803,15 @@ try_onemore: init_ino_entry_info(sbi); +#ifdef CONFIG_BLK_DEV_ZONED + err = init_blkz_info(sbi); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to initialize F2FS blkzone information"); + goto free_blkz; + } +#endif + /* setup f2fs internal modules */ err = build_segment_manager(sbi); if (err) { @@ -1922,6 +1990,10 @@ free_nm: destroy_node_manager(sbi); free_sm: destroy_segment_manager(sbi); +#ifdef CONFIG_BLK_DEV_ZONED +free_blkz: + kfree(sbi->blkz_type); +#endif kfree(sbi->ckpt); free_meta_inode: make_bad_inode(sbi->meta_inode); -- GitLab From 449e07d91889ab9e2fd0a58c6ee263068e501e93 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:06 +0900 Subject: [PATCH 0425/5498] f2fs: Reset sequential zones on zoned block devices When a zoned block device is mounted, discarding sections contained in sequential zones must reset the zone write pointer. For sections contained in conventional zones, the regular discard is used if the drive supports it. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/segment.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ec4d74c26067..8e4863bd36f5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "f2fs.h" #include "segment.h" @@ -584,6 +585,45 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_unlock(&dirty_i->seglist_lock); } +#ifdef CONFIG_BLK_DEV_ZONED +static int f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, + block_t blkstart, block_t blklen) +{ + sector_t sector = SECTOR_FROM_BLOCK(blkstart); + sector_t nr_sects = SECTOR_FROM_BLOCK(blklen); + struct block_device *bdev = sbi->sb->s_bdev; + + if (nr_sects != bdev_zone_size(bdev)) { + f2fs_msg(sbi->sb, KERN_INFO, + "Unaligned discard attempted (sector %llu + %llu)", + (unsigned long long)sector, + (unsigned long long)nr_sects); + return -EIO; + } + + /* + * We need to know the type of the zone: for conventional zones, + * use regular discard if the drive supports it. For sequential + * zones, reset the zone write pointer. + */ + switch (get_blkz_type(sbi, blkstart)) { + + case BLK_ZONE_TYPE_CONVENTIONAL: + if (!blk_queue_discard(bdev_get_queue(bdev))) + return 0; + return blkdev_issue_discard(bdev, sector, nr_sects, + GFP_NOFS, 0); + case BLK_ZONE_TYPE_SEQWRITE_REQ: + case BLK_ZONE_TYPE_SEQWRITE_PREF: + return blkdev_reset_zones(bdev, sector, + nr_sects, GFP_NOFS); + default: + /* Unknown zone type: broken device ? */ + return -EIO; + } +} +#endif + static int f2fs_issue_discard(struct f2fs_sb_info *sbi, block_t blkstart, block_t blklen) { @@ -601,6 +641,11 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, sbi->discard_blks--; } trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); + +#ifdef CONFIG_BLK_DEV_ZONED + if (f2fs_sb_mounted_blkzoned(sbi->sb)) + return f2fs_issue_discard_zone(sbi, blkstart, blklen); +#endif return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); } -- GitLab From 7cabab336dcc71499d433acc08ff01807032230f Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 28 Oct 2016 17:45:07 +0900 Subject: [PATCH 0426/5498] f2fs: Trace reset zone events Similarly to the regular discard, trace zone reset events. Signed-off-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 1 + include/trace/events/f2fs.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 8e4863bd36f5..06b9d16a19f6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -615,6 +615,7 @@ static int f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, GFP_NOFS, 0); case BLK_ZONE_TYPE_SEQWRITE_REQ: case BLK_ZONE_TYPE_SEQWRITE_PREF: + trace_f2fs_issue_reset_zone(sbi->sb, blkstart); return blkdev_reset_zones(bdev, sector, nr_sects, GFP_NOFS); default: diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 047a5d2f77ee..4ebccde84421 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1075,6 +1075,27 @@ TRACE_EVENT(f2fs_issue_discard, (unsigned long long)__entry->blklen) ); +TRACE_EVENT(f2fs_issue_reset_zone, + + TP_PROTO(struct super_block *sb, block_t blkstart), + + TP_ARGS(sb, blkstart), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(block_t, blkstart) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->blkstart = blkstart; + ), + + TP_printk("dev = (%d,%d), reset zone at block = 0x%llx", + show_dev(__entry), + (unsigned long long)__entry->blkstart) +); + TRACE_EVENT(f2fs_issue_flush, TP_PROTO(struct super_block *sb, unsigned int nobarrier, -- GitLab From d6f66e369cb90ebf1711e91e54181e55b07e8c1f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 2 Nov 2016 20:43:21 +0800 Subject: [PATCH 0427/5498] f2fs: record inode updating status correctly We should record updating status of inode only for living inode, for those unlinked inode it needs to clear its ino cache, otherwise after the ino was been reused, it will cause unneeded node page writing during ->fsync. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 7a2b3cc964a5..e6b3045b9c83 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -377,6 +377,9 @@ void f2fs_evict_inode(struct inode *inode) goto no_delete; #endif + remove_ino_entry(sbi, inode->i_ino, APPEND_INO); + remove_ino_entry(sbi, inode->i_ino, UPDATE_INO); + sb_start_intwrite(inode->i_sb); set_inode_flag(inode, FI_NO_ALLOC); i_size_write(inode, 0); @@ -409,10 +412,12 @@ no_delete: invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino); if (xnid) invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid); - if (is_inode_flag_set(inode, FI_APPEND_WRITE)) - add_ino_entry(sbi, inode->i_ino, APPEND_INO); - if (is_inode_flag_set(inode, FI_UPDATE_WRITE)) - add_ino_entry(sbi, inode->i_ino, UPDATE_INO); + if (inode->i_nlink) { + if (is_inode_flag_set(inode, FI_APPEND_WRITE)) + add_ino_entry(sbi, inode->i_ino, APPEND_INO); + if (is_inode_flag_set(inode, FI_UPDATE_WRITE)) + add_ino_entry(sbi, inode->i_ino, UPDATE_INO); + } if (is_inode_flag_set(inode, FI_FREE_NID)) { alloc_nid_failed(sbi, inode->i_ino); clear_inode_flag(inode, FI_FREE_NID); -- GitLab From 6de1f39b2cbd18c7aad461fca08cef1faafc4b43 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 4 Nov 2016 00:26:55 +0800 Subject: [PATCH 0428/5498] f2fs: fix wrong i_atime recovery Shouldn't update in-memory i_atime with on-disk i_mtime of inode when recovering inode. Shuoran found this bug which is hidden for a long time, honour is belong to him. Signed-off-by: Shuoran Liu Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 8011cbb5500c..1c9b69158dec 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -180,10 +180,10 @@ static void recover_inode(struct inode *inode, struct page *page) inode->i_mode = le16_to_cpu(raw->i_mode); f2fs_i_size_write(inode, le64_to_cpu(raw->i_size)); - inode->i_atime.tv_sec = le64_to_cpu(raw->i_mtime); + inode->i_atime.tv_sec = le64_to_cpu(raw->i_atime); inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime); inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); - inode->i_atime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); + inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec); inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec); inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); -- GitLab From 490c5cb43ed2ff2d5d89e79bc627949b9c7b623a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 4 Nov 2016 14:33:57 -0700 Subject: [PATCH 0429/5498] f2fs: assign segments correctly for direct_io Previously, we assigned CURSEG_WARM_DATA for direct_io, but if we have two or four logs, we do not use that type at all. Let's fix it. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 06b9d16a19f6..4bdf1191a36f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1422,8 +1422,12 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, struct curseg_info *curseg; bool direct_io = (type == CURSEG_DIRECT_IO); - type = direct_io ? CURSEG_WARM_DATA : type; - + if (direct_io) { + if (sbi->active_logs <= 4) + type = CURSEG_HOT_DATA; + else + type = CURSEG_WARM_DATA; + } curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); -- GitLab From 6a33fced9be919daa858615909f76d2b3eb1ca6a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 4 Nov 2016 14:59:15 -0700 Subject: [PATCH 0430/5498] f2fs: remove checkpoint in f2fs_freeze The generic freeze_super() calls sync_filesystems() before f2fs_freeze(). So, basically we don't need to do checkpoint in f2fs_freeze(). But, in xfs/068, it triggers circular locking problem below due to gc_mutex for checkpoint. ====================================================== [ INFO: possible circular locking dependency detected ] 4.9.0-rc1+ #132 Tainted: G OE ------------------------------------------------------- 1. wait for __sb_start_write() by [] dump_stack+0x85/0xc2 [] print_circular_bug+0x1cf/0x230 [] __lock_acquire+0x19e0/0x1bc0 [] lock_acquire+0x11b/0x220 [] ? f2fs_drop_inode+0x9b/0x160 [f2fs] [] __sb_start_write+0x130/0x200 [] ? f2fs_drop_inode+0x9b/0x160 [f2fs] [] f2fs_drop_inode+0x9b/0x160 [f2fs] [] iput+0x171/0x2c0 [] f2fs_sync_inode_meta+0x3f/0xf0 [f2fs] [] block_operations+0x84/0x110 [f2fs] [] write_checkpoint+0xe8/0xf20 [f2fs] [] ? trace_hardirqs_on+0xd/0x10 [] ? f2fs_sync_fs+0x79/0x190 [f2fs] [] ? sched_clock+0x9/0x10 [] ? f2fs_sync_fs+0x79/0x190 [f2fs] [] f2fs_sync_fs+0x85/0x190 [f2fs] [] ? do_fsync+0x70/0x70 [] ? do_fsync+0x70/0x70 [] sync_fs_one_sb+0x20/0x30 [] iterate_supers+0xae/0x100 [] sys_sync+0x55/0x90 [] entry_SYSCALL_64_fastpath+0x23/0xc6 2. wait for sbi->gc_mutex by [] lock_acquire+0x11b/0x220 [] mutex_lock_nested+0x76/0x3f0 [] f2fs_sync_fs+0x79/0x190 [f2fs] [] f2fs_freeze+0x1c/0x20 [f2fs] [] freeze_super+0xcf/0x190 [] do_vfs_ioctl+0x53c/0x6a0 [] SyS_ioctl+0x79/0x90 [] entry_SYSCALL_64_fastpath+0x23/0xc6 Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 160fee9e7074..3e761a2e27ee 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -787,13 +787,17 @@ int f2fs_sync_fs(struct super_block *sb, int sync) static int f2fs_freeze(struct super_block *sb) { - int err; - if (f2fs_readonly(sb)) return 0; - err = f2fs_sync_fs(sb, 1); - return err; + /* IO error happened before */ + if (unlikely(f2fs_cp_error(F2FS_SB(sb)))) + return -EIO; + + /* must be clean, since sync_filesystem() was already called */ + if (is_sbi_flag_set(F2FS_SB(sb), SBI_IS_DIRTY)) + return -EINVAL; + return 0; } static int f2fs_unfreeze(struct super_block *sb) @@ -2138,3 +2142,4 @@ module_exit(exit_f2fs_fs) MODULE_AUTHOR("Samsung Electronics's Praesto Team"); MODULE_DESCRIPTION("Flash Friendly File System"); MODULE_LICENSE("GPL"); + -- GitLab From 68ff6c51b4722a1a43dfa7b1d1cbda3cb6dc2a9d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 5 Nov 2016 11:12:40 +0800 Subject: [PATCH 0431/5498] Revert "f2fs: do not recover from previous remained wrong dnodes" i_times of inode will be set with current system time which can be configured through 'date', so it's not safe to judge dnode block as garbage data or unchanged inode depend on i_times. Now, we have used enhanced 'cp_ver + cp' crc method to verify valid dnode block, so I expect recoverying invalid dnode is almost not possible. This reverts commit 807b1e1c8e08452948495b1a9985ab46d329e5c2. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 1c9b69158dec..0e4e12d0ae35 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -196,32 +196,6 @@ static void recover_inode(struct inode *inode, struct page *page) ino_of_node(page), name); } -static bool is_same_inode(struct inode *inode, struct page *ipage) -{ - struct f2fs_inode *ri = F2FS_INODE(ipage); - struct timespec disk; - - if (!IS_INODE(ipage)) - return true; - - disk.tv_sec = le64_to_cpu(ri->i_ctime); - disk.tv_nsec = le32_to_cpu(ri->i_ctime_nsec); - if (timespec_compare(&inode->i_ctime, &disk) > 0) - return false; - - disk.tv_sec = le64_to_cpu(ri->i_atime); - disk.tv_nsec = le32_to_cpu(ri->i_atime_nsec); - if (timespec_compare(&inode->i_atime, &disk) > 0) - return false; - - disk.tv_sec = le64_to_cpu(ri->i_mtime); - disk.tv_nsec = le32_to_cpu(ri->i_mtime_nsec); - if (timespec_compare(&inode->i_mtime, &disk) > 0) - return false; - - return true; -} - static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) { struct curseg_info *curseg; @@ -248,10 +222,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) goto next; entry = get_fsync_inode(head, ino_of_node(page)); - if (entry) { - if (!is_same_inode(entry->inode, page)) - goto next; - } else { + if (!entry) { if (IS_INODE(page) && is_dent_dnode(page)) { err = recover_inode_page(sbi, page); if (err) -- GitLab From 870f2f3e38711150fa8c41b61db0b95ca773c72d Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 7 Nov 2016 21:22:31 +0800 Subject: [PATCH 0432/5498] f2fs: return directly if block has been removed from the victim If one block has been to written to a new place, just return in move data process. This patch check it again with holding page lock. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 72a0ca08f901..744031194934 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -544,7 +544,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, return true; } -static void move_encrypted_block(struct inode *inode, block_t bidx) +static void move_encrypted_block(struct inode *inode, block_t bidx, + unsigned int segno, int off) { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), @@ -564,6 +565,9 @@ static void move_encrypted_block(struct inode *inode, block_t bidx) if (!page) return; + if (!check_valid_map(F2FS_I_SB(inode), segno, off)) + goto out; + set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE); if (err) @@ -643,7 +647,8 @@ out: f2fs_put_page(page, 1); } -static void move_data_page(struct inode *inode, block_t bidx, int gc_type) +static void move_data_page(struct inode *inode, block_t bidx, int gc_type, + unsigned int segno, int off) { struct page *page; @@ -651,6 +656,9 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type) if (IS_ERR(page)) return; + if (!check_valid_map(F2FS_I_SB(inode), segno, off)) + goto out; + if (gc_type == BG_GC) { if (PageWriteback(page)) goto out; @@ -792,9 +800,9 @@ next_step: start_bidx = start_bidx_of_node(nofs, inode) + ofs_in_node; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) - move_encrypted_block(inode, start_bidx); + move_encrypted_block(inode, start_bidx, segno, off); else - move_data_page(inode, start_bidx, gc_type); + move_data_page(inode, start_bidx, gc_type, segno, off); if (locked) { up_write(&fi->dio_rwsem[WRITE]); -- GitLab From 4e3e7234a276eb393cc2bdeb26c38a87aeb564b6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 11 Nov 2016 12:31:40 -0800 Subject: [PATCH 0433/5498] f2fs: revert segment allocation for direct IO Now we don't need to be too much careful about storage alignment for dio, since its speed becomes quite fast and we'd better avoid any misalignment first. Revert: 38aa0889b250 (f2fs: align direct_io'ed data to section) Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +----- fs/f2fs/f2fs.h | 1 - fs/f2fs/segment.c | 36 +++++++++--------------------------- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b2793d043e35..a811115938de 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -583,7 +583,6 @@ static int __allocate_data_block(struct dnode_of_data *dn) struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_summary sum; struct node_info ni; - int seg = CURSEG_WARM_DATA; pgoff_t fofs; blkcnt_t count = 1; @@ -601,11 +600,8 @@ alloc: get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); - if (dn->ofs_in_node == 0 && dn->inode_page == dn->node_page) - seg = CURSEG_DIRECT_IO; - allocate_data_block(sbi, NULL, dn->data_blkaddr, &dn->data_blkaddr, - &sum, seg); + &sum, CURSEG_WARM_DATA); set_data_blkaddr(dn); /* update i_size */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4c896555397f..714673a6397c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -653,7 +653,6 @@ enum { CURSEG_WARM_NODE, /* direct node blocks of normal files */ CURSEG_COLD_NODE, /* indirect node blocks */ NO_CHECK_TYPE, - CURSEG_DIRECT_IO, /* to use for the direct IO path */ }; struct flush_cmd { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4bdf1191a36f..19ab2e63d8d7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1267,25 +1267,21 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi, stat_inc_seg_type(sbi, curseg); } -static void __allocate_new_segments(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - unsigned int old_segno; - - old_segno = curseg->segno; - SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true); - locate_dirty_segment(sbi, old_segno); -} - void allocate_new_segments(struct f2fs_sb_info *sbi) { + struct curseg_info *curseg; + unsigned int old_segno; int i; if (test_opt(sbi, LFS)) return; - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) - __allocate_new_segments(sbi, i); + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { + curseg = CURSEG_I(sbi, i); + old_segno = curseg->segno; + SIT_I(sbi)->s_ops->allocate_segment(sbi, i, true); + locate_dirty_segment(sbi, old_segno); + } } static const struct segment_allocation default_salloc_ops = { @@ -1419,25 +1415,11 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, struct f2fs_summary *sum, int type) { struct sit_info *sit_i = SIT_I(sbi); - struct curseg_info *curseg; - bool direct_io = (type == CURSEG_DIRECT_IO); - - if (direct_io) { - if (sbi->active_logs <= 4) - type = CURSEG_HOT_DATA; - else - type = CURSEG_WARM_DATA; - } - curseg = CURSEG_I(sbi, type); + struct curseg_info *curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); mutex_lock(&sit_i->sentry_lock); - /* direct_io'ed data is aligned to the segment for better performance */ - if (direct_io && curseg->next_blkoff && - !has_not_enough_free_secs(sbi, 0, 0)) - __allocate_new_segments(sbi, type); - *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); /* -- GitLab From 60a71670aeaf61eacd7fedf234cd8f3177358368 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 11 Nov 2016 12:08:22 -0800 Subject: [PATCH 0434/5498] f2fs: allow dio read for LFS mode We can allow dio reads for LFS mode, while doing buffered writes for dio writes. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a811115938de..858e0c045820 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1723,7 +1723,7 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return 0; - if (test_opt(F2FS_I_SB(inode), LFS)) + if (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) return 0; trace_f2fs_direct_IO_enter(inode, offset, count, rw); -- GitLab From 14cb68551047634ee805d950b17aaedafcdc4504 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 6 Oct 2016 19:02:05 -0700 Subject: [PATCH 0435/5498] f2fs: support multiple devices This patch implements multiple devices support for f2fs. Given multiple devices by mkfs.f2fs, f2fs shows them entirely as one big volume under one f2fs instance. Internal block management is very simple, but we will modify block allocation and background GC policy to boost IO speed by exploiting them accoording to each device speed. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c fs/f2fs/segment.c --- fs/f2fs/data.c | 55 ++++++++++++++-- fs/f2fs/f2fs.h | 29 +++++++-- fs/f2fs/segment.c | 112 +++++++++++++++++++++++--------- fs/f2fs/super.c | 138 +++++++++++++++++++++++++++++++--------- include/linux/f2fs_fs.h | 10 ++- 5 files changed, 274 insertions(+), 70 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 858e0c045820..59c6745e024b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -88,6 +88,46 @@ static void f2fs_write_end_io(struct bio *bio, int err) bio_put(bio); } +/* + * Return true, if pre_bio's bdev is same as its target device. + */ +struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi, + block_t blk_addr, struct bio *bio) +{ + struct block_device *bdev = sbi->sb->s_bdev; + int i; + + for (i = 0; i < sbi->s_ndevs; i++) { + if (FDEV(i).start_blk <= blk_addr && + FDEV(i).end_blk >= blk_addr) { + blk_addr -= FDEV(i).start_blk; + bdev = FDEV(i).bdev; + break; + } + } + if (bio) { + bio->bi_bdev = bdev; + bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(blk_addr); + } + return bdev; +} + +int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr) +{ + int i; + + for (i = 0; i < sbi->s_ndevs; i++) + if (FDEV(i).start_blk <= blkaddr && FDEV(i).end_blk >= blkaddr) + return i; + return 0; +} + +static bool __same_bdev(struct f2fs_sb_info *sbi, + block_t blk_addr, struct bio *bio) +{ + return f2fs_target_device(sbi, blk_addr, NULL) == bio->bi_bdev; +} + /* * Low-level block read/write IO operations. */ @@ -98,8 +138,7 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, bio = f2fs_bio_alloc(npages); - bio->bi_bdev = sbi->sb->s_bdev; - bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(blk_addr); + f2fs_target_device(sbi, blk_addr, bio); bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; bio->bi_private = is_read ? NULL : sbi; @@ -269,7 +308,8 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) down_write(&io->io_rwsem); if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 || - io->fio.rw != fio->rw)) + (io->fio.rw != fio->rw) || + !__same_bdev(sbi, fio->new_blkaddr, io->bio))) __submit_merged_bio(io); alloc_new: if (io->bio == NULL) { @@ -956,7 +996,6 @@ static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct fscrypt_ctx *ctx = NULL; - struct block_device *bdev = sbi->sb->s_bdev; struct bio *bio; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { @@ -974,8 +1013,7 @@ static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, fscrypt_release_ctx(ctx); return ERR_PTR(-ENOMEM); } - bio->bi_bdev = bdev; - bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(blkaddr); + f2fs_target_device(sbi, blkaddr, bio); bio->bi_end_io = f2fs_read_end_io; bio->bi_private = ctx; @@ -1069,7 +1107,8 @@ got_it: * This page will go to BIO. Do we need to send this * BIO off first? */ - if (bio && (last_block_in_bio != block_nr - 1)) { + if (bio && (last_block_in_bio != block_nr - 1 || + !__same_bdev(F2FS_I_SB(inode), block_nr, bio))) { submit_and_realloc: __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); bio = NULL; @@ -1725,6 +1764,8 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, return 0; if (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) return 0; + if (F2FS_I_SB(inode)->s_ndevs) + return 0; trace_f2fs_direct_IO_enter(inode, offset, count, rw); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 714673a6397c..cdb5778edfe1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -769,6 +769,20 @@ struct f2fs_bio_info { struct rw_semaphore io_rwsem; /* blocking op for bio */ }; +#define FDEV(i) (sbi->devs[i]) +#define RDEV(i) (raw_super->devs[i]) +struct f2fs_dev_info { + struct block_device *bdev; + char path[MAX_PATH_LEN]; + unsigned int total_segments; + block_t start_blk; + block_t end_blk; +#ifdef CONFIG_BLK_DEV_ZONED + unsigned int nr_blkz; /* Total number of zones */ + u8 *blkz_type; /* Array of zones type */ +#endif +}; + enum inode_type { DIR_INODE, /* for dirty dir inode */ FILE_INODE, /* for dirty regular/symlink inode */ @@ -817,10 +831,8 @@ struct f2fs_sb_info { #endif #ifdef CONFIG_BLK_DEV_ZONED - unsigned int nr_blkz; /* Total number of zones */ unsigned int blocks_per_blkz; /* F2FS blocks per zone */ unsigned int log_blocks_per_blkz; /* log2 F2FS blocks per zone */ - u8 *blkz_type; /* Array of zones type */ #endif /* for node-related operations */ @@ -937,6 +949,8 @@ struct f2fs_sb_info { /* For shrinker support */ struct list_head s_list; + int s_ndevs; /* number of devices */ + struct f2fs_dev_info *devs; /* for device list */ struct mutex umount_mutex; unsigned int shrinker_run_no; @@ -2205,6 +2219,9 @@ void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *, void f2fs_flush_merged_bios(struct f2fs_sb_info *); int f2fs_submit_page_bio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *); +struct block_device *f2fs_target_device(struct f2fs_sb_info *, + block_t, struct bio *); +int f2fs_target_device_index(struct f2fs_sb_info *, block_t); void set_data_blkaddr(struct dnode_of_data *); void f2fs_update_data_blkaddr(struct dnode_of_data *, block_t); int reserve_new_blocks(struct dnode_of_data *, blkcnt_t); @@ -2488,11 +2505,15 @@ static inline int f2fs_sb_mounted_blkzoned(struct super_block *sb) #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, - block_t blkaddr) + struct block_device *bdev, block_t blkaddr) { unsigned int zno = blkaddr >> sbi->log_blocks_per_blkz; + int i; - return sbi->blkz_type[zno]; + for (i = 0; i < sbi->s_ndevs; i++) + if (FDEV(i).bdev == bdev) + return FDEV(i).blkz_type[zno]; + return -EINVAL; } #endif diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 19ab2e63d8d7..30d0e9a76c62 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -401,6 +401,32 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) } } +static int __submit_flush_wait(struct block_device *bdev) +{ + struct bio *bio = f2fs_bio_alloc(0); + int ret; + + bio->bi_bdev = bdev; + ret = submit_bio_wait(WRITE_FLUSH, bio); + bio_put(bio); + return ret; +} + +static int submit_flush_wait(struct f2fs_sb_info *sbi) +{ + int ret = __submit_flush_wait(sbi->sb->s_bdev); + int i; + + if (sbi->s_ndevs && !ret) { + for (i = 1; i < sbi->s_ndevs; i++) { + ret = __submit_flush_wait(FDEV(i).bdev); + if (ret) + break; + } + } + return ret; +} + static int issue_flush_thread(void *data) { struct f2fs_sb_info *sbi = data; @@ -411,24 +437,18 @@ repeat: return 0; if (!llist_empty(&fcc->issue_list)) { - struct bio *bio; struct flush_cmd *cmd, *next; int ret; - bio = f2fs_bio_alloc(0); - fcc->dispatch_list = llist_del_all(&fcc->issue_list); fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); - bio->bi_bdev = sbi->sb->s_bdev; - ret = submit_bio_wait(WRITE_FLUSH, bio); - + ret = submit_flush_wait(sbi); llist_for_each_entry_safe(cmd, next, fcc->dispatch_list, llnode) { cmd->ret = ret; complete(&cmd->wait); } - bio_put(bio); fcc->dispatch_list = NULL; } @@ -449,14 +469,11 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) return 0; if (!test_opt(sbi, FLUSH_MERGE) || !atomic_read(&fcc->submit_flush)) { - struct bio *bio = f2fs_bio_alloc(0); int ret; atomic_inc(&fcc->submit_flush); - bio->bi_bdev = sbi->sb->s_bdev; - ret = submit_bio_wait(WRITE_FLUSH, bio); + ret = submit_flush_wait(sbi); atomic_dec(&fcc->submit_flush); - bio_put(bio); return ret; } @@ -586,18 +603,24 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) } #ifdef CONFIG_BLK_DEV_ZONED -static int f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, - block_t blkstart, block_t blklen) +static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t blkstart, block_t blklen) { - sector_t sector = SECTOR_FROM_BLOCK(blkstart); sector_t nr_sects = SECTOR_FROM_BLOCK(blklen); - struct block_device *bdev = sbi->sb->s_bdev; + sector_t sector; + int devi = 0; - if (nr_sects != bdev_zone_size(bdev)) { + if (sbi->s_ndevs) { + devi = f2fs_target_device_index(sbi, blkstart); + blkstart -= FDEV(devi).start_blk; + } + sector = SECTOR_FROM_BLOCK(blkstart); + + if (sector % bdev_zone_size(bdev) || nr_sects != bdev_zone_size(bdev)) { f2fs_msg(sbi->sb, KERN_INFO, - "Unaligned discard attempted (sector %llu + %llu)", - (unsigned long long)sector, - (unsigned long long)nr_sects); + "(%d) %s: Unaligned discard attempted (block %x + %x)", + devi, sbi->s_ndevs ? FDEV(devi).path: "", + blkstart, blklen); return -EIO; } @@ -606,7 +629,7 @@ static int f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, * use regular discard if the drive supports it. For sequential * zones, reset the zone write pointer. */ - switch (get_blkz_type(sbi, blkstart)) { + switch (get_blkz_type(sbi, bdev, blkstart)) { case BLK_ZONE_TYPE_CONVENTIONAL: if (!blk_queue_discard(bdev_get_queue(bdev))) @@ -625,29 +648,60 @@ static int f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, } #endif -static int f2fs_issue_discard(struct f2fs_sb_info *sbi, - block_t blkstart, block_t blklen) +static int __issue_discard_async(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t blkstart, block_t blklen) { sector_t start = SECTOR_FROM_BLOCK(blkstart); sector_t len = SECTOR_FROM_BLOCK(blklen); + +#ifdef CONFIG_BLK_DEV_ZONED + if (f2fs_sb_mounted_blkzoned(sbi->sb) && + bdev_zoned_model(bdev) != BLK_ZONED_NONE) + return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); +#endif + return blkdev_issue_discard(bdev, start, len, GFP_NOFS, 0); +} + +static int f2fs_issue_discard(struct f2fs_sb_info *sbi, + block_t blkstart, block_t blklen) +{ + sector_t start = blkstart, len = 0; + struct block_device *bdev; struct seg_entry *se; unsigned int offset; block_t i; + int err = 0; + + bdev = f2fs_target_device(sbi, blkstart, NULL); + + for (i = blkstart; i < blkstart + blklen; i++, len++) { + if (i != start) { + struct block_device *bdev2 = + f2fs_target_device(sbi, i, NULL); + + if (bdev2 != bdev) { + err = __issue_discard_async(sbi, bdev, + start, len); + if (err) + return err; + bdev = bdev2; + start = i; + len = 0; + } + } - for (i = blkstart; i < blkstart + blklen; i++) { se = get_seg_entry(sbi, GET_SEGNO(sbi, i)); offset = GET_BLKOFF_FROM_SEG0(sbi, i); if (!f2fs_test_and_set_bit(offset, se->discard_map)) sbi->discard_blks--; } - trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); -#ifdef CONFIG_BLK_DEV_ZONED - if (f2fs_sb_mounted_blkzoned(sbi->sb)) - return f2fs_issue_discard_zone(sbi, blkstart, blklen); -#endif - return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); + if (len) + err = __issue_discard_async(sbi, bdev, start, len); + + trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); + return err; } static void __add_discard_entry(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3e761a2e27ee..ca4ea5772bb3 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -700,6 +700,19 @@ static void destroy_percpu_info(struct f2fs_sb_info *sbi) percpu_counter_destroy(&sbi->total_valid_inode_count); } +static void destroy_device_list(struct f2fs_sb_info *sbi) +{ + int i; + + for (i = 0; i < sbi->s_ndevs; i++) { + blkdev_put(FDEV(i).bdev, FMODE_EXCL); +#ifdef CONFIG_BLK_DEV_ZONED + kfree(FDEV(i).blkz_type); +#endif + } + kfree(sbi->devs); +} + static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -760,6 +773,8 @@ static void f2fs_put_super(struct super_block *sb) crypto_free_shash(sbi->s_chksum_driver); kfree(sbi->raw_super); + destroy_device_list(sbi); + destroy_percpu_info(sbi); kfree(sbi); } @@ -1503,9 +1518,9 @@ static int init_percpu_info(struct f2fs_sb_info *sbi) } #ifdef CONFIG_BLK_DEV_ZONED -static int init_blkz_info(struct f2fs_sb_info *sbi) +static int init_blkz_info(struct f2fs_sb_info *sbi, int devi) { - struct block_device *bdev = sbi->sb->s_bdev; + struct block_device *bdev = FDEV(devi).bdev; sector_t nr_sectors = bdev->bd_part->nr_sects; sector_t sector = 0; struct blk_zone *zones; @@ -1516,15 +1531,21 @@ static int init_blkz_info(struct f2fs_sb_info *sbi) if (!f2fs_sb_mounted_blkzoned(sbi->sb)) return 0; + if (sbi->blocks_per_blkz && sbi->blocks_per_blkz != + SECTOR_TO_BLOCK(bdev_zone_size(bdev))) + return -EINVAL; sbi->blocks_per_blkz = SECTOR_TO_BLOCK(bdev_zone_size(bdev)); + if (sbi->log_blocks_per_blkz && sbi->log_blocks_per_blkz != + __ilog2_u32(sbi->blocks_per_blkz)) + return -EINVAL; sbi->log_blocks_per_blkz = __ilog2_u32(sbi->blocks_per_blkz); - sbi->nr_blkz = SECTOR_TO_BLOCK(nr_sectors) >> - sbi->log_blocks_per_blkz; + FDEV(devi).nr_blkz = SECTOR_TO_BLOCK(nr_sectors) >> + sbi->log_blocks_per_blkz; if (nr_sectors & (bdev_zone_size(bdev) - 1)) - sbi->nr_blkz++; + FDEV(devi).nr_blkz++; - sbi->blkz_type = kmalloc(sbi->nr_blkz, GFP_KERNEL); - if (!sbi->blkz_type) + FDEV(devi).blkz_type = kmalloc(FDEV(devi).nr_blkz, GFP_KERNEL); + if (!FDEV(devi).blkz_type) return -ENOMEM; #define F2FS_REPORT_NR_ZONES 4096 @@ -1549,7 +1570,7 @@ static int init_blkz_info(struct f2fs_sb_info *sbi) } for (i = 0; i < nr_zones; i++) { - sbi->blkz_type[n] = zones[i].type; + FDEV(devi).blkz_type[n] = zones[i].type; sector += zones[i].len; n++; } @@ -1653,6 +1674,77 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) return err; } +static int f2fs_scan_devices(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + int i; + + for (i = 0; i < MAX_DEVICES; i++) { + if (!RDEV(i).path[0]) + return 0; + + if (i == 0) { + sbi->devs = kzalloc(sizeof(struct f2fs_dev_info) * + MAX_DEVICES, GFP_KERNEL); + if (!sbi->devs) + return -ENOMEM; + } + + memcpy(FDEV(i).path, RDEV(i).path, MAX_PATH_LEN); + FDEV(i).total_segments = le32_to_cpu(RDEV(i).total_segments); + if (i == 0) { + FDEV(i).start_blk = 0; + FDEV(i).end_blk = FDEV(i).start_blk + + (FDEV(i).total_segments << + sbi->log_blocks_per_seg) - 1 + + le32_to_cpu(raw_super->segment0_blkaddr); + } else { + FDEV(i).start_blk = FDEV(i - 1).end_blk + 1; + FDEV(i).end_blk = FDEV(i).start_blk + + (FDEV(i).total_segments << + sbi->log_blocks_per_seg) - 1; + } + + FDEV(i).bdev = blkdev_get_by_path(FDEV(i).path, + sbi->sb->s_mode, sbi->sb->s_type); + if (IS_ERR(FDEV(i).bdev)) + return PTR_ERR(FDEV(i).bdev); + + /* to release errored devices */ + sbi->s_ndevs = i + 1; + +#ifdef CONFIG_BLK_DEV_ZONED + if (bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HM && + !f2fs_sb_mounted_blkzoned(sbi->sb)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Zoned block device feature not enabled\n"); + return -EINVAL; + } + if (bdev_zoned_model(FDEV(i).bdev) != BLK_ZONED_NONE) { + if (init_blkz_info(sbi, i)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Failed to initialize F2FS blkzone information"); + return -EINVAL; + } + f2fs_msg(sbi->sb, KERN_INFO, + "Mount Device [%2d]: %20s, %8u, %8x - %8x (zone: %s)", + i, FDEV(i).path, + FDEV(i).total_segments, + FDEV(i).start_blk, FDEV(i).end_blk, + bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HA ? + "Host-aware" : "Host-managed"); + continue; + } +#endif + f2fs_msg(sbi->sb, KERN_INFO, + "Mount Device [%2d]: %20s, %8u, %8x - %8x", + i, FDEV(i).path, + FDEV(i).total_segments, + FDEV(i).start_blk, FDEV(i).end_blk); + } + return 0; +} + static int f2fs_fill_super(struct super_block *sb, void *data, int silent) { struct f2fs_sb_info *sbi; @@ -1711,15 +1803,7 @@ try_onemore: "Zoned block device support is not enabled\n"); goto free_sb_buf; } -#else - if (bdev_zoned_model(sb->s_bdev) == BLK_ZONED_HM && - !f2fs_sb_mounted_blkzoned(sb)) { - f2fs_msg(sb, KERN_ERR, - "Zoned block device feature not enabled\n"); - goto free_sb_buf; - } #endif - default_options(sbi); /* parse mount options */ options = kstrdup((const char *)data, GFP_KERNEL); @@ -1789,6 +1873,13 @@ try_onemore: goto free_meta_inode; } + /* Initialize device list */ + err = f2fs_scan_devices(sbi); + if (err) { + f2fs_msg(sb, KERN_ERR, "Failed to find devices"); + goto free_devices; + } + sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count); percpu_counter_set(&sbi->total_valid_inode_count, @@ -1807,15 +1898,6 @@ try_onemore: init_ino_entry_info(sbi); -#ifdef CONFIG_BLK_DEV_ZONED - err = init_blkz_info(sbi); - if (err) { - f2fs_msg(sb, KERN_ERR, - "Failed to initialize F2FS blkzone information"); - goto free_blkz; - } -#endif - /* setup f2fs internal modules */ err = build_segment_manager(sbi); if (err) { @@ -1994,10 +2076,8 @@ free_nm: destroy_node_manager(sbi); free_sm: destroy_segment_manager(sbi); -#ifdef CONFIG_BLK_DEV_ZONED -free_blkz: - kfree(sbi->blkz_type); -#endif +free_devices: + destroy_device_list(sbi); kfree(sbi->ckpt); free_meta_inode: make_bad_inode(sbi->meta_inode); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 5675ff23e988..083172fed2b7 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -52,10 +52,17 @@ #define VERSION_LEN 256 #define MAX_VOLUME_NAME 512 +#define MAX_PATH_LEN 64 +#define MAX_DEVICES 8 /* * For superblock */ +struct f2fs_device { + __u8 path[MAX_PATH_LEN]; + __le32 total_segments; +} __packed; + struct f2fs_super_block { __le32 magic; /* Magic Number */ __le16 major_ver; /* Major Version */ @@ -94,7 +101,8 @@ struct f2fs_super_block { __le32 feature; /* defined features */ __u8 encryption_level; /* versioning level for encryption */ __u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */ - __u8 reserved[871]; /* valid reserved region */ + struct f2fs_device devs[MAX_DEVICES]; /* device list */ + __u8 reserved[327]; /* valid reserved region */ } __packed; /* -- GitLab From c27569d40ba41d714708426f5bd8c5c1c7f3e04b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 11 Nov 2016 16:31:56 -0800 Subject: [PATCH 0436/5498] f2fs: use err for f2fs_preallocate_blocks This patch has no functional change. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c fs/f2fs/f2fs.h fs/f2fs/file.c --- fs/f2fs/data.c | 26 +++++++++++++------------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 42 ++++++++++++++++++++++++++---------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 59c6745e024b..f5690c24aa6d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -653,11 +653,11 @@ alloc: return 0; } -ssize_t f2fs_preallocate_blocks(struct inode *inode, loff_t pos, +int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, size_t count, bool dio) { struct f2fs_map_blocks map; - ssize_t ret = 0; + int err = 0; map.m_lblk = F2FS_BLK_ALIGN(pos); map.m_len = F2FS_BYTES_TO_BLK(pos + count); @@ -669,19 +669,19 @@ ssize_t f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_next_pgofs = NULL; if (dio) { - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; + err = f2fs_convert_inline_inode(inode); + if (err) + return err; return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO); } if (pos + count > MAX_INLINE_DATA) { - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; + err = f2fs_convert_inline_inode(inode); + if (err) + return err; } if (!f2fs_has_inline_data(inode)) return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); - return ret; + return err; } /* @@ -858,19 +858,19 @@ static int __get_data_block(struct inode *inode, sector_t iblock, pgoff_t *next_pgofs) { struct f2fs_map_blocks map; - int ret; + int err; map.m_lblk = iblock; map.m_len = bh->b_size >> inode->i_blkbits; map.m_next_pgofs = next_pgofs; - ret = f2fs_map_blocks(inode, &map, create, flag); - if (!ret) { + err = f2fs_map_blocks(inode, &map, create, flag); + if (!err) { map_bh(bh, inode->i_sb, map.m_pblk); bh->b_state = (bh->b_state & ~F2FS_MAP_FLAGS) | map.m_flags; bh->b_size = map.m_len << inode->i_blkbits; } - return ret; + return err; } static int get_data_block(struct inode *inode, sector_t iblock, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cdb5778edfe1..a91dd3bd5488 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2227,7 +2227,7 @@ void f2fs_update_data_blkaddr(struct dnode_of_data *, block_t); int reserve_new_blocks(struct dnode_of_data *, blkcnt_t); int reserve_new_block(struct dnode_of_data *); int f2fs_get_block(struct dnode_of_data *, pgoff_t); -ssize_t f2fs_preallocate_blocks(struct inode *, loff_t, size_t, bool); +int f2fs_preallocate_blocks(struct inode *, loff_t, size_t, bool); int f2fs_reserve_block(struct dnode_of_data *, pgoff_t); struct page *get_read_data_page(struct inode *, pgoff_t, int, bool); struct page *find_data_page(struct inode *, pgoff_t); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 77e9af2ed341..c9fe1d76a45a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1325,15 +1325,15 @@ static int expand_inode_data(struct inode *inode, loff_t offset, pgoff_t pg_end; loff_t new_size = i_size_read(inode); loff_t off_end; - int ret; + int err; - ret = inode_newsize_ok(inode, (len + offset)); - if (ret) - return ret; + err = inode_newsize_ok(inode, (len + offset)); + if (err) + return err; - ret = f2fs_convert_inline_inode(inode); - if (ret) - return ret; + err = f2fs_convert_inline_inode(inode); + if (err) + return err; f2fs_balance_fs(sbi, true); @@ -1345,12 +1345,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset, if (off_end) map.m_len++; - ret = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); - if (ret) { + err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); + if (err) { pgoff_t last_off; if (!map.m_len) - return ret; + return err; last_off = map.m_lblk + map.m_len - 1; @@ -1364,10 +1364,18 @@ static int expand_inode_data(struct inode *inode, loff_t offset, if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) f2fs_i_size_write(inode, new_size); - return ret; + return err; } +#ifndef FALLOC_FL_COLLAPSE_RANGE +#define FALLOC_FL_COLLAPSE_RANGE 0X08 +#endif +#ifndef FALLOC_FL_ZERO_RANGE +#define FALLOC_FL_ZERO_RANGE 0X10 +#endif +#ifndef FALLOC_FL_INSERT_RANGE #define FALLOC_FL_INSERT_RANGE 0X20 +#endif static long f2fs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) @@ -2279,13 +2287,15 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) inode_lock(inode); ret = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (!ret) { - ret = f2fs_preallocate_blocks(inode, pos, count, + int err = f2fs_preallocate_blocks(inode, pos, count, file->f_flags & O_DIRECT); - if (!ret) { - blk_start_plug(&plug); - ret = __generic_file_write_iter(iocb, from); - blk_finish_plug(&plug); + if (err) { + inode_unlock(inode); + return err; } + blk_start_plug(&plug); + ret = __generic_file_write_iter(iocb, from); + blk_finish_plug(&plug); } inode_unlock(inode); -- GitLab From 61535f86098bacccbd7250f8b0c5bf8f98a71645 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 11 Nov 2016 16:46:40 -0800 Subject: [PATCH 0437/5498] f2fs: fix redundant block allocation In direct_IO path of f2fs_file_write_iter(), 1. f2fs_preallocate_blocks(F2FS_GET_BLOCK_PRE_DIO) -> allocate LBA X 2. f2fs_direct_IO() -> return 0; Then, f2fs_write_data_page() will allocate another LBA X+1. This makes EIO triggered by HM-SMR. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f5690c24aa6d..d1fb591824c2 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -653,6 +653,13 @@ alloc: return 0; } +static inline bool __force_buffered_io(struct inode *inode, int rw) +{ + return ((f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) || + (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) || + F2FS_I_SB(inode)->s_ndevs); +} + int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, size_t count, bool dio) { @@ -672,7 +679,10 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, err = f2fs_convert_inline_inode(inode); if (err) return err; - return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO); + return f2fs_map_blocks(inode, &map, 1, + __force_buffered_io(inode, WRITE) ? + F2FS_GET_BLOCK_PRE_AIO : + F2FS_GET_BLOCK_PRE_DIO); } if (pos + count > MAX_INLINE_DATA) { err = f2fs_convert_inline_inode(inode); @@ -1760,11 +1770,7 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, if (err) return err; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) - return 0; - if (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) - return 0; - if (F2FS_I_SB(inode)->s_ndevs) + if (__force_buffered_io(inode, rw)) return 0; trace_f2fs_direct_IO_enter(inode, offset, count, rw); -- GitLab From b30bfc5f5ee6ad3734ce7ff9425b7c9eae73dadd Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 14 Nov 2016 17:38:35 -0800 Subject: [PATCH 0438/5498] f2fs: avoid BG_GC in f2fs_balance_fs If many threads hit has_not_enough_free_secs() in f2fs_balance_fs() at the same time, all the threads would do FG_GC or BG_GC. In this critical path, we totally don't need to do BG_GC at all. Let's avoid that. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 7 +++++-- fs/f2fs/segment.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a91dd3bd5488..a3a5a6b9c32d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2246,7 +2246,7 @@ int f2fs_release_page(struct page *, gfp_t); int start_gc_thread(struct f2fs_sb_info *); void stop_gc_thread(struct f2fs_sb_info *); block_t start_bidx_of_node(unsigned int, struct inode *); -int f2fs_gc(struct f2fs_sb_info *, bool); +int f2fs_gc(struct f2fs_sb_info *, bool, bool); void build_gc_manager(struct f2fs_sb_info *); /* diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c9fe1d76a45a..a4acc205571a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1871,7 +1871,7 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg) mutex_lock(&sbi->gc_mutex); } - ret = f2fs_gc(sbi, sync); + ret = f2fs_gc(sbi, sync, true); out: mnt_drop_write_file(filp); return ret; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 744031194934..54d06c21af07 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -82,7 +82,7 @@ static int gc_thread_func(void *data) stat_inc_bggc_count(sbi); /* if return value is not zero, no victim was selected */ - if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC))) + if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true)) wait_ms = gc_th->no_gc_sleep_time; trace_f2fs_background_gc(sbi->sb, wait_ms, @@ -905,7 +905,7 @@ next: return sec_freed; } -int f2fs_gc(struct f2fs_sb_info *sbi, bool sync) +int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background) { unsigned int segno; int gc_type = sync ? FG_GC : BG_GC; @@ -946,6 +946,9 @@ gc_more: if (ret) goto stop; } + } else if (gc_type == BG_GC && !background) { + /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */ + goto stop; } if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type)) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 30d0e9a76c62..27e1b7c56e4c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -364,7 +364,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) */ if (has_not_enough_free_secs(sbi, 0, 0)) { mutex_lock(&sbi->gc_mutex); - f2fs_gc(sbi, false); + f2fs_gc(sbi, false, false); } } -- GitLab From f524449e022e30ddbe3a9ddc7e011fa56804c829 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 14 Nov 2016 18:20:10 -0800 Subject: [PATCH 0439/5498] f2fs: fix wrong written_valid_blocks counting Previously, written_valid_blocks was got by ckpt->valid_block_count. But if the last checkpoint has some NEW_ADDR due to power-cut, we can get wrong value. Fix it to get the number from actual written block count from sit entries. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 27e1b7c56e4c..58cae4a541a7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2176,7 +2176,6 @@ out: static int build_sit_info(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct sit_info *sit_i; unsigned int sit_segs, start; char *src_bitmap, *dst_bitmap; @@ -2243,7 +2242,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; - sit_i->written_valid_blocks = le64_to_cpu(ckpt->valid_block_count); + sit_i->written_valid_blocks = 0; sit_i->sit_bitmap = dst_bitmap; sit_i->bitmap_size = bitmap_size; sit_i->dirty_sentries = 0; @@ -2397,6 +2396,9 @@ static void init_free_segmap(struct f2fs_sb_info *sbi) struct seg_entry *sentry = get_seg_entry(sbi, start); if (!sentry->valid_blocks) __set_free(sbi, start); + else + SIT_I(sbi)->written_valid_blocks += + sentry->valid_blocks; } /* set use the current segments */ -- GitLab From 15a5af1bb0fb8cdace395eb0bd7e5d8cbfff9d38 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 16 Nov 2016 10:41:20 +0800 Subject: [PATCH 0440/5498] f2fs: don't wait writeback for datas during checkpoint Normally, while committing checkpoint, we will wait on all pages to be writebacked no matter the page is data or metadata, so in scenario where there are lots of data IO being submitted with metadata, we may suffer long latency for waiting writeback during checkpoint. Indeed, we only care about persistence for pages with metadata, but not pages with data, as file system consistent are only related to metadate, so in order to avoid encountering long latency in above scenario, let's recognize and reference metadata in submitted IOs, wait writeback only for metadatas. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 35 +++++++++++++++++++++++++++++------ fs/f2fs/debug.c | 7 ++++--- fs/f2fs/f2fs.h | 9 ++++++--- fs/f2fs/file.c | 2 -- fs/f2fs/gc.c | 2 -- fs/f2fs/segment.c | 1 - 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 37b558acccd4..a457dbdc1691 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1003,7 +1003,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi) for (;;) { prepare_to_wait(&sbi->cp_wait, &wait, TASK_UNINTERRUPTIBLE); - if (!atomic_read(&sbi->nr_wb_bios)) + if (!get_pages(sbi, F2FS_WB_CP_DATA)) break; io_schedule_timeout(5*HZ); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d1fb591824c2..8af62ec9ccca 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -30,6 +30,26 @@ #include "trace.h" #include +static bool __is_cp_guaranteed(struct page *page) +{ + struct address_space *mapping = page->mapping; + struct inode *inode; + struct f2fs_sb_info *sbi; + + if (!mapping) + return false; + + inode = mapping->host; + sbi = F2FS_I_SB(inode); + + if (inode->i_ino == F2FS_META_INO(sbi) || + inode->i_ino == F2FS_NODE_INO(sbi) || + S_ISDIR(inode->i_mode) || + is_cold_data(page)) + return true; + return false; +} + static void f2fs_read_end_io(struct bio *bio, int err) { struct bio_vec *bvec; @@ -72,6 +92,7 @@ static void f2fs_write_end_io(struct bio *bio, int err) bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; + enum count_type type = WB_DATA_TYPE(page); fscrypt_pullback_bio_page(&page, true); @@ -79,9 +100,11 @@ static void f2fs_write_end_io(struct bio *bio, int err) set_bit(AS_EIO, &page->mapping->flags); f2fs_stop_checkpoint(sbi, true); } + dec_page_count(sbi, type); + clear_cold_data(page); end_page_writeback(page); } - if (atomic_dec_and_test(&sbi->nr_wb_bios) && + if (!get_pages(sbi, F2FS_WB_CP_DATA) && wq_has_sleeper(&sbi->cp_wait)) wake_up(&sbi->cp_wait); @@ -149,7 +172,6 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, struct bio *bio, enum page_type type) { if (!is_read_io(rw)) { - atomic_inc(&sbi->nr_wb_bios); if (f2fs_sb_mounted_blkzoned(sbi->sb) && current->plug && (type == DATA || type == NODE)) blk_finish_plug(current->plug); @@ -305,6 +327,11 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) verify_block_addr(sbi, fio->old_blkaddr); verify_block_addr(sbi, fio->new_blkaddr); + bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; + + if (!is_read) + inc_page_count(sbi, WB_DATA_TYPE(bio_page)); + down_write(&io->io_rwsem); if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 || @@ -318,8 +345,6 @@ alloc_new: io->fio = *fio; } - bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; - if (bio_add_page(io->bio, bio_page, PAGE_SIZE, 0) < PAGE_SIZE) { __submit_merged_bio(io); @@ -1331,7 +1356,6 @@ done: if (err && err != -ENOENT) goto redirty_out; - clear_cold_data(page); out: inode_dec_dirty_pages(inode); if (err) @@ -1734,7 +1758,6 @@ static int f2fs_write_end(struct file *file, goto unlock_out; set_page_dirty(page); - clear_cold_data(page); if (pos + copied > i_size_read(inode)) f2fs_i_size_write(inode, pos + copied); diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 79f5b5a4e428..c20c7b403a20 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -50,7 +50,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); - si->wb_bios = atomic_read(&sbi->nr_wb_bios); + si->nr_wb_cp_data = get_pages(sbi, F2FS_WB_CP_DATA); + si->nr_wb_data = get_pages(sbi, F2FS_WB_DATA); si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->rsvd_segs = reserved_segments(sbi); si->overp_segs = overprovision_segments(sbi); @@ -315,8 +316,8 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - inmem: %4d, wb_bios: %4d\n", - si->inmem_pages, si->wb_bios); + seq_printf(s, " - inmem: %4d, wb_cp_data: %4d, wb_data: %4d\n", + si->inmem_pages, si->nr_wb_cp_data, si->nr_wb_data); seq_printf(s, " - nodes: %4d in %4d\n", si->ndirty_node, si->node_pages); seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a3a5a6b9c32d..f5d07f1c1612 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -715,6 +715,7 @@ struct f2fs_sm_info { * f2fs monitors the number of several block types such as on-writeback, * dirty dentry blocks, dirty node blocks, and dirty meta blocks. */ +#define WB_DATA_TYPE(p) (__is_cp_guaranteed(p) ? F2FS_WB_CP_DATA : F2FS_WB_DATA) enum count_type { F2FS_DIRTY_DENTS, F2FS_DIRTY_DATA, @@ -722,6 +723,8 @@ enum count_type { F2FS_DIRTY_META, F2FS_INMEM_PAGES, F2FS_DIRTY_IMETA, + F2FS_WB_CP_DATA, + F2FS_WB_DATA, NR_COUNT_TYPE, }; @@ -901,7 +904,6 @@ struct f2fs_sb_info { block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ u32 s_next_generation; /* for NFS support */ - atomic_t nr_wb_bios; /* # of writeback bios */ /* # of pages, see count_type */ atomic_t nr_pages[NR_COUNT_TYPE]; @@ -1315,7 +1317,8 @@ static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) { atomic_inc(&sbi->nr_pages[count_type]); - if (count_type == F2FS_DIRTY_DATA || count_type == F2FS_INMEM_PAGES) + if (count_type == F2FS_DIRTY_DATA || count_type == F2FS_INMEM_PAGES || + count_type == F2FS_WB_CP_DATA || count_type == F2FS_WB_DATA) return; set_sbi_flag(sbi, SBI_IS_DIRTY); @@ -2272,7 +2275,7 @@ struct f2fs_stat_info { unsigned int ndirty_dirs, ndirty_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits, free_nids, alloc_nids; int total_count, utilization; - int bg_gc, wb_bios; + int bg_gc, nr_wb_cp_data, nr_wb_data; int inline_xattr, inline_inode, inline_dir, orphans; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; unsigned int bimodal, avg_vblocks; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a4acc205571a..434dab44cb96 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -96,8 +96,6 @@ mapped: if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); - /* if gced page is attached, don't write to cold segment */ - clear_cold_data(page); out: sb_end_pagefault(inode->i_sb); f2fs_update_time(sbi, REQ_TIME); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 54d06c21af07..6390d45c1b68 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -690,8 +690,6 @@ retry: congestion_wait(BLK_RW_ASYNC, HZ/50); goto retry; } - - clear_cold_data(page); } out: f2fs_put_page(page, 1); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 58cae4a541a7..23e8892c4e60 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -288,7 +288,6 @@ static int __commit_inmem_pages(struct inode *inode, /* record old blkaddr for revoking */ cur->old_addr = fio.old_blkaddr; - clear_cold_data(page); submit_bio = true; } unlock_page(page); -- GitLab From bb56888f61ae9496666aa9fbb9314fd1819e8f7b Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 16 Nov 2016 17:26:24 +0800 Subject: [PATCH 0441/5498] f2fs: fix an infinite loop when flush nodes in cp Thread A Thread B - write_checkpoint - block_operations -blk_start_plug -sync_node_pages - f2fs_do_sync_file - fsync_node_pages - f2fs_wait_on_page_writeback Thread A wait for global F2FS_DIRTY_NODES decreased to zero, it start a plug list, some requests have been added to this list. Thread B lock one dirty node page, and wait this page write back. But this page has been in plug list of thread A with PG_writeback flag. Thread A keep on running and its plug list has no chance to finish, so it seems a deadlock between cp and fsync path. This patch add a wait on page write back before set node page dirty to avoid this problem. Signed-off-by: Yunlei He Signed-off-by: Pengyang Hou Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d3b5aa81fde2..eb30486bc077 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1415,6 +1415,7 @@ continue_unlock: "Retry to write fsync mark: ino=%u, idx=%lx", ino, last_page->index); lock_page(last_page); + f2fs_wait_on_page_writeback(last_page, NODE, true); set_page_dirty(last_page); unlock_page(last_page); goto retry; -- GitLab From 87d137d2bfa7a40db2f8c3442a0e5b42e3b071fa Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 17 Nov 2016 20:53:11 +0800 Subject: [PATCH 0442/5498] f2fs: fix to account total free nid correctly Thread A Thread B Thread C - f2fs_create - f2fs_new_inode - f2fs_lock_op - alloc_nid alloc last nid - f2fs_unlock_op - f2fs_create - f2fs_new_inode - f2fs_lock_op - alloc_nid as node count still not be increased, we will loop in alloc_nid - f2fs_write_node_pages - f2fs_balance_fs_bg - f2fs_sync_fs - write_checkpoint - block_operations - f2fs_lock_all - f2fs_lock_op While creating new inode, we do not allocate and account nid atomically, so that when there is almost no free nids left, we may encounter deadloop like above stack. In order to avoid that, reuse nm_i::available_nids for accounting free nids and make nid allocation and counting being atomical during node creation. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f5d07f1c1612..46d2b2ac4d57 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -574,7 +574,7 @@ enum nid_list { struct f2fs_nm_info { block_t nat_blkaddr; /* base disk address of NAT */ nid_t max_nid; /* maximum possible node ids */ - nid_t available_nids; /* maximum available node ids */ + nid_t available_nids; /* # of available node ids */ nid_t next_scan_nid; /* the next nid to be scanned */ unsigned int ram_thresh; /* control the memory footprint */ unsigned int ra_nid_pages; /* # of nid pages to be readaheaded */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index eb30486bc077..cc60f4d1d6be 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1888,11 +1888,13 @@ retry: if (time_to_inject(sbi, FAULT_ALLOC_NID)) return false; #endif - if (unlikely(sbi->total_valid_node_count + 1 > nm_i->available_nids)) - return false; - spin_lock(&nm_i->nid_list_lock); + if (unlikely(nm_i->available_nids == 0)) { + spin_unlock(&nm_i->nid_list_lock); + return false; + } + /* We should not use stale free nids created by build_free_nids */ if (nm_i->nid_cnt[FREE_NID_LIST] && !on_build_free_nids(nm_i)) { f2fs_bug_on(sbi, list_empty(&nm_i->nid_list[FREE_NID_LIST])); @@ -1903,6 +1905,7 @@ retry: __remove_nid_from_list(sbi, i, FREE_NID_LIST, true); i->state = NID_ALLOC; __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); + nm_i->available_nids--; spin_unlock(&nm_i->nid_list_lock); return true; } @@ -1954,6 +1957,9 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) i->state = NID_NEW; __insert_nid_to_list(sbi, i, FREE_NID_LIST, false); } + + nm_i->available_nids++; + spin_unlock(&nm_i->nid_list_lock); if (need_free) @@ -2153,6 +2159,19 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) ne = grab_nat_entry(nm_i, nid); node_info_from_raw_nat(&ne->ni, &raw_ne); } + + /* + * if a free nat in journal has not been used after last + * checkpoint, we should remove it from available nids, + * since later we will add it again. + */ + if (!get_nat_flag(ne, IS_DIRTY) && + le32_to_cpu(raw_ne.block_addr) == NULL_ADDR) { + spin_lock(&nm_i->nid_list_lock); + nm_i->available_nids--; + spin_unlock(&nm_i->nid_list_lock); + } + __set_nat_cache_dirty(nm_i, ne); } update_nats_in_cursum(journal, -i); @@ -2225,8 +2244,12 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, raw_nat_from_node_info(raw_ne, &ne->ni); nat_reset_flag(ne); __clear_nat_cache_dirty(NM_I(sbi), ne); - if (nat_get_blkaddr(ne) == NULL_ADDR) + if (nat_get_blkaddr(ne) == NULL_ADDR) { add_free_nid(sbi, nid, false); + spin_lock(&NM_I(sbi)->nid_list_lock); + NM_I(sbi)->available_nids++; + spin_unlock(&NM_I(sbi)->nid_list_lock); + } } if (to_journal) @@ -2301,7 +2324,8 @@ static int init_node_manager(struct f2fs_sb_info *sbi) nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; /* not used nids: 0, node, meta, (and root counted as valid node) */ - nm_i->available_nids = nm_i->max_nid - F2FS_RESERVED_NODE_NUM; + nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count - + F2FS_RESERVED_NODE_NUM; nm_i->nid_cnt[FREE_NID_LIST] = 0; nm_i->nid_cnt[ALLOC_NID_LIST] = 0; nm_i->nat_cnt = 0; -- GitLab From 1e2b3a57b1607c4718bb5c0422617d2628a50339 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 17 Nov 2016 20:53:31 +0800 Subject: [PATCH 0443/5498] f2fs: fix fdatasync For below two cases, we can't guarantee data consistence: a) 1. xfs_io "pwrite 0 4195328" "fsync" 2. xfs_io "pwrite 4195328 1024" "fdatasync" 3. godown 4. umount & mount --> isize we updated before fdatasync won't be recovered b) 1. xfs_io "pwrite -S 0xcc 0 4202496" "fsync" 2. xfs_io "fpunch 4194304 4096" "fdatasync" 3. godown 4. umount & mount --> dnode we punched before fdatasync won't be recovered The reason is that normally fdatasync won't be aware of modification of metadata in file, e.g. isize changing, dnode updating, so in ->fsync we will skip flushing node pages for above cases, result in making fdatasynced file being lost during recovery. Currently we have introduced DIRTY_META global list in sbi for tracking dirty inode selectively, so in fdatasync we can choose to flush nodes depend on dirty state of current inode in the list. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 11 ++++++++++- fs/f2fs/file.c | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 46d2b2ac4d57..eff3db440800 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1776,8 +1776,17 @@ static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) set_inode_flag(inode, FI_AUTO_RECOVER); } -static inline bool f2fs_skip_inode_update(struct inode *inode) +static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) { + if (dsync) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + bool ret; + + spin_lock(&sbi->inode_lock[DIRTY_META]); + ret = list_empty(&F2FS_I(inode)->gdirty_list); + spin_unlock(&sbi->inode_lock[DIRTY_META]); + return ret; + } if (!is_inode_flag_set(inode, FI_AUTO_RECOVER)) return false; return F2FS_I(inode)->last_disk_size == i_size_read(inode); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 434dab44cb96..b6151fb2688b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -210,7 +210,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, } /* if the inode is dirty, let's recover all the time */ - if (!datasync && !f2fs_skip_inode_update(inode)) { + if (!f2fs_skip_inode_update(inode, datasync)) { f2fs_write_inode(inode, NULL); goto go_write; } -- GitLab From 2376445f981bd4a7f48278611626459558b914fb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 16 Nov 2016 15:09:48 -0800 Subject: [PATCH 0444/5498] f2fs: do not recover i_size if it's valid If i_size is already valid during roll_forward recovery, we should not update it according to the block alignment. Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 0e4e12d0ae35..936c0166db59 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -425,7 +425,7 @@ retry_dn: continue; } - if ((start + 1) << PAGE_SHIFT > i_size_read(inode)) + if (i_size_read(inode) <= (start << PAGE_SHIFT)) f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT); /* -- GitLab From 457bc15131ca78ead6dbeda12fc5d068baa9d822 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 16 Nov 2016 18:53:16 -0800 Subject: [PATCH 0445/5498] f2fs: fix wrong AUTO_RECOVER condition If i_size is not aligned to the f2fs's block size, we should not skip inode update during fsync. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index eff3db440800..8934176e33aa 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1787,7 +1787,8 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) spin_unlock(&sbi->inode_lock[DIRTY_META]); return ret; } - if (!is_inode_flag_set(inode, FI_AUTO_RECOVER)) + if (!is_inode_flag_set(inode, FI_AUTO_RECOVER) || + i_size_read(inode) & PAGE_MASK) return false; return F2FS_I(inode)->last_disk_size == i_size_read(inode); } -- GitLab From 92285c2aeb8e0c2d847b561d1dc5ec8393ca1ef7 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Fri, 18 Nov 2016 22:21:13 +0800 Subject: [PATCH 0446/5498] f2fs: drop duplicate header timer.h Drop duplicate header timer.h from segment.c. Signed-off-by: Geliang Tang Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 23e8892c4e60..ba715d60c738 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "f2fs.h" #include "segment.h" -- GitLab From dd1f43cfa3a8e695d3fb6407a94dc472456c981c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 18 Nov 2016 22:27:41 +0800 Subject: [PATCH 0447/5498] f2fs: fix incorrect free inode count in ->statfs While calculating inode count that we can create at most in the left space, we should consider space which data/node blocks occupied, since we create data/node mixly in main area. So fix the wrong calculation in ->statfs. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ca4ea5772bb3..5c51962a43a1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -839,7 +839,8 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) 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_ffree = min(buf->f_files - valid_node_count(sbi), + buf->f_bavail); buf->f_namelen = F2FS_NAME_LEN; buf->f_fsid.val[0] = (u32)id; -- GitLab From f734cecb535a0f455c7fdf8d1e2f6efda0fbe29f Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Sun, 20 Nov 2016 19:57:23 +0100 Subject: [PATCH 0448/5498] f2fs: set ->owner for debugfs status file's file_operations The struct file_operations instance serving the f2fs/status debugfs file lacks an initialization of its ->owner. This means that although that file might have been opened, the f2fs module can still get removed. Any further operation on that opened file, releasing included, will cause accesses to unmapped memory. Indeed, Mike Marshall reported the following: BUG: unable to handle kernel paging request at ffffffffa0307430 IP: [] full_proxy_release+0x24/0x90 <...> Call Trace: [] __fput+0xdf/0x1d0 [] ____fput+0xe/0x10 [] task_work_run+0x8e/0xc0 [] do_exit+0x2ae/0xae0 [] ? __audit_syscall_entry+0xae/0x100 [] ? syscall_trace_enter+0x1ca/0x310 [] do_group_exit+0x44/0xc0 [] SyS_exit_group+0x14/0x20 [] do_syscall_64+0x61/0x150 [] entry_SYSCALL64_slow_path+0x25/0x25 <...> ---[ end trace f22ae883fa3ea6b8 ]--- Fixing recursive fault but reboot is needed! Fix this by initializing the f2fs/status file_operations' ->owner with THIS_MODULE. This will allow debugfs to grab a reference to the f2fs module upon any open on that file, thus preventing it from getting removed. Fixes: 902829aa0b72 ("f2fs: move proc files to debugfs") Reported-by: Mike Marshall Reported-by: Martin Brandenburg Cc: stable@vger.kernel.org Signed-off-by: Nicolai Stange Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index c20c7b403a20..29d8feaf09ee 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -379,6 +379,7 @@ static int stat_open(struct inode *inode, struct file *file) } static const struct file_operations stat_fops = { + .owner = THIS_MODULE, .open = stat_open, .read = seq_read, .llseek = seq_lseek, -- GitLab From 9f89ca0c65d19bba2483da00ad893bced152b7a4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 22 Nov 2016 15:20:16 +0100 Subject: [PATCH 0449/5498] f2fs: fix 32-bit build The addition of multiple-device support broke CONFIG_BLK_DEV_ZONED on 32-bit machines because of a 64-bit division: fs/f2fs/f2fs.o: In function `__issue_discard_async': extent_cache.c:(.text.__issue_discard_async+0xd4): undefined reference to `__aeabi_uldivmod' Fortunately, bdev_zone_size() is guaranteed to return a power-of-two number, so we can replace the % operator with a cheaper bit mask. Fixes: 792b84b74b54 ("f2fs: support multiple devices") Signed-off-by: Arnd Bergmann Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ba715d60c738..4f557e5e789d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -614,7 +614,8 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, } sector = SECTOR_FROM_BLOCK(blkstart); - if (sector % bdev_zone_size(bdev) || nr_sects != bdev_zone_size(bdev)) { + if (sector & (bdev_zone_size(bdev) - 1) || + nr_sects != bdev_zone_size(bdev)) { f2fs_msg(sbi->sb, KERN_INFO, "(%d) %s: Unaligned discard attempted (block %x + %x)", devi, sbi->s_ndevs ? FDEV(devi).path: "", -- GitLab From ff2126c332d9075f6272d7771a819afd299fbda8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 28 Nov 2016 15:33:38 -0800 Subject: [PATCH 0450/5498] f2fs: do not activate auto_recovery for fallocated i_size If a file needs to keep its i_size by fallocate, we need to turn off auto recovery during roll-forward recovery. This will resolve the below scenario. 1. xfs_io -f /mnt/f2fs/file -c "pwrite 0 4096" -c "fsync" 2. xfs_io -f /mnt/f2fs/file -c "falloc -k 4096 4096" -c "fsync" 3. md5sum /mnt/f2fs/file; 4. godown /mnt/f2fs/ 5. umount /mnt/f2fs/ 6. mount -t f2fs /dev/sdx /mnt/f2fs 7. md5sum /mnt/f2fs/file Reported-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 38 +++++++++++++++++++++----------------- fs/f2fs/file.c | 2 ++ fs/f2fs/recovery.c | 11 ++++++++--- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8934176e33aa..a3bfa7316825 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -463,6 +463,7 @@ struct f2fs_map_blocks { #define FADVISE_LOST_PINO_BIT 0x02 #define FADVISE_ENCRYPT_BIT 0x04 #define FADVISE_ENC_NAME_BIT 0x08 +#define FADVISE_KEEP_SIZE_BIT 0x10 #define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT) #define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT) @@ -475,6 +476,8 @@ struct f2fs_map_blocks { #define file_clear_encrypt(inode) clear_file(inode, FADVISE_ENCRYPT_BIT) #define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT) #define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT) +#define file_keep_isize(inode) is_file(inode, FADVISE_KEEP_SIZE_BIT) +#define file_set_keep_isize(inode) set_file(inode, FADVISE_KEEP_SIZE_BIT) #define DEF_DIR_LEVEL 0 @@ -1776,23 +1779,6 @@ static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size) set_inode_flag(inode, FI_AUTO_RECOVER); } -static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) -{ - if (dsync) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - bool ret; - - spin_lock(&sbi->inode_lock[DIRTY_META]); - ret = list_empty(&F2FS_I(inode)->gdirty_list); - spin_unlock(&sbi->inode_lock[DIRTY_META]); - return ret; - } - if (!is_inode_flag_set(inode, FI_AUTO_RECOVER) || - i_size_read(inode) & PAGE_MASK) - return false; - return F2FS_I(inode)->last_disk_size == i_size_read(inode); -} - static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth) { F2FS_I(inode)->i_current_depth = depth; @@ -1945,6 +1931,24 @@ static inline void clear_file(struct inode *inode, int type) f2fs_mark_inode_dirty_sync(inode, true); } +static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) +{ + if (dsync) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + bool ret; + + spin_lock(&sbi->inode_lock[DIRTY_META]); + ret = list_empty(&F2FS_I(inode)->gdirty_list); + spin_unlock(&sbi->inode_lock[DIRTY_META]); + return ret; + } + if (!is_inode_flag_set(inode, FI_AUTO_RECOVER) || + file_keep_isize(inode) || + i_size_read(inode) & PAGE_MASK) + return false; + return F2FS_I(inode)->last_disk_size == i_size_read(inode); +} + static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index b6151fb2688b..c20febdbb6c5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1414,6 +1414,8 @@ static long f2fs_fallocate(struct file *file, int mode, if (!ret) { inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); + if (mode & FALLOC_FL_KEEP_SIZE) + file_set_keep_isize(inode); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 936c0166db59..192da3de4fbf 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -187,6 +187,8 @@ static void recover_inode(struct inode *inode, struct page *page) inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec); inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); + F2FS_I(inode)->i_advise = raw->i_advise; + if (file_enc_name(inode)) name = ""; else @@ -425,7 +427,8 @@ retry_dn: continue; } - if (i_size_read(inode) <= (start << PAGE_SHIFT)) + if (!file_keep_isize(inode) && + (i_size_read(inode) <= (start << PAGE_SHIFT))) f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT); /* @@ -478,8 +481,10 @@ err: f2fs_put_dnode(&dn); out: f2fs_msg(sbi->sb, KERN_NOTICE, - "recover_data: ino = %lx, recovered = %d blocks, err = %d", - inode->i_ino, recovered, err); + "recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d", + inode->i_ino, + file_keep_isize(inode) ? "keep" : "recover", + recovered, err); return err; } -- GitLab From 2d38c8faab671b3dc39f141525008b1e669e9f38 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 28 Nov 2016 19:13:43 -0800 Subject: [PATCH 0451/5498] f2fs: return AOP_WRITEPAGE_ACTIVATE for writepage We should use AOP_WRITEPAGE_ACTIVATE when we bypass writing pages. Signed-off-by: Chao Yu Signed-off-by: Miao Xie Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8af62ec9ccca..340ffd56cafa 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1376,6 +1376,8 @@ out: redirty_out: redirty_page_for_writepage(wbc, page); + if (!err) + return AOP_WRITEPAGE_ACTIVATE; unlock_page(page); return err; } @@ -1471,6 +1473,15 @@ continue_unlock: ret = mapping->a_ops->writepage(page, wbc); if (unlikely(ret)) { + /* + * keep nr_to_write, since vfs uses this to + * get # of written pages. + */ + if (ret == AOP_WRITEPAGE_ACTIVATE) { + unlock_page(page); + ret = 0; + continue; + } done_index = page->index + 1; done = 1; break; -- GitLab From b4a5fa5f75dd83ade1f260817266b8f114383394 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 2 Dec 2016 15:11:32 -0800 Subject: [PATCH 0452/5498] Revert "f2fs: use percpu_counter for # of dirty pages in inode" This reverts commit 1beba1b3a953107c3ff5448ab4e4297db4619c76. The perpcu_counter doesn't provide atomicity in single core and consume more DRAM. That incurs fs_mark test failure due to ENOMEM. Cc: stable@vger.kernel.org # 4.7+ Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 10 +++++----- fs/f2fs/file.c | 2 +- fs/f2fs/super.c | 7 +------ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a3bfa7316825..dd9ba7e203b3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -493,7 +493,7 @@ struct f2fs_inode_info { /* Use below internally in f2fs*/ unsigned long flags; /* use to pass per-file flags */ struct rw_semaphore i_sem; /* protect fi info */ - struct percpu_counter dirty_pages; /* # of dirty pages */ + atomic_t dirty_pages; /* # of dirty pages */ f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ nid_t i_xattr_nid; /* node id that contains xattrs */ @@ -1329,7 +1329,7 @@ static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) static inline void inode_inc_dirty_pages(struct inode *inode) { - percpu_counter_inc(&F2FS_I(inode)->dirty_pages); + atomic_inc(&F2FS_I(inode)->dirty_pages); inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); } @@ -1345,7 +1345,7 @@ static inline void inode_dec_dirty_pages(struct inode *inode) !S_ISLNK(inode->i_mode)) return; - percpu_counter_dec(&F2FS_I(inode)->dirty_pages); + atomic_dec(&F2FS_I(inode)->dirty_pages); dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); } @@ -1355,9 +1355,9 @@ static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type) return atomic_read(&sbi->nr_pages[count_type]); } -static inline s64 get_dirty_pages(struct inode *inode) +static inline int get_dirty_pages(struct inode *inode) { - return percpu_counter_sum_positive(&F2FS_I(inode)->dirty_pages); + return atomic_read(&F2FS_I(inode)->dirty_pages); } static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c20febdbb6c5..18b0cda9ce2b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1548,7 +1548,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) goto out; f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, - "Unexpected flush for atomic writes: ino=%lu, npages=%lld", + "Unexpected flush for atomic writes: ino=%lu, npages=%u", inode->i_ino, get_dirty_pages(inode)); ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); if (ret) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5c51962a43a1..e23668ea1982 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -561,13 +561,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_once((void *) fi); - if (percpu_counter_init(&fi->dirty_pages, 0, GFP_NOFS)) { - kmem_cache_free(f2fs_inode_cachep, fi); - return NULL; - } - /* Initialize f2fs-specific inode info */ fi->vfs_inode.i_version = 1; + atomic_set(&fi->dirty_pages, 0); fi->i_current_depth = 1; fi->i_advise = 0; init_rwsem(&fi->i_sem); @@ -690,7 +686,6 @@ static void f2fs_i_callback(struct rcu_head *head) static void f2fs_destroy_inode(struct inode *inode) { - percpu_counter_destroy(&F2FS_I(inode)->dirty_pages); call_rcu(&inode->i_rcu, f2fs_i_callback); } -- GitLab From 44b20b051d3583b9effac81eb36c4317f9fe74bc Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 5 Dec 2016 11:37:14 -0800 Subject: [PATCH 0453/5498] f2fs: call sync_fs when f2fs is idle The sync_fs in f2fs_balance_fs_bg must avoid interrupting current user requests. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4f557e5e789d..b95f07559d90 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -381,12 +381,15 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) else build_free_nids(sbi, false); + if (!is_idle(sbi)) + return; + /* checkpoint is the only way to shrink partial cached entries */ if (!available_free_memory(sbi, NAT_ENTRIES) || !available_free_memory(sbi, INO_ENTRIES) || excess_prefree_segs(sbi) || excess_dirty_nats(sbi) || - (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { + f2fs_time_over(sbi, CP_TIME)) { if (test_opt(sbi, DATA_FLUSH)) { struct blk_plug plug; -- GitLab From e6868492c77b8c58157059cbdb8a997ace0f4af0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 5 Dec 2016 13:56:04 -0800 Subject: [PATCH 0454/5498] f2fs: detect wrong layout Previous mkfs.f2fs allows small partition inappropriately, so f2fs should detect that as well. Refer this in f2fs-tools. mkfs.f2fs: detect small partition by overprovision ratio and # of segments Reported-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 2 ++ fs/f2fs/super.c | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 59c4673244eb..4fe8d9ccce62 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -17,6 +17,8 @@ #define DEF_RECLAIM_PREFREE_SEGMENTS 5 /* 5% over total segments */ #define DEF_MAX_RECLAIM_PREFREE_SEGMENTS 4096 /* 8GB in maximum */ +#define F2FS_MIN_SEGMENTS 9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */ + /* L: Logical segment # in volume, R: Relative segment # in main area */ #define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno) #define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index e23668ea1982..9ad2c520e988 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1439,6 +1439,7 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) unsigned int total, fsmeta; struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + unsigned int ovp_segments, reserved_segments; total = le32_to_cpu(raw_super->segment_count); fsmeta = le32_to_cpu(raw_super->segment_count_ckpt); @@ -1450,6 +1451,16 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) if (unlikely(fsmeta >= total)) return 1; + ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); + reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); + + if (unlikely(fsmeta < F2FS_MIN_SEGMENTS || + ovp_segments == 0 || reserved_segments == 0)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Wrong layout: check mkfs.f2fs version"); + return 1; + } + if (unlikely(f2fs_cp_error(sbi))) { f2fs_msg(sbi->sb, KERN_ERR, "A bug case: need to run fsck"); return 1; -- GitLab From 8af4734411cba21b9b4658176dc6945cd695f0cf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 5 Dec 2016 17:25:32 -0800 Subject: [PATCH 0455/5498] f2fs: free meta pages if sanity check for ckpt is failed This fixes missing freeing meta pages in the error case. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a457dbdc1691..c2fcd9c8cce7 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -766,7 +766,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) /* Sanity checking of checkpoint */ if (sanity_check_ckpt(sbi)) - goto fail_no_cp; + goto free_fail_no_cp; if (cur_page == cp1) sbi->cur_cp_pack = 1; @@ -794,6 +794,9 @@ done: f2fs_put_page(cp2, 1); return 0; +free_fail_no_cp: + f2fs_put_page(cp1, 1); + f2fs_put_page(cp2, 1); fail_no_cp: kfree(sbi->ckpt); return -EINVAL; -- GitLab From f9f8de8a1889272e7c5f68b6e670b8b4df1da082 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 7 Dec 2016 16:23:32 -0800 Subject: [PATCH 0456/5498] f2fs: fix to access nullified flush_cmd_control pointer f2fs_sync_file() remount_ro - f2fs_readonly - destroy_flush_cmd_control - f2fs_issue_flush - no fcc pointer! So, this patch doesn't free fcc in this case, but just stop its kernel thread which sends flush commands. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/segment.c | 33 +++++++++++++++++++++++++-------- fs/f2fs/super.c | 5 +++-- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dd9ba7e203b3..8ff0423c2364 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2165,7 +2165,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *, bool); void f2fs_balance_fs_bg(struct f2fs_sb_info *); int f2fs_issue_flush(struct f2fs_sb_info *); int create_flush_cmd_control(struct f2fs_sb_info *); -void destroy_flush_cmd_control(struct f2fs_sb_info *); +void destroy_flush_cmd_control(struct f2fs_sb_info *, bool); void invalidate_blocks(struct f2fs_sb_info *, block_t); bool is_checkpointed_data(struct f2fs_sb_info *, block_t); void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index b95f07559d90..a288de069164 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -486,8 +486,13 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) if (!fcc->dispatch_list) wake_up(&fcc->flush_wait_queue); - wait_for_completion(&cmd.wait); - atomic_dec(&fcc->submit_flush); + if (fcc->f2fs_issue_flush) { + wait_for_completion(&cmd.wait); + atomic_dec(&fcc->submit_flush); + } else { + llist_del_all(&fcc->issue_list); + atomic_set(&fcc->submit_flush, 0); + } return cmd.ret; } @@ -498,6 +503,11 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) struct flush_cmd_control *fcc; int err = 0; + if (SM_I(sbi)->cmd_control_info) { + fcc = SM_I(sbi)->cmd_control_info; + goto init_thread; + } + fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); if (!fcc) return -ENOMEM; @@ -505,6 +515,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) init_waitqueue_head(&fcc->flush_wait_queue); init_llist_head(&fcc->issue_list); SM_I(sbi)->cmd_control_info = fcc; +init_thread: fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(fcc->f2fs_issue_flush)) { @@ -517,14 +528,20 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) return err; } -void destroy_flush_cmd_control(struct f2fs_sb_info *sbi) +void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) { struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; - if (fcc && fcc->f2fs_issue_flush) - kthread_stop(fcc->f2fs_issue_flush); - kfree(fcc); - SM_I(sbi)->cmd_control_info = NULL; + if (fcc && fcc->f2fs_issue_flush) { + struct task_struct *flush_thread = fcc->f2fs_issue_flush; + + fcc->f2fs_issue_flush = NULL; + kthread_stop(flush_thread); + } + if (free) { + kfree(fcc); + SM_I(sbi)->cmd_control_info = NULL; + } } static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, @@ -2658,7 +2675,7 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi) if (!sm_info) return; - destroy_flush_cmd_control(sbi); + destroy_flush_cmd_control(sbi, true); destroy_dirty_segmap(sbi); destroy_curseg(sbi); destroy_free_segmap(sbi); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9ad2c520e988..d8ae09e27fbb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1089,8 +1089,9 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) * 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) { + clear_opt(sbi, FLUSH_MERGE); + destroy_flush_cmd_control(sbi, false); + } else { err = create_flush_cmd_control(sbi); if (err) goto restore_gc; -- GitLab From 3cfa5940f128e3a994d534c369e78c1af5931f19 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 9 Dec 2016 11:39:37 -0800 Subject: [PATCH 0457/5498] scripts/tags.sh: catch 4.9-rc6 Signed-off-by: Jaegeuk Kim --- scripts/tags.sh | 218 +++++++++++++++++++++++++----------------------- 1 file changed, 113 insertions(+), 105 deletions(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index cdb491d84503..a2ff3388e5ea 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Generate tags or cscope files # Usage tags.sh # @@ -134,11 +134,6 @@ all_kconfigs() find_other_sources 'Kconfig*' } -all_defconfigs() -{ - find_sources $ALLSOURCE_ARCHS "defconfig" -} - docscope() { (echo \-k; echo \-q; all_target_sources) > cscope.files @@ -150,11 +145,114 @@ dogtags() all_target_sources | gtags -i -f - } +# Basic regular expressions with an optional /kind-spec/ for ctags and +# the following limitations: +# - No regex modifiers +# - Use \{0,1\} instead of \?, because etags expects an unescaped ? +# - \s is not working with etags, use a space or [ \t] +# - \w works, but does not match underscores in etags +# - etags regular expressions have to match at the start of a line; +# a ^[^#] is prepended by setup_regex unless an anchor is already present +regex_asm=( + '/^\(ENTRY\|_GLOBAL\)(\([[:alnum:]_\\]*\)).*/\2/' +) +regex_c=( + '/^SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/sys_\1/' + '/^COMPAT_SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/compat_sys_\1/' + '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1/' + '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1_rcuidle/' + '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1/' + '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1_rcuidle/' + '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/get_\1_slot/' + '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/free_\1_slot/' + '/^PAGEFLAG(\([[:alnum:]_]*\).*/Page\1/' + '/^PAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/' + '/^PAGEFLAG(\([[:alnum:]_]*\).*/ClearPage\1/' + '/^TESTSETFLAG(\([[:alnum:]_]*\).*/TestSetPage\1/' + '/^TESTPAGEFLAG(\([[:alnum:]_]*\).*/Page\1/' + '/^SETPAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/' + '/\<__SETPAGEFLAG(\([[:alnum:]_]*\).*/__SetPage\1/' + '/\ Date: Mon, 26 Sep 2016 18:07:48 +0200 Subject: [PATCH 0458/5498] fs/super.c: fix race between freeze_super() and thaw_super() Change thaw_super() to check frozen != SB_FREEZE_COMPLETE rather than frozen == SB_UNFROZEN, otherwise it can race with freeze_super() which drops sb->s_umount after SB_FREEZE_WRITE to preserve the lock ordering. In this case thaw_super() will wrongly call s_op->unfreeze_fs() before it was actually frozen, and call sb_freeze_unlock() which leads to the unbalanced percpu_up_write(). Unfortunately lockdep can't detect this, so this triggers misc BUG_ON()'s in kernel/rcu/sync.c. Reported-and-tested-by: Nikolay Borisov Signed-off-by: Oleg Nesterov Cc: stable@vger.kernel.org Signed-off-by: Al Viro --- fs/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/super.c b/fs/super.c index eae088f6aaae..19fafe86071a 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1346,8 +1346,8 @@ int freeze_super(struct super_block *sb) } } /* - * This is just for debugging purposes so that fs can warn if it - * sees write activity when frozen is set to SB_FREEZE_COMPLETE. + * For debugging purposes so that fs can warn if it sees write activity + * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super(). */ sb->s_writers.frozen = SB_FREEZE_COMPLETE; up_write(&sb->s_umount); @@ -1366,7 +1366,7 @@ int thaw_super(struct super_block *sb) int error; down_write(&sb->s_umount); - if (sb->s_writers.frozen == SB_UNFROZEN) { + if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) { up_write(&sb->s_umount); return -EINVAL; } -- GitLab From cd65ec892ee6922a9553ab0b8c7a065c9065fbd8 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Sun, 11 Dec 2016 15:35:15 +0800 Subject: [PATCH 0459/5498] f2fs: fix a missing size change in f2fs_setattr This patch fix a missing size change in f2fs_setattr Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 18b0cda9ce2b..732256d457d2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -679,6 +679,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); int err; + bool size_changed = false; err = inode_change_ok(inode, attr); if (err) @@ -709,6 +710,8 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } inode->i_mtime = inode->i_ctime = current_time(inode); } + + size_changed = true; } __setattr_copy(inode, attr); @@ -721,8 +724,8 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } } - /* update attributes only */ - f2fs_mark_inode_dirty_sync(inode, false); + /* file size may changed here */ + f2fs_mark_inode_dirty_sync(inode, size_changed); /* inode change will produce dirty node pages flushed by checkpoint */ f2fs_balance_fs(F2FS_I_SB(inode), true); -- GitLab From 4f2190fce811a893de890dba76963740621a6eab Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 11 Jan 2017 18:24:54 -0800 Subject: [PATCH 0460/5498] f2fs: resolve op and op_flags confilcts Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++++--- fs/f2fs/data.c | 54 ++++++++++++++++++++----------------- fs/f2fs/f2fs.h | 24 +++++++++++++++-- fs/f2fs/gc.c | 12 ++++++--- fs/f2fs/inline.c | 3 ++- fs/f2fs/node.c | 12 +++++---- fs/f2fs/segment.c | 9 ++++--- fs/f2fs/trace.c | 7 ++--- include/trace/events/f2fs.h | 19 ++++++++----- 9 files changed, 97 insertions(+), 53 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c2fcd9c8cce7..ad73c39da965 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -64,14 +64,15 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index, struct f2fs_io_info fio = { .sbi = sbi, .type = META, - .rw = READ_SYNC | REQ_META | REQ_PRIO, + .op = REQ_OP_READ, + .op_flags = REQ_META | REQ_PRIO, .old_blkaddr = index, .new_blkaddr = index, .encrypted_page = NULL, }; if (unlikely(!is_meta)) - fio.rw &= ~REQ_META; + fio.op_flags &= ~REQ_META; repeat: page = f2fs_grab_cache_page(mapping, index, false); if (!page) { @@ -159,13 +160,14 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, struct f2fs_io_info fio = { .sbi = sbi, .type = META, - .rw = sync ? (READ_SYNC | REQ_META | REQ_PRIO) : READA, + .op = REQ_OP_READ, + .op_flags = sync ? (REQ_META | REQ_PRIO) : REQ_RAHEAD, .encrypted_page = NULL, }; struct blk_plug plug; if (unlikely(type == META_POR)) - fio.rw &= ~REQ_META; + fio.op_flags &= ~REQ_META; blk_start_plug(&plug); for (; nrpages-- > 0; blkno++) { diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 340ffd56cafa..1fae639b6552 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -168,15 +168,15 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, return bio; } -static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, - struct bio *bio, enum page_type type) +static inline void __submit_bio(struct f2fs_sb_info *sbi, + struct bio *bio, enum page_type type) { - if (!is_read_io(rw)) { + if (!is_read_io(bio_op(bio))) { if (f2fs_sb_mounted_blkzoned(sbi->sb) && current->plug && (type == DATA || type == NODE)) blk_finish_plug(current->plug); } - submit_bio(rw, bio); + submit_bio(0, bio); } static void __submit_merged_bio(struct f2fs_bio_info *io) @@ -186,12 +186,14 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) if (!io->bio) return; - if (is_read_io(fio->rw)) + if (is_read_io(fio->op)) trace_f2fs_submit_read_bio(io->sbi->sb, fio, io->bio); else trace_f2fs_submit_write_bio(io->sbi->sb, fio, io->bio); - __submit_bio(io->sbi, fio->rw, io->bio, fio->type); + bio_set_op_attrs(io->bio, fio->op, fio->op_flags); + + __submit_bio(io->sbi, io->bio, fio->type); io->bio = NULL; } @@ -257,10 +259,10 @@ static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, /* change META to META_FLUSH in the checkpoint procedure */ if (type >= META_FLUSH) { io->fio.type = META_FLUSH; - if (test_opt(sbi, NOBARRIER)) - io->fio.rw = WRITE_FLUSH | REQ_META | REQ_PRIO; - else - io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO; + io->fio.op = REQ_OP_WRITE; + io->fio.op_flags = WRITE_FLUSH | REQ_META | REQ_PRIO; + if (!test_opt(sbi, NOBARRIER)) + io->fio.op_flags |= REQ_FUA; } __submit_merged_bio(io); out: @@ -302,14 +304,15 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) f2fs_trace_ios(fio, 0); /* Allocate a new bio */ - bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->rw)); + bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->op)); if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { bio_put(bio); return -EFAULT; } + bio_set_op_attrs(bio, fio->op, fio->op_flags); - __submit_bio(fio->sbi, fio->rw, bio, fio->type); + __submit_bio(fio->sbi, bio, fio->type); return 0; } @@ -318,7 +321,7 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) struct f2fs_sb_info *sbi = fio->sbi; enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); struct f2fs_bio_info *io; - bool is_read = is_read_io(fio->rw); + bool is_read = is_read_io(fio->op); struct page *bio_page; io = is_read ? &sbi->read_io : &sbi->write_io[btype]; @@ -335,7 +338,7 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) down_write(&io->io_rwsem); if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 || - (io->fio.rw != fio->rw) || + (io->fio.op != fio->op || io->fio.op_flags != fio->op_flags) || !__same_bdev(sbi, fio->new_blkaddr, io->bio))) __submit_merged_bio(io); alloc_new: @@ -463,7 +466,7 @@ int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index) } struct page *get_read_data_page(struct inode *inode, pgoff_t index, - int rw, bool for_write) + int op_flags, bool for_write) { struct address_space *mapping = inode->i_mapping; struct dnode_of_data dn; @@ -473,7 +476,8 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index, struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .type = DATA, - .rw = rw, + .op = REQ_OP_READ, + .op_flags = op_flags, .encrypted_page = NULL, }; @@ -541,7 +545,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index) return page; f2fs_put_page(page, 0); - page = get_read_data_page(inode, index, READ_SYNC, false); + page = get_read_data_page(inode, index, 0, false); if (IS_ERR(page)) return page; @@ -567,7 +571,7 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index, struct address_space *mapping = inode->i_mapping; struct page *page; repeat: - page = get_read_data_page(inode, index, READ_SYNC, for_write); + page = get_read_data_page(inode, index, 0, for_write); if (IS_ERR(page)) return page; @@ -1145,7 +1149,7 @@ got_it: if (bio && (last_block_in_bio != block_nr - 1 || !__same_bdev(F2FS_I_SB(inode), block_nr, bio))) { submit_and_realloc: - __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); + __submit_bio(F2FS_I_SB(inode), bio, DATA); bio = NULL; } if (bio == NULL) { @@ -1154,6 +1158,7 @@ submit_and_realloc: bio = NULL; goto set_error_page; } + bio_set_op_attrs(bio, REQ_OP_READ, 0); } if (bio_add_page(bio, page, blocksize, 0) < blocksize) @@ -1168,7 +1173,7 @@ set_error_page: goto next_page; confused: if (bio) { - __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); + __submit_bio(F2FS_I_SB(inode), bio, DATA); bio = NULL; } unlock_page(page); @@ -1178,7 +1183,7 @@ next_page: } BUG_ON(pages && !list_empty(pages)); if (bio) - __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); + __submit_bio(F2FS_I_SB(inode), bio, DATA); return 0; } @@ -1296,7 +1301,8 @@ static int f2fs_write_data_page(struct page *page, struct f2fs_io_info fio = { .sbi = sbi, .type = DATA, - .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + .op = REQ_OP_WRITE, + .op_flags = wbc_to_write_flags(wbc), .page = page, .encrypted_page = NULL, }; @@ -1718,14 +1724,14 @@ repeat: err = PTR_ERR(bio); goto fail; } - + bio->bi_rw = REQ_OP_READ; if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { bio_put(bio); err = -EFAULT; goto fail; } - __submit_bio(sbi, READ_SYNC, bio, DATA); + __submit_bio(sbi, bio, DATA); lock_page(page); if (unlikely(page->mapping != mapping)) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8ff0423c2364..993b63016259 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) @@ -113,6 +114,24 @@ struct f2fs_mount_info { #define F2FS_CLEAR_FEATURE(sb, mask) \ F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask) +/* bio stuffs */ +#define REQ_OP_READ READ +#define REQ_OP_WRITE WRITE +#define bio_op(bio) ((bio)->bi_rw & 1) + +static inline void bio_set_op_attrs(struct bio *bio, unsigned op, + unsigned op_flags) +{ + bio->bi_rw = op | op_flags; +} + +static inline int wbc_to_write_flags(struct writeback_control *wbc) +{ + if (wbc->sync_mode == WB_SYNC_ALL) + return REQ_SYNC; + return 0; +} + static inline void inode_lock(struct inode *inode) { mutex_lock(&inode->i_mutex); @@ -759,14 +778,15 @@ enum page_type { struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ - int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */ + int op; /* contains REQ_OP_ */ + int op_flags; /* req_flag_bits */ block_t new_blkaddr; /* new block address to be written */ block_t old_blkaddr; /* old block address before Cow */ struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ }; -#define is_read_io(rw) (((rw) & 1) == READ) +#define is_read_io(rw) (rw == READ) struct f2fs_bio_info { struct f2fs_sb_info *sbi; /* f2fs superblock */ struct bio *bio; /* bios to merge */ diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 6390d45c1b68..88bfc3dff496 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -550,7 +550,8 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .type = DATA, - .rw = READ_SYNC, + .op = REQ_OP_READ, + .op_flags = 0, .encrypted_page = NULL, }; struct dnode_of_data dn; @@ -627,7 +628,8 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, /* allocate block address */ f2fs_wait_on_page_writeback(dn.node_page, NODE, true); - fio.rw = WRITE_SYNC; + fio.op = REQ_OP_WRITE; + fio.op_flags = REQ_SYNC; fio.new_blkaddr = newaddr; f2fs_submit_page_mbio(&fio); @@ -668,7 +670,8 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .type = DATA, - .rw = WRITE_SYNC, + .op = REQ_OP_WRITE, + .op_flags = REQ_SYNC, .page = page, .encrypted_page = NULL, }; @@ -767,7 +770,8 @@ next_step: start_bidx = start_bidx_of_node(nofs, inode); data_page = get_read_data_page(inode, - start_bidx + ofs_in_node, READA, true); + start_bidx + ofs_in_node, REQ_RAHEAD, + true); if (IS_ERR(data_page)) { iput(inode); continue; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 3f8bfc87c6dc..e32a9e527968 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -110,7 +110,8 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) struct f2fs_io_info fio = { .sbi = F2FS_I_SB(dn->inode), .type = DATA, - .rw = WRITE_SYNC | REQ_PRIO, + .op = REQ_OP_WRITE, + .op_flags = REQ_SYNC | REQ_PRIO, .page = page, .encrypted_page = NULL, }; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index cc60f4d1d6be..cf492039035c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1068,14 +1068,15 @@ fail: * 0: f2fs_put_page(page, 0) * LOCKED_PAGE or error: f2fs_put_page(page, 1) */ -static int read_node_page(struct page *page, int rw) +static int read_node_page(struct page *page, int op_flags) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); struct node_info ni; struct f2fs_io_info fio = { .sbi = sbi, .type = NODE, - .rw = rw, + .op = REQ_OP_READ, + .op_flags = op_flags, .page = page, .encrypted_page = NULL, }; @@ -1116,7 +1117,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) if (!apage) return; - err = read_node_page(apage, READA); + err = read_node_page(apage, REQ_RAHEAD); f2fs_put_page(apage, err ? 1 : 0); } @@ -1134,7 +1135,7 @@ repeat: if (!page) return ERR_PTR(-ENOMEM); - err = read_node_page(page, READ_SYNC); + err = read_node_page(page, 0); if (err < 0) { f2fs_put_page(page, 1); return ERR_PTR(err); @@ -1581,7 +1582,8 @@ static int f2fs_write_node_page(struct page *page, struct f2fs_io_info fio = { .sbi = sbi, .type = NODE, - .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + .op = REQ_OP_WRITE, + .op_flags = wbc_to_write_flags(wbc), .page = page, .encrypted_page = NULL, }; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a288de069164..fd3d97d8b747 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -257,7 +257,8 @@ static int __commit_inmem_pages(struct inode *inode, struct f2fs_io_info fio = { .sbi = sbi, .type = DATA, - .rw = WRITE_SYNC | REQ_PRIO, + .op = REQ_OP_WRITE, + .op_flags = REQ_SYNC | REQ_PRIO, .encrypted_page = NULL, }; bool submit_bio = false; @@ -407,6 +408,7 @@ static int __submit_flush_wait(struct block_device *bdev) struct bio *bio = f2fs_bio_alloc(0); int ret; + bio->bi_rw = REQ_OP_WRITE; bio->bi_bdev = bdev; ret = submit_bio_wait(WRITE_FLUSH, bio); bio_put(bio); @@ -1544,7 +1546,8 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) struct f2fs_io_info fio = { .sbi = sbi, .type = META, - .rw = WRITE_SYNC | REQ_META | REQ_PRIO, + .op = REQ_OP_WRITE, + .op_flags = REQ_SYNC | REQ_META | REQ_PRIO, .old_blkaddr = page->index, .new_blkaddr = page->index, .page = page, @@ -1552,7 +1555,7 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) }; if (unlikely(page->index >= MAIN_BLKADDR(sbi))) - fio.rw &= ~REQ_META; + fio.op_flags &= ~REQ_META; set_page_writeback(page); f2fs_submit_page_mbio(&fio); diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c index 562ce0821559..73b4e1d1912a 100644 --- a/fs/f2fs/trace.c +++ b/fs/f2fs/trace.c @@ -25,11 +25,11 @@ static inline void __print_last_io(void) if (!last_io.len) return; - trace_printk("%3x:%3x %4x %-16s %2x %5x %12x %4x\n", + trace_printk("%3x:%3x %4x %-16s %2x %5x %5x %12x %4x\n", last_io.major, last_io.minor, last_io.pid, "----------------", last_io.type, - last_io.fio.rw, + last_io.fio.op, last_io.fio.op_flags, last_io.fio.new_blkaddr, last_io.len); memset(&last_io, 0, sizeof(last_io)); @@ -101,7 +101,8 @@ void f2fs_trace_ios(struct f2fs_io_info *fio, int flush) if (last_io.major == major && last_io.minor == minor && last_io.pid == pid && last_io.type == __file_type(inode, pid) && - last_io.fio.rw == fio->rw && + last_io.fio.op == fio->op && + last_io.fio.op_flags == fio->op_flags && last_io.fio.new_blkaddr + last_io.len == fio->new_blkaddr) { last_io.len++; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 4ebccde84421..0da26e12b5b6 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -24,7 +24,8 @@ #define F2FS_BIO_MASK(t) (t & (READA | WRITE_FLUSH_FUA)) #define F2FS_BIO_EXTRA_MASK(t) (t & (REQ_META | REQ_PRIO)) -#define show_bio_type(type) show_bio_base(type), show_bio_extra(type) +#define show_bio_type(op, op_flags) \ + show_bio_base((op|op_flags)), show_bio_extra((op|op_flags)) #define show_bio_base(type) \ __print_symbolic(F2FS_BIO_MASK(type), \ @@ -699,7 +700,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio, __field(pgoff_t, index) __field(block_t, old_blkaddr) __field(block_t, new_blkaddr) - __field(int, rw) + __field(int, op) + __field(int, op_flags) __field(int, type) ), @@ -709,7 +711,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio, __entry->index = page->index; __entry->old_blkaddr = fio->old_blkaddr; __entry->new_blkaddr = fio->new_blkaddr; - __entry->rw = fio->rw; + __entry->op = fio->op; + __entry->op_flags = fio->op_flags; __entry->type = fio->type; ), @@ -719,7 +722,7 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio, (unsigned long)__entry->index, (unsigned long long)__entry->old_blkaddr, (unsigned long long)__entry->new_blkaddr, - show_bio_type(__entry->rw), + show_bio_type(__entry->op, __entry->op_flags), show_block_type(__entry->type)) ); @@ -750,7 +753,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio, TP_STRUCT__entry( __field(dev_t, dev) - __field(int, rw) + __field(int, op) + __field(int, op_flags) __field(int, type) __field(sector_t, sector) __field(unsigned int, size) @@ -758,7 +762,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio, TP_fast_assign( __entry->dev = sb->s_dev; - __entry->rw = fio->rw; + __entry->op = fio->op; + __entry->op_flags = fio->op_flags; __entry->type = fio->type; __entry->sector = bio->bi_iter.bi_sector; __entry->size = bio->bi_iter.bi_size; @@ -766,7 +771,7 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio, TP_printk("dev = (%d,%d), %s%s, %s, sector = %lld, size = %u", show_dev(__entry), - show_bio_type(__entry->rw), + show_bio_type(__entry->op, __entry->op_flags), show_block_type(__entry->type), (unsigned long long)__entry->sector, __entry->size) -- GitLab From e9e0fcc25fe81776adfb3b82111ebfe47aed1fd7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 11 Jan 2017 16:41:25 -0800 Subject: [PATCH 0461/5498] f2fs: support async discard based on v4.9 This patch is based on commit 275b66b09e85 (f2fs: support async discard). Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/checkpoint.c fs/f2fs/segment.c --- fs/f2fs/checkpoint.c | 7 +- fs/f2fs/f2fs.h | 9 +++ fs/f2fs/segment.c | 183 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 188 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index ad73c39da965..891b0d6c5414 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1252,6 +1252,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_bug_on(sbi, prefree_segments(sbi)); flush_sit_entries(sbi, cpc); clear_prefree_segments(sbi, cpc); + f2fs_wait_all_discard_bio(sbi); unblock_operations(sbi); goto out; } @@ -1270,10 +1271,12 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* unlock all the fs_lock[] in do_checkpoint() */ err = do_checkpoint(sbi, cpc); - if (err) + if (err) { release_discard_addrs(sbi); - else + } else { clear_prefree_segments(sbi, cpc); + f2fs_wait_all_discard_bio(sbi); + } unblock_operations(sbi); stat_inc_cp_count(sbi->stat_info); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 993b63016259..eb3156276ee6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -269,6 +269,13 @@ struct discard_entry { int len; /* # of consecutive blocks of the discard */ }; +struct bio_entry { + struct list_head list; + struct bio *bio; + struct completion event; + int error; +}; + /* for the list of fsync inodes, used only during recovery */ struct fsync_inode_entry { struct list_head list; /* list head */ @@ -711,6 +718,7 @@ struct f2fs_sm_info { /* for small discard management */ struct list_head discard_list; /* 4KB discard list */ + struct list_head wait_list; /* linked with issued discard bio */ int nr_discards; /* # of discards in the list */ int max_discards; /* max. discards to be issued */ @@ -2189,6 +2197,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *, bool); void invalidate_blocks(struct f2fs_sb_info *, block_t); bool is_checkpointed_data(struct f2fs_sb_info *, block_t); void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); +void f2fs_wait_all_discard_bio(struct f2fs_sb_info *); void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *); void release_discard_addrs(struct f2fs_sb_info *); int npages_for_summary_flush(struct f2fs_sb_info *, bool); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fd3d97d8b747..ccc9c24558ab 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -26,6 +26,7 @@ #define __reverse_ffz(x) __reverse_ffs(~(x)) static struct kmem_cache *discard_entry_slab; +static struct kmem_cache *bio_entry_slab; static struct kmem_cache *sit_entry_set_slab; static struct kmem_cache *inmem_entry_slab; @@ -622,6 +623,162 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_unlock(&dirty_i->seglist_lock); } +static struct bio_entry *__add_bio_entry(struct f2fs_sb_info *sbi, + struct bio *bio) +{ + struct list_head *wait_list = &(SM_I(sbi)->wait_list); + struct bio_entry *be = f2fs_kmem_cache_alloc(bio_entry_slab, GFP_NOFS); + + INIT_LIST_HEAD(&be->list); + be->bio = bio; + init_completion(&be->event); + list_add_tail(&be->list, wait_list); + + return be; +} + +void f2fs_wait_all_discard_bio(struct f2fs_sb_info *sbi) +{ + struct list_head *wait_list = &(SM_I(sbi)->wait_list); + struct bio_entry *be, *tmp; + + list_for_each_entry_safe(be, tmp, wait_list, list) { + struct bio *bio = be->bio; + int err; + + wait_for_completion_io(&be->event); + err = be->error; + if (err == -EOPNOTSUPP) + err = 0; + + if (err) + f2fs_msg(sbi->sb, KERN_INFO, + "Issue discard failed, ret: %d", err); + + bio_put(bio); + list_del(&be->list); + kmem_cache_free(bio_entry_slab, be); + } +} + +static void f2fs_submit_bio_wait_endio(struct bio *bio, int err) +{ + struct bio_entry *be = (struct bio_entry *)bio->bi_private; + + be->error = err; + complete(&be->event); +} + +/* copied from block/blk-lib.c in 4.10-rc1 */ +static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, + sector_t nr_sects, gfp_t gfp_mask, int flags, + struct bio **biop) +{ + struct request_queue *q = bdev_get_queue(bdev); + struct bio *bio = *biop; + unsigned int granularity; + int op = REQ_WRITE | REQ_DISCARD; + int alignment; + sector_t bs_mask; + + if (!q) + return -ENXIO; + + if (!blk_queue_discard(q)) + return -EOPNOTSUPP; + + if (flags & BLKDEV_DISCARD_SECURE) { + if (!blk_queue_secdiscard(q)) + return -EOPNOTSUPP; + op |= REQ_SECURE; + } + + bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1; + if ((sector | nr_sects) & bs_mask) + return -EINVAL; + + /* Zero-sector (unknown) and one-sector granularities are the same. */ + granularity = max(q->limits.discard_granularity >> 9, 1U); + alignment = (bdev_discard_alignment(bdev) >> 9) % granularity; + + while (nr_sects) { + unsigned int req_sects; + sector_t end_sect, tmp; + + /* Make sure bi_size doesn't overflow */ + req_sects = min_t(sector_t, nr_sects, UINT_MAX >> 9); + + /** + * If splitting a request, and the next starting sector would be + * misaligned, stop the discard at the previous aligned sector. + */ + end_sect = sector + req_sects; + tmp = end_sect; + if (req_sects < nr_sects && + sector_div(tmp, granularity) != alignment) { + end_sect = end_sect - alignment; + sector_div(end_sect, granularity); + end_sect = end_sect * granularity + alignment; + req_sects = end_sect - sector; + } + + if (bio) { + int ret = submit_bio_wait(0, bio); + bio_put(bio); + if (ret) + return ret; + } + bio = f2fs_bio_alloc(1); + bio->bi_iter.bi_sector = sector; + bio->bi_bdev = bdev; + bio_set_op_attrs(bio, op, 0); + + bio->bi_iter.bi_size = req_sects << 9; + nr_sects -= req_sects; + sector = end_sect; + + /* + * We can loop for a long time in here, if someone does + * full device discards (like mkfs). Be nice and allow + * us to schedule out to avoid softlocking if preempt + * is disabled. + */ + cond_resched(); + } + + *biop = bio; + return 0; +} + +/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ +static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t blkstart, block_t blklen) +{ + struct bio *bio = NULL; + int err; + + trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); + + if (sbi->s_ndevs) { + int devi = f2fs_target_device_index(sbi, blkstart); + + blkstart -= FDEV(devi).start_blk; + } + err = __blkdev_issue_discard(bdev, + SECTOR_FROM_BLOCK(blkstart), + SECTOR_FROM_BLOCK(blklen), + GFP_NOFS, 0, &bio); + if (!err && bio) { + struct bio_entry *be = __add_bio_entry(sbi, bio); + + bio->bi_private = be; + bio->bi_end_io = f2fs_submit_bio_wait_endio; + submit_bio(REQ_SYNC, bio); + } + + return err; +} + #ifdef CONFIG_BLK_DEV_ZONED static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) @@ -655,8 +812,7 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, case BLK_ZONE_TYPE_CONVENTIONAL: if (!blk_queue_discard(bdev_get_queue(bdev))) return 0; - return blkdev_issue_discard(bdev, sector, nr_sects, - GFP_NOFS, 0); + return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); case BLK_ZONE_TYPE_SEQWRITE_REQ: case BLK_ZONE_TYPE_SEQWRITE_PREF: trace_f2fs_issue_reset_zone(sbi->sb, blkstart); @@ -672,15 +828,12 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, static int __issue_discard_async(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) { - sector_t start = SECTOR_FROM_BLOCK(blkstart); - sector_t len = SECTOR_FROM_BLOCK(blklen); - #ifdef CONFIG_BLK_DEV_ZONED if (f2fs_sb_mounted_blkzoned(sbi->sb) && bdev_zoned_model(bdev) != BLK_ZONED_NONE) return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); #endif - return blkdev_issue_discard(bdev, start, len, GFP_NOFS, 0); + return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); } static int f2fs_issue_discard(struct f2fs_sb_info *sbi, @@ -720,8 +873,6 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, if (len) err = __issue_discard_async(sbi, bdev, start, len); - - trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); return err; } @@ -822,11 +973,14 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct list_head *head = &(SM_I(sbi)->discard_list); struct discard_entry *entry, *this; struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + struct blk_plug plug; unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int start = 0, end = -1; unsigned int secno, start_segno; bool force = (cpc->reason == CP_DISCARD); + blk_start_plug(&plug); + mutex_lock(&dirty_i->seglist_lock); while (1) { @@ -875,6 +1029,8 @@ skip: SM_I(sbi)->nr_discards -= entry->len; kmem_cache_free(discard_entry_slab, entry); } + + blk_finish_plug(&plug); } static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) @@ -2551,6 +2707,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; INIT_LIST_HEAD(&sm_info->discard_list); + INIT_LIST_HEAD(&sm_info->wait_list); sm_info->nr_discards = 0; sm_info->max_discards = 0; @@ -2694,10 +2851,15 @@ int __init create_segment_manager_caches(void) if (!discard_entry_slab) goto fail; + bio_entry_slab = f2fs_kmem_cache_create("bio_entry", + sizeof(struct bio_entry)); + if (!bio_entry_slab) + goto destroy_discard_entry; + sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set", sizeof(struct sit_entry_set)); if (!sit_entry_set_slab) - goto destroy_discard_entry; + goto destroy_bio_entry; inmem_entry_slab = f2fs_kmem_cache_create("inmem_page_entry", sizeof(struct inmem_pages)); @@ -2707,6 +2869,8 @@ int __init create_segment_manager_caches(void) destroy_sit_entry_set: kmem_cache_destroy(sit_entry_set_slab); +destroy_bio_entry: + kmem_cache_destroy(bio_entry_slab); destroy_discard_entry: kmem_cache_destroy(discard_entry_slab); fail: @@ -2716,6 +2880,7 @@ fail: void destroy_segment_manager_caches(void) { kmem_cache_destroy(sit_entry_set_slab); + kmem_cache_destroy(bio_entry_slab); kmem_cache_destroy(discard_entry_slab); kmem_cache_destroy(inmem_entry_slab); } -- GitLab From d0bddcc9dd1b843a9b88a2ba28b658aeaa49db34 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 13 Jan 2017 14:54:32 -0800 Subject: [PATCH 0462/5498] f2fs: add migrate_page to avoid patch conflicts This is disabled by default. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 3 +++ fs/f2fs/data.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 4 ++++ fs/f2fs/key.c | 34 +++++++++++++++++++++++++++ fs/f2fs/node.c | 3 +++ 5 files changed, 99 insertions(+) create mode 100644 fs/f2fs/key.c diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 891b0d6c5414..6e946c276758 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -386,6 +386,9 @@ const struct address_space_operations f2fs_meta_aops = { .set_page_dirty = f2fs_set_meta_page_dirty, .invalidatepage = f2fs_invalidate_page, .releasepage = f2fs_release_page, +#ifdef CONFIG_F2FS_MIGRATION + .migratepage = f2fs_migrate_page, +#endif }; static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1fae639b6552..16a54b3148ff 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1946,6 +1946,58 @@ static sector_t f2fs_bmap(struct address_space *mapping, sector_t block) return generic_block_bmap(mapping, block, get_data_block_bmap); } +#ifdef CONFIG_F2FS_MIGRATION +#include + +int f2fs_migrate_page(struct address_space *mapping, + struct page *newpage, struct page *page, enum migrate_mode mode) +{ + int rc, extra_count; + struct f2fs_inode_info *fi = F2FS_I(mapping->host); + bool atomic_written = IS_ATOMIC_WRITTEN_PAGE(page); + + BUG_ON(PageWriteback(page)); + + /* migrating an atomic written page is safe with the inmem_lock hold */ + if (atomic_written && !mutex_trylock(&fi->inmem_lock)) + return -EAGAIN; + + /* + * A reference is expected if PagePrivate set when move mapping, + * however F2FS breaks this for maintaining dirty page counts when + * truncating pages. So here adjusting the 'extra_count' make it work. + */ + extra_count = (atomic_written ? 1 : 0) - page_has_private(page); + rc = migrate_page_move_mapping(mapping, newpage, + page, NULL, mode, extra_count); + if (rc != MIGRATEPAGE_SUCCESS) { + if (atomic_written) + mutex_unlock(&fi->inmem_lock); + return rc; + } + + if (atomic_written) { + struct inmem_pages *cur; + list_for_each_entry(cur, &fi->inmem_pages, list) + if (cur->page == page) { + cur->page = newpage; + break; + } + mutex_unlock(&fi->inmem_lock); + put_page(page); + get_page(newpage); + } + + if (PagePrivate(page)) + SetPagePrivate(newpage); + set_page_private(newpage, page_private(page)); + + migrate_page_copy(newpage, page); + + return MIGRATEPAGE_SUCCESS; +} +#endif + const struct address_space_operations f2fs_dblock_aops = { .readpage = f2fs_read_data_page, .readpages = f2fs_read_data_pages, @@ -1958,4 +2010,7 @@ const struct address_space_operations f2fs_dblock_aops = { .releasepage = f2fs_release_page, .direct_IO = f2fs_direct_IO, .bmap = f2fs_bmap, +#ifdef CONFIG_F2FS_MIGRATION + .migratepage = f2fs_migrate_page, +#endif }; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index eb3156276ee6..2ef8dd8525ee 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2285,6 +2285,10 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); void f2fs_set_page_dirty_nobuffers(struct page *); void f2fs_invalidate_page(struct page *, unsigned int, unsigned int); int f2fs_release_page(struct page *, gfp_t); +#ifdef CONFIG_F2FS_MIGRATION +int f2fs_migrate_page(struct address_space *, struct page *, struct page *, + enum migrate_mode); +#endif /* * gc.c diff --git a/fs/f2fs/key.c b/fs/f2fs/key.c new file mode 100644 index 000000000000..948f5bc1660f --- /dev/null +++ b/fs/f2fs/key.c @@ -0,0 +1,34 @@ +/* fill fi->keyring_key */ +int f2fs_validate_key(struct inode *inode) +{ + u8 full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE + + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1]; + struct key *keyring_key = NULL; + u8 key[F2FS_SET_KEY_SIZE]; + int ret; + + ret = f2fs_getxattr(inode, F2FS_XATTR_INDEX_KEY, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + key, F2FS_SET_KEY_SIZE, NULL); + if (ret) + return ret; + + memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX, + F2FS_KEY_DESC_PREFIX_SIZE); + sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE, + "%*phN", F2FS_KEY_DESCRIPTOR_SIZE, key); + full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + + (2 * F2FS_KEY_DESCRIPTOR_SIZE)] = '\0'; + keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); + if (IS_ERR(keyring_key)) + return PTR_ERR(keyring_key); + + if (keyring_key->type != &key_type_logon) { + printk_once(KERN_WARNING + "%s: key type must be logon\n", __func__); + key_put(keyring_key); + return -ENOKEY; + } + key_put(keyring_key); + return 0; +} diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index cf492039035c..b2f464fb7b32 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1694,6 +1694,9 @@ const struct address_space_operations f2fs_node_aops = { .set_page_dirty = f2fs_set_node_page_dirty, .invalidatepage = f2fs_invalidate_page, .releasepage = f2fs_release_page, +#ifdef CONFIG_F2FS_MIGRATION + .migratepage = f2fs_migrate_page, +#endif }; static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, -- GitLab From 1969af330d1731e82c96257a4b593b7de2747469 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 13 Dec 2016 17:23:37 +0800 Subject: [PATCH 0463/5498] f2fs: remove unused values in recover_fsync_data This patch remove unused values in function recover_fsync_data Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 192da3de4fbf..1e3da757abff 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -552,10 +552,8 @@ next: int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) { - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); struct list_head inode_list; struct list_head dir_list; - block_t blkaddr; int err; int ret = 0; bool need_writecp = false; @@ -571,8 +569,6 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) /* prevent checkpoint */ mutex_lock(&sbi->cp_mutex); - blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); - /* step #1: find fsynced inode numbers */ err = find_fsync_dnodes(sbi, &inode_list); if (err || list_empty(&inode_list)) -- GitLab From 8d623d2921d599ba72d0dbcbb54435c83f683ace Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 13 Dec 2016 18:54:59 +0800 Subject: [PATCH 0464/5498] f2fs: don't cache nat entry if out of memory If we run out of memory, in cache_nat_entry, it's better to avoid loop for allocating memory to cache nat entry, so in low memory scenario, for read path of node block, I expect this can avoid unneeded latency. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b2f464fb7b32..9bb6d024c07c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -245,12 +245,24 @@ bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) return need_update; } -static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) +static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, + bool no_fail) { struct nat_entry *new; - new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS); - f2fs_radix_tree_insert(&nm_i->nat_root, nid, new); + if (no_fail) { + new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS); + f2fs_radix_tree_insert(&nm_i->nat_root, nid, new); + } else { + new = kmem_cache_alloc(nat_entry_slab, GFP_NOFS); + if (!new) + return NULL; + if (radix_tree_insert(&nm_i->nat_root, nid, new)) { + kmem_cache_free(nat_entry_slab, new); + return NULL; + } + } + memset(new, 0, sizeof(struct nat_entry)); nat_set_nid(new, nid); nat_reset_flag(new); @@ -267,8 +279,9 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, e = __lookup_nat_cache(nm_i, nid); if (!e) { - e = grab_nat_entry(nm_i, nid); - node_info_from_raw_nat(&e->ni, ne); + e = grab_nat_entry(nm_i, nid, false); + if (e) + node_info_from_raw_nat(&e->ni, ne); } else { f2fs_bug_on(sbi, nat_get_ino(e) != le32_to_cpu(ne->ino) || nat_get_blkaddr(e) != @@ -286,7 +299,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, ni->nid); if (!e) { - e = grab_nat_entry(nm_i, ni->nid); + e = grab_nat_entry(nm_i, ni->nid, true); copy_node_info(&e->ni, ni); f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR); } else if (new_blkaddr == NEW_ADDR) { @@ -2161,7 +2174,7 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) ne = __lookup_nat_cache(nm_i, nid); if (!ne) { - ne = grab_nat_entry(nm_i, nid); + ne = grab_nat_entry(nm_i, nid, true); node_info_from_raw_nat(&ne->ni, &raw_ne); } -- GitLab From 07a2b6699f4b5b9ff3a4b062440c15c69cf1a4bd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 16 Dec 2016 11:18:15 +0300 Subject: [PATCH 0465/5498] f2fs: remove unneeded condition We checked that "inode" is not an error pointer earlier so there is no need to check again here. Signed-off-by: Dan Carpenter Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 60920dc84f29..172c8a189e71 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -322,9 +322,9 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (err) goto err_out; } - if (!IS_ERR(inode) && f2fs_encrypted_inode(dir) && - (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && - !fscrypt_has_permitted_context(dir, inode)) { + if (f2fs_encrypted_inode(dir) && + (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && + !fscrypt_has_permitted_context(dir, inode)) { bool nokey = f2fs_encrypted_inode(inode) && !fscrypt_has_encryption_key(inode); err = nokey ? -ENOKEY : -EPERM; -- GitLab From 7e2966de149c452fe197b8745fbc789b2141bdcc Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 19 Dec 2016 20:10:48 +0800 Subject: [PATCH 0466/5498] f2fs: fix a problem of using memory after free This patch fix a problem of using memory after free in function __try_merge_extent_node. Fixes: 0f825ee6e873 ("f2fs: add new interfaces for extent tree") Cc: Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 4db44da7ef69..e02c3d88dc9a 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -352,11 +352,12 @@ static struct extent_node *__try_merge_extent_node(struct inode *inode, } if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) { - if (en) - __release_extent_node(sbi, et, prev_ex); next_ex->ei.fofs = ei->fofs; next_ex->ei.blk = ei->blk; next_ex->ei.len += ei->len; + if (en) + __release_extent_node(sbi, et, prev_ex); + en = next_ex; } -- GitLab From 58bea94e3cce20871008cb4a64a80b0c6d65e482 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 20 Dec 2016 11:11:35 +0800 Subject: [PATCH 0467/5498] f2fs: add a case of no need to read a page in write begin If the range we write cover the whole valid data in the last page, we do not need to read it. Signed-off-by: Yunlei He [Jaegeuk Kim: nullify the remaining area (fix: xfstests/f2fs/001)] Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 16a54b3148ff..408e9c570ca4 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1713,6 +1713,11 @@ repeat: if (len == PAGE_SIZE || PageUptodate(page)) return 0; + if (!(pos & (PAGE_SIZE - 1)) && (pos + len) >= i_size_read(inode)) { + zero_user_segment(page, len, PAGE_SIZE); + return 0; + } + if (blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_SIZE); SetPageUptodate(page); @@ -1766,7 +1771,7 @@ static int f2fs_write_end(struct file *file, * let generic_perform_write() try to copy data again through copied=0. */ if (!PageUptodate(page)) { - if (unlikely(copied != PAGE_SIZE)) + if (unlikely(copied != len)) copied = 0; else SetPageUptodate(page); -- GitLab From b1607fb7fee3e803ab721dcb217487c20131f571 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Tue, 20 Dec 2016 21:57:42 +0800 Subject: [PATCH 0468/5498] f2fs: use rb_entry_safe Use rb_entry_safe() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 17 ++++++----------- fs/f2fs/f2fs.h | 5 +++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index e02c3d88dc9a..6ed6424807b6 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -311,28 +311,24 @@ static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et, tmp_node = parent; if (parent && fofs > en->ei.fofs) tmp_node = rb_next(parent); - *next_ex = tmp_node ? - rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + *next_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); tmp_node = parent; if (parent && fofs < en->ei.fofs) tmp_node = rb_prev(parent); - *prev_ex = tmp_node ? - rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + *prev_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); return NULL; lookup_neighbors: if (fofs == en->ei.fofs) { /* lookup prev node for merging backward later */ tmp_node = rb_prev(&en->rb_node); - *prev_ex = tmp_node ? - rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + *prev_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); } if (fofs == en->ei.fofs + en->ei.len - 1) { /* lookup next node for merging frontward later */ tmp_node = rb_next(&en->rb_node); - *next_ex = tmp_node ? - rb_entry(tmp_node, struct extent_node, rb_node) : NULL; + *next_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); } return en; } @@ -493,9 +489,8 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, if (!next_en) { struct rb_node *node = rb_next(&en->rb_node); - next_en = node ? - rb_entry(node, struct extent_node, rb_node) - : NULL; + next_en = rb_entry_safe(node, struct extent_node, + rb_node); } if (parts) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2ef8dd8525ee..d0192ab0f595 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -147,6 +147,11 @@ static inline void inode_unlock(struct inode *inode) mutex_unlock(&inode->i_mutex); } +#define rb_entry_safe(ptr, type, member) \ + ({ typeof(ptr) ____ptr = (ptr); \ + ____ptr ? rb_entry(____ptr, type, member) : NULL; \ + }) + /** * wq_has_sleeper - check if there are any waiting processes * @wq: wait queue head -- GitLab From e42a21e777f06d63882bc34e1ec1fe0d37ed443b Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 22 Dec 2016 11:46:24 +0800 Subject: [PATCH 0469/5498] f2fs: fix a missing discard prefree segments If userspace issue a fstrim with a range not involve prefree segments, it will reuse these segments without discard. This patch fix it. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ccc9c24558ab..a997a9496d23 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -996,9 +996,13 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) dirty_i->nr_dirty[PRE] -= end - start; - if (force || !test_opt(sbi, DISCARD)) + if (!test_opt(sbi, DISCARD)) continue; + if (force && start >= cpc->trim_start && + (end - 1) <= cpc->trim_end) + continue; + if (!test_opt(sbi, LFS) || sbi->segs_per_sec == 1) { f2fs_issue_discard(sbi, START_BLOCK(sbi, start), (end - start) << sbi->log_blocks_per_seg); @@ -2343,8 +2347,12 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_bug_on(sbi, sit_i->dirty_sentries); out: if (cpc->reason == CP_DISCARD) { + __u64 trim_start = cpc->trim_start; + for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) add_discard_addrs(sbi, cpc); + + cpc->trim_start = trim_start; } mutex_unlock(&sit_i->sentry_lock); -- GitLab From 89c9f3bbc67952538b6d80cc1aa11dc8841ffe97 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Dec 2016 11:51:32 -0800 Subject: [PATCH 0470/5498] f2fs: reassign new segment for mode=lfs Otherwise we can remain wrong curseg->next_blkoff, resulting in fsck failure. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a997a9496d23..97db4d2895a5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1508,9 +1508,6 @@ void allocate_new_segments(struct f2fs_sb_info *sbi) unsigned int old_segno; int i; - if (test_opt(sbi, LFS)) - return; - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { curseg = CURSEG_I(sbi, i); old_segno = curseg->segno; -- GitLab From 4042420996e6c836f2d257d30b51c4aeb6038f17 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Dec 2016 12:13:03 -0800 Subject: [PATCH 0471/5498] f2fs: add submit_bio tracepoint This patch adds final submit_bio() tracepoint. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 12 ++++++---- include/trace/events/f2fs.h | 45 ++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 408e9c570ca4..f8b08994a9b5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -176,6 +176,10 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi, current->plug && (type == DATA || type == NODE)) blk_finish_plug(current->plug); } + if (is_read_io(bio_op(bio))) + trace_f2fs_submit_read_bio(sbi->sb, type, bio); + else + trace_f2fs_submit_write_bio(sbi->sb, type, bio); submit_bio(0, bio); } @@ -186,12 +190,12 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) if (!io->bio) return; + bio_set_op_attrs(io->bio, fio->op, fio->op_flags); + if (is_read_io(fio->op)) - trace_f2fs_submit_read_bio(io->sbi->sb, fio, io->bio); + trace_f2fs_prepare_read_bio(io->sbi->sb, fio->type, io->bio); else - trace_f2fs_submit_write_bio(io->sbi->sb, fio, io->bio); - - bio_set_op_attrs(io->bio, fio->op, fio->op_flags); + trace_f2fs_prepare_write_bio(io->sbi->sb, fio->type, io->bio); __submit_bio(io->sbi, io->bio, fio->type); io->bio = NULL; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 0da26e12b5b6..75fe15994f65 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -744,12 +744,11 @@ DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio, TP_CONDITION(page->mapping) ); -DECLARE_EVENT_CLASS(f2fs__submit_bio, +DECLARE_EVENT_CLASS(f2fs__bio, - TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, - struct bio *bio), + TP_PROTO(struct super_block *sb, int type, struct bio *bio), - TP_ARGS(sb, fio, bio), + TP_ARGS(sb, type, bio), TP_STRUCT__entry( __field(dev_t, dev) @@ -762,9 +761,9 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio, TP_fast_assign( __entry->dev = sb->s_dev; - __entry->op = fio->op; - __entry->op_flags = fio->op_flags; - __entry->type = fio->type; + __entry->op = bio_op(bio); + __entry->op_flags = bio->bi_rw; + __entry->type = type; __entry->sector = bio->bi_iter.bi_sector; __entry->size = bio->bi_iter.bi_size; ), @@ -777,22 +776,38 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio, __entry->size) ); -DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_write_bio, +DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_prepare_write_bio, + + TP_PROTO(struct super_block *sb, int type, struct bio *bio), + + TP_ARGS(sb, type, bio), + + TP_CONDITION(bio) +); + +DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_prepare_read_bio, + + TP_PROTO(struct super_block *sb, int type, struct bio *bio), + + TP_ARGS(sb, type, bio), + + TP_CONDITION(bio) +); + +DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_submit_read_bio, - TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, - struct bio *bio), + TP_PROTO(struct super_block *sb, int type, struct bio *bio), - TP_ARGS(sb, fio, bio), + TP_ARGS(sb, type, bio), TP_CONDITION(bio) ); -DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_read_bio, +DEFINE_EVENT_CONDITION(f2fs__bio, f2fs_submit_write_bio, - TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, - struct bio *bio), + TP_PROTO(struct super_block *sb, int type, struct bio *bio), - TP_ARGS(sb, fio, bio), + TP_ARGS(sb, type, bio), TP_CONDITION(bio) ); -- GitLab From 4e4c0d563d1f4d463dbba80866973c74486ef0ef Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 14 Dec 2016 10:12:56 -0800 Subject: [PATCH 0472/5498] f2fs: support IO alignment for DATA and NODE writes This patch implements IO alignment by filling dummy blocks in DATA and NODE write bios. If we can guarantee, for example, 32KB or 64KB for such the IOs, we can eliminate underlying dummy page problem which FTL conducts in order to close MLC or TLC partial written pages. Note that, - it requires "-o mode=lfs". - IO size should be power of 2, not exceed BIO_MAX_PAGES, 256. - read IO is still 4KB. - do checkpoint at fsync, if dummy NODE page was written. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 55 +++++++++++++++++++++++++++++++++++++++-- fs/f2fs/f2fs.h | 4 ++- fs/f2fs/segment.c | 9 +++++-- fs/f2fs/segment.h | 3 +++ fs/f2fs/super.c | 14 ++++++++++- include/linux/f2fs_fs.h | 6 +++++ 6 files changed, 85 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f8b08994a9b5..f4bc867e5b3a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -94,6 +94,17 @@ static void f2fs_write_end_io(struct bio *bio, int err) struct page *page = bvec->bv_page; enum count_type type = WB_DATA_TYPE(page); + if (IS_DUMMY_WRITTEN_PAGE(page)) { + set_page_private(page, (unsigned long)NULL); + ClearPagePrivate(page); + unlock_page(page); + mempool_free(page, sbi->write_io_dummy); + + if (unlikely(err)) + f2fs_stop_checkpoint(sbi, true); + continue; + } + fscrypt_pullback_bio_page(&page, true); if (unlikely(err)) { @@ -172,10 +183,42 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi, struct bio *bio, enum page_type type) { if (!is_read_io(bio_op(bio))) { + unsigned int start; + if (f2fs_sb_mounted_blkzoned(sbi->sb) && current->plug && (type == DATA || type == NODE)) blk_finish_plug(current->plug); + + if (type != DATA && type != NODE) + goto submit_io; + + start = bio->bi_iter.bi_size >> F2FS_BLKSIZE_BITS; + start %= F2FS_IO_SIZE(sbi); + + if (start == 0) + goto submit_io; + + /* fill dummy pages */ + for (; start < F2FS_IO_SIZE(sbi); start++) { + struct page *page = + mempool_alloc(sbi->write_io_dummy, + GFP_NOIO | __GFP_ZERO | __GFP_NOFAIL); + f2fs_bug_on(sbi, !page); + + SetPagePrivate(page); + set_page_private(page, (unsigned long)DUMMY_WRITTEN_PAGE); + lock_page(page); + if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) + f2fs_bug_on(sbi, 1); + } + /* + * In the NODE case, we lose next block address chain. So, we + * need to do checkpoint in f2fs_sync_file. + */ + if (type == NODE) + set_sbi_flag(sbi, SBI_NEED_CP); } +submit_io: if (is_read_io(bio_op(bio))) trace_f2fs_submit_read_bio(sbi->sb, type, bio); else @@ -320,13 +363,14 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) return 0; } -void f2fs_submit_page_mbio(struct f2fs_io_info *fio) +int f2fs_submit_page_mbio(struct f2fs_io_info *fio) { struct f2fs_sb_info *sbi = fio->sbi; enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); struct f2fs_bio_info *io; bool is_read = is_read_io(fio->op); struct page *bio_page; + int err = 0; io = is_read ? &sbi->read_io : &sbi->write_io[btype]; @@ -347,6 +391,12 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio) __submit_merged_bio(io); alloc_new: if (io->bio == NULL) { + if ((fio->type == DATA || fio->type == NODE) && + fio->new_blkaddr & F2FS_IO_SIZE_MASK(sbi)) { + err = -EAGAIN; + dec_page_count(sbi, WB_DATA_TYPE(bio_page)); + goto out_fail; + } io->bio = __bio_alloc(sbi, fio->new_blkaddr, BIO_MAX_PAGES, is_read); io->fio = *fio; @@ -360,9 +410,10 @@ alloc_new: io->last_block_in_bio = fio->new_blkaddr; f2fs_trace_ios(fio, 0); - +out_fail: up_write(&io->io_rwsem); trace_f2fs_submit_page_mbio(fio->page, fio); + return err; } static void __set_data_blkaddr(struct dnode_of_data *dn) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d0192ab0f595..d2daefc0816a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -885,6 +885,8 @@ struct f2fs_sb_info { struct f2fs_bio_info read_io; /* for read bios */ struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ struct mutex wio_mutex[NODE + 1]; /* bio ordering for NODE/DATA */ + int write_io_size_bits; /* Write IO size bits */ + mempool_t *write_io_dummy; /* Dummy pages */ /* for checkpoint */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ @@ -2269,7 +2271,7 @@ void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *, struct page *, nid_t, enum page_type, int); void f2fs_flush_merged_bios(struct f2fs_sb_info *); int f2fs_submit_page_bio(struct f2fs_io_info *); -void f2fs_submit_page_mbio(struct f2fs_io_info *); +int f2fs_submit_page_mbio(struct f2fs_io_info *); struct block_device *f2fs_target_device(struct f2fs_sb_info *, block_t, struct bio *); int f2fs_target_device_index(struct f2fs_sb_info *, block_t); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 97db4d2895a5..7939b48c5c2c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1684,15 +1684,20 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { int type = __get_segment_type(fio->page, fio->type); + int err; if (fio->type == NODE || fio->type == DATA) mutex_lock(&fio->sbi->wio_mutex[fio->type]); - +reallocate: allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, &fio->new_blkaddr, sum, type); /* writeout dirty page into bdev */ - f2fs_submit_page_mbio(fio); + err = f2fs_submit_page_mbio(fio); + if (err == -EAGAIN) { + fio->old_blkaddr = fio->new_blkaddr; + goto reallocate; + } if (fio->type == NODE || fio->type == DATA) mutex_unlock(&fio->sbi->wio_mutex[fio->type]); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 4fe8d9ccce62..502154022224 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -185,9 +185,12 @@ struct segment_allocation { * the page is atomically written, and it is in inmem_pages list. */ #define ATOMIC_WRITTEN_PAGE ((unsigned long)-1) +#define DUMMY_WRITTEN_PAGE ((unsigned long)-2) #define IS_ATOMIC_WRITTEN_PAGE(page) \ (page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE) +#define IS_DUMMY_WRITTEN_PAGE(page) \ + (page_private(page) == (unsigned long)DUMMY_WRITTEN_PAGE) struct inmem_pages { struct list_head list; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d8ae09e27fbb..8a0c58d251d5 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1750,6 +1750,8 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) FDEV(i).total_segments, FDEV(i).start_blk, FDEV(i).end_blk); } + f2fs_msg(sbi->sb, KERN_INFO, + "IO Block Size: %8d KB", F2FS_IO_SIZE_KB(sbi)); return 0; } @@ -1867,12 +1869,19 @@ try_onemore: if (err) goto free_options; + if (F2FS_IO_SIZE(sbi) > 1) { + sbi->write_io_dummy = + mempool_create_page_pool(F2FS_IO_SIZE(sbi) - 1, 0); + if (!sbi->write_io_dummy) + goto free_options; + } + /* get an inode for meta space */ sbi->meta_inode = f2fs_iget(sb, F2FS_META_INO(sbi)); if (IS_ERR(sbi->meta_inode)) { f2fs_msg(sb, KERN_ERR, "Failed to read F2FS meta data inode"); err = PTR_ERR(sbi->meta_inode); - goto free_options; + goto free_io_dummy; } err = get_valid_checkpoint(sbi); @@ -2090,6 +2099,9 @@ free_devices: free_meta_inode: make_bad_inode(sbi->meta_inode); iput(sbi->meta_inode); +free_io_dummy: + if (sbi->write_io_dummy) + mempool_destroy(sbi->write_io_dummy); free_options: destroy_percpu_info(sbi); kfree(options); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 083172fed2b7..8d156ac1f38b 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -36,6 +36,12 @@ #define F2FS_NODE_INO(sbi) (sbi->node_ino_num) #define F2FS_META_INO(sbi) (sbi->meta_ino_num) +#define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */ +#define F2FS_IO_SIZE_KB(sbi) (1 << ((sbi)->write_io_size_bits + 2)) /* KB */ +#define F2FS_IO_SIZE_BYTES(sbi) (1 << ((sbi)->write_io_size_bits + 12)) /* B */ +#define F2FS_IO_SIZE_BITS(sbi) ((sbi)->write_io_size_bits) /* power of 2 */ +#define F2FS_IO_SIZE_MASK(sbi) (F2FS_IO_SIZE(sbi) - 1) + /* This flag is used by node and meta inodes, and by recovery */ #define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) #define GFP_F2FS_HIGH_ZERO (GFP_NOFS | __GFP_ZERO | __GFP_HIGHMEM) -- GitLab From 3f834ca41fb8bb2b2c62f5476ffdefebcad6b05e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Dec 2016 17:09:19 -0800 Subject: [PATCH 0473/5498] f2fs: get io size bit from mount option This patch adds to set io_size_bits from mount option. Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 2 ++ fs/f2fs/super.c | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index c809792186b2..c034b2268a9e 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -156,6 +156,8 @@ data_flush Enable data flushing before checkpoint in order to mode=%s Control block allocation mode which supports "adaptive" and "lfs". In "lfs" mode, there should be no random writes towards main area. +io_bits=%u Set the bit size of write IO requests. It should be set + with "mode=lfs". ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8a0c58d251d5..9b9b98fa0603 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -101,6 +101,7 @@ enum { Opt_noinline_data, Opt_data_flush, Opt_mode, + Opt_io_size_bits, Opt_fault_injection, Opt_err, }; @@ -131,6 +132,7 @@ static match_table_t f2fs_tokens = { {Opt_noinline_data, "noinline_data"}, {Opt_data_flush, "data_flush"}, {Opt_mode, "mode=%s"}, + {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, {Opt_err, NULL}, }; @@ -531,6 +533,17 @@ static int parse_options(struct super_block *sb, char *options) } kfree(name); break; + case Opt_io_size_bits: + if (args->from && match_int(args, &arg)) + return -EINVAL; + if (arg > __ilog2_u32(BIO_MAX_PAGES)) { + f2fs_msg(sb, KERN_WARNING, + "Not support %d, larger than %d", + 1 << arg, BIO_MAX_PAGES); + return -EINVAL; + } + sbi->write_io_size_bits = arg; + break; case Opt_fault_injection: if (args->from && match_int(args, &arg)) return -EINVAL; @@ -548,6 +561,13 @@ static int parse_options(struct super_block *sb, char *options) return -EINVAL; } } + + if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) { + f2fs_msg(sb, KERN_ERR, + "Should set mode=lfs with %uKB-sized IO", + F2FS_IO_SIZE_KB(sbi)); + return -EINVAL; + } return 0; } @@ -905,6 +925,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) else if (test_opt(sbi, LFS)) seq_puts(seq, "lfs"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); + if (F2FS_IO_SIZE_BITS(sbi)) + seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi)); return 0; } -- GitLab From becdf19982da6987d0063a742e13a30fb59a0489 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 28 Dec 2016 13:55:09 -0800 Subject: [PATCH 0474/5498] f2fs: show the max number of atomic operations This patch adds to show the max number of atomic operations which are conducting concurrently. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 7 +++++++ fs/f2fs/f2fs.h | 17 +++++++++++++++++ fs/f2fs/file.c | 8 ++++++-- fs/f2fs/segment.c | 1 + 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 29d8feaf09ee..e4de9755e115 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -50,6 +50,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); + si->aw_cnt = atomic_read(&sbi->aw_cnt); + si->max_aw_cnt = atomic_read(&sbi->max_aw_cnt); si->nr_wb_cp_data = get_pages(sbi, F2FS_WB_CP_DATA); si->nr_wb_data = get_pages(sbi, F2FS_WB_DATA); si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; @@ -258,6 +260,8 @@ static int stat_show(struct seq_file *s, void *v) si->inline_dir); seq_printf(s, " - Orphan Inode: %u\n", si->orphans); + seq_printf(s, " - Atomic write count: %4d (Max. %4d)\n", + si->aw_cnt, si->max_aw_cnt); seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", si->main_area_segs, si->main_area_sections, si->main_area_zones); @@ -416,6 +420,9 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) atomic_set(&sbi->inline_dir, 0); atomic_set(&sbi->inplace_count, 0); + atomic_set(&sbi->aw_cnt, 0); + atomic_set(&sbi->max_aw_cnt, 0); + mutex_lock(&f2fs_stat_mutex); list_add_tail(&si->stat_list, &f2fs_stat_list); mutex_unlock(&f2fs_stat_mutex); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d2daefc0816a..c73848d18248 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -977,6 +977,8 @@ struct f2fs_sb_info { atomic_t inline_xattr; /* # of inline_xattr inodes */ atomic_t inline_inode; /* # of inline_data inodes */ atomic_t inline_dir; /* # of inline_dentry inodes */ + atomic_t aw_cnt; /* # of atomic writes */ + atomic_t max_aw_cnt; /* max # of atomic writes */ int bg_gc; /* background gc calls */ unsigned int ndirty_inode[NR_INODE_TYPE]; /* # of dirty inodes */ #endif @@ -2331,6 +2333,7 @@ struct f2fs_stat_info { int total_count, utilization; int bg_gc, nr_wb_cp_data, nr_wb_data; int inline_xattr, inline_inode, inline_dir, orphans; + int aw_cnt, max_aw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; unsigned int bimodal, avg_vblocks; int util_free, util_valid, util_invalid; @@ -2402,6 +2405,17 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) ((sbi)->block_count[(curseg)->alloc_type]++) #define stat_inc_inplace_blocks(sbi) \ (atomic_inc(&(sbi)->inplace_count)) +#define stat_inc_atomic_write(inode) \ + (atomic_inc(&F2FS_I_SB(inode)->aw_cnt)); +#define stat_dec_atomic_write(inode) \ + (atomic_dec(&F2FS_I_SB(inode)->aw_cnt)); +#define stat_update_max_atomic_write(inode) \ + do { \ + int cur = atomic_read(&F2FS_I_SB(inode)->aw_cnt); \ + int max = atomic_read(&F2FS_I_SB(inode)->max_aw_cnt); \ + if (cur > max) \ + atomic_set(&F2FS_I_SB(inode)->max_aw_cnt, cur); \ + } while (0) #define stat_inc_seg_count(sbi, type, gc_type) \ do { \ struct f2fs_stat_info *si = F2FS_STAT(sbi); \ @@ -2455,6 +2469,9 @@ void f2fs_destroy_root_stats(void); #define stat_dec_inline_inode(inode) #define stat_inc_inline_dir(inode) #define stat_dec_inline_dir(inode) +#define stat_inc_atomic_write(inode) +#define stat_dec_atomic_write(inode) +#define stat_update_max_atomic_write(inode) #define stat_inc_seg_type(sbi, curseg) #define stat_inc_block_count(sbi, curseg) #define stat_inc_inplace_blocks(sbi) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 732256d457d2..701d3cfdae4b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1557,6 +1557,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) if (ret) clear_inode_flag(inode, FI_ATOMIC_FILE); out: + stat_inc_atomic_write(inode); + stat_update_max_atomic_write(inode); inode_unlock(inode); mnt_drop_write_file(filp); return ret; @@ -1586,9 +1588,11 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) set_inode_flag(inode, FI_ATOMIC_FILE); goto err_out; } + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); + stat_dec_atomic_write(inode); + } else { + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); } - - ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); err_out: inode_unlock(inode); mnt_drop_write_file(filp); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7939b48c5c2c..4cc4cac53d88 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -243,6 +243,7 @@ void drop_inmem_pages(struct inode *inode) struct f2fs_inode_info *fi = F2FS_I(inode); clear_inode_flag(inode, FI_ATOMIC_FILE); + stat_dec_atomic_write(inode); mutex_lock(&fi->inmem_lock); __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); -- GitLab From 6083e1f7147fd732029939c9dfda977759ce146b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 28 Dec 2016 17:31:15 -0800 Subject: [PATCH 0475/5498] f2fs: don't allow encrypted operations without keys This patch fixes the renaming bug on encrypted filenames, which was pointed by (ext4: don't allow encrypted operations without keys) Cc: Theodore Ts'o Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 172c8a189e71..17b7ead25115 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -675,6 +675,12 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, bool is_old_inline = f2fs_has_inline_dentry(old_dir); int err = -ENOENT; + if ((f2fs_encrypted_inode(old_dir) && + !fscrypt_has_encryption_key(old_dir)) || + (f2fs_encrypted_inode(new_dir) && + !fscrypt_has_encryption_key(new_dir))) + return -ENOKEY; + if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && !fscrypt_has_permitted_context(new_dir, old_inode)) { err = -EPERM; @@ -855,6 +861,12 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, int old_nlink = 0, new_nlink = 0; int err = -ENOENT; + if ((f2fs_encrypted_inode(old_dir) && + !fscrypt_has_encryption_key(old_dir)) || + (f2fs_encrypted_inode(new_dir) && + !fscrypt_has_encryption_key(new_dir))) + return -ENOKEY; + if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) && (old_dir != new_dir) && (!fscrypt_has_permitted_context(new_dir, old_inode) || -- GitLab From 1828b04ae0faf61e08accf981fa4d6a8aebe5a2d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 3 Jan 2017 17:19:30 -0800 Subject: [PATCH 0476/5498] f2fs: drop exist_data for inline_data when truncated to 0 A test program gets the SEEK_DATA with two values between a new created file and the exist file on f2fs filesystem. F2FS filesystem, (the first "test1" is a new file) SEEK_DATA size != 0 (offset = 8192) SEEK_DATA size != 0 (offset = 4096) PNFS filesystem, (the first "test1" is a new file) SEEK_DATA size != 0 (offset = 4096) SEEK_DATA size != 0 (offset = 4096) int main(int argc, char **argv) { char *filename = argv[1]; int offset = 1, i = 0, fd = -1; if (argc < 2) { printf("Usage: %s f2fsfilename\n", argv[0]); return -1; } /* if (!access(filename, F_OK) || errno != ENOENT) { printf("Needs a new file for test, %m\n"); return -1; }*/ fd = open(filename, O_RDWR | O_CREAT, 0777); if (fd < 0) { printf("Create test file %s failed, %m\n", filename); return -1; } for (i = 0; i < 20; i++) { offset = 1 << i; ftruncate(fd, 0); lseek(fd, offset, SEEK_SET); write(fd, "test", 5); /* Get the alloc size by seek data equal zero*/ if (lseek(fd, 0, SEEK_DATA)) { printf("SEEK_DATA size != 0 (offset = %d)\n", offset); break; } } close(fd); return 0; } Reported-and-Tested-by: Kinglong Mee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 701d3cfdae4b..5f0715d95765 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -572,6 +572,8 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) if (f2fs_has_inline_data(inode)) { if (truncate_inline_inode(ipage, from)) set_page_dirty(ipage); + if (from == 0) + clear_inode_flag(inode, FI_DATA_EXIST); f2fs_put_page(ipage, 1); truncate_page = true; goto out; -- GitLab From ff2bfb4d94e78d707ad978ba766f2a7e2d5fd1a5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 29 Dec 2016 14:07:53 -0800 Subject: [PATCH 0477/5498] f2fs: relax async discard commands more This patch relaxes async discard commands to avoid waiting its end_io during checkpoint. Instead of waiting them during checkpoint, it will be done when actually reusing them. Test on initial partition of nvme drive. # time fstrim /mnt/test Before : 6.158s After : 4.822s Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 ++----- fs/f2fs/f2fs.h | 4 +++- fs/f2fs/segment.c | 24 +++++++++++++++++++----- fs/f2fs/super.c | 3 +++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 6e946c276758..3de524589905 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1255,7 +1255,6 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_bug_on(sbi, prefree_segments(sbi)); flush_sit_entries(sbi, cpc); clear_prefree_segments(sbi, cpc); - f2fs_wait_all_discard_bio(sbi); unblock_operations(sbi); goto out; } @@ -1274,12 +1273,10 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* unlock all the fs_lock[] in do_checkpoint() */ err = do_checkpoint(sbi, cpc); - if (err) { + if (err) release_discard_addrs(sbi); - } else { + else clear_prefree_segments(sbi, cpc); - f2fs_wait_all_discard_bio(sbi); - } unblock_operations(sbi); stat_inc_cp_count(sbi->stat_info); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c73848d18248..929a7cf94dfb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -276,6 +276,8 @@ struct discard_entry { struct bio_entry { struct list_head list; + block_t lstart; + block_t len; struct bio *bio; struct completion event; int error; @@ -2206,7 +2208,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *, bool); void invalidate_blocks(struct f2fs_sb_info *, block_t); bool is_checkpointed_data(struct f2fs_sb_info *, block_t); void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); -void f2fs_wait_all_discard_bio(struct f2fs_sb_info *); +void f2fs_wait_discard_bio(struct f2fs_sb_info *, block_t); void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *); void release_discard_addrs(struct f2fs_sb_info *); int npages_for_summary_flush(struct f2fs_sb_info *, bool); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4cc4cac53d88..c98f771c7ee1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -625,20 +625,23 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) } static struct bio_entry *__add_bio_entry(struct f2fs_sb_info *sbi, - struct bio *bio) + struct bio *bio, block_t lstart, block_t len) { struct list_head *wait_list = &(SM_I(sbi)->wait_list); struct bio_entry *be = f2fs_kmem_cache_alloc(bio_entry_slab, GFP_NOFS); INIT_LIST_HEAD(&be->list); be->bio = bio; + be->lstart = lstart; + be->len = len; init_completion(&be->event); list_add_tail(&be->list, wait_list); return be; } -void f2fs_wait_all_discard_bio(struct f2fs_sb_info *sbi) +/* This should be covered by global mutex, &sit_i->sentry_lock */ +void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { struct list_head *wait_list = &(SM_I(sbi)->wait_list); struct bio_entry *be, *tmp; @@ -647,7 +650,15 @@ void f2fs_wait_all_discard_bio(struct f2fs_sb_info *sbi) struct bio *bio = be->bio; int err; - wait_for_completion_io(&be->event); + if (!completion_done(&be->event)) { + if ((be->lstart <= blkaddr && + blkaddr < be->lstart + be->len) || + blkaddr == NULL_ADDR) + wait_for_completion_io(&be->event); + else + continue; + } + err = be->error; if (err == -EOPNOTSUPP) err = 0; @@ -756,6 +767,7 @@ static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) { struct bio *bio = NULL; + block_t lblkstart = blkstart; int err; trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); @@ -770,13 +782,13 @@ static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, SECTOR_FROM_BLOCK(blklen), GFP_NOFS, 0, &bio); if (!err && bio) { - struct bio_entry *be = __add_bio_entry(sbi, bio); + struct bio_entry *be = __add_bio_entry(sbi, bio, + lblkstart, blklen); bio->bi_private = be; bio->bi_end_io = f2fs_submit_bio_wait_endio; submit_bio(REQ_SYNC, bio); } - return err; } @@ -1655,6 +1667,8 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); + f2fs_wait_discard_bio(sbi, *new_blkaddr); + /* * __add_sum_entry should be resided under the curseg_mutex * because, this function updates a summary entry in the diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9b9b98fa0603..20655bbbba44 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -757,6 +757,9 @@ static void f2fs_put_super(struct super_block *sb) write_checkpoint(sbi, &cpc); } + /* be sure to wait for any on-going discard commands */ + f2fs_wait_discard_bio(sbi, NULL_ADDR); + /* write_checkpoint can update stat informaion */ f2fs_destroy_stats(sbi); -- GitLab From e8a9d1c835ded6fcd39b7f51e54da3d33cd967b1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 29 Dec 2016 16:58:54 -0800 Subject: [PATCH 0478/5498] f2fs: avoid needless checkpoint in f2fs_trim_fs The f2fs_trim_fs() doesn't need to do checkpoint if there are newly allocated data blocks only which didn't change the critical checkpoint data such as nat and sit entries. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 3de524589905..cbd8f22ac881 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1249,14 +1249,15 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_flush_merged_bios(sbi); /* this is the case of multiple fstrims without any changes */ - if (cpc->reason == CP_DISCARD && !is_sbi_flag_set(sbi, SBI_IS_DIRTY)) { - f2fs_bug_on(sbi, NM_I(sbi)->dirty_nat_cnt); - f2fs_bug_on(sbi, SIT_I(sbi)->dirty_sentries); - f2fs_bug_on(sbi, prefree_segments(sbi)); - flush_sit_entries(sbi, cpc); - clear_prefree_segments(sbi, cpc); - unblock_operations(sbi); - goto out; + if (cpc->reason == CP_DISCARD) { + if (NM_I(sbi)->dirty_nat_cnt == 0 && + SIT_I(sbi)->dirty_sentries == 0 && + prefree_segments(sbi) == 0) { + flush_sit_entries(sbi, cpc); + clear_prefree_segments(sbi, cpc); + unblock_operations(sbi); + goto out; + } } /* -- GitLab From 8647f573b1a30b7ef124dd32ad1b5cc2e82ec9fb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 29 Dec 2016 22:06:15 -0800 Subject: [PATCH 0479/5498] f2fs: return fs_trim if there is no candidate If there is no candidate to submit discard command during f2fs_trim_fs, let's return without checkpoint. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 5 +++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 33 ++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index cbd8f22ac881..b5dd571381c3 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1250,6 +1250,11 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* this is the case of multiple fstrims without any changes */ if (cpc->reason == CP_DISCARD) { + if (!exist_trim_candidates(sbi, cpc)) { + unblock_operations(sbi); + goto out; + } + if (NM_I(sbi)->dirty_nat_cnt == 0 && SIT_I(sbi)->dirty_sentries == 0 && prefree_segments(sbi) == 0) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 929a7cf94dfb..88f97d503edf 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2214,6 +2214,7 @@ void release_discard_addrs(struct f2fs_sb_info *); int npages_for_summary_flush(struct f2fs_sb_info *, bool); void allocate_new_segments(struct f2fs_sb_info *); int f2fs_trim_fs(struct f2fs_sb_info *, struct fstrim_range *); +bool exist_trim_candidates(struct f2fs_sb_info *, struct cp_control *); struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); void update_meta_page(struct f2fs_sb_info *, void *, block_t); void write_meta_page(struct f2fs_sb_info *, struct page *); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c98f771c7ee1..3ff610fe33e0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -914,7 +914,8 @@ done: SM_I(sbi)->nr_discards += end - start; } -static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) +static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, + bool check_only) { int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); int max_blocks = sbi->blocks_per_seg; @@ -928,12 +929,12 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) int i; if (se->valid_blocks == max_blocks || !f2fs_discard_en(sbi)) - return; + return false; if (!force) { if (!test_opt(sbi, DISCARD) || !se->valid_blocks || SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards) - return; + return false; } /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ @@ -951,8 +952,12 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) && (end - start) < cpc->trim_minlen) continue; + if (check_only) + return true; + __add_discard_entry(sbi, cpc, se, start, end); } + return false; } void release_discard_addrs(struct f2fs_sb_info *sbi) @@ -1533,6 +1538,24 @@ static const struct segment_allocation default_salloc_ops = { .allocate_segment = allocate_segment_by_default, }; +bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{ + __u64 trim_start = cpc->trim_start; + bool has_candidate = false; + + mutex_lock(&SIT_I(sbi)->sentry_lock); + for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { + if (add_discard_addrs(sbi, cpc, true)) { + has_candidate = true; + break; + } + } + mutex_unlock(&SIT_I(sbi)->sentry_lock); + + cpc->trim_start = trim_start; + return has_candidate; +} + int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) { __u64 start = F2FS_BYTES_TO_BLK(range->start); @@ -2329,7 +2352,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* add discard candidates */ if (cpc->reason != CP_DISCARD) { cpc->trim_start = segno; - add_discard_addrs(sbi, cpc); + add_discard_addrs(sbi, cpc, false); } if (to_journal) { @@ -2367,7 +2390,7 @@ out: __u64 trim_start = cpc->trim_start; for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) - add_discard_addrs(sbi, cpc); + add_discard_addrs(sbi, cpc, false); cpc->trim_start = trim_start; } -- GitLab From 652069314b5b755251d70aa04f65da0d5295289f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 7 Jan 2017 18:49:42 +0800 Subject: [PATCH 0480/5498] f2fs: clean up with list_{first, last}_entry Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 ++-- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 6 ++++++ fs/f2fs/node.h | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index b5dd571381c3..587641925477 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -892,7 +892,7 @@ retry: F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA)); return 0; } - fi = list_entry(head->next, struct f2fs_inode_info, dirty_list); + fi = list_first_entry(head, struct f2fs_inode_info, dirty_list); inode = igrab(&fi->vfs_inode); spin_unlock(&sbi->inode_lock[type]); if (inode) { @@ -925,7 +925,7 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi) spin_unlock(&sbi->inode_lock[DIRTY_META]); return 0; } - fi = list_entry(head->next, struct f2fs_inode_info, + fi = list_first_entry(head, struct f2fs_inode_info, gdirty_list); inode = igrab(&fi->vfs_inode); spin_unlock(&sbi->inode_lock[DIRTY_META]); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f4bc867e5b3a..1538946fb495 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1144,7 +1144,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, prefetchw(&page->flags); if (pages) { - page = list_entry(pages->prev, struct page, lru); + page = list_last_entry(pages, struct page, lru); list_del(&page->lru); if (add_to_page_cache_lru(page, mapping, page->index, GFP_KERNEL)) @@ -1262,7 +1262,7 @@ static int f2fs_read_data_pages(struct file *file, struct list_head *pages, unsigned nr_pages) { struct inode *inode = file->f_mapping->host; - struct page *page = list_entry(pages->prev, struct page, lru); + struct page *page = list_last_entry(pages, struct page, lru); trace_f2fs_readpages(inode, page, nr_pages); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 88f97d503edf..4aaa973db9a9 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -152,6 +152,12 @@ static inline void inode_unlock(struct inode *inode) ____ptr ? rb_entry(____ptr, type, member) : NULL; \ }) +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + /** * wq_has_sleeper - check if there are any waiting processes * @wq: wait queue head diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index e7997e240366..9278b21ee073 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -174,7 +174,7 @@ static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) spin_unlock(&nm_i->nid_list_lock); return; } - fnid = list_entry(nm_i->nid_list[FREE_NID_LIST].next, + fnid = list_first_entry(&nm_i->nid_list[FREE_NID_LIST], struct free_nid, list); *nid = fnid->nid; spin_unlock(&nm_i->nid_list_lock); -- GitLab From 64b87e490032264ca8f6d88c430b5aa00c3a6709 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 7 Jan 2017 18:50:26 +0800 Subject: [PATCH 0481/5498] f2fs: introduce FI_ATOMIC_COMMIT This patch introduces a new flag to indicate inode status of doing atomic write committing, so that, we can keep atomic write status for inode during atomic committing, then we can skip GCing pages of atomic write inode, that avoids random GCed datas being mixed with current transaction, so isolation of transaction can be kept. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 6 ++++++ fs/f2fs/file.c | 11 ++++++----- fs/f2fs/gc.c | 6 ++++++ fs/f2fs/segment.c | 10 +++++++--- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1538946fb495..8316b0a37a58 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1972,7 +1972,7 @@ static int f2fs_set_data_page_dirty(struct page *page) if (!PageUptodate(page)) SetPageUptodate(page); - if (f2fs_is_atomic_file(inode)) { + if (f2fs_is_atomic_file(inode) && !f2fs_is_commit_atomic_write(inode)) { if (!IS_ATOMIC_WRITTEN_PAGE(page)) { register_inmem_page(inode, page); return 1; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4aaa973db9a9..61b4e34878d6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1738,6 +1738,7 @@ enum { FI_UPDATE_WRITE, /* inode has in-place-update data */ FI_NEED_IPU, /* used for ipu per file */ FI_ATOMIC_FILE, /* indicate atomic file */ + FI_ATOMIC_COMMIT, /* indicate the state of atomical committing */ FI_VOLATILE_FILE, /* indicate volatile file */ FI_FIRST_BLOCK_WRITTEN, /* indicate #0 data block was written */ FI_DROP_CACHE, /* drop dirty page cache */ @@ -1927,6 +1928,11 @@ static inline bool f2fs_is_atomic_file(struct inode *inode) return is_inode_flag_set(inode, FI_ATOMIC_FILE); } +static inline bool f2fs_is_commit_atomic_write(struct inode *inode) +{ + return is_inode_flag_set(inode, FI_ATOMIC_COMMIT); +} + static inline bool f2fs_is_volatile_file(struct inode *inode) { return is_inode_flag_set(inode, FI_VOLATILE_FILE); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 5f0715d95765..f52d637429e3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1584,14 +1584,15 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) goto err_out; if (f2fs_is_atomic_file(inode)) { - clear_inode_flag(inode, FI_ATOMIC_FILE); ret = commit_inmem_pages(inode); - if (ret) { - set_inode_flag(inode, FI_ATOMIC_FILE); + if (ret) goto err_out; - } + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); - stat_dec_atomic_write(inode); + if (!ret) { + clear_inode_flag(inode, FI_ATOMIC_FILE); + stat_dec_atomic_write(inode); + } } else { ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 88bfc3dff496..88e5e7b10ab6 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -569,6 +569,9 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, if (!check_valid_map(F2FS_I_SB(inode), segno, off)) goto out; + if (f2fs_is_atomic_file(inode)) + goto out; + set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE); if (err) @@ -661,6 +664,9 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, if (!check_valid_map(F2FS_I_SB(inode), segno, off)) goto out; + if (f2fs_is_atomic_file(inode)) + goto out; + if (gc_type == BG_GC) { if (PageWriteback(page)) goto out; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3ff610fe33e0..068b1da89a9a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -242,12 +242,12 @@ void drop_inmem_pages(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); - clear_inode_flag(inode, FI_ATOMIC_FILE); - stat_dec_atomic_write(inode); - mutex_lock(&fi->inmem_lock); __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); mutex_unlock(&fi->inmem_lock); + + clear_inode_flag(inode, FI_ATOMIC_FILE); + stat_dec_atomic_write(inode); } static int __commit_inmem_pages(struct inode *inode, @@ -316,6 +316,8 @@ int commit_inmem_pages(struct inode *inode) f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); + set_inode_flag(inode, FI_ATOMIC_COMMIT); + mutex_lock(&fi->inmem_lock); err = __commit_inmem_pages(inode, &revoke_list); if (err) { @@ -337,6 +339,8 @@ int commit_inmem_pages(struct inode *inode) } mutex_unlock(&fi->inmem_lock); + clear_inode_flag(inode, FI_ATOMIC_COMMIT); + f2fs_unlock_op(sbi); return err; } -- GitLab From e56a89f1749d2b96ecc2b0feadba868e5fce7eac Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 7 Jan 2017 18:51:01 +0800 Subject: [PATCH 0482/5498] f2fs: check in-memory block bitmap This patch adds a mirror for valid block bitmap, and use it to detect in-memory bitmap corruption which may be caused by bit-transition of cache or memory overflow. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 32 ++++++++++++++++++++++++++++++-- fs/f2fs/segment.h | 6 ++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 068b1da89a9a..1b9402694c50 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1101,14 +1101,32 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) /* Update valid block bitmap */ if (del > 0) { - if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) + if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) { +#ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_test_and_set_bit(offset, + se->cur_valid_map_mir)) + f2fs_bug_on(sbi, 1); + else + WARN_ON(1); +#else f2fs_bug_on(sbi, 1); +#endif + } if (f2fs_discard_en(sbi) && !f2fs_test_and_set_bit(offset, se->discard_map)) sbi->discard_blks--; } else { - if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) + if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) { +#ifdef CONFIG_F2FS_CHECK_FS + if (!f2fs_test_and_clear_bit(offset, + se->cur_valid_map_mir)) + f2fs_bug_on(sbi, 1); + else + WARN_ON(1); +#else f2fs_bug_on(sbi, 1); +#endif + } if (f2fs_discard_en(sbi) && f2fs_test_and_clear_bit(offset, se->discard_map)) sbi->discard_blks++; @@ -2437,6 +2455,13 @@ static int build_sit_info(struct f2fs_sb_info *sbi) !sit_i->sentries[start].ckpt_valid_map) return -ENOMEM; +#ifdef CONFIG_F2FS_CHECK_FS + sit_i->sentries[start].cur_valid_map_mir + = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->sentries[start].cur_valid_map_mir) + return -ENOMEM; +#endif + if (f2fs_discard_en(sbi)) { sit_i->sentries[start].discard_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); @@ -2866,6 +2891,9 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) if (sit_i->sentries) { for (start = 0; start < MAIN_SEGS(sbi); start++) { kfree(sit_i->sentries[start].cur_valid_map); +#ifdef CONFIG_F2FS_CHECK_FS + kfree(sit_i->sentries[start].cur_valid_map_mir); +#endif kfree(sit_i->sentries[start].ckpt_valid_map); kfree(sit_i->sentries[start].discard_map); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 502154022224..bec503f54af5 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -163,6 +163,9 @@ struct seg_entry { unsigned int ckpt_valid_blocks:10; /* # of valid blocks last cp */ unsigned int padding:6; /* padding */ unsigned char *cur_valid_map; /* validity bitmap of blocks */ +#ifdef CONFIG_F2FS_CHECK_FS + unsigned char *cur_valid_map_mir; /* mirror of current valid bitmap */ +#endif /* * # of valid blocks and the validity bitmap stored in the the last * checkpoint pack. This information is used by the SSR mode. @@ -319,6 +322,9 @@ static inline void seg_info_from_raw_sit(struct seg_entry *se, se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs); memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); +#ifdef CONFIG_F2FS_CHECK_FS + memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE); +#endif se->type = GET_SIT_TYPE(rs); se->mtime = le64_to_cpu(rs->mtime); } -- GitLab From 5be4872d7c0a108ceb4356337fb6f9d914f83664 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 7 Jan 2017 18:52:01 +0800 Subject: [PATCH 0483/5498] f2fs: check in-memory nat version bitmap This patch adds a mirror for nat version bitmap, and use it to detect in-memory bitmap corruption which may be caused by bit-transition of cache or memory overflow. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +++ fs/f2fs/node.c | 11 +++++++++++ fs/f2fs/node.h | 15 +++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 61b4e34878d6..183fe154d68c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -639,6 +639,9 @@ struct f2fs_nm_info { /* for checkpoint */ char *nat_bitmap; /* NAT bitmap pointer */ +#ifdef CONFIG_F2FS_CHECK_FS + char *nat_bitmap_mir; /* NAT bitmap mirror */ +#endif int bitmap_size; /* bitmap size */ }; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9bb6d024c07c..7d00bdd0e636 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2372,6 +2372,14 @@ static int init_node_manager(struct f2fs_sb_info *sbi) GFP_KERNEL); if (!nm_i->nat_bitmap) return -ENOMEM; + +#ifdef CONFIG_F2FS_CHECK_FS + nm_i->nat_bitmap_mir = kmemdup(version_bitmap, nm_i->bitmap_size, + GFP_KERNEL); + if (!nm_i->nat_bitmap_mir) + return -ENOMEM; +#endif + return 0; } @@ -2446,6 +2454,9 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) up_write(&nm_i->nat_tree_lock); kfree(nm_i->nat_bitmap); +#ifdef CONFIG_F2FS_CHECK_FS + kfree(nm_i->nat_bitmap_mir); +#endif sbi->nm_info = NULL; kfree(nm_i); } diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 9278b21ee073..29ff783eb9c3 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -186,6 +186,12 @@ static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) static inline void get_nat_bitmap(struct f2fs_sb_info *sbi, void *addr) { struct f2fs_nm_info *nm_i = NM_I(sbi); + +#ifdef CONFIG_F2FS_CHECK_FS + if (memcmp(nm_i->nat_bitmap, nm_i->nat_bitmap_mir, + nm_i->bitmap_size)) + f2fs_bug_on(sbi, 1); +#endif memcpy(addr, nm_i->nat_bitmap, nm_i->bitmap_size); } @@ -203,6 +209,12 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start) (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & (sbi->blocks_per_seg - 1))); +#ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_test_bit(block_off, nm_i->nat_bitmap) != + f2fs_test_bit(block_off, nm_i->nat_bitmap_mir)) + f2fs_bug_on(sbi, 1); +#endif + if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; @@ -228,6 +240,9 @@ static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid) unsigned int block_off = NAT_BLOCK_OFFSET(start_nid); f2fs_change_bit(block_off, nm_i->nat_bitmap); +#ifdef CONFIG_F2FS_CHECK_FS + f2fs_change_bit(block_off, nm_i->nat_bitmap_mir); +#endif } static inline nid_t ino_of_node(struct page *node_page) -- GitLab From fb66e13b87a344587297b65b4b9411646db8ba90 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 7 Jan 2017 18:52:34 +0800 Subject: [PATCH 0484/5498] f2fs: check in-memory sit version bitmap This patch adds a mirror for sit version bitmap, and use it to detect in-memory bitmap corruption which may be caused by bit-transition of cache or memory overflow. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 16 ++++++++++++---- fs/f2fs/segment.h | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1b9402694c50..db3d18b6e0b9 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2426,7 +2426,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct sit_info *sit_i; unsigned int sit_segs, start; - char *src_bitmap, *dst_bitmap; + char *src_bitmap; unsigned int bitmap_size; /* allocate memory for SIT information */ @@ -2488,17 +2488,22 @@ static int build_sit_info(struct f2fs_sb_info *sbi) bitmap_size = __bitmap_size(sbi, SIT_BITMAP); src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); - dst_bitmap = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL); - if (!dst_bitmap) + sit_i->sit_bitmap = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL); + if (!sit_i->sit_bitmap) return -ENOMEM; +#ifdef CONFIG_F2FS_CHECK_FS + sit_i->sit_bitmap_mir = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL); + if (!sit_i->sit_bitmap_mir) + return -ENOMEM; +#endif + /* init SIT information */ sit_i->s_ops = &default_salloc_ops; sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; sit_i->written_valid_blocks = 0; - sit_i->sit_bitmap = dst_bitmap; sit_i->bitmap_size = bitmap_size; sit_i->dirty_sentries = 0; sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; @@ -2906,6 +2911,9 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) SM_I(sbi)->sit_info = NULL; kfree(sit_i->sit_bitmap); +#ifdef CONFIG_F2FS_CHECK_FS + kfree(sit_i->sit_bitmap_mir); +#endif kfree(sit_i); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index bec503f54af5..4b0e1bd621a1 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -208,6 +208,9 @@ struct sit_info { block_t sit_blocks; /* # of blocks used by SIT area */ block_t written_valid_blocks; /* # of valid blocks in main area */ char *sit_bitmap; /* SIT bitmap pointer */ +#ifdef CONFIG_F2FS_CHECK_FS + char *sit_bitmap_mir; /* SIT bitmap mirror */ +#endif unsigned int bitmap_size; /* SIT bitmap size */ unsigned long *tmp_map; /* bitmap for temporal use */ @@ -422,6 +425,12 @@ static inline void get_sit_bitmap(struct f2fs_sb_info *sbi, void *dst_addr) { struct sit_info *sit_i = SIT_I(sbi); + +#ifdef CONFIG_F2FS_CHECK_FS + if (memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir, + sit_i->bitmap_size)) + f2fs_bug_on(sbi, 1); +#endif memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size); } @@ -642,6 +651,12 @@ static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, check_seg_range(sbi, start); +#ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_test_bit(offset, sit_i->sit_bitmap) != + f2fs_test_bit(offset, sit_i->sit_bitmap_mir)) + f2fs_bug_on(sbi, 1); +#endif + /* calculate sit block address */ if (f2fs_test_bit(offset, sit_i->sit_bitmap)) blk_addr += sit_i->sit_blocks; @@ -667,6 +682,9 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start) unsigned int block_off = SIT_BLOCK_OFFSET(start); f2fs_change_bit(block_off, sit_i->sit_bitmap); +#ifdef CONFIG_F2FS_CHECK_FS + f2fs_change_bit(block_off, sit_i->sit_bitmap_mir); +#endif } static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi) -- GitLab From 6de48e16e82e52c90cb9cbf319350495dce692eb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 9 Jan 2017 14:13:03 -0800 Subject: [PATCH 0485/5498] f2fs: clean up flush/discard command namings This patch simply cleans up the names for flush/discard commands. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/debug.c | 2 +- fs/f2fs/f2fs.h | 19 +++++---- fs/f2fs/segment.c | 99 ++++++++++++++++++++++++----------------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index e4de9755e115..0a8daa40f642 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -194,7 +194,7 @@ get_cache: si->cache_mem += sizeof(struct f2fs_gc_kthread); /* build merge flush thread */ - if (SM_I(sbi)->cmd_control_info) + if (SM_I(sbi)->fcc_info) si->cache_mem += sizeof(struct flush_cmd_control); /* free nids */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 183fe154d68c..28f43c212e66 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -280,12 +280,12 @@ struct discard_entry { int len; /* # of consecutive blocks of the discard */ }; -struct bio_entry { - struct list_head list; - block_t lstart; - block_t len; - struct bio *bio; - struct completion event; +struct discard_cmd { + struct list_head list; /* command list */ + struct completion wait; /* compleation */ + block_t lstart; /* logical start address */ + block_t len; /* length */ + struct bio *bio; /* bio */ int error; }; @@ -733,8 +733,8 @@ struct f2fs_sm_info { unsigned int rec_prefree_segments; /* for small discard management */ - struct list_head discard_list; /* 4KB discard list */ - struct list_head wait_list; /* linked with issued discard bio */ + struct list_head discard_entry_list; /* 4KB discard entry list */ + struct list_head discard_cmd_list; /* discard cmd list */ int nr_discards; /* # of discards in the list */ int max_discards; /* max. discards to be issued */ @@ -748,8 +748,7 @@ struct f2fs_sm_info { unsigned int min_fsync_blocks; /* threshold for fsync */ /* for flush command control */ - struct flush_cmd_control *cmd_control_info; - + struct flush_cmd_control *fcc_info; }; /* diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index db3d18b6e0b9..0415d06a5fbf 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -26,7 +26,7 @@ #define __reverse_ffz(x) __reverse_ffs(~(x)) static struct kmem_cache *discard_entry_slab; -static struct kmem_cache *bio_entry_slab; +static struct kmem_cache *discard_cmd_slab; static struct kmem_cache *sit_entry_set_slab; static struct kmem_cache *inmem_entry_slab; @@ -439,7 +439,7 @@ static int submit_flush_wait(struct f2fs_sb_info *sbi) static int issue_flush_thread(void *data) { struct f2fs_sb_info *sbi = data; - struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; + struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; wait_queue_head_t *q = &fcc->flush_wait_queue; repeat: if (kthread_should_stop()) @@ -468,7 +468,7 @@ repeat: int f2fs_issue_flush(struct f2fs_sb_info *sbi) { - struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; + struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; struct flush_cmd cmd; trace_f2fs_issue_flush(sbi->sb, test_opt(sbi, NOBARRIER), @@ -511,8 +511,8 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) struct flush_cmd_control *fcc; int err = 0; - if (SM_I(sbi)->cmd_control_info) { - fcc = SM_I(sbi)->cmd_control_info; + if (SM_I(sbi)->fcc_info) { + fcc = SM_I(sbi)->fcc_info; goto init_thread; } @@ -522,14 +522,14 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) atomic_set(&fcc->submit_flush, 0); init_waitqueue_head(&fcc->flush_wait_queue); init_llist_head(&fcc->issue_list); - SM_I(sbi)->cmd_control_info = fcc; + SM_I(sbi)->fcc_info = fcc; init_thread: fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(fcc->f2fs_issue_flush)) { err = PTR_ERR(fcc->f2fs_issue_flush); kfree(fcc); - SM_I(sbi)->cmd_control_info = NULL; + SM_I(sbi)->fcc_info = NULL; return err; } @@ -538,7 +538,7 @@ init_thread: void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) { - struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; + struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; if (fcc && fcc->f2fs_issue_flush) { struct task_struct *flush_thread = fcc->f2fs_issue_flush; @@ -548,7 +548,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) } if (free) { kfree(fcc); - SM_I(sbi)->cmd_control_info = NULL; + SM_I(sbi)->fcc_info = NULL; } } @@ -628,42 +628,43 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_unlock(&dirty_i->seglist_lock); } -static struct bio_entry *__add_bio_entry(struct f2fs_sb_info *sbi, +static struct discard_cmd *__add_discard_cmd(struct f2fs_sb_info *sbi, struct bio *bio, block_t lstart, block_t len) { - struct list_head *wait_list = &(SM_I(sbi)->wait_list); - struct bio_entry *be = f2fs_kmem_cache_alloc(bio_entry_slab, GFP_NOFS); + struct list_head *wait_list = &(SM_I(sbi)->discard_cmd_list); + struct discard_cmd *dc; - INIT_LIST_HEAD(&be->list); - be->bio = bio; - be->lstart = lstart; - be->len = len; - init_completion(&be->event); - list_add_tail(&be->list, wait_list); + dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); + INIT_LIST_HEAD(&dc->list); + dc->bio = bio; + dc->lstart = lstart; + dc->len = len; + init_completion(&dc->wait); + list_add_tail(&dc->list, wait_list); - return be; + return dc; } /* This should be covered by global mutex, &sit_i->sentry_lock */ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { - struct list_head *wait_list = &(SM_I(sbi)->wait_list); - struct bio_entry *be, *tmp; + struct list_head *wait_list = &(SM_I(sbi)->discard_cmd_list); + struct discard_cmd *dc, *tmp; - list_for_each_entry_safe(be, tmp, wait_list, list) { - struct bio *bio = be->bio; + list_for_each_entry_safe(dc, tmp, wait_list, list) { + struct bio *bio = dc->bio; int err; - if (!completion_done(&be->event)) { - if ((be->lstart <= blkaddr && - blkaddr < be->lstart + be->len) || + if (!completion_done(&dc->wait)) { + if ((dc->lstart <= blkaddr && + blkaddr < dc->lstart + dc->len) || blkaddr == NULL_ADDR) - wait_for_completion_io(&be->event); + wait_for_completion_io(&dc->wait); else continue; } - err = be->error; + err = dc->error; if (err == -EOPNOTSUPP) err = 0; @@ -672,17 +673,17 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) "Issue discard failed, ret: %d", err); bio_put(bio); - list_del(&be->list); - kmem_cache_free(bio_entry_slab, be); + list_del(&dc->list); + kmem_cache_free(discard_cmd_slab, dc); } } -static void f2fs_submit_bio_wait_endio(struct bio *bio, int err) +static void f2fs_submit_discard_endio(struct bio *bio, int err) { - struct bio_entry *be = (struct bio_entry *)bio->bi_private; + struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private; - be->error = err; - complete(&be->event); + dc->error = err; + complete(&dc->wait); } /* copied from block/blk-lib.c in 4.10-rc1 */ @@ -786,11 +787,11 @@ static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, SECTOR_FROM_BLOCK(blklen), GFP_NOFS, 0, &bio); if (!err && bio) { - struct bio_entry *be = __add_bio_entry(sbi, bio, + struct discard_cmd *dc = __add_discard_cmd(sbi, bio, lblkstart, blklen); - bio->bi_private = be; - bio->bi_end_io = f2fs_submit_bio_wait_endio; + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; submit_bio(REQ_SYNC, bio); } return err; @@ -897,7 +898,7 @@ static void __add_discard_entry(struct f2fs_sb_info *sbi, struct cp_control *cpc, struct seg_entry *se, unsigned int start, unsigned int end) { - struct list_head *head = &SM_I(sbi)->discard_list; + struct list_head *head = &SM_I(sbi)->discard_entry_list; struct discard_entry *new, *last; if (!list_empty(head)) { @@ -966,7 +967,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, void release_discard_addrs(struct f2fs_sb_info *sbi) { - struct list_head *head = &(SM_I(sbi)->discard_list); + struct list_head *head = &(SM_I(sbi)->discard_entry_list); struct discard_entry *entry, *this; /* drop caches */ @@ -992,7 +993,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) { - struct list_head *head = &(SM_I(sbi)->discard_list); + struct list_head *head = &(SM_I(sbi)->discard_entry_list); struct discard_entry *entry, *this; struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct blk_plug plug; @@ -2788,8 +2789,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; - INIT_LIST_HEAD(&sm_info->discard_list); - INIT_LIST_HEAD(&sm_info->wait_list); + INIT_LIST_HEAD(&sm_info->discard_entry_list); + INIT_LIST_HEAD(&sm_info->discard_cmd_list); sm_info->nr_discards = 0; sm_info->max_discards = 0; @@ -2939,15 +2940,15 @@ int __init create_segment_manager_caches(void) if (!discard_entry_slab) goto fail; - bio_entry_slab = f2fs_kmem_cache_create("bio_entry", - sizeof(struct bio_entry)); - if (!bio_entry_slab) + discard_cmd_slab = f2fs_kmem_cache_create("discard_cmd", + sizeof(struct discard_cmd)); + if (!discard_cmd_slab) goto destroy_discard_entry; sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set", sizeof(struct sit_entry_set)); if (!sit_entry_set_slab) - goto destroy_bio_entry; + goto destroy_discard_cmd; inmem_entry_slab = f2fs_kmem_cache_create("inmem_page_entry", sizeof(struct inmem_pages)); @@ -2957,8 +2958,8 @@ int __init create_segment_manager_caches(void) destroy_sit_entry_set: kmem_cache_destroy(sit_entry_set_slab); -destroy_bio_entry: - kmem_cache_destroy(bio_entry_slab); +destroy_discard_cmd: + kmem_cache_destroy(discard_cmd_slab); destroy_discard_entry: kmem_cache_destroy(discard_entry_slab); fail: @@ -2968,7 +2969,7 @@ fail: void destroy_segment_manager_caches(void) { kmem_cache_destroy(sit_entry_set_slab); - kmem_cache_destroy(bio_entry_slab); + kmem_cache_destroy(discard_cmd_slab); kmem_cache_destroy(discard_entry_slab); kmem_cache_destroy(inmem_entry_slab); } -- GitLab From 7c6ca7ea972012924da80eacd31eacf03e8ef1af Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 11 Jan 2017 10:21:15 -0800 Subject: [PATCH 0486/5498] f2fs: reorganize stat information This patch modifies stat information more clearly. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 0a8daa40f642..0493b7af3780 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -260,8 +260,6 @@ static int stat_show(struct seq_file *s, void *v) si->inline_dir); seq_printf(s, " - Orphan Inode: %u\n", si->orphans); - seq_printf(s, " - Atomic write count: %4d (Max. %4d)\n", - si->aw_cnt, si->max_aw_cnt); seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", si->main_area_segs, si->main_area_sections, si->main_area_zones); @@ -320,8 +318,10 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - inmem: %4d, wb_cp_data: %4d, wb_data: %4d\n", - si->inmem_pages, si->nr_wb_cp_data, si->nr_wb_data); + seq_printf(s, " - IO (CP: %4d, Data: %4d)\n", + si->nr_wb_cp_data, si->nr_wb_data); + seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d)\n", + si->inmem_pages, si->aw_cnt, si->max_aw_cnt); seq_printf(s, " - nodes: %4d in %4d\n", si->ndirty_node, si->node_pages); seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n", -- GitLab From 5c23a1d5e7ae33fca683a3ff136d6f76881be2be Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 11 Jan 2017 14:40:24 -0800 Subject: [PATCH 0487/5498] f2fs: factor out discard command info into discard_cmd_control This patch adds discard_cmd_control with the existing discarding controls. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 2 ++ fs/f2fs/f2fs.h | 16 ++++++----- fs/f2fs/segment.c | 68 ++++++++++++++++++++++++++++++++++++----------- fs/f2fs/super.c | 5 +++- 4 files changed, 69 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 0493b7af3780..d96f703c0d03 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -196,6 +196,8 @@ get_cache: /* build merge flush thread */ if (SM_I(sbi)->fcc_info) si->cache_mem += sizeof(struct flush_cmd_control); + if (SM_I(sbi)->dcc_info) + si->cache_mem += sizeof(struct discard_cmd_control); /* free nids */ si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID_LIST] + diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 28f43c212e66..e2330fb17fc4 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -289,6 +289,13 @@ struct discard_cmd { int error; }; +struct discard_cmd_control { + struct list_head discard_entry_list; /* 4KB discard entry list */ + int nr_discards; /* # of discards in the list */ + struct list_head discard_cmd_list; /* discard cmd list */ + int max_discards; /* max. discards to be issued */ +}; + /* for the list of fsync inodes, used only during recovery */ struct fsync_inode_entry { struct list_head list; /* list head */ @@ -732,12 +739,6 @@ struct f2fs_sm_info { /* a threshold to reclaim prefree segments */ unsigned int rec_prefree_segments; - /* for small discard management */ - struct list_head discard_entry_list; /* 4KB discard entry list */ - struct list_head discard_cmd_list; /* discard cmd list */ - int nr_discards; /* # of discards in the list */ - int max_discards; /* max. discards to be issued */ - /* for batched trimming */ unsigned int trim_sections; /* # of sections to trim */ @@ -749,6 +750,9 @@ struct f2fs_sm_info { /* for flush command control */ struct flush_cmd_control *fcc_info; + + /* for discard command control */ + struct discard_cmd_control *dcc_info; }; /* diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0415d06a5fbf..14208834a49d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -631,7 +631,8 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) static struct discard_cmd *__add_discard_cmd(struct f2fs_sb_info *sbi, struct bio *bio, block_t lstart, block_t len) { - struct list_head *wait_list = &(SM_I(sbi)->discard_cmd_list); + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *cmd_list = &(dcc->discard_cmd_list); struct discard_cmd *dc; dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); @@ -640,7 +641,7 @@ static struct discard_cmd *__add_discard_cmd(struct f2fs_sb_info *sbi, dc->lstart = lstart; dc->len = len; init_completion(&dc->wait); - list_add_tail(&dc->list, wait_list); + list_add_tail(&dc->list, cmd_list); return dc; } @@ -648,7 +649,8 @@ static struct discard_cmd *__add_discard_cmd(struct f2fs_sb_info *sbi, /* This should be covered by global mutex, &sit_i->sentry_lock */ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { - struct list_head *wait_list = &(SM_I(sbi)->discard_cmd_list); + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *wait_list = &(dcc->discard_cmd_list); struct discard_cmd *dc, *tmp; list_for_each_entry_safe(dc, tmp, wait_list, list) { @@ -898,7 +900,7 @@ static void __add_discard_entry(struct f2fs_sb_info *sbi, struct cp_control *cpc, struct seg_entry *se, unsigned int start, unsigned int end) { - struct list_head *head = &SM_I(sbi)->discard_entry_list; + struct list_head *head = &SM_I(sbi)->dcc_info->discard_entry_list; struct discard_entry *new, *last; if (!list_empty(head)) { @@ -916,7 +918,7 @@ static void __add_discard_entry(struct f2fs_sb_info *sbi, new->len = end - start; list_add_tail(&new->list, head); done: - SM_I(sbi)->nr_discards += end - start; + SM_I(sbi)->dcc_info->nr_discards += end - start; } static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, @@ -938,7 +940,8 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, if (!force) { if (!test_opt(sbi, DISCARD) || !se->valid_blocks || - SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards) + SM_I(sbi)->dcc_info->nr_discards >= + SM_I(sbi)->dcc_info->max_discards) return false; } @@ -947,7 +950,8 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; - while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) { + while (force || SM_I(sbi)->dcc_info->nr_discards <= + SM_I(sbi)->dcc_info->max_discards) { start = __find_rev_next_bit(dmap, max_blocks, end + 1); if (start >= max_blocks) break; @@ -967,7 +971,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, void release_discard_addrs(struct f2fs_sb_info *sbi) { - struct list_head *head = &(SM_I(sbi)->discard_entry_list); + struct list_head *head = &(SM_I(sbi)->dcc_info->discard_entry_list); struct discard_entry *entry, *this; /* drop caches */ @@ -993,7 +997,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) { - struct list_head *head = &(SM_I(sbi)->discard_entry_list); + struct list_head *head = &(SM_I(sbi)->dcc_info->discard_entry_list); struct discard_entry *entry, *this; struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct blk_plug plug; @@ -1053,13 +1057,47 @@ next: cpc->trimmed += entry->len; skip: list_del(&entry->list); - SM_I(sbi)->nr_discards -= entry->len; + SM_I(sbi)->dcc_info->nr_discards -= entry->len; kmem_cache_free(discard_entry_slab, entry); } blk_finish_plug(&plug); } +int create_discard_cmd_control(struct f2fs_sb_info *sbi) +{ + struct discard_cmd_control *dcc; + int err = 0; + + if (SM_I(sbi)->dcc_info) { + dcc = SM_I(sbi)->dcc_info; + goto init_thread; + } + + dcc = kzalloc(sizeof(struct discard_cmd_control), GFP_KERNEL); + if (!dcc) + return -ENOMEM; + + INIT_LIST_HEAD(&dcc->discard_entry_list); + INIT_LIST_HEAD(&dcc->discard_cmd_list); + dcc->nr_discards = 0; + dcc->max_discards = 0; + + SM_I(sbi)->dcc_info = dcc; +init_thread: + return err; +} + +void destroy_discard_cmd_control(struct f2fs_sb_info *sbi, bool free) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + + if (free) { + kfree(dcc); + SM_I(sbi)->dcc_info = NULL; + } +} + static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); @@ -2789,11 +2827,6 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; - INIT_LIST_HEAD(&sm_info->discard_entry_list); - INIT_LIST_HEAD(&sm_info->discard_cmd_list); - sm_info->nr_discards = 0; - sm_info->max_discards = 0; - sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS; INIT_LIST_HEAD(&sm_info->sit_entry_set); @@ -2804,6 +2837,10 @@ int build_segment_manager(struct f2fs_sb_info *sbi) return err; } + err = create_discard_cmd_control(sbi); + if (err) + return err; + err = build_sit_info(sbi); if (err) return err; @@ -2925,6 +2962,7 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi) if (!sm_info) return; destroy_flush_cmd_control(sbi, true); + destroy_discard_cmd_control(sbi, true); destroy_dirty_segmap(sbi); destroy_curseg(sbi); destroy_free_segmap(sbi); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 20655bbbba44..3f8cd20518fa 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -141,6 +141,7 @@ static match_table_t f2fs_tokens = { enum { GC_THREAD, /* struct f2fs_gc_thread */ SM_INFO, /* struct f2fs_sm_info */ + DCC_INFO, /* struct discard_cmd_control */ NM_INFO, /* struct f2fs_nm_info */ F2FS_SBI, /* struct f2fs_sb_info */ #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -164,6 +165,8 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) 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) @@ -279,7 +282,7 @@ F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); -F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, max_small_discards, max_discards); +F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); -- GitLab From ffce5876dd8885809ef2118d58fc0877e7efdce4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 9 Jan 2017 20:32:07 -0800 Subject: [PATCH 0488/5498] f2fs: add a kernel thread to issue discard commands asynchronously This patch adds a kernel thread to issue discard commands. It proposes three states, D_PREP, D_SUBMIT, and D_DONE to identify current bio status. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/f2fs.h | 12 +++++ fs/f2fs/segment.c | 127 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 108 insertions(+), 31 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e2330fb17fc4..88a52760d83c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -232,6 +232,8 @@ enum { (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) #define BATCHED_TRIM_BLOCKS(sbi) \ (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) + +#define DISCARD_ISSUE_RATE 8 #define DEF_CP_INTERVAL 60 /* 60 secs */ #define DEF_IDLE_INTERVAL 5 /* 5 secs */ @@ -280,6 +282,12 @@ struct discard_entry { int len; /* # of consecutive blocks of the discard */ }; +enum { + D_PREP, + D_SUBMIT, + D_DONE, +}; + struct discard_cmd { struct list_head list; /* command list */ struct completion wait; /* compleation */ @@ -287,12 +295,16 @@ struct discard_cmd { block_t len; /* length */ struct bio *bio; /* bio */ int error; + int state; /* state */ }; struct discard_cmd_control { + struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head discard_entry_list; /* 4KB discard entry list */ int nr_discards; /* # of discards in the list */ struct list_head discard_cmd_list; /* discard cmd list */ + wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ + struct mutex cmd_lock; int max_discards; /* max. discards to be issued */ }; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 14208834a49d..d398180a5149 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -628,7 +628,7 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_unlock(&dirty_i->seglist_lock); } -static struct discard_cmd *__add_discard_cmd(struct f2fs_sb_info *sbi, +static void __add_discard_cmd(struct f2fs_sb_info *sbi, struct bio *bio, block_t lstart, block_t len) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; @@ -638,12 +638,30 @@ static struct discard_cmd *__add_discard_cmd(struct f2fs_sb_info *sbi, dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); INIT_LIST_HEAD(&dc->list); dc->bio = bio; + bio->bi_private = dc; dc->lstart = lstart; dc->len = len; + dc->state = D_PREP; init_completion(&dc->wait); + + mutex_lock(&dcc->cmd_lock); list_add_tail(&dc->list, cmd_list); + mutex_unlock(&dcc->cmd_lock); +} + +static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc) +{ + int err = dc->error; - return dc; + if (err == -EOPNOTSUPP) + err = 0; + + if (err) + f2fs_msg(sbi->sb, KERN_INFO, + "Issue discard failed, ret: %d", err); + bio_put(dc->bio); + list_del(&dc->list); + kmem_cache_free(discard_cmd_slab, dc); } /* This should be covered by global mutex, &sit_i->sentry_lock */ @@ -653,31 +671,28 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) struct list_head *wait_list = &(dcc->discard_cmd_list); struct discard_cmd *dc, *tmp; + mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, wait_list, list) { - struct bio *bio = dc->bio; - int err; - if (!completion_done(&dc->wait)) { - if ((dc->lstart <= blkaddr && - blkaddr < dc->lstart + dc->len) || - blkaddr == NULL_ADDR) + if (blkaddr == NULL_ADDR) { + if (dc->state == D_PREP) { + dc->state = D_SUBMIT; + submit_bio(REQ_SYNC, dc->bio); + } + wait_for_completion_io(&dc->wait); + + __remove_discard_cmd(sbi, dc); + continue; + } + + if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) { + if (dc->state == D_SUBMIT) wait_for_completion_io(&dc->wait); else - continue; + __remove_discard_cmd(sbi, dc); } - - err = dc->error; - if (err == -EOPNOTSUPP) - err = 0; - - if (err) - f2fs_msg(sbi->sb, KERN_INFO, - "Issue discard failed, ret: %d", err); - - bio_put(bio); - list_del(&dc->list); - kmem_cache_free(discard_cmd_slab, dc); } + mutex_unlock(&dcc->cmd_lock); } static void f2fs_submit_discard_endio(struct bio *bio, int err) @@ -686,6 +701,7 @@ static void f2fs_submit_discard_endio(struct bio *bio, int err) dc->error = err; complete(&dc->wait); + dc->state = D_DONE; } /* copied from block/blk-lib.c in 4.10-rc1 */ @@ -769,6 +785,45 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, return 0; } +static int issue_discard_thread(void *data) +{ + struct f2fs_sb_info *sbi = data; + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + wait_queue_head_t *q = &dcc->discard_wait_queue; + struct list_head *cmd_list = &dcc->discard_cmd_list; + struct discard_cmd *dc, *tmp; + struct blk_plug plug; + int iter = 0; +repeat: + if (kthread_should_stop()) + return 0; + + blk_start_plug(&plug); + + mutex_lock(&dcc->cmd_lock); + list_for_each_entry_safe(dc, tmp, cmd_list, list) { + if (dc->state == D_PREP) { + dc->state = D_SUBMIT; + submit_bio(REQ_SYNC, dc->bio); + if (iter++ > DISCARD_ISSUE_RATE) + break; + } else if (dc->state == D_DONE) { + __remove_discard_cmd(sbi, dc); + } + } + mutex_unlock(&dcc->cmd_lock); + + blk_finish_plug(&plug); + + iter = 0; + congestion_wait(BLK_RW_SYNC, HZ/50); + + wait_event_interruptible(*q, + kthread_should_stop() || !list_empty(&dcc->discard_cmd_list)); + goto repeat; +} + + /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) @@ -789,12 +844,9 @@ static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, SECTOR_FROM_BLOCK(blklen), GFP_NOFS, 0, &bio); if (!err && bio) { - struct discard_cmd *dc = __add_discard_cmd(sbi, bio, - lblkstart, blklen); - - bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; - submit_bio(REQ_SYNC, bio); + __add_discard_cmd(sbi, bio, lblkstart, blklen); + wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); } return err; } @@ -1000,14 +1052,11 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct list_head *head = &(SM_I(sbi)->dcc_info->discard_entry_list); struct discard_entry *entry, *this; struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - struct blk_plug plug; unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int start = 0, end = -1; unsigned int secno, start_segno; bool force = (cpc->reason == CP_DISCARD); - blk_start_plug(&plug); - mutex_lock(&dirty_i->seglist_lock); while (1) { @@ -1060,12 +1109,11 @@ skip: SM_I(sbi)->dcc_info->nr_discards -= entry->len; kmem_cache_free(discard_entry_slab, entry); } - - blk_finish_plug(&plug); } int create_discard_cmd_control(struct f2fs_sb_info *sbi) { + dev_t dev = sbi->sb->s_bdev->bd_dev; struct discard_cmd_control *dcc; int err = 0; @@ -1080,11 +1128,22 @@ int create_discard_cmd_control(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&dcc->discard_entry_list); INIT_LIST_HEAD(&dcc->discard_cmd_list); + mutex_init(&dcc->cmd_lock); dcc->nr_discards = 0; dcc->max_discards = 0; + init_waitqueue_head(&dcc->discard_wait_queue); SM_I(sbi)->dcc_info = dcc; init_thread: + dcc->f2fs_issue_discard = kthread_run(issue_discard_thread, sbi, + "f2fs_discard-%u:%u", MAJOR(dev), MINOR(dev)); + if (IS_ERR(dcc->f2fs_issue_discard)) { + err = PTR_ERR(dcc->f2fs_issue_discard); + kfree(dcc); + SM_I(sbi)->dcc_info = NULL; + return err; + } + return err; } @@ -1092,6 +1151,12 @@ void destroy_discard_cmd_control(struct f2fs_sb_info *sbi, bool free) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + if (dcc && dcc->f2fs_issue_discard) { + struct task_struct *discard_thread = dcc->f2fs_issue_discard; + + dcc->f2fs_issue_discard = NULL; + kthread_stop(discard_thread); + } if (free) { kfree(dcc); SM_I(sbi)->dcc_info = NULL; -- GitLab From 2fce421261c43a256708f5b70d049cf1dc032f76 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 11 Jan 2017 10:20:04 -0800 Subject: [PATCH 0489/5498] f2fs: show # of on-going flush and discard bios This patch adds stat information for flush and discard commands. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/debug.c | 11 +++++++++-- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/segment.c | 6 ++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index d96f703c0d03..3af986b4fdaf 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -54,6 +54,12 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->max_aw_cnt = atomic_read(&sbi->max_aw_cnt); si->nr_wb_cp_data = get_pages(sbi, F2FS_WB_CP_DATA); si->nr_wb_data = get_pages(sbi, F2FS_WB_DATA); + if (SM_I(sbi) && SM_I(sbi)->fcc_info) + si->nr_flush = + atomic_read(&SM_I(sbi)->fcc_info->submit_flush); + if (SM_I(sbi) && SM_I(sbi)->dcc_info) + si->nr_discard = + atomic_read(&SM_I(sbi)->dcc_info->submit_discard); si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->rsvd_segs = reserved_segments(sbi); si->overp_segs = overprovision_segments(sbi); @@ -320,8 +326,9 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - IO (CP: %4d, Data: %4d)\n", - si->nr_wb_cp_data, si->nr_wb_data); + seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: %4d, Discard: %4d)\n", + si->nr_wb_cp_data, si->nr_wb_data, + si->nr_flush, si->nr_discard); seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d)\n", si->inmem_pages, si->aw_cnt, si->max_aw_cnt); seq_printf(s, " - nodes: %4d in %4d\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 88a52760d83c..055181c0eeff 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -306,6 +306,7 @@ struct discard_cmd_control { wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ struct mutex cmd_lock; int max_discards; /* max. discards to be issued */ + atomic_t submit_discard; /* # of issued discard */ }; /* for the list of fsync inodes, used only during recovery */ @@ -2364,7 +2365,7 @@ struct f2fs_stat_info { unsigned int ndirty_dirs, ndirty_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits, free_nids, alloc_nids; int total_count, utilization; - int bg_gc, nr_wb_cp_data, nr_wb_data; + int bg_gc, nr_wb_cp_data, nr_wb_data, nr_flush, nr_discard; int inline_xattr, inline_inode, inline_dir, orphans; int aw_cnt, max_aw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d398180a5149..f18b68a3df40 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -653,6 +653,9 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *d { int err = dc->error; + if (dc->state == D_DONE) + atomic_dec(&(SM_I(sbi)->dcc_info->submit_discard)); + if (err == -EOPNOTSUPP) err = 0; @@ -678,6 +681,7 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) if (dc->state == D_PREP) { dc->state = D_SUBMIT; submit_bio(REQ_SYNC, dc->bio); + atomic_inc(&dcc->submit_discard); } wait_for_completion_io(&dc->wait); @@ -805,6 +809,7 @@ repeat: if (dc->state == D_PREP) { dc->state = D_SUBMIT; submit_bio(REQ_SYNC, dc->bio); + atomic_inc(&dcc->submit_discard); if (iter++ > DISCARD_ISSUE_RATE) break; } else if (dc->state == D_DONE) { @@ -1129,6 +1134,7 @@ int create_discard_cmd_control(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&dcc->discard_entry_list); INIT_LIST_HEAD(&dcc->discard_cmd_list); mutex_init(&dcc->cmd_lock); + atomic_set(&dcc->submit_discard, 0); dcc->nr_discards = 0; dcc->max_discards = 0; -- GitLab From f636e019dbe52fa10f39a3d9684495fe26b647cf Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Sun, 22 Jan 2017 12:21:02 +0800 Subject: [PATCH 0490/5498] f2fs: fix a dead loop in f2fs_fiemap() A dead loop can be triggered in f2fs_fiemap() using the test case as below: ... fd = open(); fallocate(fd, 0, 0, 4294967296); ioctl(fd, FS_IOC_FIEMAP, fiemap_buf); ... It's caused by an overflow in __get_data_block(): ... bh->b_size = map.m_len << inode->i_blkbits; ... map.m_len is an unsigned int, and bh->b_size is a size_t which is 64 bits on 64 bits archtecture, type conversion from an unsigned int to a size_t will result in an overflow. In the above-mentioned case, bh->b_size will be zero, and f2fs_fiemap() will call get_data_block() at block 0 again an again. Fix this by adding a force conversion before left shift. Signed-off-by: Wei Fang Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8316b0a37a58..42538dc6225a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -962,7 +962,7 @@ static int __get_data_block(struct inode *inode, sector_t iblock, if (!err) { map_bh(bh, inode->i_sb, map.m_pblk); bh->b_state = (bh->b_state & ~F2FS_MAP_FLAGS) | map.m_flags; - bh->b_size = map.m_len << inode->i_blkbits; + bh->b_size = (u64)map.m_len << inode->i_blkbits; } return err; } -- GitLab From 63386b988a81396e6ff61026fe69740a3507d8b0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 24 Jan 2017 20:39:51 +0800 Subject: [PATCH 0491/5498] f2fs: enhance lookup xattr Previously, in getxattr we will load all entries both in inline xattr and xattr node block, and then do the lookup in all entries, but our lookup flow shows low efficiency, since if we can lookup and hit in inline xattr of inode page cache first, we don't need to load and lookup xattr node block, which can obviously save cpu time and IO latency. Signed-off-by: Chao Yu [Jaegeuk Kim: initialize NULL to avoid warning] Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 132 ++++++++++++++++++++++++++++++++++++++++++------ fs/f2fs/xattr.h | 7 +-- 2 files changed, 121 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index f7d043803947..eca6e7dc43fb 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -266,6 +266,112 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, return entry; } +static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr, + void **last_addr, int index, + size_t len, const char *name) +{ + struct f2fs_xattr_entry *entry; + unsigned int inline_size = F2FS_INLINE_XATTR_ADDRS << 2; + + list_for_each_xattr(entry, base_addr) { + if ((void *)entry + sizeof(__u32) > base_addr + inline_size || + (void *)XATTR_NEXT_ENTRY(entry) + sizeof(__u32) > + base_addr + inline_size) { + *last_addr = entry; + return NULL; + } + if (entry->e_name_index != index) + continue; + if (entry->e_name_len != len) + continue; + if (!memcmp(entry->e_name, name, len)) + break; + } + return entry; +} + +static int lookup_all_xattrs(struct inode *inode, struct page *ipage, + unsigned int index, unsigned int len, + const char *name, struct f2fs_xattr_entry **xe, + void **base_addr) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + void *cur_addr, *txattr_addr, *last_addr = NULL; + nid_t xnid = F2FS_I(inode)->i_xattr_nid; + unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0; + unsigned int inline_size = 0; + int err = 0; + + inline_size = inline_xattr_size(inode); + + if (!size && !inline_size) + return -ENODATA; + + txattr_addr = kzalloc(inline_size + size + sizeof(__u32), + GFP_F2FS_ZERO); + if (!txattr_addr) + return -ENOMEM; + + /* read from inline xattr */ + if (inline_size) { + struct page *page = NULL; + void *inline_addr; + + if (ipage) { + inline_addr = inline_xattr_addr(ipage); + } else { + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + inline_addr = inline_xattr_addr(page); + } + memcpy(txattr_addr, inline_addr, inline_size); + f2fs_put_page(page, 1); + + *xe = __find_inline_xattr(txattr_addr, &last_addr, + index, len, name); + if (*xe) + goto check; + } + + /* read from xattr node block */ + if (xnid) { + struct page *xpage; + void *xattr_addr; + + /* The inode already has an extended attribute block. */ + xpage = get_node_page(sbi, xnid); + if (IS_ERR(xpage)) { + err = PTR_ERR(xpage); + goto out; + } + + xattr_addr = page_address(xpage); + memcpy(txattr_addr + inline_size, xattr_addr, size); + f2fs_put_page(xpage, 1); + } + + if (last_addr) + cur_addr = XATTR_HDR(last_addr) - 1; + else + cur_addr = txattr_addr; + + *xe = __find_xattr(cur_addr, index, len, name); +check: + if (IS_XATTR_LAST_ENTRY(*xe)) { + err = -ENODATA; + goto out; + } + + *base_addr = txattr_addr; + return 0; +out: + kzfree(txattr_addr); + return err; +} + static int read_all_xattrs(struct inode *inode, struct page *ipage, void **base_addr) { @@ -397,8 +503,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, } xattr_addr = page_address(xpage); - memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE - - sizeof(struct node_footer)); + memcpy(xattr_addr, txattr_addr + inline_size, MAX_XATTR_BLOCK_SIZE); set_page_dirty(xpage); f2fs_put_page(xpage, 1); @@ -410,10 +515,11 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, int f2fs_getxattr(struct inode *inode, int index, const char *name, void *buffer, size_t buffer_size, struct page *ipage) { - struct f2fs_xattr_entry *entry; - void *base_addr; + struct f2fs_xattr_entry *entry = NULL; int error = 0; - size_t size, len; + unsigned int size, len; + char *pval; + void *base_addr = NULL; if (name == NULL) return -EINVAL; @@ -422,30 +528,26 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name, if (len > F2FS_NAME_LEN) return -ERANGE; - error = read_all_xattrs(inode, ipage, &base_addr); + error = lookup_all_xattrs(inode, ipage, index, len, name, + &entry, &base_addr); if (error) return error; - entry = __find_xattr(base_addr, index, len, name); - if (IS_XATTR_LAST_ENTRY(entry)) { - error = -ENODATA; - goto cleanup; - } - size = le16_to_cpu(entry->e_value_size); if (buffer && size > buffer_size) { error = -ERANGE; - goto cleanup; + goto out; } + pval = entry->e_name + entry->e_name_len; + if (buffer) { char *pval = entry->e_name + entry->e_name_len; memcpy(buffer, pval, size); } error = size; - -cleanup: +out: kzfree(base_addr); return error; } diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index d2fd0387a3c7..ba64f43d163d 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -72,9 +72,10 @@ struct f2fs_xattr_entry { for (entry = XATTR_FIRST_ENTRY(addr);\ !IS_XATTR_LAST_ENTRY(entry);\ entry = XATTR_NEXT_ENTRY(entry)) - -#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + PAGE_SIZE - \ - sizeof(struct node_footer) - sizeof(__u32)) +#define MAX_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer)) +#define VALID_XATTR_BLOCK_SIZE (MAX_XATTR_BLOCK_SIZE - sizeof(__u32)) +#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \ + VALID_XATTR_BLOCK_SIZE) #define MAX_VALUE_LEN(i) (MIN_OFFSET(i) - \ sizeof(struct f2fs_xattr_header) - \ -- GitLab From 32db0b935598a206118fee228bdeb0c47e1d8faa Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 25 Jan 2017 10:52:39 +0800 Subject: [PATCH 0492/5498] f2fs: fix to avoid overflow when left shifting page offset We use following method to calculate size with current page index: size = index << PAGE_SHIFT If type of index has only 32-bits size, left shifting will incur overflow, which makes result incorrect. So let's cast index with 64-bits type to avoid such issue. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 1e3da757abff..ec0589a5ecae 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -428,8 +428,9 @@ retry_dn: } if (!file_keep_isize(inode) && - (i_size_read(inode) <= (start << PAGE_SHIFT))) - f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT); + (i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT))) + f2fs_i_size_write(inode, + (loff_t)(start + 1) << PAGE_SHIFT); /* * dest is reserved block, invalidate src block -- GitLab From ae9cd29515dffb3524a00e7cc6585fe5915ce1b8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 25 Jan 2017 10:52:40 +0800 Subject: [PATCH 0493/5498] f2fs: fix null pointer dereference when issuing flush in ->fsync We only allocate flush merge control structure sbi::sm_info::fcc_info when flush_merge option is on, but in f2fs_issue_flush we still try to access member of the control structure without that option, it incurs panic as show below, fix it. Call Trace: __remove_ino_entry+0xa9/0xc0 [f2fs] f2fs_do_sync_file.isra.27+0x214/0x6d0 [f2fs] f2fs_sync_file+0x18/0x20 [f2fs] vfs_fsync_range+0x3d/0xb0 __do_page_fault+0x261/0x4d0 do_fsync+0x3d/0x70 SyS_fsync+0x10/0x20 do_syscall_64+0x6e/0x180 entry_SYSCALL64_slow_path+0x25/0x25 RIP: 0033:0x7f18ce260de0 RSP: 002b:00007ffdd4589258 EFLAGS: 00000246 ORIG_RAX: 000000000000004a RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007f18ce260de0 RDX: 0000000000000006 RSI: 00000000016c0360 RDI: 0000000000000003 RBP: 00000000016c0360 R08: 000000000000ffff R09: 000000000000001f R10: 00007ffdd4589020 R11: 0000000000000246 R12: 00000000016c0100 R13: 0000000000000000 R14: 00000000016c1f00 R15: 00000000016c0100 Code: fb 81 e3 00 08 00 00 48 89 45 a0 0f 1f 44 00 00 31 c0 85 db 75 27 41 81 e7 00 04 00 00 74 0c 41 8b 45 20 85 c0 0f 85 81 00 00 00 41 ff 45 20 4c 89 e7 e8 f8 e9 ff ff f0 41 ff 4d 20 48 83 c4 RIP: f2fs_issue_flush+0x5b/0x170 [f2fs] RSP: ffffc90003b5fd78 CR2: 0000000000000020 ---[ end trace a09314c24f037648 ]--- Reported-by: Shuoran Liu Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f18b68a3df40..2386aed973d5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -477,7 +477,10 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) if (test_opt(sbi, NOBARRIER)) return 0; - if (!test_opt(sbi, FLUSH_MERGE) || !atomic_read(&fcc->submit_flush)) { + if (!test_opt(sbi, FLUSH_MERGE)) + return submit_flush_wait(sbi); + + if (!atomic_read(&fcc->submit_flush)) { int ret; atomic_inc(&fcc->submit_flush); -- GitLab From 690a217a7ab78541396779a74e85246cc2ee0c97 Mon Sep 17 00:00:00 2001 From: Kaixu Xia Date: Fri, 27 Jan 2017 09:35:37 +0800 Subject: [PATCH 0494/5498] f2fs: show the fault injection mount option This patch shows the fault injection mount option in f2fs_show_options(). Signed-off-by: Kaixu Xia Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3f8cd20518fa..7d17592a3b04 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -552,6 +552,7 @@ static int parse_options(struct super_block *sb, char *options) return -EINVAL; #ifdef CONFIG_F2FS_FAULT_INJECTION f2fs_build_fault_attr(sbi, arg); + set_opt(sbi, FAULT_INJECTION); #else f2fs_msg(sb, KERN_INFO, "FAULT_INJECTION was not selected"); @@ -933,6 +934,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_printf(seq, ",active_logs=%u", sbi->active_logs); if (F2FS_IO_SIZE_BITS(sbi)) seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi)); +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (test_opt(sbi, FAULT_INJECTION)) + seq_puts(seq, ",fault_injection"); +#endif return 0; } -- GitLab From a46a4cb18721d1f3ef86cae0e746088f63ae220d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 29 Jan 2017 14:27:02 +0900 Subject: [PATCH 0495/5498] f2fs: declare missing static function We missed two functions declared as static functions. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2386aed973d5..e50053f3d725 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1119,7 +1119,7 @@ skip: } } -int create_discard_cmd_control(struct f2fs_sb_info *sbi) +static int create_discard_cmd_control(struct f2fs_sb_info *sbi) { dev_t dev = sbi->sb->s_bdev->bd_dev; struct discard_cmd_control *dcc; @@ -1156,7 +1156,7 @@ init_thread: return err; } -void destroy_discard_cmd_control(struct f2fs_sb_info *sbi, bool free) +static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi, bool free) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; -- GitLab From 5f98cd4f22a8207da24f9c4c7e2172b52c254d51 Mon Sep 17 00:00:00 2001 From: DongOh Shin Date: Mon, 30 Jan 2017 10:55:17 -0800 Subject: [PATCH 0496/5498] f2fs: fix 3 coding style errors in f2fs.h Two coding style errors below have been resolved: "Macros with complex values should be enclosed in parentheses" And a coding style error below has been resolved: "space prohibited before that ',' (ctx:WxW)" Signed-off-by: DongOh Shin Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 055181c0eeff..d01465cb77b8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -110,9 +110,9 @@ struct f2fs_mount_info { #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) #define F2FS_SET_FEATURE(sb, mask) \ - F2FS_SB(sb)->raw_super->feature |= cpu_to_le32(mask) + (F2FS_SB(sb)->raw_super->feature |= cpu_to_le32(mask)) #define F2FS_CLEAR_FEATURE(sb, mask) \ - F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask) + (F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask)) /* bio stuffs */ #define REQ_OP_READ READ @@ -2149,7 +2149,7 @@ void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); int update_dent_inode(struct inode *, struct inode *, const struct qstr *); void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, - const struct qstr *, f2fs_hash_t , unsigned int); + const struct qstr *, f2fs_hash_t, unsigned int); int f2fs_add_regular_entry(struct inode *, const struct qstr *, const struct qstr *, struct inode *, nid_t, umode_t); int __f2fs_do_add_link(struct inode *, struct fscrypt_name*, struct inode *, -- GitLab From d6c3c4b493713ef568b16bb9ba716b71925e52d4 Mon Sep 17 00:00:00 2001 From: DongOh Shin Date: Mon, 30 Jan 2017 10:55:18 -0800 Subject: [PATCH 0497/5498] f2fs: fix 446 coding style warnings in f2fs.h 1) Nine coding style warnings below have been resolved: "Missing a blank line after declarations" 2) 435 coding style warnings below have been resolved: "function definition argument 'x' should also have an identifier name" 3) Two coding style warnings below have been resolved: "macros should not use a trailing semicolon" Signed-off-by: DongOh Shin Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 501 +++++++++++++++++++++++++++---------------------- 1 file changed, 272 insertions(+), 229 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d01465cb77b8..820171d05322 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -331,6 +331,7 @@ struct fsync_inode_entry { static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i) { int before = nats_in_cursum(journal); + journal->n_nats = cpu_to_le16(before + i); return before; } @@ -338,6 +339,7 @@ static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i) static inline int update_sits_in_cursum(struct f2fs_journal *journal, int i) { int before = sits_in_cursum(journal); + journal->n_sits = cpu_to_le16(before + i); return before; } @@ -423,12 +425,14 @@ static inline void make_dentry_ptr(struct inode *inode, if (type == 1) { struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src; + d->max = NR_DENTRY_IN_BLOCK; d->bitmap = &t->dentry_bitmap; d->dentry = t->dentry; d->filename = t->filename; } else { struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src; + d->max = NR_INLINE_DENTRY; d->bitmap = &t->dentry_bitmap; d->dentry = t->dentry; @@ -617,7 +621,7 @@ static inline bool __is_front_mergeable(struct extent_info *cur, return __is_extent_mergeable(cur, front); } -extern void f2fs_mark_inode_dirty_sync(struct inode *, bool); +extern void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync); static inline void __try_update_largest_extent(struct inode *inode, struct extent_tree *et, struct extent_node *en) { @@ -1578,6 +1582,7 @@ static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, { #ifdef CONFIG_F2FS_FAULT_INJECTION struct page *page = find_lock_page(mapping, index); + if (page) return page; @@ -1661,6 +1666,7 @@ static inline void f2fs_radix_tree_insert(struct radix_tree_root *root, static inline bool IS_INODE(struct page *page) { struct f2fs_node *p = F2FS_NODE(page); + return RAW_IS_INODE(p); } @@ -1674,6 +1680,7 @@ static inline block_t datablock_addr(struct page *node_page, { struct f2fs_node *raw_node; __le32 *addr_array; + raw_node = F2FS_NODE(node_page); addr_array = blkaddr_in_node(raw_node); return le32_to_cpu(addr_array[offset]); @@ -1909,6 +1916,7 @@ static inline unsigned int addrs_per_inode(struct inode *inode) static inline void *inline_xattr_addr(struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); + return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS]); } @@ -1970,6 +1978,7 @@ static inline bool f2fs_is_drop_cache(struct inode *inode) static inline void *inline_data_addr(struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); + return (void *)&(ri->i_addr[1]); } @@ -2094,29 +2103,30 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags) /* * file.c */ -int f2fs_sync_file(struct file *, loff_t, loff_t, int); -void truncate_data_blocks(struct dnode_of_data *); -int truncate_blocks(struct inode *, u64, bool); -int f2fs_truncate(struct inode *); -int f2fs_getattr(struct vfsmount *, struct dentry *, struct kstat *); -int f2fs_setattr(struct dentry *, struct iattr *); -int truncate_hole(struct inode *, pgoff_t, pgoff_t); -int truncate_data_blocks_range(struct dnode_of_data *, int); -long f2fs_ioctl(struct file *, unsigned int, unsigned long); -long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); +int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync); +void truncate_data_blocks(struct dnode_of_data *dn); +int truncate_blocks(struct inode *inode, u64 from, bool lock); +int f2fs_truncate(struct inode *inode); +int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat); +int f2fs_setattr(struct dentry *dentry, struct iattr *attr); +int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end); +int truncate_data_blocks_range(struct dnode_of_data *dn, int count); +long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* * inode.c */ -void f2fs_set_inode_flags(struct inode *); -struct inode *f2fs_iget(struct super_block *, unsigned long); -struct inode *f2fs_iget_retry(struct super_block *, unsigned long); -int try_to_free_nats(struct f2fs_sb_info *, int); -int update_inode(struct inode *, struct page *); -int update_inode_page(struct inode *); -int f2fs_write_inode(struct inode *, struct writeback_control *); -void f2fs_evict_inode(struct inode *); -void handle_failed_inode(struct inode *); +void f2fs_set_inode_flags(struct inode *inode); +struct inode *f2fs_iget(struct super_block *sb, unsigned long ino); +struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino); +int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink); +int update_inode(struct inode *inode, struct page *node_page); +int update_inode_page(struct inode *inode); +int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc); +void f2fs_evict_inode(struct inode *inode); +void handle_failed_inode(struct inode *inode); /* * namei.c @@ -2126,40 +2136,47 @@ struct dentry *f2fs_get_parent(struct dentry *child); /* * dir.c */ -void set_de_type(struct f2fs_dir_entry *, umode_t); -unsigned char get_de_type(struct f2fs_dir_entry *); -struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *, - f2fs_hash_t, int *, struct f2fs_dentry_ptr *); -int f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, - unsigned int, struct fscrypt_str *); -void do_make_empty_dir(struct inode *, struct inode *, - struct f2fs_dentry_ptr *); -struct page *init_inode_metadata(struct inode *, struct inode *, - const struct qstr *, const struct qstr *, struct page *); -void update_parent_metadata(struct inode *, struct inode *, unsigned int); -int room_for_filename(const void *, int, int); -void f2fs_drop_nlink(struct inode *, struct inode *); -struct f2fs_dir_entry *__f2fs_find_entry(struct inode *, struct fscrypt_name *, - struct page **); -struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, - struct page **); -struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); -ino_t f2fs_inode_by_name(struct inode *, struct qstr *, struct page **); -void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, - struct page *, struct inode *); -int update_dent_inode(struct inode *, struct inode *, const struct qstr *); -void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, - const struct qstr *, f2fs_hash_t, unsigned int); -int f2fs_add_regular_entry(struct inode *, const struct qstr *, - const struct qstr *, struct inode *, nid_t, umode_t); -int __f2fs_do_add_link(struct inode *, struct fscrypt_name*, struct inode *, - nid_t, umode_t); -int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, - umode_t); -void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, - struct inode *); -int f2fs_do_tmpfile(struct inode *, struct inode *); -bool f2fs_empty_dir(struct inode *); +void set_de_type(struct f2fs_dir_entry *de, umode_t mode); +unsigned char get_de_type(struct f2fs_dir_entry *de); +struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, + f2fs_hash_t namehash, int *max_slots, + struct f2fs_dentry_ptr *d); +int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, + unsigned int start_pos, struct fscrypt_str *fstr); +void do_make_empty_dir(struct inode *inode, struct inode *parent, + struct f2fs_dentry_ptr *d); +struct page *init_inode_metadata(struct inode *inode, struct inode *dir, + const struct qstr *new_name, + const struct qstr *orig_name, struct page *dpage); +void update_parent_metadata(struct inode *dir, struct inode *inode, + unsigned int current_depth); +int room_for_filename(const void *bitmap, int slots, int max_slots); +void f2fs_drop_nlink(struct inode *dir, struct inode *inode); +struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir, + struct fscrypt_name *fname, struct page **res_page); +struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, + struct qstr *child, struct page **res_page); +struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p); +ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr, + struct page **page); +void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, + struct page *page, struct inode *inode); +int update_dent_inode(struct inode *inode, struct inode *to, + const struct qstr *name); +void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, + const struct qstr *name, f2fs_hash_t name_hash, + unsigned int bit_pos); +int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, + const struct qstr *orig_name, + struct inode *inode, nid_t ino, umode_t mode); +int __f2fs_do_add_link(struct inode *dir, struct fscrypt_name *fname, + struct inode *inode, nid_t ino, umode_t mode); +int __f2fs_add_link(struct inode *dir, const struct qstr *name, + struct inode *inode, nid_t ino, umode_t mode); +void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, + struct inode *dir, struct inode *inode); +int f2fs_do_tmpfile(struct inode *inode, struct inode *dir); +bool f2fs_empty_dir(struct inode *dir); static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) { @@ -2170,18 +2187,18 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) /* * super.c */ -int f2fs_inode_dirtied(struct inode *, bool); -void f2fs_inode_synced(struct inode *); -int f2fs_commit_super(struct f2fs_sb_info *, bool); -int f2fs_sync_fs(struct super_block *, int); +int f2fs_inode_dirtied(struct inode *inode, bool sync); +void f2fs_inode_synced(struct inode *inode); +int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); +int f2fs_sync_fs(struct super_block *sb, int sync); extern __printf(3, 4) -void f2fs_msg(struct super_block *, const char *, const char *, ...); +void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...); int sanity_check_ckpt(struct f2fs_sb_info *sbi); /* * hash.c */ -f2fs_hash_t f2fs_dentry_hash(const struct qstr *); +f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info); /* * node.c @@ -2189,164 +2206,184 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *); struct dnode_of_data; struct node_info; -bool available_free_memory(struct f2fs_sb_info *, int); -int need_dentry_mark(struct f2fs_sb_info *, nid_t); -bool is_checkpointed_node(struct f2fs_sb_info *, nid_t); -bool need_inode_block_update(struct f2fs_sb_info *, nid_t); -void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); -pgoff_t get_next_page_offset(struct dnode_of_data *, pgoff_t); -int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); -int truncate_inode_blocks(struct inode *, pgoff_t); -int truncate_xattr_node(struct inode *, struct page *); -int wait_on_node_pages_writeback(struct f2fs_sb_info *, nid_t); -int remove_inode_page(struct inode *); -struct page *new_inode_page(struct inode *); -struct page *new_node_page(struct dnode_of_data *, unsigned int, struct page *); -void ra_node_page(struct f2fs_sb_info *, nid_t); -struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); -struct page *get_node_page_ra(struct page *, int); -void move_node_page(struct page *, int); -int fsync_node_pages(struct f2fs_sb_info *, struct inode *, - struct writeback_control *, bool); -int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); -void build_free_nids(struct f2fs_sb_info *, bool); -bool alloc_nid(struct f2fs_sb_info *, nid_t *); -void alloc_nid_done(struct f2fs_sb_info *, nid_t); -void alloc_nid_failed(struct f2fs_sb_info *, nid_t); -int try_to_free_nids(struct f2fs_sb_info *, int); -void recover_inline_xattr(struct inode *, struct page *); -void recover_xattr_data(struct inode *, struct page *, block_t); -int recover_inode_page(struct f2fs_sb_info *, struct page *); -int restore_node_summary(struct f2fs_sb_info *, unsigned int, - struct f2fs_summary_block *); -void flush_nat_entries(struct f2fs_sb_info *); -int build_node_manager(struct f2fs_sb_info *); -void destroy_node_manager(struct f2fs_sb_info *); +bool available_free_memory(struct f2fs_sb_info *sbi, int type); +int need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid); +bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid); +bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino); +void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni); +pgoff_t get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs); +int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode); +int truncate_inode_blocks(struct inode *inode, pgoff_t from); +int truncate_xattr_node(struct inode *inode, struct page *page); +int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino); +int remove_inode_page(struct inode *inode); +struct page *new_inode_page(struct inode *inode); +struct page *new_node_page(struct dnode_of_data *dn, + unsigned int ofs, struct page *ipage); +void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid); +struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid); +struct page *get_node_page_ra(struct page *parent, int start); +void move_node_page(struct page *node_page, int gc_type); +int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, + struct writeback_control *wbc, bool atomic); +int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc); +void build_free_nids(struct f2fs_sb_info *sbi, bool sync); +bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid); +void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); +void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid); +int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink); +void recover_inline_xattr(struct inode *inode, struct page *page); +void recover_xattr_data(struct inode *inode, struct page *page, + block_t blkaddr); +int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page); +int restore_node_summary(struct f2fs_sb_info *sbi, + unsigned int segno, struct f2fs_summary_block *sum); +void flush_nat_entries(struct f2fs_sb_info *sbi); +int build_node_manager(struct f2fs_sb_info *sbi); +void destroy_node_manager(struct f2fs_sb_info *sbi); int __init create_node_manager_caches(void); void destroy_node_manager_caches(void); /* * segment.c */ -void register_inmem_page(struct inode *, struct page *); -void drop_inmem_pages(struct inode *); -int commit_inmem_pages(struct inode *); -void f2fs_balance_fs(struct f2fs_sb_info *, bool); -void f2fs_balance_fs_bg(struct f2fs_sb_info *); -int f2fs_issue_flush(struct f2fs_sb_info *); -int create_flush_cmd_control(struct f2fs_sb_info *); -void destroy_flush_cmd_control(struct f2fs_sb_info *, bool); -void invalidate_blocks(struct f2fs_sb_info *, block_t); -bool is_checkpointed_data(struct f2fs_sb_info *, block_t); -void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); -void f2fs_wait_discard_bio(struct f2fs_sb_info *, block_t); -void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *); -void release_discard_addrs(struct f2fs_sb_info *); -int npages_for_summary_flush(struct f2fs_sb_info *, bool); -void allocate_new_segments(struct f2fs_sb_info *); -int f2fs_trim_fs(struct f2fs_sb_info *, struct fstrim_range *); -bool exist_trim_candidates(struct f2fs_sb_info *, struct cp_control *); -struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); -void update_meta_page(struct f2fs_sb_info *, void *, block_t); -void write_meta_page(struct f2fs_sb_info *, struct page *); -void write_node_page(unsigned int, struct f2fs_io_info *); -void write_data_page(struct dnode_of_data *, struct f2fs_io_info *); -void rewrite_data_page(struct f2fs_io_info *); -void __f2fs_replace_block(struct f2fs_sb_info *, struct f2fs_summary *, - block_t, block_t, bool, bool); -void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *, - block_t, block_t, unsigned char, bool, bool); -void allocate_data_block(struct f2fs_sb_info *, struct page *, - block_t, block_t *, struct f2fs_summary *, int); -void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool); -void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *, block_t); -void write_data_summaries(struct f2fs_sb_info *, block_t); -void write_node_summaries(struct f2fs_sb_info *, block_t); -int lookup_journal_in_cursum(struct f2fs_journal *, int, unsigned int, int); -void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *); -int build_segment_manager(struct f2fs_sb_info *); -void destroy_segment_manager(struct f2fs_sb_info *); +void register_inmem_page(struct inode *inode, struct page *page); +void drop_inmem_pages(struct inode *inode); +int commit_inmem_pages(struct inode *inode); +void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need); +void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi); +int f2fs_issue_flush(struct f2fs_sb_info *sbi); +int create_flush_cmd_control(struct f2fs_sb_info *sbi); +void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); +void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); +bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); +void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); +void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr); +void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); +void release_discard_addrs(struct f2fs_sb_info *sbi); +int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); +void allocate_new_segments(struct f2fs_sb_info *sbi); +int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range); +bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc); +struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno); +void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr); +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page); +void write_node_page(unsigned int nid, struct f2fs_io_info *fio); +void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio); +void rewrite_data_page(struct f2fs_io_info *fio); +void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, + block_t old_blkaddr, block_t new_blkaddr, + bool recover_curseg, bool recover_newaddr); +void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, + block_t old_addr, block_t new_addr, + unsigned char version, bool recover_curseg, + bool recover_newaddr); +void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, + block_t old_blkaddr, block_t *new_blkaddr, + struct f2fs_summary *sum, int type); +void f2fs_wait_on_page_writeback(struct page *page, + enum page_type type, bool ordered); +void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, + block_t blkaddr); +void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk); +void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk); +int lookup_journal_in_cursum(struct f2fs_journal *journal, int type, + unsigned int val, int alloc); +void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc); +int build_segment_manager(struct f2fs_sb_info *sbi); +void destroy_segment_manager(struct f2fs_sb_info *sbi); int __init create_segment_manager_caches(void); void destroy_segment_manager_caches(void); /* * checkpoint.c */ -void f2fs_stop_checkpoint(struct f2fs_sb_info *, bool); -struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); -struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); -struct page *get_tmp_page(struct f2fs_sb_info *, pgoff_t); -bool is_valid_blkaddr(struct f2fs_sb_info *, block_t, int); -int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int, bool); -void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t); -long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); -void add_ino_entry(struct f2fs_sb_info *, nid_t, int type); -void remove_ino_entry(struct f2fs_sb_info *, nid_t, int type); -void release_ino_entry(struct f2fs_sb_info *, bool); -bool exist_written_data(struct f2fs_sb_info *, nid_t, int); -int f2fs_sync_inode_meta(struct f2fs_sb_info *); -int acquire_orphan_inode(struct f2fs_sb_info *); -void release_orphan_inode(struct f2fs_sb_info *); -void add_orphan_inode(struct inode *); -void remove_orphan_inode(struct f2fs_sb_info *, nid_t); -int recover_orphan_inodes(struct f2fs_sb_info *); -int get_valid_checkpoint(struct f2fs_sb_info *); -void update_dirty_page(struct inode *, struct page *); -void remove_dirty_inode(struct inode *); -int sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type); -int write_checkpoint(struct f2fs_sb_info *, struct cp_control *); -void init_ino_entry_info(struct f2fs_sb_info *); +void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io); +struct page *grab_meta_page(struct f2fs_sb_info *sbi, pgoff_t index); +struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index); +struct page *get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index); +bool is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type); +int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, + int type, bool sync); +void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index); +long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, + long nr_to_write); +void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); +void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); +void release_ino_entry(struct f2fs_sb_info *sbi, bool all); +bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode); +int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi); +int acquire_orphan_inode(struct f2fs_sb_info *sbi); +void release_orphan_inode(struct f2fs_sb_info *sbi); +void add_orphan_inode(struct inode *inode); +void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino); +int recover_orphan_inodes(struct f2fs_sb_info *sbi); +int get_valid_checkpoint(struct f2fs_sb_info *sbi); +void update_dirty_page(struct inode *inode, struct page *page); +void remove_dirty_inode(struct inode *inode); +int sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type); +int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc); +void init_ino_entry_info(struct f2fs_sb_info *sbi); int __init create_checkpoint_caches(void); void destroy_checkpoint_caches(void); /* * data.c */ -void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); -void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *, - struct page *, nid_t, enum page_type, int); -void f2fs_flush_merged_bios(struct f2fs_sb_info *); -int f2fs_submit_page_bio(struct f2fs_io_info *); -int f2fs_submit_page_mbio(struct f2fs_io_info *); -struct block_device *f2fs_target_device(struct f2fs_sb_info *, - block_t, struct bio *); -int f2fs_target_device_index(struct f2fs_sb_info *, block_t); -void set_data_blkaddr(struct dnode_of_data *); -void f2fs_update_data_blkaddr(struct dnode_of_data *, block_t); -int reserve_new_blocks(struct dnode_of_data *, blkcnt_t); -int reserve_new_block(struct dnode_of_data *); -int f2fs_get_block(struct dnode_of_data *, pgoff_t); -int f2fs_preallocate_blocks(struct inode *, loff_t, size_t, bool); -int f2fs_reserve_block(struct dnode_of_data *, pgoff_t); -struct page *get_read_data_page(struct inode *, pgoff_t, int, bool); -struct page *find_data_page(struct inode *, pgoff_t); -struct page *get_lock_data_page(struct inode *, pgoff_t, bool); -struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool); -int do_write_data_page(struct f2fs_io_info *); -int f2fs_map_blocks(struct inode *, struct f2fs_map_blocks *, int, int); -int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); -void f2fs_set_page_dirty_nobuffers(struct page *); -void f2fs_invalidate_page(struct page *, unsigned int, unsigned int); -int f2fs_release_page(struct page *, gfp_t); -#ifdef CONFIG_F2FS_MIGRATION -int f2fs_migrate_page(struct address_space *, struct page *, struct page *, - enum migrate_mode); +void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type, + int rw); +void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi, + struct inode *inode, struct page *page, + nid_t ino, enum page_type type, int rw); +void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi); +int f2fs_submit_page_bio(struct f2fs_io_info *fio); +int f2fs_submit_page_mbio(struct f2fs_io_info *fio); +struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi, + block_t blk_addr, struct bio *bio); +int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr); +void set_data_blkaddr(struct dnode_of_data *dn); +void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr); +int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count); +int reserve_new_block(struct dnode_of_data *dn); +int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index); +int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, + size_t count, bool dio); +int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index); +struct page *get_read_data_page(struct inode *inode, pgoff_t index, + int op_flags, bool for_write); +struct page *find_data_page(struct inode *inode, pgoff_t index); +struct page *get_lock_data_page(struct inode *inode, pgoff_t index, + bool for_write); +struct page *get_new_data_page(struct inode *inode, + struct page *ipage, pgoff_t index, bool new_i_size); +int do_write_data_page(struct f2fs_io_info *fio); +int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, + int create, int flag); +int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 len); +void f2fs_set_page_dirty_nobuffers(struct page *page); +void f2fs_invalidate_page(struct page *page, unsigned int offset, + unsigned int length); +int f2fs_release_page(struct page *page, gfp_t wait); +#ifdef CONFIG_MIGRATION +int f2fs_migrate_page(struct address_space *mapping, struct page *newpage, + struct page *page, enum migrate_mode mode); #endif /* * gc.c */ -int start_gc_thread(struct f2fs_sb_info *); -void stop_gc_thread(struct f2fs_sb_info *); -block_t start_bidx_of_node(unsigned int, struct inode *); -int f2fs_gc(struct f2fs_sb_info *, bool, bool); -void build_gc_manager(struct f2fs_sb_info *); +int start_gc_thread(struct f2fs_sb_info *sbi); +void stop_gc_thread(struct f2fs_sb_info *sbi); +block_t start_bidx_of_node(unsigned int node_ofs, struct inode *inode); +int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background); +void build_gc_manager(struct f2fs_sb_info *sbi); /* * recovery.c */ -int recover_fsync_data(struct f2fs_sb_info *, bool); -bool space_for_roll_forward(struct f2fs_sb_info *); +int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only); +bool space_for_roll_forward(struct f2fs_sb_info *sbi); /* * debug.c @@ -2440,9 +2477,9 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) #define stat_inc_inplace_blocks(sbi) \ (atomic_inc(&(sbi)->inplace_count)) #define stat_inc_atomic_write(inode) \ - (atomic_inc(&F2FS_I_SB(inode)->aw_cnt)); + (atomic_inc(&F2FS_I_SB(inode)->aw_cnt)) #define stat_dec_atomic_write(inode) \ - (atomic_dec(&F2FS_I_SB(inode)->aw_cnt)); + (atomic_dec(&F2FS_I_SB(inode)->aw_cnt)) #define stat_update_max_atomic_write(inode) \ do { \ int cur = atomic_read(&F2FS_I_SB(inode)->aw_cnt); \ @@ -2482,8 +2519,8 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) si->bg_node_blks += (gc_type == BG_GC) ? (blks) : 0; \ } while (0) -int f2fs_build_stats(struct f2fs_sb_info *); -void f2fs_destroy_stats(struct f2fs_sb_info *); +int f2fs_build_stats(struct f2fs_sb_info *sbi); +void f2fs_destroy_stats(struct f2fs_sb_info *sbi); int __init f2fs_create_root_stats(void); void f2fs_destroy_root_stats(void); #else @@ -2535,49 +2572,55 @@ extern struct kmem_cache *inode_entry_slab; /* * inline.c */ -bool f2fs_may_inline_data(struct inode *); -bool f2fs_may_inline_dentry(struct inode *); -void read_inline_data(struct page *, struct page *); -bool truncate_inline_inode(struct page *, u64); -int f2fs_read_inline_data(struct inode *, struct page *); -int f2fs_convert_inline_page(struct dnode_of_data *, struct page *); -int f2fs_convert_inline_inode(struct inode *); -int f2fs_write_inline_data(struct inode *, struct page *); -bool recover_inline_data(struct inode *, struct page *); -struct f2fs_dir_entry *find_in_inline_dir(struct inode *, - struct fscrypt_name *, struct page **); -int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); -int f2fs_add_inline_entry(struct inode *, const struct qstr *, - const struct qstr *, struct inode *, nid_t, umode_t); -void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *, - struct inode *, struct inode *); -bool f2fs_empty_inline_dir(struct inode *); -int f2fs_read_inline_dir(struct file *, struct dir_context *, - struct fscrypt_str *); -int f2fs_inline_data_fiemap(struct inode *, - struct fiemap_extent_info *, __u64, __u64); +bool f2fs_may_inline_data(struct inode *inode); +bool f2fs_may_inline_dentry(struct inode *inode); +void read_inline_data(struct page *page, struct page *ipage); +bool truncate_inline_inode(struct page *ipage, u64 from); +int f2fs_read_inline_data(struct inode *inode, struct page *page); +int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page); +int f2fs_convert_inline_inode(struct inode *inode); +int f2fs_write_inline_data(struct inode *inode, struct page *page); +bool recover_inline_data(struct inode *inode, struct page *npage); +struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, + struct fscrypt_name *fname, struct page **res_page); +int make_empty_inline_dir(struct inode *inode, struct inode *parent, + struct page *ipage); +int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, + const struct qstr *orig_name, + struct inode *inode, nid_t ino, umode_t mode); +void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, + struct inode *dir, struct inode *inode); +bool f2fs_empty_inline_dir(struct inode *dir); +int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, + struct fscrypt_str *fstr); +int f2fs_inline_data_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len); /* * shrinker.c */ -unsigned long f2fs_shrink_count(struct shrinker *, struct shrink_control *); -unsigned long f2fs_shrink_scan(struct shrinker *, struct shrink_control *); -void f2fs_join_shrinker(struct f2fs_sb_info *); -void f2fs_leave_shrinker(struct f2fs_sb_info *); +unsigned long f2fs_shrink_count(struct shrinker *shrink, + struct shrink_control *sc); +unsigned long f2fs_shrink_scan(struct shrinker *shrink, + struct shrink_control *sc); +void f2fs_join_shrinker(struct f2fs_sb_info *sbi); +void f2fs_leave_shrinker(struct f2fs_sb_info *sbi); /* * extent_cache.c */ -unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int); -bool f2fs_init_extent_tree(struct inode *, struct f2fs_extent *); -void f2fs_drop_extent_tree(struct inode *); -unsigned int f2fs_destroy_extent_node(struct inode *); -void f2fs_destroy_extent_tree(struct inode *); -bool f2fs_lookup_extent_cache(struct inode *, pgoff_t, struct extent_info *); -void f2fs_update_extent_cache(struct dnode_of_data *); +unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink); +bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext); +void f2fs_drop_extent_tree(struct inode *inode); +unsigned int f2fs_destroy_extent_node(struct inode *inode); +void f2fs_destroy_extent_tree(struct inode *inode); +bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs, + struct extent_info *ei); +void f2fs_update_extent_cache(struct dnode_of_data *dn); void f2fs_update_extent_cache_range(struct dnode_of_data *dn, - pgoff_t, block_t, unsigned int); -void init_extent_cache_info(struct f2fs_sb_info *); + pgoff_t fofs, block_t blkaddr, unsigned int len); +void init_extent_cache_info(struct f2fs_sb_info *sbi); int __init create_extent_cache(void); void destroy_extent_cache(void); -- GitLab From c8aa24c05c502a0f8023e507c72e9bcf8a50f5f9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 1 Feb 2017 15:40:11 -0800 Subject: [PATCH 0498/5498] f2fs: show # of APPEND and UPDATE inodes This patch shows cached # of APPEND and UPDATE inode entries. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 6 ++++-- fs/f2fs/f2fs.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 3af986b4fdaf..ed8992d483a3 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -70,6 +70,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->inline_xattr = atomic_read(&sbi->inline_xattr); si->inline_inode = atomic_read(&sbi->inline_inode); si->inline_dir = atomic_read(&sbi->inline_dir); + si->append = sbi->im[APPEND_INO].ino_num; + si->update = sbi->im[UPDATE_INO].ino_num; si->orphans = sbi->im[ORPHAN_INO].ino_num; si->utilization = utilization(sbi); @@ -266,8 +268,8 @@ static int stat_show(struct seq_file *s, void *v) si->inline_inode); seq_printf(s, " - Inline_dentry Inode: %u\n", si->inline_dir); - seq_printf(s, " - Orphan Inode: %u\n", - si->orphans); + seq_printf(s, " - Orphan/Append/Update Inode: %u, %u, %u\n", + si->orphans, si->append, si->update); seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", si->main_area_segs, si->main_area_sections, si->main_area_zones); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 820171d05322..1187195c7d84 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2403,7 +2403,7 @@ struct f2fs_stat_info { int nats, dirty_nats, sits, dirty_sits, free_nids, alloc_nids; int total_count, utilization; int bg_gc, nr_wb_cp_data, nr_wb_data, nr_flush, nr_discard; - int inline_xattr, inline_inode, inline_dir, orphans; + int inline_xattr, inline_inode, inline_dir, append, update, orphans; int aw_cnt, max_aw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; unsigned int bimodal, avg_vblocks; -- GitLab From b7dbb15e2648984c8b0a74e97e83b365df07b469 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Feb 2017 16:40:55 -0800 Subject: [PATCH 0499/5498] f2fs: move flush tracepoint This patch moves the tracepoint location for flush command. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e50053f3d725..1faad2bc32d8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -426,6 +426,9 @@ static int submit_flush_wait(struct f2fs_sb_info *sbi) int ret = __submit_flush_wait(sbi->sb->s_bdev); int i; + trace_f2fs_issue_flush(sbi->sb, test_opt(sbi, NOBARRIER), + test_opt(sbi, FLUSH_MERGE)); + if (sbi->s_ndevs && !ret) { for (i = 1; i < sbi->s_ndevs; i++) { ret = __submit_flush_wait(FDEV(i).bdev); @@ -471,9 +474,6 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; struct flush_cmd cmd; - trace_f2fs_issue_flush(sbi->sb, test_opt(sbi, NOBARRIER), - test_opt(sbi, FLUSH_MERGE)); - if (test_opt(sbi, NOBARRIER)) return 0; -- GitLab From 651226eb2ebba58e15e69b7d2a38234408c60c3f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Feb 2017 18:27:17 -0800 Subject: [PATCH 0500/5498] f2fs: move write_node_page above fsync_node_pages This patch just moves write_node_page and introduces an inner function. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 140 ++++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 67 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7d00bdd0e636..8d391d6d0db6 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1324,6 +1324,78 @@ continue_unlock: return last_page; } +static int __write_node_page(struct page *page, + struct writeback_control *wbc) +{ + struct f2fs_sb_info *sbi = F2FS_P_SB(page); + nid_t nid; + struct node_info ni; + struct f2fs_io_info fio = { + .sbi = sbi, + .type = NODE, + .op = REQ_OP_WRITE, + .op_flags = wbc_to_write_flags(wbc), + .page = page, + .encrypted_page = NULL, + }; + + trace_f2fs_writepage(page, NODE); + + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto redirty_out; + if (unlikely(f2fs_cp_error(sbi))) + goto redirty_out; + + /* get old block addr of this node page */ + nid = nid_of_node(page); + f2fs_bug_on(sbi, page->index != nid); + + if (wbc->for_reclaim) { + if (!down_read_trylock(&sbi->node_write)) + goto redirty_out; + } else { + down_read(&sbi->node_write); + } + + get_node_info(sbi, nid, &ni); + + /* This page is already truncated */ + if (unlikely(ni.blk_addr == NULL_ADDR)) { + ClearPageUptodate(page); + dec_page_count(sbi, F2FS_DIRTY_NODES); + up_read(&sbi->node_write); + unlock_page(page); + return 0; + } + + set_page_writeback(page); + fio.old_blkaddr = ni.blk_addr; + write_node_page(nid, &fio); + set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page)); + dec_page_count(sbi, F2FS_DIRTY_NODES); + up_read(&sbi->node_write); + + if (wbc->for_reclaim) + f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE); + + unlock_page(page); + + if (unlikely(f2fs_cp_error(sbi))) + f2fs_submit_merged_bio(sbi, NODE, WRITE); + + return 0; + +redirty_out: + redirty_page_for_writepage(wbc, page); + return AOP_WRITEPAGE_ACTIVATE; +} + +static int f2fs_write_node_page(struct page *page, + struct writeback_control *wbc) +{ + return __write_node_page(page, wbc); +} + int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic) { @@ -1403,7 +1475,7 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = NODE_MAPPING(sbi)->a_ops->writepage(page, wbc); + ret = __write_node_page(page, wbc); if (ret) { unlock_page(page); f2fs_put_page(last_page, 0); @@ -1586,72 +1658,6 @@ int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino) return ret; } -static int f2fs_write_node_page(struct page *page, - struct writeback_control *wbc) -{ - struct f2fs_sb_info *sbi = F2FS_P_SB(page); - nid_t nid; - struct node_info ni; - struct f2fs_io_info fio = { - .sbi = sbi, - .type = NODE, - .op = REQ_OP_WRITE, - .op_flags = wbc_to_write_flags(wbc), - .page = page, - .encrypted_page = NULL, - }; - - trace_f2fs_writepage(page, NODE); - - if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) - goto redirty_out; - if (unlikely(f2fs_cp_error(sbi))) - goto redirty_out; - - /* get old block addr of this node page */ - nid = nid_of_node(page); - f2fs_bug_on(sbi, page->index != nid); - - if (wbc->for_reclaim) { - if (!down_read_trylock(&sbi->node_write)) - goto redirty_out; - } else { - down_read(&sbi->node_write); - } - - get_node_info(sbi, nid, &ni); - - /* This page is already truncated */ - if (unlikely(ni.blk_addr == NULL_ADDR)) { - ClearPageUptodate(page); - dec_page_count(sbi, F2FS_DIRTY_NODES); - up_read(&sbi->node_write); - unlock_page(page); - return 0; - } - - set_page_writeback(page); - fio.old_blkaddr = ni.blk_addr; - write_node_page(nid, &fio); - set_node_addr(sbi, &ni, fio.new_blkaddr, is_fsync_dnode(page)); - dec_page_count(sbi, F2FS_DIRTY_NODES); - up_read(&sbi->node_write); - - if (wbc->for_reclaim) - f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE); - - unlock_page(page); - - if (unlikely(f2fs_cp_error(sbi))) - f2fs_submit_merged_bio(sbi, NODE, WRITE); - - return 0; - -redirty_out: - redirty_page_for_writepage(wbc, page); - return AOP_WRITEPAGE_ACTIVATE; -} - static int f2fs_write_node_pages(struct address_space *mapping, struct writeback_control *wbc) { -- GitLab From cfb949e4bda73577eb15dfbcdbf7f6a561003e44 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Feb 2017 18:18:06 -0800 Subject: [PATCH 0501/5498] f2fs: avoid out-of-order execution of atomic writes We need to flush data writes before flushing last node block writes by using FUA with PREFLUSH. We don't need to guarantee precedent node writes since if those are not written, we can't reach to the last node block when scanning node block chain during roll-forward recovery. Afterwards f2fs_wait_on_page_writeback guarantees all the IO submission to disk, which builds a valid node block chain. Signed-off-by: Jaegeuk Kim Conflicts: include/trace/events/f2fs.h --- fs/f2fs/file.c | 3 ++- fs/f2fs/node.c | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f52d637429e3..c6b31a2a433a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -279,7 +279,8 @@ sync_nodes: flush_out: remove_ino_entry(sbi, ino, UPDATE_INO); clear_inode_flag(inode, FI_UPDATE_WRITE); - ret = f2fs_issue_flush(sbi); + if (!atomic) + ret = f2fs_issue_flush(sbi); f2fs_update_time(sbi, REQ_TIME); out: trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 8d391d6d0db6..06b8212f79a8 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1324,7 +1324,7 @@ continue_unlock: return last_page; } -static int __write_node_page(struct page *page, +static int __write_node_page(struct page *page, bool atomic, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); @@ -1368,6 +1368,9 @@ static int __write_node_page(struct page *page, return 0; } + if (atomic && !test_opt(sbi, NOBARRIER)) + fio.op_flags |= WRITE_FLUSH_FUA; + set_page_writeback(page); fio.old_blkaddr = ni.blk_addr; write_node_page(nid, &fio); @@ -1393,7 +1396,7 @@ redirty_out: static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { - return __write_node_page(page, wbc); + return __write_node_page(page, false, wbc); } int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, @@ -1475,7 +1478,8 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = __write_node_page(page, wbc); + ret = __write_node_page(page, atomic && + page == last_page, wbc); if (ret) { unlock_page(page); f2fs_put_page(last_page, 0); -- GitLab From 47062c4118929bf72f222f2153ce3e685090dd22 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 3 Feb 2017 17:18:00 -0800 Subject: [PATCH 0502/5498] f2fs: call internal __write_data_page directly This patch introduces __write_data_page to call it by f2fs_write_cache_pages directly.. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 42538dc6225a..e0a585c47d2d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1341,7 +1341,7 @@ out_writepage: return err; } -static int f2fs_write_data_page(struct page *page, +static int __write_data_page(struct page *page, struct writeback_control *wbc) { struct inode *inode = page->mapping->host; @@ -1443,6 +1443,12 @@ redirty_out: return err; } +static int f2fs_write_data_page(struct page *page, + struct writeback_control *wbc) +{ + return __write_data_page(page, wbc); +} + /* * This function was copied from write_cche_pages from mm/page-writeback.c. * The major change is making write step of cold data page separately from @@ -1532,7 +1538,7 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = mapping->a_ops->writepage(page, wbc); + ret = __write_data_page(page, wbc); if (unlikely(ret)) { /* * keep nr_to_write, since vfs uses this to -- GitLab From 873b6bd9fb3fc702308f15d5337a03f921790e65 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 3 Feb 2017 17:44:04 -0800 Subject: [PATCH 0503/5498] f2fs: check io submission more precisely This patch check IO submission more precisely than previous rough check. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 23 +++++++++++++++++------ fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 27 +++++++++++++++++++-------- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index e0a585c47d2d..b6f2daea7206 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -380,6 +380,9 @@ int f2fs_submit_page_mbio(struct f2fs_io_info *fio) bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; + /* set submitted = 1 as a return value */ + fio->submitted = 1; + if (!is_read) inc_page_count(sbi, WB_DATA_TYPE(bio_page)); @@ -1341,8 +1344,8 @@ out_writepage: return err; } -static int __write_data_page(struct page *page, - struct writeback_control *wbc) +static int __write_data_page(struct page *page, bool *submitted, + struct writeback_control *wbc) { struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -1360,6 +1363,7 @@ static int __write_data_page(struct page *page, .op_flags = wbc_to_write_flags(wbc), .page = page, .encrypted_page = NULL, + .submitted = false, }; trace_f2fs_writepage(page, DATA); @@ -1425,13 +1429,19 @@ out: if (wbc->for_reclaim) { f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, DATA, WRITE); remove_dirty_inode(inode); + submitted = NULL; } unlock_page(page); f2fs_balance_fs(sbi, need_balance_fs); - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) { f2fs_submit_merged_bio(sbi, DATA, WRITE); + submitted = NULL; + } + + if (submitted) + *submitted = fio.submitted; return 0; @@ -1446,7 +1456,7 @@ redirty_out: static int f2fs_write_data_page(struct page *page, struct writeback_control *wbc) { - return __write_data_page(page, wbc); + return __write_data_page(page, NULL, wbc); } /* @@ -1505,6 +1515,7 @@ retry: for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; + bool submitted = false; if (page->index > end) { done = 1; @@ -1538,7 +1549,7 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = __write_data_page(page, wbc); + ret = __write_data_page(page, &submitted, wbc); if (unlikely(ret)) { /* * keep nr_to_write, since vfs uses this to @@ -1552,7 +1563,7 @@ continue_unlock: done_index = page->index + 1; done = 1; break; - } else { + } else if (submitted) { nwritten++; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1187195c7d84..454f1748457e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -828,6 +828,7 @@ struct f2fs_io_info { block_t old_blkaddr; /* old block address before Cow */ struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ + bool submitted; /* indicate IO submission */ }; #define is_read_io(rw) (rw == READ) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 06b8212f79a8..475a5c72d156 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1324,7 +1324,7 @@ continue_unlock: return last_page; } -static int __write_node_page(struct page *page, bool atomic, +static int __write_node_page(struct page *page, bool atomic, bool *submitted, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); @@ -1337,6 +1337,7 @@ static int __write_node_page(struct page *page, bool atomic, .op_flags = wbc_to_write_flags(wbc), .page = page, .encrypted_page = NULL, + .submitted = false, }; trace_f2fs_writepage(page, NODE); @@ -1378,13 +1379,19 @@ static int __write_node_page(struct page *page, bool atomic, dec_page_count(sbi, F2FS_DIRTY_NODES); up_read(&sbi->node_write); - if (wbc->for_reclaim) + if (wbc->for_reclaim) { f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE); + submitted = NULL; + } unlock_page(page); - if (unlikely(f2fs_cp_error(sbi))) + if (unlikely(f2fs_cp_error(sbi))) { f2fs_submit_merged_bio(sbi, NODE, WRITE); + submitted = NULL; + } + if (submitted) + *submitted = fio.submitted; return 0; @@ -1396,7 +1403,7 @@ redirty_out: static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { - return __write_node_page(page, false, wbc); + return __write_node_page(page, false, NULL, wbc); } int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, @@ -1430,6 +1437,7 @@ retry: for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; + bool submitted = false; if (unlikely(f2fs_cp_error(sbi))) { f2fs_put_page(last_page, 0); @@ -1479,12 +1487,13 @@ continue_unlock: goto continue_unlock; ret = __write_node_page(page, atomic && - page == last_page, wbc); + page == last_page, + &submitted, wbc); if (ret) { unlock_page(page); f2fs_put_page(last_page, 0); break; - } else { + } else if (submitted) { nwritten++; } @@ -1540,6 +1549,7 @@ next_step: for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; + bool submitted = false; if (unlikely(f2fs_cp_error(sbi))) { pagevec_release(&pvec); @@ -1593,9 +1603,10 @@ continue_unlock: set_fsync_mark(page, 0); set_dentry_mark(page, 0); - if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) + ret = __write_node_page(page, false, &submitted, wbc); + if (ret) unlock_page(page); - else + else if (submitted) nwritten++; if (--wbc->nr_to_write == 0) -- GitLab From 23f3e62a6b41938237aceb0cb177531c3b06ed95 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 1 Feb 2017 16:51:22 -0800 Subject: [PATCH 0504/5498] f2fs: check last page index in cached bio to decide submission If the cached bio has the last page's index, then we need to submit it. Otherwise, we don't need to submit it and can wait for further IO merges. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 3 ++- fs/f2fs/data.c | 43 ++++++++++++++++++++++--------------------- fs/f2fs/f2fs.h | 4 ++-- fs/f2fs/node.c | 12 +++++++----- fs/f2fs/segment.c | 13 +++++++------ 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 587641925477..b39383c3f3bd 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -250,7 +250,8 @@ static int f2fs_write_meta_page(struct page *page, dec_page_count(sbi, F2FS_DIRTY_META); if (wbc->for_reclaim) - f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, META, WRITE); + f2fs_submit_merged_bio_cond(sbi, page->mapping->host, + 0, page->index, META, WRITE); unlock_page(page); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b6f2daea7206..42a476413caf 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -244,8 +244,8 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) io->bio = NULL; } -static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, - struct page *page, nid_t ino) +static bool __has_merged_page(struct f2fs_bio_info *io, + struct inode *inode, nid_t ino, pgoff_t idx) { struct bio_vec *bvec; struct page *target; @@ -254,7 +254,7 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, if (!io->bio) return false; - if (!inode && !page && !ino) + if (!inode && !ino) return true; bio_for_each_segment_all(bvec, io->bio, i) { @@ -264,10 +264,11 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, else target = fscrypt_control_page(bvec->bv_page); + if (idx != target->index) + continue; + if (inode && inode == target->mapping->host) return true; - if (page && page == target) - return true; if (ino && ino == ino_of_node(target)) return true; } @@ -276,22 +277,21 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode, } static bool has_merged_page(struct f2fs_sb_info *sbi, struct inode *inode, - struct page *page, nid_t ino, - enum page_type type) + nid_t ino, pgoff_t idx, enum page_type type) { enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io = &sbi->write_io[btype]; bool ret; down_read(&io->io_rwsem); - ret = __has_merged_page(io, inode, page, ino); + ret = __has_merged_page(io, inode, ino, idx); up_read(&io->io_rwsem); return ret; } static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, - struct inode *inode, struct page *page, - nid_t ino, enum page_type type, int rw) + struct inode *inode, nid_t ino, pgoff_t idx, + enum page_type type, int rw) { enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io; @@ -300,7 +300,7 @@ static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, down_write(&io->io_rwsem); - if (!__has_merged_page(io, inode, page, ino)) + if (!__has_merged_page(io, inode, ino, idx)) goto out; /* change META to META_FLUSH in the checkpoint procedure */ @@ -319,15 +319,15 @@ out: void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type, int rw) { - __f2fs_submit_merged_bio(sbi, NULL, NULL, 0, type, rw); + __f2fs_submit_merged_bio(sbi, NULL, 0, 0, type, rw); } void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi, - struct inode *inode, struct page *page, - nid_t ino, enum page_type type, int rw) + struct inode *inode, nid_t ino, pgoff_t idx, + enum page_type type, int rw) { - if (has_merged_page(sbi, inode, page, ino, type)) - __f2fs_submit_merged_bio(sbi, inode, page, ino, type, rw); + if (has_merged_page(sbi, inode, ino, idx, type)) + __f2fs_submit_merged_bio(sbi, inode, ino, idx, type, rw); } void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi) @@ -1427,7 +1427,8 @@ out: ClearPageUptodate(page); if (wbc->for_reclaim) { - f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, DATA, WRITE); + f2fs_submit_merged_bio_cond(sbi, inode, 0, page->index, + DATA, WRITE); remove_dirty_inode(inode); submitted = NULL; } @@ -1475,10 +1476,10 @@ static int f2fs_write_cache_pages(struct address_space *mapping, pgoff_t index; pgoff_t end; /* Inclusive */ pgoff_t done_index; + pgoff_t last_idx = ULONG_MAX; int cycled; int range_whole = 0; int tag; - int nwritten = 0; pagevec_init(&pvec, 0); @@ -1564,7 +1565,7 @@ continue_unlock: done = 1; break; } else if (submitted) { - nwritten++; + last_idx = page->index; } if (--wbc->nr_to_write <= 0 && @@ -1586,9 +1587,9 @@ continue_unlock: if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = done_index; - if (nwritten) + if (last_idx != ULONG_MAX) f2fs_submit_merged_bio_cond(F2FS_M_SB(mapping), mapping->host, - NULL, 0, DATA, WRITE); + 0, last_idx, DATA, WRITE); return ret; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 454f1748457e..b1effd500db6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2334,8 +2334,8 @@ void destroy_checkpoint_caches(void); void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type, int rw); void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi, - struct inode *inode, struct page *page, - nid_t ino, enum page_type type, int rw); + struct inode *inode, nid_t ino, pgoff_t idx, + enum page_type type, int rw); void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi); int f2fs_submit_page_bio(struct f2fs_io_info *fio); int f2fs_submit_page_mbio(struct f2fs_io_info *fio); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 475a5c72d156..31d0a03d552f 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1380,7 +1380,8 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, up_read(&sbi->node_write); if (wbc->for_reclaim) { - f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE); + f2fs_submit_merged_bio_cond(sbi, page->mapping->host, 0, + page->index, NODE, WRITE); submitted = NULL; } @@ -1410,12 +1411,12 @@ int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic) { pgoff_t index, end; + pgoff_t last_idx = ULONG_MAX; struct pagevec pvec; int ret = 0; struct page *last_page = NULL; bool marked = false; nid_t ino = inode->i_ino; - int nwritten = 0; if (atomic) { last_page = last_fsync_dnode(sbi, ino); @@ -1494,7 +1495,7 @@ continue_unlock: f2fs_put_page(last_page, 0); break; } else if (submitted) { - nwritten++; + last_idx = page->index; } if (page == last_page) { @@ -1520,8 +1521,9 @@ continue_unlock: goto retry; } out: - if (nwritten) - f2fs_submit_merged_bio_cond(sbi, NULL, NULL, ino, NODE, WRITE); + if (last_idx != ULONG_MAX) + f2fs_submit_merged_bio_cond(sbi, NULL, ino, last_idx, + NODE, WRITE); return ret ? -EIO: 0; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1faad2bc32d8..2563d3b27234 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -263,7 +263,7 @@ static int __commit_inmem_pages(struct inode *inode, .op_flags = REQ_SYNC | REQ_PRIO, .encrypted_page = NULL, }; - bool submit_bio = false; + pgoff_t last_idx = ULONG_MAX; int err = 0; list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { @@ -289,15 +289,15 @@ static int __commit_inmem_pages(struct inode *inode, /* record old blkaddr for revoking */ cur->old_addr = fio.old_blkaddr; - - submit_bio = true; + last_idx = page->index; } unlock_page(page); list_move_tail(&cur->list, revoke_list); } - if (submit_bio) - f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE); + if (last_idx != ULONG_MAX) + f2fs_submit_merged_bio_cond(sbi, inode, 0, last_idx, + DATA, WRITE); if (!err) __revoke_inmem_pages(inode, revoke_list, false, false); @@ -2012,7 +2012,8 @@ void f2fs_wait_on_page_writeback(struct page *page, if (PageWriteback(page)) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); - f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, type, WRITE); + f2fs_submit_merged_bio_cond(sbi, page->mapping->host, + 0, page->index, type, WRITE); if (ordered) wait_on_page_writeback(page); else -- GitLab From d1a1f4c556523973de2b3dc97af73f3212e18af9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 6 Feb 2017 13:57:58 -0800 Subject: [PATCH 0505/5498] f2fs: remove preflush for nobarrier case This patch removes REQ_PREFLUSH in the nobarrier case. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 42a476413caf..3207fd3e8a08 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -307,9 +307,9 @@ static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, if (type >= META_FLUSH) { io->fio.type = META_FLUSH; io->fio.op = REQ_OP_WRITE; - io->fio.op_flags = WRITE_FLUSH | REQ_META | REQ_PRIO; + io->fio.op_flags = REQ_META | REQ_PRIO; if (!test_opt(sbi, NOBARRIER)) - io->fio.op_flags |= REQ_FUA; + io->fio.op_flags |= WRITE_FLUSH | REQ_FUA; } __submit_merged_bio(io); out: -- GitLab From d2252881c39bd14765eed1da9d91680d897fa861 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 9 Feb 2017 13:25:35 -0800 Subject: [PATCH 0506/5498] f2fs: show checkpoint version at mount time If we mounted f2fs successfully, let's show current checkpoint version. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7d17592a3b04..13b8d8cd5ecd 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2089,6 +2089,8 @@ skip_recovery: sbi->valid_super_block ? 1 : 2, err); } + f2fs_msg(sbi->sb, KERN_NOTICE, "Mounted with checkpoint version = %llx", + cur_cp_version(F2FS_CKPT(sbi))); f2fs_update_time(sbi, CP_TIME); f2fs_update_time(sbi, REQ_TIME); return 0; -- GitLab From 1bd3ef9dcdd62e8fd9be4180e263d35a8d453961 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sat, 11 Feb 2017 15:50:46 +0530 Subject: [PATCH 0507/5498] f2fs: super: constify fscrypt_operations structure Declare fscrypt_operations structure as const as it is only stored in the s_cop field of a super_block structure. This field is of type const, so fscrypt_operations structure having this property can be made const too. File size before: fs/f2fs/super.o text data bss dec hex filename 54131 31355 184 85670 14ea6 fs/f2fs/super.o File size after: fs/f2fs/super.o text data bss dec hex filename 54227 31259 184 85670 14ea6 fs/f2fs/super.o Signed-off-by: Bhumika Goyal Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 13b8d8cd5ecd..573161d6d0c8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1196,7 +1196,7 @@ static unsigned f2fs_max_namelen(struct inode *inode) inode->i_sb->s_blocksize : F2FS_NAME_LEN; } -static struct fscrypt_operations f2fs_cryptops = { +static const struct fscrypt_operations f2fs_cryptops = { .get_context = f2fs_get_context, .key_prefix = f2fs_key_prefix, .set_context = f2fs_set_context, @@ -1205,7 +1205,7 @@ static struct fscrypt_operations f2fs_cryptops = { .max_namelen = f2fs_max_namelen, }; #else -static struct fscrypt_operations f2fs_cryptops = { +static const struct fscrypt_operations f2fs_cryptops = { .is_encrypted = f2fs_encrypted_inode, }; #endif -- GitLab From f227a5389b6119f248ec2ae16252e56f7ed8d9a3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 8 Feb 2017 17:39:45 +0800 Subject: [PATCH 0508/5498] f2fs: change recovery policy of xattr node block Currently, if we call fsync after updating the xattr date belongs to the file, f2fs needs to trigger checkpoint to keep xattr data consistent. But, this policy cause low performance as checkpoint will block most foreground operations and cause unneeded and unrelated IOs around checkpoint. This patch will reuse regular file recovery policy for xattr node block, so, we change to write xattr node block tagged with fsync flag to warm area instead of cold area, and during recovery, we search warm node chain for fsynced xattr block, and do the recovery. So, for below application IO pattern, performance can be improved obviously: - touch file - create/update/delete xattr entry in file - fsync file Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/file.c | 3 --- fs/f2fs/node.c | 29 +++++++++++++++++------------ fs/f2fs/node.h | 2 +- fs/f2fs/recovery.c | 8 +++----- fs/f2fs/xattr.c | 2 -- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b1effd500db6..3ab2b0947516 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -560,7 +560,6 @@ struct f2fs_inode_info { f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ nid_t i_xattr_nid; /* node id that contains xattrs */ - unsigned long long xattr_ver; /* cp version of xattr modification */ loff_t last_disk_size; /* lastly written file size */ struct list_head dirty_list; /* dirty list for dirs and files */ @@ -2234,7 +2233,7 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid); int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink); void recover_inline_xattr(struct inode *inode, struct page *page); -void recover_xattr_data(struct inode *inode, struct page *page, +int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr); int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page); int restore_node_summary(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c6b31a2a433a..cc0066f0a64a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -143,8 +143,6 @@ static inline bool need_do_checkpoint(struct inode *inode) need_cp = true; else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino)) need_cp = true; - else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi))) - need_cp = true; else if (test_opt(sbi, FASTBOOT)) need_cp = true; else if (sbi->active_logs == 2) @@ -170,7 +168,6 @@ static void try_to_fix_pino(struct inode *inode) nid_t pino; down_write(&fi->i_sem); - fi->xattr_ver = 0; if (file_wrong_pino(inode) && inode->i_nlink == 1 && get_parent_ino(inode, &pino)) { f2fs_i_pino_write(inode, pino); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 31d0a03d552f..2bbd7cf6cee8 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -971,9 +971,6 @@ int truncate_xattr_node(struct inode *inode, struct page *page) f2fs_i_xnid_write(inode, 0); - /* need to do checkpoint during fsync */ - F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); - set_new_dnode(&dn, inode, page, npage, nid); if (page) @@ -2063,18 +2060,18 @@ update_inode: f2fs_put_page(ipage, 1); } -void recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) +int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid; nid_t new_xnid = nid_of_node(page); struct node_info ni; + struct page *xpage; - /* 1: invalidate the previous xattr nid */ if (!prev_xnid) goto recover_xnid; - /* Deallocate node address */ + /* 1: invalidate the previous xattr nid */ get_node_info(sbi, prev_xnid, &ni); f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); invalidate_blocks(sbi, ni.blk_addr); @@ -2082,19 +2079,27 @@ void recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) set_node_addr(sbi, &ni, NULL_ADDR, false); recover_xnid: - /* 2: allocate new xattr nid */ + /* 2: update xattr nid in inode */ + remove_free_nid(sbi, new_xnid); + f2fs_i_xnid_write(inode, new_xnid); if (unlikely(!inc_valid_node_count(sbi, inode))) f2fs_bug_on(sbi, 1); + update_inode_page(inode); + + /* 3: update and set xattr node page dirty */ + xpage = grab_cache_page(NODE_MAPPING(sbi), new_xnid); + if (!xpage) + return -ENOMEM; + + memcpy(F2FS_NODE(xpage), F2FS_NODE(page), PAGE_SIZE); - remove_free_nid(sbi, new_xnid); get_node_info(sbi, new_xnid, &ni); ni.ino = inode->i_ino; set_node_addr(sbi, &ni, NEW_ADDR, false); - f2fs_i_xnid_write(inode, new_xnid); + set_page_dirty(xpage); + f2fs_put_page(xpage, 1); - /* 3: update xattr blkaddr */ - refresh_sit_entry(sbi, NEW_ADDR, blkaddr); - set_node_addr(sbi, &ni, blkaddr, false); + return 0; } int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 29ff783eb9c3..d3d289306469 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -358,7 +358,7 @@ static inline bool IS_DNODE(struct page *node_page) unsigned int ofs = ofs_of_node(node_page); if (f2fs_has_xattr_block(ofs)) - return false; + return true; if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK || ofs == 5 + 2 * NIDS_PER_BLOCK) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index ec0589a5ecae..647cbe5acea7 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -378,11 +378,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (IS_INODE(page)) { recover_inline_xattr(inode, page); } else if (f2fs_has_xattr_block(ofs_of_node(page))) { - /* - * Deprecated; xattr blocks should be found from cold log. - * But, we should remain this for backward compatibility. - */ - recover_xattr_data(inode, page, blkaddr); + err = recover_xattr_data(inode, page, blkaddr); + if (!err) + recovered++; goto out; } diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index eca6e7dc43fb..9e293af68b79 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -507,8 +507,6 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, set_page_dirty(xpage); f2fs_put_page(xpage, 1); - /* need to checkpoint during fsync */ - F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); return 0; } -- GitLab From 1674ce538208b401a371cd1ce56201e6a4bf880f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 11 Feb 2017 10:46:44 -0800 Subject: [PATCH 0509/5498] f2fs: remove build_free_nids() during checkpoint Let's avoid build_free_nids() in checkpoint path. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index b39383c3f3bd..08d7df834a60 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1000,8 +1000,6 @@ out: static void unblock_operations(struct f2fs_sb_info *sbi) { up_write(&sbi->node_write); - - build_free_nids(sbi, false); f2fs_unlock_all(sbi); } -- GitLab From c7e8e2d9fd4d2b2d9da72698171905c5a2e638d1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Feb 2017 17:02:44 -0800 Subject: [PATCH 0510/5498] f2fs: avoid reading NAT page by get_node_info We've not seen this buggy case for a long time, so it's time to avoid this unnecessary get_node_info() call which reading NAT page to cache nat entry. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2bbd7cf6cee8..d2c9ff470699 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1028,7 +1028,7 @@ struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs, struct page *ipage) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); - struct node_info old_ni, new_ni; + struct node_info new_ni; struct page *page; int err; @@ -1043,13 +1043,15 @@ struct page *new_node_page(struct dnode_of_data *dn, err = -ENOSPC; goto fail; } - - get_node_info(sbi, dn->nid, &old_ni); - - /* Reinitialize old_ni with new node page */ - f2fs_bug_on(sbi, old_ni.blk_addr != NULL_ADDR); - new_ni = old_ni; +#ifdef CONFIG_F2FS_CHECK_FS + get_node_info(sbi, dn->nid, &new_ni); + f2fs_bug_on(sbi, new_ni.blk_addr != NULL_ADDR); +#endif + new_ni.nid = dn->nid; new_ni.ino = dn->inode->i_ino; + new_ni.blk_addr = NULL_ADDR; + new_ni.flag = 0; + new_ni.version = 0; set_node_addr(sbi, &new_ni, NEW_ADDR, false); f2fs_wait_on_page_writeback(page, NODE, true); -- GitLab From 19ebd0191879a34c806cac32cb059c177f80dbe5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 15 Feb 2017 10:34:45 +0800 Subject: [PATCH 0511/5498] f2fs: introduce noinline_xattr mount option This patch introduces new mount option 'noinline_xattr', so we can disable inline xattr functionality which is already set as a default mount option. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 1 + fs/f2fs/super.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index c034b2268a9e..e7fc81f3fa91 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -124,6 +124,7 @@ active_logs=%u Support configuring the number of active logs. In the disable_ext_identify Disable the extension list configured by mkfs, so f2fs does not aware of cold files such as media files. inline_xattr Enable the inline xattrs feature. +noinline_xattr Disable the inline xattrs feature. inline_data Enable the inline data feature: New created small(<~3.4k) files can be written into inode block. inline_dentry Enable the inline dir feature: data in new created diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 573161d6d0c8..b1e9fe629f6b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -89,6 +89,7 @@ enum { Opt_active_logs, Opt_disable_ext_identify, Opt_inline_xattr, + Opt_noinline_xattr, Opt_inline_data, Opt_inline_dentry, Opt_noinline_dentry, @@ -120,6 +121,7 @@ static match_table_t f2fs_tokens = { {Opt_active_logs, "active_logs=%u"}, {Opt_disable_ext_identify, "disable_ext_identify"}, {Opt_inline_xattr, "inline_xattr"}, + {Opt_noinline_xattr, "noinline_xattr"}, {Opt_inline_data, "inline_data"}, {Opt_inline_dentry, "inline_dentry"}, {Opt_noinline_dentry, "noinline_dentry"}, @@ -440,6 +442,9 @@ static int parse_options(struct super_block *sb, char *options) case Opt_inline_xattr: set_opt(sbi, INLINE_XATTR); break; + case Opt_noinline_xattr: + clear_opt(sbi, INLINE_XATTR); + break; #else case Opt_user_xattr: f2fs_msg(sb, KERN_INFO, @@ -453,6 +458,10 @@ static int parse_options(struct super_block *sb, char *options) f2fs_msg(sb, KERN_INFO, "inline_xattr options not supported"); break; + case Opt_noinline_xattr: + f2fs_msg(sb, KERN_INFO, + "noinline_xattr options not supported"); + break; #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL case Opt_acl: @@ -896,6 +905,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",nouser_xattr"); if (test_opt(sbi, INLINE_XATTR)) seq_puts(seq, ",inline_xattr"); + else + seq_puts(seq, ",noinline_xattr"); #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL if (test_opt(sbi, POSIX_ACL)) -- GitLab From 176a111bd4e34164fd10aecb2667e1b0202932ad Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 8 Feb 2017 17:39:44 +0800 Subject: [PATCH 0512/5498] f2fs: enable inline_xattr by default In android, since SElinux is enable, security policy will be appliedd for each file, it stores in inode as an xattr entry, so it will take one 4k size node block additionally for each file. Let's enable inline_xattr by default in order to save storage space. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b1e9fe629f6b..836bd4d286ee 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1027,6 +1027,7 @@ static void default_options(struct f2fs_sb_info *sbi) sbi->active_logs = NR_CURSEG_TYPE; set_opt(sbi, BG_GC); + set_opt(sbi, INLINE_XATTR); set_opt(sbi, INLINE_DATA); set_opt(sbi, INLINE_DENTRY); set_opt(sbi, EXTENT_CACHE); -- GitLab From aea94a7f0e7bc4a26a8a62449be8c3b4e971b634 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 14 Feb 2017 19:32:51 -0800 Subject: [PATCH 0513/5498] f2fs: use SSR for warm node as well We have had node chains, but haven't used it so far due to stale node blocks. Now, we have crc|cp_ver in node footer and give random cp_ver at format time, we can start to use it again. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2563d3b27234..a12e775eee07 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1643,7 +1643,8 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi, if (force) new_curseg(sbi, type, true); - else if (type == CURSEG_WARM_NODE) + else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) && + type == CURSEG_WARM_NODE) new_curseg(sbi, type, false); else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type)) new_curseg(sbi, type, false); -- GitLab From 8b6b7ef5ad92b49337f212c5189fb29a4d610b4f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 15 Feb 2017 11:14:06 -0800 Subject: [PATCH 0514/5498] f2fs: show actual device info in tracepoints This patch shows actual device information in the tracepoints. Signed-off-by: Jaegeuk Kim Conflicts: include/trace/events/f2fs.h --- fs/f2fs/segment.c | 10 ++++---- include/trace/events/f2fs.h | 49 ++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a12e775eee07..56f5c042b861 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -426,11 +426,11 @@ static int submit_flush_wait(struct f2fs_sb_info *sbi) int ret = __submit_flush_wait(sbi->sb->s_bdev); int i; - trace_f2fs_issue_flush(sbi->sb, test_opt(sbi, NOBARRIER), - test_opt(sbi, FLUSH_MERGE)); - if (sbi->s_ndevs && !ret) { for (i = 1; i < sbi->s_ndevs; i++) { + trace_f2fs_issue_flush(FDEV(i).bdev, + test_opt(sbi, NOBARRIER), + test_opt(sbi, FLUSH_MERGE)); ret = __submit_flush_wait(FDEV(i).bdev); if (ret) break; @@ -840,7 +840,7 @@ static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, block_t lblkstart = blkstart; int err; - trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); + trace_f2fs_issue_discard(bdev, blkstart, blklen); if (sbi->s_ndevs) { int devi = f2fs_target_device_index(sbi, blkstart); @@ -895,7 +895,7 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); case BLK_ZONE_TYPE_SEQWRITE_REQ: case BLK_ZONE_TYPE_SEQWRITE_PREF: - trace_f2fs_issue_reset_zone(sbi->sb, blkstart); + trace_f2fs_issue_reset_zone(bdev, blkstart); return blkdev_reset_zones(bdev, sector, nr_sects, GFP_NOFS); default: diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 75fe15994f65..2e1aefca3c02 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -6,8 +6,8 @@ #include -#define show_dev(entry) MAJOR(entry->dev), MINOR(entry->dev) -#define show_dev_ino(entry) show_dev(entry), (unsigned long)entry->ino +#define show_dev(dev) MAJOR(dev), MINOR(dev) +#define show_dev_ino(entry) show_dev(entry->dev), (unsigned long)entry->ino #define show_block_type(type) \ __print_symbolic(type, \ @@ -204,7 +204,7 @@ TRACE_EVENT(f2fs_sync_fs, ), TP_printk("dev = (%d,%d), superblock is %s, wait = %d", - show_dev(__entry), + show_dev(__entry->dev), __entry->dirty ? "dirty" : "not dirty", __entry->wait) ); @@ -503,7 +503,7 @@ TRACE_EVENT(f2fs_background_gc, ), TP_printk("dev = (%d,%d), wait_ms = %ld, prefree = %u, free = %u", - show_dev(__entry), + show_dev(__entry->dev), __entry->wait_ms, __entry->prefree, __entry->free) @@ -545,7 +545,7 @@ TRACE_EVENT(f2fs_get_victim, TP_printk("dev = (%d,%d), type = %s, policy = (%s, %s, %s), victim = %u " "ofs_unit = %u, pre_victim_secno = %d, prefree = %u, free = %u", - show_dev(__entry), + show_dev(__entry->dev), show_data_type(__entry->type), show_gc_type(__entry->gc_type), show_alloc_mode(__entry->alloc_mode), @@ -682,7 +682,7 @@ TRACE_EVENT(f2fs_reserve_new_blocks, ), TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u, count = %llu", - show_dev(__entry), + show_dev(__entry->dev), (unsigned int)__entry->nid, __entry->ofs_in_node, (unsigned long long)__entry->count) @@ -752,6 +752,7 @@ DECLARE_EVENT_CLASS(f2fs__bio, TP_STRUCT__entry( __field(dev_t, dev) + __field(dev_t, target) __field(int, op) __field(int, op_flags) __field(int, type) @@ -761,6 +762,7 @@ DECLARE_EVENT_CLASS(f2fs__bio, TP_fast_assign( __entry->dev = sb->s_dev; + __entry->target = bio->bi_bdev->bd_dev; __entry->op = bio_op(bio); __entry->op_flags = bio->bi_rw; __entry->type = type; @@ -768,8 +770,9 @@ DECLARE_EVENT_CLASS(f2fs__bio, __entry->size = bio->bi_iter.bi_size; ), - TP_printk("dev = (%d,%d), %s%s, %s, sector = %lld, size = %u", - show_dev(__entry), + TP_printk("dev = (%d,%d)/(%d,%d), rw = %s%s, %s, sector = %lld, size = %u", + show_dev(__entry->target), + show_dev(__entry->dev), show_bio_type(__entry->op, __entry->op_flags), show_block_type(__entry->type), (unsigned long long)__entry->sector, @@ -1066,16 +1069,16 @@ TRACE_EVENT(f2fs_write_checkpoint, ), TP_printk("dev = (%d,%d), checkpoint for %s, state = %s", - show_dev(__entry), + show_dev(__entry->dev), show_cpreason(__entry->reason), __entry->msg) ); TRACE_EVENT(f2fs_issue_discard, - TP_PROTO(struct super_block *sb, block_t blkstart, block_t blklen), + TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen), - TP_ARGS(sb, blkstart, blklen), + TP_ARGS(dev, blkstart, blklen), TP_STRUCT__entry( __field(dev_t, dev) @@ -1084,22 +1087,22 @@ TRACE_EVENT(f2fs_issue_discard, ), TP_fast_assign( - __entry->dev = sb->s_dev; + __entry->dev = dev->bd_dev; __entry->blkstart = blkstart; __entry->blklen = blklen; ), TP_printk("dev = (%d,%d), blkstart = 0x%llx, blklen = 0x%llx", - show_dev(__entry), + show_dev(__entry->dev), (unsigned long long)__entry->blkstart, (unsigned long long)__entry->blklen) ); TRACE_EVENT(f2fs_issue_reset_zone, - TP_PROTO(struct super_block *sb, block_t blkstart), + TP_PROTO(struct block_device *dev, block_t blkstart), - TP_ARGS(sb, blkstart), + TP_ARGS(dev, blkstart), TP_STRUCT__entry( __field(dev_t, dev) @@ -1107,21 +1110,21 @@ TRACE_EVENT(f2fs_issue_reset_zone, ), TP_fast_assign( - __entry->dev = sb->s_dev; + __entry->dev = dev->bd_dev; __entry->blkstart = blkstart; ), TP_printk("dev = (%d,%d), reset zone at block = 0x%llx", - show_dev(__entry), + show_dev(__entry->dev), (unsigned long long)__entry->blkstart) ); TRACE_EVENT(f2fs_issue_flush, - TP_PROTO(struct super_block *sb, unsigned int nobarrier, + TP_PROTO(struct block_device *dev, unsigned int nobarrier, unsigned int flush_merge), - TP_ARGS(sb, nobarrier, flush_merge), + TP_ARGS(dev, nobarrier, flush_merge), TP_STRUCT__entry( __field(dev_t, dev) @@ -1130,13 +1133,13 @@ TRACE_EVENT(f2fs_issue_flush, ), TP_fast_assign( - __entry->dev = sb->s_dev; + __entry->dev = dev->bd_dev; __entry->nobarrier = nobarrier; __entry->flush_merge = flush_merge; ), TP_printk("dev = (%d,%d), %s %s", - show_dev(__entry), + show_dev(__entry->dev), __entry->nobarrier ? "skip (nobarrier)" : "issue", __entry->flush_merge ? " with flush_merge" : "") ); @@ -1251,7 +1254,7 @@ TRACE_EVENT(f2fs_shrink_extent_tree, ), TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u", - show_dev(__entry), + show_dev(__entry->dev), __entry->node_cnt, __entry->tree_cnt) ); @@ -1298,7 +1301,7 @@ DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes, ), TP_printk("dev = (%d,%d), %s, dirty count = %lld", - show_dev(__entry), + show_dev(__entry->dev), show_file_type(__entry->type), __entry->count) ); -- GitLab From e000d88356a8ef150c7ef551fda09be2a93df17b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 14 Feb 2017 09:54:37 -0800 Subject: [PATCH 0515/5498] f2fs: fix multiple f2fs_add_link() calls having same name It turns out a stakable filesystem like sdcardfs in AOSP can trigger multiple vfs_create() to lower filesystem. In that case, f2fs will add multiple dentries having same name which breaks filesystem consistency. Until upper layer fixes, let's work around by f2fs, which shows actually not much performance regression. Cc: Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 34 +++++++++++++++++++++++++++++----- fs/f2fs/f2fs.h | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 05843b0ace56..4b31663eb5c2 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -207,9 +207,13 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, f2fs_put_page(dentry_page, 0); } - if (!de && room && F2FS_I(dir)->chash != namehash) { - F2FS_I(dir)->chash = namehash; - F2FS_I(dir)->clevel = level; + /* This is to increase the speed of f2fs_create */ + if (!de && room) { + F2FS_I(dir)->task = current; + if (F2FS_I(dir)->chash != namehash) { + F2FS_I(dir)->chash = namehash; + F2FS_I(dir)->clevel = level; + } } return de; @@ -643,14 +647,34 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode, nid_t ino, umode_t mode) { struct fscrypt_name fname; + struct page *page = NULL; + struct f2fs_dir_entry *de = NULL; int err; err = fscrypt_setup_filename(dir, name, 0, &fname); if (err) return err; - err = __f2fs_do_add_link(dir, &fname, inode, ino, mode); - + /* + * An immature stakable filesystem shows a race condition between lookup + * and create. If we have same task when doing lookup and create, it's + * definitely fine as expected by VFS normally. Otherwise, let's just + * verify on-disk dentry one more time, which guarantees filesystem + * consistency more. + */ + if (current != F2FS_I(dir)->task) { + de = __f2fs_find_entry(dir, &fname, &page); + F2FS_I(dir)->task = NULL; + } + if (de) { + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + err = -EEXIST; + } else if (IS_ERR(page)) { + err = PTR_ERR(page); + } else { + err = __f2fs_do_add_link(dir, &fname, inode, ino, mode); + } fscrypt_free_filename(&fname); return err; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3ab2b0947516..a424016f7683 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -559,6 +559,7 @@ struct f2fs_inode_info { atomic_t dirty_pages; /* # of dirty pages */ f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ + struct task_struct *task; /* lookup and create consistency */ nid_t i_xattr_nid; /* node id that contains xattrs */ loff_t last_disk_size; /* lastly written file size */ -- GitLab From 48081a55a0085d536449483de7cf3604c7b75c4f Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Fri, 17 Feb 2017 17:16:38 +0800 Subject: [PATCH 0516/5498] f2fs: replace __get_victim by dirty_segments in FG_GC In FG_GC process, it will search victim section twice. This will cause some dirty section with less valid blocks skip garbage collection. section # 26425 : valid blocks # 3 142.037567: get_victim_by_default: victim 26425 : valid blocks # 3 142.037585: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26425 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 244 142.039494: f2fs_get_victim: dev = (259,30), type = Hot DATA, policy = (Background GC, SSR-mode, Greedy), victim = 19022 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 24 142.070247: new_curseg: Debug: alloc new segment 26746 142.244341: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26054 ofs_unit = 1, pre_victim_secno = 26054, prefree = 0, free = 243 142.254475: do_garbage_collect: Debug: FG_GC, seg_freed = 1 142.293131: f2fs_get_victim: dev = (259,30), type = Warm DATA, policy = (Background GC, SSR-mode, Greedy), victim = 23466 ofs_unit = 1, pre_victim_secno = -1, prefree = 0, free = 244 142.319001: f2fs_get_victim: dev = (259,30), type = Warm DATA, policy = (Background GC, SSR-mode, Greedy), victim = 23467 ofs_unit = 1, pre_victim_secno = -1, prefree = 0, free = 244 142.368879: get_victim_by_default: victim 26425 : valid blocks # 3 142.368894: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26425 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 244 142.378127: f2fs_get_victim: dev = (259,30), type = Hot DATA, policy = (Background GC, SSR-mode, Greedy), victim = 19612 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 24 142.416917: new_curseg: Debug: alloc new segment 26054 142.656794: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 25404 ofs_unit = 1, pre_victim_secno = 25404, prefree = 0, free = 243 142.662139: do_garbage_collect: Debug: FG_GC, seg_freed = 1 142.684159: new_curseg: Debug: alloc new segment 25197 142.685059: get_victim_by_default: victim 26425 : valid blocks # 3 142.685079: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26425 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 243 142.701427: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26238 ofs_unit = 1, pre_victim_secno = 26238, prefree = 0, free = 243 142.707105: do_garbage_collect: Debug: FG_GC, seg_freed = 1 142.802444: f2fs_get_victim: dev = (259,30), type = Warm DATA, policy = (Background GC, SSR-mode, Greedy), victim = 23473 ofs_unit = 1, pre_victim_secno = -1, prefree = 0, free = 244 142.804422: get_victim_by_default: victim 26425 : valid blocks # 3 142.804443: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26425 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 244 142.851567: f2fs_get_victim: dev = (259,30), type = Hot DATA, policy = (Background GC, SSR-mode, Greedy), victim = 19092 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 24 142.865014: new_curseg: Debug: alloc new segment 26238 143.082245: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26307 ofs_unit = 1, pre_victim_secno = 26307, prefree = 0, free = 244 143.088252: do_garbage_collect: Debug: FG_GC, seg_freed = 1 143.128307: new_curseg: Debug: alloc new segment 25404 143.181846: get_victim_by_default: victim 26425 : valid blocks # 3 143.181872: f2fs_get_victim: dev = (259,30), type = No TYPE, policy = (Foreground GC, LFS-mode, Greedy), victim = 26425 ofs_unit = 1, pre_victim_secno = 26425, prefree = 0, free = 244 Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 88e5e7b10ab6..5ee258ebf6ca 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -927,8 +927,6 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background) cpc.reason = __get_cp_reason(sbi); gc_more: - segno = NULL_SEGNO; - if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) goto stop; if (unlikely(f2fs_cp_error(sbi))) { @@ -943,12 +941,10 @@ gc_more: * enough free sections, we should flush dent/node blocks and do * garbage collections. */ - if (__get_victim(sbi, &segno, gc_type) || - prefree_segments(sbi)) { + if (dirty_segments(sbi) || prefree_segments(sbi)) { ret = write_checkpoint(sbi, &cpc); if (ret) goto stop; - segno = NULL_SEGNO; } else if (has_not_enough_free_secs(sbi, 0, 0)) { ret = write_checkpoint(sbi, &cpc); if (ret) @@ -959,7 +955,7 @@ gc_more: goto stop; } - if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type)) + if (!__get_victim(sbi, &segno, gc_type)) goto stop; ret = 0; -- GitLab From d35570b8746904301299b7e11c194d314ce8ccfd Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 17 Feb 2017 09:55:55 -0800 Subject: [PATCH 0517/5498] f2fs: do not wait for writeback in write_begin Otherwise we can get livelock like below. [79880.428136] dbench D 0 18405 18404 0x00000000 [79880.428139] Call Trace: [79880.428142] __schedule+0x219/0x6b0 [79880.428144] schedule+0x36/0x80 [79880.428147] schedule_timeout+0x243/0x2e0 [79880.428152] ? update_sd_lb_stats+0x16b/0x5f0 [79880.428155] ? ktime_get+0x3c/0xb0 [79880.428157] io_schedule_timeout+0xa6/0x110 [79880.428161] __lock_page+0xf7/0x130 [79880.428164] ? unlock_page+0x30/0x30 [79880.428167] pagecache_get_page+0x16b/0x250 [79880.428171] grab_cache_page_write_begin+0x20/0x40 [79880.428182] f2fs_write_begin+0xa2/0xdb0 [f2fs] [79880.428192] ? f2fs_mark_inode_dirty_sync+0x16/0x30 [f2fs] [79880.428197] ? kmem_cache_free+0x79/0x200 [79880.428203] ? __mark_inode_dirty+0x17f/0x360 [79880.428206] generic_perform_write+0xbb/0x190 [79880.428213] ? file_update_time+0xa4/0xf0 [79880.428217] __generic_file_write_iter+0x19b/0x1e0 [79880.428226] f2fs_file_write_iter+0x9c/0x180 [f2fs] [79880.428231] __vfs_write+0xc5/0x140 [79880.428235] vfs_write+0xb2/0x1b0 [79880.428238] SyS_write+0x46/0xa0 [79880.428242] entry_SYSCALL_64_fastpath+0x1e/0xad Fixes: cae96a5c8ab6 ("f2fs: check io submission more precisely") Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3207fd3e8a08..ea21c984a125 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1753,7 +1753,11 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, goto fail; } repeat: - page = grab_cache_page_write_begin(mapping, index, flags); + /* + * Do not use grab_cache_page_write_begin() to avoid deadlock due to + * wait_for_stable_page. Will wait that below with our IO control. + */ + page = grab_cache_page(mapping, index); if (!page) { err = -ENOMEM; goto fail; -- GitLab From fb7255e9e6958a289e386b9a4749ec1afbd60925 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Thu, 16 Feb 2017 12:34:31 +0000 Subject: [PATCH 0518/5498] f2fs: add ovp valid_blocks check for bg gc victim to fg_gc For foreground gc, greedy algorithm should be adapted, which makes this formula work well: (2 * (100 / config.overprovision + 1) + 6) But currently, we fg_gc have a prior to select bg_gc victim segments to gc first, these victims are selected by cost-benefit algorithm, we can't guarantee such segments have the small valid blocks, which may destroy the f2fs rule, on the worstest case, would consume all the free segments. This patch fix this by add a filter in check_bg_victims, if segment's has # of valid blocks over overprovision ratio, skip such segments. Cc: Signed-off-by: Hou Pengyang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +++ fs/f2fs/gc.c | 22 ++++++++++++++++++++-- fs/f2fs/segment.h | 9 +++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a424016f7683..b1f90d79a3df 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -990,6 +990,9 @@ struct f2fs_sb_info { struct f2fs_gc_kthread *gc_thread; /* GC thread */ unsigned int cur_victim_sec; /* current victim section num */ + /* threshold for converting bg victims for fg */ + u64 fggc_threshold; + /* maximum # of trials to find a victim segment for SSR and GC */ unsigned int max_victim_search; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 5ee258ebf6ca..e93aecb0138b 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -166,7 +166,8 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, p->ofs_unit = sbi->segs_per_sec; } - if (p->max_search > sbi->max_victim_search) + /* we need to check every dirty segments in the FG_GC case */ + if (gc_type != FG_GC && p->max_search > sbi->max_victim_search) p->max_search = sbi->max_victim_search; p->offset = sbi->last_victim[p->gc_mode]; @@ -199,6 +200,10 @@ static unsigned int check_bg_victims(struct f2fs_sb_info *sbi) for_each_set_bit(secno, dirty_i->victim_secmap, MAIN_SECS(sbi)) { if (sec_usage_check(sbi, secno)) continue; + + if (no_fggc_candidate(sbi, secno)) + continue; + clear_bit(secno, dirty_i->victim_secmap); return secno * sbi->segs_per_sec; } @@ -322,13 +327,15 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, nsearched++; } - secno = GET_SECNO(sbi, segno); if (sec_usage_check(sbi, secno)) goto next; if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap)) goto next; + if (gc_type == FG_GC && p.alloc_mode == LFS && + no_fggc_candidate(sbi, secno)) + goto next; cost = get_gc_cost(sbi, segno, &p); @@ -985,5 +992,16 @@ stop: void build_gc_manager(struct f2fs_sb_info *sbi) { + u64 main_count, resv_count, ovp_count, blocks_per_sec; + DIRTY_I(sbi)->v_ops = &default_v_ops; + + /* threshold of # of valid blocks in a section for victims of FG_GC */ + main_count = SM_I(sbi)->main_segments << sbi->log_blocks_per_seg; + resv_count = SM_I(sbi)->reserved_segments << sbi->log_blocks_per_seg; + ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg; + blocks_per_sec = sbi->blocks_per_seg * sbi->segs_per_sec; + + sbi->fggc_threshold = div_u64((main_count - ovp_count) * blocks_per_sec, + (main_count - resv_count)); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 4b0e1bd621a1..4bec6885eaeb 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -715,6 +715,15 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) - (base + 1) + type; } +static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi, + unsigned int secno) +{ + if (get_valid_blocks(sbi, secno, sbi->segs_per_sec) >= + sbi->fggc_threshold) + return true; + return false; +} + static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno) { if (IS_CURSEC(sbi, secno) || (sbi->cur_victim_sec == secno)) -- GitLab From c2b9ef3530d64411934bd082a5c76b58c7a17744 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Tue, 21 Feb 2017 16:59:26 +0800 Subject: [PATCH 0519/5498] f2fs: put allocate_segment after refresh_sit_entry SIT information should be updated before segment allocation, since SSR needs latest valid block information. Current code does not update the old_blkaddr info in sit_entry, so adjust the allocate_segment to its proper location. Commit 5e443818fa0b2a2845561ee25bec181424fb2889 ("f2fs: handle dirty segments inside refresh_sit_entry") puts it into wrong location. Signed-off-by: Yunlong Song Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 56f5c042b861..fd5cf39ab063 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1839,14 +1839,15 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, stat_inc_block_count(sbi, curseg); - if (!__has_curseg_space(sbi, type)) - sit_i->s_ops->allocate_segment(sbi, type, false); /* * SIT information should be updated before segment allocation, * since SSR needs latest valid block information. */ refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr); + if (!__has_curseg_space(sbi, type)) + sit_i->s_ops->allocate_segment(sbi, type, false); + mutex_unlock(&sit_i->sentry_lock); if (page && IS_NODESEG(type)) -- GitLab From cd66e74be81e4e7ccbfbffd5273aa07c00741e86 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Wed, 22 Feb 2017 10:28:59 +0000 Subject: [PATCH 0520/5498] f2fs: node segment is prior to data segment selected victim As data segment gc may lead dnode dirty, so the greedy cost for data segment should be valid blocks * 2, that is data segment is prior to node segment. Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e93aecb0138b..11416c7cb705 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -242,6 +242,16 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) return UINT_MAX - ((100 * (100 - u) * age) / (100 + u)); } +static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + unsigned int valid_blocks = + get_valid_blocks(sbi, segno, sbi->segs_per_sec); + + return IS_DATASEG(get_seg_entry(sbi, segno)->type) ? + valid_blocks * 2 : valid_blocks; +} + static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, unsigned int segno, struct victim_sel_policy *p) { @@ -250,7 +260,7 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, /* alloc_mode == LFS */ if (p->gc_mode == GC_GREEDY) - return get_valid_blocks(sbi, segno, sbi->segs_per_sec); + return get_greedy_cost(sbi, segno); else return get_cb_cost(sbi, segno); } -- GitLab From 6c556834470b6fcaa008ddea6639db08219d7d1d Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Wed, 22 Feb 2017 20:50:49 +0800 Subject: [PATCH 0521/5498] f2fs: do SSR for data when there is enough free space In allocate_segment_by_default(), need_SSR() already detected it's time to do SSR. So, let's try to find victims for data segments more aggressively in time. Signed-off-by: Yunlong Song Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fd5cf39ab063..8131bfa116c2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1620,7 +1620,7 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) struct curseg_info *curseg = CURSEG_I(sbi, type); const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; - if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0, 0)) + if (IS_NODESEG(type)) return v_ops->get_victim(sbi, &(curseg)->next_segno, BG_GC, type, SSR); -- GitLab From 8a7cb82b0874fe85243204d09bef80517e72cf55 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 22 Feb 2017 16:39:11 -0800 Subject: [PATCH 0522/5498] f2fs: do SSR in higher priority Let's check SSR in prior to LFS allocation. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 8131bfa116c2..300aafc5b8d1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1404,17 +1404,6 @@ static void write_current_sum_page(struct f2fs_sb_info *sbi, f2fs_put_page(page, 1); } -static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - unsigned int segno = curseg->segno + 1; - struct free_segmap_info *free_i = FREE_I(sbi); - - if (segno < MAIN_SEGS(sbi) && segno % sbi->segs_per_sec) - return !test_bit(segno, free_i->free_segmap); - return 0; -} - /* * Find a new segment from the free segments bitmap to right order * This function should be returned with success, otherwise BUG @@ -1639,21 +1628,17 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) static void allocate_segment_by_default(struct f2fs_sb_info *sbi, int type, bool force) { - struct curseg_info *curseg = CURSEG_I(sbi, type); - if (force) new_curseg(sbi, type, true); else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) && type == CURSEG_WARM_NODE) new_curseg(sbi, type, false); - else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type)) - new_curseg(sbi, type, false); else if (need_SSR(sbi) && get_ssr_segment(sbi, type)) change_curseg(sbi, type, true); else new_curseg(sbi, type, false); - stat_inc_seg_type(sbi, curseg); + stat_inc_seg_type(sbi, CURSEG_I(sbi, type)); } void allocate_new_segments(struct f2fs_sb_info *sbi) -- GitLab From 4112374fd5a337d637a3b3530cb27071f8aca3be Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 22 Feb 2017 17:10:18 -0800 Subject: [PATCH 0523/5498] f2fs: find data segments across all the types Previously, if type is CURSEG_HOT_DATA, we only check CURSEG_HOT_DATA only. This patch fixes to search all the different types for SSR. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 300aafc5b8d1..c36ca2c25b61 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1608,16 +1608,23 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; + int i; + + /* need_SSR() already forces to do this */ + if (v_ops->get_victim(sbi, &(curseg)->next_segno, BG_GC, type, SSR)) + return 1; if (IS_NODESEG(type)) - return v_ops->get_victim(sbi, - &(curseg)->next_segno, BG_GC, type, SSR); + return 0; /* For data segments, let's do SSR more intensively */ - for (; type >= CURSEG_HOT_DATA; type--) + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { + if (i == type) + continue; if (v_ops->get_victim(sbi, &(curseg)->next_segno, - BG_GC, type, SSR)) + BG_GC, i, SSR)) return 1; + } return 0; } -- GitLab From 39af5bbb059e777d68d88b3cde7128317d55da15 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 22 Feb 2017 19:10:35 -0800 Subject: [PATCH 0524/5498] f2fs: avoid very large discard command This patch adds MAX_DISCARD_BLOCKS() to avoid issuing too much large single discard command. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/segment.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b1f90d79a3df..cdf0eb63b642 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -232,7 +232,8 @@ enum { (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) #define BATCHED_TRIM_BLOCKS(sbi) \ (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) - +#define MAX_DISCARD_BLOCKS(sbi) \ + ((1 << (sbi)->log_blocks_per_seg) * (sbi)->segs_per_sec) #define DISCARD_ISSUE_RATE 8 #define DEF_CP_INTERVAL 60 /* 60 secs */ #define DEF_IDLE_INTERVAL 5 /* 5 secs */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c36ca2c25b61..6e7dd80ba5a6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -966,7 +966,8 @@ static void __add_discard_entry(struct f2fs_sb_info *sbi, if (!list_empty(head)) { last = list_last_entry(head, struct discard_entry, list); if (START_BLOCK(sbi, cpc->trim_start) + start == - last->blkaddr + last->len) { + last->blkaddr + last->len && + last->len < MAX_DISCARD_BLOCKS(sbi)) { last->len += end - start; goto done; } -- GitLab From c80e2e1d61b6de6e780a924d906caeb28600a7ae Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 22 Feb 2017 19:53:07 -0800 Subject: [PATCH 0525/5498] f2fs: much larger batched trim_fs job We have a kernel thread to issue discard commands, so we can increase the number of batched discard sections. By default, now it becomes 4GB range. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cdf0eb63b642..c5cb75bab893 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -227,7 +227,7 @@ enum { CP_DISCARD, }; -#define DEF_BATCHED_TRIM_SECTIONS 2 +#define DEF_BATCHED_TRIM_SECTIONS 2048 #define BATCHED_TRIM_SEGMENTS(sbi) \ (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) #define BATCHED_TRIM_BLOCKS(sbi) \ -- GitLab From e1f4667b41a4d3b1e0cb6f5b0debdc6908dfcc86 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 22 Feb 2017 19:58:23 -0800 Subject: [PATCH 0526/5498] f2fs: wait for discard completion after submission We don't need to wait for each discard commands when unmounting the image. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6e7dd80ba5a6..ece5b817f95c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -676,8 +676,12 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *wait_list = &(dcc->discard_cmd_list); struct discard_cmd *dc, *tmp; + struct blk_plug plug; mutex_lock(&dcc->cmd_lock); + + blk_start_plug(&plug); + list_for_each_entry_safe(dc, tmp, wait_list, list) { if (blkaddr == NULL_ADDR) { @@ -686,9 +690,6 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) submit_bio(REQ_SYNC, dc->bio); atomic_inc(&dcc->submit_discard); } - wait_for_completion_io(&dc->wait); - - __remove_discard_cmd(sbi, dc); continue; } @@ -699,6 +700,15 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) __remove_discard_cmd(sbi, dc); } } + blk_finish_plug(&plug); + + /* this comes from f2fs_put_super */ + if (blkaddr == NULL_ADDR) { + list_for_each_entry_safe(dc, tmp, wait_list, list) { + wait_for_completion_io(&dc->wait); + __remove_discard_cmd(sbi, dc); + } + } mutex_unlock(&dcc->cmd_lock); } -- GitLab From d375f8e85e21b20ff3fb5c98ba181c6cb8b30e33 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 22 Feb 2017 20:18:35 -0800 Subject: [PATCH 0527/5498] f2fs: check discard alignment only for SEQWRITE zones For converntional zones, we don't need to align discard commands to exact zone size. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/segment.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ece5b817f95c..05ea06ade076 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -873,24 +873,13 @@ static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) { - sector_t nr_sects = SECTOR_FROM_BLOCK(blklen); - sector_t sector; + sector_t sector, nr_sects; int devi = 0; if (sbi->s_ndevs) { devi = f2fs_target_device_index(sbi, blkstart); blkstart -= FDEV(devi).start_blk; } - sector = SECTOR_FROM_BLOCK(blkstart); - - if (sector & (bdev_zone_size(bdev) - 1) || - nr_sects != bdev_zone_size(bdev)) { - f2fs_msg(sbi->sb, KERN_INFO, - "(%d) %s: Unaligned discard attempted (block %x + %x)", - devi, sbi->s_ndevs ? FDEV(devi).path: "", - blkstart, blklen); - return -EIO; - } /* * We need to know the type of the zone: for conventional zones, @@ -905,6 +894,17 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); case BLK_ZONE_TYPE_SEQWRITE_REQ: case BLK_ZONE_TYPE_SEQWRITE_PREF: + sector = SECTOR_FROM_BLOCK(blkstart); + nr_sects = SECTOR_FROM_BLOCK(blklen); + + if (sector & (bdev_zone_size(bdev) - 1) || + nr_sects != bdev_zone_size(bdev)) { + f2fs_msg(sbi->sb, KERN_INFO, + "(%d) %s: Unaligned discard attempted (block %x + %x)", + devi, sbi->s_ndevs ? FDEV(devi).path: "", + blkstart, blklen); + return -EIO; + } trace_f2fs_issue_reset_zone(bdev, blkstart); return blkdev_reset_zones(bdev, sector, nr_sects, GFP_NOFS); -- GitLab From cd7944b51f3951881102a5ff2b74cfdd6367d03d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 22 Feb 2017 17:02:32 -0800 Subject: [PATCH 0528/5498] f2fs: do SSR for node segments more aggresively This patch gives more SSR chances for node blocks. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 05ea06ade076..40d7b3df7a6a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1619,17 +1619,22 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; - int i; + int i, n; /* need_SSR() already forces to do this */ if (v_ops->get_victim(sbi, &(curseg)->next_segno, BG_GC, type, SSR)) return 1; - if (IS_NODESEG(type)) - return 0; + /* For node segments, let's do SSR more intensively */ + if (IS_NODESEG(type)) { + i = CURSEG_HOT_NODE; + n = CURSEG_COLD_NODE; + } else { + i = CURSEG_HOT_DATA; + n = CURSEG_COLD_DATA; + } - /* For data segments, let's do SSR more intensively */ - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { + for (; i <= n; i++) { if (i == type) continue; if (v_ops->get_victim(sbi, &(curseg)->next_segno, -- GitLab From 7df2f83a32af4a96e26757cecc5743f43579a092 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Tue, 21 Feb 2017 20:43:48 +0800 Subject: [PATCH 0529/5498] f2fs: remove unnecessary condition check for write_checkpoint in f2fs_gc Since has_not_enough_free_secs(sbi, 0, 0) must be true if has_not_enough_ free_secs(sbi, sec_freed, 0) is true, write_checkpoint is sure to execute in both conditions. Signed-off-by: Yunlong Song Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 11416c7cb705..6c996e39b59a 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -958,15 +958,9 @@ gc_more: * enough free sections, we should flush dent/node blocks and do * garbage collections. */ - if (dirty_segments(sbi) || prefree_segments(sbi)) { - ret = write_checkpoint(sbi, &cpc); - if (ret) - goto stop; - } else if (has_not_enough_free_secs(sbi, 0, 0)) { - ret = write_checkpoint(sbi, &cpc); - if (ret) - goto stop; - } + ret = write_checkpoint(sbi, &cpc); + if (ret) + goto stop; } else if (gc_type == BG_GC && !background) { /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */ goto stop; -- GitLab From ec35f1ab15a24b3ea3872412ed91561a8829a5aa Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Thu, 23 Feb 2017 09:18:05 +0000 Subject: [PATCH 0530/5498] f2fs: init local extent_info to avoid stale stack info in tp To avoid such stale(fops, blk, len) info in f2fs_lookup_extent_tree_end tp dio-23095 [005] ...1 17878.856859: f2fs_lookup_extent_tree_end: dev = (259,30), ino = 856, pgofs = 0, ext_info(fofs: 3441207040, blk: 4294967232, len: 3481143808) Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 8 ++++---- fs/f2fs/file.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ea21c984a125..eb37ac5b7d18 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -512,7 +512,7 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index) int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index) { - struct extent_info ei; + struct extent_info ei = {0,0,0}; struct inode *inode = dn->inode; if (f2fs_lookup_extent_cache(inode, index, &ei)) { @@ -529,7 +529,7 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index, struct address_space *mapping = inode->i_mapping; struct dnode_of_data dn; struct page *page; - struct extent_info ei; + struct extent_info ei = {0,0,0}; int err; struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), @@ -801,7 +801,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int err = 0, ofs = 1; unsigned int ofs_in_node, last_ofs_in_node; blkcnt_t prealloc; - struct extent_info ei; + struct extent_info ei = {0,0,0}; block_t blkaddr; if (!maxblocks) @@ -1662,7 +1662,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, struct dnode_of_data dn; struct page *ipage; bool locked = false; - struct extent_info ei; + struct extent_info ei = {0,0,0}; int err = 0; /* diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index cc0066f0a64a..d1ff7175dde5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1913,7 +1913,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, { struct inode *inode = file_inode(filp); struct f2fs_map_blocks map = { .m_next_pgofs = NULL }; - struct extent_info ei; + struct extent_info ei = {0,0,0}; pgoff_t pg_start, pg_end; unsigned int blk_per_seg = sbi->blocks_per_seg; unsigned int total = 0, sec_num; -- GitLab From e31f566c679da16877f3251ba0e47d551878af87 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Thu, 23 Feb 2017 09:18:06 +0000 Subject: [PATCH 0531/5498] f2fs: remove unsafe bitmap checking proc A: proc B: - writeback_sb_inodes - __writeback_single_inode - do_writepages - f2fs_write_node_pages - f2fs_balance_fs_bg - write_checkpoint - build_free_nids - flush_nat_entries - __build_free_nids - __flush_nat_entry_set - ra_meta_pages - get_next_nat_page - current_nat_addr - set_to_next_nat [do nat_bitmap checking] - f2fs_change_bit For proc A, nat_bitmap and nat_bitmap_mir would be compared without lock_op and nm_i->nat_tree_lock, while proc B is changing nat_bitmap/nat_bitmap_ver in cp. So it is normal for nat_bitmap/nat_bitmap diffrence under such scenario. This patch fix this by removing the monitoring point. [Fix: 599a09b f2fs: check in-memory nat version bitmap] Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index d3d289306469..3fc9c4b1dce9 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -209,12 +209,6 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start) (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & (sbi->blocks_per_seg - 1))); -#ifdef CONFIG_F2FS_CHECK_FS - if (f2fs_test_bit(block_off, nm_i->nat_bitmap) != - f2fs_test_bit(block_off, nm_i->nat_bitmap_mir)) - f2fs_bug_on(sbi, 1); -#endif - if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; -- GitLab From 7e603f533f2085edef9f5fbb4af4f4091aa50e43 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Thu, 23 Feb 2017 19:55:05 +0800 Subject: [PATCH 0532/5498] f2fs: avoid m_flags overlay when allocating more data blocks When more than one data blocks are allocated, the F2FS_MAP_UNWRITTEN/MAPPED flags will be overlapped by F2FS_MAP_NEW at the later times. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index eb37ac5b7d18..4aebeb0c7402 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -865,7 +865,7 @@ next_block: } if (err) goto sync_out; - map->m_flags = F2FS_MAP_NEW; + map->m_flags |= F2FS_MAP_NEW; blkaddr = dn.data_blkaddr; } else { if (flag == F2FS_GET_BLOCK_BMAP) { -- GitLab From ad8df91d334c082122aa7619b4f8d27d3d471fd1 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 23 Feb 2017 19:39:59 +0800 Subject: [PATCH 0533/5498] f2fs: replace rw semaphore extent_tree_lock with mutex lock This patch replace rw semaphore extent_tree_lock with mutex lock for no read cases with this lock. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 22 +++++++++++----------- fs/f2fs/f2fs.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 6ed6424807b6..0ab5518e45c2 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -77,7 +77,7 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) struct extent_tree *et; nid_t ino = inode->i_ino; - down_write(&sbi->extent_tree_lock); + mutex_lock(&sbi->extent_tree_lock); et = radix_tree_lookup(&sbi->extent_tree_root, ino); if (!et) { et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS); @@ -94,7 +94,7 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) atomic_dec(&sbi->total_zombie_tree); list_del_init(&et->list); } - up_write(&sbi->extent_tree_lock); + mutex_unlock(&sbi->extent_tree_lock); /* never died until evict_inode */ F2FS_I(inode)->extent_tree = et; @@ -548,7 +548,7 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) if (!atomic_read(&sbi->total_zombie_tree)) goto free_node; - if (!down_write_trylock(&sbi->extent_tree_lock)) + if (!mutex_trylock(&sbi->extent_tree_lock)) goto out; /* 1. remove unreferenced extent tree */ @@ -570,11 +570,11 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) goto unlock_out; cond_resched(); } - up_write(&sbi->extent_tree_lock); + mutex_unlock(&sbi->extent_tree_lock); free_node: /* 2. remove LRU extent entries */ - if (!down_write_trylock(&sbi->extent_tree_lock)) + if (!mutex_trylock(&sbi->extent_tree_lock)) goto out; remained = nr_shrink - (node_cnt + tree_cnt); @@ -604,7 +604,7 @@ free_node: spin_unlock(&sbi->extent_lock); unlock_out: - up_write(&sbi->extent_tree_lock); + mutex_unlock(&sbi->extent_tree_lock); out: trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt); @@ -651,10 +651,10 @@ void f2fs_destroy_extent_tree(struct inode *inode) if (inode->i_nlink && !is_bad_inode(inode) && atomic_read(&et->node_cnt)) { - down_write(&sbi->extent_tree_lock); + mutex_lock(&sbi->extent_tree_lock); list_add_tail(&et->list, &sbi->zombie_list); atomic_inc(&sbi->total_zombie_tree); - up_write(&sbi->extent_tree_lock); + mutex_unlock(&sbi->extent_tree_lock); return; } @@ -662,12 +662,12 @@ void f2fs_destroy_extent_tree(struct inode *inode) node_cnt = f2fs_destroy_extent_node(inode); /* delete extent tree entry in radix tree */ - down_write(&sbi->extent_tree_lock); + mutex_lock(&sbi->extent_tree_lock); f2fs_bug_on(sbi, atomic_read(&et->node_cnt)); radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); kmem_cache_free(extent_tree_slab, et); atomic_dec(&sbi->total_ext_tree); - up_write(&sbi->extent_tree_lock); + mutex_unlock(&sbi->extent_tree_lock); F2FS_I(inode)->extent_tree = NULL; @@ -714,7 +714,7 @@ void f2fs_update_extent_cache_range(struct dnode_of_data *dn, void init_extent_cache_info(struct f2fs_sb_info *sbi) { INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO); - init_rwsem(&sbi->extent_tree_lock); + mutex_init(&sbi->extent_tree_lock); INIT_LIST_HEAD(&sbi->extent_list); spin_lock_init(&sbi->extent_lock); atomic_set(&sbi->total_ext_tree, 0); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c5cb75bab893..e8c68256e894 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -944,7 +944,7 @@ struct f2fs_sb_info { /* for extent tree cache */ struct radix_tree_root extent_tree_root;/* cache extent cache entries */ - struct rw_semaphore extent_tree_lock; /* locking extent radix tree */ + struct mutex extent_tree_lock; /* locking extent radix tree */ struct list_head extent_list; /* lru list for shrinker */ spinlock_t extent_lock; /* locking extent lru list */ atomic_t total_ext_tree; /* extent tree count */ -- GitLab From 5b96c7015be8baa0fbe9105156fb86680be15166 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 9 Feb 2017 10:38:09 -0800 Subject: [PATCH 0534/5498] f2fs: add bitmaps for empty or full NAT blocks This patches adds bitmaps to represent empty or full NAT blocks containing free nid entries. If we can find valid crc|cp_ver in the last block of checkpoint pack, we'll use these bitmaps when building free nids. In order to avoid checkpointing burden, up-to-date bitmaps will be flushed only during umount time. So, normally we can get this gain, but when power-cut happens, we rely on fsck.f2fs which recovers this bitmap again. After this patch, we build free nids from nid #0 at mount time to make more full NAT blocks, but in runtime, we check empty NAT blocks to load free nids without loading any NAT pages from disk. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 28 +++++- fs/f2fs/debug.c | 1 + fs/f2fs/f2fs.h | 31 ++++++- fs/f2fs/node.c | 188 ++++++++++++++++++++++++++++++++++++---- fs/f2fs/segment.c | 2 +- include/linux/f2fs_fs.h | 1 + 6 files changed, 231 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 08d7df834a60..71a14ee4d222 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1025,6 +1025,10 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) spin_lock(&sbi->cp_lock); + if (cpc->reason == CP_UMOUNT && ckpt->cp_pack_total_block_count > + sbi->blocks_per_seg - NM_I(sbi)->nat_bits_blocks) + disable_nat_bits(sbi, false); + if (cpc->reason == CP_UMOUNT) __set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); else @@ -1137,6 +1141,28 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) start_blk = __start_cp_next_addr(sbi); + /* write nat bits */ + if (enabled_nat_bits(sbi, cpc)) { + __u64 cp_ver = cur_cp_version(ckpt); + unsigned int i; + block_t blk; + + cp_ver |= ((__u64)crc32 << 32); + *(__le64 *)nm_i->nat_bits = cpu_to_le64(cp_ver); + + blk = start_blk + sbi->blocks_per_seg - nm_i->nat_bits_blocks; + for (i = 0; i < nm_i->nat_bits_blocks; i++) + update_meta_page(sbi, nm_i->nat_bits + + (i << F2FS_BLKSIZE_BITS), blk + i); + + /* Flush all the NAT BITS pages */ + while (get_pages(sbi, F2FS_DIRTY_META)) { + sync_meta_pages(sbi, META, LONG_MAX); + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + } + } + /* need to wait for end_io results */ wait_on_all_pages_writeback(sbi); if (unlikely(f2fs_cp_error(sbi))) @@ -1273,7 +1299,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) ckpt->checkpoint_ver = cpu_to_le64(++ckpt_ver); /* write cached NAT/SIT entries to NAT/SIT area */ - flush_nat_entries(sbi); + flush_nat_entries(sbi, cpc); flush_sit_entries(sbi, cpc); /* unlock all the fs_lock[] in do_checkpoint() */ diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index ed8992d483a3..0f8d582e32d9 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -193,6 +193,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi) /* build nm */ si->base_mem += sizeof(struct f2fs_nm_info); si->base_mem += __bitmap_size(sbi, NAT_BITMAP); + si->base_mem += (NM_I(sbi)->nat_bits_blocks << F2FS_BLKSIZE_BITS); get_cache: si->cache_mem = 0; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e8c68256e894..e35ec1a257cf 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -654,6 +654,7 @@ struct f2fs_nm_info { struct list_head nat_entries; /* cached nat entry list (clean) */ unsigned int nat_cnt; /* the # of cached nat entries */ unsigned int dirty_nat_cnt; /* total num of nat entries in set */ + unsigned int nat_blocks; /* # of nat blocks */ /* free node ids management */ struct radix_tree_root free_nid_root;/* root of the free_nid cache */ @@ -664,6 +665,11 @@ struct f2fs_nm_info { /* for checkpoint */ char *nat_bitmap; /* NAT bitmap pointer */ + + unsigned int nat_bits_blocks; /* # of nat bits blocks */ + unsigned char *nat_bits; /* NAT bits blocks */ + unsigned char *full_nat_bits; /* full NAT pages */ + unsigned char *empty_nat_bits; /* empty NAT pages */ #ifdef CONFIG_F2FS_CHECK_FS char *nat_bitmap_mir; /* NAT bitmap mirror */ #endif @@ -1271,6 +1277,27 @@ static inline void clear_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) spin_unlock(&sbi->cp_lock); } +static inline void disable_nat_bits(struct f2fs_sb_info *sbi, bool lock) +{ + set_sbi_flag(sbi, SBI_NEED_FSCK); + + if (lock) + spin_lock(&sbi->cp_lock); + __clear_ckpt_flags(F2FS_CKPT(sbi), CP_NAT_BITS_FLAG); + kfree(NM_I(sbi)->nat_bits); + NM_I(sbi)->nat_bits = NULL; + if (lock) + spin_unlock(&sbi->cp_lock); +} + +static inline bool enabled_nat_bits(struct f2fs_sb_info *sbi, + struct cp_control *cpc) +{ + bool set = is_set_ckpt_flags(sbi, CP_NAT_BITS_FLAG); + + return (cpc) ? (cpc->reason == CP_UMOUNT) && set : set; +} + static inline void f2fs_lock_op(struct f2fs_sb_info *sbi) { down_read(&sbi->cp_rwsem); @@ -2232,7 +2259,7 @@ void move_node_page(struct page *node_page, int gc_type); int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic); int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc); -void build_free_nids(struct f2fs_sb_info *sbi, bool sync); +void build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount); bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid); void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid); @@ -2243,7 +2270,7 @@ int recover_xattr_data(struct inode *inode, struct page *page, int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page); int restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum); -void flush_nat_entries(struct f2fs_sb_info *sbi); +void flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc); int build_node_manager(struct f2fs_sb_info *sbi); void destroy_node_manager(struct f2fs_sb_info *sbi); int __init create_node_manager_caches(void); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d2c9ff470699..d93ebc066365 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -338,6 +338,9 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, set_nat_flag(e, IS_CHECKPOINTED, false); __set_nat_cache_dirty(nm_i, e); + if (enabled_nat_bits(sbi, NULL) && new_blkaddr == NEW_ADDR) + __clear_bit_le(NAT_BLOCK_OFFSET(ni->nid), nm_i->empty_nat_bits); + /* update fsync_mark if its inode nat entry is still alive */ if (ni->nid != ni->ino) e = __lookup_nat_cache(nm_i, ni->ino); @@ -1850,7 +1853,60 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, } } -static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync) +static int scan_nat_bits(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct page *page; + unsigned int i = 0; + nid_t target = FREE_NID_PAGES * NAT_ENTRY_PER_BLOCK; + nid_t nid; + + if (!enabled_nat_bits(sbi, NULL)) + return -EAGAIN; + + down_read(&nm_i->nat_tree_lock); +check_empty: + i = find_next_bit_le(nm_i->empty_nat_bits, nm_i->nat_blocks, i); + if (i >= nm_i->nat_blocks) { + i = 0; + goto check_partial; + } + + for (nid = i * NAT_ENTRY_PER_BLOCK; nid < (i + 1) * NAT_ENTRY_PER_BLOCK; + nid++) { + if (unlikely(nid >= nm_i->max_nid)) + break; + add_free_nid(sbi, nid, true); + } + + if (nm_i->nid_cnt[FREE_NID_LIST] >= target) + goto out; + i++; + goto check_empty; + +check_partial: + i = find_next_zero_bit_le(nm_i->full_nat_bits, nm_i->nat_blocks, i); + if (i >= nm_i->nat_blocks) { + disable_nat_bits(sbi, true); + up_read(&nm_i->nat_tree_lock); + return -EINVAL; + } + + nid = i * NAT_ENTRY_PER_BLOCK; + page = get_current_nat_page(sbi, nid); + scan_nat_page(sbi, page, nid); + f2fs_put_page(page, 1); + + if (nm_i->nid_cnt[FREE_NID_LIST] < target) { + i++; + goto check_partial; + } +out: + up_read(&nm_i->nat_tree_lock); + return 0; +} + +static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); @@ -1865,6 +1921,21 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync) if (!sync && !available_free_memory(sbi, FREE_NIDS)) return; + /* try to find free nids with nat_bits */ + if (!mount && !scan_nat_bits(sbi) && nm_i->nid_cnt[FREE_NID_LIST]) + return; + + /* find next valid candidate */ + if (enabled_nat_bits(sbi, NULL)) { + int idx = find_next_zero_bit_le(nm_i->full_nat_bits, + nm_i->nat_blocks, 0); + + if (idx >= nm_i->nat_blocks) + set_sbi_flag(sbi, SBI_NEED_FSCK); + else + nid = idx * NAT_ENTRY_PER_BLOCK; + } + /* readahead nat pages to be scanned */ ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, META_NAT, true); @@ -1907,10 +1978,10 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync) nm_i->ra_nid_pages, META_NAT, false); } -void build_free_nids(struct f2fs_sb_info *sbi, bool sync) +void build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) { mutex_lock(&NM_I(sbi)->build_lock); - __build_free_nids(sbi, sync); + __build_free_nids(sbi, sync, mount); mutex_unlock(&NM_I(sbi)->build_lock); } @@ -1952,7 +2023,7 @@ retry: spin_unlock(&nm_i->nid_list_lock); /* Let's scan nat pages and its caches to get free nids */ - build_free_nids(sbi, true); + build_free_nids(sbi, true, false); goto retry; } @@ -2244,8 +2315,39 @@ add_out: list_add_tail(&nes->set_list, head); } +void __update_nat_bits(struct f2fs_sb_info *sbi, nid_t start_nid, + struct page *page) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned int nat_index = start_nid / NAT_ENTRY_PER_BLOCK; + struct f2fs_nat_block *nat_blk = page_address(page); + int valid = 0; + int i; + + if (!enabled_nat_bits(sbi, NULL)) + return; + + for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) { + if (start_nid == 0 && i == 0) + valid++; + if (nat_blk->entries[i].block_addr) + valid++; + } + if (valid == 0) { + __set_bit_le(nat_index, nm_i->empty_nat_bits); + __clear_bit_le(nat_index, nm_i->full_nat_bits); + return; + } + + __clear_bit_le(nat_index, nm_i->empty_nat_bits); + if (valid == NAT_ENTRY_PER_BLOCK) + __set_bit_le(nat_index, nm_i->full_nat_bits); + else + __clear_bit_le(nat_index, nm_i->full_nat_bits); +} + static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, - struct nat_entry_set *set) + struct nat_entry_set *set, struct cp_control *cpc) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = curseg->journal; @@ -2260,7 +2362,8 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, * #1, flush nat entries to journal in current hot data summary block. * #2, flush nat entries to nat page. */ - if (!__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL)) + if (enabled_nat_bits(sbi, cpc) || + !__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL)) to_journal = false; if (to_journal) { @@ -2300,10 +2403,12 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, } } - if (to_journal) + if (to_journal) { up_write(&curseg->journal_rwsem); - else + } else { + __update_nat_bits(sbi, start_nid, page); f2fs_put_page(page, 1); + } f2fs_bug_on(sbi, set->entry_cnt); @@ -2314,7 +2419,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, /* * This function is called during the checkpointing process. */ -void flush_nat_entries(struct f2fs_sb_info *sbi) +void flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); @@ -2335,7 +2440,8 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) * entries, remove all entries from journal and merge them * into nat entry set. */ - if (!__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL)) + if (cpc->reason == CP_UMOUNT || + !__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL)) remove_nats_in_journal(sbi); while ((found = __gang_lookup_nat_set(nm_i, @@ -2349,27 +2455,72 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) /* flush dirty nats in nat entry set */ list_for_each_entry_safe(set, tmp, &sets, set_list) - __flush_nat_entry_set(sbi, set); + __flush_nat_entry_set(sbi, set, cpc); up_write(&nm_i->nat_tree_lock); f2fs_bug_on(sbi, nm_i->dirty_nat_cnt); } +static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned int nat_bits_bytes = nm_i->nat_blocks / BITS_PER_BYTE; + unsigned int i; + __u64 cp_ver = cur_cp_version(ckpt); + size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); + __u64 crc = le32_to_cpu(*((__le32 *) + ((unsigned char *)ckpt + crc_offset))); + block_t nat_bits_addr; + + if (!enabled_nat_bits(sbi, NULL)) + return 0; + + nm_i->nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + + F2FS_BLKSIZE - 1); + nm_i->nat_bits = kzalloc(nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, + GFP_KERNEL); + if (!nm_i->nat_bits) + return -ENOMEM; + + nat_bits_addr = __start_cp_addr(sbi) + sbi->blocks_per_seg - + nm_i->nat_bits_blocks; + for (i = 0; i < nm_i->nat_bits_blocks; i++) { + struct page *page = get_meta_page(sbi, nat_bits_addr++); + + memcpy(nm_i->nat_bits + (i << F2FS_BLKSIZE_BITS), + page_address(page), F2FS_BLKSIZE); + f2fs_put_page(page, 1); + } + + cp_ver |= (crc << 32); + if (cpu_to_le64(cp_ver) != *(__le64 *)nm_i->nat_bits) { + disable_nat_bits(sbi, true); + return 0; + } + + nm_i->full_nat_bits = nm_i->nat_bits + 8; + nm_i->empty_nat_bits = nm_i->full_nat_bits + nat_bits_bytes; + + f2fs_msg(sbi->sb, KERN_NOTICE, "Found nat_bits in checkpoint"); + return 0; +} + static int init_node_manager(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb_raw = F2FS_RAW_SUPER(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned char *version_bitmap; - unsigned int nat_segs, nat_blocks; + unsigned int nat_segs; + int err; nm_i->nat_blkaddr = le32_to_cpu(sb_raw->nat_blkaddr); /* segment_count_nat includes pair segment so divide to 2. */ nat_segs = le32_to_cpu(sb_raw->segment_count_nat) >> 1; - nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg); - - nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; + nm_i->nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg); + nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nm_i->nat_blocks; /* not used nids: 0, node, meta, (and root counted as valid node) */ nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count - @@ -2403,6 +2554,10 @@ static int init_node_manager(struct f2fs_sb_info *sbi) if (!nm_i->nat_bitmap) return -ENOMEM; + err = __get_nat_bitmaps(sbi); + if (err) + return err; + #ifdef CONFIG_F2FS_CHECK_FS nm_i->nat_bitmap_mir = kmemdup(version_bitmap, nm_i->bitmap_size, GFP_KERNEL); @@ -2425,7 +2580,7 @@ int build_node_manager(struct f2fs_sb_info *sbi) if (err) return err; - build_free_nids(sbi, true); + build_free_nids(sbi, true, true); return 0; } @@ -2484,6 +2639,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) up_write(&nm_i->nat_tree_lock); kfree(nm_i->nat_bitmap); + kfree(nm_i->nat_bits); #ifdef CONFIG_F2FS_CHECK_FS kfree(nm_i->nat_bitmap_mir); #endif diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 40d7b3df7a6a..80c70942a27f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -386,7 +386,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) if (!available_free_memory(sbi, FREE_NIDS)) try_to_free_nids(sbi, MAX_FREE_NIDS); else - build_free_nids(sbi, false); + build_free_nids(sbi, false, false); if (!is_idle(sbi)) return; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 8d156ac1f38b..ae5c0c2c8692 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -114,6 +114,7 @@ struct f2fs_super_block { /* * For checkpoint */ +#define CP_NAT_BITS_FLAG 0x00000080 #define CP_CRC_RECOVERY_FLAG 0x00000040 #define CP_FASTBOOT_FLAG 0x00000020 #define CP_FSCK_FLAG 0x00000010 -- GitLab From 2b59d56ba80ca93923e82a3f1aa004ea3d44120a Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 23 Feb 2017 20:31:20 +0800 Subject: [PATCH 0535/5498] f2fs: no need lock_op in f2fs_write_inline_data Similar as f2fs_write_inode, f2fs_write_inline_data just mark inode page dirty, so it's no need to write inline data under read lock of cp_rwsem. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4aebeb0c7402..0f40ec82b1f5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1409,9 +1409,12 @@ write: goto redirty_out; err = -EAGAIN; - f2fs_lock_op(sbi); - if (f2fs_has_inline_data(inode)) + if (f2fs_has_inline_data(inode)) { err = f2fs_write_inline_data(inode, page); + if (!err) + goto out; + } + f2fs_lock_op(sbi); if (err == -EAGAIN) err = do_write_data_page(&fio); if (F2FS_I(inode)->last_disk_size < psize) -- GitLab From 6fc7252e196bbd4617e2401fe840c41bc79e3ea3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 23 Feb 2017 17:43:49 -0800 Subject: [PATCH 0536/5498] f2fs: use __clear_bit_le Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 4b31663eb5c2..f7e04bd5a592 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -745,7 +745,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, dentry_blk = page_address(page); bit_pos = dentry - dentry_blk->dentry; for (i = 0; i < slots; i++) - clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); + __clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); /* Let's check and deallocate this dentry page */ bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, -- GitLab From 19ba9bae14ee048028b6ba19f685c54f0b423925 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 24 Feb 2017 15:09:16 -0800 Subject: [PATCH 0537/5498] fscrypt: catch fscrypto_get_policy in v4.10-rc6 Signed-off-by: Jaegeuk Kim --- fs/crypto/policy.c | 59 ++++++++++++++++++++++++---------------- fs/f2fs/f2fs.h | 4 +-- fs/f2fs/file.c | 26 ++---------------- include/linux/fscrypto.h | 12 ++++---- 4 files changed, 46 insertions(+), 55 deletions(-) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 77338d91b3a3..c8e239f01540 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -13,6 +13,16 @@ #include #include +static inline void inode_lock(struct inode *inode) +{ + mutex_lock(&inode->i_mutex); +} + +static inline void inode_unlock(struct inode *inode) +{ + mutex_unlock(&inode->i_mutex); +} + static int inode_has_encryption_context(struct inode *inode) { if (!inode->i_sb->s_cop->get_context) @@ -93,26 +103,19 @@ static int create_encryption_context_from_policy(struct inode *inode, return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL); } -static inline void inode_lock(struct inode *inode) -{ - mutex_lock(&inode->i_mutex); -} - -static inline void inode_unlock(struct inode *inode) -{ - mutex_unlock(&inode->i_mutex); -} - -int fscrypt_process_policy(struct file *filp, - const struct fscrypt_policy *policy) +int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) { + struct fscrypt_policy policy; struct inode *inode = file_inode(filp); int ret; + if (copy_from_user(&policy, arg, sizeof(policy))) + return -EFAULT; + if (!inode_owner_or_capable(inode)) return -EACCES; - if (policy->version != 0) + if (policy.version != 0) return -EINVAL; ret = mnt_want_write_file(filp); @@ -130,9 +133,9 @@ int fscrypt_process_policy(struct file *filp, ret = -ENOTEMPTY; else ret = create_encryption_context_from_policy(inode, - policy); + &policy); } else if (!is_encryption_context_consistent_with_policy(inode, - policy)) { + &policy)) { printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n", __func__); @@ -144,11 +147,13 @@ int fscrypt_process_policy(struct file *filp, mnt_drop_write_file(filp); return ret; } -EXPORT_SYMBOL(fscrypt_process_policy); +EXPORT_SYMBOL(fscrypt_ioctl_set_policy); -int fscrypt_get_policy(struct inode *inode, struct fscrypt_policy *policy) +int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) { + struct inode *inode = file_inode(filp); struct fscrypt_context ctx; + struct fscrypt_policy policy; int res; if (!inode->i_sb->s_cop->get_context || @@ -161,15 +166,18 @@ int fscrypt_get_policy(struct inode *inode, struct fscrypt_policy *policy) if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) return -EINVAL; - policy->version = 0; - policy->contents_encryption_mode = ctx.contents_encryption_mode; - policy->filenames_encryption_mode = ctx.filenames_encryption_mode; - policy->flags = ctx.flags; - memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor, + policy.version = 0; + policy.contents_encryption_mode = ctx.contents_encryption_mode; + policy.filenames_encryption_mode = ctx.filenames_encryption_mode; + policy.flags = ctx.flags; + memcpy(policy.master_key_descriptor, ctx.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE); + + if (copy_to_user(arg, &policy, sizeof(policy))) + return -EFAULT; return 0; } -EXPORT_SYMBOL(fscrypt_get_policy); +EXPORT_SYMBOL(fscrypt_ioctl_get_policy); int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) { @@ -181,6 +189,11 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) BUG_ON(1); } + /* No restrictions on file types which are never encrypted */ + if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && + !S_ISLNK(child->i_mode)) + return 1; + /* no restrictions if the parent directory is not encrypted */ if (!parent->i_sb->s_cop->is_encrypted(parent)) return 1; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e35ec1a257cf..b64e1a6039b1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2743,8 +2743,8 @@ static inline bool f2fs_may_encrypt(struct inode *inode) #define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page #define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page #define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range -#define fscrypt_process_policy fscrypt_notsupp_process_policy -#define fscrypt_get_policy fscrypt_notsupp_get_policy +#define fscrypt_ioctl_set_policy fscrypt_notsupp_ioctl_set_policy +#define fscrypt_ioctl_get_policy fscrypt_notsupp_ioctl_get_policy #define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context #define fscrypt_inherit_context fscrypt_notsupp_inherit_context #define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d1ff7175dde5..c8cac30498f8 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1782,38 +1782,16 @@ static bool uuid_is_nonzero(__u8 u[16]) static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) { - struct fscrypt_policy policy; struct inode *inode = file_inode(filp); - int ret; - - if (copy_from_user(&policy, (struct fscrypt_policy __user *)arg, - sizeof(policy))) - return -EFAULT; - - ret = mnt_want_write_file(filp); - if (ret) - return ret; f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); - ret = fscrypt_process_policy(filp, &policy); - mnt_drop_write_file(filp); - return ret; + return fscrypt_ioctl_set_policy(filp, (const void __user *)arg); } static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) { - struct fscrypt_policy policy; - struct inode *inode = file_inode(filp); - int err; - - err = fscrypt_get_policy(inode, &policy); - if (err) - return err; - - if (copy_to_user((struct fscrypt_policy __user *)arg, &policy, sizeof(policy))) - return -EFAULT; - return 0; + return fscrypt_ioctl_get_policy(filp, (void __user *)arg); } static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h index 181937edadad..d6c716566f1a 100644 --- a/include/linux/fscrypto.h +++ b/include/linux/fscrypto.h @@ -249,8 +249,8 @@ extern void fscrypt_restore_control_page(struct page *); extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t, unsigned int); /* policy.c */ -extern int fscrypt_process_policy(struct file *, const struct fscrypt_policy *); -extern int fscrypt_get_policy(struct inode *, struct fscrypt_policy *); +extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); +extern int fscrypt_ioctl_get_policy(struct file *, void __user *); extern int fscrypt_has_permitted_context(struct inode *, struct inode *); extern int fscrypt_inherit_context(struct inode *, struct inode *, void *, bool); @@ -319,14 +319,14 @@ static inline int fscrypt_notsupp_zeroout_range(struct inode *i, pgoff_t p, } /* policy.c */ -static inline int fscrypt_notsupp_process_policy(struct file *f, - const struct fscrypt_policy *p) +static inline int fscrypt_notsupp_ioctl_set_policy(struct file *f, + const void __user *arg) { return -EOPNOTSUPP; } -static inline int fscrypt_notsupp_get_policy(struct inode *i, - struct fscrypt_policy *p) +static inline int fscrypt_notsupp_ioctl_get_policy(struct file *f, + void __user *arg) { return -EOPNOTSUPP; } -- GitLab From e4b46860bd51a6b5c6152171a09dd9a9365fb20b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 25 Feb 2017 11:08:28 +0800 Subject: [PATCH 0538/5498] f2fs: show simple call stack in fault injection message Previously kernel message can show that in which function we do the injection, but unfortunately, most of the caller are the same, for tracking more information of injection path, it needs to show upper caller's name. This patch supports that ability. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/data.c --- fs/f2fs/checkpoint.c | 1 + fs/f2fs/data.c | 4 +++- fs/f2fs/dir.c | 4 +++- fs/f2fs/f2fs.h | 20 +++++++++++++------- fs/f2fs/gc.c | 4 +++- fs/f2fs/inode.c | 4 +++- fs/f2fs/node.c | 4 +++- fs/f2fs/segment.c | 4 +++- 8 files changed, 32 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 71a14ee4d222..8c114b1f8e77 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -495,6 +495,7 @@ int acquire_orphan_inode(struct f2fs_sb_info *sbi) #ifdef CONFIG_F2FS_FAULT_INJECTION if (time_to_inject(sbi, FAULT_ORPHAN)) { spin_unlock(&im->ino_lock); + f2fs_show_injection_info(FAULT_ORPHAN); return -ENOSPC; } #endif diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0f40ec82b1f5..4241f114f756 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -56,8 +56,10 @@ static void f2fs_read_end_io(struct bio *bio, int err) int i; #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(F2FS_P_SB(bio->bi_io_vec->bv_page), FAULT_IO)) + if (time_to_inject(F2FS_P_SB(bio->bi_io_vec->bv_page), FAULT_IO)) { + f2fs_show_injection_info(FAULT_IO); err = -EIO; + } #endif if (f2fs_bio_encrypted(bio)) { diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index f7e04bd5a592..212e1c6a4ee6 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -549,8 +549,10 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, start: #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(F2FS_I_SB(dir), FAULT_DIR_DEPTH)) + if (time_to_inject(F2FS_I_SB(dir), FAULT_DIR_DEPTH)) { + f2fs_show_injection_info(FAULT_DIR_DEPTH); return -ENOSPC; + } #endif if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) return -ENOSPC; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b64e1a6039b1..96c0ea1fb216 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1052,6 +1052,10 @@ struct f2fs_sb_info { }; #ifdef CONFIG_F2FS_FAULT_INJECTION +#define f2fs_show_injection_info(type) \ + printk("%sF2FS-fs : inject %s in %s of %pF\n", \ + KERN_INFO, fault_name[type], \ + __func__, __builtin_return_address(0)) static inline bool time_to_inject(struct f2fs_sb_info *sbi, int type) { struct f2fs_fault_info *ffi = &sbi->fault_info; @@ -1065,10 +1069,6 @@ static inline bool time_to_inject(struct f2fs_sb_info *sbi, int type) atomic_inc(&ffi->inject_ops); if (atomic_read(&ffi->inject_ops) >= ffi->inject_rate) { atomic_set(&ffi->inject_ops, 0); - printk("%sF2FS-fs : inject %s in %pF\n", - KERN_INFO, - fault_name[type], - __builtin_return_address(0)); return true; } return false; @@ -1377,8 +1377,10 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, blkcnt_t diff; #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(sbi, FAULT_BLOCK)) + if (time_to_inject(sbi, FAULT_BLOCK)) { + f2fs_show_injection_info(FAULT_BLOCK); return false; + } #endif /* * let's increase this in prior to actual block count change in order @@ -1618,8 +1620,10 @@ static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, if (page) return page; - if (time_to_inject(F2FS_M_SB(mapping), FAULT_PAGE_ALLOC)) + if (time_to_inject(F2FS_M_SB(mapping), FAULT_PAGE_ALLOC)) { + f2fs_show_injection_info(FAULT_PAGE_ALLOC); return NULL; + } #endif if (!for_write) return grab_cache_page(mapping, index); @@ -2096,8 +2100,10 @@ static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi, size_t size, gfp_t flags) { #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(sbi, FAULT_KMALLOC)) + if (time_to_inject(sbi, FAULT_KMALLOC)) { + f2fs_show_injection_info(FAULT_KMALLOC); return NULL; + } #endif return kmalloc(size, flags); } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 6c996e39b59a..8be5144da8e6 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -48,8 +48,10 @@ static int gc_thread_func(void *data) } #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(sbi, FAULT_CHECKPOINT)) + if (time_to_inject(sbi, FAULT_CHECKPOINT)) { + f2fs_show_injection_info(FAULT_CHECKPOINT); f2fs_stop_checkpoint(sbi, false); + } #endif /* diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index e6b3045b9c83..788718fe9230 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -373,8 +373,10 @@ void f2fs_evict_inode(struct inode *inode) goto no_delete; #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(sbi, FAULT_EVICT_INODE)) + if (time_to_inject(sbi, FAULT_EVICT_INODE)) { + f2fs_show_injection_info(FAULT_EVICT_INODE); goto no_delete; + } #endif remove_ino_entry(sbi, inode->i_ino, APPEND_INO); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d93ebc066365..f8333d4e7d14 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1996,8 +1996,10 @@ bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid) struct free_nid *i = NULL; retry: #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(sbi, FAULT_ALLOC_NID)) + if (time_to_inject(sbi, FAULT_ALLOC_NID)) { + f2fs_show_injection_info(FAULT_ALLOC_NID); return false; + } #endif spin_lock(&nm_i->nid_list_lock); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 80c70942a27f..274ea23939a6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -352,8 +352,10 @@ int commit_inmem_pages(struct inode *inode) void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) { #ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(sbi, FAULT_CHECKPOINT)) + if (time_to_inject(sbi, FAULT_CHECKPOINT)) { + f2fs_show_injection_info(FAULT_CHECKPOINT); f2fs_stop_checkpoint(sbi, false); + } #endif if (!need) -- GitLab From c2dfa2e4f53ef9a7c62ba33932d21a2520576277 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 24 Feb 2017 18:46:00 +0800 Subject: [PATCH 0539/5498] f2fs: select target segment with closer temperature in SSR mode In SSR mode, we can allocate target segment which has different temperature type from the type of current block, in order to avoid mixing coldest and hottest data/node as much as possible, change SSR allocation policy to select closer temperature for current block prior. Signed-off-by: Yunlong Song Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 274ea23939a6..574dae01e2f6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1621,7 +1621,8 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; - int i, n; + int i, cnt; + bool reversed = false; /* need_SSR() already forces to do this */ if (v_ops->get_victim(sbi, &(curseg)->next_segno, BG_GC, type, SSR)) @@ -1629,14 +1630,24 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) /* For node segments, let's do SSR more intensively */ if (IS_NODESEG(type)) { - i = CURSEG_HOT_NODE; - n = CURSEG_COLD_NODE; + if (type >= CURSEG_WARM_NODE) { + reversed = true; + i = CURSEG_COLD_NODE; + } else { + i = CURSEG_HOT_NODE; + } + cnt = NR_CURSEG_NODE_TYPE; } else { - i = CURSEG_HOT_DATA; - n = CURSEG_COLD_DATA; + if (type >= CURSEG_WARM_DATA) { + reversed = true; + i = CURSEG_COLD_DATA; + } else { + i = CURSEG_HOT_DATA; + } + cnt = NR_CURSEG_DATA_TYPE; } - for (; i <= n; i++) { + for (; cnt-- > 0; reversed ? i-- : i++) { if (i == type) continue; if (v_ops->get_victim(sbi, &(curseg)->next_segno, -- GitLab From 080d39f99b59829ef6670a8b2cace8373f5e2f91 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Sat, 25 Feb 2017 03:57:38 +0000 Subject: [PATCH 0540/5498] f2fs: avoid bggc->fggc when enough free segments are avaliable after cp We use has_not_enough_free_secs to check if there are enough free segments, (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + imeta_secs + reserved_sections(sbi) + needed); Under scenario with large number of dirty nodes, these nodes would be flushed during cp, as a result, right side of the inequality would be decreased, while left side stays unchanged if these nodes are flushed in SSR way, which means there are enough free segments after this cp. For this case, we just do a bggc instead of fggc. Signed-off-by: Hou Pengyang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8be5144da8e6..023f043edefd 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -953,21 +953,22 @@ gc_more: goto stop; } - if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed, 0)) { - gc_type = FG_GC; + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) { /* - * If there is no victim and no prefree segment but still not - * enough free sections, we should flush dent/node blocks and do - * garbage collections. + * For example, if there are many prefree_segments below given + * threshold, we can make them free by checkpoint. Then, we + * secure free segments which doesn't need fggc any more. */ ret = write_checkpoint(sbi, &cpc); if (ret) goto stop; - } else if (gc_type == BG_GC && !background) { - /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */ - goto stop; + if (has_not_enough_free_secs(sbi, 0, 0)) + gc_type = FG_GC; } + /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */ + if (gc_type == BG_GC && !background) + goto stop; if (!__get_victim(sbi, &segno, gc_type)) goto stop; ret = 0; -- GitLab From b3c1f5d07ab3ad74116f8d41cf36a9a081c73085 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 25 Feb 2017 17:29:54 +0800 Subject: [PATCH 0541/5498] f2fs: kill __is_extent_same Since commit ee6d182f2a19 ("f2fs: remove syncing inode page in all the cases") delayed inode element updating from inode cache to node page cache, so once largest cached extent is updated, we can make inode dirty immediately instead of checking and updating it in the end of extent cache update. The above commit didn't clean up unneeded codes in extent_cache.c, let's finish the job in this patch. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 8 +++----- fs/f2fs/f2fs.h | 7 ------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 0ab5518e45c2..c6934f014e0f 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -413,7 +413,7 @@ do_insert: return en; } -static unsigned int f2fs_update_extent_tree_range(struct inode *inode, +static void f2fs_update_extent_tree_range(struct inode *inode, pgoff_t fofs, block_t blkaddr, unsigned int len) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -426,7 +426,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, unsigned int pos = (unsigned int)fofs; if (!et) - return false; + return; trace_f2fs_update_extent_tree_range(inode, fofs, blkaddr, len); @@ -434,7 +434,7 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, if (is_inode_flag_set(inode, FI_NO_EXTENT)) { write_unlock(&et->lock); - return false; + return; } prev = et->largest; @@ -531,8 +531,6 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, __free_extent_tree(sbi, et); write_unlock(&et->lock); - - return !__is_extent_same(&prev, &et->largest); } unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 96c0ea1fb216..ab550c633c70 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -596,13 +596,6 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, ei->len = len; } -static inline bool __is_extent_same(struct extent_info *ei1, - struct extent_info *ei2) -{ - return (ei1->fofs == ei2->fofs && ei1->blk == ei2->blk && - ei1->len == ei2->len); -} - static inline bool __is_extent_mergeable(struct extent_info *back, struct extent_info *front) { -- GitLab From 378311d2fdb9f73302e258371bc57936713b3502 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 25 Feb 2017 19:23:27 +0800 Subject: [PATCH 0542/5498] f2fs: Don't update the xattr data that same as the exist f2fs removes the old xattr data and appends the new data although the new data is same as the exist. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 9e293af68b79..5af7c3de114b 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -587,6 +587,13 @@ cleanup: return error; } +static bool f2fs_xattr_value_same(struct f2fs_xattr_entry *entry, + const void *value, size_t size) +{ + void *pval = entry->e_name + entry->e_name_len; + return (entry->e_value_size == size) && !memcmp(pval, value, size); +} + static int __f2fs_setxattr(struct inode *inode, int index, const char *name, const void *value, size_t size, struct page *ipage, int flags) @@ -621,12 +628,17 @@ static int __f2fs_setxattr(struct inode *inode, int index, found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1; - if ((flags & XATTR_REPLACE) && !found) { + if (found) { + if ((flags & XATTR_CREATE)) { + error = -EEXIST; + goto exit; + } + + if (f2fs_xattr_value_same(here, value, size)) + goto exit; + } else if ((flags & XATTR_REPLACE)) { error = -ENODATA; goto exit; - } else if ((flags & XATTR_CREATE) && found) { - error = -EEXIST; - goto exit; } last = here; -- GitLab From 1ad790abc514edc279b761591ca758ddf4609ce2 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 25 Feb 2017 19:23:40 +0800 Subject: [PATCH 0543/5498] f2fs: drop the duplicate pval in f2fs_getxattr Fixes: ba38c27eb9 ("f2fs: enhance lookup xattr") Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 5af7c3de114b..8e1632c946ef 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -516,7 +516,6 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name, struct f2fs_xattr_entry *entry = NULL; int error = 0; unsigned int size, len; - char *pval; void *base_addr = NULL; if (name == NULL) @@ -538,8 +537,6 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name, goto out; } - pval = entry->e_name + entry->e_name_len; - if (buffer) { char *pval = entry->e_name + entry->e_name_len; memcpy(buffer, pval, size); -- GitLab From 5896f1867488ef60d5475d8891874215e4ee2626 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 25 Feb 2017 19:32:21 +0800 Subject: [PATCH 0544/5498] f2fs: update the comment of default nr_pages to skipping Fixes: 2c237ebaa4 ("f2fs: avoid writing node/metapages during writes") Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 4bec6885eaeb..32fa84b4dde6 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -735,8 +735,8 @@ static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno) * It is very important to gather dirty pages and write at once, so that we can * submit a big bio without interfering other data writes. * By default, 512 pages for directory data, - * 512 pages (2MB) * 3 for three types of nodes, and - * max_bio_blocks for meta are set. + * 512 pages (2MB) * 8 for nodes, and + * 256 pages * 8 for meta are set. */ static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type) { -- GitLab From c6c4feda8a26bf8e71ba70e6a50bfc59312e1da3 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 25 Feb 2017 19:53:39 +0800 Subject: [PATCH 0545/5498] f2fs: new helper cur_cp_crc() getting crc in f2fs_checkpoint There are four places that getting the crc value in f2fs_checkpoint, just add a new helper cur_cp_crc for them. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 3 +-- fs/f2fs/f2fs.h | 6 ++++++ fs/f2fs/node.c | 5 +---- fs/f2fs/node.h | 20 +++++++------------- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 8c114b1f8e77..e9c022519966 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -684,8 +684,7 @@ static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr, return -EINVAL; } - crc = le32_to_cpu(*((__le32 *)((unsigned char *)*cp_block - + crc_offset))); + crc = cur_cp_crc(*cp_block); if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) { f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value"); return -EINVAL; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ab550c633c70..08d13fad1cbf 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1226,6 +1226,12 @@ static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) return le64_to_cpu(cp->checkpoint_ver); } +static inline __u64 cur_cp_crc(struct f2fs_checkpoint *cp) +{ + size_t crc_offset = le32_to_cpu(cp->checksum_offset); + return le32_to_cpu(*((__le32 *)((unsigned char *)cp + crc_offset))); +} + static inline bool __is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) { unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index f8333d4e7d14..8fa35f93b9da 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2471,9 +2471,6 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) unsigned int nat_bits_bytes = nm_i->nat_blocks / BITS_PER_BYTE; unsigned int i; __u64 cp_ver = cur_cp_version(ckpt); - size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); - __u64 crc = le32_to_cpu(*((__le32 *) - ((unsigned char *)ckpt + crc_offset))); block_t nat_bits_addr; if (!enabled_nat_bits(sbi, NULL)) @@ -2496,7 +2493,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) f2fs_put_page(page, 1); } - cp_ver |= (crc << 32); + cp_ver |= (cur_cp_crc(ckpt) << 32); if (cpu_to_le64(cp_ver) != *(__le64 *)nm_i->nat_bits) { disable_nat_bits(sbi, true); return 0; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 3fc9c4b1dce9..2f9603fa85a5 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -300,14 +300,11 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page)); struct f2fs_node *rn = F2FS_NODE(page); - size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); - __u64 cp_ver = le64_to_cpu(ckpt->checkpoint_ver); + __u64 cp_ver = cur_cp_version(ckpt); + + if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) + cp_ver |= (cur_cp_crc(ckpt) << 32); - if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { - __u64 crc = le32_to_cpu(*((__le32 *) - ((unsigned char *)ckpt + crc_offset))); - cp_ver |= (crc << 32); - } rn->footer.cp_ver = cpu_to_le64(cp_ver); rn->footer.next_blkaddr = cpu_to_le32(blkaddr); } @@ -315,14 +312,11 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr) static inline bool is_recoverable_dnode(struct page *page) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page)); - size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); __u64 cp_ver = cur_cp_version(ckpt); - if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { - __u64 crc = le32_to_cpu(*((__le32 *) - ((unsigned char *)ckpt + crc_offset))); - cp_ver |= (crc << 32); - } + if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) + cp_ver |= (cur_cp_crc(ckpt) << 32); + return cp_ver == cpver_of_node(page); } -- GitLab From 05ebe69e6adab1367a048e9459dcfeb208ea2381 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 23 Feb 2017 10:53:49 +0800 Subject: [PATCH 0546/5498] f2fs: introduce free nid bitmap In scenario of intensively node allocation, free nids will be ran out soon, then it needs to stop to load free nids by traversing NAT blocks, in worse case, if NAT blocks does not be cached in memory, it generates IOs which slows down our foreground operations. In order to speed up node allocation, in this patch we introduce a new free_nid_bitmap array, so there is an bitmap table for each NAT block, Once the NAT block is loaded, related bitmap cache will be switched on, and bitmap will be set during traversing nat entries in NAT block, later we can query and update nid usage status in memory completely. With such implementation, I expect performance of node allocation can be improved in the long-term after filesystem image is mounted. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: include/linux/f2fs_fs.h --- fs/f2fs/debug.c | 2 + fs/f2fs/f2fs.h | 2 + fs/f2fs/node.c | 125 ++++++++++++++++++++++++++++++++++++---- include/linux/f2fs_fs.h | 3 +- 4 files changed, 121 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 0f8d582e32d9..ba6d2b8aadc2 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -194,6 +194,8 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct f2fs_nm_info); si->base_mem += __bitmap_size(sbi, NAT_BITMAP); si->base_mem += (NM_I(sbi)->nat_bits_blocks << F2FS_BLKSIZE_BITS); + si->base_mem += NM_I(sbi)->nat_blocks * NAT_ENTRY_BITMAP_SIZE; + si->base_mem += NM_I(sbi)->nat_blocks / 8; get_cache: si->cache_mem = 0; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 08d13fad1cbf..ade4245985b8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -655,6 +655,8 @@ struct f2fs_nm_info { unsigned int nid_cnt[MAX_NID_LIST]; /* the number of free node id */ spinlock_t nid_list_lock; /* protect nid lists ops */ struct mutex build_lock; /* lock for build free nids */ + unsigned char (*free_nid_bitmap)[NAT_ENTRY_BITMAP_SIZE]; + unsigned char *nat_block_bitmap; /* for checkpoint */ char *nat_bitmap; /* NAT bitmap pointer */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 8fa35f93b9da..e4953892edb6 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1774,7 +1774,8 @@ static void __remove_nid_from_list(struct f2fs_sb_info *sbi, radix_tree_delete(&nm_i->free_nid_root, i->nid); } -static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) +/* return if the nid is recognized as free */ +static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i; @@ -1783,14 +1784,14 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) /* 0 nid should not be used */ if (unlikely(nid == 0)) - return 0; + return false; if (build) { /* do not add allocated nids */ ne = __lookup_nat_cache(nm_i, nid); if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || nat_get_blkaddr(ne) != NULL_ADDR)) - return 0; + return false; } i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS); @@ -1799,7 +1800,7 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) if (radix_tree_preload(GFP_NOFS)) { kmem_cache_free(free_nid_slab, i); - return 0; + return true; } spin_lock(&nm_i->nid_list_lock); @@ -1808,9 +1809,9 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) radix_tree_preload_end(); if (err) { kmem_cache_free(free_nid_slab, i); - return 0; + return true; } - return 1; + return true; } static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) @@ -1831,17 +1832,36 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) kmem_cache_free(free_nid_slab, i); } +void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, bool set) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); + unsigned int nid_ofs = nid - START_NID(nid); + + if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) + return; + + if (set) + set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + else + clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); +} + static void scan_nat_page(struct f2fs_sb_info *sbi, struct page *nat_page, nid_t start_nid) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nat_block *nat_blk = page_address(nat_page); block_t blk_addr; + unsigned int nat_ofs = NAT_BLOCK_OFFSET(start_nid); int i; + set_bit_le(nat_ofs, nm_i->nat_block_bitmap); + i = start_nid % NAT_ENTRY_PER_BLOCK; for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) { + bool freed = false; if (unlikely(start_nid >= nm_i->max_nid)) break; @@ -1849,8 +1869,52 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr); f2fs_bug_on(sbi, blk_addr == NEW_ADDR); if (blk_addr == NULL_ADDR) - add_free_nid(sbi, start_nid, true); + freed = add_free_nid(sbi, start_nid, true); + update_free_nid_bitmap(sbi, start_nid, freed); + } +} + +static void scan_free_nid_bits(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_journal *journal = curseg->journal; + unsigned int i, idx; + unsigned int target = FREE_NID_PAGES * NAT_ENTRY_PER_BLOCK; + + down_read(&nm_i->nat_tree_lock); + + for (i = 0; i < nm_i->nat_blocks; i++) { + if (!test_bit_le(i, nm_i->nat_block_bitmap)) + continue; + for (idx = 0; idx < NAT_ENTRY_PER_BLOCK; idx++) { + nid_t nid; + + if (!test_bit_le(idx, nm_i->free_nid_bitmap[i])) + continue; + + nid = i * NAT_ENTRY_PER_BLOCK + idx; + add_free_nid(sbi, nid, true); + + if (nm_i->nid_cnt[FREE_NID_LIST] >= target) + goto out; + } + } +out: + down_read(&curseg->journal_rwsem); + for (i = 0; i < nats_in_cursum(journal); i++) { + block_t addr; + nid_t nid; + + addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); + nid = le32_to_cpu(nid_in_journal(journal, i)); + if (addr == NULL_ADDR) + add_free_nid(sbi, nid, true); + else + remove_free_nid(sbi, nid); } + up_read(&curseg->journal_rwsem); + up_read(&nm_i->nat_tree_lock); } static int scan_nat_bits(struct f2fs_sb_info *sbi) @@ -1921,9 +1985,17 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) if (!sync && !available_free_memory(sbi, FREE_NIDS)) return; - /* try to find free nids with nat_bits */ - if (!mount && !scan_nat_bits(sbi) && nm_i->nid_cnt[FREE_NID_LIST]) - return; + if (!mount) { + /* try to find free nids in free_nid_bitmap */ + scan_free_nid_bits(sbi); + + if (nm_i->nid_cnt[FREE_NID_LIST]) + return; + + /* try to find free nids with nat_bits */ + if (!scan_nat_bits(sbi) && nm_i->nid_cnt[FREE_NID_LIST]) + return; + } /* find next valid candidate */ if (enabled_nat_bits(sbi, NULL)) { @@ -2019,6 +2091,9 @@ retry: i->state = NID_ALLOC; __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); nm_i->available_nids--; + + update_free_nid_bitmap(sbi, *nid, false); + spin_unlock(&nm_i->nid_list_lock); return true; } @@ -2073,6 +2148,8 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) nm_i->available_nids++; + update_free_nid_bitmap(sbi, nid, true); + spin_unlock(&nm_i->nid_list_lock); if (need_free) @@ -2401,6 +2478,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, add_free_nid(sbi, nid, false); spin_lock(&NM_I(sbi)->nid_list_lock); NM_I(sbi)->available_nids++; + update_free_nid_bitmap(sbi, nid, true); + spin_unlock(&NM_I(sbi)->nid_list_lock); + } else { + spin_lock(&NM_I(sbi)->nid_list_lock); + update_free_nid_bitmap(sbi, nid, false); spin_unlock(&NM_I(sbi)->nid_list_lock); } } @@ -2567,6 +2649,22 @@ static int init_node_manager(struct f2fs_sb_info *sbi) return 0; } +int init_free_nid_cache(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + + nm_i->free_nid_bitmap = f2fs_kvzalloc(nm_i->nat_blocks * + NAT_ENTRY_BITMAP_SIZE, GFP_KERNEL); + if (!nm_i->free_nid_bitmap) + return -ENOMEM; + + nm_i->nat_block_bitmap = f2fs_kvzalloc(nm_i->nat_blocks / 8, + GFP_KERNEL); + if (!nm_i->nat_block_bitmap) + return -ENOMEM; + return 0; +} + int build_node_manager(struct f2fs_sb_info *sbi) { int err; @@ -2579,6 +2677,10 @@ int build_node_manager(struct f2fs_sb_info *sbi) if (err) return err; + err = init_free_nid_cache(sbi); + if (err) + return err; + build_free_nids(sbi, true, true); return 0; } @@ -2637,6 +2739,9 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) } up_write(&nm_i->nat_tree_lock); + kvfree(nm_i->nat_block_bitmap); + kvfree(nm_i->free_nid_bitmap); + kfree(nm_i->nat_bitmap); kfree(nm_i->nat_bits); #ifdef CONFIG_F2FS_CHECK_FS diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index ae5c0c2c8692..acdeb739053d 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -278,7 +278,8 @@ struct f2fs_node { /* * For NAT entries */ -#define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry)) +#define NAT_ENTRY_PER_BLOCK (PAGE_SIZE / sizeof(struct f2fs_nat_entry)) +#define NAT_ENTRY_BITMAP_SIZE ((NAT_ENTRY_PER_BLOCK + 7) / 8) struct f2fs_nat_entry { __u8 version; /* latest version of cached nat entry */ -- GitLab From 717b7274143ca1881b8e39d8f06ab74f7748ac3d Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sun, 26 Feb 2017 20:47:16 +0800 Subject: [PATCH 0547/5498] f2fs: use MAX_FREE_NIDS for the free nids target F2FS has define MAX_FREE_NIDS for maximum of cached free nids target. Signed-off-by: Kinglong Mee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index e4953892edb6..7ff7d4113a05 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1880,7 +1880,6 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = curseg->journal; unsigned int i, idx; - unsigned int target = FREE_NID_PAGES * NAT_ENTRY_PER_BLOCK; down_read(&nm_i->nat_tree_lock); @@ -1896,7 +1895,7 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) nid = i * NAT_ENTRY_PER_BLOCK + idx; add_free_nid(sbi, nid, true); - if (nm_i->nid_cnt[FREE_NID_LIST] >= target) + if (nm_i->nid_cnt[FREE_NID_LIST] >= MAX_FREE_NIDS) goto out; } } @@ -1922,7 +1921,6 @@ static int scan_nat_bits(struct f2fs_sb_info *sbi) struct f2fs_nm_info *nm_i = NM_I(sbi); struct page *page; unsigned int i = 0; - nid_t target = FREE_NID_PAGES * NAT_ENTRY_PER_BLOCK; nid_t nid; if (!enabled_nat_bits(sbi, NULL)) @@ -1943,7 +1941,7 @@ check_empty: add_free_nid(sbi, nid, true); } - if (nm_i->nid_cnt[FREE_NID_LIST] >= target) + if (nm_i->nid_cnt[FREE_NID_LIST] >= MAX_FREE_NIDS) goto out; i++; goto check_empty; @@ -1961,7 +1959,7 @@ check_partial: scan_nat_page(sbi, page, nid); f2fs_put_page(page, 1); - if (nm_i->nid_cnt[FREE_NID_LIST] < target) { + if (nm_i->nid_cnt[FREE_NID_LIST] < MAX_FREE_NIDS) { i++; goto check_partial; } -- GitLab From 2793782721d48257ca6ab854ade274348921b31a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 27 Feb 2017 17:10:45 +0800 Subject: [PATCH 0548/5498] f2fs: fix to update F2FS_{CP_}WB_DATA count correctly We should only account F2FS_{CP_}WB_DATA IOs for write path, fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 4241f114f756..f1088c59b936 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -399,7 +399,8 @@ alloc_new: if ((fio->type == DATA || fio->type == NODE) && fio->new_blkaddr & F2FS_IO_SIZE_MASK(sbi)) { err = -EAGAIN; - dec_page_count(sbi, WB_DATA_TYPE(bio_page)); + if (!is_read) + dec_page_count(sbi, WB_DATA_TYPE(bio_page)); goto out_fail; } io->bio = __bio_alloc(sbi, fio->new_blkaddr, -- GitLab From 9fa8af7eff5d1f6689eb4aa4314e8c75a67bfae1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 27 Feb 2017 18:43:12 +0800 Subject: [PATCH 0549/5498] f2fs: fix memory leak of write_io_dummy mempool during umount Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 836bd4d286ee..356a83bbe4fc 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -805,7 +805,8 @@ static void f2fs_put_super(struct super_block *sb) kfree(sbi->raw_super); destroy_device_list(sbi); - + if (sbi->write_io_dummy) + mempool_destroy(sbi->write_io_dummy); destroy_percpu_info(sbi); kfree(sbi); } -- GitLab From 1a8fa512fd061863cee7e81faafdd70db7bf66bf Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 27 Feb 2017 18:43:13 +0800 Subject: [PATCH 0550/5498] f2fs: fix to enlarge size of write_io_dummy mempool It needs to double cache size of write_io_dummy mempool, otherwise we may run out of cache in scenraio of Data/Node IOs were issued concurrently. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 356a83bbe4fc..939a3a328a35 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1917,7 +1917,7 @@ try_onemore: if (F2FS_IO_SIZE(sbi) > 1) { sbi->write_io_dummy = - mempool_create_page_pool(F2FS_IO_SIZE(sbi) - 1, 0); + mempool_create_page_pool(2 * (F2FS_IO_SIZE(sbi) - 1), 0); if (!sbi->write_io_dummy) goto free_options; } -- GitLab From 1702679d07a3cc73c681609f3247e2d458b3ffc8 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 27 Feb 2017 18:59:53 +0800 Subject: [PATCH 0551/5498] f2fs: remove redundant set_page_dirty() This patch remove redundant set_page_dirty in truncate_blocks Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c8cac30498f8..984fffbc1f2c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -568,8 +568,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) } if (f2fs_has_inline_data(inode)) { - if (truncate_inline_inode(ipage, from)) - set_page_dirty(ipage); + truncate_inline_inode(ipage, from); if (from == 0) clear_inode_flag(inode, FI_DATA_EXIST); f2fs_put_page(ipage, 1); -- GitLab From 06c5299e235f408562f1d4f3f315f335c2761b4d Mon Sep 17 00:00:00 2001 From: Masato Suzuki Date: Mon, 27 Feb 2017 20:52:49 +0900 Subject: [PATCH 0552/5498] f2fs: Fix zoned block device support The introduction of the multi-device feature partially broke the support for zoned block devices. In the function f2fs_scan_devices, sbi->devs allocation and initialization is skipped in the case of a single device mount. This result in no device information structure being allocated for the device. This is fine if the device is a regular device, but in the case of a zoned block device, the device zone type array is not initialized, which causes the function __f2fs_issue_discard_zone to fail as get_blkz_type is unable to determine the zone type of a section. Fix this by always allocating and initializing the sbi->devs device information array even in the case of a single device if that device is zoned. For this particular case, make sure to obtain a reference on the single device so that the call to blkdev_put() in destroy_device_list operates as expected. Fixes: 3c62be17d4f562f4 ("f2fs: support multiple devices") Cc: # v4.10 Signed-off-by: Masato Suzuki Acked-by: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 71 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 939a3a328a35..d8cc5806f857 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1731,36 +1731,59 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) static int f2fs_scan_devices(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + unsigned int max_devices = MAX_DEVICES; int i; - for (i = 0; i < MAX_DEVICES; i++) { - if (!RDEV(i).path[0]) + /* Initialize single device information */ + if (!RDEV(0).path[0]) { +#ifdef CONFIG_BLK_DEV_ZONED + if (bdev_zoned_model(sbi->sb->s_bdev) == BLK_ZONED_NONE) return 0; + max_devices = 1; +#else + return 0; +#endif + } - if (i == 0) { - sbi->devs = kzalloc(sizeof(struct f2fs_dev_info) * - MAX_DEVICES, GFP_KERNEL); - if (!sbi->devs) - return -ENOMEM; - } + /* + * Initialize multiple devices information, or single + * zoned block device information. + */ + sbi->devs = kcalloc(max_devices, sizeof(struct f2fs_dev_info), + GFP_KERNEL); + if (!sbi->devs) + return -ENOMEM; - memcpy(FDEV(i).path, RDEV(i).path, MAX_PATH_LEN); - FDEV(i).total_segments = le32_to_cpu(RDEV(i).total_segments); - if (i == 0) { - FDEV(i).start_blk = 0; - FDEV(i).end_blk = FDEV(i).start_blk + - (FDEV(i).total_segments << - sbi->log_blocks_per_seg) - 1 + - le32_to_cpu(raw_super->segment0_blkaddr); - } else { - FDEV(i).start_blk = FDEV(i - 1).end_blk + 1; - FDEV(i).end_blk = FDEV(i).start_blk + - (FDEV(i).total_segments << - sbi->log_blocks_per_seg) - 1; - } + for (i = 0; i < max_devices; i++) { - FDEV(i).bdev = blkdev_get_by_path(FDEV(i).path, + if (i > 0 && !RDEV(i).path[0]) + break; + + if (max_devices == 1) { + /* Single zoned block device mount */ + FDEV(0).bdev = + blkdev_get_by_dev(sbi->sb->s_bdev->bd_dev, sbi->sb->s_mode, sbi->sb->s_type); + } else { + /* Multi-device mount */ + memcpy(FDEV(i).path, RDEV(i).path, MAX_PATH_LEN); + FDEV(i).total_segments = + le32_to_cpu(RDEV(i).total_segments); + if (i == 0) { + FDEV(i).start_blk = 0; + FDEV(i).end_blk = FDEV(i).start_blk + + (FDEV(i).total_segments << + sbi->log_blocks_per_seg) - 1 + + le32_to_cpu(raw_super->segment0_blkaddr); + } else { + FDEV(i).start_blk = FDEV(i - 1).end_blk + 1; + FDEV(i).end_blk = FDEV(i).start_blk + + (FDEV(i).total_segments << + sbi->log_blocks_per_seg) - 1; + } + FDEV(i).bdev = blkdev_get_by_path(FDEV(i).path, + sbi->sb->s_mode, sbi->sb->s_type); + } if (IS_ERR(FDEV(i).bdev)) return PTR_ERR(FDEV(i).bdev); @@ -1780,6 +1803,8 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) "Failed to initialize F2FS blkzone information"); return -EINVAL; } + if (max_devices == 1) + break; f2fs_msg(sbi->sb, KERN_INFO, "Mount Device [%2d]: %20s, %8u, %8x - %8x (zone: %s)", i, FDEV(i).path, -- GitLab From b71a1836f6a4049d32559e6f5e4b0f9eb3ba0149 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Mon, 27 Feb 2017 13:02:58 +0000 Subject: [PATCH 0553/5498] f2fs: add f2fs_drop_inode tracepoint Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 7 +++++-- include/trace/events/f2fs.h | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d8cc5806f857..0bde614cbf3a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -614,6 +614,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) static int f2fs_drop_inode(struct inode *inode) { + int ret; /* * This is to avoid a deadlock condition like below. * writeback_single_inode(inode) @@ -646,10 +647,12 @@ static int f2fs_drop_inode(struct inode *inode) spin_lock(&inode->i_lock); atomic_dec(&inode->i_count); } + trace_f2fs_drop_inode(inode, 0); return 0; } - - return generic_drop_inode(inode); + ret = generic_drop_inode(inode); + trace_f2fs_drop_inode(inode, ret); + return ret; } int f2fs_inode_dirtied(struct inode *inode, bool sync) diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 2e1aefca3c02..4b1cab6743f7 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -274,6 +274,13 @@ DEFINE_EVENT(f2fs__inode_exit, f2fs_unlink_exit, TP_ARGS(inode, ret) ); +DEFINE_EVENT(f2fs__inode_exit, f2fs_drop_inode, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret) +); + DEFINE_EVENT(f2fs__inode, f2fs_truncate, TP_PROTO(struct inode *inode), -- GitLab From 2e67588ad9170924fbe195ae7071568351dd5339 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Mon, 27 Feb 2017 13:02:59 +0000 Subject: [PATCH 0554/5498] f2fs: fix a plint compile warning fix such pclint warning: ... Loss of precision (arg. no. 2) (unsigned long long to unsigned int)) Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 023f043edefd..418fd9881646 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1009,6 +1009,6 @@ void build_gc_manager(struct f2fs_sb_info *sbi) ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg; blocks_per_sec = sbi->blocks_per_seg * sbi->segs_per_sec; - sbi->fggc_threshold = div_u64((main_count - ovp_count) * blocks_per_sec, + sbi->fggc_threshold = div64_u64((main_count - ovp_count) * blocks_per_sec, (main_count - resv_count)); } -- GitLab From 277825fe72bd50c4018f66d13cee63c97ed1b5a7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 27 Feb 2017 11:57:11 -0800 Subject: [PATCH 0555/5498] f2fs: avoid to issue redundant discard commands If segs_per_sec is over 1 like under SMR, previously f2fs issues discard commands redundantly on the same section, since we didn't move end position for the previous discard command. E.g., start end | | prefree_bitmap = [01111100111100] And, after issue discard for this section, end start | | prefree_bitmap = [01111100111100] Select this section again by searching from (end + 1), start end | | prefree_bitmap = [01111100111100] Fixes: 36abef4e796d38 ("f2fs: introduce mode=lfs mount option") Cc: Cc: Damien Le Moal Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 574dae01e2f6..baf606157c20 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1116,6 +1116,8 @@ next: start = start_segno + sbi->segs_per_sec; if (start < end) goto next; + else + end = start - 1; } mutex_unlock(&dirty_i->seglist_lock); -- GitLab From 64e25d8e1d162a3e90ad7ed8187c9bab0a289b6d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 27 Feb 2017 21:28:53 -0800 Subject: [PATCH 0556/5498] f2fs: avoid to flush nat journal entries This patch adds a missing condition which flushes nat journal entries unnecessarily introduced by: f2fs: add bitmaps for empty or full NAT blocks Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7ff7d4113a05..027a74562575 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2522,7 +2522,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) * entries, remove all entries from journal and merge them * into nat entry set. */ - if (cpc->reason == CP_UMOUNT || + if (enabled_nat_bits(sbi, cpc) || !__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL)) remove_nats_in_journal(sbi); -- GitLab From d52bc3496c087d2bba6c3a97ce62af660e00e10e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 8 Mar 2017 15:24:43 -0800 Subject: [PATCH 0557/5498] fscrypt: catch up to v4.11-rc1 Keep validate_user_key() due to kasprintf() panic. fscrypt: - skcipher_ -> ablkcipher_ - fs/crypto/bio.c changes f2fs: - fscrypt: use ENOKEY when file cannot be created w/o key - fscrypt: split supp and notsupp declarations into their own headers - fscrypt: make fscrypt_operations.key_prefix a string Signed-off-by: Jaegeuk Kim --- fs/crypto/Makefile | 1 + fs/crypto/bio.c | 143 +++++++++++ fs/crypto/crypto.c | 250 +++++++------------ fs/crypto/fname.c | 20 +- fs/crypto/fscrypt_private.h | 132 ++++++++++ fs/crypto/keyinfo.c | 64 +++-- fs/crypto/policy.c | 107 +++------ fs/f2fs/data.c | 4 +- fs/f2fs/dir.c | 5 +- fs/f2fs/f2fs.h | 40 +--- fs/f2fs/key.c | 34 --- fs/f2fs/namei.c | 4 +- fs/f2fs/super.c | 14 +- include/linux/fscrypt_common.h | 146 ++++++++++++ include/linux/fscrypt_notsupp.h | 168 +++++++++++++ include/linux/fscrypt_supp.h | 66 +++++ include/linux/fscrypto.h | 410 -------------------------------- include/uapi/linux/fs.h | 15 ++ 18 files changed, 844 insertions(+), 779 deletions(-) create mode 100644 fs/crypto/bio.c create mode 100644 fs/crypto/fscrypt_private.h delete mode 100644 fs/f2fs/key.c create mode 100644 include/linux/fscrypt_common.h create mode 100644 include/linux/fscrypt_notsupp.h create mode 100644 include/linux/fscrypt_supp.h delete mode 100644 include/linux/fscrypto.h diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index f17684c48739..9f6607f17b53 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o fscrypto-y := crypto.o fname.o policy.o keyinfo.o +fscrypto-$(CONFIG_BLOCK) += bio.o diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c new file mode 100644 index 000000000000..a91ed46fe503 --- /dev/null +++ b/fs/crypto/bio.c @@ -0,0 +1,143 @@ +/* + * This contains encryption functions for per-file encryption. + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * Written by Michael Halcrow, 2014. + * + * Filename encryption additions + * Uday Savagaonkar, 2014 + * Encryption policy handling additions + * Ildar Muslukhov, 2014 + * Add fscrypt_pullback_bio_page() + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + * + * The usage of AES-XTS should conform to recommendations in NIST + * Special Publication 800-38E and IEEE P1619/D16. + */ + +#include +#include +#include +#include +#include "fscrypt_private.h" + +/* + * Call fscrypt_decrypt_page on every single page, reusing the encryption + * context. + */ +static void completion_pages(struct work_struct *work) +{ + struct fscrypt_ctx *ctx = + container_of(work, struct fscrypt_ctx, r.work); + struct bio *bio = ctx->r.bio; + struct bio_vec *bv; + int i; + + bio_for_each_segment_all(bv, bio, i) { + struct page *page = bv->bv_page; + int ret = fscrypt_decrypt_page(page->mapping->host, page, + PAGE_SIZE, 0, page->index); + + if (ret) { + WARN_ON_ONCE(1); + SetPageError(page); + } else { + SetPageUptodate(page); + } + unlock_page(page); + } + fscrypt_release_ctx(ctx); + bio_put(bio); +} + +void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio) +{ + INIT_WORK(&ctx->r.work, completion_pages); + ctx->r.bio = bio; + queue_work(fscrypt_read_workqueue, &ctx->r.work); +} +EXPORT_SYMBOL(fscrypt_decrypt_bio_pages); + +void fscrypt_pullback_bio_page(struct page **page, bool restore) +{ + struct fscrypt_ctx *ctx; + struct page *bounce_page; + + /* The bounce data pages are unmapped. */ + if ((*page)->mapping) + return; + + /* The bounce data page is unmapped. */ + bounce_page = *page; + ctx = (struct fscrypt_ctx *)page_private(bounce_page); + + /* restore control page */ + *page = ctx->w.control_page; + + if (restore) + fscrypt_restore_control_page(bounce_page); +} +EXPORT_SYMBOL(fscrypt_pullback_bio_page); + +int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, + sector_t pblk, unsigned int len) +{ + struct fscrypt_ctx *ctx; + struct page *ciphertext_page = NULL; + struct bio *bio; + int ret, err = 0; + + BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE); + + ctx = fscrypt_get_ctx(inode, GFP_NOFS); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT); + if (IS_ERR(ciphertext_page)) { + err = PTR_ERR(ciphertext_page); + goto errout; + } + + while (len--) { + err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk, + ZERO_PAGE(0), ciphertext_page, + PAGE_SIZE, 0, GFP_NOFS); + if (err) + goto errout; + + bio = bio_alloc(GFP_NOWAIT, 1); + if (!bio) { + err = -ENOMEM; + goto errout; + } + bio->bi_bdev = inode->i_sb->s_bdev; + bio->bi_iter.bi_sector = + pblk << (inode->i_sb->s_blocksize_bits - 9); + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); + ret = bio_add_page(bio, ciphertext_page, + inode->i_sb->s_blocksize, 0); + if (ret != inode->i_sb->s_blocksize) { + /* should never happen! */ + WARN_ON(1); + bio_put(bio); + err = -EIO; + goto errout; + } + err = submit_bio_wait(0, bio); + bio_put(bio); + if (err) + goto errout; + lblk++; + pblk++; + } + err = 0; +errout: + fscrypt_release_ctx(ctx); + return err; +} +EXPORT_SYMBOL(fscrypt_zeroout_range); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 83ece4d31d54..611fa002785c 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -19,17 +19,14 @@ * Special Publication 800-38E and IEEE P1619/D16. */ -#include -#include #include #include #include #include #include -#include #include #include -#include +#include "fscrypt_private.h" static unsigned int num_prealloc_crypto_pages = 32; static unsigned int num_prealloc_crypto_ctxs = 128; @@ -46,7 +43,7 @@ static mempool_t *fscrypt_bounce_page_pool = NULL; static LIST_HEAD(fscrypt_free_ctxs); static DEFINE_SPINLOCK(fscrypt_ctx_lock); -static struct workqueue_struct *fscrypt_read_workqueue; +struct workqueue_struct *fscrypt_read_workqueue; static DEFINE_MUTEX(fscrypt_init_mutex); static struct kmem_cache *fscrypt_ctx_cachep; @@ -65,7 +62,7 @@ void fscrypt_release_ctx(struct fscrypt_ctx *ctx) { unsigned long flags; - if (ctx->flags & FS_WRITE_PATH_FL && ctx->w.bounce_page) { + if (ctx->flags & FS_CTX_HAS_BOUNCE_BUFFER_FL && ctx->w.bounce_page) { mempool_free(ctx->w.bounce_page, fscrypt_bounce_page_pool); ctx->w.bounce_page = NULL; } @@ -90,7 +87,7 @@ EXPORT_SYMBOL(fscrypt_release_ctx); * Return: An allocated and initialized encryption context on success; error * value or NULL otherwise. */ -struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags) +struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) { struct fscrypt_ctx *ctx = NULL; struct fscrypt_info *ci = inode->i_crypt_info; @@ -123,7 +120,7 @@ struct fscrypt_ctx *fscrypt_get_ctx(struct inode *inode, gfp_t gfp_flags) } else { ctx->flags &= ~FS_CTX_REQUIRES_FREE_ENCRYPT_FL; } - ctx->flags &= ~FS_WRITE_PATH_FL; + ctx->flags &= ~FS_CTX_HAS_BOUNCE_BUFFER_FL; return ctx; } EXPORT_SYMBOL(fscrypt_get_ctx); @@ -143,15 +140,10 @@ static void page_crypt_complete(struct crypto_async_request *req, int res) complete(&ecr->completion); } -typedef enum { - FS_DECRYPT = 0, - FS_ENCRYPT, -} fscrypt_direction_t; - -static int do_page_crypto(struct inode *inode, - fscrypt_direction_t rw, pgoff_t index, - struct page *src_page, struct page *dest_page, - gfp_t gfp_flags) +int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, + u64 lblk_num, struct page *src_page, + struct page *dest_page, unsigned int len, + unsigned int offs, gfp_t gfp_flags) { struct { __le64 index; @@ -164,6 +156,8 @@ static int do_page_crypto(struct inode *inode, struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; + BUG_ON(len == 0); + req = ablkcipher_request_alloc(tfm, gfp_flags); if (!req) { printk_ratelimited(KERN_ERR @@ -177,15 +171,14 @@ static int do_page_crypto(struct inode *inode, page_crypt_complete, &ecr); BUILD_BUG_ON(sizeof(xts_tweak) != FS_XTS_TWEAK_SIZE); - xts_tweak.index = cpu_to_le64(index); + xts_tweak.index = cpu_to_le64(lblk_num); memset(xts_tweak.padding, 0, sizeof(xts_tweak.padding)); sg_init_table(&dst, 1); - sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0); + sg_set_page(&dst, dest_page, len, offs); sg_init_table(&src, 1); - sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0); - ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE, - &xts_tweak); + sg_set_page(&src, src_page, len, offs); + ablkcipher_request_set_crypt(req, &src, &dst, len, &xts_tweak); if (rw == FS_DECRYPT) res = crypto_ablkcipher_decrypt(req); else @@ -205,53 +198,86 @@ static int do_page_crypto(struct inode *inode, return 0; } -static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags) +struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, + gfp_t gfp_flags) { ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags); if (ctx->w.bounce_page == NULL) return ERR_PTR(-ENOMEM); - ctx->flags |= FS_WRITE_PATH_FL; + ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL; return ctx->w.bounce_page; } /** * fscypt_encrypt_page() - Encrypts a page - * @inode: The inode for which the encryption should take place - * @plaintext_page: The page to encrypt. Must be locked. - * @gfp_flags: The gfp flag for memory allocation + * @inode: The inode for which the encryption should take place + * @page: The page to encrypt. Must be locked for bounce-page + * encryption. + * @len: Length of data to encrypt in @page and encrypted + * data in returned page. + * @offs: Offset of data within @page and returned + * page holding encrypted data. + * @lblk_num: Logical block number. This must be unique for multiple + * calls with same inode, except when overwriting + * previously written data. + * @gfp_flags: The gfp flag for memory allocation * - * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx - * encryption context. + * Encrypts @page using the ctx encryption context. Performs encryption + * either in-place or into a newly allocated bounce page. + * Called on the page write path. * - * Called on the page write path. The caller must call + * Bounce page allocation is the default. + * In this case, the contents of @page are encrypted and stored in an + * allocated bounce page. @page has to be locked and the caller must call * fscrypt_restore_control_page() on the returned ciphertext page to * release the bounce buffer and the encryption context. * - * Return: An allocated page with the encrypted content on success. Else, an + * In-place encryption is used by setting the FS_CFLG_OWN_PAGES flag in + * fscrypt_operations. Here, the input-page is returned with its content + * encrypted. + * + * Return: A page with the encrypted content on success. Else, an * error value or NULL. */ -struct page *fscrypt_encrypt_page(struct inode *inode, - struct page *plaintext_page, gfp_t gfp_flags) +struct page *fscrypt_encrypt_page(const struct inode *inode, + struct page *page, + unsigned int len, + unsigned int offs, + u64 lblk_num, gfp_t gfp_flags) + { struct fscrypt_ctx *ctx; - struct page *ciphertext_page = NULL; + struct page *ciphertext_page = page; int err; - BUG_ON(!PageLocked(plaintext_page)); + BUG_ON(len % FS_CRYPTO_BLOCK_SIZE != 0); + + if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) { + /* with inplace-encryption we just encrypt the page */ + err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, page, + ciphertext_page, len, offs, + gfp_flags); + if (err) + return ERR_PTR(err); + + return ciphertext_page; + } + + BUG_ON(!PageLocked(page)); ctx = fscrypt_get_ctx(inode, gfp_flags); if (IS_ERR(ctx)) return (struct page *)ctx; /* The encryption operation will require a bounce page. */ - ciphertext_page = alloc_bounce_page(ctx, gfp_flags); + ciphertext_page = fscrypt_alloc_bounce_page(ctx, gfp_flags); if (IS_ERR(ciphertext_page)) goto errout; - ctx->w.control_page = plaintext_page; - err = do_page_crypto(inode, FS_ENCRYPT, plaintext_page->index, - plaintext_page, ciphertext_page, - gfp_flags); + ctx->w.control_page = page; + err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, + page, ciphertext_page, len, offs, + gfp_flags); if (err) { ciphertext_page = ERR_PTR(err); goto errout; @@ -268,8 +294,13 @@ errout: EXPORT_SYMBOL(fscrypt_encrypt_page); /** - * f2crypt_decrypt_page() - Decrypts a page in-place - * @page: The page to decrypt. Must be locked. + * fscrypt_decrypt_page() - Decrypts a page in-place + * @inode: The corresponding inode for the page to decrypt. + * @page: The page to decrypt. Must be locked in case + * it is a writeback page (FS_CFLG_OWN_PAGES unset). + * @len: Number of bytes in @page to be decrypted. + * @offs: Start of data in @page. + * @lblk_num: Logical block number. * * Decrypts page in-place using the ctx encryption context. * @@ -277,73 +308,17 @@ EXPORT_SYMBOL(fscrypt_encrypt_page); * * Return: Zero on success, non-zero otherwise. */ -int fscrypt_decrypt_page(struct page *page) +int fscrypt_decrypt_page(const struct inode *inode, struct page *page, + unsigned int len, unsigned int offs, u64 lblk_num) { - BUG_ON(!PageLocked(page)); + if (!(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES)) + BUG_ON(!PageLocked(page)); - return do_page_crypto(page->mapping->host, - FS_DECRYPT, page->index, page, page, GFP_NOFS); + return fscrypt_do_page_crypto(inode, FS_DECRYPT, lblk_num, page, page, + len, offs, GFP_NOFS); } EXPORT_SYMBOL(fscrypt_decrypt_page); -int fscrypt_zeroout_range(struct inode *inode, pgoff_t lblk, - sector_t pblk, unsigned int len) -{ - struct fscrypt_ctx *ctx; - struct page *ciphertext_page = NULL; - struct bio *bio; - int ret, err = 0; - - BUG_ON(inode->i_sb->s_blocksize != PAGE_CACHE_SIZE); - - ctx = fscrypt_get_ctx(inode, GFP_NOFS); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); - - ciphertext_page = alloc_bounce_page(ctx, GFP_NOWAIT); - if (IS_ERR(ciphertext_page)) { - err = PTR_ERR(ciphertext_page); - goto errout; - } - - while (len--) { - err = do_page_crypto(inode, FS_ENCRYPT, lblk, - ZERO_PAGE(0), ciphertext_page, - GFP_NOFS); - if (err) - goto errout; - - bio = bio_alloc(GFP_NOWAIT, 1); - if (!bio) { - err = -ENOMEM; - goto errout; - } - bio->bi_bdev = inode->i_sb->s_bdev; - bio->bi_iter.bi_sector = - pblk << (inode->i_sb->s_blocksize_bits - 9); - ret = bio_add_page(bio, ciphertext_page, - inode->i_sb->s_blocksize, 0); - if (ret != inode->i_sb->s_blocksize) { - /* should never happen! */ - WARN_ON(1); - bio_put(bio); - err = -EIO; - goto errout; - } - err = submit_bio_wait(WRITE, bio); - bio_put(bio); - if (err) - goto errout; - lblk++; - pblk++; - } - err = 0; -errout: - fscrypt_release_ctx(ctx); - return err; -} -EXPORT_SYMBOL(fscrypt_zeroout_range); - /* * Validate dentries for encrypted directories to make sure we aren't * potentially caching stale data after a key has been added or @@ -400,63 +375,6 @@ const struct dentry_operations fscrypt_d_ops = { }; EXPORT_SYMBOL(fscrypt_d_ops); -/* - * Call fscrypt_decrypt_page on every single page, reusing the encryption - * context. - */ -static void completion_pages(struct work_struct *work) -{ - struct fscrypt_ctx *ctx = - container_of(work, struct fscrypt_ctx, r.work); - struct bio *bio = ctx->r.bio; - struct bio_vec *bv; - int i; - - bio_for_each_segment_all(bv, bio, i) { - struct page *page = bv->bv_page; - int ret = fscrypt_decrypt_page(page); - - if (ret) { - WARN_ON_ONCE(1); - SetPageError(page); - } else { - SetPageUptodate(page); - } - unlock_page(page); - } - fscrypt_release_ctx(ctx); - bio_put(bio); -} - -void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio) -{ - INIT_WORK(&ctx->r.work, completion_pages); - ctx->r.bio = bio; - queue_work(fscrypt_read_workqueue, &ctx->r.work); -} -EXPORT_SYMBOL(fscrypt_decrypt_bio_pages); - -void fscrypt_pullback_bio_page(struct page **page, bool restore) -{ - struct fscrypt_ctx *ctx; - struct page *bounce_page; - - /* The bounce data pages are unmapped. */ - if ((*page)->mapping) - return; - - /* The bounce data page is unmapped. */ - bounce_page = *page; - ctx = (struct fscrypt_ctx *)page_private(bounce_page); - - /* restore control page */ - *page = ctx->w.control_page; - - if (restore) - fscrypt_restore_control_page(bounce_page); -} -EXPORT_SYMBOL(fscrypt_pullback_bio_page); - void fscrypt_restore_control_page(struct page *page) { struct fscrypt_ctx *ctx; @@ -482,17 +400,22 @@ static void fscrypt_destroy(void) /** * fscrypt_initialize() - allocate major buffers for fs encryption. + * @cop_flags: fscrypt operations flags * * We only call this when we start accessing encrypted files, since it * results in memory getting allocated that wouldn't otherwise be used. * * Return: Zero on success, non-zero otherwise. */ -int fscrypt_initialize(void) +int fscrypt_initialize(unsigned int cop_flags) { int i, res = -ENOMEM; - if (fscrypt_bounce_page_pool) + /* + * No need to allocate a bounce page pool if there already is one or + * this FS won't use it. + */ + if (cop_flags & FS_CFLG_OWN_PAGES || fscrypt_bounce_page_pool) return 0; mutex_lock(&fscrypt_init_mutex); @@ -521,7 +444,6 @@ fail: mutex_unlock(&fscrypt_init_mutex); return res; } -EXPORT_SYMBOL(fscrypt_initialize); /** * fscrypt_init() - Set up for fs encryption. diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 3e907874840b..c7d8814c0e31 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -10,14 +10,9 @@ * This has not yet undergone a rigorous security audit. */ -#include -#include -#include -#include -#include #include #include -#include +#include "fscrypt_private.h" /** * fname_crypt_complete() - completion callback for filename crypto @@ -72,7 +67,7 @@ static int fname_encrypt(struct inode *inode, /* Initialize the IV */ memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); - /* Allocate request */ + /* Set up the encryption request */ req = ablkcipher_request_alloc(tfm, GFP_NOFS); if (!req) { printk_ratelimited(KERN_ERR @@ -82,7 +77,6 @@ static int fname_encrypt(struct inode *inode, ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, fname_crypt_complete, &ecr); - sg_init_one(&sg, oname->name, cryptlen); ablkcipher_request_set_crypt(req, &sg, &sg, cryptlen, iv); @@ -215,7 +209,7 @@ static int digest_decode(const char *src, int len, char *dst) return cp - dst; } -u32 fscrypt_fname_encrypted_size(struct inode *inode, u32 ilen) +u32 fscrypt_fname_encrypted_size(const struct inode *inode, u32 ilen) { int padding = 32; struct fscrypt_info *ci = inode->i_crypt_info; @@ -233,7 +227,7 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size); * Allocates an output buffer that is sufficient for the crypto operation * specified by the context and the direction. */ -int fscrypt_fname_alloc_buffer(struct inode *inode, +int fscrypt_fname_alloc_buffer(const struct inode *inode, u32 ilen, struct fscrypt_str *crypto_str) { unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen); @@ -338,7 +332,7 @@ int fscrypt_fname_usr_to_disk(struct inode *inode, * in a directory. Consequently, a user space name cannot be mapped to * a disk-space name */ - return -EACCES; + return -ENOKEY; } EXPORT_SYMBOL(fscrypt_fname_usr_to_disk); @@ -356,7 +350,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, fname->disk_name.len = iname->len; return 0; } - ret = get_crypt_info(dir); + ret = fscrypt_get_crypt_info(dir); if (ret && ret != -EOPNOTSUPP) return ret; @@ -373,7 +367,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, return 0; } if (!lookup) - return -EACCES; + return -ENOKEY; /* * We don't have the key and we are doing a lookup; decode the diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h new file mode 100644 index 000000000000..d26b6c03a2a1 --- /dev/null +++ b/fs/crypto/fscrypt_private.h @@ -0,0 +1,132 @@ +/* + * fscrypt_private.h + * + * Copyright (C) 2015, Google, Inc. + * + * This contains encryption key functions. + * + * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015. + */ + +#ifndef _FSCRYPT_PRIVATE_H +#define _FSCRYPT_PRIVATE_H + +#include + +#define FS_FNAME_CRYPTO_DIGEST_SIZE 32 + +/* Encryption parameters */ +#define FS_XTS_TWEAK_SIZE 16 +#define FS_AES_128_ECB_KEY_SIZE 16 +#define FS_AES_256_GCM_KEY_SIZE 32 +#define FS_AES_256_CBC_KEY_SIZE 32 +#define FS_AES_256_CTS_KEY_SIZE 32 +#define FS_AES_256_XTS_KEY_SIZE 64 +#define FS_MAX_KEY_SIZE 64 + +#define FS_KEY_DESC_PREFIX "fscrypt:" +#define FS_KEY_DESC_PREFIX_SIZE 8 + +#define FS_KEY_DERIVATION_NONCE_SIZE 16 + +/** + * Encryption context for inode + * + * Protector format: + * 1 byte: Protector format (1 = this version) + * 1 byte: File contents encryption mode + * 1 byte: File names encryption mode + * 1 byte: Flags + * 8 bytes: Master Key descriptor + * 16 bytes: Encryption Key derivation nonce + */ +struct fscrypt_context { + u8 format; + u8 contents_encryption_mode; + u8 filenames_encryption_mode; + u8 flags; + u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; + u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; +} __packed; + +#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 + +/* This is passed in from userspace into the kernel keyring */ +struct fscrypt_key { + u32 mode; + u8 raw[FS_MAX_KEY_SIZE]; + u32 size; +} __packed; + +/* + * A pointer to this structure is stored in the file system's in-core + * representation of an inode. + */ +struct fscrypt_info { + u8 ci_data_mode; + u8 ci_filename_mode; + u8 ci_flags; + struct crypto_ablkcipher *ci_ctfm; + struct key *ci_keyring_key; + u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE]; +}; + +typedef enum { + FS_DECRYPT = 0, + FS_ENCRYPT, +} fscrypt_direction_t; + +#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 +#define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002 + +struct fscrypt_completion_result { + struct completion completion; + int res; +}; + +#define DECLARE_FS_COMPLETION_RESULT(ecr) \ + struct fscrypt_completion_result ecr = { \ + COMPLETION_INITIALIZER_ONSTACK((ecr).completion), 0 } + +static inline void inode_lock(struct inode *inode) +{ + mutex_lock(&inode->i_mutex); +} + +static inline void inode_unlock(struct inode *inode) +{ + mutex_unlock(&inode->i_mutex); +} + +static inline const struct user_key_payload *user_key_payload(const struct key *key) +{ + return (struct user_key_payload *)rcu_dereference_key(key); +} + +/* bio stuffs */ +#define REQ_OP_READ READ +#define REQ_OP_WRITE WRITE +#define bio_op(bio) ((bio)->bi_rw & 1) + +static inline void bio_set_op_attrs(struct bio *bio, unsigned op, + unsigned op_flags) +{ + bio->bi_rw = op | op_flags; +} + +/* crypto.c */ +extern int fscrypt_initialize(unsigned int cop_flags); +extern struct workqueue_struct *fscrypt_read_workqueue; +extern int fscrypt_do_page_crypto(const struct inode *inode, + fscrypt_direction_t rw, u64 lblk_num, + struct page *src_page, + struct page *dest_page, + unsigned int len, unsigned int offs, + gfp_t gfp_flags); +extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, + gfp_t gfp_flags); + +/* keyinfo.c */ +extern int fscrypt_get_crypt_info(struct inode *); + +#endif /* _FSCRYPT_PRIVATE_H */ diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index cff4641941fb..54e49dbf32f0 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -10,9 +10,7 @@ #include #include -#include -#include -#include +#include "fscrypt_private.h" static void derive_crypt_complete(struct crypto_async_request *req, int rc) { @@ -41,8 +39,7 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], struct ablkcipher_request *req = NULL; DECLARE_FS_COMPLETION_RESULT(ecr); struct scatterlist src_sg, dst_sg; - struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, - 0); + struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, 0); if (IS_ERR(tfm)) { res = PTR_ERR(tfm); @@ -82,26 +79,33 @@ out: static int validate_user_key(struct fscrypt_info *crypt_info, struct fscrypt_context *ctx, u8 *raw_key, - u8 *prefix, int prefix_size) + const char *prefix) { - u8 *full_key_descriptor; + char *description; struct key *keyring_key; struct fscrypt_key *master_key; const struct user_key_payload *ukp; + int prefix_size = strlen(prefix); int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1; int res; - full_key_descriptor = kmalloc(full_key_len, GFP_NOFS); - if (!full_key_descriptor) + /* FIXME: 3.18 causes kernel panic. + description = kasprintf(GFP_NOFS, "%s%*phN", prefix, + FS_KEY_DESCRIPTOR_SIZE, + ctx->master_key_descriptor); + */ + description = kmalloc(full_key_len, GFP_NOFS); + if (!description) return -ENOMEM; - memcpy(full_key_descriptor, prefix, prefix_size); - sprintf(full_key_descriptor + prefix_size, + memcpy(description, prefix, prefix_size); + sprintf(description + prefix_size, "%*phN", FS_KEY_DESCRIPTOR_SIZE, ctx->master_key_descriptor); - full_key_descriptor[full_key_len - 1] = '\0'; - keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); - kfree(full_key_descriptor); + description[full_key_len - 1] = '\0'; + + keyring_key = request_key(&key_type_logon, description, NULL); + kfree(description); if (IS_ERR(keyring_key)) return PTR_ERR(keyring_key); @@ -112,7 +116,7 @@ static int validate_user_key(struct fscrypt_info *crypt_info, goto out; } down_read(&keyring_key->sem); - ukp = ((struct user_key_payload *)keyring_key->payload.data); + ukp = user_key_payload(keyring_key); if (ukp->datalen != sizeof(struct fscrypt_key)) { res = -EINVAL; up_read(&keyring_key->sem); @@ -184,7 +188,7 @@ static void put_crypt_info(struct fscrypt_info *ci) kmem_cache_free(fscrypt_info_cachep, ci); } -int get_crypt_info(struct inode *inode) +int fscrypt_get_crypt_info(struct inode *inode) { struct fscrypt_info *crypt_info; struct fscrypt_context ctx; @@ -194,7 +198,7 @@ int get_crypt_info(struct inode *inode) u8 *raw_key = NULL; int res; - res = fscrypt_initialize(); + res = fscrypt_initialize(inode->i_sb->s_cop->flags); if (res) return res; @@ -212,12 +216,15 @@ retry: res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); if (res < 0) { - if (!fscrypt_dummy_context_enabled(inode)) + if (!fscrypt_dummy_context_enabled(inode) || + inode->i_sb->s_cop->is_encrypted(inode)) return res; + /* Fake up a context for an unencrypted directory */ + memset(&ctx, 0, sizeof(ctx)); ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS; ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; - ctx.flags = 0; + memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE); } else if (res != sizeof(ctx)) { return -EINVAL; } @@ -253,20 +260,10 @@ retry: if (!raw_key) goto out; - if (fscrypt_dummy_context_enabled(inode)) { - memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE); - goto got_key; - } - - res = validate_user_key(crypt_info, &ctx, raw_key, - FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE); + res = validate_user_key(crypt_info, &ctx, raw_key, FS_KEY_DESC_PREFIX); if (res && inode->i_sb->s_cop->key_prefix) { - u8 *prefix = NULL; - int prefix_size, res2; - - prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix); - res2 = validate_user_key(crypt_info, &ctx, raw_key, - prefix, prefix_size); + int res2 = validate_user_key(crypt_info, &ctx, raw_key, + inode->i_sb->s_cop->key_prefix); if (res2) { if (res2 == -ENOKEY) res = -ENOKEY; @@ -275,7 +272,6 @@ retry: } else if (res) { goto out; } -got_key: ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); if (!ctfm || IS_ERR(ctfm)) { res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; @@ -333,7 +329,7 @@ int fscrypt_get_encryption_info(struct inode *inode) (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | (1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_DEAD))))) - return get_crypt_info(inode); + return fscrypt_get_crypt_info(inode); return 0; } EXPORT_SYMBOL(fscrypt_get_encryption_info); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index c8e239f01540..14b76da71269 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -10,50 +10,23 @@ #include #include -#include #include - -static inline void inode_lock(struct inode *inode) -{ - mutex_lock(&inode->i_mutex); -} - -static inline void inode_unlock(struct inode *inode) -{ - mutex_unlock(&inode->i_mutex); -} - -static int inode_has_encryption_context(struct inode *inode) -{ - if (!inode->i_sb->s_cop->get_context) - return 0; - return (inode->i_sb->s_cop->get_context(inode, NULL, 0L) > 0); -} +#include "fscrypt_private.h" /* - * check whether the policy is consistent with the encryption context - * for the inode + * check whether an encryption policy is consistent with an encryption context */ -static int is_encryption_context_consistent_with_policy(struct inode *inode, +static bool is_encryption_context_consistent_with_policy( + const struct fscrypt_context *ctx, const struct fscrypt_policy *policy) { - struct fscrypt_context ctx; - int res; - - if (!inode->i_sb->s_cop->get_context) - return 0; - - res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); - if (res != sizeof(ctx)) - return 0; - - return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor, - FS_KEY_DESCRIPTOR_SIZE) == 0 && - (ctx.flags == policy->flags) && - (ctx.contents_encryption_mode == - policy->contents_encryption_mode) && - (ctx.filenames_encryption_mode == - policy->filenames_encryption_mode)); + return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor, + FS_KEY_DESCRIPTOR_SIZE) == 0 && + (ctx->flags == policy->flags) && + (ctx->contents_encryption_mode == + policy->contents_encryption_mode) && + (ctx->filenames_encryption_mode == + policy->filenames_encryption_mode); } static int create_encryption_context_from_policy(struct inode *inode, @@ -76,20 +49,12 @@ static int create_encryption_context_from_policy(struct inode *inode, FS_KEY_DESCRIPTOR_SIZE); if (!fscrypt_valid_contents_enc_mode( - policy->contents_encryption_mode)) { - printk(KERN_WARNING - "%s: Invalid contents encryption mode %d\n", __func__, - policy->contents_encryption_mode); + policy->contents_encryption_mode)) return -EINVAL; - } if (!fscrypt_valid_filenames_enc_mode( - policy->filenames_encryption_mode)) { - printk(KERN_WARNING - "%s: Invalid filenames encryption mode %d\n", __func__, - policy->filenames_encryption_mode); + policy->filenames_encryption_mode)) return -EINVAL; - } if (policy->flags & ~FS_POLICY_FLAGS_VALID) return -EINVAL; @@ -108,6 +73,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) struct fscrypt_policy policy; struct inode *inode = file_inode(filp); int ret; + struct fscrypt_context ctx; if (copy_from_user(&policy, arg, sizeof(policy))) return -EFAULT; @@ -124,9 +90,10 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) inode_lock(inode); - if (!inode_has_encryption_context(inode)) { + ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); + if (ret == -ENODATA) { if (!S_ISDIR(inode->i_mode)) - ret = -EINVAL; + ret = -ENOTDIR; else if (!inode->i_sb->s_cop->empty_dir) ret = -EOPNOTSUPP; else if (!inode->i_sb->s_cop->empty_dir(inode)) @@ -134,12 +101,14 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) else ret = create_encryption_context_from_policy(inode, &policy); - } else if (!is_encryption_context_consistent_with_policy(inode, - &policy)) { - printk(KERN_WARNING - "%s: Policy inconsistent with encryption context\n", - __func__); - ret = -EINVAL; + } else if (ret == sizeof(ctx) && + is_encryption_context_consistent_with_policy(&ctx, + &policy)) { + /* The file already uses the same encryption policy. */ + ret = 0; + } else if (ret >= 0 || ret == -ERANGE) { + /* The file already uses a different encryption policy. */ + ret = -EEXIST; } inode_unlock(inode); @@ -161,8 +130,10 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) return -ENODATA; res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); + if (res < 0 && res != -ERANGE) + return res; if (res != sizeof(ctx)) - return -ENODATA; + return -EINVAL; if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) return -EINVAL; @@ -227,9 +198,9 @@ EXPORT_SYMBOL(fscrypt_has_permitted_context); * @parent: Parent inode from which the context is inherited. * @child: Child inode that inherits the context from @parent. * @fs_data: private data given by FS. - * @preload: preload child i_crypt_info + * @preload: preload child i_crypt_info if true * - * Return: Zero on success, non-zero otherwise + * Return: 0 on success, -errno on failure */ int fscrypt_inherit_context(struct inode *parent, struct inode *child, void *fs_data, bool preload) @@ -250,19 +221,11 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child, return -ENOKEY; ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; - if (fscrypt_dummy_context_enabled(parent)) { - ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS; - ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; - ctx.flags = 0; - memset(ctx.master_key_descriptor, 0x42, FS_KEY_DESCRIPTOR_SIZE); - res = 0; - } else { - ctx.contents_encryption_mode = ci->ci_data_mode; - ctx.filenames_encryption_mode = ci->ci_filename_mode; - ctx.flags = ci->ci_flags; - memcpy(ctx.master_key_descriptor, ci->ci_master_key, - FS_KEY_DESCRIPTOR_SIZE); - } + ctx.contents_encryption_mode = ci->ci_data_mode; + ctx.filenames_encryption_mode = ci->ci_filename_mode; + ctx.flags = ci->ci_flags; + memcpy(ctx.master_key_descriptor, ci->ci_master_key, + FS_KEY_DESCRIPTOR_SIZE); get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); res = parent->i_sb->s_cop->set_context(child, &ctx, sizeof(ctx), fs_data); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f1088c59b936..7d51648ba261 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1307,7 +1307,9 @@ int do_write_data_page(struct f2fs_io_info *fio) fio->old_blkaddr); retry_encrypt: fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, - gfp_flags); + PAGE_SIZE, 0, + fio->page->index, + gfp_flags); if (IS_ERR(fio->encrypted_page)) { err = PTR_ERR(fio->encrypted_page); if (err == -ENOMEM) { diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 212e1c6a4ee6..8525f609b293 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -272,7 +272,10 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, err = fscrypt_setup_filename(dir, child, 1, &fname); if (err) { - *res_page = ERR_PTR(err); + if (err == -ENOENT) + *res_page = NULL; + else + *res_page = ERR_PTR(err); return NULL; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ade4245985b8..e3f6603351db 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -22,7 +22,11 @@ #include #include #include -#include +#ifdef CONFIG_F2FS_FS_ENCRYPTION +#include +#else +#include +#endif #include #include @@ -887,10 +891,6 @@ enum { MAX_TIME, }; -#ifdef CONFIG_F2FS_FS_ENCRYPTION -#define F2FS_KEY_DESC_PREFIX "f2fs:" -#define F2FS_KEY_DESC_PREFIX_SIZE 5 -#endif struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -898,11 +898,6 @@ struct f2fs_sb_info { int valid_super_block; /* valid super block no */ unsigned long s_flag; /* flags for sbi */ -#ifdef CONFIG_F2FS_FS_ENCRYPTION - u8 key_prefix[F2FS_KEY_DESC_PREFIX_SIZE]; - u8 key_prefix_size; -#endif - #ifdef CONFIG_BLK_DEV_ZONED unsigned int blocks_per_blkz; /* F2FS blocks per zone */ unsigned int log_blocks_per_blkz; /* log2 F2FS blocks per zone */ @@ -2739,29 +2734,4 @@ static inline bool f2fs_may_encrypt(struct inode *inode) return 0; #endif } - -#ifndef CONFIG_F2FS_FS_ENCRYPTION -#define fscrypt_set_d_op(i) -#define fscrypt_get_ctx fscrypt_notsupp_get_ctx -#define fscrypt_release_ctx fscrypt_notsupp_release_ctx -#define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page -#define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page -#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages -#define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page -#define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page -#define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range -#define fscrypt_ioctl_set_policy fscrypt_notsupp_ioctl_set_policy -#define fscrypt_ioctl_get_policy fscrypt_notsupp_ioctl_get_policy -#define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context -#define fscrypt_inherit_context fscrypt_notsupp_inherit_context -#define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info -#define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info -#define fscrypt_setup_filename fscrypt_notsupp_setup_filename -#define fscrypt_free_filename fscrypt_notsupp_free_filename -#define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size -#define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer -#define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer -#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr -#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk -#endif #endif diff --git a/fs/f2fs/key.c b/fs/f2fs/key.c deleted file mode 100644 index 948f5bc1660f..000000000000 --- a/fs/f2fs/key.c +++ /dev/null @@ -1,34 +0,0 @@ -/* fill fi->keyring_key */ -int f2fs_validate_key(struct inode *inode) -{ - u8 full_key_descriptor[FS_KEY_DESC_PREFIX_SIZE + - (FS_KEY_DESCRIPTOR_SIZE * 2) + 1]; - struct key *keyring_key = NULL; - u8 key[F2FS_SET_KEY_SIZE]; - int ret; - - ret = f2fs_getxattr(inode, F2FS_XATTR_INDEX_KEY, - F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, - key, F2FS_SET_KEY_SIZE, NULL); - if (ret) - return ret; - - memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX, - F2FS_KEY_DESC_PREFIX_SIZE); - sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE, - "%*phN", F2FS_KEY_DESCRIPTOR_SIZE, key); - full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + - (2 * F2FS_KEY_DESCRIPTOR_SIZE)] = '\0'; - keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); - if (IS_ERR(keyring_key)) - return PTR_ERR(keyring_key); - - if (keyring_key->type != &key_type_logon) { - printk_once(KERN_WARNING - "%s: key type must be logon\n", __func__); - key_put(keyring_key); - return -ENOKEY; - } - key_put(keyring_key); - return 0; -} diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 17b7ead25115..bf6ec7efc743 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -412,7 +412,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, return err; if (!fscrypt_has_encryption_key(dir)) - return -EPERM; + return -ENOKEY; disk_link.len = (fscrypt_fname_encrypted_size(dir, len) + sizeof(struct fscrypt_symlink_data)); @@ -456,7 +456,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, goto err_out; if (!fscrypt_has_encryption_key(inode)) { - err = -EPERM; + err = -ENOKEY; goto err_out; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0bde614cbf3a..ac673b233640 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1192,12 +1192,6 @@ static int f2fs_get_context(struct inode *inode, void *ctx, size_t len) ctx, len, NULL); } -static int f2fs_key_prefix(struct inode *inode, u8 **key) -{ - *key = F2FS_I_SB(inode)->key_prefix; - return F2FS_I_SB(inode)->key_prefix_size; -} - static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len, void *fs_data) { @@ -1213,8 +1207,8 @@ static unsigned f2fs_max_namelen(struct inode *inode) } static const struct fscrypt_operations f2fs_cryptops = { + .key_prefix = "f2fs:", .get_context = f2fs_get_context, - .key_prefix = f2fs_key_prefix, .set_context = f2fs_set_context, .is_encrypted = f2fs_encrypted_inode, .empty_dir = f2fs_empty_dir, @@ -1554,12 +1548,6 @@ static void init_sb_info(struct f2fs_sb_info *sbi) mutex_init(&sbi->wio_mutex[NODE]); mutex_init(&sbi->wio_mutex[DATA]); spin_lock_init(&sbi->cp_lock); - -#ifdef CONFIG_F2FS_FS_ENCRYPTION - memcpy(sbi->key_prefix, F2FS_KEY_DESC_PREFIX, - F2FS_KEY_DESC_PREFIX_SIZE); - sbi->key_prefix_size = F2FS_KEY_DESC_PREFIX_SIZE; -#endif } static int init_percpu_info(struct f2fs_sb_info *sbi) diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt_common.h new file mode 100644 index 000000000000..547f81592ba1 --- /dev/null +++ b/include/linux/fscrypt_common.h @@ -0,0 +1,146 @@ +/* + * fscrypt_common.h: common declarations for per-file encryption + * + * Copyright (C) 2015, Google, Inc. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ + +#ifndef _LINUX_FSCRYPT_COMMON_H +#define _LINUX_FSCRYPT_COMMON_H + +#include +#include +#include +#include +#include +#include +#include + +#define FS_CRYPTO_BLOCK_SIZE 16 + +struct fscrypt_info; + +struct fscrypt_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + u8 flags; /* Flags */ +}; + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct fscrypt_symlink_data { + __le16 len; + char encrypted_path[1]; +} __packed; + +/** + * This function is used to calculate the disk space required to + * store a filename of length l in encrypted symlink format. + */ +static inline u32 fscrypt_symlink_data_len(u32 l) +{ + if (l < FS_CRYPTO_BLOCK_SIZE) + l = FS_CRYPTO_BLOCK_SIZE; + return (l + sizeof(struct fscrypt_symlink_data) - 1); +} + +struct fscrypt_str { + unsigned char *name; + u32 len; +}; + +struct fscrypt_name { + const struct qstr *usr_fname; + struct fscrypt_str disk_name; + u32 hash; + u32 minor_hash; + struct fscrypt_str crypto_buf; +}; + +#define FSTR_INIT(n, l) { .name = n, .len = l } +#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) + +/* + * fscrypt superblock flags + */ +#define FS_CFLG_OWN_PAGES (1U << 1) + +/* + * crypto opertions for filesystems + */ +struct fscrypt_operations { + unsigned int flags; + const char *key_prefix; + int (*get_context)(struct inode *, void *, size_t); + int (*prepare_context)(struct inode *); + int (*set_context)(struct inode *, const void *, size_t, void *); + int (*dummy_context)(struct inode *); + bool (*is_encrypted)(struct inode *); + bool (*empty_dir)(struct inode *); + unsigned (*max_namelen)(struct inode *); +}; + +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + if (inode->i_sb->s_cop->dummy_context && + inode->i_sb->s_cop->dummy_context(inode)) + return true; + return false; +} + +static inline bool fscrypt_valid_contents_enc_mode(u32 mode) +{ + return (mode == FS_ENCRYPTION_MODE_AES_256_XTS); +} + +static inline bool fscrypt_valid_filenames_enc_mode(u32 mode) +{ + return (mode == FS_ENCRYPTION_MODE_AES_256_CTS); +} + +static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) +{ + if (str->len == 1 && str->name[0] == '.') + return true; + + if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') + return true; + + return false; +} + +static inline struct page *fscrypt_control_page(struct page *page) +{ +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + return ((struct fscrypt_ctx *)page_private(page))->w.control_page; +#else + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +#endif +} + +static inline int fscrypt_has_encryption_key(const struct inode *inode) +{ +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + return (inode->i_crypt_info != NULL); +#else + return 0; +#endif +} + +#endif /* _LINUX_FSCRYPT_COMMON_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h new file mode 100644 index 000000000000..3511ca798804 --- /dev/null +++ b/include/linux/fscrypt_notsupp.h @@ -0,0 +1,168 @@ +/* + * fscrypt_notsupp.h + * + * This stubs out the fscrypt functions for filesystems configured without + * encryption support. + */ + +#ifndef _LINUX_FSCRYPT_NOTSUPP_H +#define _LINUX_FSCRYPT_NOTSUPP_H + +#include + +/* crypto.c */ +static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, + gfp_t gfp_flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void fscrypt_release_ctx(struct fscrypt_ctx *ctx) +{ + return; +} + +static inline struct page *fscrypt_encrypt_page(const struct inode *inode, + struct page *page, + unsigned int len, + unsigned int offs, + u64 lblk_num, gfp_t gfp_flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int fscrypt_decrypt_page(const struct inode *inode, + struct page *page, + unsigned int len, unsigned int offs, + u64 lblk_num) +{ + return -EOPNOTSUPP; +} + + +static inline void fscrypt_restore_control_page(struct page *page) +{ + return; +} + +static inline void fscrypt_set_d_op(struct dentry *dentry) +{ + return; +} + +static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry) +{ + return; +} + +/* policy.c */ +static inline int fscrypt_ioctl_set_policy(struct file *filp, + const void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_has_permitted_context(struct inode *parent, + struct inode *child) +{ + return 0; +} + +static inline int fscrypt_inherit_context(struct inode *parent, + struct inode *child, + void *fs_data, bool preload) +{ + return -EOPNOTSUPP; +} + +/* keyinfo.c */ +static inline int fscrypt_get_encryption_info(struct inode *inode) +{ + return -EOPNOTSUPP; +} + +static inline void fscrypt_put_encryption_info(struct inode *inode, + struct fscrypt_info *ci) +{ + return; +} + + /* fname.c */ +static inline int fscrypt_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct fscrypt_name *fname) +{ + if (dir->i_sb->s_cop->is_encrypted(dir)) + return -EOPNOTSUPP; + + memset(fname, 0, sizeof(struct fscrypt_name)); + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; +} + +static inline void fscrypt_free_filename(struct fscrypt_name *fname) +{ + return; +} + +static inline u32 fscrypt_fname_encrypted_size(const struct inode *inode, + u32 ilen) +{ + /* never happens */ + WARN_ON(1); + return 0; +} + +static inline int fscrypt_fname_alloc_buffer(const struct inode *inode, + u32 ilen, + struct fscrypt_str *crypto_str) +{ + return -EOPNOTSUPP; +} + +static inline void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str) +{ + return; +} + +static inline int fscrypt_fname_disk_to_usr(struct inode *inode, + u32 hash, u32 minor_hash, + const struct fscrypt_str *iname, + struct fscrypt_str *oname) +{ + return -EOPNOTSUPP; +} + +static inline int fscrypt_fname_usr_to_disk(struct inode *inode, + const struct qstr *iname, + struct fscrypt_str *oname) +{ + return -EOPNOTSUPP; +} + +/* bio.c */ +static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, + struct bio *bio) +{ + return; +} + +static inline void fscrypt_pullback_bio_page(struct page **page, bool restore) +{ + return; +} + +static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, + sector_t pblk, unsigned int len) +{ + return -EOPNOTSUPP; +} + +#endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h new file mode 100644 index 000000000000..a140f47e9b27 --- /dev/null +++ b/include/linux/fscrypt_supp.h @@ -0,0 +1,66 @@ +/* + * fscrypt_supp.h + * + * This is included by filesystems configured with encryption support. + */ + +#ifndef _LINUX_FSCRYPT_SUPP_H +#define _LINUX_FSCRYPT_SUPP_H + +#include + +/* crypto.c */ +extern struct kmem_cache *fscrypt_info_cachep; +extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); +extern void fscrypt_release_ctx(struct fscrypt_ctx *); +extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, + unsigned int, unsigned int, + u64, gfp_t); +extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, + unsigned int, u64); +extern void fscrypt_restore_control_page(struct page *); + +extern const struct dentry_operations fscrypt_d_ops; + +static inline void fscrypt_set_d_op(struct dentry *dentry) +{ + d_set_d_op(dentry, &fscrypt_d_ops); +} + +static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY; + spin_unlock(&dentry->d_lock); +} + +/* policy.c */ +extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); +extern int fscrypt_ioctl_get_policy(struct file *, void __user *); +extern int fscrypt_has_permitted_context(struct inode *, struct inode *); +extern int fscrypt_inherit_context(struct inode *, struct inode *, + void *, bool); +/* keyinfo.c */ +extern int fscrypt_get_encryption_info(struct inode *); +extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); + +/* fname.c */ +extern int fscrypt_setup_filename(struct inode *, const struct qstr *, + int lookup, struct fscrypt_name *); +extern void fscrypt_free_filename(struct fscrypt_name *); +extern u32 fscrypt_fname_encrypted_size(const struct inode *, u32); +extern int fscrypt_fname_alloc_buffer(const struct inode *, u32, + struct fscrypt_str *); +extern void fscrypt_fname_free_buffer(struct fscrypt_str *); +extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, + const struct fscrypt_str *, struct fscrypt_str *); +extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, + struct fscrypt_str *); + +/* bio.c */ +extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); +extern void fscrypt_pullback_bio_page(struct page **, bool); +extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, + unsigned int); + +#endif /* _LINUX_FSCRYPT_SUPP_H */ diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h deleted file mode 100644 index d6c716566f1a..000000000000 --- a/include/linux/fscrypto.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * General per-file encryption definition - * - * Copyright (C) 2015, Google, Inc. - * - * Written by Michael Halcrow, 2015. - * Modified by Jaegeuk Kim, 2015. - */ - -#ifndef _LINUX_FSCRYPTO_H -#define _LINUX_FSCRYPTO_H - -#include -#include -#include -#include -#include -#include - -#define FS_KEY_DERIVATION_NONCE_SIZE 16 -#define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 - -#define FS_POLICY_FLAGS_PAD_4 0x00 -#define FS_POLICY_FLAGS_PAD_8 0x01 -#define FS_POLICY_FLAGS_PAD_16 0x02 -#define FS_POLICY_FLAGS_PAD_32 0x03 -#define FS_POLICY_FLAGS_PAD_MASK 0x03 -#define FS_POLICY_FLAGS_VALID 0x03 - -/* Encryption algorithms */ -#define FS_ENCRYPTION_MODE_INVALID 0 -#define FS_ENCRYPTION_MODE_AES_256_XTS 1 -#define FS_ENCRYPTION_MODE_AES_256_GCM 2 -#define FS_ENCRYPTION_MODE_AES_256_CBC 3 -#define FS_ENCRYPTION_MODE_AES_256_CTS 4 - -/** - * Encryption context for inode - * - * Protector format: - * 1 byte: Protector format (1 = this version) - * 1 byte: File contents encryption mode - * 1 byte: File names encryption mode - * 1 byte: Flags - * 8 bytes: Master Key descriptor - * 16 bytes: Encryption Key derivation nonce - */ -struct fscrypt_context { - u8 format; - u8 contents_encryption_mode; - u8 filenames_encryption_mode; - u8 flags; - u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; - u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; -} __packed; - -/* Encryption parameters */ -#define FS_XTS_TWEAK_SIZE 16 -#define FS_AES_128_ECB_KEY_SIZE 16 -#define FS_AES_256_GCM_KEY_SIZE 32 -#define FS_AES_256_CBC_KEY_SIZE 32 -#define FS_AES_256_CTS_KEY_SIZE 32 -#define FS_AES_256_XTS_KEY_SIZE 64 -#define FS_MAX_KEY_SIZE 64 - -#define FS_KEY_DESC_PREFIX "fscrypt:" -#define FS_KEY_DESC_PREFIX_SIZE 8 - -/* This is passed in from userspace into the kernel keyring */ -struct fscrypt_key { - u32 mode; - u8 raw[FS_MAX_KEY_SIZE]; - u32 size; -} __packed; - -struct fscrypt_info { - u8 ci_data_mode; - u8 ci_filename_mode; - u8 ci_flags; - struct crypto_ablkcipher *ci_ctfm; - struct key *ci_keyring_key; - u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE]; -}; - -#define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 -#define FS_WRITE_PATH_FL 0x00000002 - -struct fscrypt_ctx { - union { - struct { - struct page *bounce_page; /* Ciphertext page */ - struct page *control_page; /* Original page */ - } w; - struct { - struct bio *bio; - struct work_struct work; - } r; - struct list_head free_list; /* Free list */ - }; - u8 flags; /* Flags */ - u8 mode; /* Encryption mode for tfm */ -}; - -struct fscrypt_completion_result { - struct completion completion; - int res; -}; - -#define DECLARE_FS_COMPLETION_RESULT(ecr) \ - struct fscrypt_completion_result ecr = { \ - COMPLETION_INITIALIZER((ecr).completion), 0 } - -#define FS_FNAME_NUM_SCATTER_ENTRIES 4 -#define FS_CRYPTO_BLOCK_SIZE 16 -#define FS_FNAME_CRYPTO_DIGEST_SIZE 32 - -/** - * For encrypted symlinks, the ciphertext length is stored at the beginning - * of the string in little-endian format. - */ -struct fscrypt_symlink_data { - __le16 len; - char encrypted_path[1]; -} __packed; - -/** - * This function is used to calculate the disk space required to - * store a filename of length l in encrypted symlink format. - */ -static inline u32 fscrypt_symlink_data_len(u32 l) -{ - if (l < FS_CRYPTO_BLOCK_SIZE) - l = FS_CRYPTO_BLOCK_SIZE; - return (l + sizeof(struct fscrypt_symlink_data) - 1); -} - -struct fscrypt_str { - unsigned char *name; - u32 len; -}; - -struct fscrypt_name { - const struct qstr *usr_fname; - struct fscrypt_str disk_name; - u32 hash; - u32 minor_hash; - struct fscrypt_str crypto_buf; -}; - -#define FSTR_INIT(n, l) { .name = n, .len = l } -#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) -#define fname_name(p) ((p)->disk_name.name) -#define fname_len(p) ((p)->disk_name.len) - -/* - * crypto opertions for filesystems - */ -struct fscrypt_operations { - int (*get_context)(struct inode *, void *, size_t); - int (*key_prefix)(struct inode *, u8 **); - int (*prepare_context)(struct inode *); - int (*set_context)(struct inode *, const void *, size_t, void *); - int (*dummy_context)(struct inode *); - bool (*is_encrypted)(struct inode *); - bool (*empty_dir)(struct inode *); - unsigned (*max_namelen)(struct inode *); -}; - -static inline bool fscrypt_dummy_context_enabled(struct inode *inode) -{ - if (inode->i_sb->s_cop->dummy_context && - inode->i_sb->s_cop->dummy_context(inode)) - return true; - return false; -} - -static inline bool fscrypt_valid_contents_enc_mode(u32 mode) -{ - return (mode == FS_ENCRYPTION_MODE_AES_256_XTS); -} - -static inline bool fscrypt_valid_filenames_enc_mode(u32 mode) -{ - return (mode == FS_ENCRYPTION_MODE_AES_256_CTS); -} - -static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) -{ - if (str->len == 1 && str->name[0] == '.') - return true; - - if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') - return true; - - return false; -} - -static inline struct page *fscrypt_control_page(struct page *page) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - return ((struct fscrypt_ctx *)page_private(page))->w.control_page; -#else - WARN_ON_ONCE(1); - return ERR_PTR(-EINVAL); -#endif -} - -static inline int fscrypt_has_encryption_key(struct inode *inode) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - return (inode->i_crypt_info != NULL); -#else - return 0; -#endif -} - -static inline void fscrypt_set_encrypted_dentry(struct dentry *dentry) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY; - spin_unlock(&dentry->d_lock); -#endif -} - -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) -extern const struct dentry_operations fscrypt_d_ops; -#endif - -static inline void fscrypt_set_d_op(struct dentry *dentry) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - d_set_d_op(dentry, &fscrypt_d_ops); -#endif -} - -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) -/* crypto.c */ -extern struct kmem_cache *fscrypt_info_cachep; -int fscrypt_initialize(void); - -extern struct fscrypt_ctx *fscrypt_get_ctx(struct inode *, gfp_t); -extern void fscrypt_release_ctx(struct fscrypt_ctx *); -extern struct page *fscrypt_encrypt_page(struct inode *, struct page *, gfp_t); -extern int fscrypt_decrypt_page(struct page *); -extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); -extern void fscrypt_pullback_bio_page(struct page **, bool); -extern void fscrypt_restore_control_page(struct page *); -extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t, - unsigned int); -/* policy.c */ -extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); -extern int fscrypt_ioctl_get_policy(struct file *, void __user *); -extern int fscrypt_has_permitted_context(struct inode *, struct inode *); -extern int fscrypt_inherit_context(struct inode *, struct inode *, - void *, bool); -/* keyinfo.c */ -extern int get_crypt_info(struct inode *); -extern int fscrypt_get_encryption_info(struct inode *); -extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); - -/* fname.c */ -extern int fscrypt_setup_filename(struct inode *, const struct qstr *, - int lookup, struct fscrypt_name *); -extern void fscrypt_free_filename(struct fscrypt_name *); -extern u32 fscrypt_fname_encrypted_size(struct inode *, u32); -extern int fscrypt_fname_alloc_buffer(struct inode *, u32, - struct fscrypt_str *); -extern void fscrypt_fname_free_buffer(struct fscrypt_str *); -extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, - const struct fscrypt_str *, struct fscrypt_str *); -extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, - struct fscrypt_str *); -#endif - -/* crypto.c */ -static inline struct fscrypt_ctx *fscrypt_notsupp_get_ctx(struct inode *i, - gfp_t f) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void fscrypt_notsupp_release_ctx(struct fscrypt_ctx *c) -{ - return; -} - -static inline struct page *fscrypt_notsupp_encrypt_page(struct inode *i, - struct page *p, gfp_t f) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline int fscrypt_notsupp_decrypt_page(struct page *p) -{ - return -EOPNOTSUPP; -} - -static inline void fscrypt_notsupp_decrypt_bio_pages(struct fscrypt_ctx *c, - struct bio *b) -{ - return; -} - -static inline void fscrypt_notsupp_pullback_bio_page(struct page **p, bool b) -{ - return; -} - -static inline void fscrypt_notsupp_restore_control_page(struct page *p) -{ - return; -} - -static inline int fscrypt_notsupp_zeroout_range(struct inode *i, pgoff_t p, - sector_t s, unsigned int f) -{ - return -EOPNOTSUPP; -} - -/* policy.c */ -static inline int fscrypt_notsupp_ioctl_set_policy(struct file *f, - const void __user *arg) -{ - return -EOPNOTSUPP; -} - -static inline int fscrypt_notsupp_ioctl_get_policy(struct file *f, - void __user *arg) -{ - return -EOPNOTSUPP; -} - -static inline int fscrypt_notsupp_has_permitted_context(struct inode *p, - struct inode *i) -{ - return 0; -} - -static inline int fscrypt_notsupp_inherit_context(struct inode *p, - struct inode *i, void *v, bool b) -{ - return -EOPNOTSUPP; -} - -/* keyinfo.c */ -static inline int fscrypt_notsupp_get_encryption_info(struct inode *i) -{ - return -EOPNOTSUPP; -} - -static inline void fscrypt_notsupp_put_encryption_info(struct inode *i, - struct fscrypt_info *f) -{ - return; -} - - /* fname.c */ -static inline int fscrypt_notsupp_setup_filename(struct inode *dir, - const struct qstr *iname, - int lookup, struct fscrypt_name *fname) -{ - if (dir->i_sb->s_cop->is_encrypted(dir)) - return -EOPNOTSUPP; - - memset(fname, 0, sizeof(struct fscrypt_name)); - fname->usr_fname = iname; - fname->disk_name.name = (unsigned char *)iname->name; - fname->disk_name.len = iname->len; - return 0; -} - -static inline void fscrypt_notsupp_free_filename(struct fscrypt_name *fname) -{ - return; -} - -static inline u32 fscrypt_notsupp_fname_encrypted_size(struct inode *i, u32 s) -{ - /* never happens */ - WARN_ON(1); - return 0; -} - -static inline int fscrypt_notsupp_fname_alloc_buffer(struct inode *inode, - u32 ilen, struct fscrypt_str *crypto_str) -{ - return -EOPNOTSUPP; -} - -static inline void fscrypt_notsupp_fname_free_buffer(struct fscrypt_str *c) -{ - return; -} - -static inline int fscrypt_notsupp_fname_disk_to_usr(struct inode *inode, - u32 hash, u32 minor_hash, - const struct fscrypt_str *iname, - struct fscrypt_str *oname) -{ - return -EOPNOTSUPP; -} - -static inline int fscrypt_notsupp_fname_usr_to_disk(struct inode *inode, - const struct qstr *iname, - struct fscrypt_str *oname) -{ - return -EOPNOTSUPP; -} -#endif /* _LINUX_FSCRYPTO_H */ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 7ab5efce3a58..2c86c2fd4934 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -174,6 +174,21 @@ struct inodes_stat_t { /* Policy provided via an ioctl on the topmost directory */ #define FS_KEY_DESCRIPTOR_SIZE 8 +#define FS_POLICY_FLAGS_PAD_4 0x00 +#define FS_POLICY_FLAGS_PAD_8 0x01 +#define FS_POLICY_FLAGS_PAD_16 0x02 +#define FS_POLICY_FLAGS_PAD_32 0x03 +#define FS_POLICY_FLAGS_PAD_MASK 0x03 +#define FS_POLICY_FLAGS_VALID 0x03 + +/* Encryption algorithms */ +#define FS_ENCRYPTION_MODE_INVALID 0 +#define FS_ENCRYPTION_MODE_AES_256_XTS 1 +#define FS_ENCRYPTION_MODE_AES_256_GCM 2 +#define FS_ENCRYPTION_MODE_AES_256_CBC 3 +#define FS_ENCRYPTION_MODE_AES_256_CTS 4 + + struct fscrypt_policy { __u8 version; __u8 contents_encryption_mode; -- GitLab From a78b3669034b194cbd9a351888e04715f6b9ba06 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 28 Feb 2017 20:32:41 +0800 Subject: [PATCH 0558/5498] f2fs: fix an error return value in truncate_partial_data_page This patch fix a error return value in truncate_partial_data_page Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 984fffbc1f2c..1da9514dd392 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -530,7 +530,7 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, page = get_lock_data_page(inode, index, true); if (IS_ERR(page)) - return 0; + return PTR_ERR(page) == -ENOENT ? 0 : PTR_ERR(page); truncate_out: f2fs_wait_on_page_writeback(page, DATA, true); zero_user(page, offset, PAGE_SIZE - offset); -- GitLab From 16bb5e8a17dbc291e97c0879d9c233205b3a5dee Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 4 Mar 2017 13:56:10 -0800 Subject: [PATCH 0559/5498] f2fs: don't need to invalidate wrong node page If f2fs_new_inode() is failed, the bad inode will invalidate 0'th node page during f2fs_evict_inode(), which doesn't need to do. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 788718fe9230..4799193a5821 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -411,7 +411,10 @@ no_delete: stat_dec_inline_dir(inode); stat_dec_inline_inode(inode); - invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino); + /* ino == 0, if f2fs_new_inode() was failed t*/ + if (inode->i_ino) + invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, + inode->i_ino); if (xnid) invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid); if (inode->i_nlink) { -- GitLab From 516ed235abacc1804fd05c671116fccfb64d35b3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 6 Mar 2017 11:59:56 -0800 Subject: [PATCH 0560/5498] f2fs: don't overwrite node block by SSR This patch fixes that SSR can overwrite previous warm node block consisting of a node chain since the last checkpoint. Fixes: 5b6c6be2d878 ("f2fs: use SSR for warm node as well") Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index baf606157c20..0ef48e7e908a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1243,6 +1243,12 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) if (f2fs_discard_en(sbi) && !f2fs_test_and_set_bit(offset, se->discard_map)) sbi->discard_blks--; + + /* don't overwrite by SSR to keep node chain */ + if (se->type == CURSEG_WARM_NODE) { + if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) + se->ckpt_valid_blocks++; + } } else { if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) { #ifdef CONFIG_F2FS_CHECK_FS -- GitLab From 0000942708daff1011170275cc6ba93257532363 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 10 Mar 2017 09:36:10 -0800 Subject: [PATCH 0561/5498] f2fs: le16_to_cpu for xattr->e_value_size This patch fixes missing le16 conversion, reported by kbuild test robot. Fixes: 5f35a2cd5 ("f2fs: Don't update the xattr data that same as the exist") Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 8e1632c946ef..1bb1acfee68e 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -588,7 +588,9 @@ static bool f2fs_xattr_value_same(struct f2fs_xattr_entry *entry, const void *value, size_t size) { void *pval = entry->e_name + entry->e_name_len; - return (entry->e_value_size == size) && !memcmp(pval, value, size); + + return (le16_to_cpu(entry->e_value_size) == size) && + !memcmp(pval, value, size); } static int __f2fs_setxattr(struct inode *inode, int index, -- GitLab From 2c3764009b578b9cda8f72ac422ee6eded2edda3 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 10 Mar 2017 15:25:59 +0800 Subject: [PATCH 0562/5498] f2fs: __update_nat_bits() can be static Signed-off-by: Fengguang Wu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 027a74562575..cd34ea14dfad 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2392,7 +2392,7 @@ add_out: list_add_tail(&nes->set_list, head); } -void __update_nat_bits(struct f2fs_sb_info *sbi, nid_t start_nid, +static void __update_nat_bits(struct f2fs_sb_info *sbi, nid_t start_nid, struct page *page) { struct f2fs_nm_info *nm_i = NM_I(sbi); -- GitLab From 4a73111cf8fe7690bdbcb628d639b21a59d0ab04 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 10 Mar 2017 15:54:31 +0800 Subject: [PATCH 0563/5498] f2fs: update_free_nid_bitmap() can be static Signed-off-by: Fengguang Wu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index cd34ea14dfad..88fce54f772c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1832,7 +1832,7 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) kmem_cache_free(free_nid_slab, i); } -void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, bool set) +static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, bool set) { struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); @@ -2647,7 +2647,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi) return 0; } -int init_free_nid_cache(struct f2fs_sb_info *sbi) +static int init_free_nid_cache(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); -- GitLab From 7b0c178ab973da0bc75a86325b4401de0313354a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Mar 2017 14:11:06 -0800 Subject: [PATCH 0564/5498] f2fs: use __set{__clear}_bit_le This patch uses __set{__clear}_bit_le for highter speed. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 88fce54f772c..6e6e959d9dd2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1842,9 +1842,9 @@ static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, bool set return; if (set) - set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); else - clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); } static void scan_nat_page(struct f2fs_sb_info *sbi, @@ -1856,7 +1856,7 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, unsigned int nat_ofs = NAT_BLOCK_OFFSET(start_nid); int i; - set_bit_le(nat_ofs, nm_i->nat_block_bitmap); + __set_bit_le(nat_ofs, nm_i->nat_block_bitmap); i = start_nid % NAT_ENTRY_PER_BLOCK; -- GitLab From 8834c938f98185100c014c9db725237962242925 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 1 Mar 2017 17:09:07 +0800 Subject: [PATCH 0565/5498] f2fs: skip scanning free nid bitmap of full NAT blocks This patch adds to account free nids for each NAT blocks, and while scanning all free nid bitmap, do check count and skip lookuping in full NAT block. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/node.c --- fs/f2fs/debug.c | 1 + fs/f2fs/f2fs.h | 2 ++ fs/f2fs/node.c | 34 ++++++++++++++++++++++++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index ba6d2b8aadc2..dd714ec533ce 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -196,6 +196,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += (NM_I(sbi)->nat_bits_blocks << F2FS_BLKSIZE_BITS); si->base_mem += NM_I(sbi)->nat_blocks * NAT_ENTRY_BITMAP_SIZE; si->base_mem += NM_I(sbi)->nat_blocks / 8; + si->base_mem += NM_I(sbi)->nat_blocks * sizeof(unsigned short); get_cache: si->cache_mem = 0; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e3f6603351db..a27b75145e99 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -661,6 +661,8 @@ struct f2fs_nm_info { struct mutex build_lock; /* lock for build free nids */ unsigned char (*free_nid_bitmap)[NAT_ENTRY_BITMAP_SIZE]; unsigned char *nat_block_bitmap; + unsigned short *free_nid_count; /* free nid count of NAT block */ + spinlock_t free_nid_lock; /* protect updating of nid count */ /* for checkpoint */ char *nat_bitmap; /* NAT bitmap pointer */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 6e6e959d9dd2..0e8d2949d6e4 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1832,7 +1832,8 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) kmem_cache_free(free_nid_slab, i); } -static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, bool set) +static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, + bool set, bool build) { struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); @@ -1845,6 +1846,13 @@ static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, bool set __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); else __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + + spin_lock(&nm_i->free_nid_lock); + if (set) + nm_i->free_nid_count[nat_ofs]++; + else if (!build) + nm_i->free_nid_count[nat_ofs]--; + spin_unlock(&nm_i->free_nid_lock); } static void scan_nat_page(struct f2fs_sb_info *sbi, @@ -1856,6 +1864,9 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, unsigned int nat_ofs = NAT_BLOCK_OFFSET(start_nid); int i; + if (test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) + return; + __set_bit_le(nat_ofs, nm_i->nat_block_bitmap); i = start_nid % NAT_ENTRY_PER_BLOCK; @@ -1870,7 +1881,7 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, blk_addr == NEW_ADDR); if (blk_addr == NULL_ADDR) freed = add_free_nid(sbi, start_nid, true); - update_free_nid_bitmap(sbi, start_nid, freed); + update_free_nid_bitmap(sbi, start_nid, freed, true); } } @@ -1886,6 +1897,8 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) for (i = 0; i < nm_i->nat_blocks; i++) { if (!test_bit_le(i, nm_i->nat_block_bitmap)) continue; + if (!nm_i->free_nid_count[i]) + continue; for (idx = 0; idx < NAT_ENTRY_PER_BLOCK; idx++) { nid_t nid; @@ -2090,7 +2103,7 @@ retry: __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); nm_i->available_nids--; - update_free_nid_bitmap(sbi, *nid, false); + update_free_nid_bitmap(sbi, *nid, false, false); spin_unlock(&nm_i->nid_list_lock); return true; @@ -2146,7 +2159,7 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) nm_i->available_nids++; - update_free_nid_bitmap(sbi, nid, true); + update_free_nid_bitmap(sbi, nid, true, false); spin_unlock(&nm_i->nid_list_lock); @@ -2476,11 +2489,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, add_free_nid(sbi, nid, false); spin_lock(&NM_I(sbi)->nid_list_lock); NM_I(sbi)->available_nids++; - update_free_nid_bitmap(sbi, nid, true); + update_free_nid_bitmap(sbi, nid, true, false); spin_unlock(&NM_I(sbi)->nid_list_lock); } else { spin_lock(&NM_I(sbi)->nid_list_lock); - update_free_nid_bitmap(sbi, nid, false); + update_free_nid_bitmap(sbi, nid, false, false); spin_unlock(&NM_I(sbi)->nid_list_lock); } } @@ -2660,6 +2673,14 @@ static int init_free_nid_cache(struct f2fs_sb_info *sbi) GFP_KERNEL); if (!nm_i->nat_block_bitmap) return -ENOMEM; + + nm_i->free_nid_count = f2fs_kvzalloc(nm_i->nat_blocks * + sizeof(unsigned short), GFP_KERNEL); + if (!nm_i->free_nid_count) + return -ENOMEM; + + spin_lock_init(&nm_i->free_nid_lock); + return 0; } @@ -2739,6 +2760,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) kvfree(nm_i->nat_block_bitmap); kvfree(nm_i->free_nid_bitmap); + kvfree(nm_i->free_nid_count); kfree(nm_i->nat_bitmap); kfree(nm_i->nat_bits); -- GitLab From b2de4685840addb2718276831dc98969c44b3aab Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 8 Mar 2017 20:07:49 +0800 Subject: [PATCH 0566/5498] f2fs: combine nat_bits and free_nid_bitmap cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both nat_bits cache and free_nid_bitmap cache provide same functionality as a intermediate cache between free nid cache and disk, but with different granularity of indicating free nid range, and different persistence policy. nat_bits cache provides better persistence ability, and free_nid_bitmap provides better granularity. In this patch we combine advantage of both caches, so finally policy of the intermediate cache would be: - init: load free nid status from nat_bits into free_nid_bitmap - lookup: scan free_nid_bitmap before load NAT blocks - update: update free_nid_bitmap in real-time - persistence: udpate and persist nat_bits in checkpoint This patch also resolves performance regression reported by lkp-robot. commit: 4ac912427c4214d8031d9ad6fbc3bc75e71512df ("f2fs: introduce free nid bitmap") d00030cf9cd0bb96fdccc41e33d3c91dcbb672ba ("f2fs: use __set{__clear}_bit_le") 1382c0f3f9d3f936c8bc42ed1591cf7a593ef9f7 ("f2fs: combine nat_bits and free_nid_bitmap cache") 4ac912427c4214d8 d00030cf9cd0bb96fdccc41e33 1382c0f3f9d3f936c8bc42ed15 ---------------- -------------------------- -------------------------- %stddev %change %stddev %change %stddev \ | \ | \ 77863 ± 0% +2.1% 79485 ± 1% +50.8% 117404 ± 0% aim7.jobs-per-min 231.63 ± 0% -2.0% 227.01 ± 1% -33.6% 153.80 ± 0% aim7.time.elapsed_time 231.63 ± 0% -2.0% 227.01 ± 1% -33.6% 153.80 ± 0% aim7.time.elapsed_time.max 896604 ± 0% -0.8% 889221 ± 3% -20.2% 715260 ± 1% aim7.time.involuntary_context_switches 2394 ± 1% +4.6% 2503 ± 1% +3.7% 2481 ± 2% aim7.time.maximum_resident_set_size 6240 ± 0% -1.5% 6145 ± 1% -14.1% 5360 ± 1% aim7.time.system_time 1111357 ± 3% +1.9% 1132509 ± 2% -6.2% 1041932 ± 2% aim7.time.voluntary_context_switches ... Signed-off-by: Chao Yu Tested-by: Xiaolong Ye Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 125 +++++++++++++++++++------------------------------ 1 file changed, 47 insertions(+), 78 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0e8d2949d6e4..78b891a4fa15 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -338,9 +338,6 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, set_nat_flag(e, IS_CHECKPOINTED, false); __set_nat_cache_dirty(nm_i, e); - if (enabled_nat_bits(sbi, NULL) && new_blkaddr == NEW_ADDR) - __clear_bit_le(NAT_BLOCK_OFFSET(ni->nid), nm_i->empty_nat_bits); - /* update fsync_mark if its inode nat entry is still alive */ if (ni->nid != ni->ino) e = __lookup_nat_cache(nm_i, ni->ino); @@ -1833,7 +1830,7 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) } static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, - bool set, bool build) + bool set, bool build, bool locked) { struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); @@ -1847,12 +1844,14 @@ static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, else __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - spin_lock(&nm_i->free_nid_lock); + if (!locked) + spin_lock(&nm_i->free_nid_lock); if (set) nm_i->free_nid_count[nat_ofs]++; else if (!build) nm_i->free_nid_count[nat_ofs]--; - spin_unlock(&nm_i->free_nid_lock); + if (!locked) + spin_unlock(&nm_i->free_nid_lock); } static void scan_nat_page(struct f2fs_sb_info *sbi, @@ -1881,7 +1880,7 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, blk_addr == NEW_ADDR); if (blk_addr == NULL_ADDR) freed = add_free_nid(sbi, start_nid, true); - update_free_nid_bitmap(sbi, start_nid, freed, true); + update_free_nid_bitmap(sbi, start_nid, freed, true, false); } } @@ -1929,58 +1928,6 @@ out: up_read(&nm_i->nat_tree_lock); } -static int scan_nat_bits(struct f2fs_sb_info *sbi) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct page *page; - unsigned int i = 0; - nid_t nid; - - if (!enabled_nat_bits(sbi, NULL)) - return -EAGAIN; - - down_read(&nm_i->nat_tree_lock); -check_empty: - i = find_next_bit_le(nm_i->empty_nat_bits, nm_i->nat_blocks, i); - if (i >= nm_i->nat_blocks) { - i = 0; - goto check_partial; - } - - for (nid = i * NAT_ENTRY_PER_BLOCK; nid < (i + 1) * NAT_ENTRY_PER_BLOCK; - nid++) { - if (unlikely(nid >= nm_i->max_nid)) - break; - add_free_nid(sbi, nid, true); - } - - if (nm_i->nid_cnt[FREE_NID_LIST] >= MAX_FREE_NIDS) - goto out; - i++; - goto check_empty; - -check_partial: - i = find_next_zero_bit_le(nm_i->full_nat_bits, nm_i->nat_blocks, i); - if (i >= nm_i->nat_blocks) { - disable_nat_bits(sbi, true); - up_read(&nm_i->nat_tree_lock); - return -EINVAL; - } - - nid = i * NAT_ENTRY_PER_BLOCK; - page = get_current_nat_page(sbi, nid); - scan_nat_page(sbi, page, nid); - f2fs_put_page(page, 1); - - if (nm_i->nid_cnt[FREE_NID_LIST] < MAX_FREE_NIDS) { - i++; - goto check_partial; - } -out: - up_read(&nm_i->nat_tree_lock); - return 0; -} - static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) { struct f2fs_nm_info *nm_i = NM_I(sbi); @@ -2002,21 +1949,6 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) if (nm_i->nid_cnt[FREE_NID_LIST]) return; - - /* try to find free nids with nat_bits */ - if (!scan_nat_bits(sbi) && nm_i->nid_cnt[FREE_NID_LIST]) - return; - } - - /* find next valid candidate */ - if (enabled_nat_bits(sbi, NULL)) { - int idx = find_next_zero_bit_le(nm_i->full_nat_bits, - nm_i->nat_blocks, 0); - - if (idx >= nm_i->nat_blocks) - set_sbi_flag(sbi, SBI_NEED_FSCK); - else - nid = idx * NAT_ENTRY_PER_BLOCK; } /* readahead nat pages to be scanned */ @@ -2103,7 +2035,7 @@ retry: __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); nm_i->available_nids--; - update_free_nid_bitmap(sbi, *nid, false, false); + update_free_nid_bitmap(sbi, *nid, false, false, false); spin_unlock(&nm_i->nid_list_lock); return true; @@ -2159,7 +2091,7 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) nm_i->available_nids++; - update_free_nid_bitmap(sbi, nid, true, false); + update_free_nid_bitmap(sbi, nid, true, false, false); spin_unlock(&nm_i->nid_list_lock); @@ -2489,11 +2421,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, add_free_nid(sbi, nid, false); spin_lock(&NM_I(sbi)->nid_list_lock); NM_I(sbi)->available_nids++; - update_free_nid_bitmap(sbi, nid, true, false); + update_free_nid_bitmap(sbi, nid, true, false, false); spin_unlock(&NM_I(sbi)->nid_list_lock); } else { spin_lock(&NM_I(sbi)->nid_list_lock); - update_free_nid_bitmap(sbi, nid, false, false); + update_free_nid_bitmap(sbi, nid, false, false, false); spin_unlock(&NM_I(sbi)->nid_list_lock); } } @@ -2599,6 +2531,40 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) return 0; } +inline void load_free_nid_bitmap(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned int i = 0; + nid_t nid, last_nid; + + if (!enabled_nat_bits(sbi, NULL)) + return; + + for (i = 0; i < nm_i->nat_blocks; i++) { + i = find_next_bit_le(nm_i->empty_nat_bits, nm_i->nat_blocks, i); + if (i >= nm_i->nat_blocks) + break; + + __set_bit_le(i, nm_i->nat_block_bitmap); + + nid = i * NAT_ENTRY_PER_BLOCK; + last_nid = (i + 1) * NAT_ENTRY_PER_BLOCK; + + spin_lock(&nm_i->free_nid_lock); + for (; nid < last_nid; nid++) + update_free_nid_bitmap(sbi, nid, true, true, true); + spin_unlock(&nm_i->free_nid_lock); + } + + for (i = 0; i < nm_i->nat_blocks; i++) { + i = find_next_bit_le(nm_i->full_nat_bits, nm_i->nat_blocks, i); + if (i >= nm_i->nat_blocks) + break; + + __set_bit_le(i, nm_i->nat_block_bitmap); + } +} + static int init_node_manager(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb_raw = F2FS_RAW_SUPER(sbi); @@ -2700,6 +2666,9 @@ int build_node_manager(struct f2fs_sb_info *sbi) if (err) return err; + /* load free nid status from nat_bits table */ + load_free_nid_bitmap(sbi); + build_free_nids(sbi, true, true); return 0; } -- GitLab From 25998451e47dfb8d8098e64ce6f49a6286610c6b Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 11 Mar 2017 21:18:01 +0800 Subject: [PATCH 0567/5498] f2fs: le32_to_cpu for ckpt->cp_pack_total_block_count Fixes: 22ad0b6ab4 ("f2fs: add bitmaps for empty or full NAT blocks") Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index e9c022519966..63256a5dc32f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1025,7 +1025,8 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) spin_lock(&sbi->cp_lock); - if (cpc->reason == CP_UMOUNT && ckpt->cp_pack_total_block_count > + if (cpc->reason == CP_UMOUNT && + le32_to_cpu(ckpt->cp_pack_total_block_count) > sbi->blocks_per_seg - NM_I(sbi)->nat_bits_blocks) disable_nat_bits(sbi, false); -- GitLab From 968d31410d421d9e754d71a195302e75836d0b7d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Mar 2017 13:32:20 -0800 Subject: [PATCH 0568/5498] f2fs: fix wrong error injection for evict_inode The previous one was not a proper location to inject an error, since there is no point to get errors. Instead, we can emulate EIO during truncation, and the below logic should handle it correctly. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 4799193a5821..8ca67ebcd069 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -372,13 +372,6 @@ void f2fs_evict_inode(struct inode *inode) if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; -#ifdef CONFIG_F2FS_FAULT_INJECTION - if (time_to_inject(sbi, FAULT_EVICT_INODE)) { - f2fs_show_injection_info(FAULT_EVICT_INODE); - goto no_delete; - } -#endif - remove_ino_entry(sbi, inode->i_ino, APPEND_INO); remove_ino_entry(sbi, inode->i_ino, UPDATE_INO); @@ -389,6 +382,12 @@ retry: if (F2FS_HAS_BLOCKS(inode)) err = f2fs_truncate(inode); +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_EVICT_INODE)) { + f2fs_show_injection_info(FAULT_EVICT_INODE); + err = -EIO; + } +#endif if (!err) { f2fs_lock_op(sbi); err = remove_inode_page(inode); -- GitLab From d94c16b4e62b2b65513caae37dc9c0df2519262b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Mar 2017 11:22:45 -0800 Subject: [PATCH 0569/5498] f2fs: don't allow to get pino when filename is encrypted After renaming an encrypted file, we have no way to get its encrypted filename from its dentry. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 1da9514dd392..3a4edccdceac 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -112,6 +112,9 @@ static int get_parent_ino(struct inode *inode, nid_t *pino) { struct dentry *dentry; + if (file_enc_name(inode)) + return 0; + inode = igrab(inode); dentry = d_find_any_alias(inode); iput(inode); -- GitLab From 277a5aba15bc315222d8f53636a98fb1bfa89517 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 4 Mar 2017 21:48:28 +0800 Subject: [PATCH 0570/5498] f2fs: fix the fault of checking F2FS_LINK_MAX for rename inode The parent directory's nlink will change, not the inode. Signed-off-by: Kinglong Mee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index bf6ec7efc743..6e94e6caec15 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -920,8 +920,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, old_nlink = old_dir_entry ? -1 : 1; new_nlink = -old_nlink; err = -EMLINK; - if ((old_nlink > 0 && old_inode->i_nlink >= F2FS_LINK_MAX) || - (new_nlink > 0 && new_inode->i_nlink >= F2FS_LINK_MAX)) + if ((old_nlink > 0 && old_dir->i_nlink >= F2FS_LINK_MAX) || + (new_nlink > 0 && new_dir->i_nlink >= F2FS_LINK_MAX)) goto out_new_dir; } -- GitLab From c3f4b2a3cdb0b8becf39c92fbc6c08a1ce29dcf8 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Wed, 8 Mar 2017 09:49:53 +0800 Subject: [PATCH 0571/5498] f2fs: fix the fault of calculating blkstart twice When the zone type is BLK_ZONE_TYPE_CONVENTIONAL, the blkstart is calculated twice. Signed-off-by: Kinglong Mee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0ef48e7e908a..47a915c6539d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -876,6 +876,7 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) { sector_t sector, nr_sects; + block_t lblkstart = blkstart; int devi = 0; if (sbi->s_ndevs) { @@ -893,7 +894,7 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, case BLK_ZONE_TYPE_CONVENTIONAL: if (!blk_queue_discard(bdev_get_queue(bdev))) return 0; - return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); + return __f2fs_issue_discard_async(sbi, bdev, lblkstart, blklen); case BLK_ZONE_TYPE_SEQWRITE_REQ: case BLK_ZONE_TYPE_SEQWRITE_PREF: sector = SECTOR_FROM_BLOCK(blkstart); -- GitLab From 4ee9334f412aa4f187e3b666948470a258cd748b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Mar 2017 13:41:22 -0800 Subject: [PATCH 0572/5498] f2fs: build stat_info before orphan inode recovery f2fs_sync_fs() -> write_checkpoint() calls stat_inc_cp_count(sbi->stat_info), which needs stat_info allocation. Otherwise, we can hit: [254042.598623] ? count_shadow_nodes+0xa0/0xa0 [254042.598633] f2fs_sync_fs+0x65/0xd0 [f2fs] [254042.598645] f2fs_balance_fs_bg+0xe4/0x1c0 [f2fs] [254042.598657] f2fs_write_node_pages+0x34/0x1a0 [f2fs] [254042.598664] ? pagevec_lookup_entries+0x1e/0x30 [254042.598673] do_writepages+0x1e/0x30 [254042.598682] __writeback_single_inode+0x45/0x330 [254042.598688] writeback_single_inode+0xd7/0x190 [254042.598694] write_inode_now+0x86/0xa0 [254042.598699] iput+0x122/0x200 [254042.598709] f2fs_fill_super+0xd4a/0x14d0 [f2fs] [254042.598717] mount_bdev+0x184/0x1c0 [254042.598934] ? f2fs_commit_super+0x100/0x100 [f2fs] [254042.599142] f2fs_mount+0x15/0x20 [f2fs] [254042.599349] mount_fs+0x39/0x160 [254042.599554] ? __alloc_percpu+0x15/0x20 [254042.599759] vfs_kern_mount+0x67/0x110 [254042.599972] do_mount+0x1bb/0xc80 [254042.600175] ? memdup_user+0x42/0x60 [254042.600380] SyS_mount+0x83/0xd0 [254042.600583] entry_SYSCALL_64_fastpath+0x1e/0xad Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ac673b233640..e5db2ff7f030 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2014,6 +2014,10 @@ try_onemore: f2fs_join_shrinker(sbi); + err = f2fs_build_stats(sbi); + if (err) + goto free_nm; + /* if there are nt orphan nodes free them */ err = recover_orphan_inodes(sbi); if (err) @@ -2038,10 +2042,6 @@ try_onemore: goto free_root_inode; } - err = f2fs_build_stats(sbi); - if (err) - goto free_root_inode; - if (f2fs_proc_root) sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); @@ -2135,7 +2135,6 @@ free_proc: remove_proc_entry("segment_bits", sbi->s_proc); remove_proc_entry(sb->s_id, f2fs_proc_root); } - f2fs_destroy_stats(sbi); free_root_inode: dput(sb->s_root); sb->s_root = NULL; @@ -2153,6 +2152,7 @@ free_node_inode: truncate_inode_pages_final(META_MAPPING(sbi)); iput(sbi->node_inode); mutex_unlock(&sbi->umount_mutex); + f2fs_destroy_stats(sbi); free_nm: destroy_node_manager(sbi); free_sm: -- GitLab From 253d19f72ba63e5e1ace2e784714061dbf411f37 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 17 Mar 2017 09:55:52 +0800 Subject: [PATCH 0573/5498] f2fs: fix stale ATOMIC_WRITTEN_PAGE private pointer When I forced to enable atomic operations intentionally, I could hit the below panic, since we didn't clear page->private in f2fs_invalidate_page called by file truncation. The panic occurs due to NULL mapping having page->private. BUG: unable to handle kernel paging request at ffffffffffffffff IP: drop_buffers+0x38/0xe0 PGD 5d00c067 PUD 5d00e067 PMD 0 CPU: 3 PID: 1648 Comm: fsstress Tainted: G D OE 4.10.0+ #5 Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 task: ffff9151952863c0 task.stack: ffffaaec40db4000 RIP: 0010:drop_buffers+0x38/0xe0 RSP: 0018:ffffaaec40db74c8 EFLAGS: 00010292 Call Trace: ? page_referenced+0x8b/0x170 try_to_free_buffers+0xc5/0xe0 try_to_release_page+0x49/0x50 shrink_page_list+0x8bc/0x9f0 shrink_inactive_list+0x1dd/0x500 ? shrink_active_list+0x2c0/0x430 shrink_node_memcg+0x5eb/0x7c0 shrink_node+0xe1/0x320 do_try_to_free_pages+0xef/0x2e0 try_to_free_pages+0xe9/0x190 __alloc_pages_slowpath+0x390/0xe70 __alloc_pages_nodemask+0x291/0x2b0 alloc_pages_current+0x95/0x140 __page_cache_alloc+0xc4/0xe0 pagecache_get_page+0xab/0x2a0 grab_cache_page_write_begin+0x20/0x40 get_read_data_page+0x2e6/0x4c0 [f2fs] ? f2fs_mark_inode_dirty_sync+0x16/0x30 [f2fs] ? truncate_data_blocks_range+0x238/0x2b0 [f2fs] get_lock_data_page+0x30/0x190 [f2fs] __exchange_data_block+0xaaf/0xf40 [f2fs] f2fs_fallocate+0x418/0xd00 [f2fs] vfs_fallocate+0x157/0x220 SyS_fallocate+0x48/0x80 Signed-off-by: Yunlei He Signed-off-by: Chao Yu [Chao Yu: use INMEM_INVALIDATE for better tracing] Signed-off-by: Jaegeuk Kim Conflicts: include/trace/events/f2fs.h --- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/segment.c | 30 ++++++++++++++++++++++++++++++ include/trace/events/f2fs.h | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 7d51648ba261..5a0d6fcf5669 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1944,7 +1944,7 @@ void f2fs_invalidate_page(struct page *page, unsigned int offset, /* This is atomic written page, keep Private */ if (IS_ATOMIC_WRITTEN_PAGE(page)) - return; + return drop_inmem_page(inode, page); set_page_private(page, 0); ClearPagePrivate(page); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a27b75145e99..084a238f70b5 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -822,6 +822,7 @@ enum page_type { META_FLUSH, INMEM, /* the below types are used by tracepoints only. */ INMEM_DROP, + INMEM_INVALIDATE, INMEM_REVOKE, IPU, OPU, @@ -2285,6 +2286,7 @@ void destroy_node_manager_caches(void); */ void register_inmem_page(struct inode *inode, struct page *page); void drop_inmem_pages(struct inode *inode); +void drop_inmem_page(struct inode *inode, struct page *page); int commit_inmem_pages(struct inode *inode); void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need); void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 47a915c6539d..400753db95df 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -250,6 +250,36 @@ void drop_inmem_pages(struct inode *inode) stat_dec_atomic_write(inode); } +void drop_inmem_page(struct inode *inode, struct page *page) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct list_head *head = &fi->inmem_pages; + struct inmem_pages *cur = NULL; + + f2fs_bug_on(sbi, !IS_ATOMIC_WRITTEN_PAGE(page)); + + mutex_lock(&fi->inmem_lock); + list_for_each_entry(cur, head, list) { + if (cur->page == page) + break; + } + + f2fs_bug_on(sbi, !cur || cur->page != page); + list_del(&cur->list); + mutex_unlock(&fi->inmem_lock); + + dec_page_count(sbi, F2FS_INMEM_PAGES); + kmem_cache_free(inmem_entry_slab, cur); + + ClearPageUptodate(page); + set_page_private(page, 0); + ClearPagePrivate(page); + f2fs_put_page(page, 0); + + trace_f2fs_commit_inmem_page(page, INMEM_INVALIDATE); +} + static int __commit_inmem_pages(struct inode *inode, struct list_head *revoke_list) { diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 4b1cab6743f7..1ba88e688358 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -17,6 +17,7 @@ { META_FLUSH, "META_FLUSH" }, \ { INMEM, "INMEM" }, \ { INMEM_DROP, "INMEM_DROP" }, \ + { INMEM_INVALIDATE, "INMEM_INVALIDATE" }, \ { INMEM_REVOKE, "INMEM_REVOKE" }, \ { IPU, "IN-PLACE" }, \ { OPU, "OUT-OF-PLACE" }) -- GitLab From fb98d82424abad1892639581cb5b668627d1b644 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 17 Mar 2017 10:04:15 +0800 Subject: [PATCH 0574/5498] f2fs: don't allow atomic writes for not regular files The atomic writes only supports regular files for database. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3a4edccdceac..2a6ce4af6a19 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1533,6 +1533,9 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) if (!inode_owner_or_capable(inode)) return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + ret = mnt_want_write_file(filp); if (ret) return ret; -- GitLab From 02b385dacd8069fd27cbb8afd46b96e22ae9504e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 17 Mar 2017 15:43:57 +0800 Subject: [PATCH 0575/5498] f2fs: don't allow volatile writes for non-regular file Now f2fs only supports volatile writes for journal db regular file. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2a6ce4af6a19..57a2d1544d20 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1613,6 +1613,9 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) if (!inode_owner_or_capable(inode)) return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + ret = mnt_want_write_file(filp); if (ret) return ret; -- GitLab From 11415646ed3daa18b23036dc5fec51dde71239f8 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 4 Mar 2017 22:13:10 +0800 Subject: [PATCH 0576/5498] f2fs: make sure trace all f2fs_issue_flush The root device's issue flush trace is missing, add it and tracing the result from submit. Fixes d50aaeec90 ("f2fs: show actual device info in tracepoints") Signed-off-by: Kinglong Mee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 24 +++++++++++++----------- include/trace/events/f2fs.h | 11 +++++++---- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 400753db95df..fec1ed02df44 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -441,7 +441,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) } } -static int __submit_flush_wait(struct block_device *bdev) +static int __submit_flush_wait(struct f2fs_sb_info *sbi, + struct block_device *bdev) { struct bio *bio = f2fs_bio_alloc(0); int ret; @@ -450,23 +451,24 @@ static int __submit_flush_wait(struct block_device *bdev) bio->bi_bdev = bdev; ret = submit_bio_wait(WRITE_FLUSH, bio); bio_put(bio); + + trace_f2fs_issue_flush(bdev, test_opt(sbi, NOBARRIER), + test_opt(sbi, FLUSH_MERGE), ret); return ret; } static int submit_flush_wait(struct f2fs_sb_info *sbi) { - int ret = __submit_flush_wait(sbi->sb->s_bdev); + int ret = __submit_flush_wait(sbi, sbi->sb->s_bdev); int i; - if (sbi->s_ndevs && !ret) { - for (i = 1; i < sbi->s_ndevs; i++) { - trace_f2fs_issue_flush(FDEV(i).bdev, - test_opt(sbi, NOBARRIER), - test_opt(sbi, FLUSH_MERGE)); - ret = __submit_flush_wait(FDEV(i).bdev); - if (ret) - break; - } + if (!sbi->s_ndevs || ret) + return ret; + + for (i = 1; i < sbi->s_ndevs; i++) { + ret = __submit_flush_wait(sbi, FDEV(i).bdev); + if (ret) + break; } return ret; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 1ba88e688358..310ff1f223a0 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1130,26 +1130,29 @@ TRACE_EVENT(f2fs_issue_reset_zone, TRACE_EVENT(f2fs_issue_flush, TP_PROTO(struct block_device *dev, unsigned int nobarrier, - unsigned int flush_merge), + unsigned int flush_merge, int ret), - TP_ARGS(dev, nobarrier, flush_merge), + TP_ARGS(dev, nobarrier, flush_merge, ret), TP_STRUCT__entry( __field(dev_t, dev) __field(unsigned int, nobarrier) __field(unsigned int, flush_merge) + __field(int, ret) ), TP_fast_assign( __entry->dev = dev->bd_dev; __entry->nobarrier = nobarrier; __entry->flush_merge = flush_merge; + __entry->ret = ret; ), - TP_printk("dev = (%d,%d), %s %s", + TP_printk("dev = (%d,%d), %s %s, ret = %d", show_dev(__entry->dev), __entry->nobarrier ? "skip (nobarrier)" : "issue", - __entry->flush_merge ? " with flush_merge" : "") + __entry->flush_merge ? " with flush_merge" : "", + __entry->ret) ); TRACE_EVENT(f2fs_lookup_extent_tree_start, -- GitLab From cd93c1ada77228a1589c840f33533d9765403439 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Tue, 28 Feb 2017 21:34:47 +0800 Subject: [PATCH 0577/5498] f2fs: drop duplicate radix tree lookup of nat_entry_set The nat entry is listed from the set list for freeing, it's duplicate to do radix tree lookup again. Signed-off-by: Kinglong Mee [Jaegeuk Kim: remove unnecessary f2fs_bug_on] Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 78b891a4fa15..1a8587d044cf 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -177,18 +177,12 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, } static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i, - struct nat_entry *ne) + struct nat_entry_set *set, struct nat_entry *ne) { - nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid); - struct nat_entry_set *head; - - head = radix_tree_lookup(&nm_i->nat_set_root, set); - if (head) { - list_move_tail(&ne->list, &nm_i->nat_entries); - set_nat_flag(ne, IS_DIRTY, false); - head->entry_cnt--; - nm_i->dirty_nat_cnt--; - } + list_move_tail(&ne->list, &nm_i->nat_entries); + set_nat_flag(ne, IS_DIRTY, false); + set->entry_cnt--; + nm_i->dirty_nat_cnt--; } static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i, @@ -2416,7 +2410,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, } raw_nat_from_node_info(raw_ne, &ne->ni); nat_reset_flag(ne); - __clear_nat_cache_dirty(NM_I(sbi), ne); + __clear_nat_cache_dirty(NM_I(sbi), set, ne); if (nat_get_blkaddr(ne) == NULL_ADDR) { add_free_nid(sbi, nid, false); spin_lock(&NM_I(sbi)->nid_list_lock); -- GitLab From fb0cf2548c95945b906bb79608c8483a4326322e Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Tue, 28 Feb 2017 21:34:37 +0800 Subject: [PATCH 0578/5498] f2fs: remove dead macro PGOFS_OF_NEXT_DNODE Fixes: 3cf4574705 ("f2fs: introduce get_next_page_offset to speed up SEEK_DATA") Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 084a238f70b5..1e668ce89eac 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2131,12 +2131,6 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags) ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) -/* get offset of first page in next direct node */ -#define PGOFS_OF_NEXT_DNODE(pgofs, inode) \ - ((pgofs < ADDRS_PER_INODE(inode)) ? ADDRS_PER_INODE(inode) : \ - (pgofs - ADDRS_PER_INODE(inode) + ADDRS_PER_BLOCK) / \ - ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode)) - /* * file.c */ -- GitLab From d8f9c77663085c473352d298735ffba53b3a992e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Mar 2017 13:54:56 -0800 Subject: [PATCH 0579/5498] f2fs: show more precise message on orphan recovery failure This case is not caused by fsck.f2fs. User needs to retry mount. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 63256a5dc32f..c3621dfaab57 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -568,7 +568,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) if (ni.blk_addr != NULL_ADDR) { set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_msg(sbi->sb, KERN_WARNING, - "%s: orphan failed (ino=%x), run fsck to fix.", + "%s: orphan failed (ino=%x) by kernel, retry mount.", __func__, ino); return -EIO; } -- GitLab From 02ccaef0b308059f956c6a63ebe83ebca98c0600 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 1 Mar 2017 18:07:10 +0800 Subject: [PATCH 0580/5498] f2fs: skip writeback meta pages if cp_mutex acquire failed Skip writeback meta pages if cp_mutex lock acquire failed, cp will flush dirty pages instead. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c3621dfaab57..7b7d0eeda3ec 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -276,10 +276,11 @@ static int f2fs_write_meta_pages(struct address_space *mapping, get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META)) goto skip_write; - trace_f2fs_writepages(mapping->host, wbc, META); + /* if locked failed, cp will flush dirty pages instead */ + if (!mutex_trylock(&sbi->cp_mutex)) + goto skip_write; - /* if mounting is failed, skip writing node pages */ - mutex_lock(&sbi->cp_mutex); + trace_f2fs_writepages(mapping->host, wbc, META); diff = nr_pages_to_write(sbi, META, wbc); written = sync_meta_pages(sbi, META, wbc->nr_to_write); mutex_unlock(&sbi->cp_mutex); -- GitLab From 40dd3c774817692dd0b23a7858c99320a435286c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 7 Mar 2017 18:02:02 -0800 Subject: [PATCH 0581/5498] f2fs: allocate a bio for discarding when actually issuing it Let's allocate a bio when issuing discard commands later. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/f2fs.h fs/f2fs/segment.c --- fs/f2fs/f2fs.h | 5 +- fs/f2fs/segment.c | 193 ++++++++++++++++++++++++---------------------- 2 files changed, 105 insertions(+), 93 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1e668ce89eac..a5f80f79c277 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -296,11 +296,12 @@ enum { struct discard_cmd { struct list_head list; /* command list */ struct completion wait; /* compleation */ + struct block_device *bdev; /* bdev */ block_t lstart; /* logical start address */ + block_t start; /* actual start address in dev */ block_t len; /* length */ - struct bio *bio; /* bio */ - int error; int state; /* state */ + int error; /* bio error */ }; struct discard_cmd_control { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fec1ed02df44..a8aaecd10488 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -666,7 +666,8 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) } static void __add_discard_cmd(struct f2fs_sb_info *sbi, - struct bio *bio, block_t lstart, block_t len) + struct block_device *bdev, block_t lstart, + block_t start, block_t len) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *cmd_list = &(dcc->discard_cmd_list); @@ -674,11 +675,12 @@ static void __add_discard_cmd(struct f2fs_sb_info *sbi, dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); INIT_LIST_HEAD(&dc->list); - dc->bio = bio; - bio->bi_private = dc; + dc->bdev = bdev; dc->lstart = lstart; + dc->start = start; dc->len = len; dc->state = D_PREP; + dc->error = 0; init_completion(&dc->wait); mutex_lock(&dcc->cmd_lock); @@ -688,71 +690,27 @@ static void __add_discard_cmd(struct f2fs_sb_info *sbi, static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc) { - int err = dc->error; - if (dc->state == D_DONE) atomic_dec(&(SM_I(sbi)->dcc_info->submit_discard)); - if (err == -EOPNOTSUPP) - err = 0; + if (dc->error == -EOPNOTSUPP) + dc->error = 0; - if (err) + if (dc->error) f2fs_msg(sbi->sb, KERN_INFO, - "Issue discard failed, ret: %d", err); - bio_put(dc->bio); + "Issue discard failed, ret: %d", dc->error); list_del(&dc->list); kmem_cache_free(discard_cmd_slab, dc); } -/* This should be covered by global mutex, &sit_i->sentry_lock */ -void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) -{ - struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *wait_list = &(dcc->discard_cmd_list); - struct discard_cmd *dc, *tmp; - struct blk_plug plug; - - mutex_lock(&dcc->cmd_lock); - - blk_start_plug(&plug); - - list_for_each_entry_safe(dc, tmp, wait_list, list) { - - if (blkaddr == NULL_ADDR) { - if (dc->state == D_PREP) { - dc->state = D_SUBMIT; - submit_bio(REQ_SYNC, dc->bio); - atomic_inc(&dcc->submit_discard); - } - continue; - } - - if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) { - if (dc->state == D_SUBMIT) - wait_for_completion_io(&dc->wait); - else - __remove_discard_cmd(sbi, dc); - } - } - blk_finish_plug(&plug); - - /* this comes from f2fs_put_super */ - if (blkaddr == NULL_ADDR) { - list_for_each_entry_safe(dc, tmp, wait_list, list) { - wait_for_completion_io(&dc->wait); - __remove_discard_cmd(sbi, dc); - } - } - mutex_unlock(&dcc->cmd_lock); -} - static void f2fs_submit_discard_endio(struct bio *bio, int err) { struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private; - dc->error = err; complete(&dc->wait); + dc->error = err; dc->state = D_DONE; + bio_put(bio); } /* copied from block/blk-lib.c in 4.10-rc1 */ @@ -836,6 +794,88 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, return 0; } +static void __submit_discard_cmd(struct f2fs_sb_info *sbi, + struct discard_cmd *dc) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct bio *bio = NULL; + + if (dc->state != D_PREP) + return; + + dc->error = __blkdev_issue_discard(dc->bdev, + SECTOR_FROM_BLOCK(dc->start), + SECTOR_FROM_BLOCK(dc->len), + GFP_NOFS, 0, &bio); + if (!dc->error) { + /* should keep before submission to avoid D_DONE right away */ + dc->state = D_SUBMIT; + atomic_inc(&dcc->submit_discard); + if (bio) { + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; + submit_bio(REQ_SYNC, bio); + } + } else { + __remove_discard_cmd(sbi, dc); + } +} + +static int __queue_discard_cmd(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t blkstart, block_t blklen) +{ + block_t lblkstart = blkstart; + + trace_f2fs_issue_discard(bdev, blkstart, blklen); + + if (sbi->s_ndevs) { + int devi = f2fs_target_device_index(sbi, blkstart); + + blkstart -= FDEV(devi).start_blk; + } + __add_discard_cmd(sbi, bdev, lblkstart, blkstart, blklen); + wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); + return 0; +} + +/* This should be covered by global mutex, &sit_i->sentry_lock */ +void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *wait_list = &(dcc->discard_cmd_list); + struct discard_cmd *dc, *tmp; + struct blk_plug plug; + + mutex_lock(&dcc->cmd_lock); + + blk_start_plug(&plug); + + list_for_each_entry_safe(dc, tmp, wait_list, list) { + + if (blkaddr == NULL_ADDR) { + __submit_discard_cmd(sbi, dc); + continue; + } + + if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) { + if (dc->state == D_SUBMIT) + wait_for_completion_io(&dc->wait); + else + __remove_discard_cmd(sbi, dc); + } + } + blk_finish_plug(&plug); + + /* this comes from f2fs_put_super */ + if (blkaddr == NULL_ADDR) { + list_for_each_entry_safe(dc, tmp, wait_list, list) { + wait_for_completion_io(&dc->wait); + __remove_discard_cmd(sbi, dc); + } + } + mutex_unlock(&dcc->cmd_lock); +} + static int issue_discard_thread(void *data) { struct f2fs_sb_info *sbi = data; @@ -853,15 +893,14 @@ repeat: mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, cmd_list, list) { - if (dc->state == D_PREP) { - dc->state = D_SUBMIT; - submit_bio(REQ_SYNC, dc->bio); - atomic_inc(&dcc->submit_discard); - if (iter++ > DISCARD_ISSUE_RATE) - break; - } else if (dc->state == D_DONE) { + + if (is_idle(sbi)) + __submit_discard_cmd(sbi, dc); + + if (dc->state == D_PREP && iter++ > DISCARD_ISSUE_RATE) + break; + if (dc->state == D_DONE) __remove_discard_cmd(sbi, dc); - } } mutex_unlock(&dcc->cmd_lock); @@ -875,34 +914,6 @@ repeat: goto repeat; } - -/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ -static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, - struct block_device *bdev, block_t blkstart, block_t blklen) -{ - struct bio *bio = NULL; - block_t lblkstart = blkstart; - int err; - - trace_f2fs_issue_discard(bdev, blkstart, blklen); - - if (sbi->s_ndevs) { - int devi = f2fs_target_device_index(sbi, blkstart); - - blkstart -= FDEV(devi).start_blk; - } - err = __blkdev_issue_discard(bdev, - SECTOR_FROM_BLOCK(blkstart), - SECTOR_FROM_BLOCK(blklen), - GFP_NOFS, 0, &bio); - if (!err && bio) { - bio->bi_end_io = f2fs_submit_discard_endio; - __add_discard_cmd(sbi, bio, lblkstart, blklen); - wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); - } - return err; -} - #ifdef CONFIG_BLK_DEV_ZONED static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) @@ -926,7 +937,7 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, case BLK_ZONE_TYPE_CONVENTIONAL: if (!blk_queue_discard(bdev_get_queue(bdev))) return 0; - return __f2fs_issue_discard_async(sbi, bdev, lblkstart, blklen); + return __queue_discard_cmd(sbi, bdev, lblkstart, blklen); case BLK_ZONE_TYPE_SEQWRITE_REQ: case BLK_ZONE_TYPE_SEQWRITE_PREF: sector = SECTOR_FROM_BLOCK(blkstart); @@ -958,7 +969,7 @@ static int __issue_discard_async(struct f2fs_sb_info *sbi, bdev_zoned_model(bdev) != BLK_ZONED_NONE) return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); #endif - return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); + return __queue_discard_cmd(sbi, bdev, blkstart, blklen); } static int f2fs_issue_discard(struct f2fs_sb_info *sbi, -- GitLab From 59acb96f53457fdfb00b371d346091d124fc58ba Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 2 Mar 2017 10:36:20 +0800 Subject: [PATCH 0582/5498] f2fs: add a punch discard command function This patch add a function to punch discard command if one segment reuse before discard. Split this segment from multi-segments discard range, and discard the left bigger range. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a8aaecd10488..402ea41986cf 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -838,6 +838,25 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, return 0; } +static void __punch_discard_cmd(struct f2fs_sb_info *sbi, + struct discard_cmd *dc, block_t blkaddr) +{ + block_t end_block = START_BLOCK(sbi, GET_SEGNO(sbi, blkaddr) + 1); + + if (dc->state == D_DONE || dc->lstart + dc->len <= end_block) { + __remove_discard_cmd(sbi, dc); + return; + } + + if (blkaddr - dc->lstart < dc->lstart + dc->len - end_block) { + dc->start += (end_block - dc->lstart); + dc->len -= (end_block - dc->lstart); + dc->lstart = end_block; + } else { + dc->len = blkaddr - dc->lstart; + } +} + /* This should be covered by global mutex, &sit_i->sentry_lock */ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { @@ -860,8 +879,7 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) { if (dc->state == D_SUBMIT) wait_for_completion_io(&dc->wait); - else - __remove_discard_cmd(sbi, dc); + __punch_discard_cmd(sbi, dc, blkaddr); } } blk_finish_plug(&plug); -- GitLab From f097fad7972a40e259f774ef66a3234da41e4ae1 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 8 Mar 2017 10:47:11 +0800 Subject: [PATCH 0583/5498] f2fs: use parameter max_items instead of PIDVEC_SIZE Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c index 73b4e1d1912a..c82ab4048127 100644 --- a/fs/f2fs/trace.c +++ b/fs/f2fs/trace.c @@ -138,7 +138,7 @@ static unsigned int gang_lookup_pids(pid_t *results, unsigned long first_index, radix_tree_for_each_slot(slot, &pids, &iter, first_index) { results[ret] = iter.index; - if (++ret == PIDVEC_SIZE) + if (++ret == max_items) break; } return ret; -- GitLab From 7d00727952a82f0c941f3673fcc739e9af4ef6ed Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 8 Mar 2017 10:47:12 +0800 Subject: [PATCH 0584/5498] f2fs: check range before defragment This patch checks the parameter range passed by ioctl to void that range exceeds the max_file_blocks limit. Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 57a2d1544d20..484edbaa257e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2069,6 +2069,12 @@ static int f2fs_ioc_defragment(struct file *filp, unsigned long arg) goto out; } + if (unlikely((range.start + range.len) >> PAGE_SHIFT > + sbi->max_file_blocks)) { + err = -EINVAL; + goto out; + } + err = f2fs_defragment_range(sbi, filp, &range); f2fs_update_time(sbi, REQ_TIME); if (err < 0) -- GitLab From b8dd635f6b27a8bfe738e44adb212429fc5822b2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 9 Mar 2017 15:24:24 -0800 Subject: [PATCH 0585/5498] f2fs: add fault injection on f2fs_truncate Inject a fault during f2fs_truncate(). Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 6 ++++++ fs/f2fs/super.c | 1 + 3 files changed, 8 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a5f80f79c277..a383c7e59282 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -52,6 +52,7 @@ enum { FAULT_BLOCK, FAULT_DIR_DEPTH, FAULT_EVICT_INODE, + FAULT_TRUNCATE, FAULT_IO, FAULT_CHECKPOINT, FAULT_MAX, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 484edbaa257e..475d59ecdeda 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -622,6 +622,12 @@ int f2fs_truncate(struct inode *inode) trace_f2fs_truncate(inode); +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(F2FS_I_SB(inode), FAULT_TRUNCATE)) { + f2fs_show_injection_info(FAULT_TRUNCATE); + return -EIO; + } +#endif /* we should check inline_data size */ if (!f2fs_may_inline_data(inode)) { err = f2fs_convert_inline_inode(inode); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index e5db2ff7f030..895ebb5e6934 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -49,6 +49,7 @@ char *fault_name[FAULT_MAX] = { [FAULT_BLOCK] = "no more block", [FAULT_DIR_DEPTH] = "too big dir depth", [FAULT_EVICT_INODE] = "evict_inode fail", + [FAULT_TRUNCATE] = "truncate fail", [FAULT_IO] = "IO error", [FAULT_CHECKPOINT] = "checkpoint error", }; -- GitLab From fb56043a9fcda9b04250191e9633e68ca6426ced Mon Sep 17 00:00:00 2001 From: Fan Li Date: Wed, 8 Mar 2017 13:39:16 +0800 Subject: [PATCH 0586/5498] f2fs: adjust the way of calculating nat block use a slightly simpler expression to calculate nat block with nid. Signed-off-by: Fan Li Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 2f9603fa85a5..ebed0240aa53 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -200,13 +200,16 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start) struct f2fs_nm_info *nm_i = NM_I(sbi); pgoff_t block_off; pgoff_t block_addr; - int seg_off; + /* + * block_off = segment_off * 512 + off_in_segment + * OLD = (segment_off * 512) * 2 + off_in_segment + * NEW = 2 * (segment_off * 512 + off_in_segment) - off_in_segment + */ block_off = NAT_BLOCK_OFFSET(start); - seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(nm_i->nat_blkaddr + - (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off << 1) - (block_off & (sbi->blocks_per_seg - 1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) -- GitLab From dfcdf0a1eab7f94fbd3cc9408bcabd4c9d99f47b Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Fri, 10 Mar 2017 17:54:03 +0800 Subject: [PATCH 0587/5498] f2fs: drop duplicate new_size assign in f2fs_zero_range Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 475d59ecdeda..60f436c1bb52 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1202,8 +1202,6 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, if (ret) return ret; - if (offset + len > new_size) - new_size = offset + len; new_size = max_t(loff_t, new_size, offset + len); } else { if (off_start) { -- GitLab From b85713f39a493d6b36f29575f5d0123c10f5e6c0 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Fri, 10 Mar 2017 17:54:26 +0800 Subject: [PATCH 0588/5498] f2fs: avoid copy date to user-space if move file range fail If move file range return error, the data copied to user-space is duplicate. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 60f436c1bb52..12681842cb90 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2223,6 +2223,8 @@ static int f2fs_ioc_move_range(struct file *filp, unsigned long arg) range.pos_out, range.len); mnt_drop_write_file(filp); + if (err) + goto err_out; if (copy_to_user((struct f2fs_move_range __user *)arg, &range, sizeof(range))) -- GitLab From 376554c648c0cf3dcaca8b612bcbb6b53e6114a5 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Fri, 10 Mar 2017 17:54:52 +0800 Subject: [PATCH 0589/5498] f2fs: check new size by inode_newsize_ok in f2fs_insert_range The inode_newsize_ok is better than only checking the maxbytes, eg. the rlimit etc. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 12681842cb90..078c8bd19d14 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1269,8 +1269,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) int ret = 0; new_size = i_size_read(inode) + len; - if (new_size > inode->i_sb->s_maxbytes) - return -EFBIG; + ret = inode_newsize_ok(inode, new_size); + if (ret) + return ret; if (offset >= i_size_read(inode)) return -EINVAL; -- GitLab From 7501b5724596851835f46cb691d4cb6e3f6c9102 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Fri, 10 Mar 2017 17:55:07 +0800 Subject: [PATCH 0590/5498] f2fs: move mnt_want_write_file after arguments checking It's needless of mnt_want_write_file for arguments checking. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 078c8bd19d14..7c0877b22d7a 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2052,45 +2052,37 @@ static int f2fs_ioc_defragment(struct file *filp, unsigned long arg) if (!S_ISREG(inode->i_mode)) return -EINVAL; - err = mnt_want_write_file(filp); - if (err) - return err; - - if (f2fs_readonly(sbi->sb)) { - err = -EROFS; - goto out; - } + if (f2fs_readonly(sbi->sb)) + return -EROFS; if (copy_from_user(&range, (struct f2fs_defragment __user *)arg, - sizeof(range))) { - err = -EFAULT; - goto out; - } + sizeof(range))) + return -EFAULT; /* verify alignment of offset & size */ - if (range.start & (F2FS_BLKSIZE - 1) || - range.len & (F2FS_BLKSIZE - 1)) { - err = -EINVAL; - goto out; - } + if (range.start & (F2FS_BLKSIZE - 1) || range.len & (F2FS_BLKSIZE - 1)) + return -EINVAL; if (unlikely((range.start + range.len) >> PAGE_SHIFT > - sbi->max_file_blocks)) { - err = -EINVAL; - goto out; - } + sbi->max_file_blocks)) + return -EINVAL; + + err = mnt_want_write_file(filp); + if (err) + return err; err = f2fs_defragment_range(sbi, filp, &range); + mnt_drop_write_file(filp); + f2fs_update_time(sbi, REQ_TIME); if (err < 0) - goto out; + return err; if (copy_to_user((struct f2fs_defragment __user *)arg, &range, sizeof(range))) - err = -EFAULT; -out: - mnt_drop_write_file(filp); - return err; + return -EFAULT; + + return 0; } static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, -- GitLab From 7ffc7d70ecb60ff59dc447ee329607a1d05e2ffb Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Fri, 10 Mar 2017 20:43:20 +0800 Subject: [PATCH 0591/5498] f2fs: clear FI_DATA_EXIST flag in truncate_inline_inode Clear FI_DATA_EXIST flag atomically in truncate_inline_inode, and the return value from truncate_inline_inode isn't used, remove it. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 +------- fs/f2fs/file.c | 4 +--- fs/f2fs/inline.c | 21 +++++++++++---------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a383c7e59282..714bc6a2f82d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1971,12 +1971,6 @@ static inline int f2fs_has_inline_data(struct inode *inode) return is_inode_flag_set(inode, FI_INLINE_DATA); } -static inline void f2fs_clear_inline_inode(struct inode *inode) -{ - clear_inode_flag(inode, FI_INLINE_DATA); - clear_inode_flag(inode, FI_DATA_EXIST); -} - static inline int f2fs_exist_data(struct inode *inode) { return is_inode_flag_set(inode, FI_DATA_EXIST); @@ -2609,7 +2603,7 @@ extern struct kmem_cache *inode_entry_slab; bool f2fs_may_inline_data(struct inode *inode); bool f2fs_may_inline_dentry(struct inode *inode); void read_inline_data(struct page *page, struct page *ipage); -bool truncate_inline_inode(struct page *ipage, u64 from); +void truncate_inline_inode(struct inode *inode, struct page *ipage, u64 from); int f2fs_read_inline_data(struct inode *inode, struct page *page); int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page); int f2fs_convert_inline_inode(struct inode *inode); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 7c0877b22d7a..f921aac0ef57 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -571,9 +571,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) } if (f2fs_has_inline_data(inode)) { - truncate_inline_inode(ipage, from); - if (from == 0) - clear_inode_flag(inode, FI_DATA_EXIST); + truncate_inline_inode(inode, ipage, from); f2fs_put_page(ipage, 1); truncate_page = true; goto out; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e32a9e527968..93c15337bcd9 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -63,19 +63,21 @@ void read_inline_data(struct page *page, struct page *ipage) SetPageUptodate(page); } -bool truncate_inline_inode(struct page *ipage, u64 from) +void truncate_inline_inode(struct inode *inode, struct page *ipage, u64 from) { void *addr; if (from >= MAX_INLINE_DATA) - return false; + return; addr = inline_data_addr(ipage); f2fs_wait_on_page_writeback(ipage, NODE, true); memset(addr + from, 0, MAX_INLINE_DATA - from); set_page_dirty(ipage); - return true; + + if (from == 0) + clear_inode_flag(inode, FI_DATA_EXIST); } int f2fs_read_inline_data(struct inode *inode, struct page *page) @@ -146,11 +148,11 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) set_inode_flag(dn->inode, FI_APPEND_WRITE); /* clear inline data and flag after data writeback */ - truncate_inline_inode(dn->inode_page, 0); + truncate_inline_inode(dn->inode, dn->inode_page, 0); clear_inline_node(dn->inode_page); clear_out: stat_dec_inline_inode(dn->inode); - f2fs_clear_inline_inode(dn->inode); + clear_inode_flag(dn->inode, FI_INLINE_DATA); f2fs_put_dnode(dn); return 0; } @@ -267,9 +269,8 @@ process_inline: if (f2fs_has_inline_data(inode)) { ipage = get_node_page(sbi, inode->i_ino); f2fs_bug_on(sbi, IS_ERR(ipage)); - if (!truncate_inline_inode(ipage, 0)) - return false; - f2fs_clear_inline_inode(inode); + truncate_inline_inode(inode, ipage, 0); + clear_inode_flag(inode, FI_INLINE_DATA); f2fs_put_page(ipage, 1); } else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) { if (truncate_blocks(inode, 0, false)) @@ -380,7 +381,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, set_page_dirty(page); /* clear inline dir and flag after data writeback */ - truncate_inline_inode(ipage, 0); + truncate_inline_inode(dir, ipage, 0); stat_dec_inline_dir(dir); clear_inode_flag(dir, FI_INLINE_DENTRY); @@ -455,7 +456,7 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, } memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA); - truncate_inline_inode(ipage, 0); + truncate_inline_inode(dir, ipage, 0); unlock_page(ipage); -- GitLab From 980f1ae33e13cbc56b87904975aee98983fc2552 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Mon, 13 Mar 2017 16:35:13 +0800 Subject: [PATCH 0592/5498] f2fs: fix bad prefetchw of NULL page For f2fs_read_data_pages, the f2fs_mpage_readpages gets "page == NULL", so that, the prefetchw(&page->flags) is operated on NULL. Fixes: f1e8866016 ("f2fs: expose f2fs_mpage_readpages") Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5a0d6fcf5669..8ec94d96c684 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1148,9 +1148,10 @@ static int f2fs_mpage_readpages(struct address_space *mapping, for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { - prefetchw(&page->flags); if (pages) { page = list_last_entry(pages, struct page, lru); + + prefetchw(&page->flags); list_del(&page->lru); if (add_to_page_cache_lru(page, mapping, page->index, GFP_KERNEL)) -- GitLab From 99d14c62536bb53cc618842fc25b338974b6aa22 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 13 Mar 2017 20:10:41 +0800 Subject: [PATCH 0593/5498] f2fs: cover update_free_nid_bitmap with nid_list_lock free_nid_bitmap and free_nid_count in update_free_nid_bitmap should be updated atomically, use nid_list_lock cover them to avoid race in concurrent scenario. Signed-off-by: Chao Yu Reviewed-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - fs/f2fs/node.c | 27 +++++++++++---------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 714bc6a2f82d..3efa1e987b78 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -664,7 +664,6 @@ struct f2fs_nm_info { unsigned char (*free_nid_bitmap)[NAT_ENTRY_BITMAP_SIZE]; unsigned char *nat_block_bitmap; unsigned short *free_nid_count; /* free nid count of NAT block */ - spinlock_t free_nid_lock; /* protect updating of nid count */ /* for checkpoint */ char *nat_bitmap; /* NAT bitmap pointer */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 1a8587d044cf..3d04ec1c03e0 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1824,7 +1824,7 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) } static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, - bool set, bool build, bool locked) + bool set, bool build) { struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); @@ -1838,14 +1838,10 @@ static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, else __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - if (!locked) - spin_lock(&nm_i->free_nid_lock); if (set) nm_i->free_nid_count[nat_ofs]++; else if (!build) nm_i->free_nid_count[nat_ofs]--; - if (!locked) - spin_unlock(&nm_i->free_nid_lock); } static void scan_nat_page(struct f2fs_sb_info *sbi, @@ -1874,7 +1870,9 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, blk_addr == NEW_ADDR); if (blk_addr == NULL_ADDR) freed = add_free_nid(sbi, start_nid, true); - update_free_nid_bitmap(sbi, start_nid, freed, true, false); + spin_lock(&NM_I(sbi)->nid_list_lock); + update_free_nid_bitmap(sbi, start_nid, freed, true); + spin_unlock(&NM_I(sbi)->nid_list_lock); } } @@ -2029,7 +2027,7 @@ retry: __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); nm_i->available_nids--; - update_free_nid_bitmap(sbi, *nid, false, false, false); + update_free_nid_bitmap(sbi, *nid, false, false); spin_unlock(&nm_i->nid_list_lock); return true; @@ -2085,7 +2083,7 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) nm_i->available_nids++; - update_free_nid_bitmap(sbi, nid, true, false, false); + update_free_nid_bitmap(sbi, nid, true, false); spin_unlock(&nm_i->nid_list_lock); @@ -2415,11 +2413,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, add_free_nid(sbi, nid, false); spin_lock(&NM_I(sbi)->nid_list_lock); NM_I(sbi)->available_nids++; - update_free_nid_bitmap(sbi, nid, true, false, false); + update_free_nid_bitmap(sbi, nid, true, false); spin_unlock(&NM_I(sbi)->nid_list_lock); } else { spin_lock(&NM_I(sbi)->nid_list_lock); - update_free_nid_bitmap(sbi, nid, false, false, false); + update_free_nid_bitmap(sbi, nid, false, false); spin_unlock(&NM_I(sbi)->nid_list_lock); } } @@ -2544,10 +2542,10 @@ inline void load_free_nid_bitmap(struct f2fs_sb_info *sbi) nid = i * NAT_ENTRY_PER_BLOCK; last_nid = (i + 1) * NAT_ENTRY_PER_BLOCK; - spin_lock(&nm_i->free_nid_lock); + spin_lock(&NM_I(sbi)->nid_list_lock); for (; nid < last_nid; nid++) - update_free_nid_bitmap(sbi, nid, true, true, true); - spin_unlock(&nm_i->free_nid_lock); + update_free_nid_bitmap(sbi, nid, true, true); + spin_unlock(&NM_I(sbi)->nid_list_lock); } for (i = 0; i < nm_i->nat_blocks; i++) { @@ -2638,9 +2636,6 @@ static int init_free_nid_cache(struct f2fs_sb_info *sbi) sizeof(unsigned short), GFP_KERNEL); if (!nm_i->free_nid_count) return -ENOMEM; - - spin_lock_init(&nm_i->free_nid_lock); - return 0; } -- GitLab From 1139ceda1e960b73831d61ec2ed59fd60c082ebc Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Fri, 10 Mar 2017 16:28:46 +0800 Subject: [PATCH 0594/5498] f2fs: cleanup the disk level filename updating As discuss with Jaegeuk and Chao, "Once checkpoint is done, f2fs doesn't need to update there-in filename at all." The disk-level filename is used only one case, 1. create a file A under a dir 2. sync A 3. godown 4. umount 5. mount (roll_forward) Only the rename/cross_rename changes the filename, if it happens, a. between step 1 and 2, the sync A will caused checkpoint, so that, the roll_forward at step 5 never happens. b. after step 2, the roll_forward happens, file A will roll forward to the result as after step 1. So that, any updating the disk filename is useless, just cleanup it. Signed-off-by: Kinglong Mee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 25 ++++--------------------- fs/f2fs/f2fs.h | 2 -- fs/f2fs/file.c | 8 -------- fs/f2fs/inline.c | 2 -- fs/f2fs/namei.c | 29 ----------------------------- 5 files changed, 4 insertions(+), 62 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 8525f609b293..16e009dfc3a7 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -337,24 +337,6 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage) set_page_dirty(ipage); } -int update_dent_inode(struct inode *inode, struct inode *to, - const struct qstr *name) -{ - struct page *page; - - if (file_enc_name(to)) - return 0; - - page = get_node_page(F2FS_I_SB(inode), inode->i_ino); - if (IS_ERR(page)) - return PTR_ERR(page); - - init_dent_inode(name, page); - f2fs_put_page(page, 1); - - return 0; -} - void do_make_empty_dir(struct inode *inode, struct inode *parent, struct f2fs_dentry_ptr *d) { @@ -438,8 +420,11 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, set_cold_node(inode, page); } - if (new_name) + if (new_name) { init_dent_inode(new_name, page); + if (f2fs_encrypted_inode(dir)) + file_set_enc_name(inode); + } /* * This file should be checkpointed during fsync. @@ -599,8 +584,6 @@ add_dentry: err = PTR_ERR(page); goto fail; } - if (f2fs_encrypted_inode(dir)) - file_set_enc_name(inode); } make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3efa1e987b78..8dc3436fa40e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2187,8 +2187,6 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr, struct page **page); void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, struct page *page, struct inode *inode); -int update_dent_inode(struct inode *inode, struct inode *to, - const struct qstr *name); void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, const struct qstr *name, f2fs_hash_t name_hash, unsigned int bit_pos); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f921aac0ef57..ec9b43204bd2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -112,20 +112,12 @@ static int get_parent_ino(struct inode *inode, nid_t *pino) { struct dentry *dentry; - if (file_enc_name(inode)) - return 0; - inode = igrab(inode); dentry = d_find_any_alias(inode); iput(inode); if (!dentry) return 0; - if (update_dent_inode(inode, inode, &dentry->d_name)) { - dput(dentry); - return 0; - } - *pino = parent_ino(dentry); dput(dentry); return 1; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 93c15337bcd9..701bbd8d10ba 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -528,8 +528,6 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, err = PTR_ERR(page); goto fail; } - if (f2fs_encrypted_inode(dir)) - file_set_enc_name(inode); } f2fs_wait_on_page_writeback(ipage, NODE, true); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 6e94e6caec15..01e837a0b43c 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -732,13 +732,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (err) goto put_out_dir; - err = update_dent_inode(old_inode, new_inode, - &new_dentry->d_name); - if (err) { - release_orphan_inode(sbi); - goto put_out_dir; - } - f2fs_set_link(new_dir, new_entry, new_page, old_inode); new_inode->i_ctime = current_time(new_inode); @@ -791,8 +784,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, down_write(&F2FS_I(old_inode)->i_sem); file_lost_pino(old_inode); - if (new_inode && file_enc_name(new_inode)) - file_set_enc_name(old_inode); up_write(&F2FS_I(old_inode)->i_sem); old_inode->i_ctime = current_time(old_inode); @@ -929,18 +920,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_lock_op(sbi); - err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name); - if (err) - goto out_unlock; - if (file_enc_name(new_inode)) - file_set_enc_name(old_inode); - - err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name); - if (err) - goto out_undo; - if (file_enc_name(old_inode)) - file_set_enc_name(new_inode); - /* update ".." directory entry info of old dentry */ if (old_dir_entry) f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir); @@ -984,14 +963,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) f2fs_sync_fs(sbi->sb, 1); return 0; -out_undo: - /* - * Still we may fail to recover name info of f2fs_inode here - * Drop it, once its name is set as encrypted - */ - update_dent_inode(old_inode, old_inode, &old_dentry->d_name); -out_unlock: - f2fs_unlock_op(sbi); out_new_dir: if (new_dir_entry) { f2fs_dentry_kunmap(new_inode, new_dir_page); -- GitLab From cfa4937409951d952f871724aee8d2081da5c5b0 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Wed, 15 Mar 2017 21:12:50 +0800 Subject: [PATCH 0595/5498] f2fs: sanity check of crc_offset from raw checkpoint The crc_offset towards or beyond the end of block is wrong, sanity check it. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 7b7d0eeda3ec..427cf5eaff09 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -679,7 +679,7 @@ static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr, *cp_block = (struct f2fs_checkpoint *)page_address(*cp_page); crc_offset = le32_to_cpu((*cp_block)->checksum_offset); - if (crc_offset >= blk_size) { + if (crc_offset > (blk_size - sizeof(__le32))) { f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc_offset: %zu", crc_offset); return -EINVAL; -- GitLab From 94031934f799822fcb1a302ccf03b777b3ef6211 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 18 Mar 2017 09:20:55 +0800 Subject: [PATCH 0596/5498] f2fs: avoid stat_inc_atomic_write for non-atomic file After filemap_write_and_wait_range fail, the FI_ATOMIC_FILE flags is removed, so that f2fs should not increase the stat of atomic_write. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index ec9b43204bd2..9bdad07bace5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1548,17 +1548,21 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); if (!get_dirty_pages(inode)) - goto out; + goto inc_stat; f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, "Unexpected flush for atomic writes: ino=%lu, npages=%u", inode->i_ino, get_dirty_pages(inode)); ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); - if (ret) + if (ret) { clear_inode_flag(inode, FI_ATOMIC_FILE); -out: + goto out; + } + +inc_stat: stat_inc_atomic_write(inode); stat_update_max_atomic_write(inode); +out: inode_unlock(inode); mnt_drop_write_file(filp); return ret; -- GitLab From 03544d5450a8d80d6db4596eef597e331f8c3509 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 18 Mar 2017 09:25:05 +0800 Subject: [PATCH 0597/5498] f2fs: calculate the f2fs_stat_info into base_mem The memory size of f2fs_stat_info also should be calculated. Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index dd714ec533ce..af2bd18321f0 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -156,7 +156,11 @@ static void update_mem_info(struct f2fs_sb_info *sbi) if (si->base_mem) goto get_cache; - si->base_mem = sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize; + /* build stat */ + si->base_mem = sizeof(struct f2fs_stat_info); + + /* build superblock */ + si->base_mem += sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize; si->base_mem += 2 * sizeof(struct f2fs_inode_info); si->base_mem += sizeof(*sbi->ckpt); si->base_mem += sizeof(struct percpu_counter) * NR_COUNT_TYPE; -- GitLab From b993b82084475194b425c56c7f11c71486d84a3c Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Sat, 18 Mar 2017 09:26:13 +0800 Subject: [PATCH 0598/5498] f2fs: more reasonable mem_size calculating of ino_entry Signed-off-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 3d04ec1c03e0..82867d48a3ad 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -63,8 +63,9 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) int i; for (i = 0; i <= UPDATE_INO; i++) - mem_size += (sbi->im[i].ino_num * - sizeof(struct ino_entry)) >> PAGE_SHIFT; + mem_size += sbi->im[i].ino_num * + sizeof(struct ino_entry); + mem_size >>= PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); } else if (type == EXTENT_CACHE) { mem_size = (atomic_read(&sbi->total_ext_tree) * -- GitLab From 85dc038beea8da30a8b973ea56044a31d55e1336 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 21 Mar 2017 20:09:45 +0800 Subject: [PATCH 0599/5498] f2fs: fix recording invalid last_victim When doing garbage collection, we try to record segment offset which locates at next one of last victim, using it as the start offset in next searching. But in some corner cases, recorded offset may cross the end of main segment area, it will cause incorrectly searching in dirty_segmap bitmap. This patch adds modular operation to avoid this issue. Reported-by: Yunlei He Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 418fd9881646..939be88a8833 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -361,6 +361,7 @@ next: sbi->last_victim[p.gc_mode] = last_victim + 1; else sbi->last_victim[p.gc_mode] = segno + 1; + sbi->last_victim[p.gc_mode] %= MAIN_SEGS(sbi); break; } } -- GitLab From ecf9ef3a6979e1e147b22af41a4a9161693b55e8 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 22 Mar 2017 11:59:30 +0800 Subject: [PATCH 0600/5498] f2fs: use set_page_private marcro in f2fs_trace_pid Use set_page_private marcro instead of operte page struct directly Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c index c82ab4048127..bccbbf2616d2 100644 --- a/fs/f2fs/trace.c +++ b/fs/f2fs/trace.c @@ -59,7 +59,7 @@ void f2fs_trace_pid(struct page *page) pid_t pid = task_pid_nr(current); void *p; - page->private = pid; + set_page_private(page, (unsigned long)pid); if (radix_tree_preload(GFP_NOFS)) return; -- GitLab From fa53a53904d7bece0b0a697b4375256443231918 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 22 Mar 2017 14:45:05 +0800 Subject: [PATCH 0601/5498] f2fs: fix race condition in between free nid allocator/initializer In below concurrent case, allocated nid can be loaded into free nid cache and be allocated again. Thread A Thread B - f2fs_create - f2fs_new_inode - alloc_nid - __insert_nid_to_list(ALLOC_NID_LIST) - f2fs_balance_fs_bg - build_free_nids - __build_free_nids - scan_nat_page - add_free_nid - __lookup_nat_cache - f2fs_add_link - init_inode_metadata - new_inode_page - new_node_page - set_node_addr - alloc_nid_done - __remove_nid_from_list(ALLOC_NID_LIST) - __insert_nid_to_list(FREE_NID_LIST) This patch makes nat cache lookup and free nid list operation being atomical to avoid this race condition. Signed-off-by: Jaegeuk Kim Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 63 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 82867d48a3ad..2f02c5a2f317 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1770,40 +1770,67 @@ static void __remove_nid_from_list(struct f2fs_sb_info *sbi, static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) { struct f2fs_nm_info *nm_i = NM_I(sbi); - struct free_nid *i; + struct free_nid *i, *e; struct nat_entry *ne; - int err; + int err = -EINVAL; + bool ret = false; /* 0 nid should not be used */ if (unlikely(nid == 0)) return false; - if (build) { - /* do not add allocated nids */ - ne = __lookup_nat_cache(nm_i, nid); - if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || - nat_get_blkaddr(ne) != NULL_ADDR)) - return false; - } - i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS); i->nid = nid; i->state = NID_NEW; - if (radix_tree_preload(GFP_NOFS)) { - kmem_cache_free(free_nid_slab, i); - return true; - } + if (radix_tree_preload(GFP_NOFS)) + goto err; spin_lock(&nm_i->nid_list_lock); + + if (build) { + /* + * Thread A Thread B + * - f2fs_create + * - f2fs_new_inode + * - alloc_nid + * - __insert_nid_to_list(ALLOC_NID_LIST) + * - f2fs_balance_fs_bg + * - build_free_nids + * - __build_free_nids + * - scan_nat_page + * - add_free_nid + * - __lookup_nat_cache + * - f2fs_add_link + * - init_inode_metadata + * - new_inode_page + * - new_node_page + * - set_node_addr + * - alloc_nid_done + * - __remove_nid_from_list(ALLOC_NID_LIST) + * - __insert_nid_to_list(FREE_NID_LIST) + */ + ne = __lookup_nat_cache(nm_i, nid); + if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || + nat_get_blkaddr(ne) != NULL_ADDR)) + goto err_out; + + e = __lookup_free_nid_list(nm_i, nid); + if (e) { + if (e->state == NID_NEW) + ret = true; + goto err_out; + } + } + ret = true; err = __insert_nid_to_list(sbi, i, FREE_NID_LIST, true); +err_out: spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); - if (err) { +err: + if (err) kmem_cache_free(free_nid_slab, i); - return true; - } - return true; + return ret; } static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) -- GitLab From b60940ef0e7387374dc057cdb4b770f7ced4052a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 22 Mar 2017 17:23:45 +0800 Subject: [PATCH 0602/5498] f2fs: show the max number of volatile operations This patch adds to show the max number of volatile operations which are conducting concurrently. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 10 ++++++++-- fs/f2fs/f2fs.h | 18 +++++++++++++++++- fs/f2fs/file.c | 5 +++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index af2bd18321f0..2d1aa4eb1fc3 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -51,7 +51,9 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->aw_cnt = atomic_read(&sbi->aw_cnt); + si->vw_cnt = atomic_read(&sbi->vw_cnt); si->max_aw_cnt = atomic_read(&sbi->max_aw_cnt); + si->max_vw_cnt = atomic_read(&sbi->max_vw_cnt); si->nr_wb_cp_data = get_pages(sbi, F2FS_WB_CP_DATA); si->nr_wb_data = get_pages(sbi, F2FS_WB_DATA); if (SM_I(sbi) && SM_I(sbi)->fcc_info) @@ -339,8 +341,10 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: %4d, Discard: %4d)\n", si->nr_wb_cp_data, si->nr_wb_data, si->nr_flush, si->nr_discard); - seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d)\n", - si->inmem_pages, si->aw_cnt, si->max_aw_cnt); + seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), " + "volatile IO: %4d (Max. %4d)\n", + si->inmem_pages, si->aw_cnt, si->max_aw_cnt, + si->vw_cnt, si->max_vw_cnt); seq_printf(s, " - nodes: %4d in %4d\n", si->ndirty_node, si->node_pages); seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n", @@ -440,7 +444,9 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) atomic_set(&sbi->inplace_count, 0); atomic_set(&sbi->aw_cnt, 0); + atomic_set(&sbi->vw_cnt, 0); atomic_set(&sbi->max_aw_cnt, 0); + atomic_set(&sbi->max_vw_cnt, 0); mutex_lock(&f2fs_stat_mutex); list_add_tail(&si->stat_list, &f2fs_stat_list); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8dc3436fa40e..61496a90b304 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1014,7 +1014,9 @@ struct f2fs_sb_info { atomic_t inline_inode; /* # of inline_data inodes */ atomic_t inline_dir; /* # of inline_dentry inodes */ atomic_t aw_cnt; /* # of atomic writes */ + atomic_t vw_cnt; /* # of volatile writes */ atomic_t max_aw_cnt; /* max # of atomic writes */ + atomic_t max_vw_cnt; /* max # of volatile writes */ int bg_gc; /* background gc calls */ unsigned int ndirty_inode[NR_INODE_TYPE]; /* # of dirty inodes */ #endif @@ -2429,7 +2431,7 @@ struct f2fs_stat_info { int total_count, utilization; int bg_gc, nr_wb_cp_data, nr_wb_data, nr_flush, nr_discard; int inline_xattr, inline_inode, inline_dir, append, update, orphans; - int aw_cnt, max_aw_cnt; + int aw_cnt, max_aw_cnt, vw_cnt, max_vw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; unsigned int bimodal, avg_vblocks; int util_free, util_valid, util_invalid; @@ -2512,6 +2514,17 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) if (cur > max) \ atomic_set(&F2FS_I_SB(inode)->max_aw_cnt, cur); \ } while (0) +#define stat_inc_volatile_write(inode) \ + (atomic_inc(&F2FS_I_SB(inode)->vw_cnt)) +#define stat_dec_volatile_write(inode) \ + (atomic_dec(&F2FS_I_SB(inode)->vw_cnt)) +#define stat_update_max_volatile_write(inode) \ + do { \ + int cur = atomic_read(&F2FS_I_SB(inode)->vw_cnt); \ + int max = atomic_read(&F2FS_I_SB(inode)->max_vw_cnt); \ + if (cur > max) \ + atomic_set(&F2FS_I_SB(inode)->max_vw_cnt, cur); \ + } while (0) #define stat_inc_seg_count(sbi, type, gc_type) \ do { \ struct f2fs_stat_info *si = F2FS_STAT(sbi); \ @@ -2568,6 +2581,9 @@ void f2fs_destroy_root_stats(void); #define stat_inc_atomic_write(inode) #define stat_dec_atomic_write(inode) #define stat_update_max_atomic_write(inode) +#define stat_inc_volatile_write(inode) +#define stat_dec_volatile_write(inode) +#define stat_update_max_volatile_write(inode) #define stat_inc_seg_type(sbi, curseg) #define stat_inc_block_count(sbi, curseg) #define stat_inc_inplace_blocks(sbi) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 9bdad07bace5..111ef80f46ea 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1441,6 +1441,7 @@ static int f2fs_release_file(struct inode *inode, struct file *filp) drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { clear_inode_flag(inode, FI_VOLATILE_FILE); + stat_dec_volatile_write(inode); set_inode_flag(inode, FI_DROP_CACHE); filemap_fdatawrite(inode->i_mapping); clear_inode_flag(inode, FI_DROP_CACHE); @@ -1628,6 +1629,9 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) if (ret) goto out; + stat_inc_volatile_write(inode); + stat_update_max_volatile_write(inode); + set_inode_flag(inode, FI_VOLATILE_FILE); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); out: @@ -1683,6 +1687,7 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) drop_inmem_pages(inode); if (f2fs_is_volatile_file(inode)) { clear_inode_flag(inode, FI_VOLATILE_FILE); + stat_dec_volatile_write(inode); ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); } -- GitLab From d912071487fa057034efffa2bb8cde4401099ba2 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 22 Mar 2017 17:23:46 +0800 Subject: [PATCH 0603/5498] f2fs: don't track volatile file in dirty inode list Don't track volatile file in dirty inode list, otherwise with data_flush option, background thread will entry into endless loop for flushing journal file's pages. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 427cf5eaff09..73562abb639c 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -818,7 +818,9 @@ static void __add_dirty_inode(struct inode *inode, enum inode_type type) return; set_inode_flag(inode, flag); - list_add_tail(&F2FS_I(inode)->dirty_list, &sbi->inode_list[type]); + if (!f2fs_is_volatile_file(inode)) + list_add_tail(&F2FS_I(inode)->dirty_list, + &sbi->inode_list[type]); stat_inc_dirty_inode(sbi, type); } -- GitLab From e79c49f3a525589d614fbc44da8ade664c8241b8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 23 Mar 2017 13:38:25 +0800 Subject: [PATCH 0604/5498] f2fs: clean up xattr operation 1. don't allocate redundant memory in read_all_xattrs. 2. introduce RESERVED_XATTR_SIZE for cleanup. Signed-off-by: Chao Yu Reviewed-by: Kinglong Mee Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 25 +++++++++++-------------- fs/f2fs/xattr.h | 3 ++- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 1bb1acfee68e..10d16a29e2b5 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -299,15 +299,13 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, void *cur_addr, *txattr_addr, *last_addr = NULL; nid_t xnid = F2FS_I(inode)->i_xattr_nid; unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0; - unsigned int inline_size = 0; + unsigned int inline_size = inline_xattr_size(inode); int err = 0; - inline_size = inline_xattr_size(inode); - if (!size && !inline_size) return -ENODATA; - txattr_addr = kzalloc(inline_size + size + sizeof(__u32), + txattr_addr = kzalloc(inline_size + size + RESERVED_XATTR_SIZE, GFP_F2FS_ZERO); if (!txattr_addr) return -ENOMEM; @@ -377,13 +375,14 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_xattr_header *header; - size_t size = PAGE_SIZE, inline_size = 0; + nid_t xnid = F2FS_I(inode)->i_xattr_nid; + unsigned int size = VALID_XATTR_BLOCK_SIZE; + unsigned int inline_size = inline_xattr_size(inode); void *txattr_addr; int err; - inline_size = inline_xattr_size(inode); - - txattr_addr = kzalloc(inline_size + size, GFP_F2FS_ZERO); + txattr_addr = kzalloc(inline_size + size + RESERVED_XATTR_SIZE, + GFP_F2FS_ZERO); if (!txattr_addr) return -ENOMEM; @@ -407,19 +406,19 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, } /* read from xattr node block */ - if (F2FS_I(inode)->i_xattr_nid) { + if (xnid) { struct page *xpage; void *xattr_addr; /* The inode already has an extended attribute block. */ - xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); + xpage = get_node_page(sbi, xnid); if (IS_ERR(xpage)) { err = PTR_ERR(xpage); goto fail; } xattr_addr = page_address(xpage); - memcpy(txattr_addr + inline_size, xattr_addr, PAGE_SIZE); + memcpy(txattr_addr + inline_size, xattr_addr, size); f2fs_put_page(xpage, 1); } @@ -441,14 +440,12 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, void *txattr_addr, struct page *ipage) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - size_t inline_size = 0; + size_t inline_size = inline_xattr_size(inode); void *xattr_addr; struct page *xpage; nid_t new_nid = 0; int err; - inline_size = inline_xattr_size(inode); - if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid) if (!alloc_nid(sbi, &new_nid)) return -ENOSPC; diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index ba64f43d163d..d111568daf83 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -73,7 +73,8 @@ struct f2fs_xattr_entry { !IS_XATTR_LAST_ENTRY(entry);\ entry = XATTR_NEXT_ENTRY(entry)) #define MAX_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer)) -#define VALID_XATTR_BLOCK_SIZE (MAX_XATTR_BLOCK_SIZE - sizeof(__u32)) +#define RESERVED_XATTR_SIZE (sizeof(__u32)) +#define VALID_XATTR_BLOCK_SIZE (MAX_XATTR_BLOCK_SIZE - RESERVED_XATTR_SIZE) #define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \ VALID_XATTR_BLOCK_SIZE) -- GitLab From 655a40092b09126e74407acd5332093050e37537 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 23 Mar 2017 13:38:26 +0800 Subject: [PATCH 0605/5498] f2fs: don't reserve additional space in xattr block In this patch, we change xattr block disk layout as below: Before: xattr node block layout +---------------------------------------------+---------------+-------------+ | node block xattr entries | reserved | node footer | | 4068 Bytes | 4 Bytes | 24 Bytes | In memory layout +--------------------+---------------------------------+--------------------+ | inline xattr | node block xattr entries | reserved | | 200 Bytes | 4068 Bytes | 4 Bytes | After: xattr node block layout +-------------------------------------------------------------+-------------+ | node block xattr entries | node footer | | 4072 Bytes | 24 Bytes | In memory layout +--------------------+---------------------------------+--------------------+ | inline xattr | node block xattr entries | reserved | | 200 Bytes | 4072 Bytes | 4 Bytes | With this change, we don't need to reserve additional space in node block, just keep reserved space in logical in-memory layout. So that it would help to enlarge valid free space of xattr node block. As tested, generic/026 shows max stored xattr entires number increases from 531 to 532 when inline_xattr option is enabled. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 6 +++--- fs/f2fs/xattr.h | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 10d16a29e2b5..c01e2b61e747 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -305,7 +305,7 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, if (!size && !inline_size) return -ENODATA; - txattr_addr = kzalloc(inline_size + size + RESERVED_XATTR_SIZE, + txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE, GFP_F2FS_ZERO); if (!txattr_addr) return -ENOMEM; @@ -381,7 +381,7 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, void *txattr_addr; int err; - txattr_addr = kzalloc(inline_size + size + RESERVED_XATTR_SIZE, + txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE, GFP_F2FS_ZERO); if (!txattr_addr) return -ENOMEM; @@ -500,7 +500,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, } xattr_addr = page_address(xpage); - memcpy(xattr_addr, txattr_addr + inline_size, MAX_XATTR_BLOCK_SIZE); + memcpy(xattr_addr, txattr_addr + inline_size, VALID_XATTR_BLOCK_SIZE); set_page_dirty(xpage); f2fs_put_page(xpage, 1); diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index d111568daf83..91f3bd88dcc6 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -72,9 +72,8 @@ struct f2fs_xattr_entry { for (entry = XATTR_FIRST_ENTRY(addr);\ !IS_XATTR_LAST_ENTRY(entry);\ entry = XATTR_NEXT_ENTRY(entry)) -#define MAX_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer)) -#define RESERVED_XATTR_SIZE (sizeof(__u32)) -#define VALID_XATTR_BLOCK_SIZE (MAX_XATTR_BLOCK_SIZE - RESERVED_XATTR_SIZE) +#define VALID_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer)) +#define XATTR_PADDING_SIZE (sizeof(__u32)) #define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \ VALID_XATTR_BLOCK_SIZE) -- GitLab From 50bc8dec36a7e3ecb383df5ebfb70ac43dd20333 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 13 Mar 2017 20:22:18 +0800 Subject: [PATCH 0606/5498] f2fs: allow write page cache when writting cp This patch allow write data to normal file when writting new checkpoint. We relax three limitations for write_begin path: 1. data allocation 2. node allocation 3. variables in checkpoint Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 40 ++++++++++++++++++++++++++++------------ fs/f2fs/data.c | 28 ++++++++++++++++++++++------ fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 12 ++++++------ fs/f2fs/super.c | 1 + 5 files changed, 58 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 73562abb639c..41ebce6f7f49 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -945,6 +945,19 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi) return 0; } +static void __prepare_cp_block(struct f2fs_sb_info *sbi) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct f2fs_nm_info *nm_i = NM_I(sbi); + nid_t last_nid = nm_i->next_scan_nid; + + next_free_nid(sbi, &last_nid); + ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi)); + ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi)); + ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi)); + ckpt->next_free_nid = cpu_to_le32(last_nid); +} + /* * Freeze all the FS-operations for checkpoint. */ @@ -971,7 +984,14 @@ retry_flush_dents: goto retry_flush_dents; } + /* + * POR: we should ensure that there are no dirty node pages + * until finishing nat/sit flush. inode->i_blocks can be updated. + */ + down_write(&sbi->node_change); + if (get_pages(sbi, F2FS_DIRTY_IMETA)) { + up_write(&sbi->node_change); f2fs_unlock_all(sbi); err = f2fs_sync_inode_meta(sbi); if (err) @@ -979,10 +999,6 @@ retry_flush_dents: goto retry_flush_dents; } - /* - * POR: we should ensure that there are no dirty node pages - * until finishing nat/sit flush. - */ retry_flush_nodes: down_write(&sbi->node_write); @@ -990,11 +1006,19 @@ retry_flush_nodes: up_write(&sbi->node_write); err = sync_node_pages(sbi, &wbc); if (err) { + up_write(&sbi->node_change); f2fs_unlock_all(sbi); goto out; } goto retry_flush_nodes; } + + /* + * sbi->node_change is used only for AIO write_begin path which produces + * dirty node blocks and some checkpoint values by block allocation. + */ + __prepare_cp_block(sbi); + up_write(&sbi->node_change); out: blk_finish_plug(&plug); return err; @@ -1062,7 +1086,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; - nid_t last_nid = nm_i->next_scan_nid; block_t start_blk; unsigned int data_sum_blocks, orphan_blocks; __u32 crc32 = 0; @@ -1079,14 +1102,11 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) return -EIO; } - next_free_nid(sbi, &last_nid); - /* * modify checkpoint * version number is already updated */ ckpt->elapsed_time = cpu_to_le64(get_mtime(sbi)); - ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi)); ckpt->free_segment_count = cpu_to_le32(free_segments(sbi)); for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) { ckpt->cur_node_segno[i] = @@ -1105,10 +1125,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) curseg_alloc_type(sbi, i + CURSEG_HOT_DATA); } - ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi)); - ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi)); - ckpt->next_free_nid = cpu_to_le32(last_nid); - /* 2 cp + n data seg summary + orphan inode blocks */ data_sum_blocks = npages_for_summary_flush(sbi, false); spin_lock(&sbi->cp_lock); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8ec94d96c684..c9e10bf1afc6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -784,6 +784,21 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, return err; } +static inline void __do_map_lock(struct f2fs_sb_info *sbi, int flag, bool lock) +{ + if (flag == F2FS_GET_BLOCK_PRE_AIO) { + if (lock) + down_read(&sbi->node_change); + else + up_read(&sbi->node_change); + } else { + if (lock) + f2fs_lock_op(sbi); + else + f2fs_unlock_op(sbi); + } +} + /* * f2fs_map_blocks() now supported readahead/bmap/rw direct_IO with * f2fs_map_blocks structure. @@ -826,7 +841,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, next_dnode: if (create) - f2fs_lock_op(sbi); + __do_map_lock(sbi, flag, true); /* When reading holes, we need its node page */ set_new_dnode(&dn, inode, NULL, NULL, 0); @@ -936,7 +951,7 @@ skip: f2fs_put_dnode(&dn); if (create) { - f2fs_unlock_op(sbi); + __do_map_lock(sbi, flag, false); f2fs_balance_fs(sbi, dn.node_changed); } goto next_dnode; @@ -945,7 +960,7 @@ sync_out: f2fs_put_dnode(&dn); unlock_out: if (create) { - f2fs_unlock_op(sbi); + __do_map_lock(sbi, flag, false); f2fs_balance_fs(sbi, dn.node_changed); } out: @@ -1683,7 +1698,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, if (f2fs_has_inline_data(inode) || (pos & PAGE_MASK) >= i_size_read(inode)) { - f2fs_lock_op(sbi); + __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true); locked = true; } restart: @@ -1719,7 +1734,8 @@ restart: err = get_dnode_of_data(&dn, index, LOOKUP_NODE); if (err || dn.data_blkaddr == NULL_ADDR) { f2fs_put_dnode(&dn); - f2fs_lock_op(sbi); + __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, + true); locked = true; goto restart; } @@ -1733,7 +1749,7 @@ out: f2fs_put_dnode(&dn); unlock_out: if (locked) - f2fs_unlock_op(sbi); + __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false); return err; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 61496a90b304..44c8e662a0ec 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -929,6 +929,7 @@ struct f2fs_sb_info { struct mutex cp_mutex; /* checkpoint procedure lock */ struct rw_semaphore cp_rwsem; /* blocking FS operations */ struct rw_semaphore node_write; /* locking node writes */ + struct rw_semaphore node_change; /* locking node change */ wait_queue_head_t cp_wait; unsigned long last_time[MAX_TIME]; /* to store time in jiffies */ long interval_time[MAX_TIME]; /* to store thresholds */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2f02c5a2f317..50750a5dec95 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2457,10 +2457,11 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, f2fs_put_page(page, 1); } - f2fs_bug_on(sbi, set->entry_cnt); - - radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set); - kmem_cache_free(nat_entry_set_slab, set); + /* Allow dirty nats by node block allocation in write_begin */ + if (!set->entry_cnt) { + radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set); + kmem_cache_free(nat_entry_set_slab, set); + } } /* @@ -2505,8 +2506,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) __flush_nat_entry_set(sbi, set, cpc); up_write(&nm_i->nat_tree_lock); - - f2fs_bug_on(sbi, nm_i->dirty_nat_cnt); + /* Allow dirty nats by node block allocation in write_begin */ } static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 895ebb5e6934..64b12320a8ca 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1910,6 +1910,7 @@ try_onemore: mutex_init(&sbi->gc_mutex); mutex_init(&sbi->cp_mutex); init_rwsem(&sbi->node_write); + init_rwsem(&sbi->node_change); /* disallow all the data/node/meta page writes */ set_sbi_flag(sbi, SBI_POR_DOING); -- GitLab From 8ba8540deb81b6e4f6e73815a4ca0cdc0a94b638 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 25 Mar 2017 00:03:02 -0700 Subject: [PATCH 0607/5498] f2fs: fix wrong max cost initialization This patch fixes missing increased max cost caused by a patch that we increased cose of data segments in greedy algorithm. Cc: # v4.10+ Fixes: b9cd20619 "f2fs: node segment is prior to data segment selected victim" Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 939be88a8833..704bea678d37 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -182,7 +182,7 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi, if (p->alloc_mode == SSR) return sbi->blocks_per_seg; if (p->gc_mode == GC_GREEDY) - return sbi->blocks_per_seg * p->ofs_unit; + return 2 * sbi->blocks_per_seg * p->ofs_unit; else if (p->gc_mode == GC_CB) return UINT_MAX; else /* No other gc_mode */ -- GitLab From 882323d22f0a1b1e0ade0c4506b128ac199ba301 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 24 Mar 2017 20:41:45 -0400 Subject: [PATCH 0608/5498] f2fs: allocate node and hot data in the beginning of partition In order to give more spatial locality, this patch changes the block allocation policy which assigns beginning of partition for small and hot data/node blocks. In order to do this, we set noheap allocation by default and introduce another mount option, heap, to reset it back. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/super.c --- fs/f2fs/gc.c | 6 +++++- fs/f2fs/segment.c | 9 +++++++++ fs/f2fs/super.c | 10 +++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 704bea678d37..63fefef04184 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -172,7 +172,11 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, if (gc_type != FG_GC && p->max_search > sbi->max_victim_search) p->max_search = sbi->max_victim_search; - p->offset = sbi->last_victim[p->gc_mode]; + /* let's select beginning hot/small space first */ + if (type == CURSEG_HOT_DATA || IS_NODESEG(type)) + p->offset = 0; + else + p->offset = sbi->last_victim[p->gc_mode]; } static unsigned int get_max_cost(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 402ea41986cf..267c23645cc0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1598,6 +1598,14 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) __set_sit_entry_type(sbi, type, curseg->segno, modified); } +static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) +{ + if (type == CURSEG_HOT_DATA || IS_NODESEG(type)) + return 0; + + return CURSEG_I(sbi, type)->segno; +} + /* * Allocate a current working segment. * This function always allocates a free segment in LFS manner. @@ -1616,6 +1624,7 @@ static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec) if (test_opt(sbi, NOHEAP)) dir = ALLOC_RIGHT; + segno = __get_next_segno(sbi, type); get_new_segment(sbi, &segno, new_sec, dir); curseg->next_segno = segno; reset_curseg(sbi, type, 1); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 64b12320a8ca..39649b73e2f9 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -83,6 +83,7 @@ enum { Opt_discard, Opt_nodiscard, Opt_noheap, + Opt_heap, Opt_user_xattr, Opt_nouser_xattr, Opt_acl, @@ -115,6 +116,7 @@ static match_table_t f2fs_tokens = { {Opt_discard, "discard"}, {Opt_nodiscard, "nodiscard"}, {Opt_noheap, "no_heap"}, + {Opt_heap, "heap"}, {Opt_user_xattr, "user_xattr"}, {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, @@ -433,6 +435,9 @@ static int parse_options(struct super_block *sb, char *options) case Opt_noheap: set_opt(sbi, NOHEAP); break; + case Opt_heap: + clear_opt(sbi, NOHEAP); + break; #ifdef CONFIG_F2FS_FS_XATTR case Opt_user_xattr: set_opt(sbi, XATTR_USER); @@ -902,7 +907,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) if (test_opt(sbi, DISCARD)) seq_puts(seq, ",discard"); if (test_opt(sbi, NOHEAP)) - seq_puts(seq, ",no_heap_alloc"); + seq_puts(seq, ",no_heap"); + else + seq_puts(seq, ",heap"); #ifdef CONFIG_F2FS_FS_XATTR if (test_opt(sbi, XATTR_USER)) seq_puts(seq, ",user_xattr"); @@ -1036,6 +1043,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, INLINE_DATA); set_opt(sbi, INLINE_DENTRY); set_opt(sbi, EXTENT_CACHE); + set_opt(sbi, NOHEAP); set_opt(sbi, FLUSH_MERGE); if (f2fs_sb_mounted_blkzoned(sbi->sb)) { set_opt_mode(sbi, F2FS_MOUNT_LFS); -- GitLab From e1ca40afc68af4e261f7ea9ae052ecc2f849c70b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 24 Mar 2017 21:08:56 -0400 Subject: [PATCH 0609/5498] f2fs: start SSR much eariler to avoid FG_GC This patch initiates SSR much eariler, resulting in less FG_GC. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 32fa84b4dde6..f12206a20ed0 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -494,7 +494,7 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) return false; return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + - reserved_sections(sbi) + 1); + 2 * reserved_sections(sbi)); } static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, -- GitLab From 540bbb1626b2b5165961cb6a6645d091feba195d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 21 Mar 2017 10:59:50 -0400 Subject: [PATCH 0610/5498] f2fs: relax node version check for victim data in gc - has_not_enough_free_secs node_secs: 0 dent_secs: 0 freed:0 free_segments:103 reserved:104 - f2fs_gc - get_victim_by_default alloc_mode 0, gc_mode 1, max_search 2672, offset 4654, ofs_unit 1 - do_garbage_collect start_segno 3976, end_segno 3977 type 0 - is_alive nid 22797, blkaddr 2131882, ofs_in_node 0, version 0x8/0x0 - gc_data_segment 766, segno 3976, block 512/426 not alive So, this patch fixes subtle corrupted case where node version does not match to summary version which results in infinite loop by gc. Reported-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 63fefef04184..c52656ccbde5 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -555,8 +555,10 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, get_node_info(sbi, nid, dni); if (sum->version != dni->version) { - f2fs_put_page(node_page, 1); - return false; + f2fs_msg(sbi->sb, KERN_WARNING, + "%s: valid data with mismatched node version.", + __func__); + set_sbi_flag(sbi, SBI_NEED_FSCK); } *nofs = ofs_of_node(node_page); -- GitLab From 40605acbd8996b6e205a736db2cedf73aed73464 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 25 Mar 2017 17:19:58 +0800 Subject: [PATCH 0611/5498] f2fs: show issued flush/discard count Show historical count of flush command and discard command. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 24 ++++++++++++++++-------- fs/f2fs/f2fs.h | 9 ++++++--- fs/f2fs/segment.c | 37 +++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 2d1aa4eb1fc3..23ee44796741 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -56,12 +56,18 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->max_vw_cnt = atomic_read(&sbi->max_vw_cnt); si->nr_wb_cp_data = get_pages(sbi, F2FS_WB_CP_DATA); si->nr_wb_data = get_pages(sbi, F2FS_WB_DATA); - if (SM_I(sbi) && SM_I(sbi)->fcc_info) - si->nr_flush = - atomic_read(&SM_I(sbi)->fcc_info->submit_flush); - if (SM_I(sbi) && SM_I(sbi)->dcc_info) - si->nr_discard = - atomic_read(&SM_I(sbi)->dcc_info->submit_discard); + if (SM_I(sbi) && SM_I(sbi)->fcc_info) { + si->nr_flushed = + atomic_read(&SM_I(sbi)->fcc_info->issued_flush); + si->nr_flushing = + atomic_read(&SM_I(sbi)->fcc_info->issing_flush); + } + if (SM_I(sbi) && SM_I(sbi)->dcc_info) { + si->nr_discarded = + atomic_read(&SM_I(sbi)->dcc_info->issued_discard); + si->nr_discarding = + atomic_read(&SM_I(sbi)->dcc_info->issing_discard); + } si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->rsvd_segs = reserved_segments(sbi); si->overp_segs = overprovision_segments(sbi); @@ -338,9 +344,11 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: %4d, Discard: %4d)\n", + seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d), " + "Discard: (%4d %4d))\n", si->nr_wb_cp_data, si->nr_wb_data, - si->nr_flush, si->nr_discard); + si->nr_flushing, si->nr_flushed, + si->nr_discarding, si->nr_discarded); seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), " "volatile IO: %4d (Max. %4d)\n", si->inmem_pages, si->aw_cnt, si->max_aw_cnt, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 44c8e662a0ec..dbf18d7dbbb2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -313,7 +313,8 @@ struct discard_cmd_control { wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ struct mutex cmd_lock; int max_discards; /* max. discards to be issued */ - atomic_t submit_discard; /* # of issued discard */ + atomic_t issued_discard; /* # of issued discard */ + atomic_t issing_discard; /* # of issing discard */ }; /* for the list of fsync inodes, used only during recovery */ @@ -742,7 +743,8 @@ struct flush_cmd { struct flush_cmd_control { struct task_struct *f2fs_issue_flush; /* flush thread */ wait_queue_head_t flush_wait_queue; /* waiting queue for wake-up */ - atomic_t submit_flush; /* # of issued flushes */ + atomic_t issued_flush; /* # of issued flushes */ + atomic_t issing_flush; /* # of issing flushes */ struct llist_head issue_list; /* list for command issue */ struct llist_node *dispatch_list; /* list for command dispatch */ }; @@ -2430,7 +2432,8 @@ struct f2fs_stat_info { unsigned int ndirty_dirs, ndirty_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits, free_nids, alloc_nids; int total_count, utilization; - int bg_gc, nr_wb_cp_data, nr_wb_data, nr_flush, nr_discard; + int bg_gc, nr_wb_cp_data, nr_wb_data; + int nr_flushing, nr_flushed, nr_discarding, nr_discarded; int inline_xattr, inline_inode, inline_dir, append, update, orphans; int aw_cnt, max_aw_cnt, vw_cnt, max_vw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 267c23645cc0..0ef51a305dc4 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -490,6 +490,8 @@ repeat: fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); ret = submit_flush_wait(sbi); + atomic_inc(&fcc->issued_flush); + llist_for_each_entry_safe(cmd, next, fcc->dispatch_list, llnode) { cmd->ret = ret; @@ -507,25 +509,29 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) { struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; struct flush_cmd cmd; + int ret; if (test_opt(sbi, NOBARRIER)) return 0; - if (!test_opt(sbi, FLUSH_MERGE)) - return submit_flush_wait(sbi); - - if (!atomic_read(&fcc->submit_flush)) { - int ret; + if (!test_opt(sbi, FLUSH_MERGE)) { + ret = submit_flush_wait(sbi); + atomic_inc(&fcc->issued_flush); + return ret; + } - atomic_inc(&fcc->submit_flush); + if (!atomic_read(&fcc->issing_flush)) { + atomic_inc(&fcc->issing_flush); ret = submit_flush_wait(sbi); - atomic_dec(&fcc->submit_flush); + atomic_dec(&fcc->issing_flush); + + atomic_inc(&fcc->issued_flush); return ret; } init_completion(&cmd.wait); - atomic_inc(&fcc->submit_flush); + atomic_inc(&fcc->issing_flush); llist_add(&cmd.llnode, &fcc->issue_list); if (!fcc->dispatch_list) @@ -533,10 +539,10 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) if (fcc->f2fs_issue_flush) { wait_for_completion(&cmd.wait); - atomic_dec(&fcc->submit_flush); + atomic_dec(&fcc->issing_flush); } else { llist_del_all(&fcc->issue_list); - atomic_set(&fcc->submit_flush, 0); + atomic_set(&fcc->issing_flush, 0); } return cmd.ret; @@ -556,7 +562,8 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); if (!fcc) return -ENOMEM; - atomic_set(&fcc->submit_flush, 0); + atomic_set(&fcc->issued_flush, 0); + atomic_set(&fcc->issing_flush, 0); init_waitqueue_head(&fcc->flush_wait_queue); init_llist_head(&fcc->issue_list); SM_I(sbi)->fcc_info = fcc; @@ -691,7 +698,7 @@ static void __add_discard_cmd(struct f2fs_sb_info *sbi, static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc) { if (dc->state == D_DONE) - atomic_dec(&(SM_I(sbi)->dcc_info->submit_discard)); + atomic_dec(&(SM_I(sbi)->dcc_info->issing_discard)); if (dc->error == -EOPNOTSUPP) dc->error = 0; @@ -810,7 +817,8 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, if (!dc->error) { /* should keep before submission to avoid D_DONE right away */ dc->state = D_SUBMIT; - atomic_inc(&dcc->submit_discard); + atomic_inc(&dcc->issued_discard); + atomic_inc(&dcc->issing_discard); if (bio) { bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; @@ -1214,7 +1222,8 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&dcc->discard_entry_list); INIT_LIST_HEAD(&dcc->discard_cmd_list); mutex_init(&dcc->cmd_lock); - atomic_set(&dcc->submit_discard, 0); + atomic_set(&dcc->issued_discard, 0); + atomic_set(&dcc->issing_discard, 0); dcc->nr_discards = 0; dcc->max_discards = 0; -- GitLab From 8912c9e1c2cf210a020a18c88ba45b5594f82e9f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 25 Mar 2017 17:19:59 +0800 Subject: [PATCH 0612/5498] f2fs: count discard command entry Adds to count discard command entry and show the number in debugfs, also fix to add cost of discard command cache into total comsumed memory footprint. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 12 +++++++++--- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/segment.c | 4 ++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 23ee44796741..ce0a3db52708 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -67,6 +67,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) atomic_read(&SM_I(sbi)->dcc_info->issued_discard); si->nr_discarding = atomic_read(&SM_I(sbi)->dcc_info->issing_discard); + si->nr_discard_cmd = + atomic_read(&SM_I(sbi)->dcc_info->discard_cmd_cnt); } si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->rsvd_segs = reserved_segments(sbi); @@ -220,8 +222,11 @@ get_cache: /* build merge flush thread */ if (SM_I(sbi)->fcc_info) si->cache_mem += sizeof(struct flush_cmd_control); - if (SM_I(sbi)->dcc_info) + if (SM_I(sbi)->dcc_info) { si->cache_mem += sizeof(struct discard_cmd_control); + si->cache_mem += sizeof(struct discard_cmd) * + atomic_read(&SM_I(sbi)->dcc_info->discard_cmd_cnt); + } /* free nids */ si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID_LIST] + @@ -345,10 +350,11 @@ static int stat_show(struct seq_file *s, void *v) si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d), " - "Discard: (%4d %4d))\n", + "Discard: (%4d %4d)) cmd: %4d\n", si->nr_wb_cp_data, si->nr_wb_data, si->nr_flushing, si->nr_flushed, - si->nr_discarding, si->nr_discarded); + si->nr_discarding, si->nr_discarded, + si->nr_discard_cmd); seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), " "volatile IO: %4d (Max. %4d)\n", si->inmem_pages, si->aw_cnt, si->max_aw_cnt, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dbf18d7dbbb2..4af5545671fb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -315,6 +315,7 @@ struct discard_cmd_control { int max_discards; /* max. discards to be issued */ atomic_t issued_discard; /* # of issued discard */ atomic_t issing_discard; /* # of issing discard */ + atomic_t discard_cmd_cnt; /* # of cached cmd count */ }; /* for the list of fsync inodes, used only during recovery */ @@ -2434,6 +2435,7 @@ struct f2fs_stat_info { int total_count, utilization; int bg_gc, nr_wb_cp_data, nr_wb_data; int nr_flushing, nr_flushed, nr_discarding, nr_discarded; + int nr_discard_cmd; int inline_xattr, inline_inode, inline_dir, append, update, orphans; int aw_cnt, max_aw_cnt, vw_cnt, max_vw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0ef51a305dc4..2106e8382efa 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -693,6 +693,8 @@ static void __add_discard_cmd(struct f2fs_sb_info *sbi, mutex_lock(&dcc->cmd_lock); list_add_tail(&dc->list, cmd_list); mutex_unlock(&dcc->cmd_lock); + + atomic_inc(&dcc->discard_cmd_cnt); } static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc) @@ -708,6 +710,7 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *d "Issue discard failed, ret: %d", dc->error); list_del(&dc->list); kmem_cache_free(discard_cmd_slab, dc); + atomic_dec(&SM_I(sbi)->dcc_info->discard_cmd_cnt); } static void f2fs_submit_discard_endio(struct bio *bio, int err) @@ -1224,6 +1227,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) mutex_init(&dcc->cmd_lock); atomic_set(&dcc->issued_discard, 0); atomic_set(&dcc->issing_discard, 0); + atomic_set(&dcc->discard_cmd_cnt, 0); dcc->nr_discards = 0; dcc->max_discards = 0; -- GitLab From 4a6712f707b1ed6b1ad411f2dccbfec2668ea13e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 27 Mar 2017 18:14:04 +0800 Subject: [PATCH 0613/5498] f2fs: clean up destroy_discard_cmd_control Remove unneeded parameter and simply change flow in destroy_discard_cmd_control. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2106e8382efa..29a96205ad49 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1246,20 +1246,22 @@ init_thread: return err; } -static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi, bool free) +static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - if (dcc && dcc->f2fs_issue_discard) { + if (!dcc) + return; + + if (dcc->f2fs_issue_discard) { struct task_struct *discard_thread = dcc->f2fs_issue_discard; dcc->f2fs_issue_discard = NULL; kthread_stop(discard_thread); } - if (free) { - kfree(dcc); - SM_I(sbi)->dcc_info = NULL; - } + + kfree(dcc); + SM_I(sbi)->dcc_info = NULL; } static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) @@ -3152,7 +3154,7 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi) if (!sm_info) return; destroy_flush_cmd_control(sbi, true); - destroy_discard_cmd_control(sbi, true); + destroy_discard_cmd_control(sbi); destroy_dirty_segmap(sbi); destroy_curseg(sbi); destroy_free_segmap(sbi); -- GitLab From b08f1e87151395d410db98aecee0a41a204b66ee Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 28 Mar 2017 18:18:50 +0800 Subject: [PATCH 0614/5498] f2fs: use bitmap in discard_entry This patch changes to use bitmap instead of extent in struct discard_entry to indicate discard range in one segment, for fragmented space, this implementation can save memory footprint. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 6 ++-- fs/f2fs/segment.c | 72 ++++++++++++++++++++++++++--------------------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4af5545671fb..ee024cf301e8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -281,11 +281,11 @@ struct inode_entry { struct inode *inode; /* vfs inode pointer */ }; -/* for the list of blockaddresses to be discarded */ +/* for the bitmap indicate blocks to be discarded */ struct discard_entry { struct list_head list; /* list head */ - block_t blkaddr; /* block address to be discarded */ - int len; /* # of consecutive blocks of the discard */ + block_t start_blkaddr; /* start blockaddr of current segment */ + unsigned char discard_map[SIT_VBLOCK_MAP_SIZE]; /* segment discard bitmap */ }; enum { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 29a96205ad49..3300446cc4b8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1041,32 +1041,6 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, return err; } -static void __add_discard_entry(struct f2fs_sb_info *sbi, - struct cp_control *cpc, struct seg_entry *se, - unsigned int start, unsigned int end) -{ - struct list_head *head = &SM_I(sbi)->dcc_info->discard_entry_list; - struct discard_entry *new, *last; - - if (!list_empty(head)) { - last = list_last_entry(head, struct discard_entry, list); - if (START_BLOCK(sbi, cpc->trim_start) + start == - last->blkaddr + last->len && - last->len < MAX_DISCARD_BLOCKS(sbi)) { - last->len += end - start; - goto done; - } - } - - new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); - INIT_LIST_HEAD(&new->list); - new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start; - new->len = end - start; - list_add_tail(&new->list, head); -done: - SM_I(sbi)->dcc_info->nr_discards += end - start; -} - static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, bool check_only) { @@ -1079,6 +1053,8 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, unsigned long *dmap = SIT_I(sbi)->tmp_map; unsigned int start = 0, end = -1; bool force = (cpc->reason == CP_DISCARD); + struct discard_entry *de = NULL; + struct list_head *head = &SM_I(sbi)->dcc_info->discard_entry_list; int i; if (se->valid_blocks == max_blocks || !f2fs_discard_en(sbi)) @@ -1110,7 +1086,17 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, if (check_only) return true; - __add_discard_entry(sbi, cpc, se, start, end); + if (!de) { + de = f2fs_kmem_cache_alloc(discard_entry_slab, + GFP_F2FS_ZERO); + de->start_blkaddr = START_BLOCK(sbi, cpc->trim_start); + list_add_tail(&de->list, head); + } + + for (i = start; i < end; i++) + __set_bit_le(i, (void *)de->discard_map); + + SM_I(sbi)->dcc_info->nr_discards += end - start; } return false; } @@ -1196,13 +1182,35 @@ next: /* send small discards */ list_for_each_entry_safe(entry, this, head, list) { - if (force && entry->len < cpc->trim_minlen) - goto skip; - f2fs_issue_discard(sbi, entry->blkaddr, entry->len); - cpc->trimmed += entry->len; + unsigned int cur_pos = 0, next_pos, len, total_len = 0; + bool is_valid = test_bit_le(0, entry->discard_map); + +find_next: + if (is_valid) { + next_pos = find_next_zero_bit_le(entry->discard_map, + sbi->blocks_per_seg, cur_pos); + len = next_pos - cur_pos; + + if (force && len < cpc->trim_minlen) + goto skip; + + f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, + len); + cpc->trimmed += len; + total_len += len; + } else { + next_pos = find_next_bit_le(entry->discard_map, + sbi->blocks_per_seg, cur_pos); + } skip: + cur_pos = next_pos; + is_valid = !is_valid; + + if (cur_pos < sbi->blocks_per_seg) + goto find_next; + list_del(&entry->list); - SM_I(sbi)->dcc_info->nr_discards -= entry->len; + SM_I(sbi)->dcc_info->nr_discards -= total_len; kmem_cache_free(discard_entry_slab, entry); } } -- GitLab From d3d480bce8e51fcecac56cdf292bf0866870742c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 24 Mar 2017 20:05:13 -0400 Subject: [PATCH 0615/5498] f2fs: write small sized IO to hot log It would better split small and large IOs separately in order to get more consecutive big writes. The default threshold is set to 64KB, but configurable by sysfs/min_hot_blocks. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/f2fs.h --- fs/f2fs/data.c | 9 +++++++++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/inline.c | 1 + fs/f2fs/segment.c | 13 ++++++------- fs/f2fs/segment.h | 1 + fs/f2fs/super.c | 2 ++ 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c9e10bf1afc6..70e6aca0808c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1428,6 +1428,8 @@ write: need_balance_fs = true; else if (has_not_enough_free_secs(sbi, 0, 0)) goto redirty_out; + else + set_inode_flag(inode, FI_HOT_DATA); err = -EAGAIN; if (f2fs_has_inline_data(inode)) { @@ -1453,6 +1455,7 @@ out: if (wbc->for_reclaim) { f2fs_submit_merged_bio_cond(sbi, inode, 0, page->index, DATA, WRITE); + clear_inode_flag(inode, FI_HOT_DATA); remove_dirty_inode(inode); submitted = NULL; } @@ -1507,6 +1510,12 @@ static int f2fs_write_cache_pages(struct address_space *mapping, pagevec_init(&pvec, 0); + if (get_dirty_pages(mapping->host) <= + SM_I(F2FS_M_SB(mapping))->min_hot_blocks) + set_inode_flag(mapping->host, FI_HOT_DATA); + else + clear_inode_flag(mapping->host, FI_HOT_DATA); + if (wbc->range_cyclic) { writeback_index = mapping->writeback_index; /* prev offset */ index = writeback_index; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ee024cf301e8..b187cfd088c2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -776,6 +776,7 @@ struct f2fs_sm_info { unsigned int ipu_policy; /* in-place-update policy */ unsigned int min_ipu_util; /* in-place-update threshold */ unsigned int min_fsync_blocks; /* threshold for fsync */ + unsigned int min_hot_blocks; /* threshold for hot block allocation */ /* for flush command control */ struct flush_cmd_control *fcc_info; @@ -1814,6 +1815,7 @@ enum { FI_INLINE_DOTS, /* indicate inline dot dentries */ FI_DO_DEFRAG, /* indicate defragment is running */ FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ + FI_HOT_DATA, /* indicate file is hot */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 701bbd8d10ba..031c3d78cbc6 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -137,6 +137,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) /* write data page to try to make data consistent */ set_page_writeback(page); fio.old_blkaddr = dn->data_blkaddr; + set_inode_flag(dn->inode, FI_HOT_DATA); write_data_page(dn, &fio); f2fs_wait_on_page_writeback(page, DATA, true); if (dirty) { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3300446cc4b8..f75ad58c34bc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1908,18 +1908,16 @@ static int __get_segment_type_6(struct page *page, enum page_type p_type) if (p_type == DATA) { struct inode *inode = page->mapping->host; - if (S_ISDIR(inode->i_mode)) - return CURSEG_HOT_DATA; - else if (is_cold_data(page) || file_is_cold(inode)) + if (is_cold_data(page) || file_is_cold(inode)) return CURSEG_COLD_DATA; - else - return CURSEG_WARM_DATA; + if (is_inode_flag_set(inode, FI_HOT_DATA)) + return CURSEG_HOT_DATA; + return CURSEG_WARM_DATA; } else { if (IS_DNODE(page)) return is_cold_node(page) ? CURSEG_WARM_NODE : CURSEG_HOT_NODE; - else - return CURSEG_COLD_NODE; + return CURSEG_COLD_NODE; } } @@ -3026,6 +3024,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; + sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS; sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index f12206a20ed0..92eb9b144566 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -539,6 +539,7 @@ static inline int utilization(struct f2fs_sb_info *sbi) */ #define DEF_MIN_IPU_UTIL 70 #define DEF_MIN_FSYNC_BLOCKS 8 +#define DEF_MIN_HOT_BLOCKS 16 enum { F2FS_IPU_FORCE, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 39649b73e2f9..9c983e4c89f8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -292,6 +292,7 @@ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_blocks); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio); @@ -317,6 +318,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(ipu_policy), ATTR_LIST(min_ipu_util), ATTR_LIST(min_fsync_blocks), + ATTR_LIST(min_hot_blocks), ATTR_LIST(max_victim_search), ATTR_LIST(dir_level), ATTR_LIST(ram_thresh), -- GitLab From c873cf96a48e223ff7dff922444db90aa1d59cb1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 28 Mar 2017 18:07:38 -0700 Subject: [PATCH 0616/5498] f2fs: avoid IO split due to mixed WB_SYNC_ALL and WB_SYNC_NONE If two threads try to flush dirty pages in different inodes respectively, f2fs_write_data_pages() will produce WRITE and WRITE_SYNC one at a time, resulting in a lot of 4KB seperated IOs. So, this patch gives higher priority to WB_SYNC_ALL IOs and gathers write IOs with a big WRITE_SYNC'ed bio. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 15 +++++++++++++-- fs/f2fs/f2fs.h | 3 +++ fs/f2fs/super.c | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 70e6aca0808c..a4b9c4cc1c48 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1601,8 +1601,10 @@ continue_unlock: last_idx = page->index; } - if (--wbc->nr_to_write <= 0 && - wbc->sync_mode == WB_SYNC_NONE) { + /* give a priority to WB_SYNC threads */ + if ((atomic_read(&F2FS_M_SB(mapping)->wb_sync_req) || + --wbc->nr_to_write <= 0) && + wbc->sync_mode == WB_SYNC_NONE) { done = 1; break; } @@ -1658,9 +1660,18 @@ static int f2fs_write_data_pages(struct address_space *mapping, trace_f2fs_writepages(mapping->host, wbc, DATA); + /* to avoid spliting IOs due to mixed WB_SYNC_ALL and WB_SYNC_NONE */ + if (wbc->sync_mode == WB_SYNC_ALL) + atomic_inc(&sbi->wb_sync_req); + else if (atomic_read(&sbi->wb_sync_req)) + goto skip_write; + blk_start_plug(&plug); ret = f2fs_write_cache_pages(mapping, wbc); blk_finish_plug(&plug); + + if (wbc->sync_mode == WB_SYNC_ALL) + atomic_dec(&sbi->wb_sync_req); /* * if some pages were truncated, we cannot guarantee its mapping->host * to detect pending bios. diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b187cfd088c2..3e358218f610 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -986,6 +986,9 @@ struct f2fs_sb_info { /* # of allocated blocks */ struct percpu_counter alloc_valid_block_count; + /* writeback control */ + atomic_t wb_sync_req; /* count # of WB_SYNC threads */ + /* valid inode count */ struct percpu_counter total_valid_inode_count; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9c983e4c89f8..f585321c6ea8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1554,6 +1554,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi) for (i = 0; i < NR_COUNT_TYPE; i++) atomic_set(&sbi->nr_pages[i], 0); + atomic_set(&sbi->wb_sync_req, 0); + INIT_LIST_HEAD(&sbi->s_list); mutex_init(&sbi->umount_mutex); mutex_init(&sbi->wio_mutex[NODE]); -- GitLab From 66e005d063e8b693041a249e423ea7ff94190400 Mon Sep 17 00:00:00 2001 From: Kaixu Xia Date: Sun, 2 Apr 2017 02:39:48 +0800 Subject: [PATCH 0617/5498] f2fs: remove the redundant variable definition The variable 'i' has been defined before, so here we can use it directly. Signed-off-by: Kaixu Xia Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 41ebce6f7f49..ad12c7a71b75 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1164,7 +1164,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* write nat bits */ if (enabled_nat_bits(sbi, cpc)) { __u64 cp_ver = cur_cp_version(ckpt); - unsigned int i; block_t blk; cp_ver |= ((__u64)crc32 << 32); -- GitLab From c7873f9c409b28d0e558be009cce1b9e05ca48d3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 30 Mar 2017 21:02:46 -0700 Subject: [PATCH 0618/5498] f2fs: submit bio of in-place-update pages This patch tries to split in-place-update bios from sequential bios. Suggested-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 ++++- fs/f2fs/f2fs.h | 2 +- fs/f2fs/segment.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a4b9c4cc1c48..c2cfa23f9e2b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -362,6 +362,9 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) bio_set_op_attrs(bio, fio->op, fio->op_flags); __submit_bio(fio->sbi, bio, fio->type); + + if (!is_read_io(fio->op)) + inc_page_count(fio->sbi, WB_DATA_TYPE(fio->page)); return 0; } @@ -1350,7 +1353,7 @@ retry_encrypt: !is_cold_data(page) && !IS_ATOMIC_WRITTEN_PAGE(page) && need_inplace_update(inode))) { - rewrite_data_page(fio); + err = rewrite_data_page(fio); set_inode_flag(inode, FI_UPDATE_WRITE); trace_f2fs_do_write_data_page(page, IPU); } else { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3e358218f610..7e7302ac9f47 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2306,7 +2306,7 @@ void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr); void write_meta_page(struct f2fs_sb_info *sbi, struct page *page); void write_node_page(unsigned int nid, struct f2fs_io_info *fio); void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio); -void rewrite_data_page(struct f2fs_io_info *fio); +int rewrite_data_page(struct f2fs_io_info *fio); void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, block_t old_blkaddr, block_t new_blkaddr, bool recover_curseg, bool recover_newaddr); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f75ad58c34bc..661e26a55127 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2040,11 +2040,11 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) f2fs_update_data_blkaddr(dn, fio->new_blkaddr); } -void rewrite_data_page(struct f2fs_io_info *fio) +int rewrite_data_page(struct f2fs_io_info *fio) { fio->new_blkaddr = fio->old_blkaddr; stat_inc_inplace_blocks(fio->sbi); - f2fs_submit_page_mbio(fio); + return f2fs_submit_page_bio(fio); } void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, -- GitLab From 223fb7d681b4e6ed91a2008d78db71b8d1cbaa0e Mon Sep 17 00:00:00 2001 From: Tomohiro Kusumi Date: Tue, 4 Apr 2017 13:01:22 +0300 Subject: [PATCH 0619/5498] f2fs: split make_dentry_ptr() into block and inline versions Since callers statically know which type to use, make_dentry_ptr() can simply be splitted into two inline functions. This way, the code has less inlined, fewer arguments, and no cast. Signed-off-by: Tomohiro Kusumi Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 8 ++++---- fs/f2fs/f2fs.h | 32 +++++++++++++++----------------- fs/f2fs/inline.c | 10 +++++----- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 16e009dfc3a7..815e99cf3882 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -94,7 +94,7 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page); - make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + make_dentry_ptr_block(NULL, &d, dentry_blk); de = find_target_dentry(fname, namehash, max_slots, &d); if (de) *res_page = dentry_page; @@ -366,7 +366,7 @@ static int make_empty_dir(struct inode *inode, dentry_blk = kmap_atomic(dentry_page); - make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + make_dentry_ptr_block(NULL, &d, dentry_blk); do_make_empty_dir(inode, parent, &d); kunmap_atomic(dentry_blk); @@ -586,7 +586,7 @@ add_dentry: } } - make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + make_dentry_ptr_block(NULL, &d, dentry_blk); f2fs_update_dentry(ino, mode, &d, new_name, dentry_hash, bit_pos); set_page_dirty(dentry_page); @@ -894,7 +894,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) dentry_blk = kmap(dentry_page); - make_dentry_ptr(inode, &d, (void *)dentry_blk, 1); + make_dentry_ptr_block(inode, &d, dentry_blk); err = f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7e7302ac9f47..a98fcf163597 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -427,26 +427,24 @@ struct f2fs_dentry_ptr { int max; }; -static inline void make_dentry_ptr(struct inode *inode, - struct f2fs_dentry_ptr *d, void *src, int type) +static inline void make_dentry_ptr_block(struct inode *inode, + struct f2fs_dentry_ptr *d, struct f2fs_dentry_block *t) { d->inode = inode; + d->max = NR_DENTRY_IN_BLOCK; + d->bitmap = &t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; +} - if (type == 1) { - struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src; - - d->max = NR_DENTRY_IN_BLOCK; - d->bitmap = &t->dentry_bitmap; - d->dentry = t->dentry; - d->filename = t->filename; - } else { - struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src; - - d->max = NR_INLINE_DENTRY; - d->bitmap = &t->dentry_bitmap; - d->dentry = t->dentry; - d->filename = t->filename; - } +static inline void make_dentry_ptr_inline(struct inode *inode, + struct f2fs_dentry_ptr *d, struct f2fs_inline_dentry *t) +{ + d->inode = inode; + d->max = NR_INLINE_DENTRY; + d->bitmap = &t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; } /* diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 031c3d78cbc6..0ccdefe9fdba 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -302,7 +302,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, inline_dentry = inline_data_addr(ipage); - make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2); + make_dentry_ptr_inline(NULL, &d, inline_dentry); de = find_target_dentry(fname, namehash, NULL, &d); unlock_page(ipage); if (de) @@ -321,7 +321,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, dentry_blk = inline_data_addr(ipage); - make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); + make_dentry_ptr_inline(NULL, &d, dentry_blk); do_make_empty_dir(inode, parent, &d); set_page_dirty(ipage); @@ -402,7 +402,7 @@ static int f2fs_add_inline_entries(struct inode *dir, unsigned long bit_pos = 0; int err = 0; - make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2); + make_dentry_ptr_inline(NULL, &d, inline_dentry); while (bit_pos < d.max) { struct f2fs_dir_entry *de; @@ -534,7 +534,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, f2fs_wait_on_page_writeback(ipage, NODE, true); name_hash = f2fs_dentry_hash(new_name); - make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); + make_dentry_ptr_inline(NULL, &d, dentry_blk); f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); set_page_dirty(ipage); @@ -623,7 +623,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, inline_dentry = inline_data_addr(ipage); - make_dentry_ptr(inode, &d, (void *)inline_dentry, 2); + make_dentry_ptr_inline(inode, &d, inline_dentry); err = f2fs_fill_dentries(ctx, &d, 0, fstr); if (!err) -- GitLab From 1ab7acd33551959f1168c6a999864033e06db2ca Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 4 Apr 2017 16:45:30 -0700 Subject: [PATCH 0620/5498] Revert "f2fs: put allocate_segment after refresh_sit_entry" This reverts commit 3436c4bdb30de421d46f58c9174669fbcfd40ce0. This makes a leak to register dirty segments. I reproduced the issue by modified postmark which injects a lot of file create/delete/update and finally triggers huge number of SSR allocations. Cc: # v4.10+ [Jaegeuk Kim: Change missing incorrect comment] Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 661e26a55127..97e417f8970c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1960,15 +1960,14 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, stat_inc_block_count(sbi, curseg); + if (!__has_curseg_space(sbi, type)) + sit_i->s_ops->allocate_segment(sbi, type, false); /* - * SIT information should be updated before segment allocation, - * since SSR needs latest valid block information. + * SIT information should be updated after segment allocation, + * since we need to keep dirty segments precisely under SSR. */ refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr); - if (!__has_curseg_space(sbi, type)) - sit_i->s_ops->allocate_segment(sbi, type, false); - mutex_unlock(&sit_i->sentry_lock); if (page && IS_NODESEG(type)) -- GitLab From 2f2774d6f4bfc99d2b4d36aa7cb9183f8268558e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 5 Apr 2017 18:19:48 +0800 Subject: [PATCH 0621/5498] f2fs: split discard_cmd_list Split discard_cmd_list to discard_{pend,wait}_list, so while sending/waiting discard command, we can avoid traversing unneeded entries in original list. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/segment.c | 47 ++++++++++++++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a98fcf163597..38b5855bd072 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -309,7 +309,8 @@ struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head discard_entry_list; /* 4KB discard entry list */ int nr_discards; /* # of discards in the list */ - struct list_head discard_cmd_list; /* discard cmd list */ + struct list_head discard_pend_list; /* store pending entries */ + struct list_head discard_wait_list; /* store on-flushing entries */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ struct mutex cmd_lock; int max_discards; /* max. discards to be issued */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 97e417f8970c..e0407886dcbc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -677,7 +677,7 @@ static void __add_discard_cmd(struct f2fs_sb_info *sbi, block_t start, block_t len) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *cmd_list = &(dcc->discard_cmd_list); + struct list_head *pend_list = &(dcc->discard_pend_list); struct discard_cmd *dc; dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); @@ -691,7 +691,7 @@ static void __add_discard_cmd(struct f2fs_sb_info *sbi, init_completion(&dc->wait); mutex_lock(&dcc->cmd_lock); - list_add_tail(&dc->list, cmd_list); + list_add_tail(&dc->list, pend_list); mutex_unlock(&dcc->cmd_lock); atomic_inc(&dcc->discard_cmd_cnt); @@ -826,6 +826,7 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; submit_bio(REQ_SYNC, bio); + list_move_tail(&dc->list, &dcc->discard_wait_list); } } else { __remove_discard_cmd(sbi, dc); @@ -872,31 +873,37 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *wait_list = &(dcc->discard_cmd_list); + struct list_head *pend_list = &(dcc->discard_pend_list); + struct list_head *wait_list = &(dcc->discard_wait_list); struct discard_cmd *dc, *tmp; struct blk_plug plug; mutex_lock(&dcc->cmd_lock); - blk_start_plug(&plug); - - list_for_each_entry_safe(dc, tmp, wait_list, list) { + if (blkaddr == NULL_ADDR) + goto release_discard; - if (blkaddr == NULL_ADDR) { - __submit_discard_cmd(sbi, dc); - continue; - } + list_for_each_entry_safe(dc, tmp, pend_list, list) { + if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) + __punch_discard_cmd(sbi, dc, blkaddr); + } + list_for_each_entry_safe(dc, tmp, wait_list, list) { if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) { if (dc->state == D_SUBMIT) wait_for_completion_io(&dc->wait); __punch_discard_cmd(sbi, dc, blkaddr); } } - blk_finish_plug(&plug); +release_discard: /* this comes from f2fs_put_super */ if (blkaddr == NULL_ADDR) { + blk_start_plug(&plug); + list_for_each_entry_safe(dc, tmp, pend_list, list) + __submit_discard_cmd(sbi, dc); + blk_finish_plug(&plug); + list_for_each_entry_safe(dc, tmp, wait_list, list) { wait_for_completion_io(&dc->wait); __remove_discard_cmd(sbi, dc); @@ -910,7 +917,8 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; - struct list_head *cmd_list = &dcc->discard_cmd_list; + struct list_head *pend_list = &dcc->discard_pend_list; + struct list_head *wait_list = &dcc->discard_wait_list; struct discard_cmd *dc, *tmp; struct blk_plug plug; int iter = 0; @@ -921,13 +929,17 @@ repeat: blk_start_plug(&plug); mutex_lock(&dcc->cmd_lock); - list_for_each_entry_safe(dc, tmp, cmd_list, list) { + list_for_each_entry_safe(dc, tmp, pend_list, list) { + f2fs_bug_on(sbi, dc->state != D_PREP); if (is_idle(sbi)) __submit_discard_cmd(sbi, dc); - if (dc->state == D_PREP && iter++ > DISCARD_ISSUE_RATE) + if (iter++ > DISCARD_ISSUE_RATE) break; + } + + list_for_each_entry_safe(dc, tmp, wait_list, list) { if (dc->state == D_DONE) __remove_discard_cmd(sbi, dc); } @@ -938,8 +950,8 @@ repeat: iter = 0; congestion_wait(BLK_RW_SYNC, HZ/50); - wait_event_interruptible(*q, - kthread_should_stop() || !list_empty(&dcc->discard_cmd_list)); + wait_event_interruptible(*q, kthread_should_stop() || + !list_empty(pend_list) || !list_empty(wait_list)); goto repeat; } @@ -1231,7 +1243,8 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) return -ENOMEM; INIT_LIST_HEAD(&dcc->discard_entry_list); - INIT_LIST_HEAD(&dcc->discard_cmd_list); + INIT_LIST_HEAD(&dcc->discard_pend_list); + INIT_LIST_HEAD(&dcc->discard_wait_list); mutex_init(&dcc->cmd_lock); atomic_set(&dcc->issued_discard, 0); atomic_set(&dcc->issing_discard, 0); -- GitLab From ce4f972a05d11f79dc0703e258ff779164c15299 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 5 Apr 2017 18:19:49 +0800 Subject: [PATCH 0622/5498] f2fs: introduce f2fs_wait_discard_bios Split f2fs_wait_discard_bios from f2fs_wait_discard_bio, just for cleanup, no logic change. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/segment.c | 37 ++++++++++++++++++++++--------------- fs/f2fs/super.c | 2 +- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 38b5855bd072..3df5cd5689ff 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2293,7 +2293,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); -void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr); +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void release_discard_addrs(struct f2fs_sb_info *sbi); int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e0407886dcbc..f05385db5b0a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -876,13 +876,9 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) struct list_head *pend_list = &(dcc->discard_pend_list); struct list_head *wait_list = &(dcc->discard_wait_list); struct discard_cmd *dc, *tmp; - struct blk_plug plug; mutex_lock(&dcc->cmd_lock); - if (blkaddr == NULL_ADDR) - goto release_discard; - list_for_each_entry_safe(dc, tmp, pend_list, list) { if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) __punch_discard_cmd(sbi, dc, blkaddr); @@ -896,19 +892,30 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) } } -release_discard: - /* this comes from f2fs_put_super */ - if (blkaddr == NULL_ADDR) { - blk_start_plug(&plug); - list_for_each_entry_safe(dc, tmp, pend_list, list) - __submit_discard_cmd(sbi, dc); - blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); +} - list_for_each_entry_safe(dc, tmp, wait_list, list) { - wait_for_completion_io(&dc->wait); - __remove_discard_cmd(sbi, dc); - } +/* This comes from f2fs_put_super */ +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *pend_list = &(dcc->discard_pend_list); + struct list_head *wait_list = &(dcc->discard_wait_list); + struct discard_cmd *dc, *tmp; + struct blk_plug plug; + + mutex_lock(&dcc->cmd_lock); + + blk_start_plug(&plug); + list_for_each_entry_safe(dc, tmp, pend_list, list) + __submit_discard_cmd(sbi, dc); + blk_finish_plug(&plug); + + list_for_each_entry_safe(dc, tmp, wait_list, list) { + wait_for_completion_io(&dc->wait); + __remove_discard_cmd(sbi, dc); } + mutex_unlock(&dcc->cmd_lock); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f585321c6ea8..0e6eaa798e8b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -782,7 +782,7 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - f2fs_wait_discard_bio(sbi, NULL_ADDR); + f2fs_wait_discard_bios(sbi); /* write_checkpoint can update stat informaion */ f2fs_destroy_stats(sbi); -- GitLab From fc5aa048114def45b7d29b83322ee45c3f90dca9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 5 Apr 2017 18:26:26 +0800 Subject: [PATCH 0623/5498] f2fs: prevent waiter encountering incorrect discard states In f2fs_submit_discard_endio, we will wake up waiter before setting discard command states, so waiter may use incorrect states. Change the order between complete() and states setting to fix this issue. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/segment.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f05385db5b0a..f901d17a5785 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -717,9 +717,9 @@ static void f2fs_submit_discard_endio(struct bio *bio, int err) { struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private; - complete(&dc->wait); dc->error = err; dc->state = D_DONE; + complete(&dc->wait); bio_put(bio); } @@ -886,8 +886,7 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) list_for_each_entry_safe(dc, tmp, wait_list, list) { if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) { - if (dc->state == D_SUBMIT) - wait_for_completion_io(&dc->wait); + wait_for_completion_io(&dc->wait); __punch_discard_cmd(sbi, dc, blkaddr); } } @@ -947,8 +946,10 @@ repeat: } list_for_each_entry_safe(dc, tmp, wait_list, list) { - if (dc->state == D_DONE) + if (dc->state == D_DONE) { + wait_for_completion_io(&dc->wait); __remove_discard_cmd(sbi, dc); + } } mutex_unlock(&dcc->cmd_lock); -- GitLab From 648c287c56c841417c2336b930f7c57e7d9ac577 Mon Sep 17 00:00:00 2001 From: Tomohiro Kusumi Date: Wed, 5 Apr 2017 22:49:44 +0300 Subject: [PATCH 0624/5498] f2fs: fix comment on f2fs_flush_merged_bios() after 86531d6b Callers are to unlock the page on failure after 86531d6b. Signed-off-by: Tomohiro Kusumi Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c2cfa23f9e2b..11c2c1962bcf 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -341,7 +341,7 @@ void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi) /* * Fill the locked page with data located in the block address. - * Return unlocked page. + * A caller needs to unlock the page on failure. */ int f2fs_submit_page_bio(struct f2fs_io_info *fio) { -- GitLab From 1e7c3aab8efa51d61cfb346e3053cdc1d1eb2172 Mon Sep 17 00:00:00 2001 From: Tomohiro Kusumi Date: Sun, 9 Apr 2017 02:11:36 +0300 Subject: [PATCH 0625/5498] f2fs: guard macro variables with braces Add braces around variables used within macros for those make sense to do it. Many of the macros in f2fs already do this. What this commit doesn't do is anything that changes line# as a result of adding braces, which usually affects the binary via __LINE__. Confirmed no diff in fs/f2fs/f2fs.ko before/after this commit on x86_64, to make sure this has no functional change as well as there's been no unexpected side effect due to callers' arithmetics within the existing code. Signed-off-by: Tomohiro Kusumi Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 36 +++++++++++----------- fs/f2fs/node.c | 2 +- fs/f2fs/node.h | 22 +++++++------- fs/f2fs/segment.h | 76 +++++++++++++++++++++++------------------------ fs/f2fs/xattr.h | 4 +-- 5 files changed, 70 insertions(+), 70 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3df5cd5689ff..a9ed612a438f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -65,7 +65,7 @@ struct f2fs_fault_info { }; extern char *fault_name[FAULT_MAX]; -#define IS_FAULT_SET(fi, type) (fi->inject_type & (1 << (type))) +#define IS_FAULT_SET(fi, type) ((fi)->inject_type & (1 << (type))) #endif /* @@ -91,9 +91,9 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_ADAPTIVE 0x00020000 #define F2FS_MOUNT_LFS 0x00040000 -#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) -#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) -#define test_opt(sbi, option) (sbi->mount_opt.opt & F2FS_MOUNT_##option) +#define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) +#define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) +#define test_opt(sbi, option) ((sbi)->mount_opt.opt & F2FS_MOUNT_##option) #define ver_after(a, b) (typecheck(unsigned long long, a) && \ typecheck(unsigned long long, b) && \ @@ -327,13 +327,13 @@ struct fsync_inode_entry { block_t last_dentry; /* block address locating the last dentry */ }; -#define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats)) -#define sits_in_cursum(jnl) (le16_to_cpu(jnl->n_sits)) +#define nats_in_cursum(jnl) (le16_to_cpu((jnl)->n_nats)) +#define sits_in_cursum(jnl) (le16_to_cpu((jnl)->n_sits)) -#define nat_in_journal(jnl, i) (jnl->nat_j.entries[i].ne) -#define nid_in_journal(jnl, i) (jnl->nat_j.entries[i].nid) -#define sit_in_journal(jnl, i) (jnl->sit_j.entries[i].se) -#define segno_in_journal(jnl, i) (jnl->sit_j.entries[i].segno) +#define nat_in_journal(jnl, i) ((jnl)->nat_j.entries[i].ne) +#define nid_in_journal(jnl, i) ((jnl)->nat_j.entries[i].nid) +#define sit_in_journal(jnl, i) ((jnl)->sit_j.entries[i].se) +#define segno_in_journal(jnl, i) ((jnl)->sit_j.entries[i].segno) #define MAX_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl)) #define MAX_SIT_JENTRIES(jnl) (SIT_JOURNAL_ENTRIES - sits_in_cursum(jnl)) @@ -844,7 +844,7 @@ struct f2fs_io_info { bool submitted; /* indicate IO submission */ }; -#define is_read_io(rw) (rw == READ) +#define is_read_io(rw) ((rw) == READ) struct f2fs_bio_info { struct f2fs_sb_info *sbi; /* f2fs superblock */ struct bio *bio; /* bios to merge */ @@ -1082,8 +1082,8 @@ static inline bool time_to_inject(struct f2fs_sb_info *sbi, int type) * and the return value is in kbytes. s is of struct f2fs_sb_info. */ #define BD_PART_WRITTEN(s) \ -(((u64)part_stat_read(s->sb->s_bdev->bd_part, sectors[1]) - \ - s->sectors_written_start) >> 1) +(((u64)part_stat_read((s)->sb->s_bdev->bd_part, sectors[1]) - \ + (s)->sectors_written_start) >> 1) static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type) { @@ -2538,8 +2538,8 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) #define stat_inc_seg_count(sbi, type, gc_type) \ do { \ struct f2fs_stat_info *si = F2FS_STAT(sbi); \ - (si)->tot_segs++; \ - if (type == SUM_TYPE_DATA) { \ + si->tot_segs++; \ + if ((type) == SUM_TYPE_DATA) { \ si->data_segs++; \ si->bg_data_segs += (gc_type == BG_GC) ? 1 : 0; \ } else { \ @@ -2549,14 +2549,14 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) } while (0) #define stat_inc_tot_blk_count(si, blks) \ - (si->tot_blks += (blks)) + ((si)->tot_blks += (blks)) #define stat_inc_data_blk_count(sbi, blks, gc_type) \ do { \ struct f2fs_stat_info *si = F2FS_STAT(sbi); \ stat_inc_tot_blk_count(si, blks); \ si->data_blks += (blks); \ - si->bg_data_blks += (gc_type == BG_GC) ? (blks) : 0; \ + si->bg_data_blks += ((gc_type) == BG_GC) ? (blks) : 0; \ } while (0) #define stat_inc_node_blk_count(sbi, blks, gc_type) \ @@ -2564,7 +2564,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) struct f2fs_stat_info *si = F2FS_STAT(sbi); \ stat_inc_tot_blk_count(si, blks); \ si->node_blks += (blks); \ - si->bg_node_blks += (gc_type == BG_GC) ? (blks) : 0; \ + si->bg_node_blks += ((gc_type) == BG_GC) ? (blks) : 0; \ } while (0) int f2fs_build_stats(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 50750a5dec95..6bd74327da1a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -22,7 +22,7 @@ #include "trace.h" #include -#define on_build_free_nids(nmi) mutex_is_locked(&nm_i->build_lock) +#define on_build_free_nids(nmi) mutex_is_locked(&(nm_i)->build_lock) static struct kmem_cache *nat_entry_slab; static struct kmem_cache *free_nid_slab; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index ebed0240aa53..558048e33cf9 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -9,10 +9,10 @@ * published by the Free Software Foundation. */ /* start node id of a node block dedicated to the given node id */ -#define START_NID(nid) ((nid / NAT_ENTRY_PER_BLOCK) * NAT_ENTRY_PER_BLOCK) +#define START_NID(nid) (((nid) / NAT_ENTRY_PER_BLOCK) * NAT_ENTRY_PER_BLOCK) /* node block offset on the NAT area dedicated to the given start node id */ -#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK) +#define NAT_BLOCK_OFFSET(start_nid) ((start_nid) / NAT_ENTRY_PER_BLOCK) /* # of pages to perform synchronous readahead before building free nids */ #define FREE_NID_PAGES 8 @@ -62,16 +62,16 @@ struct nat_entry { struct node_info ni; /* in-memory node information */ }; -#define nat_get_nid(nat) (nat->ni.nid) -#define nat_set_nid(nat, n) (nat->ni.nid = n) -#define nat_get_blkaddr(nat) (nat->ni.blk_addr) -#define nat_set_blkaddr(nat, b) (nat->ni.blk_addr = b) -#define nat_get_ino(nat) (nat->ni.ino) -#define nat_set_ino(nat, i) (nat->ni.ino = i) -#define nat_get_version(nat) (nat->ni.version) -#define nat_set_version(nat, v) (nat->ni.version = v) +#define nat_get_nid(nat) ((nat)->ni.nid) +#define nat_set_nid(nat, n) ((nat)->ni.nid = (n)) +#define nat_get_blkaddr(nat) ((nat)->ni.blk_addr) +#define nat_set_blkaddr(nat, b) ((nat)->ni.blk_addr = (b)) +#define nat_get_ino(nat) ((nat)->ni.ino) +#define nat_set_ino(nat, i) ((nat)->ni.ino = (i)) +#define nat_get_version(nat) ((nat)->ni.version) +#define nat_set_version(nat, v) ((nat)->ni.version = (v)) -#define inc_node_version(version) (++version) +#define inc_node_version(version) (++(version)) static inline void copy_node_info(struct node_info *dst, struct node_info *src) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 92eb9b144566..596a4673b1b4 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -20,78 +20,78 @@ #define F2FS_MIN_SEGMENTS 9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */ /* L: Logical segment # in volume, R: Relative segment # in main area */ -#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno) -#define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno) +#define GET_L2R_SEGNO(free_i, segno) ((segno) - (free_i)->start_segno) +#define GET_R2L_SEGNO(free_i, segno) ((segno) + (free_i)->start_segno) -#define IS_DATASEG(t) (t <= CURSEG_COLD_DATA) -#define IS_NODESEG(t) (t >= CURSEG_HOT_NODE) +#define IS_DATASEG(t) ((t) <= CURSEG_COLD_DATA) +#define IS_NODESEG(t) ((t) >= CURSEG_HOT_NODE) #define IS_CURSEG(sbi, seg) \ - ((seg == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \ - (seg == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \ - (seg == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \ - (seg == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \ - (seg == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \ - (seg == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno)) + (((seg) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \ + ((seg) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \ + ((seg) == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \ + ((seg) == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \ + ((seg) == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \ + ((seg) == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno)) #define IS_CURSEC(sbi, secno) \ - ((secno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \ - sbi->segs_per_sec)) \ + (((secno) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \ + (sbi)->segs_per_sec) || \ + ((secno) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno / \ + (sbi)->segs_per_sec) || \ + ((secno) == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno / \ + (sbi)->segs_per_sec) || \ + ((secno) == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno / \ + (sbi)->segs_per_sec) || \ + ((secno) == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno / \ + (sbi)->segs_per_sec) || \ + ((secno) == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \ + (sbi)->segs_per_sec)) \ #define MAIN_BLKADDR(sbi) (SM_I(sbi)->main_blkaddr) #define SEG0_BLKADDR(sbi) (SM_I(sbi)->seg0_blkaddr) #define MAIN_SEGS(sbi) (SM_I(sbi)->main_segments) -#define MAIN_SECS(sbi) (sbi->total_sections) +#define MAIN_SECS(sbi) ((sbi)->total_sections) #define TOTAL_SEGS(sbi) (SM_I(sbi)->segment_count) -#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << sbi->log_blocks_per_seg) +#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << (sbi)->log_blocks_per_seg) #define MAX_BLKADDR(sbi) (SEG0_BLKADDR(sbi) + TOTAL_BLKS(sbi)) -#define SEGMENT_SIZE(sbi) (1ULL << (sbi->log_blocksize + \ - sbi->log_blocks_per_seg)) +#define SEGMENT_SIZE(sbi) (1ULL << ((sbi)->log_blocksize + \ + (sbi)->log_blocks_per_seg)) #define START_BLOCK(sbi, segno) (SEG0_BLKADDR(sbi) + \ - (GET_R2L_SEGNO(FREE_I(sbi), segno) << sbi->log_blocks_per_seg)) + (GET_R2L_SEGNO(FREE_I(sbi), segno) << (sbi)->log_blocks_per_seg)) #define NEXT_FREE_BLKADDR(sbi, curseg) \ - (START_BLOCK(sbi, curseg->segno) + curseg->next_blkoff) + (START_BLOCK(sbi, (curseg)->segno) + (curseg)->next_blkoff) #define GET_SEGOFF_FROM_SEG0(sbi, blk_addr) ((blk_addr) - SEG0_BLKADDR(sbi)) #define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \ - (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg) + (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> (sbi)->log_blocks_per_seg) #define GET_BLKOFF_FROM_SEG0(sbi, blk_addr) \ - (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (sbi->blocks_per_seg - 1)) + (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & ((sbi)->blocks_per_seg - 1)) #define GET_SEGNO(sbi, blk_addr) \ - (((blk_addr == NULL_ADDR) || (blk_addr == NEW_ADDR)) ? \ + ((((blk_addr) == NULL_ADDR) || ((blk_addr) == NEW_ADDR)) ? \ NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \ GET_SEGNO_FROM_SEG0(sbi, blk_addr))) #define GET_SECNO(sbi, segno) \ - ((segno) / sbi->segs_per_sec) + ((segno) / (sbi)->segs_per_sec) #define GET_ZONENO_FROM_SEGNO(sbi, segno) \ - ((segno / sbi->segs_per_sec) / sbi->secs_per_zone) + (((segno) / (sbi)->segs_per_sec) / (sbi)->secs_per_zone) #define GET_SUM_BLOCK(sbi, segno) \ - ((sbi->sm_info->ssa_blkaddr) + segno) + ((sbi)->sm_info->ssa_blkaddr + (segno)) #define GET_SUM_TYPE(footer) ((footer)->entry_type) -#define SET_SUM_TYPE(footer, type) ((footer)->entry_type = type) +#define SET_SUM_TYPE(footer, type) ((footer)->entry_type = (type)) #define SIT_ENTRY_OFFSET(sit_i, segno) \ - (segno % sit_i->sents_per_block) + ((segno) % (sit_i)->sents_per_block) #define SIT_BLOCK_OFFSET(segno) \ - (segno / SIT_ENTRY_PER_BLOCK) + ((segno) / SIT_ENTRY_PER_BLOCK) #define START_SEGNO(segno) \ (SIT_BLOCK_OFFSET(segno) * SIT_ENTRY_PER_BLOCK) #define SIT_BLK_CNT(sbi) \ @@ -102,7 +102,7 @@ #define SECTOR_FROM_BLOCK(blk_addr) \ (((sector_t)blk_addr) << F2FS_LOG_SECTORS_PER_BLOCK) #define SECTOR_TO_BLOCK(sectors) \ - (sectors >> F2FS_LOG_SECTORS_PER_BLOCK) + ((sectors) >> F2FS_LOG_SECTORS_PER_BLOCK) /* * indicate a block allocation direction: RIGHT and LEFT. diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index 91f3bd88dcc6..08a4840d6d7d 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -58,10 +58,10 @@ struct f2fs_xattr_entry { #define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr) + 1)) #define XATTR_ROUND (3) -#define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND) +#define XATTR_ALIGN(size) (((size) + XATTR_ROUND) & ~XATTR_ROUND) #define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \ - entry->e_name_len + le16_to_cpu(entry->e_value_size))) + (entry)->e_name_len + le16_to_cpu((entry)->e_value_size))) #define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *)((char *)(entry) +\ ENTRY_SIZE(entry))) -- GitLab From 7548d30e4b136a70d8847d53049fc973f49bedf5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 7 Apr 2017 14:27:07 -0700 Subject: [PATCH 0626/5498] f2fs: use segment number for get_valid_blocks This patch fixes to submit a segment number for get_valid_blocks. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 596a4673b1b4..b576237a7bff 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -79,6 +79,8 @@ GET_SEGNO_FROM_SEG0(sbi, blk_addr))) #define GET_SECNO(sbi, segno) \ ((segno) / (sbi)->segs_per_sec) +#define GET_SEGNO_FROM_SECNO(sbi, secno) \ + ((secno) * (sbi)->segs_per_sec) #define GET_ZONENO_FROM_SEGNO(sbi, segno) \ (((segno) / (sbi)->segs_per_sec) / (sbi)->secs_per_zone) @@ -719,8 +721,8 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi, unsigned int secno) { - if (get_valid_blocks(sbi, secno, sbi->segs_per_sec) >= - sbi->fggc_threshold) + if (get_valid_blocks(sbi, GET_SEGNO_FROM_SECNO(sbi, secno), + sbi->segs_per_sec) >= sbi->fggc_threshold) return true; return false; } -- GitLab From 5a9345c5b073c2274dcc2c8b577a835412ede28d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 7 Apr 2017 14:33:22 -0700 Subject: [PATCH 0627/5498] f2fs: clean up get_valid_blocks with consistent parameter This patch cleans up get_valid_blocks, which has no functional change. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 2 +- fs/f2fs/gc.c | 8 ++++---- fs/f2fs/segment.c | 8 ++++---- fs/f2fs/segment.h | 8 ++++---- fs/f2fs/super.c | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index ce0a3db52708..1dae2729345d 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -137,7 +137,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi) blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg; hblks_per_sec = blks_per_sec / 2; for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { - vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); + vblocks = get_valid_blocks(sbi, segno, true); dist = abs(vblocks - hblks_per_sec); bimodal += dist * dist; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c52656ccbde5..439887c3aaf4 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -229,7 +229,7 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) for (i = 0; i < sbi->segs_per_sec; i++) mtime += get_seg_entry(sbi, start + i)->mtime; - vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); + vblocks = get_valid_blocks(sbi, segno, true); mtime = div_u64(mtime, sbi->segs_per_sec); vblocks = div_u64(vblocks, sbi->segs_per_sec); @@ -252,7 +252,7 @@ static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi, unsigned int segno) { unsigned int valid_blocks = - get_valid_blocks(sbi, segno, sbi->segs_per_sec); + get_valid_blocks(sbi, segno, true); return IS_DATASEG(get_seg_entry(sbi, segno)->type) ? valid_blocks * 2 : valid_blocks; @@ -897,7 +897,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, GET_SUM_BLOCK(sbi, segno)); f2fs_put_page(sum_page, 0); - if (get_valid_blocks(sbi, segno, 1) == 0 || + if (get_valid_blocks(sbi, segno, false) == 0 || !PageUptodate(sum_page) || unlikely(f2fs_cp_error(sbi))) goto next; @@ -931,7 +931,7 @@ next: blk_finish_plug(&plug); if (gc_type == FG_GC && - get_valid_blocks(sbi, start_segno, sbi->segs_per_sec) == 0) + get_valid_blocks(sbi, start_segno, true) == 0) sec_freed = 1; stat_inc_call_count(sbi->stat_info); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f901d17a5785..76e1691ecfb1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -636,7 +636,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) dirty_i->nr_dirty[t]--; - if (get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0) + if (get_valid_blocks(sbi, segno, true) == 0) clear_bit(GET_SECNO(sbi, segno), dirty_i->victim_secmap); } @@ -657,7 +657,7 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_lock(&dirty_i->seglist_lock); - valid_blocks = get_valid_blocks(sbi, segno, 0); + valid_blocks = get_valid_blocks(sbi, segno, false); if (valid_blocks == 0) { __locate_dirty_segment(sbi, segno, PRE); @@ -1188,7 +1188,7 @@ next: secno = GET_SECNO(sbi, start); start_segno = secno * sbi->segs_per_sec; if (!IS_CURSEC(sbi, secno) && - !get_valid_blocks(sbi, start, sbi->segs_per_sec)) + !get_valid_blocks(sbi, start, true)) f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno), sbi->segs_per_sec << sbi->log_blocks_per_seg); @@ -2938,7 +2938,7 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi) if (segno >= MAIN_SEGS(sbi)) break; offset = segno + 1; - valid_blocks = get_valid_blocks(sbi, segno, 0); + valid_blocks = get_valid_blocks(sbi, segno, false); if (valid_blocks == sbi->blocks_per_seg || !valid_blocks) continue; if (valid_blocks > sbi->blocks_per_seg) { diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index b576237a7bff..ee1fd9292d93 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -308,13 +308,13 @@ static inline struct sec_entry *get_sec_entry(struct f2fs_sb_info *sbi, } static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi, - unsigned int segno, int section) + unsigned int segno, bool use_section) { /* * In order to get # of valid blocks in a section instantly from many * segments, f2fs manages two counting structures separately. */ - if (section > 1) + if (use_section && sbi->segs_per_sec > 1) return get_sec_entry(sbi, segno)->valid_blocks; else return get_seg_entry(sbi, segno)->valid_blocks; @@ -721,8 +721,8 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi, unsigned int secno) { - if (get_valid_blocks(sbi, GET_SEGNO_FROM_SECNO(sbi, secno), - sbi->segs_per_sec) >= sbi->fggc_threshold) + if (get_valid_blocks(sbi, GET_SEGNO_FROM_SECNO(sbi, secno), true) >= + sbi->fggc_threshold) return true; return false; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0e6eaa798e8b..c201ff1c2b9c 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -984,7 +984,7 @@ static int segment_info_seq_show(struct seq_file *seq, void *offset) if ((i % 10) == 0) seq_printf(seq, "%-10d", i); seq_printf(seq, "%d|%-3u", se->type, - get_valid_blocks(sbi, i, 1)); + get_valid_blocks(sbi, i, false)); if ((i % 10) == 9 || i == (total_segs - 1)) seq_putc(seq, '\n'); else @@ -1010,7 +1010,7 @@ static int segment_bits_seq_show(struct seq_file *seq, void *offset) seq_printf(seq, "%-10d", i); seq_printf(seq, "%d|%-3u|", se->type, - get_valid_blocks(sbi, i, 1)); + get_valid_blocks(sbi, i, false)); for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) seq_printf(seq, " %.2x", se->cur_valid_map[j]); seq_putc(seq, '\n'); -- GitLab From eacf10ae061a7cac4ca4f916de41d4eca7af5da7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 7 Apr 2017 15:08:17 -0700 Subject: [PATCH 0628/5498] f2fs: clean up some macros in terms of GET_SEGNO This patch cleans several macros by introducing: - BLKS_PER_SEC - GET_SEC_FROM_SEG - GET_SEG_FROM_SEC - GET_ZONE_FROM_SEC - GET_ZONE_FROM_SEG Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 6 +++--- fs/f2fs/f2fs.h | 7 +++---- fs/f2fs/file.c | 3 +-- fs/f2fs/gc.c | 17 ++++++++--------- fs/f2fs/segment.c | 20 ++++++++++---------- fs/f2fs/segment.h | 34 ++++++++++++++++++++-------------- 6 files changed, 45 insertions(+), 42 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 1dae2729345d..1866962c00be 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -109,8 +109,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_NODE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); si->curseg[i] = curseg->segno; - si->cursec[i] = curseg->segno / sbi->segs_per_sec; - si->curzone[i] = si->cursec[i] / sbi->secs_per_zone; + si->cursec[i] = GET_SEC_FROM_SEG(sbi, curseg->segno); + si->curzone[i] = GET_ZONE_FROM_SEC(sbi, si->cursec[i]); } for (i = 0; i < 2; i++) { @@ -134,7 +134,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi) bimodal = 0; total_vblocks = 0; - blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg; + blks_per_sec = BLKS_PER_SEC(sbi); hblks_per_sec = blks_per_sec / 2; for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { vblocks = get_valid_blocks(sbi, segno, true); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a9ed612a438f..80b3ed513f42 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -234,12 +234,11 @@ enum { #define DEF_BATCHED_TRIM_SECTIONS 2048 #define BATCHED_TRIM_SEGMENTS(sbi) \ - (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) + (GET_SEG_FROM_SEC(sbi, SM_I(sbi)->trim_sections)) #define BATCHED_TRIM_BLOCKS(sbi) \ (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) -#define MAX_DISCARD_BLOCKS(sbi) \ - ((1 << (sbi)->log_blocks_per_seg) * (sbi)->segs_per_sec) -#define DISCARD_ISSUE_RATE 8 +#define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi) +#define DISCARD_ISSUE_RATE 8 #define DEF_CP_INTERVAL 60 /* 60 secs */ #define DEF_IDLE_INTERVAL 5 /* 5 secs */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 111ef80f46ea..49f9e2779fca 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1907,7 +1907,6 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, pgoff_t pg_start, pg_end; unsigned int blk_per_seg = sbi->blocks_per_seg; unsigned int total = 0, sec_num; - unsigned int pages_per_sec = sbi->segs_per_sec * blk_per_seg; block_t blk_end = 0; bool fragmented = false; int err; @@ -1971,7 +1970,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, map.m_lblk = pg_start; map.m_len = pg_end - pg_start; - sec_num = (map.m_len + pages_per_sec - 1) / pages_per_sec; + sec_num = (map.m_len + BLKS_PER_SEC(sbi) - 1) / BLKS_PER_SEC(sbi); /* * make sure there are enough free section for LFS allocation, this can diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 439887c3aaf4..e2f9b2b12b74 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -211,7 +211,7 @@ static unsigned int check_bg_victims(struct f2fs_sb_info *sbi) continue; clear_bit(secno, dirty_i->victim_secmap); - return secno * sbi->segs_per_sec; + return GET_SEG_FROM_SEC(sbi, secno); } return NULL_SEGNO; } @@ -219,8 +219,8 @@ static unsigned int check_bg_victims(struct f2fs_sb_info *sbi) static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); - unsigned int secno = GET_SECNO(sbi, segno); - unsigned int start = secno * sbi->segs_per_sec; + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + unsigned int start = GET_SEG_FROM_SEC(sbi, secno); unsigned long long mtime = 0; unsigned int vblocks; unsigned char age = 0; @@ -343,7 +343,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, nsearched++; } - secno = GET_SECNO(sbi, segno); + secno = GET_SEC_FROM_SEG(sbi, segno); if (sec_usage_check(sbi, secno)) goto next; @@ -372,7 +372,7 @@ next: if (p.min_segno != NULL_SEGNO) { got_it: if (p.alloc_mode == LFS) { - secno = GET_SECNO(sbi, p.min_segno); + secno = GET_SEC_FROM_SEG(sbi, p.min_segno); if (gc_type == FG_GC) sbi->cur_victim_sec = secno; else @@ -1006,7 +1006,7 @@ stop: void build_gc_manager(struct f2fs_sb_info *sbi) { - u64 main_count, resv_count, ovp_count, blocks_per_sec; + u64 main_count, resv_count, ovp_count; DIRTY_I(sbi)->v_ops = &default_v_ops; @@ -1014,8 +1014,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi) main_count = SM_I(sbi)->main_segments << sbi->log_blocks_per_seg; resv_count = SM_I(sbi)->reserved_segments << sbi->log_blocks_per_seg; ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg; - blocks_per_sec = sbi->blocks_per_seg * sbi->segs_per_sec; - sbi->fggc_threshold = div64_u64((main_count - ovp_count) * blocks_per_sec, - (main_count - resv_count)); + sbi->fggc_threshold = div64_u64((main_count - ovp_count) * + BLKS_PER_SEC(sbi), (main_count - resv_count)); } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 76e1691ecfb1..9771d2538a41 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -637,7 +637,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, dirty_i->nr_dirty[t]--; if (get_valid_blocks(sbi, segno, true) == 0) - clear_bit(GET_SECNO(sbi, segno), + clear_bit(GET_SEC_FROM_SEG(sbi, segno), dirty_i->victim_secmap); } } @@ -1185,8 +1185,8 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) continue; } next: - secno = GET_SECNO(sbi, start); - start_segno = secno * sbi->segs_per_sec; + secno = GET_SEC_FROM_SEG(sbi, start); + start_segno = GET_SEG_FROM_SEC(sbi, secno); if (!IS_CURSEC(sbi, secno) && !get_valid_blocks(sbi, start, true)) f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno), @@ -1541,8 +1541,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi, struct free_segmap_info *free_i = FREE_I(sbi); unsigned int segno, secno, zoneno; unsigned int total_zones = MAIN_SECS(sbi) / sbi->secs_per_zone; - unsigned int hint = *newseg / sbi->segs_per_sec; - unsigned int old_zoneno = GET_ZONENO_FROM_SEGNO(sbi, *newseg); + unsigned int hint = GET_SEC_FROM_SEG(sbi, *newseg); + unsigned int old_zoneno = GET_ZONE_FROM_SEG(sbi, *newseg); unsigned int left_start = hint; bool init = true; int go_left = 0; @@ -1552,8 +1552,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi, if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { segno = find_next_zero_bit(free_i->free_segmap, - (hint + 1) * sbi->segs_per_sec, *newseg + 1); - if (segno < (hint + 1) * sbi->segs_per_sec) + GET_SEG_FROM_SEC(sbi, hint + 1), *newseg + 1); + if (segno < GET_SEG_FROM_SEC(sbi, hint + 1)) goto got_it; } find_other_zone: @@ -1584,8 +1584,8 @@ find_other_zone: secno = left_start; skip_left: hint = secno; - segno = secno * sbi->segs_per_sec; - zoneno = secno / sbi->secs_per_zone; + segno = GET_SEG_FROM_SEC(sbi, secno); + zoneno = GET_ZONE_FROM_SEC(sbi, secno); /* give up on finding another zone */ if (!init) @@ -1629,7 +1629,7 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) struct summary_footer *sum_footer; curseg->segno = curseg->next_segno; - curseg->zone = GET_ZONENO_FROM_SEGNO(sbi, curseg->segno); + curseg->zone = GET_ZONE_FROM_SEG(sbi, curseg->segno); curseg->next_blkoff = 0; curseg->next_segno = NULL_SEGNO; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index ee1fd9292d93..7fbe87149d1f 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -77,12 +77,16 @@ ((((blk_addr) == NULL_ADDR) || ((blk_addr) == NEW_ADDR)) ? \ NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \ GET_SEGNO_FROM_SEG0(sbi, blk_addr))) -#define GET_SECNO(sbi, segno) \ +#define BLKS_PER_SEC(sbi) \ + ((sbi)->segs_per_sec * (sbi)->blocks_per_seg) +#define GET_SEC_FROM_SEG(sbi, segno) \ ((segno) / (sbi)->segs_per_sec) -#define GET_SEGNO_FROM_SECNO(sbi, secno) \ +#define GET_SEG_FROM_SEC(sbi, secno) \ ((secno) * (sbi)->segs_per_sec) -#define GET_ZONENO_FROM_SEGNO(sbi, segno) \ - (((segno) / (sbi)->segs_per_sec) / (sbi)->secs_per_zone) +#define GET_ZONE_FROM_SEC(sbi, secno) \ + ((secno) / (sbi)->secs_per_zone) +#define GET_ZONE_FROM_SEG(sbi, segno) \ + GET_ZONE_FROM_SEC(sbi, GET_SEC_FROM_SEG(sbi, segno)) #define GET_SUM_BLOCK(sbi, segno) \ ((sbi)->sm_info->ssa_blkaddr + (segno)) @@ -304,7 +308,7 @@ static inline struct sec_entry *get_sec_entry(struct f2fs_sb_info *sbi, unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); - return &sit_i->sec_entries[GET_SECNO(sbi, segno)]; + return &sit_i->sec_entries[GET_SEC_FROM_SEG(sbi, segno)]; } static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi, @@ -359,8 +363,8 @@ static inline unsigned int find_next_inuse(struct free_segmap_info *free_i, static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno) { struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; - unsigned int start_segno = secno * sbi->segs_per_sec; + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno); unsigned int next; spin_lock(&free_i->segmap_lock); @@ -380,7 +384,8 @@ static inline void __set_inuse(struct f2fs_sb_info *sbi, unsigned int segno) { struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + set_bit(segno, free_i->free_segmap); free_i->free_segments--; if (!test_and_set_bit(secno, free_i->free_secmap)) @@ -391,8 +396,8 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi, unsigned int segno) { struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; - unsigned int start_segno = secno * sbi->segs_per_sec; + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno); unsigned int next; spin_lock(&free_i->segmap_lock); @@ -413,7 +418,8 @@ static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi, unsigned int segno) { struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + spin_lock(&free_i->segmap_lock); if (!test_and_set_bit(segno, free_i->free_segmap)) { free_i->free_segments--; @@ -478,12 +484,12 @@ static inline int overprovision_segments(struct f2fs_sb_info *sbi) static inline int overprovision_sections(struct f2fs_sb_info *sbi) { - return ((unsigned int) overprovision_segments(sbi)) / sbi->segs_per_sec; + return GET_SEC_FROM_SEG(sbi, (unsigned int)overprovision_segments(sbi)); } static inline int reserved_sections(struct f2fs_sb_info *sbi) { - return ((unsigned int) reserved_segments(sbi)) / sbi->segs_per_sec; + return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi)); } static inline bool need_SSR(struct f2fs_sb_info *sbi) @@ -721,7 +727,7 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi, unsigned int secno) { - if (get_valid_blocks(sbi, GET_SEGNO_FROM_SECNO(sbi, secno), true) >= + if (get_valid_blocks(sbi, GET_SEG_FROM_SEC(sbi, secno), true) >= sbi->fggc_threshold) return true; return false; -- GitLab From 4a511a34b08eaf055ce7930a3c2eeaab7c4e66c6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 7 Apr 2017 17:25:54 -0700 Subject: [PATCH 0629/5498] f2fs: avoid frequent checkpoint during f2fs_gc Now we're doing SSR aggressively more than ever before, so once we reach to the reserved_segment, f2fs_balance_fs will call f2fs_gc, which triggers checkpoint everytime. We actually must avoid that. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e2f9b2b12b74..9172112d6246 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -966,9 +966,11 @@ gc_more: * threshold, we can make them free by checkpoint. Then, we * secure free segments which doesn't need fggc any more. */ - ret = write_checkpoint(sbi, &cpc); - if (ret) - goto stop; + if (prefree_segments(sbi)) { + ret = write_checkpoint(sbi, &cpc); + if (ret) + goto stop; + } if (has_not_enough_free_secs(sbi, 0, 0)) gc_type = FG_GC; } -- GitLab From 3728be9828b2c515d3b91008dd09ef71b89ed71b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 11 Apr 2017 09:25:22 +0800 Subject: [PATCH 0630/5498] f2fs: extract rb-tree operation infrastructure rb-tree lookup/update functions are deeply coupled into extent cache codes, it's very hard to reuse these basic functions, this patch extracts common rb-tree operation infrastructure for latter reusing. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 291 +++++++++++++++++++++++------------------ fs/f2fs/f2fs.h | 20 ++- 2 files changed, 179 insertions(+), 132 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index c6934f014e0f..68e649a31c7d 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -18,6 +18,146 @@ #include "node.h" #include +static struct rb_entry *__lookup_rb_tree_fast(struct rb_entry *cached_re, + unsigned int ofs) +{ + if (cached_re) { + if (cached_re->ofs <= ofs && + cached_re->ofs + cached_re->len > ofs) { + return cached_re; + } + } + return NULL; +} + +static struct rb_entry *__lookup_rb_tree_slow(struct rb_root *root, + unsigned int ofs) +{ + struct rb_node *node = root->rb_node; + struct rb_entry *re; + + while (node) { + re = rb_entry(node, struct rb_entry, rb_node); + + if (ofs < re->ofs) + node = node->rb_left; + else if (ofs >= re->ofs + re->len) + node = node->rb_right; + else + return re; + } + return NULL; +} + +static struct rb_entry *__lookup_rb_tree(struct rb_root *root, + struct rb_entry *cached_re, unsigned int ofs) +{ + struct rb_entry *re; + + re = __lookup_rb_tree_fast(cached_re, ofs); + if (!re) + return __lookup_rb_tree_slow(root, ofs); + + return re; +} + +static struct rb_node **__lookup_rb_tree_for_insert(struct f2fs_sb_info *sbi, + struct rb_root *root, struct rb_node **parent, + unsigned int ofs) +{ + struct rb_node **p = &root->rb_node; + struct rb_entry *re; + + while (*p) { + *parent = *p; + re = rb_entry(*parent, struct rb_entry, rb_node); + + if (ofs < re->ofs) + p = &(*p)->rb_left; + else if (ofs >= re->ofs + re->len) + p = &(*p)->rb_right; + else + f2fs_bug_on(sbi, 1); + } + + return p; +} + +/* + * lookup rb entry in position of @ofs in rb-tree, + * if hit, return the entry, otherwise, return NULL + * @prev_ex: extent before ofs + * @next_ex: extent after ofs + * @insert_p: insert point for new extent at ofs + * in order to simpfy the insertion after. + * tree must stay unchanged between lookup and insertion. + */ +static struct rb_entry *__lookup_rb_tree_ret(struct rb_root *root, + struct rb_entry *cached_re, + unsigned int ofs, + struct rb_entry **prev_entry, + struct rb_entry **next_entry, + struct rb_node ***insert_p, + struct rb_node **insert_parent) +{ + struct rb_node **pnode = &root->rb_node; + struct rb_node *parent = NULL, *tmp_node; + struct rb_entry *re = cached_re; + + *insert_p = NULL; + *insert_parent = NULL; + *prev_entry = NULL; + *next_entry = NULL; + + if (RB_EMPTY_ROOT(root)) + return NULL; + + if (re) { + if (re->ofs <= ofs && re->ofs + re->len > ofs) + goto lookup_neighbors; + } + + while (*pnode) { + parent = *pnode; + re = rb_entry(*pnode, struct rb_entry, rb_node); + + if (ofs < re->ofs) + pnode = &(*pnode)->rb_left; + else if (ofs >= re->ofs + re->len) + pnode = &(*pnode)->rb_right; + else + goto lookup_neighbors; + } + + *insert_p = pnode; + *insert_parent = parent; + + re = rb_entry(parent, struct rb_entry, rb_node); + tmp_node = parent; + if (parent && ofs > re->ofs) + tmp_node = rb_next(parent); + *next_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node); + + tmp_node = parent; + if (parent && ofs < re->ofs) + tmp_node = rb_prev(parent); + *prev_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node); + return NULL; + +lookup_neighbors: + if (ofs == re->ofs) { + /* lookup prev node for merging backward later */ + tmp_node = rb_prev(&re->rb_node); + *prev_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node); + } + if (ofs == re->ofs + re->len - 1) { + /* lookup next node for merging frontward later */ + tmp_node = rb_next(&re->rb_node); + *next_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node); + } + return re; +} + static struct kmem_cache *extent_tree_slab; static struct kmem_cache *extent_node_slab; @@ -102,36 +242,6 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) return et; } -static struct extent_node *__lookup_extent_tree(struct f2fs_sb_info *sbi, - struct extent_tree *et, unsigned int fofs) -{ - struct rb_node *node = et->root.rb_node; - struct extent_node *en = et->cached_en; - - if (en) { - struct extent_info *cei = &en->ei; - - if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) { - stat_inc_cached_node_hit(sbi); - return en; - } - } - - while (node) { - en = rb_entry(node, struct extent_node, rb_node); - - if (fofs < en->ei.fofs) { - node = node->rb_left; - } else if (fofs >= en->ei.fofs + en->ei.len) { - node = node->rb_right; - } else { - stat_inc_rbtree_node_hit(sbi); - return en; - } - } - return NULL; -} - static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi, struct extent_tree *et, struct extent_info *ei) { @@ -237,17 +347,24 @@ static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, goto out; } - en = __lookup_extent_tree(sbi, et, pgofs); - if (en) { - *ei = en->ei; - spin_lock(&sbi->extent_lock); - if (!list_empty(&en->list)) { - list_move_tail(&en->list, &sbi->extent_list); - et->cached_en = en; - } - spin_unlock(&sbi->extent_lock); - ret = true; + en = (struct extent_node *)__lookup_rb_tree(&et->root, + (struct rb_entry *)et->cached_en, pgofs); + if (!en) + goto out; + + if (en == et->cached_en) + stat_inc_cached_node_hit(sbi); + else + stat_inc_rbtree_node_hit(sbi); + + *ei = en->ei; + spin_lock(&sbi->extent_lock); + if (!list_empty(&en->list)) { + list_move_tail(&en->list, &sbi->extent_list); + et->cached_en = en; } + spin_unlock(&sbi->extent_lock); + ret = true; out: stat_inc_total_hit(sbi); read_unlock(&et->lock); @@ -256,83 +373,6 @@ out: return ret; } - -/* - * lookup extent at @fofs, if hit, return the extent - * if not, return NULL and - * @prev_ex: extent before fofs - * @next_ex: extent after fofs - * @insert_p: insert point for new extent at fofs - * in order to simpfy the insertion after. - * tree must stay unchanged between lookup and insertion. - */ -static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et, - unsigned int fofs, - struct extent_node **prev_ex, - struct extent_node **next_ex, - struct rb_node ***insert_p, - struct rb_node **insert_parent) -{ - struct rb_node **pnode = &et->root.rb_node; - struct rb_node *parent = NULL, *tmp_node; - struct extent_node *en = et->cached_en; - - *insert_p = NULL; - *insert_parent = NULL; - *prev_ex = NULL; - *next_ex = NULL; - - if (RB_EMPTY_ROOT(&et->root)) - return NULL; - - if (en) { - struct extent_info *cei = &en->ei; - - if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) - goto lookup_neighbors; - } - - while (*pnode) { - parent = *pnode; - en = rb_entry(*pnode, struct extent_node, rb_node); - - if (fofs < en->ei.fofs) - pnode = &(*pnode)->rb_left; - else if (fofs >= en->ei.fofs + en->ei.len) - pnode = &(*pnode)->rb_right; - else - goto lookup_neighbors; - } - - *insert_p = pnode; - *insert_parent = parent; - - en = rb_entry(parent, struct extent_node, rb_node); - tmp_node = parent; - if (parent && fofs > en->ei.fofs) - tmp_node = rb_next(parent); - *next_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); - - tmp_node = parent; - if (parent && fofs < en->ei.fofs) - tmp_node = rb_prev(parent); - *prev_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); - return NULL; - -lookup_neighbors: - if (fofs == en->ei.fofs) { - /* lookup prev node for merging backward later */ - tmp_node = rb_prev(&en->rb_node); - *prev_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); - } - if (fofs == en->ei.fofs + en->ei.len - 1) { - /* lookup next node for merging frontward later */ - tmp_node = rb_next(&en->rb_node); - *next_ex = rb_entry_safe(tmp_node, struct extent_node, rb_node); - } - return en; -} - static struct extent_node *__try_merge_extent_node(struct inode *inode, struct extent_tree *et, struct extent_info *ei, struct extent_node *prev_ex, @@ -387,17 +427,7 @@ static struct extent_node *__insert_extent_tree(struct inode *inode, goto do_insert; } - while (*p) { - parent = *p; - en = rb_entry(parent, struct extent_node, rb_node); - - if (ei->fofs < en->ei.fofs) - p = &(*p)->rb_left; - else if (ei->fofs >= en->ei.fofs + en->ei.len) - p = &(*p)->rb_right; - else - f2fs_bug_on(sbi, 1); - } + p = __lookup_rb_tree_for_insert(sbi, &et->root, &parent, ei->fofs); do_insert: en = __attach_extent_node(sbi, et, ei, parent, p); if (!en) @@ -447,7 +477,10 @@ static void f2fs_update_extent_tree_range(struct inode *inode, __drop_largest_extent(inode, fofs, len); /* 1. lookup first extent node in range [fofs, fofs + len - 1] */ - en = __lookup_extent_tree_ret(et, fofs, &prev_en, &next_en, + en = (struct extent_node *)__lookup_rb_tree_ret(&et->root, + (struct rb_entry *)et->cached_en, fofs, + (struct rb_entry **)&prev_en, + (struct rb_entry **)&next_en, &insert_p, &insert_parent); if (!en) en = next_en; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 80b3ed513f42..c9f3e3eb73f8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -476,16 +476,30 @@ enum { /* number of extent info in extent cache we try to shrink */ #define EXTENT_CACHE_SHRINK_NUMBER 128 +struct rb_entry { + struct rb_node rb_node; /* rb node located in rb-tree */ + unsigned int ofs; /* start offset of the entry */ + unsigned int len; /* length of the entry */ +}; + struct extent_info { unsigned int fofs; /* start offset in a file */ - u32 blk; /* start block address of the extent */ unsigned int len; /* length of the extent */ + u32 blk; /* start block address of the extent */ }; struct extent_node { - struct rb_node rb_node; /* rb node located in rb-tree */ + struct rb_node rb_node; + union { + struct { + unsigned int fofs; + unsigned int len; + u32 blk; + }; + struct extent_info ei; /* extent info */ + + }; struct list_head list; /* node in global extent list of sbi */ - struct extent_info ei; /* extent info */ struct extent_tree *et; /* extent tree pointer */ }; -- GitLab From 4a746572e6dd75b6192e5dddcba96a3ae649a9fd Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 27 Mar 2017 18:14:05 +0800 Subject: [PATCH 0631/5498] f2fs: shrink blk plug region Don't use blk plug covering area where there won't be any IOs being issued. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9771d2538a41..951a2bc4b69b 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -932,9 +932,8 @@ repeat: if (kthread_should_stop()) return 0; - blk_start_plug(&plug); - mutex_lock(&dcc->cmd_lock); + blk_start_plug(&plug); list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); @@ -944,6 +943,7 @@ repeat: if (iter++ > DISCARD_ISSUE_RATE) break; } + blk_finish_plug(&plug); list_for_each_entry_safe(dc, tmp, wait_list, list) { if (dc->state == D_DONE) { @@ -953,8 +953,6 @@ repeat: } mutex_unlock(&dcc->cmd_lock); - blk_finish_plug(&plug); - iter = 0; congestion_wait(BLK_RW_SYNC, HZ/50); -- GitLab From 785b19288ec0f8059f05c674bf7613a3dc5b7871 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 11 Apr 2017 19:01:26 -0700 Subject: [PATCH 0632/5498] f2fs: fix fs corruption due to zero inode page This patch fixes the following scenario. - f2fs_create/f2fs_mkdir - write_checkpoint - f2fs_mark_inode_dirty_sync - block_operations - f2fs_lock_all - f2fs_sync_inode_meta - f2fs_unlock_all - sync_inode_metadata - f2fs_lock_op - f2fs_write_inode - update_inode_page - get_node_page return -ENOENT - new_inode_page - fill_node_footer - f2fs_mark_inode_dirty_sync - ... - f2fs_unlock_op - f2fs_inode_synced - f2fs_lock_all - do_checkpoint In this checkpoint, we can get an inode page which contains zeros having valid node footer only. Cc: Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 2 +- fs/f2fs/namei.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 8ca67ebcd069..c689227f7174 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -316,7 +316,6 @@ retry: } else if (err != -ENOENT) { f2fs_stop_checkpoint(sbi, false); } - f2fs_inode_synced(inode); return 0; } ret = update_inode(inode, node_page); @@ -450,6 +449,7 @@ void handle_failed_inode(struct inode *inode) * in a panic when flushing dirty inodes in gdirty_list. */ update_inode_page(inode); + f2fs_inode_synced(inode); /* don't make bad inode, since it becomes a regular file. */ unlock_new_inode(inode); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 01e837a0b43c..ae985a311ace 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -149,8 +149,6 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, inode->i_mapping->a_ops = &f2fs_dblock_aops; ino = inode->i_ino; - f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -164,6 +162,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); + + f2fs_balance_fs(sbi, true); return 0; out: handle_failed_inode(inode); @@ -432,8 +432,6 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, inode_nohighmem(inode); inode->i_mapping->a_ops = &f2fs_dblock_aops; - f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -496,6 +494,8 @@ err_out: } kfree(sd); + + f2fs_balance_fs(sbi, true); return err; out: handle_failed_inode(inode); @@ -517,8 +517,6 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_mapping->a_ops = &f2fs_dblock_aops; mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); - f2fs_balance_fs(sbi, true); - set_inode_flag(inode, FI_INC_LINK); f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); @@ -533,6 +531,8 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); + + f2fs_balance_fs(sbi, true); return 0; out_fail: @@ -566,8 +566,6 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, init_special_inode(inode, inode->i_mode, rdev); inode->i_op = &f2fs_special_inode_operations; - f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); err = f2fs_add_link(dentry, inode); if (err) @@ -581,6 +579,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); + + f2fs_balance_fs(sbi, true); return 0; out: handle_failed_inode(inode); @@ -607,8 +607,6 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, inode->i_mapping->a_ops = &f2fs_dblock_aops; } - f2fs_balance_fs(sbi, true); - f2fs_lock_op(sbi); err = acquire_orphan_inode(sbi); if (err) @@ -634,6 +632,8 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, /* link_count was changed by d_tmpfile as well. */ f2fs_unlock_op(sbi); unlock_new_inode(inode); + + f2fs_balance_fs(sbi, true); return 0; release_out: -- GitLab From c4609776e3b71567d211d31fac65f5dcf0725311 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 11 Apr 2017 19:15:33 -0700 Subject: [PATCH 0633/5498] f2fs: give time to flush dirty pages for checkpoint If all the threads are waiting for checkpoint, we have no chance to flush required dirty pages. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index ad12c7a71b75..8cc2f116495e 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -981,6 +981,7 @@ retry_flush_dents: err = sync_dirty_inodes(sbi, DIR_INODE); if (err) goto out; + cond_resched(); goto retry_flush_dents; } @@ -996,6 +997,7 @@ retry_flush_dents: err = f2fs_sync_inode_meta(sbi); if (err) goto out; + cond_resched(); goto retry_flush_dents; } @@ -1010,6 +1012,7 @@ retry_flush_nodes: f2fs_unlock_all(sbi); goto out; } + cond_resched(); goto retry_flush_nodes; } -- GitLab From dcb718829b2b1b5c98dac6fcf648cde4421e4d6d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 12 Apr 2017 10:01:33 -0700 Subject: [PATCH 0634/5498] f2fs: allocate hot_data for atomic writes We'd better allocate atomic writes to hot_data zone. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 49f9e2779fca..7cb66de6e81e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1546,6 +1546,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) goto out; set_inode_flag(inode, FI_ATOMIC_FILE); + set_inode_flag(inode, FI_HOT_DATA); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); if (!get_dirty_pages(inode)) -- GitLab From 9d796e5cc1ace5fd723baaaa67a0e5375b496e17 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 12 Apr 2017 12:02:00 -0700 Subject: [PATCH 0635/5498] f2fs: fix not to set fsync/dentry mark Otherwise, we can see stale fsync/dentry mark given by previous calls, resulting in giving up roll-forward recovery due to wrong dentry mark. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 6bd74327da1a..d8304ae09222 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1464,6 +1464,9 @@ continue_unlock: f2fs_wait_on_page_writeback(page, NODE, true); BUG_ON(PageWriteback(page)); + set_fsync_mark(page, 0); + set_dentry_mark(page, 0); + if (!atomic || page == last_page) { set_fsync_mark(page, 1); if (IS_INODE(page)) { -- GitLab From 95e0d0ddf855511839a2c97adcd89dc22059b3f6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 14 Apr 2017 15:46:23 -0700 Subject: [PATCH 0636/5498] f2fs: avoid dirty node pages in check_only recovery In the check_only mode, we should not make any dirty node pages. Otherwise, we can get this panic: F2FS-fs (nvme0n1p1): Need to recover fsync data ------------[ cut here ]------------ kernel BUG at fs/f2fs/node.c:2204! CPU: 7 PID: 19923 Comm: mount Tainted: G OE 4.9.8 #2 RIP: 0010:[] [] flush_nat_entries+0x43b/0x7d0 [f2fs] Call Trace: [] ? __f2fs_submit_merged_bio+0x5a/0xd0 [f2fs] [] ? __f2fs_submit_merged_bio+0x5a/0xd0 [f2fs] [] ? __f2fs_submit_merged_bio+0x8b/0xd0 [f2fs] [] ? up_write+0x1f/0x40 [] ? __f2fs_submit_merged_bio+0x8b/0xd0 [f2fs] [] write_checkpoint+0x2f4/0xf20 [f2fs] [] ? trace_hardirqs_on+0xd/0x10 [] ? f2fs_sync_fs+0x79/0x190 [f2fs] [] ? f2fs_sync_fs+0x79/0x190 [f2fs] [] f2fs_sync_fs+0x85/0x190 [f2fs] [] f2fs_balance_fs_bg+0x7e/0x1c0 [f2fs] [] f2fs_write_node_pages+0x34/0x350 [f2fs] [] ? __lock_is_held+0x52/0x70 [] do_writepages+0x21/0x30 [] __writeback_single_inode+0x61/0x760 [] ? _raw_spin_unlock+0x27/0x40 [] writeback_single_inode+0xd5/0x190 [] write_inode_now+0x99/0xc0 [] iput+0x1f6/0x2c0 [] f2fs_fill_super+0xc32/0x10c0 [f2fs] [] mount_bdev+0x182/0x1b0 [] ? f2fs_commit_super+0x100/0x100 [f2fs] [] f2fs_mount+0x15/0x20 [f2fs] [] mount_fs+0x38/0x170 [] vfs_kern_mount+0x6b/0x160 [] do_mount+0x1be/0xd60 Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 647cbe5acea7..20147d297f89 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -198,7 +198,8 @@ static void recover_inode(struct inode *inode, struct page *page) ino_of_node(page), name); } -static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) +static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, + bool check_only) { struct curseg_info *curseg; struct page *page = NULL; @@ -225,7 +226,8 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) entry = get_fsync_inode(head, ino_of_node(page)); if (!entry) { - if (IS_INODE(page) && is_dent_dnode(page)) { + if (!check_only && + IS_INODE(page) && is_dent_dnode(page)) { err = recover_inode_page(sbi, page); if (err) break; @@ -569,7 +571,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) mutex_lock(&sbi->cp_mutex); /* step #1: find fsynced inode numbers */ - err = find_fsync_dnodes(sbi, &inode_list); + err = find_fsync_dnodes(sbi, &inode_list, check_only); if (err || list_empty(&inode_list)) goto out; -- GitLab From 38d46553c0a55c3debb140789f590502c4f568fa Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 14 Apr 2017 23:24:55 +0800 Subject: [PATCH 0637/5498] f2fs: use rb-tree to track pending discard commands Introduce rb-tree based discard cache infrastructure to speed up lookup and merge operation of discard entry. Signed-off-by: Chao Yu [Jaegeuk Kim: initialize dc to avoid build warning] Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 15 +-- fs/f2fs/f2fs.h | 48 ++++++++- fs/f2fs/segment.c | 223 +++++++++++++++++++++++++++++++++-------- 3 files changed, 236 insertions(+), 50 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 68e649a31c7d..221ad086ee00 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -49,7 +49,7 @@ static struct rb_entry *__lookup_rb_tree_slow(struct rb_root *root, return NULL; } -static struct rb_entry *__lookup_rb_tree(struct rb_root *root, +struct rb_entry *__lookup_rb_tree(struct rb_root *root, struct rb_entry *cached_re, unsigned int ofs) { struct rb_entry *re; @@ -61,7 +61,7 @@ static struct rb_entry *__lookup_rb_tree(struct rb_root *root, return re; } -static struct rb_node **__lookup_rb_tree_for_insert(struct f2fs_sb_info *sbi, +struct rb_node **__lookup_rb_tree_for_insert(struct f2fs_sb_info *sbi, struct rb_root *root, struct rb_node **parent, unsigned int ofs) { @@ -92,13 +92,14 @@ static struct rb_node **__lookup_rb_tree_for_insert(struct f2fs_sb_info *sbi, * in order to simpfy the insertion after. * tree must stay unchanged between lookup and insertion. */ -static struct rb_entry *__lookup_rb_tree_ret(struct rb_root *root, +struct rb_entry *__lookup_rb_tree_ret(struct rb_root *root, struct rb_entry *cached_re, unsigned int ofs, struct rb_entry **prev_entry, struct rb_entry **next_entry, struct rb_node ***insert_p, - struct rb_node **insert_parent) + struct rb_node **insert_parent, + bool force) { struct rb_node **pnode = &root->rb_node; struct rb_node *parent = NULL, *tmp_node; @@ -145,12 +146,12 @@ static struct rb_entry *__lookup_rb_tree_ret(struct rb_root *root, return NULL; lookup_neighbors: - if (ofs == re->ofs) { + if (ofs == re->ofs || force) { /* lookup prev node for merging backward later */ tmp_node = rb_prev(&re->rb_node); *prev_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node); } - if (ofs == re->ofs + re->len - 1) { + if (ofs == re->ofs + re->len - 1 || force) { /* lookup next node for merging frontward later */ tmp_node = rb_next(&re->rb_node); *next_entry = rb_entry_safe(tmp_node, struct rb_entry, rb_node); @@ -481,7 +482,7 @@ static void f2fs_update_extent_tree_range(struct inode *inode, (struct rb_entry *)et->cached_en, fofs, (struct rb_entry **)&prev_en, (struct rb_entry **)&next_en, - &insert_p, &insert_parent); + &insert_p, &insert_parent, false); if (!en) en = next_en; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c9f3e3eb73f8..a02e1ac9badb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -293,13 +293,26 @@ enum { D_DONE, }; +struct discard_info { + block_t lstart; /* logical start address */ + block_t len; /* length */ + block_t start; /* actual start address in dev */ +}; + struct discard_cmd { + struct rb_node rb_node; /* rb node located in rb-tree */ + union { + struct { + block_t lstart; /* logical start address */ + block_t len; /* length */ + block_t start; /* actual start address in dev */ + }; + struct discard_info di; /* discard info */ + + }; struct list_head list; /* command list */ struct completion wait; /* compleation */ struct block_device *bdev; /* bdev */ - block_t lstart; /* logical start address */ - block_t start; /* actual start address in dev */ - block_t len; /* length */ int state; /* state */ int error; /* bio error */ }; @@ -316,6 +329,7 @@ struct discard_cmd_control { atomic_t issued_discard; /* # of issued discard */ atomic_t issing_discard; /* # of issing discard */ atomic_t discard_cmd_cnt; /* # of cached cmd count */ + struct rb_root root; /* root of discard rb-tree */ }; /* for the list of fsync inodes, used only during recovery */ @@ -616,6 +630,24 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, ei->len = len; } +static inline bool __is_discard_mergeable(struct discard_info *back, + struct discard_info *front) +{ + return back->lstart + back->len == front->lstart; +} + +static inline bool __is_discard_back_mergeable(struct discard_info *cur, + struct discard_info *back) +{ + return __is_discard_mergeable(back, cur); +} + +static inline bool __is_discard_front_mergeable(struct discard_info *cur, + struct discard_info *front) +{ + return __is_discard_mergeable(cur, front); +} + static inline bool __is_extent_mergeable(struct extent_info *back, struct extent_info *front) { @@ -2674,6 +2706,16 @@ void f2fs_leave_shrinker(struct f2fs_sb_info *sbi); /* * extent_cache.c */ +struct rb_entry *__lookup_rb_tree(struct rb_root *root, + struct rb_entry *cached_re, unsigned int ofs); +struct rb_node **__lookup_rb_tree_for_insert(struct f2fs_sb_info *sbi, + struct rb_root *root, struct rb_node **parent, + unsigned int ofs); +struct rb_entry *__lookup_rb_tree_ret(struct rb_root *root, + struct rb_entry *cached_re, unsigned int ofs, + struct rb_entry **prev_entry, struct rb_entry **next_entry, + struct rb_node ***insert_p, struct rb_node **insert_parent, + bool force); unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink); bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext); void f2fs_drop_extent_tree(struct inode *inode); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 951a2bc4b69b..0b8deca4e760 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -672,7 +672,7 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_unlock(&dirty_i->seglist_lock); } -static void __add_discard_cmd(struct f2fs_sb_info *sbi, +static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t lstart, block_t start, block_t len) { @@ -689,18 +689,46 @@ static void __add_discard_cmd(struct f2fs_sb_info *sbi, dc->state = D_PREP; dc->error = 0; init_completion(&dc->wait); - - mutex_lock(&dcc->cmd_lock); list_add_tail(&dc->list, pend_list); - mutex_unlock(&dcc->cmd_lock); - atomic_inc(&dcc->discard_cmd_cnt); + + return dc; +} + +static struct discard_cmd *__attach_discard_cmd(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t lstart, + block_t start, block_t len, + struct rb_node *parent, struct rb_node **p) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct discard_cmd *dc; + + dc = __create_discard_cmd(sbi, bdev, lstart, start, len); + + rb_link_node(&dc->rb_node, parent, p); + rb_insert_color(&dc->rb_node, &dcc->root); + + return dc; } -static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc) +static void __detach_discard_cmd(struct discard_cmd_control *dcc, + struct discard_cmd *dc) { if (dc->state == D_DONE) - atomic_dec(&(SM_I(sbi)->dcc_info->issing_discard)); + atomic_dec(&dcc->issing_discard); + + list_del(&dc->list); + rb_erase(&dc->rb_node, &dcc->root); + + kmem_cache_free(discard_cmd_slab, dc); + + atomic_dec(&dcc->discard_cmd_cnt); +} + +static void __remove_discard_cmd(struct f2fs_sb_info *sbi, + struct discard_cmd *dc) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; if (dc->error == -EOPNOTSUPP) dc->error = 0; @@ -708,9 +736,7 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *d if (dc->error) f2fs_msg(sbi->sb, KERN_INFO, "Issue discard failed, ret: %d", dc->error); - list_del(&dc->list); - kmem_cache_free(discard_cmd_slab, dc); - atomic_dec(&SM_I(sbi)->dcc_info->discard_cmd_cnt); + __detach_discard_cmd(dcc, dc); } static void f2fs_submit_discard_endio(struct bio *bio, int err) @@ -833,62 +859,178 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, } } -static int __queue_discard_cmd(struct f2fs_sb_info *sbi, - struct block_device *bdev, block_t blkstart, block_t blklen) +static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t lstart, + block_t start, block_t len, + struct rb_node **insert_p, + struct rb_node *insert_parent) { - block_t lblkstart = blkstart; + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct rb_node **p = &dcc->root.rb_node; + struct rb_node *parent = NULL; + struct discard_cmd *dc = NULL; - trace_f2fs_issue_discard(bdev, blkstart, blklen); + if (insert_p && insert_parent) { + parent = insert_parent; + p = insert_p; + goto do_insert; + } - if (sbi->s_ndevs) { - int devi = f2fs_target_device_index(sbi, blkstart); + p = __lookup_rb_tree_for_insert(sbi, &dcc->root, &parent, lstart); +do_insert: + dc = __attach_discard_cmd(sbi, bdev, lstart, start, len, parent, p); + if (!dc) + return NULL; - blkstart -= FDEV(devi).start_blk; - } - __add_discard_cmd(sbi, bdev, lblkstart, blkstart, blklen); - wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); - return 0; + return dc; } static void __punch_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc, block_t blkaddr) { - block_t end_block = START_BLOCK(sbi, GET_SEGNO(sbi, blkaddr) + 1); + struct discard_info di = dc->di; + bool modified = false; - if (dc->state == D_DONE || dc->lstart + dc->len <= end_block) { + if (dc->state == D_DONE || dc->len == 1) { __remove_discard_cmd(sbi, dc); return; } - if (blkaddr - dc->lstart < dc->lstart + dc->len - end_block) { - dc->start += (end_block - dc->lstart); - dc->len -= (end_block - dc->lstart); - dc->lstart = end_block; - } else { + if (blkaddr > di.lstart) { dc->len = blkaddr - dc->lstart; + modified = true; + } + + if (blkaddr < di.lstart + di.len - 1) { + if (modified) { + __insert_discard_tree(sbi, dc->bdev, blkaddr + 1, + di.start + blkaddr + 1 - di.lstart, + di.lstart + di.len - 1 - blkaddr, + NULL, NULL); + } else { + dc->lstart++; + dc->len--; + dc->start++; + } } } -/* This should be covered by global mutex, &sit_i->sentry_lock */ -void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) +static void __update_discard_tree_range(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t lstart, + block_t start, block_t len) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *pend_list = &(dcc->discard_pend_list); - struct list_head *wait_list = &(dcc->discard_wait_list); - struct discard_cmd *dc, *tmp; + struct discard_cmd *prev_dc = NULL, *next_dc = NULL; + struct discard_cmd *dc; + struct discard_info di = {0}; + struct rb_node **insert_p = NULL, *insert_parent = NULL; + block_t end = lstart + len; mutex_lock(&dcc->cmd_lock); - list_for_each_entry_safe(dc, tmp, pend_list, list) { - if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) - __punch_discard_cmd(sbi, dc, blkaddr); + dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root, + NULL, lstart, + (struct rb_entry **)&prev_dc, + (struct rb_entry **)&next_dc, + &insert_p, &insert_parent, true); + if (dc) + prev_dc = dc; + + if (!prev_dc) { + di.lstart = lstart; + di.len = next_dc ? next_dc->lstart - lstart : len; + di.len = min(di.len, len); + di.start = start; } - list_for_each_entry_safe(dc, tmp, wait_list, list) { - if (dc->lstart <= blkaddr && blkaddr < dc->lstart + dc->len) { - wait_for_completion_io(&dc->wait); - __punch_discard_cmd(sbi, dc, blkaddr); + while (1) { + struct rb_node *node; + bool merged = false; + struct discard_cmd *tdc = NULL; + + if (prev_dc) { + di.lstart = prev_dc->lstart + prev_dc->len; + if (di.lstart < lstart) + di.lstart = lstart; + if (di.lstart >= end) + break; + + if (!next_dc || next_dc->lstart > end) + di.len = end - di.lstart; + else + di.len = next_dc->lstart - di.lstart; + di.start = start + di.lstart - lstart; + } + + if (!di.len) + goto next; + + if (prev_dc && prev_dc->state == D_PREP && + prev_dc->bdev == bdev && + __is_discard_back_mergeable(&di, &prev_dc->di)) { + prev_dc->di.len += di.len; + di = prev_dc->di; + tdc = prev_dc; + merged = true; + } + + if (next_dc && next_dc->state == D_PREP && + next_dc->bdev == bdev && + __is_discard_front_mergeable(&di, &next_dc->di)) { + next_dc->di.lstart = di.lstart; + next_dc->di.len += di.len; + next_dc->di.start = di.start; + if (tdc) + __remove_discard_cmd(sbi, tdc); + + merged = true; } + + if (!merged) + __insert_discard_tree(sbi, bdev, di.lstart, di.start, + di.len, NULL, NULL); + next: + prev_dc = next_dc; + if (!prev_dc) + break; + + node = rb_next(&prev_dc->rb_node); + next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); + } + + mutex_unlock(&dcc->cmd_lock); +} + +static int __queue_discard_cmd(struct f2fs_sb_info *sbi, + struct block_device *bdev, block_t blkstart, block_t blklen) +{ + block_t lblkstart = blkstart; + + trace_f2fs_issue_discard(bdev, blkstart, blklen); + + if (sbi->s_ndevs) { + int devi = f2fs_target_device_index(sbi, blkstart); + + blkstart -= FDEV(devi).start_blk; + } + __update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen); + wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); + return 0; +} + +/* This should be covered by global mutex, &sit_i->sentry_lock */ +void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct discard_cmd *dc; + + mutex_lock(&dcc->cmd_lock); + + dc = (struct discard_cmd *)__lookup_rb_tree(&dcc->root, NULL, blkaddr); + if (dc) { + if (dc->state != D_PREP) + wait_for_completion_io(&dc->wait); + __punch_discard_cmd(sbi, dc, blkaddr); } mutex_unlock(&dcc->cmd_lock); @@ -1257,6 +1399,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) atomic_set(&dcc->discard_cmd_cnt, 0); dcc->nr_discards = 0; dcc->max_discards = 0; + dcc->root = RB_ROOT; init_waitqueue_head(&dcc->discard_wait_queue); SM_I(sbi)->dcc_info = dcc; -- GitLab From 844489836f88cde3dfb858a68e5e79f3069578a2 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 15 Apr 2017 14:09:36 +0800 Subject: [PATCH 0638/5498] f2fs: clean up discard_cmd_control structure Avoid long variable name in discard_cmd_control structure, no logic change. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/f2fs.h | 8 ++++---- fs/f2fs/segment.c | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a02e1ac9badb..6ee273e50fa9 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -319,12 +319,12 @@ struct discard_cmd { struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ - struct list_head discard_entry_list; /* 4KB discard entry list */ - int nr_discards; /* # of discards in the list */ - struct list_head discard_pend_list; /* store pending entries */ - struct list_head discard_wait_list; /* store on-flushing entries */ + struct list_head entry_list; /* 4KB discard entry list */ + struct list_head pend_list; /* store pending entries */ + struct list_head wait_list; /* store on-flushing entries */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ struct mutex cmd_lock; + int nr_discards; /* # of discards in the list */ int max_discards; /* max. discards to be issued */ atomic_t issued_discard; /* # of issued discard */ atomic_t issing_discard; /* # of issing discard */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0b8deca4e760..0f8a6a477cb1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -677,7 +677,7 @@ static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, block_t start, block_t len) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *pend_list = &(dcc->discard_pend_list); + struct list_head *pend_list = &(dcc->pend_list); struct discard_cmd *dc; dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); @@ -852,7 +852,7 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; submit_bio(REQ_SYNC, bio); - list_move_tail(&dc->list, &dcc->discard_wait_list); + list_move_tail(&dc->list, &dcc->wait_list); } } else { __remove_discard_cmd(sbi, dc); @@ -1040,8 +1040,8 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *pend_list = &(dcc->discard_pend_list); - struct list_head *wait_list = &(dcc->discard_wait_list); + struct list_head *pend_list = &(dcc->pend_list); + struct list_head *wait_list = &(dcc->wait_list); struct discard_cmd *dc, *tmp; struct blk_plug plug; @@ -1065,8 +1065,8 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; - struct list_head *pend_list = &dcc->discard_pend_list; - struct list_head *wait_list = &dcc->discard_wait_list; + struct list_head *pend_list = &dcc->pend_list; + struct list_head *wait_list = &dcc->wait_list; struct discard_cmd *dc, *tmp; struct blk_plug plug; int iter = 0; @@ -1214,7 +1214,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, unsigned int start = 0, end = -1; bool force = (cpc->reason == CP_DISCARD); struct discard_entry *de = NULL; - struct list_head *head = &SM_I(sbi)->dcc_info->discard_entry_list; + struct list_head *head = &SM_I(sbi)->dcc_info->entry_list; int i; if (se->valid_blocks == max_blocks || !f2fs_discard_en(sbi)) @@ -1263,7 +1263,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, void release_discard_addrs(struct f2fs_sb_info *sbi) { - struct list_head *head = &(SM_I(sbi)->dcc_info->discard_entry_list); + struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list); struct discard_entry *entry, *this; /* drop caches */ @@ -1289,7 +1289,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) { - struct list_head *head = &(SM_I(sbi)->dcc_info->discard_entry_list); + struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list); struct discard_entry *entry, *this; struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; @@ -1390,9 +1390,9 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) if (!dcc) return -ENOMEM; - INIT_LIST_HEAD(&dcc->discard_entry_list); - INIT_LIST_HEAD(&dcc->discard_pend_list); - INIT_LIST_HEAD(&dcc->discard_wait_list); + INIT_LIST_HEAD(&dcc->entry_list); + INIT_LIST_HEAD(&dcc->pend_list); + INIT_LIST_HEAD(&dcc->wait_list); mutex_init(&dcc->cmd_lock); atomic_set(&dcc->issued_discard, 0); atomic_set(&dcc->issing_discard, 0); -- GitLab From 3c901ddf0f5eb5f9286c55466ffa3e09026bff30 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 15 Apr 2017 14:09:37 +0800 Subject: [PATCH 0639/5498] f2fs: in prior to issue big discard Keep issuing big size discard in prior instead of the one with random size, so that we expect that it will help to: - be quick to recycle unused large space in flash storage device. - give a chance for a) wait to merge small piece discards into bigger one, or b) avoid issuing discards while they have being reallocated by SSR. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 7 +++++- fs/f2fs/segment.c | 54 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6ee273e50fa9..32186771672b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -287,6 +287,11 @@ struct discard_entry { unsigned char discard_map[SIT_VBLOCK_MAP_SIZE]; /* segment discard bitmap */ }; +/* max discard pend list number */ +#define MAX_PLIST_NUM 512 +#define plist_idx(blk_num) ((blk_num) >= MAX_PLIST_NUM ? \ + (MAX_PLIST_NUM - 1) : (blk_num - 1)) + enum { D_PREP, D_SUBMIT, @@ -320,7 +325,7 @@ struct discard_cmd { struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head entry_list; /* 4KB discard entry list */ - struct list_head pend_list; /* store pending entries */ + struct list_head pend_list[MAX_PLIST_NUM];/* store pending entries */ struct list_head wait_list; /* store on-flushing entries */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ struct mutex cmd_lock; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0f8a6a477cb1..f844422c1568 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -677,9 +677,13 @@ static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, block_t start, block_t len) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *pend_list = &(dcc->pend_list); + struct list_head *pend_list; struct discard_cmd *dc; + f2fs_bug_on(sbi, !len); + + pend_list = &dcc->pend_list[plist_idx(len)]; + dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); INIT_LIST_HEAD(&dc->list); dc->bdev = bdev; @@ -885,9 +889,16 @@ do_insert: return dc; } +static void __relocate_discard_cmd(struct discard_cmd_control *dcc, + struct discard_cmd *dc) +{ + list_move_tail(&dc->list, &dcc->pend_list[plist_idx(dc->len)]); +} + static void __punch_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc, block_t blkaddr) { + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_info di = dc->di; bool modified = false; @@ -898,6 +909,7 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, if (blkaddr > di.lstart) { dc->len = blkaddr - dc->lstart; + __relocate_discard_cmd(dcc, dc); modified = true; } @@ -911,6 +923,7 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, dc->lstart++; dc->len--; dc->start++; + __relocate_discard_cmd(dcc, dc); } } } @@ -969,6 +982,7 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, prev_dc->bdev == bdev && __is_discard_back_mergeable(&di, &prev_dc->di)) { prev_dc->di.len += di.len; + __relocate_discard_cmd(dcc, prev_dc); di = prev_dc->di; tdc = prev_dc; merged = true; @@ -980,6 +994,7 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start; + __relocate_discard_cmd(dcc, next_dc); if (tdc) __remove_discard_cmd(sbi, tdc); @@ -1040,16 +1055,20 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *pend_list = &(dcc->pend_list); + struct list_head *pend_list; struct list_head *wait_list = &(dcc->wait_list); struct discard_cmd *dc, *tmp; struct blk_plug plug; + int i; mutex_lock(&dcc->cmd_lock); blk_start_plug(&plug); - list_for_each_entry_safe(dc, tmp, pend_list, list) - __submit_discard_cmd(sbi, dc); + for (i = 0; i < MAX_PLIST_NUM; i++) { + pend_list = &dcc->pend_list[i]; + list_for_each_entry_safe(dc, tmp, pend_list, list) + __submit_discard_cmd(sbi, dc); + } blk_finish_plug(&plug); list_for_each_entry_safe(dc, tmp, wait_list, list) { @@ -1065,26 +1084,30 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; - struct list_head *pend_list = &dcc->pend_list; + struct list_head *pend_list; struct list_head *wait_list = &dcc->wait_list; struct discard_cmd *dc, *tmp; struct blk_plug plug; - int iter = 0; + int iter = 0, i; repeat: if (kthread_should_stop()) return 0; mutex_lock(&dcc->cmd_lock); blk_start_plug(&plug); - list_for_each_entry_safe(dc, tmp, pend_list, list) { - f2fs_bug_on(sbi, dc->state != D_PREP); + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + pend_list = &dcc->pend_list[i]; + list_for_each_entry_safe(dc, tmp, pend_list, list) { + f2fs_bug_on(sbi, dc->state != D_PREP); - if (is_idle(sbi)) - __submit_discard_cmd(sbi, dc); + if (is_idle(sbi)) + __submit_discard_cmd(sbi, dc); - if (iter++ > DISCARD_ISSUE_RATE) - break; + if (iter++ > DISCARD_ISSUE_RATE) + goto next_step; + } } +next_step: blk_finish_plug(&plug); list_for_each_entry_safe(dc, tmp, wait_list, list) { @@ -1099,7 +1122,7 @@ repeat: congestion_wait(BLK_RW_SYNC, HZ/50); wait_event_interruptible(*q, kthread_should_stop() || - !list_empty(pend_list) || !list_empty(wait_list)); + atomic_read(&dcc->discard_cmd_cnt)); goto repeat; } @@ -1379,7 +1402,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) { dev_t dev = sbi->sb->s_bdev->bd_dev; struct discard_cmd_control *dcc; - int err = 0; + int err = 0, i; if (SM_I(sbi)->dcc_info) { dcc = SM_I(sbi)->dcc_info; @@ -1391,7 +1414,8 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) return -ENOMEM; INIT_LIST_HEAD(&dcc->entry_list); - INIT_LIST_HEAD(&dcc->pend_list); + for (i = 0; i < MAX_PLIST_NUM; i++) + INIT_LIST_HEAD(&dcc->pend_list[i]); INIT_LIST_HEAD(&dcc->wait_list); mutex_init(&dcc->cmd_lock); atomic_set(&dcc->issued_discard, 0); -- GitLab From 95ae34b262b64079ee6c746688ccf5431add2658 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 15 Apr 2017 14:09:38 +0800 Subject: [PATCH 0640/5498] f2fs: trace __submit_discard_cmd Add an even class f2fs_discard for introducing f2fs_queue_discard, then use f2fs_{queue,issue}_discard to trace __{queue,submit}_discard_cmd. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 +++- include/trace/events/f2fs.h | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f844422c1568..a41153e06737 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -843,6 +843,8 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, if (dc->state != D_PREP) return; + trace_f2fs_issue_discard(dc->bdev, dc->start, dc->len); + dc->error = __blkdev_issue_discard(dc->bdev, SECTOR_FROM_BLOCK(dc->start), SECTOR_FROM_BLOCK(dc->len), @@ -1021,7 +1023,7 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, { block_t lblkstart = blkstart; - trace_f2fs_issue_discard(bdev, blkstart, blklen); + trace_f2fs_queue_discard(bdev, blkstart, blklen); if (sbi->s_ndevs) { int devi = f2fs_target_device_index(sbi, blkstart); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 310ff1f223a0..f4cd25fd7d8a 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1082,7 +1082,7 @@ TRACE_EVENT(f2fs_write_checkpoint, __entry->msg) ); -TRACE_EVENT(f2fs_issue_discard, +DECLARE_EVENT_CLASS(f2fs_discard, TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen), @@ -1106,6 +1106,20 @@ TRACE_EVENT(f2fs_issue_discard, (unsigned long long)__entry->blklen) ); +DEFINE_EVENT(f2fs_discard, f2fs_queue_discard, + + TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen), + + TP_ARGS(dev, blkstart, blklen) +); + +DEFINE_EVENT(f2fs_discard, f2fs_issue_discard, + + TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen), + + TP_ARGS(dev, blkstart, blklen) +); + TRACE_EVENT(f2fs_issue_reset_zone, TP_PROTO(struct block_device *dev, block_t blkstart), -- GitLab From 3cb8d1a13e33c02d90256c994fe3c1c68e56e570 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 17 Apr 2017 18:21:43 +0800 Subject: [PATCH 0641/5498] f2fs: introduce __check_rb_tree_consistence Introduce __check_rb_tree_consistence to check consistence of rb-tree based discard cache in runtime. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 32 ++++++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/segment.c | 15 +++++++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 221ad086ee00..2f98d7039701 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -159,6 +159,38 @@ lookup_neighbors: return re; } +bool __check_rb_tree_consistence(struct f2fs_sb_info *sbi, + struct rb_root *root) +{ +#ifdef CONFIG_F2FS_CHECK_FS + struct rb_node *cur = rb_first(root), *next; + struct rb_entry *cur_re, *next_re; + + if (!cur) + return true; + + while (cur) { + next = rb_next(cur); + if (!next) + return true; + + cur_re = rb_entry(cur, struct rb_entry, rb_node); + next_re = rb_entry(next, struct rb_entry, rb_node); + + if (cur_re->ofs + cur_re->len > next_re->ofs) { + f2fs_msg(sbi->sb, KERN_INFO, "inconsistent rbtree, " + "cur(%u, %u) next(%u, %u)", + cur_re->ofs, cur_re->len, + next_re->ofs, next_re->len); + return false; + } + + cur = next; + } +#endif + return true; +} + static struct kmem_cache *extent_tree_slab; static struct kmem_cache *extent_node_slab; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 32186771672b..4194e520e6cd 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2721,6 +2721,8 @@ struct rb_entry *__lookup_rb_tree_ret(struct rb_root *root, struct rb_entry **prev_entry, struct rb_entry **next_entry, struct rb_node ***insert_p, struct rb_node **insert_parent, bool force); +bool __check_rb_tree_consistence(struct f2fs_sb_info *sbi, + struct rb_root *root); unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink); bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext); void f2fs_drop_extent_tree(struct inode *inode); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a41153e06737..1c4cafe264ac 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -912,6 +912,7 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, if (blkaddr > di.lstart) { dc->len = blkaddr - dc->lstart; __relocate_discard_cmd(dcc, dc); + f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); modified = true; } @@ -921,11 +922,15 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, di.start + blkaddr + 1 - di.lstart, di.lstart + di.len - 1 - blkaddr, NULL, NULL); + f2fs_bug_on(sbi, + !__check_rb_tree_consistence(sbi, &dcc->root)); } else { dc->lstart++; dc->len--; dc->start++; __relocate_discard_cmd(dcc, dc); + f2fs_bug_on(sbi, + !__check_rb_tree_consistence(sbi, &dcc->root)); } } } @@ -985,6 +990,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, __is_discard_back_mergeable(&di, &prev_dc->di)) { prev_dc->di.len += di.len; __relocate_discard_cmd(dcc, prev_dc); + f2fs_bug_on(sbi, + !__check_rb_tree_consistence(sbi, &dcc->root)); di = prev_dc->di; tdc = prev_dc; merged = true; @@ -999,13 +1006,17 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, __relocate_discard_cmd(dcc, next_dc); if (tdc) __remove_discard_cmd(sbi, tdc); - + f2fs_bug_on(sbi, + !__check_rb_tree_consistence(sbi, &dcc->root)); merged = true; } - if (!merged) + if (!merged) { __insert_discard_tree(sbi, bdev, di.lstart, di.start, di.len, NULL, NULL); + f2fs_bug_on(sbi, + !__check_rb_tree_consistence(sbi, &dcc->root)); + } next: prev_dc = next_dc; if (!prev_dc) -- GitLab From ce97d273015d7b181f601be989f4851d8b14e160 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 18 Apr 2017 19:23:39 +0800 Subject: [PATCH 0642/5498] f2fs: unlock cp_rwsem early for IPU writes For IPU writes, there won't be any udpates in dnode page since we will reuse old block address instead of allocating new one, so we don't need to lock cp_rwsem during IPU IO submitting. Signed-off-by: Chao Yu --- fs/f2fs/data.c | 6 +++++- fs/f2fs/f2fs.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 11c2c1962bcf..a8d041629915 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1353,6 +1353,8 @@ retry_encrypt: !is_cold_data(page) && !IS_ATOMIC_WRITTEN_PAGE(page) && need_inplace_update(inode))) { + f2fs_unlock_op(F2FS_I_SB(inode)); + fio->cp_rwsem_locked = false; err = rewrite_data_page(fio); set_inode_flag(inode, FI_UPDATE_WRITE); trace_f2fs_do_write_data_page(page, IPU); @@ -1388,6 +1390,7 @@ static int __write_data_page(struct page *page, bool *submitted, .page = page, .encrypted_page = NULL, .submitted = false, + .cp_rwsem_locked = true, }; trace_f2fs_writepage(page, DATA); @@ -1445,7 +1448,8 @@ write: err = do_write_data_page(&fio); if (F2FS_I(inode)->last_disk_size < psize) F2FS_I(inode)->last_disk_size = psize; - f2fs_unlock_op(sbi); + if (fio.cp_rwsem_locked) + f2fs_unlock_op(sbi); done: if (err && err != -ENOENT) goto redirty_out; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4194e520e6cd..064b9373f798 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -892,6 +892,7 @@ struct f2fs_io_info { struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ bool submitted; /* indicate IO submission */ + bool cp_rwsem_locked; /* indicate cp_rwsem is held */ }; #define is_read_io(rw) ((rw) == READ) -- GitLab From cf54a09663758760d68db30c572c7ce96e0f9396 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 18 Apr 2017 19:27:39 +0800 Subject: [PATCH 0643/5498] f2fs: add undiscard blocks stat This patch adds to account undiscard blocks. Signed-off-by: Chao Yu --- fs/f2fs/debug.c | 5 +++-- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/segment.c | 9 +++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 1866962c00be..aa27ae495377 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -69,6 +69,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) atomic_read(&SM_I(sbi)->dcc_info->issing_discard); si->nr_discard_cmd = atomic_read(&SM_I(sbi)->dcc_info->discard_cmd_cnt); + si->undiscard_blks = SM_I(sbi)->dcc_info->undiscard_blks; } si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->rsvd_segs = reserved_segments(sbi); @@ -350,11 +351,11 @@ static int stat_show(struct seq_file *s, void *v) si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d), " - "Discard: (%4d %4d)) cmd: %4d\n", + "Discard: (%4d %4d)) cmd: %4d undiscard:%4u\n", si->nr_wb_cp_data, si->nr_wb_data, si->nr_flushing, si->nr_flushed, si->nr_discarding, si->nr_discarded, - si->nr_discard_cmd); + si->nr_discard_cmd, si->undiscard_blks); seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), " "volatile IO: %4d (Max. %4d)\n", si->inmem_pages, si->aw_cnt, si->max_aw_cnt, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 064b9373f798..4b5aafdde014 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -331,6 +331,7 @@ struct discard_cmd_control { struct mutex cmd_lock; int nr_discards; /* # of discards in the list */ int max_discards; /* max. discards to be issued */ + unsigned int undiscard_blks; /* # of undiscard blocks */ atomic_t issued_discard; /* # of issued discard */ atomic_t issing_discard; /* # of issing discard */ atomic_t discard_cmd_cnt; /* # of cached cmd count */ @@ -2491,6 +2492,7 @@ struct f2fs_stat_info { int bg_gc, nr_wb_cp_data, nr_wb_data; int nr_flushing, nr_flushed, nr_discarding, nr_discarded; int nr_discard_cmd; + unsigned int undiscard_blks; int inline_xattr, inline_inode, inline_dir, append, update, orphans; int aw_cnt, max_aw_cnt, vw_cnt, max_vw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1c4cafe264ac..7cbfb33e4330 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -695,6 +695,7 @@ static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, init_completion(&dc->wait); list_add_tail(&dc->list, pend_list); atomic_inc(&dcc->discard_cmd_cnt); + dcc->undiscard_blks += len; return dc; } @@ -723,6 +724,7 @@ static void __detach_discard_cmd(struct discard_cmd_control *dcc, list_del(&dc->list); rb_erase(&dc->rb_node, &dcc->root); + dcc->undiscard_blks -= dc->len; kmem_cache_free(discard_cmd_slab, dc); @@ -909,8 +911,11 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, return; } + dcc->undiscard_blks -= di.len; + if (blkaddr > di.lstart) { dc->len = blkaddr - dc->lstart; + dcc->undiscard_blks += dc->len; __relocate_discard_cmd(dcc, dc); f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); modified = true; @@ -928,6 +933,7 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, dc->lstart++; dc->len--; dc->start++; + dcc->undiscard_blks += dc->len; __relocate_discard_cmd(dcc, dc); f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); @@ -989,6 +995,7 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, prev_dc->bdev == bdev && __is_discard_back_mergeable(&di, &prev_dc->di)) { prev_dc->di.len += di.len; + dcc->undiscard_blks += di.len; __relocate_discard_cmd(dcc, prev_dc); f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); @@ -1003,6 +1010,7 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start; + dcc->undiscard_blks += di.len; __relocate_discard_cmd(dcc, next_dc); if (tdc) __remove_discard_cmd(sbi, tdc); @@ -1436,6 +1444,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) atomic_set(&dcc->discard_cmd_cnt, 0); dcc->nr_discards = 0; dcc->max_discards = 0; + dcc->undiscard_blks = 0; dcc->root = RB_ROOT; init_waitqueue_head(&dcc->discard_wait_queue); -- GitLab From 67716cdaca5b16c95c2f4c50fb734af9b535d008 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Tue, 18 Apr 2017 11:57:16 +0000 Subject: [PATCH 0644/5498] f2fs: introduce async IPU policy This patch introduces an ASYNC IPU policy. Under senario of large # of async updating(e.g. log writing in Android), disk would be seriously fragmented, and higher frequent gc would be triggered. This patch uses IPU to rewrite the async update writting, since async is NOT sensitive to io latency. Signed-off-by: Hou Pengyang --- fs/f2fs/data.c | 2 +- fs/f2fs/file.c | 2 +- fs/f2fs/segment.h | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a8d041629915..ae839acb4b36 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1352,7 +1352,7 @@ retry_encrypt: if (unlikely(fio->old_blkaddr != NEW_ADDR && !is_cold_data(page) && !IS_ATOMIC_WRITTEN_PAGE(page) && - need_inplace_update(inode))) { + need_inplace_update(inode, fio))) { f2fs_unlock_op(F2FS_I_SB(inode)); fio->cp_rwsem_locked = false; err = rewrite_data_page(fio); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 7cb66de6e81e..13cb2a8b4bfa 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1913,7 +1913,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, int err; /* if in-place-update policy is enabled, don't waste time here */ - if (need_inplace_update(inode)) + if (need_inplace_update(inode, NULL)) return -EINVAL; pg_start = range->start >> PAGE_SHIFT; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 7fbe87149d1f..884fc7d62f67 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -555,9 +555,11 @@ enum { F2FS_IPU_UTIL, F2FS_IPU_SSR_UTIL, F2FS_IPU_FSYNC, + F2FS_IPU_ASYNC, }; -static inline bool need_inplace_update(struct inode *inode) +static inline bool need_inplace_update(struct inode *inode, + struct f2fs_io_info *fio) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); unsigned int policy = SM_I(sbi)->ipu_policy; @@ -580,6 +582,14 @@ static inline bool need_inplace_update(struct inode *inode) utilization(sbi) > SM_I(sbi)->min_ipu_util) return true; + /* + * IPU for rewrite async pages + */ + if (policy & (0x1 << F2FS_IPU_ASYNC) && + fio && fio->op == REQ_OP_WRITE && + !(fio->op_flags & REQ_SYNC)) + return true; + /* this is only set during fdatasync */ if (policy & (0x1 << F2FS_IPU_FSYNC) && is_inode_flag_set(inode, FI_NEED_IPU)) -- GitLab From 29e4eede8952b072896484633ccff96b015a2e0c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 13 Apr 2017 15:17:00 -0700 Subject: [PATCH 0645/5498] f2fs: add ioctl to flush data from faster device to cold area This patch adds an ioctl to flush data in faster device to cold area. User can give device number and number of segments to move. It doesn't move it if there is only one device. The parameter looks like: struct f2fs_flush_device { u32 dev_num; /* device number to flush */ u32 segments; /* # of segments to flush */ }; Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 11 ++++++-- fs/f2fs/file.c | 69 +++++++++++++++++++++++++++++++++++++++++++++-- fs/f2fs/gc.c | 42 +++++++++++++++++++---------- fs/f2fs/segment.c | 14 +++++++--- fs/f2fs/segment.h | 7 ++++- 5 files changed, 120 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4b5aafdde014..19c91f1811c1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -399,6 +399,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8) #define F2FS_IOC_MOVE_RANGE _IOWR(F2FS_IOCTL_MAGIC, 9, \ struct f2fs_move_range) +#define F2FS_IOC_FLUSH_DEVICE _IOW(F2FS_IOCTL_MAGIC, 10, \ + struct f2fs_flush_device) #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY @@ -435,6 +437,11 @@ struct f2fs_move_range { u64 len; /* size to move */ }; +struct f2fs_flush_device { + u32 dev_num; /* device number to flush */ + u32 segments; /* # of segments to flush */ +}; + /* * For INODE and NODE manager */ @@ -1079,7 +1086,6 @@ struct f2fs_sb_info { int bg_gc; /* background gc calls */ unsigned int ndirty_inode[NR_INODE_TYPE]; /* # of dirty inodes */ #endif - unsigned int last_victim[2]; /* last victim segment # */ spinlock_t stat_lock; /* lock for stat operations */ /* For sysfs suppport */ @@ -2463,7 +2469,8 @@ int f2fs_migrate_page(struct address_space *mapping, struct page *newpage, int start_gc_thread(struct f2fs_sb_info *sbi); void stop_gc_thread(struct f2fs_sb_info *sbi); block_t start_bidx_of_node(unsigned int node_ofs, struct inode *inode); -int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background); +int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background, + unsigned int segno); void build_gc_manager(struct f2fs_sb_info *sbi); /* diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 13cb2a8b4bfa..bdbe832cd897 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1870,7 +1870,7 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg) mutex_lock(&sbi->gc_mutex); } - ret = f2fs_gc(sbi, sync, true); + ret = f2fs_gc(sbi, sync, true, NULL_SEGNO); out: mnt_drop_write_file(filp); return ret; @@ -2226,6 +2226,69 @@ err_out: return err; } +static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct sit_info *sm = SIT_I(sbi); + unsigned int start_segno = 0, end_segno = 0; + unsigned int dev_start_segno = 0, dev_end_segno = 0; + struct f2fs_flush_device range; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (f2fs_readonly(sbi->sb)) + return -EROFS; + + if (copy_from_user(&range, (struct f2fs_flush_device __user *)arg, + sizeof(range))) + return -EFAULT; + + if (sbi->s_ndevs <= 1 || sbi->s_ndevs - 1 <= range.dev_num || + sbi->segs_per_sec != 1) { + f2fs_msg(sbi->sb, KERN_WARNING, + "Can't flush %u in %d for segs_per_sec %u != 1\n", + range.dev_num, sbi->s_ndevs, + sbi->segs_per_sec); + return -EINVAL; + } + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + if (range.dev_num != 0) + dev_start_segno = GET_SEGNO(sbi, FDEV(range.dev_num).start_blk); + dev_end_segno = GET_SEGNO(sbi, FDEV(range.dev_num).end_blk); + + start_segno = sm->last_victim[FLUSH_DEVICE]; + if (start_segno < dev_start_segno || start_segno >= dev_end_segno) + start_segno = dev_start_segno; + end_segno = min(start_segno + range.segments, dev_end_segno); + + while (start_segno < end_segno) { + if (!mutex_trylock(&sbi->gc_mutex)) { + ret = -EBUSY; + goto out; + } + sm->last_victim[GC_CB] = end_segno + 1; + sm->last_victim[GC_GREEDY] = end_segno + 1; + sm->last_victim[ALLOC_NEXT] = end_segno + 1; + ret = f2fs_gc(sbi, true, true, start_segno); + if (ret == -EAGAIN) + ret = 0; + else if (ret < 0) + break; + start_segno++; + } +out: + mnt_drop_write_file(filp); + return ret; +} + + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -2263,6 +2326,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_defragment(filp, arg); case F2FS_IOC_MOVE_RANGE: return f2fs_ioc_move_range(filp, arg); + case F2FS_IOC_FLUSH_DEVICE: + return f2fs_ioc_flush_device(filp, arg); default: return -ENOTTY; } @@ -2332,8 +2397,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_GARBAGE_COLLECT: case F2FS_IOC_WRITE_CHECKPOINT: case F2FS_IOC_DEFRAGMENT: - break; case F2FS_IOC_MOVE_RANGE: + case F2FS_IOC_FLUSH_DEVICE: break; default: return -ENOIOCTLCMD; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 9172112d6246..74a10b7675f5 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -84,7 +84,7 @@ static int gc_thread_func(void *data) stat_inc_bggc_count(sbi); /* if return value is not zero, no victim was selected */ - if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true)) + if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true, NULL_SEGNO)) wait_ms = gc_th->no_gc_sleep_time; trace_f2fs_background_gc(sbi->sb, wait_ms, @@ -176,7 +176,7 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, if (type == CURSEG_HOT_DATA || IS_NODESEG(type)) p->offset = 0; else - p->offset = sbi->last_victim[p->gc_mode]; + p->offset = SIT_I(sbi)->last_victim[p->gc_mode]; } static unsigned int get_max_cost(struct f2fs_sb_info *sbi, @@ -295,6 +295,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, unsigned int *result, int gc_type, int type, char alloc_mode) { struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + struct sit_info *sm = SIT_I(sbi); struct victim_sel_policy p; unsigned int secno, last_victim; unsigned int last_segment = MAIN_SEGS(sbi); @@ -308,10 +309,18 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, p.min_segno = NULL_SEGNO; p.min_cost = get_max_cost(sbi, &p); + if (*result != NULL_SEGNO) { + if (IS_DATASEG(get_seg_entry(sbi, *result)->type) && + get_valid_blocks(sbi, *result, false) && + !sec_usage_check(sbi, GET_SEC_FROM_SEG(sbi, *result))) + p.min_segno = *result; + goto out; + } + if (p.max_search == 0) goto out; - last_victim = sbi->last_victim[p.gc_mode]; + last_victim = sm->last_victim[p.gc_mode]; if (p.alloc_mode == LFS && gc_type == FG_GC) { p.min_segno = check_bg_victims(sbi); if (p.min_segno != NULL_SEGNO) @@ -324,9 +333,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, segno = find_next_bit(p.dirty_segmap, last_segment, p.offset); if (segno >= last_segment) { - if (sbi->last_victim[p.gc_mode]) { - last_segment = sbi->last_victim[p.gc_mode]; - sbi->last_victim[p.gc_mode] = 0; + if (sm->last_victim[p.gc_mode]) { + last_segment = + sm->last_victim[p.gc_mode]; + sm->last_victim[p.gc_mode] = 0; p.offset = 0; continue; } @@ -361,11 +371,11 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, } next: if (nsearched >= p.max_search) { - if (!sbi->last_victim[p.gc_mode] && segno <= last_victim) - sbi->last_victim[p.gc_mode] = last_victim + 1; + if (!sm->last_victim[p.gc_mode] && segno <= last_victim) + sm->last_victim[p.gc_mode] = last_victim + 1; else - sbi->last_victim[p.gc_mode] = segno + 1; - sbi->last_victim[p.gc_mode] %= MAIN_SEGS(sbi); + sm->last_victim[p.gc_mode] = segno + 1; + sm->last_victim[p.gc_mode] %= MAIN_SEGS(sbi); break; } } @@ -912,7 +922,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, * - mutex_lock(sentry_lock) - change_curseg() * - lock_page(sum_page) */ - if (type == SUM_TYPE_NODE) gc_node_segment(sbi, sum->entries, segno, gc_type); else @@ -939,13 +948,14 @@ next: return sec_freed; } -int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background) +int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, + bool background, unsigned int segno) { - unsigned int segno; int gc_type = sync ? FG_GC : BG_GC; int sec_freed = 0; int ret = -EINVAL; struct cp_control cpc; + unsigned int init_segno = segno; struct gc_inode_list gc_list = { .ilist = LIST_HEAD_INIT(gc_list.ilist), .iroot = RADIX_TREE_INIT(GFP_NOFS), @@ -990,13 +1000,17 @@ gc_more: sbi->cur_victim_sec = NULL_SEGNO; if (!sync) { - if (has_not_enough_free_secs(sbi, sec_freed, 0)) + if (has_not_enough_free_secs(sbi, sec_freed, 0)) { + segno = NULL_SEGNO; goto gc_more; + } if (gc_type == FG_GC) ret = write_checkpoint(sbi, &cpc); } stop: + SIT_I(sbi)->last_victim[ALLOC_NEXT] = 0; + SIT_I(sbi)->last_victim[FLUSH_DEVICE] = init_segno; mutex_unlock(&sbi->gc_mutex); put_gc_inode(&gc_list); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7cbfb33e4330..c9ee1aa3c027 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -401,7 +401,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) */ if (has_not_enough_free_secs(sbi, 0, 0)) { mutex_lock(&sbi->gc_mutex); - f2fs_gc(sbi, false, false); + f2fs_gc(sbi, false, false, NULL_SEGNO); } } @@ -1834,6 +1834,8 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) if (type == CURSEG_HOT_DATA || IS_NODESEG(type)) return 0; + if (SIT_I(sbi)->last_victim[ALLOC_NEXT]) + return SIT_I(sbi)->last_victim[ALLOC_NEXT]; return CURSEG_I(sbi, type)->segno; } @@ -1931,12 +1933,15 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; + unsigned segno = NULL_SEGNO; int i, cnt; bool reversed = false; /* need_SSR() already forces to do this */ - if (v_ops->get_victim(sbi, &(curseg)->next_segno, BG_GC, type, SSR)) + if (v_ops->get_victim(sbi, &segno, BG_GC, type, SSR)) { + curseg->next_segno = segno; return 1; + } /* For node segments, let's do SSR more intensively */ if (IS_NODESEG(type)) { @@ -1960,9 +1965,10 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) for (; cnt-- > 0; reversed ? i-- : i++) { if (i == type) continue; - if (v_ops->get_victim(sbi, &(curseg)->next_segno, - BG_GC, i, SSR)) + if (v_ops->get_victim(sbi, &segno, BG_GC, i, SSR)) { + curseg->next_segno = segno; return 1; + } } return 0; } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 884fc7d62f67..826b607a26df 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -137,7 +137,10 @@ enum { */ enum { GC_CB = 0, - GC_GREEDY + GC_GREEDY, + ALLOC_NEXT, + FLUSH_DEVICE, + MAX_GC_POLICY, }; /* @@ -232,6 +235,8 @@ struct sit_info { unsigned long long mounted_time; /* mount time */ unsigned long long min_mtime; /* min. modification time */ unsigned long long max_mtime; /* max. modification time */ + + unsigned int last_victim[MAX_GC_POLICY]; /* last victim segment # */ }; struct free_segmap_info { -- GitLab From 48036ed378348805e1980c285c720a859c0c4010 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 18 Apr 2017 13:47:25 -0700 Subject: [PATCH 0646/5498] f2fs: fix _IOW usage This patch fixes wrong _IOW usage. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 19c91f1811c1..dfce8f95cbbc 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -394,9 +394,10 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) #define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4) #define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) -#define F2FS_IOC_GARBAGE_COLLECT _IO(F2FS_IOCTL_MAGIC, 6) +#define F2FS_IOC_GARBAGE_COLLECT _IOW(F2FS_IOCTL_MAGIC, 6, __u32) #define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7) -#define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8) +#define F2FS_IOC_DEFRAGMENT _IOWR(F2FS_IOCTL_MAGIC, 8, \ + struct f2fs_defragment) #define F2FS_IOC_MOVE_RANGE _IOWR(F2FS_IOCTL_MAGIC, 9, \ struct f2fs_move_range) #define F2FS_IOC_FLUSH_DEVICE _IOW(F2FS_IOCTL_MAGIC, 10, \ -- GitLab From 2e06ed5b238abe7c01f4000c9584c09e8cf86efc Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 18 Apr 2017 15:03:15 -0700 Subject: [PATCH 0647/5498] f2fs: assign allocation hint for warm/cold data This patch gives slower device region to warm/cold data area more eagerly. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 74a10b7675f5..84db41ca27c1 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1033,4 +1033,9 @@ void build_gc_manager(struct f2fs_sb_info *sbi) sbi->fggc_threshold = div64_u64((main_count - ovp_count) * BLKS_PER_SEC(sbi), (main_count - resv_count)); + + /* give warm/cold data area from slower device */ + if (sbi->s_ndevs && sbi->segs_per_sec == 1) + SIT_I(sbi)->last_victim[ALLOC_NEXT] = + GET_SEGNO(sbi, FDEV(0).end_blk) + 1; } -- GitLab From c9d01c3d957e255980930c81e7349b8fd0cabdeb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Apr 2017 19:38:33 +0200 Subject: [PATCH 0648/5498] f2fs: improve definition of statistic macros With a recent addition of f2fs_lookup_extent_tree(), we get a warning about the use of empty macros: fs/f2fs/extent_cache.c: In function 'f2fs_lookup_extent_tree': fs/f2fs/extent_cache.c:358:32: error: suggest braces around empty body in an 'else' statement [-Werror=empty-body] stat_inc_rbtree_node_hit(sbi); A good way to avoid the warning and make the code more robust is to define all no-op macros as 'do { } while (0)'. Fixes: 54c2258cd63a ("f2fs: extract rb-tree operation infrastructure") Signed-off-by: Arnd Bergmann Reivewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 58 +++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index dfce8f95cbbc..864036f624eb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2633,35 +2633,35 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi); int __init f2fs_create_root_stats(void); void f2fs_destroy_root_stats(void); #else -#define stat_inc_cp_count(si) -#define stat_inc_bg_cp_count(si) -#define stat_inc_call_count(si) -#define stat_inc_bggc_count(si) -#define stat_inc_dirty_inode(sbi, type) -#define stat_dec_dirty_inode(sbi, type) -#define stat_inc_total_hit(sb) -#define stat_inc_rbtree_node_hit(sb) -#define stat_inc_largest_node_hit(sbi) -#define stat_inc_cached_node_hit(sbi) -#define stat_inc_inline_xattr(inode) -#define stat_dec_inline_xattr(inode) -#define stat_inc_inline_inode(inode) -#define stat_dec_inline_inode(inode) -#define stat_inc_inline_dir(inode) -#define stat_dec_inline_dir(inode) -#define stat_inc_atomic_write(inode) -#define stat_dec_atomic_write(inode) -#define stat_update_max_atomic_write(inode) -#define stat_inc_volatile_write(inode) -#define stat_dec_volatile_write(inode) -#define stat_update_max_volatile_write(inode) -#define stat_inc_seg_type(sbi, curseg) -#define stat_inc_block_count(sbi, curseg) -#define stat_inc_inplace_blocks(sbi) -#define stat_inc_seg_count(sbi, type, gc_type) -#define stat_inc_tot_blk_count(si, blks) -#define stat_inc_data_blk_count(sbi, blks, gc_type) -#define stat_inc_node_blk_count(sbi, blks, gc_type) +#define stat_inc_cp_count(si) do { } while (0) +#define stat_inc_bg_cp_count(si) do { } while (0) +#define stat_inc_call_count(si) do { } while (0) +#define stat_inc_bggc_count(si) do { } while (0) +#define stat_inc_dirty_inode(sbi, type) do { } while (0) +#define stat_dec_dirty_inode(sbi, type) do { } while (0) +#define stat_inc_total_hit(sb) do { } while (0) +#define stat_inc_rbtree_node_hit(sb) do { } while (0) +#define stat_inc_largest_node_hit(sbi) do { } while (0) +#define stat_inc_cached_node_hit(sbi) do { } while (0) +#define stat_inc_inline_xattr(inode) do { } while (0) +#define stat_dec_inline_xattr(inode) do { } while (0) +#define stat_inc_inline_inode(inode) do { } while (0) +#define stat_dec_inline_inode(inode) do { } while (0) +#define stat_inc_inline_dir(inode) do { } while (0) +#define stat_dec_inline_dir(inode) do { } while (0) +#define stat_inc_atomic_write(inode) do { } while (0) +#define stat_dec_atomic_write(inode) do { } while (0) +#define stat_update_max_atomic_write(inode) do { } while (0) +#define stat_inc_volatile_write(inode) do { } while (0) +#define stat_dec_volatile_write(inode) do { } while (0) +#define stat_update_max_volatile_write(inode) do { } while (0) +#define stat_inc_seg_type(sbi, curseg) do { } while (0) +#define stat_inc_block_count(sbi, curseg) do { } while (0) +#define stat_inc_inplace_blocks(sbi) do { } while (0) +#define stat_inc_seg_count(sbi, type, gc_type) do { } while (0) +#define stat_inc_tot_blk_count(si, blks) do { } while (0) +#define stat_inc_data_blk_count(sbi, blks, gc_type) do { } while (0) +#define stat_inc_node_blk_count(sbi, blks, gc_type) do { } while (0) static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } -- GitLab From 40cd2e9a4860f106845256f06347885ea6440813 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 20 Apr 2017 13:51:57 -0700 Subject: [PATCH 0649/5498] f2fs: fix out-of free segments This patch also reverts d0db7703ac1 ("f2fs: do SSR in higher priority"). This patch fixes out of free segments caused by many small file creation by 1) mkfs -s 1 2G 2) mount 3) untar - preoduce 60000 small files burstly 4) sync - flush node pages - flush imeta Here, when we do f2fs_balance_fs, we missed # of imeta blocks, resulting in skipping to check has_not_enough_free_secs. Another test is done by 1) mkfs -s 12 2G 2) mount 3) untar - preoduce 60000 small files burstly 4) sync - flush node pages - flush imeta In this case, this patch also fixes wrong block allocation under large section size. Reported-by: William Brana Cc: Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- fs/f2fs/inode.c | 3 ++- fs/f2fs/segment.c | 26 +++++++++++++++++++++----- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ae839acb4b36..77eb3fadcdb3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1468,7 +1468,8 @@ out: } unlock_page(page); - f2fs_balance_fs(sbi, need_balance_fs); + if (!S_ISDIR(inode->i_mode)) + f2fs_balance_fs(sbi, need_balance_fs); if (unlikely(f2fs_cp_error(sbi))) { f2fs_submit_merged_bio(sbi, DATA, WRITE); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c689227f7174..20782deb51f3 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -338,7 +338,8 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) * We need to balance fs here to prevent from producing dirty node pages * during the urgent cleaning time when runing out of free sections. */ - if (update_inode_page(inode) && wbc && wbc->nr_to_write) + update_inode_page(inode); + if (wbc && wbc->nr_to_write) f2fs_balance_fs(sbi, true); return 0; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c9ee1aa3c027..577fda3f7914 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -388,11 +388,8 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) } #endif - if (!need) - return; - /* balance_fs_bg is able to be pending */ - if (excess_cached_nats(sbi)) + if (need && excess_cached_nats(sbi)) f2fs_balance_fs_bg(sbi); /* @@ -1718,6 +1715,17 @@ static void write_current_sum_page(struct f2fs_sb_info *sbi, f2fs_put_page(page, 1); } +static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + unsigned int segno = curseg->segno + 1; + struct free_segmap_info *free_i = FREE_I(sbi); + + if (segno < MAIN_SEGS(sbi) && segno % sbi->segs_per_sec) + return !test_bit(segno, free_i->free_segmap); + return 0; +} + /* * Find a new segment from the free segments bitmap to right order * This function should be returned with success, otherwise BUG @@ -1831,6 +1839,10 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) { + /* if segs_per_sec is large than 1, we need to keep original policy. */ + if (sbi->segs_per_sec != 1) + return CURSEG_I(sbi, type)->segno; + if (type == CURSEG_HOT_DATA || IS_NODESEG(type)) return 0; @@ -1980,17 +1992,21 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) static void allocate_segment_by_default(struct f2fs_sb_info *sbi, int type, bool force) { + struct curseg_info *curseg = CURSEG_I(sbi, type); + if (force) new_curseg(sbi, type, true); else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) && type == CURSEG_WARM_NODE) new_curseg(sbi, type, false); + else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type)) + new_curseg(sbi, type, false); else if (need_SSR(sbi) && get_ssr_segment(sbi, type)) change_curseg(sbi, type, true); else new_curseg(sbi, type, false); - stat_inc_seg_type(sbi, CURSEG_I(sbi, type)); + stat_inc_seg_type(sbi, curseg); } void allocate_new_segments(struct f2fs_sb_info *sbi) -- GitLab From 174241e4805bf9a02c01b0f700663f9fd8f7a163 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Fri, 21 Apr 2017 12:41:48 +0000 Subject: [PATCH 0650/5498] f2fs: skip encrypted inode in ASYNC IPU policy Async request may be throttled in block layer, so page for async may keep WRITE_BACK for a long time. For encrytped inode, we need wait on page writeback no matter if the device supports BDI_CAP_STABLE_WRITES. This may result in a higher waiting page writeback time for async encrypted inode page. This patch skips IPU for encrypted inode's updating write. Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 826b607a26df..f6499ae8602d 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -592,7 +592,8 @@ static inline bool need_inplace_update(struct inode *inode, */ if (policy & (0x1 << F2FS_IPU_ASYNC) && fio && fio->op == REQ_OP_WRITE && - !(fio->op_flags & REQ_SYNC)) + !(fio->op_flags & REQ_SYNC) && + !f2fs_encrypted_inode(inode)) return true; /* this is only set during fdatasync */ -- GitLab From dfccf3aa21e8e0184c903f115da348343f7f60ae Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Sat, 22 Apr 2017 10:39:20 +0800 Subject: [PATCH 0651/5498] f2fs: fix multiple f2fs_add_link() having same name for inline dentry Commit 88c5c13a5027 (f2fs: fix multiple f2fs_add_link() calls having same name) does not cover the scenario where inline dentry is enabled. In that case, F2FS_I(dir)->task will be NULL, and __f2fs_add_link will lookup dentries one more time. This patch fixes it by moving the assigment of current task to a upper level to cover both normal and inline dentry. Cc: Fixes: 88c5c13a5027 (f2fs: fix multiple f2fs_add_link() calls having same name) Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 815e99cf3882..daa251de50b2 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -207,13 +207,9 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, f2fs_put_page(dentry_page, 0); } - /* This is to increase the speed of f2fs_create */ - if (!de && room) { - F2FS_I(dir)->task = current; - if (F2FS_I(dir)->chash != namehash) { - F2FS_I(dir)->chash = namehash; - F2FS_I(dir)->clevel = level; - } + if (!de && room && F2FS_I(dir)->chash != namehash) { + F2FS_I(dir)->chash = namehash; + F2FS_I(dir)->clevel = level; } return de; @@ -254,6 +250,9 @@ struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir, break; } out: + /* This is to increase the speed of f2fs_create */ + if (!de) + F2FS_I(dir)->task = current; return de; } -- GitLab From 0c5081e4cf7d443196d549bfe41ac226a220267a Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Sat, 22 Apr 2017 18:06:26 +0800 Subject: [PATCH 0652/5498] f2fs: seperate read nat page from nat_tree_lock This patch seperate nat page read io from nat_tree_lock. -lock_page -get_node_info() -current_nat_addr ...... -> write_checkpoint -get_meta_page Because we lock node page, we can make sure no other threads modify this nid concurrently. So we just obtain current_nat_addr under nat_tree_lock, node info is always same in both nat pack. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d8304ae09222..38ecc549b504 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -376,6 +376,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) struct page *page = NULL; struct f2fs_nat_entry ne; struct nat_entry *e; + pgoff_t index; int i; ni->nid = nid; @@ -401,17 +402,21 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) node_info_from_raw_nat(ni, &ne); } up_read(&curseg->journal_rwsem); - if (i >= 0) + if (i >= 0) { + up_read(&nm_i->nat_tree_lock); goto cache; + } /* Fill node_info from nat page */ - page = get_current_nat_page(sbi, start_nid); + index = current_nat_addr(sbi, nid); + up_read(&nm_i->nat_tree_lock); + + page = get_meta_page(sbi, index); nat_blk = (struct f2fs_nat_block *)page_address(page); ne = nat_blk->entries[nid - start_nid]; node_info_from_raw_nat(ni, &ne); f2fs_put_page(page, 1); cache: - up_read(&nm_i->nat_tree_lock); /* cache nat entry */ down_write(&nm_i->nat_tree_lock); cache_nat_entry(sbi, nid, &ne); -- GitLab From 7ab07b578a6085d598ab63820706ce974a65c2ae Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 25 Apr 2017 00:21:34 +0800 Subject: [PATCH 0653/5498] f2fs: delay awaking discard thread It's better to delay awaking discard thread while queuing discard commands in checkpoint, it will help to give more chances for merging big and small discard. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 577fda3f7914..02a17c8990d7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1047,7 +1047,6 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, blkstart -= FDEV(devi).start_blk; } __update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen); - wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); return 0; } @@ -1414,6 +1413,8 @@ skip: SM_I(sbi)->dcc_info->nr_discards -= total_len; kmem_cache_free(discard_entry_slab, entry); } + + wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); } static int create_discard_cmd_control(struct f2fs_sb_info *sbi) -- GitLab From e6e3b6a07edef02ba44406db10214b4b7d3b6597 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 25 Apr 2017 00:21:35 +0800 Subject: [PATCH 0654/5498] f2fs: enable small discard by default This patch start to enable 4K granularity small discard by default when realtime discard is on, so, in seriously fragmented space, small size discard can be issued in time to avoid useless storage space occupying of invalid filesystem's data, then performance of flash storage can be recovered. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 4 ++-- fs/f2fs/segment.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 864036f624eb..ee1a86825066 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -329,8 +329,8 @@ struct discard_cmd_control { struct list_head wait_list; /* store on-flushing entries */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ struct mutex cmd_lock; - int nr_discards; /* # of discards in the list */ - int max_discards; /* max. discards to be issued */ + unsigned int nr_discards; /* # of discards in the list */ + unsigned int max_discards; /* max. discards to be issued */ unsigned int undiscard_blks; /* # of undiscard blocks */ atomic_t issued_discard; /* # of issued discard */ atomic_t issing_discard; /* # of issing discard */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 02a17c8990d7..9a4fd49e7ce1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1441,7 +1441,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) atomic_set(&dcc->issing_discard, 0); atomic_set(&dcc->discard_cmd_cnt, 0); dcc->nr_discards = 0; - dcc->max_discards = 0; + dcc->max_discards = MAIN_SEGS(sbi) << sbi->log_blocks_per_seg; dcc->undiscard_blks = 0; dcc->root = RB_ROOT; -- GitLab From a9711d690b416853dcc34c0b477a7993f63e83ff Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 25 Apr 2017 20:21:37 +0800 Subject: [PATCH 0655/5498] f2fs: introduce __issue_discard_cmd Just cleanup, no logic change. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 63 ++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9a4fd49e7ce1..6deb6037c7e6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1050,6 +1050,32 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, return 0; } +static void __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *pend_list; + struct discard_cmd *dc, *tmp; + struct blk_plug plug; + int i, iter = 0; + + mutex_lock(&dcc->cmd_lock); + blk_start_plug(&plug); + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + pend_list = &dcc->pend_list[i]; + list_for_each_entry_safe(dc, tmp, pend_list, list) { + f2fs_bug_on(sbi, dc->state != D_PREP); + + if (!issue_cond || is_idle(sbi)) + __submit_discard_cmd(sbi, dc); + if (issue_cond && iter++ > DISCARD_ISSUE_RATE) + goto out; + } + } +out: + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); +} + /* This should be covered by global mutex, &sit_i->sentry_lock */ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { @@ -1072,27 +1098,16 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *pend_list; struct list_head *wait_list = &(dcc->wait_list); struct discard_cmd *dc, *tmp; - struct blk_plug plug; - int i; - mutex_lock(&dcc->cmd_lock); - - blk_start_plug(&plug); - for (i = 0; i < MAX_PLIST_NUM; i++) { - pend_list = &dcc->pend_list[i]; - list_for_each_entry_safe(dc, tmp, pend_list, list) - __submit_discard_cmd(sbi, dc); - } - blk_finish_plug(&plug); + __issue_discard_cmd(sbi, false); + mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, wait_list, list) { wait_for_completion_io(&dc->wait); __remove_discard_cmd(sbi, dc); } - mutex_unlock(&dcc->cmd_lock); } @@ -1101,32 +1116,15 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; - struct list_head *pend_list; struct list_head *wait_list = &dcc->wait_list; struct discard_cmd *dc, *tmp; - struct blk_plug plug; - int iter = 0, i; repeat: if (kthread_should_stop()) return 0; - mutex_lock(&dcc->cmd_lock); - blk_start_plug(&plug); - for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { - pend_list = &dcc->pend_list[i]; - list_for_each_entry_safe(dc, tmp, pend_list, list) { - f2fs_bug_on(sbi, dc->state != D_PREP); - - if (is_idle(sbi)) - __submit_discard_cmd(sbi, dc); - - if (iter++ > DISCARD_ISSUE_RATE) - goto next_step; - } - } -next_step: - blk_finish_plug(&plug); + __issue_discard_cmd(sbi, true); + mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, wait_list, list) { if (dc->state == D_DONE) { wait_for_completion_io(&dc->wait); @@ -1135,7 +1133,6 @@ next_step: } mutex_unlock(&dcc->cmd_lock); - iter = 0; congestion_wait(BLK_RW_SYNC, HZ/50); wait_event_interruptible(*q, kthread_should_stop() || -- GitLab From 9875a1bf26ef627b536c67678063f25ce9e6d517 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 25 Apr 2017 20:21:38 +0800 Subject: [PATCH 0656/5498] f2fs: introduce __wait_discard_cmd Just cleanup, no logic change. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6deb6037c7e6..2848a029c9be 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1076,6 +1076,22 @@ out: mutex_unlock(&dcc->cmd_lock); } +static void __wait_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *wait_list = &(dcc->wait_list); + struct discard_cmd *dc, *tmp; + + mutex_lock(&dcc->cmd_lock); + list_for_each_entry_safe(dc, tmp, wait_list, list) { + if (!wait_cond || dc->state == D_DONE) { + wait_for_completion_io(&dc->wait); + __remove_discard_cmd(sbi, dc); + } + } + mutex_unlock(&dcc->cmd_lock); +} + /* This should be covered by global mutex, &sit_i->sentry_lock */ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { @@ -1097,18 +1113,8 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) /* This comes from f2fs_put_super */ void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { - struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *wait_list = &(dcc->wait_list); - struct discard_cmd *dc, *tmp; - __issue_discard_cmd(sbi, false); - - mutex_lock(&dcc->cmd_lock); - list_for_each_entry_safe(dc, tmp, wait_list, list) { - wait_for_completion_io(&dc->wait); - __remove_discard_cmd(sbi, dc); - } - mutex_unlock(&dcc->cmd_lock); + __wait_discard_cmd(sbi, false); } static int issue_discard_thread(void *data) @@ -1116,22 +1122,12 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; - struct list_head *wait_list = &dcc->wait_list; - struct discard_cmd *dc, *tmp; repeat: if (kthread_should_stop()) return 0; __issue_discard_cmd(sbi, true); - - mutex_lock(&dcc->cmd_lock); - list_for_each_entry_safe(dc, tmp, wait_list, list) { - if (dc->state == D_DONE) { - wait_for_completion_io(&dc->wait); - __remove_discard_cmd(sbi, dc); - } - } - mutex_unlock(&dcc->cmd_lock); + __wait_discard_cmd(sbi, true); congestion_wait(BLK_RW_SYNC, HZ/50); -- GitLab From 4c660c47188c2a97f2fa3b44ac76d0799a2b0f62 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Tue, 25 Apr 2017 12:45:12 +0000 Subject: [PATCH 0657/5498] f2fs: reconstruct code to write a data page This patch introduces encrypt_one_page which encrypts one data page before submit_bio, and change the use of need_inplace_update. Signed-off-by: Hou Pengyang Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 81 +++++++++++++++++++++++++++++------------------ fs/f2fs/file.c | 4 +-- fs/f2fs/segment.h | 6 +--- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 77eb3fadcdb3..77844886e436 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1298,6 +1298,49 @@ static int f2fs_read_data_pages(struct file *file, return f2fs_mpage_readpages(mapping, pages, NULL, nr_pages); } +static int encrypt_one_page(struct f2fs_io_info *fio) +{ + struct inode *inode = fio->page->mapping->host; + gfp_t gfp_flags = GFP_NOFS; + + if (!f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) + return 0; + + /* wait for GCed encrypted page writeback */ + f2fs_wait_on_encrypted_page_writeback(fio->sbi, fio->old_blkaddr); + +retry_encrypt: + fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, + PAGE_SIZE, 0, fio->page->index, gfp_flags); + if (!IS_ERR(fio->encrypted_page)) + return 0; + + /* flush pending IOs and wait for a while in the ENOMEM case */ + if (PTR_ERR(fio->encrypted_page) == -ENOMEM) { + f2fs_flush_merged_bios(fio->sbi); + congestion_wait(BLK_RW_ASYNC, HZ/50); + gfp_flags |= __GFP_NOFAIL; + goto retry_encrypt; + } + return PTR_ERR(fio->encrypted_page); +} + +static inline bool need_inplace_update(struct f2fs_io_info *fio) +{ + struct inode *inode = fio->page->mapping->host; + + if (fio->old_blkaddr == NEW_ADDR) + return false; + if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) + return false; + if (is_cold_data(fio->page)) + return false; + if (IS_ATOMIC_WRITTEN_PAGE(fio->page)) + return false; + + return need_inplace_update_policy(inode, fio); +} + int do_write_data_page(struct f2fs_io_info *fio) { struct page *page = fio->page; @@ -1318,30 +1361,9 @@ int do_write_data_page(struct f2fs_io_info *fio) goto out_writepage; } - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { - gfp_t gfp_flags = GFP_NOFS; - - /* wait for GCed encrypted page writeback */ - f2fs_wait_on_encrypted_page_writeback(F2FS_I_SB(inode), - fio->old_blkaddr); -retry_encrypt: - fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, - PAGE_SIZE, 0, - fio->page->index, - gfp_flags); - if (IS_ERR(fio->encrypted_page)) { - err = PTR_ERR(fio->encrypted_page); - if (err == -ENOMEM) { - /* flush pending ios and wait for a while */ - f2fs_flush_merged_bios(F2FS_I_SB(inode)); - congestion_wait(BLK_RW_ASYNC, HZ/50); - gfp_flags |= __GFP_NOFAIL; - err = 0; - goto retry_encrypt; - } - goto out_writepage; - } - } + err = encrypt_one_page(fio); + if (err) + goto out_writepage; set_page_writeback(page); @@ -1349,15 +1371,14 @@ retry_encrypt: * If current allocation needs SSR, * it had better in-place writes for updated data. */ - if (unlikely(fio->old_blkaddr != NEW_ADDR && - !is_cold_data(page) && - !IS_ATOMIC_WRITTEN_PAGE(page) && - need_inplace_update(inode, fio))) { - f2fs_unlock_op(F2FS_I_SB(inode)); + if (need_inplace_update(fio)) { + f2fs_bug_on(fio->sbi, !fio->cp_rwsem_locked); + f2fs_unlock_op(fio->sbi); fio->cp_rwsem_locked = false; + err = rewrite_data_page(fio); + trace_f2fs_do_write_data_page(fio->page, IPU); set_inode_flag(inode, FI_UPDATE_WRITE); - trace_f2fs_do_write_data_page(page, IPU); } else { write_data_page(&dn, fio); trace_f2fs_do_write_data_page(page, OPU); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index bdbe832cd897..fef143f8ecf9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1913,7 +1913,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, int err; /* if in-place-update policy is enabled, don't waste time here */ - if (need_inplace_update(inode, NULL)) + if (need_inplace_update_policy(inode, NULL)) return -EINVAL; pg_start = range->start >> PAGE_SHIFT; @@ -2048,7 +2048,7 @@ static int f2fs_ioc_defragment(struct file *filp, unsigned long arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!S_ISREG(inode->i_mode)) + if (!S_ISREG(inode->i_mode) || f2fs_is_atomic_file(inode)) return -EINVAL; if (f2fs_readonly(sbi->sb)) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index f6499ae8602d..f488f0e2d529 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -563,16 +563,12 @@ enum { F2FS_IPU_ASYNC, }; -static inline bool need_inplace_update(struct inode *inode, +static inline bool need_inplace_update_policy(struct inode *inode, struct f2fs_io_info *fio) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); unsigned int policy = SM_I(sbi)->ipu_policy; - /* IPU can be done only for the user data */ - if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) - return false; - if (test_opt(sbi, LFS)) return false; -- GitLab From a69dbfd6d2d64bbc71d2f41c72865436e5ca7374 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Tue, 25 Apr 2017 12:45:13 +0000 Subject: [PATCH 0658/5498] f2fs: lookup extent cache first under IPU scenario If a page is cold, NOT atomit written and need_ipu now, there is a high probability that IPU should be adapted. For IPU, we try to check extent tree to get the block index first, instead of reading the dnode page, where may lead to an useless dnode IO, since no need to update the dnode index for IPU. Signed-off-by: Hou Pengyang Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/gc.c --- fs/f2fs/data.c | 16 ++++++++++++++-- fs/f2fs/gc.c | 1 + fs/f2fs/segment.c | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 77844886e436..dbea44b532cd 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1346,9 +1346,20 @@ int do_write_data_page(struct f2fs_io_info *fio) struct page *page = fio->page; struct inode *inode = page->mapping->host; struct dnode_of_data dn; + struct extent_info ei = {0,0,0}; + bool ipu_force = false; int err = 0; set_new_dnode(&dn, inode, NULL, NULL, 0); + if (need_inplace_update(fio) && + f2fs_lookup_extent_cache(inode, page->index, &ei)) { + fio->old_blkaddr = ei.blk + page->index - ei.fofs; + if (fio->old_blkaddr != NULL_ADDR && + fio->old_blkaddr != NEW_ADDR) { + ipu_force = true; + goto got_it; + } + } err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE); if (err) return err; @@ -1360,7 +1371,7 @@ int do_write_data_page(struct f2fs_io_info *fio) ClearPageUptodate(page); goto out_writepage; } - +got_it: err = encrypt_one_page(fio); if (err) goto out_writepage; @@ -1371,7 +1382,7 @@ int do_write_data_page(struct f2fs_io_info *fio) * If current allocation needs SSR, * it had better in-place writes for updated data. */ - if (need_inplace_update(fio)) { + if (ipu_force || need_inplace_update(fio)) { f2fs_bug_on(fio->sbi, !fio->cp_rwsem_locked); f2fs_unlock_op(fio->sbi); fio->cp_rwsem_locked = false; @@ -1408,6 +1419,7 @@ static int __write_data_page(struct page *page, bool *submitted, .type = DATA, .op = REQ_OP_WRITE, .op_flags = wbc_to_write_flags(wbc), + .old_blkaddr = NULL_ADDR, .page = page, .encrypted_page = NULL, .submitted = false, diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 84db41ca27c1..c2a9ae8397d3 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -714,6 +714,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC, + .old_blkaddr = NULL_ADDR, .page = page, .encrypted_page = NULL, }; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2848a029c9be..045708075491 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -311,6 +311,7 @@ static int __commit_inmem_pages(struct inode *inode, } fio.page = page; + fio.old_blkaddr = NULL_ADDR; err = do_write_data_page(&fio); if (err) { unlock_page(page); -- GitLab From 4a8cf45ffa8d0a70db91ad7f6366e5b7ceaf8389 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 24 Apr 2017 15:20:16 -0700 Subject: [PATCH 0659/5498] f2fs: introduce valid_ipu_blkaddr to clean up This patch introduces valid_ipu_blkaddr to clean up checking block address for inplace-update. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index dbea44b532cd..65c5c78722ef 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1329,8 +1329,6 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio) { struct inode *inode = fio->page->mapping->host; - if (fio->old_blkaddr == NEW_ADDR) - return false; if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) return false; if (is_cold_data(fio->page)) @@ -1341,6 +1339,15 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio) return need_inplace_update_policy(inode, fio); } +static inline bool valid_ipu_blkaddr(struct f2fs_io_info *fio) +{ + if (fio->old_blkaddr == NEW_ADDR) + return false; + if (fio->old_blkaddr == NULL_ADDR) + return false; + return true; +} + int do_write_data_page(struct f2fs_io_info *fio) { struct page *page = fio->page; @@ -1354,8 +1361,8 @@ int do_write_data_page(struct f2fs_io_info *fio) if (need_inplace_update(fio) && f2fs_lookup_extent_cache(inode, page->index, &ei)) { fio->old_blkaddr = ei.blk + page->index - ei.fofs; - if (fio->old_blkaddr != NULL_ADDR && - fio->old_blkaddr != NEW_ADDR) { + + if (valid_ipu_blkaddr(fio)) { ipu_force = true; goto got_it; } @@ -1382,7 +1389,7 @@ got_it: * If current allocation needs SSR, * it had better in-place writes for updated data. */ - if (ipu_force || need_inplace_update(fio)) { + if (ipu_force || (valid_ipu_blkaddr(fio) && need_inplace_update(fio))) { f2fs_bug_on(fio->sbi, !fio->cp_rwsem_locked); f2fs_unlock_op(fio->sbi); fio->cp_rwsem_locked = false; -- GitLab From 24b316e007fd02469489ce0d34646479ff607d0d Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 25 Apr 2017 16:28:48 -0700 Subject: [PATCH 0660/5498] f2fs: sanity check segment count F2FS uses 4 bytes to represent block address. As a result, supported size of disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments. Signed-off-by: Jin Qian Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 7 +++++++ include/linux/f2fs_fs.h | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c201ff1c2b9c..a87b9f65d5a0 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1482,6 +1482,13 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, return 1; } + if (le32_to_cpu(raw_super->segment_count) > F2FS_MAX_SEGMENT) { + f2fs_msg(sb, KERN_INFO, + "Invalid segment count (%u)", + le32_to_cpu(raw_super->segment_count)); + return 1; + } + /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */ if (sanity_check_area_boundary(sbi, bh)) return 1; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index acdeb739053d..ef70be9c73b3 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -301,6 +301,12 @@ struct f2fs_nat_block { #define SIT_VBLOCK_MAP_SIZE 64 #define SIT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_sit_entry)) +/* + * F2FS uses 4 bytes to represent block address. As a result, supported size of + * disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments. + */ +#define F2FS_MAX_SEGMENT ((16 * 1024 * 1024) / 2) + /* * Note that f2fs_sit_entry->vblocks has the following bit-field information. * [15:10] : allocation type such as CURSEG_XXXX_TYPE -- GitLab From ef0d56e4a16482531cbd7da97ac6dd0c038e5f8d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 26 Apr 2017 11:11:12 -0700 Subject: [PATCH 0661/5498] f2fs: nullify fio->encrypted_page for each writes This makes sure each write request has nullified encrypted_page pointer. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/segment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 045708075491..28618d0be589 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -291,7 +291,6 @@ static int __commit_inmem_pages(struct inode *inode, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC | REQ_PRIO, - .encrypted_page = NULL, }; pgoff_t last_idx = ULONG_MAX; int err = 0; @@ -312,6 +311,7 @@ static int __commit_inmem_pages(struct inode *inode, fio.page = page; fio.old_blkaddr = NULL_ADDR; + fio.encrypted_page = NULL; err = do_write_data_page(&fio); if (err) { unlock_page(page); -- GitLab From ade8dd57482bffcaa832bfb197d1d114a6499482 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 26 Apr 2017 17:39:54 +0800 Subject: [PATCH 0662/5498] f2fs: don't hold cmd_lock during waiting discard command Previously, with protection of cmd_lock, we will wait for end io of discard command which potentially may lead long latency, making worse concurrency. So, in this patch, we try to add reference into discard entry to prevent the entry being released by other thread, then we can avoid holding global cmd_lock during waiting discard to finish. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ee1a86825066..c650ddb4f6c2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -318,6 +318,7 @@ struct discard_cmd { struct list_head list; /* command list */ struct completion wait; /* compleation */ struct block_device *bdev; /* bdev */ + unsigned short ref; /* reference count */ int state; /* state */ int error; /* bio error */ }; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 28618d0be589..74f0237c957a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -688,6 +688,7 @@ static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, dc->lstart = lstart; dc->start = start; dc->len = len; + dc->ref = 0; dc->state = D_PREP; dc->error = 0; init_completion(&dc->wait); @@ -1086,6 +1087,8 @@ static void __wait_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, wait_list, list) { if (!wait_cond || dc->state == D_DONE) { + if (dc->ref) + continue; wait_for_completion_io(&dc->wait); __remove_discard_cmd(sbi, dc); } @@ -1098,17 +1101,29 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_cmd *dc; + bool need_wait = false; mutex_lock(&dcc->cmd_lock); - dc = (struct discard_cmd *)__lookup_rb_tree(&dcc->root, NULL, blkaddr); if (dc) { - if (dc->state != D_PREP) - wait_for_completion_io(&dc->wait); - __punch_discard_cmd(sbi, dc, blkaddr); + if (dc->state == D_PREP) { + __punch_discard_cmd(sbi, dc, blkaddr); + } else { + dc->ref++; + need_wait = true; + } } - mutex_unlock(&dcc->cmd_lock); + + if (need_wait) { + wait_for_completion_io(&dc->wait); + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, dc->state != D_DONE); + dc->ref--; + if (!dc->ref) + __remove_discard_cmd(sbi, dc); + mutex_unlock(&dcc->cmd_lock); + } } /* This comes from f2fs_put_super */ -- GitLab From 1095698a8b9569678bff76093ff0c6767151129d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 26 Apr 2017 17:39:55 +0800 Subject: [PATCH 0663/5498] f2fs: shrink size of struct discard_cmd In order to shrink size of struct discard_cmd, change variable type of @state in struct discard_cmd from int to unsigned char. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c650ddb4f6c2..39346979a85a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -319,7 +319,7 @@ struct discard_cmd { struct completion wait; /* compleation */ struct block_device *bdev; /* bdev */ unsigned short ref; /* reference count */ - int state; /* state */ + unsigned char state; /* state */ int error; /* bio error */ }; -- GitLab From 46a8ca8646776fe4808de01b296fdc779737058a Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Thu, 27 Apr 2017 00:17:21 +0800 Subject: [PATCH 0664/5498] f2fs: release cp and dnode lock before IPU We don't need to rewrite the page under cp_rwsem and dnode locks. Signed-off-by: Hou Pengyang Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 39 ++++++++++++++++++++++++--------------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/gc.c | 1 + fs/f2fs/segment.c | 1 + 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 65c5c78722ef..35da69f63769 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1364,12 +1364,17 @@ int do_write_data_page(struct f2fs_io_info *fio) if (valid_ipu_blkaddr(fio)) { ipu_force = true; + fio->need_lock = false; goto got_it; } } + + if (fio->need_lock) + f2fs_lock_op(fio->sbi); + err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE); if (err) - return err; + goto out; fio->old_blkaddr = dn.data_blkaddr; @@ -1390,22 +1395,26 @@ got_it: * it had better in-place writes for updated data. */ if (ipu_force || (valid_ipu_blkaddr(fio) && need_inplace_update(fio))) { - f2fs_bug_on(fio->sbi, !fio->cp_rwsem_locked); - f2fs_unlock_op(fio->sbi); - fio->cp_rwsem_locked = false; - + f2fs_put_dnode(&dn); + if (fio->need_lock) + f2fs_unlock_op(fio->sbi); err = rewrite_data_page(fio); trace_f2fs_do_write_data_page(fio->page, IPU); set_inode_flag(inode, FI_UPDATE_WRITE); - } else { - write_data_page(&dn, fio); - trace_f2fs_do_write_data_page(page, OPU); - set_inode_flag(inode, FI_APPEND_WRITE); - if (page->index == 0) - set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); + return err; } + + /* LFS mode write path */ + write_data_page(&dn, fio); + trace_f2fs_do_write_data_page(page, OPU); + set_inode_flag(inode, FI_APPEND_WRITE); + if (page->index == 0) + set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); out_writepage: f2fs_put_dnode(&dn); +out: + if (fio->need_lock) + f2fs_unlock_op(fio->sbi); return err; } @@ -1430,7 +1439,7 @@ static int __write_data_page(struct page *page, bool *submitted, .page = page, .encrypted_page = NULL, .submitted = false, - .cp_rwsem_locked = true, + .need_lock = true, }; trace_f2fs_writepage(page, DATA); @@ -1466,6 +1475,7 @@ write: /* Dentry blocks are controlled by checkpoint */ if (S_ISDIR(inode->i_mode)) { + fio.need_lock = false; err = do_write_data_page(&fio); goto done; } @@ -1483,13 +1493,12 @@ write: if (!err) goto out; } - f2fs_lock_op(sbi); + if (err == -EAGAIN) err = do_write_data_page(&fio); if (F2FS_I(inode)->last_disk_size < psize) F2FS_I(inode)->last_disk_size = psize; - if (fio.cp_rwsem_locked) - f2fs_unlock_op(sbi); + done: if (err && err != -ENOENT) goto redirty_out; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 39346979a85a..5b3d8e92f5d8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -902,7 +902,7 @@ struct f2fs_io_info { struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ bool submitted; /* indicate IO submission */ - bool cp_rwsem_locked; /* indicate cp_rwsem is held */ + bool need_lock; /* indicate we need to lock cp_rwsem */ }; #define is_read_io(rw) ((rw) == READ) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c2a9ae8397d3..026522107ca3 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -717,6 +717,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, .old_blkaddr = NULL_ADDR, .page = page, .encrypted_page = NULL, + .need_lock = true, }; bool is_dirty = PageDirty(page); int err; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 74f0237c957a..9e3dd5d80504 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -312,6 +312,7 @@ static int __commit_inmem_pages(struct inode *inode, fio.page = page; fio.old_blkaddr = NULL_ADDR; fio.encrypted_page = NULL; + fio.need_lock = false, err = do_write_data_page(&fio); if (err) { unlock_page(page); -- GitLab From eee6a96e82a9c7254a3c35f3ca247f9e66b027a8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 27 Apr 2017 20:40:39 +0800 Subject: [PATCH 0665/5498] f2fs: allow cpc->reason to indicate more than one reason Change to use different bits of cpc->reason to indicate different status, so cpc->reason can indicate more than one reason. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 14 +++++++------- fs/f2fs/f2fs.h | 16 +++++++--------- fs/f2fs/segment.c | 8 ++++---- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 8cc2f116495e..4d7e3d9a7bb0 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1055,17 +1055,17 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) spin_lock(&sbi->cp_lock); - if (cpc->reason == CP_UMOUNT && + if ((cpc->reason & CP_UMOUNT) && le32_to_cpu(ckpt->cp_pack_total_block_count) > sbi->blocks_per_seg - NM_I(sbi)->nat_bits_blocks) disable_nat_bits(sbi, false); - if (cpc->reason == CP_UMOUNT) + if (cpc->reason & CP_UMOUNT) __set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); else __clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); - if (cpc->reason == CP_FASTBOOT) + if (cpc->reason & CP_FASTBOOT) __set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); else __clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); @@ -1273,8 +1273,8 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) mutex_lock(&sbi->cp_mutex); if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) && - (cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC || - (cpc->reason == CP_DISCARD && !sbi->discard_blks))) + ((cpc->reason & CP_FASTBOOT) || (cpc->reason & CP_SYNC) || + ((cpc->reason & CP_DISCARD) && !sbi->discard_blks))) goto out; if (unlikely(f2fs_cp_error(sbi))) { err = -EIO; @@ -1296,7 +1296,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_flush_merged_bios(sbi); /* this is the case of multiple fstrims without any changes */ - if (cpc->reason == CP_DISCARD) { + if (cpc->reason & CP_DISCARD) { if (!exist_trim_candidates(sbi, cpc)) { unblock_operations(sbi); goto out; @@ -1334,7 +1334,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) unblock_operations(sbi); stat_inc_cp_count(sbi->stat_info); - if (cpc->reason == CP_RECOVERY) + if (cpc->reason & CP_RECOVERY) f2fs_msg(sbi->sb, KERN_NOTICE, "checkpoint: version = %llx", ckpt_ver); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5b3d8e92f5d8..421766f9707a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -224,13 +224,11 @@ enum { SIT_BITMAP }; -enum { - CP_UMOUNT, - CP_FASTBOOT, - CP_SYNC, - CP_RECOVERY, - CP_DISCARD, -}; +#define CP_UMOUNT 0x00000001 +#define CP_FASTBOOT 0x00000002 +#define CP_SYNC 0x00000004 +#define CP_RECOVERY 0x00000008 +#define CP_DISCARD 0x00000010 #define DEF_BATCHED_TRIM_SECTIONS 2048 #define BATCHED_TRIM_SEGMENTS(sbi) \ @@ -1364,7 +1362,7 @@ static inline bool enabled_nat_bits(struct f2fs_sb_info *sbi, { bool set = is_set_ckpt_flags(sbi, CP_NAT_BITS_FLAG); - return (cpc) ? (cpc->reason == CP_UMOUNT) && set : set; + return (cpc) ? (cpc->reason & CP_UMOUNT) && set : set; } static inline void f2fs_lock_op(struct f2fs_sb_info *sbi) @@ -1400,7 +1398,7 @@ static inline int __get_cp_reason(struct f2fs_sb_info *sbi) static inline bool __remain_node_summaries(int reason) { - return (reason == CP_UMOUNT || reason == CP_FASTBOOT); + return (reason & (CP_UMOUNT | CP_FASTBOOT)); } static inline bool __exist_node_summaries(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9e3dd5d80504..dbec9fbe1be8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1262,7 +1262,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, unsigned long *discard_map = (unsigned long *)se->discard_map; unsigned long *dmap = SIT_I(sbi)->tmp_map; unsigned int start = 0, end = -1; - bool force = (cpc->reason == CP_DISCARD); + bool force = (cpc->reason & CP_DISCARD); struct discard_entry *de = NULL; struct list_head *head = &SM_I(sbi)->dcc_info->entry_list; int i; @@ -1345,7 +1345,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int start = 0, end = -1; unsigned int secno, start_segno; - bool force = (cpc->reason == CP_DISCARD); + bool force = (cpc->reason & CP_DISCARD); mutex_lock(&dirty_i->seglist_lock); @@ -2849,7 +2849,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) se = get_seg_entry(sbi, segno); /* add discard candidates */ - if (cpc->reason != CP_DISCARD) { + if (!(cpc->reason & CP_DISCARD)) { cpc->trim_start = segno; add_discard_addrs(sbi, cpc, false); } @@ -2885,7 +2885,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_bug_on(sbi, !list_empty(head)); f2fs_bug_on(sbi, sit_i->dirty_sentries); out: - if (cpc->reason == CP_DISCARD) { + if (cpc->reason & CP_DISCARD) { __u64 trim_start = cpc->trim_start; for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) -- GitLab From 39b3840cf675a852eed6c75059560dbedcbce434 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 28 Apr 2017 13:56:08 +0800 Subject: [PATCH 0666/5498] f2fs: introduce CP_TRIMMED_FLAG to avoid unneeded discard Introduce CP_TRIMMED_FLAG to indicate all invalid block were trimmed before umount, so once we do mount with image which contain the flag, we don't record invalid blocks as undiscard one, when fstrim is being triggered, we can avoid issuing redundant discard commands. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: include/trace/events/f2fs.h --- fs/f2fs/checkpoint.c | 3 +++ fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 28 ++++++++++++++++++++-------- fs/f2fs/super.c | 7 +++++++ include/linux/f2fs_fs.h | 1 + include/trace/events/f2fs.h | 3 ++- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 4d7e3d9a7bb0..b39de99aa58f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1060,6 +1060,9 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) sbi->blocks_per_seg - NM_I(sbi)->nat_bits_blocks) disable_nat_bits(sbi, false); + if (cpc->reason & CP_TRIMMED) + __set_ckpt_flags(ckpt, CP_TRIMMED_FLAG); + if (cpc->reason & CP_UMOUNT) __set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); else diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 421766f9707a..1bd14fec10f5 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -229,6 +229,7 @@ enum { #define CP_SYNC 0x00000004 #define CP_RECOVERY 0x00000008 #define CP_DISCARD 0x00000010 +#define CP_TRIMMED 0x00000020 #define DEF_BATCHED_TRIM_SECTIONS 2048 #define BATCHED_TRIM_SEGMENTS(sbi) \ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index dbec9fbe1be8..7d45825b5dbb 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3084,10 +3084,17 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) /* build discard map only one time */ if (f2fs_discard_en(sbi)) { - memcpy(se->discard_map, se->cur_valid_map, - SIT_VBLOCK_MAP_SIZE); - sbi->discard_blks += sbi->blocks_per_seg - - se->valid_blocks; + if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { + memset(se->discard_map, 0xff, + SIT_VBLOCK_MAP_SIZE); + } else { + memcpy(se->discard_map, + se->cur_valid_map, + SIT_VBLOCK_MAP_SIZE); + sbi->discard_blks += + sbi->blocks_per_seg - + se->valid_blocks; + } } if (sbi->segs_per_sec > 1) @@ -3111,10 +3118,15 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) seg_info_from_raw_sit(se, &sit); if (f2fs_discard_en(sbi)) { - memcpy(se->discard_map, se->cur_valid_map, - SIT_VBLOCK_MAP_SIZE); - sbi->discard_blks += old_valid_blocks - - se->valid_blocks; + if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { + memset(se->discard_map, 0xff, + SIT_VBLOCK_MAP_SIZE); + } else { + memcpy(se->discard_map, se->cur_valid_map, + SIT_VBLOCK_MAP_SIZE); + sbi->discard_blks += old_valid_blocks - + se->valid_blocks; + } } if (sbi->segs_per_sec > 1) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a87b9f65d5a0..375dfab4b157 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -784,6 +784,13 @@ static void f2fs_put_super(struct super_block *sb) /* be sure to wait for any on-going discard commands */ f2fs_wait_discard_bios(sbi); + if (!sbi->discard_blks) { + struct cp_control cpc = { + .reason = CP_UMOUNT | CP_TRIMMED, + }; + write_checkpoint(sbi, &cpc); + } + /* write_checkpoint can update stat informaion */ f2fs_destroy_stats(sbi); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index ef70be9c73b3..67303b808ff1 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -114,6 +114,7 @@ struct f2fs_super_block { /* * For checkpoint */ +#define CP_TRIMMED_FLAG 0x00000100 #define CP_NAT_BITS_FLAG 0x00000080 #define CP_CRC_RECOVERY_FLAG 0x00000040 #define CP_FASTBOOT_FLAG 0x00000020 diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index f4cd25fd7d8a..83c7a11daafa 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -82,7 +82,8 @@ { CP_FASTBOOT, "Fastboot" }, \ { CP_SYNC, "Sync" }, \ { CP_RECOVERY, "Recovery" }, \ - { CP_DISCARD, "Discard" }) + { CP_DISCARD, "Discard" }, \ + { CP_UMOUNT | CP_TRIMMED, "Umount,Trimmed" }) struct victim_sel_policy; struct f2fs_map_blocks; -- GitLab From 57708b788fc7678b2210e7202e7f8781a9becda9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 1 May 2017 18:09:44 -0700 Subject: [PATCH 0667/5498] f2fs: flush dirty nats periodically This patch flushes dirty nats in order to acquire available nids by writing checkpoint. Otherwise, we can have no chance to get freed nids. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7d45825b5dbb..fc951ac05859 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -419,7 +419,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) else build_free_nids(sbi, false, false); - if (!is_idle(sbi)) + if (!is_idle(sbi) && !excess_dirty_nats(sbi)) return; /* checkpoint is the only way to shrink partial cached entries */ -- GitLab From c7ffd6d2667ee6070952a6579e0dd525d041ac2c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 1 May 2017 18:13:03 -0700 Subject: [PATCH 0668/5498] f2fs: show available_nids in f2fs/status This patch adds an entry in f2fs/status to show # of available nids. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 5 +++-- fs/f2fs/f2fs.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index aa27ae495377..629c57024106 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -97,6 +97,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->sits = MAIN_SEGS(sbi); si->dirty_sits = SIT_I(sbi)->dirty_sentries; si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID_LIST]; + si->avail_nids = NM_I(sbi)->available_nids; si->alloc_nids = NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]; si->bg_gc = sbi->bg_gc; si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg) @@ -372,8 +373,8 @@ static int stat_show(struct seq_file *s, void *v) si->ndirty_imeta); seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", si->dirty_nats, si->nats, si->dirty_sits, si->sits); - seq_printf(s, " - free_nids: %9d, alloc_nids: %9d\n", - si->free_nids, si->alloc_nids); + seq_printf(s, " - free_nids: %9d/%9d\n - alloc_nids: %9d\n", + si->free_nids, si->avail_nids, si->alloc_nids); seq_puts(s, "\nDistribution of User Blocks:"); seq_puts(s, " [ valid | invalid | free ]\n"); seq_puts(s, " ["); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1bd14fec10f5..20e176019e4d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2495,7 +2495,8 @@ struct f2fs_stat_info { int ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta; int inmem_pages; unsigned int ndirty_dirs, ndirty_files, ndirty_all; - int nats, dirty_nats, sits, dirty_sits, free_nids, alloc_nids; + int nats, dirty_nats, sits, dirty_sits; + int free_nids, avail_nids, alloc_nids; int total_count, utilization; int bg_gc, nr_wb_cp_data, nr_wb_data; int nr_flushing, nr_flushed, nr_discarding, nr_discarded; -- GitLab From 0bd2e751743d631d0c5c65280850db81401ddf55 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 3 May 2017 23:59:13 +0800 Subject: [PATCH 0669/5498] f2fs: relocate inode_{,un}lock in F2FS_IOC_SETFLAGS This patch expands cover region of inode->i_rwsem to keep setting flag atomically. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index fef143f8ecf9..5e8ea4967ed5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1488,10 +1488,10 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) if (ret) return ret; - flags = f2fs_mask_flags(inode->i_mode, flags); - inode_lock(inode); + flags = f2fs_mask_flags(inode->i_mode, flags); + oldflags = fi->i_flags; if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { @@ -1505,10 +1505,11 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) flags = flags & FS_FL_USER_MODIFIABLE; flags |= oldflags & ~FS_FL_USER_MODIFIABLE; fi->i_flags = flags; - inode_unlock(inode); inode->i_ctime = current_time(inode); f2fs_set_inode_flags(inode); + + inode_unlock(inode); out: mnt_drop_write_file(filp); return ret; -- GitLab From ce6e24ea2327bd70b048bcab32bdbe83fe48a052 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 26 Apr 2017 15:56:52 +0800 Subject: [PATCH 0670/5498] f2fs: fix a mount fail for wrong next_scan_nid -write_checkpoint -do_checkpoint -next_free_nid <--- something wrong with next free nid -f2fs_fill_super -build_node_manager -build_free_nids -get_current_nat_page -__get_meta_page <--- attempt to access beyond end of device Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 38ecc549b504..b06e270b0fbe 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1964,6 +1964,9 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) int i = 0; nid_t nid = nm_i->next_scan_nid; + if (unlikely(nid >= nm_i->max_nid)) + nid = 0; + /* Enough entries */ if (nm_i->nid_cnt[FREE_NID_LIST] >= NAT_ENTRY_PER_BLOCK) return; -- GitLab From ef9980d16c296d8d2a1ef35f02f048f0e826af7b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 7 Apr 2017 10:58:39 -0700 Subject: [PATCH 0671/5498] f2fs: sync f2fs_lookup() with ext4_lookup() As for ext4, now that fscrypt_has_permitted_context() correctly handles the case where we have the key for the parent directory but not the child, f2fs_lookup() no longer has to work around it. Also add the same warning message that ext4 uses. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/f2fs/namei.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index ae985a311ace..b5a523972ffd 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -325,9 +325,10 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (f2fs_encrypted_inode(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && !fscrypt_has_permitted_context(dir, inode)) { - bool nokey = f2fs_encrypted_inode(inode) && - !fscrypt_has_encryption_key(inode); - err = nokey ? -ENOKEY : -EPERM; + f2fs_msg(inode->i_sb, KERN_WARNING, + "Inconsistent encryption contexts: %lu/%lu", + dir->i_ino, inode->i_ino); + err = -EPERM; goto err_out; } return d_splice_alias(inode, dentry); -- GitLab From 9f0ac4af605c83b8edddc255b562bd5d53692063 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 24 Apr 2017 10:00:08 -0700 Subject: [PATCH 0672/5498] f2fs: check entire encrypted bigname when finding a dentry If user has no key under an encrypted dir, fscrypt gives digested dentries. Previously, when looking up a dentry, f2fs only checks its hash value with first 4 bytes of the digested dentry, which didn't handle hash collisions fully. This patch enhances to check entire dentry bytes likewise ext4. Eric reported how to reproduce this issue by: # seq -f "edir/abcdefghijklmnopqrstuvwxyz012345%.0f" 100000 | xargs touch # find edir -type f | xargs stat -c %i | sort | uniq | wc -l 100000 # sync # echo 3 > /proc/sys/vm/drop_caches # keyctl new_session # find edir -type f | xargs stat -c %i | sort | uniq | wc -l 99999 Cc: Reported-by: Eric Biggers Signed-off-by: Jaegeuk Kim (fixed f2fs_dentry_hash() to work even when the hash is 0) Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o Conflicts: fs/f2fs/inline.c --- fs/f2fs/dir.c | 37 +++++++++++++++++++++---------------- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/hash.c | 7 ++++++- fs/f2fs/inline.c | 4 ++-- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index daa251de50b2..ceac607e2867 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -130,19 +130,29 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, continue; } - /* encrypted case */ + if (de->hash_code != namehash) + goto not_match; + de_name.name = d->filename[bit_pos]; de_name.len = le16_to_cpu(de->name_len); - /* show encrypted name */ - if (fname->hash) { - if (de->hash_code == cpu_to_le32(fname->hash)) - goto found; - } else if (de_name.len == name->len && - de->hash_code == namehash && - !memcmp(de_name.name, name->name, name->len)) +#ifdef CONFIG_F2FS_FS_ENCRYPTION + if (unlikely(!name->name)) { + if (fname->usr_fname->name[0] == '_') { + if (de_name.len >= 16 && + !memcmp(de_name.name + de_name.len - 16, + fname->crypto_buf.name + 8, 16)) + goto found; + goto not_match; + } + name->name = fname->crypto_buf.name; + name->len = fname->crypto_buf.len; + } +#endif + if (de_name.len == name->len && + !memcmp(de_name.name, name->name, name->len)) goto found; - +not_match: if (max_slots && max_len > *max_slots) *max_slots = max_len; max_len = 0; @@ -170,12 +180,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, struct f2fs_dir_entry *de = NULL; bool room = false; int max_slots; - f2fs_hash_t namehash; - - if(fname->hash) - namehash = cpu_to_le32(fname->hash); - else - namehash = f2fs_dentry_hash(&name); + f2fs_hash_t namehash = f2fs_dentry_hash(&name, fname); nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level); nblock = bucket_blocks(level); @@ -526,7 +531,7 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, level = 0; slots = GET_DENTRY_SLOTS(new_name->len); - dentry_hash = f2fs_dentry_hash(new_name); + dentry_hash = f2fs_dentry_hash(new_name, NULL); current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 20e176019e4d..eae62da5578f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2291,7 +2291,8 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi); /* * hash.c */ -f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info); +f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info, + struct fscrypt_name *fname); /* * node.c diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index 71b7206c431e..eb2e031ea887 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned char *msg, size_t len, *buf++ = pad; } -f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info) +f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info, + struct fscrypt_name *fname) { __u32 hash; f2fs_hash_t f2fs_hash; @@ -79,6 +80,10 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info) const unsigned char *name = name_info->name; size_t len = name_info->len; + /* encrypted bigname case */ + if (fname && !fname->disk_name.name) + return cpu_to_le32(fname->hash); + if (is_dot_dotdot(name_info)) return 0; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 0ccdefe9fdba..e4c527c4e7d0 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -298,7 +298,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, return NULL; } - namehash = f2fs_dentry_hash(&name); + namehash = f2fs_dentry_hash(&name, fname); inline_dentry = inline_data_addr(ipage); @@ -533,7 +533,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, f2fs_wait_on_page_writeback(ipage, NODE, true); - name_hash = f2fs_dentry_hash(new_name); + name_hash = f2fs_dentry_hash(new_name, NULL); make_dentry_ptr_inline(NULL, &d, dentry_blk); f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); -- GitLab From 43d2cb9bd9fc0c6706b1b628304999dd20c26084 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 24 Apr 2017 10:00:09 -0700 Subject: [PATCH 0673/5498] fscrypt: avoid collisions when presenting long encrypted filenames When accessing an encrypted directory without the key, userspace must operate on filenames derived from the ciphertext names, which contain arbitrary bytes. Since we must support filenames as long as NAME_MAX, we can't always just base64-encode the ciphertext, since that may make it too long. Currently, this is solved by presenting long names in an abbreviated form containing any needed filesystem-specific hashes (e.g. to identify a directory block), then the last 16 bytes of ciphertext. This needs to be sufficient to identify the actual name on lookup. However, there is a bug. It seems to have been assumed that due to the use of a CBC (ciphertext block chaining)-based encryption mode, the last 16 bytes (i.e. the AES block size) of ciphertext would depend on the full plaintext, preventing collisions. However, we actually use CBC with ciphertext stealing (CTS), which handles the last two blocks specially, causing them to appear "flipped". Thus, it's actually the second-to-last block which depends on the full plaintext. This caused long filenames that differ only near the end of their plaintexts to, when observed without the key, point to the wrong inode and be undeletable. For example, with ext4: # echo pass | e4crypt add_key -p 16 edir/ # seq -f "edir/abcdefghijklmnopqrstuvwxyz012345%.0f" 100000 | xargs touch # find edir/ -type f | xargs stat -c %i | sort | uniq | wc -l 100000 # sync # echo 3 > /proc/sys/vm/drop_caches # keyctl new_session # find edir/ -type f | xargs stat -c %i | sort | uniq | wc -l 2004 # rm -rf edir/ rm: cannot remove 'edir/_A7nNFi3rhkEQlJ6P,hdzluhODKOeWx5V': Structure needs cleaning ... To fix this, when presenting long encrypted filenames, encode the second-to-last block of ciphertext rather than the last 16 bytes. Although it would be nice to solve this without depending on a specific encryption mode, that would mean doing a cryptographic hash like SHA-256 which would be much less efficient. This way is sufficient for now, and it's still compatible with encryption modes like HEH which are strong pseudorandom permutations. Also, changing the presented names is still allowed at any time because they are only provided to allow applications to do things like delete encrypted directories. They're not designed to be used to persistently identify files --- which would be hard to do anyway, given that they're encrypted after all. For ease of backports, this patch only makes the minimal fix to both ext4 and f2fs. It leaves ubifs as-is, since ubifs doesn't compare the ciphertext block yet. Follow-on patches will clean things up properly and make the filesystems use a shared helper function. Fixes: 5de0b4d0cd15 ("ext4 crypto: simplify and speed up filename encryption") Reported-by: Gwendal Grignou Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 2 +- fs/f2fs/dir.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index c7d8814c0e31..376e62a10950 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -300,7 +300,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, } else { memset(buf, 0, 8); } - memcpy(buf + 8, iname->name + iname->len - 16, 16); + memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16); oname->name[0] = '_'; oname->len = 1 + digest_encode(buf, 24, oname->name + 1); return 0; diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index ceac607e2867..f73615b152d6 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -139,8 +139,8 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, #ifdef CONFIG_F2FS_FS_ENCRYPTION if (unlikely(!name->name)) { if (fname->usr_fname->name[0] == '_') { - if (de_name.len >= 16 && - !memcmp(de_name.name + de_name.len - 16, + if (de_name.len > 32 && + !memcmp(de_name.name + ((de_name.len - 17) & ~15), fname->crypto_buf.name + 8, 16)) goto found; goto not_match; -- GitLab From a9244ccda6b192a6c3477130a8eec8e6badd1975 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 21 Feb 2017 15:07:11 -0800 Subject: [PATCH 0674/5498] fscrypt: remove broken support for detecting keyring key revocation Filesystem encryption ostensibly supported revoking a keyring key that had been used to "unlock" encrypted files, causing those files to become "locked" again. This was, however, buggy for several reasons, the most severe of which was that when key revocation happened to be detected for an inode, its fscrypt_info was immediately freed, even while other threads could be using it for encryption or decryption concurrently. This could be exploited to crash the kernel or worse. This patch fixes the use-after-free by removing the code which detects the keyring key having been revoked, invalidated, or expired. Instead, an encrypted inode that is "unlocked" now simply remains unlocked until it is evicted from memory. Note that this is no worse than the case for block device-level encryption, e.g. dm-crypt, and it still remains possible for a privileged user to evict unused pages, inodes, and dentries by running 'sync; echo 3 > /proc/sys/vm/drop_caches', or by simply unmounting the filesystem. In fact, one of those actions was already needed anyway for key revocation to work even somewhat sanely. This change is not expected to break any applications. In the future I'd like to implement a real API for fscrypt key revocation that interacts sanely with ongoing filesystem operations --- waiting for existing operations to complete and blocking new operations, and invalidating and sanitizing key material and plaintext from the VFS caches. But this is a hard problem, and for now this bug must be fixed. This bug affected almost all versions of ext4, f2fs, and ubifs encryption, and it was potentially reachable in any kernel configured with encryption support (CONFIG_EXT4_ENCRYPTION=y, CONFIG_EXT4_FS_ENCRYPTION=y, CONFIG_F2FS_FS_ENCRYPTION=y, or CONFIG_UBIFS_FS_ENCRYPTION=y). Note that older kernels did not use the shared fs/crypto/ code, but due to the potential security implications of this bug, it may still be worthwhile to backport this fix to them. Fixes: b7236e21d55f ("ext4 crypto: reorganize how we store keys in the inode") Cc: stable@vger.kernel.org # v4.2+ Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o Acked-by: Michael Halcrow Conflicts: fs/crypto/fscrypt_private.h --- fs/crypto/crypto.c | 10 +------ fs/crypto/fname.c | 2 +- fs/crypto/fscrypt_private.h | 4 --- fs/crypto/keyinfo.c | 53 +++++++------------------------------ 4 files changed, 11 insertions(+), 58 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 611fa002785c..2100bc8ac7b1 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -327,7 +327,6 @@ EXPORT_SYMBOL(fscrypt_decrypt_page); static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) { struct dentry *dir; - struct fscrypt_info *ci; int dir_has_key, cached_with_key; if (flags & LOOKUP_RCU) @@ -339,18 +338,11 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) return 0; } - ci = d_inode(dir)->i_crypt_info; - if (ci && ci->ci_keyring_key && - (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | - (1 << KEY_FLAG_REVOKED) | - (1 << KEY_FLAG_DEAD)))) - ci = NULL; - /* this should eventually be an flag in d_flags */ spin_lock(&dentry->d_lock); cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY; spin_unlock(&dentry->d_lock); - dir_has_key = (ci != NULL); + dir_has_key = (d_inode(dir)->i_crypt_info != NULL); dput(dir); /* diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 376e62a10950..f71706b860fa 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -350,7 +350,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, fname->disk_name.len = iname->len; return 0; } - ret = fscrypt_get_crypt_info(dir); + ret = fscrypt_get_encryption_info(dir); if (ret && ret != -EOPNOTSUPP) return ret; diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index d26b6c03a2a1..6bb116ff07c6 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -67,7 +67,6 @@ struct fscrypt_info { u8 ci_filename_mode; u8 ci_flags; struct crypto_ablkcipher *ci_ctfm; - struct key *ci_keyring_key; u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE]; }; @@ -126,7 +125,4 @@ extern int fscrypt_do_page_crypto(const struct inode *inode, extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags); -/* keyinfo.c */ -extern int fscrypt_get_crypt_info(struct inode *); - #endif /* _FSCRYPT_PRIVATE_H */ diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 54e49dbf32f0..1ef790f4932b 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -108,6 +108,7 @@ static int validate_user_key(struct fscrypt_info *crypt_info, kfree(description); if (IS_ERR(keyring_key)) return PTR_ERR(keyring_key); + down_read(&keyring_key->sem); if (keyring_key->type != &key_type_logon) { printk_once(KERN_WARNING @@ -115,11 +116,9 @@ static int validate_user_key(struct fscrypt_info *crypt_info, res = -ENOKEY; goto out; } - down_read(&keyring_key->sem); ukp = user_key_payload(keyring_key); if (ukp->datalen != sizeof(struct fscrypt_key)) { res = -EINVAL; - up_read(&keyring_key->sem); goto out; } master_key = (struct fscrypt_key *)ukp->data; @@ -130,17 +129,11 @@ static int validate_user_key(struct fscrypt_info *crypt_info, "%s: key size incorrect: %d\n", __func__, master_key->size); res = -ENOKEY; - up_read(&keyring_key->sem); goto out; } res = derive_key_aes(ctx->nonce, master_key->raw, raw_key); - up_read(&keyring_key->sem); - if (res) - goto out; - - crypt_info->ci_keyring_key = keyring_key; - return 0; out: + up_read(&keyring_key->sem); key_put(keyring_key); return res; } @@ -182,13 +175,11 @@ static void put_crypt_info(struct fscrypt_info *ci) if (!ci) return; - if (ci->ci_keyring_key) - key_put(ci->ci_keyring_key); crypto_free_ablkcipher(ci->ci_ctfm); kmem_cache_free(fscrypt_info_cachep, ci); } -int fscrypt_get_crypt_info(struct inode *inode) +int fscrypt_get_encryption_info(struct inode *inode) { struct fscrypt_info *crypt_info; struct fscrypt_context ctx; @@ -198,21 +189,15 @@ int fscrypt_get_crypt_info(struct inode *inode) u8 *raw_key = NULL; int res; + if (inode->i_crypt_info) + return 0; + res = fscrypt_initialize(inode->i_sb->s_cop->flags); if (res) return res; if (!inode->i_sb->s_cop->get_context) return -EOPNOTSUPP; -retry: - crypt_info = ACCESS_ONCE(inode->i_crypt_info); - if (crypt_info) { - if (!crypt_info->ci_keyring_key || - key_validate(crypt_info->ci_keyring_key) == 0) - return 0; - fscrypt_put_encryption_info(inode, crypt_info); - goto retry; - } res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); if (res < 0) { @@ -243,7 +228,6 @@ retry: crypt_info->ci_data_mode = ctx.contents_encryption_mode; crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; crypt_info->ci_ctfm = NULL; - crypt_info->ci_keyring_key = NULL; memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, sizeof(crypt_info->ci_master_key)); @@ -287,14 +271,8 @@ retry: if (res) goto out; - kzfree(raw_key); - raw_key = NULL; - if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) != NULL) { - put_crypt_info(crypt_info); - goto retry; - } - return 0; - + if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL) + crypt_info = NULL; out: if (res == -ENOKEY) res = 0; @@ -302,6 +280,7 @@ out: kzfree(raw_key); return res; } +EXPORT_SYMBOL(fscrypt_get_encryption_info); void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci) { @@ -319,17 +298,3 @@ void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci) put_crypt_info(ci); } EXPORT_SYMBOL(fscrypt_put_encryption_info); - -int fscrypt_get_encryption_info(struct inode *inode) -{ - struct fscrypt_info *ci = inode->i_crypt_info; - - if (!ci || - (ci->ci_keyring_key && - (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | - (1 << KEY_FLAG_REVOKED) | - (1 << KEY_FLAG_DEAD))))) - return fscrypt_get_crypt_info(inode); - return 0; -} -EXPORT_SYMBOL(fscrypt_get_encryption_info); -- GitLab From 6b332b933e18bb0f8cec011982b5f26cf7d2ca31 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 22 Feb 2017 13:25:14 -0800 Subject: [PATCH 0675/5498] fscrypt: eliminate ->prepare_context() operation The only use of the ->prepare_context() fscrypt operation was to allow ext4 to evict inline data from the inode before ->set_context(). However, there is no reason why this cannot be done as simply the first step in ->set_context(), and in fact it makes more sense to do it that way because then the policy modes and flags get validated before any real work is done. Therefore, merge ext4_prepare_context() into ext4_set_context(), and remove ->prepare_context(). Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o Conflicts: fs/ext4/super.c --- fs/crypto/policy.c | 7 ------- include/linux/fscrypt_common.h | 1 - 2 files changed, 8 deletions(-) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 14b76da71269..4908906d54d5 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -33,17 +33,10 @@ static int create_encryption_context_from_policy(struct inode *inode, const struct fscrypt_policy *policy) { struct fscrypt_context ctx; - int res; if (!inode->i_sb->s_cop->set_context) return -EOPNOTSUPP; - if (inode->i_sb->s_cop->prepare_context) { - res = inode->i_sb->s_cop->prepare_context(inode); - if (res) - return res; - } - ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE); diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt_common.h index 547f81592ba1..10c1abfbac6c 100644 --- a/include/linux/fscrypt_common.h +++ b/include/linux/fscrypt_common.h @@ -87,7 +87,6 @@ struct fscrypt_operations { unsigned int flags; const char *key_prefix; int (*get_context)(struct inode *, void *, size_t); - int (*prepare_context)(struct inode *); int (*set_context)(struct inode *, const void *, size_t, void *); int (*dummy_context)(struct inode *); bool (*is_encrypted)(struct inode *); -- GitLab From 0cb2fd25f4a544e7f224a8de66f2038559e673ca Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 4 Apr 2017 14:39:41 -0700 Subject: [PATCH 0676/5498] fscrypt: remove unnecessary checks for NULL operations The functions in fs/crypto/*.c are only called by filesystems configured with encryption support. Since the ->get_context(), ->set_context(), and ->empty_dir() operations are always provided in that case (and must be, otherwise there would be no way to get/set encryption policies, or in the case of ->get_context() even access encrypted files at all), there is no need to check for these operations being NULL and we can remove these unneeded checks. Signed-off-by: Eric Biggers Reviewed-by: Richard Weinberger Signed-off-by: Theodore Ts'o --- fs/crypto/keyinfo.c | 3 --- fs/crypto/policy.c | 11 +---------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 1ef790f4932b..995003cb5301 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -196,9 +196,6 @@ int fscrypt_get_encryption_info(struct inode *inode) if (res) return res; - if (!inode->i_sb->s_cop->get_context) - return -EOPNOTSUPP; - res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); if (res < 0) { if (!fscrypt_dummy_context_enabled(inode) || diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 4908906d54d5..d71ec3780d0c 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -34,9 +34,6 @@ static int create_encryption_context_from_policy(struct inode *inode, { struct fscrypt_context ctx; - if (!inode->i_sb->s_cop->set_context) - return -EOPNOTSUPP; - ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE); @@ -87,8 +84,6 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) if (ret == -ENODATA) { if (!S_ISDIR(inode->i_mode)) ret = -ENOTDIR; - else if (!inode->i_sb->s_cop->empty_dir) - ret = -EOPNOTSUPP; else if (!inode->i_sb->s_cop->empty_dir(inode)) ret = -ENOTEMPTY; else @@ -118,8 +113,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) struct fscrypt_policy policy; int res; - if (!inode->i_sb->s_cop->get_context || - !inode->i_sb->s_cop->is_encrypted(inode)) + if (!inode->i_sb->s_cop->is_encrypted(inode)) return -ENODATA; res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); @@ -202,9 +196,6 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child, struct fscrypt_info *ci; int res; - if (!parent->i_sb->s_cop->set_context) - return -EOPNOTSUPP; - res = fscrypt_get_encryption_info(parent); if (res < 0) return res; -- GitLab From ae8ffd31bf62ae2ef57099a2716d173aee41f869 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 Apr 2017 16:14:05 -0700 Subject: [PATCH 0677/5498] fscrypt: Move key structure and constants to uapi This commit exposes the necessary constants and structures for a userspace program to pass filesystem encryption keys into the keyring. The fscrypt_key structure was already part of the kernel ABI, this change just makes it so programs no longer have to redeclare these structures (like e4crypt in e2fsprogs currently does). Note that we do not expose the other FS_*_KEY_SIZE constants as they are not necessary. Only XTS is supported for contents_encryption_mode, so currently FS_MAX_KEY_SIZE bytes of key material must always be passed to the kernel. This commit also removes __packed from fscrypt_key as it does not contain any implicit padding and does not refer to an on-disk structure. Signed-off-by: Joe Richey Signed-off-by: Theodore Ts'o --- fs/crypto/fscrypt_private.h | 11 ----------- include/uapi/linux/fs.h | 13 +++++++++++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 6bb116ff07c6..65fc3807d9aa 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -22,10 +22,6 @@ #define FS_AES_256_CBC_KEY_SIZE 32 #define FS_AES_256_CTS_KEY_SIZE 32 #define FS_AES_256_XTS_KEY_SIZE 64 -#define FS_MAX_KEY_SIZE 64 - -#define FS_KEY_DESC_PREFIX "fscrypt:" -#define FS_KEY_DESC_PREFIX_SIZE 8 #define FS_KEY_DERIVATION_NONCE_SIZE 16 @@ -51,13 +47,6 @@ struct fscrypt_context { #define FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 -/* This is passed in from userspace into the kernel keyring */ -struct fscrypt_key { - u32 mode; - u8 raw[FS_MAX_KEY_SIZE]; - u32 size; -} __packed; - /* * A pointer to this structure is stored in the file system's in-core * representation of an inode. diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 2c86c2fd4934..9606df30a5f5 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -201,6 +201,19 @@ struct fscrypt_policy { #define FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) #define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy) +/* Parameters for passing an encryption key into the kernel keyring */ +#define FS_KEY_DESC_PREFIX "fscrypt:" +#define FS_KEY_DESC_PREFIX_SIZE 8 + +/* Structure that userspace passes to the kernel keyring */ +#define FS_MAX_KEY_SIZE 64 + +struct fscrypt_key { + __u32 mode; + __u8 raw[FS_MAX_KEY_SIZE]; + __u32 size; +}; + /* * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS) */ -- GitLab From ba8661cbbc782a027d6ddf1a07f53f451c1e3e0a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 7 Apr 2017 10:58:37 -0700 Subject: [PATCH 0678/5498] fscrypt: fix context consistency check when key(s) unavailable To mitigate some types of offline attacks, filesystem encryption is designed to enforce that all files in an encrypted directory tree use the same encryption policy (i.e. the same encryption context excluding the nonce). However, the fscrypt_has_permitted_context() function which enforces this relies on comparing struct fscrypt_info's, which are only available when we have the encryption keys. This can cause two incorrect behaviors: 1. If we have the parent directory's key but not the child's key, or vice versa, then fscrypt_has_permitted_context() returned false, causing applications to see EPERM or ENOKEY. This is incorrect if the encryption contexts are in fact consistent. Although we'd normally have either both keys or neither key in that case since the master_key_descriptors would be the same, this is not guaranteed because keys can be added or removed from keyrings at any time. 2. If we have neither the parent's key nor the child's key, then fscrypt_has_permitted_context() returned true, causing applications to see no error (or else an error for some other reason). This is incorrect if the encryption contexts are in fact inconsistent, since in that case we should deny access. To fix this, retrieve and compare the fscrypt_contexts if we are unable to set up both fscrypt_infos. While this slightly hurts performance when accessing an encrypted directory tree without the key, this isn't a case we really need to be optimizing for; access *with* the key is much more important. Furthermore, the performance hit is barely noticeable given that we are already retrieving the fscrypt_context and doing two keyring searches in fscrypt_get_encryption_info(). If we ever actually wanted to optimize this case we might start by caching the fscrypt_contexts. Cc: stable@vger.kernel.org # 4.0+ Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/policy.c | 87 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index d71ec3780d0c..210976e7a269 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -137,27 +137,61 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) } EXPORT_SYMBOL(fscrypt_ioctl_get_policy); +/** + * fscrypt_has_permitted_context() - is a file's encryption policy permitted + * within its directory? + * + * @parent: inode for parent directory + * @child: inode for file being looked up, opened, or linked into @parent + * + * Filesystems must call this before permitting access to an inode in a + * situation where the parent directory is encrypted (either before allowing + * ->lookup() to succeed, or for a regular file before allowing it to be opened) + * and before any operation that involves linking an inode into an encrypted + * directory, including link, rename, and cross rename. It enforces the + * constraint that within a given encrypted directory tree, all files use the + * same encryption policy. The pre-access check is needed to detect potentially + * malicious offline violations of this constraint, while the link and rename + * checks are needed to prevent online violations of this constraint. + * + * Return: 1 if permitted, 0 if forbidden. If forbidden, the caller must fail + * the filesystem operation with EPERM. + */ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) { - struct fscrypt_info *parent_ci, *child_ci; + const struct fscrypt_operations *cops = parent->i_sb->s_cop; + const struct fscrypt_info *parent_ci, *child_ci; + struct fscrypt_context parent_ctx, child_ctx; int res; - if ((parent == NULL) || (child == NULL)) { - printk(KERN_ERR "parent %p child %p\n", parent, child); - BUG_ON(1); - } - /* No restrictions on file types which are never encrypted */ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && !S_ISLNK(child->i_mode)) return 1; - /* no restrictions if the parent directory is not encrypted */ - if (!parent->i_sb->s_cop->is_encrypted(parent)) + /* No restrictions if the parent directory is unencrypted */ + if (!cops->is_encrypted(parent)) return 1; - /* if the child directory is not encrypted, this is always a problem */ - if (!parent->i_sb->s_cop->is_encrypted(child)) + + /* Encrypted directories must not contain unencrypted files */ + if (!cops->is_encrypted(child)) return 0; + + /* + * Both parent and child are encrypted, so verify they use the same + * encryption policy. Compare the fscrypt_info structs if the keys are + * available, otherwise retrieve and compare the fscrypt_contexts. + * + * Note that the fscrypt_context retrieval will be required frequently + * when accessing an encrypted directory tree without the key. + * Performance-wise this is not a big deal because we already don't + * really optimize for file access without the key (to the extent that + * such access is even possible), given that any attempted access + * already causes a fscrypt_context retrieval and keyring search. + * + * In any case, if an unexpected error occurs, fall back to "forbidden". + */ + res = fscrypt_get_encryption_info(parent); if (res) return 0; @@ -166,17 +200,32 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) return 0; parent_ci = parent->i_crypt_info; child_ci = child->i_crypt_info; - if (!parent_ci && !child_ci) - return 1; - if (!parent_ci || !child_ci) + + if (parent_ci && child_ci) { + return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key, + FS_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ci->ci_data_mode == child_ci->ci_data_mode) && + (parent_ci->ci_filename_mode == + child_ci->ci_filename_mode) && + (parent_ci->ci_flags == child_ci->ci_flags); + } + + res = cops->get_context(parent, &parent_ctx, sizeof(parent_ctx)); + if (res != sizeof(parent_ctx)) + return 0; + + res = cops->get_context(child, &child_ctx, sizeof(child_ctx)); + if (res != sizeof(child_ctx)) return 0; - return (memcmp(parent_ci->ci_master_key, - child_ci->ci_master_key, - FS_KEY_DESCRIPTOR_SIZE) == 0 && - (parent_ci->ci_data_mode == child_ci->ci_data_mode) && - (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && - (parent_ci->ci_flags == child_ci->ci_flags)); + return memcmp(parent_ctx.master_key_descriptor, + child_ctx.master_key_descriptor, + FS_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ctx.contents_encryption_mode == + child_ctx.contents_encryption_mode) && + (parent_ctx.filenames_encryption_mode == + child_ctx.filenames_encryption_mode) && + (parent_ctx.flags == child_ctx.flags); } EXPORT_SYMBOL(fscrypt_has_permitted_context); -- GitLab From c5ab4ebe9333b512f620309f9c5641a5f09d7a16 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 24 Apr 2017 10:00:10 -0700 Subject: [PATCH 0679/5498] fscrypt: introduce helper function for filename matching Introduce a helper function fscrypt_match_name() which tests whether a fscrypt_name matches a directory entry. Also clean up the magic numbers and document things properly. Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 90 +++++++++++++++++++++++++-------- fs/crypto/fscrypt_private.h | 2 - include/linux/fscrypt_notsupp.h | 9 ++++ include/linux/fscrypt_supp.h | 78 ++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 22 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index f71706b860fa..17905ce0b92c 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -159,6 +159,8 @@ static int fname_decrypt(struct inode *inode, static const char *lookup_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; +#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) + /** * digest_encode() - * @@ -230,11 +232,14 @@ EXPORT_SYMBOL(fscrypt_fname_encrypted_size); int fscrypt_fname_alloc_buffer(const struct inode *inode, u32 ilen, struct fscrypt_str *crypto_str) { - unsigned int olen = fscrypt_fname_encrypted_size(inode, ilen); + u32 olen = fscrypt_fname_encrypted_size(inode, ilen); + const u32 max_encoded_len = + max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE), + 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name))); crypto_str->len = olen; - if (olen < FS_FNAME_CRYPTO_DIGEST_SIZE * 2) - olen = FS_FNAME_CRYPTO_DIGEST_SIZE * 2; + olen = max(olen, max_encoded_len); + /* * Allocated buffer can hold one more character to null-terminate the * string @@ -266,6 +271,10 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer); * * The caller must have allocated sufficient memory for the @oname string. * + * If the key is available, we'll decrypt the disk name; otherwise, we'll encode + * it for presentation. Short names are directly base64-encoded, while long + * names are encoded in fscrypt_digested_name format. + * * Return: 0 on success, -errno on failure */ int fscrypt_fname_disk_to_usr(struct inode *inode, @@ -274,7 +283,7 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, struct fscrypt_str *oname) { const struct qstr qname = FSTR_TO_QSTR(iname); - char buf[24]; + struct fscrypt_digested_name digested_name; if (fscrypt_is_dot_dotdot(&qname)) { oname->name[0] = '.'; @@ -289,20 +298,24 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, if (inode->i_crypt_info) return fname_decrypt(inode, iname, oname); - if (iname->len <= FS_FNAME_CRYPTO_DIGEST_SIZE) { + if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) { oname->len = digest_encode(iname->name, iname->len, oname->name); return 0; } if (hash) { - memcpy(buf, &hash, 4); - memcpy(buf + 4, &minor_hash, 4); + digested_name.hash = hash; + digested_name.minor_hash = minor_hash; } else { - memset(buf, 0, 8); + digested_name.hash = 0; + digested_name.minor_hash = 0; } - memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16); + memcpy(digested_name.digest, + FSCRYPT_FNAME_DIGEST(iname->name, iname->len), + FSCRYPT_FNAME_DIGEST_SIZE); oname->name[0] = '_'; - oname->len = 1 + digest_encode(buf, 24, oname->name + 1); + oname->len = 1 + digest_encode((const char *)&digested_name, + sizeof(digested_name), oname->name + 1); return 0; } EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); @@ -336,10 +349,35 @@ int fscrypt_fname_usr_to_disk(struct inode *inode, } EXPORT_SYMBOL(fscrypt_fname_usr_to_disk); +/** + * fscrypt_setup_filename() - prepare to search a possibly encrypted directory + * @dir: the directory that will be searched + * @iname: the user-provided filename being searched for + * @lookup: 1 if we're allowed to proceed without the key because it's + * ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot + * proceed without the key because we're going to create the dir_entry. + * @fname: the filename information to be filled in + * + * Given a user-provided filename @iname, this function sets @fname->disk_name + * to the name that would be stored in the on-disk directory entry, if possible. + * If the directory is unencrypted this is simply @iname. Else, if we have the + * directory's encryption key, then @iname is the plaintext, so we encrypt it to + * get the disk_name. + * + * Else, for keyless @lookup operations, @iname is the presented ciphertext, so + * we decode it to get either the ciphertext disk_name (for short names) or the + * fscrypt_digested_name (for long names). Non-@lookup operations will be + * impossible in this case, so we fail them with ENOKEY. + * + * If successful, fscrypt_free_filename() must be called later to clean up. + * + * Return: 0 on success, -errno on failure + */ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, int lookup, struct fscrypt_name *fname) { - int ret = 0, bigname = 0; + int ret; + int digested; memset(fname, 0, sizeof(struct fscrypt_name)); fname->usr_fname = iname; @@ -373,25 +411,37 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, * We don't have the key and we are doing a lookup; decode the * user-supplied name */ - if (iname->name[0] == '_') - bigname = 1; - if ((bigname && (iname->len != 33)) || (!bigname && (iname->len > 43))) - return -ENOENT; + if (iname->name[0] == '_') { + if (iname->len != + 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name))) + return -ENOENT; + digested = 1; + } else { + if (iname->len > + BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE)) + return -ENOENT; + digested = 0; + } - fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); + fname->crypto_buf.name = + kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE, + sizeof(struct fscrypt_digested_name)), + GFP_KERNEL); if (fname->crypto_buf.name == NULL) return -ENOMEM; - ret = digest_decode(iname->name + bigname, iname->len - bigname, + ret = digest_decode(iname->name + digested, iname->len - digested, fname->crypto_buf.name); if (ret < 0) { ret = -ENOENT; goto errout; } fname->crypto_buf.len = ret; - if (bigname) { - memcpy(&fname->hash, fname->crypto_buf.name, 4); - memcpy(&fname->minor_hash, fname->crypto_buf.name + 4, 4); + if (digested) { + const struct fscrypt_digested_name *n = + (const void *)fname->crypto_buf.name; + fname->hash = n->hash; + fname->minor_hash = n->minor_hash; } else { fname->disk_name.name = fname->crypto_buf.name; fname->disk_name.len = fname->crypto_buf.len; diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 65fc3807d9aa..1bc34d9814d9 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -13,8 +13,6 @@ #include -#define FS_FNAME_CRYPTO_DIGEST_SIZE 32 - /* Encryption parameters */ #define FS_XTS_TWEAK_SIZE 16 #define FS_AES_128_ECB_KEY_SIZE 16 diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 3511ca798804..ec406aed2f2f 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -147,6 +147,15 @@ static inline int fscrypt_fname_usr_to_disk(struct inode *inode, return -EOPNOTSUPP; } +static inline bool fscrypt_match_name(const struct fscrypt_name *fname, + const u8 *de_name, u32 de_name_len) +{ + /* Encryption support disabled; use standard comparison */ + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); +} + /* bio.c */ static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio) diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index a140f47e9b27..e12c224a0d1e 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -57,6 +57,84 @@ extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, extern int fscrypt_fname_usr_to_disk(struct inode *, const struct qstr *, struct fscrypt_str *); +#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32 + +/* Extracts the second-to-last ciphertext block; see explanation below */ +#define FSCRYPT_FNAME_DIGEST(name, len) \ + ((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \ + FS_CRYPTO_BLOCK_SIZE)) + +#define FSCRYPT_FNAME_DIGEST_SIZE FS_CRYPTO_BLOCK_SIZE + +/** + * fscrypt_digested_name - alternate identifier for an on-disk filename + * + * When userspace lists an encrypted directory without access to the key, + * filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE + * bytes are shown in this abbreviated form (base64-encoded) rather than as the + * full ciphertext (base64-encoded). This is necessary to allow supporting + * filenames up to NAME_MAX bytes, since base64 encoding expands the length. + * + * To make it possible for filesystems to still find the correct directory entry + * despite not knowing the full on-disk name, we encode any filesystem-specific + * 'hash' and/or 'minor_hash' which the filesystem may need for its lookups, + * followed by the second-to-last ciphertext block of the filename. Due to the + * use of the CBC-CTS encryption mode, the second-to-last ciphertext block + * depends on the full plaintext. (Note that ciphertext stealing causes the + * last two blocks to appear "flipped".) This makes collisions very unlikely: + * just a 1 in 2^128 chance for two filenames to collide even if they share the + * same filesystem-specific hashes. + * + * This scheme isn't strictly immune to intentional collisions because it's + * basically like a CBC-MAC, which isn't secure on variable-length inputs. + * However, generating a CBC-MAC collision requires the ability to choose + * arbitrary ciphertext, which won't normally be possible with filename + * encryption since it would require write access to the raw disk. + * + * Taking a real cryptographic hash like SHA-256 over the full ciphertext would + * be better in theory but would be less efficient and more complicated to + * implement, especially since the filesystem would need to calculate it for + * each directory entry examined during a search. + */ +struct fscrypt_digested_name { + u32 hash; + u32 minor_hash; + u8 digest[FSCRYPT_FNAME_DIGEST_SIZE]; +}; + +/** + * fscrypt_match_name() - test whether the given name matches a directory entry + * @fname: the name being searched for + * @de_name: the name from the directory entry + * @de_name_len: the length of @de_name in bytes + * + * Normally @fname->disk_name will be set, and in that case we simply compare + * that to the name stored in the directory entry. The only exception is that + * if we don't have the key for an encrypted directory and a filename in it is + * very long, then we won't have the full disk_name and we'll instead need to + * match against the fscrypt_digested_name. + * + * Return: %true if the name matches, otherwise %false. + */ +static inline bool fscrypt_match_name(const struct fscrypt_name *fname, + const u8 *de_name, u32 de_name_len) +{ + if (unlikely(!fname->disk_name.name)) { + const struct fscrypt_digested_name *n = + (const void *)fname->crypto_buf.name; + if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_')) + return false; + if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) + return false; + return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len), + n->digest, FSCRYPT_FNAME_DIGEST_SIZE); + } + + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); +} + /* bio.c */ extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); extern void fscrypt_pullback_bio_page(struct page **, bool); -- GitLab From 396a2965850160ca5427239d9af56ec42fe4774f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 24 Apr 2017 10:00:12 -0700 Subject: [PATCH 0680/5498] f2fs: switch to using fscrypt_match_name() Switch f2fs directory searches to use the fscrypt_match_name() helper function. There should be no functional change. Signed-off-by: Eric Biggers Acked-by: Jaegeuk Kim Signed-off-by: Theodore Ts'o --- fs/f2fs/dir.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index f73615b152d6..f0333d1ef222 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -111,8 +111,6 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, struct f2fs_dir_entry *de; unsigned long bit_pos = 0; int max_len = 0; - struct fscrypt_str de_name = FSTR_INIT(NULL, 0); - struct fscrypt_str *name = &fname->disk_name; if (max_slots) *max_slots = 0; @@ -130,29 +128,11 @@ struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname, continue; } - if (de->hash_code != namehash) - goto not_match; - - de_name.name = d->filename[bit_pos]; - de_name.len = le16_to_cpu(de->name_len); - -#ifdef CONFIG_F2FS_FS_ENCRYPTION - if (unlikely(!name->name)) { - if (fname->usr_fname->name[0] == '_') { - if (de_name.len > 32 && - !memcmp(de_name.name + ((de_name.len - 17) & ~15), - fname->crypto_buf.name + 8, 16)) - goto found; - goto not_match; - } - name->name = fname->crypto_buf.name; - name->len = fname->crypto_buf.len; - } -#endif - if (de_name.len == name->len && - !memcmp(de_name.name, name->name, name->len)) + if (de->hash_code == namehash && + fscrypt_match_name(fname, d->filename[bit_pos], + le16_to_cpu(de->name_len))) goto found; -not_match: + if (max_slots && max_len > *max_slots) *max_slots = max_len; max_len = 0; -- GitLab From 6ff1b5305050a8ddfa7e2b74bfcc370fc6609047 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 7 Jun 2017 11:49:33 -0700 Subject: [PATCH 0681/5498] f2fs: enable f2fs_migrate_page Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 4 ++-- fs/f2fs/node.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index b39de99aa58f..2693cda3e462 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -388,7 +388,7 @@ const struct address_space_operations f2fs_meta_aops = { .set_page_dirty = f2fs_set_meta_page_dirty, .invalidatepage = f2fs_invalidate_page, .releasepage = f2fs_release_page, -#ifdef CONFIG_F2FS_MIGRATION +#ifdef CONFIG_MIGRATION .migratepage = f2fs_migrate_page, #endif }; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 35da69f63769..e17fdee64c62 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2130,7 +2130,7 @@ static sector_t f2fs_bmap(struct address_space *mapping, sector_t block) return generic_block_bmap(mapping, block, get_data_block_bmap); } -#ifdef CONFIG_F2FS_MIGRATION +#ifdef CONFIG_MIGRATION #include int f2fs_migrate_page(struct address_space *mapping, @@ -2194,7 +2194,7 @@ const struct address_space_operations f2fs_dblock_aops = { .releasepage = f2fs_release_page, .direct_IO = f2fs_direct_IO, .bmap = f2fs_bmap, -#ifdef CONFIG_F2FS_MIGRATION +#ifdef CONFIG_MIGRATION .migratepage = f2fs_migrate_page, #endif }; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b06e270b0fbe..7d892aa5e53a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1732,7 +1732,7 @@ const struct address_space_operations f2fs_node_aops = { .set_page_dirty = f2fs_set_node_page_dirty, .invalidatepage = f2fs_invalidate_page, .releasepage = f2fs_release_page, -#ifdef CONFIG_F2FS_MIGRATION +#ifdef CONFIG_MIGRATION .migratepage = f2fs_migrate_page, #endif }; -- GitLab From 707b6514a347ed76a40ba67852aa1f396e493bc8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 9 Jun 2017 16:52:11 -0700 Subject: [PATCH 0682/5498] f2fs: use WRITE_SYNC for REQ_SYNC WRITE_SYNC contains REQ_SYNC and REQ_NOIDLE. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index eae62da5578f..0b89bdcc9e73 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -133,7 +133,7 @@ static inline void bio_set_op_attrs(struct bio *bio, unsigned op, static inline int wbc_to_write_flags(struct writeback_control *wbc) { if (wbc->sync_mode == WB_SYNC_ALL) - return REQ_SYNC; + return WRITE_SYNC; return 0; } -- GitLab From bdd5b7b48a1d3874df6f0fa41b100dae27ad4a3a Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 13 Feb 2015 15:49:17 +1100 Subject: [PATCH 0683/5498] sched: Prevent recursion in io_schedule() io_schedule() calls blk_flush_plug() which, depending on the contents of current->plug, can initiate arbitrary blk-io requests. Note that this contrasts with blk_schedule_flush_plug() which requires all non-trivial work to be handed off to a separate thread. This makes it possible for io_schedule() to recurse, and initiating block requests could possibly call mempool_alloc() which, in times of memory pressure, uses io_schedule(). Apart from any stack usage issues, io_schedule() will not behave correctly when called recursively as delayacct_blkio_start() does not allow for repeated calls. So: - use ->in_iowait to detect recursion. Set it earlier, and restore it to the old value. - move the call to "raw_rq" after the call to blk_flush_plug(). As this is some sort of per-cpu thing, we want some chance that we are on the right CPU - When io_schedule() is called recurively, use blk_schedule_flush_plug() which cannot further recurse. - as this makes io_schedule() a lot more complex and as io_schedule() must match io_schedule_timeout(), but all the changes in io_schedule_timeout() and make io_schedule a simple wrapper for that. Signed-off-by: NeilBrown Signed-off-by: Peter Zijlstra (Intel) [ Moved the now rudimentary io_schedule() into sched.h. ] Cc: Jens Axboe Cc: Linus Torvalds Cc: Tony Battersby Link: http://lkml.kernel.org/r/20150213162600.059fffb2@notabene.brown Signed-off-by: Ingo Molnar --- include/linux/sched.h | 10 +++++++--- kernel/sched/core.c | 30 +++++++++++------------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index ebfd7a5ea6d4..ecf76425b9fb 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -324,9 +324,6 @@ extern void show_regs(struct pt_regs *); */ extern void show_stack(struct task_struct *task, unsigned long *sp); -void io_schedule(void); -long io_schedule_timeout(long timeout); - extern void cpu_init (void); extern void trap_init(void); extern void update_process_times(int user); @@ -383,6 +380,13 @@ extern signed long schedule_timeout_uninterruptible(signed long timeout); asmlinkage void schedule(void); extern void schedule_preempt_disabled(void); +extern long io_schedule_timeout(long timeout); + +static inline void io_schedule(void) +{ + io_schedule_timeout(MAX_SCHEDULE_TIMEOUT); +} + struct nsproxy; struct user_namespace; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 36eaa355e72c..f37985941b65 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4431,34 +4431,26 @@ EXPORT_SYMBOL_GPL(yield_to); * This task is about to go to sleep on IO. Increment rq->nr_iowait so * that process accounting knows that this is a task in IO wait state. */ -void __sched io_schedule(void) -{ - struct rq *rq = raw_rq(); - - delayacct_blkio_start(); - atomic_inc(&rq->nr_iowait); - blk_flush_plug(current); - current->in_iowait = 1; - schedule(); - current->in_iowait = 0; - atomic_dec(&rq->nr_iowait); - delayacct_blkio_end(); -} -EXPORT_SYMBOL(io_schedule); - long __sched io_schedule_timeout(long timeout) { - struct rq *rq = raw_rq(); + int old_iowait = current->in_iowait; + struct rq *rq; long ret; + current->in_iowait = 1; + if (old_iowait) + blk_schedule_flush_plug(current); + else + blk_flush_plug(current); + delayacct_blkio_start(); + rq = raw_rq(); atomic_inc(&rq->nr_iowait); - blk_flush_plug(current); - current->in_iowait = 1; ret = schedule_timeout(timeout); - current->in_iowait = 0; + current->in_iowait = old_iowait; atomic_dec(&rq->nr_iowait); delayacct_blkio_end(); + return ret; } EXPORT_SYMBOL(io_schedule_timeout); -- GitLab From 583611d145a04cbc1ff73fa02f0a331ca0f19051 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Fri, 8 May 2015 10:51:29 -0700 Subject: [PATCH 0684/5498] sched: always use blk_schedule_flush_plug in io_schedule_out block plug callback could sleep, so we introduce a parameter 'from_schedule' and corresponding drivers can use it to destinguish a schedule plug flush or a plug finish. Unfortunately io_schedule_out still uses blk_flush_plug(). This causes below output (Note, I added a might_sleep() in raid1_unplug to make it trigger faster, but the whole thing doesn't matter if I add might_sleep). In raid1/10, this can cause deadlock. This patch makes io_schedule_out always uses blk_schedule_flush_plug. This should only impact drivers (as far as I know, raid 1/10) which are sensitive to the 'from_schedule' parameter. [ 370.817949] ------------[ cut here ]------------ [ 370.817960] WARNING: CPU: 7 PID: 145 at ../kernel/sched/core.c:7306 __might_sleep+0x7f/0x90() [ 370.817969] do not call blocking ops when !TASK_RUNNING; state=2 set at [] prepare_to_wait+0x2f/0x90 [ 370.817971] Modules linked in: raid1 [ 370.817976] CPU: 7 PID: 145 Comm: kworker/u16:9 Tainted: G W 4.0.0+ #361 [ 370.817977] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140709_153802- 04/01/2014 [ 370.817983] Workqueue: writeback bdi_writeback_workfn (flush-9:1) [ 370.817985] ffffffff81cd83be ffff8800ba8cb298 ffffffff819dd7af 0000000000000001 [ 370.817988] ffff8800ba8cb2e8 ffff8800ba8cb2d8 ffffffff81051afc ffff8800ba8cb2c8 [ 370.817990] ffffffffa00061a8 000000000000041e 0000000000000000 ffff8800ba8cba28 [ 370.817993] Call Trace: [ 370.817999] [] dump_stack+0x4f/0x7b [ 370.818002] [] warn_slowpath_common+0x8c/0xd0 [ 370.818004] [] warn_slowpath_fmt+0x46/0x50 [ 370.818006] [] ? prepare_to_wait+0x2f/0x90 [ 370.818008] [] ? prepare_to_wait+0x2f/0x90 [ 370.818010] [] __might_sleep+0x7f/0x90 [ 370.818014] [] raid1_unplug+0xd3/0x170 [raid1] [ 370.818024] [] blk_flush_plug_list+0x8a/0x1e0 [ 370.818028] [] ? bit_wait+0x50/0x50 [ 370.818031] [] io_schedule_timeout+0x130/0x140 [ 370.818033] [] bit_wait_io+0x36/0x50 [ 370.818034] [] __wait_on_bit+0x65/0x90 [ 370.818041] [] ? ext4_read_block_bitmap_nowait+0xbc/0x630 [ 370.818043] [] ? bit_wait+0x50/0x50 [ 370.818045] [] out_of_line_wait_on_bit+0x72/0x80 [ 370.818047] [] ? autoremove_wake_function+0x40/0x40 [ 370.818050] [] __wait_on_buffer+0x44/0x50 [ 370.818053] [] ext4_wait_block_bitmap+0xe0/0xf0 [ 370.818058] [] ext4_mb_init_cache+0x206/0x790 [ 370.818062] [] ? lru_cache_add+0x1c/0x50 [ 370.818064] [] ext4_mb_init_group+0x11e/0x200 [ 370.818066] [] ext4_mb_load_buddy+0x341/0x360 [ 370.818068] [] ext4_mb_find_by_goal+0x93/0x2f0 [ 370.818070] [] ? ext4_mb_normalize_request+0x1e4/0x5b0 [ 370.818072] [] ext4_mb_regular_allocator+0x67/0x460 [ 370.818074] [] ? ext4_mb_normalize_request+0x1e4/0x5b0 [ 370.818076] [] ext4_mb_new_blocks+0x4cb/0x620 [ 370.818079] [] ext4_ext_map_blocks+0x4c6/0x14d0 [ 370.818081] [] ? ext4_es_lookup_extent+0x4e/0x290 [ 370.818085] [] ext4_map_blocks+0x14d/0x4f0 [ 370.818088] [] ext4_writepages+0x76d/0xe50 [ 370.818094] [] do_writepages+0x21/0x50 [ 370.818097] [] __writeback_single_inode+0x60/0x490 [ 370.818099] [] writeback_sb_inodes+0x2da/0x590 [ 370.818103] [] ? trylock_super+0x1b/0x50 [ 370.818105] [] ? trylock_super+0x1b/0x50 [ 370.818107] [] __writeback_inodes_wb+0x9f/0xd0 [ 370.818109] [] wb_writeback+0x34b/0x3c0 [ 370.818111] [] bdi_writeback_workfn+0x23f/0x550 [ 370.818116] [] process_one_work+0x1c8/0x570 [ 370.818117] [] ? process_one_work+0x14b/0x570 [ 370.818119] [] worker_thread+0x11b/0x470 [ 370.818121] [] ? process_one_work+0x570/0x570 [ 370.818124] [] kthread+0xf8/0x110 [ 370.818126] [] ? kthread_create_on_node+0x210/0x210 [ 370.818129] [] ret_from_fork+0x42/0x70 [ 370.818131] [] ? kthread_create_on_node+0x210/0x210 [ 370.818132] ---[ end trace 7b4deb71e68b6605 ]--- V2: don't change ->in_iowait Cc: NeilBrown Signed-off-by: Shaohua Li Reviewed-by: Jeff Moyer Signed-off-by: Jens Axboe --- kernel/sched/core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f37985941b65..5eae6758f402 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4438,10 +4438,7 @@ long __sched io_schedule_timeout(long timeout) long ret; current->in_iowait = 1; - if (old_iowait) - blk_schedule_flush_plug(current); - else - blk_flush_plug(current); + blk_schedule_flush_plug(current); delayacct_blkio_start(); rq = raw_rq(); -- GitLab From b879d0bc068e5f05ba63f567ded5fef8a0857cc7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 6 Jul 2017 12:24:49 -0700 Subject: [PATCH 0685/5498] f2fs, block_dump: give WRITE direction to submit_bio The block_dump in submit_bio uses rw, instead of bio->bi_rw. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- fs/f2fs/segment.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index e17fdee64c62..7be8d4231484 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -225,7 +225,7 @@ submit_io: trace_f2fs_submit_read_bio(sbi->sb, type, bio); else trace_f2fs_submit_write_bio(sbi->sb, type, bio); - submit_bio(0, bio); + submit_bio(bio_op(bio), bio); } static void __submit_merged_bio(struct f2fs_bio_info *io) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fc951ac05859..f53593d80384 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -809,7 +809,7 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, } if (bio) { - int ret = submit_bio_wait(0, bio); + int ret = submit_bio_wait(op, bio); bio_put(bio); if (ret) return ret; -- GitLab From deaeed5b8acdd10c388616bbc57416cf3db213ff Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 15 May 2017 10:45:08 -0700 Subject: [PATCH 0686/5498] f2fs: sanity check checkpoint segno and blkoff Make sure segno and blkoff read from raw image are valid. Cc: stable@vger.kernel.org Signed-off-by: Jin Qian [Jaegeuk Kim: adjust minor coding style] Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 375dfab4b157..e04be723448d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1509,6 +1509,8 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); unsigned int ovp_segments, reserved_segments; + unsigned int main_segs, blocks_per_seg; + int i; total = le32_to_cpu(raw_super->segment_count); fsmeta = le32_to_cpu(raw_super->segment_count_ckpt); @@ -1530,6 +1532,20 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) return 1; } + main_segs = le32_to_cpu(raw_super->segment_count_main); + blocks_per_seg = sbi->blocks_per_seg; + + for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) { + if (le32_to_cpu(ckpt->cur_node_segno[i]) >= main_segs || + le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg) + return 1; + } + for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) { + if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs || + le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg) + return 1; + } + if (unlikely(f2fs_cp_error(sbi))) { f2fs_msg(sbi->sb, KERN_ERR, "A bug case: need to run fsck"); return 1; -- GitLab From fde02efee8d83e85c6531f5d0d2aa8edcf720331 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 16 May 2017 13:20:16 -0700 Subject: [PATCH 0687/5498] f2fs: load inode's flag from disk This patch fixes missing inode flag loaded from disk, reported by Tom. [tom@localhost ~]$ sudo mount /dev/loop0 /mnt/ [tom@localhost ~]$ sudo chown tom:tom /mnt/ [tom@localhost ~]$ touch /mnt/testfile [tom@localhost ~]$ sudo chattr +i /mnt/testfile [tom@localhost ~]$ echo test > /mnt/testfile bash: /mnt/testfile: Operation not permitted [tom@localhost ~]$ rm /mnt/testfile rm: cannot remove '/mnt/testfile': Operation not permitted [tom@localhost ~]$ sudo umount /mnt/ [tom@localhost ~]$ sudo mount /dev/loop0 /mnt/ [tom@localhost ~]$ lsattr /mnt/testfile ----i-------------- /mnt/testfile [tom@localhost ~]$ echo test > /mnt/testfile [tom@localhost ~]$ rm /mnt/testfile [tom@localhost ~]$ sudo umount /mnt/ Cc: stable@vger.kernel.org Reported-by: Tom Yan Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 1 + fs/f2fs/inode.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 5e8ea4967ed5..8045d0133723 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1508,6 +1508,7 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) inode->i_ctime = current_time(inode); f2fs_set_inode_flags(inode); + f2fs_mark_inode_dirty_sync(inode, false); inode_unlock(inode); out: diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 20782deb51f3..133f9b702585 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -44,7 +44,6 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_DIRSYNC; inode_set_flags(inode, new_fl, S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); - f2fs_mark_inode_dirty_sync(inode, false); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) @@ -226,6 +225,7 @@ make_now: ret = -EIO; goto bad_inode; } + f2fs_set_inode_flags(inode); unlock_new_inode(inode); trace_f2fs_iget(inode); return inode; -- GitLab From 21a170cba3f890aaefd124169cfb79b721a97c73 Mon Sep 17 00:00:00 2001 From: Weichao Guo Date: Thu, 11 May 2017 04:28:00 +0800 Subject: [PATCH 0688/5498] f2fs: make sure f2fs_gc returns consistent errno By default, f2fs_gc returns -EINVAL in general error cases, e.g., no victim was selected. However, the default errno may be overwritten in two cases: gc_more and BG_GC -> FG_GC. We should return consistent errno in such cases. Signed-off-by: Weichao Guo Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 026522107ca3..965fbf5d0a2e 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -955,7 +955,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, { int gc_type = sync ? FG_GC : BG_GC; int sec_freed = 0; - int ret = -EINVAL; + int ret; struct cp_control cpc; unsigned int init_segno = segno; struct gc_inode_list gc_list = { @@ -965,8 +965,10 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, cpc.reason = __get_cp_reason(sbi); gc_more: - if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) + if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) { + ret = -EINVAL; goto stop; + } if (unlikely(f2fs_cp_error(sbi))) { ret = -EIO; goto stop; @@ -987,6 +989,7 @@ gc_more: gc_type = FG_GC; } + ret = -EINVAL; /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */ if (gc_type == BG_GC && !background) goto stop; -- GitLab From 0a8b160b890f29c848d6af4d1b63b69cee32a36a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 10 May 2017 11:23:36 -0700 Subject: [PATCH 0689/5498] f2fs: use f2fs_submit_page_bio for ra_meta_pages This patch avoids to use f2fs_submit_merged_bio for read, which was the only read case. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2693cda3e462..15e010f17cce 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -208,12 +208,10 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, } fio.page = page; - fio.old_blkaddr = fio.new_blkaddr; - f2fs_submit_page_mbio(&fio); + f2fs_submit_page_bio(&fio); f2fs_put_page(page, 0); } out: - f2fs_submit_merged_bio(sbi, META, READ); blk_finish_plug(&plug); return blkno - start; } -- GitLab From 90de9e01a761214ba5cab6cddeba34175ce909ff Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 10 May 2017 11:28:38 -0700 Subject: [PATCH 0690/5498] f2fs: remove unnecessary read cases in merged IO flow Merged IO flow doesn't need to care about read IOs. f2fs_submit_merged_bio -> f2fs_submit_merged_write f2fs_submit_merged_bios -> f2fs_submit_merged_writes f2fs_submit_merged_bio_cond -> f2fs_submit_merged_write_cond Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 14 +++++----- fs/f2fs/data.c | 55 ++++++++++++++++--------------------- fs/f2fs/f2fs.h | 12 ++++---- fs/f2fs/gc.c | 6 ++-- fs/f2fs/node.c | 11 ++++---- fs/f2fs/segment.c | 11 ++++---- fs/f2fs/super.c | 5 +--- include/trace/events/f2fs.h | 2 +- 8 files changed, 51 insertions(+), 65 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 15e010f17cce..0ae9241af473 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -31,7 +31,7 @@ void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io) set_ckpt_flags(sbi, CP_ERROR_FLAG); sbi->sb->s_flags |= MS_RDONLY; if (!end_io) - f2fs_flush_merged_bios(sbi); + f2fs_flush_merged_writes(sbi); } /* @@ -248,13 +248,13 @@ static int f2fs_write_meta_page(struct page *page, dec_page_count(sbi, F2FS_DIRTY_META); if (wbc->for_reclaim) - f2fs_submit_merged_bio_cond(sbi, page->mapping->host, - 0, page->index, META, WRITE); + f2fs_submit_merged_write_cond(sbi, page->mapping->host, + 0, page->index, META); unlock_page(page); if (unlikely(f2fs_cp_error(sbi))) - f2fs_submit_merged_bio(sbi, META, WRITE); + f2fs_submit_merged_write(sbi, META); return 0; @@ -357,7 +357,7 @@ continue_unlock: } stop: if (nwritten) - f2fs_submit_merged_bio(sbi, type, WRITE); + f2fs_submit_merged_write(sbi, type); blk_finish_plug(&plug); @@ -905,7 +905,7 @@ retry: * We should submit bio, since it exists several * wribacking dentry pages in the freeing inode. */ - f2fs_submit_merged_bio(sbi, DATA, WRITE); + f2fs_submit_merged_write(sbi, DATA); cond_resched(); } goto retry; @@ -1294,7 +1294,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops"); - f2fs_flush_merged_bios(sbi); + f2fs_flush_merged_writes(sbi); /* this is the case of multiple fstrims without any changes */ if (cpc->reason & CP_DISCARD) { diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 7be8d4231484..2944001a9d2f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -291,14 +291,12 @@ static bool has_merged_page(struct f2fs_sb_info *sbi, struct inode *inode, return ret; } -static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, +static void __f2fs_submit_merged_write(struct f2fs_sb_info *sbi, struct inode *inode, nid_t ino, pgoff_t idx, - enum page_type type, int rw) + enum page_type type) { enum page_type btype = PAGE_TYPE_OF_BIO(type); - struct f2fs_bio_info *io; - - io = is_read_io(rw) ? &sbi->read_io : &sbi->write_io[btype]; + struct f2fs_bio_info *io = &sbi->write_io[btype]; down_write(&io->io_rwsem); @@ -318,25 +316,24 @@ out: up_write(&io->io_rwsem); } -void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type, - int rw) +void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type) { - __f2fs_submit_merged_bio(sbi, NULL, 0, 0, type, rw); + __f2fs_submit_merged_write(sbi, NULL, 0, 0, type); } -void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi, +void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi, struct inode *inode, nid_t ino, pgoff_t idx, - enum page_type type, int rw) + enum page_type type) { if (has_merged_page(sbi, inode, ino, idx, type)) - __f2fs_submit_merged_bio(sbi, inode, ino, idx, type, rw); + __f2fs_submit_merged_write(sbi, inode, ino, idx, type); } -void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi) +void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi) { - f2fs_submit_merged_bio(sbi, DATA, WRITE); - f2fs_submit_merged_bio(sbi, NODE, WRITE); - f2fs_submit_merged_bio(sbi, META, WRITE); + f2fs_submit_merged_write(sbi, DATA); + f2fs_submit_merged_write(sbi, NODE); + f2fs_submit_merged_write(sbi, META); } /* @@ -368,16 +365,15 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) return 0; } -int f2fs_submit_page_mbio(struct f2fs_io_info *fio) +int f2fs_submit_page_write(struct f2fs_io_info *fio) { struct f2fs_sb_info *sbi = fio->sbi; enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); - struct f2fs_bio_info *io; - bool is_read = is_read_io(fio->op); + struct f2fs_bio_info *io = &sbi->write_io[btype]; struct page *bio_page; int err = 0; - io = is_read ? &sbi->read_io : &sbi->write_io[btype]; + f2fs_bug_on(sbi, is_read_io(fio->op)); if (fio->old_blkaddr != NEW_ADDR) verify_block_addr(sbi, fio->old_blkaddr); @@ -388,8 +384,7 @@ int f2fs_submit_page_mbio(struct f2fs_io_info *fio) /* set submitted = 1 as a return value */ fio->submitted = 1; - if (!is_read) - inc_page_count(sbi, WB_DATA_TYPE(bio_page)); + inc_page_count(sbi, WB_DATA_TYPE(bio_page)); down_write(&io->io_rwsem); @@ -402,12 +397,11 @@ alloc_new: if ((fio->type == DATA || fio->type == NODE) && fio->new_blkaddr & F2FS_IO_SIZE_MASK(sbi)) { err = -EAGAIN; - if (!is_read) - dec_page_count(sbi, WB_DATA_TYPE(bio_page)); + dec_page_count(sbi, WB_DATA_TYPE(bio_page)); goto out_fail; } io->bio = __bio_alloc(sbi, fio->new_blkaddr, - BIO_MAX_PAGES, is_read); + BIO_MAX_PAGES, false); io->fio = *fio; } @@ -421,7 +415,7 @@ alloc_new: f2fs_trace_ios(fio, 0); out_fail: up_write(&io->io_rwsem); - trace_f2fs_submit_page_mbio(fio->page, fio); + trace_f2fs_submit_page_write(fio->page, fio); return err; } @@ -1317,7 +1311,7 @@ retry_encrypt: /* flush pending IOs and wait for a while in the ENOMEM case */ if (PTR_ERR(fio->encrypted_page) == -ENOMEM) { - f2fs_flush_merged_bios(fio->sbi); + f2fs_flush_merged_writes(fio->sbi); congestion_wait(BLK_RW_ASYNC, HZ/50); gfp_flags |= __GFP_NOFAIL; goto retry_encrypt; @@ -1509,8 +1503,7 @@ out: ClearPageUptodate(page); if (wbc->for_reclaim) { - f2fs_submit_merged_bio_cond(sbi, inode, 0, page->index, - DATA, WRITE); + f2fs_submit_merged_write_cond(sbi, inode, 0, page->index, DATA); clear_inode_flag(inode, FI_HOT_DATA); remove_dirty_inode(inode); submitted = NULL; @@ -1521,7 +1514,7 @@ out: f2fs_balance_fs(sbi, need_balance_fs); if (unlikely(f2fs_cp_error(sbi))) { - f2fs_submit_merged_bio(sbi, DATA, WRITE); + f2fs_submit_merged_write(sbi, DATA); submitted = NULL; } @@ -1680,8 +1673,8 @@ continue_unlock: mapping->writeback_index = done_index; if (last_idx != ULONG_MAX) - f2fs_submit_merged_bio_cond(F2FS_M_SB(mapping), mapping->host, - 0, last_idx, DATA, WRITE); + f2fs_submit_merged_write_cond(F2FS_M_SB(mapping), mapping->host, + 0, last_idx, DATA); return ret; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0b89bdcc9e73..4c8f3fe3c306 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -978,7 +978,6 @@ struct f2fs_sb_info { struct f2fs_sm_info *sm_info; /* segment manager */ /* for bio operations */ - struct f2fs_bio_info read_io; /* for read bios */ struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ struct mutex wio_mutex[NODE + 1]; /* bio ordering for NODE/DATA */ int write_io_size_bits; /* Write IO size bits */ @@ -2425,14 +2424,13 @@ void destroy_checkpoint_caches(void); /* * data.c */ -void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type, - int rw); -void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi, +void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type); +void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi, struct inode *inode, nid_t ino, pgoff_t idx, - enum page_type type, int rw); -void f2fs_flush_merged_bios(struct f2fs_sb_info *sbi); + enum page_type type); +void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi); int f2fs_submit_page_bio(struct f2fs_io_info *fio); -int f2fs_submit_page_mbio(struct f2fs_io_info *fio); +int f2fs_submit_page_write(struct f2fs_io_info *fio); struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi, block_t blk_addr, struct bio *bio); int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 965fbf5d0a2e..67b87155bc48 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -670,7 +670,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, fio.op = REQ_OP_WRITE; fio.op_flags = REQ_SYNC; fio.new_blkaddr = newaddr; - f2fs_submit_page_mbio(&fio); + f2fs_submit_page_write(&fio); f2fs_update_data_blkaddr(&dn, newaddr); set_inode_flag(inode, FI_APPEND_WRITE); @@ -936,8 +936,8 @@ next: } if (gc_type == FG_GC) - f2fs_submit_merged_bio(sbi, - (type == SUM_TYPE_NODE) ? NODE : DATA, WRITE); + f2fs_submit_merged_write(sbi, + (type == SUM_TYPE_NODE) ? NODE : DATA); blk_finish_plug(&plug); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7d892aa5e53a..e7539a9099c3 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1379,15 +1379,15 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, up_read(&sbi->node_write); if (wbc->for_reclaim) { - f2fs_submit_merged_bio_cond(sbi, page->mapping->host, 0, - page->index, NODE, WRITE); + f2fs_submit_merged_write_cond(sbi, page->mapping->host, 0, + page->index, NODE); submitted = NULL; } unlock_page(page); if (unlikely(f2fs_cp_error(sbi))) { - f2fs_submit_merged_bio(sbi, NODE, WRITE); + f2fs_submit_merged_write(sbi, NODE); submitted = NULL; } if (submitted) @@ -1524,8 +1524,7 @@ continue_unlock: } out: if (last_idx != ULONG_MAX) - f2fs_submit_merged_bio_cond(sbi, NULL, ino, last_idx, - NODE, WRITE); + f2fs_submit_merged_write_cond(sbi, NULL, ino, last_idx, NODE); return ret ? -EIO: 0; } @@ -1631,7 +1630,7 @@ continue_unlock: } out: if (nwritten) - f2fs_submit_merged_bio(sbi, NODE, WRITE); + f2fs_submit_merged_write(sbi, NODE); return ret; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f53593d80384..98016465d32f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -328,8 +328,7 @@ static int __commit_inmem_pages(struct inode *inode, } if (last_idx != ULONG_MAX) - f2fs_submit_merged_bio_cond(sbi, inode, 0, last_idx, - DATA, WRITE); + f2fs_submit_merged_write_cond(sbi, inode, 0, last_idx, DATA); if (!err) __revoke_inmem_pages(inode, revoke_list, false, false); @@ -2229,7 +2228,7 @@ reallocate: &fio->new_blkaddr, sum, type); /* writeout dirty page into bdev */ - err = f2fs_submit_page_mbio(fio); + err = f2fs_submit_page_write(fio); if (err == -EAGAIN) { fio->old_blkaddr = fio->new_blkaddr; goto reallocate; @@ -2256,7 +2255,7 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) fio.op_flags &= ~REQ_META; set_page_writeback(page); - f2fs_submit_page_mbio(&fio); + f2fs_submit_page_write(&fio); } void write_node_page(unsigned int nid, struct f2fs_io_info *fio) @@ -2375,8 +2374,8 @@ void f2fs_wait_on_page_writeback(struct page *page, if (PageWriteback(page)) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); - f2fs_submit_merged_bio_cond(sbi, page->mapping->host, - 0, page->index, type, WRITE); + f2fs_submit_merged_write_cond(sbi, page->mapping->host, + 0, page->index, type); if (ordered) wait_on_page_writeback(page); else diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index e04be723448d..b4110bcc9d97 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -804,7 +804,7 @@ static void f2fs_put_super(struct super_block *sb) mutex_unlock(&sbi->umount_mutex); /* our cp_error case, we can wait for any writeback page */ - f2fs_flush_merged_bios(sbi); + f2fs_flush_merged_writes(sbi); iput(sbi->node_inode); iput(sbi->meta_inode); @@ -1958,9 +1958,6 @@ try_onemore: set_sbi_flag(sbi, SBI_POR_DOING); spin_lock_init(&sbi->stat_lock); - init_rwsem(&sbi->read_io.io_rwsem); - sbi->read_io.sbi = sbi; - sbi->read_io.bio = NULL; for (i = 0; i < NR_PAGE_TYPE; i++) { init_rwsem(&sbi->write_io[i].io_rwsem); sbi->write_io[i].sbi = sbi; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 83c7a11daafa..ce9aa66d8e7d 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -744,7 +744,7 @@ DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_bio, TP_CONDITION(page->mapping) ); -DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio, +DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_write, TP_PROTO(struct page *page, struct f2fs_io_info *fio), -- GitLab From 5b1a13d6817e334fdc51c282496a3215f20e12d2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 10 May 2017 14:19:54 -0700 Subject: [PATCH 0691/5498] f2fs: use fio instead of multiple parameters This patch just changes using fio instead of parameters. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 98016465d32f..9ad989cc93b6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2118,61 +2118,62 @@ static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) return false; } -static int __get_segment_type_2(struct page *page, enum page_type p_type) +static int __get_segment_type_2(struct f2fs_io_info *fio) { - if (p_type == DATA) + if (fio->type == DATA) return CURSEG_HOT_DATA; else return CURSEG_HOT_NODE; } -static int __get_segment_type_4(struct page *page, enum page_type p_type) +static int __get_segment_type_4(struct f2fs_io_info *fio) { - if (p_type == DATA) { - struct inode *inode = page->mapping->host; + if (fio->type == DATA) { + struct inode *inode = fio->page->mapping->host; if (S_ISDIR(inode->i_mode)) return CURSEG_HOT_DATA; else return CURSEG_COLD_DATA; } else { - if (IS_DNODE(page) && is_cold_node(page)) + if (IS_DNODE(fio->page) && is_cold_node(fio->page)) return CURSEG_WARM_NODE; else return CURSEG_COLD_NODE; } } -static int __get_segment_type_6(struct page *page, enum page_type p_type) +static int __get_segment_type_6(struct f2fs_io_info *fio) { - if (p_type == DATA) { - struct inode *inode = page->mapping->host; + if (fio->type == DATA) { + struct inode *inode = fio->page->mapping->host; - if (is_cold_data(page) || file_is_cold(inode)) + if (is_cold_data(fio->page) || file_is_cold(inode)) return CURSEG_COLD_DATA; if (is_inode_flag_set(inode, FI_HOT_DATA)) return CURSEG_HOT_DATA; return CURSEG_WARM_DATA; } else { - if (IS_DNODE(page)) - return is_cold_node(page) ? CURSEG_WARM_NODE : + if (IS_DNODE(fio->page)) + return is_cold_node(fio->page) ? CURSEG_WARM_NODE : CURSEG_HOT_NODE; return CURSEG_COLD_NODE; } } -static int __get_segment_type(struct page *page, enum page_type p_type) +static int __get_segment_type(struct f2fs_io_info *fio) { - switch (F2FS_P_SB(page)->active_logs) { + switch (fio->sbi->active_logs) { case 2: - return __get_segment_type_2(page, p_type); + return __get_segment_type_2(fio); case 4: - return __get_segment_type_4(page, p_type); + return __get_segment_type_4(fio); } + /* NR_CURSEG_TYPE(6) logs by default */ - f2fs_bug_on(F2FS_P_SB(page), - F2FS_P_SB(page)->active_logs != NR_CURSEG_TYPE); - return __get_segment_type_6(page, p_type); + f2fs_bug_on(fio->sbi, fio->sbi->active_logs != NR_CURSEG_TYPE); + + return __get_segment_type_6(fio); } void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, @@ -2218,7 +2219,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { - int type = __get_segment_type(fio->page, fio->type); + int type = __get_segment_type(fio); int err; if (fio->type == NODE || fio->type == DATA) -- GitLab From 8f84b2b92706407db684a3d61361e035eacaf7ff Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 10 May 2017 11:18:25 -0700 Subject: [PATCH 0692/5498] f2fs: split bio cache Split DATA/NODE type bio cache according to different temperature, so write IOs with the same temperature can be merged in corresponding bio cache as much as possible, otherwise, different temperature write IOs submitting into one bio cache will always cause split of bio. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: include/trace/events/f2fs.h --- fs/f2fs/data.c | 57 +++++++++++++++++++++++++------------ fs/f2fs/f2fs.h | 10 ++++++- fs/f2fs/gc.c | 2 ++ fs/f2fs/segment.c | 24 ++++++++++++---- fs/f2fs/segment.h | 4 +++ fs/f2fs/super.c | 21 ++++++++++++-- include/trace/events/f2fs.h | 11 ++++++- 7 files changed, 100 insertions(+), 29 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 2944001a9d2f..a37f8a0ac173 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -282,27 +282,32 @@ static bool has_merged_page(struct f2fs_sb_info *sbi, struct inode *inode, nid_t ino, pgoff_t idx, enum page_type type) { enum page_type btype = PAGE_TYPE_OF_BIO(type); - struct f2fs_bio_info *io = &sbi->write_io[btype]; - bool ret; + enum temp_type temp; + struct f2fs_bio_info *io; + bool ret = false; - down_read(&io->io_rwsem); - ret = __has_merged_page(io, inode, ino, idx); - up_read(&io->io_rwsem); + for (temp = HOT; temp < NR_TEMP_TYPE; temp++) { + io = sbi->write_io[btype] + temp; + + down_read(&io->io_rwsem); + ret = __has_merged_page(io, inode, ino, idx); + up_read(&io->io_rwsem); + + /* TODO: use HOT temp only for meta pages now. */ + if (ret || btype == META) + break; + } return ret; } static void __f2fs_submit_merged_write(struct f2fs_sb_info *sbi, - struct inode *inode, nid_t ino, pgoff_t idx, - enum page_type type) + enum page_type type, enum temp_type temp) { enum page_type btype = PAGE_TYPE_OF_BIO(type); - struct f2fs_bio_info *io = &sbi->write_io[btype]; + struct f2fs_bio_info *io = sbi->write_io[btype] + temp; down_write(&io->io_rwsem); - if (!__has_merged_page(io, inode, ino, idx)) - goto out; - /* change META to META_FLUSH in the checkpoint procedure */ if (type >= META_FLUSH) { io->fio.type = META_FLUSH; @@ -312,21 +317,38 @@ static void __f2fs_submit_merged_write(struct f2fs_sb_info *sbi, io->fio.op_flags |= WRITE_FLUSH | REQ_FUA; } __submit_merged_bio(io); -out: up_write(&io->io_rwsem); } +static void __submit_merged_write_cond(struct f2fs_sb_info *sbi, + struct inode *inode, nid_t ino, pgoff_t idx, + enum page_type type, bool force) +{ + enum temp_type temp; + + if (!force && !has_merged_page(sbi, inode, ino, idx, type)) + return; + + for (temp = HOT; temp < NR_TEMP_TYPE; temp++) { + + __f2fs_submit_merged_write(sbi, type, temp); + + /* TODO: use HOT temp only for meta pages now. */ + if (type >= META) + break; + } +} + void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type) { - __f2fs_submit_merged_write(sbi, NULL, 0, 0, type); + __submit_merged_write_cond(sbi, NULL, 0, 0, type, true); } void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi, struct inode *inode, nid_t ino, pgoff_t idx, enum page_type type) { - if (has_merged_page(sbi, inode, ino, idx, type)) - __f2fs_submit_merged_write(sbi, inode, ino, idx, type); + __submit_merged_write_cond(sbi, inode, ino, idx, type, false); } void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi) @@ -369,7 +391,7 @@ int f2fs_submit_page_write(struct f2fs_io_info *fio) { struct f2fs_sb_info *sbi = fio->sbi; enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); - struct f2fs_bio_info *io = &sbi->write_io[btype]; + struct f2fs_bio_info *io = sbi->write_io[btype] + fio->temp; struct page *bio_page; int err = 0; @@ -405,8 +427,7 @@ alloc_new: io->fio = *fio; } - if (bio_add_page(io->bio, bio_page, PAGE_SIZE, 0) < - PAGE_SIZE) { + if (bio_add_page(io->bio, bio_page, PAGE_SIZE, 0) < PAGE_SIZE) { __submit_merged_bio(io); goto alloc_new; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4c8f3fe3c306..7d68999f1348 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -891,9 +891,17 @@ enum page_type { OPU, }; +enum temp_type { + HOT = 0, /* must be zero for meta bio */ + WARM, + COLD, + NR_TEMP_TYPE, +}; + struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ + enum temp_type temp; /* contains HOT/WARM/COLD */ int op; /* contains REQ_OP_ */ int op_flags; /* req_flag_bits */ block_t new_blkaddr; /* new block address to be written */ @@ -978,7 +986,7 @@ struct f2fs_sb_info { struct f2fs_sm_info *sm_info; /* segment manager */ /* for bio operations */ - struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ + struct f2fs_bio_info *write_io[NR_PAGE_TYPE]; /* for write bios */ struct mutex wio_mutex[NODE + 1]; /* bio ordering for NODE/DATA */ int write_io_size_bits; /* Write IO size bits */ mempool_t *write_io_dummy; /* Dummy pages */ diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 67b87155bc48..e2b13558a915 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -586,6 +586,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .type = DATA, + .temp = COLD, .op = REQ_OP_READ, .op_flags = 0, .encrypted_page = NULL, @@ -712,6 +713,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .type = DATA, + .temp = COLD, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC, .old_blkaddr = NULL_ADDR, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9ad989cc93b6..b3992b90f44a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2163,17 +2163,29 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) static int __get_segment_type(struct f2fs_io_info *fio) { + int type = 0; + switch (fio->sbi->active_logs) { case 2: - return __get_segment_type_2(fio); + type = __get_segment_type_2(fio); + break; case 4: - return __get_segment_type_4(fio); + type = __get_segment_type_4(fio); + break; + case 6: + type = __get_segment_type_6(fio); + break; + default: + f2fs_bug_on(fio->sbi, true); } - /* NR_CURSEG_TYPE(6) logs by default */ - f2fs_bug_on(fio->sbi, fio->sbi->active_logs != NR_CURSEG_TYPE); - - return __get_segment_type_6(fio); + if (IS_HOT(type)) + fio->temp = HOT; + else if (IS_WARM(type)) + fio->temp = WARM; + else + fio->temp = COLD; + return type; } void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index f488f0e2d529..ebd7df66ba58 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -26,6 +26,10 @@ #define IS_DATASEG(t) ((t) <= CURSEG_COLD_DATA) #define IS_NODESEG(t) ((t) >= CURSEG_HOT_NODE) +#define IS_HOT(t) ((t) == CURSEG_HOT_NODE || (t) == CURSEG_HOT_DATA) +#define IS_WARM(t) ((t) == CURSEG_WARM_NODE || (t) == CURSEG_WARM_DATA) +#define IS_COLD(t) ((t) == CURSEG_COLD_NODE || (t) == CURSEG_COLD_DATA) + #define IS_CURSEG(sbi, seg) \ (((seg) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \ ((seg) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b4110bcc9d97..3c500342a3fe 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -755,6 +755,7 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); + int i; if (sbi->s_proc) { remove_proc_entry("segment_info", sbi->s_proc); @@ -826,6 +827,8 @@ static void f2fs_put_super(struct super_block *sb) if (sbi->write_io_dummy) mempool_destroy(sbi->write_io_dummy); destroy_percpu_info(sbi); + for (i = 0; i < NR_PAGE_TYPE; i++) + kfree(sbi->write_io[i]); kfree(sbi); } @@ -1959,9 +1962,19 @@ try_onemore: spin_lock_init(&sbi->stat_lock); for (i = 0; i < NR_PAGE_TYPE; i++) { - init_rwsem(&sbi->write_io[i].io_rwsem); - sbi->write_io[i].sbi = sbi; - sbi->write_io[i].bio = NULL; + int n = (i == META) ? 1: NR_TEMP_TYPE; + int j; + + sbi->write_io[i] = kmalloc(n * sizeof(struct f2fs_bio_info), + GFP_KERNEL); + if (!sbi->write_io[i]) + goto free_options; + + for (j = HOT; j < n; j++) { + init_rwsem(&sbi->write_io[i][j].io_rwsem); + sbi->write_io[i][j].sbi = sbi; + sbi->write_io[i][j].bio = NULL; + } } init_rwsem(&sbi->cp_rwsem); @@ -2208,6 +2221,8 @@ free_io_dummy: if (sbi->write_io_dummy) mempool_destroy(sbi->write_io_dummy); free_options: + for (i = 0; i < NR_PAGE_TYPE; i++) + kfree(sbi->write_io[i]); destroy_percpu_info(sbi); kfree(options); free_sb_buf: diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index ce9aa66d8e7d..78849b1b04f3 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -46,6 +46,12 @@ { REQ_META | REQ_PRIO, "(MP)" }, \ { 0, " \b" }) +#define show_block_temp(temp) \ + __print_symbolic(temp, \ + { HOT, "HOT" }, \ + { WARM, "WARM" }, \ + { COLD, "COLD" }) + #define show_data_type(type) \ __print_symbolic(type, \ { CURSEG_HOT_DATA, "Hot DATA" }, \ @@ -711,6 +717,7 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio, __field(block_t, new_blkaddr) __field(int, op) __field(int, op_flags) + __field(int, temp) __field(int, type) ), @@ -722,16 +729,18 @@ DECLARE_EVENT_CLASS(f2fs__submit_page_bio, __entry->new_blkaddr = fio->new_blkaddr; __entry->op = fio->op; __entry->op_flags = fio->op_flags; + __entry->temp = fio->temp; __entry->type = fio->type; ), TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, " - "oldaddr = 0x%llx, newaddr = 0x%llx rw = %s%s, type = %s", + "oldaddr = 0x%llx, newaddr = 0x%llx, rw = %s(%s), type = %s_%s", show_dev_ino(__entry), (unsigned long)__entry->index, (unsigned long long)__entry->old_blkaddr, (unsigned long long)__entry->new_blkaddr, show_bio_type(__entry->op, __entry->op_flags), + show_block_temp(__entry->temp), show_block_type(__entry->type)) ); -- GitLab From 6e634537a3ca9fab16891a94d984c55ba91dca02 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 12 May 2017 13:51:34 -0700 Subject: [PATCH 0693/5498] f2fs: avoid f2fs_lock_op for IPU writes Currently, if we do get_node_of_data before f2fs_lock_op, there may be dead lock as follows, where process A would be in infinite loop, and B will NOT be awaked. Process A(cp): Process B: f2fs_lock_all(sbi) get_dnode_of_data <---- lock dn.node_page flush_nodes f2fs_lock_op So, this patch adds f2fs_trylock_op to avoid f2fs_lock_op done by IPU. Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 44 +++++++++++++++++++++++++++++++------------- fs/f2fs/f2fs.h | 13 ++++++++++++- fs/f2fs/gc.c | 2 +- fs/f2fs/segment.c | 2 +- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a37f8a0ac173..128442d90d50 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1379,12 +1379,12 @@ int do_write_data_page(struct f2fs_io_info *fio) if (valid_ipu_blkaddr(fio)) { ipu_force = true; - fio->need_lock = false; + fio->need_lock = LOCK_DONE; goto got_it; } } - if (fio->need_lock) + if (fio->need_lock == LOCK_REQ) f2fs_lock_op(fio->sbi); err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE); @@ -1399,19 +1399,18 @@ int do_write_data_page(struct f2fs_io_info *fio) goto out_writepage; } got_it: - err = encrypt_one_page(fio); - if (err) - goto out_writepage; - - set_page_writeback(page); - /* * If current allocation needs SSR, * it had better in-place writes for updated data. */ if (ipu_force || (valid_ipu_blkaddr(fio) && need_inplace_update(fio))) { + err = encrypt_one_page(fio); + if (err) + goto out_writepage; + + set_page_writeback(page); f2fs_put_dnode(&dn); - if (fio->need_lock) + if (fio->need_lock == LOCK_REQ) f2fs_unlock_op(fio->sbi); err = rewrite_data_page(fio); trace_f2fs_do_write_data_page(fio->page, IPU); @@ -1419,6 +1418,20 @@ got_it: return err; } + if (fio->need_lock == LOCK_RETRY) { + if (!f2fs_trylock_op(fio->sbi)) { + err = -EAGAIN; + goto out_writepage; + } + fio->need_lock = LOCK_REQ; + } + + err = encrypt_one_page(fio); + if (err) + goto out_writepage; + + set_page_writeback(page); + /* LFS mode write path */ write_data_page(&dn, fio); trace_f2fs_do_write_data_page(page, OPU); @@ -1428,7 +1441,7 @@ got_it: out_writepage: f2fs_put_dnode(&dn); out: - if (fio->need_lock) + if (fio->need_lock == LOCK_REQ) f2fs_unlock_op(fio->sbi); return err; } @@ -1454,7 +1467,7 @@ static int __write_data_page(struct page *page, bool *submitted, .page = page, .encrypted_page = NULL, .submitted = false, - .need_lock = true, + .need_lock = LOCK_RETRY, }; trace_f2fs_writepage(page, DATA); @@ -1490,7 +1503,7 @@ write: /* Dentry blocks are controlled by checkpoint */ if (S_ISDIR(inode->i_mode)) { - fio.need_lock = false; + fio.need_lock = LOCK_DONE; err = do_write_data_page(&fio); goto done; } @@ -1509,8 +1522,13 @@ write: goto out; } - if (err == -EAGAIN) + if (err == -EAGAIN) { err = do_write_data_page(&fio); + if (err == -EAGAIN) { + fio.need_lock = LOCK_REQ; + err = do_write_data_page(&fio); + } + } if (F2FS_I(inode)->last_disk_size < psize) F2FS_I(inode)->last_disk_size = psize; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7d68999f1348..f9f84a8c623b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -898,6 +898,12 @@ enum temp_type { NR_TEMP_TYPE, }; +enum need_lock_type { + LOCK_REQ = 0, + LOCK_DONE, + LOCK_RETRY, +}; + struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ @@ -909,7 +915,7 @@ struct f2fs_io_info { struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ bool submitted; /* indicate IO submission */ - bool need_lock; /* indicate we need to lock cp_rwsem */ + int need_lock; /* indicate we need to lock cp_rwsem */ }; #define is_read_io(rw) ((rw) == READ) @@ -1378,6 +1384,11 @@ static inline void f2fs_lock_op(struct f2fs_sb_info *sbi) down_read(&sbi->cp_rwsem); } +static inline int f2fs_trylock_op(struct f2fs_sb_info *sbi) +{ + return down_read_trylock(&sbi->cp_rwsem); +} + static inline void f2fs_unlock_op(struct f2fs_sb_info *sbi) { up_read(&sbi->cp_rwsem); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e2b13558a915..14c71ac76062 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -719,7 +719,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, .old_blkaddr = NULL_ADDR, .page = page, .encrypted_page = NULL, - .need_lock = true, + .need_lock = LOCK_REQ, }; bool is_dirty = PageDirty(page); int err; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index b3992b90f44a..9f46090c8bb4 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -312,7 +312,7 @@ static int __commit_inmem_pages(struct inode *inode, fio.page = page; fio.old_blkaddr = NULL_ADDR; fio.encrypted_page = NULL; - fio.need_lock = false, + fio.need_lock = LOCK_DONE; err = do_write_data_page(&fio); if (err) { unlock_page(page); -- GitLab From 53acf4af9d8e7e7818e2253bfdb042851c4eae17 Mon Sep 17 00:00:00 2001 From: Hou Pengyang Date: Wed, 17 May 2017 02:48:48 +0000 Subject: [PATCH 0694/5498] f2fs: declare load_free_nid_bitmap static Signed-off-by: Hou Pengyang Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index e7539a9099c3..ad86efb3a171 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2561,7 +2561,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) return 0; } -inline void load_free_nid_bitmap(struct f2fs_sb_info *sbi) +static inline void load_free_nid_bitmap(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int i = 0; -- GitLab From d3358ba1e015abcb3a98450a0d76f2b767b48bca Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 17 May 2017 17:22:51 +0800 Subject: [PATCH 0695/5498] f2fs: add a new function get_ssr_cost This patch add a new method get_ssr_cost to select SSR segment more accurately. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 14c71ac76062..81392970fb2d 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -258,11 +258,20 @@ static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi, valid_blocks * 2 : valid_blocks; } +static unsigned int get_ssr_cost(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + struct seg_entry *se = get_seg_entry(sbi, segno); + + return se->ckpt_valid_blocks > se->valid_blocks ? + se->ckpt_valid_blocks : se->valid_blocks; +} + static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, unsigned int segno, struct victim_sel_policy *p) { if (p->alloc_mode == SSR) - return get_seg_entry(sbi, segno)->ckpt_valid_blocks; + return get_ssr_cost(sbi, segno); /* alloc_mode == LFS */ if (p->gc_mode == GC_GREEDY) -- GitLab From e6ac837ebd751227007d2ae97c9a57da6229cc5c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 17 May 2017 10:36:58 -0700 Subject: [PATCH 0696/5498] f2fs: try to freeze in gc and discard threads This allows to freeze gc and discard threads. Cc: stable@vger.kernel.org Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 9 +++++---- fs/f2fs/segment.c | 25 ++++++++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 81392970fb2d..570480571d72 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -32,13 +32,14 @@ static int gc_thread_func(void *data) wait_ms = gc_th->min_sleep_time; + set_freezable(); do { + wait_event_interruptible_timeout(*wq, + kthread_should_stop() || freezing(current), + msecs_to_jiffies(wait_ms)); + if (try_to_freeze()) continue; - else - wait_event_interruptible_timeout(*wq, - kthread_should_stop(), - msecs_to_jiffies(wait_ms)); if (kthread_should_stop()) break; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9f46090c8bb4..2eb0cb57c468 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "f2fs.h" #include "segment.h" @@ -1138,18 +1139,24 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; -repeat: - if (kthread_should_stop()) - return 0; - __issue_discard_cmd(sbi, true); - __wait_discard_cmd(sbi, true); + set_freezable(); - congestion_wait(BLK_RW_SYNC, HZ/50); + do { + wait_event_interruptible(*q, kthread_should_stop() || + freezing(current) || + atomic_read(&dcc->discard_cmd_cnt)); + if (try_to_freeze()) + continue; + if (kthread_should_stop()) + return 0; - wait_event_interruptible(*q, kthread_should_stop() || - atomic_read(&dcc->discard_cmd_cnt)); - goto repeat; + __issue_discard_cmd(sbi, true); + __wait_discard_cmd(sbi, true); + + congestion_wait(BLK_RW_SYNC, HZ/50); + } while (!kthread_should_stop()); + return 0; } #ifdef CONFIG_BLK_DEV_ZONED -- GitLab From bd5f5875e6f5567ce619fc2720197de65b3b01db Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Fri, 19 May 2017 15:06:12 +0800 Subject: [PATCH 0697/5498] f2fs: fix a bug caused by NULL extent tree Thread A: Thread B: -f2fs_remount -sbi->mount_opt.opt = 0; <--- -f2fs_iget -do_read_inode -f2fs_init_extent_tree -F2FS_I(inode)->extent_tree is NULL -default_options && parse_options -remount return <--- -f2fs_map_blocks -f2fs_lookup_extent_tree -f2fs_bug_on(sbi, !et); The same problem with f2fs_new_inode. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 2f98d7039701..ff2352a0ed15 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -320,7 +320,7 @@ static void __drop_largest_extent(struct inode *inode, } /* return true, if inode page is changed */ -bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) +static bool __f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct extent_tree *et; @@ -358,6 +358,16 @@ out: return false; } +bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) +{ + bool ret = __f2fs_init_extent_tree(inode, i_ext); + + if (!F2FS_I(inode)->extent_tree) + set_inode_flag(inode, FI_NO_EXTENT); + + return ret; +} + static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, struct extent_info *ei) { -- GitLab From c3a9919e9c99a3d4d820965cfc63bbd52fe3970e Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Fri, 19 May 2017 14:42:12 +0800 Subject: [PATCH 0698/5498] f2fs: combine huge num of discard rb tree consistence checks Came across a hungtask caused by huge number of rb tree traversing during adding discard addrs in cp. This patch combine these consistence checks and move it to discard thread. Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2eb0cb57c468..1e3496f9c8af 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -917,7 +917,6 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, dc->len = blkaddr - dc->lstart; dcc->undiscard_blks += dc->len; __relocate_discard_cmd(dcc, dc); - f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); modified = true; } @@ -927,16 +926,12 @@ static void __punch_discard_cmd(struct f2fs_sb_info *sbi, di.start + blkaddr + 1 - di.lstart, di.lstart + di.len - 1 - blkaddr, NULL, NULL); - f2fs_bug_on(sbi, - !__check_rb_tree_consistence(sbi, &dcc->root)); } else { dc->lstart++; dc->len--; dc->start++; dcc->undiscard_blks += dc->len; __relocate_discard_cmd(dcc, dc); - f2fs_bug_on(sbi, - !__check_rb_tree_consistence(sbi, &dcc->root)); } } } @@ -997,8 +992,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, prev_dc->di.len += di.len; dcc->undiscard_blks += di.len; __relocate_discard_cmd(dcc, prev_dc); - f2fs_bug_on(sbi, - !__check_rb_tree_consistence(sbi, &dcc->root)); di = prev_dc->di; tdc = prev_dc; merged = true; @@ -1014,16 +1007,12 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, __relocate_discard_cmd(dcc, next_dc); if (tdc) __remove_discard_cmd(sbi, tdc); - f2fs_bug_on(sbi, - !__check_rb_tree_consistence(sbi, &dcc->root)); merged = true; } if (!merged) { __insert_discard_tree(sbi, bdev, di.lstart, di.start, di.len, NULL, NULL); - f2fs_bug_on(sbi, - !__check_rb_tree_consistence(sbi, &dcc->root)); } next: prev_dc = next_dc; @@ -1062,6 +1051,8 @@ static void __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) int i, iter = 0; mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, + !__check_rb_tree_consistence(sbi, &dcc->root)); blk_start_plug(&plug); for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { pend_list = &dcc->pend_list[i]; -- GitLab From d40e5506c80e01c74c935b348e118f5b8d96f330 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 May 2017 23:37:00 +0800 Subject: [PATCH 0699/5498] f2fs: split wio_mutex Split wio_mutex to adjust different temperature bio cache. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/segment.c | 4 ++-- fs/f2fs/super.c | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f9f84a8c623b..bbc816666830 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -993,7 +993,8 @@ struct f2fs_sb_info { /* for bio operations */ struct f2fs_bio_info *write_io[NR_PAGE_TYPE]; /* for write bios */ - struct mutex wio_mutex[NODE + 1]; /* bio ordering for NODE/DATA */ + struct mutex wio_mutex[NR_PAGE_TYPE - 1][NR_TEMP_TYPE]; + /* bio ordering for NODE/DATA */ int write_io_size_bits; /* Write IO size bits */ mempool_t *write_io_dummy; /* Dummy pages */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1e3496f9c8af..227a15a27bdc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2233,7 +2233,7 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) int err; if (fio->type == NODE || fio->type == DATA) - mutex_lock(&fio->sbi->wio_mutex[fio->type]); + mutex_lock(&fio->sbi->wio_mutex[fio->type][fio->temp]); reallocate: allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, &fio->new_blkaddr, sum, type); @@ -2246,7 +2246,7 @@ reallocate: } if (fio->type == NODE || fio->type == DATA) - mutex_unlock(&fio->sbi->wio_mutex[fio->type]); + mutex_unlock(&fio->sbi->wio_mutex[fio->type][fio->temp]); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3c500342a3fe..96a11d369803 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1559,7 +1559,7 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) static void init_sb_info(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = sbi->raw_super; - int i; + int i, j; sbi->log_sectors_per_block = le32_to_cpu(raw_super->log_sectors_per_block); @@ -1591,8 +1591,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sbi->s_list); mutex_init(&sbi->umount_mutex); - mutex_init(&sbi->wio_mutex[NODE]); - mutex_init(&sbi->wio_mutex[DATA]); + for (i = 0; i < NR_PAGE_TYPE - 1; i++) + for (j = HOT; j < NR_TEMP_TYPE; j++) + mutex_init(&sbi->wio_mutex[i][j]); spin_lock_init(&sbi->cp_lock); } -- GitLab From da4f61b9a73a7762acfbe969777254b6191e8a03 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 May 2017 23:37:01 +0800 Subject: [PATCH 0700/5498] f2fs: introduce io_list for serialize data/node IOs Serialize data/node IOs by using fifo list instead of mutex lock, it will help to enhance concurrency of f2fs, meanwhile keeping LFS IO semantics. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 + fs/f2fs/data.c | 24 ++++++++++++++++++++---- fs/f2fs/f2fs.h | 7 ++++++- fs/f2fs/gc.c | 3 ++- fs/f2fs/segment.c | 22 +++++++++++++++------- fs/f2fs/super.c | 2 ++ 6 files changed, 46 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 0ae9241af473..065100552bcd 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -163,6 +163,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, .op = REQ_OP_READ, .op_flags = sync ? (REQ_META | REQ_PRIO) : REQ_RAHEAD, .encrypted_page = NULL, + .in_list = false, }; struct blk_plug plug; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 128442d90d50..2bed5ca9c814 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -397,6 +397,20 @@ int f2fs_submit_page_write(struct f2fs_io_info *fio) f2fs_bug_on(sbi, is_read_io(fio->op)); + down_write(&io->io_rwsem); +next: + if (fio->in_list) { + spin_lock(&io->io_lock); + if (list_empty(&io->io_list)) { + spin_unlock(&io->io_lock); + goto out_fail; + } + fio = list_first_entry(&io->io_list, + struct f2fs_io_info, list); + list_del(&fio->list); + spin_unlock(&io->io_lock); + } + if (fio->old_blkaddr != NEW_ADDR) verify_block_addr(sbi, fio->old_blkaddr); verify_block_addr(sbi, fio->new_blkaddr); @@ -408,8 +422,6 @@ int f2fs_submit_page_write(struct f2fs_io_info *fio) inc_page_count(sbi, WB_DATA_TYPE(bio_page)); - down_write(&io->io_rwsem); - if (io->bio && (io->last_block_in_bio != fio->new_blkaddr - 1 || (io->fio.op != fio->op || io->fio.op_flags != fio->op_flags) || !__same_bdev(sbi, fio->new_blkaddr, io->bio))) @@ -434,9 +446,13 @@ alloc_new: io->last_block_in_bio = fio->new_blkaddr; f2fs_trace_ios(fio, 0); + + trace_f2fs_submit_page_write(fio->page, fio); + + if (fio->in_list) + goto next; out_fail: up_write(&io->io_rwsem); - trace_f2fs_submit_page_write(fio->page, fio); return err; } @@ -749,7 +765,7 @@ alloc: set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); allocate_data_block(sbi, NULL, dn->data_blkaddr, &dn->data_blkaddr, - &sum, CURSEG_WARM_DATA); + &sum, CURSEG_WARM_DATA, NULL, false); set_data_blkaddr(dn); /* update i_size */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index bbc816666830..0c7ce01bc9b9 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -914,8 +914,10 @@ struct f2fs_io_info { block_t old_blkaddr; /* old block address before Cow */ struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ + struct list_head list; /* serialize IOs */ bool submitted; /* indicate IO submission */ int need_lock; /* indicate we need to lock cp_rwsem */ + bool in_list; /* indicate fio is in io_list */ }; #define is_read_io(rw) ((rw) == READ) @@ -925,6 +927,8 @@ struct f2fs_bio_info { sector_t last_block_in_bio; /* last block number */ struct f2fs_io_info fio; /* store buffered io info. */ struct rw_semaphore io_rwsem; /* blocking op for bio */ + spinlock_t io_lock; /* serialize DATA/NODE IOs */ + struct list_head io_list; /* track fios */ }; #define FDEV(i) (sbi->devs[i]) @@ -2394,7 +2398,8 @@ void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, bool recover_newaddr); void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, block_t old_blkaddr, block_t *new_blkaddr, - struct f2fs_summary *sum, int type); + struct f2fs_summary *sum, int type, + struct f2fs_io_info *fio, bool add_list); void f2fs_wait_on_page_writeback(struct page *page, enum page_type type, bool ordered); void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 570480571d72..fa3d2e2df8e7 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -600,6 +600,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, .op = REQ_OP_READ, .op_flags = 0, .encrypted_page = NULL, + .in_list = false, }; struct dnode_of_data dn; struct f2fs_summary sum; @@ -643,7 +644,7 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr; allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr, - &sum, CURSEG_COLD_DATA); + &sum, CURSEG_COLD_DATA, NULL, false); fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr, FGP_LOCK | FGP_CREAT, GFP_NOFS); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 227a15a27bdc..8f4df9ea6373 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2188,7 +2188,8 @@ static int __get_segment_type(struct f2fs_io_info *fio) void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, block_t old_blkaddr, block_t *new_blkaddr, - struct f2fs_summary *sum, int type) + struct f2fs_summary *sum, int type, + struct f2fs_io_info *fio, bool add_list) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, type); @@ -2224,6 +2225,17 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, if (page && IS_NODESEG(type)) fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); + if (add_list) { + struct f2fs_bio_info *io; + + INIT_LIST_HEAD(&fio->list); + fio->in_list = true; + io = sbi->write_io[fio->type] + fio->temp; + spin_lock(&io->io_lock); + list_add_tail(&fio->list, &io->io_list); + spin_unlock(&io->io_lock); + } + mutex_unlock(&curseg->curseg_mutex); } @@ -2232,11 +2244,9 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) int type = __get_segment_type(fio); int err; - if (fio->type == NODE || fio->type == DATA) - mutex_lock(&fio->sbi->wio_mutex[fio->type][fio->temp]); reallocate: allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, - &fio->new_blkaddr, sum, type); + &fio->new_blkaddr, sum, type, fio, true); /* writeout dirty page into bdev */ err = f2fs_submit_page_write(fio); @@ -2244,9 +2254,6 @@ reallocate: fio->old_blkaddr = fio->new_blkaddr; goto reallocate; } - - if (fio->type == NODE || fio->type == DATA) - mutex_unlock(&fio->sbi->wio_mutex[fio->type][fio->temp]); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) @@ -2260,6 +2267,7 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) .new_blkaddr = page->index, .page = page, .encrypted_page = NULL, + .in_list = false, }; if (unlikely(page->index >= MAIN_BLKADDR(sbi))) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 96a11d369803..af53de898b30 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1975,6 +1975,8 @@ try_onemore: init_rwsem(&sbi->write_io[i][j].io_rwsem); sbi->write_io[i][j].sbi = sbi; sbi->write_io[i][j].bio = NULL; + spin_lock_init(&sbi->write_io[i][j].io_lock); + INIT_LIST_HEAD(&sbi->write_io[i][j].io_list); } } -- GitLab From 0ef09c22387b73930170db46d62ef69dea350843 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 May 2017 23:46:43 +0800 Subject: [PATCH 0701/5498] f2fs: show more info if fail to issue discard Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 8f4df9ea6373..3000cb286890 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -741,7 +741,8 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, if (dc->error) f2fs_msg(sbi->sb, KERN_INFO, - "Issue discard failed, ret: %d", dc->error); + "Issue discard(%u, %u, %u) failed, ret: %d", + dc->lstart, dc->start, dc->len, dc->error); __detach_discard_cmd(dcc, dc); } -- GitLab From ae941f0495e0730b3cc155a8a97c339046892dda Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 May 2017 23:46:44 +0800 Subject: [PATCH 0702/5498] f2fs: wake up all waiters in f2fs_submit_discard_endio There could be more than one waiter waiting discard IO completion, so we need use complete_all() instead of complete() in f2fs_submit_discard_endio to avoid hungtask. Fixes: ec9895add2c5 ("f2fs: don't hold cmd_lock during waiting discard command") Cc: Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3000cb286890..b4576f7a759d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -752,7 +752,7 @@ static void f2fs_submit_discard_endio(struct bio *bio, int err) dc->error = err; dc->state = D_DONE; - complete(&dc->wait); + complete_all(&dc->wait); bio_put(bio); } -- GitLab From 15b63ce2d2758164f6d3ad8bbbb33ed659151373 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 19 May 2017 23:46:45 +0800 Subject: [PATCH 0703/5498] f2fs: wait discard IO completion without cmd_lock held Wait discard IO completion outside cmd_lock to avoid long latency of holding cmd_lock in IO busy scenario. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index b4576f7a759d..431a6ea472b1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1076,17 +1076,34 @@ static void __wait_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *wait_list = &(dcc->wait_list); struct discard_cmd *dc, *tmp; + bool need_wait; + +next: + need_wait = false; mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, wait_list, list) { - if (!wait_cond || dc->state == D_DONE) { - if (dc->ref) - continue; + if (!wait_cond || (dc->state == D_DONE && !dc->ref)) { wait_for_completion_io(&dc->wait); __remove_discard_cmd(sbi, dc); + } else { + dc->ref++; + need_wait = true; + break; } } mutex_unlock(&dcc->cmd_lock); + + if (need_wait) { + wait_for_completion_io(&dc->wait); + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, dc->state != D_DONE); + dc->ref--; + if (!dc->ref) + __remove_discard_cmd(sbi, dc); + mutex_unlock(&dcc->cmd_lock); + goto next; + } } /* This should be covered by global mutex, &sit_i->sentry_lock */ -- GitLab From f9b4b36b70af93818807804a0ce867faab30442e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 22 May 2017 17:39:43 -0700 Subject: [PATCH 0704/5498] f2fs: don't bother checking for encryption key in ->mmap() Since only an open file can be mmap'ed, and we only allow open()ing an encrypted file when its key is available, there is no need to check for the key again before permitting each mmap(). This f2fs copy of this code was also broken in that it wouldn't actually have failed if the key was in fact unavailable. Signed-off-by: Eric Biggers Reviewed-by: David Gstir Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 8045d0133723..1d55c071c039 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -417,14 +417,6 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) struct inode *inode = file_inode(file); int err; - if (f2fs_encrypted_inode(inode)) { - err = fscrypt_get_encryption_info(inode); - if (err) - return 0; - if (!f2fs_encrypted_inode(inode)) - return -ENOKEY; - } - /* we don't need to use inline_data strictly */ err = f2fs_convert_inline_inode(inode); if (err) -- GitLab From 65e3c61ab3e299a115e286cc6d8a388be064c95a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 22 May 2017 17:39:45 -0700 Subject: [PATCH 0705/5498] f2fs: don't bother checking for encryption key in ->write_iter() Since only an open file can be written to, and we only allow open()ing an encrypted file when its key is available, there is no need to check for the key again before permitting each ->write_iter(). This code was also broken in that it wouldn't actually have failed if the key was in fact unavailable. Signed-off-by: Eric Biggers Reviewed-by: David Gstir Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 1d55c071c039..29e7f09bb0af 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2336,11 +2336,6 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct blk_plug plug; ssize_t ret; - if (f2fs_encrypted_inode(inode) && - !fscrypt_has_encryption_key(inode) && - fscrypt_get_encryption_info(inode)) - return -EACCES; - inode_lock(inode); ret = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (!ret) { -- GitLab From cc190f0fabcb3a4cd11127bbd5afe23232a30028 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 26 May 2017 17:04:40 +0900 Subject: [PATCH 0706/5498] f2fs: Do not issue small discards in LFS mode clear_prefree_segments() issues small discards after discarding full segments. These small discards may not be section aligned, so not zone aligned on a zoned block device, causing __f2fs_iissue_discard_zone() to fail. Fix this by not issuing small discards for a volume mounted with the BLKZONED feature enabled. Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 431a6ea472b1..f1cc7cc40ff3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1416,7 +1416,8 @@ find_next: sbi->blocks_per_seg, cur_pos); len = next_pos - cur_pos; - if (force && len < cpc->trim_minlen) + if (f2fs_sb_mounted_blkzoned(sbi->sb) || + (force && len < cpc->trim_minlen)) goto skip; f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, -- GitLab From 986738ddf725ed67989ead643b351ee5e7c87a1a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 1 Jun 2017 15:39:27 -0700 Subject: [PATCH 0707/5498] f2fs: remove false-positive bug_on For example, f2fs_create - new_node_page is failed - handle_failed_inode - skip to add it into orphan list, since ni.blk_addr == NULL_ADDR : set_inode_flag(inode, FI_FREE_NID) f2fs_evict_inode - EIO due to fault injection - f2fs_bug_on() is triggered So, we don't need to call f2fs_bug_on in this case. Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 133f9b702585..9166f076d688 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -425,9 +425,10 @@ no_delete: if (is_inode_flag_set(inode, FI_FREE_NID)) { alloc_nid_failed(sbi, inode->i_ino); clear_inode_flag(inode, FI_FREE_NID); + } else { + f2fs_bug_on(sbi, err && + !exist_written_data(sbi, inode->i_ino, ORPHAN_INO)); } - f2fs_bug_on(sbi, err && - !exist_written_data(sbi, inode->i_ino, ORPHAN_INO)); out_clear: fscrypt_put_encryption_info(inode, NULL); clear_inode(inode); -- GitLab From e59bda11dc4fad2ef3f8fdac1f5407df677118c3 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Thu, 1 Jun 2017 16:50:10 +0800 Subject: [PATCH 0708/5498] f2fs: remove the unnecessary cast for PTR_ERR It's not necessary to specify 'int' casting for PTR_ERR. Signed-off-by: Zhang Shengju Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 8f487692c21f..a140c5e3dc54 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -233,7 +233,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size); if (IS_ERR(value)) { clear_inode_flag(inode, FI_ACL_MODE); - return (int)PTR_ERR(value); + return PTR_ERR(value); } } -- GitLab From 30a97dec2de75a6a48038b603614ee9ad766ef7f Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 1 Jun 2017 16:43:51 +0800 Subject: [PATCH 0709/5498] f2fs: fix a panic caused by NULL flush_cmd_control Mount fs with option noflush_merge, boot failed for illegal address fcc in function f2fs_issue_flush: if (!test_opt(sbi, FLUSH_MERGE)) { ret = submit_flush_wait(sbi); atomic_inc(&fcc->issued_flush); -> Here, fcc illegal return ret; } Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f1cc7cc40ff3..5996a1dc92bd 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -566,6 +566,9 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) init_waitqueue_head(&fcc->flush_wait_queue); init_llist_head(&fcc->issue_list); SM_I(sbi)->fcc_info = fcc; + if (!test_opt(sbi, FLUSH_MERGE)) + return err; + init_thread: fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); @@ -3319,7 +3322,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sm_info->sit_entry_set); - if (test_opt(sbi, FLUSH_MERGE) && !f2fs_readonly(sbi->sb)) { + if (!f2fs_readonly(sbi->sb)) { err = create_flush_cmd_control(sbi); if (err) return err; -- GitLab From d3fbc0fb57f772240011fadd8119139af7544751 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 1 Jun 2017 11:18:30 -0700 Subject: [PATCH 0710/5498] f2fs: sanity check size of nat and sit cache Make sure number of entires doesn't exceed max journal size. Cc: stable@vger.kernel.org Signed-off-by: Jin Qian Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5996a1dc92bd..cdcbc9cf21a1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2574,6 +2574,8 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) static int restore_curseg_summaries(struct f2fs_sb_info *sbi) { + struct f2fs_journal *sit_j = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal; + struct f2fs_journal *nat_j = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal; int type = CURSEG_HOT_DATA; int err; @@ -2600,6 +2602,11 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) return err; } + /* sanity check for summary blocks */ + if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES || + sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) + return -EINVAL; + return 0; } -- GitLab From 6746cca4bf471428c13ae6b07c6542a656f353ca Mon Sep 17 00:00:00 2001 From: Fan Li Date: Fri, 2 Jun 2017 15:45:42 +0800 Subject: [PATCH 0711/5498] f2fs: simplify the way of calulating next nat address The index of segment which the next nat block is in has only one different bit than the current one, so to get the next nat address, we can simply alter that one bit. Signed-off-by: Fan Li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 558048e33cf9..bb53e9955ff2 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -224,11 +224,7 @@ static inline pgoff_t next_nat_addr(struct f2fs_sb_info *sbi, struct f2fs_nm_info *nm_i = NM_I(sbi); block_addr -= nm_i->nat_blkaddr; - if ((block_addr >> sbi->log_blocks_per_seg) % 2) - block_addr -= sbi->blocks_per_seg; - else - block_addr += sbi->blocks_per_seg; - + block_addr ^= 1 << sbi->log_blocks_per_seg; return block_addr + nm_i->nat_blkaddr; } -- GitLab From dcf04354dc8c7a524f62e7c4fcc818a9e34c8d1f Mon Sep 17 00:00:00 2001 From: Qiuyang Sun Date: Thu, 18 May 2017 11:06:45 +0800 Subject: [PATCH 0712/5498] f2fs: dax: fix races between page faults and truncating pages Currently in F2FS, page faults and operations that truncate the pagecahe or data blocks, are completely unsynchronized. This can result in page fault faulting in a page into a range that we are changing after truncating, and thus we can end up with a page mapped to disk blocks that will be shortly freed. Filesystem corruption will shortly follow. This patch fixes the problem by creating new rw semaphore i_mmap_sem in f2fs_inode_info and grab it for functions removing blocks from extent tree and for read over page faults. The mechanism is similar to that in ext4. Signed-off-by: Qiuyang Sun Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/file.c --- fs/f2fs/data.c | 2 ++ fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 49 ++++++++++++++++++++++++++++++++++++++++--------- fs/f2fs/super.c | 1 + 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 2bed5ca9c814..7af87b486c0f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1797,8 +1797,10 @@ static void f2fs_write_failed(struct address_space *mapping, loff_t to) loff_t i_size = i_size_read(inode); if (to > i_size) { + down_write(&F2FS_I(inode)->i_mmap_sem); truncate_pagecache(inode, i_size); truncate_blocks(inode, i_size, true); + up_write(&F2FS_I(inode)->i_mmap_sem); } } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0c7ce01bc9b9..04432df56f82 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -618,6 +618,7 @@ struct f2fs_inode_info { struct mutex inmem_lock; /* lock for inmemory pages */ struct extent_tree *extent_tree; /* cached extent_tree entry */ struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ + struct rw_semaphore i_mmap_sem; }; static inline void get_extent_info(struct extent_info *ext, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 29e7f09bb0af..6c5c7dd4cd94 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -34,6 +34,19 @@ #include "trace.h" #include +static int f2fs_filemap_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + struct inode *inode = file_inode(vma->vm_file); + int err; + + down_read(&F2FS_I(inode)->i_mmap_sem); + err = filemap_fault(vma, vmf); + up_read(&F2FS_I(inode)->i_mmap_sem); + + return err; +} + static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { @@ -61,13 +74,14 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, f2fs_balance_fs(sbi, dn.node_changed); file_update_time(vma->vm_file); + down_read(&F2FS_I(inode)->i_mmap_sem); lock_page(page); if (unlikely(page->mapping != inode->i_mapping || page_offset(page) > i_size_read(inode) || !PageUptodate(page))) { unlock_page(page); err = -EFAULT; - goto out; + goto out_sem; } /* @@ -96,6 +110,8 @@ mapped: if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); +out_sem: + up_read(&F2FS_I(inode)->i_mmap_sem); out: sb_end_pagefault(inode->i_sb); f2fs_update_time(sbi, REQ_TIME); @@ -103,7 +119,7 @@ out: } static const struct vm_operations_struct f2fs_file_vm_ops = { - .fault = filemap_fault, + .fault = f2fs_filemap_fault, .map_pages = filemap_map_pages, .page_mkwrite = f2fs_vm_page_mkwrite, }; @@ -681,8 +697,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) return -EACCES; if (attr->ia_size <= i_size_read(inode)) { + down_write(&F2FS_I(inode)->i_mmap_sem); truncate_setsize(inode, attr->ia_size); err = f2fs_truncate(inode); + up_write(&F2FS_I(inode)->i_mmap_sem); if (err) return err; } else { @@ -690,7 +708,9 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) * do not trim all blocks after i_size if target size is * larger than i_size. */ + down_write(&F2FS_I(inode)->i_mmap_sem); truncate_setsize(inode, attr->ia_size); + up_write(&F2FS_I(inode)->i_mmap_sem); /* should convert inline inode here */ if (!f2fs_may_inline_data(inode)) { @@ -836,12 +856,14 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) blk_start = (loff_t)pg_start << PAGE_SHIFT; blk_end = (loff_t)pg_end << PAGE_SHIFT; + down_write(&F2FS_I(inode)->i_mmap_sem); truncate_inode_pages_range(mapping, blk_start, blk_end - 1); f2fs_lock_op(sbi); ret = truncate_hole(inode, pg_start, pg_end); f2fs_unlock_op(sbi); + up_write(&F2FS_I(inode)->i_mmap_sem); } } @@ -1080,16 +1102,17 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) pg_start = offset >> PAGE_SHIFT; pg_end = (offset + len) >> PAGE_SHIFT; + down_write(&F2FS_I(inode)->i_mmap_sem); /* write out all dirty pages from offset */ ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); if (ret) - return ret; + goto out; truncate_pagecache(inode, offset); ret = f2fs_do_collapse(inode, pg_start, pg_end); if (ret) - return ret; + goto out; /* write out all moved pages, if possible */ filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); @@ -1102,6 +1125,8 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); +out: + up_write(&F2FS_I(inode)->i_mmap_sem); return ret; } @@ -1166,9 +1191,10 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, if (ret) return ret; + down_write(&F2FS_I(inode)->i_mmap_sem); ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1); if (ret) - return ret; + goto out_sem; truncate_pagecache_range(inode, offset, offset + len - 1); @@ -1182,7 +1208,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, ret = fill_zero(inode, pg_start, off_start, off_end - off_start); if (ret) - return ret; + goto out_sem; new_size = max_t(loff_t, new_size, offset + len); } else { @@ -1190,7 +1216,7 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, ret = fill_zero(inode, pg_start++, off_start, PAGE_SIZE - off_start); if (ret) - return ret; + goto out_sem; new_size = max_t(loff_t, new_size, (loff_t)pg_start << PAGE_SHIFT); @@ -1239,6 +1265,8 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, out: if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) f2fs_i_size_write(inode, new_size); +out_sem: + up_write(&F2FS_I(inode)->i_mmap_sem); return ret; } @@ -1268,14 +1296,15 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(sbi, true); + down_write(&F2FS_I(inode)->i_mmap_sem); ret = truncate_blocks(inode, i_size_read(inode), true); if (ret) - return ret; + goto out; /* write out all dirty pages from offset */ ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); if (ret) - return ret; + goto out; truncate_pagecache(inode, offset); @@ -1304,6 +1333,8 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); +out: + up_write(&F2FS_I(inode)->i_mmap_sem); return ret; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index af53de898b30..b53f4bce15aa 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -614,6 +614,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) mutex_init(&fi->inmem_lock); init_rwsem(&fi->dio_rwsem[READ]); init_rwsem(&fi->dio_rwsem[WRITE]); + init_rwsem(&fi->i_mmap_sem); /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; -- GitLab From 90299d194b02a0c068888290235420705d4e3de0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 5 Jun 2017 18:29:06 +0800 Subject: [PATCH 0713/5498] f2fs: introduce __wait_one_discard_bio In order to avoid copied codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index cdcbc9cf21a1..ecc1b9640bd7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1074,6 +1074,20 @@ out: mutex_unlock(&dcc->cmd_lock); } +static void __wait_one_discard_bio(struct f2fs_sb_info *sbi, + struct discard_cmd *dc) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + + wait_for_completion_io(&dc->wait); + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, dc->state != D_DONE); + dc->ref--; + if (!dc->ref) + __remove_discard_cmd(sbi, dc); + mutex_unlock(&dcc->cmd_lock); +} + static void __wait_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; @@ -1098,13 +1112,7 @@ next: mutex_unlock(&dcc->cmd_lock); if (need_wait) { - wait_for_completion_io(&dc->wait); - mutex_lock(&dcc->cmd_lock); - f2fs_bug_on(sbi, dc->state != D_DONE); - dc->ref--; - if (!dc->ref) - __remove_discard_cmd(sbi, dc); - mutex_unlock(&dcc->cmd_lock); + __wait_one_discard_bio(sbi, dc); goto next; } } @@ -1128,15 +1136,8 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) } mutex_unlock(&dcc->cmd_lock); - if (need_wait) { - wait_for_completion_io(&dc->wait); - mutex_lock(&dcc->cmd_lock); - f2fs_bug_on(sbi, dc->state != D_DONE); - dc->ref--; - if (!dc->ref) - __remove_discard_cmd(sbi, dc); - mutex_unlock(&dcc->cmd_lock); - } + if (need_wait) + __wait_one_discard_bio(sbi, dc); } /* This comes from f2fs_put_super */ -- GitLab From ba30c770e9d32afd7d2516c611ac4173e26bc047 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 5 Jun 2017 18:29:07 +0800 Subject: [PATCH 0714/5498] f2fs: add f2fs_bug_on in __remove_discard_cmd Recently, discard related codes have changed a lot, so add f2fs_bug_on to detect potential bug. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ecc1b9640bd7..db0f04d7ac84 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -739,6 +739,8 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + f2fs_bug_on(sbi, dc->ref); + if (dc->error == -EOPNOTSUPP) dc->error = 0; -- GitLab From c73c2e920729b1ccbc4eb99bd4e86119670d720a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 5 Jun 2017 18:29:08 +0800 Subject: [PATCH 0715/5498] f2fs: don't track newly allocated nat entry in list We will never persist newly allocated nat entries during checkpoint(), so we don't need to track such nat entries in nat dirty list in order to avoid: - more latency during traversing dirty list; - sorting nat sets incorrectly due to recording wrong entry_cnt in nat entry set. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index ad86efb3a171..3368f40c9235 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -158,9 +158,6 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid); struct nat_entry_set *head; - if (get_nat_flag(ne, IS_DIRTY)) - return; - head = radix_tree_lookup(&nm_i->nat_set_root, set); if (!head) { head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_NOFS); @@ -171,10 +168,18 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, head->entry_cnt = 0; f2fs_radix_tree_insert(&nm_i->nat_set_root, set, head); } - list_move_tail(&ne->list, &head->entry_list); + + if (get_nat_flag(ne, IS_DIRTY)) + goto refresh_list; + nm_i->dirty_nat_cnt++; head->entry_cnt++; set_nat_flag(ne, IS_DIRTY, true); +refresh_list: + if (nat_get_blkaddr(ne) == NEW_ADDR) + list_del_init(&ne->list); + else + list_move_tail(&ne->list, &head->entry_list); } static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i, @@ -2432,8 +2437,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, nid_t nid = nat_get_nid(ne); int offset; - if (nat_get_blkaddr(ne) == NEW_ADDR) - continue; + f2fs_bug_on(sbi, nat_get_blkaddr(ne) == NEW_ADDR); if (to_journal) { offset = lookup_journal_in_cursum(journal, -- GitLab From feb93ff1bfbc858b2e4d38328909eef4d8db25d0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 7 Jun 2017 11:17:35 +0800 Subject: [PATCH 0716/5498] f2fs: fix to avoid panic when encountering corrupt node With fault_injection option, generic/361 of fstests will complain us with below message: Call Trace: get_node_page+0x12/0x20 [f2fs] f2fs_iget+0x92/0x7d0 [f2fs] f2fs_fill_super+0x10fb/0x15e0 [f2fs] mount_bdev+0x184/0x1c0 f2fs_mount+0x15/0x20 [f2fs] mount_fs+0x39/0x150 vfs_kern_mount+0x67/0x110 do_mount+0x1bb/0xc70 SyS_mount+0x83/0xd0 do_syscall_64+0x6e/0x160 entry_SYSCALL64_slow_path+0x25/0x25 Since mkfs loop device in f2fs partition can be failed silently due to checkpoint error injection, so root inode page can be corrupted, in order to avoid needless panic, in get_node_page, it's better to leave message and return error to caller, and let fsck repaire it later. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 3368f40c9235..cb1724c9ff4c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1157,6 +1157,7 @@ repeat: f2fs_put_page(page, 1); return ERR_PTR(err); } else if (err == LOCKED_PAGE) { + err = 0; goto page_hit; } @@ -1170,15 +1171,22 @@ repeat: goto repeat; } - if (unlikely(!PageUptodate(page))) + if (unlikely(!PageUptodate(page))) { + err = -EIO; goto out_err; + } page_hit: if(unlikely(nid != nid_of_node(page))) { - f2fs_bug_on(sbi, 1); + f2fs_msg(sbi->sb, KERN_WARNING, "inconsistent node block, " + "nid:%lu, node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]", + nid, nid_of_node(page), ino_of_node(page), + ofs_of_node(page), cpver_of_node(page), + next_blkaddr_of_node(page)); ClearPageUptodate(page); + err = -EINVAL; out_err: f2fs_put_page(page, 1); - return ERR_PTR(-EIO); + return ERR_PTR(err); } mark_page_accessed(page); return page; -- GitLab From e7059cc9f8446c615cf980b1a4b17dac6378902a Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Fri, 9 Jun 2017 06:32:54 +0800 Subject: [PATCH 0717/5498] f2fs: use proper variable name It is better to use variable name "inline_dentry" instead of "dentry_blk" when data type is "struct f2fs_inline_dentry". This patch has no functional changes, just to make code more readable especially when call the function make_dentry_ptr_inline() and f2fs_convert_inline_dir(). Signed-off-by: Tiezhu Yang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e4c527c4e7d0..e0fd4376e6fb 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -316,12 +316,12 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, int make_empty_inline_dir(struct inode *inode, struct inode *parent, struct page *ipage) { - struct f2fs_inline_dentry *dentry_blk; + struct f2fs_inline_dentry *inline_dentry; struct f2fs_dentry_ptr d; - dentry_blk = inline_data_addr(ipage); + inline_dentry = inline_data_addr(ipage); - make_dentry_ptr_inline(NULL, &d, dentry_blk); + make_dentry_ptr_inline(NULL, &d, inline_dentry); do_make_empty_dir(inode, parent, &d); set_page_dirty(ipage); @@ -500,7 +500,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, struct page *ipage; unsigned int bit_pos; f2fs_hash_t name_hash; - struct f2fs_inline_dentry *dentry_blk = NULL; + struct f2fs_inline_dentry *inline_dentry = NULL; struct f2fs_dentry_ptr d; int slots = GET_DENTRY_SLOTS(new_name->len); struct page *page = NULL; @@ -510,11 +510,11 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, if (IS_ERR(ipage)) return PTR_ERR(ipage); - dentry_blk = inline_data_addr(ipage); - bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, + inline_dentry = inline_data_addr(ipage); + bit_pos = room_for_filename(&inline_dentry->dentry_bitmap, slots, NR_INLINE_DENTRY); if (bit_pos >= NR_INLINE_DENTRY) { - err = f2fs_convert_inline_dir(dir, ipage, dentry_blk); + err = f2fs_convert_inline_dir(dir, ipage, inline_dentry); if (err) return err; err = -EAGAIN; @@ -534,7 +534,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, f2fs_wait_on_page_writeback(ipage, NODE, true); name_hash = f2fs_dentry_hash(new_name, NULL); - make_dentry_ptr_inline(NULL, &d, dentry_blk); + make_dentry_ptr_inline(NULL, &d, inline_dentry); f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); set_page_dirty(ipage); @@ -586,14 +586,14 @@ bool f2fs_empty_inline_dir(struct inode *dir) struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct page *ipage; unsigned int bit_pos = 2; - struct f2fs_inline_dentry *dentry_blk; + struct f2fs_inline_dentry *inline_dentry; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) return false; - dentry_blk = inline_data_addr(ipage); - bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, + inline_dentry = inline_data_addr(ipage); + bit_pos = find_next_bit_le(&inline_dentry->dentry_bitmap, NR_INLINE_DENTRY, bit_pos); -- GitLab From e04834a2ae0c7e053a30044ead81b3dc25d27a47 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 11 Jun 2017 09:21:11 +0200 Subject: [PATCH 0718/5498] f2fs: Fix a return value in case of error in 'f2fs_fill_super' err must be set to -ENOMEM, otherwise we return 0. Fixes: a912b54d3aaa0 ("f2fs: split bio cache") Signed-off-by: Christophe JAILLET Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b53f4bce15aa..327df47b2c33 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1969,8 +1969,10 @@ try_onemore: sbi->write_io[i] = kmalloc(n * sizeof(struct f2fs_bio_info), GFP_KERNEL); - if (!sbi->write_io[i]) + if (!sbi->write_io[i]) { + err = -ENOMEM; goto free_options; + } for (j = HOT; j < n; j++) { init_rwsem(&sbi->write_io[i][j].io_rwsem); -- GitLab From 38de60e1db9db28630ad0787c1095266210f2a81 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 12 Jun 2017 09:44:24 +0800 Subject: [PATCH 0719/5498] f2fs: fix to show injection rate in ->show_options If fault injection functionality is enabled, show additional injection rate in ->show_options. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 327df47b2c33..437c9ab85388 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -972,7 +972,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi)); #ifdef CONFIG_F2FS_FAULT_INJECTION if (test_opt(sbi, FAULT_INJECTION)) - seq_puts(seq, ",fault_injection"); + seq_printf(seq, ",fault_injection=%u", + sbi->fault_info.inject_rate); #endif return 0; -- GitLab From 7dedcf049cd67dd959ce26473064c52685efd9d2 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 12 Jun 2017 09:44:27 +0800 Subject: [PATCH 0720/5498] f2fs: fix wrong error number of fill_super This patch fixes incorrect error number in error path of fill_super. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 437c9ab85388..1c4e2322c81c 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1922,6 +1922,7 @@ try_onemore: if (f2fs_sb_mounted_blkzoned(sb)) { f2fs_msg(sb, KERN_ERR, "Zoned block device support is not enabled\n"); + err = -EOPNOTSUPP; goto free_sb_buf; } #endif @@ -1995,8 +1996,10 @@ try_onemore: if (F2FS_IO_SIZE(sbi) > 1) { sbi->write_io_dummy = mempool_create_page_pool(2 * (F2FS_IO_SIZE(sbi) - 1), 0); - if (!sbi->write_io_dummy) + if (!sbi->write_io_dummy) { + err = -ENOMEM; goto free_options; + } } /* get an inode for meta space */ -- GitLab From 4d4112df6bfe73d88977f8e56fb9d5cf6aedb060 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 14 Jun 2017 17:39:46 +0800 Subject: [PATCH 0721/5498] f2fs: clean up sysfs codes Just cleanup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 121 +++++++++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 47 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1c4e2322c81c..7f0e3ff66b87 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -345,6 +345,22 @@ static struct kobj_type f2fs_ktype = { .release = f2fs_sb_release, }; +int __init f2fs_register_sysfs(void) +{ + f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + + f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); + if (!f2fs_kset) + return -ENOMEM; + return 0; +} + +void f2fs_unregister_sysfs(void) +{ + kset_unregister(f2fs_kset); + remove_proc_entry("fs/f2fs", NULL); +} + void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) { struct va_format vaf; @@ -753,17 +769,23 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) kfree(sbi->devs); } -static void f2fs_put_super(struct super_block *sb) +void f2fs_exit_sysfs(struct f2fs_sb_info *sbi) { - struct f2fs_sb_info *sbi = F2FS_SB(sb); - int i; + kobject_del(&sbi->s_kobj); + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); if (sbi->s_proc) { remove_proc_entry("segment_info", sbi->s_proc); remove_proc_entry("segment_bits", sbi->s_proc); - remove_proc_entry(sb->s_id, f2fs_proc_root); + remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); } - kobject_del(&sbi->s_kobj); +} + +static void f2fs_put_super(struct super_block *sb) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + int i; stop_gc_thread(sbi); @@ -816,8 +838,8 @@ static void f2fs_put_super(struct super_block *sb) destroy_segment_manager(sbi); kfree(sbi->ckpt); - kobject_put(&sbi->s_kobj); - wait_for_completion(&sbi->s_kobj_unregister); + + f2fs_exit_sysfs(sbi); sb->s_fs_info = NULL; if (sbi->s_chksum_driver) @@ -1047,6 +1069,37 @@ static const struct file_operations f2fs_seq_##_name##_fops = { \ F2FS_PROC_FILE_DEF(segment_info); F2FS_PROC_FILE_DEF(segment_bits); +int f2fs_init_sysfs(struct f2fs_sb_info *sbi) +{ + struct super_block *sb = sbi->sb; + int err; + + if (f2fs_proc_root) + sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); + + if (sbi->s_proc) { + proc_create_data("segment_info", S_IRUGO, sbi->s_proc, + &f2fs_seq_segment_info_fops, sb); + proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, + &f2fs_seq_segment_bits_fops, sb); + } + + sbi->s_kobj.kset = f2fs_kset; + init_completion(&sbi->s_kobj_unregister); + err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, + "%s", sb->s_id); + if (err) + goto err_out; + return 0; +err_out: + if (sbi->s_proc) { + remove_proc_entry("segment_info", sbi->s_proc); + remove_proc_entry("segment_bits", sbi->s_proc); + remove_proc_entry(sb->s_id, f2fs_proc_root); + } + return err; +} + static void default_options(struct f2fs_sb_info *sbi) { /* init some FS parameters */ @@ -2106,22 +2159,9 @@ try_onemore: goto free_root_inode; } - if (f2fs_proc_root) - sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); - - if (sbi->s_proc) { - proc_create_data("segment_info", S_IRUGO, sbi->s_proc, - &f2fs_seq_segment_info_fops, sb); - proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, - &f2fs_seq_segment_bits_fops, sb); - } - - sbi->s_kobj.kset = f2fs_kset; - init_completion(&sbi->s_kobj_unregister); - err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, - "%s", sb->s_id); + err = f2fs_init_sysfs(sbi); if (err) - goto free_proc; + goto free_root_inode; /* recover fsynced data */ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { @@ -2132,7 +2172,7 @@ try_onemore: if (bdev_read_only(sb->s_bdev) && !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { err = -EROFS; - goto free_kobj; + goto free_sysfs; } if (need_fsck) @@ -2146,7 +2186,7 @@ try_onemore: need_fsck = true; f2fs_msg(sb, KERN_ERR, "Cannot recover all fsync data errno=%d", err); - goto free_kobj; + goto free_sysfs; } } else { err = recover_fsync_data(sbi, true); @@ -2155,7 +2195,7 @@ try_onemore: err = -EINVAL; f2fs_msg(sb, KERN_ERR, "Need to recover fsync data"); - goto free_kobj; + goto free_sysfs; } } skip_recovery: @@ -2170,7 +2210,7 @@ skip_recovery: /* After POR, we can run background GC thread.*/ err = start_gc_thread(sbi); if (err) - goto free_kobj; + goto free_sysfs; } kfree(options); @@ -2188,17 +2228,9 @@ skip_recovery: f2fs_update_time(sbi, REQ_TIME); return 0; -free_kobj: +free_sysfs: f2fs_sync_inode_meta(sbi); - kobject_del(&sbi->s_kobj); - kobject_put(&sbi->s_kobj); - wait_for_completion(&sbi->s_kobj_unregister); -free_proc: - if (sbi->s_proc) { - remove_proc_entry("segment_info", sbi->s_proc); - remove_proc_entry("segment_bits", sbi->s_proc); - remove_proc_entry(sb->s_id, f2fs_proc_root); - } + f2fs_exit_sysfs(sbi); free_root_inode: dput(sb->s_root); sb->s_root = NULL; @@ -2313,30 +2345,26 @@ static int __init init_f2fs_fs(void) err = create_extent_cache(); if (err) goto free_checkpoint_caches; - f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); - if (!f2fs_kset) { - err = -ENOMEM; + err = f2fs_register_sysfs(); + if (err) goto free_extent_cache; - } err = register_shrinker(&f2fs_shrinker_info); if (err) - goto free_kset; - + goto free_sysfs; err = register_filesystem(&f2fs_fs_type); if (err) goto free_shrinker; err = f2fs_create_root_stats(); if (err) goto free_filesystem; - f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); return 0; free_filesystem: unregister_filesystem(&f2fs_fs_type); free_shrinker: unregister_shrinker(&f2fs_shrinker_info); -free_kset: - kset_unregister(f2fs_kset); +free_sysfs: + f2fs_unregister_sysfs(); free_extent_cache: destroy_extent_cache(); free_checkpoint_caches: @@ -2353,11 +2381,10 @@ fail: static void __exit exit_f2fs_fs(void) { - remove_proc_entry("fs/f2fs", NULL); f2fs_destroy_root_stats(); unregister_filesystem(&f2fs_fs_type); unregister_shrinker(&f2fs_shrinker_info); - kset_unregister(f2fs_kset); + f2fs_unregister_sysfs(); destroy_extent_cache(); destroy_checkpoint_caches(); destroy_segment_manager_caches(); -- GitLab From 860a6bc4ed52466643c5019722eeea592748bbbc Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 14 Jun 2017 17:39:47 +0800 Subject: [PATCH 0722/5498] f2fs: move sysfs code from super.c to fs/f2fs/sysfs.c Codes related to sysfs and procfs are dispersive and mixed with sb related codes, but actually these codes are independent from others, so split them from super.c, and reorgnize and manger them in sysfs.c. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/super.c --- fs/f2fs/Makefile | 2 +- fs/f2fs/f2fs.h | 8 ++ fs/f2fs/super.c | 333 -------------------------------------------- fs/f2fs/sysfs.c | 350 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 359 insertions(+), 334 deletions(-) create mode 100644 fs/f2fs/sysfs.c diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile index ca949ea7c02f..a0dc559b1b47 100644 --- a/fs/f2fs/Makefile +++ b/fs/f2fs/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o f2fs-y := dir.o file.o inode.o namei.o hash.o super.o inline.o f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o -f2fs-y += shrinker.o extent_cache.o +f2fs-y += shrinker.o extent_cache.o sysfs.o f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 04432df56f82..7685cc668837 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2774,6 +2774,14 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi); int __init create_extent_cache(void); void destroy_extent_cache(void); +/* + * sysfs.c + */ +int __init f2fs_register_sysfs(void); +void f2fs_unregister_sysfs(void); +int f2fs_init_sysfs(struct f2fs_sb_info *sbi); +void f2fs_exit_sysfs(struct f2fs_sb_info *sbi); + /* * crypto support */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7f0e3ff66b87..eeb3057aa9f6 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -35,9 +35,7 @@ #define CREATE_TRACE_POINTS #include -static struct proc_dir_entry *f2fs_proc_root; static struct kmem_cache *f2fs_inode_cachep; -static struct kset *f2fs_kset; #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -142,225 +140,6 @@ static match_table_t f2fs_tokens = { {Opt_err, NULL}, }; -/* Sysfs support for f2fs */ -enum { - GC_THREAD, /* struct f2fs_gc_thread */ - SM_INFO, /* struct f2fs_sm_info */ - DCC_INFO, /* struct discard_cmd_control */ - NM_INFO, /* struct f2fs_nm_info */ - F2FS_SBI, /* struct f2fs_sb_info */ -#ifdef CONFIG_F2FS_FAULT_INJECTION - FAULT_INFO_RATE, /* struct f2fs_fault_info */ - FAULT_INFO_TYPE, /* struct f2fs_fault_info */ -#endif -}; - -struct f2fs_attr { - struct attribute attr; - ssize_t (*show)(struct f2fs_attr *, struct f2fs_sb_info *, char *); - ssize_t (*store)(struct f2fs_attr *, struct f2fs_sb_info *, - const char *, size_t); - int struct_type; - int offset; -}; - -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) - 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; -} - -static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, - struct f2fs_sb_info *sbi, char *buf) -{ - struct super_block *sb = sbi->sb; - - if (!sb->s_bdev->bd_part) - return snprintf(buf, PAGE_SIZE, "0\n"); - - return snprintf(buf, PAGE_SIZE, "%llu\n", - (unsigned long long)(sbi->kbytes_written + - BD_PART_WRITTEN(sbi))); -} - -static ssize_t f2fs_sbi_show(struct f2fs_attr *a, - struct f2fs_sb_info *sbi, char *buf) -{ - unsigned char *ptr = NULL; - unsigned int *ui; - - ptr = __struct_ptr(sbi, a->struct_type); - if (!ptr) - return -EINVAL; - - ui = (unsigned int *)(ptr + a->offset); - - return snprintf(buf, PAGE_SIZE, "%u\n", *ui); -} - -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 - *ui = t; - return count; -} - -static ssize_t f2fs_attr_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, - s_kobj); - struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); - - return a->show ? a->show(a, sbi, buf) : 0; -} - -static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t len) -{ - struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, - s_kobj); - struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); - - return a->store ? a->store(a, sbi, buf, len) : 0; -} - -static void f2fs_sb_release(struct kobject *kobj) -{ - struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, - s_kobj); - complete(&sbi->s_kobj_unregister); -} - -#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ -static struct f2fs_attr f2fs_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ - .struct_type = _struct_type, \ - .offset = _offset \ -} - -#define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \ - F2FS_ATTR_OFFSET(struct_type, name, 0644, \ - f2fs_sbi_show, f2fs_sbi_store, \ - offsetof(struct struct_name, elname)) - -#define F2FS_GENERAL_RO_ATTR(name) \ -static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) - -F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); -F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); -F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); -F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); -F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); -F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards); -F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); -F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); -F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); -F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); -F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_blocks); -F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); -F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); -F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio); -F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); -F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); -F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); -F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); -#ifdef CONFIG_F2FS_FAULT_INJECTION -F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); -F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); -#endif -F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); - -#define ATTR_LIST(name) (&f2fs_attr_##name.attr) -static struct attribute *f2fs_attrs[] = { - ATTR_LIST(gc_min_sleep_time), - ATTR_LIST(gc_max_sleep_time), - ATTR_LIST(gc_no_gc_sleep_time), - ATTR_LIST(gc_idle), - ATTR_LIST(reclaim_segments), - ATTR_LIST(max_small_discards), - ATTR_LIST(batched_trim_sections), - ATTR_LIST(ipu_policy), - ATTR_LIST(min_ipu_util), - ATTR_LIST(min_fsync_blocks), - ATTR_LIST(min_hot_blocks), - ATTR_LIST(max_victim_search), - ATTR_LIST(dir_level), - ATTR_LIST(ram_thresh), - ATTR_LIST(ra_nid_pages), - ATTR_LIST(dirty_nats_ratio), - ATTR_LIST(cp_interval), - ATTR_LIST(idle_interval), -#ifdef CONFIG_F2FS_FAULT_INJECTION - ATTR_LIST(inject_rate), - ATTR_LIST(inject_type), -#endif - ATTR_LIST(lifetime_write_kbytes), - NULL, -}; - -static const struct sysfs_ops f2fs_attr_ops = { - .show = f2fs_attr_show, - .store = f2fs_attr_store, -}; - -static struct kobj_type f2fs_ktype = { - .default_attrs = f2fs_attrs, - .sysfs_ops = &f2fs_attr_ops, - .release = f2fs_sb_release, -}; - -int __init f2fs_register_sysfs(void) -{ - f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); - - f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); - if (!f2fs_kset) - return -ENOMEM; - return 0; -} - -void f2fs_unregister_sysfs(void) -{ - kset_unregister(f2fs_kset); - remove_proc_entry("fs/f2fs", NULL); -} - void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) { struct va_format vaf; @@ -769,19 +548,6 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) kfree(sbi->devs); } -void f2fs_exit_sysfs(struct f2fs_sb_info *sbi) -{ - kobject_del(&sbi->s_kobj); - kobject_put(&sbi->s_kobj); - wait_for_completion(&sbi->s_kobj_unregister); - - if (sbi->s_proc) { - remove_proc_entry("segment_info", sbi->s_proc); - remove_proc_entry("segment_bits", sbi->s_proc); - remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); - } -} - static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -1001,105 +767,6 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) return 0; } -static int segment_info_seq_show(struct seq_file *seq, void *offset) -{ - struct super_block *sb = seq->private; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - unsigned int total_segs = - le32_to_cpu(sbi->raw_super->segment_count_main); - int i; - - seq_puts(seq, "format: segment_type|valid_blocks\n" - "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); - - for (i = 0; i < total_segs; i++) { - struct seg_entry *se = get_seg_entry(sbi, i); - - if ((i % 10) == 0) - seq_printf(seq, "%-10d", i); - seq_printf(seq, "%d|%-3u", se->type, - get_valid_blocks(sbi, i, false)); - if ((i % 10) == 9 || i == (total_segs - 1)) - seq_putc(seq, '\n'); - else - seq_putc(seq, ' '); - } - - return 0; -} - -static int segment_bits_seq_show(struct seq_file *seq, void *offset) -{ - struct super_block *sb = seq->private; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - unsigned int total_segs = - le32_to_cpu(sbi->raw_super->segment_count_main); - int i, j; - - seq_puts(seq, "format: segment_type|valid_blocks|bitmaps\n" - "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); - - for (i = 0; i < total_segs; i++) { - struct seg_entry *se = get_seg_entry(sbi, i); - - seq_printf(seq, "%-10d", i); - seq_printf(seq, "%d|%-3u|", se->type, - get_valid_blocks(sbi, i, false)); - for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) - seq_printf(seq, " %.2x", se->cur_valid_map[j]); - seq_putc(seq, '\n'); - } - return 0; -} - -#define F2FS_PROC_FILE_DEF(_name) \ -static int _name##_open_fs(struct inode *inode, struct file *file) \ -{ \ - return single_open(file, _name##_seq_show, PDE_DATA(inode)); \ -} \ - \ -static const struct file_operations f2fs_seq_##_name##_fops = { \ - .owner = THIS_MODULE, \ - .open = _name##_open_fs, \ - .read = seq_read, \ - .llseek = seq_lseek, \ - .release = single_release, \ -}; - -F2FS_PROC_FILE_DEF(segment_info); -F2FS_PROC_FILE_DEF(segment_bits); - -int f2fs_init_sysfs(struct f2fs_sb_info *sbi) -{ - struct super_block *sb = sbi->sb; - int err; - - if (f2fs_proc_root) - sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); - - if (sbi->s_proc) { - proc_create_data("segment_info", S_IRUGO, sbi->s_proc, - &f2fs_seq_segment_info_fops, sb); - proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, - &f2fs_seq_segment_bits_fops, sb); - } - - sbi->s_kobj.kset = f2fs_kset; - init_completion(&sbi->s_kobj_unregister); - err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, - "%s", sb->s_id); - if (err) - goto err_out; - return 0; -err_out: - if (sbi->s_proc) { - remove_proc_entry("segment_info", sbi->s_proc); - remove_proc_entry("segment_bits", sbi->s_proc); - remove_proc_entry(sb->s_id, f2fs_proc_root); - } - return err; -} - static void default_options(struct f2fs_sb_info *sbi) { /* init some FS parameters */ diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c new file mode 100644 index 000000000000..714a3e47bbe8 --- /dev/null +++ b/fs/f2fs/sysfs.c @@ -0,0 +1,350 @@ +/* + * f2fs sysfs interface + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * Copyright (c) 2017 Chao Yu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include + +#include "f2fs.h" +#include "segment.h" +#include "gc.h" + +static struct proc_dir_entry *f2fs_proc_root; +static struct kset *f2fs_kset; + +/* Sysfs support for f2fs */ +enum { + GC_THREAD, /* struct f2fs_gc_thread */ + SM_INFO, /* struct f2fs_sm_info */ + DCC_INFO, /* struct discard_cmd_control */ + NM_INFO, /* struct f2fs_nm_info */ + F2FS_SBI, /* struct f2fs_sb_info */ +#ifdef CONFIG_F2FS_FAULT_INJECTION + FAULT_INFO_RATE, /* struct f2fs_fault_info */ + FAULT_INFO_TYPE, /* struct f2fs_fault_info */ +#endif +}; + +struct f2fs_attr { + struct attribute attr; + ssize_t (*show)(struct f2fs_attr *, struct f2fs_sb_info *, char *); + ssize_t (*store)(struct f2fs_attr *, struct f2fs_sb_info *, + const char *, size_t); + int struct_type; + int offset; +}; + +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) + 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; +} + +static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct super_block *sb = sbi->sb; + + if (!sb->s_bdev->bd_part) + return snprintf(buf, PAGE_SIZE, "0\n"); + + return snprintf(buf, PAGE_SIZE, "%llu\n", + (unsigned long long)(sbi->kbytes_written + + BD_PART_WRITTEN(sbi))); +} + +static ssize_t f2fs_sbi_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + unsigned char *ptr = NULL; + unsigned int *ui; + + ptr = __struct_ptr(sbi, a->struct_type); + if (!ptr) + return -EINVAL; + + ui = (unsigned int *)(ptr + a->offset); + + return snprintf(buf, PAGE_SIZE, "%u\n", *ui); +} + +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 + *ui = t; + return count; +} + +static ssize_t f2fs_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, + s_kobj); + struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); + + return a->show ? a->show(a, sbi, buf) : 0; +} + +static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, + s_kobj); + struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); + + return a->store ? a->store(a, sbi, buf, len) : 0; +} + +static void f2fs_sb_release(struct kobject *kobj) +{ + struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, + s_kobj); + complete(&sbi->s_kobj_unregister); +} + +#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ +static struct f2fs_attr f2fs_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ + .struct_type = _struct_type, \ + .offset = _offset \ +} + +#define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \ + F2FS_ATTR_OFFSET(struct_type, name, 0644, \ + f2fs_sbi_show, f2fs_sbi_store, \ + offsetof(struct struct_name, elname)) + +#define F2FS_GENERAL_RO_ATTR(name) \ +static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) + +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); +F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_blocks); +F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); +F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); +F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); +#ifdef CONFIG_F2FS_FAULT_INJECTION +F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); +F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); +#endif +F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); + +#define ATTR_LIST(name) (&f2fs_attr_##name.attr) +static struct attribute *f2fs_attrs[] = { + ATTR_LIST(gc_min_sleep_time), + ATTR_LIST(gc_max_sleep_time), + ATTR_LIST(gc_no_gc_sleep_time), + ATTR_LIST(gc_idle), + ATTR_LIST(reclaim_segments), + ATTR_LIST(max_small_discards), + ATTR_LIST(batched_trim_sections), + ATTR_LIST(ipu_policy), + ATTR_LIST(min_ipu_util), + ATTR_LIST(min_fsync_blocks), + ATTR_LIST(min_hot_blocks), + ATTR_LIST(max_victim_search), + ATTR_LIST(dir_level), + ATTR_LIST(ram_thresh), + ATTR_LIST(ra_nid_pages), + ATTR_LIST(dirty_nats_ratio), + ATTR_LIST(cp_interval), + ATTR_LIST(idle_interval), +#ifdef CONFIG_F2FS_FAULT_INJECTION + ATTR_LIST(inject_rate), + ATTR_LIST(inject_type), +#endif + ATTR_LIST(lifetime_write_kbytes), + NULL, +}; + +static const struct sysfs_ops f2fs_attr_ops = { + .show = f2fs_attr_show, + .store = f2fs_attr_store, +}; + +static struct kobj_type f2fs_ktype = { + .default_attrs = f2fs_attrs, + .sysfs_ops = &f2fs_attr_ops, + .release = f2fs_sb_release, +}; + +static int segment_info_seq_show(struct seq_file *seq, void *offset) +{ + struct super_block *sb = seq->private; + struct f2fs_sb_info *sbi = F2FS_SB(sb); + unsigned int total_segs = + le32_to_cpu(sbi->raw_super->segment_count_main); + int i; + + seq_puts(seq, "format: segment_type|valid_blocks\n" + "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); + + for (i = 0; i < total_segs; i++) { + struct seg_entry *se = get_seg_entry(sbi, i); + + if ((i % 10) == 0) + seq_printf(seq, "%-10d", i); + seq_printf(seq, "%d|%-3u", se->type, + get_valid_blocks(sbi, i, false)); + if ((i % 10) == 9 || i == (total_segs - 1)) + seq_putc(seq, '\n'); + else + seq_putc(seq, ' '); + } + + return 0; +} + +static int segment_bits_seq_show(struct seq_file *seq, void *offset) +{ + struct super_block *sb = seq->private; + struct f2fs_sb_info *sbi = F2FS_SB(sb); + unsigned int total_segs = + le32_to_cpu(sbi->raw_super->segment_count_main); + int i, j; + + seq_puts(seq, "format: segment_type|valid_blocks|bitmaps\n" + "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); + + for (i = 0; i < total_segs; i++) { + struct seg_entry *se = get_seg_entry(sbi, i); + + seq_printf(seq, "%-10d", i); + seq_printf(seq, "%d|%-3u|", se->type, + get_valid_blocks(sbi, i, false)); + for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) + seq_printf(seq, " %.2x", se->cur_valid_map[j]); + seq_putc(seq, '\n'); + } + return 0; +} + +#define F2FS_PROC_FILE_DEF(_name) \ +static int _name##_open_fs(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, _name##_seq_show, PDE_DATA(inode)); \ +} \ + \ +static const struct file_operations f2fs_seq_##_name##_fops = { \ + .open = _name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +}; + +F2FS_PROC_FILE_DEF(segment_info); +F2FS_PROC_FILE_DEF(segment_bits); + +int __init f2fs_register_sysfs(void) +{ + f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + + f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); + if (!f2fs_kset) + return -ENOMEM; + return 0; +} + +void f2fs_unregister_sysfs(void) +{ + kset_unregister(f2fs_kset); + remove_proc_entry("fs/f2fs", NULL); +} + +int f2fs_init_sysfs(struct f2fs_sb_info *sbi) +{ + struct super_block *sb = sbi->sb; + int err; + + if (f2fs_proc_root) + sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); + + if (sbi->s_proc) { + proc_create_data("segment_info", S_IRUGO, sbi->s_proc, + &f2fs_seq_segment_info_fops, sb); + proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, + &f2fs_seq_segment_bits_fops, sb); + } + + sbi->s_kobj.kset = f2fs_kset; + init_completion(&sbi->s_kobj_unregister); + err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, + "%s", sb->s_id); + if (err) + goto err_out; + return 0; +err_out: + if (sbi->s_proc) { + remove_proc_entry("segment_info", sbi->s_proc); + remove_proc_entry("segment_bits", sbi->s_proc); + remove_proc_entry(sb->s_id, f2fs_proc_root); + } + return err; +} + +void f2fs_exit_sysfs(struct f2fs_sb_info *sbi) +{ + kobject_del(&sbi->s_kobj); + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); + + if (sbi->s_proc) { + remove_proc_entry("segment_info", sbi->s_proc); + remove_proc_entry("segment_bits", sbi->s_proc); + remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); + } +} -- GitLab From 7007458b9b6810b7bff8b9d7b090f6b2ae283fe5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 13 Jun 2017 16:47:54 -0700 Subject: [PATCH 0723/5498] f2fs: require key for truncate(2) of encrypted file Currently, filesystems allow truncate(2) on an encrypted file without the encryption key. However, it's impossible to correctly handle the case where the size being truncated to is not a multiple of the filesystem block size, because that would require decrypting the final block, zeroing the part beyond i_size, then encrypting the block. As other modifications to encrypted file contents are prohibited without the key, just prohibit truncate(2) as well, making it fail with ENOKEY. Signed-off-by: Eric Biggers Acked-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6c5c7dd4cd94..ff70a30e0d04 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -692,9 +692,13 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) return err; if (attr->ia_valid & ATTR_SIZE) { - if (f2fs_encrypted_inode(inode) && - fscrypt_get_encryption_info(inode)) - return -EACCES; + if (f2fs_encrypted_inode(inode)) { + err = fscrypt_get_encryption_info(inode); + if (err) + return err; + if (!fscrypt_has_encryption_key(inode)) + return -ENOKEY; + } if (attr->ia_size <= i_size_read(inode)) { down_write(&F2FS_I(inode)->i_mmap_sem); -- GitLab From eca076922e0e033c5fdcf9eb83cc5e3194472f21 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 14 Jun 2017 23:00:55 +0800 Subject: [PATCH 0724/5498] f2fs: set CP_TRIMMED_FLAG correctly Don't set CP_TRIMMED_FLAG for non-zoned block device or discard unsupported device, it can avoid to trigger unneeded checkpoint for that kind of device. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index eeb3057aa9f6..9de66e551008 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -574,7 +574,7 @@ static void f2fs_put_super(struct super_block *sb) /* be sure to wait for any on-going discard commands */ f2fs_wait_discard_bios(sbi); - if (!sbi->discard_blks) { + if (f2fs_discard_en(sbi) && !sbi->discard_blks) { struct cp_control cpc = { .reason = CP_UMOUNT | CP_TRIMMED, }; -- GitLab From 753bdd35dc5d9dd9f98e8a52886d12b92bdc84b7 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 14 Jun 2017 23:00:56 +0800 Subject: [PATCH 0725/5498] f2fs: measure inode.i_blocks as generic filesystem Both in memory or on disk, generic filesystems record i_blocks with 512bytes sized sector count, also VFS sub module such as disk quota follows this rule, but f2fs records it with 4096bytes sized block count, this difference leads to that once we use dquota's function which inc/dec iblocks, it will make i_blocks of f2fs being inconsistent between in memory and on disk. In order to resolve this issue, this patch changes to make in-memory i_blocks of f2fs recording sector count instead of block count, meanwhile leaving on-disk i_blocks recording block count. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 23 +++++++++++++---------- fs/f2fs/file.c | 1 - fs/f2fs/inode.c | 5 +++-- fs/f2fs/node.c | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7685cc668837..c451bc02c240 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1451,10 +1451,10 @@ static inline int check_nid_range(struct f2fs_sb_info *sbi, nid_t nid) */ static inline int F2FS_HAS_BLOCKS(struct inode *inode) { - if (F2FS_I(inode)->i_xattr_nid) - return inode->i_blocks > F2FS_DEFAULT_ALLOCATED_BLOCKS + 1; - else - return inode->i_blocks > F2FS_DEFAULT_ALLOCATED_BLOCKS; + block_t xattr_block = F2FS_I(inode)->i_xattr_nid ? 1 : 0; + + return (inode->i_blocks >> F2FS_LOG_SECTORS_PER_BLOCK) > + (F2FS_DEFAULT_ALLOCATED_BLOCKS + xattr_block); } static inline bool f2fs_has_xattr_block(unsigned int ofs) @@ -1462,7 +1462,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs) return ofs == XATTR_NODE_OFFSET; } -static inline void f2fs_i_blocks_write(struct inode *, blkcnt_t, bool); +static inline void f2fs_i_blocks_write(struct inode *, block_t, bool); static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) { @@ -1500,11 +1500,13 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, - blkcnt_t count) + block_t count) { + blkcnt_t sectors = count << F2FS_LOG_SECTORS_PER_BLOCK; + spin_lock(&sbi->stat_lock); f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count); - f2fs_bug_on(sbi, inode->i_blocks < count); + f2fs_bug_on(sbi, inode->i_blocks < sectors); sbi->total_valid_block_count -= (block_t)count; spin_unlock(&sbi->stat_lock); f2fs_i_blocks_write(inode, count, false); @@ -1954,13 +1956,14 @@ static inline void f2fs_i_links_write(struct inode *inode, bool inc) } static inline void f2fs_i_blocks_write(struct inode *inode, - blkcnt_t diff, bool add) + block_t diff, bool add) { bool clean = !is_inode_flag_set(inode, FI_DIRTY_INODE); bool recover = is_inode_flag_set(inode, FI_AUTO_RECOVER); + blkcnt_t sectors = diff << F2FS_LOG_SECTORS_PER_BLOCK; - inode->i_blocks = add ? inode->i_blocks + diff : - inode->i_blocks - diff; + inode->i_blocks = add ? inode->i_blocks + sectors : + inode->i_blocks - sectors; f2fs_mark_inode_dirty_sync(inode, true); if (clean || recover) set_inode_flag(inode, FI_AUTO_RECOVER); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index ff70a30e0d04..5e315a19e21e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -647,7 +647,6 @@ int f2fs_getattr(struct vfsmount *mnt, { struct inode *inode = d_inode(dentry); generic_fillattr(inode, stat); - stat->blocks <<= 3; return 0; } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 9166f076d688..51b0202cfa1b 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -16,6 +16,7 @@ #include "f2fs.h" #include "node.h" +#include "segment.h" #include @@ -129,7 +130,7 @@ static int do_read_inode(struct inode *inode) i_gid_write(inode, le32_to_cpu(ri->i_gid)); set_nlink(inode, le32_to_cpu(ri->i_links)); inode->i_size = le64_to_cpu(ri->i_size); - inode->i_blocks = le64_to_cpu(ri->i_blocks); + inode->i_blocks = SECTOR_FROM_BLOCK(le64_to_cpu(ri->i_blocks)); inode->i_atime.tv_sec = le64_to_cpu(ri->i_atime); inode->i_ctime.tv_sec = le64_to_cpu(ri->i_ctime); @@ -267,7 +268,7 @@ int update_inode(struct inode *inode, struct page *node_page) ri->i_gid = cpu_to_le32(i_gid_read(inode)); ri->i_links = cpu_to_le32(inode->i_nlink); ri->i_size = cpu_to_le64(i_size_read(inode)); - ri->i_blocks = cpu_to_le64(inode->i_blocks); + ri->i_blocks = cpu_to_le64(SECTOR_TO_BLOCK(inode->i_blocks)); if (et) { read_lock(&et->lock); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index cb1724c9ff4c..10460dc73957 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1011,7 +1011,7 @@ int remove_inode_page(struct inode *inode) /* 0 is possible, after f2fs_new_inode() has failed */ f2fs_bug_on(F2FS_I_SB(inode), - inode->i_blocks != 0 && inode->i_blocks != 1); + inode->i_blocks != 0 && inode->i_blocks != 8); /* will put inode & node pages */ truncate_node(&dn); -- GitLab From e65b848dabe6951c25b13c890cd11c72e4304668 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 14 Jun 2017 08:05:32 -0700 Subject: [PATCH 0726/5498] f2fs: don't need to check encrypted inode for partial truncation The cache_only is always false, if inode is encrypted. Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 5e315a19e21e..3cb598f736b4 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -537,8 +537,10 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, truncate_out: f2fs_wait_on_page_writeback(page, DATA, true); zero_user(page, offset, PAGE_SIZE - offset); - if (!cache_only || !f2fs_encrypted_inode(inode) || - !S_ISREG(inode->i_mode)) + + /* An encrypted inode should have a key and truncate the last page. */ + f2fs_bug_on(F2FS_I_SB(inode), cache_only && f2fs_encrypted_inode(inode)); + if (!cache_only) set_page_dirty(page); f2fs_put_page(page, 1); return 0; -- GitLab From fea87d812fc794d184c2ba41bd3f862b61754dda Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 15 Jun 2017 16:44:42 -0700 Subject: [PATCH 0727/5498] f2fs: add ioctl to do gc with target block address This patch adds f2fs_ioc_gc_range() to move blocks located in the given range. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 ++++++++ fs/f2fs/file.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c451bc02c240..b552554d51ab 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -402,6 +402,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, struct f2fs_move_range) #define F2FS_IOC_FLUSH_DEVICE _IOW(F2FS_IOCTL_MAGIC, 10, \ struct f2fs_flush_device) +#define F2FS_IOC_GARBAGE_COLLECT_RANGE _IOW(F2FS_IOCTL_MAGIC, 11, \ + struct f2fs_gc_range) #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY @@ -426,6 +428,12 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC32_GETVERSION FS_IOC32_GETVERSION #endif +struct f2fs_gc_range { + u32 sync; + u64 start; + u64 len; +}; + struct f2fs_defragment { u64 start; u64 len; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3cb598f736b4..f17236590762 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1906,6 +1906,50 @@ out: return ret; } +static int f2fs_ioc_gc_range(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_gc_range range; + u64 end; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(&range, (struct f2fs_gc_range __user *)arg, + sizeof(range))) + return -EFAULT; + + if (f2fs_readonly(sbi->sb)) + return -EROFS; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + end = range.start + range.len; + if (range.start < MAIN_BLKADDR(sbi) || end >= MAX_BLKADDR(sbi)) + return -EINVAL; +do_more: + if (!range.sync) { + if (!mutex_trylock(&sbi->gc_mutex)) { + ret = -EBUSY; + goto out; + } + } else { + mutex_lock(&sbi->gc_mutex); + } + + ret = f2fs_gc(sbi, range.sync, true, GET_SEGNO(sbi, range.start)); + range.start += sbi->blocks_per_seg; + if (range.start <= end) + goto do_more; +out: + mnt_drop_write_file(filp); + return ret; +} + static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -2350,6 +2394,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_get_encryption_pwsalt(filp, arg); case F2FS_IOC_GARBAGE_COLLECT: return f2fs_ioc_gc(filp, arg); + case F2FS_IOC_GARBAGE_COLLECT_RANGE: + return f2fs_ioc_gc_range(filp, arg); case F2FS_IOC_WRITE_CHECKPOINT: return f2fs_ioc_write_checkpoint(filp, arg); case F2FS_IOC_DEFRAGMENT: @@ -2420,6 +2466,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_GET_ENCRYPTION_PWSALT: case F2FS_IOC_GET_ENCRYPTION_POLICY: case F2FS_IOC_GARBAGE_COLLECT: + case F2FS_IOC_GARBAGE_COLLECT_RANGE: case F2FS_IOC_WRITE_CHECKPOINT: case F2FS_IOC_DEFRAGMENT: case F2FS_IOC_MOVE_RANGE: -- GitLab From e8eb0f0518d11686c25d98f56dce5f0f5cca1069 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Jun 2017 20:55:55 -0700 Subject: [PATCH 0728/5498] f2fs: report # of free inodes more precisely If the partition is small, we don't need to report total # of inodes including hidden free nodes. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9de66e551008..ed546461aa2c 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -668,6 +668,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) 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; + u64 avail_node_count; total_count = le64_to_cpu(sbi->raw_super->block_count); user_block_count = sbi->user_block_count; @@ -680,9 +681,16 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bfree = user_block_count - 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 = min(buf->f_files - valid_node_count(sbi), - buf->f_bavail); + avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; + + if (avail_node_count > user_block_count) { + buf->f_files = user_block_count; + buf->f_ffree = buf->f_bavail; + } else { + buf->f_files = avail_node_count; + buf->f_ffree = min(avail_node_count - valid_node_count(sbi), + buf->f_bavail); + } buf->f_namelen = F2FS_NAME_LEN; buf->f_fsid.val[0] = (u32)id; -- GitLab From 7b3ad4e4546463b3afe4e3e0534299842fc6421f Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Sat, 24 Jun 2017 15:57:19 +0800 Subject: [PATCH 0729/5498] f2fs: avoid redundant f2fs_flush after remount create_flush_cmd_control will create redundant issue_flush_thread after each remount with flush_merge option. Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index db0f04d7ac84..5366787bdc36 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -555,6 +555,8 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) if (SM_I(sbi)->fcc_info) { fcc = SM_I(sbi)->fcc_info; + if (fcc->f2fs_issue_flush) + return err; goto init_thread; } -- GitLab From 160a149e188ef9ba1b47afd2a56b9b4e1d064c13 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 26 Jun 2017 16:24:41 +0800 Subject: [PATCH 0730/5498] f2fs: introduce reserved_blocks in sysfs In this patch, we add a new sysfs interface, with it, we can control number of reserved blocks in system which could not be used by user, it enable f2fs to let user to configure for adjusting over-provision ratio dynamically instead of changing it by mkfs. So we can expect it will help to reserve more free space for relieving GC in both filesystem and flash device. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: Documentation/ABI/testing/sysfs-fs-f2fs --- Documentation/ABI/testing/sysfs-fs-f2fs | 18 ++++++++++++++++++ fs/f2fs/f2fs.h | 13 +++++++++---- fs/f2fs/super.c | 4 +++- fs/f2fs/sysfs.c | 16 +++++++++++++++- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index a809f6005f14..0421717ce26a 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -112,3 +112,21 @@ Date: January 2016 Contact: "Shuoran Liu" Description: Shows total written kbytes issued to disk. + +What: /sys/fs/f2fs//inject_rate +Date: May 2016 +Contact: "Sheng Yong" +Description: + Controls the injection rate. + +What: /sys/fs/f2fs//inject_type +Date: May 2016 +Contact: "Sheng Yong" +Description: + Controls the injection type. + +What: /sys/fs/f2fs//reserved_blocks +Date: June 2017 +Contact: "Chao Yu" +Description: + Controls current reserved blocks in system. diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b552554d51ab..b858f5e35644 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1065,6 +1065,8 @@ struct f2fs_sb_info { block_t total_valid_block_count; /* # of valid blocks */ block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ + block_t reserved_blocks; /* configurable reserved blocks */ + u32 s_next_generation; /* for NFS support */ /* # of pages, see count_type */ @@ -1475,6 +1477,7 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) { blkcnt_t diff; + block_t avail_user_block_count; #ifdef CONFIG_F2FS_FAULT_INJECTION if (time_to_inject(sbi, FAULT_BLOCK)) { @@ -1490,10 +1493,11 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); sbi->total_valid_block_count += (block_t)(*count); - if (unlikely(sbi->total_valid_block_count > sbi->user_block_count)) { - diff = sbi->total_valid_block_count - sbi->user_block_count; + avail_user_block_count = sbi->user_block_count - sbi->reserved_blocks; + if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { + diff = sbi->total_valid_block_count - avail_user_block_count; *count -= diff; - sbi->total_valid_block_count = sbi->user_block_count; + sbi->total_valid_block_count = avail_user_block_count; if (!*count) { spin_unlock(&sbi->stat_lock); percpu_counter_sub(&sbi->alloc_valid_block_count, diff); @@ -1655,7 +1659,8 @@ static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); valid_block_count = sbi->total_valid_block_count + 1; - if (unlikely(valid_block_count > sbi->user_block_count)) { + if (unlikely(valid_block_count + sbi->reserved_blocks > + sbi->user_block_count)) { spin_unlock(&sbi->stat_lock); return false; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ed546461aa2c..4b3f644f6a98 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -679,7 +679,8 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = total_count - start_count; buf->f_bfree = user_block_count - valid_user_blocks(sbi) + ovp_count; - buf->f_bavail = user_block_count - valid_user_blocks(sbi); + buf->f_bavail = user_block_count - valid_user_blocks(sbi) - + sbi->reserved_blocks; avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; @@ -1759,6 +1760,7 @@ try_onemore: sbi->total_valid_block_count = le64_to_cpu(sbi->ckpt->valid_block_count); sbi->last_valid_block_count = sbi->total_valid_block_count; + sbi->reserved_blocks = 0; for (i = 0; i < NR_INODE_TYPE; i++) { INIT_LIST_HEAD(&sbi->inode_list[i]); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 714a3e47bbe8..9adc202fcd6f 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -30,6 +30,7 @@ enum { FAULT_INFO_RATE, /* struct f2fs_fault_info */ FAULT_INFO_TYPE, /* struct f2fs_fault_info */ #endif + RESERVED_BLOCKS, }; struct f2fs_attr { @@ -51,7 +52,7 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) 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) + 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 || @@ -111,6 +112,17 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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; + } *ui = t; return count; } @@ -165,6 +177,7 @@ F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards); +F2FS_RW_ATTR(RESERVED_BLOCKS, f2fs_sb_info, reserved_blocks, reserved_blocks); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); @@ -208,6 +221,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(inject_type), #endif ATTR_LIST(lifetime_write_kbytes), + ATTR_LIST(reserved_blocks), NULL, }; -- GitLab From ab310e231caed1eb59f9e3027decccb51af92f0d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 29 Jun 2017 23:17:45 +0800 Subject: [PATCH 0731/5498] f2fs: stop gc/discard thread in prior during umount This patch resolves kernel panic for xfstests/081, caused by recent f2fs_bug_on f2fs: add f2fs_bug_on in __remove_discard_cmd For fixing, we will stop gc/discard thread in prior in ->kill_sb in order to avoid referring and releasing race among them. Signed-off-by: Jaegeuk Kim Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 19 +++++++++++++------ fs/f2fs/super.c | 7 ++++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b858f5e35644..0dd20dcb4754 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2393,6 +2393,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); +void stop_discard_thread(struct f2fs_sb_info *sbi); void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void release_discard_addrs(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5366787bdc36..3429319db996 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1144,6 +1144,18 @@ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) __wait_one_discard_bio(sbi, dc); } +void stop_discard_thread(struct f2fs_sb_info *sbi) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + + if (dcc && dcc->f2fs_issue_discard) { + struct task_struct *discard_thread = dcc->f2fs_issue_discard; + + dcc->f2fs_issue_discard = NULL; + kthread_stop(discard_thread); + } +} + /* This comes from f2fs_put_super */ void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { @@ -1501,12 +1513,7 @@ static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi) if (!dcc) return; - if (dcc->f2fs_issue_discard) { - struct task_struct *discard_thread = dcc->f2fs_issue_discard; - - dcc->f2fs_issue_discard = NULL; - kthread_stop(discard_thread); - } + stop_discard_thread(sbi); kfree(dcc); SM_I(sbi)->dcc_info = NULL; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 4b3f644f6a98..a28afef6c268 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -553,8 +553,6 @@ static void f2fs_put_super(struct super_block *sb) struct f2fs_sb_info *sbi = F2FS_SB(sb); int i; - stop_gc_thread(sbi); - /* prevent remaining shrinker jobs */ mutex_lock(&sbi->umount_mutex); @@ -1968,8 +1966,11 @@ static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags, static void kill_f2fs_super(struct super_block *sb) { - if (sb->s_root) + if (sb->s_root) { set_sbi_flag(F2FS_SB(sb), SBI_IS_CLOSE); + stop_gc_thread(F2FS_SB(sb)); + stop_discard_thread(F2FS_SB(sb)); + } kill_block_super(sb); } -- GitLab From b889d6c1118e80cfb83d24f5bdd1c426070734af Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 30 Jun 2017 17:19:02 +0800 Subject: [PATCH 0732/5498] f2fs: introduce __check_sit_bitmap After we introduce discard thread, discard command can be issued concurrently with data allocating, this patch adds new function to heck sit bitmap to ensure that userdata was invalid in which on-going discard command covered. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/segment.c --- fs/f2fs/segment.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3429319db996..b0656282b280 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -844,6 +844,31 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, return 0; } +void __check_sit_bitmap(struct f2fs_sb_info *sbi, + block_t start, block_t end) +{ +#ifdef CONFIG_F2FS_CHECK_FS + struct seg_entry *sentry; + unsigned int segno; + block_t blk = start; + unsigned long offset, size, max_blocks = sbi->blocks_per_seg; + unsigned long *map; + + while (blk < end) { + segno = GET_SEGNO(sbi, blk); + sentry = get_seg_entry(sbi, segno); + offset = GET_BLKOFF_FROM_SEG0(sbi, blk); + + size = min((unsigned long)(end - blk), max_blocks); + map = (unsigned long *)(sentry->cur_valid_map); + offset = __find_rev_next_bit(map, size, offset); + f2fs_bug_on(sbi, offset != size); + blk += size; + } +#endif +} + +/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, struct discard_cmd *dc) { @@ -869,6 +894,7 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, bio->bi_end_io = f2fs_submit_discard_endio; submit_bio(REQ_SYNC, bio); list_move_tail(&dc->list, &dcc->wait_list); + __check_sit_bitmap(sbi, dc->start, dc->start + dc->len); } } else { __remove_discard_cmd(sbi, dc); -- GitLab From 8309d6cd1aadd0a3b3b41538a1d4b330e3d3309d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 29 Jun 2017 23:20:45 +0800 Subject: [PATCH 0733/5498] f2fs: skip ->writepages for {mete,node}_inode during recovery Skip ->writepages in prior to ->writepage for {meta,node}_inode during recovery, hence unneeded loop in ->writepages can be avoided. Moreover, check SBI_POR_DOING earlier while writebacking pages. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 3 +++ fs/f2fs/data.c | 13 +++++++------ fs/f2fs/node.c | 3 +++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 065100552bcd..5162106f2d12 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -270,6 +270,9 @@ static int f2fs_write_meta_pages(struct address_space *mapping, struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); long diff, written; + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto skip_write; + /* collect a number of dirty meta pages and write together */ if (wbc->for_kupdate || get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META)) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 7af87b486c0f..f7fc1c0b45e3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1488,6 +1488,9 @@ static int __write_data_page(struct page *page, bool *submitted, trace_f2fs_writepage(page, DATA); + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto redirty_out; + if (page->index < end_index) goto write; @@ -1501,8 +1504,6 @@ static int __write_data_page(struct page *page, bool *submitted, zero_user_segment(page, offset, PAGE_SIZE); write: - if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) - goto redirty_out; if (f2fs_is_drop_cache(inode)) goto out; /* we should not write 0'th page having journal header */ @@ -1750,6 +1751,10 @@ static int f2fs_write_data_pages(struct address_space *mapping, if (!get_dirty_pages(inode) && wbc->sync_mode == WB_SYNC_NONE) return 0; + /* during POR, we don't need to trigger writepage at all. */ + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto skip_write; + if (S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_NONE && get_dirty_pages(inode) < nr_pages_to_skip(sbi, DATA) && available_free_memory(sbi, DIRTY_DENTS)) @@ -1759,10 +1764,6 @@ static int f2fs_write_data_pages(struct address_space *mapping, if (is_inode_flag_set(inode, FI_DO_DEFRAG)) goto skip_write; - /* during POR, we don't need to trigger writepage at all. */ - if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) - goto skip_write; - trace_f2fs_writepages(mapping->host, wbc, DATA); /* to avoid spliting IOs due to mixed WB_SYNC_ALL and WB_SYNC_NONE */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 10460dc73957..68b7ac1aa067 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1696,6 +1696,9 @@ static int f2fs_write_node_pages(struct address_space *mapping, struct blk_plug plug; long diff; + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto skip_write; + /* balancing f2fs's metadata in background */ f2fs_balance_fs_bg(sbi); -- GitLab From 79704e352eabba59b2c33cedc7bab14e8f17820b Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Mon, 26 Jun 2017 10:41:35 +0800 Subject: [PATCH 0734/5498] f2fs: do not set LOST_PINO for newly created dir Since directories will be written back with checkpoint and fsync a directory will always write CP, there is no need to set LOST_PINO after creating a directory. Signed-off-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index f0333d1ef222..b08d24388711 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -415,7 +415,8 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, * We lost i_pino from now on. */ if (is_inode_flag_set(inode, FI_INC_LINK)) { - file_lost_pino(inode); + if (!S_ISDIR(inode->i_mode)) + file_lost_pino(inode); /* * If link the tmpfile to alias through linkat path, * we should remove this inode from orphan list. -- GitLab From a867200264d134ab0d04d91c04e8a85c4eba9fe8 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Mon, 26 Jun 2017 10:41:36 +0800 Subject: [PATCH 0735/5498] f2fs: do not set LOST_PINO for renamed dir After renaming a directory, fsck could detect unmatched pino. The scenario can be reproduced as the following: $ mkdir /bar/subbar /foo $ rename /bar/subbar /foo Then fsck will report: [ASSERT] (__chk_dots_dentries:1182) --> Bad inode number[0x3] for '..', parent parent ino is [0x4] Rename sets LOST_PINO for old_inode. However, the flag cannot be cleared, since dir is written back with CP. So, let's get rid of LOST_PINO for a renamed dir and fix the pino directly at the end of rename. Signed-off-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index b5a523972ffd..b571873d15de 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -784,7 +784,10 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } down_write(&F2FS_I(old_inode)->i_sem); - file_lost_pino(old_inode); + if (!old_dir_entry || whiteout) + file_lost_pino(old_inode); + else + F2FS_I(old_inode)->i_pino = new_dir->i_ino; up_write(&F2FS_I(old_inode)->i_sem); old_inode->i_ctime = current_time(old_inode); -- GitLab From 24dcade00ac95c0606473e73a3fe66ec6130f4cb Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 5 Jul 2017 12:17:24 +0800 Subject: [PATCH 0736/5498] Revert "f2fs: fix to clean previous mount option when remount_fs" Don't clear old mount option before parse new option during ->remount_fs like other generic filesystems. This reverts commit 26666c8a4366debae30ae37d0688b2bec92d196a. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a28afef6c268..d05e431bb656 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -833,7 +833,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) clear_sbi_flag(sbi, SBI_NEED_SB_WRITE); } - sbi->mount_opt.opt = 0; default_options(sbi); /* parse mount options */ -- GitLab From 3937a570c4671d7c5b2adf0c425bfd22878fcdc4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 6 Jul 2017 01:11:31 +0800 Subject: [PATCH 0737/5498] f2fs: don't count inode block in in-memory inode.i_blocks Previously, we count all inode consumed blocks including inode block, xattr block, index block, data block into i_blocks, for other generic filesystems, they won't count inode block into i_blocks, so for userspace applications or quota system, they may detect incorrect block count according to i_blocks value in inode. This patch changes to count all blocks into inode.i_blocks excluding inode block, for on-disk i_blocks, we keep counting inode block for backward compatibility. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 22 ++++++++++++---------- fs/f2fs/inode.c | 4 ++-- fs/f2fs/node.c | 16 ++++++---------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0dd20dcb4754..b8cde933752f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1454,8 +1454,6 @@ static inline int check_nid_range(struct f2fs_sb_info *sbi, nid_t nid) return 0; } -#define F2FS_DEFAULT_ALLOCATED_BLOCKS 1 - /* * Check whether the inode has blocks or not */ @@ -1463,8 +1461,7 @@ static inline int F2FS_HAS_BLOCKS(struct inode *inode) { block_t xattr_block = F2FS_I(inode)->i_xattr_nid ? 1 : 0; - return (inode->i_blocks >> F2FS_LOG_SECTORS_PER_BLOCK) > - (F2FS_DEFAULT_ALLOCATED_BLOCKS + xattr_block); + return (inode->i_blocks >> F2FS_LOG_SECTORS_PER_BLOCK) > xattr_block; } static inline bool f2fs_has_xattr_block(unsigned int ofs) @@ -1651,7 +1648,7 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) } static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, - struct inode *inode) + struct inode *inode, bool is_inode) { block_t valid_block_count; unsigned int valid_node_count; @@ -1671,8 +1668,12 @@ static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, return false; } - if (inode) - f2fs_i_blocks_write(inode, 1, true); + if (inode) { + if (is_inode) + f2fs_mark_inode_dirty_sync(inode, true); + else + f2fs_i_blocks_write(inode, 1, true); + } sbi->total_valid_node_count++; sbi->total_valid_block_count++; @@ -1683,15 +1684,16 @@ static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, } static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, - struct inode *inode) + struct inode *inode, bool is_inode) { spin_lock(&sbi->stat_lock); f2fs_bug_on(sbi, !sbi->total_valid_block_count); f2fs_bug_on(sbi, !sbi->total_valid_node_count); - f2fs_bug_on(sbi, !inode->i_blocks); + f2fs_bug_on(sbi, !is_inode && !inode->i_blocks); - f2fs_i_blocks_write(inode, 1, false); + if (!is_inode) + f2fs_i_blocks_write(inode, 1, false); sbi->total_valid_node_count--; sbi->total_valid_block_count--; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 51b0202cfa1b..8b2e7a6a4826 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -130,7 +130,7 @@ static int do_read_inode(struct inode *inode) i_gid_write(inode, le32_to_cpu(ri->i_gid)); set_nlink(inode, le32_to_cpu(ri->i_links)); inode->i_size = le64_to_cpu(ri->i_size); - inode->i_blocks = SECTOR_FROM_BLOCK(le64_to_cpu(ri->i_blocks)); + inode->i_blocks = SECTOR_FROM_BLOCK(le64_to_cpu(ri->i_blocks) - 1); inode->i_atime.tv_sec = le64_to_cpu(ri->i_atime); inode->i_ctime.tv_sec = le64_to_cpu(ri->i_ctime); @@ -268,7 +268,7 @@ int update_inode(struct inode *inode, struct page *node_page) ri->i_gid = cpu_to_le32(i_gid_read(inode)); ri->i_links = cpu_to_le32(inode->i_nlink); ri->i_size = cpu_to_le64(i_size_read(inode)); - ri->i_blocks = cpu_to_le64(SECTOR_TO_BLOCK(inode->i_blocks)); + ri->i_blocks = cpu_to_le64(SECTOR_TO_BLOCK(inode->i_blocks) + 1); if (et) { read_lock(&et->lock); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 68b7ac1aa067..bf02d40c3759 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -678,15 +678,11 @@ static void truncate_node(struct dnode_of_data *dn) struct node_info ni; get_node_info(sbi, dn->nid, &ni); - if (dn->inode->i_blocks == 0) { - f2fs_bug_on(sbi, ni.blk_addr != NULL_ADDR); - goto invalidate; - } f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); /* Deallocate node address */ invalidate_blocks(sbi, ni.blk_addr); - dec_valid_node_count(sbi, dn->inode); + dec_valid_node_count(sbi, dn->inode, dn->nid == dn->inode->i_ino); set_node_addr(sbi, &ni, NULL_ADDR, false); if (dn->nid == dn->inode->i_ino) { @@ -694,7 +690,7 @@ static void truncate_node(struct dnode_of_data *dn) dec_valid_inode_count(sbi); f2fs_inode_synced(dn->inode); } -invalidate: + clear_node_page_dirty(dn->node_page); set_sbi_flag(sbi, SBI_IS_DIRTY); @@ -1044,7 +1040,7 @@ struct page *new_node_page(struct dnode_of_data *dn, if (!page) return ERR_PTR(-ENOMEM); - if (unlikely(!inc_valid_node_count(sbi, dn->inode))) { + if (unlikely(!inc_valid_node_count(sbi, dn->inode, !ofs))) { err = -ENOSPC; goto fail; } @@ -2216,14 +2212,14 @@ int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) get_node_info(sbi, prev_xnid, &ni); f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); invalidate_blocks(sbi, ni.blk_addr); - dec_valid_node_count(sbi, inode); + dec_valid_node_count(sbi, inode, false); set_node_addr(sbi, &ni, NULL_ADDR, false); recover_xnid: /* 2: update xattr nid in inode */ remove_free_nid(sbi, new_xnid); f2fs_i_xnid_write(inode, new_xnid); - if (unlikely(!inc_valid_node_count(sbi, inode))) + if (unlikely(!inc_valid_node_count(sbi, inode, false))) f2fs_bug_on(sbi, 1); update_inode_page(inode); @@ -2281,7 +2277,7 @@ retry: new_ni = old_ni; new_ni.ino = ino; - if (unlikely(!inc_valid_node_count(sbi, NULL))) + if (unlikely(!inc_valid_node_count(sbi, NULL, true))) WARN_ON(1); set_node_addr(sbi, &new_ni, NEW_ADDR, false); inc_valid_inode_count(sbi); -- GitLab From c7062628d09f5714597e2be28d582aa9f1907e12 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 6 Jul 2017 14:46:01 -0700 Subject: [PATCH 0738/5498] f2fs: relax migratepage for atomic written page In order to avoid lock contention for atomic written pages, we'd better give EBUSY in f2fs_migrate_page when mode is asynchronous. We expect it will be released soon as transaction commits. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f7fc1c0b45e3..3bfe5dfdacd6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2194,8 +2194,12 @@ int f2fs_migrate_page(struct address_space *mapping, BUG_ON(PageWriteback(page)); /* migrating an atomic written page is safe with the inmem_lock hold */ - if (atomic_written && !mutex_trylock(&fi->inmem_lock)) - return -EAGAIN; + if (atomic_written) { + if (mode != MIGRATE_SYNC) + return -EBUSY; + if (!mutex_trylock(&fi->inmem_lock)) + return -EAGAIN; + } /* * A reference is expected if PagePrivate set when move mapping, -- GitLab From 5b734573c15cef4caa673bf8b3eba9242f2d398f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 7 Jul 2017 14:10:15 +0800 Subject: [PATCH 0739/5498] f2fs: use spin_{,un}lock_irq{save,restore} generic/361 reports below warning, this is because: once, there is someone entering into critical region of sbi.cp_lock, if write_end_io. f2fs_stop_checkpoint is invoked from an triggered IRQ, we will encounter deadlock. So this patch changes to use spin_{,un}lock_irq{save,restore} to create critical region without IRQ enabled to avoid potential deadlock. irq event stamp: 83391573 loop: Write error at byte offset 438729728, length 1024. hardirqs last enabled at (83391573): [] restore_all+0xf/0x65 hardirqs last disabled at (83391572): [] reschedule_interrupt+0x30/0x3c loop: Write error at byte offset 438860288, length 1536. softirqs last enabled at (83389244): [] __do_softirq+0x1ae/0x476 softirqs last disabled at (83389237): [] do_softirq_own_stack+0x2c/0x40 loop: Write error at byte offset 438990848, length 2048. ================================ WARNING: inconsistent lock state 4.12.0-rc2+ #30 Tainted: G O -------------------------------- inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage. xfs_io/7959 [HC1[1]:SC0[0]:HE0:SE1] takes: (&(&sbi->cp_lock)->rlock){?.+...}, at: [] f2fs_stop_checkpoint+0x1c/0x50 [f2fs] {HARDIRQ-ON-W} state was registered at: __lock_acquire+0x527/0x7b0 lock_acquire+0xae/0x220 _raw_spin_lock+0x42/0x50 do_checkpoint+0x165/0x9e0 [f2fs] write_checkpoint+0x33f/0x740 [f2fs] __f2fs_sync_fs+0x92/0x1f0 [f2fs] f2fs_sync_fs+0x12/0x20 [f2fs] sync_filesystem+0x67/0x80 generic_shutdown_super+0x27/0x100 kill_block_super+0x22/0x50 kill_f2fs_super+0x3a/0x40 [f2fs] deactivate_locked_super+0x3d/0x70 deactivate_super+0x40/0x60 cleanup_mnt+0x39/0x70 __cleanup_mnt+0x10/0x20 task_work_run+0x69/0x80 exit_to_usermode_loop+0x57/0x85 do_fast_syscall_32+0x18c/0x1b0 entry_SYSENTER_32+0x4c/0x7b irq event stamp: 1957420 hardirqs last enabled at (1957419): [] _raw_spin_unlock_irq+0x27/0x50 hardirqs last disabled at (1957420): [] call_function_single_interrupt+0x30/0x3c softirqs last enabled at (1953784): [] __do_softirq+0x1ae/0x476 softirqs last disabled at (1953773): [] do_softirq_own_stack+0x2c/0x40 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&(&sbi->cp_lock)->rlock); lock(&(&sbi->cp_lock)->rlock); *** DEADLOCK *** 2 locks held by xfs_io/7959: #0: (sb_writers#13){.+.+.+}, at: [] vfs_write+0x16a/0x190 #1: (&sb->s_type->i_mutex_key#16){+.+.+.}, at: [] f2fs_file_write_iter+0x25/0x140 [f2fs] stack backtrace: CPU: 2 PID: 7959 Comm: xfs_io Tainted: G O 4.12.0-rc2+ #30 Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 Call Trace: dump_stack+0x5f/0x92 print_usage_bug+0x1d3/0x1dd ? check_usage_backwards+0xe0/0xe0 mark_lock+0x23d/0x280 __lock_acquire+0x699/0x7b0 ? __this_cpu_preempt_check+0xf/0x20 ? trace_hardirqs_off_caller+0x91/0xe0 lock_acquire+0xae/0x220 ? f2fs_stop_checkpoint+0x1c/0x50 [f2fs] _raw_spin_lock+0x42/0x50 ? f2fs_stop_checkpoint+0x1c/0x50 [f2fs] f2fs_stop_checkpoint+0x1c/0x50 [f2fs] f2fs_write_end_io+0x147/0x150 [f2fs] bio_endio+0x7a/0x1e0 blk_update_request+0xad/0x410 blk_mq_end_request+0x16/0x60 lo_complete_rq+0x3c/0x70 __blk_mq_complete_request_remote+0x11/0x20 flush_smp_call_function_queue+0x6d/0x120 ? debug_smp_processor_id+0x12/0x20 generic_smp_call_function_single_interrupt+0x12/0x30 smp_call_function_single_interrupt+0x25/0x40 call_function_single_interrupt+0x37/0x3c EIP: _raw_spin_unlock_irq+0x2d/0x50 EFLAGS: 00000296 CPU: 2 EAX: 00000001 EBX: d2ccc51c ECX: 00000001 EDX: c1aacebd ESI: 00000000 EDI: 00000000 EBP: c96c9d1c ESP: c96c9d18 DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 ? inherit_task_group.isra.98.part.99+0x6b/0xb0 __add_to_page_cache_locked+0x1d4/0x290 add_to_page_cache_lru+0x38/0xb0 pagecache_get_page+0x8e/0x200 f2fs_write_begin+0x96/0xf00 [f2fs] ? trace_hardirqs_on_caller+0xdd/0x1c0 ? current_time+0x17/0x50 ? trace_hardirqs_on+0xb/0x10 generic_perform_write+0xa9/0x170 __generic_file_write_iter+0x1a2/0x1f0 ? f2fs_preallocate_blocks+0x137/0x160 [f2fs] f2fs_file_write_iter+0x6e/0x140 [f2fs] ? __lock_acquire+0x429/0x7b0 __vfs_write+0xc1/0x140 vfs_write+0x9b/0x190 SyS_pwrite64+0x63/0xa0 do_fast_syscall_32+0xa1/0x1b0 entry_SYSENTER_32+0x4c/0x7b EIP: 0xb7786c61 EFLAGS: 00000293 CPU: 2 EAX: ffffffda EBX: 00000003 ECX: 08416000 EDX: 00001000 ESI: 18b24000 EDI: 00000000 EBP: 00000003 ESP: bf9b36b0 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b Fixes: aaec2b1d1879 ("f2fs: introduce cp_lock to protect updating of ckpt_flags") Cc: stable@vger.kernel.org Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 11 ++++++----- fs/f2fs/f2fs.h | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 5162106f2d12..e6f59f12ab86 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1054,8 +1054,9 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) { unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + unsigned long flags; - spin_lock(&sbi->cp_lock); + spin_lock_irqsave(&sbi->cp_lock, flags); if ((cpc->reason & CP_UMOUNT) && le32_to_cpu(ckpt->cp_pack_total_block_count) > @@ -1086,14 +1087,14 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* set this flag to activate crc|cp_ver for recovery */ __set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); - spin_unlock(&sbi->cp_lock); + spin_unlock_irqrestore(&sbi->cp_lock, flags); } static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); - unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; + unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num, flags; block_t start_blk; unsigned int data_sum_blocks, orphan_blocks; __u32 crc32 = 0; @@ -1135,12 +1136,12 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* 2 cp + n data seg summary + orphan inode blocks */ data_sum_blocks = npages_for_summary_flush(sbi, false); - spin_lock(&sbi->cp_lock); + spin_lock_irqsave(&sbi->cp_lock, flags); if (data_sum_blocks < NR_CURSEG_DATA_TYPE) __set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); else __clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); - spin_unlock(&sbi->cp_lock); + spin_unlock_irqrestore(&sbi->cp_lock, flags); orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num); ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b8cde933752f..3838e203cdf6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1353,9 +1353,11 @@ static inline void __set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) static inline void set_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) { - spin_lock(&sbi->cp_lock); + unsigned long flags; + + spin_lock_irqsave(&sbi->cp_lock, flags); __set_ckpt_flags(F2FS_CKPT(sbi), f); - spin_unlock(&sbi->cp_lock); + spin_unlock_irqrestore(&sbi->cp_lock, flags); } static inline void __clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) @@ -1369,22 +1371,26 @@ static inline void __clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f static inline void clear_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) { - spin_lock(&sbi->cp_lock); + unsigned long flags; + + spin_lock_irqsave(&sbi->cp_lock, flags); __clear_ckpt_flags(F2FS_CKPT(sbi), f); - spin_unlock(&sbi->cp_lock); + spin_unlock_irqrestore(&sbi->cp_lock, flags); } static inline void disable_nat_bits(struct f2fs_sb_info *sbi, bool lock) { + unsigned long flags; + set_sbi_flag(sbi, SBI_NEED_FSCK); if (lock) - spin_lock(&sbi->cp_lock); + spin_lock_irqsave(&sbi->cp_lock, flags); __clear_ckpt_flags(F2FS_CKPT(sbi), CP_NAT_BITS_FLAG); kfree(NM_I(sbi)->nat_bits); NM_I(sbi)->nat_bits = NULL; if (lock) - spin_unlock(&sbi->cp_lock); + spin_unlock_irqrestore(&sbi->cp_lock, flags); } static inline bool enabled_nat_bits(struct f2fs_sb_info *sbi, -- GitLab From 64af77fce2762b9276bc1178363ec30569dd14c9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 21 Jun 2017 17:52:39 -0700 Subject: [PATCH 0740/5498] f2fs: avoid deadlock caused by lock order of page and lock_op - punch_hole - fill_zero - f2fs_lock_op - get_new_data_page - lock_page - f2fs_write_data_pages - lock_page - do_write_data_page - f2fs_lock_op Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3bfe5dfdacd6..33ca0b70e812 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1400,8 +1400,9 @@ int do_write_data_page(struct f2fs_io_info *fio) } } - if (fio->need_lock == LOCK_REQ) - f2fs_lock_op(fio->sbi); + /* Deadlock due to between page->lock and f2fs_lock_op */ + if (fio->need_lock == LOCK_REQ && !f2fs_trylock_op(fio->sbi)) + return -EAGAIN; err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE); if (err) @@ -1663,7 +1664,7 @@ retry: } done_index = page->index; - +retry_write: lock_page(page); if (unlikely(page->mapping != mapping)) { @@ -1699,6 +1700,15 @@ continue_unlock: unlock_page(page); ret = 0; continue; + } else if (ret == -EAGAIN) { + ret = 0; + if (wbc->sync_mode == WB_SYNC_ALL) { + cond_resched(); + congestion_wait(BLK_RW_ASYNC, + HZ/50); + goto retry_write; + } + continue; } done_index = page->index + 1; done = 1; -- GitLab From bb2d119987c834f45227362f016fcfee3297221f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 9 Jul 2017 00:13:07 +0800 Subject: [PATCH 0741/5498] f2fs: support plain user/group quota This patch adds to support plain user/group quota. Change Note by Jaegeuk Kim. - Use f2fs page cache for quota files in order to consider garbage collection. so, quota files are not tolerable for sudden power-cuts, so user needs to do quotacheck. - setattr() calls dquot_transfer which will transfer inode->i_blocks. We can't reclaim that during f2fs_evict_inode(). So, we need to count node blocks as well in order to match i_blocks with dquot's space. Note that, Chao wrote a patch to count inode->i_blocks without inode block. (f2fs: don't count inode block in in-memory inode.i_blocks) - in f2fs_remount, we need to make RW in prior to dquot_resume. - handle fault_injection case during f2fs_quota_off_umount - TODO: Project quota Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/namei.c fs/f2fs/super.c --- Documentation/filesystems/f2fs.txt | 2 + fs/f2fs/data.c | 10 +- fs/f2fs/f2fs.h | 86 ++++++--- fs/f2fs/file.c | 31 +++- fs/f2fs/inode.c | 5 + fs/f2fs/namei.c | 42 ++++- fs/f2fs/node.c | 9 +- fs/f2fs/super.c | 270 +++++++++++++++++++++++++++++ 8 files changed, 415 insertions(+), 40 deletions(-) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index e7fc81f3fa91..a24117dafc93 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -159,6 +159,8 @@ mode=%s Control block allocation mode which supports "adaptive" writes towards main area. io_bits=%u Set the bit size of write IO requests. It should be set with "mode=lfs". +usrquota Enable plain user disk quota accounting. +grpquota Enable plain group disk quota accounting. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 33ca0b70e812..8724a754bd2a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -491,14 +491,15 @@ void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr) int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + int err; if (!count) return 0; if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC))) return -EPERM; - if (unlikely(!inc_valid_block_count(sbi, dn->inode, &count))) - return -ENOSPC; + if (unlikely((err = inc_valid_block_count(sbi, dn->inode, &count)))) + return err; trace_f2fs_reserve_new_blocks(dn->inode, dn->nid, dn->ofs_in_node, count); @@ -749,6 +750,7 @@ static int __allocate_data_block(struct dnode_of_data *dn) struct node_info ni; pgoff_t fofs; blkcnt_t count = 1; + int err; if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC))) return -EPERM; @@ -757,8 +759,8 @@ static int __allocate_data_block(struct dnode_of_data *dn) if (dn->data_blkaddr == NEW_ADDR) goto alloc; - if (unlikely(!inc_valid_block_count(sbi, dn->inode, &count))) - return -ENOSPC; + if (unlikely((err = inc_valid_block_count(sbi, dn->inode, &count)))) + return err; alloc: get_node_info(sbi, dn->nid, &ni); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3838e203cdf6..ec53b9d32e77 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef CONFIG_F2FS_FS_ENCRYPTION #include #else @@ -90,6 +91,8 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_FAULT_INJECTION 0x00010000 #define F2FS_MOUNT_ADAPTIVE 0x00020000 #define F2FS_MOUNT_LFS 0x00040000 +#define F2FS_MOUNT_USRQUOTA 0x00080000 +#define F2FS_MOUNT_GRPQUOTA 0x00100000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -620,6 +623,10 @@ struct f2fs_inode_info { nid_t i_xattr_nid; /* node id that contains xattrs */ loff_t last_disk_size; /* lastly written file size */ +#ifdef CONFIG_QUOTA + /* quota space reservation, managed internally by quota code */ + qsize_t i_reserved_quota; +#endif struct list_head dirty_list; /* dirty list for dirs and files */ struct list_head gdirty_list; /* linked in global dirty list */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ @@ -1475,17 +1482,23 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs) return ofs == XATTR_NODE_OFFSET; } -static inline void f2fs_i_blocks_write(struct inode *, block_t, bool); -static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, +static inline void f2fs_i_blocks_write(struct inode *, block_t, bool, bool); +static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) { - blkcnt_t diff; + blkcnt_t diff = 0, release = 0; block_t avail_user_block_count; + int ret; + + ret = dquot_reserve_block(inode, *count); + if (ret) + return ret; #ifdef CONFIG_F2FS_FAULT_INJECTION if (time_to_inject(sbi, FAULT_BLOCK)) { f2fs_show_injection_info(FAULT_BLOCK); - return false; + release = *count; + goto enospc; } #endif /* @@ -1500,17 +1513,24 @@ static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { diff = sbi->total_valid_block_count - avail_user_block_count; *count -= diff; + release = diff; sbi->total_valid_block_count = avail_user_block_count; if (!*count) { spin_unlock(&sbi->stat_lock); percpu_counter_sub(&sbi->alloc_valid_block_count, diff); - return false; + goto enospc; } } spin_unlock(&sbi->stat_lock); - f2fs_i_blocks_write(inode, *count, true); - return true; + if (release) + dquot_release_reservation_block(inode, release); + f2fs_i_blocks_write(inode, *count, true, true); + return 0; + +enospc: + dquot_release_reservation_block(inode, release); + return -ENOSPC; } static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, @@ -1524,7 +1544,7 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, inode->i_blocks < sectors); sbi->total_valid_block_count -= (block_t)count; spin_unlock(&sbi->stat_lock); - f2fs_i_blocks_write(inode, count, false); + f2fs_i_blocks_write(inode, count, false, true); } static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) @@ -1653,11 +1673,18 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); } -static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, +static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, struct inode *inode, bool is_inode) { block_t valid_block_count; unsigned int valid_node_count; + bool quota = inode && !is_inode; + + if (quota) { + int ret = dquot_reserve_block(inode, 1); + if (ret) + return ret; + } spin_lock(&sbi->stat_lock); @@ -1665,28 +1692,33 @@ static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, if (unlikely(valid_block_count + sbi->reserved_blocks > sbi->user_block_count)) { spin_unlock(&sbi->stat_lock); - return false; + goto enospc; } valid_node_count = sbi->total_valid_node_count + 1; if (unlikely(valid_node_count > sbi->total_node_count)) { spin_unlock(&sbi->stat_lock); - return false; + goto enospc; } + sbi->total_valid_node_count++; + sbi->total_valid_block_count++; + spin_unlock(&sbi->stat_lock); + if (inode) { if (is_inode) f2fs_mark_inode_dirty_sync(inode, true); else - f2fs_i_blocks_write(inode, 1, true); + f2fs_i_blocks_write(inode, 1, true, true); } - sbi->total_valid_node_count++; - sbi->total_valid_block_count++; - spin_unlock(&sbi->stat_lock); - percpu_counter_inc(&sbi->alloc_valid_block_count); - return true; + return 0; + +enospc: + if (quota) + dquot_release_reservation_block(inode, 1); + return -ENOSPC; } static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, @@ -1698,12 +1730,13 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, !sbi->total_valid_node_count); f2fs_bug_on(sbi, !is_inode && !inode->i_blocks); - if (!is_inode) - f2fs_i_blocks_write(inode, 1, false); sbi->total_valid_node_count--; sbi->total_valid_block_count--; spin_unlock(&sbi->stat_lock); + + if (!is_inode) + f2fs_i_blocks_write(inode, 1, false, true); } static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi) @@ -1977,14 +2010,21 @@ static inline void f2fs_i_links_write(struct inode *inode, bool inc) } static inline void f2fs_i_blocks_write(struct inode *inode, - block_t diff, bool add) + block_t diff, bool add, bool claim) { bool clean = !is_inode_flag_set(inode, FI_DIRTY_INODE); bool recover = is_inode_flag_set(inode, FI_AUTO_RECOVER); - blkcnt_t sectors = diff << F2FS_LOG_SECTORS_PER_BLOCK; - inode->i_blocks = add ? inode->i_blocks + sectors : - inode->i_blocks - sectors; + /* add = 1, claim = 1 should be dquot_reserve_block in pair */ + if (add) { + if (claim) + dquot_claim_block(inode, diff); + else + dquot_alloc_block_nofail(inode, diff); + } else { + dquot_free_block(inode, diff); + } + f2fs_mark_inode_dirty_sync(inode, true); if (clean || recover) set_inode_flag(inode, FI_AUTO_RECOVER); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f17236590762..871f1eccf7bc 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -445,11 +445,10 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) static int f2fs_file_open(struct inode *inode, struct file *filp) { - int ret = generic_file_open(inode, filp); struct dentry *dir; - if (!ret && f2fs_encrypted_inode(inode)) { - ret = fscrypt_get_encryption_info(inode); + if (f2fs_encrypted_inode(inode)) { + int ret = fscrypt_get_encryption_info(inode); if (ret) return -EACCES; if (!fscrypt_has_encryption_key(inode)) @@ -462,7 +461,7 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) return -EPERM; } dput(dir); - return ret; + return dquot_file_open(inode, filp); } int truncate_data_blocks_range(struct dnode_of_data *dn, int count) @@ -692,6 +691,17 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; + if (is_quota_modification(inode, attr)) + dquot_initialize(inode); + if ((attr->ia_valid & ATTR_UID && + !uid_eq(attr->ia_uid, inode->i_uid)) || + (attr->ia_valid & ATTR_GID && + !gid_eq(attr->ia_gid, inode->i_gid))) { + err = dquot_transfer(inode, attr); + if (err) + return err; + } + if (attr->ia_valid & ATTR_SIZE) { if (f2fs_encrypted_inode(inode)) { err = fscrypt_get_encryption_info(inode); @@ -981,9 +991,9 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode, if (do_replace[i]) { f2fs_i_blocks_write(src_inode, - 1, false); + 1, false, false); f2fs_i_blocks_write(dst_inode, - 1, true); + 1, true, false); f2fs_replace_block(sbi, &dn, dn.data_blkaddr, blkaddr[i], ni.version, true, false); @@ -1518,6 +1528,13 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) inode_lock(inode); + /* Is it quota file? Do not allow user to mess with it */ + if (IS_NOQUOTA(inode)) { + inode_unlock(inode); + ret = -EPERM; + goto unlock_out; + } + flags = f2fs_mask_flags(inode->i_mode, flags); oldflags = fi->i_flags; @@ -1537,7 +1554,7 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) inode->i_ctime = current_time(inode); f2fs_set_inode_flags(inode); f2fs_mark_inode_dirty_sync(inode, false); - +unlock_out: inode_unlock(inode); out: mnt_drop_write_file(filp); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 8b2e7a6a4826..d01d93ede6ee 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -373,6 +373,8 @@ void f2fs_evict_inode(struct inode *inode) if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; + dquot_initialize(inode); + remove_ino_entry(sbi, inode->i_ino, APPEND_INO); remove_ino_entry(sbi, inode->i_ino, UPDATE_INO); @@ -405,8 +407,11 @@ retry: if (err) update_inode_page(inode); + dquot_free_inode(inode); sb_end_intwrite(inode->i_sb); no_delete: + dquot_drop(inode); + stat_dec_inline_xattr(inode); stat_dec_inline_dir(inode); stat_dec_inline_inode(inode); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index b571873d15de..a88eb0a9d7e5 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "f2fs.h" #include "node.h" @@ -43,6 +44,8 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) } f2fs_unlock_op(sbi); + nid_free = true; + inode_init_owner(inode, dir, mode); inode->i_ino = ino; @@ -53,10 +56,15 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) err = insert_inode_locked(inode); if (err) { err = -EINVAL; - nid_free = true; goto fail; } + dquot_initialize(inode); + + err = dquot_alloc_inode(inode); + if (err) + goto fail_drop; + /* If the directory encrypted, then we should encrypt the inode. */ if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) f2fs_set_encrypted_inode(inode); @@ -86,6 +94,16 @@ fail: set_inode_flag(inode, FI_FREE_NID); iput(inode); return ERR_PTR(err); +fail_drop: + trace_f2fs_new_inode(inode, err); + dquot_drop(inode); + inode->i_flags |= S_NOQUOTA; + if (nid_free) + set_inode_flag(inode, FI_FREE_NID); + clear_nlink(inode); + unlock_new_inode(inode); + iput(inode); + return ERR_PTR(err); } static int is_multimedia_file(const unsigned char *s, const char *sub) @@ -137,6 +155,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, nid_t ino = 0; int err; + dquot_initialize(dir); + inode = f2fs_new_inode(dir, mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -181,6 +201,8 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, !fscrypt_has_permitted_context(dir, inode)) return -EPERM; + dquot_initialize(dir); + f2fs_balance_fs(sbi, true); inode->i_ctime = current_time(inode); @@ -348,6 +370,8 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) trace_f2fs_unlink_enter(dir, dentry); + dquot_initialize(dir); + de = f2fs_find_entry(dir, &dentry->d_name, &page); if (!de) { if (IS_ERR(page)) @@ -422,6 +446,8 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, if (disk_link.len > dir->i_sb->s_blocksize) return -ENAMETOOLONG; + dquot_initialize(dir); + inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -509,6 +535,8 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) struct inode *inode; int err; + dquot_initialize(dir); + inode = f2fs_new_inode(dir, S_IFDIR | mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -560,6 +588,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, if (!new_valid_dev(rdev)) return -EINVAL; + dquot_initialize(dir); + inode = f2fs_new_inode(dir, mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -595,6 +625,8 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, struct inode *inode; int err; + dquot_initialize(dir); + inode = f2fs_new_inode(dir, mode); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -688,6 +720,10 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } + dquot_initialize(old_dir); + + dquot_initialize(new_dir); + old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) { if (IS_ERR(old_page)) @@ -868,6 +904,10 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, !fscrypt_has_permitted_context(old_dir, new_inode))) return -EPERM; + dquot_initialize(old_dir); + + dquot_initialize(new_dir); + old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) { if (IS_ERR(old_page)) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index bf02d40c3759..7b38ebe9163c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1040,10 +1040,9 @@ struct page *new_node_page(struct dnode_of_data *dn, if (!page) return ERR_PTR(-ENOMEM); - if (unlikely(!inc_valid_node_count(sbi, dn->inode, !ofs))) { - err = -ENOSPC; + if (unlikely((err = inc_valid_node_count(sbi, dn->inode, !ofs)))) goto fail; - } + #ifdef CONFIG_F2FS_CHECK_FS get_node_info(sbi, dn->nid, &new_ni); f2fs_bug_on(sbi, new_ni.blk_addr != NULL_ADDR); @@ -2219,7 +2218,7 @@ recover_xnid: /* 2: update xattr nid in inode */ remove_free_nid(sbi, new_xnid); f2fs_i_xnid_write(inode, new_xnid); - if (unlikely(!inc_valid_node_count(sbi, inode, false))) + if (unlikely(inc_valid_node_count(sbi, inode, false))) f2fs_bug_on(sbi, 1); update_inode_page(inode); @@ -2277,7 +2276,7 @@ retry: new_ni = old_ni; new_ni.ino = ino; - if (unlikely(!inc_valid_node_count(sbi, NULL, true))) + if (unlikely(inc_valid_node_count(sbi, NULL, true))) WARN_ON(1); set_node_addr(sbi, &new_ni, NEW_ADDR, false); inc_valid_inode_count(sbi); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d05e431bb656..13a61c3cc8af 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -104,6 +105,8 @@ enum { Opt_mode, Opt_io_size_bits, Opt_fault_injection, + Opt_usrquota, + Opt_grpquota, Opt_err, }; @@ -137,6 +140,8 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, + {Opt_usrquota, "usrquota"}, + {Opt_grpquota, "grpquota"}, {Opt_err, NULL}, }; @@ -370,6 +375,20 @@ static int parse_options(struct super_block *sb, char *options) "FAULT_INJECTION was not selected"); #endif break; +#ifdef CONFIG_QUOTA + case Opt_usrquota: + set_opt(sbi, USRQUOTA); + break; + case Opt_grpquota: + set_opt(sbi, GRPQUOTA); + break; +#else + case Opt_usrquota: + case Opt_grpquota: + f2fs_msg(sb, KERN_INFO, + "quota operations not supported"); + break; +#endif default: f2fs_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" or missing value", @@ -411,6 +430,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_rwsem(&fi->dio_rwsem[WRITE]); init_rwsem(&fi->i_mmap_sem); +#ifdef CONFIG_QUOTA + fi->i_reserved_quota = 0; +#endif /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; return &fi->vfs_inode; @@ -548,11 +570,14 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) kfree(sbi->devs); } +static void f2fs_quota_off_umount(struct super_block *sb); static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); int i; + f2fs_quota_off_umount(sb); + /* prevent remaining shrinker jobs */ mutex_lock(&sbi->umount_mutex); @@ -770,6 +795,12 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_printf(seq, ",fault_injection=%u", sbi->fault_info.inject_rate); #endif +#ifdef CONFIG_QUOTA + if (test_opt(sbi, USRQUOTA)) + seq_puts(seq, ",usrquota"); + if (test_opt(sbi, GRPQUOTA)) + seq_puts(seq, ",grpquota"); +#endif return 0; } @@ -809,6 +840,7 @@ 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; + unsigned long old_sb_flags; int err, active_logs; bool need_restart_gc = false; bool need_stop_gc = false; @@ -822,6 +854,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) * need to restore them. */ org_mount_opt = sbi->mount_opt; + old_sb_flags = sb->s_flags; active_logs = sbi->active_logs; /* recover superblocks we couldn't write due to previous RO mount */ @@ -847,6 +880,16 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) if (f2fs_readonly(sb) && (*flags & MS_RDONLY)) goto skip; + if (!f2fs_readonly(sb) && (*flags & MS_RDONLY)) { + err = dquot_suspend(sb, -1); + if (err < 0) + goto restore_opts; + } else { + /* dquot_resume needs RW */ + sb->s_flags &= ~MS_RDONLY; + dquot_resume(sb, -1); + } + /* disallow enable/disable extent_cache dynamically */ if (no_extent_cache == !!test_opt(sbi, EXTENT_CACHE)) { err = -EINVAL; @@ -911,12 +954,230 @@ restore_gc: restore_opts: sbi->mount_opt = org_mount_opt; sbi->active_logs = active_logs; + sb->s_flags = old_sb_flags; #ifdef CONFIG_F2FS_FAULT_INJECTION sbi->fault_info = ffi; #endif return err; } +#ifdef CONFIG_QUOTA +/* Read data from quotafile */ +static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data, + size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + struct address_space *mapping = inode->i_mapping; + block_t blkidx = F2FS_BYTES_TO_BLK(off); + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t toread; + loff_t i_size = i_size_read(inode); + struct page *page; + char *kaddr; + + if (off > i_size) + return 0; + + if (off + len > i_size) + len = i_size - off; + toread = len; + while (toread > 0) { + tocopy = min_t(unsigned long, sb->s_blocksize - offset, toread); +repeat: + page = read_mapping_page(mapping, blkidx, NULL); + if (IS_ERR(page)) + return PTR_ERR(page); + + lock_page(page); + + if (unlikely(page->mapping != mapping)) { + f2fs_put_page(page, 1); + goto repeat; + } + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 1); + return -EIO; + } + + kaddr = kmap_atomic(page); + memcpy(data, kaddr + offset, tocopy); + kunmap_atomic(kaddr); + f2fs_put_page(page, 1); + + offset = 0; + toread -= tocopy; + data += tocopy; + blkidx++; + } + return len; +} + +/* Write to quotafile */ +static ssize_t f2fs_quota_write(struct super_block *sb, int type, + const char *data, size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + struct address_space *mapping = inode->i_mapping; + const struct address_space_operations *a_ops = mapping->a_ops; + int offset = off & (sb->s_blocksize - 1); + size_t towrite = len; + struct page *page; + char *kaddr; + int err = 0; + int tocopy; + + while (towrite > 0) { + tocopy = min_t(unsigned long, sb->s_blocksize - offset, + towrite); + + err = a_ops->write_begin(NULL, mapping, off, tocopy, 0, + &page, NULL); + if (unlikely(err)) + break; + + kaddr = kmap_atomic(page); + memcpy(kaddr + offset, data, tocopy); + kunmap_atomic(kaddr); + flush_dcache_page(page); + + a_ops->write_end(NULL, mapping, off, tocopy, tocopy, + page, NULL); + offset = 0; + towrite -= tocopy; + off += tocopy; + data += tocopy; + cond_resched(); + } + + if (len == towrite) + return err; + inode->i_version++; + inode->i_mtime = inode->i_ctime = current_time(inode); + f2fs_mark_inode_dirty_sync(inode, false); + return len - towrite; +} + +static qsize_t *f2fs_get_reserved_space(struct inode *inode) +{ + return &F2FS_I(inode)->i_reserved_quota; +} + +static int f2fs_quota_sync(struct super_block *sb, int type) +{ + struct quota_info *dqopt = sb_dqopt(sb); + int cnt; + int ret; + + ret = dquot_writeback_dquots(sb, type); + if (ret) + return ret; + + /* + * Now when everything is written we can discard the pagecache so + * that userspace sees the changes. + */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (type != -1 && cnt != type) + continue; + if (!sb_has_quota_active(sb, cnt)) + continue; + + ret = filemap_write_and_wait(dqopt->files[cnt]->i_mapping); + if (ret) + return ret; + + inode_lock(dqopt->files[cnt]); + truncate_inode_pages(&dqopt->files[cnt]->i_data, 0); + inode_unlock(dqopt->files[cnt]); + } + return 0; +} + +static int f2fs_quota_on(struct super_block *sb, int type, int format_id, + struct path *path) +{ + struct inode *inode; + int err; + + err = f2fs_quota_sync(sb, -1); + if (err) + return err; + + err = dquot_quota_on(sb, type, format_id, path); + if (err) + return err; + + inode = d_inode(path->dentry); + + inode_lock(inode); + F2FS_I(inode)->i_flags |= FS_NOATIME_FL | FS_IMMUTABLE_FL; + inode_set_flags(inode, S_NOATIME | S_IMMUTABLE, + S_NOATIME | S_IMMUTABLE); + inode_unlock(inode); + f2fs_mark_inode_dirty_sync(inode, false); + + return 0; +} + +static int f2fs_quota_off(struct super_block *sb, int type) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + int err; + + if (!inode || !igrab(inode)) + return dquot_quota_off(sb, type); + + f2fs_quota_sync(sb, -1); + + err = dquot_quota_off(sb, type); + if (err) + goto out_put; + + inode_lock(inode); + F2FS_I(inode)->i_flags &= ~(FS_NOATIME_FL | FS_IMMUTABLE_FL); + inode_set_flags(inode, 0, S_NOATIME | S_IMMUTABLE); + inode_unlock(inode); + f2fs_mark_inode_dirty_sync(inode, false); +out_put: + iput(inode); + return err; +} + +static void f2fs_quota_off_umount(struct super_block *sb) +{ + int type; + + for (type = 0; type < MAXQUOTAS; type++) + f2fs_quota_off(sb, type); +} + +static const struct dquot_operations f2fs_quota_operations = { + .get_reserved_space = f2fs_get_reserved_space, + .write_dquot = dquot_commit, + .acquire_dquot = dquot_acquire, + .release_dquot = dquot_release, + .mark_dirty = dquot_mark_dquot_dirty, + .write_info = dquot_commit_info, + .alloc_dquot = dquot_alloc, + .destroy_dquot = dquot_destroy, +}; + +static const struct quotactl_ops f2fs_quotactl_ops = { + .quota_on = f2fs_quota_on, + .quota_off = f2fs_quota_off, + .quota_sync = f2fs_quota_sync, + .get_info = dquot_get_dqinfo, + .set_info = dquot_set_dqinfo, + .get_dqblk = dquot_get_dqblk, + .set_dqblk = dquot_set_dqblk, +}; +#else +static inline void f2fs_quota_off_umount(struct super_block *sb) +{ +} +#endif + static struct super_operations f2fs_sops = { .alloc_inode = f2fs_alloc_inode, .drop_inode = f2fs_drop_inode, @@ -924,6 +1185,10 @@ static struct super_operations f2fs_sops = { .write_inode = f2fs_write_inode, .dirty_inode = f2fs_dirty_inode, .show_options = f2fs_show_options, +#ifdef CONFIG_QUOTA + .quota_read = f2fs_quota_read, + .quota_write = f2fs_quota_write, +#endif .evict_inode = f2fs_evict_inode, .put_super = f2fs_put_super, .sync_fs = f2fs_sync_fs, @@ -1670,6 +1935,11 @@ try_onemore: sb->s_max_links = F2FS_LINK_MAX; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); +#ifdef CONFIG_QUOTA + sb->dq_op = &f2fs_quota_operations; + sb->s_qcop = &f2fs_quotactl_ops; +#endif + sb->s_op = &f2fs_sops; sb->s_cop = &f2fs_cryptops; sb->s_xattr = f2fs_xattr_handlers; -- GitLab From 8f294e1b74677070c3c21e66c41c54e03c6f376b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 10 Jul 2017 19:16:28 -0700 Subject: [PATCH 0742/5498] f2fs: make more close to v4.13-rc1 Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 4 ++-- fs/f2fs/f2fs.h | 15 +++++++-------- fs/f2fs/file.c | 4 ++-- fs/f2fs/namei.c | 4 ++-- fs/f2fs/node.c | 6 +++--- fs/f2fs/segment.c | 18 +++++++++--------- fs/f2fs/super.c | 8 ++++---- 7 files changed, 29 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index b08d24388711..1380c442648b 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -248,7 +248,7 @@ out: * Entry is guaranteed to be valid. */ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, - struct qstr *child, struct page **res_page) + const struct qstr *child, struct page **res_page) { struct f2fs_dir_entry *de = NULL; struct fscrypt_name fname; @@ -276,7 +276,7 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p) return f2fs_find_entry(dir, &dotdot, p); } -ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr, +ino_t f2fs_inode_by_name(struct inode *dir, const struct qstr *qstr, struct page **page) { ino_t res = 0; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ec53b9d32e77..8907042ea571 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2238,13 +2238,11 @@ static inline bool is_dot_dotdot(const struct qstr *str) static inline bool f2fs_may_extent_tree(struct inode *inode) { - mode_t mode = inode->i_mode; - if (!test_opt(F2FS_I_SB(inode), EXTENT_CACHE) || is_inode_flag_set(inode, FI_NO_EXTENT)) return false; - return S_ISREG(mode); + return S_ISREG(inode->i_mode); } static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi, @@ -2259,7 +2257,7 @@ static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi, return kmalloc(size, flags); } -static inline void *f2fs_kvmalloc(size_t size, gfp_t flags) +static inline void *kvmalloc(size_t size, gfp_t flags) { void *ret; @@ -2269,7 +2267,7 @@ static inline void *f2fs_kvmalloc(size_t size, gfp_t flags) return ret; } -static inline void *f2fs_kvzalloc(size_t size, gfp_t flags) +static inline void *kvzalloc(size_t size, gfp_t flags) { void *ret; @@ -2338,9 +2336,9 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode); struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir, struct fscrypt_name *fname, struct page **res_page); struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, - struct qstr *child, struct page **res_page); + const struct qstr *child, struct page **res_page); struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p); -ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr, +ino_t f2fs_inode_by_name(struct inode *dir, const struct qstr *qstr, struct page **page); void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, struct page *page, struct inode *inode); @@ -2916,11 +2914,12 @@ static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt) static inline bool f2fs_may_encrypt(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION - mode_t mode = inode->i_mode; + umode_t mode = inode->i_mode; return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)); #else return 0; #endif } + #endif diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 871f1eccf7bc..faa91ce092a9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1045,11 +1045,11 @@ static int __exchange_data_block(struct inode *src_inode, while (len) { olen = min((pgoff_t)4 * ADDRS_PER_BLOCK, len); - src_blkaddr = f2fs_kvzalloc(sizeof(block_t) * olen, GFP_KERNEL); + src_blkaddr = kvzalloc(sizeof(block_t) * olen, GFP_KERNEL); if (!src_blkaddr) return -ENOMEM; - do_replace = f2fs_kvzalloc(sizeof(int) * olen, GFP_KERNEL); + do_replace = kvzalloc(sizeof(int) * olen, GFP_KERNEL); if (!do_replace) { kvfree(src_blkaddr); return -ENOMEM; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index a88eb0a9d7e5..16b5f17680a1 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -979,7 +979,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, file_lost_pino(old_inode); up_write(&F2FS_I(old_inode)->i_sem); - old_dir->i_ctime = CURRENT_TIME; + old_dir->i_ctime = current_time(old_dir); if (old_nlink) { down_write(&F2FS_I(old_dir)->i_sem); f2fs_i_links_write(old_dir, old_nlink > 0); @@ -994,7 +994,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, file_lost_pino(new_inode); up_write(&F2FS_I(new_inode)->i_sem); - new_dir->i_ctime = CURRENT_TIME; + new_dir->i_ctime = current_time(new_dir); if (new_nlink) { down_write(&F2FS_I(new_dir)->i_sem); f2fs_i_links_write(new_dir, new_nlink > 0); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7b38ebe9163c..77d27551b09b 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2670,17 +2670,17 @@ static int init_free_nid_cache(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); - nm_i->free_nid_bitmap = f2fs_kvzalloc(nm_i->nat_blocks * + nm_i->free_nid_bitmap = kvzalloc(nm_i->nat_blocks * NAT_ENTRY_BITMAP_SIZE, GFP_KERNEL); if (!nm_i->free_nid_bitmap) return -ENOMEM; - nm_i->nat_block_bitmap = f2fs_kvzalloc(nm_i->nat_blocks / 8, + nm_i->nat_block_bitmap = kvzalloc(nm_i->nat_blocks / 8, GFP_KERNEL); if (!nm_i->nat_block_bitmap) return -ENOMEM; - nm_i->free_nid_count = f2fs_kvzalloc(nm_i->nat_blocks * + nm_i->free_nid_count = kvzalloc(nm_i->nat_blocks * sizeof(unsigned short), GFP_KERNEL); if (!nm_i->free_nid_count) return -ENOMEM; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index b0656282b280..ed15e840f9a7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1243,8 +1243,8 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, sector = SECTOR_FROM_BLOCK(blkstart); nr_sects = SECTOR_FROM_BLOCK(blklen); - if (sector & (bdev_zone_size(bdev) - 1) || - nr_sects != bdev_zone_size(bdev)) { + if (sector & (bdev_zone_sectors(bdev) - 1) || + nr_sects != bdev_zone_sectors(bdev)) { f2fs_msg(sbi->sb, KERN_INFO, "(%d) %s: Unaligned discard attempted (block %x + %x)", devi, sbi->s_ndevs ? FDEV(devi).path: "", @@ -2998,13 +2998,13 @@ static int build_sit_info(struct f2fs_sb_info *sbi) SM_I(sbi)->sit_info = sit_i; - sit_i->sentries = f2fs_kvzalloc(MAIN_SEGS(sbi) * + sit_i->sentries = 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 = f2fs_kvzalloc(bitmap_size, GFP_KERNEL); + sit_i->dirty_sentries_bitmap = kvzalloc(bitmap_size, GFP_KERNEL); if (!sit_i->dirty_sentries_bitmap) return -ENOMEM; @@ -3037,7 +3037,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) return -ENOMEM; if (sbi->segs_per_sec > 1) { - sit_i->sec_entries = f2fs_kvzalloc(MAIN_SECS(sbi) * + sit_i->sec_entries = kvzalloc(MAIN_SECS(sbi) * sizeof(struct sec_entry), GFP_KERNEL); if (!sit_i->sec_entries) return -ENOMEM; @@ -3088,12 +3088,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 = f2fs_kvmalloc(bitmap_size, GFP_KERNEL); + free_i->free_segmap = 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 = f2fs_kvmalloc(sec_bitmap_size, GFP_KERNEL); + free_i->free_secmap = kvmalloc(sec_bitmap_size, GFP_KERNEL); if (!free_i->free_secmap) return -ENOMEM; @@ -3273,7 +3273,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 = f2fs_kvzalloc(bitmap_size, GFP_KERNEL); + dirty_i->victim_secmap = kvzalloc(bitmap_size, GFP_KERNEL); if (!dirty_i->victim_secmap) return -ENOMEM; return 0; @@ -3295,7 +3295,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] = f2fs_kvzalloc(bitmap_size, GFP_KERNEL); + dirty_i->dirty_segmap[i] = kvzalloc(bitmap_size, GFP_KERNEL); if (!dirty_i->dirty_segmap[i]) return -ENOMEM; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 13a61c3cc8af..94042e4682fd 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1617,16 +1617,16 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi) return 0; if (sbi->blocks_per_blkz && sbi->blocks_per_blkz != - SECTOR_TO_BLOCK(bdev_zone_size(bdev))) + SECTOR_TO_BLOCK(bdev_zone_sectors(bdev))) return -EINVAL; - sbi->blocks_per_blkz = SECTOR_TO_BLOCK(bdev_zone_size(bdev)); + sbi->blocks_per_blkz = SECTOR_TO_BLOCK(bdev_zone_sectors(bdev)); if (sbi->log_blocks_per_blkz && sbi->log_blocks_per_blkz != __ilog2_u32(sbi->blocks_per_blkz)) return -EINVAL; sbi->log_blocks_per_blkz = __ilog2_u32(sbi->blocks_per_blkz); FDEV(devi).nr_blkz = SECTOR_TO_BLOCK(nr_sectors) >> sbi->log_blocks_per_blkz; - if (nr_sectors & (bdev_zone_size(bdev) - 1)) + if (nr_sectors & (bdev_zone_sectors(bdev) - 1)) FDEV(devi).nr_blkz++; FDEV(devi).blkz_type = kmalloc(FDEV(devi).nr_blkz, GFP_KERNEL); @@ -1768,7 +1768,7 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) /* Initialize single device information */ if (!RDEV(0).path[0]) { #ifdef CONFIG_BLK_DEV_ZONED - if (bdev_zoned_model(sbi->sb->s_bdev) == BLK_ZONED_NONE) + if (!bdev_is_zoned(sbi->sb->s_bdev)) return 0; max_devices = 1; #else -- GitLab From 5d0a8710a9a48090a27e45c562721f64c6bb93e3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 22 May 2017 18:14:06 -0700 Subject: [PATCH 0743/5498] fscrypt: inline fscrypt_free_filename() fscrypt_free_filename() only needs to do a kfree() of crypto_buf.name, which works well as an inline function. We can skip setting the various pointers to NULL, since no user cares about it (the name is always freed just before it goes out of scope). Signed-off-by: Eric Biggers Reviewed-by: David Gstir Signed-off-by: Theodore Ts'o --- fs/crypto/fname.c | 9 --------- include/linux/fscrypt_supp.h | 7 ++++++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 17905ce0b92c..cef1d3e8c783 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -453,12 +453,3 @@ errout: return ret; } EXPORT_SYMBOL(fscrypt_setup_filename); - -void fscrypt_free_filename(struct fscrypt_name *fname) -{ - kfree(fname->crypto_buf.name); - fname->crypto_buf.name = NULL; - fname->usr_fname = NULL; - fname->disk_name.name = NULL; -} -EXPORT_SYMBOL(fscrypt_free_filename); diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index e12c224a0d1e..6828dc6111fa 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -47,7 +47,12 @@ extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *); /* fname.c */ extern int fscrypt_setup_filename(struct inode *, const struct qstr *, int lookup, struct fscrypt_name *); -extern void fscrypt_free_filename(struct fscrypt_name *); + +static inline void fscrypt_free_filename(struct fscrypt_name *fname) +{ + kfree(fname->crypto_buf.name); +} + extern u32 fscrypt_fname_encrypted_size(const struct inode *, u32); extern int fscrypt_fname_alloc_buffer(const struct inode *, u32, struct fscrypt_str *); -- GitLab From df5140fb1532d609eb52780b5ecfe676c79d4644 Mon Sep 17 00:00:00 2001 From: Daniel Walter Date: Mon, 19 Jun 2017 09:27:58 +0200 Subject: [PATCH 0744/5498] fscrypt: add support for AES-128-CBC fscrypt provides facilities to use different encryption algorithms which are selectable by userspace when setting the encryption policy. Currently, only AES-256-XTS for file contents and AES-256-CBC-CTS for file names are implemented. This is a clear case of kernel offers the mechanism and userspace selects a policy. Similar to what dm-crypt and ecryptfs have. This patch adds support for using AES-128-CBC for file contents and AES-128-CBC-CTS for file name encryption. To mitigate watermarking attacks, IVs are generated using the ESSIV algorithm. While AES-CBC is actually slightly less secure than AES-XTS from a security point of view, there is more widespread hardware support. Using AES-CBC gives us the acceptable performance while still providing a moderate level of security for persistent storage. Especially low-powered embedded devices with crypto accelerators such as CAAM or CESA often only support AES-CBC. Since using AES-CBC over AES-XTS is basically thought of a last resort, we use AES-128-CBC over AES-256-CBC since it has less encryption rounds and yields noticeable better performance starting from a file size of just a few kB. Signed-off-by: Daniel Walter [david@sigma-star.at: addressed review comments] Signed-off-by: David Gstir Reviewed-by: Eric Biggers Signed-off-by: Theodore Ts'o Conflicts: fs/crypto/crypto.c fs/crypto/fscrypt_private.h fs/crypto/keyinfo.c --- fs/crypto/crypto.c | 23 +++-- fs/crypto/fscrypt_private.h | 9 +- fs/crypto/keyinfo.c | 173 +++++++++++++++++++++++++-------- fs/crypto/policy.c | 8 +- include/linux/fscrypt_common.h | 16 +-- include/uapi/linux/fs.h | 2 + 6 files changed, 173 insertions(+), 58 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 2100bc8ac7b1..5182c8e07eba 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "fscrypt_private.h" static unsigned int num_prealloc_crypto_pages = 32; @@ -147,8 +148,8 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, { struct { __le64 index; - u8 padding[FS_XTS_TWEAK_SIZE - sizeof(__le64)]; - } xts_tweak; + u8 padding[FS_IV_SIZE - sizeof(__le64)]; + } iv; struct ablkcipher_request *req = NULL; DECLARE_FS_COMPLETION_RESULT(ecr); struct scatterlist dst, src; @@ -158,6 +159,16 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, BUG_ON(len == 0); + BUILD_BUG_ON(sizeof(iv) != FS_IV_SIZE); + BUILD_BUG_ON(AES_BLOCK_SIZE != FS_IV_SIZE); + iv.index = cpu_to_le64(lblk_num); + memset(iv.padding, 0, sizeof(iv.padding)); + + if (ci->ci_essiv_tfm != NULL) { + crypto_cipher_encrypt_one(ci->ci_essiv_tfm, (u8 *)&iv, + (u8 *)&iv); + } + req = ablkcipher_request_alloc(tfm, gfp_flags); if (!req) { printk_ratelimited(KERN_ERR @@ -170,15 +181,11 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, page_crypt_complete, &ecr); - BUILD_BUG_ON(sizeof(xts_tweak) != FS_XTS_TWEAK_SIZE); - xts_tweak.index = cpu_to_le64(lblk_num); - memset(xts_tweak.padding, 0, sizeof(xts_tweak.padding)); - sg_init_table(&dst, 1); sg_set_page(&dst, dest_page, len, offs); sg_init_table(&src, 1); sg_set_page(&src, src_page, len, offs); - ablkcipher_request_set_crypt(req, &src, &dst, len, &xts_tweak); + ablkcipher_request_set_crypt(req, &src, &dst, len, &iv); if (rw == FS_DECRYPT) res = crypto_ablkcipher_decrypt(req); else @@ -477,6 +484,8 @@ static void __exit fscrypt_exit(void) destroy_workqueue(fscrypt_read_workqueue); kmem_cache_destroy(fscrypt_ctx_cachep); kmem_cache_destroy(fscrypt_info_cachep); + + fscrypt_essiv_cleanup(); } module_exit(fscrypt_exit); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 1bc34d9814d9..4e4f60f134b4 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -12,10 +12,13 @@ #define _FSCRYPT_PRIVATE_H #include +#include /* Encryption parameters */ -#define FS_XTS_TWEAK_SIZE 16 +#define FS_IV_SIZE 16 #define FS_AES_128_ECB_KEY_SIZE 16 +#define FS_AES_128_CBC_KEY_SIZE 16 +#define FS_AES_128_CTS_KEY_SIZE 16 #define FS_AES_256_GCM_KEY_SIZE 32 #define FS_AES_256_CBC_KEY_SIZE 32 #define FS_AES_256_CTS_KEY_SIZE 32 @@ -54,6 +57,7 @@ struct fscrypt_info { u8 ci_filename_mode; u8 ci_flags; struct crypto_ablkcipher *ci_ctfm; + struct crypto_cipher *ci_essiv_tfm; u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE]; }; @@ -112,4 +116,7 @@ extern int fscrypt_do_page_crypto(const struct inode *inode, extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags); +/* keyinfo.c */ +extern void __exit fscrypt_essiv_cleanup(void); + #endif /* _FSCRYPT_PRIVATE_H */ diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 995003cb5301..be24dc401ac9 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -10,8 +10,13 @@ #include #include +#include +#include +#include #include "fscrypt_private.h" +static struct crypto_shash *essiv_hash_tfm; + static void derive_crypt_complete(struct crypto_async_request *req, int rc) { struct fscrypt_completion_result *ecr = req->data; @@ -27,13 +32,13 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) * derive_key_aes() - Derive a key using AES-128-ECB * @deriving_key: Encryption key used for derivation. * @source_key: Source key to which to apply derivation. - * @derived_key: Derived key. + * @derived_raw_key: Derived raw key. * * Return: Zero on success; non-zero otherwise. */ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], - u8 source_key[FS_AES_256_XTS_KEY_SIZE], - u8 derived_key[FS_AES_256_XTS_KEY_SIZE]) + const struct fscrypt_key *source_key, + u8 derived_raw_key[FS_MAX_KEY_SIZE]) { int res = 0; struct ablkcipher_request *req = NULL; @@ -60,10 +65,10 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], if (res < 0) goto out; - sg_init_one(&src_sg, source_key, FS_AES_256_XTS_KEY_SIZE); - sg_init_one(&dst_sg, derived_key, FS_AES_256_XTS_KEY_SIZE); - ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, - FS_AES_256_XTS_KEY_SIZE, NULL); + sg_init_one(&src_sg, source_key->raw, source_key->size); + sg_init_one(&dst_sg, derived_raw_key, source_key->size); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size, + NULL); res = crypto_ablkcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { wait_for_completion(&ecr.completion); @@ -79,7 +84,7 @@ out: static int validate_user_key(struct fscrypt_info *crypt_info, struct fscrypt_context *ctx, u8 *raw_key, - const char *prefix) + const char *prefix, int min_keysize) { char *description; struct key *keyring_key; @@ -124,50 +129,60 @@ static int validate_user_key(struct fscrypt_info *crypt_info, master_key = (struct fscrypt_key *)ukp->data; BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE); - if (master_key->size != FS_AES_256_XTS_KEY_SIZE) { + if (master_key->size < min_keysize || master_key->size > FS_MAX_KEY_SIZE + || master_key->size % AES_BLOCK_SIZE != 0) { printk_once(KERN_WARNING "%s: key size incorrect: %d\n", __func__, master_key->size); res = -ENOKEY; goto out; } - res = derive_key_aes(ctx->nonce, master_key->raw, raw_key); + res = derive_key_aes(ctx->nonce, master_key, raw_key); out: up_read(&keyring_key->sem); key_put(keyring_key); return res; } +static const struct { + const char *cipher_str; + int keysize; +} available_modes[] = { + [FS_ENCRYPTION_MODE_AES_256_XTS] = { "xts(aes)", + FS_AES_256_XTS_KEY_SIZE }, + [FS_ENCRYPTION_MODE_AES_256_CTS] = { "cts(cbc(aes))", + FS_AES_256_CTS_KEY_SIZE }, + [FS_ENCRYPTION_MODE_AES_128_CBC] = { "cbc(aes)", + FS_AES_128_CBC_KEY_SIZE }, + [FS_ENCRYPTION_MODE_AES_128_CTS] = { "cts(cbc(aes))", + FS_AES_128_CTS_KEY_SIZE }, +}; + static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode, const char **cipher_str_ret, int *keysize_ret) { - if (S_ISREG(inode->i_mode)) { - if (ci->ci_data_mode == FS_ENCRYPTION_MODE_AES_256_XTS) { - *cipher_str_ret = "xts(aes)"; - *keysize_ret = FS_AES_256_XTS_KEY_SIZE; - return 0; - } - pr_warn_once("fscrypto: unsupported contents encryption mode " - "%d for inode %lu\n", - ci->ci_data_mode, inode->i_ino); - return -ENOKEY; + u32 mode; + + if (!fscrypt_valid_enc_modes(ci->ci_data_mode, ci->ci_filename_mode)) { + pr_warn_ratelimited("fscrypt: inode %lu uses unsupported encryption modes (contents mode %d, filenames mode %d)\n", + inode->i_ino, + ci->ci_data_mode, ci->ci_filename_mode); + return -EINVAL; } - if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) { - if (ci->ci_filename_mode == FS_ENCRYPTION_MODE_AES_256_CTS) { - *cipher_str_ret = "cts(cbc(aes))"; - *keysize_ret = FS_AES_256_CTS_KEY_SIZE; - return 0; - } - pr_warn_once("fscrypto: unsupported filenames encryption mode " - "%d for inode %lu\n", - ci->ci_filename_mode, inode->i_ino); - return -ENOKEY; + if (S_ISREG(inode->i_mode)) { + mode = ci->ci_data_mode; + } else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) { + mode = ci->ci_filename_mode; + } else { + WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n", + inode->i_ino, (inode->i_mode & S_IFMT)); + return -EINVAL; } - pr_warn_once("fscrypto: unsupported file type %d for inode %lu\n", - (inode->i_mode & S_IFMT), inode->i_ino); - return -ENOKEY; + *cipher_str_ret = available_modes[mode].cipher_str; + *keysize_ret = available_modes[mode].keysize; + return 0; } static void put_crypt_info(struct fscrypt_info *ci) @@ -176,9 +191,76 @@ static void put_crypt_info(struct fscrypt_info *ci) return; crypto_free_ablkcipher(ci->ci_ctfm); + crypto_free_cipher(ci->ci_essiv_tfm); kmem_cache_free(fscrypt_info_cachep, ci); } +static int derive_essiv_salt(const u8 *key, int keysize, u8 *salt) +{ + struct crypto_shash *tfm = READ_ONCE(essiv_hash_tfm); + + /* init hash transform on demand */ + if (unlikely(!tfm)) { + struct crypto_shash *prev_tfm; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) { + pr_warn_ratelimited("fscrypt: error allocating SHA-256 transform: %ld\n", + PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + prev_tfm = cmpxchg(&essiv_hash_tfm, NULL, tfm); + if (prev_tfm) { + crypto_free_shash(tfm); + tfm = prev_tfm; + } + } + + { + SHASH_DESC_ON_STACK(desc, tfm); + desc->tfm = tfm; + desc->flags = 0; + + return crypto_shash_digest(desc, key, keysize, salt); + } +} + +static int init_essiv_generator(struct fscrypt_info *ci, const u8 *raw_key, + int keysize) +{ + int err; + struct crypto_cipher *essiv_tfm; + u8 salt[SHA256_DIGEST_SIZE]; + + essiv_tfm = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(essiv_tfm)) + return PTR_ERR(essiv_tfm); + + ci->ci_essiv_tfm = essiv_tfm; + + err = derive_essiv_salt(raw_key, keysize, salt); + if (err) + goto out; + + /* + * Using SHA256 to derive the salt/key will result in AES-256 being + * used for IV generation. File contents encryption will still use the + * configured keysize (AES-128) nevertheless. + */ + err = crypto_cipher_setkey(essiv_tfm, salt, sizeof(salt)); + if (err) + goto out; + +out: + memzero_explicit(salt, sizeof(salt)); + return err; +} + +void __exit fscrypt_essiv_cleanup(void) +{ + crypto_free_shash(essiv_hash_tfm); +} + int fscrypt_get_encryption_info(struct inode *inode) { struct fscrypt_info *crypt_info; @@ -225,6 +307,7 @@ int fscrypt_get_encryption_info(struct inode *inode) crypt_info->ci_data_mode = ctx.contents_encryption_mode; crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; crypt_info->ci_ctfm = NULL; + crypt_info->ci_essiv_tfm = NULL; memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, sizeof(crypt_info->ci_master_key)); @@ -241,10 +324,12 @@ int fscrypt_get_encryption_info(struct inode *inode) if (!raw_key) goto out; - res = validate_user_key(crypt_info, &ctx, raw_key, FS_KEY_DESC_PREFIX); + res = validate_user_key(crypt_info, &ctx, raw_key, FS_KEY_DESC_PREFIX, + keysize); if (res && inode->i_sb->s_cop->key_prefix) { int res2 = validate_user_key(crypt_info, &ctx, raw_key, - inode->i_sb->s_cop->key_prefix); + inode->i_sb->s_cop->key_prefix, + keysize); if (res2) { if (res2 == -ENOKEY) res = -ENOKEY; @@ -256,18 +341,30 @@ int fscrypt_get_encryption_info(struct inode *inode) ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); if (!ctfm || IS_ERR(ctfm)) { res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; - printk(KERN_DEBUG - "%s: error %d (inode %u) allocating crypto tfm\n", - __func__, res, (unsigned) inode->i_ino); + pr_debug("%s: error %d (inode %lu) allocating crypto tfm\n", + __func__, res, inode->i_ino); goto out; } crypt_info->ci_ctfm = ctfm; crypto_ablkcipher_clear_flags(ctfm, ~0); crypto_ablkcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY); + /* + * if the provided key is longer than keysize, we use the first + * keysize bytes of the derived key only + */ res = crypto_ablkcipher_setkey(ctfm, raw_key, keysize); if (res) goto out; + if (S_ISREG(inode->i_mode) && + crypt_info->ci_data_mode == FS_ENCRYPTION_MODE_AES_128_CBC) { + res = init_essiv_generator(crypt_info, raw_key, keysize); + if (res) { + pr_debug("%s: error %d (inode %lu) allocating essiv tfm\n", + __func__, res, inode->i_ino); + goto out; + } + } if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL) crypt_info = NULL; out: diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 210976e7a269..9914d51dff86 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -38,12 +38,8 @@ static int create_encryption_context_from_policy(struct inode *inode, memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE); - if (!fscrypt_valid_contents_enc_mode( - policy->contents_encryption_mode)) - return -EINVAL; - - if (!fscrypt_valid_filenames_enc_mode( - policy->filenames_encryption_mode)) + if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, + policy->filenames_encryption_mode)) return -EINVAL; if (policy->flags & ~FS_POLICY_FLAGS_VALID) diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt_common.h index 10c1abfbac6c..768203e248f0 100644 --- a/include/linux/fscrypt_common.h +++ b/include/linux/fscrypt_common.h @@ -102,14 +102,18 @@ static inline bool fscrypt_dummy_context_enabled(struct inode *inode) return false; } -static inline bool fscrypt_valid_contents_enc_mode(u32 mode) +static inline bool fscrypt_valid_enc_modes(u32 contents_mode, + u32 filenames_mode) { - return (mode == FS_ENCRYPTION_MODE_AES_256_XTS); -} + if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && + filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) + return true; -static inline bool fscrypt_valid_filenames_enc_mode(u32 mode) -{ - return (mode == FS_ENCRYPTION_MODE_AES_256_CTS); + if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && + filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) + return true; + + return false; } static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 9606df30a5f5..b3ad4c561337 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -187,6 +187,8 @@ struct inodes_stat_t { #define FS_ENCRYPTION_MODE_AES_256_GCM 2 #define FS_ENCRYPTION_MODE_AES_256_CBC 3 #define FS_ENCRYPTION_MODE_AES_256_CTS 4 +#define FS_ENCRYPTION_MODE_AES_128_CBC 5 +#define FS_ENCRYPTION_MODE_AES_128_CTS 6 struct fscrypt_policy { -- GitLab From 4a5392b5075cd1f3a33ed43d7f00d6d116c1db7f Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Tue, 11 Jul 2017 17:30:33 +0100 Subject: [PATCH 0745/5498] f2fs: remove extra inode_unlock() in error path This commit removes an extra inode_unlock() that is being done in function f2fs_ioc_setflags error path. While there, get rid of a useless 'out' label as well. Fixes: 0abd675e97e6 ("f2fs: support plain user/group quota") Signed-off-by: Luis Henriques Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index faa91ce092a9..5856efe4e5c5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1530,7 +1530,6 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) /* Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) { - inode_unlock(inode); ret = -EPERM; goto unlock_out; } @@ -1541,9 +1540,8 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { - inode_unlock(inode); ret = -EPERM; - goto out; + goto unlock_out; } } @@ -1556,7 +1554,6 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) f2fs_mark_inode_dirty_sync(inode, false); unlock_out: inode_unlock(inode); -out: mnt_drop_write_file(filp); return ret; } -- GitLab From 127a18bc9e9bf23cce501e660c852b7be74a4342 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 11 Jul 2017 14:56:49 -0700 Subject: [PATCH 0746/5498] f2fs: Don't clear SGID when inheriting ACLs This patch copies commit b7f8a09f80: "btrfs: Don't clear SGID when inheriting ACLs" written by Jan. Fixes: 073931017b49d9458aa351605b43a7e34598caef CC: stable@vger.kernel.org Signed-off-by: Jan Kara Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index a140c5e3dc54..b4b8438c42ef 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -211,7 +211,7 @@ static int __f2fs_set_acl(struct inode *inode, int type, switch (type) { case ACL_TYPE_ACCESS: name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; - if (acl) { + if (acl && !ipage) { error = posix_acl_update_mode(inode, &inode->i_mode, &acl); if (error) return error; -- GitLab From a771a3d249dd8f466a60bffab4b1fabf62f4d52c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 13 Jul 2017 17:45:21 -0700 Subject: [PATCH 0747/5498] f2fs: include seq_file.h for sysfs.c This patch includes seq_file.h to avoid compile error. Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 9adc202fcd6f..71191d89917d 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -11,6 +11,7 @@ */ #include #include +#include #include "f2fs.h" #include "segment.h" -- GitLab From 0299bf096642ca75ccfb3859cc542b5613b3d710 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 14 Jul 2017 11:45:21 -0700 Subject: [PATCH 0748/5498] f2fs: avoid cpu lockup Before retrying to flush data or dentry pages, we need to release cpu in order to prevent watchdog. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index e6f59f12ab86..01d4ca3c03ff 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -880,6 +880,7 @@ int sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type) struct inode *inode; struct f2fs_inode_info *fi; bool is_dir = (type == DIR_INODE); + unsigned long ino = 0; trace_f2fs_sync_dirty_inodes_enter(sbi->sb, is_dir, get_pages(sbi, is_dir ? @@ -902,8 +903,17 @@ retry: inode = igrab(&fi->vfs_inode); spin_unlock(&sbi->inode_lock[type]); if (inode) { + unsigned long cur_ino = inode->i_ino; + filemap_fdatawrite(inode->i_mapping); iput(inode); + /* We need to give cpu to another writers. */ + if (ino == cur_ino) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + cond_resched(); + } else { + ino = cur_ino; + } } else { /* * We should submit bio, since it exists several -- GitLab From a20521c398123fa40634e70c640bcae4b05760e8 Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Mon, 7 Aug 2017 14:58:15 +0530 Subject: [PATCH 0749/5498] mmc: core: Ignore bus resume flags when card removal event is detected Ignore bus resume flags in the resume path if there is an outstanding card removal event. With deferred resume feature enabled, we don't check for card presence (i.e., mmc_detect_change) in the resume path. This is to improve the resume latency. If at all card is removed during suspend state, in some cases we may not detect the card removal immediately but only when a request was issued to it. In some scenarios, card removal in suspend state leading to card suspend to fail (since the card is removed) and which is causing system suspend to fail. And we don't try to resume card because of deferred resume it leading to system suspend to fail continuously. To fix this scenario, ignore the bus resume flag in the resume path only if there an outstanding card removal event. By doing so, we are ensuring that the driver would check for card presence (mmc_detect_change) in the resume path (mmc_pm_notify) and it will mark the card as removed if it finds the card is no more present. Change-Id: I7d075c2a5c2aaba1ff92f4072fdd2541bb98aa95 Signed-off-by: Veerabhadrarao Badiganti --- drivers/mmc/core/core.c | 12 +++++++++++- drivers/mmc/core/sd.c | 5 ++++- include/linux/mmc/host.h | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7a1549235b09..25668023aa26 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2946,6 +2946,13 @@ static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, pm_wakeup_event(mmc_dev(host), 5000); host->detect_change = 1; + /* + * Change in cd_gpio state, so make sure detection part is + * not overided because of manual resume. + */ + if (cd_irq && mmc_bus_manual_resume(host)) + host->ignore_bus_resume_flags = true; + mmc_schedule_delayed_work(&host->detect, delay); } @@ -3866,6 +3873,8 @@ void mmc_rescan(struct work_struct *work) host->bus_ops->detect(host); host->detect_change = 0; + if (host->ignore_bus_resume_flags) + host->ignore_bus_resume_flags = false; /* * Let mmc_bus_put() free the bus/bus_ops if we've found that @@ -4119,7 +4128,8 @@ int mmc_pm_notify(struct notifier_block *notify_block, spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 0; - if (mmc_bus_manual_resume(host)) { + if (mmc_bus_manual_resume(host) && + !host->ignore_bus_resume_flags) { spin_unlock_irqrestore(&host->lock, flags); break; } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index e67b7fa7ebd0..95854b6d0ff5 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1239,7 +1239,10 @@ static int mmc_sd_suspend(struct mmc_host *host) if (!err) { pm_runtime_disable(&host->card->dev); pm_runtime_set_suspended(&host->card->dev); - } + /* if suspend fails, force mmc_detect_change during resume */ + } else if (mmc_bus_manual_resume(host)) + host->ignore_bus_resume_flags = true; + MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err); return err; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 219ade575672..0b5b048d3c99 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -542,6 +542,7 @@ struct mmc_host { unsigned int bus_resume_flags; #define MMC_BUSRESUME_MANUAL_RESUME (1 << 0) #define MMC_BUSRESUME_NEEDS_RESUME (1 << 1) + bool ignore_bus_resume_flags; unsigned int sdio_irqs; struct task_struct *sdio_irq_thread; -- GitLab From 3556dd435137a55b650638b1fdcb3b42b41191dc Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 17 Jul 2017 19:16:11 +0800 Subject: [PATCH 0750/5498] f2fs: remove unused input parameter This patch remove unused input parameter in function new_node_page. Signed-off-by: Yunlei He Signed-off-by: Yong Sheng Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/node.c | 7 +++---- fs/f2fs/xattr.c | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8907042ea571..80808ab3aac9 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2398,8 +2398,7 @@ int truncate_xattr_node(struct inode *inode, struct page *page); int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino); int remove_inode_page(struct inode *inode); struct page *new_inode_page(struct inode *inode); -struct page *new_node_page(struct dnode_of_data *dn, - unsigned int ofs, struct page *ipage); +struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs); void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid); struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid); struct page *get_node_page_ra(struct page *parent, int start); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 77d27551b09b..785612e33da5 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -613,7 +613,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) } dn->nid = nids[i]; - npage[i] = new_node_page(dn, noffset[i], NULL); + npage[i] = new_node_page(dn, noffset[i]); if (IS_ERR(npage[i])) { alloc_nid_failed(sbi, nids[i]); err = PTR_ERR(npage[i]); @@ -1022,11 +1022,10 @@ struct page *new_inode_page(struct inode *inode) set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); /* caller should f2fs_put_page(page, 1); */ - return new_node_page(&dn, 0, NULL); + return new_node_page(&dn, 0); } -struct page *new_node_page(struct dnode_of_data *dn, - unsigned int ofs, struct page *ipage) +struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct node_info new_ni; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index c01e2b61e747..833af6916534 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -491,7 +491,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, } else { struct dnode_of_data dn; set_new_dnode(&dn, inode, NULL, NULL, new_nid); - xpage = new_node_page(&dn, XATTR_NODE_OFFSET, ipage); + xpage = new_node_page(&dn, XATTR_NODE_OFFSET); if (IS_ERR(xpage)) { alloc_nid_failed(sbi, new_nid); return PTR_ERR(xpage); -- GitLab From e874e70538d59d3a182e3b4fbd470e0a15b3155a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 16 Jul 2017 15:08:54 +0800 Subject: [PATCH 0751/5498] f2fs: spread struct f2fs_dentry_ptr for inline path Use f2fs_dentry_ptr structure to indicate inline dentry structure as much as possible, so we can wrap inline dentry with size-fixed fields to the one with size-changeable fields. With this change, we can handle size-changeable inline dentry more easily. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 5 ++++- fs/f2fs/inline.c | 47 ++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 80808ab3aac9..ce04edfdf722 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -460,10 +460,11 @@ struct f2fs_flush_device { /* for directory operations */ struct f2fs_dentry_ptr { struct inode *inode; - const void *bitmap; + void *bitmap; struct f2fs_dir_entry *dentry; __u8 (*filename)[F2FS_SLOT_LEN]; int max; + int nr_bitmap; }; static inline void make_dentry_ptr_block(struct inode *inode, @@ -471,6 +472,7 @@ static inline void make_dentry_ptr_block(struct inode *inode, { d->inode = inode; d->max = NR_DENTRY_IN_BLOCK; + d->nr_bitmap = SIZE_OF_DENTRY_BITMAP; d->bitmap = &t->dentry_bitmap; d->dentry = t->dentry; d->filename = t->filename; @@ -481,6 +483,7 @@ static inline void make_dentry_ptr_inline(struct inode *inode, { d->inode = inode; d->max = NR_INLINE_DENTRY; + d->nr_bitmap = INLINE_DENTRY_BITMAP_SIZE; d->bitmap = &t->dentry_bitmap; d->dentry = t->dentry; d->filename = t->filename; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e0fd4376e6fb..2082816a504a 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -342,6 +342,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, struct page *page; struct dnode_of_data dn; struct f2fs_dentry_block *dentry_blk; + struct f2fs_dentry_ptr src, dst; int err; page = f2fs_grab_cache_page(dir->i_mapping, 0, false); @@ -360,21 +361,20 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, dentry_blk = kmap_atomic(page); + make_dentry_ptr_inline(NULL, &src, inline_dentry); + make_dentry_ptr_block(NULL, &dst, dentry_blk); + /* copy data from inline dentry block to new dentry block */ - memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap, - INLINE_DENTRY_BITMAP_SIZE); - memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0, - SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE); + memcpy(dst.bitmap, src.bitmap, src.nr_bitmap); + memset(dst.bitmap + src.nr_bitmap, 0, dst.nr_bitmap - src.nr_bitmap); /* * we do not need to zero out remainder part of dentry and filename * field, since we have used bitmap for marking the usage status of * them, besides, we can also ignore copying/zeroing reserved space * of dentry block, because them haven't been used so far. */ - memcpy(dentry_blk->dentry, inline_dentry->dentry, - sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY); - memcpy(dentry_blk->filename, inline_dentry->filename, - NR_INLINE_DENTRY * F2FS_SLOT_LEN); + memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max); + memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN); kunmap_atomic(dentry_blk); if (!PageUptodate(page)) @@ -511,9 +511,10 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, return PTR_ERR(ipage); inline_dentry = inline_data_addr(ipage); - bit_pos = room_for_filename(&inline_dentry->dentry_bitmap, - slots, NR_INLINE_DENTRY); - if (bit_pos >= NR_INLINE_DENTRY) { + make_dentry_ptr_inline(NULL, &d, inline_dentry); + + bit_pos = room_for_filename(d.bitmap, slots, d.max); + if (bit_pos >= d.max) { err = f2fs_convert_inline_dir(dir, ipage, inline_dentry); if (err) return err; @@ -534,7 +535,6 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, f2fs_wait_on_page_writeback(ipage, NODE, true); name_hash = f2fs_dentry_hash(new_name, NULL); - make_dentry_ptr_inline(NULL, &d, inline_dentry); f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); set_page_dirty(ipage); @@ -558,6 +558,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, struct inode *dir, struct inode *inode) { struct f2fs_inline_dentry *inline_dentry; + struct f2fs_dentry_ptr d; int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); unsigned int bit_pos; int i; @@ -566,10 +567,11 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, f2fs_wait_on_page_writeback(page, NODE, true); inline_dentry = inline_data_addr(page); - bit_pos = dentry - inline_dentry->dentry; + make_dentry_ptr_inline(NULL, &d, inline_dentry); + + bit_pos = dentry - d.dentry; for (i = 0; i < slots; i++) - __clear_bit_le(bit_pos + i, - &inline_dentry->dentry_bitmap); + __clear_bit_le(bit_pos + i, d.bitmap); set_page_dirty(page); f2fs_put_page(page, 1); @@ -587,19 +589,20 @@ bool f2fs_empty_inline_dir(struct inode *dir) struct page *ipage; unsigned int bit_pos = 2; struct f2fs_inline_dentry *inline_dentry; + struct f2fs_dentry_ptr d; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) return false; inline_dentry = inline_data_addr(ipage); - bit_pos = find_next_bit_le(&inline_dentry->dentry_bitmap, - NR_INLINE_DENTRY, - bit_pos); + make_dentry_ptr_inline(NULL, &d, inline_dentry); + + bit_pos = find_next_bit_le(d.bitmap, d.max, bit_pos); f2fs_put_page(ipage, 1); - if (bit_pos < NR_INLINE_DENTRY) + if (bit_pos < d.max) return false; return true; @@ -614,7 +617,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, struct f2fs_dentry_ptr d; int err; - if (ctx->pos == NR_INLINE_DENTRY) + make_dentry_ptr_inline(inode, &d, inline_dentry); + + if (ctx->pos == d.max) return 0; ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); @@ -627,7 +632,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, err = f2fs_fill_dentries(ctx, &d, 0, fstr); if (!err) - ctx->pos = NR_INLINE_DENTRY; + ctx->pos = d.max; f2fs_put_page(ipage, 1); return err < 0 ? err : 0; -- GitLab From b673d3773d3ef065ec53877beef0958c22fdb794 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 18 Jul 2017 09:48:12 +0800 Subject: [PATCH 0752/5498] f2fs: alloc new nids for xattr block in recovery recovery file A: recovery file B: -get_dnode_of_data -alloc_nid -recover_xattr_data -set_node_addr(sbi, &ni, NEW_ADDR, false); --->bug_on for nid has been used by file A In recovery process, new allocated node blocks may "reuse" xattr block nids, this patch alloc new nids for xattr blocks in recovery process to avoid this problem. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 785612e33da5..9a6ff811c31d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -19,6 +19,7 @@ #include "f2fs.h" #include "node.h" #include "segment.h" +#include "xattr.h" #include "trace.h" #include @@ -2199,7 +2200,8 @@ int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid; - nid_t new_xnid = nid_of_node(page); + nid_t new_xnid; + struct dnode_of_data dn; struct node_info ni; struct page *xpage; @@ -2215,22 +2217,22 @@ int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) recover_xnid: /* 2: update xattr nid in inode */ - remove_free_nid(sbi, new_xnid); - f2fs_i_xnid_write(inode, new_xnid); - if (unlikely(inc_valid_node_count(sbi, inode, false))) - f2fs_bug_on(sbi, 1); + if (!alloc_nid(sbi, &new_xnid)) + return -ENOSPC; + + set_new_dnode(&dn, inode, NULL, NULL, new_xnid); + xpage = new_node_page(&dn, XATTR_NODE_OFFSET); + if (IS_ERR(xpage)) { + alloc_nid_failed(sbi, new_xnid); + return PTR_ERR(xpage); + } + + alloc_nid_done(sbi, new_xnid); update_inode_page(inode); /* 3: update and set xattr node page dirty */ - xpage = grab_cache_page(NODE_MAPPING(sbi), new_xnid); - if (!xpage) - return -ENOMEM; - - memcpy(F2FS_NODE(xpage), F2FS_NODE(page), PAGE_SIZE); + memcpy(F2FS_NODE(xpage), F2FS_NODE(page), VALID_XATTR_BLOCK_SIZE); - get_node_info(sbi, new_xnid, &ni); - ni.ino = inode->i_ino; - set_node_addr(sbi, &ni, NEW_ADDR, false); set_page_dirty(xpage); f2fs_put_page(xpage, 1); -- GitLab From dc109d572713f84a131ccb2b8dfae0015ac558ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20A=2E=20Fern=C3=A1ndez?= Date: Sun, 23 Jul 2017 22:32:54 -0300 Subject: [PATCH 0753/5498] f2fs: preserve i_mode if __f2fs_set_acl() fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When changing a file's acl mask, __f2fs_set_acl() will first set the group bits of i_mode to the value of the mask, and only then set the actual extended attribute representing the new acl. If the second part fails (due to lack of space, for example) and the file had no acl attribute to begin with, the system will from now on assume that the mask permission bits are actual group permission bits, potentially granting access to the wrong users. Prevent this by only changing the inode mode after the acl has been set. Signed-off-by: Ernesto A. Fernández Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index b4b8438c42ef..436b3a1464d9 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -207,15 +207,16 @@ static int __f2fs_set_acl(struct inode *inode, int type, void *value = NULL; size_t size = 0; int error; + umode_t mode = inode->i_mode; switch (type) { case ACL_TYPE_ACCESS: name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl && !ipage) { - error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + error = posix_acl_update_mode(inode, &mode, &acl); if (error) return error; - set_acl_inode(inode, inode->i_mode); + set_acl_inode(inode, mode); } break; -- GitLab From 2988325e89edf73a60ead14c4760c2b3d69a5380 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 19 Jul 2017 10:59:55 -0700 Subject: [PATCH 0754/5498] f2fs: give a try to do atomic write in -ENOMEM case It'd be better to retry writing atomic pages when we get -ENOMEM. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ed15e840f9a7..0ca7b24da75e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -309,17 +309,21 @@ static int __commit_inmem_pages(struct inode *inode, inode_dec_dirty_pages(inode); remove_dirty_inode(inode); } - +retry: fio.page = page; fio.old_blkaddr = NULL_ADDR; fio.encrypted_page = NULL; fio.need_lock = LOCK_DONE; err = do_write_data_page(&fio); if (err) { + if (err == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + cond_resched(); + goto retry; + } unlock_page(page); break; } - /* record old blkaddr for revoking */ cur->old_addr = fio.old_blkaddr; last_idx = page->index; -- GitLab From da9b3dfdcff1b9e0d19aede625f6ee63436422e5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 24 Jul 2017 19:46:29 -0700 Subject: [PATCH 0755/5498] f2fs: don't give partially written atomic data from process crash This patch resolves the below scenario. == Process 1 == == Process 2 == open(w) open(rw) begin write(new_#1) process_crash f_op->flush locks_remove_posix f_op>release read (new_#1) In order to avoid corrupted database caused by new_#1, we must do roll-back at process_crash time. In order to check that, this patch keeps task which triggers transaction begin, and does roll-back in f_op->flush before removing file locks. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ce04edfdf722..f8d05036c25b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -633,6 +633,7 @@ struct f2fs_inode_info { struct list_head dirty_list; /* dirty list for dirs and files */ struct list_head gdirty_list; /* linked in global dirty list */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ + struct task_struct *inmem_task; /* store inmemory task */ struct mutex inmem_lock; /* lock for inmemory pages */ struct extent_tree *extent_tree; /* cached extent_tree entry */ struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 5856efe4e5c5..b9c6d5390030 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1487,6 +1487,22 @@ static int f2fs_release_file(struct inode *inode, struct file *filp) return 0; } +static int f2fs_file_flush(struct file *file, fl_owner_t id) +{ + struct inode *inode = file_inode(file); + + /* + * If the process doing a transaction is crashed, we should do + * roll-back. Otherwise, other reader/write can see corrupted database + * until all the writers close its file. Since this should be done + * before dropping file lock, it needs to do in ->flush. + */ + if (f2fs_is_atomic_file(inode) && + F2FS_I(inode)->inmem_task == current) + drop_inmem_pages(inode); + return 0; +} + #define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) #define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) @@ -1606,6 +1622,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) } inc_stat: + F2FS_I(inode)->inmem_task = current; stat_inc_atomic_write(inode); stat_update_max_atomic_write(inode); out: @@ -2502,6 +2519,7 @@ const struct file_operations f2fs_file_operations = { .open = f2fs_file_open, .release = f2fs_release_file, .mmap = f2fs_file_mmap, + .flush = f2fs_file_flush, .fsync = f2fs_sync_file, .fallocate = f2fs_fallocate, .unlocked_ioctl = f2fs_ioctl, -- GitLab From 817cd3c8c19021511e31436c031e37621b88dfcb Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 22 Jul 2017 08:52:23 +0800 Subject: [PATCH 0756/5498] f2fs: make background threads of f2fs being aware of freezing When ->freeze_fs is called from lvm for doing snapshot, it needs to make sure there will be no more changes in filesystem's data, however, previously, background threads like GC thread wasn't aware of freezing, so in environment with active background threads, data of snapshot becomes unstable. This patch fixes this issue by adding sb_{start,end}_intwrite in below background threads: - GC thread - flush thread - discard thread Note that, don't use sb_start_intwrite() in gc_thread_func() due to: generic/241 reports below bug: ====================================================== WARNING: possible circular locking dependency detected 4.13.0-rc1+ #32 Tainted: G O ------------------------------------------------------ f2fs_gc-250:0/22186 is trying to acquire lock: (&sbi->gc_mutex){+.+...}, at: [] f2fs_sync_fs+0x7b/0x1b0 [f2fs] but task is already holding lock: (sb_internal#2){++++.-}, at: [] gc_thread_func+0x159/0x4a0 [f2fs] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (sb_internal#2){++++.-}: __lock_acquire+0x405/0x7b0 lock_acquire+0xae/0x220 __sb_start_write+0x11d/0x1f0 f2fs_evict_inode+0x2d6/0x4e0 [f2fs] evict+0xa8/0x170 iput+0x1fb/0x2c0 f2fs_sync_inode_meta+0x3f/0xf0 [f2fs] write_checkpoint+0x1b1/0x750 [f2fs] f2fs_sync_fs+0x85/0x1b0 [f2fs] f2fs_do_sync_file.isra.24+0x137/0xa30 [f2fs] f2fs_sync_file+0x34/0x40 [f2fs] vfs_fsync_range+0x4a/0xa0 do_fsync+0x3c/0x60 SyS_fdatasync+0x15/0x20 do_fast_syscall_32+0xa1/0x1b0 entry_SYSENTER_32+0x4c/0x7b -> #1 (&sbi->cp_mutex){+.+...}: __lock_acquire+0x405/0x7b0 lock_acquire+0xae/0x220 __mutex_lock+0x4f/0x830 mutex_lock_nested+0x25/0x30 write_checkpoint+0x2f/0x750 [f2fs] f2fs_sync_fs+0x85/0x1b0 [f2fs] sync_filesystem+0x67/0x80 generic_shutdown_super+0x27/0x100 kill_block_super+0x22/0x50 kill_f2fs_super+0x3a/0x40 [f2fs] deactivate_locked_super+0x3d/0x70 deactivate_super+0x40/0x60 cleanup_mnt+0x39/0x70 __cleanup_mnt+0x10/0x20 task_work_run+0x69/0x80 exit_to_usermode_loop+0x57/0x92 do_fast_syscall_32+0x18c/0x1b0 entry_SYSENTER_32+0x4c/0x7b -> #0 (&sbi->gc_mutex){+.+...}: validate_chain.isra.36+0xc50/0xdb0 __lock_acquire+0x405/0x7b0 lock_acquire+0xae/0x220 __mutex_lock+0x4f/0x830 mutex_lock_nested+0x25/0x30 f2fs_sync_fs+0x7b/0x1b0 [f2fs] f2fs_balance_fs_bg+0xb9/0x200 [f2fs] gc_thread_func+0x302/0x4a0 [f2fs] kthread+0xe9/0x120 ret_from_fork+0x19/0x24 other info that might help us debug this: Chain exists of: &sbi->gc_mutex --> &sbi->cp_mutex --> sb_internal#2 Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(sb_internal#2); lock(&sbi->cp_mutex); lock(sb_internal#2); lock(&sbi->gc_mutex); *** DEADLOCK *** 1 lock held by f2fs_gc-250:0/22186: #0: (sb_internal#2){++++.-}, at: [] gc_thread_func+0x159/0x4a0 [f2fs] stack backtrace: CPU: 2 PID: 22186 Comm: f2fs_gc-250:0 Tainted: G O 4.13.0-rc1+ #32 Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 Call Trace: dump_stack+0x5f/0x92 print_circular_bug+0x1b3/0x1bd validate_chain.isra.36+0xc50/0xdb0 ? __this_cpu_preempt_check+0xf/0x20 __lock_acquire+0x405/0x7b0 lock_acquire+0xae/0x220 ? f2fs_sync_fs+0x7b/0x1b0 [f2fs] __mutex_lock+0x4f/0x830 ? f2fs_sync_fs+0x7b/0x1b0 [f2fs] mutex_lock_nested+0x25/0x30 ? f2fs_sync_fs+0x7b/0x1b0 [f2fs] f2fs_sync_fs+0x7b/0x1b0 [f2fs] f2fs_balance_fs_bg+0xb9/0x200 [f2fs] gc_thread_func+0x302/0x4a0 [f2fs] ? preempt_schedule_common+0x2f/0x4d ? f2fs_gc+0x540/0x540 [f2fs] kthread+0xe9/0x120 ? f2fs_gc+0x540/0x540 [f2fs] ? kthread_create_on_node+0x30/0x30 ret_from_fork+0x19/0x24 The deadlock occurs in below condition: GC Thread Thread B - sb_start_intwrite - f2fs_sync_file - f2fs_sync_fs - mutex_lock(&sbi->gc_mutex) - write_checkpoint - block_operations - f2fs_sync_inode_meta - iput - sb_start_intwrite - mutex_lock(&sbi->gc_mutex) Fix this by altering sb_start_intwrite to sb_start_write_trylock. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 9 +++++++-- fs/f2fs/segment.c | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index fa3d2e2df8e7..41c649c9c55c 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -55,6 +55,9 @@ static int gc_thread_func(void *data) } #endif + if (!sb_start_write_trylock(sbi->sb)) + continue; + /* * [GC triggering condition] * 0. GC is not conducted currently. @@ -69,12 +72,12 @@ static int gc_thread_func(void *data) * So, I'd like to wait some time to collect dirty segments. */ if (!mutex_trylock(&sbi->gc_mutex)) - continue; + goto next; if (!is_idle(sbi)) { increase_sleep_time(gc_th, &wait_ms); mutex_unlock(&sbi->gc_mutex); - continue; + goto next; } if (has_enough_invalid_blocks(sbi)) @@ -93,6 +96,8 @@ static int gc_thread_func(void *data) /* balancing f2fs's metadata periodically */ f2fs_balance_fs_bg(sbi); +next: + sb_end_write(sbi->sb); } while (!kthread_should_stop()); return 0; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0ca7b24da75e..98cef7f28c37 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -485,6 +485,8 @@ repeat: if (kthread_should_stop()) return 0; + sb_start_intwrite(sbi->sb); + if (!llist_empty(&fcc->issue_list)) { struct flush_cmd *cmd, *next; int ret; @@ -503,6 +505,8 @@ repeat: fcc->dispatch_list = NULL; } + sb_end_intwrite(sbi->sb); + wait_event_interruptible(*q, kthread_should_stop() || !llist_empty(&fcc->issue_list)); goto repeat; @@ -1210,9 +1214,13 @@ static int issue_discard_thread(void *data) if (kthread_should_stop()) return 0; + sb_start_intwrite(sbi->sb); + __issue_discard_cmd(sbi, true); __wait_discard_cmd(sbi, true); + sb_end_intwrite(sbi->sb); + congestion_wait(BLK_RW_SYNC, HZ/50); } while (!kthread_should_stop()); return 0; -- GitLab From 41054d54eb5b4f017b50e5da43306b8ed7b5ffc6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 21 Jul 2017 12:58:59 -0700 Subject: [PATCH 0757/5498] f2fs: add ioctl to expose current features This patch adds an ioctl to provide feature information to user. For exapmle, SQLite can use this ioctl to detect whether f2fs support atomic write or not. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f8d05036c25b..5722f95ccf1b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -114,6 +114,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_ENCRYPT 0x0001 #define F2FS_FEATURE_BLKZONED 0x0002 +#define F2FS_FEATURE_ATOMIC_WRITE 0x0004 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -407,6 +408,7 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, struct f2fs_flush_device) #define F2FS_IOC_GARBAGE_COLLECT_RANGE _IOW(F2FS_IOCTL_MAGIC, 11, \ struct f2fs_gc_range) +#define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, __u32) #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index b9c6d5390030..e8fba2586c3f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2393,6 +2393,16 @@ out: return ret; } +static int f2fs_ioc_get_features(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + u32 sb_feature = le32_to_cpu(F2FS_I_SB(inode)->raw_super->feature); + + /* Must validate to set it with SQLite behavior in Android. */ + sb_feature |= F2FS_FEATURE_ATOMIC_WRITE; + + return put_user(sb_feature, (u32 __user *)arg); +} long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -2435,6 +2445,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_move_range(filp, arg); case F2FS_IOC_FLUSH_DEVICE: return f2fs_ioc_flush_device(filp, arg); + case F2FS_IOC_GET_FEATURES: + return f2fs_ioc_get_features(filp, arg); default: return -ENOTTY; } @@ -2502,6 +2514,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_DEFRAGMENT: case F2FS_IOC_MOVE_RANGE: case F2FS_IOC_FLUSH_DEVICE: + case F2FS_IOC_GET_FEATURES: break; default: return -ENOIOCTLCMD; -- GitLab From ef1dd48543eeb3bc317fa15b9f1aca3ad7fde36c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 19 Jul 2017 00:19:05 +0800 Subject: [PATCH 0758/5498] f2fs: make max inline size changeable This patch tries to make below macros calculating max inline size, inline dentry field size considerring reserving size-changeable space: - MAX_INLINE_DATA - NR_INLINE_DENTRY - INLINE_DENTRY_BITMAP_SIZE - INLINE_RESERVED_SIZE Then, when inline_{data,dentry} options is enabled, it allows us to reserve inline space with different size flexibly for adding newly introduced inode attribute. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 +- fs/f2fs/f2fs.h | 48 +++++++++++++++++---- fs/f2fs/inline.c | 95 +++++++++++++++++++++-------------------- fs/f2fs/inode.c | 4 +- fs/f2fs/super.c | 3 ++ include/linux/f2fs_fs.h | 23 +--------- 6 files changed, 96 insertions(+), 81 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8724a754bd2a..ed6712a7e06f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -810,7 +810,7 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, F2FS_GET_BLOCK_PRE_AIO : F2FS_GET_BLOCK_PRE_DIO); } - if (pos + count > MAX_INLINE_DATA) { + if (pos + count > MAX_INLINE_DATA(inode)) { err = f2fs_convert_inline_inode(inode); if (err) return err; @@ -1852,7 +1852,7 @@ restart: set_new_dnode(&dn, inode, ipage, ipage, 0); if (f2fs_has_inline_data(inode)) { - if (pos + len <= MAX_INLINE_DATA) { + if (pos + len <= MAX_INLINE_DATA(inode)) { read_inline_data(page, ipage); set_inode_flag(inode, FI_DATA_EXIST); if (inode->i_nlink) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5722f95ccf1b..6e6c1e74b713 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -456,6 +456,25 @@ struct f2fs_flush_device { u32 segments; /* # of segments to flush */ }; +/* for inline stuff */ +#define DEF_INLINE_RESERVED_SIZE 1 + +static inline int get_inline_reserved_size(struct inode *inode); +#define MAX_INLINE_DATA(inode) (sizeof(__le32) * (DEF_ADDRS_PER_INODE -\ + get_inline_reserved_size(inode) -\ + F2FS_INLINE_XATTR_ADDRS)) + +/* for inline dir */ +#define NR_INLINE_DENTRY(inode) (MAX_INLINE_DATA(inode) * BITS_PER_BYTE / \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + BITS_PER_BYTE + 1)) +#define INLINE_DENTRY_BITMAP_SIZE(inode) ((NR_INLINE_DENTRY(inode) + \ + BITS_PER_BYTE - 1) / BITS_PER_BYTE) +#define INLINE_RESERVED_SIZE(inode) (MAX_INLINE_DATA(inode) - \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + NR_INLINE_DENTRY(inode) + \ + INLINE_DENTRY_BITMAP_SIZE(inode))) + /* * For INODE and NODE manager */ @@ -481,14 +500,19 @@ static inline void make_dentry_ptr_block(struct inode *inode, } static inline void make_dentry_ptr_inline(struct inode *inode, - struct f2fs_dentry_ptr *d, struct f2fs_inline_dentry *t) + struct f2fs_dentry_ptr *d, void *t) { + int entry_cnt = NR_INLINE_DENTRY(inode); + int bitmap_size = INLINE_DENTRY_BITMAP_SIZE(inode); + int reserved_size = INLINE_RESERVED_SIZE(inode); + d->inode = inode; - d->max = NR_INLINE_DENTRY; - d->nr_bitmap = INLINE_DENTRY_BITMAP_SIZE; - d->bitmap = &t->dentry_bitmap; - d->dentry = t->dentry; - d->filename = t->filename; + d->max = entry_cnt; + d->nr_bitmap = bitmap_size; + d->bitmap = t; + d->dentry = t + bitmap_size + reserved_size; + d->filename = t + bitmap_size + reserved_size + + SIZE_OF_DIR_ENTRY * entry_cnt; } /* @@ -640,6 +664,8 @@ struct f2fs_inode_info { struct extent_tree *extent_tree; /* cached extent_tree entry */ struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ struct rw_semaphore i_mmap_sem; + + int i_inline_reserved; /* reserved size in inline data */ }; static inline void get_extent_info(struct extent_info *ext, @@ -2168,11 +2194,12 @@ static inline bool f2fs_is_drop_cache(struct inode *inode) return is_inode_flag_set(inode, FI_DROP_CACHE); } -static inline void *inline_data_addr(struct page *page) +static inline void *inline_data_addr(struct inode *inode, struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); + int reserved_size = get_inline_reserved_size(inode); - return (void *)&(ri->i_addr[1]); + return (void *)&(ri->i_addr[reserved_size]); } static inline int f2fs_has_inline_dentry(struct inode *inode) @@ -2283,6 +2310,11 @@ static inline void *kvzalloc(size_t size, gfp_t flags) return ret; } +static inline int get_inline_reserved_size(struct inode *inode) +{ + return F2FS_I(inode)->i_inline_reserved; +} + #define get_inode_mode(i) \ ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 2082816a504a..f38be791fdf9 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -22,7 +22,7 @@ bool f2fs_may_inline_data(struct inode *inode) if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) return false; - if (i_size_read(inode) > MAX_INLINE_DATA) + if (i_size_read(inode) > MAX_INLINE_DATA(inode)) return false; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) @@ -44,6 +44,7 @@ bool f2fs_may_inline_dentry(struct inode *inode) void read_inline_data(struct page *page, struct page *ipage) { + struct inode *inode = page->mapping->host; void *src_addr, *dst_addr; if (PageUptodate(page)) @@ -51,12 +52,12 @@ void read_inline_data(struct page *page, struct page *ipage) f2fs_bug_on(F2FS_P_SB(page), page->index); - zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE); + zero_user_segment(page, MAX_INLINE_DATA(inode), PAGE_SIZE); /* Copy the whole inline data block */ - src_addr = inline_data_addr(ipage); + src_addr = inline_data_addr(inode, ipage); dst_addr = kmap_atomic(page); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode)); flush_dcache_page(page); kunmap_atomic(dst_addr); if (!PageUptodate(page)) @@ -67,13 +68,13 @@ void truncate_inline_inode(struct inode *inode, struct page *ipage, u64 from) { void *addr; - if (from >= MAX_INLINE_DATA) + if (from >= MAX_INLINE_DATA(inode)) return; - addr = inline_data_addr(ipage); + addr = inline_data_addr(inode, ipage); f2fs_wait_on_page_writeback(ipage, NODE, true); - memset(addr + from, 0, MAX_INLINE_DATA - from); + memset(addr + from, 0, MAX_INLINE_DATA(inode) - from); set_page_dirty(ipage); if (from == 0) @@ -216,8 +217,8 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) f2fs_wait_on_page_writeback(dn.inode_page, NODE, true); src_addr = kmap_atomic(page); - dst_addr = inline_data_addr(dn.inode_page); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + dst_addr = inline_data_addr(inode, dn.inode_page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode)); kunmap_atomic(src_addr); set_page_dirty(dn.inode_page); @@ -255,9 +256,9 @@ process_inline: f2fs_wait_on_page_writeback(ipage, NODE, true); - src_addr = inline_data_addr(npage); - dst_addr = inline_data_addr(ipage); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + src_addr = inline_data_addr(inode, npage); + dst_addr = inline_data_addr(inode, ipage); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode)); set_inode_flag(inode, FI_INLINE_DATA); set_inode_flag(inode, FI_DATA_EXIST); @@ -285,11 +286,11 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, struct fscrypt_name *fname, struct page **res_page) { struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - struct f2fs_inline_dentry *inline_dentry; struct qstr name = FSTR_TO_QSTR(&fname->disk_name); struct f2fs_dir_entry *de; struct f2fs_dentry_ptr d; struct page *ipage; + void *inline_dentry; f2fs_hash_t namehash; ipage = get_node_page(sbi, dir->i_ino); @@ -300,9 +301,9 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, namehash = f2fs_dentry_hash(&name, fname); - inline_dentry = inline_data_addr(ipage); + inline_dentry = inline_data_addr(dir, ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + make_dentry_ptr_inline(dir, &d, inline_dentry); de = find_target_dentry(fname, namehash, NULL, &d); unlock_page(ipage); if (de) @@ -316,19 +317,19 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, int make_empty_inline_dir(struct inode *inode, struct inode *parent, struct page *ipage) { - struct f2fs_inline_dentry *inline_dentry; struct f2fs_dentry_ptr d; + void *inline_dentry; - inline_dentry = inline_data_addr(ipage); + inline_dentry = inline_data_addr(inode, ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + make_dentry_ptr_inline(inode, &d, inline_dentry); do_make_empty_dir(inode, parent, &d); set_page_dirty(ipage); /* update i_size to MAX_INLINE_DATA */ - if (i_size_read(inode) < MAX_INLINE_DATA) - f2fs_i_size_write(inode, MAX_INLINE_DATA); + if (i_size_read(inode) < MAX_INLINE_DATA(inode)) + f2fs_i_size_write(inode, MAX_INLINE_DATA(inode)); return 0; } @@ -337,7 +338,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, * release ipage in this function. */ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, - struct f2fs_inline_dentry *inline_dentry) + void *inline_dentry) { struct page *page; struct dnode_of_data dn; @@ -357,12 +358,12 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, goto out; f2fs_wait_on_page_writeback(page, DATA, true); - zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE); + zero_user_segment(page, MAX_INLINE_DATA(dir), PAGE_SIZE); dentry_blk = kmap_atomic(page); - make_dentry_ptr_inline(NULL, &src, inline_dentry); - make_dentry_ptr_block(NULL, &dst, dentry_blk); + make_dentry_ptr_inline(dir, &src, inline_dentry); + make_dentry_ptr_block(dir, &dst, dentry_blk); /* copy data from inline dentry block to new dentry block */ memcpy(dst.bitmap, src.bitmap, src.nr_bitmap); @@ -395,14 +396,13 @@ out: return err; } -static int f2fs_add_inline_entries(struct inode *dir, - struct f2fs_inline_dentry *inline_dentry) +static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry) { struct f2fs_dentry_ptr d; unsigned long bit_pos = 0; int err = 0; - make_dentry_ptr_inline(NULL, &d, inline_dentry); + make_dentry_ptr_inline(dir, &d, inline_dentry); while (bit_pos < d.max) { struct f2fs_dir_entry *de; @@ -444,19 +444,19 @@ punch_dentry_pages: } static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, - struct f2fs_inline_dentry *inline_dentry) + void *inline_dentry) { - struct f2fs_inline_dentry *backup_dentry; + void *backup_dentry; int err; backup_dentry = f2fs_kmalloc(F2FS_I_SB(dir), - sizeof(struct f2fs_inline_dentry), GFP_F2FS_ZERO); + MAX_INLINE_DATA(dir), GFP_F2FS_ZERO); if (!backup_dentry) { f2fs_put_page(ipage, 1); return -ENOMEM; } - memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA); + memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA(dir)); truncate_inline_inode(dir, ipage, 0); unlock_page(ipage); @@ -473,9 +473,9 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, return 0; recover: lock_page(ipage); - memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); + memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA(dir)); f2fs_i_depth_write(dir, 0); - f2fs_i_size_write(dir, MAX_INLINE_DATA); + f2fs_i_size_write(dir, MAX_INLINE_DATA(dir)); set_page_dirty(ipage); f2fs_put_page(ipage, 1); @@ -484,7 +484,7 @@ recover: } static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, - struct f2fs_inline_dentry *inline_dentry) + void *inline_dentry) { if (!F2FS_I(dir)->i_dir_level) return f2fs_move_inline_dirents(dir, ipage, inline_dentry); @@ -500,7 +500,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, struct page *ipage; unsigned int bit_pos; f2fs_hash_t name_hash; - struct f2fs_inline_dentry *inline_dentry = NULL; + void *inline_dentry = NULL; struct f2fs_dentry_ptr d; int slots = GET_DENTRY_SLOTS(new_name->len); struct page *page = NULL; @@ -510,8 +510,8 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, if (IS_ERR(ipage)) return PTR_ERR(ipage); - inline_dentry = inline_data_addr(ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + inline_dentry = inline_data_addr(dir, ipage); + make_dentry_ptr_inline(dir, &d, inline_dentry); bit_pos = room_for_filename(d.bitmap, slots, d.max); if (bit_pos >= d.max) { @@ -557,8 +557,8 @@ out: void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, struct inode *dir, struct inode *inode) { - struct f2fs_inline_dentry *inline_dentry; struct f2fs_dentry_ptr d; + void *inline_dentry; int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); unsigned int bit_pos; int i; @@ -566,8 +566,8 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, lock_page(page); f2fs_wait_on_page_writeback(page, NODE, true); - inline_dentry = inline_data_addr(page); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + inline_dentry = inline_data_addr(dir, page); + make_dentry_ptr_inline(dir, &d, inline_dentry); bit_pos = dentry - d.dentry; for (i = 0; i < slots; i++) @@ -588,15 +588,15 @@ bool f2fs_empty_inline_dir(struct inode *dir) struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct page *ipage; unsigned int bit_pos = 2; - struct f2fs_inline_dentry *inline_dentry; + void *inline_dentry; struct f2fs_dentry_ptr d; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) return false; - inline_dentry = inline_data_addr(ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + inline_dentry = inline_data_addr(dir, ipage); + make_dentry_ptr_inline(dir, &d, inline_dentry); bit_pos = find_next_bit_le(d.bitmap, d.max, bit_pos); @@ -612,9 +612,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, struct fscrypt_str *fstr) { struct inode *inode = file_inode(file); - struct f2fs_inline_dentry *inline_dentry = NULL; struct page *ipage = NULL; struct f2fs_dentry_ptr d; + void *inline_dentry = NULL; int err; make_dentry_ptr_inline(inode, &d, inline_dentry); @@ -626,7 +626,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, if (IS_ERR(ipage)) return PTR_ERR(ipage); - inline_dentry = inline_data_addr(ipage); + inline_dentry = inline_data_addr(inode, ipage); make_dentry_ptr_inline(inode, &d, inline_dentry); @@ -657,7 +657,7 @@ int f2fs_inline_data_fiemap(struct inode *inode, goto out; } - ilen = min_t(size_t, MAX_INLINE_DATA, i_size_read(inode)); + ilen = min_t(size_t, MAX_INLINE_DATA(inode), i_size_read(inode)); if (start >= ilen) goto out; if (start + len < ilen) @@ -666,7 +666,8 @@ int f2fs_inline_data_fiemap(struct inode *inode, get_node_info(F2FS_I_SB(inode), inode->i_ino, &ni); byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits; - byteaddr += (char *)inline_data_addr(ipage) - (char *)F2FS_INODE(ipage); + byteaddr += (char *)inline_data_addr(inode, ipage) - + (char *)F2FS_INODE(ipage); err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags); out: f2fs_put_page(ipage, 1); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index d01d93ede6ee..ce0a2f4a78f4 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -87,9 +87,9 @@ static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri) static void __recover_inline_status(struct inode *inode, struct page *ipage) { - void *inline_data = inline_data_addr(ipage); + void *inline_data = inline_data_addr(inode, ipage); __le32 *start = inline_data; - __le32 *end = start + MAX_INLINE_DATA / sizeof(__le32); + __le32 *end = start + MAX_INLINE_DATA(inode) / sizeof(__le32); while (start < end) { if (*start++) { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 94042e4682fd..874baf54d0f4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -435,6 +435,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) #endif /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; + + fi->i_inline_reserved = DEF_INLINE_RESERVED_SIZE; + return &fi->vfs_inode; } diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 67303b808ff1..8799520e6db0 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -206,9 +206,6 @@ struct f2fs_extent { #define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ #define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ -#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ - F2FS_INLINE_XATTR_ADDRS - 1)) - struct f2fs_inode { __le16 i_mode; /* file mode */ __u8 i_advise; /* file hints */ @@ -465,7 +462,7 @@ typedef __le32 f2fs_hash_t; #define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1)) /* - * space utilization of regular dentry and inline dentry + * space utilization of regular dentry and inline dentry (w/o extra reservation) * regular dentry inline dentry * bitmap 1 * 27 = 27 1 * 23 = 23 * reserved 1 * 3 = 3 1 * 7 = 7 @@ -501,24 +498,6 @@ struct f2fs_dentry_block { __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; } __packed; -/* for inline dir */ -#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \ - ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ - BITS_PER_BYTE + 1)) -#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \ - BITS_PER_BYTE - 1) / BITS_PER_BYTE) -#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \ - ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ - NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE)) - -/* inline directory entry structure */ -struct f2fs_inline_dentry { - __u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE]; - __u8 reserved[INLINE_RESERVED_SIZE]; - struct f2fs_dir_entry dentry[NR_INLINE_DENTRY]; - __u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN]; -} __packed; - /* file types used in inode_info->flags */ enum { F2FS_FT_UNKNOWN, -- GitLab From 4222bdb569eea8dd99eef4ebf96ec42c4bb52009 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 19 Jul 2017 00:19:06 +0800 Subject: [PATCH 0759/5498] f2fs: enhance on-disk inode structure scalability This patch add new flag F2FS_EXTRA_ATTR storing in inode.i_inline to indicate that on-disk structure of current inode is extended. In order to extend, we changed the inode structure a bit: Original one: struct f2fs_inode { ... struct f2fs_extent i_ext; __le32 i_addr[DEF_ADDRS_PER_INODE]; __le32 i_nid[DEF_NIDS_PER_INODE]; } Extended one: struct f2fs_inode { ... struct f2fs_extent i_ext; union { struct { __le16 i_extra_isize; __le16 i_padding; __le32 i_extra_end[0]; }; __le32 i_addr[DEF_ADDRS_PER_INODE]; }; __le32 i_nid[DEF_NIDS_PER_INODE]; } Once F2FS_EXTRA_ATTR is set, we will steal four bytes in the head of i_addr field for storing i_extra_isize and i_padding. with i_extra_isize, we can calculate actual size of reserved space in i_addr, available attribute fields included in total extra attribute fields for current inode can be described as below: +--------------------+ | .i_mode | | ... | | .i_ext | +--------------------+ | .i_extra_isize |-----+ | .i_padding | | | .i_prjid | | | .i_atime_extra | | | .i_ctime_extra | | | .i_mtime_extra |<----+ | .i_inode_cs |<----- store blkaddr/inline from here | .i_xattr_cs | | ... | +--------------------+ | | | block address | | | +--------------------+ | .i_nid | +--------------------+ | node_footer | | (nid, ino, offset) | +--------------------+ Hence, with this patch, we would enhance scalability of f2fs inode for storing more newly added attribute. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 15 ++++++--- fs/f2fs/f2fs.h | 72 +++++++++++++++++++++++++++++++---------- fs/f2fs/file.c | 23 ++++++++----- fs/f2fs/gc.c | 2 +- fs/f2fs/inode.c | 32 +++++++++++------- fs/f2fs/namei.c | 5 +++ fs/f2fs/node.c | 7 ++-- fs/f2fs/recovery.c | 7 ++-- fs/f2fs/super.c | 11 +++++-- include/linux/f2fs_fs.h | 13 ++++++-- 10 files changed, 135 insertions(+), 52 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ed6712a7e06f..fd3c23c02934 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -460,10 +460,14 @@ static void __set_data_blkaddr(struct dnode_of_data *dn) { struct f2fs_node *rn = F2FS_NODE(dn->node_page); __le32 *addr_array; + int base = 0; + + if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode)) + base = get_extra_isize(dn->inode); /* Get physical address of data block */ addr_array = blkaddr_in_node(rn); - addr_array[dn->ofs_in_node] = cpu_to_le32(dn->data_blkaddr); + addr_array[base + dn->ofs_in_node] = cpu_to_le32(dn->data_blkaddr); } /* @@ -507,8 +511,8 @@ int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count) f2fs_wait_on_page_writeback(dn->node_page, NODE, true); for (; count > 0; dn->ofs_in_node++) { - block_t blkaddr = - datablock_addr(dn->node_page, dn->ofs_in_node); + block_t blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); if (blkaddr == NULL_ADDR) { dn->data_blkaddr = NEW_ADDR; __set_data_blkaddr(dn); @@ -755,7 +759,8 @@ static int __allocate_data_block(struct dnode_of_data *dn) if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC))) return -EPERM; - dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); + dn->data_blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); if (dn->data_blkaddr == NEW_ADDR) goto alloc; @@ -899,7 +904,7 @@ next_dnode: end_offset = ADDRS_PER_PAGE(dn.node_page, inode); next_block: - blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + blkaddr = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { if (create) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6e6c1e74b713..daa54f8857df 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -112,9 +112,10 @@ struct f2fs_mount_info { unsigned int opt; }; -#define F2FS_FEATURE_ENCRYPT 0x0001 -#define F2FS_FEATURE_BLKZONED 0x0002 -#define F2FS_FEATURE_ATOMIC_WRITE 0x0004 +#define F2FS_FEATURE_ENCRYPT 0x0001 +#define F2FS_FEATURE_BLKZONED 0x0002 +#define F2FS_FEATURE_ATOMIC_WRITE 0x0004 +#define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -458,10 +459,10 @@ struct f2fs_flush_device { /* for inline stuff */ #define DEF_INLINE_RESERVED_SIZE 1 - -static inline int get_inline_reserved_size(struct inode *inode); -#define MAX_INLINE_DATA(inode) (sizeof(__le32) * (DEF_ADDRS_PER_INODE -\ - get_inline_reserved_size(inode) -\ +static inline int get_extra_isize(struct inode *inode); +#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ + (CUR_ADDRS_PER_INODE(inode) - \ + DEF_INLINE_RESERVED_SIZE - \ F2FS_INLINE_XATTR_ADDRS)) /* for inline dir */ @@ -665,7 +666,7 @@ struct f2fs_inode_info { struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ struct rw_semaphore i_mmap_sem; - int i_inline_reserved; /* reserved size in inline data */ + int i_extra_isize; /* size of extra space located in i_addr */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1886,20 +1887,38 @@ static inline bool IS_INODE(struct page *page) return RAW_IS_INODE(p); } +static inline int offset_in_addr(struct f2fs_inode *i) +{ + return (i->i_inline & F2FS_EXTRA_ATTR) ? + (le16_to_cpu(i->i_extra_isize) / sizeof(__le32)) : 0; +} + static inline __le32 *blkaddr_in_node(struct f2fs_node *node) { return RAW_IS_INODE(node) ? node->i.i_addr : node->dn.addr; } -static inline block_t datablock_addr(struct page *node_page, - unsigned int offset) +static inline int f2fs_has_extra_attr(struct inode *inode); +static inline block_t datablock_addr(struct inode *inode, + struct page *node_page, unsigned int offset) { struct f2fs_node *raw_node; __le32 *addr_array; + int base = 0; + bool is_inode = IS_INODE(node_page); raw_node = F2FS_NODE(node_page); + + /* from GC path only */ + if (!inode) { + if (is_inode) + base = offset_in_addr(&raw_node->i); + } else if (f2fs_has_extra_attr(inode) && is_inode) { + base = get_extra_isize(inode); + } + addr_array = blkaddr_in_node(raw_node); - return le32_to_cpu(addr_array[offset]); + return le32_to_cpu(addr_array[base + offset]); } static inline int f2fs_test_bit(unsigned int nr, char *addr) @@ -1989,6 +2008,7 @@ enum { FI_DO_DEFRAG, /* indicate defragment is running */ FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ FI_HOT_DATA, /* indicate file is hot */ + FI_EXTRA_ATTR, /* indicate file has extra attribute */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, @@ -2108,6 +2128,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) set_bit(FI_DATA_EXIST, &fi->flags); if (ri->i_inline & F2FS_INLINE_DOTS) set_bit(FI_INLINE_DOTS, &fi->flags); + if (ri->i_inline & F2FS_EXTRA_ATTR) + set_bit(FI_EXTRA_ATTR, &fi->flags); } static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) @@ -2124,6 +2146,13 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) ri->i_inline |= F2FS_DATA_EXIST; if (is_inode_flag_set(inode, FI_INLINE_DOTS)) ri->i_inline |= F2FS_INLINE_DOTS; + if (is_inode_flag_set(inode, FI_EXTRA_ATTR)) + ri->i_inline |= F2FS_EXTRA_ATTR; +} + +static inline int f2fs_has_extra_attr(struct inode *inode) +{ + return is_inode_flag_set(inode, FI_EXTRA_ATTR); } static inline int f2fs_has_inline_xattr(struct inode *inode) @@ -2134,8 +2163,8 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) static inline unsigned int addrs_per_inode(struct inode *inode) { if (f2fs_has_inline_xattr(inode)) - return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS; - return DEF_ADDRS_PER_INODE; + return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS; + return CUR_ADDRS_PER_INODE(inode); } static inline void *inline_xattr_addr(struct page *page) @@ -2197,9 +2226,9 @@ static inline bool f2fs_is_drop_cache(struct inode *inode) static inline void *inline_data_addr(struct inode *inode, struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); - int reserved_size = get_inline_reserved_size(inode); + int extra_size = get_extra_isize(inode); - return (void *)&(ri->i_addr[reserved_size]); + return (void *)&(ri->i_addr[extra_size + DEF_INLINE_RESERVED_SIZE]); } static inline int f2fs_has_inline_dentry(struct inode *inode) @@ -2310,15 +2339,19 @@ static inline void *kvzalloc(size_t size, gfp_t flags) return ret; } -static inline int get_inline_reserved_size(struct inode *inode) +static inline int get_extra_isize(struct inode *inode) { - return F2FS_I(inode)->i_inline_reserved; + return F2FS_I(inode)->i_extra_isize / sizeof(__le32); } #define get_inode_mode(i) \ ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) +#define F2FS_TOTAL_EXTRA_ATTR_SIZE \ + (offsetof(struct f2fs_inode, i_extra_end) - \ + offsetof(struct f2fs_inode, i_extra_isize)) \ + /* * file.c */ @@ -2912,6 +2945,11 @@ static inline int f2fs_sb_mounted_blkzoned(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_BLKZONED); } +static inline int f2fs_sb_has_extra_attr(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_EXTRA_ATTR); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e8fba2586c3f..3c3990cefee2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -385,7 +385,8 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) dn.ofs_in_node++, pgofs++, data_ofs = (loff_t)pgofs << PAGE_SHIFT) { block_t blkaddr; - blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + blkaddr = datablock_addr(dn.inode, + dn.node_page, dn.ofs_in_node); if (__found_offset(blkaddr, dirty, pgofs, whence)) { f2fs_put_dnode(&dn); @@ -470,9 +471,13 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) struct f2fs_node *raw_node; int nr_free = 0, ofs = dn->ofs_in_node, len = count; __le32 *addr; + int base = 0; + + if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode)) + base = get_extra_isize(dn->inode); raw_node = F2FS_NODE(dn->node_page); - addr = blkaddr_in_node(raw_node) + ofs; + addr = blkaddr_in_node(raw_node) + base + ofs; for (; count > 0; count--, addr++, dn->ofs_in_node++) { block_t blkaddr = le32_to_cpu(*addr); @@ -909,7 +914,8 @@ next_dnode: done = min((pgoff_t)ADDRS_PER_PAGE(dn.node_page, inode) - dn.ofs_in_node, len); for (i = 0; i < done; i++, blkaddr++, do_replace++, dn.ofs_in_node++) { - *blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + *blkaddr = datablock_addr(dn.inode, + dn.node_page, dn.ofs_in_node); if (!is_checkpointed_data(sbi, *blkaddr)) { if (test_opt(sbi, LFS)) { @@ -985,8 +991,8 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode, ADDRS_PER_PAGE(dn.node_page, dst_inode) - dn.ofs_in_node, len - i); do { - dn.data_blkaddr = datablock_addr(dn.node_page, - dn.ofs_in_node); + dn.data_blkaddr = datablock_addr(dn.inode, + dn.node_page, dn.ofs_in_node); truncate_data_blocks_range(&dn, 1); if (do_replace[i]) { @@ -1155,7 +1161,8 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start, int ret; for (; index < end; index++, dn->ofs_in_node++) { - if (datablock_addr(dn->node_page, dn->ofs_in_node) == NULL_ADDR) + if (datablock_addr(dn->inode, dn->node_page, + dn->ofs_in_node) == NULL_ADDR) count++; } @@ -1166,8 +1173,8 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start, dn->ofs_in_node = ofs_in_node; for (index = start; index < end; index++, dn->ofs_in_node++) { - dn->data_blkaddr = - datablock_addr(dn->node_page, dn->ofs_in_node); + dn->data_blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); /* * reserve_new_blocks will not guarantee entire block * allocation. diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 41c649c9c55c..f57cadae1a30 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -587,7 +587,7 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, } *nofs = ofs_of_node(node_page); - source_blkaddr = datablock_addr(node_page, ofs_in_node); + source_blkaddr = datablock_addr(NULL, node_page, ofs_in_node); f2fs_put_page(node_page, 1); if (source_blkaddr != blkaddr) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index ce0a2f4a78f4..e1699e542d17 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -49,20 +49,22 @@ void f2fs_set_inode_flags(struct inode *inode) static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) { + int extra_size = get_extra_isize(inode); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - if (ri->i_addr[0]) - inode->i_rdev = - old_decode_dev(le32_to_cpu(ri->i_addr[0])); + if (ri->i_addr[extra_size]) + inode->i_rdev = old_decode_dev( + le32_to_cpu(ri->i_addr[extra_size])); else - inode->i_rdev = - new_decode_dev(le32_to_cpu(ri->i_addr[1])); + inode->i_rdev = new_decode_dev( + le32_to_cpu(ri->i_addr[extra_size + 1])); } } static bool __written_first_block(struct f2fs_inode *ri) { - block_t addr = le32_to_cpu(ri->i_addr[0]); + block_t addr = le32_to_cpu(ri->i_addr[offset_in_addr(ri)]); if (addr != NEW_ADDR && addr != NULL_ADDR) return true; @@ -71,16 +73,18 @@ static bool __written_first_block(struct f2fs_inode *ri) static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri) { + int extra_size = get_extra_isize(inode); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { if (old_valid_dev(inode->i_rdev)) { - ri->i_addr[0] = + ri->i_addr[extra_size] = cpu_to_le32(old_encode_dev(inode->i_rdev)); - ri->i_addr[1] = 0; + ri->i_addr[extra_size + 1] = 0; } else { - ri->i_addr[0] = 0; - ri->i_addr[1] = + ri->i_addr[extra_size] = 0; + ri->i_addr[extra_size + 1] = cpu_to_le32(new_encode_dev(inode->i_rdev)); - ri->i_addr[2] = 0; + ri->i_addr[extra_size + 2] = 0; } } } @@ -153,6 +157,9 @@ static int do_read_inode(struct inode *inode) get_inline_info(inode, ri); + fi->i_extra_isize = f2fs_has_extra_attr(inode) ? + le16_to_cpu(ri->i_extra_isize) : 0; + /* check data exist */ if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) __recover_inline_status(inode, node_page); @@ -292,6 +299,9 @@ int update_inode(struct inode *inode, struct page *node_page) ri->i_generation = cpu_to_le32(inode->i_generation); ri->i_dir_level = F2FS_I(inode)->i_dir_level; + if (f2fs_has_extra_attr(inode)) + ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 16b5f17680a1..ad8be1c53f92 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -71,6 +71,11 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) set_inode_flag(inode, FI_NEW_INODE); + if (f2fs_sb_has_extra_attr(sbi->sb)) { + set_inode_flag(inode, FI_EXTRA_ATTR); + F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; + } + if (test_opt(sbi, INLINE_XATTR)) set_inode_flag(inode, FI_INLINE_XATTR); if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode)) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9a6ff811c31d..e903cb7451dd 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -655,7 +655,8 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) dn->nid = nids[level]; dn->ofs_in_node = offset[level]; dn->node_page = npage[level]; - dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); + dn->data_blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); return 0; release_pages: @@ -2272,7 +2273,9 @@ retry: dst->i_blocks = cpu_to_le64(1); dst->i_links = cpu_to_le32(1); dst->i_xattr_nid = 0; - dst->i_inline = src->i_inline & F2FS_INLINE_XATTR; + dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR); + if (dst->i_inline & F2FS_EXTRA_ATTR) + dst->i_extra_isize = src->i_extra_isize; new_ni = old_ni; new_ni.ino = ino; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 20147d297f89..ebde404ae67f 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -361,7 +361,8 @@ out: return 0; truncate_out: - if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr) + if (datablock_addr(tdn.inode, tdn.node_page, + tdn.ofs_in_node) == blkaddr) truncate_data_blocks_range(&tdn, 1); if (dn->inode->i_ino == nid && !dn->inode_page_locked) unlock_page(dn->inode_page); @@ -414,8 +415,8 @@ retry_dn: for (; start < end; start++, dn.ofs_in_node++) { block_t src, dest; - src = datablock_addr(dn.node_page, dn.ofs_in_node); - dest = datablock_addr(page, dn.ofs_in_node); + src = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); + dest = datablock_addr(dn.inode, page, dn.ofs_in_node); /* skip recovering if dest is the same as src */ if (src == dest) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 874baf54d0f4..c76da794b6ff 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -436,8 +436,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; - fi->i_inline_reserved = DEF_INLINE_RESERVED_SIZE; - return &fi->vfs_inode; } @@ -1284,9 +1282,16 @@ static const struct export_operations f2fs_export_ops = { static loff_t max_file_blocks(void) { - loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS); + loff_t result = 0; loff_t leaf_count = ADDRS_PER_BLOCK; + /* + * note: previously, result is equal to (DEF_ADDRS_PER_INODE - + * F2FS_INLINE_XATTR_ADDRS), but now f2fs try to reserve more + * space in inode.i_addr, it will be more safe to reassign + * result as zero. + */ + /* two direct node blocks */ result += (leaf_count * 2); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 8799520e6db0..b7331bfd440f 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -186,6 +186,8 @@ struct f2fs_extent { #define F2FS_NAME_LEN 255 #define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ +#define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ + get_extra_isize(inode)) #define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ #define ADDRS_PER_INODE(inode) addrs_per_inode(inode) #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ @@ -205,6 +207,7 @@ struct f2fs_extent { #define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */ #define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ #define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ +#define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */ struct f2fs_inode { __le16 i_mode; /* file mode */ @@ -232,8 +235,14 @@ struct f2fs_inode { struct f2fs_extent i_ext; /* caching a largest extent */ - __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ - + union { + struct { + __le16 i_extra_isize; /* extra inode attribute size */ + __le16 i_padding; /* padding */ + __le32 i_extra_end[0]; /* for attribute size calculation */ + }; + __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ + }; __le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2), double_indirect(1) node id */ } __packed; -- GitLab From c2e3a2050fccad6c82f1c3fc24c24d46b9e50e5a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 24 Jul 2017 17:12:06 +0800 Subject: [PATCH 0760/5498] f2fs: record quota during dot{,dot} recovery In ->lookup(), we will have a try to recover dot or dotdot for corrupted directory, once disk quota is on, if it allocates new block during dotdot recovery, we need to record disk quota info for the allocation, so this patch fixes this issue by adding missing dquot_initialize() in __recover_dot_dentries. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index ad8be1c53f92..70e9a1d1c02f 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -261,6 +261,8 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) return 0; } + dquot_initialize(dir); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); -- GitLab From 999f38cc2759bf4dbe7636025da785a3926eaef0 Mon Sep 17 00:00:00 2001 From: Prabhakar Reddy Krishnappa Date: Thu, 18 May 2017 17:43:17 -0700 Subject: [PATCH 0761/5498] msm: vidc: Cancel userptr validation during secure decode Secure decode doesn't need userptr and uses ion fd instead. We were conducting userptr validation regardless of the decode mode (secure/ unsecure). This forced user to populate userptr with a dummy value during QBUF in secure mode decode on both output and capture ports to avoid a userptr validation fail. CRs-Fixed: 2049213 Change-Id: I0060efb52792201a2634072f648a537ebb02d17c Signed-off-by: Prabhakar Reddy Krishnappa Signed-off-by: Vasantha Balla --- drivers/media/platform/msm/vidc/msm_vidc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index 39ab47a5932c..0c60d1ace21a 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -936,7 +936,7 @@ int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b) b->m.planes[i].m.userptr = buffer_info->uvaddr[i]; b->m.planes[i].reserved[0] = buffer_info->fd[i]; b->m.planes[i].reserved[1] = buffer_info->buff_off[i]; - if (!b->m.planes[i].m.userptr) { + if (!(inst->flags & VIDC_SECURE) && !b->m.planes[i].m.userptr) { dprintk(VIDC_ERR, "%s: Failed to find user virtual address, %#lx, %d, %d\n", __func__, b->m.planes[i].m.userptr, b->type, i); -- GitLab From 74c0d2525740d6ee5e33f3aef47118134622f6ba Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Wed, 1 Jun 2016 18:09:07 +0300 Subject: [PATCH 0762/5498] xhci: Cleanup only when releasing primary hcd Under stress occasions some TI devices might not return early when reading the status register during the quirk invocation of xhci_irq made by usb_hcd_pci_remove. This means that instead of returning, we end up handling this interruption in the middle of a shutdown. Since xhci->event_ring has already been freed in xhci_mem_cleanup, we end up accessing freed memory, causing the Oops below. commit 8c24d6d7b09d ("usb: xhci: stop everything on the first call to xhci_stop") is the one that changed the instant in which we clean up the event queue when stopping a device. Before, we didn't call xhci_mem_cleanup at the first time xhci_stop is executed (for the shared HCD), instead, we only did it after the invocation for the primary HCD, much later at the removal path. The code flow for this oops looks like this: xhci_pci_remove() usb_remove_hcd(xhci->shared) xhci_stop(xhci->shared) xhci_halt() xhci_mem_cleanup(xhci); // Free the event_queue usb_hcd_pci_remove(primary) xhci_irq() // Access the event_queue if STS_EINT is set. Crash. xhci_stop() xhci_halt() // return early The fix modifies xhci_stop to only cleanup the xhci data when releasing the primary HCD. This way, we still have the event_queue configured when invoking xhci_irq. We still halt the device on the first call to xhci_stop, though. I could reproduce this issue several times on the mainline kernel by doing a bind-unbind stress test with a specific storage gadget attached. I also ran the same test over-night with my patch applied and didn't observe the issue anymore. [ 113.334124] Unable to handle kernel paging request for data at address 0x00000028 [ 113.335514] Faulting instruction address: 0xd00000000d4f767c [ 113.336839] Oops: Kernel access of bad area, sig: 11 [#1] [ 113.338214] SMP NR_CPUS=1024 NUMA PowerNV [c000000efe47ba90] c000000000720850 usb_hcd_irq+0x50/0x80 [c000000efe47bac0] c00000000073d328 usb_hcd_pci_remove+0x68/0x1f0 [c000000efe47bb00] d00000000daf0128 xhci_pci_remove+0x78/0xb0 [xhci_pci] [c000000efe47bb30] c00000000055cf70 pci_device_remove+0x70/0x110 [c000000efe47bb70] c00000000061c6bc __device_release_driver+0xbc/0x190 [c000000efe47bba0] c00000000061c7d0 device_release_driver+0x40/0x70 [c000000efe47bbd0] c000000000619510 unbind_store+0x120/0x150 [c000000efe47bc20] c0000000006183c4 drv_attr_store+0x64/0xa0 [c000000efe47bc60] c00000000039f1d0 sysfs_kf_write+0x80/0xb0 [c000000efe47bca0] c00000000039e14c kernfs_fop_write+0x18c/0x1f0 [c000000efe47bcf0] c0000000002e962c __vfs_write+0x6c/0x190 [c000000efe47bd90] c0000000002eab40 vfs_write+0xc0/0x200 [c000000efe47bde0] c0000000002ec85c SyS_write+0x6c/0x110 [c000000efe47be30] c000000000009260 system_call+0x38/0x108 Change-Id: I987ae6f8b54b6488b1668df5c67628f17e828863 Signed-off-by: Gabriel Krisman Bertazi Cc: Roger Quadros Cc: joel@jms.id.au Cc: stable@vger.kernel.org Reviewed-by: Roger Quadros Cc: #v4.3+ Tested-by: Joel Stanley Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman Git-commit: 27a41a83ec54d0edfcaf079310244e7f013a7701 Git-Repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [prathampratap@codeaurora.org: resolve trivial merge conflicts] Signed-off-by: Pratham Pratap --- drivers/usb/host/xhci-ring.c | 3 ++- drivers/usb/host/xhci.c | 29 +++++++++++------------------ 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0afcc33a4d7f..083aaa95b049 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2681,7 +2681,8 @@ hw_died: writel(irq_pending, &xhci->ir_set->irq_pending); } - if (xhci->xhc_state & XHCI_STATE_DYING) { + if (xhci->xhc_state & XHCI_STATE_DYING || + xhci->xhc_state & XHCI_STATE_HALTED) { xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " "Shouldn't IRQs be disabled?\n"); /* Clear the event handler busy flag (RW1C); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index abe3d390b62b..7b26b0720e83 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -676,15 +676,6 @@ int xhci_run(struct usb_hcd *hcd) } EXPORT_SYMBOL_GPL(xhci_run); -static void xhci_only_stop_hcd(struct usb_hcd *hcd) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - - spin_lock_irq(&xhci->lock); - xhci_halt(xhci); - spin_unlock_irq(&xhci->lock); -} - /* * Stop xHCI driver. * @@ -701,20 +692,22 @@ void xhci_stop(struct usb_hcd *hcd) mutex_lock(&xhci->mutex); + if (!(xhci->xhc_state & XHCI_STATE_HALTED)) { + spin_lock_irq(&xhci->lock); + + xhci->xhc_state |= XHCI_STATE_HALTED; + xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; + xhci_halt(xhci); + xhci_reset(xhci); + + spin_unlock_irq(&xhci->lock); + } + if (!usb_hcd_is_primary_hcd(hcd)) { - xhci_only_stop_hcd(xhci->shared_hcd); mutex_unlock(&xhci->mutex); return; } - spin_lock_irq(&xhci->lock); - /* Make sure the xHC is halted for a USB3 roothub - * (xhci_stop() could be called as part of failed init). - */ - xhci_halt(xhci); - xhci_reset(xhci); - spin_unlock_irq(&xhci->lock); - xhci_cleanup_msix(xhci); /* Deleting Compliance Mode Recovery Timer */ -- GitLab From 664265d84dd6a3d38b9df81c0d2546e189ab32cd Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 26 Jul 2017 00:01:41 +0800 Subject: [PATCH 0763/5498] f2fs: support project quota This patch adds to support plain project quota. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 1 + fs/f2fs/f2fs.h | 33 ++++++++++++++++++++++++++++++ fs/f2fs/file.c | 13 ------------ fs/f2fs/inode.c | 24 +++++++++++++++++++++- fs/f2fs/namei.c | 30 +++++++++++++++++++++++++++ fs/f2fs/node.c | 7 ++++++- fs/f2fs/super.c | 26 +++++++++++++++++++++++ include/linux/f2fs_fs.h | 3 +++ include/uapi/linux/fs.h | 1 + 9 files changed, 123 insertions(+), 15 deletions(-) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index a24117dafc93..50e9751883e6 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -161,6 +161,7 @@ io_bits=%u Set the bit size of write IO requests. It should be set with "mode=lfs". usrquota Enable plain user disk quota accounting. grpquota Enable plain group disk quota accounting. +prjquota Enable plain project quota accounting. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index daa54f8857df..02da8042feeb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -93,6 +93,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_LFS 0x00040000 #define F2FS_MOUNT_USRQUOTA 0x00080000 #define F2FS_MOUNT_GRPQUOTA 0x00100000 +#define F2FS_MOUNT_PRJQUOTA 0x00200000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -116,6 +117,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_BLKZONED 0x0002 #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 #define F2FS_FEATURE_EXTRA_ATTR 0x0008 +#define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -667,6 +669,7 @@ struct f2fs_inode_info { struct rw_semaphore i_mmap_sem; int i_extra_isize; /* size of extra space located in i_addr */ + kprojid_t i_projid; /* id for project quota */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1025,6 +1028,10 @@ enum { MAX_TIME, }; +#ifdef CONFIG_QUOTA +#define F2FS_MAXQUOTAS 2 +#endif + struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -1981,6 +1988,20 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) *addr ^= mask; } +#define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) +#define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) +#define F2FS_FL_INHERITED (FS_PROJINHERIT_FL) + +static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) +{ + if (S_ISDIR(mode)) + return flags; + else if (S_ISREG(mode)) + return flags & F2FS_REG_FLMASK; + else + return flags & F2FS_OTHER_FLMASK; +} + /* used for f2fs_inode_info->flags */ enum { FI_NEW_INODE, /* indicate newly allocated inode */ @@ -2009,6 +2030,7 @@ enum { FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ FI_HOT_DATA, /* indicate file is hot */ FI_EXTRA_ATTR, /* indicate file has extra attribute */ + FI_PROJ_INHERIT, /* indicate file inherits projectid */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, @@ -2352,6 +2374,12 @@ static inline int get_extra_isize(struct inode *inode) (offsetof(struct f2fs_inode, i_extra_end) - \ offsetof(struct f2fs_inode, i_extra_isize)) \ +#define F2FS_OLD_ATTRIBUTE_SIZE (offsetof(struct f2fs_inode, i_addr)) +#define F2FS_FITS_IN_INODE(f2fs_inode, extra_isize, field) \ + ((offsetof(typeof(*f2fs_inode), field) + \ + sizeof((f2fs_inode)->field)) \ + <= (F2FS_OLD_ATTRIBUTE_SIZE + extra_isize)) \ + /* * file.c */ @@ -2950,6 +2978,11 @@ static inline int f2fs_sb_has_extra_attr(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_EXTRA_ATTR); } +static inline int f2fs_sb_has_project_quota(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_PRJQUOTA); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3c3990cefee2..15d45610ee1c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1510,19 +1510,6 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id) return 0; } -#define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) -#define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) - -static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) -{ - if (S_ISDIR(mode)) - return flags; - else if (S_ISREG(mode)) - return flags & F2FS_REG_FLMASK; - else - return flags & F2FS_OTHER_FLMASK; -} - static int f2fs_ioc_getflags(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index e1699e542d17..c2c102eb15db 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -114,6 +114,7 @@ static int do_read_inode(struct inode *inode) struct f2fs_inode_info *fi = F2FS_I(inode); struct page *node_page; struct f2fs_inode *ri; + projid_t i_projid; /* Check if ino is within scope */ if (check_nid_range(sbi, inode->i_ino)) { @@ -173,6 +174,16 @@ static int do_read_inode(struct inode *inode) if (!need_inode_block_update(sbi, inode->i_ino)) fi->last_disk_size = inode->i_size; + if (fi->i_flags & FS_PROJINHERIT_FL) + set_inode_flag(inode, FI_PROJ_INHERIT); + + if (f2fs_has_extra_attr(inode) && f2fs_sb_has_project_quota(sbi->sb) && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_projid)) + i_projid = (projid_t)le32_to_cpu(ri->i_projid); + else + i_projid = F2FS_DEF_PROJID; + fi->i_projid = make_kprojid(&init_user_ns, i_projid); + f2fs_put_page(node_page, 1); stat_inc_inline_xattr(inode); @@ -299,9 +310,20 @@ int update_inode(struct inode *inode, struct page *node_page) ri->i_generation = cpu_to_le32(inode->i_generation); ri->i_dir_level = F2FS_I(inode)->i_dir_level; - if (f2fs_has_extra_attr(inode)) + if (f2fs_has_extra_attr(inode)) { ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) && + F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + i_projid)) { + projid_t i_projid; + + i_projid = from_kprojid(&init_user_ns, + F2FS_I(inode)->i_projid); + ri->i_projid = cpu_to_le32(i_projid); + } + } + __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 70e9a1d1c02f..47359693cf60 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -59,6 +59,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) goto fail; } + if (f2fs_sb_has_project_quota(sbi->sb) && + (F2FS_I(dir)->i_flags & FS_PROJINHERIT_FL)) + F2FS_I(inode)->i_projid = F2FS_I(dir)->i_projid; + else + F2FS_I(inode)->i_projid = make_kprojid(&init_user_ns, + F2FS_DEF_PROJID); dquot_initialize(inode); err = dquot_alloc_inode(inode); @@ -89,6 +95,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) stat_inc_inline_inode(inode); stat_inc_inline_dir(inode); + F2FS_I(inode)->i_flags = + f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED); + + if (F2FS_I(inode)->i_flags & FS_PROJINHERIT_FL) + set_inode_flag(inode, FI_PROJ_INHERIT); + trace_f2fs_new_inode(inode, 0); return inode; @@ -206,6 +218,11 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, !fscrypt_has_permitted_context(dir, inode)) return -EPERM; + if (is_inode_flag_set(dir, FI_PROJ_INHERIT) && + (!projid_eq(F2FS_I(dir)->i_projid, + F2FS_I(old_dentry->d_inode)->i_projid))) + return -EXDEV; + dquot_initialize(dir); f2fs_balance_fs(sbi, true); @@ -727,6 +744,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } + if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && + (!projid_eq(F2FS_I(new_dir)->i_projid, + F2FS_I(old_dentry->d_inode)->i_projid))) + return -EXDEV; + dquot_initialize(old_dir); dquot_initialize(new_dir); @@ -911,6 +933,14 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, !fscrypt_has_permitted_context(old_dir, new_inode))) return -EPERM; + if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && + !projid_eq(F2FS_I(new_dir)->i_projid, + F2FS_I(old_dentry->d_inode)->i_projid)) || + (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && + !projid_eq(F2FS_I(old_dir)->i_projid, + F2FS_I(new_dentry->d_inode)->i_projid))) + return -EXDEV; + dquot_initialize(old_dir); dquot_initialize(new_dir); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index e903cb7451dd..4d30dd6d28a2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2274,8 +2274,13 @@ retry: dst->i_links = cpu_to_le32(1); dst->i_xattr_nid = 0; dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR); - if (dst->i_inline & F2FS_EXTRA_ATTR) + if (dst->i_inline & F2FS_EXTRA_ATTR) { dst->i_extra_isize = src->i_extra_isize; + if (f2fs_sb_has_project_quota(sbi->sb) && + F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), + i_projid)) + dst->i_projid = src->i_projid; + } new_ni = old_ni; new_ni.ino = ino; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c76da794b6ff..06de39e237d1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -107,6 +107,7 @@ enum { Opt_fault_injection, Opt_usrquota, Opt_grpquota, + Opt_prjquota, Opt_err, }; @@ -142,6 +143,7 @@ static match_table_t f2fs_tokens = { {Opt_fault_injection, "fault_injection=%u"}, {Opt_usrquota, "usrquota"}, {Opt_grpquota, "grpquota"}, + {Opt_prjquota, "prjquota"}, {Opt_err, NULL}, }; @@ -382,9 +384,18 @@ static int parse_options(struct super_block *sb, char *options) case Opt_grpquota: set_opt(sbi, GRPQUOTA); break; + case Opt_prjquota: + if (F2FS_MAXQUOTAS <= 2) { + f2fs_msg(sb, KERN_INFO, + "prjquota operations not supported"); + return -EINVAL; + } + set_opt(sbi, PRJQUOTA); + break; #else case Opt_usrquota: case Opt_grpquota: + case Opt_prjquota: f2fs_msg(sb, KERN_INFO, "quota operations not supported"); break; @@ -801,6 +812,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",usrquota"); if (test_opt(sbi, GRPQUOTA)) seq_puts(seq, ",grpquota"); + if (test_opt(sbi, PRJQUOTA)) + seq_puts(seq, ",prjquota"); #endif return 0; @@ -1153,6 +1166,12 @@ static void f2fs_quota_off_umount(struct super_block *sb) f2fs_quota_off(sb, type); } +int f2fs_get_projid(struct inode *inode, kprojid_t *projid) +{ + *projid = F2FS_I(inode)->i_projid; + return 0; +} + static const struct dquot_operations f2fs_quota_operations = { .get_reserved_space = f2fs_get_reserved_space, .write_dquot = dquot_commit, @@ -1162,6 +1181,10 @@ static const struct dquot_operations f2fs_quota_operations = { .write_info = dquot_commit_info, .alloc_dquot = dquot_alloc, .destroy_dquot = dquot_destroy, +#if 0 /* not support */ + .get_projid = f2fs_get_projid, + .get_next_id = dquot_get_next_id, +#endif }; static const struct quotactl_ops f2fs_quotactl_ops = { @@ -1946,6 +1969,9 @@ try_onemore: #ifdef CONFIG_QUOTA sb->dq_op = &f2fs_quota_operations; sb->s_qcop = &f2fs_quotactl_ops; +#if 0 /* not support */ + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; +#endif #endif sb->s_op = &f2fs_sops; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index b7331bfd440f..bc0e6fc41575 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -239,6 +239,7 @@ struct f2fs_inode { struct { __le16 i_extra_isize; /* extra inode attribute size */ __le16 i_padding; /* padding */ + __le32 i_projid; /* project id */ __le32 i_extra_end[0]; /* for attribute size calculation */ }; __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ @@ -522,4 +523,6 @@ enum { #define S_SHIFT 12 +#define F2FS_DEF_PROJID 0 /* default project ID */ + #endif /* _LINUX_F2FS_FS_H */ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index b3ad4c561337..ecdc36cf4d42 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -243,6 +243,7 @@ struct fscrypt_key { #define FS_EXTENT_FL 0x00080000 /* Extents */ #define FS_DIRECTIO_FL 0x00100000 /* Use direct i/o */ #define FS_NOCOW_FL 0x00800000 /* Do not cow file */ +#define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ #define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ -- GitLab From 34802f2356c04a78421135ade2ff4df85ce20e37 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 26 Jul 2017 11:24:13 -0700 Subject: [PATCH 0764/5498] f2fs: avoid naming confusion of sysfs init This patch changes the function names of sysfs init to follow ext4. f2fs_init_sysfs <-> f2fs_register_sysfs f2fs_exit_sysfs <-> f2fs_unregister_sysfs Suggested-by: Chao Yu Reivewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 ++++---- fs/f2fs/super.c | 12 ++++++------ fs/f2fs/sysfs.c | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 02da8042feeb..5ad855f48b2b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2938,10 +2938,10 @@ void destroy_extent_cache(void); /* * sysfs.c */ -int __init f2fs_register_sysfs(void); -void f2fs_unregister_sysfs(void); -int f2fs_init_sysfs(struct f2fs_sb_info *sbi); -void f2fs_exit_sysfs(struct f2fs_sb_info *sbi); +int __init f2fs_init_sysfs(void); +void f2fs_exit_sysfs(void); +int f2fs_register_sysfs(struct f2fs_sb_info *sbi); +void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi); /* * crypto support diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 06de39e237d1..feca32901b3b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -640,7 +640,7 @@ static void f2fs_put_super(struct super_block *sb) kfree(sbi->ckpt); - f2fs_exit_sysfs(sbi); + f2fs_unregister_sysfs(sbi); sb->s_fs_info = NULL; if (sbi->s_chksum_driver) @@ -2137,7 +2137,7 @@ try_onemore: goto free_root_inode; } - err = f2fs_init_sysfs(sbi); + err = f2fs_register_sysfs(sbi); if (err) goto free_root_inode; @@ -2208,7 +2208,7 @@ skip_recovery: free_sysfs: f2fs_sync_inode_meta(sbi); - f2fs_exit_sysfs(sbi); + f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); sb->s_root = NULL; @@ -2326,7 +2326,7 @@ static int __init init_f2fs_fs(void) err = create_extent_cache(); if (err) goto free_checkpoint_caches; - err = f2fs_register_sysfs(); + err = f2fs_init_sysfs(); if (err) goto free_extent_cache; err = register_shrinker(&f2fs_shrinker_info); @@ -2345,7 +2345,7 @@ free_filesystem: free_shrinker: unregister_shrinker(&f2fs_shrinker_info); free_sysfs: - f2fs_unregister_sysfs(); + f2fs_exit_sysfs(); free_extent_cache: destroy_extent_cache(); free_checkpoint_caches: @@ -2365,7 +2365,7 @@ static void __exit exit_f2fs_fs(void) f2fs_destroy_root_stats(); unregister_filesystem(&f2fs_fs_type); unregister_shrinker(&f2fs_shrinker_info); - f2fs_unregister_sysfs(); + f2fs_exit_sysfs(); destroy_extent_cache(); destroy_checkpoint_caches(); destroy_segment_manager_caches(); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 71191d89917d..5a78b9af92ef 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -304,7 +304,7 @@ static const struct file_operations f2fs_seq_##_name##_fops = { \ F2FS_PROC_FILE_DEF(segment_info); F2FS_PROC_FILE_DEF(segment_bits); -int __init f2fs_register_sysfs(void) +int __init f2fs_init_sysfs(void) { f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); @@ -314,13 +314,13 @@ int __init f2fs_register_sysfs(void) return 0; } -void f2fs_unregister_sysfs(void) +void f2fs_exit_sysfs(void) { kset_unregister(f2fs_kset); remove_proc_entry("fs/f2fs", NULL); } -int f2fs_init_sysfs(struct f2fs_sb_info *sbi) +int f2fs_register_sysfs(struct f2fs_sb_info *sbi) { struct super_block *sb = sbi->sb; int err; @@ -351,7 +351,7 @@ err_out: return err; } -void f2fs_exit_sysfs(struct f2fs_sb_info *sbi) +void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) { kobject_del(&sbi->s_kobj); kobject_put(&sbi->s_kobj); -- GitLab From f5938dfc3ebf71780d70cc5d5f2ff7cbafaaa198 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 28 Jul 2017 02:29:12 -0700 Subject: [PATCH 0765/5498] f2fs: don't need to wait for node writes for atomic write We have a node chain to serialize node block writes, so if any IOs for node block writes are reordered, we'll get broken node chain. IOWs, roll-forward recovery will see all or none node blocks given fsync mark. E.g., Node chain consists of: N1 -> N2 -> N3 -> NFSYNC -> N1' -> N2' -> N'FSYNC Reordered to: 1) N1 -> N2 -> N3 -> N2' -> NFSYNC -> N'FSYNC -> power-cut 2) N1 -> N2 -> N3 -> N1' -> NFSYNC -> power-cut 3) N1 -> N2 -> NFSYNC -> N1' -> N'FSYNC -> N3 -> power-cut 4) N1 -> NFSYNC -> N1' -> N2' -> N'FSYNC -> N3 -> power-cut Roll-forward recovery can proceed to: 1) N1 -> N2 -> N3 -> NFSYNC -> X 2) N1 -> N2 -> N3 -> NFSYNC -> N1' -> X 3) N1 -> N2 -> N3 -> FSYNC -> N1' -> X 4) N1 -> X Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 15d45610ee1c..f6b9c5d6e446 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -277,9 +277,19 @@ sync_nodes: goto sync_nodes; } - ret = wait_on_node_pages_writeback(sbi, ino); - if (ret) - goto out; + /* + * If it's atomic_write, it's just fine to keep write ordering. So + * here we don't need to wait for node write completion, since we use + * node chain which serializes node blocks. If one of node writes are + * reordered, we can see simply broken chain, resulting in stopping + * roll-forward recovery. It means we'll recover all or none node blocks + * given fsync mark. + */ + if (!atomic) { + ret = wait_on_node_pages_writeback(sbi, ino); + if (ret) + goto out; + } /* once recovery info is written, don't need to tack this */ remove_ino_entry(sbi, ino, APPEND_INO); -- GitLab From e21c28557c1aa88781c177be2b01d32ca03b5728 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 29 Jul 2017 00:32:53 +0800 Subject: [PATCH 0766/5498] f2fs: introduce f2fs_statfs_project This patch introduces f2fs_statfs_project, it enables to show usage status of directory tree which is limited with project quota. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index feca32901b3b..3c598cdbd42f 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -697,6 +697,48 @@ static int f2fs_unfreeze(struct super_block *sb) return 0; } +#ifdef CONFIG_QUOTA +static int f2fs_statfs_project(struct super_block *sb, + kprojid_t projid, struct kstatfs *buf) +{ + struct kqid qid; + struct dquot *dquot; + u64 limit; + u64 curblock; + + qid = make_kqid_projid(projid); + dquot = dqget(sb, qid); + if (IS_ERR(dquot)) + return PTR_ERR(dquot); + spin_lock(&dq_data_lock); + + limit = (dquot->dq_dqb.dqb_bsoftlimit ? + dquot->dq_dqb.dqb_bsoftlimit : + dquot->dq_dqb.dqb_bhardlimit) >> sb->s_blocksize_bits; + if (limit && buf->f_blocks > limit) { + curblock = dquot->dq_dqb.dqb_curspace >> sb->s_blocksize_bits; + buf->f_blocks = limit; + buf->f_bfree = buf->f_bavail = + (buf->f_blocks > curblock) ? + (buf->f_blocks - curblock) : 0; + } + + limit = dquot->dq_dqb.dqb_isoftlimit ? + dquot->dq_dqb.dqb_isoftlimit : + dquot->dq_dqb.dqb_ihardlimit; + if (limit && buf->f_files > limit) { + buf->f_files = limit; + buf->f_ffree = + (buf->f_files > dquot->dq_dqb.dqb_curinodes) ? + (buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0; + } + + spin_unlock(&dq_data_lock); + dqput(dquot); + return 0; +} +#endif + static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -732,6 +774,12 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); +#ifdef CONFIG_QUOTA + if (is_inode_flag_set(dentry->d_inode, FI_PROJ_INHERIT) && + sb_has_quota_limits_enabled(sb, PRJQUOTA)) { + f2fs_statfs_project(sb, F2FS_I(dentry->d_inode)->i_projid, buf); + } +#endif return 0; } -- GitLab From cf5551c98609dfb24512dcc07b56a9ff7531b961 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Thu, 27 Jul 2017 20:11:00 +0800 Subject: [PATCH 0767/5498] f2fs: provide f2fs_balance_fs to __write_node_page Let node writeback also do f2fs_balance_fs to ensure there are always enough free segments. Signed-off-by: Yunlong Song Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/node.c | 16 ++++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 01d4ca3c03ff..bc30d319ce78 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1018,7 +1018,7 @@ retry_flush_nodes: if (get_pages(sbi, F2FS_DIRTY_NODES)) { up_write(&sbi->node_write); - err = sync_node_pages(sbi, &wbc); + err = sync_node_pages(sbi, &wbc, false); if (err) { up_write(&sbi->node_change); f2fs_unlock_all(sbi); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5ad855f48b2b..9b3988bce021 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2504,7 +2504,8 @@ struct page *get_node_page_ra(struct page *parent, int start); void move_node_page(struct page *node_page, int gc_type); int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic); -int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc); +int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, + bool do_balance); void build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount); bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid); void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4d30dd6d28a2..9066ebb3febf 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1333,7 +1333,7 @@ continue_unlock: } static int __write_node_page(struct page *page, bool atomic, bool *submitted, - struct writeback_control *wbc) + struct writeback_control *wbc, bool do_balance) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); nid_t nid; @@ -1402,6 +1402,8 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, if (submitted) *submitted = fio.submitted; + if (do_balance) + f2fs_balance_fs(sbi, false); return 0; redirty_out: @@ -1412,7 +1414,7 @@ redirty_out: static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { - return __write_node_page(page, false, NULL, wbc); + return __write_node_page(page, false, NULL, wbc, false); } int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, @@ -1500,7 +1502,7 @@ continue_unlock: ret = __write_node_page(page, atomic && page == last_page, - &submitted, wbc); + &submitted, wbc, true); if (ret) { unlock_page(page); f2fs_put_page(last_page, 0); @@ -1537,7 +1539,8 @@ out: return ret ? -EIO: 0; } -int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc) +int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, + bool do_balance) { pgoff_t index, end; struct pagevec pvec; @@ -1615,7 +1618,8 @@ continue_unlock: set_fsync_mark(page, 0); set_dentry_mark(page, 0); - ret = __write_node_page(page, false, &submitted, wbc); + ret = __write_node_page(page, false, &submitted, + wbc, do_balance); if (ret) unlock_page(page); else if (submitted) @@ -1707,7 +1711,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; blk_start_plug(&plug); - sync_node_pages(sbi, wbc); + sync_node_pages(sbi, wbc, true); blk_finish_plug(&plug); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return 0; -- GitLab From 1b72a823260ce538360f5224a4ea502cc17b9625 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 30 Jul 2017 09:45:14 -0700 Subject: [PATCH 0768/5498] f2fs: return wrong error number on f2fs_quota_write This must return size, not error number. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3c598cdbd42f..bd89af815832 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1113,7 +1113,7 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type, } if (len == towrite) - return err; + return 0; inode->i_version++; inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); -- GitLab From 19c7d1f7ca39ae3c13a1c0e48a91755ed71d7871 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 31 Jul 2017 20:19:09 +0800 Subject: [PATCH 0769/5498] f2fs: support inode checksum This patch adds to support inode checksum in f2fs. Signed-off-by: Chao Yu [Jaegeuk Kim: fix verification flow] Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 32 +++++++++++++++++++ fs/f2fs/inode.c | 70 +++++++++++++++++++++++++++++++++++++++++ fs/f2fs/node.c | 7 +++++ fs/f2fs/segment.c | 5 ++- fs/f2fs/super.c | 5 +++ include/linux/f2fs_fs.h | 1 + 6 files changed, 119 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9b3988bce021..eff79c8eac49 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -118,6 +118,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 #define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_FEATURE_PRJQUOTA 0x0010 +#define F2FS_FEATURE_INODE_CHKSUM 0x0020 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -1183,6 +1184,9 @@ struct f2fs_sb_info { /* Reference to checksum algorithm driver via cryptoapi */ struct crypto_shash *s_chksum_driver; + /* Precomputed FS UUID checksum for seeding other checksums */ + __u32 s_chksum_seed; + /* For fault injection */ #ifdef CONFIG_F2FS_FAULT_INJECTION struct f2fs_fault_info fault_info; @@ -1271,6 +1275,27 @@ static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc, return f2fs_crc32(sbi, buf, buf_size) == blk_crc; } +static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, + const void *address, unsigned int length) +{ + struct { + struct shash_desc shash; + char ctx[4]; + } desc; + int err; + + BUG_ON(crypto_shash_descsize(sbi->s_chksum_driver) != sizeof(desc.ctx)); + + desc.shash.tfm = sbi->s_chksum_driver; + desc.shash.flags = 0; + *(u32 *)desc.ctx = crc; + + err = crypto_shash_update(&desc.shash, address, length); + BUG_ON(err); + + return *(u32 *)desc.ctx; +} + static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) { return container_of(inode, struct f2fs_inode_info, vfs_inode); @@ -2399,6 +2424,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); * inode.c */ void f2fs_set_inode_flags(struct inode *inode); +bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page); +void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page); struct inode *f2fs_iget(struct super_block *sb, unsigned long ino); struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino); int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink); @@ -2984,6 +3011,11 @@ static inline int f2fs_sb_has_project_quota(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_PRJQUOTA); } +static inline int f2fs_sb_has_inode_chksum(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c2c102eb15db..ebbe53aab202 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -108,6 +108,76 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage) return; } +static bool f2fs_enable_inode_chksum(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_inode *ri = &F2FS_NODE(page)->i; + int extra_isize = le32_to_cpu(ri->i_extra_isize); + + if (!f2fs_sb_has_inode_chksum(sbi->sb)) + return false; + + if (!RAW_IS_INODE(F2FS_NODE(page)) || !(ri->i_inline & F2FS_EXTRA_ATTR)) + return false; + + if (!F2FS_FITS_IN_INODE(ri, extra_isize, i_inode_checksum)) + return false; + + return true; +} + +static __u32 f2fs_inode_chksum(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_node *node = F2FS_NODE(page); + struct f2fs_inode *ri = &node->i; + __le32 ino = node->footer.ino; + __le32 gen = ri->i_generation; + __u32 chksum, chksum_seed; + __u32 dummy_cs = 0; + unsigned int offset = offsetof(struct f2fs_inode, i_inode_checksum); + unsigned int cs_size = sizeof(dummy_cs); + + chksum = f2fs_chksum(sbi, sbi->s_chksum_seed, (__u8 *)&ino, + sizeof(ino)); + chksum_seed = f2fs_chksum(sbi, chksum, (__u8 *)&gen, sizeof(gen)); + + chksum = f2fs_chksum(sbi, chksum_seed, (__u8 *)ri, offset); + chksum = f2fs_chksum(sbi, chksum, (__u8 *)&dummy_cs, cs_size); + offset += cs_size; + chksum = f2fs_chksum(sbi, chksum, (__u8 *)ri + offset, + F2FS_BLKSIZE - offset); + return chksum; +} + +bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_inode *ri; + __u32 provided, calculated; + + if (!f2fs_enable_inode_chksum(sbi, page)) + return true; + + ri = &F2FS_NODE(page)->i; + provided = le32_to_cpu(ri->i_inode_checksum); + calculated = f2fs_inode_chksum(sbi, page); + + if (provided != calculated) + f2fs_msg(sbi->sb, KERN_WARNING, + "checksum invalid, ino = %x, %x vs. %x", + ino_of_node(page), provided, calculated); + + return provided == calculated; +} + +void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_inode *ri = &F2FS_NODE(page)->i; + + if (!f2fs_enable_inode_chksum(sbi, page)) + return; + + ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page)); +} + static int do_read_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9066ebb3febf..84576c014a66 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1171,6 +1171,11 @@ repeat: err = -EIO; goto out_err; } + + if (!f2fs_inode_chksum_verify(sbi, page)) { + err = -EBADMSG; + goto out_err; + } page_hit: if(unlikely(nid != nid_of_node(page))) { f2fs_msg(sbi->sb, KERN_WARNING, "inconsistent node block, " @@ -2284,6 +2289,8 @@ retry: F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), i_projid)) dst->i_projid = src->i_projid; + + f2fs_inode_chksum_set(sbi, ipage); } new_ni = old_ni; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 98cef7f28c37..7fb126a27998 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2294,9 +2294,12 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, mutex_unlock(&sit_i->sentry_lock); - if (page && IS_NODESEG(type)) + if (page && IS_NODESEG(type)) { fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); + f2fs_inode_chksum_set(sbi, page); + } + if (add_list) { struct f2fs_bio_info *io; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index bd89af815832..2b26fa383c5e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1983,6 +1983,11 @@ try_onemore: sb->s_fs_info = sbi; sbi->raw_super = raw_super; + /* precompute checksum seed for metadata */ + if (f2fs_sb_has_inode_chksum(sb)) + sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid, + sizeof(raw_super->uuid)); + /* * The BLKZONED feature indicates that the drive was formatted with * zone alignment optimization. This is optional for host-aware diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index bc0e6fc41575..00cdb19429d5 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -240,6 +240,7 @@ struct f2fs_inode { __le16 i_extra_isize; /* extra inode attribute size */ __le16 i_padding; /* padding */ __le32 i_projid; /* project id */ + __le32 i_inode_checksum;/* inode meta checksum */ __le32 i_extra_end[0]; /* for attribute size calculation */ }; __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ -- GitLab From 37ddbebd971bdded49967bc3b09ced00255dabe6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 21 Jul 2017 17:14:09 -0700 Subject: [PATCH 0770/5498] f2fs: expose features to sysfs entry This patch exposes what features are supported by current f2fs build to sysfs entry via: /sys/fs/f2fs/features/ /sys/fs/f2fs/dev/features Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 156 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 130 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 5a78b9af92ef..1e31d0c5b6ab 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -18,7 +18,6 @@ #include "gc.h" static struct proc_dir_entry *f2fs_proc_root; -static struct kset *f2fs_kset; /* Sysfs support for f2fs */ enum { @@ -41,6 +40,7 @@ struct f2fs_attr { const char *, size_t); int struct_type; int offset; + int id; }; static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) @@ -76,6 +76,34 @@ static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, BD_PART_WRITTEN(sbi))); } +static ssize_t features_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct super_block *sb = sbi->sb; + int len = 0; + + if (!sb->s_bdev->bd_part) + return snprintf(buf, PAGE_SIZE, "0\n"); + + if (f2fs_sb_has_crypto(sb)) + len += snprintf(buf, PAGE_SIZE - len, "%s", + "encryption"); + if (f2fs_sb_mounted_blkzoned(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "blkzoned"); + if (f2fs_sb_has_extra_attr(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "extra_attr"); + if (f2fs_sb_has_project_quota(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "projquota"); + if (f2fs_sb_has_inode_chksum(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "inode_checksum"); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + return len; +} + static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { @@ -155,6 +183,30 @@ static void f2fs_sb_release(struct kobject *kobj) complete(&sbi->s_kobj_unregister); } +enum feat_id { + FEAT_CRYPTO = 0, + FEAT_BLKZONED, + FEAT_ATOMIC_WRITE, + FEAT_EXTRA_ATTR, + FEAT_PROJECT_QUOTA, + FEAT_INODE_CHECKSUM, +}; + +static ssize_t f2fs_feature_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + switch (a->id) { + case FEAT_CRYPTO: + case FEAT_BLKZONED: + case FEAT_ATOMIC_WRITE: + case FEAT_EXTRA_ATTR: + case FEAT_PROJECT_QUOTA: + case FEAT_INODE_CHECKSUM: + return snprintf(buf, PAGE_SIZE, "supported\n"); + } + return 0; +} + #define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ static struct f2fs_attr f2fs_attr_##_name = { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ @@ -172,6 +224,13 @@ static struct f2fs_attr f2fs_attr_##_name = { \ #define F2FS_GENERAL_RO_ATTR(name) \ static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) +#define F2FS_FEATURE_RO_ATTR(_name, _id) \ +static struct f2fs_attr f2fs_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = 0444 }, \ + .show = f2fs_feature_show, \ + .id = _id, \ +} + F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); @@ -196,6 +255,18 @@ F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); #endif F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); +F2FS_GENERAL_RO_ATTR(features); + +#ifdef CONFIG_F2FS_FS_ENCRYPTION +F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); +#endif +#ifdef CONFIG_BLK_DEV_ZONED +F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED); +#endif +F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE); +F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); +F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); +F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -222,21 +293,53 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(inject_type), #endif ATTR_LIST(lifetime_write_kbytes), + ATTR_LIST(features), ATTR_LIST(reserved_blocks), NULL, }; +static struct attribute *f2fs_feat_attrs[] = { +#ifdef CONFIG_F2FS_FS_ENCRYPTION + ATTR_LIST(encryption), +#endif +#ifdef CONFIG_BLK_DEV_ZONED + ATTR_LIST(block_zoned), +#endif + ATTR_LIST(atomic_write), + ATTR_LIST(extra_attr), + ATTR_LIST(project_quota), + ATTR_LIST(inode_checksum), + NULL, +}; + static const struct sysfs_ops f2fs_attr_ops = { .show = f2fs_attr_show, .store = f2fs_attr_store, }; -static struct kobj_type f2fs_ktype = { +static struct kobj_type f2fs_sb_ktype = { .default_attrs = f2fs_attrs, .sysfs_ops = &f2fs_attr_ops, .release = f2fs_sb_release, }; +static struct kobj_type f2fs_ktype = { + .sysfs_ops = &f2fs_attr_ops, +}; + +static struct kset f2fs_kset = { + .kobj = {.ktype = &f2fs_ktype}, +}; + +static struct kobj_type f2fs_feat_ktype = { + .default_attrs = f2fs_feat_attrs, + .sysfs_ops = &f2fs_attr_ops, +}; + +static struct kobject f2fs_feat = { + .kset = &f2fs_kset, +}; + static int segment_info_seq_show(struct seq_file *seq, void *offset) { struct super_block *sb = seq->private; @@ -306,18 +409,29 @@ F2FS_PROC_FILE_DEF(segment_bits); int __init f2fs_init_sysfs(void) { - f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + int ret; - f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); - if (!f2fs_kset) - return -ENOMEM; - return 0; + kobject_set_name(&f2fs_kset.kobj, "f2fs"); + f2fs_kset.kobj.parent = fs_kobj; + ret = kset_register(&f2fs_kset); + if (ret) + return ret; + + ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype, + NULL, "features"); + if (ret) + kset_unregister(&f2fs_kset); + else + f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + return ret; } void f2fs_exit_sysfs(void) { - kset_unregister(f2fs_kset); + kobject_put(&f2fs_feat); + kset_unregister(&f2fs_kset); remove_proc_entry("fs/f2fs", NULL); + f2fs_proc_root = NULL; } int f2fs_register_sysfs(struct f2fs_sb_info *sbi) @@ -325,6 +439,13 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) struct super_block *sb = sbi->sb; int err; + sbi->s_kobj.kset = &f2fs_kset; + init_completion(&sbi->s_kobj_unregister); + err = kobject_init_and_add(&sbi->s_kobj, &f2fs_sb_ktype, NULL, + "%s", sb->s_id); + if (err) + return err; + if (f2fs_proc_root) sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); @@ -334,32 +455,15 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, &f2fs_seq_segment_bits_fops, sb); } - - sbi->s_kobj.kset = f2fs_kset; - init_completion(&sbi->s_kobj_unregister); - err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, - "%s", sb->s_id); - if (err) - goto err_out; return 0; -err_out: - if (sbi->s_proc) { - remove_proc_entry("segment_info", sbi->s_proc); - remove_proc_entry("segment_bits", sbi->s_proc); - remove_proc_entry(sb->s_id, f2fs_proc_root); - } - return err; } void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) { - kobject_del(&sbi->s_kobj); - kobject_put(&sbi->s_kobj); - wait_for_completion(&sbi->s_kobj_unregister); - if (sbi->s_proc) { remove_proc_entry("segment_info", sbi->s_proc); remove_proc_entry("segment_bits", sbi->s_proc); remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); } + kobject_del(&sbi->s_kobj); } -- GitLab From a888ef113451be1e31304a592e8ffd4375116d2b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 2 Aug 2017 20:58:29 -0700 Subject: [PATCH 0771/5498] f2fs: use printk_ratelimited for f2fs_msg This patch reduces contention of printks. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2b26fa383c5e..b13823dbc513 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -155,7 +155,7 @@ void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - printk("%sF2FS-fs (%s): %pV\n", level, sb->s_id, &vaf); + printk_ratelimited("%sF2FS-fs (%s): %pV\n", level, sb->s_id, &vaf); va_end(args); } -- GitLab From e052dee964759b95fd0577fd267352634c1379a1 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Wed, 2 Aug 2017 21:20:13 +0800 Subject: [PATCH 0772/5498] f2fs: update cur_valid_map_mir together with cur_valid_map When cur_valid_map passes the f2fs_test_and_set(,clear)_bit test, cur_valid_map_mir update is skipped unlikely, so fix it. The fix now changes the mirror check together with cur_valid_map all the time. Signed-off-by: Yunlong Song Signed-off-by: Chao Yu [Jaegeuk Kim: Fix unused variable and add unlikely for corner condition.] Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7fb126a27998..9b9fdca1e9f6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1583,6 +1583,10 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) struct seg_entry *se; unsigned int segno, offset; long int new_vblocks; + bool exist; +#ifdef CONFIG_F2FS_CHECK_FS + bool mir_exist; +#endif segno = GET_SEGNO(sbi, blkaddr); @@ -1599,17 +1603,23 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) /* Update valid block bitmap */ if (del > 0) { - if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) { + exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); #ifdef CONFIG_F2FS_CHECK_FS - if (f2fs_test_and_set_bit(offset, - se->cur_valid_map_mir)) - f2fs_bug_on(sbi, 1); - else - WARN_ON(1); -#else + mir_exist = f2fs_test_and_set_bit(offset, + se->cur_valid_map_mir); + if (unlikely(exist != mir_exist)) { + f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error " + "when setting bitmap, blk:%u, old bit:%d", + blkaddr, exist); f2fs_bug_on(sbi, 1); + } #endif + if (unlikely(exist)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Bitmap was wrongly set, blk:%u", blkaddr); + f2fs_bug_on(sbi, 1); } + if (f2fs_discard_en(sbi) && !f2fs_test_and_set_bit(offset, se->discard_map)) sbi->discard_blks--; @@ -1620,17 +1630,23 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) se->ckpt_valid_blocks++; } } else { - if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) { + exist = f2fs_test_and_clear_bit(offset, se->cur_valid_map); #ifdef CONFIG_F2FS_CHECK_FS - if (!f2fs_test_and_clear_bit(offset, - se->cur_valid_map_mir)) - f2fs_bug_on(sbi, 1); - else - WARN_ON(1); -#else + mir_exist = f2fs_test_and_clear_bit(offset, + se->cur_valid_map_mir); + if (unlikely(exist != mir_exist)) { + f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error " + "when clearing bitmap, blk:%u, old bit:%d", + blkaddr, exist); f2fs_bug_on(sbi, 1); + } #endif + if (unlikely(!exist)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Bitmap was wrongly cleared, blk:%u", blkaddr); + f2fs_bug_on(sbi, 1); } + if (f2fs_discard_en(sbi) && f2fs_test_and_clear_bit(offset, se->discard_map)) sbi->discard_blks++; -- GitLab From ae9f613169e74ccc4a874c7d73526a829dc0716a Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Wed, 2 Aug 2017 22:16:54 +0800 Subject: [PATCH 0773/5498] f2fs: do not change the valid_block value if cur_valid_map was wrongly set or cleared Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9b9fdca1e9f6..e8931e95607f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1618,6 +1618,8 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) f2fs_msg(sbi->sb, KERN_ERR, "Bitmap was wrongly set, blk:%u", blkaddr); f2fs_bug_on(sbi, 1); + se->valid_blocks--; + del = 0; } if (f2fs_discard_en(sbi) && @@ -1645,6 +1647,8 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) f2fs_msg(sbi->sb, KERN_ERR, "Bitmap was wrongly cleared, blk:%u", blkaddr); f2fs_bug_on(sbi, 1); + se->valid_blocks++; + del = 0; } if (f2fs_discard_en(sbi) && -- GitLab From ae2a6cc5233c34298a55e35e82d0f16c3c12d035 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 2 Aug 2017 23:21:48 +0800 Subject: [PATCH 0774/5498] f2fs: add app/fs io stat This patch enables inner app/fs io stats and introduces below virtual fs nodes for exposing stats info: /sys/fs/f2fs//iostat_enable /proc/fs/f2fs//iostat_info Signed-off-by: Chao Yu [Jaegeuk Kim: fix wrong stat assignment] Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 34 +++++++++++++++++-------- fs/f2fs/data.c | 35 +++++++++++++++++++------- fs/f2fs/f2fs.h | 59 +++++++++++++++++++++++++++++++++++++++++--- fs/f2fs/file.c | 7 +++++- fs/f2fs/gc.c | 3 +++ fs/f2fs/inline.c | 1 + fs/f2fs/node.c | 15 ++++++----- fs/f2fs/segment.c | 21 ++++++++++++++-- fs/f2fs/super.c | 4 +++ fs/f2fs/sysfs.c | 52 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 200 insertions(+), 31 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index bc30d319ce78..a310156ecf19 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -231,8 +231,9 @@ void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index) ra_meta_pages(sbi, index, BIO_MAX_PAGES, META_POR, true); } -static int f2fs_write_meta_page(struct page *page, - struct writeback_control *wbc) +static int __f2fs_write_meta_page(struct page *page, + struct writeback_control *wbc, + enum iostat_type io_type) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); @@ -245,7 +246,7 @@ static int f2fs_write_meta_page(struct page *page, if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - write_meta_page(sbi, page); + write_meta_page(sbi, page, io_type); dec_page_count(sbi, F2FS_DIRTY_META); if (wbc->for_reclaim) @@ -264,6 +265,12 @@ redirty_out: return AOP_WRITEPAGE_ACTIVATE; } +static int f2fs_write_meta_page(struct page *page, + struct writeback_control *wbc) +{ + return __f2fs_write_meta_page(page, wbc, FS_META_IO); +} + static int f2fs_write_meta_pages(struct address_space *mapping, struct writeback_control *wbc) { @@ -284,7 +291,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping, trace_f2fs_writepages(mapping->host, wbc, META); diff = nr_pages_to_write(sbi, META, wbc); - written = sync_meta_pages(sbi, META, wbc->nr_to_write); + written = sync_meta_pages(sbi, META, wbc->nr_to_write, FS_META_IO); mutex_unlock(&sbi->cp_mutex); wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff); return 0; @@ -296,7 +303,7 @@ skip_write: } long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, - long nr_to_write) + long nr_to_write, enum iostat_type io_type) { struct address_space *mapping = META_MAPPING(sbi); pgoff_t index = 0, end = ULONG_MAX, prev = ULONG_MAX; @@ -347,7 +354,7 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - if (mapping->a_ops->writepage(page, &wbc)) { + if (__f2fs_write_meta_page(page, &wbc, io_type)) { unlock_page(page); break; } @@ -905,7 +912,14 @@ retry: if (inode) { unsigned long cur_ino = inode->i_ino; + if (is_dir) + F2FS_I(inode)->cp_task = current; + filemap_fdatawrite(inode->i_mapping); + + if (is_dir) + F2FS_I(inode)->cp_task = NULL; + iput(inode); /* We need to give cpu to another writers. */ if (ino == cur_ino) { @@ -1018,7 +1032,7 @@ retry_flush_nodes: if (get_pages(sbi, F2FS_DIRTY_NODES)) { up_write(&sbi->node_write); - err = sync_node_pages(sbi, &wbc, false); + err = sync_node_pages(sbi, &wbc, false, FS_CP_NODE_IO); if (err) { up_write(&sbi->node_change); f2fs_unlock_all(sbi); @@ -1116,7 +1130,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { - sync_meta_pages(sbi, META, LONG_MAX); + sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO); if (unlikely(f2fs_cp_error(sbi))) return -EIO; } @@ -1195,7 +1209,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* Flush all the NAT BITS pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { - sync_meta_pages(sbi, META, LONG_MAX); + sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO); if (unlikely(f2fs_cp_error(sbi))) return -EIO; } @@ -1250,7 +1264,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) percpu_counter_set(&sbi->alloc_valid_block_count, 0); /* Here, we only have one bio having CP pack */ - sync_meta_pages(sbi, META_FLUSH, LONG_MAX); + sync_meta_pages(sbi, META_FLUSH, LONG_MAX, FS_CP_META_IO); /* wait for previous submitted meta pages writeback */ wait_on_all_pages_writeback(sbi); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index fd3c23c02934..619767e736fe 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1471,7 +1471,8 @@ out: } static int __write_data_page(struct page *page, bool *submitted, - struct writeback_control *wbc) + struct writeback_control *wbc, + enum iostat_type io_type) { struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -1492,6 +1493,7 @@ static int __write_data_page(struct page *page, bool *submitted, .encrypted_page = NULL, .submitted = false, .need_lock = LOCK_RETRY, + .io_type = io_type, }; trace_f2fs_writepage(page, DATA); @@ -1598,7 +1600,7 @@ redirty_out: static int f2fs_write_data_page(struct page *page, struct writeback_control *wbc) { - return __write_data_page(page, NULL, wbc); + return __write_data_page(page, NULL, wbc, FS_DATA_IO); } /* @@ -1607,7 +1609,8 @@ static int f2fs_write_data_page(struct page *page, * warm/hot data page. */ static int f2fs_write_cache_pages(struct address_space *mapping, - struct writeback_control *wbc) + struct writeback_control *wbc, + enum iostat_type io_type) { int ret = 0; int done = 0; @@ -1697,7 +1700,7 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = __write_data_page(page, &submitted, wbc); + ret = __write_data_page(page, &submitted, wbc, io_type); if (unlikely(ret)) { /* * keep nr_to_write, since vfs uses this to @@ -1752,8 +1755,9 @@ continue_unlock: return ret; } -static int f2fs_write_data_pages(struct address_space *mapping, - struct writeback_control *wbc) +int __f2fs_write_data_pages(struct address_space *mapping, + struct writeback_control *wbc, + enum iostat_type io_type) { struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -1790,7 +1794,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, goto skip_write; blk_start_plug(&plug); - ret = f2fs_write_cache_pages(mapping, wbc); + ret = f2fs_write_cache_pages(mapping, wbc, io_type); blk_finish_plug(&plug); if (wbc->sync_mode == WB_SYNC_ALL) @@ -1809,6 +1813,16 @@ skip_write: return 0; } +static int f2fs_write_data_pages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + + return __f2fs_write_data_pages(mapping, wbc, + F2FS_I(inode)->cp_task == current ? + FS_CP_DATA_IO : FS_DATA_IO); +} + static void f2fs_write_failed(struct address_space *mapping, loff_t to) { struct inode *inode = mapping->host; @@ -2072,10 +2086,13 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, up_read(&F2FS_I(inode)->dio_rwsem[rw]); if (rw & WRITE) { - if (err > 0) + if (err > 0) { + f2fs_update_iostat(F2FS_I_SB(inode), APP_DIRECT_IO, + err); set_inode_flag(inode, FI_UPDATE_WRITE); - else if (err < 0) + } else if (err < 0) { f2fs_write_failed(mapping, offset + count); + } } trace_f2fs_direct_IO_exit(inode, offset, count, rw, err); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index eff79c8eac49..38c605be0879 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -653,6 +653,7 @@ struct f2fs_inode_info { f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ struct task_struct *task; /* lookup and create consistency */ + struct task_struct *cp_task; /* separate cp/wb IO stats*/ nid_t i_xattr_nid; /* node id that contains xattrs */ loff_t last_disk_size; /* lastly written file size */ @@ -957,6 +958,23 @@ enum need_lock_type { LOCK_RETRY, }; +enum iostat_type { + APP_DIRECT_IO, /* app direct IOs */ + APP_BUFFERED_IO, /* app buffered IOs */ + APP_WRITE_IO, /* app write IOs */ + APP_MAPPED_IO, /* app mapped IOs */ + FS_DATA_IO, /* data IOs from kworker/fsync/reclaimer */ + FS_NODE_IO, /* node IOs from kworker/fsync/reclaimer */ + FS_META_IO, /* meta IOs from kworker/reclaimer */ + FS_GC_DATA_IO, /* data IOs from forground gc */ + FS_GC_NODE_IO, /* node IOs from forground gc */ + FS_CP_DATA_IO, /* data IOs from checkpoint */ + FS_CP_NODE_IO, /* node IOs from checkpoint */ + FS_CP_META_IO, /* meta IOs from checkpoint */ + FS_DISCARD, /* discard */ + NR_IO_TYPE, +}; + struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ @@ -971,6 +989,7 @@ struct f2fs_io_info { bool submitted; /* indicate IO submission */ int need_lock; /* indicate we need to lock cp_rwsem */ bool in_list; /* indicate fio is in io_list */ + enum iostat_type io_type; /* io type */ }; #define is_read_io(rw) ((rw) == READ) @@ -1166,6 +1185,11 @@ struct f2fs_sb_info { #endif spinlock_t stat_lock; /* lock for stat operations */ + /* For app/fs IO statistics */ + spinlock_t iostat_lock; + unsigned long long write_iostat[NR_IO_TYPE]; + bool iostat_enable; + /* For sysfs suppport */ struct kobject s_kobj; struct completion s_kobj_unregister; @@ -2405,6 +2429,31 @@ static inline int get_extra_isize(struct inode *inode) sizeof((f2fs_inode)->field)) \ <= (F2FS_OLD_ATTRIBUTE_SIZE + extra_isize)) \ +static inline void f2fs_reset_iostat(struct f2fs_sb_info *sbi) +{ + int i; + + spin_lock(&sbi->iostat_lock); + for (i = 0; i < NR_IO_TYPE; i++) + sbi->write_iostat[i] = 0; + spin_unlock(&sbi->iostat_lock); +} + +static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi, + enum iostat_type type, unsigned long long io_bytes) +{ + if (!sbi->iostat_enable) + return; + spin_lock(&sbi->iostat_lock); + sbi->write_iostat[type] += io_bytes; + + if (type == APP_WRITE_IO || type == APP_DIRECT_IO) + sbi->write_iostat[APP_BUFFERED_IO] = + sbi->write_iostat[APP_WRITE_IO] - + sbi->write_iostat[APP_DIRECT_IO]; + spin_unlock(&sbi->iostat_lock); +} + /* * file.c */ @@ -2532,7 +2581,7 @@ void move_node_page(struct page *node_page, int gc_type); int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic); int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, - bool do_balance); + bool do_balance, enum iostat_type io_type); void build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount); bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid); void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); @@ -2575,7 +2624,8 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range); bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc); struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno); void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr); -void write_meta_page(struct f2fs_sb_info *sbi, struct page *page); +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page, + enum iostat_type io_type); void write_node_page(unsigned int nid, struct f2fs_io_info *fio); void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio); int rewrite_data_page(struct f2fs_io_info *fio); @@ -2616,7 +2666,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type, bool sync); void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index); long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, - long nr_to_write); + long nr_to_write, enum iostat_type io_type); void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void release_ino_entry(struct f2fs_sb_info *sbi, bool all); @@ -2670,6 +2720,9 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); void f2fs_set_page_dirty_nobuffers(struct page *page); +int __f2fs_write_data_pages(struct address_space *mapping, + struct writeback_control *wbc, + enum iostat_type io_type); void f2fs_invalidate_page(struct page *page, unsigned int offset, unsigned int length); int f2fs_release_page(struct page *page, gfp_t wait); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f6b9c5d6e446..4fa4c84aae0e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -101,6 +101,8 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, if (!PageUptodate(page)) SetPageUptodate(page); + f2fs_update_iostat(sbi, APP_MAPPED_IO, F2FS_BLKSIZE); + trace_f2fs_vm_page_mkwrite(page, DATA); mapped: /* fill the page */ @@ -1799,7 +1801,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) f2fs_stop_checkpoint(sbi, false); break; case F2FS_GOING_DOWN_METAFLUSH: - sync_meta_pages(sbi, META, LONG_MAX); + sync_meta_pages(sbi, META, LONG_MAX, FS_META_IO); f2fs_stop_checkpoint(sbi, false); break; default: @@ -2477,6 +2479,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) blk_start_plug(&plug); ret = __generic_file_write_iter(iocb, from); blk_finish_plug(&plug); + + if (ret > 0) + f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret); } inode_unlock(inode); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index f57cadae1a30..620dca443b29 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -689,6 +689,8 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, fio.new_blkaddr = newaddr; f2fs_submit_page_write(&fio); + f2fs_update_iostat(fio.sbi, FS_GC_DATA_IO, F2FS_BLKSIZE); + f2fs_update_data_blkaddr(&dn, newaddr); set_inode_flag(inode, FI_APPEND_WRITE); if (page->index == 0) @@ -736,6 +738,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, .page = page, .encrypted_page = NULL, .need_lock = LOCK_REQ, + .io_type = FS_GC_DATA_IO, }; bool is_dirty = PageDirty(page); int err; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index f38be791fdf9..e63ab0d1f614 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -117,6 +117,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) .op_flags = REQ_SYNC | REQ_PRIO, .page = page, .encrypted_page = NULL, + .io_type = FS_DATA_IO, }; int dirty, err; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 84576c014a66..0b3110504326 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1338,7 +1338,8 @@ continue_unlock: } static int __write_node_page(struct page *page, bool atomic, bool *submitted, - struct writeback_control *wbc, bool do_balance) + struct writeback_control *wbc, bool do_balance, + enum iostat_type io_type) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); nid_t nid; @@ -1351,6 +1352,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, .page = page, .encrypted_page = NULL, .submitted = false, + .io_type = io_type, }; trace_f2fs_writepage(page, NODE); @@ -1419,7 +1421,7 @@ redirty_out: static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { - return __write_node_page(page, false, NULL, wbc, false); + return __write_node_page(page, false, NULL, wbc, false, FS_NODE_IO); } int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, @@ -1507,7 +1509,8 @@ continue_unlock: ret = __write_node_page(page, atomic && page == last_page, - &submitted, wbc, true); + &submitted, wbc, true, + FS_NODE_IO); if (ret) { unlock_page(page); f2fs_put_page(last_page, 0); @@ -1545,7 +1548,7 @@ out: } int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, - bool do_balance) + bool do_balance, enum iostat_type io_type) { pgoff_t index, end; struct pagevec pvec; @@ -1624,7 +1627,7 @@ continue_unlock: set_dentry_mark(page, 0); ret = __write_node_page(page, false, &submitted, - wbc, do_balance); + wbc, do_balance, io_type); if (ret) unlock_page(page); else if (submitted) @@ -1716,7 +1719,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; blk_start_plug(&plug); - sync_node_pages(sbi, wbc, true); + sync_node_pages(sbi, wbc, true, FS_NODE_IO); blk_finish_plug(&plug); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return 0; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e8931e95607f..609f782bc7c8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -292,6 +292,7 @@ static int __commit_inmem_pages(struct inode *inode, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC | REQ_PRIO, + .io_type = FS_DATA_IO, }; pgoff_t last_idx = ULONG_MAX; int err = 0; @@ -903,6 +904,8 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, submit_bio(REQ_SYNC, bio); list_move_tail(&dc->list, &dcc->wait_list); __check_sit_bitmap(sbi, dc->start, dc->start + dc->len); + + f2fs_update_iostat(sbi, FS_DISCARD, 1); } } else { __remove_discard_cmd(sbi, dc); @@ -2351,7 +2354,8 @@ reallocate: } } -void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page, + enum iostat_type io_type) { struct f2fs_io_info fio = { .sbi = sbi, @@ -2370,6 +2374,8 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) set_page_writeback(page); f2fs_submit_page_write(&fio); + + f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE); } void write_node_page(unsigned int nid, struct f2fs_io_info *fio) @@ -2378,6 +2384,8 @@ void write_node_page(unsigned int nid, struct f2fs_io_info *fio) set_summary(&sum, nid, 0, 0); do_write_page(&sum, fio); + + f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); } void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) @@ -2391,13 +2399,22 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); do_write_page(&sum, fio); f2fs_update_data_blkaddr(dn, fio->new_blkaddr); + + f2fs_update_iostat(sbi, fio->io_type, F2FS_BLKSIZE); } int rewrite_data_page(struct f2fs_io_info *fio) { + int err; + fio->new_blkaddr = fio->old_blkaddr; stat_inc_inplace_blocks(fio->sbi); - return f2fs_submit_page_bio(fio); + + err = f2fs_submit_page_bio(fio); + + f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); + + return err; } void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b13823dbc513..3421fa57c4ca 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2048,6 +2048,10 @@ try_onemore: set_sbi_flag(sbi, SBI_POR_DOING); spin_lock_init(&sbi->stat_lock); + /* init iostat info */ + spin_lock_init(&sbi->iostat_lock); + sbi->iostat_enable = false; + for (i = 0; i < NR_PAGE_TYPE; i++) { int n = (i == META) ? 1: NR_TEMP_TYPE; int j; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 1e31d0c5b6ab..06ba0adc2f19 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -153,6 +153,10 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, return count; } *ui = t; + + if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0) + f2fs_reset_iostat(sbi); + return count; } @@ -250,6 +254,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); #ifdef CONFIG_F2FS_FAULT_INJECTION F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); @@ -288,6 +293,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(dirty_nats_ratio), ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), + ATTR_LIST(iostat_enable), #ifdef CONFIG_F2FS_FAULT_INJECTION ATTR_LIST(inject_rate), ATTR_LIST(inject_type), @@ -391,6 +397,48 @@ static int segment_bits_seq_show(struct seq_file *seq, void *offset) return 0; } +static int iostat_info_seq_show(struct seq_file *seq, void *offset) +{ + struct super_block *sb = seq->private; + struct f2fs_sb_info *sbi = F2FS_SB(sb); + time64_t now = get_seconds(); + + if (!sbi->iostat_enable) + return 0; + + seq_printf(seq, "time: %-16llu\n", now); + + /* print app IOs */ + seq_printf(seq, "app buffered: %-16llu\n", + sbi->write_iostat[APP_BUFFERED_IO]); + seq_printf(seq, "app direct: %-16llu\n", + sbi->write_iostat[APP_DIRECT_IO]); + seq_printf(seq, "app mapped: %-16llu\n", + sbi->write_iostat[APP_MAPPED_IO]); + + /* print fs IOs */ + seq_printf(seq, "fs data: %-16llu\n", + sbi->write_iostat[FS_DATA_IO]); + seq_printf(seq, "fs node: %-16llu\n", + sbi->write_iostat[FS_NODE_IO]); + seq_printf(seq, "fs meta: %-16llu\n", + sbi->write_iostat[FS_META_IO]); + seq_printf(seq, "fs gc data: %-16llu\n", + sbi->write_iostat[FS_GC_DATA_IO]); + seq_printf(seq, "fs gc node: %-16llu\n", + sbi->write_iostat[FS_GC_NODE_IO]); + seq_printf(seq, "fs cp data: %-16llu\n", + sbi->write_iostat[FS_CP_DATA_IO]); + seq_printf(seq, "fs cp node: %-16llu\n", + sbi->write_iostat[FS_CP_NODE_IO]); + seq_printf(seq, "fs cp meta: %-16llu\n", + sbi->write_iostat[FS_CP_META_IO]); + seq_printf(seq, "fs discard: %-16llu\n", + sbi->write_iostat[FS_DISCARD]); + + return 0; +} + #define F2FS_PROC_FILE_DEF(_name) \ static int _name##_open_fs(struct inode *inode, struct file *file) \ { \ @@ -406,6 +454,7 @@ static const struct file_operations f2fs_seq_##_name##_fops = { \ F2FS_PROC_FILE_DEF(segment_info); F2FS_PROC_FILE_DEF(segment_bits); +F2FS_PROC_FILE_DEF(iostat_info); int __init f2fs_init_sysfs(void) { @@ -454,6 +503,8 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) &f2fs_seq_segment_info_fops, sb); proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, &f2fs_seq_segment_bits_fops, sb); + proc_create_data("iostat_info", S_IRUGO, sbi->s_proc, + &f2fs_seq_iostat_info_fops, sb); } return 0; } @@ -461,6 +512,7 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) { if (sbi->s_proc) { + remove_proc_entry("iostat_info", sbi->s_proc); remove_proc_entry("segment_info", sbi->s_proc); remove_proc_entry("segment_bits", sbi->s_proc); remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); -- GitLab From ca4208da2ed931343229bf01dbbf5f6f2325a091 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Fri, 4 Aug 2017 17:07:15 +0800 Subject: [PATCH 0775/5498] f2fs: fix the size value in __check_sit_bitmap The current size value is not correct and will miss bitmap check. Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 609f782bc7c8..add2044973f1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -868,11 +868,14 @@ void __check_sit_bitmap(struct f2fs_sb_info *sbi, sentry = get_seg_entry(sbi, segno); offset = GET_BLKOFF_FROM_SEG0(sbi, blk); - size = min((unsigned long)(end - blk), max_blocks); + if (end < START_BLOCK(sbi, segno + 1)) + size = GET_BLKOFF_FROM_SEG0(sbi, end); + else + size = max_blocks; map = (unsigned long *)(sentry->cur_valid_map); offset = __find_rev_next_bit(map, size, offset); f2fs_bug_on(sbi, offset != size); - blk += size; + blk = START_BLOCK(sbi, segno + 1); } #endif } -- GitLab From 3760b50f4e645a475dba529b75a878499d28fa72 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 5 Aug 2017 14:25:08 -0700 Subject: [PATCH 0776/5498] f2fs: use IPU for cold files We expect cold files write data sequentially, but sometimes some of small data can be updated, which incurs fragmentation. Let's avoid that. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index ebd7df66ba58..a60d057297e7 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -576,6 +576,10 @@ static inline bool need_inplace_update_policy(struct inode *inode, if (test_opt(sbi, LFS)) return false; + /* if this is cold file, we should overwrite to avoid fragmentation */ + if (file_is_cold(inode)) + return true; + if (policy & (0x1 << F2FS_IPU_FORCE)) return true; if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) -- GitLab From b7625628b65b568f1480d43e133aa0c65b43ba98 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 6 Aug 2017 22:09:00 -0700 Subject: [PATCH 0777/5498] f2fs: introduce gc_urgent mode for background GC This patch adds a sysfs entry to control urgent mode for background GC. If this is set, background GC thread conducts GC with gc_urgent_sleep_time all the time. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 12 ++++++++++++ Documentation/filesystems/f2fs.txt | 9 +++++++++ fs/f2fs/gc.c | 17 +++++++++++++++-- fs/f2fs/gc.h | 4 ++++ fs/f2fs/sysfs.c | 9 +++++++++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 0421717ce26a..528ae38a9c52 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -130,3 +130,15 @@ Date: June 2017 Contact: "Chao Yu" Description: Controls current reserved blocks in system. + +What: /sys/fs/f2fs//gc_urgent +Date: August 2017 +Contact: "Jaegeuk Kim" +Description: + Do background GC agressively + +What: /sys/fs/f2fs//gc_urgent_sleep_time +Date: August 2017 +Contact: "Jaegeuk Kim" +Description: + Controls sleep time of GC urgent mode diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 50e9751883e6..5a7fd06771f3 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -207,6 +207,15 @@ Files in /sys/fs/f2fs/ gc_idle = 1 will select the Cost Benefit approach & setting gc_idle = 2 will select the greedy aproach. + gc_urgent This parameter controls triggering background GCs + urgently or not. Setting gc_urgent = 0 [default] + makes back to default behavior, while if it is set + to 1, background thread starts to do GC by given + gc_urgent_sleep_time interval. + + gc_urgent_sleep_time This parameter controls sleep time for gc_urgent. + 500 ms is set by default. See above gc_urgent. + reclaim_segments This parameter controls the number of prefree segments to be reclaimed. If the number of prefree segments is larger than the number of segments diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 620dca443b29..8da7c14a9d29 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -35,9 +35,14 @@ static int gc_thread_func(void *data) set_freezable(); do { wait_event_interruptible_timeout(*wq, - kthread_should_stop() || freezing(current), + kthread_should_stop() || freezing(current) || + gc_th->gc_wake, msecs_to_jiffies(wait_ms)); + /* give it a try one time */ + if (gc_th->gc_wake) + gc_th->gc_wake = 0; + if (try_to_freeze()) continue; if (kthread_should_stop()) @@ -74,6 +79,11 @@ static int gc_thread_func(void *data) if (!mutex_trylock(&sbi->gc_mutex)) goto next; + if (gc_th->gc_urgent) { + wait_ms = gc_th->urgent_sleep_time; + goto do_gc; + } + if (!is_idle(sbi)) { increase_sleep_time(gc_th, &wait_ms); mutex_unlock(&sbi->gc_mutex); @@ -84,7 +94,7 @@ static int gc_thread_func(void *data) decrease_sleep_time(gc_th, &wait_ms); else increase_sleep_time(gc_th, &wait_ms); - +do_gc: stat_inc_bggc_count(sbi); /* if return value is not zero, no victim was selected */ @@ -115,11 +125,14 @@ int start_gc_thread(struct f2fs_sb_info *sbi) goto out; } + gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME; gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME; gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME; gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME; gc_th->gc_idle = 0; + gc_th->gc_urgent = 0; + gc_th->gc_wake= 0; sbi->gc_thread = gc_th; init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index a993967dcdb9..57a9000ce3af 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -13,6 +13,7 @@ * whether IO subsystem is idle * or not */ +#define DEF_GC_THREAD_URGENT_SLEEP_TIME 500 /* 500 ms */ #define DEF_GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */ #define DEF_GC_THREAD_MAX_SLEEP_TIME 60000 #define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */ @@ -27,12 +28,15 @@ struct f2fs_gc_kthread { wait_queue_head_t gc_wait_queue_head; /* for gc sleep time */ + unsigned int urgent_sleep_time; unsigned int min_sleep_time; unsigned int max_sleep_time; unsigned int no_gc_sleep_time; /* for changing gc mode */ unsigned int gc_idle; + unsigned int gc_urgent; + unsigned int gc_wake; }; struct gc_inode_list { diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 06ba0adc2f19..9a04bc4fec83 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -156,6 +156,10 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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); + } return count; } @@ -235,10 +239,13 @@ static struct f2fs_attr f2fs_attr_##_name = { \ .id = _id, \ } +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent_sleep_time, + urgent_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent, gc_urgent); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards); F2FS_RW_ATTR(RESERVED_BLOCKS, f2fs_sb_info, reserved_blocks, reserved_blocks); @@ -275,10 +282,12 @@ F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { + ATTR_LIST(gc_urgent_sleep_time), ATTR_LIST(gc_min_sleep_time), ATTR_LIST(gc_max_sleep_time), ATTR_LIST(gc_no_gc_sleep_time), ATTR_LIST(gc_idle), + ATTR_LIST(gc_urgent), ATTR_LIST(reclaim_segments), ATTR_LIST(max_small_discards), ATTR_LIST(batched_trim_sections), -- GitLab From 77b7e7863e7c928a0e6d82cb4503c70a638ed347 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 7 Aug 2017 16:37:59 +0800 Subject: [PATCH 0778/5498] f2fs: avoid unneeded sync on quota file We only need to sync quota file with appointed quota type instead of all types in f2fs_quota_{on,off}. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 3421fa57c4ca..a846e5d46b0a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1162,7 +1162,7 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id, struct inode *inode; int err; - err = f2fs_quota_sync(sb, -1); + err = f2fs_quota_sync(sb, type); if (err) return err; @@ -1190,7 +1190,7 @@ static int f2fs_quota_off(struct super_block *sb, int type) if (!inode || !igrab(inode)) return dquot_quota_off(sb, type); - f2fs_quota_sync(sb, -1); + f2fs_quota_sync(sb, type); err = dquot_quota_off(sb, type); if (err) -- GitLab From e1f4c6ff54129ffb6db1a1169d4b9236edd30a41 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 7 Aug 2017 23:12:46 +0800 Subject: [PATCH 0779/5498] f2fs: fix potential overflow when adjusting GC cycle While comparing signed and unsigned variables, compiler will converts the signed value to unsigned one, due to this reason, {in,de}crease_sleep_time may return overflowed result. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- fs/f2fs/gc.h | 23 +++++++++++++++-------- include/trace/events/f2fs.h | 6 +++--- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8da7c14a9d29..e60480f71bb5 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -28,7 +28,7 @@ static int gc_thread_func(void *data) struct f2fs_sb_info *sbi = data; struct f2fs_gc_kthread *gc_th = sbi->gc_thread; wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; - long wait_ms; + unsigned int wait_ms; wait_ms = gc_th->min_sleep_time; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 57a9000ce3af..9325191fab2d 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -69,25 +69,32 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi) } static inline void increase_sleep_time(struct f2fs_gc_kthread *gc_th, - long *wait) + unsigned int *wait) { + unsigned int min_time = gc_th->min_sleep_time; + unsigned int max_time = gc_th->max_sleep_time; + if (*wait == gc_th->no_gc_sleep_time) return; - *wait += gc_th->min_sleep_time; - if (*wait > gc_th->max_sleep_time) - *wait = gc_th->max_sleep_time; + if ((long long)*wait + (long long)min_time > (long long)max_time) + *wait = max_time; + else + *wait += min_time; } static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th, - long *wait) + unsigned int *wait) { + unsigned int min_time = gc_th->min_sleep_time; + if (*wait == gc_th->no_gc_sleep_time) *wait = gc_th->max_sleep_time; - *wait -= gc_th->min_sleep_time; - if (*wait <= gc_th->min_sleep_time) - *wait = gc_th->min_sleep_time; + if ((long long)*wait - (long long)min_time < (long long)min_time) + *wait = min_time; + else + *wait -= min_time; } static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi) diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 78849b1b04f3..fdd7de503ce1 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -498,14 +498,14 @@ TRACE_EVENT(f2fs_map_blocks, TRACE_EVENT(f2fs_background_gc, - TP_PROTO(struct super_block *sb, long wait_ms, + TP_PROTO(struct super_block *sb, unsigned int wait_ms, unsigned int prefree, unsigned int free), TP_ARGS(sb, wait_ms, prefree, free), TP_STRUCT__entry( __field(dev_t, dev) - __field(long, wait_ms) + __field(unsigned int, wait_ms) __field(unsigned int, prefree) __field(unsigned int, free) ), @@ -517,7 +517,7 @@ TRACE_EVENT(f2fs_background_gc, __entry->free = free; ), - TP_printk("dev = (%d,%d), wait_ms = %ld, prefree = %u, free = %u", + TP_printk("dev = (%d,%d), wait_ms = %u, prefree = %u, free = %u", show_dev(__entry->dev), __entry->wait_ms, __entry->prefree, -- GitLab From b15b298caa388f765e64ab62c27e590ae153bd1f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 8 Aug 2017 10:54:31 +0800 Subject: [PATCH 0780/5498] f2fs: support journalled quota This patch supports to enable f2fs to accept quota information through mount option: - {usr,grp,prj}jquota= - jqfmt= Then, in ->mount flow, we can recover quota file during log replaying, by this, journelled quota can be supported. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 9 + fs/f2fs/checkpoint.c | 26 ++- fs/f2fs/f2fs.h | 9 + fs/f2fs/recovery.c | 60 ++++- fs/f2fs/super.c | 341 +++++++++++++++++++++++++++-- include/linux/seq_file.h | 17 ++ 6 files changed, 430 insertions(+), 32 deletions(-) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 5a7fd06771f3..cd4af55df6f8 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -162,6 +162,15 @@ io_bits=%u Set the bit size of write IO requests. It should be set usrquota Enable plain user disk quota accounting. grpquota Enable plain group disk quota accounting. prjquota Enable plain project quota accounting. +usrjquota= Appoint specified file and type during mount, so that quota +grpjquota= information can be properly updated during recovery flow, +prjjquota= : must be in root directory; +jqfmt= : [vfsold,vfsv0,vfsv1]. +offusrjquota Turn off user journelled quota. +offgrpjquota Turn off group journelled quota. +offprjjquota Turn off project journelled quota. +quota Enable plain user disk quota accounting. +noquota Disable all plain disk quota option. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a310156ecf19..9e8e24f679bb 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -589,11 +589,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) int recover_orphan_inodes(struct f2fs_sb_info *sbi) { block_t start_blk, orphan_blocks, i, j; - int err; + unsigned int s_flags = sbi->sb->s_flags; + int err = 0; if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) return 0; + if (s_flags & MS_RDONLY) { + f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); + sbi->sb->s_flags &= ~MS_RDONLY; + } + +#ifdef CONFIG_QUOTA + /* Needed for iput() to work correctly and not trash data */ + sbi->sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ + f2fs_enable_quota_files(sbi); +#endif + start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi); @@ -609,14 +622,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) err = recover_orphan_inode(sbi, ino); if (err) { f2fs_put_page(page, 1); - return err; + goto out; } } f2fs_put_page(page, 1); } /* clear Orphan Flag */ clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG); - return 0; +out: +#ifdef CONFIG_QUOTA + /* Turn quotas off */ + f2fs_quota_off_umount(sbi->sb); +#endif + sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ + + return err; } static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 38c605be0879..6116fbc378f3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -94,6 +94,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_USRQUOTA 0x00080000 #define F2FS_MOUNT_GRPQUOTA 0x00100000 #define F2FS_MOUNT_PRJQUOTA 0x00200000 +#define F2FS_MOUNT_QUOTA 0x00400000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -1215,6 +1216,12 @@ struct f2fs_sb_info { #ifdef CONFIG_F2FS_FAULT_INJECTION struct f2fs_fault_info fault_info; #endif + +#ifdef CONFIG_QUOTA + /* Names of quota files with journalled quota */ + char *s_qf_names[F2FS_MAXQUOTAS]; + int s_jquota_fmt; /* Format of quota to use */ +#endif }; #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -2543,6 +2550,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) */ int f2fs_inode_dirtied(struct inode *inode, bool sync); void f2fs_inode_synced(struct inode *inode); +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi); +void f2fs_quota_off_umount(struct super_block *sb); int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); int f2fs_sync_fs(struct super_block *sb, int sync); extern __printf(3, 4) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index ebde404ae67f..03b7aa61a0c8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -69,20 +69,32 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, } static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, - struct list_head *head, nid_t ino) + struct list_head *head, nid_t ino, bool quota_inode) { struct inode *inode; struct fsync_inode_entry *entry; + int err; inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) return ERR_CAST(inode); + dquot_initialize(inode); + + if (quota_inode) { + err = dquot_alloc_inode(inode); + if (err) + goto err_out; + } + entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); entry->inode = inode; list_add_tail(&entry->list, head); return entry; +err_out: + iput(inode); + return ERR_PTR(err); } static void del_fsync_inode(struct fsync_inode_entry *entry) @@ -107,7 +119,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage, entry = get_fsync_inode(dir_list, pino); if (!entry) { - entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino); + entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, + pino, false); if (IS_ERR(entry)) { dir = ERR_CAST(entry); err = PTR_ERR(entry); @@ -140,6 +153,9 @@ retry: err = -EEXIST; goto out_unmap_put; } + + dquot_initialize(einode); + err = acquire_orphan_inode(F2FS_I_SB(inode)); if (err) { iput(einode); @@ -226,18 +242,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, entry = get_fsync_inode(head, ino_of_node(page)); if (!entry) { + bool quota_inode = false; + if (!check_only && IS_INODE(page) && is_dent_dnode(page)) { err = recover_inode_page(sbi, page); if (err) break; + quota_inode = true; } /* * CP | dnode(F) | inode(DF) * For this case, we should not give up now. */ - entry = add_fsync_inode(sbi, head, ino_of_node(page)); + entry = add_fsync_inode(sbi, head, ino_of_node(page), + quota_inode); if (IS_ERR(entry)) { err = PTR_ERR(entry); if (err == -ENOENT) { @@ -332,6 +352,8 @@ got_it: inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) return PTR_ERR(inode); + + dquot_initialize(inode); } else { inode = dn->inode; } @@ -558,12 +580,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) struct list_head dir_list; int err; int ret = 0; + unsigned long s_flags = sbi->sb->s_flags; bool need_writecp = false; + if (s_flags & MS_RDONLY) { + f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); + sbi->sb->s_flags &= ~MS_RDONLY; + } + +#ifdef CONFIG_QUOTA + /* Needed for iput() to work correctly and not trash data */ + sbi->sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ + f2fs_enable_quota_files(sbi); +#endif + fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", sizeof(struct fsync_inode_entry)); - if (!fsync_entry_slab) - return -ENOMEM; + if (!fsync_entry_slab) { + err = -ENOMEM; + goto out; + } INIT_LIST_HEAD(&inode_list); INIT_LIST_HEAD(&dir_list); @@ -574,11 +611,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) /* step #1: find fsynced inode numbers */ err = find_fsync_dnodes(sbi, &inode_list, check_only); if (err || list_empty(&inode_list)) - goto out; + goto skip; if (check_only) { ret = 1; - goto out; + goto skip; } need_writecp = true; @@ -587,7 +624,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) err = recover_data(sbi, &inode_list, &dir_list); if (!err) f2fs_bug_on(sbi, !list_empty(&inode_list)); -out: +skip: destroy_fsync_dnodes(&inode_list); /* truncate meta pages to be used by the recovery */ @@ -615,5 +652,12 @@ out: } kmem_cache_destroy(fsync_entry_slab); +out: +#ifdef CONFIG_QUOTA + /* Turn quotas off */ + f2fs_quota_off_umount(sbi->sb); +#endif + sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ + return ret ? ret: err; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a846e5d46b0a..661eac5eeb8b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -25,6 +25,10 @@ #include #include #include +#include +#ifdef CONFIG_QUOTA +#include +#endif #include "f2fs.h" #include "node.h" @@ -105,9 +109,20 @@ enum { Opt_mode, Opt_io_size_bits, Opt_fault_injection, + Opt_quota, + Opt_noquota, Opt_usrquota, Opt_grpquota, Opt_prjquota, + Opt_usrjquota, + Opt_grpjquota, + Opt_prjjquota, + Opt_offusrjquota, + Opt_offgrpjquota, + Opt_offprjjquota, + Opt_jqfmt_vfsold, + Opt_jqfmt_vfsv0, + Opt_jqfmt_vfsv1, Opt_err, }; @@ -141,9 +156,20 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, + {Opt_quota, "quota"}, + {Opt_noquota, "noquota"}, {Opt_usrquota, "usrquota"}, {Opt_grpquota, "grpquota"}, {Opt_prjquota, "prjquota"}, + {Opt_usrjquota, "usrjquota=%s"}, + {Opt_grpjquota, "grpjquota=%s"}, + {Opt_prjjquota, "prjjquota=%s"}, + {Opt_offusrjquota, "usrjquota="}, + {Opt_offgrpjquota, "grpjquota="}, + {Opt_offprjjquota, "prjjquota="}, + {Opt_jqfmt_vfsold, "jqfmt=vfsold"}, + {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"}, + {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"}, {Opt_err, NULL}, }; @@ -166,6 +192,112 @@ static void init_once(void *foo) inode_init_once(&fi->vfs_inode); } +#ifdef CONFIG_QUOTA +static const char * const quotatypes[] = INITQFNAMES; +#define QTYPE2NAME(t) (quotatypes[t]) +static int f2fs_set_qf_name(struct super_block *sb, int qtype, + substring_t *args) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + char *qname; + int ret = -EINVAL; + + if (F2FS_MAXQUOTAS <= 2 && qtype == PRJQUOTA) + return -EINVAL; + + if (sb_any_quota_loaded(sb) && !sbi->s_qf_names[qtype]) { + f2fs_msg(sb, KERN_ERR, + "Cannot change journaled " + "quota options when quota turned on"); + return -EINVAL; + } + qname = match_strdup(args); + if (!qname) { + f2fs_msg(sb, KERN_ERR, + "Not enough memory for storing quotafile name"); + return -EINVAL; + } + if (sbi->s_qf_names[qtype]) { + if (strcmp(sbi->s_qf_names[qtype], qname) == 0) + ret = 0; + else + f2fs_msg(sb, KERN_ERR, + "%s quota file already specified", + QTYPE2NAME(qtype)); + goto errout; + } + if (strchr(qname, '/')) { + f2fs_msg(sb, KERN_ERR, + "quotafile must be on filesystem root"); + goto errout; + } + sbi->s_qf_names[qtype] = qname; + set_opt(sbi, QUOTA); + return 0; +errout: + kfree(qname); + return ret; +} + +static int f2fs_clear_qf_name(struct super_block *sb, int qtype) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + + if (F2FS_MAXQUOTAS <= 2 && qtype == PRJQUOTA) + return -EINVAL; + + if (sb_any_quota_loaded(sb) && sbi->s_qf_names[qtype]) { + f2fs_msg(sb, KERN_ERR, "Cannot change journaled quota options" + " when quota turned on"); + return -EINVAL; + } + kfree(sbi->s_qf_names[qtype]); + sbi->s_qf_names[qtype] = NULL; + return 0; +} + +static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) +{ + /* + * We do the test below only for project quotas. 'usrquota' and + * 'grpquota' mount options are allowed even without quota feature + * to support legacy quotas in quota files. + */ + if (F2FS_MAXQUOTAS > 2 && test_opt(sbi, PRJQUOTA) && + !f2fs_sb_has_project_quota(sbi->sb)) { + f2fs_msg(sbi->sb, KERN_ERR, "Project quota feature not enabled. " + "Cannot enable project quota enforcement."); + return -1; + } + if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] || + (F2FS_MAXQUOTAS > 2 && sbi->s_qf_names[PRJQUOTA])) { + if (test_opt(sbi, USRQUOTA) && sbi->s_qf_names[USRQUOTA]) + clear_opt(sbi, USRQUOTA); + + if (test_opt(sbi, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA]) + clear_opt(sbi, GRPQUOTA); + + if (F2FS_MAXQUOTAS > 2 && test_opt(sbi, PRJQUOTA) && + sbi->s_qf_names[PRJQUOTA]) + clear_opt(sbi, PRJQUOTA); + + if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) || + test_opt(sbi, PRJQUOTA)) { + f2fs_msg(sbi->sb, KERN_ERR, "old and new quota " + "format mixing"); + return -1; + } + + if (!sbi->s_jquota_fmt) { + f2fs_msg(sbi->sb, KERN_ERR, "journaled quota format " + "not specified"); + return -1; + } + } + return 0; +} +#endif + static int parse_options(struct super_block *sb, char *options) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -173,6 +305,9 @@ static int parse_options(struct super_block *sb, char *options) substring_t args[MAX_OPT_ARGS]; char *p, *name; int arg = 0; +#ifdef CONFIG_QUOTA + int ret; +#endif if (!options) return 0; @@ -378,6 +513,7 @@ static int parse_options(struct super_block *sb, char *options) #endif break; #ifdef CONFIG_QUOTA + case Opt_quota: case Opt_usrquota: set_opt(sbi, USRQUOTA); break; @@ -392,10 +528,66 @@ static int parse_options(struct super_block *sb, char *options) } set_opt(sbi, PRJQUOTA); break; + case Opt_usrjquota: + ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]); + if (ret) + return ret; + break; + case Opt_grpjquota: + ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]); + if (ret) + return ret; + break; + case Opt_prjjquota: + ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]); + if (ret) + return ret; + break; + case Opt_offusrjquota: + ret = f2fs_clear_qf_name(sb, USRQUOTA); + if (ret) + return ret; + break; + case Opt_offgrpjquota: + ret = f2fs_clear_qf_name(sb, GRPQUOTA); + if (ret) + return ret; + break; + case Opt_offprjjquota: + ret = f2fs_clear_qf_name(sb, PRJQUOTA); + if (ret) + return ret; + break; + case Opt_jqfmt_vfsold: + sbi->s_jquota_fmt = QFMT_VFS_OLD; + break; + case Opt_jqfmt_vfsv0: + sbi->s_jquota_fmt = QFMT_VFS_V0; + break; + case Opt_jqfmt_vfsv1: + sbi->s_jquota_fmt = QFMT_VFS_V1; + break; + case Opt_noquota: + clear_opt(sbi, QUOTA); + clear_opt(sbi, USRQUOTA); + clear_opt(sbi, GRPQUOTA); + clear_opt(sbi, PRJQUOTA); + break; #else + case Opt_quota: case Opt_usrquota: case Opt_grpquota: case Opt_prjquota: + case Opt_usrjquota: + case Opt_grpjquota: + case Opt_prjjquota: + case Opt_offusrjquota: + case Opt_offgrpjquota: + case Opt_offprjjquota: + case Opt_jqfmt_vfsold: + case Opt_jqfmt_vfsv0: + case Opt_jqfmt_vfsv1: + case Opt_noquota: f2fs_msg(sb, KERN_INFO, "quota operations not supported"); break; @@ -407,6 +599,10 @@ static int parse_options(struct super_block *sb, char *options) return -EINVAL; } } +#ifdef CONFIG_QUOTA + if (f2fs_check_quota_options(sbi)) + return -EINVAL; +#endif if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) { f2fs_msg(sb, KERN_ERR, @@ -582,7 +778,6 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) kfree(sbi->devs); } -static void f2fs_quota_off_umount(struct super_block *sb); static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -650,6 +845,10 @@ static void f2fs_put_super(struct super_block *sb) destroy_device_list(sbi); if (sbi->write_io_dummy) mempool_destroy(sbi->write_io_dummy); +#ifdef CONFIG_QUOTA + for (i = 0; i < F2FS_MAXQUOTAS; i++) + kfree(sbi->s_qf_names[i]); +#endif destroy_percpu_info(sbi); for (i = 0; i < NR_PAGE_TYPE; i++) kfree(sbi->write_io[i]); @@ -663,6 +862,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync) trace_f2fs_sync_fs(sb, sync); + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + return -EAGAIN; + if (sync) { struct cp_control cpc; @@ -783,6 +985,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } +static inline void f2fs_show_quota_options(struct seq_file *seq, + struct super_block *sb) +{ +#ifdef CONFIG_QUOTA + struct f2fs_sb_info *sbi = F2FS_SB(sb); + + if (sbi->s_jquota_fmt) { + char *fmtname = ""; + + switch (sbi->s_jquota_fmt) { + case QFMT_VFS_OLD: + fmtname = "vfsold"; + break; + case QFMT_VFS_V0: + fmtname = "vfsv0"; + break; + case QFMT_VFS_V1: + fmtname = "vfsv1"; + break; + } + seq_printf(seq, ",jqfmt=%s", fmtname); + } + + if (sbi->s_qf_names[USRQUOTA]) + seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]); + + if (sbi->s_qf_names[GRPQUOTA]) + seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]); + + if (F2FS_MAXQUOTAS > 2 && sbi->s_qf_names[PRJQUOTA]) + seq_show_option(seq, "prjjquota", sbi->s_qf_names[PRJQUOTA]); +#endif +} + static int f2fs_show_options(struct seq_file *seq, struct dentry *root) { struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); @@ -856,6 +1092,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) sbi->fault_info.inject_rate); #endif #ifdef CONFIG_QUOTA + if (test_opt(sbi, QUOTA)) + seq_puts(seq, ",quota"); if (test_opt(sbi, USRQUOTA)) seq_puts(seq, ",usrquota"); if (test_opt(sbi, GRPQUOTA)) @@ -863,6 +1101,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) if (test_opt(sbi, PRJQUOTA)) seq_puts(seq, ",prjquota"); #endif + f2fs_show_quota_options(seq, sbi->sb); return 0; } @@ -910,6 +1149,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) #ifdef CONFIG_F2FS_FAULT_INJECTION struct f2fs_fault_info ffi = sbi->fault_info; #endif +#ifdef CONFIG_QUOTA + int s_jquota_fmt; + char *s_qf_names[F2FS_MAXQUOTAS]; + int i, j; +#endif /* * Save the old mount options in case we @@ -919,6 +1163,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) old_sb_flags = sb->s_flags; active_logs = sbi->active_logs; +#ifdef CONFIG_QUOTA + s_jquota_fmt = sbi->s_jquota_fmt; + for (i = 0; i < F2FS_MAXQUOTAS; i++) { + if (sbi->s_qf_names[i]) { + s_qf_names[i] = kstrdup(sbi->s_qf_names[i], + GFP_KERNEL); + if (!s_qf_names[i]) { + for (j = 0; j < i; j++) + kfree(s_qf_names[j]); + return -ENOMEM; + } + } else { + s_qf_names[i] = NULL; + } + } +#endif + /* recover superblocks we couldn't write due to previous RO mount */ if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) { err = f2fs_commit_super(sbi, false); @@ -1000,6 +1261,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) goto restore_gc; } skip: +#ifdef CONFIG_QUOTA + /* Release old quota file names */ + for (i = 0; i < F2FS_MAXQUOTAS; i++) + kfree(s_qf_names[i]); +#endif /* Update the POSIXACL Flag */ sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); @@ -1014,6 +1280,13 @@ restore_gc: stop_gc_thread(sbi); } restore_opts: +#ifdef CONFIG_QUOTA + sbi->s_jquota_fmt = s_jquota_fmt; + for (i = 0; i < F2FS_MAXQUOTAS; i++) { + kfree(sbi->s_qf_names[i]); + sbi->s_qf_names[i] = s_qf_names[i]; + } +#endif sbi->mount_opt = org_mount_opt; sbi->active_logs = active_logs; sb->s_flags = old_sb_flags; @@ -1125,6 +1398,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode) return &F2FS_I(inode)->i_reserved_quota; } +static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type) +{ + return dquot_quota_on_mount(sbi->sb, sbi->s_qf_names[type], + sbi->s_jquota_fmt, type); +} + +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi) +{ + int i, ret; + + for (i = 0; i < F2FS_MAXQUOTAS; i++) { + if (sbi->s_qf_names[i]) { + ret = f2fs_quota_on_mount(sbi, i); + if (ret < 0) + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on journaled " + "quota: error %d", ret); + } + } +} + static int f2fs_quota_sync(struct super_block *sb, int type) { struct quota_info *dqopt = sb_dqopt(sb); @@ -1139,7 +1433,7 @@ static int f2fs_quota_sync(struct super_block *sb, int type) * Now when everything is written we can discard the pagecache so * that userspace sees the changes. */ - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + for (cnt = 0; cnt < F2FS_MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; if (!sb_has_quota_active(sb, cnt)) @@ -1206,11 +1500,11 @@ out_put: return err; } -static void f2fs_quota_off_umount(struct super_block *sb) +void f2fs_quota_off_umount(struct super_block *sb) { int type; - for (type = 0; type < MAXQUOTAS; type++) + for (type = 0; type < F2FS_MAXQUOTAS; type++) f2fs_quota_off(sb, type); } @@ -1245,7 +1539,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = { .set_dqblk = dquot_set_dqblk, }; #else -static inline void f2fs_quota_off_umount(struct super_block *sb) +void f2fs_quota_off_umount(struct super_block *sb) { } #endif @@ -2170,11 +2464,6 @@ try_onemore: if (err) goto free_nm; - /* if there are nt orphan nodes free them */ - err = recover_orphan_inodes(sbi); - if (err) - goto free_node_inode; - /* read root inode and dentry */ root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); if (IS_ERR(root)) { @@ -2198,6 +2487,11 @@ try_onemore: if (err) goto free_root_inode; + /* if there are nt orphan nodes free them */ + err = recover_orphan_inodes(sbi); + if (err) + goto free_sysfs; + /* recover fsynced data */ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { /* @@ -2207,7 +2501,7 @@ try_onemore: if (bdev_read_only(sb->s_bdev) && !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { err = -EROFS; - goto free_sysfs; + goto free_meta; } if (need_fsck) @@ -2221,7 +2515,7 @@ try_onemore: need_fsck = true; f2fs_msg(sb, KERN_ERR, "Cannot recover all fsync data errno=%d", err); - goto free_sysfs; + goto free_meta; } } else { err = recover_fsync_data(sbi, true); @@ -2245,7 +2539,7 @@ skip_recovery: /* After POR, we can run background GC thread.*/ err = start_gc_thread(sbi); if (err) - goto free_sysfs; + goto free_meta; } kfree(options); @@ -2263,8 +2557,16 @@ skip_recovery: f2fs_update_time(sbi, REQ_TIME); return 0; -free_sysfs: +free_meta: f2fs_sync_inode_meta(sbi); + /* + * Some dirty meta pages can be produced by recover_orphan_inodes() + * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg() + * followed by write_checkpoint() through f2fs_write_node_pages(), which + * falls into an infinite loop in sync_meta_pages(). + */ + truncate_inode_pages_final(META_MAPPING(sbi)); +free_sysfs: f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); @@ -2274,13 +2576,6 @@ free_node_inode: mutex_lock(&sbi->umount_mutex); release_ino_entry(sbi, true); f2fs_leave_shrinker(sbi); - /* - * Some dirty meta pages can be produced by recover_orphan_inodes() - * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg() - * followed by write_checkpoint() through f2fs_write_node_pages(), which - * falls into an infinite loop in sync_meta_pages(). - */ - truncate_inode_pages_final(META_MAPPING(sbi)); iput(sbi->node_inode); mutex_unlock(&sbi->umount_mutex); f2fs_destroy_stats(sbi); @@ -2301,6 +2596,10 @@ free_options: for (i = 0; i < NR_PAGE_TYPE; i++) kfree(sbi->write_io[i]); destroy_percpu_info(sbi); +#ifdef CONFIG_QUOTA + for (i = 0; i < F2FS_MAXQUOTAS; i++) + kfree(sbi->s_qf_names[i]); +#endif kfree(options); free_sb_buf: kfree(raw_super); diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 52e0097f61f0..611db59c52c5 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -158,6 +158,23 @@ static inline struct user_namespace *seq_user_ns(struct seq_file *seq) #endif } +/** + * seq_show_options - display mount options with appropriate escapes. + * @m: the seq_file handle + * @name: the mount option name + * @value: the mount option name's value, can be NULL + */ +static inline void seq_show_option(struct seq_file *m, const char *name, + const char *value) +{ + seq_putc(m, ','); + seq_escape(m, name, ",= \t\n\\"); + if (value) { + seq_putc(m, '='); + seq_escape(m, value, ", \t\n\\"); + } +} + #define SEQ_START_TOKEN ((void *)1) /* * Helpers for iteration over list_head-s in seq_files -- GitLab From 91764fe18e5c641c6d0014c6e470e19a123ec7dd Mon Sep 17 00:00:00 2001 From: Qiuyang Sun Date: Wed, 9 Aug 2017 17:27:30 +0800 Subject: [PATCH 0781/5498] f2fs: merge equivalent flags F2FS_GET_BLOCK_[READ|DIO] Currently, the two flags F2FS_GET_BLOCK_[READ|DIO] are totally equivalent and can be used interchangably in all scenarios they are involved in. Neither of the flags is referenced in f2fs_map_blocks(), making them both the default case. To remove the ambiguity, this patch merges both flags into F2FS_GET_BLOCK_DEFAULT, and introduces an enum for all distinct flags. Signed-off-by: Qiuyang Sun Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 13 +++++++------ fs/f2fs/file.c | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 619767e736fe..9be18f97b7bd 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1041,7 +1041,7 @@ static int get_data_block_dio(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_DIO, NULL); + F2FS_GET_BLOCK_DEFAULT, NULL); } static int get_data_block_bmap(struct inode *inode, sector_t iblock, @@ -1240,7 +1240,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_len = last_block - block_in_file; if (f2fs_map_blocks(inode, &map, 0, - F2FS_GET_BLOCK_READ)) + F2FS_GET_BLOCK_DEFAULT)) goto set_error_page; } got_it: diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6116fbc378f3..b47ee4b08331 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -606,12 +606,13 @@ struct f2fs_map_blocks { }; /* for flag in get_data_block */ -#define F2FS_GET_BLOCK_READ 0 -#define F2FS_GET_BLOCK_DIO 1 -#define F2FS_GET_BLOCK_FIEMAP 2 -#define F2FS_GET_BLOCK_BMAP 3 -#define F2FS_GET_BLOCK_PRE_DIO 4 -#define F2FS_GET_BLOCK_PRE_AIO 5 +enum { + F2FS_GET_BLOCK_DEFAULT, + F2FS_GET_BLOCK_FIEMAP, + F2FS_GET_BLOCK_BMAP, + F2FS_GET_BLOCK_PRE_DIO, + F2FS_GET_BLOCK_PRE_AIO, +}; /* * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4fa4c84aae0e..cc0ea3f90d19 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2058,7 +2058,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, */ while (map.m_lblk < pg_end) { map.m_len = pg_end - map.m_lblk; - err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT); if (err) goto out; @@ -2100,7 +2100,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, do_map: map.m_len = pg_end - map.m_lblk; - err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT); if (err) goto clear_out; -- GitLab From 134a1f307e250b187fb0d616732fc38a157ac0db Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 10 Aug 2017 17:35:04 -0700 Subject: [PATCH 0782/5498] f2fs: let fill_super handle roll-forward errors If we set CP_ERROR_FLAG in roll-forward error, f2fs is no longer to proceed any IOs due to f2fs_cp_error(). But, for example, if some stale data is involved on roll-forward process, we're able to get -ENOENT, getting fs stuck. If we get any error, let fill_super set SBI_NEED_FSCK and try to recover back to stable point. Cc: Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 03b7aa61a0c8..ca1e7bd10446 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -637,8 +637,6 @@ skip: } clear_sbi_flag(sbi, SBI_POR_DOING); - if (err) - set_ckpt_flags(sbi, CP_ERROR_FLAG); mutex_unlock(&sbi->cp_mutex); /* let's drop all the directory inodes for clean checkpoint */ -- GitLab From 9f0fd90668f9da1e0a1152f00182d014ad38ebfa Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 8 Aug 2017 19:09:08 +0800 Subject: [PATCH 0783/5498] f2fs: retry to revoke atomic commit in -ENOMEM case During atomic committing, if we encounter -ENOMEM in revoke path, it's better to give a chance to retry revoking. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index add2044973f1..f8210199091e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -213,9 +213,15 @@ static int __revoke_inmem_pages(struct inode *inode, struct node_info ni; trace_f2fs_commit_inmem_page(page, INMEM_REVOKE); - +retry: set_new_dnode(&dn, inode, NULL, NULL, 0); - if (get_dnode_of_data(&dn, page->index, LOOKUP_NODE)) { + err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE); + if (err) { + if (err == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + cond_resched(); + goto retry; + } err = -EAGAIN; goto next; } -- GitLab From da25a320b7de6b8ed45316fb839820c7ece55dbb Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 11 Aug 2017 18:00:15 +0800 Subject: [PATCH 0784/5498] f2fs: add tracepoint for f2fs_gc This patch adds tracepoint for f2fs_gc. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 50 ++++++++++++----- include/trace/events/f2fs.h | 107 ++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e60480f71bb5..57bea2182f30 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -919,7 +919,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, struct blk_plug plug; unsigned int segno = start_segno; unsigned int end_segno = start_segno + sbi->segs_per_sec; - int sec_freed = 0; + int seg_freed = 0; unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ? SUM_TYPE_DATA : SUM_TYPE_NODE; @@ -965,6 +965,10 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, gc_type); stat_inc_seg_count(sbi, type, gc_type); + + if (gc_type == FG_GC && + get_valid_blocks(sbi, segno, false) == 0) + seg_freed++; next: f2fs_put_page(sum_page, 0); } @@ -975,21 +979,17 @@ next: blk_finish_plug(&plug); - if (gc_type == FG_GC && - get_valid_blocks(sbi, start_segno, true) == 0) - sec_freed = 1; - stat_inc_call_count(sbi->stat_info); - return sec_freed; + return seg_freed; } int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background, unsigned int segno) { int gc_type = sync ? FG_GC : BG_GC; - int sec_freed = 0; - int ret; + int sec_freed = 0, seg_freed = 0, total_freed = 0; + int ret = 0; struct cp_control cpc; unsigned int init_segno = segno; struct gc_inode_list gc_list = { @@ -997,6 +997,15 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, .iroot = RADIX_TREE_INIT(GFP_NOFS), }; + trace_f2fs_gc_begin(sbi->sb, sync, background, + get_pages(sbi, F2FS_DIRTY_NODES), + get_pages(sbi, F2FS_DIRTY_DENTS), + get_pages(sbi, F2FS_DIRTY_IMETA), + free_sections(sbi), + free_segments(sbi), + reserved_segments(sbi), + prefree_segments(sbi)); + cpc.reason = __get_cp_reason(sbi); gc_more: if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) { @@ -1023,17 +1032,20 @@ gc_more: gc_type = FG_GC; } - ret = -EINVAL; /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */ - if (gc_type == BG_GC && !background) + if (gc_type == BG_GC && !background) { + ret = -EINVAL; goto stop; - if (!__get_victim(sbi, &segno, gc_type)) + } + if (!__get_victim(sbi, &segno, gc_type)) { + ret = -ENODATA; goto stop; - ret = 0; + } - if (do_garbage_collect(sbi, segno, &gc_list, gc_type) && - gc_type == FG_GC) + seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type); + if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec) sec_freed++; + total_freed += seg_freed; if (gc_type == FG_GC) sbi->cur_victim_sec = NULL_SEGNO; @@ -1050,6 +1062,16 @@ gc_more: stop: SIT_I(sbi)->last_victim[ALLOC_NEXT] = 0; SIT_I(sbi)->last_victim[FLUSH_DEVICE] = init_segno; + + trace_f2fs_gc_end(sbi->sb, ret, total_freed, sec_freed, + get_pages(sbi, F2FS_DIRTY_NODES), + get_pages(sbi, F2FS_DIRTY_DENTS), + get_pages(sbi, F2FS_DIRTY_IMETA), + free_sections(sbi), + free_segments(sbi), + reserved_segments(sbi), + prefree_segments(sbi)); + mutex_unlock(&sbi->gc_mutex); put_gc_inode(&gc_list); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index fdd7de503ce1..8fa678a2c335 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -524,6 +524,113 @@ TRACE_EVENT(f2fs_background_gc, __entry->free) ); +TRACE_EVENT(f2fs_gc_begin, + + TP_PROTO(struct super_block *sb, bool sync, bool background, + long long dirty_nodes, long long dirty_dents, + long long dirty_imeta, unsigned int free_sec, + unsigned int free_seg, int reserved_seg, + unsigned int prefree_seg), + + TP_ARGS(sb, sync, background, dirty_nodes, dirty_dents, dirty_imeta, + free_sec, free_seg, reserved_seg, prefree_seg), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(bool, sync) + __field(bool, background) + __field(long long, dirty_nodes) + __field(long long, dirty_dents) + __field(long long, dirty_imeta) + __field(unsigned int, free_sec) + __field(unsigned int, free_seg) + __field(int, reserved_seg) + __field(unsigned int, prefree_seg) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->sync = sync; + __entry->background = background; + __entry->dirty_nodes = dirty_nodes; + __entry->dirty_dents = dirty_dents; + __entry->dirty_imeta = dirty_imeta; + __entry->free_sec = free_sec; + __entry->free_seg = free_seg; + __entry->reserved_seg = reserved_seg; + __entry->prefree_seg = prefree_seg; + ), + + TP_printk("dev = (%d,%d), sync = %d, background = %d, nodes = %lld, " + "dents = %lld, imeta = %lld, free_sec:%u, free_seg:%u, " + "rsv_seg:%d, prefree_seg:%u", + show_dev(__entry->dev), + __entry->sync, + __entry->background, + __entry->dirty_nodes, + __entry->dirty_dents, + __entry->dirty_imeta, + __entry->free_sec, + __entry->free_seg, + __entry->reserved_seg, + __entry->prefree_seg) +); + +TRACE_EVENT(f2fs_gc_end, + + TP_PROTO(struct super_block *sb, int ret, int seg_freed, + int sec_freed, long long dirty_nodes, + long long dirty_dents, long long dirty_imeta, + unsigned int free_sec, unsigned int free_seg, + int reserved_seg, unsigned int prefree_seg), + + TP_ARGS(sb, ret, seg_freed, sec_freed, dirty_nodes, dirty_dents, + dirty_imeta, free_sec, free_seg, reserved_seg, prefree_seg), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, ret) + __field(int, seg_freed) + __field(int, sec_freed) + __field(long long, dirty_nodes) + __field(long long, dirty_dents) + __field(long long, dirty_imeta) + __field(unsigned int, free_sec) + __field(unsigned int, free_seg) + __field(int, reserved_seg) + __field(unsigned int, prefree_seg) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->ret = ret; + __entry->seg_freed = seg_freed; + __entry->sec_freed = sec_freed; + __entry->dirty_nodes = dirty_nodes; + __entry->dirty_dents = dirty_dents; + __entry->dirty_imeta = dirty_imeta; + __entry->free_sec = free_sec; + __entry->free_seg = free_seg; + __entry->reserved_seg = reserved_seg; + __entry->prefree_seg = prefree_seg; + ), + + TP_printk("dev = (%d,%d), ret = %d, seg_freed = %d, sec_freed = %d, " + "nodes = %lld, dents = %lld, imeta = %lld, free_sec:%u, " + "free_seg:%u, rsv_seg:%d, prefree_seg:%u", + show_dev(__entry->dev), + __entry->ret, + __entry->seg_freed, + __entry->sec_freed, + __entry->dirty_nodes, + __entry->dirty_dents, + __entry->dirty_imeta, + __entry->free_sec, + __entry->free_seg, + __entry->reserved_seg, + __entry->prefree_seg) +); + TRACE_EVENT(f2fs_get_victim, TP_PROTO(struct super_block *sb, int type, int gc_type, -- GitLab From 14b1b46dbdf0c6d742dcbeb0b35726fec54f3627 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 12 Aug 2017 21:33:23 -0700 Subject: [PATCH 0785/5498] f2fs: check hot_data for roll-forward recovery We need to check HOT_DATA to truncate any previous data block when doing roll-forward recovery. Cc: Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index ca1e7bd10446..bc7503ed742e 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -311,7 +311,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, return 0; /* Get the previous summary */ - for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) { + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); if (curseg->segno == segno) { sum = curseg->sum_blk->entries[blkoff]; -- GitLab From 392eda2afaa14c3421bfa95bc544fd930e2b7c12 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Mon, 14 Aug 2017 16:52:43 +0800 Subject: [PATCH 0786/5498] f2fs: remove unused function overprovision_sections Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index a60d057297e7..7eba54ef224b 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -491,11 +491,6 @@ static inline int overprovision_segments(struct f2fs_sb_info *sbi) return SM_I(sbi)->ovp_segments; } -static inline int overprovision_sections(struct f2fs_sb_info *sbi) -{ - return GET_SEC_FROM_SEG(sbi, (unsigned int)overprovision_segments(sbi)); -} - static inline int reserved_sections(struct f2fs_sb_info *sbi) { return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi)); -- GitLab From b248c36e39db330505645babb9a419e95c7875f9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 7 Aug 2017 23:09:56 +0800 Subject: [PATCH 0787/5498] f2fs: introduce discard_granularity sysfs entry Commit d618ebaf0aa8 ("f2fs: enable small discard by default") enables f2fs to issue 4K size discard in real-time discard mode. However, issuing smaller discard may cost more lifetime but releasing less free space in flash device. Since f2fs has ability of separating hot/cold data and garbage collection, we can expect that small-sized invalid region would expand soon with OPU, deletion or garbage collection on valid datas, so it's better to delay or skip issuing smaller size discards, it could help to reduce overmuch consumption of IO bandwidth and lifetime of flash storage. This patch makes f2fs selectng 64K size as its default minimal granularity, and issue discard with the size which is not smaller than minimal granularity. Also it exposes discard granularity as sysfs entry for configuration in different scenario. Jaegeuk Kim: We must issue all the accumulated discard commands when fstrim is called. So, I've added pend_list_tag[] to indicate whether we should issue the commands or not. If tag sets P_ACTIVE or P_TRIM, we have to issue them. P_TRIM is set once at a time, given fstrim trigger. In addition, issue_discard_thread is calling too much due to the number of discard commands remaining in the pending list. I added a timer to control it likewise gc_thread. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 9 +++ fs/f2fs/f2fs.h | 12 ++++ fs/f2fs/segment.c | 91 +++++++++++++++++++++---- fs/f2fs/sysfs.c | 23 +++++++ 4 files changed, 121 insertions(+), 14 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 528ae38a9c52..cecb93af8967 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -57,6 +57,15 @@ Contact: "Jaegeuk Kim" Description: Controls the issue rate of small discard commands. +What: /sys/fs/f2fs//discard_granularity +Date: July 2017 +Contact: "Chao Yu" +Description: + Controls discard granularity of inner discard thread, inner thread + will not issue discards with size that is smaller than granularity. + The unit size is one block, now only support configuring in range + of [1, 512]. + What: /sys/fs/f2fs//max_victim_search Date: January 2014 Contact: "Jaegeuk Kim" diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b47ee4b08331..461f650ae352 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -247,6 +247,8 @@ enum { (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) #define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi) #define DISCARD_ISSUE_RATE 8 +#define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */ +#define DEF_MAX_DISCARD_ISSUE_TIME 60000 /* 60 s, if no candidates */ #define DEF_CP_INTERVAL 60 /* 60 secs */ #define DEF_IDLE_INTERVAL 5 /* 5 secs */ @@ -295,11 +297,18 @@ struct discard_entry { unsigned char discard_map[SIT_VBLOCK_MAP_SIZE]; /* segment discard bitmap */ }; +/* default discard granularity of inner discard thread, unit: block count */ +#define DEFAULT_DISCARD_GRANULARITY 16 + /* max discard pend list number */ #define MAX_PLIST_NUM 512 #define plist_idx(blk_num) ((blk_num) >= MAX_PLIST_NUM ? \ (MAX_PLIST_NUM - 1) : (blk_num - 1)) +#define P_ACTIVE 0x01 +#define P_TRIM 0x02 +#define plist_issue(tag) (((tag) & P_ACTIVE) || ((tag) & P_TRIM)) + enum { D_PREP, D_SUBMIT, @@ -335,11 +344,14 @@ struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head entry_list; /* 4KB discard entry list */ struct list_head pend_list[MAX_PLIST_NUM];/* store pending entries */ + unsigned char pend_list_tag[MAX_PLIST_NUM];/* tag for pending entries */ struct list_head wait_list; /* store on-flushing entries */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ + unsigned int discard_wake; /* to wake up discard thread */ struct mutex cmd_lock; unsigned int nr_discards; /* # of discards in the list */ unsigned int max_discards; /* max. discards to be issued */ + unsigned int discard_granularity; /* discard granularity */ unsigned int undiscard_blks; /* # of undiscard blocks */ atomic_t issued_discard; /* # of issued discard */ atomic_t issing_discard; /* # of issing discard */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f8210199091e..6f47f2575a2e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1096,32 +1096,65 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, return 0; } -static void __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) +static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *pend_list; struct discard_cmd *dc, *tmp; struct blk_plug plug; - int i, iter = 0; + int iter = 0, issued = 0; + int i; mutex_lock(&dcc->cmd_lock); f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); blk_start_plug(&plug); - for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + for (i = MAX_PLIST_NUM - 1; + i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) { pend_list = &dcc->pend_list[i]; list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); - if (!issue_cond || is_idle(sbi)) + /* Hurry up to finish fstrim */ + if (dcc->pend_list_tag[i] & P_TRIM) { + __submit_discard_cmd(sbi, dc); + issued++; + continue; + } + + if (!issue_cond || is_idle(sbi)) { + issued++; __submit_discard_cmd(sbi, dc); + } if (issue_cond && iter++ > DISCARD_ISSUE_RATE) goto out; } + if (list_empty(pend_list) && dcc->pend_list_tag[i] & P_TRIM) + dcc->pend_list_tag[i] &= (~P_TRIM); } out: blk_finish_plug(&plug); mutex_unlock(&dcc->cmd_lock); + + return issued; +} + +static void __drop_discard_cmd(struct f2fs_sb_info *sbi) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *pend_list; + struct discard_cmd *dc, *tmp; + int i; + + mutex_lock(&dcc->cmd_lock); + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + pend_list = &dcc->pend_list[i]; + list_for_each_entry_safe(dc, tmp, pend_list, list) { + f2fs_bug_on(sbi, dc->state != D_PREP); + __remove_discard_cmd(sbi, dc); + } + } + mutex_unlock(&dcc->cmd_lock); } static void __wait_one_discard_bio(struct f2fs_sb_info *sbi, @@ -1206,34 +1239,56 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { __issue_discard_cmd(sbi, false); + __drop_discard_cmd(sbi); __wait_discard_cmd(sbi, false); } +static void mark_discard_range_all(struct f2fs_sb_info *sbi) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + int i; + + mutex_lock(&dcc->cmd_lock); + for (i = 0; i < MAX_PLIST_NUM; i++) + dcc->pend_list_tag[i] |= P_TRIM; + mutex_unlock(&dcc->cmd_lock); +} + static int issue_discard_thread(void *data) { struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; + unsigned int wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; + int issued; set_freezable(); do { - wait_event_interruptible(*q, kthread_should_stop() || - freezing(current) || - atomic_read(&dcc->discard_cmd_cnt)); + wait_event_interruptible_timeout(*q, + kthread_should_stop() || freezing(current) || + dcc->discard_wake, + msecs_to_jiffies(wait_ms)); if (try_to_freeze()) continue; if (kthread_should_stop()) return 0; + if (dcc->discard_wake) + dcc->discard_wake = 0; + sb_start_intwrite(sbi->sb); - __issue_discard_cmd(sbi, true); - __wait_discard_cmd(sbi, true); + issued = __issue_discard_cmd(sbi, true); + if (issued) { + __wait_discard_cmd(sbi, true); + wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; + } else { + wait_ms = DEF_MAX_DISCARD_ISSUE_TIME; + } sb_end_intwrite(sbi->sb); - congestion_wait(BLK_RW_SYNC, HZ/50); } while (!kthread_should_stop()); return 0; } @@ -1424,7 +1479,8 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) { - struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list); + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *head = &dcc->entry_list; struct discard_entry *entry, *this; struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; @@ -1506,11 +1562,12 @@ skip: goto find_next; list_del(&entry->list); - SM_I(sbi)->dcc_info->nr_discards -= total_len; + dcc->nr_discards -= total_len; kmem_cache_free(discard_entry_slab, entry); } - wake_up(&SM_I(sbi)->dcc_info->discard_wait_queue); + dcc->discard_wake = 1; + wake_up_interruptible_all(&dcc->discard_wait_queue); } static int create_discard_cmd_control(struct f2fs_sb_info *sbi) @@ -1528,9 +1585,13 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) if (!dcc) return -ENOMEM; + dcc->discard_granularity = DEFAULT_DISCARD_GRANULARITY; INIT_LIST_HEAD(&dcc->entry_list); - for (i = 0; i < MAX_PLIST_NUM; i++) + for (i = 0; i < MAX_PLIST_NUM; i++) { INIT_LIST_HEAD(&dcc->pend_list[i]); + if (i >= dcc->discard_granularity - 1) + dcc->pend_list_tag[i] |= P_ACTIVE; + } INIT_LIST_HEAD(&dcc->wait_list); mutex_init(&dcc->cmd_lock); atomic_set(&dcc->issued_discard, 0); @@ -2207,6 +2268,8 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) schedule(); } + /* It's time to issue all the filed discards */ + mark_discard_range_all(sbi); out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return err; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 9a04bc4fec83..fbbe6e03658a 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -152,6 +152,27 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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); + return count; + } + *ui = t; if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0) @@ -248,6 +269,7 @@ F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent, gc_urgent); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards); +F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, discard_granularity, discard_granularity); F2FS_RW_ATTR(RESERVED_BLOCKS, f2fs_sb_info, reserved_blocks, reserved_blocks); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); @@ -290,6 +312,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(gc_urgent), ATTR_LIST(reclaim_segments), ATTR_LIST(max_small_discards), + ATTR_LIST(discard_granularity), ATTR_LIST(batched_trim_sections), ATTR_LIST(ipu_policy), ATTR_LIST(min_ipu_util), -- GitLab From 3156541150b7d81a60e0764ab781213fcd697e19 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 15 Aug 2017 21:27:19 -0700 Subject: [PATCH 0788/5498] f2fs: issue discard commands if gc_urgent is set It's time to issue all the discard commands, if user sets the idle time. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 +++++- fs/f2fs/sysfs.c | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6f47f2575a2e..103c98d27f84 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -21,6 +21,7 @@ #include "f2fs.h" #include "segment.h" #include "node.h" +#include "gc.h" #include "trace.h" #include @@ -1274,8 +1275,11 @@ static int issue_discard_thread(void *data) if (kthread_should_stop()) return 0; - if (dcc->discard_wake) + if (dcc->discard_wake) { dcc->discard_wake = 0; + if (sbi->gc_thread && sbi->gc_thread->gc_urgent) + mark_discard_range_all(sbi); + } sb_start_intwrite(sbi->sb); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index fbbe6e03658a..6da09ac21e49 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -178,8 +178,13 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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) { + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + sbi->gc_thread->gc_wake = 1; wake_up_interruptible_all(&sbi->gc_thread->gc_wait_queue_head); + + dcc->discard_wake = 1; + wake_up_interruptible_all(&dcc->discard_wait_queue); } return count; -- GitLab From 7ecdd9c94d4951a6e8c5390588b054b12fb4fb57 Mon Sep 17 00:00:00 2001 From: Abhinay Reddy Vanipally Date: Thu, 24 Aug 2017 10:49:53 -0700 Subject: [PATCH 0789/5498] defconfig: arm64: msm8996: Enable IPC Router MHI transport for perf This change adds the config variable to enable compilation of the IPC Router MHI Transport on perf configurations. Change-Id: I9c786f8596264193be8f5cc52648a585c45cdf9f Signed-off-by: Abhinay Reddy Vanipally --- arch/arm64/configs/msm-perf_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 4f453516ae6f..12a32ba368cb 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -549,6 +549,7 @@ CONFIG_MSM_QMI_INTERFACE=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_SYSMON_GLINK_COMM=y CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_IPC_ROUTER_MHI_XPRT=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y -- GitLab From c7fb8eab8f5cc95bcb25b62f3380ef1bf249e214 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Tue, 1 Aug 2017 19:05:06 +0530 Subject: [PATCH 0790/5498] msm: ipa: Fix to use after free issue Added code changes to avoid use after free if header already table already freed during ipa ioctl test. Change-Id: I5e89c28448bd3b94714451432774457a170005bd Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa_rt.c | 13 ++++++++++--- drivers/platform/msm/ipa/ipa_v3/ipa_rt.c | 22 +++++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 0531919487d7..293a60a60881 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -53,7 +53,7 @@ int __ipa_generate_rt_hw_rule_v2(enum ipa_ip_type ip, int pipe_idx; if (buf == NULL) { - memset(tmp, 0, IPA_RT_FLT_HW_RULE_BUF_SIZE); + memset(tmp, 0, (IPA_RT_FLT_HW_RULE_BUF_SIZE/4)); buf = (u8 *)tmp; } @@ -75,8 +75,15 @@ int __ipa_generate_rt_hw_rule_v2(enum ipa_ip_type ip, rule_hdr->u.hdr.pipe_dest_idx = pipe_idx; rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl; if (entry->hdr) { - rule_hdr->u.hdr.hdr_offset = - entry->hdr->offset_entry->offset >> 2; + if (entry->hdr->cookie == IPA_HDR_COOKIE) { + rule_hdr->u.hdr.hdr_offset = + entry->hdr->offset_entry->offset >> 2; + } else { + IPAERR("Entry hdr deleted by user = %d cookie = %u\n", + entry->hdr->user_deleted, entry->hdr->cookie); + WARN_ON(1); + rule_hdr->u.hdr.hdr_offset = 0; + } } else { rule_hdr->u.hdr.hdr_offset = 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 726d2253f0ce..27a379b3eba6 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -83,13 +83,21 @@ int __ipa_generate_rt_hw_rule_v3_0(enum ipa_ip_type ip, if (entry->proc_ctx || (entry->hdr && entry->hdr->is_hdr_proc_ctx)) { struct ipa3_hdr_proc_ctx_entry *proc_ctx; proc_ctx = (entry->proc_ctx) ? : entry->hdr->proc_ctx; - rule_hdr->u.hdr.system = !ipa3_ctx->hdr_proc_ctx_tbl_lcl; - BUG_ON(proc_ctx->offset_entry->offset & 31); - rule_hdr->u.hdr.proc_ctx = 1; - rule_hdr->u.hdr.hdr_offset = - (proc_ctx->offset_entry->offset + - ipa3_ctx->hdr_proc_ctx_tbl.start_offset) >> 5; - } else if (entry->hdr) { + if ((proc_ctx == NULL) || + (proc_ctx->cookie != IPA_PROC_HDR_COOKIE)) { + rule_hdr->u.hdr.proc_ctx = 0; + rule_hdr->u.hdr.hdr_offset = 0; + } else { + rule_hdr->u.hdr.system = + !ipa3_ctx->hdr_proc_ctx_tbl_lcl; + BUG_ON(proc_ctx->offset_entry->offset & 31); + rule_hdr->u.hdr.proc_ctx = 1; + rule_hdr->u.hdr.hdr_offset = + (proc_ctx->offset_entry->offset + + ipa3_ctx->hdr_proc_ctx_tbl.start_offset) >> 5; + } + } else if ((entry->hdr != NULL) && + (entry->hdr->cookie == IPA_HDR_COOKIE)) { rule_hdr->u.hdr.system = !ipa3_ctx->hdr_tbl_lcl; BUG_ON(entry->hdr->offset_entry->offset & 3); rule_hdr->u.hdr.proc_ctx = 0; -- GitLab From 65ffa82f2064734f126ff302c102d942fcd9b6f1 Mon Sep 17 00:00:00 2001 From: Samyukta Mogily Date: Thu, 31 Aug 2017 19:44:55 +0530 Subject: [PATCH 0791/5498] msm: camera: Hot fix to enable actuator on 8909 MTP Enabling i2c driver register for actuator on 8909 MTP. Change-Id: I03a304e9b489ae49e7ec48007bbbe0ae7d6a2c59 Signed-off-by: Samyukta Mogily --- .../media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 39cdae9dad15..001faf56a5d7 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -2039,8 +2039,6 @@ static int __init msm_actuator_init_module(void) int32_t rc = 0; CDBG("Enter\n"); rc = platform_driver_register(&msm_actuator_platform_driver); - if (!rc) - return rc; CDBG("%s:%d rc %d\n", __func__, __LINE__, rc); return i2c_add_driver(&msm_actuator_i2c_driver); -- GitLab From bd34b42ee68b1ec4a804c30ce899253535f628b3 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 24 Aug 2017 15:21:43 +0800 Subject: [PATCH 0792/5498] leds: led-qpnp-flash: Re-check fault status if open fault is detected If LED open fault is detected, the open_fault flag will be set always and the flash won't be controlled even if the hardware open fault is recovered. So check the LED fault status everytime if the open fault has been detected. CRs-Fixed: 2093952 Change-Id: I0b449a7d030d091d24aca992cdd7c06150d8ff30 Signed-off-by: Fenglin Wu --- drivers/leds/leds-qpnp-flash.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c index ec4b4e7d9409..785fe5130672 100644 --- a/drivers/leds/leds-qpnp-flash.c +++ b/drivers/leds/leds-qpnp-flash.c @@ -1307,7 +1307,7 @@ static void qpnp_flash_led_work(struct work_struct *work) int max_curr_avail_ma = 0; int total_curr_ma = 0; int i; - u8 val; + u8 val = 0; /* Global lock is to synchronize between the flash leds and torch */ mutex_lock(&led->flash_led_lock); @@ -1319,8 +1319,30 @@ static void qpnp_flash_led_work(struct work_struct *work) goto turn_off; if (led->open_fault) { - dev_err(&led->spmi_dev->dev, "Open fault detected\n"); - goto unlock_mutex; + if (flash_node->type == FLASH) { + dev_dbg(&led->spmi_dev->dev, "Open fault detected\n"); + goto unlock_mutex; + } + /* + * Checking LED fault status again if open_fault has been + * detected previously. Update open_fault status then the + * flash leds could be controlled again if the hardware + * status is recovered. + */ + rc = spmi_ext_register_readl(led->spmi_dev->ctrl, + led->spmi_dev->sid, + FLASH_LED_FAULT_STATUS(led->base), &val, 1); + if (rc) { + dev_err(&led->spmi_dev->dev, + "Failed to read out fault status register\n"); + goto unlock_mutex; + } + + led->open_fault = (val & FLASH_LED_OPEN_FAULT_DETECTED); + if (led->open_fault) { + dev_err(&led->spmi_dev->dev, "Open fault detected\n"); + goto unlock_mutex; + } } if (!flash_node->flash_on && flash_node->num_regulators > 0) { @@ -1804,7 +1826,7 @@ turn_off: goto exit_flash_led_work; } - led->open_fault |= (val & FLASH_LED_OPEN_FAULT_DETECTED); + led->open_fault = (val & FLASH_LED_OPEN_FAULT_DETECTED); } rc = qpnp_led_masked_write(led->spmi_dev, -- GitLab From 1c1853ccf040b1361ec56d924367fbf9ff141124 Mon Sep 17 00:00:00 2001 From: Yeleswarapu Nagaradhesh Date: Fri, 19 May 2017 21:21:57 +0530 Subject: [PATCH 0793/5498] drivers: mfd: donot clear interrupt status If there is a fake interrupt SW is clearing interrupt status, due to this some of the required interrupts are getting cleared. Remove clearing of status register to avoid this. CRs-Fixed: 2045773 Change-Id: I38247d69bd49a64731c3ec695d5470f5c591fea6 Signed-off-by: Yeleswarapu Nagaradhesh --- drivers/mfd/wcd9xxx-irq.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c index b28c1ebbe21f..02fdbf8a5d4e 100644 --- a/drivers/mfd/wcd9xxx-irq.c +++ b/drivers/mfd/wcd9xxx-irq.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -288,7 +288,7 @@ static irqreturn_t wcd9xxx_irq_thread(int irq, void *data) static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1); struct wcd9xxx_core_resource *wcd9xxx_res = data; int num_irq_regs = wcd9xxx_res->num_irq_regs; - u8 status[num_irq_regs], status1[num_irq_regs]; + u8 status[4], status1[4] = {0}, unmask_status[4] = {0}; if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) { dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n"); @@ -311,6 +311,23 @@ static irqreturn_t wcd9xxx_irq_thread(int irq, void *data) "Failed to read interrupt status: %d\n", ret); goto err_disable_irq; } + /* + * If status is 0 return without clearing. + * status contains: HW status - masked interrupts + * status1 contains: unhandled interrupts - masked interrupts + * unmasked_status contains: unhandled interrupts + */ + if (unlikely(!memcmp(status, status1, sizeof(status)))) { + pr_debug("%s: status is 0\n", __func__); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return IRQ_HANDLED; + } + + /* + * Copy status to unmask_status before masking, otherwise SW may miss + * to clear masked interrupt in corner case. + */ + memcpy(unmask_status, status, sizeof(unmask_status)); /* Apply masking */ for (i = 0; i < num_irq_regs; i++) @@ -334,6 +351,8 @@ static irqreturn_t wcd9xxx_irq_thread(int irq, void *data) wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata); status1[BIT_BYTE(irqdata.intr_num)] &= ~BYTE_BIT_MASK(irqdata.intr_num); + unmask_status[BIT_BYTE(irqdata.intr_num)] &= + ~BYTE_BIT_MASK(irqdata.intr_num); } } -- GitLab From e9ff912075e29f524b991b5028055b132a0b51fd Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Wed, 28 Jun 2017 15:06:48 -0700 Subject: [PATCH 0794/5498] ASoC: msm: Move mdm9650-auto sound card into mdm9650 card Modify the mdm9650 sound card to work for both mobile and automotive sound cards by adding separate DAI links related to codec and adding DAI links based on the sound card name from the device tree. Make other changes on the mdm9650 sound card to make it work with the tlv320aic3x codec. Change-Id: I48544193765bc05ee87e77bdb9ec7bef70cb5ab2 Signed-off-by: Gustavo Solaira --- sound/soc/msm/Makefile | 2 +- sound/soc/msm/mdm9650-auto.c | 1811 ---------------------------------- sound/soc/msm/mdm9650.c | 280 ++++-- 3 files changed, 221 insertions(+), 1872 deletions(-) delete mode 100644 sound/soc/msm/mdm9650-auto.c diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile index 7eb038d31505..d221bb85db17 100644 --- a/sound/soc/msm/Makefile +++ b/sound/soc/msm/Makefile @@ -30,7 +30,7 @@ snd-soc-msm8x16-objs += msm8952-slimbus.o msm8952-dai-links.o obj-$(CONFIG_SND_SOC_MSM8X16) += snd-soc-msm8x16.o # for MDM 9650 sound card driver -snd-soc-mdm9650-objs := mdm9650.o mdm9650-auto.o +snd-soc-mdm9650-objs := mdm9650.o obj-$(CONFIG_SND_SOC_MDM9650) += snd-soc-mdm9650.o # for MDM 9607 sound card driver diff --git a/sound/soc/msm/mdm9650-auto.c b/sound/soc/msm/mdm9650-auto.c deleted file mode 100644 index f2fd9c334ad6..000000000000 --- a/sound/soc/msm/mdm9650-auto.c +++ /dev/null @@ -1,1811 +0,0 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include <../codecs/tlv320aic3x.h> - -/* - * MDM9650 runs TLV320AIC3X at 12.288 Mhz. - */ -#define MDM_MCLK_CLK_12P288MHZ 12288000 -#define MDM_MCLK_CLK_9P6HZ 9600000 -#define MDM_MI2S_RATE 48000 -#define DEV_NAME_STR_LEN 32 - -#define SAMPLE_RATE_8KHZ 8000 -#define SAMPLE_RATE_16KHZ 16000 -#define SAMPLE_RATE_48KHZ 48000 -#define NO_OF_BITS_PER_SAMPLE 16 - -#define LPAIF_OFFSET 0x07700000 -#define LPAIF_PRI_MODE_MUXSEL (LPAIF_OFFSET + 0x2008) -#define LPAIF_SEC_MODE_MUXSEL (LPAIF_OFFSET + 0x200c) - -#define LPASS_CSR_GP_IO_MUX_SPKR_CTL (LPAIF_OFFSET + 0x2004) -#define LPASS_CSR_GP_IO_MUX_MIC_CTL (LPAIF_OFFSET + 0x2000) - -#define I2S_SEL 0 -#define PCM_SEL 1 -#define I2S_PCM_SEL_OFFSET 0 -#define I2S_PCM_MASTER_MODE 1 -#define I2S_PCM_SLAVE_MODE 0 - -#define PRI_TLMM_CLKS_EN_MASTER 0x4 -#define SEC_TLMM_CLKS_EN_MASTER 0x2 -#define PRI_TLMM_CLKS_EN_SLAVE 0x100000 -#define SEC_TLMM_CLKS_EN_SLAVE 0x800000 -#define CLOCK_ON 1 -#define CLOCK_OFF 0 - -/* Machine driver Name*/ -#define DRV_NAME "mdm9650-asoc-auto" - -enum mi2s_pcm_mux { - PRI_MI2S_PCM, - SEC_MI2S_PCM, - MI2S_PCM_MAX_INTF -}; - -struct mdm_machine_data { - u32 mclk_freq; - u16 prim_mi2s_mode; - u16 sec_mi2s_mode; - u16 prim_auxpcm_mode; - u16 sec_auxpcm_mode; - u32 prim_clk_usrs; -}; - -static const struct afe_clk_cfg lpass_default = { - AFE_API_VERSION_I2S_CONFIG, - Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, - Q6AFE_LPASS_OSR_CLK_12_P288_MHZ, - Q6AFE_LPASS_CLK_SRC_INTERNAL, - Q6AFE_LPASS_CLK_ROOT_DEFAULT, - Q6AFE_LPASS_MODE_BOTH_VALID, - 0, -}; - -static int mdm_auxpcm_rate = 8000; -static void *lpaif_pri_muxsel_virt_addr; -static void *lpaif_sec_muxsel_virt_addr; -static void *lpass_gpio_mux_spkr_ctl_virt_addr; -static void *lpass_gpio_mux_mic_ctl_virt_addr; - -static int mdm_mi2s_rx_ch = 1; -static int mdm_mi2s_tx_ch = 1; -static int mdm_sec_mi2s_rx_ch = 1; -static int mdm_sec_mi2s_tx_ch = 1; -static int mdm_mi2s_rx_rate = SAMPLE_RATE_48KHZ; -static int mdm_mi2s_tx_rate = SAMPLE_RATE_48KHZ; -static int mdm_sec_mi2s_rx_rate = SAMPLE_RATE_48KHZ; -static int mdm_sec_mi2s_tx_rate = SAMPLE_RATE_48KHZ; - -static atomic_t mi2s_ref_count; -static atomic_t sec_mi2s_ref_count; - -static void *adsp_state_notifier; -static bool dummy_device_registered; - -static int mdm_mi2s_clk_ctl(struct snd_soc_pcm_runtime *rtd, bool enable) -{ - struct snd_soc_card *card = rtd->card; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - struct afe_clk_cfg *lpass_clk = NULL; - int ret = 0; - - if (pdata == NULL) { - pr_err("%s:platform data is null\n", __func__); - - ret = -ENOMEM; - goto done; - } - lpass_clk = kzalloc(sizeof(struct afe_clk_cfg), GFP_KERNEL); - if (!lpass_clk) { - ret = -ENOMEM; - goto done; - } - memcpy(lpass_clk, &lpass_default, sizeof(struct afe_clk_cfg)); - pr_debug("%s enable = %x\n", __func__, enable); - - if (enable) { - ret = afe_set_lpass_clock(MI2S_RX, lpass_clk); - if (ret < 0) - pr_err("%s:afe_set_lpass_clock failed\n", __func__); - } else { - lpass_clk->clk_set_mode = Q6AFE_LPASS_MODE_BOTH_VALID; - lpass_clk->clk_val1 = Q6AFE_LPASS_IBIT_CLK_DISABLE; - lpass_clk->clk_val2 = Q6AFE_LPASS_OSR_CLK_DISABLE; - ret = afe_set_lpass_clock(MI2S_RX, lpass_clk); - if (ret < 0) - pr_err("%s:afe_set_lpass_clock failed\n", __func__); - } - pr_debug("%s clk 1 = %x clk2 = %x mode = %x\n", - __func__, lpass_clk->clk_val1, lpass_clk->clk_val2, - lpass_clk->clk_set_mode); - - kfree(lpass_clk); -done: - return ret; -} - -static void mdm_mi2s_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - int ret; - - if (atomic_dec_return(&mi2s_ref_count) == 0) { - ret = mdm_mi2s_clk_ctl(rtd, false); - if (ret < 0) - pr_err("%s Clock disable failed\n", __func__); - } -} - -static int mdm_mi2s_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_card *card = rtd->card; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - int ret = 0; - - if (atomic_inc_return(&mi2s_ref_count) == 1) { - if (lpaif_pri_muxsel_virt_addr != NULL) { - ret = afe_enable_lpass_core_shared_clock(MI2S_RX, - CLOCK_ON); - if (ret < 0) { - ret = -EINVAL; - goto done; - } - iowrite32(I2S_SEL << I2S_PCM_SEL_OFFSET, - lpaif_pri_muxsel_virt_addr); - if (lpass_gpio_mux_spkr_ctl_virt_addr != NULL) { - if (pdata->prim_mi2s_mode == 1) { - iowrite32(PRI_TLMM_CLKS_EN_MASTER, - lpass_gpio_mux_spkr_ctl_virt_addr); - } else if (pdata->prim_mi2s_mode == 0) { - iowrite32(PRI_TLMM_CLKS_EN_SLAVE, - lpass_gpio_mux_spkr_ctl_virt_addr); - } else { - dev_err(card->dev, "%s Invalid primary mi2s mode\n", - __func__); - ret = -EINVAL; - goto err; - } - } else { - dev_err(card->dev, "%s: mux spkr ctl virt addr is NULL\n", - __func__); - - ret = -EINVAL; - goto err; - } - } else { - dev_err(card->dev, "%s lpaif_pri_muxsel_virt_addr is NULL\n", - __func__); - - ret = -EINVAL; - goto done; - } - /* - * This sets the CONFIG PARAMETER WS_SRC. - * 1 means internal clock master mode. - * 0 means external clock slave mode. - */ - if (pdata->prim_mi2s_mode == 1) { - ret = mdm_mi2s_clk_ctl(rtd, true); - if (ret < 0) { - dev_err(card->dev, "%s clock enable failed\n", - __func__); - goto err; - } - ret = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) { - mdm_mi2s_clk_ctl(rtd, false); - dev_err(card->dev, "%s Set fmt for cpu dai failed\n", - __func__); - goto err; - } - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBS_CFS | - SND_SOC_DAIFMT_I2S); - if (ret < 0) { - mdm_mi2s_clk_ctl(rtd, false); - dev_err(card->dev, "%s Set fmt for codec dai failed\n", - __func__); - } - } else { - dev_err(card->dev, "%s Invalid primary mi2s mode\n", - __func__); - ret = -EINVAL; - } - /* Set the mclk as the sysclk for the codec dai */ - ret = snd_soc_dai_set_sysclk(codec_dai, - CLKIN_MCLK, - pdata->mclk_freq, SND_SOC_CLOCK_OUT); - if (ret < 0) - pr_err("%s Set sysclk for codec dai failed\n", - __func__); -err: - if (ret) - atomic_dec_return(&mi2s_ref_count); - afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); - } -done: - return ret; -} - -static int mdm_sec_mi2s_clk_ctl(struct snd_soc_pcm_runtime *rtd, bool enable) -{ - struct snd_soc_card *card = rtd->card; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - struct afe_clk_cfg *lpass_clk = NULL; - int ret = 0; - - if (pdata == NULL) { - dev_err(card->dev, "%s:platform data is null\n", __func__); - - ret = -EINVAL; - goto done; - } - lpass_clk = kzalloc(sizeof(struct afe_clk_cfg), GFP_KERNEL); - if (!lpass_clk) { - ret = -ENOMEM; - goto done; - } - memcpy(lpass_clk, &lpass_default, sizeof(struct afe_clk_cfg)); - dev_dbg(card->dev, "%s enable = %x\n", __func__, enable); - - lpass_clk->clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID; - if (enable) { - ret = afe_set_lpass_clock(SECONDARY_I2S_RX, lpass_clk); - if (ret < 0) - dev_err(card->dev, - "%s:afe_set_lpass_clock failed to enable\n", - __func__); - } else { - lpass_clk->clk_val1 = Q6AFE_LPASS_IBIT_CLK_DISABLE; - ret = afe_set_lpass_clock(SECONDARY_I2S_RX, lpass_clk); - if (ret < 0) - dev_err(card->dev, - "%s:afe_set_lpass_clock failed disable\n", - __func__); - } - dev_dbg(card->dev, "%s clk 1 = %x clk2 = %x mode = %x\n", - __func__, lpass_clk->clk_val1, lpass_clk->clk_val2, - lpass_clk->clk_set_mode); - - kfree(lpass_clk); -done: - return ret; -} - -static void mdm_sec_mi2s_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - int ret; - - if (atomic_dec_return(&sec_mi2s_ref_count) == 0) { - ret = mdm_sec_mi2s_clk_ctl(rtd, false); - if (ret < 0) - pr_err("%s Clock disable failed\n", __func__); - } -} - -static int mdm_sec_mi2s_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_card *card = rtd->card; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - int ret = 0; - - if (atomic_inc_return(&sec_mi2s_ref_count) == 1) { - if (lpaif_sec_muxsel_virt_addr != NULL) { - ret = afe_enable_lpass_core_shared_clock( - SECONDARY_I2S_RX, CLOCK_ON); - if (ret < 0) { - ret = -EINVAL; - goto done; - } - iowrite32(I2S_SEL << I2S_PCM_SEL_OFFSET, - lpaif_sec_muxsel_virt_addr); - - if (lpass_gpio_mux_mic_ctl_virt_addr != NULL) { - if (pdata->sec_mi2s_mode == 1) { - iowrite32(SEC_TLMM_CLKS_EN_MASTER, - lpass_gpio_mux_mic_ctl_virt_addr); - } else if (pdata->sec_mi2s_mode == 0) { - iowrite32(SEC_TLMM_CLKS_EN_SLAVE, - lpass_gpio_mux_mic_ctl_virt_addr); - } else { - dev_err(card->dev, "%s Invalid secondary mi2s mode\n", - __func__); - ret = -EINVAL; - goto err; - } - } else { - dev_err(card->dev, "%s: mux spkr ctl virt addr is NULL\n", - __func__); - ret = -EINVAL; - goto err; - } - - } else { - dev_err(card->dev, "%s lpaif_sec_muxsel_virt_addr is NULL\n", - __func__); - ret = -EINVAL; - goto done; - } - /* - * This sets the CONFIG PARAMETER WS_SRC. - * 1 means internal clock master mode. - * 0 means external clock slave mode. - */ - if (pdata->sec_mi2s_mode == 1) { - ret = mdm_sec_mi2s_clk_ctl(rtd, true); - if (ret < 0) { - dev_err(card->dev, "%s clock enable failed\n", - __func__); - goto err; - } - ret = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) { - ret = mdm_sec_mi2s_clk_ctl(rtd, false); - dev_err(card->dev, "%s Set fmt for cpu dai failed\n", - __func__); - } - } else if (pdata->sec_mi2s_mode == 0) { - /* - * Enable mclk here, if needed for external codecs. - * Optional. Refer primary mi2s slave interface. - */ - ret = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - dev_err(card->dev, - "%s Set fmt for cpu dai failed\n", - __func__); - } else { - dev_err(card->dev, - "%s Invalid secondary mi2s mode\n", - __func__); - ret = -EINVAL; - } -err: - if (ret) - atomic_dec_return(&sec_mi2s_ref_count); - afe_enable_lpass_core_shared_clock( - SECONDARY_I2S_RX, - CLOCK_OFF); - } -done: - return ret; -} - -static struct snd_soc_ops mdm_mi2s_be_ops = { - .startup = mdm_mi2s_startup, - .shutdown = mdm_mi2s_shutdown, -}; - -static struct snd_soc_ops mdm_sec_mi2s_be_ops = { - .startup = mdm_sec_mi2s_startup, - .shutdown = mdm_sec_mi2s_shutdown, -}; - -static int mdm_mi2s_rx_rate_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s: mdm_i2s_rate = %d", __func__, - mdm_mi2s_rx_rate); - ucontrol->value.integer.value[0] = mdm_mi2s_rx_rate; - return 0; -} - -static int mdm_sec_mi2s_rx_rate_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s: mdm_sec_i2s_rate = %d", __func__, - mdm_sec_mi2s_rx_rate); - ucontrol->value.integer.value[0] = mdm_sec_mi2s_rx_rate; - return 0; -} - -static int mdm_mi2s_rx_rate_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - switch (ucontrol->value.integer.value[0]) { - case 0: - mdm_mi2s_rx_rate = SAMPLE_RATE_8KHZ; - break; - case 1: - mdm_mi2s_rx_rate = SAMPLE_RATE_16KHZ; - break; - case 2: - mdm_mi2s_rx_rate = SAMPLE_RATE_48KHZ; - break; - default: - mdm_mi2s_rx_rate = SAMPLE_RATE_8KHZ; - break; - } - pr_debug("%s: mdm_i2s_rx_rate = %d ucontrol->value = %d\n", - __func__, mdm_mi2s_rx_rate, - (int)ucontrol->value.integer.value[0]); - return 0; -} - -static int mdm_sec_mi2s_rx_rate_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - switch (ucontrol->value.integer.value[0]) { - case 0: - mdm_sec_mi2s_rx_rate = SAMPLE_RATE_8KHZ; - break; - case 1: - mdm_sec_mi2s_rx_rate = SAMPLE_RATE_16KHZ; - break; - case 2: - mdm_sec_mi2s_rx_rate = SAMPLE_RATE_48KHZ; - break; - default: - mdm_sec_mi2s_rx_rate = SAMPLE_RATE_8KHZ; - break; - } - pr_debug("%s: mdm_sec_mi2s_rx_rate = %d ucontrol->value = %d\n", - __func__, mdm_sec_mi2s_rx_rate, - (int)ucontrol->value.integer.value[0]); - return 0; -} - -static int mdm_mi2s_tx_rate_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s: mdm_i2s_rate = %d", __func__, - mdm_mi2s_tx_rate); - ucontrol->value.integer.value[0] = mdm_mi2s_tx_rate; - return 0; -} - -static int mdm_sec_mi2s_tx_rate_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s: mdm_sec_mi2s_rate = %d", __func__, - mdm_sec_mi2s_tx_rate); - ucontrol->value.integer.value[0] = mdm_sec_mi2s_tx_rate; - return 0; -} - -static int mdm_mi2s_tx_rate_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - switch (ucontrol->value.integer.value[0]) { - case 0: - mdm_mi2s_tx_rate = SAMPLE_RATE_8KHZ; - break; - case 1: - mdm_mi2s_tx_rate = SAMPLE_RATE_16KHZ; - break; - case 2: - mdm_mi2s_tx_rate = SAMPLE_RATE_48KHZ; - break; - default: - mdm_mi2s_tx_rate = SAMPLE_RATE_8KHZ; - break; - } - pr_debug("%s: mdm_i2s_tx_rate = %d ucontrol->value = %d\n", - __func__, mdm_mi2s_tx_rate, - (int)ucontrol->value.integer.value[0]); - return 0; -} - -static int mdm_sec_mi2s_tx_rate_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - switch (ucontrol->value.integer.value[0]) { - case 0: - mdm_sec_mi2s_tx_rate = SAMPLE_RATE_8KHZ; - break; - case 1: - mdm_sec_mi2s_tx_rate = SAMPLE_RATE_16KHZ; - break; - case 2: - mdm_sec_mi2s_tx_rate = SAMPLE_RATE_48KHZ; - break; - default: - mdm_sec_mi2s_tx_rate = SAMPLE_RATE_8KHZ; - break; - } - pr_debug("%s: mdm_sec_mi2s_tx_rate = %d ucontrol->value = %d\n", - __func__, mdm_sec_mi2s_tx_rate, - (int)ucontrol->value.integer.value[0]); - return 0; -} - -static int mdm_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = mdm_mi2s_rx_rate; - channels->min = channels->max = mdm_mi2s_rx_ch; - return 0; -} - -static int mdm_sec_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = mdm_sec_mi2s_rx_rate; - channels->min = channels->max = mdm_sec_mi2s_rx_ch; - return 0; -} - -static int mdm_mi2s_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = mdm_mi2s_tx_rate; - channels->min = channels->max = mdm_mi2s_tx_ch; - return 0; -} - -static int mdm_sec_mi2s_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = mdm_sec_mi2s_tx_rate; - channels->min = channels->max = mdm_sec_mi2s_tx_ch; - return 0; -} - -static int mdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - rate->min = rate->max = MDM_MI2S_RATE; - return 0; -} - -static int mdm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s mdm_mi2s_rx_ch %d\n", __func__, - mdm_mi2s_rx_ch); - - ucontrol->value.integer.value[0] = mdm_mi2s_rx_ch - 1; - return 0; -} - -static int mdm_sec_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s mdm_sec_mi2s_rx_ch %d\n", __func__, - mdm_mi2s_rx_ch); - - ucontrol->value.integer.value[0] = mdm_sec_mi2s_rx_ch - 1; - return 0; -} - -static int mdm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - mdm_mi2s_rx_ch = ucontrol->value.integer.value[0] + 1; - pr_debug("%s mdm_mi2s_rx_ch %d\n", __func__, - mdm_mi2s_rx_ch); - - return 1; -} - -static int mdm_sec_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - mdm_mi2s_rx_ch = ucontrol->value.integer.value[0] + 1; - pr_debug("%s mdm_sec_mi2s_rx_ch %d\n", __func__, - mdm_sec_mi2s_rx_ch); - - return 1; -} - -static int mdm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s mdm_mi2s_tx_ch %d\n", __func__, - mdm_mi2s_tx_ch); - - ucontrol->value.integer.value[0] = mdm_mi2s_tx_ch - 1; - return 0; -} - -static int mdm_sec_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - pr_debug("%s mdm_sec_mi2s_tx_ch %d\n", __func__, - mdm_mi2s_tx_ch); - - ucontrol->value.integer.value[0] = mdm_sec_mi2s_tx_ch - 1; - return 0; -} - -static int mdm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - mdm_mi2s_tx_ch = ucontrol->value.integer.value[0] + 1; - pr_debug("%s mdm_mi2s_tx_ch %d\n", __func__, - mdm_mi2s_tx_ch); - - return 1; -} - -static int mdm_sec_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - mdm_mi2s_tx_ch = ucontrol->value.integer.value[0] + 1; - pr_debug("%s mdm_sec_mi2s_tx_ch %d\n", __func__, - mdm_sec_mi2s_tx_ch); - - return 1; -} - -static int mdm_auxpcm_startup(struct snd_pcm_substream *substream) -{ - int ret = 0; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_card *card = rtd->card; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - - if (lpaif_pri_muxsel_virt_addr != NULL) { - ret = afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_ON); - if (ret < 0) { - ret = -EINVAL; - goto done; - } - iowrite32(PCM_SEL << I2S_PCM_SEL_OFFSET, - lpaif_pri_muxsel_virt_addr); - if (lpass_gpio_mux_spkr_ctl_virt_addr != NULL) { - if (pdata->prim_auxpcm_mode == 1) { - iowrite32(PRI_TLMM_CLKS_EN_MASTER, - lpass_gpio_mux_spkr_ctl_virt_addr); - } else if (pdata->prim_auxpcm_mode == 0) { - iowrite32(PRI_TLMM_CLKS_EN_SLAVE, - lpass_gpio_mux_spkr_ctl_virt_addr); - } else { - dev_err(card->dev, "%s Invalid primary auxpcm mode\n", - __func__); - ret = -EINVAL; - } - } else { - dev_err(card->dev, "%s lpass_mux_spkr_ctl_virt_addr is NULL\n", - __func__); - ret = -EINVAL; - } - } else { - dev_err(card->dev, "%s lpaif_pri_muxsel_virt_addr is NULL\n", - __func__); - ret = -EINVAL; - goto done; - } - afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); -done: - return ret; -} - -static struct snd_soc_ops mdm_auxpcm_be_ops = { - .startup = mdm_auxpcm_startup, -}; - -static int mdm_sec_auxpcm_startup(struct snd_pcm_substream *substream) -{ - int ret = 0; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_card *card = rtd->card; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - - if (lpaif_sec_muxsel_virt_addr != NULL) { - ret = afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_ON); - if (ret < 0) { - ret = -EINVAL; - goto done; - } - iowrite32(PCM_SEL << I2S_PCM_SEL_OFFSET, - lpaif_sec_muxsel_virt_addr); - if (lpass_gpio_mux_mic_ctl_virt_addr != NULL) { - if (pdata->sec_auxpcm_mode == 1) { - iowrite32(SEC_TLMM_CLKS_EN_MASTER, - lpass_gpio_mux_mic_ctl_virt_addr); - } else if (pdata->sec_auxpcm_mode == 0) { - iowrite32(SEC_TLMM_CLKS_EN_SLAVE, - lpass_gpio_mux_mic_ctl_virt_addr); - } else { - dev_err(card->dev, "%s Invalid primary auxpcm mode\n", - __func__); - ret = -EINVAL; - } - } else { - dev_err(card->dev, - "%s lpass_gpio_mux_mic_ctl_virt_addr is NULL\n", - __func__); - ret = -EINVAL; - } - } else { - dev_err(card->dev, - "%s lpaif_sec_muxsel_virt_addr is NULL\n", __func__); - ret = -EINVAL; - goto done; - } - afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); -done: - return ret; -} - -static struct snd_soc_ops mdm_sec_auxpcm_be_ops = { - .startup = mdm_sec_auxpcm_startup, -}; - -static int mdm_auxpcm_rate_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = mdm_auxpcm_rate; - return 0; -} - -static int mdm_auxpcm_rate_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - switch (ucontrol->value.integer.value[0]) { - case 0: - mdm_auxpcm_rate = 8000; - break; - case 1: - mdm_auxpcm_rate = 16000; - break; - default: - mdm_auxpcm_rate = 8000; - break; - } - return 0; -} - -static int mdm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - - rate->min = rate->max = mdm_auxpcm_rate; - channels->min = channels->max = 1; - - return 0; -} - -static const char *const mi2s_rx_ch_text[] = {"One", "Two"}; -static const char *const mi2s_tx_ch_text[] = {"One", "Two"}; -static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"}; - -static const char *const mi2s_rx_rate_text[] = {"rate_8000", - "rate_16000", "rate_48000"}; -static const char *const mi2s_tx_rate_text[] = {"rate_8000", - "rate_16000", "rate_48000"}; - -static const struct soc_enum mdm_enum[] = { - SOC_ENUM_SINGLE_EXT(2, mi2s_rx_ch_text), - SOC_ENUM_SINGLE_EXT(2, mi2s_tx_ch_text), - SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text), - SOC_ENUM_SINGLE_EXT(3, mi2s_rx_rate_text), - SOC_ENUM_SINGLE_EXT(3, mi2s_tx_rate_text), -}; - -static const struct snd_kcontrol_new mdm_snd_controls[] = { - SOC_ENUM_EXT("MI2S_RX Channels", mdm_enum[0], - mdm_mi2s_rx_ch_get, - mdm_mi2s_rx_ch_put), - SOC_ENUM_EXT("MI2S_TX Channels", mdm_enum[1], - mdm_mi2s_tx_ch_get, - mdm_mi2s_tx_ch_put), - SOC_ENUM_EXT("AUX PCM SampleRate", mdm_enum[2], - mdm_auxpcm_rate_get, - mdm_auxpcm_rate_put), - SOC_ENUM_EXT("MI2S Rx SampleRate", mdm_enum[3], - mdm_mi2s_rx_rate_get, - mdm_mi2s_rx_rate_put), - SOC_ENUM_EXT("MI2S Tx SampleRate", mdm_enum[4], - mdm_mi2s_tx_rate_get, - mdm_mi2s_tx_rate_put), - SOC_ENUM_EXT("SEC_MI2S_RX Channels", mdm_enum[0], - mdm_sec_mi2s_rx_ch_get, - mdm_sec_mi2s_rx_ch_put), - SOC_ENUM_EXT("SEC_MI2S_TX Channels", mdm_enum[1], - mdm_sec_mi2s_tx_ch_get, - mdm_sec_mi2s_tx_ch_put), - SOC_ENUM_EXT("SEC MI2S Rx SampleRate", mdm_enum[3], - mdm_sec_mi2s_rx_rate_get, - mdm_sec_mi2s_rx_rate_put), - SOC_ENUM_EXT("SEC MI2S Tx SampleRate", mdm_enum[4], - mdm_sec_mi2s_tx_rate_get, - mdm_sec_mi2s_tx_rate_put), -}; - -static int mdm_mi2s_audrx_init(struct snd_soc_pcm_runtime *rtd) -{ - int ret = 0; - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - pr_debug("%s dev_name %s\n", __func__, dev_name(cpu_dai->dev)); - - rtd->pmdown_time = 0; - ret = snd_soc_add_codec_controls(codec, mdm_snd_controls, - ARRAY_SIZE(mdm_snd_controls)); - if (ret < 0) { - pr_err("%s: add_codec_controls failed, %d\n", - __func__, ret); - goto done; - } - -done: - return ret; -} - -/* Digital audio interface connects codec <---> CPU */ -static struct snd_soc_dai_link mdm_dai[] = { - /* FrontEnd DAI Links */ - { - .name = "MDM Media1", - .stream_name = "MultiMedia1", - .cpu_dai_name = "MultiMedia1", - .platform_name = "msm-pcm-dsp.0", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .ignore_suspend = 1, - /* This dainlink has playback support */ - .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 - }, - { - .name = "MSM VoIP", - .stream_name = "VoIP", - .cpu_dai_name = "VoIP", - .platform_name = "msm-voip-dsp", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .ignore_suspend = 1, - /* This dainlink has VOIP support */ - .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_VOIP, - }, - { - .name = "Circuit-Switch Voice", - .stream_name = "CS-Voice", - .cpu_dai_name = "CS-VOICE", - .platform_name = "msm-pcm-voice", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - .ignore_suspend = 1, - /* This dainlink has Voice support */ - .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_CS_VOICE, - }, - { - .name = "Primary MI2S RX Hostless", - .stream_name = "Primary MI2S_RX Hostless Playback", - .cpu_dai_name = "PRI_MI2S_RX_HOSTLESS", - .platform_name = "msm-pcm-hostless", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - }, - { - .name = "VoLTE", - .stream_name = "VoLTE", - .cpu_dai_name = "VoLTE", - .platform_name = "msm-pcm-voice", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - .ignore_suspend = 1, - /* this dainlink has playback support */ - .ignore_pmdown_time = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_VOLTE, - }, - { .name = "MSM AFE-PCM RX", - .stream_name = "AFE-PROXY RX", - .cpu_dai_name = "msm-dai-q6-dev.241", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .platform_name = "msm-pcm-afe", - .ignore_suspend = 1, - /* this dainlink has playback support */ - .ignore_pmdown_time = 1, - }, - { - .name = "MSM AFE-PCM TX", - .stream_name = "AFE-PROXY TX", - .cpu_dai_name = "msm-dai-q6-dev.240", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .platform_name = "msm-pcm-afe", - .ignore_suspend = 1, - }, - { - .name = "DTMF RX Hostless", - .stream_name = "DTMF RX Hostless", - .cpu_dai_name = "DTMF_RX_HOSTLESS", - .platform_name = "msm-pcm-dtmf", - .dynamic = 1, - .dpcm_playback = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_DTMF_RX, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - }, - { - .name = "DTMF TX", - .stream_name = "DTMF TX", - .cpu_dai_name = "msm-dai-stub-dev.4", - .platform_name = "msm-pcm-dtmf", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .ignore_suspend = 1, - }, - { - .name = "CS-VOICE HOST RX CAPTURE", - .stream_name = "CS-VOICE HOST RX CAPTURE", - .cpu_dai_name = "msm-dai-stub-dev.5", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .ignore_suspend = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - }, - { - .name = "CS-VOICE HOST RX PLAYBACK", - .stream_name = "CS-VOICE HOST RX PLAYBACK", - .cpu_dai_name = "msm-dai-stub-dev.6", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - }, - { - .name = "CS-VOICE HOST TX CAPTURE", - .stream_name = "CS-VOICE HOST TX CAPTURE", - .cpu_dai_name = "msm-dai-stub-dev.7", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .ignore_suspend = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - }, - { - .name = "CS-VOICE HOST TX PLAYBACK", - .stream_name = "CS-VOICE HOST TX PLAYBACK", - .cpu_dai_name = "msm-dai-stub-dev.8", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - }, - { - .name = "MDM Media2", - .stream_name = "MultiMedia2", - .cpu_dai_name = "MultiMedia2", - .platform_name = "msm-pcm-dsp.0", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - /* this dainlink has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, - }, - { - .name = "MDM Media6", - .stream_name = "MultiMedia6", - .cpu_dai_name = "MultiMedia6", - .platform_name = "msm-pcm-loopback", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - /* this dainlink has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, - }, - { - .name = "Primary MI2S TX Hostless", - .stream_name = "Primary MI2S_TX Hostless Playback", - .cpu_dai_name = "PRI_MI2S_TX_HOSTLESS", - .platform_name = "msm-pcm-hostless", - .dynamic = 1, - .dpcm_capture = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - }, - { - .name = "MDM LowLatency", - .stream_name = "MultiMedia5", - .cpu_dai_name = "MultiMedia5", - .platform_name = "msm-pcm-dsp.1", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .ignore_suspend = 1, - /* this dainlink has playback support */ - .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, - }, - { - .name = "MDM VoiceMMode1", - .stream_name = "VoiceMMode1", - .cpu_dai_name = "VoiceMMode1", - .platform_name = "msm-pcm-voice", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - .ignore_suspend = 1, - /* This dainlink has Voice support */ - .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, - }, - { - .name = "MDM VoiceMMode2", - .stream_name = "VoiceMMode2", - .cpu_dai_name = "VoiceMMode2", - .platform_name = "msm-pcm-voice", - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, - .ignore_suspend = 1, - /* This dainlink has Voice support */ - .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, - }, - { - .name = "VoiceMMode1 HOST RX CAPTURE", - .stream_name = "VoiceMMode1 HOST RX CAPTURE", - .cpu_dai_name = "msm-dai-stub-dev.5", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .ignore_suspend = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - }, - { - .name = "VoiceMMode1 HOST RX PLAYBACK", - .stream_name = "VoiceMMode1 HOST RX PLAYBACK", - .cpu_dai_name = "msm-dai-stub-dev.6", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - }, - { - .name = "VoiceMMode1 HOST TX CAPTURE", - .stream_name = "VoiceMMode1 HOST TX CAPTURE", - .cpu_dai_name = "msm-dai-stub-dev.7", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .ignore_suspend = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - }, - { - .name = "VoiceMMode1 HOST TX PLAYBACK", - .stream_name = "VoiceMMode1 HOST TX PLAYBACK", - .cpu_dai_name = "msm-dai-stub-dev.8", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - }, - { - .name = "VoiceMMode2 HOST RX CAPTURE", - .stream_name = "VoiceMMode2 HOST RX CAPTURE", - .cpu_dai_name = "msm-dai-stub-dev.5", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .ignore_suspend = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - }, - { - .name = "VoiceMMode2 HOST RX PLAYBACK", - .stream_name = "VOiceMMode2 HOST RX PLAYBACK", - .cpu_dai_name = "msm-dai-stub-dev.6", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - }, - { - .name = "VoiceMMode2 HOST TX CAPTURE", - .stream_name = "VoiceMMode2 HOST TX CAPTURE", - .cpu_dai_name = "msm-dai-stub-dev.7", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .ignore_suspend = 1, - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - }, - { - .name = "VoiceMMode2 HOST TX PLAYBACK", - .stream_name = "VOiceMMode2 HOST TX PLAYBACK", - .cpu_dai_name = "msm-dai-stub-dev.8", - .platform_name = "msm-voice-host-pcm", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .ignore_suspend = 1, - .ignore_pmdown_time = 1, - }, - /* Backend DAI Links */ - { - .name = LPASS_BE_PRI_MI2S_RX, - .stream_name = "Primary MI2S Playback", - .cpu_dai_name = "msm-dai-q6-mi2s.0", - .platform_name = "msm-pcm-routing", - .codec_name = "tlv320aic3x-codec", - .codec_dai_name = "tlv320aic3x-hifi", - .no_pcm = 1, - .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, - .init = &mdm_mi2s_audrx_init, - .be_hw_params_fixup = &mdm_mi2s_rx_be_hw_params_fixup, - .ops = &mdm_mi2s_be_ops, - .ignore_pmdown_time = 1, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_PRI_MI2S_TX, - .stream_name = "Primary MI2S Capture", - .cpu_dai_name = "msm-dai-q6-mi2s.0", - .platform_name = "msm-pcm-routing", - .codec_name = "tlv320aic3x-codec", - .codec_dai_name = "tlv320aic3x-hifi", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, - .be_hw_params_fixup = &mdm_mi2s_tx_be_hw_params_fixup, - .ops = &mdm_mi2s_be_ops, - .ignore_pmdown_time = 1, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_AFE_PCM_RX, - .stream_name = "AFE Playback", - .cpu_dai_name = "msm-dai-q6-dev.224", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .no_pcm = 1, - .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_AFE_PCM_TX, - .stream_name = "AFE Capture", - .cpu_dai_name = "msm-dai-q6-dev.225", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_AUXPCM_RX, - .stream_name = "AUX PCM Playback", - .cpu_dai_name = "msm-dai-q6-auxpcm.1", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .no_pcm = 1, - .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_AUXPCM_RX, - .be_hw_params_fixup = mdm_auxpcm_be_params_fixup, - .ops = &mdm_auxpcm_be_ops, - .ignore_pmdown_time = 1, - /* this dainlink has playback support */ - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_AUXPCM_TX, - .stream_name = "AUX PCM Capture", - .cpu_dai_name = "msm-dai-q6-auxpcm.1", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_AUXPCM_TX, - .be_hw_params_fixup = mdm_auxpcm_be_params_fixup, - .ops = &mdm_auxpcm_be_ops, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_SEC_AUXPCM_RX, - .stream_name = "Sec AUX PCM Playback", - .cpu_dai_name = "msm-dai-q6-auxpcm.2", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .no_pcm = 1, - .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, - .be_hw_params_fixup = mdm_auxpcm_be_params_fixup, - .ops = &mdm_sec_auxpcm_be_ops, - .ignore_pmdown_time = 1, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_SEC_AUXPCM_TX, - .stream_name = "Sec AUX PCM Capture", - .cpu_dai_name = "msm-dai-q6-auxpcm.2", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, - .be_hw_params_fixup = mdm_auxpcm_be_params_fixup, - .ops = &mdm_sec_auxpcm_be_ops, - .ignore_suspend = 1, - }, - /* Incall Record Uplink BACK END DAI Link */ - { - .name = LPASS_BE_INCALL_RECORD_TX, - .stream_name = "Voice Uplink Capture", - .cpu_dai_name = "msm-dai-q6-dev.32772", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, - .be_hw_params_fixup = mdm_be_hw_params_fixup, - .ignore_suspend = 1, - }, - /* Incall Record Downlink BACK END DAI Link */ - { - .name = LPASS_BE_INCALL_RECORD_RX, - .stream_name = "Voice Downlink Capture", - .cpu_dai_name = "msm-dai-q6-dev.32771", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, - .be_hw_params_fixup = mdm_be_hw_params_fixup, - .ignore_suspend = 1, - }, - /* Incall Music BACK END DAI Link */ - { - .name = LPASS_BE_VOICE_PLAYBACK_TX, - .stream_name = "Voice Farend Playback", - .cpu_dai_name = "msm-dai-q6-dev.32773", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .no_pcm = 1, - .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, - .be_hw_params_fixup = mdm_be_hw_params_fixup, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_SEC_MI2S_RX, - .stream_name = "Secondary MI2S Playback", - .cpu_dai_name = "msm-dai-q6-mi2s.1", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", - .no_pcm = 1, - .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, - .be_hw_params_fixup = &mdm_sec_mi2s_rx_be_hw_params_fixup, - .ops = &mdm_sec_mi2s_be_ops, - .ignore_pmdown_time = 1, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_SEC_MI2S_TX, - .stream_name = "Secondary MI2S Capture", - .cpu_dai_name = "msm-dai-q6-mi2s.1", - .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-tx", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, - .be_hw_params_fixup = &mdm_sec_mi2s_tx_be_hw_params_fixup, - .ops = &mdm_sec_mi2s_be_ops, - .ignore_pmdown_time = 1, - .ignore_suspend = 1, - }, -}; - -static struct snd_soc_card snd_soc_card_mdm = { - .name = "mdm-auto-i2s-snd-card", - .dai_link = mdm_dai, - .num_links = ARRAY_SIZE(mdm_dai), -}; - -static int mdm_populate_dai_link_component_of_node( - struct snd_soc_card *card) -{ - int i, index, ret = 0; - struct device *cdev = card->dev; - struct snd_soc_dai_link *dai_link = card->dai_link; - struct device_node *np; - - if (!cdev) { - pr_err("%s: Sound card device memory NULL\n", __func__); - return -ENODEV; - } - - for (i = 0; i < card->num_links; i++) { - if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) - continue; - - /* populate platform_of_node for snd card dai links */ - if (dai_link[i].platform_name && - !dai_link[i].platform_of_node) { - index = of_property_match_string(cdev->of_node, - "asoc-platform-names", - dai_link[i].platform_name); - if (index < 0) { - pr_debug("%s: No match found for platform name: %s\n", - __func__, dai_link[i].platform_name); - ret = index; - goto err; - } - - np = of_parse_phandle(cdev->of_node, "asoc-platform", - index); - if (!np) { - pr_err("%s: retrieving phandle for platform %s, index %d failed\n", - __func__, dai_link[i].platform_name, - index); - ret = -ENODEV; - goto err; - } - dai_link[i].platform_of_node = np; - dai_link[i].platform_name = NULL; - } - - /* populate cpu_of_node for snd card dai links */ - if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { - index = of_property_match_string(cdev->of_node, - "asoc-cpu-names", - dai_link[i].cpu_dai_name); - if (index >= 0) { - np = of_parse_phandle(cdev->of_node, "asoc-cpu", - index); - if (!np) { - pr_err("%s: retrieving phandle for cpu dai %s failed\n", - __func__, - dai_link[i].cpu_dai_name); - ret = -ENODEV; - goto err; - } - dai_link[i].cpu_of_node = np; - dai_link[i].cpu_dai_name = NULL; - } - } - - /* populate codec_of_node for snd card dai links */ - if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { - index = of_property_match_string(cdev->of_node, - "asoc-codec-names", - dai_link[i].codec_name); - if (index < 0) - continue; - np = of_parse_phandle(cdev->of_node, "asoc-codec", - index); - if (!np) { - pr_err("%s: retrieving phandle for codec %s failed\n", - __func__, dai_link[i].codec_name); - ret = -ENODEV; - goto err; - } - dai_link[i].codec_of_node = np; - dai_link[i].codec_name = NULL; - } - } - -err: - return ret; -} - -static int mdm_populate_mi2s_interface_mode( - struct snd_soc_card *card) -{ - int size, ret = 0; - struct device *cdev = card->dev; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - const char *val_array[MI2S_PCM_MAX_INTF]; - - size = of_property_read_string_array(cdev->of_node, - "qcom,mi2s-interface-mode", - val_array, MI2S_PCM_MAX_INTF); - if (size <= 0) { - dev_info(cdev, "%s: Looking up %s property in node %s failed", - __func__, "qcom,mi2s-interface-mode", - cdev->of_node->full_name); - pdata->prim_mi2s_mode = I2S_PCM_MASTER_MODE; - pdata->sec_mi2s_mode = I2S_PCM_MASTER_MODE; - } else { - if (!strcmp(val_array[PRI_MI2S_PCM], "pri_mi2s_master")) { - pdata->prim_mi2s_mode = I2S_PCM_MASTER_MODE; - } else if (!strcmp(val_array[PRI_MI2S_PCM], "pri_mi2s_slave")) { - pdata->prim_mi2s_mode = I2S_PCM_SLAVE_MODE; - } else { - dev_err(cdev, "%s: invalid DT intf mode\n", - __func__); - ret = -EINVAL; - goto err_intf; - } - - if (!strcmp(val_array[SEC_MI2S_PCM], "sec_mi2s_master")) { - pdata->sec_mi2s_mode = I2S_PCM_MASTER_MODE; - } else if (!strcmp(val_array[SEC_MI2S_PCM], "sec_mi2s_slave")) { - pdata->sec_mi2s_mode = I2S_PCM_SLAVE_MODE; - } else { - dev_err(cdev, "%s: invalid DT intf mode\n", - __func__); - ret = -EINVAL; - } - } -err_intf: - return ret; -} - -static int mdm_populate_auxpcm_interface_mode( - struct snd_soc_card *card) -{ - int size, ret = 0; - struct device *cdev = card->dev; - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - const char *val_array[MI2S_PCM_MAX_INTF]; - - size = of_property_read_string_array(cdev->of_node, - "qcom,auxpcm-interface-mode", - val_array, MI2S_PCM_MAX_INTF); - if (size <= 0) { - dev_info(cdev, "%s: Looking up %s property in node %s failed", - __func__, "qcom,auxpcm-interface-mode", - cdev->of_node->full_name); - pdata->prim_auxpcm_mode = I2S_PCM_MASTER_MODE; - pdata->sec_auxpcm_mode = I2S_PCM_MASTER_MODE; - } else { - if (!strcmp(val_array[PRI_MI2S_PCM], "pri_pcm_master")) { - pdata->prim_auxpcm_mode = I2S_PCM_MASTER_MODE; - } else if (!strcmp(val_array[PRI_MI2S_PCM], "pri_pcm_slave")) { - pdata->prim_auxpcm_mode = I2S_PCM_SLAVE_MODE; - } else { - dev_err(cdev, "%s: invalid DT intf mode\n", - __func__); - ret = -EINVAL; - goto err_intf; - } - if (!strcmp(val_array[SEC_MI2S_PCM], "sec_pcm_master")) { - pdata->sec_auxpcm_mode = I2S_PCM_MASTER_MODE; - } else if (!strcmp(val_array[SEC_MI2S_PCM], "sec_pcm_slave")) { - pdata->sec_auxpcm_mode = I2S_PCM_SLAVE_MODE; - } else { - dev_err(cdev, "%s: invalid DT intf mode\n", - __func__); - ret = -EINVAL; - } - } -err_intf: - return ret; -} - -static int mdm_asoc_machine_probe(struct platform_device *pdev) -{ - int ret; - struct mdm_machine_data *pdata; - struct snd_soc_card *card = &snd_soc_card_mdm; - - if (!pdev->dev.of_node) { - dev_err(&pdev->dev, - "%s No platform supplied from device tree\n", __func__); - - return -EINVAL; - } - pdata = devm_kzalloc(&pdev->dev, sizeof(struct mdm_machine_data), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - /* At present only 12.288MHz is supported on MDM. */ - pdata->mclk_freq = MDM_MCLK_CLK_12P288MHZ; - if (q6afe_check_osr_clk_freq(pdata->mclk_freq)) { - dev_err(&pdev->dev, "%s Unsupported mclk freq %u\n", - __func__, pdata->mclk_freq); - - ret = -EINVAL; - goto err; - } - - atomic_set(&mi2s_ref_count, 0); - atomic_set(&sec_mi2s_ref_count, 0); - pdata->prim_clk_usrs = 0; - - card->dev = &pdev->dev; - platform_set_drvdata(pdev, card); - snd_soc_card_set_drvdata(card, pdata); - - ret = snd_soc_of_parse_card_name(card, "qcom,model"); - if (ret) - goto err; - - ret = mdm_populate_mi2s_interface_mode(card); - if (ret) - goto err; - - ret = mdm_populate_auxpcm_interface_mode(card); - if (ret) - goto err; - - ret = mdm_populate_dai_link_component_of_node(card); - if (ret) { - ret = -EPROBE_DEFER; - goto err; - } - - ret = snd_soc_register_card(card); - if (ret == -EPROBE_DEFER) { - goto err; - } else if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - goto err; - } - - lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4); - if (lpaif_pri_muxsel_virt_addr == NULL) { - pr_err("%s Pri muxsel virt addr is null\n", __func__); - - ret = -EINVAL; - goto err; - } - lpass_gpio_mux_spkr_ctl_virt_addr = - ioremap(LPASS_CSR_GP_IO_MUX_SPKR_CTL, 4); - if (lpass_gpio_mux_spkr_ctl_virt_addr == NULL) { - pr_err("%s lpass spkr ctl virt addr is null\n", __func__); - - ret = -EINVAL; - goto err1; - } - - lpaif_sec_muxsel_virt_addr = ioremap(LPAIF_SEC_MODE_MUXSEL, 4); - if (lpaif_sec_muxsel_virt_addr == NULL) { - pr_err("%s Sec muxsel virt addr is null\n", __func__); - ret = -EINVAL; - goto err2; - } - - lpass_gpio_mux_mic_ctl_virt_addr = - ioremap(LPASS_CSR_GP_IO_MUX_MIC_CTL, 4); - if (lpass_gpio_mux_mic_ctl_virt_addr == NULL) { - pr_err("%s lpass_gpio_mux_mic_ctl_virt_addr is null\n", - __func__); - ret = -EINVAL; - goto err3; - } - - return 0; -err3: - iounmap(lpaif_sec_muxsel_virt_addr); -err2: - iounmap(lpass_gpio_mux_spkr_ctl_virt_addr); -err1: - iounmap(lpaif_pri_muxsel_virt_addr); -err: - devm_kfree(&pdev->dev, pdata); - return ret; -} - -static int mdm_asoc_machine_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); - - pdata->mclk_freq = 0; - iounmap(lpaif_pri_muxsel_virt_addr); - iounmap(lpass_gpio_mux_spkr_ctl_virt_addr); - iounmap(lpaif_sec_muxsel_virt_addr); - iounmap(lpass_gpio_mux_mic_ctl_virt_addr); - snd_soc_unregister_card(card); - - return 0; -} - -static const struct of_device_id mdm_asoc_machine_of_match[] = { - { .compatible = "qcom,mdm-audio-auto", }, - {}, -}; - -static struct platform_driver mdm_asoc_machine_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .pm = &snd_soc_pm_ops, - .of_match_table = mdm_asoc_machine_of_match, - }, - .probe = mdm_asoc_machine_probe, - .remove = mdm_asoc_machine_remove, -}; - -static int dummy_machine_probe(struct platform_device *pdev) -{ - return 0; -} - -static int dummy_machine_remove(struct platform_device *pdev) -{ - return 0; -} - -static struct platform_device dummy_machine_device = { - .name = "dummymachinedriverauto", -}; - -static struct platform_driver mdm_asoc_machine_dummy_driver = { - .driver = { - .name = "dummymachinedriverauto", - .owner = THIS_MODULE, - }, - .probe = dummy_machine_probe, - .remove = dummy_machine_remove, -}; - -static int mdm_adsp_state_callback(struct notifier_block *nb, - unsigned long value, void *priv) -{ - if (!dummy_device_registered && SUBSYS_AFTER_POWERUP == value) { - platform_driver_register(&mdm_asoc_machine_dummy_driver); - platform_device_register(&dummy_machine_device); - dummy_device_registered = true; - } - - return NOTIFY_OK; -} - -static struct notifier_block adsp_state_notifier_block = { - .notifier_call = mdm_adsp_state_callback, - .priority = -INT_MAX, -}; - -static int __init mdm_soc_platform_init(void) -{ - adsp_state_notifier = subsys_notif_register_notifier("modem", - &adsp_state_notifier_block); - return 0; -} - -module_init(mdm_soc_platform_init); - -module_platform_driver(mdm_asoc_machine_driver); - -MODULE_DESCRIPTION("ALSA SoC msm"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" MDM9650_MACHINE_DRV_NAME); -MODULE_DEVICE_TABLE(of, mdm9650_asoc_machine_of_match); diff --git a/sound/soc/msm/mdm9650.c b/sound/soc/msm/mdm9650.c index cdf2c7e3a461..34a8c7a1ec81 100644 --- a/sound/soc/msm/mdm9650.c +++ b/sound/soc/msm/mdm9650.c @@ -34,6 +34,7 @@ #include "../codecs/wcd9xxx-common.h" #include "../codecs/wcd9335.h" #include "../codecs/wsa881x.h" +#include "../codecs/tlv320aic3x.h" /* Spk control */ #define MDM_SPK_ON 1 @@ -77,7 +78,7 @@ #define CLOCK_OFF 0 /* Machine driver Name*/ -#define DRV_NAME "mdm9650-asoc-tasha" +#define DRV_NAME "mdm9650-asoc-snd" enum mi2s_pcm_mux { PRI_MI2S_PCM, @@ -229,6 +230,7 @@ static int mdm_set_lpass_clk_v1(struct snd_soc_pcm_runtime *rtd, u16 mode) { struct snd_soc_card *card = rtd->card; + struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); struct afe_clk_cfg lpass_clks = lpass_default; int bit_clk_freq = (rate * 2 * NO_OF_BITS_PER_SAMPLE); int ret; @@ -236,10 +238,19 @@ static int mdm_set_lpass_clk_v1(struct snd_soc_pcm_runtime *rtd, dev_dbg(card->dev, "%s: Setting clock using v1\n", __func__); if (mode) { - lpass_clks.clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID; - lpass_clks.clk_val1 = bit_clk_freq; - if (!enable) + /* enable mclk for the automotive card */ + if (!strcmp(card->name, "mdm-auto-i2s-snd-card")) + lpass_clks.clk_set_mode = Q6AFE_LPASS_MODE_BOTH_VALID; + else + lpass_clks.clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID; + + if (enable) { + lpass_clks.clk_val1 = bit_clk_freq; + lpass_clks.clk_val2 = pdata->mclk_freq; + } else { lpass_clks.clk_val1 = Q6AFE_LPASS_IBIT_CLK_DISABLE; + lpass_clks.clk_val2 = Q6AFE_LPASS_OSR_CLK_DISABLE; + } ret = afe_set_lpass_clock(mi2s_port, &lpass_clks); if (ret < 0) { @@ -440,7 +451,8 @@ static int mdm_mi2s_startup(struct snd_pcm_substream *substream) goto err; } ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_I2S); if (ret < 0) { mdm_mi2s_clk_ctl(rtd, false, 0, pdata->prim_mi2s_mode); @@ -448,6 +460,12 @@ static int mdm_mi2s_startup(struct snd_pcm_substream *substream) "%s Set fmt for codec dai failed\n", __func__); } + ret = snd_soc_dai_set_sysclk(codec_dai, + CLKIN_MCLK, + pdata->mclk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s Set sysclk for codec dai failed\n", + __func__); } else { /* * Disable bit clk in slave mode for QC codec. @@ -1499,6 +1517,27 @@ done: return ret; } +static int mdm_mi2s_audrx_init_auto(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + pr_debug("%s dev_name %s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + ret = snd_soc_add_codec_controls(codec, mdm_snd_controls, + ARRAY_SIZE(mdm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, %d\n", + __func__, ret); + goto done; + } + +done: + return ret; +} + static void *def_tasha_mbhc_cal(void) { void *tasha_wcd_cal; @@ -1913,37 +1952,6 @@ static struct snd_soc_dai_link mdm_dai[] = { .ignore_pmdown_time = 1, }, /* Backend DAI Links */ - { - .name = LPASS_BE_PRI_MI2S_RX, - .stream_name = "Primary MI2S Playback", - .cpu_dai_name = "msm-dai-q6-mi2s.0", - .platform_name = "msm-pcm-routing", - .codec_name = "tasha_codec", - .codec_dai_name = "tasha_i2s_rx1", - .no_pcm = 1, - .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, - .init = &mdm_mi2s_audrx_init, - .be_hw_params_fixup = &mdm_mi2s_rx_be_hw_params_fixup, - .ops = &mdm_mi2s_be_ops, - .ignore_pmdown_time = 1, - .ignore_suspend = 1, - }, - { - .name = LPASS_BE_PRI_MI2S_TX, - .stream_name = "Primary MI2S Capture", - .cpu_dai_name = "msm-dai-q6-mi2s.0", - .platform_name = "msm-pcm-routing", - .codec_name = "tasha_codec", - .codec_dai_name = "tasha_i2s_tx1", - .no_pcm = 1, - .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, - .be_hw_params_fixup = &mdm_mi2s_tx_be_hw_params_fixup, - .ops = &mdm_mi2s_be_ops, - .ignore_pmdown_time = 1, - .ignore_suspend = 1, - }, { .name = LPASS_BE_AFE_PCM_RX, .stream_name = "AFE Playback", @@ -2101,10 +2109,86 @@ static struct snd_soc_dai_link mdm_dai[] = { }, }; -static struct snd_soc_card snd_soc_card_mdm = { +static struct snd_soc_dai_link mdm_tasha_dai[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_i2s_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .init = &mdm_mi2s_audrx_init, + .be_hw_params_fixup = &mdm_mi2s_rx_be_hw_params_fixup, + .ops = &mdm_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_i2s_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = &mdm_mi2s_tx_be_hw_params_fixup, + .ops = &mdm_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link mdm_auto_dai[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "tlv320aic3x-codec", + .codec_dai_name = "tlv320aic3x-hifi", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .init = &mdm_mi2s_audrx_init_auto, + .be_hw_params_fixup = &mdm_mi2s_rx_be_hw_params_fixup, + .ops = &mdm_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "tlv320aic3x-codec", + .codec_dai_name = "tlv320aic3x-hifi", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = &mdm_mi2s_tx_be_hw_params_fixup, + .ops = &mdm_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link *mdm_tasha_dai_links; + +static struct snd_soc_dai_link *mdm_auto_dai_links; + +static struct snd_soc_card snd_soc_card_mdm_tasha = { .name = "mdm-tasha-i2s-snd-card", - .dai_link = mdm_dai, - .num_links = ARRAY_SIZE(mdm_dai), +}; + +static struct snd_soc_card snd_soc_card_mdm_auto = { + .name = "mdm-auto-i2s-snd-card", }; static int mdm_populate_dai_link_component_of_node( @@ -2357,12 +2441,68 @@ static int mdm_init_wsa_dev(struct platform_device *pdev, return 0; } +static const struct of_device_id mdm_asoc_machine_of_match[] = { + { .compatible = "qcom,mdm-audio-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,mdm-audio-auto", + .data = "auto_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + const struct of_device_id *match; + int len_1, len_2; + + match = of_match_node(mdm_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "tasha_codec")) { + len_1 = ARRAY_SIZE(mdm_dai); + len_2 = len_1 + ARRAY_SIZE(mdm_tasha_dai); + mdm_tasha_dai_links = devm_kcalloc(dev, len_2, + sizeof(*mdm_tasha_dai_links), GFP_KERNEL); + if (!mdm_tasha_dai_links) + return NULL; + card = &snd_soc_card_mdm_tasha; + memcpy(mdm_tasha_dai_links, mdm_dai, sizeof(mdm_dai)); + memcpy(mdm_tasha_dai_links + len_1, mdm_tasha_dai, + sizeof(mdm_tasha_dai)); + dailink = mdm_tasha_dai_links; + } else if (!strcmp(match->data, "auto_codec")) { + len_1 = ARRAY_SIZE(mdm_dai); + len_2 = len_1 + ARRAY_SIZE(mdm_auto_dai); + mdm_auto_dai_links = devm_kcalloc(dev, len_2, + sizeof(*mdm_auto_dai_links), GFP_KERNEL); + if (!mdm_auto_dai_links) + return NULL; + card = &snd_soc_card_mdm_auto; + memcpy(mdm_auto_dai_links, mdm_dai, sizeof(mdm_dai)); + memcpy(mdm_auto_dai_links + len_1, mdm_auto_dai, + sizeof(mdm_auto_dai)); + dailink = mdm_auto_dai_links; + } + + if (card) { + card->dai_link = dailink; + card->num_links = len_2; + } + + return card; +} static int mdm_asoc_machine_probe(struct platform_device *pdev) { int ret; struct mdm_machine_data *pdata; - struct snd_soc_card *card = &snd_soc_card_mdm; + struct snd_soc_card *card; + const struct of_device_id *match; if (!pdev->dev.of_node) { dev_err(&pdev->dev, @@ -2370,22 +2510,35 @@ static int mdm_asoc_machine_probe(struct platform_device *pdev) return -EINVAL; } + + match = of_match_node(mdm_asoc_machine_of_match, pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: No DT match found for sound card\n", + __func__); + return -EINVAL; + } + pdata = devm_kzalloc(&pdev->dev, sizeof(struct mdm_machine_data), GFP_KERNEL); if (!pdata) return -ENOMEM; - ret = of_property_read_u32(pdev->dev.of_node, - "qcom,tasha-mclk-clk-freq", - &pdata->mclk_freq); - if (ret) { - dev_err(&pdev->dev, - "%s Looking up %s property in node %s failed", - __func__, "qcom,tasha-mclk-clk-freq", - pdev->dev.of_node->full_name); + if (!strcmp(match->data, "tasha_codec")) { + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,tasha-mclk-clk-freq", + &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "%s Looking up %s property in node %s failed", + __func__, "qcom,tasha-mclk-clk-freq", + pdev->dev.of_node->full_name); - goto err; + goto err; + } + } else { + pdata->mclk_freq = MDM_MCLK_CLK_12P288MHZ; } + /* At present only 12.288MHz is supported on MDM. */ if (q6afe_check_osr_clk_freq(pdata->mclk_freq)) { dev_err(&pdev->dev, "%s Unsupported tasha mclk freq %u\n", @@ -2408,6 +2561,13 @@ static int mdm_asoc_machine_probe(struct platform_device *pdev) atomic_set(&sec_mi2s_ref_count, 0); pdata->prim_clk_usrs = 0; + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, pdata); @@ -2415,18 +2575,23 @@ static int mdm_asoc_machine_probe(struct platform_device *pdev) ret = snd_soc_of_parse_card_name(card, "qcom,model"); if (ret) goto err; - ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); - if (ret) - goto err; + if (of_property_read_bool(pdev->dev.of_node, "qcom,audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, + "qcom,audio-routing"); + if (ret) + goto err; + } ret = mdm_populate_dai_link_component_of_node(card); if (ret) { ret = -EPROBE_DEFER; goto err; } - ret = mdm_init_wsa_dev(pdev, card); - if (ret) - goto err; + if (!strcmp(match->data, "tasha_codec")) { + ret = mdm_init_wsa_dev(pdev, card); + if (ret) + goto err; + } ret = snd_soc_register_card(card); if (ret == -EPROBE_DEFER) { @@ -2497,11 +2662,6 @@ static int mdm_asoc_machine_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id mdm_asoc_machine_of_match[] = { - { .compatible = "qcom,mdm-audio-tasha", }, - {}, -}; - static struct platform_driver mdm_asoc_machine_driver = { .driver = { .name = DRV_NAME, -- GitLab From 27a4a6862234689e76895221194965e984bc90e0 Mon Sep 17 00:00:00 2001 From: Mohit Aggarwal Date: Thu, 6 Jul 2017 10:16:52 +0530 Subject: [PATCH 0795/5498] diag: Add protection while de-initializing clients Currently, while de-initializing clients, there is a possibility of using already freed memory. The patch adds proper protection to fix the issue. CRs-Fixed: 2068569 Change-Id: I4b397a82e03fa2f1c84cfa8ca912cdb6a51ba08b Signed-off-by: Mohit Aggarwal --- drivers/char/diag/diagchar_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 9f46c452ee0b..e3903a580a7f 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -1747,14 +1747,18 @@ static int diag_ioctl_lsm_deinit(void) { int i; + mutex_lock(&driver->diagchar_mutex); for (i = 0; i < driver->num_clients; i++) if (driver->client_map[i].pid == current->tgid) break; - if (i == driver->num_clients) + if (i == driver->num_clients) { + mutex_unlock(&driver->diagchar_mutex); return -EINVAL; + } driver->data_ready[i] |= DEINIT_TYPE; + mutex_unlock(&driver->diagchar_mutex); wake_up_interruptible(&driver->wait_q); return 1; -- GitLab From 9135d0d56667ff5742e7a6b8230868c7cf638d26 Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Thu, 31 Aug 2017 16:37:34 +0530 Subject: [PATCH 0796/5498] Revert "Revert "msm: mdss: enable pll regulator during phy on sequence"" This reverts commit 0a4917a2b6aca2f87dbf1b4a770ffb688bd0924e. This change is causing corruption issue on msm8996 dual dsi cmd mode panel. Change-Id: Iba316b22c7cc211c384f5b5204e0b6f11d169d8a Signed-off-by: Raghavendra Ambadas --- drivers/video/msm/mdss/msm_mdss_io_8974.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c index 8a1b72d79335..4a43949e634f 100644 --- a/drivers/video/msm/mdss/msm_mdss_io_8974.c +++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c @@ -899,8 +899,11 @@ static void mdss_dsi_8996_phy_power_off( { int ln; void __iomem *base; + u32 data; - MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0x7f); + /* Turn off PLL power */ + data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0); + MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data & ~BIT(7)); /* 4 lanes + clk lane configuration */ for (ln = 0; ln < 5; ln++) { @@ -956,6 +959,7 @@ static void mdss_dsi_8996_phy_power_on( void __iomem *base; struct mdss_dsi_phy_ctrl *pd; char *ip; + u32 data; pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); @@ -975,6 +979,10 @@ static void mdss_dsi_8996_phy_power_on( } mdss_dsi_8996_phy_regulator_enable(ctrl); + + /* Turn on PLL power */ + data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0); + MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data | BIT(7)); } static void mdss_dsi_phy_power_on( @@ -1078,6 +1086,7 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl) mdss_dsi_8996_pll_source_standalone(ctrl); } + MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0x7f); wmb(); /* make sure registers committed */ } -- GitLab From 5659ae5aff0cf3becd02e6cb4cd48531c3c43b8f Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Tue, 12 Apr 2016 18:20:10 -0700 Subject: [PATCH 0797/5498] msm: mdss: modify DSI phy init sequence for split-DSI config For split-DSI hardware configuration, both the DSI controller clocks are sourced from a single PLL (clock-master). In such cases, it is important to initialize both DSI0 PHY and DSI1 PHY prior to enabling the PLL. This is due to the fact that for certain HW versions, PLL programming for the clock-master may require configure some PLL registers on the clock-slave. If the PHY init sequence for the clock-slave is called after PLL is programmed, it could reset those PLL registers leading to unexpected behavior. Fix this by ensuring that PHY init sequence is done for both controllers at the same time for split display usecases. CRs-Fixed: 1000724 Change-Id: I09fb8097d31cd0390cea5c32bb7aabceeff2c37e Signed-off-by: Aravind Venkateswaran Signed-off-by: Ashish Garg --- drivers/video/msm/mdss/msm_mdss_io_8974.c | 60 ++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c index 4a43949e634f..fcc8545a12ed 100644 --- a/drivers/video/msm/mdss/msm_mdss_io_8974.c +++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c @@ -443,7 +443,7 @@ int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl) return rc; } -void mdss_dsi_phy_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl) +static void mdss_dsi_phy_sw_reset_sub(struct mdss_dsi_ctrl_pdata *ctrl) { struct mdss_dsi_ctrl_pdata *sctrl = NULL; struct dsi_shared_data *sdata; @@ -500,7 +500,39 @@ void mdss_dsi_phy_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl) } mutex_unlock(&sdata->phy_reg_lock); +} + +void mdss_dsi_phy_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct mdss_dsi_ctrl_pdata *sctrl = NULL; + struct dsi_shared_data *sdata; + + if (ctrl == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + sdata = ctrl->shared_data; + + /* + * When operating in split display mode, make sure that the PHY reset + * is only done from the clock master. This will ensure that the PLL is + * off when PHY reset is called. + */ + if (mdss_dsi_is_ctrl_clk_slave(ctrl)) + return; + + mdss_dsi_phy_sw_reset_sub(ctrl); + if (mdss_dsi_is_ctrl_clk_master(ctrl)) { + sctrl = mdss_dsi_get_ctrl_clk_slave(); + if (sctrl) + mdss_dsi_phy_sw_reset_sub(sctrl); + else + pr_warn("%s: unable to get slave ctrl\n", __func__); + } + + /* All other quirks go here */ if ((sdata->hw_rev == MDSS_DSI_HW_REV_103) && !mdss_dsi_is_hw_config_dual(sdata) && mdss_dsi_is_right_ctrl(ctrl)) { @@ -1202,12 +1234,36 @@ void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl) wmb(); } -void mdss_dsi_phy_init(struct mdss_dsi_ctrl_pdata *ctrl) +static void mdss_dsi_phy_init_sub(struct mdss_dsi_ctrl_pdata *ctrl) { mdss_dsi_phy_regulator_ctrl(ctrl, true); mdss_dsi_phy_ctrl(ctrl, true); } +void mdss_dsi_phy_init(struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct mdss_dsi_ctrl_pdata *sctrl = NULL; + + /* + * When operating in split display mode, make sure that both the PHY + * blocks are initialized together prior to the PLL being enabled. This + * is achieved by calling the phy_init function for the clk_slave from + * the clock_master. + */ + if (mdss_dsi_is_ctrl_clk_slave(ctrl)) + return; + + mdss_dsi_phy_init_sub(ctrl); + + if (mdss_dsi_is_ctrl_clk_master(ctrl)) { + sctrl = mdss_dsi_get_ctrl_clk_slave(); + if (sctrl) + mdss_dsi_phy_init_sub(sctrl); + else + pr_warn("%s: unable to get slave ctrl\n", __func__); + } +} + void mdss_dsi_core_clk_deinit(struct device *dev, struct dsi_shared_data *sdata) { if (sdata->mmss_misc_ahb_clk) -- GitLab From 8f88cd68ce101925238dbbcc745d1fb726b4f02b Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Tue, 25 Jul 2017 13:03:27 +0530 Subject: [PATCH 0798/5498] msm: kgsl: Disable VPC all flat shading optimization for A5XX Set ALLFLATOPTDIS bit in A5XX_VPC_DBG_ECO_CNTL register to disable VPC all flat shading optimization. ALLFLATOPTDIS is a hardware chicken bit value needs to be set one, to avoid UI rendering corruption. Change-Id: I39b5ad098daacbb1c3b0fc93963d82d477f70cc1 Signed-off-by: Hareesh Gundu --- drivers/gpu/msm/adreno_a5xx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 97b571b7a7d1..da0ffa83fdd7 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -2026,6 +2026,9 @@ static void a5xx_start(struct adreno_device *adreno_dev) } + /* Disable All flat shading optimization */ + kgsl_regrmw(device, A5XX_VPC_DBG_ECO_CNTL, 0, 0x1 << 10); + /* * VPC corner case with local memory load kill leads to corrupt * internal state. Normal Disable does not work for all a5x chips. -- GitLab From bf83f4da119b7b6409bd5ea59bdb564ba5125578 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Thu, 31 Aug 2017 11:29:11 +0530 Subject: [PATCH 0799/5498] spmi: pmic-arb: Correct the mapping table size spmi pmic arbiter V2 supports 256 peripherals. But the size of the spmi peripheral mapping table in the driver is defined as 255, which leads to the buffer overflow. Fix it by correcting the mapping table size to 256. Change-Id: I096e35bae470a56af94edca98e3e793cf3ec1751 Signed-off-by: Kiran Gunda --- drivers/spmi/spmi-pmic-arb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index d7119db49cfe..01436b0889b7 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -47,7 +47,7 @@ #define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) #define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) -#define SPMI_MAPPING_TABLE_LEN 255 +#define SPMI_MAPPING_TABLE_LEN 256 #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ #define PPID_TO_CHAN_TABLE_SZ BIT(12) /* PPID is 12bit chan is 1byte*/ -- GitLab From 14e223fdb4031e25df37f821406b78bf755be52d Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 9 Aug 2017 12:24:03 -0700 Subject: [PATCH 0800/5498] drivers: swr-wcd-ctrl: Fix wsa mute at boot Replace pm resume and suspend inside interrupt with clk enable and disable. Add clk ref count to not have the clk disabled when in use. CRs-Fixed: 2079739 Change-Id: I8f7f6e8e3c7cf8fd176a708ba5171a98546db929 Signed-off-by: Vatsal Bucha --- drivers/soundwire/swr-wcd-ctrl.c | 24 ++++++++++++++++++------ drivers/soundwire/swr-wcd-ctrl.h | 3 ++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c index 3a4e3a2912f0..7f1e0a96bcd9 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -373,11 +373,17 @@ static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable) return -EINVAL; if (enable) { - swrm->clk(swrm->handle, true); - swrm->state = SWR_MSTR_UP; - } else { + swrm->clk_ref_count++; + if (swrm->clk_ref_count == 1) { + swrm->clk(swrm->handle, true); + swrm->state = SWR_MSTR_UP; + } + } else if (--swrm->clk_ref_count == 0) { swrm->clk(swrm->handle, false); swrm->state = SWR_MSTR_DOWN; + } else if (swrm->clk_ref_count < 0) { + pr_err("%s: swrm clk count mismatch\n", __func__); + swrm->clk_ref_count = 0; } return 0; } @@ -1136,7 +1142,10 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev) u8 devnum = 0; int ret = IRQ_HANDLED; - pm_runtime_get_sync(&swrm->pdev->dev); + mutex_lock(&swrm->reslock); + swrm_clk_request(swrm, true); + mutex_unlock(&swrm->reslock); + intr_sts = swrm->read(swrm->handle, SWRM_INTERRUPT_STATUS); intr_sts &= SWRM_INTERRUPT_STATUS_RMSK; for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { @@ -1224,8 +1233,10 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev) break; } } - pm_runtime_mark_last_busy(&swrm->pdev->dev); - pm_runtime_put_autosuspend(&swrm->pdev->dev); + + mutex_lock(&swrm->reslock); + swrm_clk_request(swrm, false); + mutex_unlock(&swrm->reslock); return ret; } @@ -1416,6 +1427,7 @@ static int swrm_probe(struct platform_device *pdev) swrm->wcmd_id = 0; swrm->slave_status = 0; swrm->num_rx_chs = 0; + swrm->clk_ref_count = 0; swrm->state = SWR_MSTR_RESUME; init_completion(&swrm->reset); init_completion(&swrm->broadcast); diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h index 8992318cdbd3..57327cb87bcc 100644 --- a/drivers/soundwire/swr-wcd-ctrl.h +++ b/drivers/soundwire/swr-wcd-ctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -74,6 +74,7 @@ struct swr_mstr_ctrl { struct device *dev; struct resource *supplies; struct clk *mclk; + int clk_ref_count; struct completion reset; struct completion broadcast; struct mutex mlock; -- GitLab From 0a6a4273f4920b98e97d9a95f174eaffd86cc876 Mon Sep 17 00:00:00 2001 From: Shadab Naseem Date: Fri, 8 Sep 2017 17:06:25 +0530 Subject: [PATCH 0801/5498] defconfig: msm: Disable UART console for msm8909w perf build Disable UART console for msm8909w perf build. Change-Id: Ifb3d9403d2ecfb257444ed0586fdcb7141115621 Signed-off-by: Shadab Naseem --- arch/arm/configs/msm8909w-perf_defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 2ba1e56a21e0..4d4c42aeea01 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -263,8 +263,6 @@ CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH=y # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVMEM is not set # CONFIG_DEVKMEM is not set -CONFIG_SERIAL_MSM_HSL=y -CONFIG_SERIAL_MSM_HSL_CONSOLE=y CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y -- GitLab From cbcd422bfdc702dbc99f9e808d9f498999e9a3dd Mon Sep 17 00:00:00 2001 From: Tanvi Aggarwal Date: Thu, 24 Aug 2017 11:51:35 +0530 Subject: [PATCH 0802/5498] msm: sensor: flash: add conditional check for ioctl Add conditional check when sending VIDIOC_MSM_FLASH_CFG in 32-bit process. Change-Id: I73bcce85a212495ce94e6265947c11a6bc0e4040 CRs-Fixed: 2092793 Signed-off-by: Tanvi Aggarwal --- drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c index 88d99fd7fd6a..1670fa315800 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c @@ -1089,6 +1089,9 @@ static long msm_flash_subdev_do_ioctl( break; } break; + case VIDIOC_MSM_FLASH_CFG: + pr_err("invalid cmd 0x%x received\n", cmd); + return -EINVAL; default: return msm_flash_subdev_ioctl(sd, cmd, arg); } -- GitLab From c28591e779733150ee09bc18292750b9ee85d9b7 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Fri, 8 Sep 2017 18:51:43 +0530 Subject: [PATCH 0803/5498] msm: ais: Return -NOTTY on invalid ioctl command Check validity of command before processing. Change-Id: Iecd66b90922f8ed4b7d8d50f7c3d7f27d5d93309 CR-fixed: 2083314 Signed-off-by: Rahul Sharma --- drivers/media/platform/msm/ais/msm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c index f6d6a45c2988..dba9b0ac6b6a 100644 --- a/drivers/media/platform/msm/ais/msm.c +++ b/drivers/media/platform/msm/ais/msm.c @@ -730,6 +730,16 @@ static long msm_private_ioctl(struct file *file, void *fh, if (!event_data) return -EINVAL; + switch (cmd) { + case MSM_CAM_V4L2_IOCTL_NOTIFY: + case MSM_CAM_V4L2_IOCTL_CMD_ACK: + case MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG: + case MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR: + break; + default: + return -ENOTTY; + } + memset(&event, 0, sizeof(struct v4l2_event)); session_id = event_data->session_id; stream_id = event_data->stream_id; -- GitLab From a5a134120987e15976869c160bab9d6ac6e4f070 Mon Sep 17 00:00:00 2001 From: Dennis Cagle Date: Fri, 8 Sep 2017 11:02:41 -0700 Subject: [PATCH 0804/5498] acl: Reapply previous fix for leaks Merge commit ad95c945ce4b ("Merge tag 'google-common-android-3.18' into 'msm-3.18'") inadvertantly reverted portions of commit 4333a7d3c06b ("ANDROID: fix acl leaks"). Reapply the portions of the fix that were reverted. Change-Id: I5773b28bb68d31deecf374b480a38622c59d8e9c Signed-off-by: Dennis Cagle --- fs/gfs2/acl.c | 3 ++- fs/xfs/xfs_acl.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 581bf4de1b1a..6282f3de36e6 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -81,7 +81,8 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) struct posix_acl *old_acl = acl; error = posix_acl_update_mode(inode, &inode->i_mode, &acl); - error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + if (!acl) + posix_acl_release(old_acl); if (error) return error; if (mode != inode->i_mode) diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c index e0406717edbc..c5bda4bf69c7 100644 --- a/fs/xfs/xfs_acl.c +++ b/fs/xfs/xfs_acl.c @@ -287,8 +287,10 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) if (type == ACL_TYPE_ACCESS) { umode_t mode; - + struct posix_acl *old_acl = acl; error = posix_acl_update_mode(inode, &mode, &acl); + if (!acl) + posix_acl_release(old_acl); if (error) return error; error = xfs_set_mode(inode, mode); -- GitLab From 2c24aab6252d7b5d701410a846b2b443f30f9b89 Mon Sep 17 00:00:00 2001 From: Siba Prasad Date: Mon, 4 Sep 2017 18:09:14 +0530 Subject: [PATCH 0805/5498] block: test-iosched: Restrict number of bios in the test request num_bios is passed in test_iosched_add_wr_rd_test_req(). Restrict the number of bios to bios buffer size while preparing test request. Otherwise, it may lead to the buffer overflow if num_bios is larger than the size of bios buffer. Change-Id: Ib523f2b5bd91fea7b24f3b1260ebdcb1a1b7b9ab Signed-off-by: Siba Prasad --- block/test-iosched.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/block/test-iosched.c b/block/test-iosched.c index 2a0be42dd930..eff1043c9992 100644 --- a/block/test-iosched.c +++ b/block/test-iosched.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -316,6 +316,13 @@ struct test_request *test_iosched_create_test_req( goto err; } + /* Restrict num_bios value as it may lead to bios_buffer overflow */ + if (num_bios >= BLK_MAX_SEGMENTS) { + pr_warn("%s: num_bios %d is changed to BLK_MAX_SEGMENTS\n", + __func__, num_bios); + num_bios = BLK_MAX_SEGMENTS; + } + test_rq->buf_size = TEST_BIO_SIZE * num_bios; test_rq->wr_rd_data_pattern = pattern; -- GitLab From 32454a73ad17d3f29f46b0a4f9c20ed33a1f7da0 Mon Sep 17 00:00:00 2001 From: Abhilash Kumar Date: Tue, 8 Aug 2017 11:27:10 +0530 Subject: [PATCH 0806/5498] msm: kgsl: Correctly update fence_name for larger timestamp Since 'timestamp' is of unsigned int type, for larger timestamp the format specifier %d can show negative values. Fix this by using %u format specifier for timestamp. Change-Id: I621dfd4843099cb27436006500fe3342d1d5ddf4 Signed-off-by: Abhilash Kumar --- drivers/gpu/msm/kgsl_sync.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c index 5c3ae1bf62c4..dab7852e77b2 100644 --- a/drivers/gpu/msm/kgsl_sync.c +++ b/drivers/gpu/msm/kgsl_sync.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -180,7 +180,7 @@ int kgsl_add_fence_event(struct kgsl_device *device, goto out; } snprintf(fence_name, sizeof(fence_name), - "%s-pid-%d-ctx-%d-ts-%d", + "%s-pid-%d-ctx-%d-ts-%u", device->name, current->group_leader->pid, context_id, timestamp); -- GitLab From 98ecd7f70418128bd968ea496142cf7691ec7ab9 Mon Sep 17 00:00:00 2001 From: Manaf Meethalavalappu Pallikunhi Date: Thu, 7 Sep 2017 00:23:51 +0530 Subject: [PATCH 0807/5498] msm: thermal: Check cpu variables are initialized before setting threshold Userspace thermal daemon initiate KTM hotplug monitor related initialization. Thermal core control can be disabled/enabled from userspace via KTM sysfs for cpu related initialization after boot. There is a possible race condition between KTM hotplug initialization from thermal daemon and KTM core control re-enablement from userpsace shell. When these both events are triggered at the same time, thermal core control enablement tries to set emergency hotplug threshold prior to per cpu hotplug related initialization like sensor id, trip and threshold value etc. This leads to wrong sensor threshold settings and eventually thermal core sensor threshold list will be broken. To avoid this wrong threshold settings during thermal core control enablement, check KTM hotplug related initialization is done prior to threshold setting for each core. Change-Id: I916527d187146d5e292dd57897aa70b21cf87fbc Signed-off-by: Manaf Meethalavalappu Pallikunhi --- drivers/thermal/msm_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index d35bebc147eb..802cc79b8067 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -4865,7 +4865,7 @@ static ssize_t __ref store_cc_enabled(struct kobject *kobj, hotplug_init_cpu_offlined(); mutex_lock(&core_control_mutex); update_offline_cores(cpus_offlined); - if (hotplug_enabled) { + if (hotplug_enabled && hotplug_task) { for_each_possible_cpu(cpu) { if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu))) -- GitLab From 63ecdd5bf284120eb782748c70d2b2c56c08d2b1 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Mon, 11 Sep 2017 15:32:40 +0530 Subject: [PATCH 0808/5498] usb: ci13xxx: Add zptr cleanup code in _ep_nuke Based on req.zero flag controller driver prepares zero length TD and appends it when priming endpoint. If _ep_nuke is invoked before request completion, then req->zptr flag is not cleared. Next time when same usb_request is queued with short packet then ep_queue routine ends up appending zero length TD to it which is not initialized and may not have TERMINATE bit set. This results in EP prime failure. Fix this by clearing and freeing any references of zero length TDs in usb_request on _ep_nuke. Change-Id: I1ba3f383a49af2ef8e2f8bfce37448c14bbf03cc Signed-off-by: Ajay Agarwal --- drivers/usb/gadget/ci13xxx_udc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 02d5cc92c78b..2607129b46b0 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2253,6 +2253,12 @@ static void release_ep_request(struct ci13xxx_ep *mEp, mReq->map = 0; } + if (mReq->zptr) { + dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma); + mReq->zptr = NULL; + mReq->zdma = 0; + } + if (mEp->multi_req) { restore_original_req(mReq); mEp->multi_req = false; @@ -2314,6 +2320,12 @@ __acquires(mEp->lock) release_ep_request(mEp, mReq); } + if (mEp->last_zptr) { + dma_pool_free(mEp->td_pool, mEp->last_zptr, mEp->last_zdma); + mEp->last_zptr = NULL; + mEp->last_zdma = 0; + } + return 0; } @@ -2348,12 +2360,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) _ep_nuke(&udc->ep0in); spin_unlock_irqrestore(udc->lock, flags); - if (udc->ep0in.last_zptr) { - dma_pool_free(udc->ep0in.td_pool, udc->ep0in.last_zptr, - udc->ep0in.last_zdma); - udc->ep0in.last_zptr = NULL; - } - return 0; } @@ -2982,12 +2988,6 @@ static int ep_disable(struct usb_ep *ep) } while (mEp->dir != direction); - if (mEp->last_zptr) { - dma_pool_free(mEp->td_pool, mEp->last_zptr, - mEp->last_zdma); - mEp->last_zptr = NULL; - } - mEp->desc = NULL; mEp->ep.desc = NULL; mEp->ep.maxpacket = USHRT_MAX; -- GitLab From a16db9e03cfdbba6e3ef0cc99579cd7d9b74e030 Mon Sep 17 00:00:00 2001 From: Krishna Manikandan Date: Wed, 26 Jul 2017 14:26:57 +0530 Subject: [PATCH 0809/5498] msm: mdss: Add support for sg_table cloning Add support for cloning of the sg_table and padding it with extra buffer to avoid faults which can occur as a result of a hardware bug that over-fetches. Change-Id: I6f33c0568bab4f97aa6ed063bdd8b898723571d6 Signed-off-by: Franklin S Cooper Jr Signed-off-by: Krishna Manikandan --- drivers/video/msm/mdss/mdp3.c | 50 +++++++++-- drivers/video/msm/mdss/mdp3.h | 3 +- drivers/video/msm/mdss/mdss.h | 2 + drivers/video/msm/mdss/mdss_smmu.c | 131 ++++++++++++++++++++++++++++- drivers/video/msm/mdss/mdss_smmu.h | 14 ++- 5 files changed, 191 insertions(+), 9 deletions(-) diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index 38741d4b41ef..7a64d9aea0c0 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -1834,9 +1834,13 @@ int mdp3_put_img(struct mdp3_img_data *data, int client) return -ENOMEM; } if (data->mapped) { - mdss_smmu_unmap_dma_buf(data->srcp_table, - dom, dir, - data->srcp_dma_buf); + if (client == MDP3_CLIENT_PPP || + client == MDP3_CLIENT_DMA_P) + mdss_smmu_unmap_dma_buf(data->tab_clone, + dom, dir, data->srcp_dma_buf); + else + mdss_smmu_unmap_dma_buf(data->srcp_table, + dom, dir, data->srcp_dma_buf); data->mapped = false; } if (!data->skip_detach) { @@ -1851,6 +1855,10 @@ int mdp3_put_img(struct mdp3_img_data *data, int client) } else { return -EINVAL; } + if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) { + kfree(data->tab_clone->sgl); + kfree(data->tab_clone); + } return 0; } @@ -1913,9 +1921,27 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client) goto err_detach; } - ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf, - data->srcp_table, dom, - &data->addr, &data->len, DMA_BIDIRECTIONAL); + if (client == MDP3_CLIENT_PPP || + client == MDP3_CLIENT_DMA_P) { + data->tab_clone = + mdss_smmu_sg_table_clone(data->srcp_table, + GFP_KERNEL, true); + if (IS_ERR_OR_NULL(data->tab_clone)) { + if (!(data->tab_clone)) + ret = -EINVAL; + else + ret = PTR_ERR(data->tab_clone); + goto clone_err; + } + ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf, + data->tab_clone, dom, + &data->addr, &data->len, + DMA_BIDIRECTIONAL); + } else { + ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf, + data->srcp_table, dom, &data->addr, + &data->len, DMA_BIDIRECTIONAL); + } if (IS_ERR_VALUE(ret)) { pr_err("smmu map dma buf failed: (%d)\n", ret); @@ -1926,6 +1952,10 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client) data->skip_detach = false; } done: + if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) { + data->addr += data->tab_clone->sgl->length; + data->len -= data->tab_clone->sgl->length; + } if (!ret && (img->offset < data->len)) { data->addr += img->offset; data->len -= img->offset; @@ -1940,6 +1970,9 @@ done: } return ret; +clone_err: + dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table, + mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL)); err_detach: dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment); err_put: @@ -1950,6 +1983,11 @@ err_unmap: mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL)); dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment); dma_buf_put(data->srcp_dma_buf); + + if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) { + kfree(data->tab_clone->sgl); + kfree(data->tab_clone); + } return ret; } diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h index 814398c7afcf..c22441691991 100644 --- a/drivers/video/msm/mdss/mdp3.h +++ b/drivers/video/msm/mdss/mdp3.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This program is free software; you can redistribute it and/or modify @@ -233,6 +233,7 @@ struct mdp3_img_data { struct dma_buf *srcp_dma_buf; struct dma_buf_attachment *srcp_attachment; struct sg_table *srcp_table; + struct sg_table *tab_clone; }; extern struct mdp3_hw_resource *mdp3_res; diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h index a79ca4614c81..6846e68fc6eb 100644 --- a/drivers/video/msm/mdss/mdss.h +++ b/drivers/video/msm/mdss/mdss.h @@ -265,6 +265,8 @@ struct mdss_smmu_ops { void (*smmu_dsi_unmap_buffer)(dma_addr_t dma_addr, int domain, unsigned long size, int dir); void (*smmu_deinit)(struct mdss_data_type *mdata); + struct sg_table * (*smmu_sg_table_clone)(struct sg_table *orig_table, + gfp_t gfp_mask, bool padding); }; struct mdss_data_type { diff --git a/drivers/video/msm/mdss/mdss_smmu.c b/drivers/video/msm/mdss/mdss_smmu.c index 67e87c8700e6..e0d0579009a1 100644 --- a/drivers/video/msm/mdss/mdss_smmu.c +++ b/drivers/video/msm/mdss/mdss_smmu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2007-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -480,6 +480,134 @@ static void mdss_smmu_deinit_v2(struct mdss_data_type *mdata) } } +/* + * sg_clone - Duplicate an existing chained sgl + * @orig_sgl: Original sg list to be duplicated + * @len: Total length of sg while taking chaining into account + * @gfp_mask: GFP allocation mask + * @padding: specifies if padding is required + * + * Description: + * Clone a chained sgl. This cloned copy may be modified in some ways while + * keeping the original sgl in tact. Also allow the cloned copy to have + * a smaller length than the original which may reduce the sgl total + * sg entries and also allows cloned copy to have one extra sg entry on + * either sides of sgl. + * + * Returns: + * Pointer to new kmalloced sg list, ERR_PTR() on error + * + */ +static struct scatterlist *sg_clone(struct scatterlist *orig_sgl, u64 len, + gfp_t gfp_mask, bool padding) +{ + int nents; + bool last_entry; + struct scatterlist *sgl, *head; + + nents = sg_nents(orig_sgl); + if (nents < 0) + return ERR_PTR(-EINVAL); + if (padding) + nents += 2; + + head = kmalloc_array(nents, sizeof(struct scatterlist), gfp_mask); + if (!head) + return ERR_PTR(-ENOMEM); + + sgl = head; + + sg_init_table(sgl, nents); + + if (padding) { + *sgl = *orig_sgl; + if (sg_is_chain(orig_sgl)) { + orig_sgl = sg_next(orig_sgl); + *sgl = *orig_sgl; + } + sgl->page_link &= (unsigned long)(~0x03); + sgl = sg_next(sgl); + } + + for (; sgl; orig_sgl = sg_next(orig_sgl), sgl = sg_next(sgl)) { + + last_entry = sg_is_last(sgl); + + /* + * * If page_link is pointing to a chained sgl then set + * the sg entry in the cloned list to the next sg entry + * in the original sg list as chaining is already taken + * care. + */ + + if (sg_is_chain(orig_sgl)) + orig_sgl = sg_next(orig_sgl); + + if (padding) + last_entry = sg_is_last(orig_sgl); + + *sgl = *orig_sgl; + sgl->page_link &= (unsigned long)(~0x03); + + if (last_entry) { + if (padding) { + len -= sg_dma_len(sgl); + sgl = sg_next(sgl); + *sgl = *orig_sgl; + } + sg_dma_len(sgl) = len ? len : SZ_4K; + /* Set bit 1 to indicate end of sgl */ + sgl->page_link |= 0x02; + } else { + len -= sg_dma_len(sgl); + } + } + + return head; +} + +/* + * sg_table_clone - Duplicate an existing sg_table including chained sgl + * @orig_table: Original sg_table to be duplicated + * @len: Total length of sg while taking chaining into account + * @gfp_mask: GFP allocation mask + * @padding: specifies if padding is required + * + * Description: + * Clone a sg_table along with chained sgl. This cloned copy may be + * modified in some ways while keeping the original table and sgl in tact. + * Also allow the cloned sgl copy to have a smaller length than the original + * which may reduce the sgl total sg entries. + * + * Returns: + * Pointer to new kmalloced sg_table, ERR_PTR() on error + * + */ +static struct sg_table *sg_table_clone(struct sg_table *orig_table, + gfp_t gfp_mask, bool padding) +{ + struct sg_table *table; + struct scatterlist *sg = orig_table->sgl; + u64 len = 0; + + for (len = 0; sg; sg = sg_next(sg)) + len += sg->length; + + table = kmalloc(sizeof(struct sg_table), gfp_mask); + if (!table) + return ERR_PTR(-ENOMEM); + + table->sgl = sg_clone(orig_table->sgl, len, gfp_mask, padding); + if (IS_ERR(table->sgl)) { + kfree(table); + return ERR_PTR(-ENOMEM); + } + + table->nents = table->orig_nents = sg_nents(table->sgl); + + return table; +} + static void mdss_smmu_ops_init(struct mdss_data_type *mdata) { mdata->smmu_ops.smmu_attach = mdss_smmu_attach_v2; @@ -501,6 +629,7 @@ static void mdss_smmu_ops_init(struct mdss_data_type *mdata) mdata->smmu_ops.smmu_dsi_unmap_buffer = mdss_smmu_dsi_unmap_buffer_v2; mdata->smmu_ops.smmu_deinit = mdss_smmu_deinit_v2; + mdata->smmu_ops.smmu_sg_table_clone = sg_table_clone; } /* diff --git a/drivers/video/msm/mdss/mdss_smmu.h b/drivers/video/msm/mdss/mdss_smmu.h index 80448806ff9f..658f1eeadcec 100644 --- a/drivers/video/msm/mdss/mdss_smmu.h +++ b/drivers/video/msm/mdss/mdss_smmu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2007-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -292,4 +292,16 @@ static inline void mdss_smmu_deinit(struct mdss_data_type *mdata) mdata->smmu_ops.smmu_deinit(mdata); } +static inline struct sg_table *mdss_smmu_sg_table_clone(struct sg_table + *orig_table, gfp_t gfp_mask, bool padding) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + if (!mdata || !mdata->smmu_ops.smmu_sg_table_clone) + return NULL; + + return mdata->smmu_ops.smmu_sg_table_clone(orig_table, + gfp_mask, padding); +} + #endif /* MDSS_SMMU_H */ -- GitLab From f2229442d030e834c467d9aa5cac65dd28ebede6 Mon Sep 17 00:00:00 2001 From: Srinu Gorle Date: Mon, 11 Sep 2017 18:54:07 +0530 Subject: [PATCH 0810/5498] msm: vidc: Fix memory leak issue Amend error condition to avoid memory leak issues. CRs-Fixed: 2091851 Change-Id: Id7ee9127ba3a22857d667e854ad7bdc0510f3995 Signed-off-by: Srinu Gorle --- drivers/media/platform/msm/vidc/msm_v4l2_vidc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c index 9c897d3458b1..dc5b44e129a2 100644 --- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c @@ -489,12 +489,16 @@ static int msm_vidc_probe_vidc_device(struct platform_device *pdev) struct device *dev; int nr = BASE_DEVICE_NUMBER; + if (!vidc_driver) { + dprintk(VIDC_ERR, "Invalid vidc driver\n"); + return -EINVAL; + } + core = kzalloc(sizeof(*core), GFP_KERNEL); - if (!core || !vidc_driver) { + if (!core) { dprintk(VIDC_ERR, "Failed to allocate memory for device core\n"); - rc = -ENOMEM; - goto err_no_mem; + return -ENOMEM; } dev_set_drvdata(&pdev->dev, core); @@ -642,7 +646,6 @@ err_v4l2_register: err_core_init: dev_set_drvdata(&pdev->dev, NULL); kfree(core); -err_no_mem: return rc; } -- GitLab From 9cd709c70e0ff3bb8bfad669732913703403138a Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Fri, 25 Aug 2017 14:32:11 -0700 Subject: [PATCH 0811/5498] soc: qcom: initialize glink link state Initialize glink link state to GLINK_LINK_STATE_DOWN to return error if userspace sends glink command packet before glink link is up. Change-Id: I79b5e4eb5a743dd0b118ba1b28523bfea24044d7 Signed-off-by: Vidyakumar Athota --- drivers/soc/qcom/wcd-dsp-glink.c | 38 +++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index 3c9d8efd3956..e9da395bb8ad 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -540,7 +540,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, mutex_lock(&wpriv->glink_mutex); if (wpriv->ch) { - dev_err(wpriv->dev, "%s: glink ch memory is already allocated\n", + dev_err_ratelimited(wpriv->dev, "%s: glink ch memory is already allocated\n", __func__); ret = -EINVAL; goto done; @@ -549,7 +549,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, no_of_channels = pkt->no_of_channels; if (no_of_channels > WDSP_MAX_NO_OF_CHANNELS) { - dev_err(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", + dev_err_ratelimited(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", __func__, no_of_channels, WDSP_MAX_NO_OF_CHANNELS); ret = -EINVAL; goto done; @@ -568,20 +568,20 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, size += WDSP_CH_CFG_SIZE; if (size > pkt_size) { - dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", __func__, size, pkt_size); ret = -EINVAL; goto err_ch_mem; } if (ch_cfg->no_of_intents > WDSP_MAX_NO_OF_INTENTS) { - dev_err(wpriv->dev, "%s: Invalid no_of_intents = %d\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid no_of_intents = %d\n", __func__, ch_cfg->no_of_intents); ret = -EINVAL; goto err_ch_mem; } size += (sizeof(u32) * ch_cfg->no_of_intents); if (size > pkt_size) { - dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", __func__, size, pkt_size); ret = -EINVAL; goto err_ch_mem; @@ -716,7 +716,7 @@ static ssize_t wdsp_glink_read(struct file *file, char __user *buf, } if (count > WDSP_MAX_READ_SIZE) { - dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", + dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", __func__, count); count = WDSP_MAX_READ_SIZE; } @@ -748,7 +748,7 @@ static ssize_t wdsp_glink_read(struct file *file, char __user *buf, if (ret1) { mutex_unlock(&wpriv->rsp_mutex); - dev_err(wpriv->dev, "%s: copy_to_user failed %d\n", + dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n", __func__, ret); ret = -EFAULT; goto done; @@ -794,7 +794,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, if ((count < WDSP_WRITE_PKT_SIZE) || (count > WDSP_MAX_WRITE_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid count = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid count = %zd\n", __func__, count); ret = -EINVAL; goto done; @@ -811,7 +811,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, ret = copy_from_user(tx_buf->buf, buf, count); if (ret) { - dev_err(wpriv->dev, "%s: copy_from_user failed %d\n", + dev_err_ratelimited(wpriv->dev, "%s: copy_from_user failed %d\n", __func__, ret); ret = -EFAULT; goto free_buf; @@ -822,7 +822,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, case WDSP_REG_PKT: if (count < (WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE + WDSP_CH_CFG_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", __func__, count); ret = -EINVAL; goto free_buf; @@ -831,7 +831,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, (struct wdsp_reg_pkt *)wpkt->payload, count); if (IS_ERR_VALUE(ret)) - dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n", + dev_err_ratelimited(wpriv->dev, "%s: glink register failed, ret = %d\n", __func__, ret); vfree(tx_buf); break; @@ -841,7 +841,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, GLINK_LINK_STATE_UP), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { - dev_err(wpriv->dev, "%s: Link state wait timeout\n", + dev_err_ratelimited(wpriv->dev, "%s: Link state wait timeout\n", __func__); ret = -ETIMEDOUT; goto free_buf; @@ -851,7 +851,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, break; case WDSP_CMD_PKT: if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", __func__, count); ret = -EINVAL; goto free_buf; @@ -859,7 +859,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, mutex_lock(&wpriv->glink_mutex); if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { mutex_unlock(&wpriv->glink_mutex); - dev_err(wpriv->dev, "%s: Link state is Down\n", + dev_err_ratelimited(wpriv->dev, "%s: Link state is Down\n", __func__); ret = -ENETRESET; @@ -871,7 +871,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, sizeof(struct wdsp_cmd_pkt) + cpkt->payload_size; if (count < pkt_max_size) { - dev_err(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", __func__, count, pkt_max_size); ret = -EINVAL; goto free_buf; @@ -887,7 +887,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, } } if (!tx_buf->ch) { - dev_err(wpriv->dev, "%s: Failed to get glink channel\n", + dev_err_ratelimited(wpriv->dev, "%s: Failed to get glink channel\n", __func__); ret = -EINVAL; goto free_buf; @@ -898,7 +898,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, GLINK_CONNECTED), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { - dev_err(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", + dev_err_ratelimited(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", __func__, tx_buf->ch->ch_cfg.name, tx_buf->ch->channel_state); ret = -ETIMEDOUT; @@ -910,7 +910,8 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, queue_work(wpriv->work_queue, &tx_buf->tx_work); break; default: - dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__); + dev_err_ratelimited(wpriv->dev, "%s: Invalid packet type\n", + __func__); ret = -EINVAL; vfree(tx_buf); break; @@ -956,6 +957,7 @@ static int wdsp_glink_open(struct inode *inode, struct file *file) goto err_wq; } + wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; init_completion(&wpriv->rsp_complete); init_waitqueue_head(&wpriv->link_state_wait); mutex_init(&wpriv->rsp_mutex); -- GitLab From f7443c2baaa97a6d41deba458d87c15776f66baf Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Wed, 6 Sep 2017 18:33:29 +0530 Subject: [PATCH 0812/5498] msm: kgsl: Fix gpu fault issue while enabling stall on page fault Stall on page fault feature is supported on A5XX and later GPUs. Enabling this feature on unsupported GPUs causes GPU faults. So don't insert GPU stall related commands in ringbuffer if not supported. But allow user to capture the GPU snapshot on GPU page fault. Change-Id: Ied26a5b4f44c1877b289a0ff5c0a6d47901e453d Signed-off-by: Hareesh Gundu --- drivers/gpu/msm/adreno_ringbuffer.c | 33 ++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 84c45ee93a78..0643a32db131 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -495,12 +495,17 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP) total_sizedwords += 9; - /* WAIT_MEM_WRITES - needed in the stall on fault case - * to prevent out of order CP operations that can result - * in a CACHE_FLUSH_TS interrupt storm */ - if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, + /* Don't insert any commands if stall on fault is not supported. */ + if ((ADRENO_GPUREV(adreno_dev) > 500) && !adreno_is_a510(adreno_dev)) { + /* + * WAIT_MEM_WRITES - needed in the stall on fault case + * to prevent out of order CP operations that can result + * in a CACHE_FLUSH_TS interrupt storm + */ + if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, &adreno_dev->ft_pf_policy)) - total_sizedwords += 1; + total_sizedwords += 1; + } ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords); if (IS_ERR(ringcmds)) @@ -587,14 +592,18 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, if (profile_ready) adreno_profile_postib_processing(adreno_dev, &flags, &ringcmds); - /* - * WAIT_MEM_WRITES - needed in the stall on fault case to prevent - * out of order CP operations that can result in a CACHE_FLUSH_TS - * interrupt storm - */ - if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, + /* Don't insert any commands if stall on fault is not supported. */ + if ((ADRENO_GPUREV(adreno_dev) > 500) && !adreno_is_a510(adreno_dev)) { + /* + * WAIT_MEM_WRITES - needed in the stall on fault case + * to prevent out of order CP operations that can result + * in a CACHE_FLUSH_TS interrupt storm + */ + if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, &adreno_dev->ft_pf_policy)) - *ringcmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 0); + *ringcmds++ = cp_packet(adreno_dev, + CP_WAIT_MEM_WRITES, 0); + } /* * Do a unique memory write from the GPU. This can be used in -- GitLab From 75c569a401fc38b44414ff0d294543678c841eb6 Mon Sep 17 00:00:00 2001 From: Venkateswara Rao Tadikonda Date: Tue, 29 Aug 2017 11:02:38 +0530 Subject: [PATCH 0813/5498] msm: kgsl: Limit the kgsl log frequency in kgsl_get_unmapped_area() Reduce the frequency of kgsl logging when kgsl driver failed to get memory mapping for requested memory. Change-Id: If1a8832a9aa0396e3c990916deec7949697be12d Signed-off-by: Venkateswara Rao Tadikonda --- drivers/gpu/msm/kgsl.c | 4 ++-- drivers/gpu/msm/kgsl_log.h | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 563cb5ea2cd3..d072c8f68939 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -3617,13 +3617,13 @@ kgsl_get_unmapped_area(struct file *file, unsigned long addr, if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) { val = get_unmapped_area(NULL, addr, len, 0, flags); if (IS_ERR_VALUE(val)) - KGSL_MEM_ERR(device, + KGSL_DRV_ERR_RATELIMIT(device, "get_unmapped_area: pid %d addr %lx pgoff %lx len %ld failed error %d\n", private->pid, addr, pgoff, len, (int) val); } else { val = _get_svm_area(private, entry, addr, len, flags); if (IS_ERR_VALUE(val)) - KGSL_MEM_ERR(device, + KGSL_DRV_ERR_RATELIMIT(device, "_get_svm_area: pid %d addr %lx pgoff %lx len %ld failed error %d\n", private->pid, addr, pgoff, len, (int) val); } diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h index 51baabefb6d3..9b7833bdb2df 100644 --- a/drivers/gpu/msm/kgsl_log.h +++ b/drivers/gpu/msm/kgsl_log.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011,2013-2014,2016 The Linux Foundation. +/* Copyright (c) 2002,2008-2011,2013-2014,2016-2017 The Linux Foundation. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -67,6 +67,13 @@ __func__, ##args);\ } while (0) +#define KGSL_LOG_ERR_RATELIMITED(dev, lvl, fmt, args...) \ + do { \ + if ((lvl) >= 3) \ + dev_err_ratelimited(dev, "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + #define KGSL_DRV_INFO(_dev, fmt, args...) \ KGSL_LOG_INFO(_dev->dev, _dev->drv_log, fmt, ##args) #define KGSL_DRV_WARN(_dev, fmt, args...) \ @@ -77,6 +84,8 @@ KGSL_LOG_ERR(_dev->dev, _dev->drv_log, fmt, ##args) KGSL_LOG_CRIT(_dev->dev, _dev->drv_log, fmt, ##args) #define KGSL_DRV_CRIT_RATELIMIT(_dev, fmt, args...) \ KGSL_LOG_CRIT_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args) +#define KGSL_DRV_ERR_RATELIMIT(_dev, fmt, args...) \ +KGSL_LOG_ERR_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args) #define KGSL_DRV_FATAL(_dev, fmt, args...) \ KGSL_LOG_FATAL((_dev)->dev, (_dev)->drv_log, fmt, ##args) -- GitLab From a1ce9043241bb2ecab394af791e5cd1ab6fa01eb Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Tue, 12 Sep 2017 11:01:57 +0530 Subject: [PATCH 0814/5498] msm: ais: sensor: flash: add conditional check for ioctl Add conditional check when sending VIDIOC_MSM_FLASH_CFG in 32-bit process. Change-Id: Ia4255aba26c634018163e2cc3c1170f71b1eb661 CRs-Fixed: 2092793 Signed-off-by: Rahul Sharma --- drivers/media/platform/msm/ais/sensor/flash/msm_flash.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c index eac24da2598a..225325f70520 100644 --- a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c @@ -1090,6 +1090,9 @@ static long msm_flash_subdev_do_ioctl( break; } break; + case VIDIOC_MSM_FLASH_CFG: + pr_err("invalid cmd 0x%x received\n", cmd); + return -EINVAL; default: return msm_flash_subdev_ioctl(sd, cmd, arg); } -- GitLab From 20980cfa4075385f3b2eaa4c4a261cc7c0137216 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Tue, 17 Jan 2017 15:55:16 +0530 Subject: [PATCH 0815/5498] HID: uhid: Remove mutex_unlock while waiting Because of recently introduced kernel change, previous change to unlock mutex is not required as HID device addition is now done in worker thread instead of main thread. Previous code flow was blocking as uhid_hid_set_report was called from main thread which is not the case now. Previous code flow was uhid_char_write->uhid_dev_create2->uhid_hid_raw_request-> uhid_hid_set_report->__uhid_report_queue_and_wait-> wait_event_interruptible_timeout which is now changed to uhid_char_write->uhid_dev_create2. CRs-Fixed: 1112380 Signed-off-by: Hemant Gupta Change-Id: I11c3e5628484739acc208deff84a82f34b1b7beb --- drivers/hid/uhid.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index eeb527dd0b90..690a9f0fa042 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -188,27 +188,9 @@ static int __uhid_report_queue_and_wait(struct uhid_device *uhid, uhid_queue(uhid, ev); spin_unlock_irqrestore(&uhid->qlock, flags); - /* - * Assumption: report_lock and devlock are both locked. So unlock - * before sleeping. - */ - mutex_unlock(&uhid->report_lock); - mutex_unlock(&uhid->devlock); ret = wait_event_interruptible_timeout(uhid->report_wait, !uhid->report_running || !uhid->running, 5 * HZ); - ret = mutex_lock_interruptible(&uhid->devlock); - if (ret) - return ret; - ret = mutex_lock_interruptible(&uhid->report_lock); - if (ret) { - /* - * Failed to lock, unlock previous mutex before exiting - * this function. - */ - mutex_unlock(&uhid->devlock); - return ret; - } if (!ret || !uhid->running || uhid->report_running) ret = -EIO; else if (ret < 0) -- GitLab From 21c8ef6292838514c299b049b3d08d6f4594a9b3 Mon Sep 17 00:00:00 2001 From: Swetha Chikkaboraiah Date: Mon, 28 Aug 2017 13:19:13 +0530 Subject: [PATCH 0816/5498] defconfig: msm: Disable DEVPORT config for msm8953_64 Disable CONFIG_DEVPORT config for msm8953_64 As selinux policy requires this node to be not accessible to userspace. CRs-Fixed: 2077590 Change-Id: I187626de63d36f4f1e102ae5f417c601f6cadd72 Signed-off-by: Mohammed Khajapasha Signed-off-by: Chetan C R --- arch/arm64/configs/msmcortex-perf_defconfig | 1 + arch/arm64/configs/msmcortex_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 49d3fbbd5bdd..6d2181a2cf62 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -318,6 +318,7 @@ CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set CONFIG_MSM_SMD_PKT=y CONFIG_MSM_ADSPRPC=y CONFIG_MSM_RDBG=m diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index ff676ac1abe9..387d9795d329 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -320,6 +320,7 @@ CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set CONFIG_MSM_SMD_PKT=y CONFIG_MSM_ADSPRPC=y CONFIG_MSM_RDBG=m -- GitLab From dff273de3380b6f9307dfb6b2d3404ea71e028fa Mon Sep 17 00:00:00 2001 From: Chetan C R Date: Thu, 31 Aug 2017 12:38:39 +0530 Subject: [PATCH 0817/5498] defconfig: msm: enable configs to fix finger print mismatch on msm8909 This change is required as some of the defconfigs required for finger print matching between the runtime info of the device capability and framework compatibility matrix are missing. These missed defconfigs were causing the "Internal problem with your device" error message is coming after every reboot. Change-Id: I2d02a3f61a461c61db43766a587580e4e5aaca1e Signed-off-by: Chetan C R --- arch/arm/configs/msm8909-perf_defconfig | 4 ++++ arch/arm/configs/msm8909_defconfig | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index 04a4c6791928..b326d2501327 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -69,6 +69,7 @@ CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y @@ -80,6 +81,7 @@ CONFIG_INET_AH=y CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y CONFIG_IPV6=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y @@ -233,6 +235,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_APDS9930=y CONFIG_QSEECOM=y +CONFIG_UID_SYS_STATS=y CONFIG_MSM_MCU_TIME_SYNC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -519,6 +522,7 @@ CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_CTR=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index a0fcbbf09325..bf662274a1e6 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -73,6 +73,7 @@ CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y @@ -84,6 +85,7 @@ CONFIG_INET_AH=y CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y CONFIG_IPV6=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y @@ -239,6 +241,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_APDS9930=y CONFIG_QSEECOM=y +CONFIG_UID_SYS_STATS=y CONFIG_MSM_MCU_TIME_SYNC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -561,6 +564,7 @@ CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_CTR=y -- GitLab From 19610125bd9a34c79593b1a719ebf70ba843d9c7 Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Tue, 22 Aug 2017 18:55:50 +0530 Subject: [PATCH 0818/5498] msm: kgsl: Do not allocate scratch memory for A3xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A3xx device gets the ring buffer read pointer directly from the GPU registers. So don’t allocate scratch memory which can’t be used for A3xx GPU devices. Change-Id: I95016dfc169b9fee74e978f5560592740f34515e Signed-off-by: Hareesh Gundu --- drivers/gpu/msm/adreno.c | 4 ++++ drivers/gpu/msm/adreno_ringbuffer.c | 20 ++++++++++++++++---- drivers/gpu/msm/kgsl.c | 11 ----------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 90a393a48f3e..cb1c709796a7 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1163,6 +1163,10 @@ static int adreno_init(struct kgsl_device *device) struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); int ret; + if (!adreno_is_a3xx(adreno_dev)) + kgsl_sharedmem_set(device, &device->scratch, 0, 0, + device->scratch.size); + ret = kgsl_pwrctrl_change_state(device, KGSL_STATE_INIT); if (ret) return ret; diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 84c45ee93a78..cd3d8d54ad25 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -209,8 +209,9 @@ int adreno_ringbuffer_start(struct adreno_device *adreno_dev, FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { kgsl_sharedmem_set(device, &(rb->buffer_desc), 0, 0xAA, KGSL_RB_SIZE); - kgsl_sharedmem_writel(device, &device->scratch, - SCRATCH_RPTR_OFFSET(rb->id), 0); + if (!adreno_is_a3xx(adreno_dev)) + kgsl_sharedmem_writel(device, &device->scratch, + SCRATCH_RPTR_OFFSET(rb->id), 0); rb->wptr = 0; rb->_wptr = 0; rb->wptr_preempt_end = 0xFFFFFFFF; @@ -271,9 +272,16 @@ static int _adreno_ringbuffer_probe(struct adreno_device *adreno_dev, int adreno_ringbuffer_probe(struct adreno_device *adreno_dev, bool nopreempt) { - int status = 0; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - int i; + int i, status; + + if (!adreno_is_a3xx(adreno_dev)) { + status = kgsl_allocate_global(device, &device->scratch, + PAGE_SIZE, 0, 0, "scratch"); + if (status != 0) + return status; + } if (nopreempt == false && ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION)) adreno_dev->num_ringbuffers = gpudev->num_prio_levels; @@ -309,9 +317,13 @@ static void _adreno_ringbuffer_close(struct adreno_device *adreno_dev, void adreno_ringbuffer_close(struct adreno_device *adreno_dev) { + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct adreno_ringbuffer *rb; int i; + if (!adreno_is_a3xx(adreno_dev)) + kgsl_free_global(device, &device->scratch); + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) _adreno_ringbuffer_close(adreno_dev, rb); } diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 563cb5ea2cd3..75b98147c01f 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1101,8 +1101,6 @@ static int kgsl_open_device(struct kgsl_device *device) atomic_inc(&device->active_cnt); kgsl_sharedmem_set(device, &device->memstore, 0, 0, device->memstore.size); - kgsl_sharedmem_set(device, &device->scratch, 0, 0, - device->scratch.size); result = device->ftbl->init(device); if (result) @@ -3939,11 +3937,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device) if (status != 0) goto error_close_mmu; - status = kgsl_allocate_global(device, &device->scratch, - PAGE_SIZE, 0, 0, "scratch"); - if (status != 0) - goto error_free_memstore; - /* * The default request type PM_QOS_REQ_ALL_CORES is * applicable to all CPU cores that are online and @@ -3989,8 +3982,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device) return 0; -error_free_memstore: - kgsl_free_global(device, &device->memstore); error_close_mmu: kgsl_mmu_close(device); error_pwrctrl_close: @@ -4017,8 +4008,6 @@ void kgsl_device_platform_remove(struct kgsl_device *device) idr_destroy(&device->context_idr); - kgsl_free_global(device, &device->scratch); - kgsl_free_global(device, &device->memstore); kgsl_mmu_close(device); -- GitLab From 1ab5069b74facbd266e693f8a414bac605329c20 Mon Sep 17 00:00:00 2001 From: Mohit Aggarwal Date: Fri, 28 Jul 2017 15:40:27 +0530 Subject: [PATCH 0819/5498] diag: Fix possible usage of freed resource issue Currently, there is a possibility of using already freed memory while client reads from diag driver. The patch adds proper protection to fix the issue. CRs-Fixed: 2076623 Change-Id: Ic946865ac79f2684c06176be64fd2f3abc6048f7 Signed-off-by: Mohit Aggarwal --- drivers/char/diag/diagchar_core.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 9f46c452ee0b..c4b39d185977 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -2824,6 +2824,16 @@ static int diag_user_process_apps_data(const char __user *buf, int len, return 0; } +static int check_data_ready(int index) +{ + int data_type = 0; + + mutex_lock(&driver->diagchar_mutex); + data_type = driver->data_ready[index]; + mutex_unlock(&driver->diagchar_mutex); + return data_type; +} + static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -2836,9 +2846,11 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, int write_len = 0; struct diag_md_session_t *session_info = NULL; + mutex_lock(&driver->diagchar_mutex); for (i = 0; i < driver->num_clients; i++) if (driver->client_map[i].pid == current->tgid) index = i; + mutex_unlock(&driver->diagchar_mutex); if (index == -1) { pr_err("diag: Client PID not found in table"); @@ -2848,7 +2860,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, pr_err("diag: bad address from user side\n"); return -EFAULT; } - wait_event_interruptible(driver->wait_q, driver->data_ready[index]); + wait_event_interruptible(driver->wait_q, (check_data_ready(index)) > 0); mutex_lock(&driver->diagchar_mutex); @@ -2985,11 +2997,11 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, } exit: - mutex_unlock(&driver->diagchar_mutex); if (driver->data_ready[index] & DCI_DATA_TYPE) { - mutex_lock(&driver->dci_mutex); - /* Copy the type of data being passed */ data_type = driver->data_ready[index] & DCI_DATA_TYPE; + mutex_unlock(&driver->diagchar_mutex); + /* Copy the type of data being passed */ + mutex_lock(&driver->dci_mutex); list_for_each_safe(start, temp, &driver->dci_client_list) { entry = list_entry(start, struct diag_dci_client_tbl, track); @@ -3021,6 +3033,7 @@ exit: mutex_unlock(&driver->dci_mutex); goto end; } + mutex_unlock(&driver->diagchar_mutex); end: /* * Flush any read that is currently pending on DCI data and -- GitLab From 181ac071f89597fcd2c24df7e2d35efdcf416b0c Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Sun, 3 Sep 2017 00:04:28 +0530 Subject: [PATCH 0820/5498] usb: dwc3: ep0: Ignore Data Stage after EP0 Stall/Restart There are some requests which are not supported by our driver, like vendor-specific requests. In this case, it was a vendor request with wlen = 0 (so ep0_expect_in = FALSE). When we get such requests, we issue a SETSTALL and STARTTRANSFER command on the Ep0 OUT, expecting the next DWC3 event to be the completion event for the setup packet of the next request. This is the c040 event (Tx complete on Ep0 OUT). But when the SETSTALL command is issued, there could be a latency in this actually taking effect, until then there could be packets on the bus which can return unexpected events. In this particular issue, there were IN tokens being sent by the host during STALL and RESTART of EP0. These IN tokens were making the HW to queue unexpected events like 20c2 (Ctrl Status Tx Not Ready on Ep0 IN) and 10c2 (Ctrl DATA Tx Not Ready on Ep0 IN). As per the driver, 20c2 is ignored since the STALL and RESTART of Ep0 OUT has set next_event as EP0_COMPLETE and we return with a No-OP. But the 10c2 event does not have any such check. Hence it was giving WRONGDR error (since ep0_expect_in = FALSE but data is getting expected on Ep0 IN). We then go for another STALL and RESTART of Ep0 OUT and this was causing HW and SW to go out-of-sync and yellow bang was observed. On discussion with SNPS folks, they said that STALL will get cleared only on receiving the next setup token. Till then, we should ignore any Ep0 specific event other than c040. Change-Id: Ie1b67ea4ff0c289b0ff37e2749ec26672d60e66d Signed-off-by: Ajay Agarwal --- drivers/usb/dwc3/ep0.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 86c1e699746b..745ff533df1e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1111,9 +1111,22 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: - dwc3_trace(trace_dwc3_ep0, "Control Data"); dep->dbg_ep_events.control_data++; + /* + * When we issue a STALL and RESTART of EP0 OUT, then + * ep0_next_event is set as DWC3_EP0_COMPLETE and we wait for + * the next setup packet. We will ignore a XferNotReady (DATA) + * event until setup packet arrives, so as to avoid HW latency + * issues. + */ + if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) { + dwc3_trace(trace_dwc3_ep0, "Ignore Control Data"); + return; + } + + dwc3_trace(trace_dwc3_ep0, "Control Data"); + /* * We already have a DATA transfer in the controller's cache, * if we receive a XferNotReady(DATA) we will ignore it, unless -- GitLab From fff55b86006ad8d1240398e5abf1ac9bda0c687c Mon Sep 17 00:00:00 2001 From: Skylar Chang Date: Fri, 11 Aug 2017 15:49:21 -0700 Subject: [PATCH 0821/5498] msm: ipa3: add lock for num_q6_rule There is a race condition be observed on global variable num_q6_rule used in ipa wan-driver. The fix is to add lock to prevent different threads are accessing it at the same time. Change-Id: Ia9190c60361cb5605b61963309beca3acdeac89d Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 12 ++++++++++-- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 46841e11c462..77f1d87cc266 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -398,12 +398,15 @@ int copy_ul_filter_rule_to_ipa(struct ipa_install_fltr_rule_req_msg_v01 { int i, j; + /* prevent multi-threads accessing num_q6_rule */ + mutex_lock(&add_mux_channel_lock); if (rule_req->filter_spec_list_valid == true) { num_q6_rule = rule_req->filter_spec_list_len; IPAWANDBG("Received (%d) install_flt_req\n", num_q6_rule); } else { num_q6_rule = 0; IPAWANERR("got no UL rules from modem\n"); + mutex_unlock(&add_mux_channel_lock); return -EINVAL; } @@ -597,9 +600,11 @@ failure: num_q6_rule = 0; memset(ipa_qmi_ctx->q6_ul_filter_rule, 0, sizeof(ipa_qmi_ctx->q6_ul_filter_rule)); + mutex_unlock(&add_mux_channel_lock); return -EINVAL; success: + mutex_unlock(&add_mux_channel_lock); return 0; } @@ -1504,9 +1509,12 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* already got Q6 UL filter rules*/ if (ipa_qmi_ctx && ipa_qmi_ctx->modem_cfg_emb_pipe_flt - == false) + == false) { + /* protect num_q6_rule */ + mutex_lock(&add_mux_channel_lock); rc = wwan_add_ul_flt_rule_to_ipa(); - else + mutex_unlock(&add_mux_channel_lock); + } else rc = 0; egress_set = true; if (rc) diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index fe73080c42bc..42e243f46a7f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -416,6 +416,8 @@ int ipa3_copy_ul_filter_rule_to_ipa(struct ipa_install_fltr_rule_req_msg_v01 { int i, j; + /* prevent multi-threads accessing rmnet_ipa3_ctx->num_q6_rules */ + mutex_lock(&rmnet_ipa3_ctx->add_mux_channel_lock); if (rule_req->filter_spec_ex_list_valid == true) { rmnet_ipa3_ctx->num_q6_rules = rule_req->filter_spec_ex_list_len; @@ -424,6 +426,8 @@ int ipa3_copy_ul_filter_rule_to_ipa(struct ipa_install_fltr_rule_req_msg_v01 } else { rmnet_ipa3_ctx->num_q6_rules = 0; IPAWANERR("got no UL rules from modem\n"); + mutex_unlock(&rmnet_ipa3_ctx-> + add_mux_channel_lock); return -EINVAL; } @@ -626,9 +630,13 @@ failure: rmnet_ipa3_ctx->num_q6_rules = 0; memset(ipa3_qmi_ctx->q6_ul_filter_rule, 0, sizeof(ipa3_qmi_ctx->q6_ul_filter_rule)); + mutex_unlock(&rmnet_ipa3_ctx-> + add_mux_channel_lock); return -EINVAL; success: + mutex_unlock(&rmnet_ipa3_ctx-> + add_mux_channel_lock); return 0; } @@ -1324,8 +1332,13 @@ static int handle3_egress_format(struct net_device *dev, if (rmnet_ipa3_ctx->num_q6_rules != 0) { /* already got Q6 UL filter rules*/ - if (ipa3_qmi_ctx->modem_cfg_emb_pipe_flt == false) + if (ipa3_qmi_ctx->modem_cfg_emb_pipe_flt == false) { + /* prevent multi-threads accessing num_q6_rules */ + mutex_lock(&rmnet_ipa3_ctx->add_mux_channel_lock); rc = ipa3_wwan_add_ul_flt_rule_to_ipa(); + mutex_unlock(&rmnet_ipa3_ctx-> + add_mux_channel_lock); + } if (rc) IPAWANERR("install UL rules failed\n"); else -- GitLab From 53a0aeb71ab7aa28772ea4dbf85b5939f07c4893 Mon Sep 17 00:00:00 2001 From: Will Huang Date: Tue, 12 Sep 2017 13:33:23 +0800 Subject: [PATCH 0822/5498] net: cnss: Implement logger_dispatch_skb to handle input event The broadcast netlink input handle logger_skb_input just prints the skb len and dose not handle it, these input messages are broadcast from user space application to wlan drivers. This change is to match input skb with event registered to each radio and call corresponding event callback. Change-Id: I81d82fca34ec2d356ceecf989c264ef359dd5f40 Signed-off-by: Will Huang --- drivers/net/wireless/cnss/logger/nl_service.c | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/cnss/logger/nl_service.c b/drivers/net/wireless/cnss/logger/nl_service.c index 565cb3a0ff89..1bbdc4779f0e 100644 --- a/drivers/net/wireless/cnss/logger/nl_service.c +++ b/drivers/net/wireless/cnss/logger/nl_service.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -104,9 +104,39 @@ static struct logger_device *logger_device_is_registered( */ static int logger_dispatch_skb(struct sk_buff *skb) { + struct nlmsghdr *nlh; + struct logger_context *ctx; + struct logger_device *cur; + struct logger_event_handler *evt; + int handled = 0; + + ctx = logger_get_ctx(); + if (!ctx) + return -ENOENT; + pr_info("skb_len: %d, skb_data_len: %d\n", skb->len, skb->data_len); + if (skb->len < sizeof(struct nlmsghdr)) + return 0; + + nlh = (struct nlmsghdr *)skb->data; + list_for_each_entry(cur, &ctx->dev_list, list) { + list_for_each_entry(evt, &cur->event_list, list) { + if (nlh->nlmsg_type == evt->event) { + if (evt->cb) { + handled = 1; + evt->cb(skb); + } + /* Break inside loop, next dev */ + break; + } + } + } + + if (!handled) + pr_info("Not handled msg type: %d\n", nlh->nlmsg_type); + return 0; } -- GitLab From 39eba49c68507d8f8c7d2bffb9aa8b119eab2918 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 21 Aug 2017 22:53:45 +0800 Subject: [PATCH 0823/5498] f2fs: fix out-of-order execution in f2fs_issue_flush In f2fs_issue_flush, due to out-of-order execution of CPU, wake_up can be called before we insert issue_list, result in long latency of wait_for_completion. Fix this by adding smp_mb() to force the order of related codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 103c98d27f84..20b10a907d80 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -549,7 +549,10 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) atomic_inc(&fcc->issing_flush); llist_add(&cmd.llnode, &fcc->issue_list); - if (!fcc->dispatch_list) + /* update issue_list before we wake up issue_flush thread */ + smp_mb(); + + if (waitqueue_active(&fcc->flush_wait_queue)) wake_up(&fcc->flush_wait_queue); if (fcc->f2fs_issue_flush) { -- GitLab From c3555d266989da942ca27607c72a8343953733b4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 18 Aug 2017 23:37:36 +0800 Subject: [PATCH 0824/5498] f2fs: clear FI_HOT_DATA correctly This patch fixes to clear FI_HOT_DATA correctly in below path: - error handling in f2fs_ioc_start_atomic_write - after commit atomic write in f2fs_ioc_commit_atomic_write - after drop atomic write in drop_inmem_pages Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 ++ fs/f2fs/segment.c | 1 + 2 files changed, 3 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index cc0ea3f90d19..3c9f096abf57 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1624,6 +1624,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); if (ret) { clear_inode_flag(inode, FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_HOT_DATA); goto out; } @@ -1662,6 +1663,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); if (!ret) { clear_inode_flag(inode, FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_HOT_DATA); stat_dec_atomic_write(inode); } } else { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 20b10a907d80..5965ab48d9c2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -255,6 +255,7 @@ void drop_inmem_pages(struct inode *inode) mutex_unlock(&fi->inmem_lock); clear_inode_flag(inode, FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_HOT_DATA); stat_dec_atomic_write(inode); } -- GitLab From 40cf18118e8f999fab4749f33fe07ddaeb1a615f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 18 Aug 2017 16:20:33 +0800 Subject: [PATCH 0825/5498] f2fs: trigger normal fsync for non-atomic_write file If file was not opened with atomic write mode, but user uses atomic write ioctl to fsync datas, in the flow, we should not fsync that file with atomic write mode. Fixes: 608514deba38 ("f2fs: set fsync mark only for the last dnode") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3c9f096abf57..4dde9c5dcce6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1667,7 +1667,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) stat_dec_atomic_write(inode); } } else { - ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, false); } err_out: inode_unlock(inode); -- GitLab From 1a9e85c7ea7aa0a4094e477c7f9c8a926edddbdd Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 21 Aug 2017 13:51:32 -0700 Subject: [PATCH 0826/5498] f2fs: return error when accessing insane flie offset If file offset is insane, we have to return error instead of kernel panic. Reported-by: Eric Zhang Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0b3110504326..2ff473d68ad2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -555,7 +555,7 @@ static int get_node_path(struct inode *inode, long block, level = 3; goto got; } else { - BUG(); + return -E2BIG; } got: return level; @@ -579,6 +579,8 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) int err = 0; level = get_node_path(dn->inode, index, offset, noffset); + if (level < 0) + return level; nids[0] = dn->inode->i_ino; npage[0] = dn->inode_page; @@ -878,6 +880,8 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from) trace_f2fs_truncate_inode_blocks_enter(inode, from); level = get_node_path(inode, from, offset, noffset); + if (level < 0) + return level; page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) { -- GitLab From e96ea97f12412da3c39829427a8320be1fbca61a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Aug 2017 21:15:43 -0700 Subject: [PATCH 0827/5498] f2fs: wake up discard_thread iff there is a candidate This patch fixes to avoid needless wake ups. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 +-- fs/f2fs/segment.h | 25 +++++++++++++++++++++++++ fs/f2fs/sysfs.c | 6 +----- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5965ab48d9c2..61432929522a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1574,8 +1574,7 @@ skip: kmem_cache_free(discard_entry_slab, entry); } - dcc->discard_wake = 1; - wake_up_interruptible_all(&dcc->discard_wait_queue); + wake_up_discard_thread(sbi, false); } static int create_discard_cmd_control(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 7eba54ef224b..8b628d291c0c 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -796,3 +796,28 @@ static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type, wbc->nr_to_write = desired; return desired - nr_to_write; } + +static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + bool wakeup = false; + int i; + + if (force) + goto wake_up; + + mutex_lock(&dcc->cmd_lock); + for (i = MAX_PLIST_NUM - 1; + i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) { + if (!list_empty(&dcc->pend_list[i])) { + wakeup = true; + break; + } + } + mutex_unlock(&dcc->cmd_lock); + if (!wakeup) + return; +wake_up: + dcc->discard_wake = 1; + wake_up_interruptible_all(&dcc->discard_wait_queue); +} diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 6da09ac21e49..6f17abcbcf9c 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -178,13 +178,9 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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) { - struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - sbi->gc_thread->gc_wake = 1; wake_up_interruptible_all(&sbi->gc_thread->gc_wait_queue_head); - - dcc->discard_wake = 1; - wake_up_interruptible_all(&dcc->discard_wait_queue); + wake_up_discard_thread(sbi, true); } return count; -- GitLab From ec2e1a3a9c9113e929b97bd90a8c664a6a0f13f1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 23 Aug 2017 18:23:24 +0800 Subject: [PATCH 0828/5498] f2fs: fix to avoid race in between aio and gc We won't wait DIO synchronously when doing AIO, so there will be potential IO reorder in between AIO and GC, which will cause data corruption. This patch adds inode_dio_wait to serialize aio and data GC to avoid this issue. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 57bea2182f30..cd147e7c71e8 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -875,6 +875,9 @@ next_step: continue; } locked = true; + + /* wait for all inflight aio data */ + inode_dio_wait(inode); } start_bidx = start_bidx_of_node(nofs, inode) -- GitLab From 6c6cad701e689bb4510a20e15e4d48d4b7ff24f1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 23 Aug 2017 18:23:25 +0800 Subject: [PATCH 0829/5498] f2fs: trigger fdatasync for non-atomic_write file Sqlite only cares about synchronization of file data instead of other data unrelated attribute of inode, so in commit flow, call fdatasync is enough. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4dde9c5dcce6..f15e65daa04b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1667,7 +1667,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) stat_dec_atomic_write(inode); } } else { - ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, false); + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 1, false); } err_out: inode_unlock(inode); -- GitLab From d3f0fb48153512af53da6312da065e04ba3891e4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Aug 2017 11:10:58 -0700 Subject: [PATCH 0830/5498] f2fs: don't need to update inode checksum for recovery This patch fixes "f2fs: support inode checksum". The recovered inode page will be rewritten with valid checksum. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2ff473d68ad2..bd8ad92d9058 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2296,8 +2296,6 @@ retry: F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), i_projid)) dst->i_projid = src->i_projid; - - f2fs_inode_chksum_set(sbi, ipage); } new_ni = old_ni; -- GitLab From 48d93ca3ff6b0df3c75d51ae7bcaa49cac1f1159 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Aug 2017 16:54:51 -0700 Subject: [PATCH 0831/5498] f2fs: don't check inode's checksum if it was dirtied or writebacked If another thread already made the page dirtied or writebacked, we must avoid to verify checksum. If we got an error, we need to remove its uptodate as well. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 3 ++- fs/f2fs/node.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index ebbe53aab202..7a6f91f0df15 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -153,7 +153,8 @@ bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page) struct f2fs_inode *ri; __u32 provided, calculated; - if (!f2fs_enable_inode_chksum(sbi, page)) + if (!f2fs_enable_inode_chksum(sbi, page) || + PageDirty(page) || PageWriteback(page)) return true; ri = &F2FS_NODE(page)->i; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index bd8ad92d9058..25d2dbe1aec8 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1187,9 +1187,9 @@ page_hit: nid, nid_of_node(page), ino_of_node(page), ofs_of_node(page), cpver_of_node(page), next_blkaddr_of_node(page)); - ClearPageUptodate(page); err = -EINVAL; out_err: + ClearPageUptodate(page); f2fs_put_page(page, 1); return ERR_PTR(err); } -- GitLab From 668ba2a8cd1920f0e7f17f58031e6a28694934d1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 30 Aug 2017 18:04:47 +0800 Subject: [PATCH 0832/5498] f2fs: update i_flags correctly f2fs enables hash-indexed directory by default, so we need to tag FS_INDEX_FL in inode::i_flags during directory creataion, in order to show correct status of inode in lsattr: Before: ------------------- /mnt/f2fs/dir/ After: -----------I------- /mnt/f2fs/dir/ Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 47359693cf60..c8b28244dd0d 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -98,6 +98,9 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) F2FS_I(inode)->i_flags = f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED); + if (S_ISDIR(inode->i_mode)) + F2FS_I(inode)->i_flags |= FS_INDEX_FL; + if (F2FS_I(inode)->i_flags & FS_PROJINHERIT_FL) set_inode_flag(inode, FI_PROJ_INHERIT); -- GitLab From acdb6f6b773b66caca5c190356d2a13ea0aff8cf Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 30 Aug 2017 18:04:48 +0800 Subject: [PATCH 0833/5498] f2fs: remove unneeded parameter of change_curseg allocate_segment_by_default is the only caller of change_curseg passing @reuse with 'false', but commit 763bfe1bc575 ("f2fs: remove reusing any prefree segments") removes the calling, after that, @reuse in change_curseg always be true, so, let's clean up the unneeded parameter. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 61432929522a..3b2eb51d504c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2092,7 +2092,7 @@ static void __refresh_next_blkoff(struct f2fs_sb_info *sbi, * This function always allocates a used segment(from dirty seglist) by SSR * manner, so it should recover the existing segment information of valid blocks */ -static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) +static void change_curseg(struct f2fs_sb_info *sbi, int type) { struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, type); @@ -2113,12 +2113,10 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) curseg->alloc_type = SSR; __next_free_blkoff(sbi, curseg, 0); - if (reuse) { - sum_page = get_sum_page(sbi, new_segno); - sum_node = (struct f2fs_summary_block *)page_address(sum_page); - memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); - f2fs_put_page(sum_page, 1); - } + sum_page = get_sum_page(sbi, new_segno); + sum_node = (struct f2fs_summary_block *)page_address(sum_page); + memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); + f2fs_put_page(sum_page, 1); } static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) @@ -2182,7 +2180,7 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi, else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type)) new_curseg(sbi, type, false); else if (need_SSR(sbi) && get_ssr_segment(sbi, type)) - change_curseg(sbi, type, true); + change_curseg(sbi, type); else new_curseg(sbi, type, false); @@ -2535,7 +2533,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, /* change the current segment */ if (segno != curseg->segno) { curseg->next_segno = segno; - change_curseg(sbi, type, true); + change_curseg(sbi, type); } curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); @@ -2554,7 +2552,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, if (recover_curseg) { if (old_cursegno != curseg->segno) { curseg->next_segno = old_cursegno; - change_curseg(sbi, type, true); + change_curseg(sbi, type); } curseg->next_blkoff = old_blkoff; } -- GitLab From 750175b4f213494a612f325ffd8487b27f2a5533 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 31 Aug 2017 18:56:05 +0800 Subject: [PATCH 0834/5498] f2fs: avoid race in between atomic_read & atomic_inc Previously, we will miss merging flush command during fsync due to below race condition: Thread A Thread B Thread C - f2fs_issue_flush - atomic_read(&issing_flush) - f2fs_issue_flush - atomic_read(&issing_flush) - f2fs_issue_flush - atomic_read(&issing_flush) - atomic_inc(&issing_flush) - atomic_inc(&issing_flush) - atomic_inc(&issing_flush) - submit_flush_wait - submit_flush_wait - submit_flush_wait It needs to use atomic_inc_return instead to avoid such race. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3b2eb51d504c..27def4321be8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -536,8 +536,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) return ret; } - if (!atomic_read(&fcc->issing_flush)) { - atomic_inc(&fcc->issing_flush); + if (atomic_inc_return(&fcc->issing_flush) == 1) { ret = submit_flush_wait(sbi); atomic_dec(&fcc->issing_flush); @@ -547,7 +546,6 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) init_completion(&cmd.wait); - atomic_inc(&fcc->issing_flush); llist_add(&cmd.llnode, &fcc->issue_list); /* update issue_list before we wake up issue_flush thread */ -- GitLab From 84f5502fb240db5cc56bc49698a463fe4ebd2f43 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 31 Aug 2017 18:56:06 +0800 Subject: [PATCH 0835/5498] f2fs: fix to wake up all sleeping flusher In scenario of remount_ro vs flush, after flush_thread exits in ->remount_fs, flusher will only clean up golbal issue_list, but without waking up flushers waiting on that list, result in hang related user threads. In order to fix this issue, this patch enables the flusher to take charge of issue_flush thread: executes merged flush command, and wake up all sleeping flushers. Fixes: 5eba8c5d1fb3 ("f2fs: fix to access nullified flush_cmd_control pointer") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 27def4321be8..cfa7da61dfba 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -558,8 +558,27 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) wait_for_completion(&cmd.wait); atomic_dec(&fcc->issing_flush); } else { - llist_del_all(&fcc->issue_list); - atomic_set(&fcc->issing_flush, 0); + struct llist_node *list; + + list = llist_del_all(&fcc->issue_list); + if (!list) { + wait_for_completion(&cmd.wait); + atomic_dec(&fcc->issing_flush); + } else { + struct flush_cmd *tmp, *next; + + ret = submit_flush_wait(sbi); + + llist_for_each_entry_safe(tmp, next, list, llnode) { + if (tmp == &cmd) { + cmd.ret = ret; + atomic_dec(&fcc->issing_flush); + continue; + } + tmp->ret = ret; + complete(&tmp->wait); + } + } } return cmd.ret; -- GitLab From d8e15b16710f63fb1677734373ecfd7901618714 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 31 Aug 2017 15:06:24 +0530 Subject: [PATCH 0836/5498] f2fs: constify super_operations super_operations are not supposed to change at runtime. "struct super_block" working with super_operations provided by work with const super_operations. So mark the non-const structs as const Signed-off-by: Arvind Yadav Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 661eac5eeb8b..93e3e295d987 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1544,7 +1544,7 @@ void f2fs_quota_off_umount(struct super_block *sb) } #endif -static struct super_operations f2fs_sops = { +static const struct super_operations f2fs_sops = { .alloc_inode = f2fs_alloc_inode, .drop_inode = f2fs_drop_inode, .destroy_inode = f2fs_destroy_inode, -- GitLab From b43ee0dd367e329dae8a592e9b2ade1a0e1efec3 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Mon, 4 Sep 2017 11:10:18 +0800 Subject: [PATCH 0837/5498] Revert "f2fs: add a new function get_ssr_cost" This reverts commit b7b7c4cf1c9ef0272a65f1480457cbfdadcda19d. se->ckpt_valid_blocks will never be smaller than se->valid_blocks, so just remove get_ssr_cost. Signed-off-by: Yunlong Song Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index cd147e7c71e8..b226760afba8 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -277,20 +277,11 @@ static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi, valid_blocks * 2 : valid_blocks; } -static unsigned int get_ssr_cost(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct seg_entry *se = get_seg_entry(sbi, segno); - - return se->ckpt_valid_blocks > se->valid_blocks ? - se->ckpt_valid_blocks : se->valid_blocks; -} - static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, unsigned int segno, struct victim_sel_policy *p) { if (p->alloc_mode == SSR) - return get_ssr_cost(sbi, segno); + return get_seg_entry(sbi, segno)->ckpt_valid_blocks; /* alloc_mode == LFS */ if (p->gc_mode == GC_GREEDY) -- GitLab From 8b26063a5e0267642849d4b348ffda529155ae35 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 5 Sep 2017 16:54:24 -0700 Subject: [PATCH 0838/5498] f2fs: introduce f2fs_encrypted_file for clean-up This patch replaces (f2fs_encrypted_inode() && S_ISREG()) with f2fs_encrypted_file(), which gives no functional change. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 10 +++++----- fs/f2fs/f2fs.h | 5 +++++ fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 5 ++--- fs/f2fs/inline.c | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 9be18f97b7bd..977c0b20b82c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -581,7 +581,7 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index, .encrypted_page = NULL, }; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) return read_mapping_page(mapping, index, NULL); page = f2fs_grab_cache_page(mapping, index, for_write); @@ -786,7 +786,7 @@ alloc: static inline bool __force_buffered_io(struct inode *inode, int rw) { - return ((f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) || + return (f2fs_encrypted_file(inode) || (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) || F2FS_I_SB(inode)->s_ndevs); } @@ -1154,7 +1154,7 @@ static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, struct fscrypt_ctx *ctx = NULL; struct bio *bio; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + if (f2fs_encrypted_file(inode)) { ctx = fscrypt_get_ctx(inode, GFP_NOFS); if (IS_ERR(ctx)) return ERR_CAST(ctx); @@ -1341,7 +1341,7 @@ static int encrypt_one_page(struct f2fs_io_info *fio) struct inode *inode = fio->page->mapping->host; gfp_t gfp_flags = GFP_NOFS; - if (!f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) + if (!f2fs_encrypted_file(inode)) return 0; /* wait for GCed encrypted page writeback */ @@ -1968,7 +1968,7 @@ repeat: f2fs_wait_on_page_writeback(page, DATA, false); /* wait for GCed encrypted page writeback */ - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); if (len == PAGE_SIZE || PageUptodate(page)) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 461f650ae352..e015216c3bd9 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3054,6 +3054,11 @@ static inline bool f2fs_encrypted_inode(struct inode *inode) return file_is_encrypt(inode); } +static inline bool f2fs_encrypted_file(struct inode *inode) +{ + return f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode); +} + static inline void f2fs_set_encrypted_inode(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f15e65daa04b..9a34d0fc88fa 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -109,7 +109,7 @@ mapped: f2fs_wait_on_page_writeback(page, DATA, false); /* wait for GCed encrypted page writeback */ - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); out_sem: diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index b226760afba8..6a12f33d0cdd 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -831,8 +831,7 @@ next_step: continue; /* if encrypted inode, let's go phase 3 */ - if (f2fs_encrypted_inode(inode) && - S_ISREG(inode->i_mode)) { + if (f2fs_encrypted_file(inode)) { add_gc_inode(gc_list, inode); continue; } @@ -873,7 +872,7 @@ next_step: start_bidx = start_bidx_of_node(nofs, inode) + ofs_in_node; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) move_encrypted_block(inode, start_bidx, segno, off); else move_data_page(inode, start_bidx, gc_type, segno, off); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e63ab0d1f614..c133a4fdecf6 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -25,7 +25,7 @@ bool f2fs_may_inline_data(struct inode *inode) if (i_size_read(inode) > MAX_INLINE_DATA(inode)) return false; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) return false; return true; -- GitLab From 31411593dd02f30a8bf93e7b5f45242837f41eb1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 5 Sep 2017 17:04:35 -0700 Subject: [PATCH 0839/5498] f2fs: use generic terms used for encrypted block management This patch renames functions regarding to buffer management via META_MAPPING used for encrypted blocks especially. We can actually use them in generic way. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +++--- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 13 +++++++++---- fs/f2fs/segment.c | 3 +-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 977c0b20b82c..8af5fd4ad110 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1160,7 +1160,7 @@ static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, return ERR_CAST(ctx); /* wait the page to be moved by cleaning */ - f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); + f2fs_wait_on_block_writeback(sbi, blkaddr); } bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); @@ -1345,7 +1345,7 @@ static int encrypt_one_page(struct f2fs_io_info *fio) return 0; /* wait for GCed encrypted page writeback */ - f2fs_wait_on_encrypted_page_writeback(fio->sbi, fio->old_blkaddr); + f2fs_wait_on_block_writeback(fio->sbi, fio->old_blkaddr); retry_encrypt: fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, @@ -1969,7 +1969,7 @@ repeat: /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_file(inode)) - f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); + f2fs_wait_on_block_writeback(sbi, blkaddr); if (len == PAGE_SIZE || PageUptodate(page)) return 0; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e015216c3bd9..71731d2161b4 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2664,8 +2664,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, struct f2fs_io_info *fio, bool add_list); void f2fs_wait_on_page_writeback(struct page *page, enum page_type type, bool ordered); -void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, - block_t blkaddr); +void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr); void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk); void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk); int lookup_journal_in_cursum(struct f2fs_journal *journal, int type, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 9a34d0fc88fa..e5a47b59d1dd 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -110,7 +110,7 @@ mapped: /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_file(inode)) - f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); + f2fs_wait_on_block_writeback(sbi, dn.data_blkaddr); out_sem: up_read(&F2FS_I(inode)->i_mmap_sem); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 6a12f33d0cdd..bfe6a8ccc3a0 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -599,8 +599,12 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, return true; } -static void move_encrypted_block(struct inode *inode, block_t bidx, - unsigned int segno, int off) +/* + * Move data block via META_MAPPING while keeping locked data page. + * This can be used to move blocks, aka LBAs, directly on disk. + */ +static void move_data_block(struct inode *inode, block_t bidx, + unsigned int segno, int off) { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), @@ -873,9 +877,10 @@ next_step: start_bidx = start_bidx_of_node(nofs, inode) + ofs_in_node; if (f2fs_encrypted_file(inode)) - move_encrypted_block(inode, start_bidx, segno, off); + move_data_block(inode, start_bidx, segno, off); else - move_data_page(inode, start_bidx, gc_type, segno, off); + move_data_page(inode, start_bidx, gc_type, + segno, off); if (locked) { up_write(&fi->dio_rwsem[WRITE]); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index cfa7da61dfba..017b89a7942d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2608,8 +2608,7 @@ void f2fs_wait_on_page_writeback(struct page *page, } } -void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, - block_t blkaddr) +void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr) { struct page *cpage; -- GitLab From 9073e3d7800cdbe26b8d32626c7397402dc4eea2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 6 Sep 2017 21:04:44 -0700 Subject: [PATCH 0840/5498] f2fs: make get_lock_data_page to handle encrypted inode This patch refactors get_lock_data_page() to handle encryption case directly. In order to do that, it introduces common f2fs_submit_page_read(). Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 109 +++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8af5fd4ad110..7b72ac0bca51 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -456,6 +456,53 @@ out_fail: return err; } +static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, + unsigned nr_pages) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct fscrypt_ctx *ctx = NULL; + struct bio *bio; + + if (f2fs_encrypted_file(inode)) { + ctx = fscrypt_get_ctx(inode, GFP_NOFS); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + /* wait the page to be moved by cleaning */ + f2fs_wait_on_block_writeback(sbi, blkaddr); + } + + bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); + if (!bio) { + if (ctx) + fscrypt_release_ctx(ctx); + return ERR_PTR(-ENOMEM); + } + f2fs_target_device(sbi, blkaddr, bio); + bio->bi_end_io = f2fs_read_end_io; + bio->bi_private = ctx; + bio_set_op_attrs(bio, REQ_OP_READ, 0); + + return bio; +} + +/* This can handle encryption stuffs */ +static int f2fs_submit_page_read(struct inode *inode, struct page *page, + block_t blkaddr) +{ + struct bio *bio = f2fs_grab_read_bio(inode, blkaddr, 1); + + if (IS_ERR(bio)) + return PTR_ERR(bio); + + if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { + bio_put(bio); + return -EFAULT; + } + __submit_bio(F2FS_I_SB(inode), bio, DATA); + return 0; +} + static void __set_data_blkaddr(struct dnode_of_data *dn) { struct f2fs_node *rn = F2FS_NODE(dn->node_page); @@ -573,16 +620,6 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index, struct page *page; struct extent_info ei = {0,0,0}; int err; - struct f2fs_io_info fio = { - .sbi = F2FS_I_SB(inode), - .type = DATA, - .op = REQ_OP_READ, - .op_flags = op_flags, - .encrypted_page = NULL, - }; - - if (f2fs_encrypted_file(inode)) - return read_mapping_page(mapping, index, NULL); page = f2fs_grab_cache_page(mapping, index, for_write); if (!page) @@ -623,9 +660,7 @@ got_it: return page; } - fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr; - fio.page = page; - err = f2fs_submit_page_bio(&fio); + err = f2fs_submit_page_read(inode, page, dn.data_blkaddr); if (err) goto put_err; return page; @@ -1147,35 +1182,6 @@ out: return ret; } -static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, - unsigned nr_pages) -{ - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct fscrypt_ctx *ctx = NULL; - struct bio *bio; - - if (f2fs_encrypted_file(inode)) { - ctx = fscrypt_get_ctx(inode, GFP_NOFS); - if (IS_ERR(ctx)) - return ERR_CAST(ctx); - - /* wait the page to be moved by cleaning */ - f2fs_wait_on_block_writeback(sbi, blkaddr); - } - - bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); - if (!bio) { - if (ctx) - fscrypt_release_ctx(ctx); - return ERR_PTR(-ENOMEM); - } - f2fs_target_device(sbi, blkaddr, bio); - bio->bi_end_io = f2fs_read_end_io; - bio->bi_private = ctx; - - return bio; -} - /* * This function was originally taken from fs/mpage.c, and customized for f2fs. * Major change was from block_size == page_size in f2fs by default. @@ -1271,12 +1277,11 @@ submit_and_realloc: bio = NULL; } if (bio == NULL) { - bio = f2fs_grab_bio(inode, block_nr, nr_pages); + bio = f2fs_grab_read_bio(inode, block_nr, nr_pages); if (IS_ERR(bio)) { bio = NULL; goto set_error_page; } - bio_set_op_attrs(bio, REQ_OP_READ, 0); } if (bio_add_page(bio, page, blocksize, 0) < blocksize) @@ -1983,21 +1988,9 @@ repeat: zero_user_segment(page, 0, PAGE_SIZE); SetPageUptodate(page); } else { - struct bio *bio; - - bio = f2fs_grab_bio(inode, blkaddr, 1); - if (IS_ERR(bio)) { - err = PTR_ERR(bio); - goto fail; - } - bio->bi_rw = REQ_OP_READ; - if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { - bio_put(bio); - err = -EFAULT; + err = f2fs_submit_page_read(inode, page, blkaddr); + if (err) goto fail; - } - - __submit_bio(sbi, bio, DATA); lock_page(page); if (unlikely(page->mapping != mapping)) { -- GitLab From 622a8f632e8155cd1f7c45818aecfdabb0ce5d53 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 7 Sep 2017 10:40:54 +0800 Subject: [PATCH 0841/5498] f2fs: avoid race in between read xattr & write xattr Thread A: Thread B: -f2fs_getxattr -lookup_all_xattrs -xnid = F2FS_I(inode)->i_xattr_nid; -f2fs_setxattr -__f2fs_setxattr -write_all_xattrs -truncate_xattr_node ... ... -write_checkpoint ... ... -alloc_nid <- nid reuse -get_node_page -f2fs_bug_on <- nid != node_footer->nid It's need a rw_sem to avoid the race Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 1 + fs/f2fs/xattr.c | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 71731d2161b4..0c9d1cdb36ea 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -683,6 +683,7 @@ struct f2fs_inode_info { struct extent_tree *extent_tree; /* cached extent_tree entry */ struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ struct rw_semaphore i_mmap_sem; + struct rw_semaphore i_xattr_sem; /* avoid racing between reading and changing EAs */ int i_extra_isize; /* size of extra space located in i_addr */ kprojid_t i_projid; /* id for project quota */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 93e3e295d987..06c48dc1a2df 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -636,6 +636,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_rwsem(&fi->dio_rwsem[READ]); init_rwsem(&fi->dio_rwsem[WRITE]); init_rwsem(&fi->i_mmap_sem); + init_rwsem(&fi->i_xattr_sem); #ifdef CONFIG_QUOTA fi->i_reserved_quota = 0; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 833af6916534..ec48fe87414b 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -522,8 +522,10 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name, if (len > F2FS_NAME_LEN) return -ERANGE; + down_read(&F2FS_I(inode)->i_xattr_sem); error = lookup_all_xattrs(inode, ipage, index, len, name, &entry, &base_addr); + up_read(&F2FS_I(inode)->i_xattr_sem); if (error) return error; @@ -552,7 +554,9 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) int error = 0; size_t rest = buffer_size; + down_read(&F2FS_I(inode)->i_xattr_sem); error = read_all_xattrs(inode, NULL, &base_addr); + up_read(&F2FS_I(inode)->i_xattr_sem); if (error) return error; @@ -728,7 +732,9 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, f2fs_lock_op(sbi); /* protect xattr_ver */ down_write(&F2FS_I(inode)->i_sem); + down_write(&F2FS_I(inode)->i_xattr_sem); err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags); + up_write(&F2FS_I(inode)->i_xattr_sem); up_write(&F2FS_I(inode)->i_sem); f2fs_unlock_op(sbi); -- GitLab From 123a12d69dc573687ea208dddc4ae8dd0dd2bab2 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 9 Sep 2017 12:03:23 -0700 Subject: [PATCH 0842/5498] f2fs: better to wait for fstrim completion In android, we'd better wait for fstrim completion instead of issuing the discard commands asynchronous. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 017b89a7942d..409f80c74313 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "f2fs.h" #include "segment.h" @@ -1141,6 +1142,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) if (dcc->pend_list_tag[i] & P_TRIM) { __submit_discard_cmd(sbi, dc); issued++; + + if (fatal_signal_pending(current)) + break; continue; } @@ -1257,7 +1261,7 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) } } -/* This comes from f2fs_put_super */ +/* This comes from f2fs_put_super and f2fs_trim_fs */ void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { __issue_discard_cmd(sbi, false); @@ -2292,6 +2296,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) } /* It's time to issue all the filed discards */ mark_discard_range_all(sbi); + f2fs_wait_discard_bios(sbi); out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return err; -- GitLab From 88fcc2c87994098104408d389d3772710e8f6a31 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 9 Sep 2017 11:11:04 -0700 Subject: [PATCH 0843/5498] f2fs: speed up gc_urgent mode with SSR This patch activates SSR in gc_urgent mode. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 15 +++++++++++++++ fs/f2fs/segment.h | 13 ------------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0c9d1cdb36ea..726a71c872fd 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2625,6 +2625,7 @@ void destroy_node_manager_caches(void); /* * segment.c */ +bool need_SSR(struct f2fs_sb_info *sbi); void register_inmem_page(struct inode *inode, struct page *page); void drop_inmem_pages(struct inode *inode); void drop_inmem_page(struct inode *inode, struct page *page); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 409f80c74313..73601d290b27 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -169,6 +169,21 @@ found: return result - size + __reverse_ffz(tmp); } +bool need_SSR(struct f2fs_sb_info *sbi) +{ + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); + + if (test_opt(sbi, LFS)) + return false; + if (sbi->gc_thread && sbi->gc_thread->gc_urgent) + return true; + + return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + + 2 * reserved_sections(sbi)); +} + void register_inmem_page(struct inode *inode, struct page *page) { struct f2fs_inode_info *fi = F2FS_I(inode); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 8b628d291c0c..593b99cee4d1 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -496,19 +496,6 @@ static inline int reserved_sections(struct f2fs_sb_info *sbi) return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi)); } -static inline bool need_SSR(struct f2fs_sb_info *sbi) -{ - int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); - int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); - int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); - - if (test_opt(sbi, LFS)) - return false; - - return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + - 2 * reserved_sections(sbi)); -} - static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed, int needed) { -- GitLab From 9546525f22f340716755284982a6bef36c6eb48b Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Mon, 11 Sep 2017 16:30:28 +0900 Subject: [PATCH 0844/5498] f2fs: clear radix tree dirty tag of pages whose dirty flag is cleared On a senario like writing out the first dirty page of the inode as the inline data, we only cleared dirty flags of the pages, but didn't clear the dirty tags of those pages in the radix tree. If we don't clear the dirty tags of the pages in the radix tree, the inodes which contain the pages will be marked with I_DIRTY_PAGES again and again, and writepages() for the inodes will be invoked in every writeback period. As a result, nothing will be done in every writepages() for the inodes and it will just consume CPU time meaninglessly. Signed-off-by: Daeho Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 7 +++++++ fs/f2fs/inline.c | 7 +++++++ mm/util.c | 1 + 3 files changed, 15 insertions(+) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 1380c442648b..4f2a8fedb313 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -705,6 +705,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, struct f2fs_dentry_block *dentry_blk; unsigned int bit_pos; int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); + struct address_space *mapping = page_mapping(page); + unsigned long flags; int i; f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); @@ -735,6 +737,11 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, if (bit_pos == NR_DENTRY_IN_BLOCK && !truncate_hole(dir, page->index, page->index + 1)) { + spin_lock_irqsave(&mapping->tree_lock, flags); + radix_tree_tag_clear(&mapping->page_tree, page_index(page), + PAGECACHE_TAG_DIRTY); + spin_unlock_irqrestore(&mapping->tree_lock, flags); + clear_page_dirty_for_io(page); ClearPagePrivate(page); ClearPageUptodate(page); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index c133a4fdecf6..8322e4e7bb3f 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -202,6 +202,8 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) { void *src_addr, *dst_addr; struct dnode_of_data dn; + struct address_space *mapping = page_mapping(page); + unsigned long flags; int err; set_new_dnode(&dn, inode, NULL, NULL, 0); @@ -223,6 +225,11 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) kunmap_atomic(src_addr); set_page_dirty(dn.inode_page); + spin_lock_irqsave(&mapping->tree_lock, flags); + radix_tree_tag_clear(&mapping->page_tree, page_index(page), + PAGECACHE_TAG_DIRTY); + spin_unlock_irqrestore(&mapping->tree_lock, flags); + set_inode_flag(inode, FI_APPEND_WRITE); set_inode_flag(inode, FI_DATA_EXIST); diff --git a/mm/util.c b/mm/util.c index fec39d4509a9..41d8542a347f 100644 --- a/mm/util.c +++ b/mm/util.c @@ -310,6 +310,7 @@ struct address_space *page_mapping(struct page *page) mapping = NULL; return mapping; } +EXPORT_SYMBOL(page_mapping); int overcommit_ratio_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, -- GitLab From 425cfea497322198639219241d911b4a3e61b6a5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 12 Sep 2017 14:04:05 +0800 Subject: [PATCH 0845/5498] f2fs: detect dirty inode in evict_inode Add a bugon in f2fs_evict_inode to detect inconsistent status between inode cache and related node page cache. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 7a6f91f0df15..395ca5dd3d37 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -519,6 +519,9 @@ no_delete: stat_dec_inline_dir(inode); stat_dec_inline_inode(inode); + if (!is_set_ckpt_flags(sbi, CP_ERROR_FLAG)) + f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE)); + /* ino == 0, if f2fs_new_inode() was failed t*/ if (inode->i_ino) invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, -- GitLab From af0a96a4e47e3ce4da626b80d932a435a12b86d9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 12 Sep 2017 14:25:35 +0800 Subject: [PATCH 0846/5498] f2fs: fix to show correct discard_granularity in sysfs Fix below incorrect display when reading discard_granularity sysfs node. $ cat /sys/fs/f2fs//discard_granularity $ 16 $ echo 32 > /sys/fs/f2fs//discard_granularity $ cat /sys/fs/f2fs//discard_granularity $ 16 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 6f17abcbcf9c..06fda7cc7123 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -170,6 +170,8 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, dcc->pend_list_tag[i] &= (~P_ACTIVE); } mutex_unlock(&dcc->cmd_lock); + + *ui = t; return count; } -- GitLab From 55c20926c55482d31f9b19a8c73da85df70bbca6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 12 Sep 2017 21:35:12 +0800 Subject: [PATCH 0847/5498] f2fs: hurry up to issue discard after io interruption Once we encounter I/O interruption during issuing discards, we will delay long time before next round, but if system status is I/O idle during the time, it may loses opportunity to issue discards. So this patch changes to hurry up to issue discard after io interruption. Besides, this patch also fixes to issue discards accurately with assigned rate. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 73601d290b27..7dd2f612ec81 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1142,6 +1142,7 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) struct blk_plug plug; int iter = 0, issued = 0; int i; + bool io_interrupted = false; mutex_lock(&dcc->cmd_lock); f2fs_bug_on(sbi, @@ -1163,11 +1164,20 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) continue; } - if (!issue_cond || is_idle(sbi)) { + if (!issue_cond) { + __submit_discard_cmd(sbi, dc); issued++; + continue; + } + + if (is_idle(sbi)) { __submit_discard_cmd(sbi, dc); + issued++; + } else { + io_interrupted = true; } - if (issue_cond && iter++ > DISCARD_ISSUE_RATE) + + if (++iter >= DISCARD_ISSUE_RATE) goto out; } if (list_empty(pend_list) && dcc->pend_list_tag[i] & P_TRIM) @@ -1177,6 +1187,9 @@ out: blk_finish_plug(&plug); mutex_unlock(&dcc->cmd_lock); + if (!issued && io_interrupted) + issued = -1; + return issued; } -- GitLab From 1870ce0c08e4e253bbdba0c4079d969a26322892 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 2 Feb 2015 00:37:00 -0500 Subject: [PATCH 0848/5498] vfs: add support for a lazytime mount option Add a new mount option which enables a new "lazytime" mode. This mode causes atime, mtime, and ctime updates to only be made to the in-memory version of the inode. The on-disk times will only get updated when (a) if the inode needs to be updated for some non-time related change, (b) if userspace calls fsync(), syncfs() or sync(), or (c) just before an undeleted inode is evicted from memory. This is OK according to POSIX because there are no guarantees after a crash unless userspace explicitly requests via a fsync(2) call. For workloads which feature a large number of random write to a preallocated file, the lazytime mount option significantly reduces writes to the inode table. The repeated 4k writes to a single block will result in undesirable stress on flash devices and SMR disk drives. Even on conventional HDD's, the repeated writes to the inode table block will trigger Adjacent Track Interference (ATI) remediation latencies, which very negatively impact long tail latencies --- which is a very big deal for web serving tiers (for example). Google-Bug-Id: 18297052 Signed-off-by: Theodore Ts'o Signed-off-by: Al Viro --- fs/ext4/inode.c | 6 ++++ fs/fs-writeback.c | 62 ++++++++++++++++++++++++++------ fs/gfs2/file.c | 4 +-- fs/inode.c | 56 ++++++++++++++++++++--------- fs/jfs/file.c | 2 +- fs/libfs.c | 2 +- fs/proc_namespace.c | 1 + fs/sync.c | 8 +++++ include/linux/backing-dev.h | 1 + include/linux/fs.h | 5 +++ include/trace/events/writeback.h | 60 ++++++++++++++++++++++++++++++- include/uapi/linux/fs.h | 4 ++- mm/backing-dev.c | 10 ++++-- 13 files changed, 186 insertions(+), 35 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index e5dc3007627a..4f04173e80f3 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4912,11 +4912,17 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) * If the inode is marked synchronous, we don't honour that here - doing * so would cause a commit on atime updates, which we don't bother doing. * We handle synchronous inodes at the highest possible level. + * + * If only the I_DIRTY_TIME flag is set, we can skip everything. If + * I_DIRTY_TIME and I_DIRTY_SYNC is set, the only inode fields we need + * to copy into the on-disk inode structure are the timestamp files. */ void ext4_dirty_inode(struct inode *inode, int flags) { handle_t *handle; + if (flags == I_DIRTY_TIME) + return; handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); if (IS_ERR(handle)) goto out; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index a07634599cd7..a542921bda78 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -247,14 +247,19 @@ static bool inode_dirtied_after(struct inode *inode, unsigned long t) return ret; } +#define EXPIRE_DIRTY_ATIME 0x0001 + /* * Move expired (dirtied before work->older_than_this) dirty inodes from * @delaying_queue to @dispatch_queue. */ static int move_expired_inodes(struct list_head *delaying_queue, struct list_head *dispatch_queue, + int flags, struct wb_writeback_work *work) { + unsigned long *older_than_this = NULL; + unsigned long expire_time; LIST_HEAD(tmp); struct list_head *pos, *node; struct super_block *sb = NULL; @@ -262,13 +267,21 @@ static int move_expired_inodes(struct list_head *delaying_queue, int do_sb_sort = 0; int moved = 0; + if ((flags & EXPIRE_DIRTY_ATIME) == 0) + older_than_this = work->older_than_this; + else if ((work->reason == WB_REASON_SYNC) == 0) { + expire_time = jiffies - (HZ * 86400); + older_than_this = &expire_time; + } while (!list_empty(delaying_queue)) { inode = wb_inode(delaying_queue->prev); - if (work->older_than_this && - inode_dirtied_after(inode, *work->older_than_this)) + if (older_than_this && + inode_dirtied_after(inode, *older_than_this)) break; list_move(&inode->i_wb_list, &tmp); moved++; + if (flags & EXPIRE_DIRTY_ATIME) + set_bit(__I_DIRTY_TIME_EXPIRED, &inode->i_state); if (sb_is_blkdev_sb(inode->i_sb)) continue; if (sb && sb != inode->i_sb) @@ -309,9 +322,12 @@ out: static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work) { int moved; + assert_spin_locked(&wb->list_lock); list_splice_init(&wb->b_more_io, &wb->b_io); - moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, work); + moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work); + moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io, + EXPIRE_DIRTY_ATIME, work); trace_writeback_queue_io(wb, work, moved); } @@ -435,6 +451,8 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, * updates after data IO completion. */ redirty_tail(inode, wb); + } else if (inode->i_state & I_DIRTY_TIME) { + list_move(&inode->i_wb_list, &wb->b_dirty_time); } else { /* The inode is clean. Remove from writeback lists. */ list_del_init(&inode->i_wb_list); @@ -481,7 +499,13 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_lock(&inode->i_lock); dirty = inode->i_state & I_DIRTY; - inode->i_state &= ~I_DIRTY; + if (((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) && + (inode->i_state & I_DIRTY_TIME)) || + (inode->i_state & I_DIRTY_TIME_EXPIRED)) { + dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED; + trace_writeback_lazytime(inode); + } + inode->i_state &= ~dirty; /* * Paired with smp_mb() in __mark_inode_dirty(). This allows @@ -501,8 +525,10 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_unlock(&inode->i_lock); + if (dirty & I_DIRTY_TIME) + mark_inode_dirty_sync(inode); /* Don't write the inode if only I_DIRTY_PAGES was set */ - if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { + if (dirty & ~I_DIRTY_PAGES) { int err = write_inode(inode, wbc); if (ret == 0) ret = err; @@ -550,7 +576,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, * make sure inode is on some writeback list and leave it there unless * we have completely cleaned the inode. */ - if (!(inode->i_state & I_DIRTY) && + if (!(inode->i_state & I_DIRTY_ALL) && (wbc->sync_mode != WB_SYNC_ALL || !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK))) goto out; @@ -565,7 +591,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, * If inode is clean, remove it from writeback lists. Otherwise don't * touch it. See comment above for explanation. */ - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) list_del_init(&inode->i_wb_list); spin_unlock(&wb->list_lock); inode_sync_complete(inode); @@ -722,7 +748,7 @@ static long writeback_sb_inodes(struct super_block *sb, spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) wrote++; requeue_inode(inode, wb, &wbc); inode_sync_complete(inode); @@ -1160,16 +1186,20 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode) * page->mapping->host, so the page-dirtying time is recorded in the internal * blockdev inode. */ +#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC) void __mark_inode_dirty(struct inode *inode, int flags) { struct super_block *sb = inode->i_sb; struct backing_dev_info *bdi = NULL; + int dirtytime; + + trace_writeback_mark_inode_dirty(inode, flags); /* * Don't do this for I_DIRTY_PAGES - that doesn't actually * dirty the inode itself */ - if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { + if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_TIME)) { trace_writeback_dirty_inode_start(inode, flags); if (sb->s_op->dirty_inode) @@ -1177,6 +1207,9 @@ void __mark_inode_dirty(struct inode *inode, int flags) trace_writeback_dirty_inode(inode, flags); } + if (flags & I_DIRTY_INODE) + flags &= ~I_DIRTY_TIME; + dirtytime = flags & I_DIRTY_TIME; /* * Paired with smp_mb() in __writeback_single_inode() for the @@ -1184,16 +1217,21 @@ void __mark_inode_dirty(struct inode *inode, int flags) */ smp_mb(); - if ((inode->i_state & flags) == flags) + if (((inode->i_state & flags) == flags) || + (dirtytime && (inode->i_state & I_DIRTY_INODE))) return; if (unlikely(block_dump)) block_dump___mark_inode_dirty(inode); spin_lock(&inode->i_lock); + if (dirtytime && (inode->i_state & I_DIRTY_INODE)) + goto out_unlock_inode; if ((inode->i_state & flags) != flags) { const int was_dirty = inode->i_state & I_DIRTY; + if (flags & I_DIRTY_INODE) + inode->i_state &= ~I_DIRTY_TIME; inode->i_state |= flags; /* @@ -1240,8 +1278,10 @@ void __mark_inode_dirty(struct inode *inode, int flags) } inode->dirtied_when = jiffies; - list_move(&inode->i_wb_list, &bdi->wb.b_dirty); + list_move(&inode->i_wb_list, dirtytime ? + &bdi->wb.b_dirty_time : &bdi->wb.b_dirty); spin_unlock(&bdi->wb.list_lock); + trace_writeback_dirty_inode_enqueue(inode); if (wakeup_bdi) bdi_wakeup_thread_delayed(bdi); diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 80dd44dca028..e584bf94983f 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -654,7 +654,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, { struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; - int sync_state = inode->i_state & I_DIRTY; + int sync_state = inode->i_state & I_DIRTY_ALL; struct gfs2_inode *ip = GFS2_I(inode); int ret = 0, ret1 = 0; @@ -667,7 +667,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, if (!gfs2_is_jdata(ip)) sync_state &= ~I_DIRTY_PAGES; if (datasync) - sync_state &= ~I_DIRTY_SYNC; + sync_state &= ~(I_DIRTY_SYNC | I_DIRTY_TIME); if (sync_state) { ret = sync_inode_metadata(inode, 1); diff --git a/fs/inode.c b/fs/inode.c index 56d1d2b4bf31..9bdec9d1d71f 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -18,6 +18,7 @@ #include /* for inode_has_buffers */ #include #include +#include #include "internal.h" /* @@ -30,7 +31,7 @@ * inode_sb_list_lock protects: * sb->s_inodes, inode->i_sb_list * bdi->wb.list_lock protects: - * bdi->wb.b_{dirty,io,more_io}, inode->i_wb_list + * bdi->wb.b_{dirty,io,more_io,dirty_time}, inode->i_wb_list * inode_hash_lock protects: * inode_hashtable, inode->i_hash * @@ -414,7 +415,8 @@ static void inode_lru_list_add(struct inode *inode) */ void inode_add_lru(struct inode *inode) { - if (!(inode->i_state & (I_DIRTY | I_SYNC | I_FREEING | I_WILL_FREE)) && + if (!(inode->i_state & (I_DIRTY_ALL | I_SYNC | + I_FREEING | I_WILL_FREE)) && !atomic_read(&inode->i_count) && inode->i_sb->s_flags & MS_ACTIVE) inode_lru_list_add(inode); } @@ -645,7 +647,7 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) spin_unlock(&inode->i_lock); continue; } - if (inode->i_state & I_DIRTY && !kill_dirty) { + if (inode->i_state & I_DIRTY_ALL && !kill_dirty) { spin_unlock(&inode->i_lock); busy = 1; continue; @@ -1430,11 +1432,20 @@ static void iput_final(struct inode *inode) */ void iput(struct inode *inode) { - if (inode) { - BUG_ON(inode->i_state & I_CLEAR); - - if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) - iput_final(inode); + if (!inode) + return; + BUG_ON(inode->i_state & I_CLEAR); +retry: + if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) { + if (inode->i_nlink && (inode->i_state & I_DIRTY_TIME)) { + atomic_inc(&inode->i_count); + inode->i_state &= ~I_DIRTY_TIME; + spin_unlock(&inode->i_lock); + trace_writeback_lazytime_iput(inode); + mark_inode_dirty_sync(inode); + goto retry; + } + iput_final(inode); } } EXPORT_SYMBOL(iput); @@ -1493,14 +1504,9 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, return 0; } -/* - * This does the actual work of updating an inodes time or version. Must have - * had called mnt_want_write() before calling this. - */ -static int update_time(struct inode *inode, struct timespec *time, int flags) +int generic_update_time(struct inode *inode, struct timespec *time, int flags) { - if (inode->i_op->update_time) - return inode->i_op->update_time(inode, time, flags); + int iflags = I_DIRTY_TIME; if (flags & S_ATIME) inode->i_atime = *time; @@ -1510,9 +1516,27 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) inode->i_ctime = *time; if (flags & S_MTIME) inode->i_mtime = *time; - mark_inode_dirty_sync(inode); + + if (!(inode->i_sb->s_flags & MS_LAZYTIME) || (flags & S_VERSION)) + iflags |= I_DIRTY_SYNC; + __mark_inode_dirty(inode, iflags); return 0; } +EXPORT_SYMBOL(generic_update_time); + +/* + * This does the actual work of updating an inodes time or version. Must have + * had called mnt_want_write() before calling this. + */ +static int update_time(struct inode *inode, struct timespec *time, int flags) +{ + int (*update_time)(struct inode *, struct timespec *, int); + + update_time = inode->i_op->update_time ? inode->i_op->update_time : + generic_update_time; + + return update_time(inode, time, flags); +} /** * touch_atime - update the access time diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 33aa0cc1f8b8..10815f8dfd8b 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -39,7 +39,7 @@ int jfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) return rc; mutex_lock(&inode->i_mutex); - if (!(inode->i_state & I_DIRTY) || + if (!(inode->i_state & I_DIRTY_ALL) || (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) { /* Make sure committed changes hit the disk */ jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1); diff --git a/fs/libfs.c b/fs/libfs.c index 005843ce5dbd..b2ffdb045be4 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -948,7 +948,7 @@ int __generic_file_fsync(struct file *file, loff_t start, loff_t end, mutex_lock(&inode->i_mutex); ret = sync_mapping_buffers(inode->i_mapping); - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) goto out; if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) goto out; diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 73ca1740d839..f98234a190f2 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) { MS_SYNCHRONOUS, ",sync" }, { MS_DIRSYNC, ",dirsync" }, { MS_MANDLOCK, ",mand" }, + { MS_LAZYTIME, ",lazytime" }, { 0, NULL } }; const struct proc_fs_info *fs_infop; diff --git a/fs/sync.c b/fs/sync.c index bdc729d80e5e..6ac7bf07dfbe 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -177,8 +177,16 @@ SYSCALL_DEFINE1(syncfs, int, fd) */ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) { + struct inode *inode = file->f_mapping->host; + if (!file->f_op->fsync) return -EINVAL; + if (!datasync && (inode->i_state & I_DIRTY_TIME)) { + spin_lock(&inode->i_lock); + inode->i_state &= ~I_DIRTY_TIME; + spin_unlock(&inode->i_lock); + mark_inode_dirty_sync(inode); + } return file->f_op->fsync(file, start, end, datasync); } EXPORT_SYMBOL(vfs_fsync_range); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 5da6012b7a14..4cdf7336f64a 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -55,6 +55,7 @@ struct bdi_writeback { struct list_head b_dirty; /* dirty inodes */ struct list_head b_io; /* parked for writeback */ struct list_head b_more_io; /* parked for more writeback */ + struct list_head b_dirty_time; /* time stamps are dirty */ spinlock_t list_lock; /* protects the b_* lists */ }; diff --git a/include/linux/fs.h b/include/linux/fs.h index a45242664a7a..72dea272c4ac 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1727,8 +1727,12 @@ struct super_operations { #define __I_DIO_WAKEUP 9 #define I_DIO_WAKEUP (1 << I_DIO_WAKEUP) #define I_LINKABLE (1 << 10) +#define I_DIRTY_TIME (1 << 11) +#define __I_DIRTY_TIME_EXPIRED 12 +#define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED) #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) +#define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME) extern void __mark_inode_dirty(struct inode *, int); static inline void mark_inode_dirty(struct inode *inode) @@ -1891,6 +1895,7 @@ extern int current_umask(void); extern void ihold(struct inode * inode); extern void iput(struct inode *); +extern int generic_update_time(struct inode *, struct timespec *, int); static inline struct inode *file_inode(const struct file *f) { diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index cee02d65ab3f..5ecb4c234625 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -18,6 +18,8 @@ {I_FREEING, "I_FREEING"}, \ {I_CLEAR, "I_CLEAR"}, \ {I_SYNC, "I_SYNC"}, \ + {I_DIRTY_TIME, "I_DIRTY_TIME"}, \ + {I_DIRTY_TIME_EXPIRED, "I_DIRTY_TIME_EXPIRED"}, \ {I_REFERENCED, "I_REFERENCED"} \ ) @@ -68,6 +70,7 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, TP_STRUCT__entry ( __array(char, name, 32) __field(unsigned long, ino) + __field(unsigned long, state) __field(unsigned long, flags) ), @@ -78,16 +81,25 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, strncpy(__entry->name, bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32); __entry->ino = inode->i_ino; + __entry->state = inode->i_state; __entry->flags = flags; ), - TP_printk("bdi %s: ino=%lu flags=%s", + TP_printk("bdi %s: ino=%lu state=%s flags=%s", __entry->name, __entry->ino, + show_inode_state(__entry->state), show_inode_state(__entry->flags) ) ); +DEFINE_EVENT(writeback_dirty_inode_template, writeback_mark_inode_dirty, + + TP_PROTO(struct inode *inode, int flags), + + TP_ARGS(inode, flags) +); + DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode_start, TP_PROTO(struct inode *inode, int flags), @@ -598,6 +610,52 @@ DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode, TP_ARGS(inode, wbc, nr_to_write) ); +DECLARE_EVENT_CLASS(writeback_lazytime_template, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field(unsigned long, ino ) + __field(unsigned long, state ) + __field( __u16, mode ) + __field(unsigned long, dirtied_when ) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->state = inode->i_state; + __entry->mode = inode->i_mode; + __entry->dirtied_when = inode->dirtied_when; + ), + + TP_printk("dev %d,%d ino %lu dirtied %lu state %s mode 0%o", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, __entry->dirtied_when, + show_inode_state(__entry->state), __entry->mode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime_iput, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_dirty_inode_enqueue, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + #endif /* _TRACE_WRITEBACK_H */ /* This part must be outside protection */ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index ecdc36cf4d42..f3ef5016cf9c 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -90,6 +90,7 @@ struct inodes_stat_t { #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ #define MS_I_VERSION (1<<23) /* Update inode I_version field */ #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */ /* These sb flags are internal to the kernel */ #define MS_NOSEC (1<<28) @@ -100,7 +101,8 @@ struct inodes_stat_t { /* * Superblock flags that can be altered by MS_REMOUNT */ -#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION) +#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\ + MS_LAZYTIME) /* * Old magic mount flag and mask diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 0ae0df55000b..915feea94c66 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -69,10 +69,10 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) unsigned long background_thresh; unsigned long dirty_thresh; unsigned long bdi_thresh; - unsigned long nr_dirty, nr_io, nr_more_io; + unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time; struct inode *inode; - nr_dirty = nr_io = nr_more_io = 0; + nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0; spin_lock(&wb->list_lock); list_for_each_entry(inode, &wb->b_dirty, i_wb_list) nr_dirty++; @@ -80,6 +80,9 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) nr_io++; list_for_each_entry(inode, &wb->b_more_io, i_wb_list) nr_more_io++; + list_for_each_entry(inode, &wb->b_dirty_time, i_wb_list) + if (inode->i_state & I_DIRTY_TIME) + nr_dirty_time++; spin_unlock(&wb->list_lock); global_dirty_limits(&background_thresh, &dirty_thresh); @@ -98,6 +101,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) "b_dirty: %10lu\n" "b_io: %10lu\n" "b_more_io: %10lu\n" + "b_dirty_time: %10lu\n" "bdi_list: %10u\n" "state: %10lx\n", (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), @@ -111,6 +115,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) nr_dirty, nr_io, nr_more_io, + nr_dirty_time, !list_empty(&bdi->bdi_list), bdi->state); #undef K @@ -418,6 +423,7 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) INIT_LIST_HEAD(&wb->b_dirty); INIT_LIST_HEAD(&wb->b_io); INIT_LIST_HEAD(&wb->b_more_io); + INIT_LIST_HEAD(&wb->b_dirty_time); spin_lock_init(&wb->list_lock); INIT_DELAYED_WORK(&wb->dwork, bdi_writeback_workfn); } -- GitLab From b3a3eea5874bd5eb48dd760867380839730201cb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 21:47:24 -0700 Subject: [PATCH 0849/5498] f2fs: add lazytime mount option This patch adds lazytime support. Signed-off-by: Jaegeuk Kim Conflicts: fs/f2fs/super.c --- fs/f2fs/super.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 06c48dc1a2df..80bb604b4f54 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -109,6 +109,8 @@ enum { Opt_mode, Opt_io_size_bits, Opt_fault_injection, + Opt_lazytime, + Opt_nolazytime, Opt_quota, Opt_noquota, Opt_usrquota, @@ -156,6 +158,8 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, + {Opt_lazytime, "lazytime"}, + {Opt_nolazytime, "nolazytime"}, {Opt_quota, "quota"}, {Opt_noquota, "noquota"}, {Opt_usrquota, "usrquota"}, @@ -512,6 +516,12 @@ static int parse_options(struct super_block *sb, char *options) "FAULT_INJECTION was not selected"); #endif break; + case Opt_lazytime: + sb->s_flags |= MS_LAZYTIME; + break; + case Opt_nolazytime: + sb->s_flags &= ~MS_LAZYTIME; + break; #ifdef CONFIG_QUOTA case Opt_quota: case Opt_usrquota: @@ -743,6 +753,9 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) inode->i_ino == F2FS_META_INO(sbi)) return; + if (flags == I_DIRTY_TIME) + return; + if (is_inode_flag_set(inode, FI_AUTO_RECOVER)) clear_inode_flag(inode, FI_AUTO_RECOVER); @@ -1118,6 +1131,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, INLINE_DENTRY); set_opt(sbi, EXTENT_CACHE); set_opt(sbi, NOHEAP); + sbi->sb->s_flags |= MS_LAZYTIME; set_opt(sbi, FLUSH_MERGE); if (f2fs_sb_mounted_blkzoned(sbi->sb)) { set_opt_mode(sbi, F2FS_MOUNT_LFS); -- GitLab From 83e3e288e4fc6aa84a84cdd6f9b90eb62d62a95a Mon Sep 17 00:00:00 2001 From: Udaya Bhaskara Reddy Mallavarapu Date: Tue, 5 Sep 2017 12:01:41 +0530 Subject: [PATCH 0850/5498] drivers: media: broadcast: Fix security vulnerability issue Information leak issue is reported in mpq_sdmx_log_level_write function. Added check to validate count is not zero and initialize the string. Change-Id: Ieb2ed88c2d7d778c56be2ec3b9875270a9c74dce Signed-off-by: Udaya Bhaskara Reddy Mallavarapu --- .../media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c index 018e3ab88968..9c02da38e0d7 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c @@ -354,13 +354,17 @@ static ssize_t mpq_sdmx_log_level_write(struct file *fp, int level; struct mpq_demux *mpq_demux = fp->private_data; - if (count >= 16) + if (count == 0 || count >= 16) return -EINVAL; - ret_count = simple_write_to_buffer(user_str, 16, position, user_buffer, + memset(user_str, '\0', sizeof(user_str)); + + ret_count = simple_write_to_buffer(user_str, 15, position, user_buffer, count); if (ret_count < 0) return ret_count; + else if (ret_count == 0) + return -EINVAL; ret = kstrtoint(user_str, 0, &level); if (ret) -- GitLab From 36774bcf6900752bd51a440fb76217a1fa0e55f9 Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Wed, 13 Sep 2017 14:42:28 +0530 Subject: [PATCH 0851/5498] ARM: dts: msm: Update MPM interrupt bypass list for MDM9650 Add pcie global interrupt to MPM bypass list for MDM9650. Change-Id: I0e9215121a711c2b31f8da499803c0798fbf3e11 Signed-off-by: Rama Krishna Phani A --- arch/arm/boot/dts/qcom/mdm9650-pm.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/mdm9650-pm.dtsi b/arch/arm/boot/dts/qcom/mdm9650-pm.dtsi index 14bc3520047c..b03b5dce1897 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-pm.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650-pm.dtsi @@ -147,6 +147,7 @@ <0xff 63>, /* APCS_wcnIPCInterrupt(1) */ <0xff 66>, /* bam_irq[2] */ <0xff 73>, /* pcie20_int_msi */ + <0xff 81>, /* pcie20_int_global */ <0xff 88>, /* qpic_lcdc_irq */ <0xff 89>, /* qpic_bam_irq[1] */ <0xff 91>, /* qpic_nandc_op_done_irq */ -- GitLab From 5cb52d31fd41a216b977391044f732ce7f737b93 Mon Sep 17 00:00:00 2001 From: Rashi Bindra Date: Wed, 13 Sep 2017 14:49:07 +0530 Subject: [PATCH 0852/5498] defconfig: msm: Enable LT driver for apq8053_IoE-perf Add support for LT driver in defconfig for apq8053_IoE in perf builds. Thus, adding the configuartion. Change-Id: I6df73acdb1f7667de0100bab4d8ac4cc96f0ad6b Signed-off-by: Rashi Bindra --- arch/arm64/configs/apq8053_IoE-perf_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/apq8053_IoE-perf_defconfig b/arch/arm64/configs/apq8053_IoE-perf_defconfig index b163c06b623e..684b75be27f5 100644 --- a/arch/arm64/configs/apq8053_IoE-perf_defconfig +++ b/arch/arm64/configs/apq8053_IoE-perf_defconfig @@ -398,6 +398,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_HDMI_PANEL=y CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LT8912=y CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_USB_AUDIO=y -- GitLab From efe3e0123cb4e81154fcd8dce050f254ee8ea53d Mon Sep 17 00:00:00 2001 From: Trishansh Bhardwaj Date: Tue, 22 Aug 2017 16:51:43 +0530 Subject: [PATCH 0853/5498] msm: camera: Synchronize camera open/close calls. Synchronize camera_v4l2_open and camera_v4l2_close to avoid use after free. Change-Id: I1a203ae0753b265594f616496ab8c57e0521fd9f Signed-off-by: Trishansh Bhardwaj --- drivers/media/platform/msm/camera_v2/camera/camera.c | 6 ++++++ drivers/media/platform/msm/camera_v2/msm.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index 5bfcf282f563..12f07de2b7a3 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -623,6 +623,7 @@ static int camera_v4l2_open(struct file *filep) unsigned int opn_idx, idx; BUG_ON(!pvdev); + mutex_lock(&pvdev->video_drvdata_mutex); rc = camera_v4l2_fh_open(filep); if (rc < 0) { pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n", @@ -693,6 +694,7 @@ static int camera_v4l2_open(struct file *filep) idx |= (1 << find_first_zero_bit((const unsigned long *)&opn_idx, MSM_CAMERA_STREAM_CNT_BITS)); atomic_cmpxchg(&pvdev->opened, opn_idx, idx); + mutex_unlock(&pvdev->video_drvdata_mutex); return rc; @@ -707,6 +709,7 @@ stream_fail: vb2_q_fail: camera_v4l2_fh_release(filep); fh_open_fail: + mutex_unlock(&pvdev->video_drvdata_mutex); return rc; } @@ -737,6 +740,7 @@ static int camera_v4l2_close(struct file *filep) if (WARN_ON(!session)) return -EIO; + mutex_lock(&pvdev->video_drvdata_mutex); mutex_lock(&session->close_lock); opn_idx = atomic_read(&pvdev->opened); mask = (1 << sp->stream_id); @@ -778,6 +782,7 @@ static int camera_v4l2_close(struct file *filep) } camera_v4l2_fh_release(filep); + mutex_unlock(&pvdev->video_drvdata_mutex); return 0; } @@ -924,6 +929,7 @@ int camera_init_v4l2(struct device *dev, unsigned int *session) *session = pvdev->vdev->num; atomic_set(&pvdev->opened, 0); + mutex_init(&pvdev->video_drvdata_mutex); video_set_drvdata(pvdev->vdev, pvdev); device_init_wakeup(&pvdev->vdev->dev, 1); goto init_end; diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h index fec8ea484884..79d70a0fc1c4 100644 --- a/drivers/media/platform/msm/camera_v2/msm.h +++ b/drivers/media/platform/msm/camera_v2/msm.h @@ -45,6 +45,7 @@ extern bool is_daemon_status; struct msm_video_device { struct video_device *vdev; atomic_t opened; + struct mutex video_drvdata_mutex; }; struct msm_queue_head { -- GitLab From ae32fd2a4f2ac3e924542d2073d2db2d93e3bec1 Mon Sep 17 00:00:00 2001 From: Pranshu Gupta Date: Wed, 13 Sep 2017 15:46:02 +0530 Subject: [PATCH 0854/5498] ARM: dts: msm: add discard flag on system mount Discard flag helps to resolve adb reboot problem. Sometime after adb reboot device enter into fastboot mode because system is not mount. Change-Id: I53c153a3d93c4cba6b54570ba9725789eca28964 Signed-off-by: Pranshu Gupta --- arch/arm/boot/dts/qcom/msm8909.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 0aace78fbd65..9b901df3f2b1 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -124,7 +124,7 @@ compatible = "android,system"; dev = "/dev/block/platform/soc/7824900.sdhci/by-name/system"; type = "ext4"; - mnt_flags = "ro,barrier=1"; + mnt_flags = "ro,barrier=1,discard"; fsmgr_flags = "wait"; status = "ok"; }; -- GitLab From 488d76340f84887d0b7aad359fed821cc35df829 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Mon, 14 Aug 2017 15:53:45 +0530 Subject: [PATCH 0855/5498] clk: msm: clock: Add support to use Max uV for cpu clocks CPU clocks and GCC clocks need to vote for max uV and the new voltage level defined. Add support for the same by defining new voltage level. Change-Id: Ia7a5693d7177a1cc737091f7fe2aa7ba60ebfbb6 Signed-off-by: Odelu Kukatla --- drivers/clk/msm/clock-a7.c | 1 + drivers/clk/msm/vdd-level-9650.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/msm/clock-a7.c b/drivers/clk/msm/clock-a7.c index 96f52df8f830..1dd67b6daed6 100644 --- a/drivers/clk/msm/clock-a7.c +++ b/drivers/clk/msm/clock-a7.c @@ -221,6 +221,7 @@ static int of_get_fmax_vdd_class(struct platform_device *pdev, struct clk *c, devm_kfree(&pdev->dev, array); vdd->num_levels = prop_len; vdd->cur_level = prop_len; + vdd->use_max_uV = true; c->num_fmax = prop_len; return 0; } diff --git a/drivers/clk/msm/vdd-level-9650.h b/drivers/clk/msm/vdd-level-9650.h index d8f95b062fc2..6d496d4a0240 100644 --- a/drivers/clk/msm/vdd-level-9650.h +++ b/drivers/clk/msm/vdd-level-9650.h @@ -90,7 +90,7 @@ static int vdd_corner[] = { RPM_REGULATOR_LEVEL_LOW_SVS, /* VDD_DIG_LOWER */ RPM_REGULATOR_LEVEL_SVS, /* VDD_DIG_LOW */ RPM_REGULATOR_LEVEL_NOM, /* VDD_DIG_NOMINAL */ - RPM_REGULATOR_LEVEL_TURBO, /* VDD_DIG_HIGH */ + RPM_REGULATOR_LEVEL_TURBO_NO_CPR, /* VDD_DIG_HIGH */ }; #endif -- GitLab From 74a89b43e3899a25d88afec11cfd52944980567e Mon Sep 17 00:00:00 2001 From: Shefali Jain Date: Wed, 13 Sep 2017 18:20:13 +0530 Subject: [PATCH 0856/5498] ARM: dts: msm: Change the voltage level for cpu clock on MDM9650 For MDM9650, CPU clocks need to vote for max uV so change the voltage corner. Change-Id: Ie8dbf5b2f4ed5a2a0115a7fca85e5255be1223ea Signed-off-by: Odelu Kukatla --- arch/arm/boot/dts/qcom/mdm9650.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650.dtsi b/arch/arm/boot/dts/qcom/mdm9650.dtsi index 392c5e974092..2bb8775b13de 100644 --- a/arch/arm/boot/dts/qcom/mdm9650.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650.dtsi @@ -216,7 +216,7 @@ < 200000000 RPM_SMD_REGULATOR_LEVEL_LOW_SVS>, < 384000000 RPM_SMD_REGULATOR_LEVEL_SVS>, < 787200000 RPM_SMD_REGULATOR_LEVEL_NOM>, - <1286400000 RPM_SMD_REGULATOR_LEVEL_TURBO>; + <1286400000 RPM_SMD_REGULATOR_LEVEL_TURBO_NO_CPR>; cpu-vdd-supply = <&pmd9650_s5_level_ao>; qcom,a7ssmux-opp-store-vcorner = <&CPU0>; -- GitLab From 4760177747db0abf05d9b0f8f822b8faeb107838 Mon Sep 17 00:00:00 2001 From: Manaf Meethalavalappu Pallikunhi Date: Mon, 14 Aug 2017 17:09:21 +0530 Subject: [PATCH 0857/5498] ARM: dts: msm: Update VDD restriction parameters for mdm9650 Update thermal VDD restriction parameters for mdm9650 based on recommendation. Updated parameters are as below VDD restriction trigger threshold 10C VDD restriction clear threshold 15C VDD restriction voltage level value 416 Change-Id: I894445adac4fb035c257bc2b61dbc193181024e6 Signed-off-by: Manaf Meethalavalappu Pallikunhi --- arch/arm/boot/dts/qcom/mdm9650.dtsi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650.dtsi b/arch/arm/boot/dts/qcom/mdm9650.dtsi index 392c5e974092..6a94818615d3 100644 --- a/arch/arm/boot/dts/qcom/mdm9650.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650.dtsi @@ -1057,8 +1057,8 @@ qcom,limit-temp = <60>; qcom,temp-hysteresis = <10>; qcom,freq-step = <2>; - qcom,vdd-restriction-temp = <5>; - qcom,vdd-restriction-temp-hysteresis = <10>; + qcom,vdd-restriction-temp = <10>; + qcom,vdd-restriction-temp-hysteresis = <15>; vdd-dig-supply = <&pmd9650_s5_floor_level>; qcom,disable-cx-phase-ctrl; qcom,disable-gfx-phase-ctrl; @@ -1068,9 +1068,9 @@ qcom,vdd-dig-rstr{ qcom,vdd-rstr-reg = "vdd-dig"; - qcom,levels = ; + qcom,levels = ; qcom,min-level = ; }; }; -- GitLab From d149c03d0d894fbc92822444b371bf0f92afc6d9 Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Wed, 28 Jun 2017 16:46:44 -0700 Subject: [PATCH 0858/5498] ARM: dts: msm: Update sound card configuration on mdm9650 CCARD Update the pinctrl and dai configuration to support audio on mdm9650 CCARD. Change-Id: Icfcde376cfc78fccdc884cc6c55d90135fefdce4 Signed-off-by: Gustavo Solaira --- arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi | 58 +++++++++++++++++------ 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi b/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi index e6f9b162ca88..774cfe01da54 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi @@ -30,16 +30,19 @@ compatible = "qcom,mdm-audio-auto"; qcom,model = "mdm-auto-i2s-snd-card"; - qcom,mi2s-interface-mode = "pri_mi2s_master", "sec_mi2s_master"; - qcom,auxpcm-interface-mode = "pri_pcm_master", "sec_pcm_master"; + qcom,prim_mi2s_aux_master = <&prim_master>; + qcom,prim_mi2s_aux_slave = <&prim_slave>; + qcom,sec_mi2s_aux_master = <&sec_master>; + qcom,sec_mi2s_aux_slave = <&sec_slave>; asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, <&loopback>, <&hostless>, <&afe>, <&routing>, - <&pcm_dtmf>, <&host_pcm>; + <&pcm_dtmf>, <&host_pcm>, <&compress>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-pcm-hostless", "msm-pcm-afe", "msm-pcm-routing", - "msm-pcm-dtmf", "msm-voice-host-pcm"; + "msm-pcm-dtmf", "msm-voice-host-pcm", + "msm-compress-dsp"; asoc-cpu = <&dai_pri_auxpcm>, <&mi2s_prim>, <&mi2s_sec>, <&dtmf_tx>, <&rx_capture_tx>, <&rx_playback_rx>, @@ -208,8 +211,8 @@ }; }; - pmx_sec_mi2s_dout { - sec_mi2s_ws_sleep { + pmx_sec_mi2s_aux { + sec_ws_sleep: sec_ws_sleep { mux { pins = "gpio20"; function = "gpio"; @@ -223,7 +226,7 @@ }; }; - sec_mi2s_sck_sleep { + sec_sck_sleep: sec_sck_sleep { mux { pins = "gpio23"; function = "gpio"; @@ -237,7 +240,7 @@ }; }; - sec_mi2s_dout_sleep { + sec_dout_sleep: sec_dout_sleep { mux { pins = "gpio22"; function = "gpio"; @@ -251,7 +254,7 @@ }; }; - sec_mi2s_ws_active { + sec_ws_active_master: sec_ws_active_master { mux { pins = "gpio20"; function = "sec_mi2s_ws_b"; @@ -265,7 +268,7 @@ }; }; - sec_mi2s_sck_active { + sec_sck_active_master: sec_sck_active_master { mux { pins = "gpio23"; function = "sec_mi2s_sck_b"; @@ -279,7 +282,33 @@ }; }; - sec_mi2s_dout_active { + sec_ws_active_slave: sec_ws_active_slave { + mux { + pins = "gpio20"; + function = "sec_mi2s_ws_b"; + }; + + config { + pins = "gpio20"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL*/ + }; + }; + + sec_sck_active_slave: sec_sck_active_slave { + mux { + pins = "gpio23"; + function = "sec_mi2s_sck_b"; + }; + + config { + pins = "gpio23"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL*/ + }; + }; + + sec_dout_active: sec_dout_active { mux { pins = "gpio22"; function = "sec_mi2s_data1_b"; @@ -294,8 +323,8 @@ }; }; - pmx_sec_mi2s_din { - sec_mi2s_din_sleep { + pmx_sec_mi2s_aux_din { + sec_din_sleep: sec_din_sleep { mux { pins = "gpio21"; function = "gpio"; @@ -309,7 +338,7 @@ }; }; - sec_mi2s_din_active { + sec_din_active: sec_din_active { mux { pins = "gpio21"; function = "sec_mi2s_data0_b"; @@ -319,7 +348,6 @@ pins = "gpio21"; drive-strength = <8>; /* 8 mA */ bias-disable; /* NO PULL */ - output-high; }; }; }; -- GitLab From 4f35285448de8eb4a1dcdb7477cd367917435858 Mon Sep 17 00:00:00 2001 From: Shankar Ravi Date: Mon, 27 Mar 2017 18:44:48 +0530 Subject: [PATCH 0859/5498] msm: camera: Check for csid_irq status bit in case of time out Csid interrupt time out is happening due to system load. Check if the interrupt is actually done by hardware by reading the irq. Change-Id: I8a07f98d1b769c53762cd71e87f1900c683115cd Signed-off-by: Shankar Ravi --- .../msm/camera_v2/sensor/csid/msm_csid.c | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c index a89f33208e7b..e851c5600032 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c @@ -230,16 +230,34 @@ static void msm_csid_set_sof_freeze_debug_reg( static int msm_csid_reset(struct csid_device *csid_dev) { int32_t rc = 0; + uint32_t irq = 0, irq_bitshift; + + irq_bitshift = csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift; msm_camera_io_w(csid_dev->ctrl_reg->csid_reg.csid_rst_stb_all, csid_dev->base + csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr); rc = wait_for_completion_timeout(&csid_dev->reset_complete, CSID_TIMEOUT); - if (rc <= 0) { + if (rc < 0) { pr_err("wait_for_completion in msm_csid_reset fail rc = %d\n", rc); + } else if (rc == 0) { + irq = msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr); + pr_err_ratelimited("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n", + __func__, csid_dev->pdev->id, irq); + if (irq & (0x1 << irq_bitshift)) { + rc = 1; + CDBG("%s succeeded", __func__); + } else { + rc = 0; + pr_err("%s reset csid_irq_status failed = 0x%x\n", + __func__, irq); + } if (rc == 0) rc = -ETIMEDOUT; + } else { + CDBG("%s succeeded", __func__); } return rc; } -- GitLab From 31dffbf5b951e84f6513c30178746cf3797b4ade Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Tue, 1 Aug 2017 11:55:45 +0530 Subject: [PATCH 0860/5498] ARM: dts: msm: Disable GPU wake up on touch event for msm8909w For 8909 wearable disable GPU wake up on touch input EV_ABS event, to save the the power. Change-Id: I001d83e326eb0dd6ff71dce2a57a61e46f26fdbf Signed-off-by: Hareesh Gundu --- arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi index e81105993830..6fb509315362 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,9 @@ /delete-property/qcom,gpu-speed-config; + /* To disable GPU wake up on touch event */ + qcom,disable-wake-on-touch; + /* Bus Scale Settings */ qcom,msm-bus,num-cases = <3>; qcom,msm-bus,vectors-KBps = -- GitLab From 902182738340280701d0f0c415a9f419a110962e Mon Sep 17 00:00:00 2001 From: Srinivasarao P Date: Thu, 31 Aug 2017 16:14:21 +0530 Subject: [PATCH 0861/5498] defconfig: Disable CONFIG_SYSVIPC for msm Android SELinux policies block SysV IPC. New kernels should not be built with it. Change-Id: I64296bd66b4297d550071fb8292726ca57a2ae15 Signed-off-by: Srinivasarao P --- arch/arm64/configs/msm-perf_defconfig | 1 - arch/arm64/configs/msm_defconfig | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 0947a0197471..132ac7640db9 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -1,5 +1,4 @@ CONFIG_LOCALVERSION="-perf" -CONFIG_SYSVIPC=y # CONFIG_USELIB is not set CONFIG_AUDIT=y # CONFIG_AUDITSYSCALL is not set diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 4021d5ca3481..279316fe94c7 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -1,4 +1,3 @@ -CONFIG_SYSVIPC=y # CONFIG_USELIB is not set CONFIG_AUDIT=y # CONFIG_AUDITSYSCALL is not set -- GitLab From c7dee2828c5bf5203fd78f78f763260ef0cb428b Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Wed, 13 Sep 2017 18:01:10 +0530 Subject: [PATCH 0862/5498] msm: ais: Synchronize camera open/close calls. Synchronize camera_v4l2_open and camera_v4l2_close to avoid use after free. Change-Id: I4055f00ff30408cc0469015b3e49d877632726bf Signed-off-by: Rahul Sharma --- drivers/media/platform/msm/ais/camera/camera.c | 6 ++++++ drivers/media/platform/msm/ais/msm.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/media/platform/msm/ais/camera/camera.c b/drivers/media/platform/msm/ais/camera/camera.c index d5db823482be..bd82033fe5e6 100644 --- a/drivers/media/platform/msm/ais/camera/camera.c +++ b/drivers/media/platform/msm/ais/camera/camera.c @@ -627,6 +627,7 @@ static int camera_v4l2_open(struct file *filep) if (WARN_ON(!pvdev)) return -EIO; + mutex_lock(&pvdev->video_drvdata_mutex); rc = camera_v4l2_fh_open(filep); if (rc < 0) { pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n", @@ -697,6 +698,7 @@ static int camera_v4l2_open(struct file *filep) idx |= (1 << find_first_zero_bit((const unsigned long *)&opn_idx, MSM_CAMERA_STREAM_CNT_BITS)); atomic_cmpxchg(&pvdev->opened, opn_idx, idx); + mutex_unlock(&pvdev->video_drvdata_mutex); return rc; @@ -711,6 +713,7 @@ stream_fail: vb2_q_fail: camera_v4l2_fh_release(filep); fh_open_fail: + mutex_unlock(&pvdev->video_drvdata_mutex); return rc; } @@ -745,6 +748,7 @@ static int camera_v4l2_close(struct file *filep) if (WARN_ON(!session)) return -EIO; + mutex_lock(&pvdev->video_drvdata_mutex); mutex_lock(&session->close_lock); opn_idx = atomic_read(&pvdev->opened); mask = (1 << sp->stream_id); @@ -786,6 +790,7 @@ static int camera_v4l2_close(struct file *filep) } camera_v4l2_fh_release(filep); + mutex_unlock(&pvdev->video_drvdata_mutex); return 0; } @@ -932,6 +937,7 @@ int camera_init_v4l2(struct device *dev, unsigned int *session) *session = pvdev->vdev->num; atomic_set(&pvdev->opened, 0); + mutex_init(&pvdev->video_drvdata_mutex); video_set_drvdata(pvdev->vdev, pvdev); device_init_wakeup(&pvdev->vdev->dev, 1); goto init_end; diff --git a/drivers/media/platform/msm/ais/msm.h b/drivers/media/platform/msm/ais/msm.h index 8a316c61c22d..5c48368fd3f8 100644 --- a/drivers/media/platform/msm/ais/msm.h +++ b/drivers/media/platform/msm/ais/msm.h @@ -45,6 +45,7 @@ extern bool is_daemon_status; struct msm_video_device { struct video_device *vdev; atomic_t opened; + struct mutex video_drvdata_mutex; }; struct msm_queue_head { -- GitLab From 2a15d414852ceb3c7a5c46f734ce6c64a227a458 Mon Sep 17 00:00:00 2001 From: Puja Gupta Date: Thu, 2 Feb 2017 13:41:04 -0800 Subject: [PATCH 0863/5498] soc: qcom: pil: Fix error case scenario in subsystem ramdump During ramdump collection we assign memory to HLOS from subsystem for non-secure pil. Whether ramdump collection is successful or not, we should assign memory back to subsystem. This is to avoid access violations in powerup path which happens after ramdump. CRs-Fixed: 2002073 Change-Id: I7f1d42aebb44464fe077ca544ce91c2d7a8eefbb Signed-off-by: Puja Gupta --- drivers/soc/qcom/peripheral-loader.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index d17d2e8d231b..f239a43c0264 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -172,7 +172,11 @@ int pil_do_ramdump(struct pil_desc *desc, void *ramdump_dev) ret = do_elf_ramdump(ramdump_dev, ramdump_segs, count); kfree(ramdump_segs); - if (!ret && desc->subsys_vmid > 0) + if (ret) + pil_err(desc, "%s: Ramdump collection failed for subsys %s rc:%d\n", + __func__, desc->name, ret); + + if (desc->subsys_vmid > 0) ret = pil_assign_mem_to_subsys(desc, priv->region_start, (priv->region_end - priv->region_start)); -- GitLab From f8737e6f304de1df5fd64ef13dfa7df6357d8908 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Thu, 14 Sep 2017 17:52:00 +0530 Subject: [PATCH 0864/5498] msm: ais: Instead of read_lock use read_lock_irqsave Prevent deadlock between tasklet and delete_stream by stopping irq during delete_stream. Change-Id: Ib6b1cbf35ffa0fccf210103c096e2a4ab70426e4 CRs-Fixed: 2076578 Signed-off-by: Rahul Sharma --- drivers/media/platform/msm/ais/msm.c | 11 ++- .../media/platform/msm/ais/msm_vb2/msm_vb2.c | 83 ++++++++++--------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c index f6d6a45c2988..b450cb97901e 100644 --- a/drivers/media/platform/msm/ais/msm.c +++ b/drivers/media/platform/msm/ais/msm.c @@ -292,6 +292,7 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) return; while (1) { + unsigned long wl_flags; if (try_count > 5) { pr_err("%s : not able to delete stream %d\n", @@ -299,18 +300,20 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) break; } - write_lock(&session->stream_rwlock); + write_lock_irqsave(&session->stream_rwlock, wl_flags); try_count++; stream = msm_queue_find(&session->stream_q, struct msm_stream, list, __msm_queue_find_stream, &stream_id); if (!stream) { - write_unlock(&session->stream_rwlock); + write_unlock_irqrestore(&session->stream_rwlock, + wl_flags); return; } if (msm_vb2_get_stream_state(stream) != 1) { - write_unlock(&session->stream_rwlock); + write_unlock_irqrestore(&session->stream_rwlock, + wl_flags); continue; } @@ -320,7 +323,7 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) kfree(stream); stream = NULL; spin_unlock_irqrestore(&(session->stream_q.lock), flags); - write_unlock(&session->stream_rwlock); + write_unlock_irqrestore(&session->stream_rwlock, wl_flags); break; } diff --git a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c index aa2f6969b5d5..f782b98eda82 100644 --- a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c @@ -46,22 +46,23 @@ int msm_vb2_buf_init(struct vb2_buffer *vb) struct msm_stream *stream; struct msm_session *session; struct msm_vb2_buffer *msm_vb2_buf; + unsigned long rl_flags; session = msm_get_session_from_vb2q(vb->vb2_queue); if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s: Couldn't find stream\n", __func__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } msm_vb2_buf = container_of(vb, struct msm_vb2_buffer, vb2_buf); msm_vb2_buf->in_freeq = 0; - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return 0; } @@ -70,7 +71,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct msm_session *session; - unsigned long flags; + unsigned long flags, rl_flags; msm_vb2 = container_of(vb, struct msm_vb2_buffer, vb2_buf); @@ -83,19 +84,19 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) if (IS_ERR_OR_NULL(session)) return; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return; } spin_lock_irqsave(&stream->stream_lock, flags); list_add_tail(&msm_vb2->list, &stream->queued_list); spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); } static void msm_vb2_buf_finish(struct vb2_buffer *vb) @@ -103,7 +104,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct msm_session *session; - unsigned long flags; + unsigned long flags, rl_flags; struct msm_vb2_buffer *msm_vb2_entry, *temp; msm_vb2 = container_of(vb, struct msm_vb2_buffer, vb2_buf); @@ -117,12 +118,12 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) if (IS_ERR_OR_NULL(session)) return; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return; } @@ -135,7 +136,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) } } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); } static void msm_vb2_stop_stream(struct vb2_queue *q) @@ -143,19 +144,19 @@ static void msm_vb2_stop_stream(struct vb2_queue *q) struct msm_vb2_buffer *msm_vb2, *temp; struct msm_stream *stream; struct msm_session *session; - unsigned long flags; + unsigned long flags, rl_flags; struct vb2_buffer *vb2_buf; session = msm_get_session_from_vb2q(q); if (IS_ERR_OR_NULL(session)) return; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream_from_vb2q(q); if (!stream) { pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return; } @@ -174,7 +175,7 @@ static void msm_vb2_stop_stream(struct vb2_queue *q) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); } int msm_vb2_get_stream_state(struct msm_stream *stream) @@ -252,17 +253,17 @@ static struct vb2_buffer *msm_vb2_get_buf(int session_id, struct vb2_buffer *vb2_buf = NULL; struct msm_session *session; struct msm_vb2_buffer *msm_vb2 = NULL; - unsigned long flags; + unsigned long flags, rl_flags; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return NULL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return NULL; } @@ -288,7 +289,7 @@ static struct vb2_buffer *msm_vb2_get_buf(int session_id, vb2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return vb2_buf; } @@ -299,18 +300,18 @@ static struct vb2_buffer *msm_vb2_get_buf_by_idx(int session_id, struct vb2_buffer *vb2_buf = NULL; struct msm_session *session; struct msm_vb2_buffer *msm_vb2 = NULL; - unsigned long flags; + unsigned long flags, rl_flags; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return NULL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return NULL; } @@ -334,7 +335,7 @@ static struct vb2_buffer *msm_vb2_get_buf_by_idx(int session_id, vb2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return vb2_buf; } @@ -346,17 +347,17 @@ static int msm_vb2_put_buf(struct vb2_buffer *vb, int session_id, struct msm_vb2_buffer *msm_vb2; struct vb2_buffer *vb2_buf = NULL; int rc = 0; - unsigned long flags; + unsigned long flags, rl_flags; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -371,6 +372,8 @@ static int msm_vb2_put_buf(struct vb2_buffer *vb, int session_id, pr_err("VB buffer is INVALID vb=%pK, ses_id=%d, str_id=%d\n", vb, session_id, stream_id); spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock_irqrestore(&session->stream_rwlock, + rl_flags); return -EINVAL; } msm_vb2 = @@ -386,7 +389,7 @@ static int msm_vb2_put_buf(struct vb2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return rc; } @@ -394,7 +397,7 @@ static int msm_vb2_buf_done(struct vb2_buffer *vb, int session_id, unsigned int stream_id, uint32_t sequence, struct timeval *ts, uint32_t reserved) { - unsigned long flags; + unsigned long flags, rl_flags; struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct vb2_buffer *vb2_buf = NULL; @@ -405,11 +408,11 @@ static int msm_vb2_buf_done(struct vb2_buffer *vb, int session_id, if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -424,6 +427,8 @@ static int msm_vb2_buf_done(struct vb2_buffer *vb, int session_id, pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n", session_id, stream_id, vb); spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock_irqrestore(&session->stream_rwlock, + rl_flags); return -EINVAL; } msm_vb2 = @@ -444,7 +449,7 @@ static int msm_vb2_buf_done(struct vb2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return rc; } @@ -455,18 +460,18 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, struct vb2_buffer *vb2_buf = NULL; struct msm_session *session; struct msm_vb2_buffer *msm_vb2 = NULL; - unsigned long flags; + unsigned long flags, rl_flags; long rc = -EINVAL; session = msm_get_session(session_id); if (IS_ERR_OR_NULL(session)) return rc; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -494,14 +499,14 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, end: spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return rc; } EXPORT_SYMBOL(msm_vb2_return_buf_by_idx); static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) { - unsigned long flags; + unsigned long flags, rl_flags; struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct vb2_buffer *vb2_buf = NULL; @@ -511,11 +516,11 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) if (IS_ERR_OR_NULL(session)) return -EINVAL; - read_lock(&session->stream_rwlock); + read_lock_irqsave(&session->stream_rwlock, rl_flags); stream = msm_get_stream(session, stream_id); if (IS_ERR_OR_NULL(stream)) { - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return -EINVAL; } @@ -527,7 +532,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock(&session->stream_rwlock); + read_unlock_irqrestore(&session->stream_rwlock, rl_flags); return 0; } -- GitLab From a0cd7f822f9365d7067b29ba5722d489473ac841 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 14 Sep 2017 21:25:27 +0530 Subject: [PATCH 0865/5498] soc: qcom: bgcom: add bg ssr ramdump Add bg ssr ramdump and provide SPI exclusion between HLOS and TZ Avoid xpu voilation at boot up and ramdump Signed-off-by: Arjun Singh Change-Id: I20657ef25d07629c78386a0c0c5f38e28d32191a --- drivers/soc/qcom/bgcom_interface.c | 1 + drivers/soc/qcom/bgcom_spi.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/soc/qcom/bgcom_interface.c b/drivers/soc/qcom/bgcom_interface.c index 29dee0313077..bf180456a152 100644 --- a/drivers/soc/qcom/bgcom_interface.c +++ b/drivers/soc/qcom/bgcom_interface.c @@ -285,6 +285,7 @@ static int ssr_bg_cb(struct notifier_block *this, switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: bge.e_type = BG_BEFORE_POWER_DOWN; + bgcom_set_spi_state(BGCOM_SPI_BUSY); send_uevent(&bge); break; case SUBSYS_AFTER_POWERUP: diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c index 13658bf0eeb3..745438419cb5 100644 --- a/drivers/soc/qcom/bgcom_spi.c +++ b/drivers/soc/qcom/bgcom_spi.c @@ -40,6 +40,7 @@ #define BG_SPI_MAX_WORDS (0x3FFFFFFD) #define BG_SPI_MAX_REGS (0x0A) +#define SLEEP_IN_STATE_CHNG 2000 enum bgcom_state { /*BGCOM Staus ready*/ @@ -90,9 +91,19 @@ static enum bgcom_spi_state spi_state; int bgcom_set_spi_state(enum bgcom_spi_state state) { + struct bg_spi_priv *bg_spi = container_of(bg_com_drv, + struct bg_spi_priv, lhandle); if (state < 0 || state > 1) return -EINVAL; + + if (state == spi_state) + return 0; + + mutex_lock(&bg_spi->xfer_mutex); spi_state = state; + if (spi_state == BGCOM_SPI_BUSY) + msleep(SLEEP_IN_STATE_CHNG); + mutex_unlock(&bg_spi->xfer_mutex); return 0; } EXPORT_SYMBOL(bgcom_set_spi_state); -- GitLab From 284f963af0accf7f921ec10e23acafd71c3a724b Mon Sep 17 00:00:00 2001 From: Xu YiPing Date: Tue, 5 Sep 2017 10:21:52 -0700 Subject: [PATCH 0866/5498] FROMLIST: binder: fix memory corruption in binder_transaction binder (from https://patchwork.kernel.org/patch/9939405/) commit 7a4408c6bd3e ("binder: make sure accesses to proc/thread are safe") made a change to enqueue tcomplete to thread->todo before enqueuing the transaction. However, in err_dead_proc_or_thread case, the tcomplete is directly freed, without dequeued. It may cause the thread->todo list to be corrupted. So, dequeue it before freeing. Bug: 65333488 Change-Id: I14ef48095d9f690148b1a50ea62d05dd67779505 Signed-off-by: Xu YiPing Signed-off-by: Todd Kjos Git-commit: 86578a0fd70edffb11c78b5df85b8e113e44bfe1 Git-repo: https://android.googlesource.com/kernel/common Signed-off-by: Kyle Yan Signed-off-by: Srinivasarao P --- drivers/staging/android/binder.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index b356ad7d7c62..705906ac217f 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -3250,6 +3250,7 @@ static void binder_transaction(struct binder_proc *proc, err_dead_proc_or_thread: return_error = BR_DEAD_REPLY; return_error_line = __LINE__; + binder_dequeue_work(proc, tcomplete); err_translate_failed: err_bad_object_type: err_bad_offset: -- GitLab From c7722df2cec55450130a487a6ffd6409653f85d6 Mon Sep 17 00:00:00 2001 From: Divya Narayanan Poojary Date: Wed, 13 Sep 2017 20:01:02 +0530 Subject: [PATCH 0867/5498] ASoC: msm: Handle bit_width to configure ASM session for FLAC playback ASM session is configured with 16 bit for 24 bit flac clips. Read sample size from compress params passed by the userspace and configure ASM session accordingly. CRs-Fixed: 2086257 Change-Id: Id4ae560b423b6f997661836c50989d2996c91b54 Signed-off-by: Divya Narayanan Poojary --- sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 8aa3d726f794..838cc2a66e93 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -1062,6 +1062,9 @@ static int msm_compr_configure_dsp_for_playback int dir = IN, ret = 0; struct audio_client *ac = prtd->audio_client; uint32_t stream_index; + union snd_codec_options *codec_options = + &(prtd->codec_param.codec.options); + struct asm_softpause_params softpause = { .enable = SOFT_PAUSE_ENABLE, .period = SOFT_PAUSE_PERIOD, @@ -1086,6 +1089,9 @@ static int msm_compr_configure_dsp_for_playback bits_per_sample = 24; else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE) bits_per_sample = 32; + else if (prtd->codec == FORMAT_FLAC && codec_options && + (codec_options->flac_dec.sample_size != 0)) + bits_per_sample = codec_options->flac_dec.sample_size; if (prtd->compr_passthr != LEGACY_PCM) { ret = q6asm_open_write_compressed(ac, prtd->codec, @@ -1899,6 +1905,8 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) int stream_id; uint32_t stream_index; uint16_t bits_per_sample = 16; + union snd_codec_options *codec_options = + &(prtd->codec_param.codec.options); spin_lock_irqsave(&prtd->lock, flags); if (atomic_read(&prtd->error)) { @@ -2318,6 +2326,9 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE) bits_per_sample = 32; + else if (prtd->codec == FORMAT_FLAC && codec_options && + (codec_options->flac_dec.sample_size != 0)) + bits_per_sample = codec_options->flac_dec.sample_size; pr_debug("%s: open_write stream_id %d bits_per_sample %d", __func__, stream_id, bits_per_sample); -- GitLab From 4dfdfa055e8bd6872a32a0d860125f62bc1025ab Mon Sep 17 00:00:00 2001 From: Archana Obannagari Date: Fri, 8 Sep 2017 20:35:28 +0530 Subject: [PATCH 0868/5498] msm: kgsl: Fixing an uninitialized variable in _gpuobj_map_useraddr() Uninitalized kernel variable passes to userspace, it may have data from stack. To avoid data leak from kernel to userspace initializes struct kgsl_gpuobj_import_useraddr to zero. CRs-Fixed: 2096006 Change-Id: Ib79b030cd5e3edd7279632af20dc3fac95eb73d4 Signed-off-by: Archana Obannagari --- drivers/gpu/msm/kgsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 563cb5ea2cd3..0abcb6e962b0 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -2163,7 +2163,7 @@ static long _gpuobj_map_useraddr(struct kgsl_device *device, struct kgsl_mem_entry *entry, struct kgsl_gpuobj_import *param) { - struct kgsl_gpuobj_import_useraddr useraddr; + struct kgsl_gpuobj_import_useraddr useraddr = {0}; int ret; param->flags &= KGSL_MEMFLAGS_GPUREADONLY -- GitLab From 5d781e3a6be30f22ca8418f5cfc3e8701a2ae1fe Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Thu, 24 Aug 2017 23:11:09 +0530 Subject: [PATCH 0869/5498] msm: kgsl: Do not crash on context detach timeout failure If context wait for global timeouts in detach path there is no use to process its pending commands. Hence to handle context detach timeout failure,invalidate the context and remove all the pending commands from that context. So that other context commands proceed successfully after the context detach timeout fault recovery. Change-Id: Ie4ff0ed5d08312d345b248a2404ce085552b0b09 Signed-off-by: Hareesh Gundu --- drivers/gpu/msm/adreno.h | 1 + drivers/gpu/msm/adreno_dispatch.c | 3 ++- drivers/gpu/msm/adreno_drawctxt.c | 34 ++++++++++++++++++++++--------- drivers/gpu/msm/adreno_drawctxt.h | 4 +++- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 3cb40c2ed275..80640e123444 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -182,6 +182,7 @@ enum adreno_gpurev { #define ADRENO_TIMEOUT_FAULT BIT(2) #define ADRENO_IOMMU_PAGE_FAULT BIT(3) #define ADRENO_PREEMPT_FAULT BIT(4) +#define ADRENO_CTX_DETATCH_TIMEOUT_FAULT BIT(5) #define ADRENO_SPTP_PC_CTRL 0 #define ADRENO_PPD_CTRL 1 diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index ab1aaee5127a..98ff434a64e1 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -1519,7 +1519,8 @@ static void process_cmdbatch_fault(struct kgsl_device *device, * because we won't see this cmdbatch again */ - if (fault & ADRENO_TIMEOUT_FAULT) + if ((fault & ADRENO_TIMEOUT_FAULT) || + (fault & ADRENO_CTX_DETATCH_TIMEOUT_FAULT)) bitmap_zero(&cmdbatch->fault_policy, BITS_PER_LONG); /* diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 8a66875e414f..13ef0f51e4ae 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -297,6 +297,7 @@ void adreno_drawctxt_invalidate(struct kgsl_device *device, /* Give the bad news to everybody waiting around */ wake_up_all(&drawctxt->waiting); wake_up_all(&drawctxt->wq); + wake_up_all(&drawctxt->timeout); } /* @@ -389,6 +390,7 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv, spin_lock_init(&drawctxt->lock); init_waitqueue_head(&drawctxt->wq); init_waitqueue_head(&drawctxt->waiting); + init_waitqueue_head(&drawctxt->timeout); /* Set the context priority */ _set_context_priority(drawctxt); @@ -501,20 +503,32 @@ void adreno_drawctxt_detach(struct kgsl_context *context) drawctxt->internal_timestamp, 30 * 1000); /* - * If the wait for global fails due to timeout then nothing after this - * point is likely to work very well - Get GPU snapshot and BUG_ON() - * so we can take advantage of the debug tools to figure out what the - * h - e - double hockey sticks happened. If EAGAIN error is returned + * If the wait for global fails due to timeout then mark it as + * context detach timeout fault and schedule dispatcher to kick + * in GPU recovery. For a ADRENO_CTX_DETATCH_TIMEOUT_FAULT we clear + * the policy and invalidate the context. If EAGAIN error is returned * then recovery will kick in and there will be no more commands in the - * RB pipe from this context which is waht we are waiting for, so ignore - * -EAGAIN error + * RB pipe from this context which is what we are waiting for, so ignore + * -EAGAIN error. */ if (ret && ret != -EAGAIN) { - KGSL_DRV_ERR(device, "Wait for global ts=%d type=%d error=%d\n", - drawctxt->internal_timestamp, + KGSL_DRV_ERR(device, + "Wait for global ctx=%d ts=%d type=%d error=%d\n", + drawctxt->base.id, drawctxt->internal_timestamp, drawctxt->type, ret); - device->force_panic = 1; - kgsl_device_snapshot(device, context); + + adreno_set_gpu_fault(adreno_dev, + ADRENO_CTX_DETATCH_TIMEOUT_FAULT); + mutex_unlock(&device->mutex); + + /* Schedule dispatcher to kick in recovery */ + adreno_dispatcher_schedule(device); + + /* Wait for context to be invalidated and release context */ + ret = wait_event_interruptible_timeout(drawctxt->timeout, + kgsl_context_invalid(&drawctxt->base), + msecs_to_jiffies(5000)); + return; } kgsl_sharedmem_writel(device, &device->memstore, diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h index 5ea911954991..0908862fe068 100644 --- a/drivers/gpu/msm/adreno_drawctxt.h +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -39,6 +39,7 @@ struct kgsl_context; * @pending: Priority list node for the dispatcher list of pending contexts * @wq: Workqueue structure for contexts to sleep pending room in the queue * @waiting: Workqueue structure for contexts waiting for a timestamp or event + * @timeout: Workqueue structure for contexts waiting to invalidate * @queued: Number of commands queued in the cmdqueue * @fault_policy: GFT fault policy set in cmdbatch_skip_cmd(); * @debug_root: debugfs entry for this context. @@ -67,6 +68,7 @@ struct adreno_context { struct plist_node pending; wait_queue_head_t wq; wait_queue_head_t waiting; + wait_queue_head_t timeout; int queued; unsigned int fault_policy; -- GitLab From 020f38341d737352992806f1df24f119f6d7efd3 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Sat, 9 Sep 2017 12:49:01 +0530 Subject: [PATCH 0870/5498] usb: gadget: f_gps: Do not clean up response queue on suspend The current driver cleans up the response queue on suspend. This results in data loss. Fix this by not clearing the responses on suspend and wakeup the host if any response is available. And the response is also dropped if there is an enqueuing error on interrupt endpoint which might lead to data loss. Fix this by not dropping the response on error. If the device is suspended and remote wakeup is not enabled, all the responses will be queued and may lead to a memory shortage. Fix this by limiting the responses that can be queued. CRs-Fixed: 2109762 Change-Id: I4390602875d3e85f333b107b058e299f973c837c Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/function/f_gps.c | 133 +++++++++++++++++----------- 1 file changed, 82 insertions(+), 51 deletions(-) diff --git a/drivers/usb/gadget/function/f_gps.c b/drivers/usb/gadget/function/f_gps.c index f25c79a68217..5b2e37a28f72 100644 --- a/drivers/usb/gadget/function/f_gps.c +++ b/drivers/usb/gadget/function/f_gps.c @@ -22,7 +22,7 @@ #define GPS_NOTIFY_INTERVAL 5 #define GPS_MAX_NOTIFY_SIZE 64 - +#define GPS_RESP_Q_LENGTH 100 #define ACM_CTRL_DTR (1 << 0) @@ -46,11 +46,14 @@ struct f_gps { /* control info */ struct list_head cpkt_resp_q; atomic_t notify_count; + atomic_t resp_q_count; unsigned long cpkts_len; /* remote wakeup info */ bool is_suspended; bool is_rw_allowed; + + struct delayed_work wakeup_work; }; static struct gps_ports { @@ -146,6 +149,10 @@ static struct usb_gadget_strings *gps_strings[] = { static void gps_ctrl_response_available(struct f_gps *dev); +static void gps_queue_notify_request(struct f_gps *dev); + +static int gps_wakeup_host(struct f_gps *dev); + /* ------- misc functions --------------------*/ static inline struct f_gps *func_to_gps(struct usb_function *f) @@ -245,9 +252,18 @@ static void gps_unbind(struct usb_configuration *c, struct usb_function *f) gps_free_req(dev->notify, dev->notify_req); + cancel_delayed_work_sync(&dev->wakeup_work); kfree(f->name); } +static void gps_remote_wakeup_work(struct work_struct *data) +{ + struct f_gps *dev = container_of(data, struct f_gps, wakeup_work.work); + + pr_debug("%s: wakeup host\n", __func__); + gps_wakeup_host(dev); +} + static void gps_purge_responses(struct f_gps *dev) { unsigned long flags; @@ -265,12 +281,16 @@ static void gps_purge_responses(struct f_gps *dev) rmnet_free_ctrl_pkt(cpkt); } atomic_set(&dev->notify_count, 0); + atomic_set(&dev->resp_q_count, 0); spin_unlock_irqrestore(&dev->lock, flags); } +#define GPS_WAKEUP_HOST_DELAY msecs_to_jiffies(100) + static void gps_suspend(struct usb_function *f) { struct f_gps *dev = func_to_gps(f); + unsigned long flags; pr_debug("%s: suspending gps function\n", __func__); if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) @@ -278,13 +298,26 @@ static void gps_suspend(struct usb_function *f) else dev->is_rw_allowed = f->config->cdev->gadget->remote_wakeup; - gps_purge_responses(dev); dev->is_suspended = true; + spin_lock_irqsave(&dev->lock, flags); + /* + * The notify count is set to zero and the correct count of + * notifications for responses received (before suspend + during + * suspend) will be handled during resume + */ + atomic_set(&dev->notify_count, 0); + if (dev->is_rw_allowed && (atomic_read(&dev->notify_count) > 0)) { + pr_debug("%s: queue not empty; wakeup host\n", __func__); + schedule_delayed_work(&dev->wakeup_work, + GPS_WAKEUP_HOST_DELAY); + } + spin_unlock_irqrestore(&dev->lock, flags); } static void gps_resume(struct usb_function *f) { struct f_gps *dev = func_to_gps(f); + struct list_head *cpkt; pr_debug("%s: resume gps function, func_is_supended:%d\n", __func__, f->func_is_suspended); @@ -304,7 +337,9 @@ static void gps_resume(struct usb_function *f) return; } spin_unlock(&dev->lock); - gps_ctrl_response_available(dev); + + list_for_each(cpkt, &dev->cpkt_resp_q) + gps_ctrl_response_available(dev); } static int gps_get_status(struct usb_function *f) @@ -381,6 +416,8 @@ static void gps_disable(struct usb_function *f) atomic_set(&dev->online, 0); + cancel_delayed_work(&dev->wakeup_work); + gps_purge_responses(dev); gport_gps_disconnect(dev); @@ -419,6 +456,7 @@ gps_set_alt(struct usb_function *f, unsigned intf, unsigned alt) atomic_set(&dev->online, 1); + cancel_delayed_work(&dev->wakeup_work); /* In case notifications were aborted, but there are pending control packets in the response queue, re-add the notifications */ list_for_each(cpkt, &dev->cpkt_resp_q) @@ -427,13 +465,32 @@ gps_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return ret; } +static void gps_queue_notify_request(struct f_gps *dev) +{ + int ret; + + pr_debug("%s: queue new notification[%d]\n", + __func__, atomic_read(&dev->notify_count)); + ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC); + if (ret) { + /* + * The modem response thread and the response completion + * thread can try to queue the same notification and may + * result in EBUSY error + */ + if (ret == -EBUSY) { + pr_debug("%s: notify_count:%u\n", + __func__, atomic_read(&dev->notify_count)); + } + pr_debug("ep enqueue error %d\n", ret); + } +} + static void gps_ctrl_response_available(struct f_gps *dev) { struct usb_request *req = dev->notify_req; struct usb_cdc_notification *event; unsigned long flags; - int ret; - struct rmnet_ctrl_pkt *cpkt; pr_debug("%s:dev:%pK\n", __func__, dev); @@ -457,24 +514,8 @@ static void gps_ctrl_response_available(struct f_gps *dev) event->wLength = cpu_to_le16(0); spin_unlock_irqrestore(&dev->lock, flags); - ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC); - if (ret) { - if (ret == -EBUSY) { - pr_err("%s: notify_count:%u\n", - __func__, atomic_read(&dev->notify_count)); - WARN_ON(1); - } - spin_lock_irqsave(&dev->lock, flags); - if (!list_empty(&dev->cpkt_resp_q)) { - atomic_dec(&dev->notify_count); - cpkt = list_first_entry(&dev->cpkt_resp_q, - struct rmnet_ctrl_pkt, list); - list_del(&cpkt->list); - gps_free_ctrl_pkt(cpkt); - } - spin_unlock_irqrestore(&dev->lock, flags); - pr_debug("ep enqueue error %d\n", ret); - } + gps_queue_notify_request(dev); + } static void gps_connect(struct grmnet *gr) @@ -543,19 +584,27 @@ gps_send_cpkt_response(void *gr, void *buf, size_t len) return 0; } + if (atomic_read(&dev->resp_q_count) >= GPS_RESP_Q_LENGTH) { + pr_err_ratelimited("%s: dropping packets as queue is full\n", + __func__); + gps_free_ctrl_pkt(cpkt); + return 0; + } + spin_lock_irqsave(&dev->lock, flags); list_add_tail(&cpkt->list, &dev->cpkt_resp_q); + atomic_inc(&dev->resp_q_count); spin_unlock_irqrestore(&dev->lock, flags); - if (dev->is_suspended && dev->is_rw_allowed) { - pr_debug("%s: calling gps_wakeup_host\n", __func__); - gps_wakeup_host(dev); - goto end; + if (dev->is_suspended) { + if (dev->is_rw_allowed) { + pr_debug("%s: calling gps_wakeup_host\n", __func__); + gps_wakeup_host(dev); + } + return 0; } gps_ctrl_response_available(dev); - -end: return 0; } @@ -582,8 +631,6 @@ static void gps_notify_complete(struct usb_ep *ep, struct usb_request *req) { struct f_gps *dev = req->context; int status = req->status; - unsigned long flags; - struct rmnet_ctrl_pkt *cpkt; pr_debug("%s: dev:%pK port#%d\n", __func__, dev, dev->port_num); @@ -605,26 +652,7 @@ static void gps_notify_complete(struct usb_ep *ep, struct usb_request *req) if (atomic_dec_and_test(&dev->notify_count)) break; - status = usb_ep_queue(dev->notify, req, GFP_ATOMIC); - if (status) { - if (status == -EBUSY) { - pr_err("%s: notify_count:%u\n", - __func__, - atomic_read(&dev->notify_count)); - WARN_ON(1); - } - - spin_lock_irqsave(&dev->lock, flags); - if (!list_empty(&dev->cpkt_resp_q)) { - atomic_dec(&dev->notify_count); - cpkt = list_first_entry(&dev->cpkt_resp_q, - struct rmnet_ctrl_pkt, list); - list_del(&cpkt->list); - gps_free_ctrl_pkt(cpkt); - } - spin_unlock_irqrestore(&dev->lock, flags); - pr_debug("ep enqueue error %d\n", status); - } + gps_queue_notify_request(dev); break; } } @@ -674,6 +702,7 @@ gps_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) cpkt = list_first_entry(&dev->cpkt_resp_q, struct rmnet_ctrl_pkt, list); list_del(&cpkt->list); + atomic_dec(&dev->resp_q_count); spin_unlock(&dev->lock); len = min_t(unsigned, w_length, cpkt->len); @@ -786,6 +815,8 @@ static int gps_bind(struct usb_configuration *c, struct usb_function *f) __func__, dev->port_num, gadget_is_dualspeed(cdev->gadget) ? "dual" : "full"); + INIT_DELAYED_WORK(&dev->wakeup_work, gps_remote_wakeup_work); + return 0; fail: -- GitLab From f32541204afca16ae15b6951cbe92795ffa56755 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Thu, 14 Sep 2017 17:11:53 +0530 Subject: [PATCH 0871/5498] usb: gadget: f_gps: Fix multiple notifications for single response When there are multiple responses pending with the driver, the notify count is greater than 1. In this case host may acknowledge multiple notifications but is still fetching only one response. This breaks the sync between the notify count and number of responses and results in data loss. Fix this by notifying the host of a available response only on the completion handler of previous get response. CRs-Fixed: 2098360 Change-Id: Ica48682246ecaacc4cdc1f45edf60ad954636663 Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/function/f_gps.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_gps.c b/drivers/usb/gadget/function/f_gps.c index 5b2e37a28f72..073915cc40d6 100644 --- a/drivers/usb/gadget/function/f_gps.c +++ b/drivers/usb/gadget/function/f_gps.c @@ -649,14 +649,24 @@ static void gps_notify_complete(struct usb_ep *ep, struct usb_request *req) pr_debug("%s: decrement notify_count:%u\n", __func__, atomic_read(&dev->notify_count)); - if (atomic_dec_and_test(&dev->notify_count)) - break; + atomic_dec(&dev->notify_count); - gps_queue_notify_request(dev); break; } } +static void gps_ctrl_send_response_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct f_gps *dev = (struct f_gps *)req->context; + + pr_debug("%s: response queue count:%u notify count: %u\n", __func__, + atomic_read(&dev->resp_q_count), + atomic_read(&dev->notify_count)); + if (atomic_read(&dev->notify_count) > 0) + gps_queue_notify_request(dev); +} + static int gps_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -709,6 +719,8 @@ gps_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) memcpy(req->buf, cpkt->buf, len); ret = len; + req->complete = gps_ctrl_send_response_complete; + req->context = dev; gps_free_ctrl_pkt(cpkt); } break; -- GitLab From 573c28d6edd2d627930394785dccdb61de78ee0e Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Fri, 15 Sep 2017 16:17:26 -0700 Subject: [PATCH 0872/5498] ANDROID: configs: remove config fragments The kernel config fragments for Android have moved into their own repository located at https://android.googlesource.com/kernel/configs/ Bug: 63994171 Change-Id: I837bac54cb5c90e6a6eb0f6f0ad5c90588c1a46a Signed-off-by: Steve Muckle --- android/configs/README | 15 --- android/configs/android-base-arm64.cfg | 5 - android/configs/android-base.cfg | 157 ------------------------ android/configs/android-recommended.cfg | 145 ---------------------- 4 files changed, 322 deletions(-) delete mode 100644 android/configs/README delete mode 100644 android/configs/android-base-arm64.cfg delete mode 100644 android/configs/android-base.cfg delete mode 100644 android/configs/android-recommended.cfg diff --git a/android/configs/README b/android/configs/README deleted file mode 100644 index 8798731f8904..000000000000 --- a/android/configs/README +++ /dev/null @@ -1,15 +0,0 @@ -The files in this directory are meant to be used as a base for an Android -kernel config. All devices should have the options in android-base.cfg enabled. -While not mandatory, the options in android-recommended.cfg enable advanced -Android features. - -Assuming you already have a minimalist defconfig for your device, a possible -way to enable these options would be: - - ARCH= scripts/kconfig/merge_config.sh /_defconfig android/configs/android-base.cfg android/configs/android-recommended.cfg - -This will generate a .config that can then be used to save a new defconfig or -compile a new kernel with Android features enabled. - -Because there is no tool to consistently generate these config fragments, -lets keep them alphabetically sorted instead of random. diff --git a/android/configs/android-base-arm64.cfg b/android/configs/android-base-arm64.cfg deleted file mode 100644 index 43f23d6b5391..000000000000 --- a/android/configs/android-base-arm64.cfg +++ /dev/null @@ -1,5 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -CONFIG_ARMV8_DEPRECATED=y -CONFIG_CP15_BARRIER_EMULATION=y -CONFIG_SETEND_EMULATION=y -CONFIG_SWP_EMULATION=y diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg deleted file mode 100644 index 15f44fc3c33f..000000000000 --- a/android/configs/android-base.cfg +++ /dev/null @@ -1,157 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_DEVKMEM is not set -# CONFIG_DEVMEM is not set -# CONFIG_FHANDLE is not set -# CONFIG_INET_LRO is not set -# CONFIG_NFSD is not set -# CONFIG_NFS_FS is not set -# CONFIG_OABI_COMPAT is not set -# CONFIG_SYSVIPC is not set -# CONFIG_USELIB is not set -CONFIG_ANDROID=y -CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder -CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ANDROID_LOW_MEMORY_KILLER=y -CONFIG_ASHMEM=y -CONFIG_AUDIT=y -CONFIG_BLK_DEV_INITRD=y -CONFIG_CGROUPS=y -CONFIG_CGROUP_CPUACCT=y -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_SCHED=y -CONFIG_DEFAULT_SECURITY_SELINUX=y -CONFIG_EMBEDDED=y -CONFIG_FB=y -CONFIG_HARDENED_USERCOPY=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_INET=y -CONFIG_INET_DIAG_DESTROY=y -CONFIG_INET_ESP=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_MATCH_RPFILTER=y -CONFIG_IP6_NF_RAW=y -CONFIG_IP6_NF_TARGET_REJECT=y -CONFIG_IPV6=y -CONFIG_IPV6_MIP6=y -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTICAST=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_NF_ARPFILTER=y -CONFIG_IP_NF_ARPTABLES=y -CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_IPTABLES=y -CONFIG_IP_NF_MANGLE=y -CONFIG_IP_NF_MATCH_AH=y -CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_TTL=y -CONFIG_IP_NF_NAT=y -CONFIG_IP_NF_RAW=y -CONFIG_IP_NF_SECURITY=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -CONFIG_IP_NF_TARGET_NETMAP=y -CONFIG_IP_NF_TARGET_REDIRECT=y -CONFIG_IP_NF_TARGET_REJECT=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_NET=y -CONFIG_NETDEVICES=y -CONFIG_NETFILTER=y -CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y -CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y -CONFIG_NETFILTER_XT_MATCH_IPRANGE=y -CONFIG_NETFILTER_XT_MATCH_LENGTH=y -CONFIG_NETFILTER_XT_MATCH_LIMIT=y -CONFIG_NETFILTER_XT_MATCH_MAC=y -CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_POLICY=y -CONFIG_NETFILTER_XT_MATCH_QTAGUID=y -CONFIG_NETFILTER_XT_MATCH_QUOTA2=y -CONFIG_NETFILTER_XT_MATCH_QUOTA=y -CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y -CONFIG_NETFILTER_XT_MATCH_STATISTIC=y -CONFIG_NETFILTER_XT_MATCH_STRING=y -CONFIG_NETFILTER_XT_MATCH_TIME=y -CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y -CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_SECMARK=y -CONFIG_NETFILTER_XT_TARGET_TCPMSS=y -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y -CONFIG_NET_CLS_ACT=y -CONFIG_NET_CLS_U32=y -CONFIG_NET_EMATCH=y -CONFIG_NET_EMATCH_U32=y -CONFIG_NET_KEY=y -CONFIG_NET_SCHED=y -CONFIG_NET_SCH_HTB=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IPV4=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_NAT=y -CONFIG_NO_HZ=y -CONFIG_PACKET=y -CONFIG_PM_AUTOSLEEP=y -CONFIG_PM_WAKELOCKS=y -CONFIG_PPP=y -CONFIG_PPPOLAC=y -CONFIG_PPPOPNS=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_MPPE=y -CONFIG_PREEMPT=y -CONFIG_RESOURCE_COUNTERS=y -CONFIG_RTC_CLASS=y -CONFIG_RT_GROUP_SCHED=y -CONFIG_SECCOMP=y -CONFIG_SECURITY=y -CONFIG_SECURITY_NETWORK=y -CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y -CONFIG_SECURITY_SELINUX=y -CONFIG_STAGING=y -CONFIG_SWITCH=y -CONFIG_SYNC=y -CONFIG_TUN=y -CONFIG_UID_SYS_STATS=y -CONFIG_UNIX=y -CONFIG_USB_GADGET=y -CONFIG_XFRM_USER=y diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg deleted file mode 100644 index d2b1e9fa39f1..000000000000 --- a/android/configs/android-recommended.cfg +++ /dev/null @@ -1,145 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_LEGACY_PTYS is not set -# CONFIG_NF_CONNTRACK_SIP is not set -# CONFIG_PM_WAKELOCKS_GC is not set -# CONFIG_VT is not set -CONFIG_ANDROID_TIMED_GPIO=y -CONFIG_ARM_KERNMEM_PERMS=y -CONFIG_ARM64_SW_TTBR0_PAN=y -CONFIG_ASYMMETRIC_KEY_TYPE=y -CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y -CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_BLK_DEV_DM=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_CC_STACKPROTECTOR_STRONG=y -CONFIG_COMPACTION=y -CONFIG_CPU_SW_DOMAIN_PAN=y -CONFIG_DEBUG_RODATA=y -CONFIG_DM_CRYPT=y -CONFIG_DM_UEVENT=y -CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y -CONFIG_DRAGONRISE_FF=y -CONFIG_ENABLE_DEFAULT_TRACERS=y -CONFIG_EXT4_FS=y -CONFIG_EXT4_FS_SECURITY=y -CONFIG_FUSE_FS=y -CONFIG_GREENASIA_FF=y -CONFIG_HIDRAW=y -CONFIG_HID_A4TECH=y -CONFIG_HID_ACRUX=y -CONFIG_HID_ACRUX_FF=y -CONFIG_HID_APPLE=y -CONFIG_HID_BELKIN=y -CONFIG_HID_CHERRY=y -CONFIG_HID_CHICONY=y -CONFIG_HID_CYPRESS=y -CONFIG_HID_DRAGONRISE=y -CONFIG_HID_ELECOM=y -CONFIG_HID_EMS_FF=y -CONFIG_HID_EZKEY=y -CONFIG_HID_GREENASIA=y -CONFIG_HID_GYRATION=y -CONFIG_HID_HOLTEK=y -CONFIG_HID_KENSINGTON=y -CONFIG_HID_KEYTOUCH=y -CONFIG_HID_KYE=y -CONFIG_HID_LCPOWER=y -CONFIG_HID_LOGITECH=y -CONFIG_HID_LOGITECH_DJ=y -CONFIG_HID_MAGICMOUSE=y -CONFIG_HID_MICROSOFT=y -CONFIG_HID_MONTEREY=y -CONFIG_HID_MULTITOUCH=y -CONFIG_HID_NTRIG=y -CONFIG_HID_ORTEK=y -CONFIG_HID_PANTHERLORD=y -CONFIG_HID_PETALYNX=y -CONFIG_HID_PICOLCD=y -CONFIG_HID_PRIMAX=y -CONFIG_HID_PRODIKEYS=y -CONFIG_HID_ROCCAT=y -CONFIG_HID_SAITEK=y -CONFIG_HID_SAMSUNG=y -CONFIG_HID_SMARTJOYPLUS=y -CONFIG_HID_SONY=y -CONFIG_HID_SPEEDLINK=y -CONFIG_HID_SUNPLUS=y -CONFIG_HID_THRUSTMASTER=y -CONFIG_HID_TIVO=y -CONFIG_HID_TOPSEED=y -CONFIG_HID_TWINHAN=y -CONFIG_HID_UCLOGIC=y -CONFIG_HID_WACOM=y -CONFIG_HID_WALTOP=y -CONFIG_HID_WIIMOTE=y -CONFIG_HID_ZEROPLUS=y -CONFIG_HID_ZYDACRON=y -CONFIG_INPUT_EVDEV=y -CONFIG_INPUT_GPIO=y -CONFIG_INPUT_JOYSTICK=y -CONFIG_INPUT_KEYCHORD=y -CONFIG_INPUT_KEYRESET=y -CONFIG_INPUT_MISC=y -CONFIG_INPUT_TABLET=y -CONFIG_INPUT_UINPUT=y -CONFIG_ION=y -CONFIG_JOYSTICK_XPAD=y -CONFIG_JOYSTICK_XPAD_FF=y -CONFIG_JOYSTICK_XPAD_LEDS=y -CONFIG_KALLSYMS_ALL=y -CONFIG_KSM=y -CONFIG_LOGIG940_FF=y -CONFIG_LOGIRUMBLEPAD2_FF=y -CONFIG_LOGITECH_FF=y -CONFIG_MD=y -CONFIG_MEDIA_SUPPORT=y -CONFIG_MSDOS_FS=y -CONFIG_PANIC_TIMEOUT=5 -CONFIG_PANTHERLORD_FF=y -CONFIG_PERF_EVENTS=y -CONFIG_PKCS7_MESSAGE_PARSER=y -CONFIG_PKCS7_TEST_KEY=y -CONFIG_PM_DEBUG=y -CONFIG_PM_RUNTIME=y -CONFIG_PM_WAKELOCKS_LIMIT=0 -CONFIG_POWER_SUPPLY=y -CONFIG_PSTORE=y -CONFIG_PSTORE_CONSOLE=y -CONFIG_PSTORE_RAM=y -CONFIG_QFMT_V2=y -CONFIG_QUOTA=y -CONFIG_QUOTACTL=y -CONFIG_QUOTA_NETLINK_INTERFACE=y -CONFIG_QUOTA_TREE=y -CONFIG_SCHEDSTATS=y -CONFIG_SMARTJOYPLUS_FF=y -CONFIG_SND=y -CONFIG_SOUND=y -CONFIG_SUSPEND_TIME=y -CONFIG_TABLET_USB_ACECAD=y -CONFIG_TABLET_USB_AIPTEK=y -CONFIG_TABLET_USB_GTCO=y -CONFIG_TABLET_USB_HANWANG=y -CONFIG_TABLET_USB_KBTAB=y -CONFIG_TABLET_USB_WACOM=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_IO_ACCOUNTING=y -CONFIG_TASK_XACCT=y -CONFIG_TIMER_STATS=y -CONFIG_TMPFS=y -CONFIG_TMPFS_POSIX_ACL=y -CONFIG_UHID=y -CONFIG_UID_STAT=y -CONFIG_MEMORY_STATE_TIME=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_HIDDEV=y -CONFIG_USB_USBNET=y -CONFIG_VFAT_FS=y -CONFIG_X509_CERTIFICATE_PARSER=y -- GitLab From cd91232245809ffe6cbe9c6724cc612242745843 Mon Sep 17 00:00:00 2001 From: Krishna Manikandan Date: Mon, 18 Sep 2017 12:13:40 +0530 Subject: [PATCH 0873/5498] msm: mdss: Fix potential race condition in rotator There might be a possible race condition in rotator, where two threads can free same perf structures. Adding proper locking to avoid such race conditions. Change-Id: I4976da6f176df24da2ec86c4c1f176cc43aba05b Signed-off-by: Krishna Chaitanya Devarakonda Signed-off-by: Krishna Manikandan --- drivers/video/msm/mdss/mdss_rotator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_rotator.c b/drivers/video/msm/mdss/mdss_rotator.c index 523b1ffe9122..25d56e392842 100644 --- a/drivers/video/msm/mdss/mdss_rotator.c +++ b/drivers/video/msm/mdss/mdss_rotator.c @@ -1126,6 +1126,7 @@ static void mdss_rotator_release_from_work_distribution( bool free_perf = false; u32 wb_idx = entry->queue->hw->wb_id; + mutex_lock(&mgr->lock); mutex_lock(&entry->perf->work_dis_lock); if (entry->perf->work_distribution[wb_idx]) entry->perf->work_distribution[wb_idx]--; @@ -1149,6 +1150,7 @@ static void mdss_rotator_release_from_work_distribution( mdss_rotator_clk_ctrl(mgr, false); entry->perf = NULL; } + mutex_unlock(&mgr->lock); } } @@ -2045,7 +2047,6 @@ static int mdss_rotator_close_session(struct mdss_rot_mgr *mgr, list_del_init(&perf->list); mutex_unlock(&perf->work_dis_lock); mutex_unlock(&private->perf_lock); - mutex_unlock(&mgr->lock); if (offload_release_work) goto done; @@ -2058,6 +2059,7 @@ static int mdss_rotator_close_session(struct mdss_rot_mgr *mgr, done: pr_debug("Closed session id:%u", id); ATRACE_END(__func__); + mutex_unlock(&mgr->lock); return 0; } -- GitLab From 64043b4150917741d803996a92c164009fc7e193 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Mon, 18 Sep 2017 14:07:50 +0530 Subject: [PATCH 0874/5498] msm: ipa: Fix boundary cross on array element Fix possible attempt of accessing MAX array element. Change-Id: Ia117565b6cadb1eafe91df16b2441afd1ffd13a8 Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c | 10 +++++----- drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index 70f9cd88f650..f45420555770 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -431,7 +431,7 @@ static ssize_t ipa_read_hdr(struct file *file, char __user *ubuf, size_t count, link) { nbytes = scnprintf( dbg_buff, - IPA_MAX_MSG_LEN, + IPA_MAX_MSG_LEN - 1, "name:%s len=%d ref=%d partial=%d type=%s ", entry->name, entry->hdr_len, @@ -442,23 +442,23 @@ static ssize_t ipa_read_hdr(struct file *file, char __user *ubuf, size_t count, if (entry->is_hdr_proc_ctx) { nbytes += scnprintf( dbg_buff + nbytes, - IPA_MAX_MSG_LEN - nbytes, + IPA_MAX_MSG_LEN - 1 - nbytes, "phys_base=0x%pa ", &entry->phys_base); } else { nbytes += scnprintf( dbg_buff + nbytes, - IPA_MAX_MSG_LEN - nbytes, + IPA_MAX_MSG_LEN - 1 - nbytes, "ofst=%u ", entry->offset_entry->offset >> 2); } for (i = 0; i < entry->hdr_len; i++) { scnprintf(dbg_buff + nbytes + i * 2, - IPA_MAX_MSG_LEN - nbytes - i * 2, + IPA_MAX_MSG_LEN - 1 - nbytes - i * 2, "%02x", entry->hdr[i]); } scnprintf(dbg_buff + nbytes + entry->hdr_len * 2, - IPA_MAX_MSG_LEN - nbytes - entry->hdr_len * 2, + IPA_MAX_MSG_LEN - 1 - nbytes - entry->hdr_len * 2, "\n"); pr_err("%s", dbg_buff); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index 0efcf2ff8a07..503e1c935f05 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -358,7 +358,7 @@ static ssize_t ipa3_read_hdr(struct file *file, char __user *ubuf, size_t count, link) { nbytes = scnprintf( dbg_buff, - IPA_MAX_MSG_LEN, + IPA_MAX_MSG_LEN - 1, "name:%s len=%d ref=%d partial=%d type=%s ", entry->name, entry->hdr_len, @@ -369,23 +369,23 @@ static ssize_t ipa3_read_hdr(struct file *file, char __user *ubuf, size_t count, if (entry->is_hdr_proc_ctx) { nbytes += scnprintf( dbg_buff + nbytes, - IPA_MAX_MSG_LEN - nbytes, + IPA_MAX_MSG_LEN - 1 - nbytes, "phys_base=0x%pa ", &entry->phys_base); } else { nbytes += scnprintf( dbg_buff + nbytes, - IPA_MAX_MSG_LEN - nbytes, + IPA_MAX_MSG_LEN - 1 - nbytes, "ofst=%u ", entry->offset_entry->offset >> 2); } for (i = 0; i < entry->hdr_len; i++) { scnprintf(dbg_buff + nbytes + i * 2, - IPA_MAX_MSG_LEN - nbytes - i * 2, + IPA_MAX_MSG_LEN - 1 - nbytes - i * 2, "%02x", entry->hdr[i]); } scnprintf(dbg_buff + nbytes + entry->hdr_len * 2, - IPA_MAX_MSG_LEN - nbytes - entry->hdr_len * 2, + IPA_MAX_MSG_LEN - 1 - nbytes - entry->hdr_len * 2, "\n"); pr_err("%s", dbg_buff); } -- GitLab From 13914968e4bf852385d48de15476b0e9056b07de Mon Sep 17 00:00:00 2001 From: Lingutla Chandrasekhar Date: Tue, 12 Sep 2017 22:24:55 +0530 Subject: [PATCH 0875/5498] arm64: Add support to print hardware id info Initial support to print hardware id is added with commit 'eab25833f0d4 ("ARM/ARM64: Introduce arch_read_hardware_id")', with recent down port changes, this print hardware info is missed to propagate. Adding it back with defining common variables in header file. Change-Id: I4da63bd2346c0d0296087941967ae1a98eb6d86f Signed-off-by: Abhimanyu Kapur Signed-off-by: Lingutla Chandrasekhar --- arch/arm64/include/asm/system_misc.h | 1 + arch/arm64/kernel/cpuinfo.c | 6 ++++++ arch/arm64/kernel/setup.c | 8 ++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h index fb87ac566887..2df83b2065d8 100644 --- a/arch/arm64/include/asm/system_misc.h +++ b/arch/arm64/include/asm/system_misc.h @@ -43,6 +43,7 @@ extern void __show_regs(struct pt_regs *); extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); extern char* (*arch_read_hardware_id)(void); +extern const char *machine_name; #define UDBG_UNDEFINED (1 << 0) #define UDBG_SYSCALL (1 << 1) diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index 103a7b622698..f50585e764fc 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -152,6 +153,11 @@ static int c_show(struct seq_file *m, void *v) seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr)); } + if (!arch_read_hardware_id) + seq_printf(m, "Hardware\t: %s\n", machine_name); + else + seq_printf(m, "Hardware\t: %s\n", arch_read_hardware_id()); + return 0; } diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 0538e6ebbd42..0bfd0484edb6 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -61,9 +61,7 @@ #include #include #include - -char* (*arch_read_hardware_id)(void); -EXPORT_SYMBOL(arch_read_hardware_id); +#include unsigned int boot_reason; EXPORT_SYMBOL(boot_reason); @@ -71,7 +69,9 @@ EXPORT_SYMBOL(boot_reason); unsigned int cold_boot; EXPORT_SYMBOL(cold_boot); -static const char *machine_name; +char* (*arch_read_hardware_id)(void); +const char *machine_name; + phys_addr_t __fdt_pointer __initdata; /* -- GitLab From 36d390baa6a0b9c78621e437521abbdc8bf514b0 Mon Sep 17 00:00:00 2001 From: Skylar Chang Date: Fri, 12 May 2017 17:13:13 -0700 Subject: [PATCH 0876/5498] msm: ipa3: fix usb statuses exception path Set APPS_LAN_CONS pipe as the statuses exception pipe for usb-prod pipes. Change-Id: Ia0938da6ec2b2cbdbab4dddf177d2af76fad3aaa Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v3/ipa.c | 39 ++----------------- .../msm/ipa/ipa_v3/ipahal/ipahal_reg.c | 17 -------- .../msm/ipa/ipa_v3/ipahal/ipahal_reg.h | 2 - 3 files changed, 4 insertions(+), 54 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index c4ed4738223a..0f3f2e536f55 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -2446,7 +2446,6 @@ static int ipa3_q6_set_ex_path_to_apps(void) struct ipahal_imm_cmd_register_write reg_write; struct ipahal_imm_cmd_pyld *cmd_pyld; int retval; - struct ipahal_reg_valmask valmask; desc = kcalloc(ipa3_ctx->ipa_num_pipes, sizeof(struct ipa3_desc), GFP_KERNEL); @@ -2461,40 +2460,10 @@ static int ipa3_q6_set_ex_path_to_apps(void) if (ep_idx == -1) continue; - if (ipa3_ctx->ep[ep_idx].valid && - ipa3_ctx->ep[ep_idx].skip_ep_cfg) { - BUG_ON(num_descs >= ipa3_ctx->ipa_num_pipes); - - reg_write.skip_pipeline_clear = false; - reg_write.pipeline_clear_options = - IPAHAL_HPS_CLEAR; - reg_write.offset = - ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n, - ep_idx); - ipahal_get_status_ep_valmask( - ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS), - &valmask); - reg_write.value = valmask.val; - reg_write.value_mask = valmask.mask; - cmd_pyld = ipahal_construct_imm_cmd( - IPA_IMM_CMD_REGISTER_WRITE, ®_write, false); - if (!cmd_pyld) { - IPAERR("fail construct register_write cmd\n"); - BUG(); - } - - desc[num_descs].opcode = ipahal_imm_cmd_get_opcode( - IPA_IMM_CMD_REGISTER_WRITE); - desc[num_descs].type = IPA_IMM_CMD_DESC; - desc[num_descs].callback = ipa3_destroy_imm; - desc[num_descs].user1 = cmd_pyld; - desc[num_descs].pyld = cmd_pyld->data; - desc[num_descs].len = cmd_pyld->len; - num_descs++; - } - - /* disable statuses for modem producers */ - if (IPA_CLIENT_IS_Q6_PROD(client_idx)) { + /* disable statuses for all modem controlled prod pipes */ + if (IPA_CLIENT_IS_Q6_PROD(client_idx) || + (ipa3_ctx->ep[ep_idx].valid && + ipa3_ctx->ep[ep_idx].skip_ep_cfg)) { ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes); reg_write.skip_pipeline_clear = false; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c index 407530173438..ca1c128c3bb0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c @@ -1605,20 +1605,3 @@ void ipahal_get_fltrt_hash_flush_valmask( valmask->mask = valmask->val; } - -void ipahal_get_status_ep_valmask(int pipe_num, - struct ipahal_reg_valmask *valmask) -{ - if (!valmask) { - IPAHAL_ERR("Input error\n"); - return; - } - - valmask->val = - (pipe_num & IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK) << - IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT; - - valmask->mask = - IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK << - IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT; -} diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h index ecb96e1d049f..f37ddd974f3b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h @@ -464,8 +464,6 @@ void ipahal_get_aggr_force_close_valmask(int ep_idx, void ipahal_get_fltrt_hash_flush_valmask( struct ipahal_reg_fltrt_hash_flush *flush, struct ipahal_reg_valmask *valmask); -void ipahal_get_status_ep_valmask(int pipe_num, - struct ipahal_reg_valmask *valmask); #endif /* _IPAHAL_REG_H_ */ -- GitLab From 1dd8bd2aa04279b1994751de90805f583e400da3 Mon Sep 17 00:00:00 2001 From: hqu Date: Tue, 19 Sep 2017 14:47:09 +0800 Subject: [PATCH 0877/5498] cfg80211: Define macro to indicate multiple beacon interval support Define macro to indicate backport support for multiple beacon interval. CRs-Fixed: 2112309 Change-Id: Ibbcf8afe7ddbfbb6359fbcd6fbc5b1bef5b59e6d Signed-off-by: Huashan Qu --- include/net/cfg80211.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a37530eb5009..e3a7ef57c449 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -78,6 +78,7 @@ struct wiphy; #define CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT 1 #define CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME 1 #define CFG80211_REPORT_BETTER_BSS_IN_SCHED_SCAN 1 +#define CFG80211_BEACON_INTERVAL_BACKPORT 1 /* * wireless hardware capability structures -- GitLab From bd92285e0b0083184f3701360523aafbb6086320 Mon Sep 17 00:00:00 2001 From: Chaithanya Krishna Bacharaju Date: Mon, 18 Sep 2017 15:26:53 +0530 Subject: [PATCH 0878/5498] ASoC: msm: qdsp6v2: Extend support to set legacy PCM mode Extend support to set legacy PCM mode through topology controls. Change-Id: I9bff0992130a69a483713e4b2d6c3809b557bfe7 Signed-off-by: Chaithanya Krishna Bacharaju --- sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index 9710ed4f6730..eefae71a27bf 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -122,7 +122,7 @@ static unsigned long msm_pcm_fe_topology[MSM_FRONTEND_DAI_MAX][MAX_PCM_STREAMS]; /* default value is DTS (i.e read from device tree) */ static char const *msm_pcm_fe_topology_text[] = { - "DTS", "ULL", "ULL_PP", "LL" }; + "DTS", "ULL", "ULL_PP", "LL", "LEGACY" }; static const struct soc_enum msm_pcm_fe_topology_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_pcm_fe_topology_text), @@ -985,6 +985,8 @@ static int msm_pcm_hw_params(struct snd_pcm_substream *substream, pdata->perf_mode_set = ULTRA_LOW_LATENCY_PCM_MODE; else if (!strcmp(msm_pcm_fe_topology_text[topology], "LL")) pdata->perf_mode_set = LOW_LATENCY_PCM_MODE; + else if (!strcmp(msm_pcm_fe_topology_text[topology], "LEGACY")) + pdata->perf_mode_set = LEGACY_PCM_MODE; else /* use the default from the device tree */ pdata->perf_mode_set = pdata->perf_mode; -- GitLab From 94888a8302c032f979f2a345bb86dcf292fe41b2 Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Mon, 18 Sep 2017 15:41:27 +0530 Subject: [PATCH 0879/5498] msm: ep_pcie: Remove invalid return value Enumeration can be called as part of handling PERST interrupt as well as from probe function. Remove invalid error value being returned and skip initialization of PCIe resources if already done. Change-Id: Idcae4fe98ccc06c98042b89060dae66018808f27 Signed-off-by: Rama Krishna Phani A --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index c535bee03fc1..8d649e4a278b 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -1050,7 +1050,6 @@ int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) EP_PCIE_ERR(dev, "PCIe V%d: request to turn on the power when link is already powered on.\n", dev->rev); - ret = EP_PCIE_ERROR; goto out; } -- GitLab From a0ad043522ba24f5798775bbbaa28c2cd45e2efb Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Tue, 19 Sep 2017 16:10:51 +0530 Subject: [PATCH 0880/5498] msm: ep_pcie: Update logging for PCIe EP driver Interrupt mask register needs to be read from PARF region. Update change such that mask register is read from PARF region while logging. Change-Id: Ia183049e24c90ea080d661f77d49c719aa269e68 Signed-off-by: Rama Krishna Phani A --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index c535bee03fc1..89caf2ce7bde 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -1364,7 +1364,7 @@ int ep_pcie_core_mask_irq_event(enum ep_pcie_irq_event event, spin_lock_irqsave(&dev->ext_lock, irqsave_flags); if (dev->aggregated_irq) { - mask = readl_relaxed(dev->dm_core + PCIE20_PARF_INT_ALL_MASK); + mask = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK); EP_PCIE_DUMP(dev, "PCIe V%d: current PCIE20_PARF_INT_ALL_MASK:0x%x\n", dev->rev, mask); @@ -1377,7 +1377,7 @@ int ep_pcie_core_mask_irq_event(enum ep_pcie_irq_event event, EP_PCIE_DUMP(dev, "PCIe V%d: new PCIE20_PARF_INT_ALL_MASK:0x%x\n", dev->rev, - readl_relaxed(dev->dm_core + PCIE20_PARF_INT_ALL_MASK)); + readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK)); } else { EP_PCIE_ERR(dev, "PCIe V%d: Client askes to %s IRQ event 0x%x when aggregated IRQ is not supported.\n", -- GitLab From 05cba409ca727ae9ac8a15dbf3cac3fefca486ff Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Thu, 14 Sep 2017 20:35:11 +0530 Subject: [PATCH 0881/5498] msm: ep_pcie: Update aggregate IRQ logic Some targets support individual interrupt line for MHI a7 and MHI driver can register for interrupt handler. Avoid programming MHI a7 interrupt bit from PCIe in such case. Change-Id: I5aeb136d087ac2daadbddff5c41849e13aec9329 Signed-off-by: Rama Krishna Phani A --- .../devicetree/bindings/pci/msm_ep_pcie.txt | 2 ++ drivers/platform/msm/ep_pcie/ep_pcie_com.h | 1 + drivers/platform/msm/ep_pcie/ep_pcie_core.c | 13 ++++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt b/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt index 6930ee5379b6..1f6d79ddb0fa 100644 --- a/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt +++ b/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt @@ -50,6 +50,7 @@ Optional Properties: - qcom,pcie-active-config: boolean type; active configuration of PCIe addressing. - qcom,pcie-aggregated-irq: boolean type; interrupts are aggregated. + - qcom,pcie-mhi-a7-irq: boolean type; MHI a7 has separate irq. - qcom,pcie-perst-enum: Link enumeration will be triggered by PERST deassertion. - mdm2apstatus-gpio: GPIO used by PCIe endpoint side to notify the host side. @@ -117,6 +118,7 @@ Example: qcom,pcie-link-speed = <1>; qcom,pcie-active-config; qcom,pcie-aggregated-irq; + qcom,pcie-mhi-a7-irq; qcom,pcie-perst-enum; qcom,phy-status-reg = <0x728>; qcom,dbi-base-reg = <0x168>; diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index c9b5995d77c5..185c9cd8ef83 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -289,6 +289,7 @@ struct ep_pcie_dev_t { u32 link_speed; bool active_config; bool aggregated_irq; + bool mhi_a7_irq; u32 dbi_base_reg; u32 slv_space_reg; u32 phy_status_reg; diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 89caf2ce7bde..683903837b5d 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -600,9 +600,13 @@ static void ep_pcie_core_init(struct ep_pcie_dev_t *dev, bool configured) BIT(EP_PCIE_INT_EVT_LINK_DOWN) | BIT(EP_PCIE_INT_EVT_BME) | BIT(EP_PCIE_INT_EVT_PM_TURNOFF) | - BIT(EP_PCIE_INT_EVT_MHI_A7) | BIT(EP_PCIE_INT_EVT_DSTATE_CHANGE) | BIT(EP_PCIE_INT_EVT_LINK_UP)); + if (!dev->mhi_a7_irq) + ep_pcie_write_mask(dev->parf + + PCIE20_PARF_INT_ALL_MASK, 0, + BIT(EP_PCIE_INT_EVT_MHI_A7)); + EP_PCIE_DBG(dev, "PCIe V%d: PCIE20_PARF_INT_ALL_MASK:0x%x\n", dev->rev, readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK)); @@ -2287,6 +2291,13 @@ static int ep_pcie_probe(struct platform_device *pdev) "PCIe V%d: aggregated IRQ is %s enabled.\n", ep_pcie_dev.rev, ep_pcie_dev.aggregated_irq ? "" : "not"); + ep_pcie_dev.mhi_a7_irq = + of_property_read_bool((&pdev->dev)->of_node, + "qcom,pcie-mhi-a7-irq"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: Mhi a7 IRQ is %s enabled.\n", + ep_pcie_dev.rev, ep_pcie_dev.mhi_a7_irq ? "" : "not"); + ep_pcie_dev.perst_enum = of_property_read_bool((&pdev->dev)->of_node, "qcom,pcie-perst-enum"); EP_PCIE_DBG(&ep_pcie_dev, -- GitLab From 01af4d3a7e991af27092f41f17072e3e50eb6c66 Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Fri, 1 Sep 2017 16:09:59 +0530 Subject: [PATCH 0882/5498] msm: mhi_dev: Update logic to support mhi interrupt Update logic to support mhi interrupt. Enable channel doorbell interrupts during restoring MHI mmio registers. Change-Id: Ie9bda37ce4ce711dd2569e86dc510366280cf622 Signed-off-by: Rama Krishna Phani A --- Documentation/devicetree/bindings/mhi/msm_mhi_dev.txt | 1 + drivers/platform/msm/mhi_dev/mhi.c | 11 +++++++---- drivers/platform/msm/mhi_dev/mhi.h | 3 +++ drivers/platform/msm/mhi_dev/mhi_mmio.c | 3 +++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mhi/msm_mhi_dev.txt b/Documentation/devicetree/bindings/mhi/msm_mhi_dev.txt index fe3c261ac7ac..30174680a91d 100644 --- a/Documentation/devicetree/bindings/mhi/msm_mhi_dev.txt +++ b/Documentation/devicetree/bindings/mhi/msm_mhi_dev.txt @@ -25,6 +25,7 @@ Optional property: between host and device. - qcom,mhi-config-iatu: If property is present map the control and data region between host and device using iatu. + - qcom,mhi-interrupt: If property is present register for mhi interrupt. - qcom,mhi-local-pa-base: The physical base address on the device used by the MHI device driver to map the control and data region with the MHI driver on the host. This property is required if iatu diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index b1464097baff..2e95f6c0a5cc 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -1139,7 +1139,7 @@ static void mhi_dev_scheduler(struct work_struct *work) mutex_unlock(&mhi_ctx->mhi_lock); - if (mhi->config_iatu) + if (mhi->config_iatu || mhi->mhi_int) enable_irq(mhi->mhi_irq); else ep_pcie_mask_irq_event(mhi->phandle, @@ -1940,7 +1940,7 @@ static void mhi_dev_enable(struct work_struct *work) return; } - if (mhi_ctx->config_iatu) + if (mhi_ctx->config_iatu || mhi_ctx->mhi_int) enable_irq(mhi_ctx->mhi_irq); mhi_update_state_info(MHI_STATE_CONNECTED); @@ -2072,7 +2072,10 @@ static int get_device_tree_data(struct platform_device *pdev) } } - if (mhi->config_iatu) { + mhi_ctx->mhi_int = of_property_read_bool((&pdev->dev)->of_node, + "qcom,mhi-interrupt"); + + if (mhi->config_iatu || mhi_ctx->mhi_int) { mhi->mhi_irq = platform_get_irq_byname(pdev, "mhi-device-inta"); if (mhi->mhi_irq < 0) { pr_err("Invalid MHI device interrupt\n"); @@ -2249,7 +2252,7 @@ static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx) /* All set, notify the host */ mhi_dev_sm_set_ready(); - if (mhi_ctx->config_iatu) { + if (mhi_ctx->config_iatu || mhi_ctx->mhi_int) { rc = devm_request_irq(&pdev->dev, mhi_ctx->mhi_irq, mhi_dev_isr, IRQF_TRIGGER_HIGH, "mhi_isr", mhi_ctx); if (rc) { diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index 535653ddcaa2..4cc67478cabb 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -557,6 +557,9 @@ struct mhi_dev { /* MHI state info */ enum mhi_ctrl_info ctrl_info; + + /*Register for interrupt*/ + bool mhi_int; }; enum mhi_msg_level { diff --git a/drivers/platform/msm/mhi_dev/mhi_mmio.c b/drivers/platform/msm/mhi_dev/mhi_mmio.c index 1031c84a6ad9..42355c8f72d3 100644 --- a/drivers/platform/msm/mhi_dev/mhi_mmio.c +++ b/drivers/platform/msm/mhi_dev/mhi_mmio.c @@ -863,6 +863,9 @@ int mhi_dev_restore_mmio(struct mhi_dev *dev) mhi_dev_mmio_clear_interrupts(dev); mhi_dev_mmio_enable_ctrl_interrupt(dev); + /*Enable chdb interrupt*/ + mhi_dev_mmio_enable_chdb_interrupts(dev); + /* Mask and enable control interrupt */ mb(); -- GitLab From d347c94a3f476e10a15a71ef71a8074d86dc7610 Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Fri, 15 Sep 2017 19:44:54 +0530 Subject: [PATCH 0883/5498] ARM: dts: msm: Add interrupt property support for SDX20 MHI driver has individual interrupt supported in SDX20. Add DT property such that PCIe will not configure for a7 interrupt. Change-Id: I6176c57906335ac35d48915304f84e6aff024423 Signed-off-by: Rama Krishna Phani A --- arch/arm/boot/dts/qcom/sdx20.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/sdx20.dtsi b/arch/arm/boot/dts/qcom/sdx20.dtsi index c045800e24dc..b4ed367e6cda 100644 --- a/arch/arm/boot/dts/qcom/sdx20.dtsi +++ b/arch/arm/boot/dts/qcom/sdx20.dtsi @@ -353,6 +353,7 @@ qcom,pcie-phy-ver = <5>; qcom,pcie-perst-enum; + qcom,pcie-mhi-a7-irq; status = "disabled"; }; -- GitLab From 24b46755a876264fdae0284e197d5583d2d3acdc Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Fri, 15 Sep 2017 20:00:04 +0530 Subject: [PATCH 0884/5498] ARM: dts: msm: Add interrupt property support for SDX20 MHI driver has individual interrupt supported in SDX20. Add DT property such that MHI driver will register for interrupt handler function. Change-Id: I02d5c208caf153cccec9a3e6280063a9f42954c4 Signed-off-by: Rama Krishna Phani A --- arch/arm/boot/dts/qcom/sdx20.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/sdx20.dtsi b/arch/arm/boot/dts/qcom/sdx20.dtsi index b4ed367e6cda..0d1b7dfb8baf 100644 --- a/arch/arm/boot/dts/qcom/sdx20.dtsi +++ b/arch/arm/boot/dts/qcom/sdx20.dtsi @@ -393,6 +393,7 @@ interrupts = <0 119 0>; interrupt-names = "mhi-device-inta"; qcom,mhi-ifc-id = <0x030317cb>; + qcom,mhi-interrupt; status = "disabled"; }; -- GitLab From 53ba4782c2721efacda39debfe45f68815c2de72 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Sun, 3 Sep 2017 17:24:38 +0530 Subject: [PATCH 0885/5498] ARM: dts: msm: update touch pinctrl settings for APQ8909W V2 device Update pinctrl settings for Synaptics touch for APQ8909W V2 device. Change-Id: Ic25f644224ed772572eaeb0fc603fa0685598445 Signed-off-by: Shantanu Jain --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 46a3878d219b..940ac8807054 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -168,3 +168,54 @@ pinctrl-names = "default"; pinctrl-0 = <&uart_console_sleep>; }; + +/* Pinctrl dt nodes for interrupt and reset gpio for Synaptics controller */ +&ts_int_active { + mux { + pins = "gpio98"; + }; + + config { + pins = "gpio98"; + }; +}; + +&ts_int_suspend { + mux { + pins = "gpio98"; + }; + + config { + pins = "gpio98"; + }; +}; + +&ts_reset_active { + mux { + pins = "gpio16"; + }; + + config { + pins = "gpio16"; + }; +}; + +&ts_reset_suspend { + mux { + pins = "gpio16"; + }; + + config { + pins = "gpio16"; + }; +}; + +&ts_release { + mux { + pins = "gpio98", "gpio16"; + }; + + config { + pins = "gpio98", "gpio16"; + }; +}; -- GitLab From 559ca3e14794c2f897575a950c097188064568f5 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Tue, 19 Sep 2017 18:27:16 +0530 Subject: [PATCH 0886/5498] defconfig: msm8909w-perf: Enable QPNP HAPTIC driver QPNP_HAPTIC supports vibrator on PM660. Change-Id: Ide47780a4ac9cdb59f200294c181296fe38e68fe Signed-off-by: Anirudh Ghayal --- arch/arm/configs/msm8909w-perf_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 7d4289a087fa..20f0972d1d6f 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -392,6 +392,7 @@ CONFIG_SPS=y CONFIG_USB_BAM=y CONFIG_SPS_SUPPORT_NDP_BAM=y CONFIG_QPNP_VIBRATOR=y +CONFIG_QPNP_HAPTIC=y CONFIG_MSM_SPMI=y CONFIG_MSM_SPMI_PMIC_ARB=y CONFIG_MSM_QPNP_INT=y -- GitLab From 46ca0b90bffa4c9e6d1e8b703d825414aaa107ab Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 17 Jul 2017 19:16:11 +0800 Subject: [PATCH 0887/5498] f2fs: remove unused input parameter commit 5f4ce6abc2dadac67bfb8a14cfb1b9ac3941810f upstream. This patch remove unused input parameter in function new_node_page. Signed-off-by: Yunlei He Signed-off-by: Yong Sheng Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/node.c | 7 +++---- fs/f2fs/xattr.c | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 40cc122a778b..bbba08d57a8e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2413,8 +2413,7 @@ int truncate_xattr_node(struct inode *inode, struct page *page); int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino); int remove_inode_page(struct inode *inode); struct page *new_inode_page(struct inode *inode); -struct page *new_node_page(struct dnode_of_data *dn, - unsigned int ofs, struct page *ipage); +struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs); void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid); struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid); struct page *get_node_page_ra(struct page *parent, int start); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 77d27551b09b..785612e33da5 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -613,7 +613,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) } dn->nid = nids[i]; - npage[i] = new_node_page(dn, noffset[i], NULL); + npage[i] = new_node_page(dn, noffset[i]); if (IS_ERR(npage[i])) { alloc_nid_failed(sbi, nids[i]); err = PTR_ERR(npage[i]); @@ -1022,11 +1022,10 @@ struct page *new_inode_page(struct inode *inode) set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); /* caller should f2fs_put_page(page, 1); */ - return new_node_page(&dn, 0, NULL); + return new_node_page(&dn, 0); } -struct page *new_node_page(struct dnode_of_data *dn, - unsigned int ofs, struct page *ipage) +struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct node_info new_ni; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index c01e2b61e747..833af6916534 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -491,7 +491,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, } else { struct dnode_of_data dn; set_new_dnode(&dn, inode, NULL, NULL, new_nid); - xpage = new_node_page(&dn, XATTR_NODE_OFFSET, ipage); + xpage = new_node_page(&dn, XATTR_NODE_OFFSET); if (IS_ERR(xpage)) { alloc_nid_failed(sbi, new_nid); return PTR_ERR(xpage); -- GitLab From 3d9f03b50f64181d1db0695124e494c0393b3072 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 16 Jul 2017 15:08:54 +0800 Subject: [PATCH 0888/5498] f2fs: spread struct f2fs_dentry_ptr for inline path commit 76a9dd85d43b7b3d94e116437dc882e159887a6a upstream. Use f2fs_dentry_ptr structure to indicate inline dentry structure as much as possible, so we can wrap inline dentry with size-fixed fields to the one with size-changeable fields. With this change, we can handle size-changeable inline dentry more easily. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 5 ++++- fs/f2fs/inline.c | 47 ++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index bbba08d57a8e..b9940eb65e3b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -474,10 +474,11 @@ struct f2fs_flush_device { /* for directory operations */ struct f2fs_dentry_ptr { struct inode *inode; - const void *bitmap; + void *bitmap; struct f2fs_dir_entry *dentry; __u8 (*filename)[F2FS_SLOT_LEN]; int max; + int nr_bitmap; }; static inline void make_dentry_ptr_block(struct inode *inode, @@ -485,6 +486,7 @@ static inline void make_dentry_ptr_block(struct inode *inode, { d->inode = inode; d->max = NR_DENTRY_IN_BLOCK; + d->nr_bitmap = SIZE_OF_DENTRY_BITMAP; d->bitmap = &t->dentry_bitmap; d->dentry = t->dentry; d->filename = t->filename; @@ -495,6 +497,7 @@ static inline void make_dentry_ptr_inline(struct inode *inode, { d->inode = inode; d->max = NR_INLINE_DENTRY; + d->nr_bitmap = INLINE_DENTRY_BITMAP_SIZE; d->bitmap = &t->dentry_bitmap; d->dentry = t->dentry; d->filename = t->filename; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 8f4cdf7f63dc..69459849ab00 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -360,6 +360,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, struct page *page; struct dnode_of_data dn; struct f2fs_dentry_block *dentry_blk; + struct f2fs_dentry_ptr src, dst; int err; page = f2fs_grab_cache_page(dir->i_mapping, 0, false); @@ -378,21 +379,20 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, dentry_blk = kmap_atomic(page); + make_dentry_ptr_inline(NULL, &src, inline_dentry); + make_dentry_ptr_block(NULL, &dst, dentry_blk); + /* copy data from inline dentry block to new dentry block */ - memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap, - INLINE_DENTRY_BITMAP_SIZE); - memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0, - SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE); + memcpy(dst.bitmap, src.bitmap, src.nr_bitmap); + memset(dst.bitmap + src.nr_bitmap, 0, dst.nr_bitmap - src.nr_bitmap); /* * we do not need to zero out remainder part of dentry and filename * field, since we have used bitmap for marking the usage status of * them, besides, we can also ignore copying/zeroing reserved space * of dentry block, because them haven't been used so far. */ - memcpy(dentry_blk->dentry, inline_dentry->dentry, - sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY); - memcpy(dentry_blk->filename, inline_dentry->filename, - NR_INLINE_DENTRY * F2FS_SLOT_LEN); + memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max); + memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN); kunmap_atomic(dentry_blk); if (!PageUptodate(page)) @@ -529,9 +529,10 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, return PTR_ERR(ipage); inline_dentry = inline_data_addr(ipage); - bit_pos = room_for_filename(&inline_dentry->dentry_bitmap, - slots, NR_INLINE_DENTRY); - if (bit_pos >= NR_INLINE_DENTRY) { + make_dentry_ptr_inline(NULL, &d, inline_dentry); + + bit_pos = room_for_filename(d.bitmap, slots, d.max); + if (bit_pos >= d.max) { err = f2fs_convert_inline_dir(dir, ipage, inline_dentry); if (err) return err; @@ -552,7 +553,6 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, f2fs_wait_on_page_writeback(ipage, NODE, true); name_hash = f2fs_dentry_hash(new_name, NULL); - make_dentry_ptr_inline(NULL, &d, inline_dentry); f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); set_page_dirty(ipage); @@ -576,6 +576,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, struct inode *dir, struct inode *inode) { struct f2fs_inline_dentry *inline_dentry; + struct f2fs_dentry_ptr d; int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); unsigned int bit_pos; int i; @@ -584,10 +585,11 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, f2fs_wait_on_page_writeback(page, NODE, true); inline_dentry = inline_data_addr(page); - bit_pos = dentry - inline_dentry->dentry; + make_dentry_ptr_inline(NULL, &d, inline_dentry); + + bit_pos = dentry - d.dentry; for (i = 0; i < slots; i++) - __clear_bit_le(bit_pos + i, - &inline_dentry->dentry_bitmap); + __clear_bit_le(bit_pos + i, d.bitmap); set_page_dirty(page); f2fs_put_page(page, 1); @@ -605,19 +607,20 @@ bool f2fs_empty_inline_dir(struct inode *dir) struct page *ipage; unsigned int bit_pos = 2; struct f2fs_inline_dentry *inline_dentry; + struct f2fs_dentry_ptr d; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) return false; inline_dentry = inline_data_addr(ipage); - bit_pos = find_next_bit_le(&inline_dentry->dentry_bitmap, - NR_INLINE_DENTRY, - bit_pos); + make_dentry_ptr_inline(NULL, &d, inline_dentry); + + bit_pos = find_next_bit_le(d.bitmap, d.max, bit_pos); f2fs_put_page(ipage, 1); - if (bit_pos < NR_INLINE_DENTRY) + if (bit_pos < d.max) return false; return true; @@ -632,7 +635,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, struct f2fs_dentry_ptr d; int err; - if (ctx->pos == NR_INLINE_DENTRY) + make_dentry_ptr_inline(inode, &d, inline_dentry); + + if (ctx->pos == d.max) return 0; ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); @@ -645,7 +650,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, err = f2fs_fill_dentries(ctx, &d, 0, fstr); if (!err) - ctx->pos = NR_INLINE_DENTRY; + ctx->pos = d.max; f2fs_put_page(ipage, 1); return err < 0 ? err : 0; -- GitLab From 134fccda6cab92c6af97d14ac1602247fc96614b Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 18 Jul 2017 09:48:12 +0800 Subject: [PATCH 0889/5498] f2fs: alloc new nids for xattr block in recovery commit 8790568255f3f0b29ec417089c47008a3d493490 upstream. recovery file A: recovery file B: -get_dnode_of_data -alloc_nid -recover_xattr_data -set_node_addr(sbi, &ni, NEW_ADDR, false); --->bug_on for nid has been used by file A In recovery process, new allocated node blocks may "reuse" xattr block nids, this patch alloc new nids for xattr blocks in recovery process to avoid this problem. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 785612e33da5..9a6ff811c31d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -19,6 +19,7 @@ #include "f2fs.h" #include "node.h" #include "segment.h" +#include "xattr.h" #include "trace.h" #include @@ -2199,7 +2200,8 @@ int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid; - nid_t new_xnid = nid_of_node(page); + nid_t new_xnid; + struct dnode_of_data dn; struct node_info ni; struct page *xpage; @@ -2215,22 +2217,22 @@ int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) recover_xnid: /* 2: update xattr nid in inode */ - remove_free_nid(sbi, new_xnid); - f2fs_i_xnid_write(inode, new_xnid); - if (unlikely(inc_valid_node_count(sbi, inode, false))) - f2fs_bug_on(sbi, 1); + if (!alloc_nid(sbi, &new_xnid)) + return -ENOSPC; + + set_new_dnode(&dn, inode, NULL, NULL, new_xnid); + xpage = new_node_page(&dn, XATTR_NODE_OFFSET); + if (IS_ERR(xpage)) { + alloc_nid_failed(sbi, new_xnid); + return PTR_ERR(xpage); + } + + alloc_nid_done(sbi, new_xnid); update_inode_page(inode); /* 3: update and set xattr node page dirty */ - xpage = grab_cache_page(NODE_MAPPING(sbi), new_xnid); - if (!xpage) - return -ENOMEM; - - memcpy(F2FS_NODE(xpage), F2FS_NODE(page), PAGE_SIZE); + memcpy(F2FS_NODE(xpage), F2FS_NODE(page), VALID_XATTR_BLOCK_SIZE); - get_node_info(sbi, new_xnid, &ni); - ni.ino = inode->i_ino; - set_node_addr(sbi, &ni, NEW_ADDR, false); set_page_dirty(xpage); f2fs_put_page(xpage, 1); -- GitLab From fe3a28a8ac6f7128e5b2ae8359f20bc973305411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20A=2E=20Fern=C3=A1ndez?= Date: Sun, 23 Jul 2017 22:32:54 -0300 Subject: [PATCH 0890/5498] f2fs: preserve i_mode if __f2fs_set_acl() fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 14af20fcb1833dd776822361891963c90f7b0262 upstream. When changing a file's acl mask, __f2fs_set_acl() will first set the group bits of i_mode to the value of the mask, and only then set the actual extended attribute representing the new acl. If the second part fails (due to lack of space, for example) and the file had no acl attribute to begin with, the system will from now on assume that the mask permission bits are actual group permission bits, potentially granting access to the wrong users. Prevent this by only changing the inode mode after the acl has been set. Signed-off-by: Ernesto A. Fernández Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index b4b8438c42ef..436b3a1464d9 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -207,15 +207,16 @@ static int __f2fs_set_acl(struct inode *inode, int type, void *value = NULL; size_t size = 0; int error; + umode_t mode = inode->i_mode; switch (type) { case ACL_TYPE_ACCESS: name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl && !ipage) { - error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + error = posix_acl_update_mode(inode, &mode, &acl); if (error) return error; - set_acl_inode(inode, inode->i_mode); + set_acl_inode(inode, mode); } break; -- GitLab From 1137e8fb959fb3d9b18708eb245c8778579d51f4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 19 Jul 2017 00:19:05 +0800 Subject: [PATCH 0891/5498] f2fs: make max inline size changeable commit f247037120ecd3dcbbc196b51ded8b57edf4904f upstream. This patch tries to make below macros calculating max inline size, inline dentry field size considerring reserving size-changeable space: - MAX_INLINE_DATA - NR_INLINE_DENTRY - INLINE_DENTRY_BITMAP_SIZE - INLINE_RESERVED_SIZE Then, when inline_{data,dentry} options is enabled, it allows us to reserve inline space with different size flexibly for adding newly introduced inode attribute. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 +- fs/f2fs/f2fs.h | 48 +++++++++++++++++---- fs/f2fs/inline.c | 95 +++++++++++++++++++++-------------------- fs/f2fs/inode.c | 4 +- fs/f2fs/super.c | 3 ++ include/linux/f2fs_fs.h | 23 +--------- 6 files changed, 96 insertions(+), 81 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6d467fdaf900..a71ced0ba4b7 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -811,7 +811,7 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, F2FS_GET_BLOCK_PRE_AIO : F2FS_GET_BLOCK_PRE_DIO); } - if (pos + count > MAX_INLINE_DATA) { + if (pos + count > MAX_INLINE_DATA(inode)) { err = f2fs_convert_inline_inode(inode); if (err) return err; @@ -1853,7 +1853,7 @@ restart: set_new_dnode(&dn, inode, ipage, ipage, 0); if (f2fs_has_inline_data(inode)) { - if (pos + len <= MAX_INLINE_DATA) { + if (pos + len <= MAX_INLINE_DATA(inode)) { read_inline_data(page, ipage); set_inode_flag(inode, FI_DATA_EXIST); if (inode->i_nlink) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b9940eb65e3b..31e38f824877 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -468,6 +468,25 @@ struct f2fs_flush_device { u32 segments; /* # of segments to flush */ }; +/* for inline stuff */ +#define DEF_INLINE_RESERVED_SIZE 1 + +static inline int get_inline_reserved_size(struct inode *inode); +#define MAX_INLINE_DATA(inode) (sizeof(__le32) * (DEF_ADDRS_PER_INODE -\ + get_inline_reserved_size(inode) -\ + F2FS_INLINE_XATTR_ADDRS)) + +/* for inline dir */ +#define NR_INLINE_DENTRY(inode) (MAX_INLINE_DATA(inode) * BITS_PER_BYTE / \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + BITS_PER_BYTE + 1)) +#define INLINE_DENTRY_BITMAP_SIZE(inode) ((NR_INLINE_DENTRY(inode) + \ + BITS_PER_BYTE - 1) / BITS_PER_BYTE) +#define INLINE_RESERVED_SIZE(inode) (MAX_INLINE_DATA(inode) - \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + NR_INLINE_DENTRY(inode) + \ + INLINE_DENTRY_BITMAP_SIZE(inode))) + /* * For INODE and NODE manager */ @@ -493,14 +512,19 @@ static inline void make_dentry_ptr_block(struct inode *inode, } static inline void make_dentry_ptr_inline(struct inode *inode, - struct f2fs_dentry_ptr *d, struct f2fs_inline_dentry *t) + struct f2fs_dentry_ptr *d, void *t) { + int entry_cnt = NR_INLINE_DENTRY(inode); + int bitmap_size = INLINE_DENTRY_BITMAP_SIZE(inode); + int reserved_size = INLINE_RESERVED_SIZE(inode); + d->inode = inode; - d->max = NR_INLINE_DENTRY; - d->nr_bitmap = INLINE_DENTRY_BITMAP_SIZE; - d->bitmap = &t->dentry_bitmap; - d->dentry = t->dentry; - d->filename = t->filename; + d->max = entry_cnt; + d->nr_bitmap = bitmap_size; + d->bitmap = t; + d->dentry = t + bitmap_size + reserved_size; + d->filename = t + bitmap_size + reserved_size + + SIZE_OF_DIR_ENTRY * entry_cnt; } /* @@ -652,6 +676,8 @@ struct f2fs_inode_info { struct extent_tree *extent_tree; /* cached extent_tree entry */ struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ struct rw_semaphore i_mmap_sem; + + int i_inline_reserved; /* reserved size in inline data */ }; static inline void get_extent_info(struct extent_info *ext, @@ -2180,11 +2206,12 @@ static inline bool f2fs_is_drop_cache(struct inode *inode) return is_inode_flag_set(inode, FI_DROP_CACHE); } -static inline void *inline_data_addr(struct page *page) +static inline void *inline_data_addr(struct inode *inode, struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); + int reserved_size = get_inline_reserved_size(inode); - return (void *)&(ri->i_addr[1]); + return (void *)&(ri->i_addr[reserved_size]); } static inline int f2fs_has_inline_dentry(struct inode *inode) @@ -2295,6 +2322,11 @@ static inline void *kvzalloc(size_t size, gfp_t flags) return ret; } +static inline int get_inline_reserved_size(struct inode *inode) +{ + return F2FS_I(inode)->i_inline_reserved; +} + #define get_inode_mode(i) \ ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 69459849ab00..848deebef900 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -23,7 +23,7 @@ bool f2fs_may_inline_data(struct inode *inode) if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) return false; - if (i_size_read(inode) > MAX_INLINE_DATA) + if (i_size_read(inode) > MAX_INLINE_DATA(inode)) return false; if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) @@ -45,6 +45,7 @@ bool f2fs_may_inline_dentry(struct inode *inode) void read_inline_data(struct page *page, struct page *ipage) { + struct inode *inode = page->mapping->host; void *src_addr, *dst_addr; if (PageUptodate(page)) @@ -52,12 +53,12 @@ void read_inline_data(struct page *page, struct page *ipage) f2fs_bug_on(F2FS_P_SB(page), page->index); - zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE); + zero_user_segment(page, MAX_INLINE_DATA(inode), PAGE_SIZE); /* Copy the whole inline data block */ - src_addr = inline_data_addr(ipage); + src_addr = inline_data_addr(inode, ipage); dst_addr = kmap_atomic(page); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode)); flush_dcache_page(page); kunmap_atomic(dst_addr); if (!PageUptodate(page)) @@ -68,13 +69,13 @@ void truncate_inline_inode(struct inode *inode, struct page *ipage, u64 from) { void *addr; - if (from >= MAX_INLINE_DATA) + if (from >= MAX_INLINE_DATA(inode)) return; - addr = inline_data_addr(ipage); + addr = inline_data_addr(inode, ipage); f2fs_wait_on_page_writeback(ipage, NODE, true); - memset(addr + from, 0, MAX_INLINE_DATA - from); + memset(addr + from, 0, MAX_INLINE_DATA(inode) - from); set_page_dirty(ipage); if (from == 0) @@ -234,8 +235,8 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) f2fs_wait_on_page_writeback(dn.inode_page, NODE, true); src_addr = kmap_atomic(page); - dst_addr = inline_data_addr(dn.inode_page); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + dst_addr = inline_data_addr(inode, dn.inode_page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode)); kunmap_atomic(src_addr); set_page_dirty(dn.inode_page); @@ -273,9 +274,9 @@ process_inline: f2fs_wait_on_page_writeback(ipage, NODE, true); - src_addr = inline_data_addr(npage); - dst_addr = inline_data_addr(ipage); - memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + src_addr = inline_data_addr(inode, npage); + dst_addr = inline_data_addr(inode, ipage); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA(inode)); set_inode_flag(inode, FI_INLINE_DATA); set_inode_flag(inode, FI_DATA_EXIST); @@ -303,11 +304,11 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, struct fscrypt_name *fname, struct page **res_page) { struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - struct f2fs_inline_dentry *inline_dentry; struct qstr name = FSTR_TO_QSTR(&fname->disk_name); struct f2fs_dir_entry *de; struct f2fs_dentry_ptr d; struct page *ipage; + void *inline_dentry; f2fs_hash_t namehash; ipage = get_node_page(sbi, dir->i_ino); @@ -318,9 +319,9 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, namehash = f2fs_dentry_hash(&name, fname); - inline_dentry = inline_data_addr(ipage); + inline_dentry = inline_data_addr(dir, ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + make_dentry_ptr_inline(dir, &d, inline_dentry); de = find_target_dentry(fname, namehash, NULL, &d); unlock_page(ipage); if (de) @@ -334,19 +335,19 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, int make_empty_inline_dir(struct inode *inode, struct inode *parent, struct page *ipage) { - struct f2fs_inline_dentry *inline_dentry; struct f2fs_dentry_ptr d; + void *inline_dentry; - inline_dentry = inline_data_addr(ipage); + inline_dentry = inline_data_addr(inode, ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + make_dentry_ptr_inline(inode, &d, inline_dentry); do_make_empty_dir(inode, parent, &d); set_page_dirty(ipage); /* update i_size to MAX_INLINE_DATA */ - if (i_size_read(inode) < MAX_INLINE_DATA) - f2fs_i_size_write(inode, MAX_INLINE_DATA); + if (i_size_read(inode) < MAX_INLINE_DATA(inode)) + f2fs_i_size_write(inode, MAX_INLINE_DATA(inode)); return 0; } @@ -355,7 +356,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, * release ipage in this function. */ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, - struct f2fs_inline_dentry *inline_dentry) + void *inline_dentry) { struct page *page; struct dnode_of_data dn; @@ -375,12 +376,12 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, goto out; f2fs_wait_on_page_writeback(page, DATA, true); - zero_user_segment(page, MAX_INLINE_DATA, PAGE_SIZE); + zero_user_segment(page, MAX_INLINE_DATA(dir), PAGE_SIZE); dentry_blk = kmap_atomic(page); - make_dentry_ptr_inline(NULL, &src, inline_dentry); - make_dentry_ptr_block(NULL, &dst, dentry_blk); + make_dentry_ptr_inline(dir, &src, inline_dentry); + make_dentry_ptr_block(dir, &dst, dentry_blk); /* copy data from inline dentry block to new dentry block */ memcpy(dst.bitmap, src.bitmap, src.nr_bitmap); @@ -413,14 +414,13 @@ out: return err; } -static int f2fs_add_inline_entries(struct inode *dir, - struct f2fs_inline_dentry *inline_dentry) +static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry) { struct f2fs_dentry_ptr d; unsigned long bit_pos = 0; int err = 0; - make_dentry_ptr_inline(NULL, &d, inline_dentry); + make_dentry_ptr_inline(dir, &d, inline_dentry); while (bit_pos < d.max) { struct f2fs_dir_entry *de; @@ -462,19 +462,19 @@ punch_dentry_pages: } static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, - struct f2fs_inline_dentry *inline_dentry) + void *inline_dentry) { - struct f2fs_inline_dentry *backup_dentry; + void *backup_dentry; int err; backup_dentry = f2fs_kmalloc(F2FS_I_SB(dir), - sizeof(struct f2fs_inline_dentry), GFP_F2FS_ZERO); + MAX_INLINE_DATA(dir), GFP_F2FS_ZERO); if (!backup_dentry) { f2fs_put_page(ipage, 1); return -ENOMEM; } - memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA); + memcpy(backup_dentry, inline_dentry, MAX_INLINE_DATA(dir)); truncate_inline_inode(dir, ipage, 0); unlock_page(ipage); @@ -491,9 +491,9 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, return 0; recover: lock_page(ipage); - memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA); + memcpy(inline_dentry, backup_dentry, MAX_INLINE_DATA(dir)); f2fs_i_depth_write(dir, 0); - f2fs_i_size_write(dir, MAX_INLINE_DATA); + f2fs_i_size_write(dir, MAX_INLINE_DATA(dir)); set_page_dirty(ipage); f2fs_put_page(ipage, 1); @@ -502,7 +502,7 @@ recover: } static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, - struct f2fs_inline_dentry *inline_dentry) + void *inline_dentry) { if (!F2FS_I(dir)->i_dir_level) return f2fs_move_inline_dirents(dir, ipage, inline_dentry); @@ -518,7 +518,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, struct page *ipage; unsigned int bit_pos; f2fs_hash_t name_hash; - struct f2fs_inline_dentry *inline_dentry = NULL; + void *inline_dentry = NULL; struct f2fs_dentry_ptr d; int slots = GET_DENTRY_SLOTS(new_name->len); struct page *page = NULL; @@ -528,8 +528,8 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, if (IS_ERR(ipage)) return PTR_ERR(ipage); - inline_dentry = inline_data_addr(ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + inline_dentry = inline_data_addr(dir, ipage); + make_dentry_ptr_inline(dir, &d, inline_dentry); bit_pos = room_for_filename(d.bitmap, slots, d.max); if (bit_pos >= d.max) { @@ -575,8 +575,8 @@ out: void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, struct inode *dir, struct inode *inode) { - struct f2fs_inline_dentry *inline_dentry; struct f2fs_dentry_ptr d; + void *inline_dentry; int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); unsigned int bit_pos; int i; @@ -584,8 +584,8 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, lock_page(page); f2fs_wait_on_page_writeback(page, NODE, true); - inline_dentry = inline_data_addr(page); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + inline_dentry = inline_data_addr(dir, page); + make_dentry_ptr_inline(dir, &d, inline_dentry); bit_pos = dentry - d.dentry; for (i = 0; i < slots; i++) @@ -606,15 +606,15 @@ bool f2fs_empty_inline_dir(struct inode *dir) struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct page *ipage; unsigned int bit_pos = 2; - struct f2fs_inline_dentry *inline_dentry; + void *inline_dentry; struct f2fs_dentry_ptr d; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) return false; - inline_dentry = inline_data_addr(ipage); - make_dentry_ptr_inline(NULL, &d, inline_dentry); + inline_dentry = inline_data_addr(dir, ipage); + make_dentry_ptr_inline(dir, &d, inline_dentry); bit_pos = find_next_bit_le(d.bitmap, d.max, bit_pos); @@ -630,9 +630,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, struct fscrypt_str *fstr) { struct inode *inode = file_inode(file); - struct f2fs_inline_dentry *inline_dentry = NULL; struct page *ipage = NULL; struct f2fs_dentry_ptr d; + void *inline_dentry = NULL; int err; make_dentry_ptr_inline(inode, &d, inline_dentry); @@ -644,7 +644,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, if (IS_ERR(ipage)) return PTR_ERR(ipage); - inline_dentry = inline_data_addr(ipage); + inline_dentry = inline_data_addr(inode, ipage); make_dentry_ptr_inline(inode, &d, inline_dentry); @@ -675,7 +675,7 @@ int f2fs_inline_data_fiemap(struct inode *inode, goto out; } - ilen = min_t(size_t, MAX_INLINE_DATA, i_size_read(inode)); + ilen = min_t(size_t, MAX_INLINE_DATA(inode), i_size_read(inode)); if (start >= ilen) goto out; if (start + len < ilen) @@ -684,7 +684,8 @@ int f2fs_inline_data_fiemap(struct inode *inode, get_node_info(F2FS_I_SB(inode), inode->i_ino, &ni); byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits; - byteaddr += (char *)inline_data_addr(ipage) - (char *)F2FS_INODE(ipage); + byteaddr += (char *)inline_data_addr(inode, ipage) - + (char *)F2FS_INODE(ipage); err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags); out: f2fs_put_page(ipage, 1); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index d01d93ede6ee..ce0a2f4a78f4 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -87,9 +87,9 @@ static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri) static void __recover_inline_status(struct inode *inode, struct page *ipage) { - void *inline_data = inline_data_addr(ipage); + void *inline_data = inline_data_addr(inode, ipage); __le32 *start = inline_data; - __le32 *end = start + MAX_INLINE_DATA / sizeof(__le32); + __le32 *end = start + MAX_INLINE_DATA(inode) / sizeof(__le32); while (start < end) { if (*start++) { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 981d0d3923d7..64ff95f0ba3e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -435,6 +435,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) #endif /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; + + fi->i_inline_reserved = DEF_INLINE_RESERVED_SIZE; + return &fi->vfs_inode; } diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 67303b808ff1..8799520e6db0 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -206,9 +206,6 @@ struct f2fs_extent { #define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ #define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ -#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ - F2FS_INLINE_XATTR_ADDRS - 1)) - struct f2fs_inode { __le16 i_mode; /* file mode */ __u8 i_advise; /* file hints */ @@ -465,7 +462,7 @@ typedef __le32 f2fs_hash_t; #define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1)) /* - * space utilization of regular dentry and inline dentry + * space utilization of regular dentry and inline dentry (w/o extra reservation) * regular dentry inline dentry * bitmap 1 * 27 = 27 1 * 23 = 23 * reserved 1 * 3 = 3 1 * 7 = 7 @@ -501,24 +498,6 @@ struct f2fs_dentry_block { __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; } __packed; -/* for inline dir */ -#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \ - ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ - BITS_PER_BYTE + 1)) -#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \ - BITS_PER_BYTE - 1) / BITS_PER_BYTE) -#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \ - ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ - NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE)) - -/* inline directory entry structure */ -struct f2fs_inline_dentry { - __u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE]; - __u8 reserved[INLINE_RESERVED_SIZE]; - struct f2fs_dir_entry dentry[NR_INLINE_DENTRY]; - __u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN]; -} __packed; - /* file types used in inode_info->flags */ enum { F2FS_FT_UNKNOWN, -- GitLab From bfa88f75a09a8135a2da6e0b0dee713004a21930 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 19 Jul 2017 00:19:06 +0800 Subject: [PATCH 0892/5498] f2fs: enhance on-disk inode structure scalability commit 7a2af766af15887754f7f7a0869b4603b390876a upstream. This patch add new flag F2FS_EXTRA_ATTR storing in inode.i_inline to indicate that on-disk structure of current inode is extended. In order to extend, we changed the inode structure a bit: Original one: struct f2fs_inode { ... struct f2fs_extent i_ext; __le32 i_addr[DEF_ADDRS_PER_INODE]; __le32 i_nid[DEF_NIDS_PER_INODE]; } Extended one: struct f2fs_inode { ... struct f2fs_extent i_ext; union { struct { __le16 i_extra_isize; __le16 i_padding; __le32 i_extra_end[0]; }; __le32 i_addr[DEF_ADDRS_PER_INODE]; }; __le32 i_nid[DEF_NIDS_PER_INODE]; } Once F2FS_EXTRA_ATTR is set, we will steal four bytes in the head of i_addr field for storing i_extra_isize and i_padding. with i_extra_isize, we can calculate actual size of reserved space in i_addr, available attribute fields included in total extra attribute fields for current inode can be described as below: +--------------------+ | .i_mode | | ... | | .i_ext | +--------------------+ | .i_extra_isize |-----+ | .i_padding | | | .i_prjid | | | .i_atime_extra | | | .i_ctime_extra | | | .i_mtime_extra |<----+ | .i_inode_cs |<----- store blkaddr/inline from here | .i_xattr_cs | | ... | +--------------------+ | | | block address | | | +--------------------+ | .i_nid | +--------------------+ | node_footer | | (nid, ino, offset) | +--------------------+ Hence, with this patch, we would enhance scalability of f2fs inode for storing more newly added attribute. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 15 ++++++--- fs/f2fs/f2fs.h | 72 +++++++++++++++++++++++++++++++---------- fs/f2fs/file.c | 23 ++++++++----- fs/f2fs/gc.c | 2 +- fs/f2fs/inode.c | 32 +++++++++++------- fs/f2fs/namei.c | 5 +++ fs/f2fs/node.c | 7 ++-- fs/f2fs/recovery.c | 7 ++-- fs/f2fs/super.c | 11 +++++-- include/linux/f2fs_fs.h | 13 ++++++-- 10 files changed, 135 insertions(+), 52 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a71ced0ba4b7..5ea4bb6d895a 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -461,10 +461,14 @@ static void __set_data_blkaddr(struct dnode_of_data *dn) { struct f2fs_node *rn = F2FS_NODE(dn->node_page); __le32 *addr_array; + int base = 0; + + if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode)) + base = get_extra_isize(dn->inode); /* Get physical address of data block */ addr_array = blkaddr_in_node(rn); - addr_array[dn->ofs_in_node] = cpu_to_le32(dn->data_blkaddr); + addr_array[base + dn->ofs_in_node] = cpu_to_le32(dn->data_blkaddr); } /* @@ -508,8 +512,8 @@ int reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count) f2fs_wait_on_page_writeback(dn->node_page, NODE, true); for (; count > 0; dn->ofs_in_node++) { - block_t blkaddr = - datablock_addr(dn->node_page, dn->ofs_in_node); + block_t blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); if (blkaddr == NULL_ADDR) { dn->data_blkaddr = NEW_ADDR; __set_data_blkaddr(dn); @@ -756,7 +760,8 @@ static int __allocate_data_block(struct dnode_of_data *dn) if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC))) return -EPERM; - dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); + dn->data_blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); if (dn->data_blkaddr == NEW_ADDR) goto alloc; @@ -900,7 +905,7 @@ next_dnode: end_offset = ADDRS_PER_PAGE(dn.node_page, inode); next_block: - blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + blkaddr = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { if (create) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 31e38f824877..ce9912d5597e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -112,9 +112,10 @@ struct f2fs_mount_info { unsigned int opt; }; -#define F2FS_FEATURE_ENCRYPT 0x0001 -#define F2FS_FEATURE_BLKZONED 0x0002 -#define F2FS_FEATURE_ATOMIC_WRITE 0x0004 +#define F2FS_FEATURE_ENCRYPT 0x0001 +#define F2FS_FEATURE_BLKZONED 0x0002 +#define F2FS_FEATURE_ATOMIC_WRITE 0x0004 +#define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -470,10 +471,10 @@ struct f2fs_flush_device { /* for inline stuff */ #define DEF_INLINE_RESERVED_SIZE 1 - -static inline int get_inline_reserved_size(struct inode *inode); -#define MAX_INLINE_DATA(inode) (sizeof(__le32) * (DEF_ADDRS_PER_INODE -\ - get_inline_reserved_size(inode) -\ +static inline int get_extra_isize(struct inode *inode); +#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ + (CUR_ADDRS_PER_INODE(inode) - \ + DEF_INLINE_RESERVED_SIZE - \ F2FS_INLINE_XATTR_ADDRS)) /* for inline dir */ @@ -677,7 +678,7 @@ struct f2fs_inode_info { struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ struct rw_semaphore i_mmap_sem; - int i_inline_reserved; /* reserved size in inline data */ + int i_extra_isize; /* size of extra space located in i_addr */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1898,20 +1899,38 @@ static inline bool IS_INODE(struct page *page) return RAW_IS_INODE(p); } +static inline int offset_in_addr(struct f2fs_inode *i) +{ + return (i->i_inline & F2FS_EXTRA_ATTR) ? + (le16_to_cpu(i->i_extra_isize) / sizeof(__le32)) : 0; +} + static inline __le32 *blkaddr_in_node(struct f2fs_node *node) { return RAW_IS_INODE(node) ? node->i.i_addr : node->dn.addr; } -static inline block_t datablock_addr(struct page *node_page, - unsigned int offset) +static inline int f2fs_has_extra_attr(struct inode *inode); +static inline block_t datablock_addr(struct inode *inode, + struct page *node_page, unsigned int offset) { struct f2fs_node *raw_node; __le32 *addr_array; + int base = 0; + bool is_inode = IS_INODE(node_page); raw_node = F2FS_NODE(node_page); + + /* from GC path only */ + if (!inode) { + if (is_inode) + base = offset_in_addr(&raw_node->i); + } else if (f2fs_has_extra_attr(inode) && is_inode) { + base = get_extra_isize(inode); + } + addr_array = blkaddr_in_node(raw_node); - return le32_to_cpu(addr_array[offset]); + return le32_to_cpu(addr_array[base + offset]); } static inline int f2fs_test_bit(unsigned int nr, char *addr) @@ -2001,6 +2020,7 @@ enum { FI_DO_DEFRAG, /* indicate defragment is running */ FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ FI_HOT_DATA, /* indicate file is hot */ + FI_EXTRA_ATTR, /* indicate file has extra attribute */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, @@ -2120,6 +2140,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) set_bit(FI_DATA_EXIST, &fi->flags); if (ri->i_inline & F2FS_INLINE_DOTS) set_bit(FI_INLINE_DOTS, &fi->flags); + if (ri->i_inline & F2FS_EXTRA_ATTR) + set_bit(FI_EXTRA_ATTR, &fi->flags); } static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) @@ -2136,6 +2158,13 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) ri->i_inline |= F2FS_DATA_EXIST; if (is_inode_flag_set(inode, FI_INLINE_DOTS)) ri->i_inline |= F2FS_INLINE_DOTS; + if (is_inode_flag_set(inode, FI_EXTRA_ATTR)) + ri->i_inline |= F2FS_EXTRA_ATTR; +} + +static inline int f2fs_has_extra_attr(struct inode *inode) +{ + return is_inode_flag_set(inode, FI_EXTRA_ATTR); } static inline int f2fs_has_inline_xattr(struct inode *inode) @@ -2146,8 +2175,8 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) static inline unsigned int addrs_per_inode(struct inode *inode) { if (f2fs_has_inline_xattr(inode)) - return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS; - return DEF_ADDRS_PER_INODE; + return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS; + return CUR_ADDRS_PER_INODE(inode); } static inline void *inline_xattr_addr(struct page *page) @@ -2209,9 +2238,9 @@ static inline bool f2fs_is_drop_cache(struct inode *inode) static inline void *inline_data_addr(struct inode *inode, struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); - int reserved_size = get_inline_reserved_size(inode); + int extra_size = get_extra_isize(inode); - return (void *)&(ri->i_addr[reserved_size]); + return (void *)&(ri->i_addr[extra_size + DEF_INLINE_RESERVED_SIZE]); } static inline int f2fs_has_inline_dentry(struct inode *inode) @@ -2322,15 +2351,19 @@ static inline void *kvzalloc(size_t size, gfp_t flags) return ret; } -static inline int get_inline_reserved_size(struct inode *inode) +static inline int get_extra_isize(struct inode *inode) { - return F2FS_I(inode)->i_inline_reserved; + return F2FS_I(inode)->i_extra_isize / sizeof(__le32); } #define get_inode_mode(i) \ ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) +#define F2FS_TOTAL_EXTRA_ATTR_SIZE \ + (offsetof(struct f2fs_inode, i_extra_end) - \ + offsetof(struct f2fs_inode, i_extra_isize)) \ + /* * file.c */ @@ -2924,6 +2957,11 @@ static inline int f2fs_sb_mounted_blkzoned(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_BLKZONED); } +static inline int f2fs_sb_has_extra_attr(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_EXTRA_ATTR); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e8fba2586c3f..3c3990cefee2 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -385,7 +385,8 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) dn.ofs_in_node++, pgofs++, data_ofs = (loff_t)pgofs << PAGE_SHIFT) { block_t blkaddr; - blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + blkaddr = datablock_addr(dn.inode, + dn.node_page, dn.ofs_in_node); if (__found_offset(blkaddr, dirty, pgofs, whence)) { f2fs_put_dnode(&dn); @@ -470,9 +471,13 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) struct f2fs_node *raw_node; int nr_free = 0, ofs = dn->ofs_in_node, len = count; __le32 *addr; + int base = 0; + + if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode)) + base = get_extra_isize(dn->inode); raw_node = F2FS_NODE(dn->node_page); - addr = blkaddr_in_node(raw_node) + ofs; + addr = blkaddr_in_node(raw_node) + base + ofs; for (; count > 0; count--, addr++, dn->ofs_in_node++) { block_t blkaddr = le32_to_cpu(*addr); @@ -909,7 +914,8 @@ next_dnode: done = min((pgoff_t)ADDRS_PER_PAGE(dn.node_page, inode) - dn.ofs_in_node, len); for (i = 0; i < done; i++, blkaddr++, do_replace++, dn.ofs_in_node++) { - *blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + *blkaddr = datablock_addr(dn.inode, + dn.node_page, dn.ofs_in_node); if (!is_checkpointed_data(sbi, *blkaddr)) { if (test_opt(sbi, LFS)) { @@ -985,8 +991,8 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode, ADDRS_PER_PAGE(dn.node_page, dst_inode) - dn.ofs_in_node, len - i); do { - dn.data_blkaddr = datablock_addr(dn.node_page, - dn.ofs_in_node); + dn.data_blkaddr = datablock_addr(dn.inode, + dn.node_page, dn.ofs_in_node); truncate_data_blocks_range(&dn, 1); if (do_replace[i]) { @@ -1155,7 +1161,8 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start, int ret; for (; index < end; index++, dn->ofs_in_node++) { - if (datablock_addr(dn->node_page, dn->ofs_in_node) == NULL_ADDR) + if (datablock_addr(dn->inode, dn->node_page, + dn->ofs_in_node) == NULL_ADDR) count++; } @@ -1166,8 +1173,8 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start, dn->ofs_in_node = ofs_in_node; for (index = start; index < end; index++, dn->ofs_in_node++) { - dn->data_blkaddr = - datablock_addr(dn->node_page, dn->ofs_in_node); + dn->data_blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); /* * reserve_new_blocks will not guarantee entire block * allocation. diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 41c649c9c55c..f57cadae1a30 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -587,7 +587,7 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, } *nofs = ofs_of_node(node_page); - source_blkaddr = datablock_addr(node_page, ofs_in_node); + source_blkaddr = datablock_addr(NULL, node_page, ofs_in_node); f2fs_put_page(node_page, 1); if (source_blkaddr != blkaddr) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index ce0a2f4a78f4..e1699e542d17 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -49,20 +49,22 @@ void f2fs_set_inode_flags(struct inode *inode) static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) { + int extra_size = get_extra_isize(inode); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - if (ri->i_addr[0]) - inode->i_rdev = - old_decode_dev(le32_to_cpu(ri->i_addr[0])); + if (ri->i_addr[extra_size]) + inode->i_rdev = old_decode_dev( + le32_to_cpu(ri->i_addr[extra_size])); else - inode->i_rdev = - new_decode_dev(le32_to_cpu(ri->i_addr[1])); + inode->i_rdev = new_decode_dev( + le32_to_cpu(ri->i_addr[extra_size + 1])); } } static bool __written_first_block(struct f2fs_inode *ri) { - block_t addr = le32_to_cpu(ri->i_addr[0]); + block_t addr = le32_to_cpu(ri->i_addr[offset_in_addr(ri)]); if (addr != NEW_ADDR && addr != NULL_ADDR) return true; @@ -71,16 +73,18 @@ static bool __written_first_block(struct f2fs_inode *ri) static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri) { + int extra_size = get_extra_isize(inode); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { if (old_valid_dev(inode->i_rdev)) { - ri->i_addr[0] = + ri->i_addr[extra_size] = cpu_to_le32(old_encode_dev(inode->i_rdev)); - ri->i_addr[1] = 0; + ri->i_addr[extra_size + 1] = 0; } else { - ri->i_addr[0] = 0; - ri->i_addr[1] = + ri->i_addr[extra_size] = 0; + ri->i_addr[extra_size + 1] = cpu_to_le32(new_encode_dev(inode->i_rdev)); - ri->i_addr[2] = 0; + ri->i_addr[extra_size + 2] = 0; } } } @@ -153,6 +157,9 @@ static int do_read_inode(struct inode *inode) get_inline_info(inode, ri); + fi->i_extra_isize = f2fs_has_extra_attr(inode) ? + le16_to_cpu(ri->i_extra_isize) : 0; + /* check data exist */ if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) __recover_inline_status(inode, node_page); @@ -292,6 +299,9 @@ int update_inode(struct inode *inode, struct page *node_page) ri->i_generation = cpu_to_le32(inode->i_generation); ri->i_dir_level = F2FS_I(inode)->i_dir_level; + if (f2fs_has_extra_attr(inode)) + ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 16b5f17680a1..ad8be1c53f92 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -71,6 +71,11 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) set_inode_flag(inode, FI_NEW_INODE); + if (f2fs_sb_has_extra_attr(sbi->sb)) { + set_inode_flag(inode, FI_EXTRA_ATTR); + F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; + } + if (test_opt(sbi, INLINE_XATTR)) set_inode_flag(inode, FI_INLINE_XATTR); if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode)) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9a6ff811c31d..e903cb7451dd 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -655,7 +655,8 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) dn->nid = nids[level]; dn->ofs_in_node = offset[level]; dn->node_page = npage[level]; - dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); + dn->data_blkaddr = datablock_addr(dn->inode, + dn->node_page, dn->ofs_in_node); return 0; release_pages: @@ -2272,7 +2273,9 @@ retry: dst->i_blocks = cpu_to_le64(1); dst->i_links = cpu_to_le32(1); dst->i_xattr_nid = 0; - dst->i_inline = src->i_inline & F2FS_INLINE_XATTR; + dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR); + if (dst->i_inline & F2FS_EXTRA_ATTR) + dst->i_extra_isize = src->i_extra_isize; new_ni = old_ni; new_ni.ino = ino; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 20147d297f89..ebde404ae67f 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -361,7 +361,8 @@ out: return 0; truncate_out: - if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr) + if (datablock_addr(tdn.inode, tdn.node_page, + tdn.ofs_in_node) == blkaddr) truncate_data_blocks_range(&tdn, 1); if (dn->inode->i_ino == nid && !dn->inode_page_locked) unlock_page(dn->inode_page); @@ -414,8 +415,8 @@ retry_dn: for (; start < end; start++, dn.ofs_in_node++) { block_t src, dest; - src = datablock_addr(dn.node_page, dn.ofs_in_node); - dest = datablock_addr(page, dn.ofs_in_node); + src = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); + dest = datablock_addr(dn.inode, page, dn.ofs_in_node); /* skip recovering if dest is the same as src */ if (src == dest) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 64ff95f0ba3e..cba156aca1c5 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -436,8 +436,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; - fi->i_inline_reserved = DEF_INLINE_RESERVED_SIZE; - return &fi->vfs_inode; } @@ -1284,9 +1282,16 @@ static const struct export_operations f2fs_export_ops = { static loff_t max_file_blocks(void) { - loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS); + loff_t result = 0; loff_t leaf_count = ADDRS_PER_BLOCK; + /* + * note: previously, result is equal to (DEF_ADDRS_PER_INODE - + * F2FS_INLINE_XATTR_ADDRS), but now f2fs try to reserve more + * space in inode.i_addr, it will be more safe to reassign + * result as zero. + */ + /* two direct node blocks */ result += (leaf_count * 2); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 8799520e6db0..b7331bfd440f 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -186,6 +186,8 @@ struct f2fs_extent { #define F2FS_NAME_LEN 255 #define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ +#define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ + get_extra_isize(inode)) #define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ #define ADDRS_PER_INODE(inode) addrs_per_inode(inode) #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ @@ -205,6 +207,7 @@ struct f2fs_extent { #define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */ #define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ #define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ +#define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */ struct f2fs_inode { __le16 i_mode; /* file mode */ @@ -232,8 +235,14 @@ struct f2fs_inode { struct f2fs_extent i_ext; /* caching a largest extent */ - __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ - + union { + struct { + __le16 i_extra_isize; /* extra inode attribute size */ + __le16 i_padding; /* padding */ + __le32 i_extra_end[0]; /* for attribute size calculation */ + }; + __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ + }; __le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2), double_indirect(1) node id */ } __packed; -- GitLab From c068e5d803089bbe089524550de5ee231eec688e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 24 Jul 2017 17:12:06 +0800 Subject: [PATCH 0893/5498] f2fs: record quota during dot{,dot} recovery commit a6d3a479ae082173c6102eba0e502cc439dacf21 upstream. In ->lookup(), we will have a try to recover dot or dotdot for corrupted directory, once disk quota is on, if it allocates new block during dotdot recovery, we need to record disk quota info for the allocation, so this patch fixes this issue by adding missing dquot_initialize() in __recover_dot_dentries. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index ad8be1c53f92..70e9a1d1c02f 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -261,6 +261,8 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) return 0; } + dquot_initialize(dir); + f2fs_balance_fs(sbi, true); f2fs_lock_op(sbi); -- GitLab From ce2617a969fb02e3f0760eafcd837cea6852bb28 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 26 Jul 2017 00:01:41 +0800 Subject: [PATCH 0894/5498] f2fs: support project quota commit 5c57132eaf5265937e46340bfbfb97ffb078c423 upstream. This patch adds to support plain project quota. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 1 + fs/f2fs/f2fs.h | 33 ++++++++++++++++++++++++++++++ fs/f2fs/file.c | 13 ------------ fs/f2fs/inode.c | 24 +++++++++++++++++++++- fs/f2fs/namei.c | 30 +++++++++++++++++++++++++++ fs/f2fs/node.c | 7 ++++++- fs/f2fs/super.c | 26 +++++++++++++++++++++++ include/linux/f2fs_fs.h | 3 +++ include/uapi/linux/fs.h | 1 + 9 files changed, 123 insertions(+), 15 deletions(-) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index f5bb1ae7f7d7..30f00215ca7e 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -151,6 +151,7 @@ io_bits=%u Set the bit size of write IO requests. It should be set with "mode=lfs". usrquota Enable plain user disk quota accounting. grpquota Enable plain group disk quota accounting. +prjquota Enable plain project quota accounting. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ce9912d5597e..28264abc1990 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -93,6 +93,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_LFS 0x00040000 #define F2FS_MOUNT_USRQUOTA 0x00080000 #define F2FS_MOUNT_GRPQUOTA 0x00100000 +#define F2FS_MOUNT_PRJQUOTA 0x00200000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -116,6 +117,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_BLKZONED 0x0002 #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 #define F2FS_FEATURE_EXTRA_ATTR 0x0008 +#define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -679,6 +681,7 @@ struct f2fs_inode_info { struct rw_semaphore i_mmap_sem; int i_extra_isize; /* size of extra space located in i_addr */ + kprojid_t i_projid; /* id for project quota */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1037,6 +1040,10 @@ enum { MAX_TIME, }; +#ifdef CONFIG_QUOTA +#define F2FS_MAXQUOTAS 2 +#endif + struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -1993,6 +2000,20 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) *addr ^= mask; } +#define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) +#define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) +#define F2FS_FL_INHERITED (FS_PROJINHERIT_FL) + +static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) +{ + if (S_ISDIR(mode)) + return flags; + else if (S_ISREG(mode)) + return flags & F2FS_REG_FLMASK; + else + return flags & F2FS_OTHER_FLMASK; +} + /* used for f2fs_inode_info->flags */ enum { FI_NEW_INODE, /* indicate newly allocated inode */ @@ -2021,6 +2042,7 @@ enum { FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ FI_HOT_DATA, /* indicate file is hot */ FI_EXTRA_ATTR, /* indicate file has extra attribute */ + FI_PROJ_INHERIT, /* indicate file inherits projectid */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, @@ -2364,6 +2386,12 @@ static inline int get_extra_isize(struct inode *inode) (offsetof(struct f2fs_inode, i_extra_end) - \ offsetof(struct f2fs_inode, i_extra_isize)) \ +#define F2FS_OLD_ATTRIBUTE_SIZE (offsetof(struct f2fs_inode, i_addr)) +#define F2FS_FITS_IN_INODE(f2fs_inode, extra_isize, field) \ + ((offsetof(typeof(*f2fs_inode), field) + \ + sizeof((f2fs_inode)->field)) \ + <= (F2FS_OLD_ATTRIBUTE_SIZE + extra_isize)) \ + /* * file.c */ @@ -2962,6 +2990,11 @@ static inline int f2fs_sb_has_extra_attr(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_EXTRA_ATTR); } +static inline int f2fs_sb_has_project_quota(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_PRJQUOTA); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3c3990cefee2..15d45610ee1c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1510,19 +1510,6 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id) return 0; } -#define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) -#define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) - -static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) -{ - if (S_ISDIR(mode)) - return flags; - else if (S_ISREG(mode)) - return flags & F2FS_REG_FLMASK; - else - return flags & F2FS_OTHER_FLMASK; -} - static int f2fs_ioc_getflags(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index e1699e542d17..c2c102eb15db 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -114,6 +114,7 @@ static int do_read_inode(struct inode *inode) struct f2fs_inode_info *fi = F2FS_I(inode); struct page *node_page; struct f2fs_inode *ri; + projid_t i_projid; /* Check if ino is within scope */ if (check_nid_range(sbi, inode->i_ino)) { @@ -173,6 +174,16 @@ static int do_read_inode(struct inode *inode) if (!need_inode_block_update(sbi, inode->i_ino)) fi->last_disk_size = inode->i_size; + if (fi->i_flags & FS_PROJINHERIT_FL) + set_inode_flag(inode, FI_PROJ_INHERIT); + + if (f2fs_has_extra_attr(inode) && f2fs_sb_has_project_quota(sbi->sb) && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_projid)) + i_projid = (projid_t)le32_to_cpu(ri->i_projid); + else + i_projid = F2FS_DEF_PROJID; + fi->i_projid = make_kprojid(&init_user_ns, i_projid); + f2fs_put_page(node_page, 1); stat_inc_inline_xattr(inode); @@ -299,9 +310,20 @@ int update_inode(struct inode *inode, struct page *node_page) ri->i_generation = cpu_to_le32(inode->i_generation); ri->i_dir_level = F2FS_I(inode)->i_dir_level; - if (f2fs_has_extra_attr(inode)) + if (f2fs_has_extra_attr(inode)) { ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) && + F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + i_projid)) { + projid_t i_projid; + + i_projid = from_kprojid(&init_user_ns, + F2FS_I(inode)->i_projid); + ri->i_projid = cpu_to_le32(i_projid); + } + } + __set_inode_rdev(inode, ri); set_cold_node(inode, node_page); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 70e9a1d1c02f..47359693cf60 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -59,6 +59,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) goto fail; } + if (f2fs_sb_has_project_quota(sbi->sb) && + (F2FS_I(dir)->i_flags & FS_PROJINHERIT_FL)) + F2FS_I(inode)->i_projid = F2FS_I(dir)->i_projid; + else + F2FS_I(inode)->i_projid = make_kprojid(&init_user_ns, + F2FS_DEF_PROJID); dquot_initialize(inode); err = dquot_alloc_inode(inode); @@ -89,6 +95,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) stat_inc_inline_inode(inode); stat_inc_inline_dir(inode); + F2FS_I(inode)->i_flags = + f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED); + + if (F2FS_I(inode)->i_flags & FS_PROJINHERIT_FL) + set_inode_flag(inode, FI_PROJ_INHERIT); + trace_f2fs_new_inode(inode, 0); return inode; @@ -206,6 +218,11 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, !fscrypt_has_permitted_context(dir, inode)) return -EPERM; + if (is_inode_flag_set(dir, FI_PROJ_INHERIT) && + (!projid_eq(F2FS_I(dir)->i_projid, + F2FS_I(old_dentry->d_inode)->i_projid))) + return -EXDEV; + dquot_initialize(dir); f2fs_balance_fs(sbi, true); @@ -727,6 +744,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } + if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && + (!projid_eq(F2FS_I(new_dir)->i_projid, + F2FS_I(old_dentry->d_inode)->i_projid))) + return -EXDEV; + dquot_initialize(old_dir); dquot_initialize(new_dir); @@ -911,6 +933,14 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, !fscrypt_has_permitted_context(old_dir, new_inode))) return -EPERM; + if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && + !projid_eq(F2FS_I(new_dir)->i_projid, + F2FS_I(old_dentry->d_inode)->i_projid)) || + (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && + !projid_eq(F2FS_I(old_dir)->i_projid, + F2FS_I(new_dentry->d_inode)->i_projid))) + return -EXDEV; + dquot_initialize(old_dir); dquot_initialize(new_dir); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index e903cb7451dd..4d30dd6d28a2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2274,8 +2274,13 @@ retry: dst->i_links = cpu_to_le32(1); dst->i_xattr_nid = 0; dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR); - if (dst->i_inline & F2FS_EXTRA_ATTR) + if (dst->i_inline & F2FS_EXTRA_ATTR) { dst->i_extra_isize = src->i_extra_isize; + if (f2fs_sb_has_project_quota(sbi->sb) && + F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), + i_projid)) + dst->i_projid = src->i_projid; + } new_ni = old_ni; new_ni.ino = ino; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index cba156aca1c5..407fa6d67bcf 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -107,6 +107,7 @@ enum { Opt_fault_injection, Opt_usrquota, Opt_grpquota, + Opt_prjquota, Opt_err, }; @@ -142,6 +143,7 @@ static match_table_t f2fs_tokens = { {Opt_fault_injection, "fault_injection=%u"}, {Opt_usrquota, "usrquota"}, {Opt_grpquota, "grpquota"}, + {Opt_prjquota, "prjquota"}, {Opt_err, NULL}, }; @@ -382,9 +384,18 @@ static int parse_options(struct super_block *sb, char *options) case Opt_grpquota: set_opt(sbi, GRPQUOTA); break; + case Opt_prjquota: + if (F2FS_MAXQUOTAS <= 2) { + f2fs_msg(sb, KERN_INFO, + "prjquota operations not supported"); + return -EINVAL; + } + set_opt(sbi, PRJQUOTA); + break; #else case Opt_usrquota: case Opt_grpquota: + case Opt_prjquota: f2fs_msg(sb, KERN_INFO, "quota operations not supported"); break; @@ -801,6 +812,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",usrquota"); if (test_opt(sbi, GRPQUOTA)) seq_puts(seq, ",grpquota"); + if (test_opt(sbi, PRJQUOTA)) + seq_puts(seq, ",prjquota"); #endif return 0; @@ -1153,6 +1166,12 @@ static void f2fs_quota_off_umount(struct super_block *sb) f2fs_quota_off(sb, type); } +int f2fs_get_projid(struct inode *inode, kprojid_t *projid) +{ + *projid = F2FS_I(inode)->i_projid; + return 0; +} + static const struct dquot_operations f2fs_quota_operations = { .get_reserved_space = f2fs_get_reserved_space, .write_dquot = dquot_commit, @@ -1162,6 +1181,10 @@ static const struct dquot_operations f2fs_quota_operations = { .write_info = dquot_commit_info, .alloc_dquot = dquot_alloc, .destroy_dquot = dquot_destroy, +#if 0 /* not support */ + .get_projid = f2fs_get_projid, + .get_next_id = dquot_get_next_id, +#endif }; static const struct quotactl_ops f2fs_quotactl_ops = { @@ -1948,6 +1971,9 @@ try_onemore: #ifdef CONFIG_QUOTA sb->dq_op = &f2fs_quota_operations; sb->s_qcop = &f2fs_quotactl_ops; +#if 0 /* not support */ + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; +#endif #endif sb->s_op = &f2fs_sops; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index b7331bfd440f..bc0e6fc41575 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -239,6 +239,7 @@ struct f2fs_inode { struct { __le16 i_extra_isize; /* extra inode attribute size */ __le16 i_padding; /* padding */ + __le32 i_projid; /* project id */ __le32 i_extra_end[0]; /* for attribute size calculation */ }; __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ @@ -522,4 +523,6 @@ enum { #define S_SHIFT 12 +#define F2FS_DEF_PROJID 0 /* default project ID */ + #endif /* _LINUX_F2FS_FS_H */ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 84600b25a4e4..ee21fa9efe8a 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -245,6 +245,7 @@ struct fscrypt_key { #define FS_EXTENT_FL 0x00080000 /* Extents */ #define FS_DIRECTIO_FL 0x00100000 /* Use direct i/o */ #define FS_NOCOW_FL 0x00800000 /* Do not cow file */ +#define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ #define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ -- GitLab From ef3855a3e1c857fa358dabcb017fe0a33e3e84e7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 26 Jul 2017 11:24:13 -0700 Subject: [PATCH 0895/5498] f2fs: avoid naming confusion of sysfs init commit dc6b20551044a05cd4d8ad2356a6bd888570f52a upstream. This patch changes the function names of sysfs init to follow ext4. f2fs_init_sysfs <-> f2fs_register_sysfs f2fs_exit_sysfs <-> f2fs_unregister_sysfs Suggested-by: Chao Yu Reivewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 ++++---- fs/f2fs/super.c | 12 ++++++------ fs/f2fs/sysfs.c | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 28264abc1990..3ebf0e5a9322 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2950,10 +2950,10 @@ void destroy_extent_cache(void); /* * sysfs.c */ -int __init f2fs_register_sysfs(void); -void f2fs_unregister_sysfs(void); -int f2fs_init_sysfs(struct f2fs_sb_info *sbi); -void f2fs_exit_sysfs(struct f2fs_sb_info *sbi); +int __init f2fs_init_sysfs(void); +void f2fs_exit_sysfs(void); +int f2fs_register_sysfs(struct f2fs_sb_info *sbi); +void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi); /* * crypto support diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 407fa6d67bcf..434c1c82dd7c 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -640,7 +640,7 @@ static void f2fs_put_super(struct super_block *sb) kfree(sbi->ckpt); - f2fs_exit_sysfs(sbi); + f2fs_unregister_sysfs(sbi); sb->s_fs_info = NULL; if (sbi->s_chksum_driver) @@ -2139,7 +2139,7 @@ try_onemore: goto free_root_inode; } - err = f2fs_init_sysfs(sbi); + err = f2fs_register_sysfs(sbi); if (err) goto free_root_inode; @@ -2210,7 +2210,7 @@ skip_recovery: free_sysfs: f2fs_sync_inode_meta(sbi); - f2fs_exit_sysfs(sbi); + f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); sb->s_root = NULL; @@ -2328,7 +2328,7 @@ static int __init init_f2fs_fs(void) err = create_extent_cache(); if (err) goto free_checkpoint_caches; - err = f2fs_register_sysfs(); + err = f2fs_init_sysfs(); if (err) goto free_extent_cache; err = register_shrinker(&f2fs_shrinker_info); @@ -2347,7 +2347,7 @@ free_filesystem: free_shrinker: unregister_shrinker(&f2fs_shrinker_info); free_sysfs: - f2fs_unregister_sysfs(); + f2fs_exit_sysfs(); free_extent_cache: destroy_extent_cache(); free_checkpoint_caches: @@ -2367,7 +2367,7 @@ static void __exit exit_f2fs_fs(void) f2fs_destroy_root_stats(); unregister_filesystem(&f2fs_fs_type); unregister_shrinker(&f2fs_shrinker_info); - f2fs_unregister_sysfs(); + f2fs_exit_sysfs(); destroy_extent_cache(); destroy_checkpoint_caches(); destroy_segment_manager_caches(); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index a96fd1945b19..ad5845dd0aff 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -327,7 +327,7 @@ static const struct file_operations f2fs_seq_##_name##_fops = { \ F2FS_PROC_FILE_DEF(segment_info); F2FS_PROC_FILE_DEF(segment_bits); -int __init f2fs_register_sysfs(void) +int __init f2fs_init_sysfs(void) { f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); @@ -337,13 +337,13 @@ int __init f2fs_register_sysfs(void) return 0; } -void f2fs_unregister_sysfs(void) +void f2fs_exit_sysfs(void) { kset_unregister(f2fs_kset); remove_proc_entry("fs/f2fs", NULL); } -int f2fs_init_sysfs(struct f2fs_sb_info *sbi) +int f2fs_register_sysfs(struct f2fs_sb_info *sbi) { struct super_block *sb = sbi->sb; int err; @@ -374,7 +374,7 @@ err_out: return err; } -void f2fs_exit_sysfs(struct f2fs_sb_info *sbi) +void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) { kobject_del(&sbi->s_kobj); kobject_put(&sbi->s_kobj); -- GitLab From 95b3f1b48b137e8a5a9fe9ddc29b2cc0005ff077 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 28 Jul 2017 02:29:12 -0700 Subject: [PATCH 0896/5498] f2fs: don't need to wait for node writes for atomic write commit b6a245eb34cd935f48235e1160d8b7539b228e8e upstream. We have a node chain to serialize node block writes, so if any IOs for node block writes are reordered, we'll get broken node chain. IOWs, roll-forward recovery will see all or none node blocks given fsync mark. E.g., Node chain consists of: N1 -> N2 -> N3 -> NFSYNC -> N1' -> N2' -> N'FSYNC Reordered to: 1) N1 -> N2 -> N3 -> N2' -> NFSYNC -> N'FSYNC -> power-cut 2) N1 -> N2 -> N3 -> N1' -> NFSYNC -> power-cut 3) N1 -> N2 -> NFSYNC -> N1' -> N'FSYNC -> N3 -> power-cut 4) N1 -> NFSYNC -> N1' -> N2' -> N'FSYNC -> N3 -> power-cut Roll-forward recovery can proceed to: 1) N1 -> N2 -> N3 -> NFSYNC -> X 2) N1 -> N2 -> N3 -> NFSYNC -> N1' -> X 3) N1 -> N2 -> N3 -> FSYNC -> N1' -> X 4) N1 -> X Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 15d45610ee1c..f6b9c5d6e446 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -277,9 +277,19 @@ sync_nodes: goto sync_nodes; } - ret = wait_on_node_pages_writeback(sbi, ino); - if (ret) - goto out; + /* + * If it's atomic_write, it's just fine to keep write ordering. So + * here we don't need to wait for node write completion, since we use + * node chain which serializes node blocks. If one of node writes are + * reordered, we can see simply broken chain, resulting in stopping + * roll-forward recovery. It means we'll recover all or none node blocks + * given fsync mark. + */ + if (!atomic) { + ret = wait_on_node_pages_writeback(sbi, ino); + if (ret) + goto out; + } /* once recovery info is written, don't need to tack this */ remove_ino_entry(sbi, ino, APPEND_INO); -- GitLab From 67a7cccea2de11ea9415e22bd39ff19e57645fbb Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 29 Jul 2017 00:32:53 +0800 Subject: [PATCH 0897/5498] f2fs: introduce f2fs_statfs_project commit ddc34e328d067b15b937dea7ad29034920130200 upstream. This patch introduces f2fs_statfs_project, it enables to show usage status of directory tree which is limited with project quota. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 434c1c82dd7c..a1316ff3a772 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -697,6 +697,48 @@ static int f2fs_unfreeze(struct super_block *sb) return 0; } +#ifdef CONFIG_QUOTA +static int f2fs_statfs_project(struct super_block *sb, + kprojid_t projid, struct kstatfs *buf) +{ + struct kqid qid; + struct dquot *dquot; + u64 limit; + u64 curblock; + + qid = make_kqid_projid(projid); + dquot = dqget(sb, qid); + if (IS_ERR(dquot)) + return PTR_ERR(dquot); + spin_lock(&dq_data_lock); + + limit = (dquot->dq_dqb.dqb_bsoftlimit ? + dquot->dq_dqb.dqb_bsoftlimit : + dquot->dq_dqb.dqb_bhardlimit) >> sb->s_blocksize_bits; + if (limit && buf->f_blocks > limit) { + curblock = dquot->dq_dqb.dqb_curspace >> sb->s_blocksize_bits; + buf->f_blocks = limit; + buf->f_bfree = buf->f_bavail = + (buf->f_blocks > curblock) ? + (buf->f_blocks - curblock) : 0; + } + + limit = dquot->dq_dqb.dqb_isoftlimit ? + dquot->dq_dqb.dqb_isoftlimit : + dquot->dq_dqb.dqb_ihardlimit; + if (limit && buf->f_files > limit) { + buf->f_files = limit; + buf->f_ffree = + (buf->f_files > dquot->dq_dqb.dqb_curinodes) ? + (buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0; + } + + spin_unlock(&dq_data_lock); + dqput(dquot); + return 0; +} +#endif + static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -732,6 +774,12 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); +#ifdef CONFIG_QUOTA + if (is_inode_flag_set(dentry->d_inode, FI_PROJ_INHERIT) && + sb_has_quota_limits_enabled(sb, PRJQUOTA)) { + f2fs_statfs_project(sb, F2FS_I(dentry->d_inode)->i_projid, buf); + } +#endif return 0; } -- GitLab From 33fae76de14cf6bc6bbff3758d100b080f933e5d Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Thu, 27 Jul 2017 20:11:00 +0800 Subject: [PATCH 0898/5498] f2fs: provide f2fs_balance_fs to __write_node_page commit 401db79f61e0810e7c9fa3c7425c0aef84e5c30a upstream. Let node writeback also do f2fs_balance_fs to ensure there are always enough free segments. Signed-off-by: Yunlong Song Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/f2fs.h | 3 ++- fs/f2fs/node.c | 16 ++++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 01d4ca3c03ff..bc30d319ce78 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1018,7 +1018,7 @@ retry_flush_nodes: if (get_pages(sbi, F2FS_DIRTY_NODES)) { up_write(&sbi->node_write); - err = sync_node_pages(sbi, &wbc); + err = sync_node_pages(sbi, &wbc, false); if (err) { up_write(&sbi->node_change); f2fs_unlock_all(sbi); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3ebf0e5a9322..3de3c3757006 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2516,7 +2516,8 @@ struct page *get_node_page_ra(struct page *parent, int start); void move_node_page(struct page *node_page, int gc_type); int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic); -int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc); +int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, + bool do_balance); void build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount); bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid); void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4d30dd6d28a2..9066ebb3febf 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1333,7 +1333,7 @@ continue_unlock: } static int __write_node_page(struct page *page, bool atomic, bool *submitted, - struct writeback_control *wbc) + struct writeback_control *wbc, bool do_balance) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); nid_t nid; @@ -1402,6 +1402,8 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, if (submitted) *submitted = fio.submitted; + if (do_balance) + f2fs_balance_fs(sbi, false); return 0; redirty_out: @@ -1412,7 +1414,7 @@ redirty_out: static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { - return __write_node_page(page, false, NULL, wbc); + return __write_node_page(page, false, NULL, wbc, false); } int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, @@ -1500,7 +1502,7 @@ continue_unlock: ret = __write_node_page(page, atomic && page == last_page, - &submitted, wbc); + &submitted, wbc, true); if (ret) { unlock_page(page); f2fs_put_page(last_page, 0); @@ -1537,7 +1539,8 @@ out: return ret ? -EIO: 0; } -int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc) +int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, + bool do_balance) { pgoff_t index, end; struct pagevec pvec; @@ -1615,7 +1618,8 @@ continue_unlock: set_fsync_mark(page, 0); set_dentry_mark(page, 0); - ret = __write_node_page(page, false, &submitted, wbc); + ret = __write_node_page(page, false, &submitted, + wbc, do_balance); if (ret) unlock_page(page); else if (submitted) @@ -1707,7 +1711,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; blk_start_plug(&plug); - sync_node_pages(sbi, wbc); + sync_node_pages(sbi, wbc, true); blk_finish_plug(&plug); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return 0; -- GitLab From c65b214c205086e02e8973576055b18714c1d49f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 30 Jul 2017 09:45:14 -0700 Subject: [PATCH 0899/5498] f2fs: return wrong error number on f2fs_quota_write commit 4f31d26b0c17f2aae6a6afeb823a87e20671ab4b upstream. This must return size, not error number. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a1316ff3a772..abc5544bffe0 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1113,7 +1113,7 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type, } if (len == towrite) - return err; + return 0; inode->i_version++; inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); -- GitLab From 0ab91ceb94b5b87a54116e578fdccef71f4d90b6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 31 Jul 2017 20:19:09 +0800 Subject: [PATCH 0900/5498] f2fs: support inode checksum commit 704956ecf5bcdc14d14650f39f2b545b34c96265 upstream. This patch adds to support inode checksum in f2fs. Signed-off-by: Chao Yu [Jaegeuk Kim: fix verification flow] Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 32 +++++++++++++++++++ fs/f2fs/inode.c | 70 +++++++++++++++++++++++++++++++++++++++++ fs/f2fs/node.c | 7 +++++ fs/f2fs/segment.c | 5 ++- fs/f2fs/super.c | 5 +++ include/linux/f2fs_fs.h | 1 + 6 files changed, 119 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3de3c3757006..29aaf57c6b10 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -118,6 +118,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 #define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_FEATURE_PRJQUOTA 0x0010 +#define F2FS_FEATURE_INODE_CHKSUM 0x0020 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -1195,6 +1196,9 @@ struct f2fs_sb_info { /* Reference to checksum algorithm driver via cryptoapi */ struct crypto_shash *s_chksum_driver; + /* Precomputed FS UUID checksum for seeding other checksums */ + __u32 s_chksum_seed; + /* For fault injection */ #ifdef CONFIG_F2FS_FAULT_INJECTION struct f2fs_fault_info fault_info; @@ -1283,6 +1287,27 @@ static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc, return f2fs_crc32(sbi, buf, buf_size) == blk_crc; } +static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, + const void *address, unsigned int length) +{ + struct { + struct shash_desc shash; + char ctx[4]; + } desc; + int err; + + BUG_ON(crypto_shash_descsize(sbi->s_chksum_driver) != sizeof(desc.ctx)); + + desc.shash.tfm = sbi->s_chksum_driver; + desc.shash.flags = 0; + *(u32 *)desc.ctx = crc; + + err = crypto_shash_update(&desc.shash, address, length); + BUG_ON(err); + + return *(u32 *)desc.ctx; +} + static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) { return container_of(inode, struct f2fs_inode_info, vfs_inode); @@ -2411,6 +2436,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); * inode.c */ void f2fs_set_inode_flags(struct inode *inode); +bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page); +void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page); struct inode *f2fs_iget(struct super_block *sb, unsigned long ino); struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino); int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink); @@ -2996,6 +3023,11 @@ static inline int f2fs_sb_has_project_quota(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_PRJQUOTA); } +static inline int f2fs_sb_has_inode_chksum(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c2c102eb15db..ebbe53aab202 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -108,6 +108,76 @@ static void __recover_inline_status(struct inode *inode, struct page *ipage) return; } +static bool f2fs_enable_inode_chksum(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_inode *ri = &F2FS_NODE(page)->i; + int extra_isize = le32_to_cpu(ri->i_extra_isize); + + if (!f2fs_sb_has_inode_chksum(sbi->sb)) + return false; + + if (!RAW_IS_INODE(F2FS_NODE(page)) || !(ri->i_inline & F2FS_EXTRA_ATTR)) + return false; + + if (!F2FS_FITS_IN_INODE(ri, extra_isize, i_inode_checksum)) + return false; + + return true; +} + +static __u32 f2fs_inode_chksum(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_node *node = F2FS_NODE(page); + struct f2fs_inode *ri = &node->i; + __le32 ino = node->footer.ino; + __le32 gen = ri->i_generation; + __u32 chksum, chksum_seed; + __u32 dummy_cs = 0; + unsigned int offset = offsetof(struct f2fs_inode, i_inode_checksum); + unsigned int cs_size = sizeof(dummy_cs); + + chksum = f2fs_chksum(sbi, sbi->s_chksum_seed, (__u8 *)&ino, + sizeof(ino)); + chksum_seed = f2fs_chksum(sbi, chksum, (__u8 *)&gen, sizeof(gen)); + + chksum = f2fs_chksum(sbi, chksum_seed, (__u8 *)ri, offset); + chksum = f2fs_chksum(sbi, chksum, (__u8 *)&dummy_cs, cs_size); + offset += cs_size; + chksum = f2fs_chksum(sbi, chksum, (__u8 *)ri + offset, + F2FS_BLKSIZE - offset); + return chksum; +} + +bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_inode *ri; + __u32 provided, calculated; + + if (!f2fs_enable_inode_chksum(sbi, page)) + return true; + + ri = &F2FS_NODE(page)->i; + provided = le32_to_cpu(ri->i_inode_checksum); + calculated = f2fs_inode_chksum(sbi, page); + + if (provided != calculated) + f2fs_msg(sbi->sb, KERN_WARNING, + "checksum invalid, ino = %x, %x vs. %x", + ino_of_node(page), provided, calculated); + + return provided == calculated; +} + +void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_inode *ri = &F2FS_NODE(page)->i; + + if (!f2fs_enable_inode_chksum(sbi, page)) + return; + + ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page)); +} + static int do_read_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9066ebb3febf..84576c014a66 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1171,6 +1171,11 @@ repeat: err = -EIO; goto out_err; } + + if (!f2fs_inode_chksum_verify(sbi, page)) { + err = -EBADMSG; + goto out_err; + } page_hit: if(unlikely(nid != nid_of_node(page))) { f2fs_msg(sbi->sb, KERN_WARNING, "inconsistent node block, " @@ -2284,6 +2289,8 @@ retry: F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), i_projid)) dst->i_projid = src->i_projid; + + f2fs_inode_chksum_set(sbi, ipage); } new_ni = old_ni; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1b24027654c9..b289bbb809c9 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2357,9 +2357,12 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, mutex_unlock(&sit_i->sentry_lock); - if (page && IS_NODESEG(type)) + if (page && IS_NODESEG(type)) { fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); + f2fs_inode_chksum_set(sbi, page); + } + if (add_list) { struct f2fs_bio_info *io; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index abc5544bffe0..f2e7a6df333d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1985,6 +1985,11 @@ try_onemore: sb->s_fs_info = sbi; sbi->raw_super = raw_super; + /* precompute checksum seed for metadata */ + if (f2fs_sb_has_inode_chksum(sb)) + sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid, + sizeof(raw_super->uuid)); + /* * The BLKZONED feature indicates that the drive was formatted with * zone alignment optimization. This is optional for host-aware diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index bc0e6fc41575..00cdb19429d5 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -240,6 +240,7 @@ struct f2fs_inode { __le16 i_extra_isize; /* extra inode attribute size */ __le16 i_padding; /* padding */ __le32 i_projid; /* project id */ + __le32 i_inode_checksum;/* inode meta checksum */ __le32 i_extra_end[0]; /* for attribute size calculation */ }; __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ -- GitLab From a6c2844763f52117705d83beaab17fd49afeaee0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 21 Jul 2017 17:14:09 -0700 Subject: [PATCH 0901/5498] f2fs: expose features to sysfs entry commit bf9e697ecd4214c86ff35764449348db45c697b1 upstream. This patch exposes what features are supported by current f2fs build to sysfs entry via: /sys/fs/f2fs/features/ /sys/fs/f2fs/dev/features Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 156 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 130 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index ad5845dd0aff..6431a967a32a 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -18,7 +18,6 @@ #include "gc.h" static struct proc_dir_entry *f2fs_proc_root; -static struct kset *f2fs_kset; /* Sysfs support for f2fs */ enum { @@ -41,6 +40,7 @@ struct f2fs_attr { const char *, size_t); int struct_type; int offset; + int id; }; static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) @@ -76,6 +76,34 @@ static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, BD_PART_WRITTEN(sbi))); } +static ssize_t features_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct super_block *sb = sbi->sb; + int len = 0; + + if (!sb->s_bdev->bd_part) + return snprintf(buf, PAGE_SIZE, "0\n"); + + if (f2fs_sb_has_crypto(sb)) + len += snprintf(buf, PAGE_SIZE - len, "%s", + "encryption"); + if (f2fs_sb_mounted_blkzoned(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "blkzoned"); + if (f2fs_sb_has_extra_attr(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "extra_attr"); + if (f2fs_sb_has_project_quota(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "projquota"); + if (f2fs_sb_has_inode_chksum(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "inode_checksum"); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + return len; +} + static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { @@ -176,6 +204,30 @@ static void f2fs_sb_release(struct kobject *kobj) complete(&sbi->s_kobj_unregister); } +enum feat_id { + FEAT_CRYPTO = 0, + FEAT_BLKZONED, + FEAT_ATOMIC_WRITE, + FEAT_EXTRA_ATTR, + FEAT_PROJECT_QUOTA, + FEAT_INODE_CHECKSUM, +}; + +static ssize_t f2fs_feature_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + switch (a->id) { + case FEAT_CRYPTO: + case FEAT_BLKZONED: + case FEAT_ATOMIC_WRITE: + case FEAT_EXTRA_ATTR: + case FEAT_PROJECT_QUOTA: + case FEAT_INODE_CHECKSUM: + return snprintf(buf, PAGE_SIZE, "supported\n"); + } + return 0; +} + #define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ static struct f2fs_attr f2fs_attr_##_name = { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ @@ -193,6 +245,13 @@ static struct f2fs_attr f2fs_attr_##_name = { \ #define F2FS_GENERAL_RO_ATTR(name) \ static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) +#define F2FS_FEATURE_RO_ATTR(_name, _id) \ +static struct f2fs_attr f2fs_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = 0444 }, \ + .show = f2fs_feature_show, \ + .id = _id, \ +} + F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); @@ -218,6 +277,18 @@ F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); #endif F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); +F2FS_GENERAL_RO_ATTR(features); + +#ifdef CONFIG_F2FS_FS_ENCRYPTION +F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); +#endif +#ifdef CONFIG_BLK_DEV_ZONED +F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED); +#endif +F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE); +F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); +F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); +F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -245,21 +316,53 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(inject_type), #endif ATTR_LIST(lifetime_write_kbytes), + ATTR_LIST(features), ATTR_LIST(reserved_blocks), NULL, }; +static struct attribute *f2fs_feat_attrs[] = { +#ifdef CONFIG_F2FS_FS_ENCRYPTION + ATTR_LIST(encryption), +#endif +#ifdef CONFIG_BLK_DEV_ZONED + ATTR_LIST(block_zoned), +#endif + ATTR_LIST(atomic_write), + ATTR_LIST(extra_attr), + ATTR_LIST(project_quota), + ATTR_LIST(inode_checksum), + NULL, +}; + static const struct sysfs_ops f2fs_attr_ops = { .show = f2fs_attr_show, .store = f2fs_attr_store, }; -static struct kobj_type f2fs_ktype = { +static struct kobj_type f2fs_sb_ktype = { .default_attrs = f2fs_attrs, .sysfs_ops = &f2fs_attr_ops, .release = f2fs_sb_release, }; +static struct kobj_type f2fs_ktype = { + .sysfs_ops = &f2fs_attr_ops, +}; + +static struct kset f2fs_kset = { + .kobj = {.ktype = &f2fs_ktype}, +}; + +static struct kobj_type f2fs_feat_ktype = { + .default_attrs = f2fs_feat_attrs, + .sysfs_ops = &f2fs_attr_ops, +}; + +static struct kobject f2fs_feat = { + .kset = &f2fs_kset, +}; + static int segment_info_seq_show(struct seq_file *seq, void *offset) { struct super_block *sb = seq->private; @@ -329,18 +432,29 @@ F2FS_PROC_FILE_DEF(segment_bits); int __init f2fs_init_sysfs(void) { - f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + int ret; - f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); - if (!f2fs_kset) - return -ENOMEM; - return 0; + kobject_set_name(&f2fs_kset.kobj, "f2fs"); + f2fs_kset.kobj.parent = fs_kobj; + ret = kset_register(&f2fs_kset); + if (ret) + return ret; + + ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype, + NULL, "features"); + if (ret) + kset_unregister(&f2fs_kset); + else + f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + return ret; } void f2fs_exit_sysfs(void) { - kset_unregister(f2fs_kset); + kobject_put(&f2fs_feat); + kset_unregister(&f2fs_kset); remove_proc_entry("fs/f2fs", NULL); + f2fs_proc_root = NULL; } int f2fs_register_sysfs(struct f2fs_sb_info *sbi) @@ -348,6 +462,13 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) struct super_block *sb = sbi->sb; int err; + sbi->s_kobj.kset = &f2fs_kset; + init_completion(&sbi->s_kobj_unregister); + err = kobject_init_and_add(&sbi->s_kobj, &f2fs_sb_ktype, NULL, + "%s", sb->s_id); + if (err) + return err; + if (f2fs_proc_root) sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); @@ -357,32 +478,15 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, &f2fs_seq_segment_bits_fops, sb); } - - sbi->s_kobj.kset = f2fs_kset; - init_completion(&sbi->s_kobj_unregister); - err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, - "%s", sb->s_id); - if (err) - goto err_out; return 0; -err_out: - if (sbi->s_proc) { - remove_proc_entry("segment_info", sbi->s_proc); - remove_proc_entry("segment_bits", sbi->s_proc); - remove_proc_entry(sb->s_id, f2fs_proc_root); - } - return err; } void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) { - kobject_del(&sbi->s_kobj); - kobject_put(&sbi->s_kobj); - wait_for_completion(&sbi->s_kobj_unregister); - if (sbi->s_proc) { remove_proc_entry("segment_info", sbi->s_proc); remove_proc_entry("segment_bits", sbi->s_proc); remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); } + kobject_del(&sbi->s_kobj); } -- GitLab From 282c24c98e89d758f3586c0bfc97edd7812c9be6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 2 Aug 2017 20:58:29 -0700 Subject: [PATCH 0902/5498] f2fs: use printk_ratelimited for f2fs_msg commit a36c106dffb616250117efb1cab271c19a8f94ff upstream. This patch reduces contention of printks. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f2e7a6df333d..f0aa7f670f6d 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -155,7 +155,7 @@ void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - printk("%sF2FS-fs (%s): %pV\n", level, sb->s_id, &vaf); + printk_ratelimited("%sF2FS-fs (%s): %pV\n", level, sb->s_id, &vaf); va_end(args); } -- GitLab From a0a6bcdebf0db411439c18ae0e766a7abb95c877 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Wed, 2 Aug 2017 21:20:13 +0800 Subject: [PATCH 0903/5498] f2fs: update cur_valid_map_mir together with cur_valid_map commit 6415fedc572219e0c8e6b4d3c17c46ed36997ae7 upstream. When cur_valid_map passes the f2fs_test_and_set(,clear)_bit test, cur_valid_map_mir update is skipped unlikely, so fix it. The fix now changes the mirror check together with cur_valid_map all the time. Signed-off-by: Yunlong Song Signed-off-by: Chao Yu [Jaegeuk Kim: Fix unused variable and add unlikely for corner condition.] Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index b289bbb809c9..87e4e23ab399 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1644,6 +1644,10 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) struct seg_entry *se; unsigned int segno, offset; long int new_vblocks; + bool exist; +#ifdef CONFIG_F2FS_CHECK_FS + bool mir_exist; +#endif segno = GET_SEGNO(sbi, blkaddr); @@ -1660,17 +1664,23 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) /* Update valid block bitmap */ if (del > 0) { - if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) { + exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); #ifdef CONFIG_F2FS_CHECK_FS - if (f2fs_test_and_set_bit(offset, - se->cur_valid_map_mir)) - f2fs_bug_on(sbi, 1); - else - WARN_ON(1); -#else + mir_exist = f2fs_test_and_set_bit(offset, + se->cur_valid_map_mir); + if (unlikely(exist != mir_exist)) { + f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error " + "when setting bitmap, blk:%u, old bit:%d", + blkaddr, exist); f2fs_bug_on(sbi, 1); + } #endif + if (unlikely(exist)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Bitmap was wrongly set, blk:%u", blkaddr); + f2fs_bug_on(sbi, 1); } + if (f2fs_discard_en(sbi) && !f2fs_test_and_set_bit(offset, se->discard_map)) sbi->discard_blks--; @@ -1681,17 +1691,23 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) se->ckpt_valid_blocks++; } } else { - if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) { + exist = f2fs_test_and_clear_bit(offset, se->cur_valid_map); #ifdef CONFIG_F2FS_CHECK_FS - if (!f2fs_test_and_clear_bit(offset, - se->cur_valid_map_mir)) - f2fs_bug_on(sbi, 1); - else - WARN_ON(1); -#else + mir_exist = f2fs_test_and_clear_bit(offset, + se->cur_valid_map_mir); + if (unlikely(exist != mir_exist)) { + f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error " + "when clearing bitmap, blk:%u, old bit:%d", + blkaddr, exist); f2fs_bug_on(sbi, 1); + } #endif + if (unlikely(!exist)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Bitmap was wrongly cleared, blk:%u", blkaddr); + f2fs_bug_on(sbi, 1); } + if (f2fs_discard_en(sbi) && f2fs_test_and_clear_bit(offset, se->discard_map)) sbi->discard_blks++; -- GitLab From 2853021472d4262f02d9ca7089c8a905fd239bae Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Wed, 2 Aug 2017 22:16:54 +0800 Subject: [PATCH 0904/5498] f2fs: do not change the valid_block value if cur_valid_map was wrongly set or cleared commit 35ee82ca133a58011e648a407d4ab13275c975d4 upstream. Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 87e4e23ab399..912cd31d941f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1679,6 +1679,8 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) f2fs_msg(sbi->sb, KERN_ERR, "Bitmap was wrongly set, blk:%u", blkaddr); f2fs_bug_on(sbi, 1); + se->valid_blocks--; + del = 0; } if (f2fs_discard_en(sbi) && @@ -1706,6 +1708,8 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) f2fs_msg(sbi->sb, KERN_ERR, "Bitmap was wrongly cleared, blk:%u", blkaddr); f2fs_bug_on(sbi, 1); + se->valid_blocks++; + del = 0; } if (f2fs_discard_en(sbi) && -- GitLab From 0056fb1376227e748e7b20207c0db3e03be7f946 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 2 Aug 2017 23:21:48 +0800 Subject: [PATCH 0905/5498] f2fs: add app/fs io stat commit b0af6d491a6b5f5622fa91ac75f34f3640f862c4 upstream. This patch enables inner app/fs io stats and introduces below virtual fs nodes for exposing stats info: /sys/fs/f2fs//iostat_enable /proc/fs/f2fs//iostat_info Signed-off-by: Chao Yu [Jaegeuk Kim: fix wrong stat assignment] Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 34 +++++++++++++++++-------- fs/f2fs/data.c | 35 +++++++++++++++++++------- fs/f2fs/f2fs.h | 59 +++++++++++++++++++++++++++++++++++++++++--- fs/f2fs/file.c | 7 +++++- fs/f2fs/gc.c | 3 +++ fs/f2fs/inline.c | 1 + fs/f2fs/node.c | 15 ++++++----- fs/f2fs/segment.c | 21 ++++++++++++++-- fs/f2fs/super.c | 4 +++ fs/f2fs/sysfs.c | 52 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 200 insertions(+), 31 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index bc30d319ce78..a310156ecf19 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -231,8 +231,9 @@ void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index) ra_meta_pages(sbi, index, BIO_MAX_PAGES, META_POR, true); } -static int f2fs_write_meta_page(struct page *page, - struct writeback_control *wbc) +static int __f2fs_write_meta_page(struct page *page, + struct writeback_control *wbc, + enum iostat_type io_type) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); @@ -245,7 +246,7 @@ static int f2fs_write_meta_page(struct page *page, if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - write_meta_page(sbi, page); + write_meta_page(sbi, page, io_type); dec_page_count(sbi, F2FS_DIRTY_META); if (wbc->for_reclaim) @@ -264,6 +265,12 @@ redirty_out: return AOP_WRITEPAGE_ACTIVATE; } +static int f2fs_write_meta_page(struct page *page, + struct writeback_control *wbc) +{ + return __f2fs_write_meta_page(page, wbc, FS_META_IO); +} + static int f2fs_write_meta_pages(struct address_space *mapping, struct writeback_control *wbc) { @@ -284,7 +291,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping, trace_f2fs_writepages(mapping->host, wbc, META); diff = nr_pages_to_write(sbi, META, wbc); - written = sync_meta_pages(sbi, META, wbc->nr_to_write); + written = sync_meta_pages(sbi, META, wbc->nr_to_write, FS_META_IO); mutex_unlock(&sbi->cp_mutex); wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff); return 0; @@ -296,7 +303,7 @@ skip_write: } long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, - long nr_to_write) + long nr_to_write, enum iostat_type io_type) { struct address_space *mapping = META_MAPPING(sbi); pgoff_t index = 0, end = ULONG_MAX, prev = ULONG_MAX; @@ -347,7 +354,7 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - if (mapping->a_ops->writepage(page, &wbc)) { + if (__f2fs_write_meta_page(page, &wbc, io_type)) { unlock_page(page); break; } @@ -905,7 +912,14 @@ retry: if (inode) { unsigned long cur_ino = inode->i_ino; + if (is_dir) + F2FS_I(inode)->cp_task = current; + filemap_fdatawrite(inode->i_mapping); + + if (is_dir) + F2FS_I(inode)->cp_task = NULL; + iput(inode); /* We need to give cpu to another writers. */ if (ino == cur_ino) { @@ -1018,7 +1032,7 @@ retry_flush_nodes: if (get_pages(sbi, F2FS_DIRTY_NODES)) { up_write(&sbi->node_write); - err = sync_node_pages(sbi, &wbc, false); + err = sync_node_pages(sbi, &wbc, false, FS_CP_NODE_IO); if (err) { up_write(&sbi->node_change); f2fs_unlock_all(sbi); @@ -1116,7 +1130,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { - sync_meta_pages(sbi, META, LONG_MAX); + sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO); if (unlikely(f2fs_cp_error(sbi))) return -EIO; } @@ -1195,7 +1209,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* Flush all the NAT BITS pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { - sync_meta_pages(sbi, META, LONG_MAX); + sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO); if (unlikely(f2fs_cp_error(sbi))) return -EIO; } @@ -1250,7 +1264,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) percpu_counter_set(&sbi->alloc_valid_block_count, 0); /* Here, we only have one bio having CP pack */ - sync_meta_pages(sbi, META_FLUSH, LONG_MAX); + sync_meta_pages(sbi, META_FLUSH, LONG_MAX, FS_CP_META_IO); /* wait for previous submitted meta pages writeback */ wait_on_all_pages_writeback(sbi); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5ea4bb6d895a..0d0726836c30 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1472,7 +1472,8 @@ out: } static int __write_data_page(struct page *page, bool *submitted, - struct writeback_control *wbc) + struct writeback_control *wbc, + enum iostat_type io_type) { struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -1493,6 +1494,7 @@ static int __write_data_page(struct page *page, bool *submitted, .encrypted_page = NULL, .submitted = false, .need_lock = LOCK_RETRY, + .io_type = io_type, }; trace_f2fs_writepage(page, DATA); @@ -1599,7 +1601,7 @@ redirty_out: static int f2fs_write_data_page(struct page *page, struct writeback_control *wbc) { - return __write_data_page(page, NULL, wbc); + return __write_data_page(page, NULL, wbc, FS_DATA_IO); } /* @@ -1608,7 +1610,8 @@ static int f2fs_write_data_page(struct page *page, * warm/hot data page. */ static int f2fs_write_cache_pages(struct address_space *mapping, - struct writeback_control *wbc) + struct writeback_control *wbc, + enum iostat_type io_type) { int ret = 0; int done = 0; @@ -1698,7 +1701,7 @@ continue_unlock: if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = __write_data_page(page, &submitted, wbc); + ret = __write_data_page(page, &submitted, wbc, io_type); if (unlikely(ret)) { /* * keep nr_to_write, since vfs uses this to @@ -1753,8 +1756,9 @@ continue_unlock: return ret; } -static int f2fs_write_data_pages(struct address_space *mapping, - struct writeback_control *wbc) +int __f2fs_write_data_pages(struct address_space *mapping, + struct writeback_control *wbc, + enum iostat_type io_type) { struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); @@ -1791,7 +1795,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, goto skip_write; blk_start_plug(&plug); - ret = f2fs_write_cache_pages(mapping, wbc); + ret = f2fs_write_cache_pages(mapping, wbc, io_type); blk_finish_plug(&plug); if (wbc->sync_mode == WB_SYNC_ALL) @@ -1810,6 +1814,16 @@ skip_write: return 0; } +static int f2fs_write_data_pages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + + return __f2fs_write_data_pages(mapping, wbc, + F2FS_I(inode)->cp_task == current ? + FS_CP_DATA_IO : FS_DATA_IO); +} + static void f2fs_write_failed(struct address_space *mapping, loff_t to) { struct inode *inode = mapping->host; @@ -2105,10 +2119,13 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, up_read(&F2FS_I(inode)->dio_rwsem[rw]); if (rw & WRITE) { - if (err > 0) + if (err > 0) { + f2fs_update_iostat(F2FS_I_SB(inode), APP_DIRECT_IO, + err); set_inode_flag(inode, FI_UPDATE_WRITE); - else if (err < 0) + } else if (err < 0) { f2fs_write_failed(mapping, offset + count); + } } trace_f2fs_direct_IO_exit(inode, offset, count, rw, err); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 29aaf57c6b10..8212fa276da2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -665,6 +665,7 @@ struct f2fs_inode_info { f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ struct task_struct *task; /* lookup and create consistency */ + struct task_struct *cp_task; /* separate cp/wb IO stats*/ nid_t i_xattr_nid; /* node id that contains xattrs */ loff_t last_disk_size; /* lastly written file size */ @@ -969,6 +970,23 @@ enum need_lock_type { LOCK_RETRY, }; +enum iostat_type { + APP_DIRECT_IO, /* app direct IOs */ + APP_BUFFERED_IO, /* app buffered IOs */ + APP_WRITE_IO, /* app write IOs */ + APP_MAPPED_IO, /* app mapped IOs */ + FS_DATA_IO, /* data IOs from kworker/fsync/reclaimer */ + FS_NODE_IO, /* node IOs from kworker/fsync/reclaimer */ + FS_META_IO, /* meta IOs from kworker/reclaimer */ + FS_GC_DATA_IO, /* data IOs from forground gc */ + FS_GC_NODE_IO, /* node IOs from forground gc */ + FS_CP_DATA_IO, /* data IOs from checkpoint */ + FS_CP_NODE_IO, /* node IOs from checkpoint */ + FS_CP_META_IO, /* meta IOs from checkpoint */ + FS_DISCARD, /* discard */ + NR_IO_TYPE, +}; + struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ @@ -983,6 +1001,7 @@ struct f2fs_io_info { bool submitted; /* indicate IO submission */ int need_lock; /* indicate we need to lock cp_rwsem */ bool in_list; /* indicate fio is in io_list */ + enum iostat_type io_type; /* io type */ }; #define is_read_io(rw) ((rw) == READ) @@ -1178,6 +1197,11 @@ struct f2fs_sb_info { #endif spinlock_t stat_lock; /* lock for stat operations */ + /* For app/fs IO statistics */ + spinlock_t iostat_lock; + unsigned long long write_iostat[NR_IO_TYPE]; + bool iostat_enable; + /* For sysfs suppport */ struct kobject s_kobj; struct completion s_kobj_unregister; @@ -2417,6 +2441,31 @@ static inline int get_extra_isize(struct inode *inode) sizeof((f2fs_inode)->field)) \ <= (F2FS_OLD_ATTRIBUTE_SIZE + extra_isize)) \ +static inline void f2fs_reset_iostat(struct f2fs_sb_info *sbi) +{ + int i; + + spin_lock(&sbi->iostat_lock); + for (i = 0; i < NR_IO_TYPE; i++) + sbi->write_iostat[i] = 0; + spin_unlock(&sbi->iostat_lock); +} + +static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi, + enum iostat_type type, unsigned long long io_bytes) +{ + if (!sbi->iostat_enable) + return; + spin_lock(&sbi->iostat_lock); + sbi->write_iostat[type] += io_bytes; + + if (type == APP_WRITE_IO || type == APP_DIRECT_IO) + sbi->write_iostat[APP_BUFFERED_IO] = + sbi->write_iostat[APP_WRITE_IO] - + sbi->write_iostat[APP_DIRECT_IO]; + spin_unlock(&sbi->iostat_lock); +} + /* * file.c */ @@ -2544,7 +2593,7 @@ void move_node_page(struct page *node_page, int gc_type); int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic); int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, - bool do_balance); + bool do_balance, enum iostat_type io_type); void build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount); bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid); void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); @@ -2587,7 +2636,8 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range); bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc); struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno); void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr); -void write_meta_page(struct f2fs_sb_info *sbi, struct page *page); +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page, + enum iostat_type io_type); void write_node_page(unsigned int nid, struct f2fs_io_info *fio); void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio); int rewrite_data_page(struct f2fs_io_info *fio); @@ -2628,7 +2678,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type, bool sync); void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index); long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, - long nr_to_write); + long nr_to_write, enum iostat_type io_type); void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void release_ino_entry(struct f2fs_sb_info *sbi, bool all); @@ -2682,6 +2732,9 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); void f2fs_set_page_dirty_nobuffers(struct page *page); +int __f2fs_write_data_pages(struct address_space *mapping, + struct writeback_control *wbc, + enum iostat_type io_type); void f2fs_invalidate_page(struct page *page, unsigned int offset, unsigned int length); int f2fs_release_page(struct page *page, gfp_t wait); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f6b9c5d6e446..4fa4c84aae0e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -101,6 +101,8 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, if (!PageUptodate(page)) SetPageUptodate(page); + f2fs_update_iostat(sbi, APP_MAPPED_IO, F2FS_BLKSIZE); + trace_f2fs_vm_page_mkwrite(page, DATA); mapped: /* fill the page */ @@ -1799,7 +1801,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) f2fs_stop_checkpoint(sbi, false); break; case F2FS_GOING_DOWN_METAFLUSH: - sync_meta_pages(sbi, META, LONG_MAX); + sync_meta_pages(sbi, META, LONG_MAX, FS_META_IO); f2fs_stop_checkpoint(sbi, false); break; default: @@ -2477,6 +2479,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) blk_start_plug(&plug); ret = __generic_file_write_iter(iocb, from); blk_finish_plug(&plug); + + if (ret > 0) + f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret); } inode_unlock(inode); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index f57cadae1a30..620dca443b29 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -689,6 +689,8 @@ static void move_encrypted_block(struct inode *inode, block_t bidx, fio.new_blkaddr = newaddr; f2fs_submit_page_write(&fio); + f2fs_update_iostat(fio.sbi, FS_GC_DATA_IO, F2FS_BLKSIZE); + f2fs_update_data_blkaddr(&dn, newaddr); set_inode_flag(inode, FI_APPEND_WRITE); if (page->index == 0) @@ -736,6 +738,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, .page = page, .encrypted_page = NULL, .need_lock = LOCK_REQ, + .io_type = FS_GC_DATA_IO, }; bool is_dirty = PageDirty(page); int err; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 848deebef900..c70153a80a52 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -135,6 +135,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) .op_flags = REQ_SYNC | REQ_PRIO, .page = page, .encrypted_page = NULL, + .io_type = FS_DATA_IO, }; int dirty, err; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 84576c014a66..0b3110504326 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1338,7 +1338,8 @@ continue_unlock: } static int __write_node_page(struct page *page, bool atomic, bool *submitted, - struct writeback_control *wbc, bool do_balance) + struct writeback_control *wbc, bool do_balance, + enum iostat_type io_type) { struct f2fs_sb_info *sbi = F2FS_P_SB(page); nid_t nid; @@ -1351,6 +1352,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, .page = page, .encrypted_page = NULL, .submitted = false, + .io_type = io_type, }; trace_f2fs_writepage(page, NODE); @@ -1419,7 +1421,7 @@ redirty_out: static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { - return __write_node_page(page, false, NULL, wbc, false); + return __write_node_page(page, false, NULL, wbc, false, FS_NODE_IO); } int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, @@ -1507,7 +1509,8 @@ continue_unlock: ret = __write_node_page(page, atomic && page == last_page, - &submitted, wbc, true); + &submitted, wbc, true, + FS_NODE_IO); if (ret) { unlock_page(page); f2fs_put_page(last_page, 0); @@ -1545,7 +1548,7 @@ out: } int sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, - bool do_balance) + bool do_balance, enum iostat_type io_type) { pgoff_t index, end; struct pagevec pvec; @@ -1624,7 +1627,7 @@ continue_unlock: set_dentry_mark(page, 0); ret = __write_node_page(page, false, &submitted, - wbc, do_balance); + wbc, do_balance, io_type); if (ret) unlock_page(page); else if (submitted) @@ -1716,7 +1719,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; blk_start_plug(&plug); - sync_node_pages(sbi, wbc, true); + sync_node_pages(sbi, wbc, true, FS_NODE_IO); blk_finish_plug(&plug); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return 0; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 912cd31d941f..35107603e570 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -292,6 +292,7 @@ static int __commit_inmem_pages(struct inode *inode, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC | REQ_PRIO, + .io_type = FS_DATA_IO, }; pgoff_t last_idx = ULONG_MAX; int err = 0; @@ -903,6 +904,8 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, submit_bio(REQ_SYNC, bio); list_move_tail(&dc->list, &dcc->wait_list); __check_sit_bitmap(sbi, dc->start, dc->start + dc->len); + + f2fs_update_iostat(sbi, FS_DISCARD, 1); } } else { __remove_discard_cmd(sbi, dc); @@ -2414,7 +2417,8 @@ reallocate: } } -void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page, + enum iostat_type io_type) { struct f2fs_io_info fio = { .sbi = sbi, @@ -2433,6 +2437,8 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) set_page_writeback(page); f2fs_submit_page_write(&fio); + + f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE); } void write_node_page(unsigned int nid, struct f2fs_io_info *fio) @@ -2441,6 +2447,8 @@ void write_node_page(unsigned int nid, struct f2fs_io_info *fio) set_summary(&sum, nid, 0, 0); do_write_page(&sum, fio); + + f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); } void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) @@ -2454,13 +2462,22 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); do_write_page(&sum, fio); f2fs_update_data_blkaddr(dn, fio->new_blkaddr); + + f2fs_update_iostat(sbi, fio->io_type, F2FS_BLKSIZE); } int rewrite_data_page(struct f2fs_io_info *fio) { + int err; + fio->new_blkaddr = fio->old_blkaddr; stat_inc_inplace_blocks(fio->sbi); - return f2fs_submit_page_bio(fio); + + err = f2fs_submit_page_bio(fio); + + f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); + + return err; } void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f0aa7f670f6d..642075d60dde 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2050,6 +2050,10 @@ try_onemore: set_sbi_flag(sbi, SBI_POR_DOING); spin_lock_init(&sbi->stat_lock); + /* init iostat info */ + spin_lock_init(&sbi->iostat_lock); + sbi->iostat_enable = false; + for (i = 0; i < NR_PAGE_TYPE; i++) { int n = (i == META) ? 1: NR_TEMP_TYPE; int j; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 6431a967a32a..ce1314669b11 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -174,6 +174,10 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, } *ui = t; + + if (!strcmp(a->attr.name, "iostat_enable") && *ui == 0) + f2fs_reset_iostat(sbi); + return count; } @@ -272,6 +276,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); #ifdef CONFIG_F2FS_FAULT_INJECTION F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); @@ -311,6 +316,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(dirty_nats_ratio), ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), + ATTR_LIST(iostat_enable), #ifdef CONFIG_F2FS_FAULT_INJECTION ATTR_LIST(inject_rate), ATTR_LIST(inject_type), @@ -414,6 +420,48 @@ static int segment_bits_seq_show(struct seq_file *seq, void *offset) return 0; } +static int iostat_info_seq_show(struct seq_file *seq, void *offset) +{ + struct super_block *sb = seq->private; + struct f2fs_sb_info *sbi = F2FS_SB(sb); + time64_t now = get_seconds(); + + if (!sbi->iostat_enable) + return 0; + + seq_printf(seq, "time: %-16llu\n", now); + + /* print app IOs */ + seq_printf(seq, "app buffered: %-16llu\n", + sbi->write_iostat[APP_BUFFERED_IO]); + seq_printf(seq, "app direct: %-16llu\n", + sbi->write_iostat[APP_DIRECT_IO]); + seq_printf(seq, "app mapped: %-16llu\n", + sbi->write_iostat[APP_MAPPED_IO]); + + /* print fs IOs */ + seq_printf(seq, "fs data: %-16llu\n", + sbi->write_iostat[FS_DATA_IO]); + seq_printf(seq, "fs node: %-16llu\n", + sbi->write_iostat[FS_NODE_IO]); + seq_printf(seq, "fs meta: %-16llu\n", + sbi->write_iostat[FS_META_IO]); + seq_printf(seq, "fs gc data: %-16llu\n", + sbi->write_iostat[FS_GC_DATA_IO]); + seq_printf(seq, "fs gc node: %-16llu\n", + sbi->write_iostat[FS_GC_NODE_IO]); + seq_printf(seq, "fs cp data: %-16llu\n", + sbi->write_iostat[FS_CP_DATA_IO]); + seq_printf(seq, "fs cp node: %-16llu\n", + sbi->write_iostat[FS_CP_NODE_IO]); + seq_printf(seq, "fs cp meta: %-16llu\n", + sbi->write_iostat[FS_CP_META_IO]); + seq_printf(seq, "fs discard: %-16llu\n", + sbi->write_iostat[FS_DISCARD]); + + return 0; +} + #define F2FS_PROC_FILE_DEF(_name) \ static int _name##_open_fs(struct inode *inode, struct file *file) \ { \ @@ -429,6 +477,7 @@ static const struct file_operations f2fs_seq_##_name##_fops = { \ F2FS_PROC_FILE_DEF(segment_info); F2FS_PROC_FILE_DEF(segment_bits); +F2FS_PROC_FILE_DEF(iostat_info); int __init f2fs_init_sysfs(void) { @@ -477,6 +526,8 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) &f2fs_seq_segment_info_fops, sb); proc_create_data("segment_bits", S_IRUGO, sbi->s_proc, &f2fs_seq_segment_bits_fops, sb); + proc_create_data("iostat_info", S_IRUGO, sbi->s_proc, + &f2fs_seq_iostat_info_fops, sb); } return 0; } @@ -484,6 +535,7 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) { if (sbi->s_proc) { + remove_proc_entry("iostat_info", sbi->s_proc); remove_proc_entry("segment_info", sbi->s_proc); remove_proc_entry("segment_bits", sbi->s_proc); remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); -- GitLab From 2f9f8a0baf38a7ed6e8d2d935a064e2b4b96ec44 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Fri, 4 Aug 2017 17:07:15 +0800 Subject: [PATCH 0906/5498] f2fs: fix the size value in __check_sit_bitmap commit 008396e1b026b3873091b555b808155da7d9d18f upstream. The current size value is not correct and will miss bitmap check. Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 35107603e570..37df4b8c1006 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -868,11 +868,14 @@ void __check_sit_bitmap(struct f2fs_sb_info *sbi, sentry = get_seg_entry(sbi, segno); offset = GET_BLKOFF_FROM_SEG0(sbi, blk); - size = min((unsigned long)(end - blk), max_blocks); + if (end < START_BLOCK(sbi, segno + 1)) + size = GET_BLKOFF_FROM_SEG0(sbi, end); + else + size = max_blocks; map = (unsigned long *)(sentry->cur_valid_map); offset = __find_rev_next_bit(map, size, offset); f2fs_bug_on(sbi, offset != size); - blk += size; + blk = START_BLOCK(sbi, segno + 1); } #endif } -- GitLab From 9860ff4bc6abdd4c756694696a42b18fa7d5f3a3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 6 Aug 2017 22:09:00 -0700 Subject: [PATCH 0907/5498] f2fs: introduce gc_urgent mode for background GC commit d9872a698c393e0d1abca86bf05b62712cbfc581 upstream. This patch adds a sysfs entry to control urgent mode for background GC. If this is set, background GC thread conducts GC with gc_urgent_sleep_time all the time. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 12 ++++++++++++ Documentation/filesystems/f2fs.txt | 9 +++++++++ fs/f2fs/gc.c | 17 +++++++++++++++-- fs/f2fs/gc.h | 4 ++++ fs/f2fs/sysfs.c | 9 +++++++++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 1c3dc27c9725..cecb93af8967 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -139,3 +139,15 @@ Date: June 2017 Contact: "Chao Yu" Description: Controls current reserved blocks in system. + +What: /sys/fs/f2fs//gc_urgent +Date: August 2017 +Contact: "Jaegeuk Kim" +Description: + Do background GC agressively + +What: /sys/fs/f2fs//gc_urgent_sleep_time +Date: August 2017 +Contact: "Jaegeuk Kim" +Description: + Controls sleep time of GC urgent mode diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 30f00215ca7e..a220111210e6 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -197,6 +197,15 @@ Files in /sys/fs/f2fs/ gc_idle = 1 will select the Cost Benefit approach & setting gc_idle = 2 will select the greedy aproach. + gc_urgent This parameter controls triggering background GCs + urgently or not. Setting gc_urgent = 0 [default] + makes back to default behavior, while if it is set + to 1, background thread starts to do GC by given + gc_urgent_sleep_time interval. + + gc_urgent_sleep_time This parameter controls sleep time for gc_urgent. + 500 ms is set by default. See above gc_urgent. + reclaim_segments This parameter controls the number of prefree segments to be reclaimed. If the number of prefree segments is larger than the number of segments diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 620dca443b29..8da7c14a9d29 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -35,9 +35,14 @@ static int gc_thread_func(void *data) set_freezable(); do { wait_event_interruptible_timeout(*wq, - kthread_should_stop() || freezing(current), + kthread_should_stop() || freezing(current) || + gc_th->gc_wake, msecs_to_jiffies(wait_ms)); + /* give it a try one time */ + if (gc_th->gc_wake) + gc_th->gc_wake = 0; + if (try_to_freeze()) continue; if (kthread_should_stop()) @@ -74,6 +79,11 @@ static int gc_thread_func(void *data) if (!mutex_trylock(&sbi->gc_mutex)) goto next; + if (gc_th->gc_urgent) { + wait_ms = gc_th->urgent_sleep_time; + goto do_gc; + } + if (!is_idle(sbi)) { increase_sleep_time(gc_th, &wait_ms); mutex_unlock(&sbi->gc_mutex); @@ -84,7 +94,7 @@ static int gc_thread_func(void *data) decrease_sleep_time(gc_th, &wait_ms); else increase_sleep_time(gc_th, &wait_ms); - +do_gc: stat_inc_bggc_count(sbi); /* if return value is not zero, no victim was selected */ @@ -115,11 +125,14 @@ int start_gc_thread(struct f2fs_sb_info *sbi) goto out; } + gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME; gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME; gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME; gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME; gc_th->gc_idle = 0; + gc_th->gc_urgent = 0; + gc_th->gc_wake= 0; sbi->gc_thread = gc_th; init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index a993967dcdb9..57a9000ce3af 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -13,6 +13,7 @@ * whether IO subsystem is idle * or not */ +#define DEF_GC_THREAD_URGENT_SLEEP_TIME 500 /* 500 ms */ #define DEF_GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */ #define DEF_GC_THREAD_MAX_SLEEP_TIME 60000 #define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */ @@ -27,12 +28,15 @@ struct f2fs_gc_kthread { wait_queue_head_t gc_wait_queue_head; /* for gc sleep time */ + unsigned int urgent_sleep_time; unsigned int min_sleep_time; unsigned int max_sleep_time; unsigned int no_gc_sleep_time; /* for changing gc mode */ unsigned int gc_idle; + unsigned int gc_urgent; + unsigned int gc_wake; }; struct gc_inode_list { diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index ce1314669b11..fbbe6e03658a 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -177,6 +177,10 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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); + } return count; } @@ -256,10 +260,13 @@ static struct f2fs_attr f2fs_attr_##_name = { \ .id = _id, \ } +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent_sleep_time, + urgent_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent, gc_urgent); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards); F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, discard_granularity, discard_granularity); @@ -297,10 +304,12 @@ F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { + ATTR_LIST(gc_urgent_sleep_time), ATTR_LIST(gc_min_sleep_time), ATTR_LIST(gc_max_sleep_time), ATTR_LIST(gc_no_gc_sleep_time), ATTR_LIST(gc_idle), + ATTR_LIST(gc_urgent), ATTR_LIST(reclaim_segments), ATTR_LIST(max_small_discards), ATTR_LIST(discard_granularity), -- GitLab From 6199dd017e6116450f542b784707d7322c0e9282 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 7 Aug 2017 16:37:59 +0800 Subject: [PATCH 0908/5498] f2fs: avoid unneeded sync on quota file commit 9a20d391cd099d547c0f17626bf6f13e06da519a upstream. We only need to sync quota file with appointed quota type instead of all types in f2fs_quota_{on,off}. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 642075d60dde..7a808dd95773 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1162,7 +1162,7 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id, struct inode *inode; int err; - err = f2fs_quota_sync(sb, -1); + err = f2fs_quota_sync(sb, type); if (err) return err; @@ -1190,7 +1190,7 @@ static int f2fs_quota_off(struct super_block *sb, int type) if (!inode || !igrab(inode)) return dquot_quota_off(sb, type); - f2fs_quota_sync(sb, -1); + f2fs_quota_sync(sb, type); err = dquot_quota_off(sb, type); if (err) -- GitLab From 453594453de8518c1894834670fff0bbc51fe9f4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 7 Aug 2017 23:12:46 +0800 Subject: [PATCH 0909/5498] f2fs: fix potential overflow when adjusting GC cycle commit b8c502b81e3f899c6488967dec61eed0f5907db3 upstream. While comparing signed and unsigned variables, compiler will converts the signed value to unsigned one, due to this reason, {in,de}crease_sleep_time may return overflowed result. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- fs/f2fs/gc.h | 23 +++++++++++++++-------- include/trace/events/f2fs.h | 6 +++--- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8da7c14a9d29..e60480f71bb5 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -28,7 +28,7 @@ static int gc_thread_func(void *data) struct f2fs_sb_info *sbi = data; struct f2fs_gc_kthread *gc_th = sbi->gc_thread; wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; - long wait_ms; + unsigned int wait_ms; wait_ms = gc_th->min_sleep_time; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 57a9000ce3af..9325191fab2d 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -69,25 +69,32 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi) } static inline void increase_sleep_time(struct f2fs_gc_kthread *gc_th, - long *wait) + unsigned int *wait) { + unsigned int min_time = gc_th->min_sleep_time; + unsigned int max_time = gc_th->max_sleep_time; + if (*wait == gc_th->no_gc_sleep_time) return; - *wait += gc_th->min_sleep_time; - if (*wait > gc_th->max_sleep_time) - *wait = gc_th->max_sleep_time; + if ((long long)*wait + (long long)min_time > (long long)max_time) + *wait = max_time; + else + *wait += min_time; } static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th, - long *wait) + unsigned int *wait) { + unsigned int min_time = gc_th->min_sleep_time; + if (*wait == gc_th->no_gc_sleep_time) *wait = gc_th->max_sleep_time; - *wait -= gc_th->min_sleep_time; - if (*wait <= gc_th->min_sleep_time) - *wait = gc_th->min_sleep_time; + if ((long long)*wait - (long long)min_time < (long long)min_time) + *wait = min_time; + else + *wait -= min_time; } static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi) diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 78849b1b04f3..fdd7de503ce1 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -498,14 +498,14 @@ TRACE_EVENT(f2fs_map_blocks, TRACE_EVENT(f2fs_background_gc, - TP_PROTO(struct super_block *sb, long wait_ms, + TP_PROTO(struct super_block *sb, unsigned int wait_ms, unsigned int prefree, unsigned int free), TP_ARGS(sb, wait_ms, prefree, free), TP_STRUCT__entry( __field(dev_t, dev) - __field(long, wait_ms) + __field(unsigned int, wait_ms) __field(unsigned int, prefree) __field(unsigned int, free) ), @@ -517,7 +517,7 @@ TRACE_EVENT(f2fs_background_gc, __entry->free = free; ), - TP_printk("dev = (%d,%d), wait_ms = %ld, prefree = %u, free = %u", + TP_printk("dev = (%d,%d), wait_ms = %u, prefree = %u, free = %u", show_dev(__entry->dev), __entry->wait_ms, __entry->prefree, -- GitLab From de76d5aca9095747856f4286b9ace5abfd2baa2e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 8 Aug 2017 10:54:31 +0800 Subject: [PATCH 0910/5498] f2fs: support journalled quota commit 4b2414d04e99120ce852ba15a1926c9c3a77d9ce upstream. This patch supports to enable f2fs to accept quota information through mount option: - {usr,grp,prj}jquota= - jqfmt= Then, in ->mount flow, we can recover quota file during log replaying, by this, journelled quota can be supported. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 9 + fs/f2fs/checkpoint.c | 26 ++- fs/f2fs/f2fs.h | 9 + fs/f2fs/recovery.c | 60 ++++- fs/f2fs/super.c | 341 +++++++++++++++++++++++++++-- include/linux/seq_file.h | 17 ++ 6 files changed, 430 insertions(+), 32 deletions(-) diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index a220111210e6..6f9cb3690b57 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -152,6 +152,15 @@ io_bits=%u Set the bit size of write IO requests. It should be set usrquota Enable plain user disk quota accounting. grpquota Enable plain group disk quota accounting. prjquota Enable plain project quota accounting. +usrjquota= Appoint specified file and type during mount, so that quota +grpjquota= information can be properly updated during recovery flow, +prjjquota= : must be in root directory; +jqfmt= : [vfsold,vfsv0,vfsv1]. +offusrjquota Turn off user journelled quota. +offgrpjquota Turn off group journelled quota. +offprjjquota Turn off project journelled quota. +quota Enable plain user disk quota accounting. +noquota Disable all plain disk quota option. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a310156ecf19..9e8e24f679bb 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -589,11 +589,24 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) int recover_orphan_inodes(struct f2fs_sb_info *sbi) { block_t start_blk, orphan_blocks, i, j; - int err; + unsigned int s_flags = sbi->sb->s_flags; + int err = 0; if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) return 0; + if (s_flags & MS_RDONLY) { + f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); + sbi->sb->s_flags &= ~MS_RDONLY; + } + +#ifdef CONFIG_QUOTA + /* Needed for iput() to work correctly and not trash data */ + sbi->sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ + f2fs_enable_quota_files(sbi); +#endif + start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi); @@ -609,14 +622,21 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) err = recover_orphan_inode(sbi, ino); if (err) { f2fs_put_page(page, 1); - return err; + goto out; } } f2fs_put_page(page, 1); } /* clear Orphan Flag */ clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG); - return 0; +out: +#ifdef CONFIG_QUOTA + /* Turn quotas off */ + f2fs_quota_off_umount(sbi->sb); +#endif + sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ + + return err; } static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8212fa276da2..d348bb21a311 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -94,6 +94,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_USRQUOTA 0x00080000 #define F2FS_MOUNT_GRPQUOTA 0x00100000 #define F2FS_MOUNT_PRJQUOTA 0x00200000 +#define F2FS_MOUNT_QUOTA 0x00400000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -1227,6 +1228,12 @@ struct f2fs_sb_info { #ifdef CONFIG_F2FS_FAULT_INJECTION struct f2fs_fault_info fault_info; #endif + +#ifdef CONFIG_QUOTA + /* Names of quota files with journalled quota */ + char *s_qf_names[F2FS_MAXQUOTAS]; + int s_jquota_fmt; /* Format of quota to use */ +#endif }; #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -2555,6 +2562,8 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) */ int f2fs_inode_dirtied(struct inode *inode, bool sync); void f2fs_inode_synced(struct inode *inode); +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi); +void f2fs_quota_off_umount(struct super_block *sb); int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); int f2fs_sync_fs(struct super_block *sb, int sync); extern __printf(3, 4) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index ebde404ae67f..03b7aa61a0c8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -69,20 +69,32 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, } static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi, - struct list_head *head, nid_t ino) + struct list_head *head, nid_t ino, bool quota_inode) { struct inode *inode; struct fsync_inode_entry *entry; + int err; inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) return ERR_CAST(inode); + dquot_initialize(inode); + + if (quota_inode) { + err = dquot_alloc_inode(inode); + if (err) + goto err_out; + } + entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); entry->inode = inode; list_add_tail(&entry->list, head); return entry; +err_out: + iput(inode); + return ERR_PTR(err); } static void del_fsync_inode(struct fsync_inode_entry *entry) @@ -107,7 +119,8 @@ static int recover_dentry(struct inode *inode, struct page *ipage, entry = get_fsync_inode(dir_list, pino); if (!entry) { - entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino); + entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, + pino, false); if (IS_ERR(entry)) { dir = ERR_CAST(entry); err = PTR_ERR(entry); @@ -140,6 +153,9 @@ retry: err = -EEXIST; goto out_unmap_put; } + + dquot_initialize(einode); + err = acquire_orphan_inode(F2FS_I_SB(inode)); if (err) { iput(einode); @@ -226,18 +242,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, entry = get_fsync_inode(head, ino_of_node(page)); if (!entry) { + bool quota_inode = false; + if (!check_only && IS_INODE(page) && is_dent_dnode(page)) { err = recover_inode_page(sbi, page); if (err) break; + quota_inode = true; } /* * CP | dnode(F) | inode(DF) * For this case, we should not give up now. */ - entry = add_fsync_inode(sbi, head, ino_of_node(page)); + entry = add_fsync_inode(sbi, head, ino_of_node(page), + quota_inode); if (IS_ERR(entry)) { err = PTR_ERR(entry); if (err == -ENOENT) { @@ -332,6 +352,8 @@ got_it: inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) return PTR_ERR(inode); + + dquot_initialize(inode); } else { inode = dn->inode; } @@ -558,12 +580,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) struct list_head dir_list; int err; int ret = 0; + unsigned long s_flags = sbi->sb->s_flags; bool need_writecp = false; + if (s_flags & MS_RDONLY) { + f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); + sbi->sb->s_flags &= ~MS_RDONLY; + } + +#ifdef CONFIG_QUOTA + /* Needed for iput() to work correctly and not trash data */ + sbi->sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ + f2fs_enable_quota_files(sbi); +#endif + fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", sizeof(struct fsync_inode_entry)); - if (!fsync_entry_slab) - return -ENOMEM; + if (!fsync_entry_slab) { + err = -ENOMEM; + goto out; + } INIT_LIST_HEAD(&inode_list); INIT_LIST_HEAD(&dir_list); @@ -574,11 +611,11 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) /* step #1: find fsynced inode numbers */ err = find_fsync_dnodes(sbi, &inode_list, check_only); if (err || list_empty(&inode_list)) - goto out; + goto skip; if (check_only) { ret = 1; - goto out; + goto skip; } need_writecp = true; @@ -587,7 +624,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) err = recover_data(sbi, &inode_list, &dir_list); if (!err) f2fs_bug_on(sbi, !list_empty(&inode_list)); -out: +skip: destroy_fsync_dnodes(&inode_list); /* truncate meta pages to be used by the recovery */ @@ -615,5 +652,12 @@ out: } kmem_cache_destroy(fsync_entry_slab); +out: +#ifdef CONFIG_QUOTA + /* Turn quotas off */ + f2fs_quota_off_umount(sbi->sb); +#endif + sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ + return ret ? ret: err; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 7a808dd95773..d68bfd9ce0ce 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -25,6 +25,10 @@ #include #include #include +#include +#ifdef CONFIG_QUOTA +#include +#endif #include "f2fs.h" #include "node.h" @@ -105,9 +109,20 @@ enum { Opt_mode, Opt_io_size_bits, Opt_fault_injection, + Opt_quota, + Opt_noquota, Opt_usrquota, Opt_grpquota, Opt_prjquota, + Opt_usrjquota, + Opt_grpjquota, + Opt_prjjquota, + Opt_offusrjquota, + Opt_offgrpjquota, + Opt_offprjjquota, + Opt_jqfmt_vfsold, + Opt_jqfmt_vfsv0, + Opt_jqfmt_vfsv1, Opt_err, }; @@ -141,9 +156,20 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, + {Opt_quota, "quota"}, + {Opt_noquota, "noquota"}, {Opt_usrquota, "usrquota"}, {Opt_grpquota, "grpquota"}, {Opt_prjquota, "prjquota"}, + {Opt_usrjquota, "usrjquota=%s"}, + {Opt_grpjquota, "grpjquota=%s"}, + {Opt_prjjquota, "prjjquota=%s"}, + {Opt_offusrjquota, "usrjquota="}, + {Opt_offgrpjquota, "grpjquota="}, + {Opt_offprjjquota, "prjjquota="}, + {Opt_jqfmt_vfsold, "jqfmt=vfsold"}, + {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"}, + {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"}, {Opt_err, NULL}, }; @@ -166,6 +192,112 @@ static void init_once(void *foo) inode_init_once(&fi->vfs_inode); } +#ifdef CONFIG_QUOTA +static const char * const quotatypes[] = INITQFNAMES; +#define QTYPE2NAME(t) (quotatypes[t]) +static int f2fs_set_qf_name(struct super_block *sb, int qtype, + substring_t *args) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + char *qname; + int ret = -EINVAL; + + if (F2FS_MAXQUOTAS <= 2 && qtype == PRJQUOTA) + return -EINVAL; + + if (sb_any_quota_loaded(sb) && !sbi->s_qf_names[qtype]) { + f2fs_msg(sb, KERN_ERR, + "Cannot change journaled " + "quota options when quota turned on"); + return -EINVAL; + } + qname = match_strdup(args); + if (!qname) { + f2fs_msg(sb, KERN_ERR, + "Not enough memory for storing quotafile name"); + return -EINVAL; + } + if (sbi->s_qf_names[qtype]) { + if (strcmp(sbi->s_qf_names[qtype], qname) == 0) + ret = 0; + else + f2fs_msg(sb, KERN_ERR, + "%s quota file already specified", + QTYPE2NAME(qtype)); + goto errout; + } + if (strchr(qname, '/')) { + f2fs_msg(sb, KERN_ERR, + "quotafile must be on filesystem root"); + goto errout; + } + sbi->s_qf_names[qtype] = qname; + set_opt(sbi, QUOTA); + return 0; +errout: + kfree(qname); + return ret; +} + +static int f2fs_clear_qf_name(struct super_block *sb, int qtype) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + + if (F2FS_MAXQUOTAS <= 2 && qtype == PRJQUOTA) + return -EINVAL; + + if (sb_any_quota_loaded(sb) && sbi->s_qf_names[qtype]) { + f2fs_msg(sb, KERN_ERR, "Cannot change journaled quota options" + " when quota turned on"); + return -EINVAL; + } + kfree(sbi->s_qf_names[qtype]); + sbi->s_qf_names[qtype] = NULL; + return 0; +} + +static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) +{ + /* + * We do the test below only for project quotas. 'usrquota' and + * 'grpquota' mount options are allowed even without quota feature + * to support legacy quotas in quota files. + */ + if (F2FS_MAXQUOTAS > 2 && test_opt(sbi, PRJQUOTA) && + !f2fs_sb_has_project_quota(sbi->sb)) { + f2fs_msg(sbi->sb, KERN_ERR, "Project quota feature not enabled. " + "Cannot enable project quota enforcement."); + return -1; + } + if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] || + (F2FS_MAXQUOTAS > 2 && sbi->s_qf_names[PRJQUOTA])) { + if (test_opt(sbi, USRQUOTA) && sbi->s_qf_names[USRQUOTA]) + clear_opt(sbi, USRQUOTA); + + if (test_opt(sbi, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA]) + clear_opt(sbi, GRPQUOTA); + + if (F2FS_MAXQUOTAS > 2 && test_opt(sbi, PRJQUOTA) && + sbi->s_qf_names[PRJQUOTA]) + clear_opt(sbi, PRJQUOTA); + + if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) || + test_opt(sbi, PRJQUOTA)) { + f2fs_msg(sbi->sb, KERN_ERR, "old and new quota " + "format mixing"); + return -1; + } + + if (!sbi->s_jquota_fmt) { + f2fs_msg(sbi->sb, KERN_ERR, "journaled quota format " + "not specified"); + return -1; + } + } + return 0; +} +#endif + static int parse_options(struct super_block *sb, char *options) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -173,6 +305,9 @@ static int parse_options(struct super_block *sb, char *options) substring_t args[MAX_OPT_ARGS]; char *p, *name; int arg = 0; +#ifdef CONFIG_QUOTA + int ret; +#endif if (!options) return 0; @@ -378,6 +513,7 @@ static int parse_options(struct super_block *sb, char *options) #endif break; #ifdef CONFIG_QUOTA + case Opt_quota: case Opt_usrquota: set_opt(sbi, USRQUOTA); break; @@ -392,10 +528,66 @@ static int parse_options(struct super_block *sb, char *options) } set_opt(sbi, PRJQUOTA); break; + case Opt_usrjquota: + ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]); + if (ret) + return ret; + break; + case Opt_grpjquota: + ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]); + if (ret) + return ret; + break; + case Opt_prjjquota: + ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]); + if (ret) + return ret; + break; + case Opt_offusrjquota: + ret = f2fs_clear_qf_name(sb, USRQUOTA); + if (ret) + return ret; + break; + case Opt_offgrpjquota: + ret = f2fs_clear_qf_name(sb, GRPQUOTA); + if (ret) + return ret; + break; + case Opt_offprjjquota: + ret = f2fs_clear_qf_name(sb, PRJQUOTA); + if (ret) + return ret; + break; + case Opt_jqfmt_vfsold: + sbi->s_jquota_fmt = QFMT_VFS_OLD; + break; + case Opt_jqfmt_vfsv0: + sbi->s_jquota_fmt = QFMT_VFS_V0; + break; + case Opt_jqfmt_vfsv1: + sbi->s_jquota_fmt = QFMT_VFS_V1; + break; + case Opt_noquota: + clear_opt(sbi, QUOTA); + clear_opt(sbi, USRQUOTA); + clear_opt(sbi, GRPQUOTA); + clear_opt(sbi, PRJQUOTA); + break; #else + case Opt_quota: case Opt_usrquota: case Opt_grpquota: case Opt_prjquota: + case Opt_usrjquota: + case Opt_grpjquota: + case Opt_prjjquota: + case Opt_offusrjquota: + case Opt_offgrpjquota: + case Opt_offprjjquota: + case Opt_jqfmt_vfsold: + case Opt_jqfmt_vfsv0: + case Opt_jqfmt_vfsv1: + case Opt_noquota: f2fs_msg(sb, KERN_INFO, "quota operations not supported"); break; @@ -407,6 +599,10 @@ static int parse_options(struct super_block *sb, char *options) return -EINVAL; } } +#ifdef CONFIG_QUOTA + if (f2fs_check_quota_options(sbi)) + return -EINVAL; +#endif if (F2FS_IO_SIZE_BITS(sbi) && !test_opt(sbi, LFS)) { f2fs_msg(sb, KERN_ERR, @@ -582,7 +778,6 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) kfree(sbi->devs); } -static void f2fs_quota_off_umount(struct super_block *sb); static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -650,6 +845,10 @@ static void f2fs_put_super(struct super_block *sb) destroy_device_list(sbi); if (sbi->write_io_dummy) mempool_destroy(sbi->write_io_dummy); +#ifdef CONFIG_QUOTA + for (i = 0; i < F2FS_MAXQUOTAS; i++) + kfree(sbi->s_qf_names[i]); +#endif destroy_percpu_info(sbi); for (i = 0; i < NR_PAGE_TYPE; i++) kfree(sbi->write_io[i]); @@ -663,6 +862,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync) trace_f2fs_sync_fs(sb, sync); + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + return -EAGAIN; + if (sync) { struct cp_control cpc; @@ -783,6 +985,40 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } +static inline void f2fs_show_quota_options(struct seq_file *seq, + struct super_block *sb) +{ +#ifdef CONFIG_QUOTA + struct f2fs_sb_info *sbi = F2FS_SB(sb); + + if (sbi->s_jquota_fmt) { + char *fmtname = ""; + + switch (sbi->s_jquota_fmt) { + case QFMT_VFS_OLD: + fmtname = "vfsold"; + break; + case QFMT_VFS_V0: + fmtname = "vfsv0"; + break; + case QFMT_VFS_V1: + fmtname = "vfsv1"; + break; + } + seq_printf(seq, ",jqfmt=%s", fmtname); + } + + if (sbi->s_qf_names[USRQUOTA]) + seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]); + + if (sbi->s_qf_names[GRPQUOTA]) + seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]); + + if (F2FS_MAXQUOTAS > 2 && sbi->s_qf_names[PRJQUOTA]) + seq_show_option(seq, "prjjquota", sbi->s_qf_names[PRJQUOTA]); +#endif +} + static int f2fs_show_options(struct seq_file *seq, struct dentry *root) { struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); @@ -856,6 +1092,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) sbi->fault_info.inject_rate); #endif #ifdef CONFIG_QUOTA + if (test_opt(sbi, QUOTA)) + seq_puts(seq, ",quota"); if (test_opt(sbi, USRQUOTA)) seq_puts(seq, ",usrquota"); if (test_opt(sbi, GRPQUOTA)) @@ -863,6 +1101,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) if (test_opt(sbi, PRJQUOTA)) seq_puts(seq, ",prjquota"); #endif + f2fs_show_quota_options(seq, sbi->sb); return 0; } @@ -910,6 +1149,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) #ifdef CONFIG_F2FS_FAULT_INJECTION struct f2fs_fault_info ffi = sbi->fault_info; #endif +#ifdef CONFIG_QUOTA + int s_jquota_fmt; + char *s_qf_names[F2FS_MAXQUOTAS]; + int i, j; +#endif /* * Save the old mount options in case we @@ -919,6 +1163,23 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) old_sb_flags = sb->s_flags; active_logs = sbi->active_logs; +#ifdef CONFIG_QUOTA + s_jquota_fmt = sbi->s_jquota_fmt; + for (i = 0; i < F2FS_MAXQUOTAS; i++) { + if (sbi->s_qf_names[i]) { + s_qf_names[i] = kstrdup(sbi->s_qf_names[i], + GFP_KERNEL); + if (!s_qf_names[i]) { + for (j = 0; j < i; j++) + kfree(s_qf_names[j]); + return -ENOMEM; + } + } else { + s_qf_names[i] = NULL; + } + } +#endif + /* recover superblocks we couldn't write due to previous RO mount */ if (!(*flags & MS_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) { err = f2fs_commit_super(sbi, false); @@ -1000,6 +1261,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) goto restore_gc; } skip: +#ifdef CONFIG_QUOTA + /* Release old quota file names */ + for (i = 0; i < F2FS_MAXQUOTAS; i++) + kfree(s_qf_names[i]); +#endif /* Update the POSIXACL Flag */ sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); @@ -1014,6 +1280,13 @@ restore_gc: stop_gc_thread(sbi); } restore_opts: +#ifdef CONFIG_QUOTA + sbi->s_jquota_fmt = s_jquota_fmt; + for (i = 0; i < F2FS_MAXQUOTAS; i++) { + kfree(sbi->s_qf_names[i]); + sbi->s_qf_names[i] = s_qf_names[i]; + } +#endif sbi->mount_opt = org_mount_opt; sbi->active_logs = active_logs; sb->s_flags = old_sb_flags; @@ -1125,6 +1398,27 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode) return &F2FS_I(inode)->i_reserved_quota; } +static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type) +{ + return dquot_quota_on_mount(sbi->sb, sbi->s_qf_names[type], + sbi->s_jquota_fmt, type); +} + +void f2fs_enable_quota_files(struct f2fs_sb_info *sbi) +{ + int i, ret; + + for (i = 0; i < F2FS_MAXQUOTAS; i++) { + if (sbi->s_qf_names[i]) { + ret = f2fs_quota_on_mount(sbi, i); + if (ret < 0) + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on journaled " + "quota: error %d", ret); + } + } +} + static int f2fs_quota_sync(struct super_block *sb, int type) { struct quota_info *dqopt = sb_dqopt(sb); @@ -1139,7 +1433,7 @@ static int f2fs_quota_sync(struct super_block *sb, int type) * Now when everything is written we can discard the pagecache so * that userspace sees the changes. */ - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + for (cnt = 0; cnt < F2FS_MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; if (!sb_has_quota_active(sb, cnt)) @@ -1206,11 +1500,11 @@ out_put: return err; } -static void f2fs_quota_off_umount(struct super_block *sb) +void f2fs_quota_off_umount(struct super_block *sb) { int type; - for (type = 0; type < MAXQUOTAS; type++) + for (type = 0; type < F2FS_MAXQUOTAS; type++) f2fs_quota_off(sb, type); } @@ -1245,7 +1539,7 @@ static const struct quotactl_ops f2fs_quotactl_ops = { .set_dqblk = dquot_set_dqblk, }; #else -static inline void f2fs_quota_off_umount(struct super_block *sb) +void f2fs_quota_off_umount(struct super_block *sb) { } #endif @@ -2172,11 +2466,6 @@ try_onemore: if (err) goto free_nm; - /* if there are nt orphan nodes free them */ - err = recover_orphan_inodes(sbi); - if (err) - goto free_node_inode; - /* read root inode and dentry */ root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); if (IS_ERR(root)) { @@ -2200,6 +2489,11 @@ try_onemore: if (err) goto free_root_inode; + /* if there are nt orphan nodes free them */ + err = recover_orphan_inodes(sbi); + if (err) + goto free_sysfs; + /* recover fsynced data */ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { /* @@ -2209,7 +2503,7 @@ try_onemore: if (bdev_read_only(sb->s_bdev) && !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { err = -EROFS; - goto free_sysfs; + goto free_meta; } if (need_fsck) @@ -2223,7 +2517,7 @@ try_onemore: need_fsck = true; f2fs_msg(sb, KERN_ERR, "Cannot recover all fsync data errno=%d", err); - goto free_sysfs; + goto free_meta; } } else { err = recover_fsync_data(sbi, true); @@ -2247,7 +2541,7 @@ skip_recovery: /* After POR, we can run background GC thread.*/ err = start_gc_thread(sbi); if (err) - goto free_sysfs; + goto free_meta; } kfree(options); @@ -2265,8 +2559,16 @@ skip_recovery: f2fs_update_time(sbi, REQ_TIME); return 0; -free_sysfs: +free_meta: f2fs_sync_inode_meta(sbi); + /* + * Some dirty meta pages can be produced by recover_orphan_inodes() + * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg() + * followed by write_checkpoint() through f2fs_write_node_pages(), which + * falls into an infinite loop in sync_meta_pages(). + */ + truncate_inode_pages_final(META_MAPPING(sbi)); +free_sysfs: f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); @@ -2276,13 +2578,6 @@ free_node_inode: mutex_lock(&sbi->umount_mutex); release_ino_entry(sbi, true); f2fs_leave_shrinker(sbi); - /* - * Some dirty meta pages can be produced by recover_orphan_inodes() - * failed by EIO. Then, iput(node_inode) can trigger balance_fs_bg() - * followed by write_checkpoint() through f2fs_write_node_pages(), which - * falls into an infinite loop in sync_meta_pages(). - */ - truncate_inode_pages_final(META_MAPPING(sbi)); iput(sbi->node_inode); mutex_unlock(&sbi->umount_mutex); f2fs_destroy_stats(sbi); @@ -2303,6 +2598,10 @@ free_options: for (i = 0; i < NR_PAGE_TYPE; i++) kfree(sbi->write_io[i]); destroy_percpu_info(sbi); +#ifdef CONFIG_QUOTA + for (i = 0; i < F2FS_MAXQUOTAS; i++) + kfree(sbi->s_qf_names[i]); +#endif kfree(options); free_sb_buf: kfree(raw_super); diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 52e0097f61f0..611db59c52c5 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -158,6 +158,23 @@ static inline struct user_namespace *seq_user_ns(struct seq_file *seq) #endif } +/** + * seq_show_options - display mount options with appropriate escapes. + * @m: the seq_file handle + * @name: the mount option name + * @value: the mount option name's value, can be NULL + */ +static inline void seq_show_option(struct seq_file *m, const char *name, + const char *value) +{ + seq_putc(m, ','); + seq_escape(m, name, ",= \t\n\\"); + if (value) { + seq_putc(m, '='); + seq_escape(m, value, ", \t\n\\"); + } +} + #define SEQ_START_TOKEN ((void *)1) /* * Helpers for iteration over list_head-s in seq_files -- GitLab From 26dcfb6a6c94ea93161b15b1c27d97ac86f58e1c Mon Sep 17 00:00:00 2001 From: Qiuyang Sun Date: Wed, 9 Aug 2017 17:27:30 +0800 Subject: [PATCH 0911/5498] f2fs: merge equivalent flags F2FS_GET_BLOCK_[READ|DIO] commit upstream. Currently, the two flags F2FS_GET_BLOCK_[READ|DIO] are totally equivalent and can be used interchangably in all scenarios they are involved in. Neither of the flags is referenced in f2fs_map_blocks(), making them both the default case. To remove the ambiguity, this patch merges both flags into F2FS_GET_BLOCK_DEFAULT, and introduces an enum for all distinct flags. Signed-off-by: Qiuyang Sun Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 13 +++++++------ fs/f2fs/file.c | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0d0726836c30..5055597114ec 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1042,7 +1042,7 @@ static int get_data_block_dio(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_DIO, NULL); + F2FS_GET_BLOCK_DEFAULT, NULL); } static int get_data_block_bmap(struct inode *inode, sector_t iblock, @@ -1241,7 +1241,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_len = last_block - block_in_file; if (f2fs_map_blocks(inode, &map, 0, - F2FS_GET_BLOCK_READ)) + F2FS_GET_BLOCK_DEFAULT)) goto set_error_page; } got_it: diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d348bb21a311..240aa4055cce 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -618,12 +618,13 @@ struct f2fs_map_blocks { }; /* for flag in get_data_block */ -#define F2FS_GET_BLOCK_READ 0 -#define F2FS_GET_BLOCK_DIO 1 -#define F2FS_GET_BLOCK_FIEMAP 2 -#define F2FS_GET_BLOCK_BMAP 3 -#define F2FS_GET_BLOCK_PRE_DIO 4 -#define F2FS_GET_BLOCK_PRE_AIO 5 +enum { + F2FS_GET_BLOCK_DEFAULT, + F2FS_GET_BLOCK_FIEMAP, + F2FS_GET_BLOCK_BMAP, + F2FS_GET_BLOCK_PRE_DIO, + F2FS_GET_BLOCK_PRE_AIO, +}; /* * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4fa4c84aae0e..cc0ea3f90d19 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2058,7 +2058,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, */ while (map.m_lblk < pg_end) { map.m_len = pg_end - map.m_lblk; - err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT); if (err) goto out; @@ -2100,7 +2100,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, do_map: map.m_len = pg_end - map.m_lblk; - err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ); + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT); if (err) goto clear_out; -- GitLab From bbbdb8a0515b40212f3b10c22de9d328cf2a8064 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 10 Aug 2017 17:35:04 -0700 Subject: [PATCH 0912/5498] f2fs: let fill_super handle roll-forward errors commit afd2b4da40b3b567ef8d8e6881479345a2312a03 upstream. If we set CP_ERROR_FLAG in roll-forward error, f2fs is no longer to proceed any IOs due to f2fs_cp_error(). But, for example, if some stale data is involved on roll-forward process, we're able to get -ENOENT, getting fs stuck. If we get any error, let fill_super set SBI_NEED_FSCK and try to recover back to stable point. Cc: Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 03b7aa61a0c8..ca1e7bd10446 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -637,8 +637,6 @@ skip: } clear_sbi_flag(sbi, SBI_POR_DOING); - if (err) - set_ckpt_flags(sbi, CP_ERROR_FLAG); mutex_unlock(&sbi->cp_mutex); /* let's drop all the directory inodes for clean checkpoint */ -- GitLab From 575235158966fd7f6eac82213331d7bcee3bbefd Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 8 Aug 2017 19:09:08 +0800 Subject: [PATCH 0913/5498] f2fs: retry to revoke atomic commit in -ENOMEM case commit 7f2b4e8ea5b49846f7d20f1694dbca81f88b8d50 upstream. During atomic committing, if we encounter -ENOMEM in revoke path, it's better to give a chance to retry revoking. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 37df4b8c1006..6f47f2575a2e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -213,9 +213,15 @@ static int __revoke_inmem_pages(struct inode *inode, struct node_info ni; trace_f2fs_commit_inmem_page(page, INMEM_REVOKE); - +retry: set_new_dnode(&dn, inode, NULL, NULL, 0); - if (get_dnode_of_data(&dn, page->index, LOOKUP_NODE)) { + err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE); + if (err) { + if (err == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + cond_resched(); + goto retry; + } err = -EAGAIN; goto next; } -- GitLab From 6929c542c9dbf5b9ecbf4349928a9bf2c671fe67 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 11 Aug 2017 18:00:15 +0800 Subject: [PATCH 0914/5498] f2fs: add tracepoint for f2fs_gc commit c56f16dab0dfc8a37bc6133f2c2a02ffb873648b upstream. This patch adds tracepoint for f2fs_gc. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 50 ++++++++++++----- include/trace/events/f2fs.h | 107 ++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e60480f71bb5..57bea2182f30 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -919,7 +919,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, struct blk_plug plug; unsigned int segno = start_segno; unsigned int end_segno = start_segno + sbi->segs_per_sec; - int sec_freed = 0; + int seg_freed = 0; unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ? SUM_TYPE_DATA : SUM_TYPE_NODE; @@ -965,6 +965,10 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, gc_type); stat_inc_seg_count(sbi, type, gc_type); + + if (gc_type == FG_GC && + get_valid_blocks(sbi, segno, false) == 0) + seg_freed++; next: f2fs_put_page(sum_page, 0); } @@ -975,21 +979,17 @@ next: blk_finish_plug(&plug); - if (gc_type == FG_GC && - get_valid_blocks(sbi, start_segno, true) == 0) - sec_freed = 1; - stat_inc_call_count(sbi->stat_info); - return sec_freed; + return seg_freed; } int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background, unsigned int segno) { int gc_type = sync ? FG_GC : BG_GC; - int sec_freed = 0; - int ret; + int sec_freed = 0, seg_freed = 0, total_freed = 0; + int ret = 0; struct cp_control cpc; unsigned int init_segno = segno; struct gc_inode_list gc_list = { @@ -997,6 +997,15 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, .iroot = RADIX_TREE_INIT(GFP_NOFS), }; + trace_f2fs_gc_begin(sbi->sb, sync, background, + get_pages(sbi, F2FS_DIRTY_NODES), + get_pages(sbi, F2FS_DIRTY_DENTS), + get_pages(sbi, F2FS_DIRTY_IMETA), + free_sections(sbi), + free_segments(sbi), + reserved_segments(sbi), + prefree_segments(sbi)); + cpc.reason = __get_cp_reason(sbi); gc_more: if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) { @@ -1023,17 +1032,20 @@ gc_more: gc_type = FG_GC; } - ret = -EINVAL; /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */ - if (gc_type == BG_GC && !background) + if (gc_type == BG_GC && !background) { + ret = -EINVAL; goto stop; - if (!__get_victim(sbi, &segno, gc_type)) + } + if (!__get_victim(sbi, &segno, gc_type)) { + ret = -ENODATA; goto stop; - ret = 0; + } - if (do_garbage_collect(sbi, segno, &gc_list, gc_type) && - gc_type == FG_GC) + seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type); + if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec) sec_freed++; + total_freed += seg_freed; if (gc_type == FG_GC) sbi->cur_victim_sec = NULL_SEGNO; @@ -1050,6 +1062,16 @@ gc_more: stop: SIT_I(sbi)->last_victim[ALLOC_NEXT] = 0; SIT_I(sbi)->last_victim[FLUSH_DEVICE] = init_segno; + + trace_f2fs_gc_end(sbi->sb, ret, total_freed, sec_freed, + get_pages(sbi, F2FS_DIRTY_NODES), + get_pages(sbi, F2FS_DIRTY_DENTS), + get_pages(sbi, F2FS_DIRTY_IMETA), + free_sections(sbi), + free_segments(sbi), + reserved_segments(sbi), + prefree_segments(sbi)); + mutex_unlock(&sbi->gc_mutex); put_gc_inode(&gc_list); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index fdd7de503ce1..8fa678a2c335 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -524,6 +524,113 @@ TRACE_EVENT(f2fs_background_gc, __entry->free) ); +TRACE_EVENT(f2fs_gc_begin, + + TP_PROTO(struct super_block *sb, bool sync, bool background, + long long dirty_nodes, long long dirty_dents, + long long dirty_imeta, unsigned int free_sec, + unsigned int free_seg, int reserved_seg, + unsigned int prefree_seg), + + TP_ARGS(sb, sync, background, dirty_nodes, dirty_dents, dirty_imeta, + free_sec, free_seg, reserved_seg, prefree_seg), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(bool, sync) + __field(bool, background) + __field(long long, dirty_nodes) + __field(long long, dirty_dents) + __field(long long, dirty_imeta) + __field(unsigned int, free_sec) + __field(unsigned int, free_seg) + __field(int, reserved_seg) + __field(unsigned int, prefree_seg) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->sync = sync; + __entry->background = background; + __entry->dirty_nodes = dirty_nodes; + __entry->dirty_dents = dirty_dents; + __entry->dirty_imeta = dirty_imeta; + __entry->free_sec = free_sec; + __entry->free_seg = free_seg; + __entry->reserved_seg = reserved_seg; + __entry->prefree_seg = prefree_seg; + ), + + TP_printk("dev = (%d,%d), sync = %d, background = %d, nodes = %lld, " + "dents = %lld, imeta = %lld, free_sec:%u, free_seg:%u, " + "rsv_seg:%d, prefree_seg:%u", + show_dev(__entry->dev), + __entry->sync, + __entry->background, + __entry->dirty_nodes, + __entry->dirty_dents, + __entry->dirty_imeta, + __entry->free_sec, + __entry->free_seg, + __entry->reserved_seg, + __entry->prefree_seg) +); + +TRACE_EVENT(f2fs_gc_end, + + TP_PROTO(struct super_block *sb, int ret, int seg_freed, + int sec_freed, long long dirty_nodes, + long long dirty_dents, long long dirty_imeta, + unsigned int free_sec, unsigned int free_seg, + int reserved_seg, unsigned int prefree_seg), + + TP_ARGS(sb, ret, seg_freed, sec_freed, dirty_nodes, dirty_dents, + dirty_imeta, free_sec, free_seg, reserved_seg, prefree_seg), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, ret) + __field(int, seg_freed) + __field(int, sec_freed) + __field(long long, dirty_nodes) + __field(long long, dirty_dents) + __field(long long, dirty_imeta) + __field(unsigned int, free_sec) + __field(unsigned int, free_seg) + __field(int, reserved_seg) + __field(unsigned int, prefree_seg) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->ret = ret; + __entry->seg_freed = seg_freed; + __entry->sec_freed = sec_freed; + __entry->dirty_nodes = dirty_nodes; + __entry->dirty_dents = dirty_dents; + __entry->dirty_imeta = dirty_imeta; + __entry->free_sec = free_sec; + __entry->free_seg = free_seg; + __entry->reserved_seg = reserved_seg; + __entry->prefree_seg = prefree_seg; + ), + + TP_printk("dev = (%d,%d), ret = %d, seg_freed = %d, sec_freed = %d, " + "nodes = %lld, dents = %lld, imeta = %lld, free_sec:%u, " + "free_seg:%u, rsv_seg:%d, prefree_seg:%u", + show_dev(__entry->dev), + __entry->ret, + __entry->seg_freed, + __entry->sec_freed, + __entry->dirty_nodes, + __entry->dirty_dents, + __entry->dirty_imeta, + __entry->free_sec, + __entry->free_seg, + __entry->reserved_seg, + __entry->prefree_seg) +); + TRACE_EVENT(f2fs_get_victim, TP_PROTO(struct super_block *sb, int type, int gc_type, -- GitLab From aa2e98fd2193ebf951ace3199610811afcb5aa4c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 12 Aug 2017 21:33:23 -0700 Subject: [PATCH 0915/5498] f2fs: check hot_data for roll-forward recovery commit 125c9fb1ccb53eb2ea9380df40f3c743f3fb2fed upstream. We need to check HOT_DATA to truncate any previous data block when doing roll-forward recovery. Cc: Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index ca1e7bd10446..bc7503ed742e 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -311,7 +311,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, return 0; /* Get the previous summary */ - for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) { + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); if (curseg->segno == segno) { sum = curseg->sum_blk->entries[blkoff]; -- GitLab From 7b784f455d9953fe21f90803c04e425e54d41eed Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Mon, 14 Aug 2017 16:52:43 +0800 Subject: [PATCH 0916/5498] f2fs: remove unused function overprovision_sections commit f24b150a63b208f4437efc7dc7a42954363641ca upstream. Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index a60d057297e7..7eba54ef224b 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -491,11 +491,6 @@ static inline int overprovision_segments(struct f2fs_sb_info *sbi) return SM_I(sbi)->ovp_segments; } -static inline int overprovision_sections(struct f2fs_sb_info *sbi) -{ - return GET_SEC_FROM_SEG(sbi, (unsigned int)overprovision_segments(sbi)); -} - static inline int reserved_sections(struct f2fs_sb_info *sbi) { return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi)); -- GitLab From b85f24776244f59490ff2e3808accdc8193c1685 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 15 Aug 2017 21:27:19 -0700 Subject: [PATCH 0917/5498] f2fs: issue discard commands if gc_urgent is set commit 5f656541ff6e4f58b4ab5b4ae59badb97a9ff749 upstream. It's time to issue all the discard commands, if user sets the idle time. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 +++++- fs/f2fs/sysfs.c | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6f47f2575a2e..103c98d27f84 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -21,6 +21,7 @@ #include "f2fs.h" #include "segment.h" #include "node.h" +#include "gc.h" #include "trace.h" #include @@ -1274,8 +1275,11 @@ static int issue_discard_thread(void *data) if (kthread_should_stop()) return 0; - if (dcc->discard_wake) + if (dcc->discard_wake) { dcc->discard_wake = 0; + if (sbi->gc_thread && sbi->gc_thread->gc_urgent) + mark_discard_range_all(sbi); + } sb_start_intwrite(sbi->sb); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index fbbe6e03658a..6da09ac21e49 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -178,8 +178,13 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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) { + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + sbi->gc_thread->gc_wake = 1; wake_up_interruptible_all(&sbi->gc_thread->gc_wait_queue_head); + + dcc->discard_wake = 1; + wake_up_interruptible_all(&dcc->discard_wait_queue); } return count; -- GitLab From 6ed7ad609e72c81155ce6c03a1160d69bf4ef1ff Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 21 Aug 2017 22:53:45 +0800 Subject: [PATCH 0918/5498] f2fs: fix out-of-order execution in f2fs_issue_flush commit 6f890df0a7efe3181aceb5d8bcd4af7deb2abce5 upstream. In f2fs_issue_flush, due to out-of-order execution of CPU, wake_up can be called before we insert issue_list, result in long latency of wait_for_completion. Fix this by adding smp_mb() to force the order of related codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 103c98d27f84..20b10a907d80 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -549,7 +549,10 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) atomic_inc(&fcc->issing_flush); llist_add(&cmd.llnode, &fcc->issue_list); - if (!fcc->dispatch_list) + /* update issue_list before we wake up issue_flush thread */ + smp_mb(); + + if (waitqueue_active(&fcc->flush_wait_queue)) wake_up(&fcc->flush_wait_queue); if (fcc->f2fs_issue_flush) { -- GitLab From 98601fe6e98ac0f6fcc8269560be9f508dfbf17b Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 18 Aug 2017 23:37:36 +0800 Subject: [PATCH 0919/5498] f2fs: clear FI_HOT_DATA correctly commit 84a23fbe96b4e307eb749046a74515329119b08d upstream. This patch fixes to clear FI_HOT_DATA correctly in below path: - error handling in f2fs_ioc_start_atomic_write - after commit atomic write in f2fs_ioc_commit_atomic_write - after drop atomic write in drop_inmem_pages Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 ++ fs/f2fs/segment.c | 1 + 2 files changed, 3 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index cc0ea3f90d19..3c9f096abf57 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1624,6 +1624,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); if (ret) { clear_inode_flag(inode, FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_HOT_DATA); goto out; } @@ -1662,6 +1663,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); if (!ret) { clear_inode_flag(inode, FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_HOT_DATA); stat_dec_atomic_write(inode); } } else { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 20b10a907d80..5965ab48d9c2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -255,6 +255,7 @@ void drop_inmem_pages(struct inode *inode) mutex_unlock(&fi->inmem_lock); clear_inode_flag(inode, FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_HOT_DATA); stat_dec_atomic_write(inode); } -- GitLab From c123fc70ccf6659cd1d5e0647560b4babd86ab9f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 18 Aug 2017 16:20:33 +0800 Subject: [PATCH 0920/5498] f2fs: trigger normal fsync for non-atomic_write file commit 0adf6a1b796777f5d19a9b7442172016aeb8020a upstream. If file was not opened with atomic write mode, but user uses atomic write ioctl to fsync datas, in the flow, we should not fsync that file with atomic write mode. Fixes: 608514deba38 ("f2fs: set fsync mark only for the last dnode") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3c9f096abf57..4dde9c5dcce6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1667,7 +1667,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) stat_dec_atomic_write(inode); } } else { - ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true); + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, false); } err_out: inode_unlock(inode); -- GitLab From 68842d7d41231111595f4f188c607545df38a2cf Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 21 Aug 2017 13:51:32 -0700 Subject: [PATCH 0921/5498] f2fs: return error when accessing insane flie offset commit adb6dc197187e2a5f5a7bed01e722f46a58676af upstream. If file offset is insane, we have to return error instead of kernel panic. Reported-by: Eric Zhang Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0b3110504326..2ff473d68ad2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -555,7 +555,7 @@ static int get_node_path(struct inode *inode, long block, level = 3; goto got; } else { - BUG(); + return -E2BIG; } got: return level; @@ -579,6 +579,8 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) int err = 0; level = get_node_path(dn->inode, index, offset, noffset); + if (level < 0) + return level; nids[0] = dn->inode->i_ino; npage[0] = dn->inode_page; @@ -878,6 +880,8 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from) trace_f2fs_truncate_inode_blocks_enter(inode, from); level = get_node_path(inode, from, offset, noffset); + if (level < 0) + return level; page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) { -- GitLab From ff3bafc60ab19577ff1f80f8b9b743cd99361be0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 22 Aug 2017 21:15:43 -0700 Subject: [PATCH 0922/5498] f2fs: wake up discard_thread iff there is a candidate commit 01983c715ad0e78842a885f361ad927a3a985994 upstream. This patch fixes to avoid needless wake ups. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 +-- fs/f2fs/segment.h | 25 +++++++++++++++++++++++++ fs/f2fs/sysfs.c | 6 +----- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5965ab48d9c2..61432929522a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1574,8 +1574,7 @@ skip: kmem_cache_free(discard_entry_slab, entry); } - dcc->discard_wake = 1; - wake_up_interruptible_all(&dcc->discard_wait_queue); + wake_up_discard_thread(sbi, false); } static int create_discard_cmd_control(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 7eba54ef224b..8b628d291c0c 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -796,3 +796,28 @@ static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type, wbc->nr_to_write = desired; return desired - nr_to_write; } + +static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + bool wakeup = false; + int i; + + if (force) + goto wake_up; + + mutex_lock(&dcc->cmd_lock); + for (i = MAX_PLIST_NUM - 1; + i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) { + if (!list_empty(&dcc->pend_list[i])) { + wakeup = true; + break; + } + } + mutex_unlock(&dcc->cmd_lock); + if (!wakeup) + return; +wake_up: + dcc->discard_wake = 1; + wake_up_interruptible_all(&dcc->discard_wait_queue); +} diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 6da09ac21e49..6f17abcbcf9c 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -178,13 +178,9 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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) { - struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - sbi->gc_thread->gc_wake = 1; wake_up_interruptible_all(&sbi->gc_thread->gc_wait_queue_head); - - dcc->discard_wake = 1; - wake_up_interruptible_all(&dcc->discard_wait_queue); + wake_up_discard_thread(sbi, true); } return count; -- GitLab From 663305a79510c30ef3bf3df77a6dab65ac5f6c0a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 23 Aug 2017 18:23:24 +0800 Subject: [PATCH 0923/5498] f2fs: fix to avoid race in between aio and gc commit 73ac2f4e8256b9605c84364011322f015b31f499 upstream. We won't wait DIO synchronously when doing AIO, so there will be potential IO reorder in between AIO and GC, which will cause data corruption. This patch adds inode_dio_wait to serialize aio and data GC to avoid this issue. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 57bea2182f30..cd147e7c71e8 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -875,6 +875,9 @@ next_step: continue; } locked = true; + + /* wait for all inflight aio data */ + inode_dio_wait(inode); } start_bidx = start_bidx_of_node(nofs, inode) -- GitLab From de335a892b3a492f7fdd680fefae795b3772cacd Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 23 Aug 2017 18:23:25 +0800 Subject: [PATCH 0924/5498] f2fs: trigger fdatasync for non-atomic_write file commit 774e1b78a0f90a7e81f7a23d9ebfa8b8233c1ffa upstream. Sqlite only cares about synchronization of file data instead of other data unrelated attribute of inode, so in commit flow, call fdatasync is enough. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4dde9c5dcce6..f15e65daa04b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1667,7 +1667,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) stat_dec_atomic_write(inode); } } else { - ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, false); + ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 1, false); } err_out: inode_unlock(inode); -- GitLab From 654ed8fc0e8680cbb4d7deb9021bd0f4592bf133 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Aug 2017 11:10:58 -0700 Subject: [PATCH 0925/5498] f2fs: don't need to update inode checksum for recovery commit a298d57f960255d8618b86f403dee305d3c8a29c upstream. This patch fixes "f2fs: support inode checksum". The recovered inode page will be rewritten with valid checksum. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2ff473d68ad2..bd8ad92d9058 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2296,8 +2296,6 @@ retry: F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), i_projid)) dst->i_projid = src->i_projid; - - f2fs_inode_chksum_set(sbi, ipage); } new_ni = old_ni; -- GitLab From 07b182a8e5136fe1772faa6d0d554c0230b37da5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Aug 2017 16:54:51 -0700 Subject: [PATCH 0926/5498] f2fs: don't check inode's checksum if it was dirtied or writebacked commit ee605234996627c4fe874ea580e36211fb2bf6d5 upstream. If another thread already made the page dirtied or writebacked, we must avoid to verify checksum. If we got an error, we need to remove its uptodate as well. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 3 ++- fs/f2fs/node.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index ebbe53aab202..7a6f91f0df15 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -153,7 +153,8 @@ bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page) struct f2fs_inode *ri; __u32 provided, calculated; - if (!f2fs_enable_inode_chksum(sbi, page)) + if (!f2fs_enable_inode_chksum(sbi, page) || + PageDirty(page) || PageWriteback(page)) return true; ri = &F2FS_NODE(page)->i; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index bd8ad92d9058..25d2dbe1aec8 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1187,9 +1187,9 @@ page_hit: nid, nid_of_node(page), ino_of_node(page), ofs_of_node(page), cpver_of_node(page), next_blkaddr_of_node(page)); - ClearPageUptodate(page); err = -EINVAL; out_err: + ClearPageUptodate(page); f2fs_put_page(page, 1); return ERR_PTR(err); } -- GitLab From e418b4c0cf3b90fb40044cc43bfe850fa222130e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 30 Aug 2017 18:04:47 +0800 Subject: [PATCH 0927/5498] f2fs: update i_flags correctly commit 11f5020d2f43a9a7b9b5cf22873e5a9a06a884f7 upstream. f2fs enables hash-indexed directory by default, so we need to tag FS_INDEX_FL in inode::i_flags during directory creataion, in order to show correct status of inode in lsattr: Before: ------------------- /mnt/f2fs/dir/ After: -----------I------- /mnt/f2fs/dir/ Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 47359693cf60..c8b28244dd0d 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -98,6 +98,9 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) F2FS_I(inode)->i_flags = f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED); + if (S_ISDIR(inode->i_mode)) + F2FS_I(inode)->i_flags |= FS_INDEX_FL; + if (F2FS_I(inode)->i_flags & FS_PROJINHERIT_FL) set_inode_flag(inode, FI_PROJ_INHERIT); -- GitLab From 35e06daf9d6bc73520125fe3be374173ee11908c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 30 Aug 2017 18:04:48 +0800 Subject: [PATCH 0928/5498] f2fs: remove unneeded parameter of change_curseg commit 025d63a486aad611b20fa39184fc86e4b76d260e upstream. allocate_segment_by_default is the only caller of change_curseg passing @reuse with 'false', but commit 763bfe1bc575 ("f2fs: remove reusing any prefree segments") removes the calling, after that, @reuse in change_curseg always be true, so, let's clean up the unneeded parameter. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 61432929522a..3b2eb51d504c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2092,7 +2092,7 @@ static void __refresh_next_blkoff(struct f2fs_sb_info *sbi, * This function always allocates a used segment(from dirty seglist) by SSR * manner, so it should recover the existing segment information of valid blocks */ -static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) +static void change_curseg(struct f2fs_sb_info *sbi, int type) { struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, type); @@ -2113,12 +2113,10 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) curseg->alloc_type = SSR; __next_free_blkoff(sbi, curseg, 0); - if (reuse) { - sum_page = get_sum_page(sbi, new_segno); - sum_node = (struct f2fs_summary_block *)page_address(sum_page); - memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); - f2fs_put_page(sum_page, 1); - } + sum_page = get_sum_page(sbi, new_segno); + sum_node = (struct f2fs_summary_block *)page_address(sum_page); + memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); + f2fs_put_page(sum_page, 1); } static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) @@ -2182,7 +2180,7 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi, else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type)) new_curseg(sbi, type, false); else if (need_SSR(sbi) && get_ssr_segment(sbi, type)) - change_curseg(sbi, type, true); + change_curseg(sbi, type); else new_curseg(sbi, type, false); @@ -2535,7 +2533,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, /* change the current segment */ if (segno != curseg->segno) { curseg->next_segno = segno; - change_curseg(sbi, type, true); + change_curseg(sbi, type); } curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); @@ -2554,7 +2552,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, if (recover_curseg) { if (old_cursegno != curseg->segno) { curseg->next_segno = old_cursegno; - change_curseg(sbi, type, true); + change_curseg(sbi, type); } curseg->next_blkoff = old_blkoff; } -- GitLab From 3010ad5a391b5bd58acfdcb8cf3d4e1b51b73046 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 31 Aug 2017 18:56:05 +0800 Subject: [PATCH 0929/5498] f2fs: avoid race in between atomic_read & atomic_inc commit edd748e6c8e824a8281f8a8450f12c4a95ec61ee upstream. Previously, we will miss merging flush command during fsync due to below race condition: Thread A Thread B Thread C - f2fs_issue_flush - atomic_read(&issing_flush) - f2fs_issue_flush - atomic_read(&issing_flush) - f2fs_issue_flush - atomic_read(&issing_flush) - atomic_inc(&issing_flush) - atomic_inc(&issing_flush) - atomic_inc(&issing_flush) - submit_flush_wait - submit_flush_wait - submit_flush_wait It needs to use atomic_inc_return instead to avoid such race. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3b2eb51d504c..27def4321be8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -536,8 +536,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) return ret; } - if (!atomic_read(&fcc->issing_flush)) { - atomic_inc(&fcc->issing_flush); + if (atomic_inc_return(&fcc->issing_flush) == 1) { ret = submit_flush_wait(sbi); atomic_dec(&fcc->issing_flush); @@ -547,7 +546,6 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) init_completion(&cmd.wait); - atomic_inc(&fcc->issing_flush); llist_add(&cmd.llnode, &fcc->issue_list); /* update issue_list before we wake up issue_flush thread */ -- GitLab From f294dd370995177a0b8e2cd1c59ec03d3d4d9535 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 31 Aug 2017 18:56:06 +0800 Subject: [PATCH 0930/5498] f2fs: fix to wake up all sleeping flusher commit d3238691ed5f7be29f7b8b7cf7c68bbb2924361d upstream. In scenario of remount_ro vs flush, after flush_thread exits in ->remount_fs, flusher will only clean up golbal issue_list, but without waking up flushers waiting on that list, result in hang related user threads. In order to fix this issue, this patch enables the flusher to take charge of issue_flush thread: executes merged flush command, and wake up all sleeping flushers. Fixes: 5eba8c5d1fb3 ("f2fs: fix to access nullified flush_cmd_control pointer") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 27def4321be8..cfa7da61dfba 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -558,8 +558,27 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) wait_for_completion(&cmd.wait); atomic_dec(&fcc->issing_flush); } else { - llist_del_all(&fcc->issue_list); - atomic_set(&fcc->issing_flush, 0); + struct llist_node *list; + + list = llist_del_all(&fcc->issue_list); + if (!list) { + wait_for_completion(&cmd.wait); + atomic_dec(&fcc->issing_flush); + } else { + struct flush_cmd *tmp, *next; + + ret = submit_flush_wait(sbi); + + llist_for_each_entry_safe(tmp, next, list, llnode) { + if (tmp == &cmd) { + cmd.ret = ret; + atomic_dec(&fcc->issing_flush); + continue; + } + tmp->ret = ret; + complete(&tmp->wait); + } + } } return cmd.ret; -- GitLab From 72eceb89aa1c1c27864f995af291eeebb5a3f517 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 31 Aug 2017 15:06:24 +0530 Subject: [PATCH 0931/5498] f2fs: constify super_operations commit f62fc9f97602d5f52fd574db80d95d71869bfe21 upstream. super_operations are not supposed to change at runtime. "struct super_block" working with super_operations provided by work with const super_operations. So mark the non-const structs as const Signed-off-by: Arvind Yadav Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d68bfd9ce0ce..fb69055ace6a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1544,7 +1544,7 @@ void f2fs_quota_off_umount(struct super_block *sb) } #endif -static struct super_operations f2fs_sops = { +static const struct super_operations f2fs_sops = { .alloc_inode = f2fs_alloc_inode, .drop_inode = f2fs_drop_inode, .destroy_inode = f2fs_destroy_inode, -- GitLab From 5a3512a921b820b13416a32561bfc2bc26e963ee Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Mon, 4 Sep 2017 11:10:18 +0800 Subject: [PATCH 0932/5498] Revert "f2fs: add a new function get_ssr_cost" commit 2afce76a1151fe2f1104962c327d8f85047045e6 upstream. This reverts commit b7b7c4cf1c9ef0272a65f1480457cbfdadcda19d. se->ckpt_valid_blocks will never be smaller than se->valid_blocks, so just remove get_ssr_cost. Signed-off-by: Yunlong Song Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index cd147e7c71e8..b226760afba8 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -277,20 +277,11 @@ static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi, valid_blocks * 2 : valid_blocks; } -static unsigned int get_ssr_cost(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct seg_entry *se = get_seg_entry(sbi, segno); - - return se->ckpt_valid_blocks > se->valid_blocks ? - se->ckpt_valid_blocks : se->valid_blocks; -} - static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, unsigned int segno, struct victim_sel_policy *p) { if (p->alloc_mode == SSR) - return get_ssr_cost(sbi, segno); + return get_seg_entry(sbi, segno)->ckpt_valid_blocks; /* alloc_mode == LFS */ if (p->gc_mode == GC_GREEDY) -- GitLab From 9c87cd0a2e67231ae0b8a1d96625199c05cc9db4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 5 Sep 2017 16:54:24 -0700 Subject: [PATCH 0933/5498] f2fs: introduce f2fs_encrypted_file for clean-up commit 1958593e4fa9fa9e3e2d03b27e72af314c2891be upstream. This patch replaces (f2fs_encrypted_inode() && S_ISREG()) with f2fs_encrypted_file(), which gives no functional change. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 10 +++++----- fs/f2fs/f2fs.h | 5 +++++ fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 5 ++--- fs/f2fs/inline.c | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5055597114ec..d5eaa3588ed6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -582,7 +582,7 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index, .encrypted_page = NULL, }; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) return read_mapping_page(mapping, index, NULL); page = f2fs_grab_cache_page(mapping, index, for_write); @@ -787,7 +787,7 @@ alloc: static inline bool __force_buffered_io(struct inode *inode, int rw) { - return ((f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) || + return (f2fs_encrypted_file(inode) || (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) || F2FS_I_SB(inode)->s_ndevs); } @@ -1155,7 +1155,7 @@ static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, struct fscrypt_ctx *ctx = NULL; struct bio *bio; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + if (f2fs_encrypted_file(inode)) { ctx = fscrypt_get_ctx(inode, GFP_NOFS); if (IS_ERR(ctx)) return ERR_CAST(ctx); @@ -1342,7 +1342,7 @@ static int encrypt_one_page(struct f2fs_io_info *fio) struct inode *inode = fio->page->mapping->host; gfp_t gfp_flags = GFP_NOFS; - if (!f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) + if (!f2fs_encrypted_file(inode)) return 0; /* wait for GCed encrypted page writeback */ @@ -1979,7 +1979,7 @@ repeat: f2fs_wait_on_page_writeback(page, DATA, false); /* wait for GCed encrypted page writeback */ - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); if (len == PAGE_SIZE || PageUptodate(page)) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 240aa4055cce..57dc741572fd 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3054,6 +3054,11 @@ static inline bool f2fs_encrypted_inode(struct inode *inode) return file_is_encrypt(inode); } +static inline bool f2fs_encrypted_file(struct inode *inode) +{ + return f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode); +} + static inline void f2fs_set_encrypted_inode(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f15e65daa04b..9a34d0fc88fa 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -109,7 +109,7 @@ mapped: f2fs_wait_on_page_writeback(page, DATA, false); /* wait for GCed encrypted page writeback */ - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); out_sem: diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index b226760afba8..6a12f33d0cdd 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -831,8 +831,7 @@ next_step: continue; /* if encrypted inode, let's go phase 3 */ - if (f2fs_encrypted_inode(inode) && - S_ISREG(inode->i_mode)) { + if (f2fs_encrypted_file(inode)) { add_gc_inode(gc_list, inode); continue; } @@ -873,7 +872,7 @@ next_step: start_bidx = start_bidx_of_node(nofs, inode) + ofs_in_node; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) move_encrypted_block(inode, start_bidx, segno, off); else move_data_page(inode, start_bidx, gc_type, segno, off); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index c70153a80a52..b67d30ab1954 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -26,7 +26,7 @@ bool f2fs_may_inline_data(struct inode *inode) if (i_size_read(inode) > MAX_INLINE_DATA(inode)) return false; - if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + if (f2fs_encrypted_file(inode)) return false; return true; -- GitLab From 9431401c28c6873a45ef1c7cbe7d6bb0902ee2e4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 5 Sep 2017 17:04:35 -0700 Subject: [PATCH 0934/5498] f2fs: use generic terms used for encrypted block management commit d4c759ee5faa51e0b0ee55d8a229ba5b80c4917e upstream. This patch renames functions regarding to buffer management via META_MAPPING used for encrypted blocks especially. We can actually use them in generic way. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +++--- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 13 +++++++++---- fs/f2fs/segment.c | 3 +-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d5eaa3588ed6..368926450c8b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1161,7 +1161,7 @@ static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, return ERR_CAST(ctx); /* wait the page to be moved by cleaning */ - f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); + f2fs_wait_on_block_writeback(sbi, blkaddr); } bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); @@ -1346,7 +1346,7 @@ static int encrypt_one_page(struct f2fs_io_info *fio) return 0; /* wait for GCed encrypted page writeback */ - f2fs_wait_on_encrypted_page_writeback(fio->sbi, fio->old_blkaddr); + f2fs_wait_on_block_writeback(fio->sbi, fio->old_blkaddr); retry_encrypt: fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page, @@ -1980,7 +1980,7 @@ repeat: /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_file(inode)) - f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); + f2fs_wait_on_block_writeback(sbi, blkaddr); if (len == PAGE_SIZE || PageUptodate(page)) return 0; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 57dc741572fd..a6f67b06bf96 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2664,8 +2664,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, struct f2fs_io_info *fio, bool add_list); void f2fs_wait_on_page_writeback(struct page *page, enum page_type type, bool ordered); -void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, - block_t blkaddr); +void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr); void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk); void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk); int lookup_journal_in_cursum(struct f2fs_journal *journal, int type, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 9a34d0fc88fa..e5a47b59d1dd 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -110,7 +110,7 @@ mapped: /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_file(inode)) - f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); + f2fs_wait_on_block_writeback(sbi, dn.data_blkaddr); out_sem: up_read(&F2FS_I(inode)->i_mmap_sem); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 6a12f33d0cdd..bfe6a8ccc3a0 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -599,8 +599,12 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, return true; } -static void move_encrypted_block(struct inode *inode, block_t bidx, - unsigned int segno, int off) +/* + * Move data block via META_MAPPING while keeping locked data page. + * This can be used to move blocks, aka LBAs, directly on disk. + */ +static void move_data_block(struct inode *inode, block_t bidx, + unsigned int segno, int off) { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), @@ -873,9 +877,10 @@ next_step: start_bidx = start_bidx_of_node(nofs, inode) + ofs_in_node; if (f2fs_encrypted_file(inode)) - move_encrypted_block(inode, start_bidx, segno, off); + move_data_block(inode, start_bidx, segno, off); else - move_data_page(inode, start_bidx, gc_type, segno, off); + move_data_page(inode, start_bidx, gc_type, + segno, off); if (locked) { up_write(&fi->dio_rwsem[WRITE]); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index cfa7da61dfba..017b89a7942d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2608,8 +2608,7 @@ void f2fs_wait_on_page_writeback(struct page *page, } } -void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi, - block_t blkaddr) +void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr) { struct page *cpage; -- GitLab From ac3458af434e45a050b1c57fc1d93fa16472cb6b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 6 Sep 2017 21:04:44 -0700 Subject: [PATCH 0935/5498] f2fs: make get_lock_data_page to handle encrypted inode commit 13ba41e346170e594b7ce582561b3efa5b85f18f upstream. This patch refactors get_lock_data_page() to handle encryption case directly. In order to do that, it introduces common f2fs_submit_page_read(). Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 109 +++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 368926450c8b..2da4cd318d63 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -457,6 +457,53 @@ out_fail: return err; } +static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, + unsigned nr_pages) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct fscrypt_ctx *ctx = NULL; + struct bio *bio; + + if (f2fs_encrypted_file(inode)) { + ctx = fscrypt_get_ctx(inode, GFP_NOFS); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + /* wait the page to be moved by cleaning */ + f2fs_wait_on_block_writeback(sbi, blkaddr); + } + + bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); + if (!bio) { + if (ctx) + fscrypt_release_ctx(ctx); + return ERR_PTR(-ENOMEM); + } + f2fs_target_device(sbi, blkaddr, bio); + bio->bi_end_io = f2fs_read_end_io; + bio->bi_private = ctx; + bio_set_op_attrs(bio, REQ_OP_READ, 0); + + return bio; +} + +/* This can handle encryption stuffs */ +static int f2fs_submit_page_read(struct inode *inode, struct page *page, + block_t blkaddr) +{ + struct bio *bio = f2fs_grab_read_bio(inode, blkaddr, 1); + + if (IS_ERR(bio)) + return PTR_ERR(bio); + + if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { + bio_put(bio); + return -EFAULT; + } + __submit_bio(F2FS_I_SB(inode), bio, DATA); + return 0; +} + static void __set_data_blkaddr(struct dnode_of_data *dn) { struct f2fs_node *rn = F2FS_NODE(dn->node_page); @@ -574,16 +621,6 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index, struct page *page; struct extent_info ei = {0,0,0}; int err; - struct f2fs_io_info fio = { - .sbi = F2FS_I_SB(inode), - .type = DATA, - .op = REQ_OP_READ, - .op_flags = op_flags, - .encrypted_page = NULL, - }; - - if (f2fs_encrypted_file(inode)) - return read_mapping_page(mapping, index, NULL); page = f2fs_grab_cache_page(mapping, index, for_write); if (!page) @@ -624,9 +661,7 @@ got_it: return page; } - fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr; - fio.page = page; - err = f2fs_submit_page_bio(&fio); + err = f2fs_submit_page_read(inode, page, dn.data_blkaddr); if (err) goto put_err; return page; @@ -1148,35 +1183,6 @@ out: return ret; } -static struct bio *f2fs_grab_bio(struct inode *inode, block_t blkaddr, - unsigned nr_pages) -{ - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct fscrypt_ctx *ctx = NULL; - struct bio *bio; - - if (f2fs_encrypted_file(inode)) { - ctx = fscrypt_get_ctx(inode, GFP_NOFS); - if (IS_ERR(ctx)) - return ERR_CAST(ctx); - - /* wait the page to be moved by cleaning */ - f2fs_wait_on_block_writeback(sbi, blkaddr); - } - - bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); - if (!bio) { - if (ctx) - fscrypt_release_ctx(ctx); - return ERR_PTR(-ENOMEM); - } - f2fs_target_device(sbi, blkaddr, bio); - bio->bi_end_io = f2fs_read_end_io; - bio->bi_private = ctx; - - return bio; -} - /* * This function was originally taken from fs/mpage.c, and customized for f2fs. * Major change was from block_size == page_size in f2fs by default. @@ -1272,12 +1278,11 @@ submit_and_realloc: bio = NULL; } if (bio == NULL) { - bio = f2fs_grab_bio(inode, block_nr, nr_pages); + bio = f2fs_grab_read_bio(inode, block_nr, nr_pages); if (IS_ERR(bio)) { bio = NULL; goto set_error_page; } - bio_set_op_attrs(bio, REQ_OP_READ, 0); } if (bio_add_page(bio, page, blocksize, 0) < blocksize) @@ -1994,21 +1999,9 @@ repeat: zero_user_segment(page, 0, PAGE_SIZE); SetPageUptodate(page); } else { - struct bio *bio; - - bio = f2fs_grab_bio(inode, blkaddr, 1); - if (IS_ERR(bio)) { - err = PTR_ERR(bio); - goto fail; - } - bio->bi_rw = REQ_OP_READ; - if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { - bio_put(bio); - err = -EFAULT; + err = f2fs_submit_page_read(inode, page, blkaddr); + if (err) goto fail; - } - - __submit_bio(sbi, bio, DATA); lock_page(page); if (unlikely(page->mapping != mapping)) { -- GitLab From 6ef0d7aefaf6073369948bd8859dcc06c1419e70 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 7 Sep 2017 10:40:54 +0800 Subject: [PATCH 0936/5498] f2fs: avoid race in between read xattr & write xattr commit 27161f13e3c3241944846ac24942a85cceda0a2c upstream. Thread A: Thread B: -f2fs_getxattr -lookup_all_xattrs -xnid = F2FS_I(inode)->i_xattr_nid; -f2fs_setxattr -__f2fs_setxattr -write_all_xattrs -truncate_xattr_node ... ... -write_checkpoint ... ... -alloc_nid <- nid reuse -get_node_page -f2fs_bug_on <- nid != node_footer->nid It's need a rw_sem to avoid the race Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/super.c | 1 + fs/f2fs/xattr.c | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a6f67b06bf96..d65763640bb3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -683,6 +683,7 @@ struct f2fs_inode_info { struct extent_tree *extent_tree; /* cached extent_tree entry */ struct rw_semaphore dio_rwsem[2];/* avoid racing between dio and gc */ struct rw_semaphore i_mmap_sem; + struct rw_semaphore i_xattr_sem; /* avoid racing between reading and changing EAs */ int i_extra_isize; /* size of extra space located in i_addr */ kprojid_t i_projid; /* id for project quota */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index fb69055ace6a..ad6b805190bb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -636,6 +636,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_rwsem(&fi->dio_rwsem[READ]); init_rwsem(&fi->dio_rwsem[WRITE]); init_rwsem(&fi->i_mmap_sem); + init_rwsem(&fi->i_xattr_sem); #ifdef CONFIG_QUOTA fi->i_reserved_quota = 0; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 833af6916534..ec48fe87414b 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -522,8 +522,10 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name, if (len > F2FS_NAME_LEN) return -ERANGE; + down_read(&F2FS_I(inode)->i_xattr_sem); error = lookup_all_xattrs(inode, ipage, index, len, name, &entry, &base_addr); + up_read(&F2FS_I(inode)->i_xattr_sem); if (error) return error; @@ -552,7 +554,9 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) int error = 0; size_t rest = buffer_size; + down_read(&F2FS_I(inode)->i_xattr_sem); error = read_all_xattrs(inode, NULL, &base_addr); + up_read(&F2FS_I(inode)->i_xattr_sem); if (error) return error; @@ -728,7 +732,9 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, f2fs_lock_op(sbi); /* protect xattr_ver */ down_write(&F2FS_I(inode)->i_sem); + down_write(&F2FS_I(inode)->i_xattr_sem); err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags); + up_write(&F2FS_I(inode)->i_xattr_sem); up_write(&F2FS_I(inode)->i_sem); f2fs_unlock_op(sbi); -- GitLab From 33ccbba0d0e97e22711895a5ebb51d3d7ad9b470 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 9 Sep 2017 12:03:23 -0700 Subject: [PATCH 0937/5498] f2fs: better to wait for fstrim completion commit 1eb1ef4a8e9f6a4d9c2c7a645aba21d2f8719728 upstream. In android, we'd better wait for fstrim completion instead of issuing the discard commands asynchronous. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 017b89a7942d..409f80c74313 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "f2fs.h" #include "segment.h" @@ -1141,6 +1142,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) if (dcc->pend_list_tag[i] & P_TRIM) { __submit_discard_cmd(sbi, dc); issued++; + + if (fatal_signal_pending(current)) + break; continue; } @@ -1257,7 +1261,7 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) } } -/* This comes from f2fs_put_super */ +/* This comes from f2fs_put_super and f2fs_trim_fs */ void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { __issue_discard_cmd(sbi, false); @@ -2292,6 +2296,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) } /* It's time to issue all the filed discards */ mark_discard_range_all(sbi); + f2fs_wait_discard_bios(sbi); out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return err; -- GitLab From 55828d6ebe3d70f7aa92b39d672a3269d0a4da62 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 9 Sep 2017 11:11:04 -0700 Subject: [PATCH 0938/5498] f2fs: speed up gc_urgent mode with SSR commit b3a97a2a9a7b2d50bcf13d32857cd6f5695c6b65 upstream. This patch activates SSR in gc_urgent mode. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 15 +++++++++++++++ fs/f2fs/segment.h | 13 ------------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d65763640bb3..5711b8f0c963 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2625,6 +2625,7 @@ void destroy_node_manager_caches(void); /* * segment.c */ +bool need_SSR(struct f2fs_sb_info *sbi); void register_inmem_page(struct inode *inode, struct page *page); void drop_inmem_pages(struct inode *inode); void drop_inmem_page(struct inode *inode, struct page *page); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 409f80c74313..73601d290b27 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -169,6 +169,21 @@ found: return result - size + __reverse_ffz(tmp); } +bool need_SSR(struct f2fs_sb_info *sbi) +{ + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); + + if (test_opt(sbi, LFS)) + return false; + if (sbi->gc_thread && sbi->gc_thread->gc_urgent) + return true; + + return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + + 2 * reserved_sections(sbi)); +} + void register_inmem_page(struct inode *inode, struct page *page) { struct f2fs_inode_info *fi = F2FS_I(inode); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 8b628d291c0c..593b99cee4d1 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -496,19 +496,6 @@ static inline int reserved_sections(struct f2fs_sb_info *sbi) return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi)); } -static inline bool need_SSR(struct f2fs_sb_info *sbi) -{ - int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); - int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); - int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); - - if (test_opt(sbi, LFS)) - return false; - - return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + - 2 * reserved_sections(sbi)); -} - static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed, int needed) { -- GitLab From ddd52a4ea4dc543d293c2163387530bfc23cbfe2 Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Mon, 11 Sep 2017 16:30:28 +0900 Subject: [PATCH 0939/5498] f2fs: clear radix tree dirty tag of pages whose dirty flag is cleared commit 0abd8e70d24b665dd00972d4a259e05528cbf4c6 upstream. On a senario like writing out the first dirty page of the inode as the inline data, we only cleared dirty flags of the pages, but didn't clear the dirty tags of those pages in the radix tree. If we don't clear the dirty tags of the pages in the radix tree, the inodes which contain the pages will be marked with I_DIRTY_PAGES again and again, and writepages() for the inodes will be invoked in every writeback period. As a result, nothing will be done in every writepages() for the inodes and it will just consume CPU time meaninglessly. Signed-off-by: Daeho Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 7 +++++++ fs/f2fs/inline.c | 7 +++++++ mm/util.c | 1 + 3 files changed, 15 insertions(+) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 1380c442648b..4f2a8fedb313 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -705,6 +705,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, struct f2fs_dentry_block *dentry_blk; unsigned int bit_pos; int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); + struct address_space *mapping = page_mapping(page); + unsigned long flags; int i; f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); @@ -735,6 +737,11 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, if (bit_pos == NR_DENTRY_IN_BLOCK && !truncate_hole(dir, page->index, page->index + 1)) { + spin_lock_irqsave(&mapping->tree_lock, flags); + radix_tree_tag_clear(&mapping->page_tree, page_index(page), + PAGECACHE_TAG_DIRTY); + spin_unlock_irqrestore(&mapping->tree_lock, flags); + clear_page_dirty_for_io(page); ClearPagePrivate(page); ClearPageUptodate(page); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index b67d30ab1954..bbd092269454 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -220,6 +220,8 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) { void *src_addr, *dst_addr; struct dnode_of_data dn; + struct address_space *mapping = page_mapping(page); + unsigned long flags; int err; set_new_dnode(&dn, inode, NULL, NULL, 0); @@ -241,6 +243,11 @@ int f2fs_write_inline_data(struct inode *inode, struct page *page) kunmap_atomic(src_addr); set_page_dirty(dn.inode_page); + spin_lock_irqsave(&mapping->tree_lock, flags); + radix_tree_tag_clear(&mapping->page_tree, page_index(page), + PAGECACHE_TAG_DIRTY); + spin_unlock_irqrestore(&mapping->tree_lock, flags); + set_inode_flag(inode, FI_APPEND_WRITE); set_inode_flag(inode, FI_DATA_EXIST); diff --git a/mm/util.c b/mm/util.c index fec39d4509a9..41d8542a347f 100644 --- a/mm/util.c +++ b/mm/util.c @@ -310,6 +310,7 @@ struct address_space *page_mapping(struct page *page) mapping = NULL; return mapping; } +EXPORT_SYMBOL(page_mapping); int overcommit_ratio_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, -- GitLab From 5d4bfc3cd9b6314ae6500f0f3721d8eeae2a574c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 12 Sep 2017 14:04:05 +0800 Subject: [PATCH 0940/5498] f2fs: detect dirty inode in evict_inode commit ca7d802a7d8ee4c47dce9be86ef4b27e587086bb upstream. Add a bugon in f2fs_evict_inode to detect inconsistent status between inode cache and related node page cache. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 7a6f91f0df15..395ca5dd3d37 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -519,6 +519,9 @@ no_delete: stat_dec_inline_dir(inode); stat_dec_inline_inode(inode); + if (!is_set_ckpt_flags(sbi, CP_ERROR_FLAG)) + f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE)); + /* ino == 0, if f2fs_new_inode() was failed t*/ if (inode->i_ino) invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, -- GitLab From 81f5fd847ca5754c4576ca5ab988ff9eac67aa0c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 12 Sep 2017 14:25:35 +0800 Subject: [PATCH 0941/5498] f2fs: fix to show correct discard_granularity in sysfs commit 80647e5f4c728ecea7d9190c6e7163755ff6835c upstream. Fix below incorrect display when reading discard_granularity sysfs node. $ cat /sys/fs/f2fs//discard_granularity $ 16 $ echo 32 > /sys/fs/f2fs//discard_granularity $ cat /sys/fs/f2fs//discard_granularity $ 16 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 6f17abcbcf9c..06fda7cc7123 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -170,6 +170,8 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, dcc->pend_list_tag[i] &= (~P_ACTIVE); } mutex_unlock(&dcc->cmd_lock); + + *ui = t; return count; } -- GitLab From 5776b62b0ffc00bcecfc1fe6009d89c0de1e7de3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 12 Sep 2017 21:35:12 +0800 Subject: [PATCH 0942/5498] f2fs: hurry up to issue discard after io interruption commit e6c6de18f010d9a7d592f4044d2c30213cb3a7bc upstream. Once we encounter I/O interruption during issuing discards, we will delay long time before next round, but if system status is I/O idle during the time, it may loses opportunity to issue discards. So this patch changes to hurry up to issue discard after io interruption. Besides, this patch also fixes to issue discards accurately with assigned rate. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 73601d290b27..7dd2f612ec81 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1142,6 +1142,7 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) struct blk_plug plug; int iter = 0, issued = 0; int i; + bool io_interrupted = false; mutex_lock(&dcc->cmd_lock); f2fs_bug_on(sbi, @@ -1163,11 +1164,20 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) continue; } - if (!issue_cond || is_idle(sbi)) { + if (!issue_cond) { + __submit_discard_cmd(sbi, dc); issued++; + continue; + } + + if (is_idle(sbi)) { __submit_discard_cmd(sbi, dc); + issued++; + } else { + io_interrupted = true; } - if (issue_cond && iter++ > DISCARD_ISSUE_RATE) + + if (++iter >= DISCARD_ISSUE_RATE) goto out; } if (list_empty(pend_list) && dcc->pend_list_tag[i] & P_TRIM) @@ -1177,6 +1187,9 @@ out: blk_finish_plug(&plug); mutex_unlock(&dcc->cmd_lock); + if (!issued && io_interrupted) + issued = -1; + return issued; } -- GitLab From 159c8d40bd517081f5140c48e19ac63d6ed5bc89 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 2 Feb 2015 00:37:00 -0500 Subject: [PATCH 0943/5498] vfs: add support for a lazytime mount option commit 0ae45f63d4ef8d8eeec49c7d8b44a1775fff13e8 upstream. Add a new mount option which enables a new "lazytime" mode. This mode causes atime, mtime, and ctime updates to only be made to the in-memory version of the inode. The on-disk times will only get updated when (a) if the inode needs to be updated for some non-time related change, (b) if userspace calls fsync(), syncfs() or sync(), or (c) just before an undeleted inode is evicted from memory. This is OK according to POSIX because there are no guarantees after a crash unless userspace explicitly requests via a fsync(2) call. For workloads which feature a large number of random write to a preallocated file, the lazytime mount option significantly reduces writes to the inode table. The repeated 4k writes to a single block will result in undesirable stress on flash devices and SMR disk drives. Even on conventional HDD's, the repeated writes to the inode table block will trigger Adjacent Track Interference (ATI) remediation latencies, which very negatively impact long tail latencies --- which is a very big deal for web serving tiers (for example). Google-Bug-Id: 18297052 Signed-off-by: Theodore Ts'o Signed-off-by: Al Viro --- fs/ext4/inode.c | 6 ++++ fs/fs-writeback.c | 62 ++++++++++++++++++++++++++------ fs/gfs2/file.c | 4 +-- fs/inode.c | 56 ++++++++++++++++++++--------- fs/jfs/file.c | 2 +- fs/libfs.c | 2 +- fs/proc_namespace.c | 1 + fs/sync.c | 8 +++++ include/linux/backing-dev.h | 1 + include/linux/fs.h | 5 +++ include/trace/events/writeback.h | 60 ++++++++++++++++++++++++++++++- include/uapi/linux/fs.h | 4 ++- mm/backing-dev.c | 10 ++++-- 13 files changed, 186 insertions(+), 35 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 72d07c77bc7f..52456ba7083f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5103,11 +5103,17 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) * If the inode is marked synchronous, we don't honour that here - doing * so would cause a commit on atime updates, which we don't bother doing. * We handle synchronous inodes at the highest possible level. + * + * If only the I_DIRTY_TIME flag is set, we can skip everything. If + * I_DIRTY_TIME and I_DIRTY_SYNC is set, the only inode fields we need + * to copy into the on-disk inode structure are the timestamp files. */ void ext4_dirty_inode(struct inode *inode, int flags) { handle_t *handle; + if (flags == I_DIRTY_TIME) + return; handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); if (IS_ERR(handle)) goto out; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 618e0095127d..8c61a53a2dd6 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -247,14 +247,19 @@ static bool inode_dirtied_after(struct inode *inode, unsigned long t) return ret; } +#define EXPIRE_DIRTY_ATIME 0x0001 + /* * Move expired (dirtied before work->older_than_this) dirty inodes from * @delaying_queue to @dispatch_queue. */ static int move_expired_inodes(struct list_head *delaying_queue, struct list_head *dispatch_queue, + int flags, struct wb_writeback_work *work) { + unsigned long *older_than_this = NULL; + unsigned long expire_time; LIST_HEAD(tmp); struct list_head *pos, *node; struct super_block *sb = NULL; @@ -262,13 +267,21 @@ static int move_expired_inodes(struct list_head *delaying_queue, int do_sb_sort = 0; int moved = 0; + if ((flags & EXPIRE_DIRTY_ATIME) == 0) + older_than_this = work->older_than_this; + else if ((work->reason == WB_REASON_SYNC) == 0) { + expire_time = jiffies - (HZ * 86400); + older_than_this = &expire_time; + } while (!list_empty(delaying_queue)) { inode = wb_inode(delaying_queue->prev); - if (work->older_than_this && - inode_dirtied_after(inode, *work->older_than_this)) + if (older_than_this && + inode_dirtied_after(inode, *older_than_this)) break; list_move(&inode->i_wb_list, &tmp); moved++; + if (flags & EXPIRE_DIRTY_ATIME) + set_bit(__I_DIRTY_TIME_EXPIRED, &inode->i_state); if (sb_is_blkdev_sb(inode->i_sb)) continue; if (sb && sb != inode->i_sb) @@ -309,9 +322,12 @@ out: static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work) { int moved; + assert_spin_locked(&wb->list_lock); list_splice_init(&wb->b_more_io, &wb->b_io); - moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, work); + moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work); + moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io, + EXPIRE_DIRTY_ATIME, work); trace_writeback_queue_io(wb, work, moved); } @@ -435,6 +451,8 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, * updates after data IO completion. */ redirty_tail(inode, wb); + } else if (inode->i_state & I_DIRTY_TIME) { + list_move(&inode->i_wb_list, &wb->b_dirty_time); } else { /* The inode is clean. Remove from writeback lists. */ list_del_init(&inode->i_wb_list); @@ -481,7 +499,13 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_lock(&inode->i_lock); dirty = inode->i_state & I_DIRTY; - inode->i_state &= ~I_DIRTY; + if (((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) && + (inode->i_state & I_DIRTY_TIME)) || + (inode->i_state & I_DIRTY_TIME_EXPIRED)) { + dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED; + trace_writeback_lazytime(inode); + } + inode->i_state &= ~dirty; /* * Paired with smp_mb() in __mark_inode_dirty(). This allows @@ -501,8 +525,10 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_unlock(&inode->i_lock); + if (dirty & I_DIRTY_TIME) + mark_inode_dirty_sync(inode); /* Don't write the inode if only I_DIRTY_PAGES was set */ - if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { + if (dirty & ~I_DIRTY_PAGES) { int err = write_inode(inode, wbc); if (ret == 0) ret = err; @@ -550,7 +576,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, * make sure inode is on some writeback list and leave it there unless * we have completely cleaned the inode. */ - if (!(inode->i_state & I_DIRTY) && + if (!(inode->i_state & I_DIRTY_ALL) && (wbc->sync_mode != WB_SYNC_ALL || !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK))) goto out; @@ -565,7 +591,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, * If inode is clean, remove it from writeback lists. Otherwise don't * touch it. See comment above for explanation. */ - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) list_del_init(&inode->i_wb_list); spin_unlock(&wb->list_lock); inode_sync_complete(inode); @@ -722,7 +748,7 @@ static long writeback_sb_inodes(struct super_block *sb, spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) wrote++; requeue_inode(inode, wb, &wbc); inode_sync_complete(inode); @@ -1160,16 +1186,20 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode) * page->mapping->host, so the page-dirtying time is recorded in the internal * blockdev inode. */ +#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC) void __mark_inode_dirty(struct inode *inode, int flags) { struct super_block *sb = inode->i_sb; struct backing_dev_info *bdi = NULL; + int dirtytime; + + trace_writeback_mark_inode_dirty(inode, flags); /* * Don't do this for I_DIRTY_PAGES - that doesn't actually * dirty the inode itself */ - if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { + if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_TIME)) { trace_writeback_dirty_inode_start(inode, flags); if (sb->s_op->dirty_inode) @@ -1177,6 +1207,9 @@ void __mark_inode_dirty(struct inode *inode, int flags) trace_writeback_dirty_inode(inode, flags); } + if (flags & I_DIRTY_INODE) + flags &= ~I_DIRTY_TIME; + dirtytime = flags & I_DIRTY_TIME; /* * Paired with smp_mb() in __writeback_single_inode() for the @@ -1184,16 +1217,21 @@ void __mark_inode_dirty(struct inode *inode, int flags) */ smp_mb(); - if ((inode->i_state & flags) == flags) + if (((inode->i_state & flags) == flags) || + (dirtytime && (inode->i_state & I_DIRTY_INODE))) return; if (unlikely(block_dump > 1)) block_dump___mark_inode_dirty(inode); spin_lock(&inode->i_lock); + if (dirtytime && (inode->i_state & I_DIRTY_INODE)) + goto out_unlock_inode; if ((inode->i_state & flags) != flags) { const int was_dirty = inode->i_state & I_DIRTY; + if (flags & I_DIRTY_INODE) + inode->i_state &= ~I_DIRTY_TIME; inode->i_state |= flags; /* @@ -1240,8 +1278,10 @@ void __mark_inode_dirty(struct inode *inode, int flags) } inode->dirtied_when = jiffies; - list_move(&inode->i_wb_list, &bdi->wb.b_dirty); + list_move(&inode->i_wb_list, dirtytime ? + &bdi->wb.b_dirty_time : &bdi->wb.b_dirty); spin_unlock(&bdi->wb.list_lock); + trace_writeback_dirty_inode_enqueue(inode); if (wakeup_bdi) bdi_wakeup_thread_delayed(bdi); diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 80dd44dca028..e584bf94983f 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -654,7 +654,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, { struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; - int sync_state = inode->i_state & I_DIRTY; + int sync_state = inode->i_state & I_DIRTY_ALL; struct gfs2_inode *ip = GFS2_I(inode); int ret = 0, ret1 = 0; @@ -667,7 +667,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, if (!gfs2_is_jdata(ip)) sync_state &= ~I_DIRTY_PAGES; if (datasync) - sync_state &= ~I_DIRTY_SYNC; + sync_state &= ~(I_DIRTY_SYNC | I_DIRTY_TIME); if (sync_state) { ret = sync_inode_metadata(inode, 1); diff --git a/fs/inode.c b/fs/inode.c index da55347c44a2..e906da6eb807 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -18,6 +18,7 @@ #include /* for inode_has_buffers */ #include #include +#include #include "internal.h" /* @@ -30,7 +31,7 @@ * inode_sb_list_lock protects: * sb->s_inodes, inode->i_sb_list * bdi->wb.list_lock protects: - * bdi->wb.b_{dirty,io,more_io}, inode->i_wb_list + * bdi->wb.b_{dirty,io,more_io,dirty_time}, inode->i_wb_list * inode_hash_lock protects: * inode_hashtable, inode->i_hash * @@ -414,7 +415,8 @@ static void inode_lru_list_add(struct inode *inode) */ void inode_add_lru(struct inode *inode) { - if (!(inode->i_state & (I_DIRTY | I_SYNC | I_FREEING | I_WILL_FREE)) && + if (!(inode->i_state & (I_DIRTY_ALL | I_SYNC | + I_FREEING | I_WILL_FREE)) && !atomic_read(&inode->i_count) && inode->i_sb->s_flags & MS_ACTIVE) inode_lru_list_add(inode); } @@ -645,7 +647,7 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) spin_unlock(&inode->i_lock); continue; } - if (inode->i_state & I_DIRTY && !kill_dirty) { + if (inode->i_state & I_DIRTY_ALL && !kill_dirty) { spin_unlock(&inode->i_lock); busy = 1; continue; @@ -1430,11 +1432,20 @@ static void iput_final(struct inode *inode) */ void iput(struct inode *inode) { - if (inode) { - BUG_ON(inode->i_state & I_CLEAR); - - if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) - iput_final(inode); + if (!inode) + return; + BUG_ON(inode->i_state & I_CLEAR); +retry: + if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) { + if (inode->i_nlink && (inode->i_state & I_DIRTY_TIME)) { + atomic_inc(&inode->i_count); + inode->i_state &= ~I_DIRTY_TIME; + spin_unlock(&inode->i_lock); + trace_writeback_lazytime_iput(inode); + mark_inode_dirty_sync(inode); + goto retry; + } + iput_final(inode); } } EXPORT_SYMBOL(iput); @@ -1493,14 +1504,9 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, return 0; } -/* - * This does the actual work of updating an inodes time or version. Must have - * had called mnt_want_write() before calling this. - */ -static int update_time(struct inode *inode, struct timespec *time, int flags) +int generic_update_time(struct inode *inode, struct timespec *time, int flags) { - if (inode->i_op->update_time) - return inode->i_op->update_time(inode, time, flags); + int iflags = I_DIRTY_TIME; if (flags & S_ATIME) inode->i_atime = *time; @@ -1510,9 +1516,27 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) inode->i_ctime = *time; if (flags & S_MTIME) inode->i_mtime = *time; - mark_inode_dirty_sync(inode); + + if (!(inode->i_sb->s_flags & MS_LAZYTIME) || (flags & S_VERSION)) + iflags |= I_DIRTY_SYNC; + __mark_inode_dirty(inode, iflags); return 0; } +EXPORT_SYMBOL(generic_update_time); + +/* + * This does the actual work of updating an inodes time or version. Must have + * had called mnt_want_write() before calling this. + */ +static int update_time(struct inode *inode, struct timespec *time, int flags) +{ + int (*update_time)(struct inode *, struct timespec *, int); + + update_time = inode->i_op->update_time ? inode->i_op->update_time : + generic_update_time; + + return update_time(inode, time, flags); +} /** * touch_atime - update the access time diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 33aa0cc1f8b8..10815f8dfd8b 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -39,7 +39,7 @@ int jfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) return rc; mutex_lock(&inode->i_mutex); - if (!(inode->i_state & I_DIRTY) || + if (!(inode->i_state & I_DIRTY_ALL) || (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) { /* Make sure committed changes hit the disk */ jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1); diff --git a/fs/libfs.c b/fs/libfs.c index 005843ce5dbd..b2ffdb045be4 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -948,7 +948,7 @@ int __generic_file_fsync(struct file *file, loff_t start, loff_t end, mutex_lock(&inode->i_mutex); ret = sync_mapping_buffers(inode->i_mapping); - if (!(inode->i_state & I_DIRTY)) + if (!(inode->i_state & I_DIRTY_ALL)) goto out; if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) goto out; diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index f68d0fe2abd4..d5f4bf33b7d1 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) { MS_SYNCHRONOUS, ",sync" }, { MS_DIRSYNC, ",dirsync" }, { MS_MANDLOCK, ",mand" }, + { MS_LAZYTIME, ",lazytime" }, { 0, NULL } }; const struct proc_fs_info *fs_infop; diff --git a/fs/sync.c b/fs/sync.c index cdaf1d275c0c..5a1333782e83 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -177,8 +177,16 @@ SYSCALL_DEFINE1(syncfs, int, fd) */ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) { + struct inode *inode = file->f_mapping->host; + if (!file->f_op->fsync) return -EINVAL; + if (!datasync && (inode->i_state & I_DIRTY_TIME)) { + spin_lock(&inode->i_lock); + inode->i_state &= ~I_DIRTY_TIME; + spin_unlock(&inode->i_lock); + mark_inode_dirty_sync(inode); + } return file->f_op->fsync(file, start, end, datasync); } EXPORT_SYMBOL(vfs_fsync_range); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 5da6012b7a14..4cdf7336f64a 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -55,6 +55,7 @@ struct bdi_writeback { struct list_head b_dirty; /* dirty inodes */ struct list_head b_io; /* parked for writeback */ struct list_head b_more_io; /* parked for more writeback */ + struct list_head b_dirty_time; /* time stamps are dirty */ spinlock_t list_lock; /* protects the b_* lists */ }; diff --git a/include/linux/fs.h b/include/linux/fs.h index ab1e4b339285..16c0394c45c2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1741,8 +1741,12 @@ struct super_operations { #define __I_DIO_WAKEUP 9 #define I_DIO_WAKEUP (1 << I_DIO_WAKEUP) #define I_LINKABLE (1 << 10) +#define I_DIRTY_TIME (1 << 11) +#define __I_DIRTY_TIME_EXPIRED 12 +#define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED) #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) +#define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME) extern void __mark_inode_dirty(struct inode *, int); static inline void mark_inode_dirty(struct inode *inode) @@ -1908,6 +1912,7 @@ extern int current_umask(void); extern void ihold(struct inode * inode); extern void iput(struct inode *); +extern int generic_update_time(struct inode *, struct timespec *, int); static inline struct inode *file_inode(const struct file *f) { diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index cee02d65ab3f..5ecb4c234625 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -18,6 +18,8 @@ {I_FREEING, "I_FREEING"}, \ {I_CLEAR, "I_CLEAR"}, \ {I_SYNC, "I_SYNC"}, \ + {I_DIRTY_TIME, "I_DIRTY_TIME"}, \ + {I_DIRTY_TIME_EXPIRED, "I_DIRTY_TIME_EXPIRED"}, \ {I_REFERENCED, "I_REFERENCED"} \ ) @@ -68,6 +70,7 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, TP_STRUCT__entry ( __array(char, name, 32) __field(unsigned long, ino) + __field(unsigned long, state) __field(unsigned long, flags) ), @@ -78,16 +81,25 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, strncpy(__entry->name, bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32); __entry->ino = inode->i_ino; + __entry->state = inode->i_state; __entry->flags = flags; ), - TP_printk("bdi %s: ino=%lu flags=%s", + TP_printk("bdi %s: ino=%lu state=%s flags=%s", __entry->name, __entry->ino, + show_inode_state(__entry->state), show_inode_state(__entry->flags) ) ); +DEFINE_EVENT(writeback_dirty_inode_template, writeback_mark_inode_dirty, + + TP_PROTO(struct inode *inode, int flags), + + TP_ARGS(inode, flags) +); + DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode_start, TP_PROTO(struct inode *inode, int flags), @@ -598,6 +610,52 @@ DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode, TP_ARGS(inode, wbc, nr_to_write) ); +DECLARE_EVENT_CLASS(writeback_lazytime_template, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field(unsigned long, ino ) + __field(unsigned long, state ) + __field( __u16, mode ) + __field(unsigned long, dirtied_when ) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->state = inode->i_state; + __entry->mode = inode->i_mode; + __entry->dirtied_when = inode->dirtied_when; + ), + + TP_printk("dev %d,%d ino %lu dirtied %lu state %s mode 0%o", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, __entry->dirtied_when, + show_inode_state(__entry->state), __entry->mode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime_iput, + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(writeback_lazytime_template, writeback_dirty_inode_enqueue, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + #endif /* _TRACE_WRITEBACK_H */ /* This part must be outside protection */ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index ee21fa9efe8a..60d27496c328 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -90,6 +90,7 @@ struct inodes_stat_t { #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ #define MS_I_VERSION (1<<23) /* Update inode I_version field */ #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */ /* These sb flags are internal to the kernel */ #define MS_NOSEC (1<<28) @@ -100,7 +101,8 @@ struct inodes_stat_t { /* * Superblock flags that can be altered by MS_REMOUNT */ -#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION) +#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\ + MS_LAZYTIME) /* * Old magic mount flag and mask diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 0ae0df55000b..915feea94c66 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -69,10 +69,10 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) unsigned long background_thresh; unsigned long dirty_thresh; unsigned long bdi_thresh; - unsigned long nr_dirty, nr_io, nr_more_io; + unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time; struct inode *inode; - nr_dirty = nr_io = nr_more_io = 0; + nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0; spin_lock(&wb->list_lock); list_for_each_entry(inode, &wb->b_dirty, i_wb_list) nr_dirty++; @@ -80,6 +80,9 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) nr_io++; list_for_each_entry(inode, &wb->b_more_io, i_wb_list) nr_more_io++; + list_for_each_entry(inode, &wb->b_dirty_time, i_wb_list) + if (inode->i_state & I_DIRTY_TIME) + nr_dirty_time++; spin_unlock(&wb->list_lock); global_dirty_limits(&background_thresh, &dirty_thresh); @@ -98,6 +101,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) "b_dirty: %10lu\n" "b_io: %10lu\n" "b_more_io: %10lu\n" + "b_dirty_time: %10lu\n" "bdi_list: %10u\n" "state: %10lx\n", (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), @@ -111,6 +115,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) nr_dirty, nr_io, nr_more_io, + nr_dirty_time, !list_empty(&bdi->bdi_list), bdi->state); #undef K @@ -418,6 +423,7 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) INIT_LIST_HEAD(&wb->b_dirty); INIT_LIST_HEAD(&wb->b_io); INIT_LIST_HEAD(&wb->b_more_io); + INIT_LIST_HEAD(&wb->b_dirty_time); spin_lock_init(&wb->list_lock); INIT_DELAYED_WORK(&wb->dwork, bdi_writeback_workfn); } -- GitLab From deabce183a24684a5aaa8a2f4fb33409004ebb5b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 21:47:24 -0700 Subject: [PATCH 0944/5498] f2fs: add lazytime mount option commit 6d94c74ab85fe70dc1ac29b1ffc55cf23b3cf3f9 upstream. This patch adds lazytime support. Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ad6b805190bb..89f30c620416 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -109,6 +109,8 @@ enum { Opt_mode, Opt_io_size_bits, Opt_fault_injection, + Opt_lazytime, + Opt_nolazytime, Opt_quota, Opt_noquota, Opt_usrquota, @@ -156,6 +158,8 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, + {Opt_lazytime, "lazytime"}, + {Opt_nolazytime, "nolazytime"}, {Opt_quota, "quota"}, {Opt_noquota, "noquota"}, {Opt_usrquota, "usrquota"}, @@ -512,6 +516,12 @@ static int parse_options(struct super_block *sb, char *options) "FAULT_INJECTION was not selected"); #endif break; + case Opt_lazytime: + sb->s_flags |= MS_LAZYTIME; + break; + case Opt_nolazytime: + sb->s_flags &= ~MS_LAZYTIME; + break; #ifdef CONFIG_QUOTA case Opt_quota: case Opt_usrquota: @@ -743,6 +753,9 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) inode->i_ino == F2FS_META_INO(sbi)) return; + if (flags == I_DIRTY_TIME) + return; + if (is_inode_flag_set(inode, FI_AUTO_RECOVER)) clear_inode_flag(inode, FI_AUTO_RECOVER); @@ -1118,6 +1131,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, INLINE_DENTRY); set_opt(sbi, EXTENT_CACHE); set_opt(sbi, NOHEAP); + sbi->sb->s_flags |= MS_LAZYTIME; set_opt(sbi, FLUSH_MERGE); if (f2fs_sb_mounted_blkzoned(sbi->sb)) { set_opt_mode(sbi, F2FS_MOUNT_LFS); -- GitLab From 5f80a26ba8747e2869d34112f36b4027fba54c09 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 19 Sep 2017 17:10:22 -0700 Subject: [PATCH 0945/5498] ANDROID: f2fs: correct inconsistent cherry-pick This patch corrects the below cherry-picked patch from mainline. commit ca1d19b699aad7a94 ("FROMLIST: f2fs: sanity check checkpoint segno and blkoff") Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 89f30c620416..80bb604b4f54 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1892,8 +1892,8 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) unsigned int total, fsmeta; struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - unsigned int main_segs, blocks_per_seg; unsigned int ovp_segments, reserved_segments; + unsigned int main_segs, blocks_per_seg; int i; total = le32_to_cpu(raw_super->segment_count); @@ -1906,30 +1906,28 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi) if (unlikely(fsmeta >= total)) return 1; - main_segs = le32_to_cpu(sbi->raw_super->segment_count_main); + ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); + reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); + + if (unlikely(fsmeta < F2FS_MIN_SEGMENTS || + ovp_segments == 0 || reserved_segments == 0)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Wrong layout: check mkfs.f2fs version"); + return 1; + } + + main_segs = le32_to_cpu(raw_super->segment_count_main); blocks_per_seg = sbi->blocks_per_seg; for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) { if (le32_to_cpu(ckpt->cur_node_segno[i]) >= main_segs || - le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg) { + le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg) return 1; - } } for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) { if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs || - le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg) { + le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg) return 1; - } - } - - ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); - reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); - - if (unlikely(fsmeta < F2FS_MIN_SEGMENTS || - ovp_segments == 0 || reserved_segments == 0)) { - f2fs_msg(sbi->sb, KERN_ERR, - "Wrong layout: check mkfs.f2fs version"); - return 1; } if (unlikely(f2fs_cp_error(sbi))) { -- GitLab From 4941b289f6190bb15b33be12793e5686b1e60d29 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Mon, 18 Sep 2017 17:42:51 +0530 Subject: [PATCH 0946/5498] USB: phy: Disable IRQs before resetting clocks As a part of msm_otg_reset, we go for disabling and enabling of clocks. There is a chance that IRQ may get fired when the clocks are disabled. This may lead to NOC error because of unclocked access when we try to access some memory region as a part of IRQ handling. Fix this by disabling IRQs before going for clock reset and enable IRQs later. Change-Id: I8ccec08c9c520f0573a2dbe1a9f8e780b3516e85 Signed-off-by: Ajay Agarwal --- drivers/usb/phy/phy-msm-usb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 55e83e1c8271..3ada46301ff5 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -746,12 +746,24 @@ static int msm_otg_reset(struct usb_phy *phy) } motg->reset_counter++; + disable_irq(motg->irq); + if (motg->phy_irq) + disable_irq(motg->phy_irq); + ret = msm_otg_phy_reset(motg); if (ret) { dev_err(phy->dev, "phy_reset failed\n"); + if (motg->phy_irq) + enable_irq(motg->phy_irq); + + enable_irq(motg->irq); return ret; } + if (motg->phy_irq) + enable_irq(motg->phy_irq); + + enable_irq(motg->irq); ret = msm_otg_link_reset(motg); if (ret) { dev_err(phy->dev, "link reset failed\n"); -- GitLab From 8618cb6db84831f75e1fba5422b1014faccc01ed Mon Sep 17 00:00:00 2001 From: Patrick Daly Date: Tue, 1 Aug 2017 19:56:52 -0700 Subject: [PATCH 0947/5498] ion: Convert allocation counters to long On 64bit platforms, the amount of memory allocated by ion can exceed 32 bits. Therefore the counters which track this information must be 64 bits as well. Change-Id: I81daf2cec78de5787279daaf98264c5462ba6645 Signed-off-by: Patrick Daly Signed-off-by: Prakash Gupta --- drivers/staging/android/ion/ion.c | 16 ++++++++-------- drivers/staging/android/ion/ion_priv.h | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 720524507982..7860ded7c7a9 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -264,7 +264,7 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, mutex_lock(&dev->buffer_lock); ion_buffer_add(dev, buffer); mutex_unlock(&dev->buffer_lock); - atomic_add(len, &heap->total_allocated); + atomic_long_add(len, &heap->total_allocated); return buffer; err: @@ -282,7 +282,7 @@ void ion_buffer_destroy(struct ion_buffer *buffer) buffer->heap->ops->unmap_kernel(buffer->heap, buffer); buffer->heap->ops->unmap_dma(buffer->heap, buffer); - atomic_sub(buffer->size, &buffer->heap->total_allocated); + atomic_long_sub(buffer->size, &buffer->heap->total_allocated); buffer->heap->ops->free(buffer); if (buffer->pages) vfree(buffer->pages); @@ -321,7 +321,7 @@ static void ion_buffer_add_to_handle(struct ion_buffer *buffer) { mutex_lock(&buffer->lock); if (buffer->handle_count == 0) - atomic_add(buffer->size, &buffer->heap->total_handles); + atomic_long_add(buffer->size, &buffer->heap->total_handles); buffer->handle_count++; mutex_unlock(&buffer->lock); @@ -347,7 +347,7 @@ static void ion_buffer_remove_from_handle(struct ion_buffer *buffer) task = current->group_leader; get_task_comm(buffer->task_comm, task); buffer->pid = task_pid_nr(task); - atomic_sub(buffer->size, &buffer->heap->total_handles); + atomic_long_sub(buffer->size, &buffer->heap->total_handles); } mutex_unlock(&buffer->lock); } @@ -1906,10 +1906,10 @@ void show_ion_usage(struct ion_device *dev) "Total orphaned size"); pr_info("---------------------------------\n"); plist_for_each_entry(heap, &dev->heaps, node) { - pr_info("%16.s 0x%16.x 0x%16.x\n", - heap->name, atomic_read(&heap->total_allocated), - atomic_read(&heap->total_allocated) - - atomic_read(&heap->total_handles)); + pr_info("%16.s 0x%16.lx 0x%16.lx\n", + heap->name, atomic_long_read(&heap->total_allocated), + atomic_long_read(&heap->total_allocated) - + atomic_long_read(&heap->total_handles)); if (heap->debug_show) heap->debug_show(heap, NULL, 0); diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h index c681c1ac95c1..1313f838ba38 100644 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -2,7 +2,7 @@ * drivers/staging/android/ion/ion_priv.h * * Copyright (C) 2011 Google, Inc. - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -200,8 +200,8 @@ struct ion_heap { struct task_struct *task; int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *); - atomic_t total_allocated; - atomic_t total_handles; + atomic_long_t total_allocated; + atomic_long_t total_handles; }; /** -- GitLab From 8c4d8a30976ba2959b6cceb43c985c031f648c91 Mon Sep 17 00:00:00 2001 From: smanag Date: Tue, 19 Sep 2017 11:12:09 +0530 Subject: [PATCH 0948/5498] ASoC: wsa881x-analog: Handle update of WSA probe count wsa881x_probing_count is checked in machine driver of 8953/37/17 for sound card register to proceed. Machine probe was getting deferred continuously if targets do not have wsa speakers as the wsa probe count was not updated. Change is to update wsa probe count in this case. Change-Id: Ib86f70442daae5066e1a8082b65d377c3de667fb Signed-off-by: smanag --- sound/soc/codecs/wsa881x-analog.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wsa881x-analog.c b/sound/soc/codecs/wsa881x-analog.c index ba26579d71d5..8f9256a9aebc 100644 --- a/sound/soc/codecs/wsa881x-analog.c +++ b/sound/soc/codecs/wsa881x-analog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1258,8 +1258,10 @@ static int wsa881x_i2c_probe(struct i2c_client *client, if ((client->addr == WSA881X_I2C_SPK0_SLAVE1_ADDR || client->addr == WSA881X_I2C_SPK1_SLAVE1_ADDR) && - (pdata->status == WSA881X_STATUS_PROBING)) + (pdata->status == WSA881X_STATUS_PROBING)) { + wsa881x_probing_count++; return ret; + } if (pdata->status == WSA881X_STATUS_I2C) { dev_dbg(&client->dev, "%s:probe for other slaves\n" @@ -1342,6 +1344,7 @@ static int wsa881x_i2c_probe(struct i2c_client *client, dev_err(&client->dev, "failed to ping wsa with addr:%x, ret = %d\n", client->addr, ret); + wsa881x_probing_count++; goto err1; } pdata->version = wsa881x_i2c_read_device(pdata, -- GitLab From 3b58b010a765968902bb3e3fca7888184cef8788 Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Tue, 19 Sep 2017 17:40:15 +0530 Subject: [PATCH 0949/5498] soc: qcom: Collect ramdump before reset if BG was crashed during cold boot When MSM is doing cold boot and tries to reset BG, it may get failure due to BG being already crashed. In such scenarios need to collect BG ramdump before continuing with BG reset sequence. Change-Id: Ibc55f821ee8af60808eaec41d1f1dffb719d28cd Signed-off-by: Avaneesh Kumar Dwivedi --- drivers/soc/qcom/pil_bg_intf.h | 1 + drivers/soc/qcom/subsys-pil-bg.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/soc/qcom/pil_bg_intf.h b/drivers/soc/qcom/pil_bg_intf.h index cc9219b58b13..ba25af8f5a8e 100644 --- a/drivers/soc/qcom/pil_bg_intf.h +++ b/drivers/soc/qcom/pil_bg_intf.h @@ -22,6 +22,7 @@ enum bg_tz_commands { BGPIL_RAMDUMP, BGPIL_IMAGE_LOAD, BGPIL_AUTH_MDT, + BGPIL_DLOAD_CONT, }; /* tzapp bg request.*/ diff --git a/drivers/soc/qcom/subsys-pil-bg.c b/drivers/soc/qcom/subsys-pil-bg.c index 8816c47ef146..e09801ba2c60 100644 --- a/drivers/soc/qcom/subsys-pil-bg.c +++ b/drivers/soc/qcom/subsys-pil-bg.c @@ -36,6 +36,7 @@ #define desc_to_data(d) container_of(d, struct pil_bg_data, desc) #define subsys_to_data(d) container_of(d, struct pil_bg_data, subsys_desc) #define BG_RAMDUMP_SZ 0x00102000 +#define BG_CRASH_IN_TWM 2 /** * struct pil_bg_data * @qseecom_handle: handle of TZ app @@ -264,6 +265,7 @@ static int bg_powerup(const struct subsys_desc *subsys) return ret; } enable_irq(bg_data->status_irq); + enable_irq(bg_data->errfatal_irq); ret = wait_for_err_ready(bg_data); if (ret) { dev_err(bg_data->desc.dev, @@ -375,6 +377,12 @@ static int bg_auth_and_xfer(struct pil_desc *pil) bg_tz_req.size_fw = bg_data->size_fw; ret = bgpil_tzapp_comm(bg_data, &bg_tz_req); + if (bg_data->cmd_status == BG_CRASH_IN_TWM) { + /* Do ramdump and resend boot cmd */ + bg_data->subsys_desc.ramdump(true, &bg_data->subsys_desc); + bg_tz_req.tzapp_bg_cmd = BGPIL_DLOAD_CONT; + ret = bgpil_tzapp_comm(bg_data, &bg_tz_req); + } if (ret || bg_data->cmd_status) { dev_err(pil->dev, "%s: BGPIL_IMAGE_LOAD qseecom call failed\n", -- GitLab From accb0913057798fa838d062d45d041e40f149925 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Wed, 20 Sep 2017 16:22:41 +0530 Subject: [PATCH 0950/5498] input: synaptics_dsx_2.6: correct suspend path for Synaptics V2.6 driver Correct the suspend path for Synaptics V2.6 driver by adding the brackets in the error path while calling the pinctrl call. This change will make the driver not go into undesirable path and complete the function calls necessary for suspend routine. Change-Id: I327eff97f584cefb2c957c43e9820f3a37d4751c Signed-off-by: Shantanu Jain --- .../input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c index f50371c833c0..5e2b8ce5493d 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c @@ -4577,9 +4577,10 @@ static int synaptics_rmi4_suspend(struct device *dev) if (rmi4_data->ts_pinctrl) { retval = pinctrl_select_state(rmi4_data->ts_pinctrl, rmi4_data->pinctrl_state_suspend); - if (retval < 0) + if (retval < 0) { dev_err(dev, "Cannot get idle pinctrl state\n"); goto err_pinctrl; + } } exit: mutex_lock(&exp_data.mutex); -- GitLab From 268374ca0a12948f674ed561a881d8ae778b7300 Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Thu, 24 Aug 2017 20:20:56 +0530 Subject: [PATCH 0951/5498] msm: kgsl: Add debug log in adreno_of_get_pwrlevels() Add debug log to dump the GPU speed bin value incase probe fails due to efused bin value mismatch with speed bin value. Change-Id: I329523f8dbb82272418981a54a1c2e6cf5e90b85 Signed-off-by: Hareesh Gundu --- drivers/gpu/msm/adreno.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 90a393a48f3e..56ff5877db0f 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -806,13 +806,13 @@ static int adreno_of_get_pwrlevels(struct adreno_device *adreno_dev, struct device_node *parent) { struct device_node *node, *child; + unsigned int bin = 0; node = of_find_node_by_name(parent, "qcom,gpu-pwrlevel-bins"); if (node == NULL) return adreno_of_get_legacy_pwrlevels(adreno_dev, parent); for_each_child_of_node(node, child) { - unsigned int bin; if (of_property_read_u32(child, "qcom,speed-bin", &bin)) continue; @@ -828,6 +828,8 @@ static int adreno_of_get_pwrlevels(struct adreno_device *adreno_dev, } } + KGSL_CORE_ERR("GPU speed_bin:%d mismatch for efused bin:%d\n", + adreno_dev->speed_bin, bin); return -ENODEV; } -- GitLab From f8b6c56e558bd504ee8cbfe56f805b83023f6c84 Mon Sep 17 00:00:00 2001 From: Shihuan Liu Date: Thu, 14 Sep 2017 17:03:09 -0700 Subject: [PATCH 0952/5498] msm: ipa: add support for WDI 3.0 in IPA_v2 Add support for WDI 3.0 in IPA v2 driver. Change-Id: I768af53561997a136460fecf2fe66fe32adc88bf Acked-by: Shihuan Liu Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v2/Makefile | 3 +- drivers/platform/msm/ipa/ipa_v2/ipa_i.h | 6 + .../msm/ipa/ipa_v2/ipa_uc_offload_i.h | 64 ++- drivers/platform/msm/ipa/ipa_v2/ipa_utils.c | 4 + drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c | 406 ++++++++++++++++++ 5 files changed, 480 insertions(+), 3 deletions(-) create mode 100644 drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c diff --git a/drivers/platform/msm/ipa/ipa_v2/Makefile b/drivers/platform/msm/ipa/ipa_v2/Makefile index 69b8a4c94461..fb039709261e 100644 --- a/drivers/platform/msm/ipa/ipa_v2/Makefile +++ b/drivers/platform/msm/ipa/ipa_v2/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_IPA) += ipat.o ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \ ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \ - ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o + ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \ + ipa_wdi3_i.o obj-$(CONFIG_RMNET_IPA) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index 2f60bb587eae..fe2695aee20c 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -1558,6 +1558,12 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl); int ipa2_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv); void ipa2_ntn_uc_dereg_rdyCB(void); +int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, + struct ipa_wdi3_conn_out_params *out); +int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); +int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); +int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); + /* * To retrieve doorbell physical address of * wlan pipes diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h index 75cc897c378e..fb4986dd5e1c 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h @@ -26,6 +26,9 @@ #define IPA_NTN_TX_DIR 1 #define IPA_NTN_RX_DIR 2 +#define IPA_WDI3_TX_DIR 1 +#define IPA_WDI3_RX_DIR 2 + /** * @brief Enum value determined based on the feature it * corresponds to @@ -45,16 +48,20 @@ * enum ipa_hw_features - Values that represent the features supported in IPA HW * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW + * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW * @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW * @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW + * @IPA_HW_FEATURE_WDI3 : Feature related to WDI operation in IPA HW */ enum ipa_hw_features { IPA_HW_FEATURE_COMMON = 0x0, IPA_HW_FEATURE_MHI = 0x1, + IPA_HW_FEATURE_POWER_COLLAPSE = 0x2, IPA_HW_FEATURE_WDI = 0x3, IPA_HW_FEATURE_NTN = 0x4, IPA_HW_FEATURE_OFFLOAD = 0x5, + IPA_HW_FEATURE_WDI3 = 0x6, IPA_HW_FEATURE_MAX = IPA_HW_NUM_FEATURES }; @@ -274,6 +281,33 @@ struct IpaHwNtnSetUpCmdData_t { } __packed; +struct IpaHwWdi3SetUpCmdData_t { + u32 transfer_ring_base_pa; + u32 transfer_ring_base_pa_hi; + + u32 transfer_ring_size; + + u32 transfer_ring_doorbell_pa; + u32 transfer_ring_doorbell_pa_hi; + + u32 event_ring_base_pa; + u32 event_ring_base_pa_hi; + + u32 event_ring_size; + + u32 event_ring_doorbell_pa; + u32 event_ring_doorbell_pa_hi; + + u16 num_pkt_buffers; + u8 ipa_pipe_number; + u8 dir; + + u16 pkt_offset; + u16 reserved0; + + u32 desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE]; +} __packed; + /** * struct IpaHwNtnCommonChCmdData_t - Structure holding the * parameters for Ntn Tear down command data params @@ -288,6 +322,13 @@ union IpaHwNtnCommonChCmdData_t { uint32_t raw32b; } __packed; +union IpaHwWdi3CommonChCmdData_t { + struct IpaHwWdi3CommonChCmdParams_t { + u32 ipa_pipe_number :8; + u32 reserved :24; + } __packed params; + u32 raw32b; +} __packed; /** * struct IpaHwNTNErrorEventData_t - Structure holding the @@ -405,13 +446,30 @@ struct IpaHwStatsNTNInfoData_t { * the offload commands from CPU * @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up * Offload protocol's Tx/Rx Path - * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down + * @IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN : Command to tear down + * Offload protocol's Tx/ Rx Path + * @IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE : Command to enable + * Offload protocol's Tx/Rx Path + * @IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE : Command to disable + * Offload protocol's Tx/ Rx Path + * @IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND : Command to suspend + * Offload protocol's Tx/Rx Path + * @IPA_CPU_2_HW_CMD_OFFLOAD_RESUME : Command to resume * Offload protocol's Tx/ Rx Path */ enum ipa_cpu_2_hw_offload_commands { IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP = FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1), - IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, + IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2), + IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3), + IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4), + IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5), + IPA_CPU_2_HW_CMD_OFFLOAD_RESUME = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6), }; @@ -519,6 +577,7 @@ enum ipa_hw_2_cpu_cmd_resp_status { */ union IpaHwSetUpCmd { struct IpaHwNtnSetUpCmdData_t NtnSetupCh_params; + struct IpaHwWdi3SetUpCmdData_t Wdi3SetupCh_params; } __packed; /** @@ -539,6 +598,7 @@ struct IpaHwOffloadSetUpCmdData_t { */ union IpaHwCommonChCmd { union IpaHwNtnCommonChCmdData_t NtnCommonCh_params; + union IpaHwWdi3CommonChCmdData_t Wdi3CommonCh_params; } __packed; struct IpaHwOffloadCommonChCmdData_t { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 87128064538f..6783d20b06cb 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -5207,6 +5207,10 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_get_pdev = ipa2_get_pdev; api_ctrl->ipa_ntn_uc_reg_rdyCB = ipa2_ntn_uc_reg_rdyCB; api_ctrl->ipa_ntn_uc_dereg_rdyCB = ipa2_ntn_uc_dereg_rdyCB; + api_ctrl->ipa_conn_wdi3_pipes = ipa2_conn_wdi3_pipes; + api_ctrl->ipa_disconn_wdi3_pipes = ipa2_disconn_wdi3_pipes; + api_ctrl->ipa_enable_wdi3_pipes = ipa2_enable_wdi3_pipes; + api_ctrl->ipa_disable_wdi3_pipes = ipa2_disable_wdi3_pipes; return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c b/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c new file mode 100644 index 000000000000..fe527ffadd9b --- /dev/null +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c @@ -0,0 +1,406 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include "ipa_i.h" +#include "ipa_uc_offload_i.h" +#include + +#define IPA_HW_WDI3_RX_MBOX_START_INDEX 48 +#define IPA_HW_WDI3_TX_MBOX_START_INDEX 50 + +static int ipa_send_wdi3_setup_pipe_cmd( + struct ipa_wdi3_setup_info *info, u8 dir) +{ + int ipa_ep_idx; + int result = 0; + struct ipa_mem_buffer cmd; + struct IpaHwWdi3SetUpCmdData_t *wdi3_params; + struct IpaHwOffloadSetUpCmdData_t *cmd_data; + + if (info == NULL) { + IPAERR("invalid input\n"); + return -EINVAL; + } + + ipa_ep_idx = ipa_get_ep_mapping(info->client); + IPAERR("ep number: %d\n", ipa_ep_idx); + if (ipa_ep_idx == -1) { + IPAERR("fail to get ep idx.\n"); + return -EFAULT; + } + + IPAERR("client=%d ep=%d\n", info->client, ipa_ep_idx); + IPAERR("ring_base_pa = 0x%pad\n", &info->transfer_ring_base_pa); + IPAERR("ring_size = %hu\n", info->transfer_ring_size); + IPAERR("ring_db_pa = 0x%pad\n", &info->transfer_ring_doorbell_pa); + IPAERR("evt_ring_base_pa = 0x%pad\n", &info->event_ring_base_pa); + IPAERR("evt_ring_size = %hu\n", info->event_ring_size); + IPAERR("evt_ring_db_pa = 0x%pad\n", &info->event_ring_doorbell_pa); + IPAERR("num_pkt_buffers = %hu\n", info->num_pkt_buffers); + IPAERR("pkt_offset = %d.\n", info->pkt_offset); + + cmd.size = sizeof(*cmd_data); + cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size, + &cmd.phys_base, GFP_KERNEL); + if (cmd.base == NULL) { + IPAERR("fail to get DMA memory.\n"); + return -ENOMEM; + } + IPAERR("suceeded in allocating memory.\n"); + + cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base; + cmd_data->protocol = IPA_HW_FEATURE_WDI3; + + wdi3_params = &cmd_data->SetupCh_params.Wdi3SetupCh_params; + wdi3_params->transfer_ring_base_pa = (u32)info->transfer_ring_base_pa; + wdi3_params->transfer_ring_base_pa_hi = + (u32)((u64)info->transfer_ring_base_pa >> 32); + wdi3_params->transfer_ring_size = info->transfer_ring_size; + wdi3_params->transfer_ring_doorbell_pa = + (u32)info->transfer_ring_doorbell_pa; + wdi3_params->transfer_ring_doorbell_pa_hi = + (u32)((u64)info->transfer_ring_doorbell_pa >> 32); + wdi3_params->event_ring_base_pa = (u32)info->event_ring_base_pa; + wdi3_params->event_ring_base_pa_hi = + (u32)((u64)info->event_ring_base_pa >> 32); + wdi3_params->event_ring_size = info->event_ring_size; + wdi3_params->event_ring_doorbell_pa = + (u32)info->event_ring_doorbell_pa; + wdi3_params->event_ring_doorbell_pa_hi = + (u32)((u64)info->event_ring_doorbell_pa >> 32); + wdi3_params->num_pkt_buffers = info->num_pkt_buffers; + wdi3_params->ipa_pipe_number = ipa_ep_idx; + wdi3_params->dir = dir; + wdi3_params->pkt_offset = info->pkt_offset; + memcpy(wdi3_params->desc_format_template, info->desc_format_template, + sizeof(wdi3_params->desc_format_template)); + IPAERR("suceeded in populating the command memory.\n"); + + result = ipa_uc_send_cmd((u32)(cmd.phys_base), + IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP, + IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, + false, 10*HZ); + if (result) { + IPAERR("uc setup channel cmd failed: %d\n", result); + result = -EFAULT; + } + + dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); + IPAERR("suceeded in freeing memory.\n"); + return result; +} + +int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in, + struct ipa_wdi3_conn_out_params *out) +{ + struct ipa_ep_context *ep_rx; + struct ipa_ep_context *ep_tx; + int ipa_ep_idx_rx; + int ipa_ep_idx_tx; + int result = 0; + + if (in == NULL || out == NULL) { + IPAERR("invalid input\n"); + return -EINVAL; + } + + ipa_ep_idx_rx = ipa_get_ep_mapping(in->rx.client); + ipa_ep_idx_tx = ipa_get_ep_mapping(in->tx.client); + if (ipa_ep_idx_rx == -1 || ipa_ep_idx_tx == -1) { + IPAERR("fail to alloc EP.\n"); + return -EFAULT; + } + + ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; + ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; + + if (ep_rx->valid || ep_tx->valid) { + IPAERR("EP already allocated.\n"); + return -EFAULT; + } + + memset(ep_rx, 0, offsetof(struct ipa_ep_context, sys)); + memset(ep_tx, 0, offsetof(struct ipa_ep_context, sys)); + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + /* setup rx ep cfg */ + ep_rx->valid = 1; + ep_rx->client = in->rx.client; + result = ipa_disable_data_path(ipa_ep_idx_rx); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_rx); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + ep_rx->client_notify = in->notify; + ep_rx->priv = in->priv; + + memcpy(&ep_rx->cfg, &in->rx.ipa_ep_cfg, sizeof(ep_rx->cfg)); + + if (ipa_cfg_ep(ipa_ep_idx_rx, &ep_rx->cfg)) { + IPAERR("fail to setup rx pipe cfg\n"); + result = -EFAULT; + goto fail; + } + IPAERR("configured RX EP.\n"); + + if (ipa_send_wdi3_setup_pipe_cmd(&in->rx, IPA_WDI3_RX_DIR)) { + IPAERR("fail to send cmd to uc for rx pipe\n"); + result = -EFAULT; + goto fail; + } + IPAERR("rx pipe was setup.\n"); + + ipa_install_dflt_flt_rules(ipa_ep_idx_rx); + out->rx_uc_db_pa = ipa_ctx->ipa_wrapper_base + + IPA_REG_BASE_OFST_v2_5 + + IPA_UC_MAILBOX_m_n_OFFS_v2_5( + IPA_HW_WDI3_RX_MBOX_START_INDEX/32, + IPA_HW_WDI3_RX_MBOX_START_INDEX % 32); + IPADBG("client %d (ep: %d) connected\n", in->rx.client, + ipa_ep_idx_rx); + + /* setup dl ep cfg */ + ep_tx->valid = 1; + ep_tx->client = in->tx.client; + result = ipa_disable_data_path(ipa_ep_idx_tx); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_tx); + result = -EFAULT; + goto fail; + } + + memcpy(&ep_tx->cfg, &in->tx.ipa_ep_cfg, sizeof(ep_tx->cfg)); + + if (ipa_cfg_ep(ipa_ep_idx_tx, &ep_tx->cfg)) { + IPAERR("fail to setup tx pipe cfg\n"); + result = -EFAULT; + goto fail; + } + IPAERR("configured TX EP in DMA mode.\n"); + + if (ipa_send_wdi3_setup_pipe_cmd(&in->tx, IPA_WDI3_TX_DIR)) { + IPAERR("fail to send cmd to uc for tx pipe\n"); + result = -EFAULT; + goto fail; + } + IPAERR("tx pipe was setup.\n"); + + out->tx_uc_db_pa = ipa_ctx->ipa_wrapper_base + + IPA_REG_BASE_OFST_v2_5 + + IPA_UC_MAILBOX_m_n_OFFS_v2_5( + IPA_HW_WDI3_TX_MBOX_START_INDEX/32, + IPA_HW_WDI3_TX_MBOX_START_INDEX % 32); + out->tx_uc_db_va = ioremap(out->tx_uc_db_pa, 4); + IPADBG("client %d (ep: %d) connected\n", in->tx.client, + ipa_ep_idx_tx); + +fail: + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return result; +} + +static int ipa_send_wdi3_common_ch_cmd(int ipa_ep_idx, int command) +{ + struct ipa_mem_buffer cmd; + struct IpaHwOffloadCommonChCmdData_t *cmd_data; + union IpaHwWdi3CommonChCmdData_t *wdi3; + int result = 0; + + cmd.size = sizeof(*cmd_data); + cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size, + &cmd.phys_base, GFP_KERNEL); + if (cmd.base == NULL) { + IPAERR("fail to get DMA memory.\n"); + return -ENOMEM; + } + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + /* enable the TX pipe */ + cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base; + cmd_data->protocol = IPA_HW_FEATURE_WDI3; + + wdi3 = &cmd_data->CommonCh_params.Wdi3CommonCh_params; + wdi3->params.ipa_pipe_number = ipa_ep_idx; + IPAERR("cmd: %d ep_idx: %d\n", command, ipa_ep_idx); + result = ipa_uc_send_cmd((u32)(cmd.phys_base), command, + IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, + false, 10*HZ); + if (result) { + result = -EFAULT; + goto fail; + } + +fail: + dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return result; +} + +int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) +{ + struct ipa_ep_context *ep_tx, *ep_rx; + int result = 0; + + IPADBG("ep_tx = %d\n", ipa_ep_idx_tx); + IPADBG("ep_rx = %d\n", ipa_ep_idx_rx); + + ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; + ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; + + /* tear down tx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, + IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) { + IPAERR("fail to tear down tx pipe\n"); + result = -EFAULT; + goto fail; + } + ipa_disable_data_path(ipa_ep_idx_tx); + memset(ep_tx, 0, sizeof(struct ipa_ep_context)); + IPADBG("tx client (ep: %d) disconnected\n", ipa_ep_idx_tx); + + /* tear down rx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, + IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) { + IPAERR("fail to tear down rx pipe\n"); + result = -EFAULT; + goto fail; + } + ipa_disable_data_path(ipa_ep_idx_rx); + ipa_delete_dflt_flt_rules(ipa_ep_idx_rx); + memset(ep_rx, 0, sizeof(struct ipa_ep_context)); + IPADBG("rx client (ep: %d) disconnected\n", ipa_ep_idx_rx); + +fail: + return result; +} + +int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) +{ + struct ipa_ep_context *ep_tx, *ep_rx; + int result = 0; + + IPAERR("ep_tx = %d\n", ipa_ep_idx_tx); + IPAERR("ep_rx = %d\n", ipa_ep_idx_rx); + + ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; + ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; + + /* enable tx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, + IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) { + IPAERR("fail to enable tx pipe\n"); + BUG(); + result = -EFAULT; + goto fail; + } + + /* resume tx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, + IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) { + IPAERR("fail to resume tx pipe\n"); + BUG(); + result = -EFAULT; + goto fail; + } + + /* enable rx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, + IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) { + IPAERR("fail to enable rx pipe\n"); + BUG(); + result = -EFAULT; + goto fail; + } + + /* resume rx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, + IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) { + IPAERR("fail to resume rx pipe\n"); + BUG(); + result = -EFAULT; + goto fail; + } + + IPA_ACTIVE_CLIENTS_INC_SIMPLE(); + + /* enable data path */ + result = ipa_enable_data_path(ipa_ep_idx_rx); + if (result) { + IPAERR("enable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_rx); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + + result = ipa_enable_data_path(ipa_ep_idx_tx); + if (result) { + IPAERR("enable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_tx); + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + return -EFAULT; + } + + IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); + +fail: + return result; +} + +int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx) +{ + struct ipa_ep_context *ep_tx, *ep_rx; + int result = 0; + + IPADBG("ep_tx = %d\n", ipa_ep_idx_tx); + IPADBG("ep_rx = %d\n", ipa_ep_idx_rx); + + ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx]; + ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx]; + + /* suspend tx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, + IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) { + IPAERR("fail to suspend tx pipe\n"); + result = -EFAULT; + goto fail; + } + + /* disable tx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx, + IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) { + IPAERR("fail to disable tx pipe\n"); + result = -EFAULT; + goto fail; + } + + /* suspend rx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, + IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) { + IPAERR("fail to suspend rx pipe\n"); + result = -EFAULT; + goto fail; + } + + /* disable rx pipe */ + if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx, + IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) { + IPAERR("fail to disable rx pipe\n"); + result = -EFAULT; + goto fail; + } + +fail: + return result; +} -- GitLab From 9f8cd00208a0945804a83c7e43b2ab38ff0785ae Mon Sep 17 00:00:00 2001 From: wadesong Date: Mon, 11 Sep 2017 16:03:21 +0800 Subject: [PATCH 0953/5498] msm: wlan: Fix a corner case build error in CNSS platform driver When the following conditions are met: 1) Client code includes both include/net/cnss.h and include/net/cnss_prealloc.h 2) CONFIG_SLUB_DEBUG is not defined in the defconfig of a perticular platform function wcnss_prealloc_check_memory_leak will be declared as: void wcnss_prealloc_check_memory_leak(void); in include/net/cnss_prealloc.h, and implemented as: static inline void wcnss_prealloc_check_memory_leak(void) {} in include/net/cnss.h, which will result in a build error. Move the inline implementation of wcnss_prealloc_check_memory_leak into include/net/cnss_prealloc.h and make the same function's declaration enclosed by some conditional macros to avoid this kind of build errors. Change-Id: I4a1922a6293eaa257ae9a764bdbff27dee7c22ac Signed-off-by: Wade Song --- drivers/net/wireless/cnss_prealloc/cnss_prealloc.c | 1 + include/net/cnss.h | 4 ---- include/net/cnss_prealloc.h | 7 ++++++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c index 6335ade2c43b..af956f68cbd6 100644 --- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c +++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c @@ -20,6 +20,7 @@ #include #endif #include +#include static DEFINE_SPINLOCK(alloc_lock); diff --git a/include/net/cnss.h b/include/net/cnss.h index 2bf13858a287..03b76ee9f38d 100644 --- a/include/net/cnss.h +++ b/include/net/cnss.h @@ -169,10 +169,6 @@ extern void cnss_set_driver_status(enum cnss_driver_status driver_status); static inline int wcnss_pre_alloc_reset(void) { return 0; } #endif -#if !defined(CONFIG_WCNSS_MEM_PRE_ALLOC) || !defined(CONFIG_SLUB_DEBUG) -static inline void wcnss_prealloc_check_memory_leak(void) {} -#endif - extern int msm_pcie_enumerate(u32 rc_idx); extern int cnss_auto_suspend(void); extern int cnss_auto_resume(void); diff --git a/include/net/cnss_prealloc.h b/include/net/cnss_prealloc.h index 21c564dce562..bf50c16ec613 100644 --- a/include/net/cnss_prealloc.h +++ b/include/net/cnss_prealloc.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,7 +21,12 @@ extern void *wcnss_prealloc_get(unsigned int size); extern int wcnss_prealloc_put(void *ptr); extern int wcnss_pre_alloc_reset(void); + +#if !defined(CONFIG_WCNSS_MEM_PRE_ALLOC) || !defined(CONFIG_SLUB_DEBUG) +static inline void wcnss_prealloc_check_memory_leak(void) {} +#else void wcnss_prealloc_check_memory_leak(void); +#endif extern void wcnss_skb_prealloc_check_memory_leak(void); extern int wcnss_skb_pre_alloc_reset(void); -- GitLab From 472975aa19d978e9729ae7b88a476d62bfacc418 Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Fri, 8 Sep 2017 02:09:26 -0400 Subject: [PATCH 0954/5498] FROMLIST: android: binder: Drop lru lock in isolate callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (from https://patchwork.kernel.org/patch/9945123/) Drop the global lru lock in isolate callback before calling zap_page_range which calls cond_resched, and re-acquire the global lru lock before returning. Also change return code to LRU_REMOVED_RETRY. Use mmput_async when fail to acquire mmap sem in an atomic context. Fix "BUG: sleeping function called from invalid context" errors when CONFIG_DEBUG_ATOMIC_SLEEP is enabled. Bug: 63926541 Change-Id: I45dbada421b715abed9a66d03d30ae2285671ca1 Fixes: f2517eb76f1f2 ("android: binder: Add global lru shrinker to binder") Reported-by: Kyle Yan Acked-by: Arve HjønnevÃ¥g Signed-off-by: Sherry Yang --- drivers/staging/android/binder_alloc.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index 916ac64de02f..89bbdfcbe34b 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -912,6 +912,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item, struct binder_alloc *alloc; uintptr_t page_addr; size_t index; + struct vm_area_struct *vma; alloc = page->alloc; if (!mutex_trylock(&alloc->mutex)) @@ -922,16 +923,22 @@ enum lru_status binder_alloc_free_page(struct list_head *item, index = page - alloc->pages; page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; - if (alloc->vma) { + vma = alloc->vma; + if (vma) { mm = get_task_mm(alloc->tsk); if (!mm) goto err_get_task_mm_failed; if (!down_write_trylock(&mm->mmap_sem)) goto err_down_write_mmap_sem_failed; + } + + list_del_init(item); + spin_unlock(lock); + if (vma) { trace_binder_unmap_user_start(alloc, index); - zap_page_range(alloc->vma, + zap_page_range(vma, page_addr + alloc->user_buffer_offset, PAGE_SIZE, NULL); @@ -950,13 +957,12 @@ enum lru_status binder_alloc_free_page(struct list_head *item, trace_binder_unmap_kernel_end(alloc, index); - list_del_init(item); - + spin_lock(lock); mutex_unlock(&alloc->mutex); - return LRU_REMOVED; + return LRU_REMOVED_RETRY; err_down_write_mmap_sem_failed: - mmput(mm); + mmput_async(mm); err_get_task_mm_failed: err_page_already_freed: mutex_unlock(&alloc->mutex); -- GitLab From 91f6f6d9bfa8bc89c3cc0203fe709a07af87c3b7 Mon Sep 17 00:00:00 2001 From: Divya Narayanan Poojary Date: Wed, 13 Sep 2017 11:57:25 +0530 Subject: [PATCH 0955/5498] defconfig: Enable defconfig for AVTIMER on 8909 Enable CONFIG_MSM_AVTIMER for msm8909 MTP. Change-Id: Ieea7aba4c80fb9bc816bd1bc36d0caca04a6a4ea Signed-off-by: Divya Narayanan Poojary --- arch/arm/configs/msm8909-perf_defconfig | 1 + arch/arm/configs/msm8909_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index b326d2501327..f1ae76976931 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -429,6 +429,7 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y CONFIG_ION=y CONFIG_ION_MSM=y +CONFIG_MSM_AVTIMER=y CONFIG_MSM_BUS_SCALING=y CONFIG_BUS_TOPOLOGY_ADHOC=y CONFIG_QPNP_POWER_ON=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index bf662274a1e6..a273170404ce 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -441,6 +441,7 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y CONFIG_ION=y CONFIG_ION_MSM=y +CONFIG_MSM_AVTIMER=y CONFIG_MSM_BUS_SCALING=y CONFIG_BUS_TOPOLOGY_ADHOC=y CONFIG_QPNP_POWER_ON=y -- GitLab From 52a7f96ce95812b0f250fc410acdc03b21e36f81 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 9 May 2017 06:29:19 -0700 Subject: [PATCH 0956/5498] dccp/tcp: do not inherit mc_list from parent syzkaller found a way to trigger double frees from ip_mc_drop_socket() It turns out that leave a copy of parent mc_list at accept() time, which is very bad. Very similar to commit 8b485ce69876 ("tcp: do not inherit fastopen_req from parent") Initial report from Pray3r, completed by Andrey one. Thanks a lot to them ! Change-Id: Ic896b02962475ca922075674f99ebc03f237193f Signed-off-by: Eric Dumazet Reported-by: Pray3r Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: David S. Miller Git-commit: 657831ffc38e30092a2d5f03d385d710eb88b09a Git-repo: Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Srinivasa Rao Kuppala --- net/ipv4/inet_connection_sock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 1da002090d26..b24c6c660942 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -688,6 +688,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, inet_sk(newsk)->mc_list = NULL; + inet_sk(newsk)->mc_list = NULL; + newsk->sk_mark = inet_rsk(req)->ir_mark; newicsk->icsk_retransmits = 0; -- GitLab From 03566c46d3b5db682e3f2071a900f7e2ba0fc06d Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 8 May 2017 00:04:09 +0200 Subject: [PATCH 0957/5498] bpf: don't let ldimm64 leak map addresses on unprivileged The patch fixes two things at once: 1) It checks the env->allow_ptr_leaks and only prints the map address to the log if we have the privileges to do so, otherwise it just dumps 0 as we would when kptr_restrict is enabled on %pK. Given the latter is off by default and not every distro sets it, I don't want to rely on this, hence the 0 by default for unprivileged. 2) Printing of ldimm64 in the verifier log is currently broken in that we don't print the full immediate, but only the 32 bit part of the first insn part for ldimm64. Thus, fix this up as well; it's okay to access, since we verified all ldimm64 earlier already (including just constants) through replace_map_fd_with_map_ptr(). Change-Id: I25392dede9879a8854e38ce82a68e13ba1391564 Fixes: 1be7f75d1668 ("bpf: enable non-root eBPF programs") Fixes: cbd357008604 ("bpf: verifier (add ability to receive verification log)") Reported-by: Jann Horn Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller Git-commit: 0d0e57697f162da4aa218b5feafe614fb666db07 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Srinivasa Rao Kuppala --- kernel/bpf/verifier.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index eac45b688c08..dbed4b4487ec 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -315,7 +315,8 @@ static const char *const bpf_jmp_string[] = { [BPF_EXIT >> 4] = "exit", }; -static void print_bpf_insn(struct bpf_insn *insn) +static void print_bpf_insn(const struct bpf_verifier_env *env, + const struct bpf_insn *insn) { u8 class = BPF_CLASS(insn->code); @@ -379,9 +380,19 @@ static void print_bpf_insn(struct bpf_insn *insn) insn->code, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->src_reg, insn->imm); - } else if (BPF_MODE(insn->code) == BPF_IMM) { - verbose("(%02x) r%d = 0x%x\n", - insn->code, insn->dst_reg, insn->imm); + } else if (BPF_MODE(insn->code) == BPF_IMM && + BPF_SIZE(insn->code) == BPF_DW) { + /* At this point, we already made sure that the second + * part of the ldimm64 insn is accessible. + */ + u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm; + bool map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD; + + if (map_ptr && !env->allow_ptr_leaks) + imm = 0; + + verbose("(%02x) r%d = 0x%llx\n", insn->code, + insn->dst_reg, (unsigned long long)imm); } else { verbose("BUG_ld_%02x\n", insn->code); return; @@ -1534,7 +1545,7 @@ static int do_check(struct verifier_env *env) if (log_level) { verbose("%d: ", insn_idx); - print_bpf_insn(insn); + print_bpf_insn(env, insn); } if (class == BPF_ALU || class == BPF_ALU64) { -- GitLab From bea346f0d8e039a31754d32a12e5a69b1b5b3714 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 21 Sep 2017 12:20:50 +0200 Subject: [PATCH 0958/5498] Revert "FROMLIST: android: binder: Drop lru lock in isolate callback" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 472975aa19d978e9729ae7b88a476d62bfacc418 as it breaks the build. Cc: Kyle Yan Cc: Arve HjønnevÃ¥g Cc: Sherry Yang Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder_alloc.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index 89bbdfcbe34b..916ac64de02f 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -912,7 +912,6 @@ enum lru_status binder_alloc_free_page(struct list_head *item, struct binder_alloc *alloc; uintptr_t page_addr; size_t index; - struct vm_area_struct *vma; alloc = page->alloc; if (!mutex_trylock(&alloc->mutex)) @@ -923,22 +922,16 @@ enum lru_status binder_alloc_free_page(struct list_head *item, index = page - alloc->pages; page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; - vma = alloc->vma; - if (vma) { + if (alloc->vma) { mm = get_task_mm(alloc->tsk); if (!mm) goto err_get_task_mm_failed; if (!down_write_trylock(&mm->mmap_sem)) goto err_down_write_mmap_sem_failed; - } - - list_del_init(item); - spin_unlock(lock); - if (vma) { trace_binder_unmap_user_start(alloc, index); - zap_page_range(vma, + zap_page_range(alloc->vma, page_addr + alloc->user_buffer_offset, PAGE_SIZE, NULL); @@ -957,12 +950,13 @@ enum lru_status binder_alloc_free_page(struct list_head *item, trace_binder_unmap_kernel_end(alloc, index); - spin_lock(lock); + list_del_init(item); + mutex_unlock(&alloc->mutex); - return LRU_REMOVED_RETRY; + return LRU_REMOVED; err_down_write_mmap_sem_failed: - mmput_async(mm); + mmput(mm); err_get_task_mm_failed: err_page_already_freed: mutex_unlock(&alloc->mutex); -- GitLab From 2ebb94ce1132c81396803fe250ff0570cfa1d1f5 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 5 Jan 2015 17:38:41 -0700 Subject: [PATCH 0959/5498] UPSTREAM: arm64: fix missing asm/pgtable-hwdef.h include in asm/processor.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 2ec4560b7c73e6c9febc4fb2a3e6af257c904979) On next-20150105, defconfig compilation breaks with: ./arch/arm64/include/asm/processor.h:47:32: error: ‘PHYS_MASK’ undeclared (first use in this function) Fix by including asm/pgtable-hwdef.h, where PHYS_MASK is defined. This second version incorporates a comment from Mark Rutland to keep the includes in alphabetical order by filename. Signed-off-by: Paul Walmsley Cc: Paul Walmsley Cc: Catalin Marinas Cc: Will Deacon Acked-by: Mark Rutland Signed-off-by: Will Deacon --- arch/arm64/include/asm/processor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index f0854ea2ff04..a2d484cffb36 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -31,6 +31,7 @@ #include #include +#include #include #include -- GitLab From 88c7dd42784a47bf5ea243807874e319647adb30 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 5 Jan 2015 17:38:42 -0700 Subject: [PATCH 0960/5498] UPSTREAM: arm64: fix missing asm/io.h include in kernel/smp_spin_table.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 59c68329a00eee7759568bc7a5383407d0d40be1) On next-20150105, defconfig compilation breaks with: arch/arm64/kernel/smp_spin_table.c:80:2: error: implicit declaration of function ‘ioremap_cache’ [-Werror=implicit-function-declaration] arch/arm64/kernel/smp_spin_table.c:92:2: error: implicit declaration of function ‘writeq_relaxed’ [-Werror=implicit-function-declaration] arch/arm64/kernel/smp_spin_table.c:101:2: error: implicit declaration of function ‘iounmap’ [-Werror=implicit-function-declaration] Fix by including asm/io.h, which contains definitions or prototypes for these macros or functions. This second version incorporates a comment from Mark Rutland to keep the includes in alphabetical order by filename. Signed-off-by: Paul Walmsley Cc: Paul Walmsley Cc: Catalin Marinas Cc: Will Deacon Acked-by: Mark Rutland Signed-off-by: Will Deacon --- arch/arm64/kernel/smp_spin_table.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c index 4f93c67e63de..14944e5b28da 100644 --- a/arch/arm64/kernel/smp_spin_table.c +++ b/arch/arm64/kernel/smp_spin_table.c @@ -25,6 +25,7 @@ #include #include #include +#include #include extern void secondary_holding_pen(void); -- GitLab From b015da945bd68d5cd8b276eac75353820ccd5fa9 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 5 Jan 2015 17:38:41 -0700 Subject: [PATCH 0961/5498] UPSTREAM: arm64: fix missing linux/bug.h include in asm/arch_timer.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 082471a8efe1a91d4e44abec202d9e3067dcec91) On next-20150105, defconfig compilation breaks with: ./arch/arm64/include/asm/arch_timer.h:112:2: error: implicit declaration of function ‘BUG’ [-Werror=implicit-function-declaration] Fix by including linux/bug.h, where the BUG macro is defined. This second version incorporates a comment from Mark Rutland to keep the includes in alphabetical order by filename. Signed-off-by: Paul Walmsley Cc: Paul Walmsley Cc: Catalin Marinas Cc: Will Deacon Acked-by: Mark Rutland Signed-off-by: Will Deacon --- arch/arm64/include/asm/arch_timer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index b1fa4e614718..fbe0ca31a99c 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -21,6 +21,7 @@ #include +#include #include #include -- GitLab From 20d32e0b4fd01671cfda736b9f48f453cd1dd723 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Sun, 21 Dec 2014 19:46:56 +0100 Subject: [PATCH 0962/5498] UPSTREAM: time: move the timecounter/cyclecounter code into its own file. (cherry picked from commit 74d23cc704d19732e70ef1579a669f7d5f09dd9a) The timecounter code has almost nothing to do with the clocksource code. Let it live in its own file. This will help isolate the timecounter users from the clocksource users in the source tree. Signed-off-by: Richard Cochran Acked-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe.h | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 2 +- drivers/net/ethernet/freescale/fec.h | 1 + drivers/net/ethernet/intel/e1000e/e1000.h | 2 +- drivers/net/ethernet/intel/igb/igb.h | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 +- drivers/net/ethernet/ti/cpts.h | 1 + include/clocksource/arm_arch_timer.h | 2 +- include/linux/clocksource.h | 102 ---------------- include/linux/mlx4/device.h | 2 +- include/linux/timecounter.h | 122 ++++++++++++++++++++ include/linux/types.h | 3 + kernel/time/Makefile | 2 +- kernel/time/clocksource.c | 76 ------------ kernel/time/timecounter.c | 95 +++++++++++++++ sound/pci/hda/hda_priv.h | 2 +- 16 files changed, 231 insertions(+), 187 deletions(-) create mode 100644 include/linux/timecounter.h create mode 100644 kernel/time/timecounter.c diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 789957d43a13..ef301f45cbdc 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -124,7 +124,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 2559206d8704..29e8e6eceb64 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -22,7 +22,7 @@ #include #include -#include +#include /* compilation time flags */ diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 9af296a1ca99..e5ea8b9973c4 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 7785240a0da1..9416e5a7e0c8 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 95f47b9f50d4..5c8be806b83a 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -29,7 +29,7 @@ #include "e1000_mac.h" #include "e1000_82575.h" -#include +#include #include #include #include diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 5032a602d5c9..f26eeb705a77 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 1a581ef7eee8..69a46b92c7d6 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -27,6 +27,7 @@ #include #include #include +#include struct cpsw_cpts { u32 idver; /* Identification and version */ diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index 6d26b40cbf5d..9916d0e4eff5 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -16,7 +16,7 @@ #ifndef __CLKSOURCE_ARM_ARCH_TIMER_H #define __CLKSOURCE_ARM_ARCH_TIMER_H -#include +#include #include #define ARCH_TIMER_CTRL_ENABLE (1 << 0) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index abcafaa20b86..9c78d15d33e4 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -18,8 +18,6 @@ #include #include -/* clocksource cycle base type */ -typedef u64 cycle_t; struct clocksource; struct module; @@ -27,106 +25,6 @@ struct module; #include #endif -/** - * struct cyclecounter - hardware abstraction for a free running counter - * Provides completely state-free accessors to the underlying hardware. - * Depending on which hardware it reads, the cycle counter may wrap - * around quickly. Locking rules (if necessary) have to be defined - * by the implementor and user of specific instances of this API. - * - * @read: returns the current cycle value - * @mask: bitmask for two's complement - * subtraction of non 64 bit counters, - * see CLOCKSOURCE_MASK() helper macro - * @mult: cycle to nanosecond multiplier - * @shift: cycle to nanosecond divisor (power of two) - */ -struct cyclecounter { - cycle_t (*read)(const struct cyclecounter *cc); - cycle_t mask; - u32 mult; - u32 shift; -}; - -/** - * struct timecounter - layer above a %struct cyclecounter which counts nanoseconds - * Contains the state needed by timecounter_read() to detect - * cycle counter wrap around. Initialize with - * timecounter_init(). Also used to convert cycle counts into the - * corresponding nanosecond counts with timecounter_cyc2time(). Users - * of this code are responsible for initializing the underlying - * cycle counter hardware, locking issues and reading the time - * more often than the cycle counter wraps around. The nanosecond - * counter will only wrap around after ~585 years. - * - * @cc: the cycle counter used by this instance - * @cycle_last: most recent cycle counter value seen by - * timecounter_read() - * @nsec: continuously increasing count - */ -struct timecounter { - const struct cyclecounter *cc; - cycle_t cycle_last; - u64 nsec; -}; - -/** - * cyclecounter_cyc2ns - converts cycle counter cycles to nanoseconds - * @cc: Pointer to cycle counter. - * @cycles: Cycles - * - * XXX - This could use some mult_lxl_ll() asm optimization. Same code - * as in cyc2ns, but with unsigned result. - */ -static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc, - cycle_t cycles) -{ - u64 ret = (u64)cycles; - ret = (ret * cc->mult) >> cc->shift; - return ret; -} - -/** - * timecounter_init - initialize a time counter - * @tc: Pointer to time counter which is to be initialized/reset - * @cc: A cycle counter, ready to be used. - * @start_tstamp: Arbitrary initial time stamp. - * - * After this call the current cycle register (roughly) corresponds to - * the initial time stamp. Every call to timecounter_read() increments - * the time stamp counter by the number of elapsed nanoseconds. - */ -extern void timecounter_init(struct timecounter *tc, - const struct cyclecounter *cc, - u64 start_tstamp); - -/** - * timecounter_read - return nanoseconds elapsed since timecounter_init() - * plus the initial time stamp - * @tc: Pointer to time counter. - * - * In other words, keeps track of time since the same epoch as - * the function which generated the initial time stamp. - */ -extern u64 timecounter_read(struct timecounter *tc); - -/** - * timecounter_cyc2time - convert a cycle counter to same - * time base as values returned by - * timecounter_read() - * @tc: Pointer to time counter. - * @cycle_tstamp: a value returned by tc->cc->read() - * - * Cycle counts that are converted correctly as long as they - * fall into the interval [-1/2 max cycle count, +1/2 max cycle count], - * with "max cycle count" == cs->mask+1. - * - * This allows conversion of cycle counter values which were generated - * in the past. - */ -extern u64 timecounter_cyc2time(struct timecounter *tc, - cycle_t cycle_tstamp); - /** * struct clocksource - hardware abstraction for a free running counter * Provides mostly state-free accessors to the underlying hardware. diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 37e4404d0227..22fc8f9edc04 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -42,7 +42,7 @@ #include -#include +#include #define MAX_MSIX_P_PORT 17 #define MAX_MSIX 64 diff --git a/include/linux/timecounter.h b/include/linux/timecounter.h new file mode 100644 index 000000000000..146f07a6651b --- /dev/null +++ b/include/linux/timecounter.h @@ -0,0 +1,122 @@ +/* + * linux/include/linux/timecounter.h + * + * based on code that migrated away from + * linux/include/linux/clocksource.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _LINUX_TIMECOUNTER_H +#define _LINUX_TIMECOUNTER_H + +#include + +/** + * struct cyclecounter - hardware abstraction for a free running counter + * Provides completely state-free accessors to the underlying hardware. + * Depending on which hardware it reads, the cycle counter may wrap + * around quickly. Locking rules (if necessary) have to be defined + * by the implementor and user of specific instances of this API. + * + * @read: returns the current cycle value + * @mask: bitmask for two's complement + * subtraction of non 64 bit counters, + * see CLOCKSOURCE_MASK() helper macro + * @mult: cycle to nanosecond multiplier + * @shift: cycle to nanosecond divisor (power of two) + */ +struct cyclecounter { + cycle_t (*read)(const struct cyclecounter *cc); + cycle_t mask; + u32 mult; + u32 shift; +}; + +/** + * struct timecounter - layer above a %struct cyclecounter which counts nanoseconds + * Contains the state needed by timecounter_read() to detect + * cycle counter wrap around. Initialize with + * timecounter_init(). Also used to convert cycle counts into the + * corresponding nanosecond counts with timecounter_cyc2time(). Users + * of this code are responsible for initializing the underlying + * cycle counter hardware, locking issues and reading the time + * more often than the cycle counter wraps around. The nanosecond + * counter will only wrap around after ~585 years. + * + * @cc: the cycle counter used by this instance + * @cycle_last: most recent cycle counter value seen by + * timecounter_read() + * @nsec: continuously increasing count + */ +struct timecounter { + const struct cyclecounter *cc; + cycle_t cycle_last; + u64 nsec; +}; + +/** + * cyclecounter_cyc2ns - converts cycle counter cycles to nanoseconds + * @cc: Pointer to cycle counter. + * @cycles: Cycles + * + * XXX - This could use some mult_lxl_ll() asm optimization. Same code + * as in cyc2ns, but with unsigned result. + */ +static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc, + cycle_t cycles) +{ + u64 ret = (u64)cycles; + ret = (ret * cc->mult) >> cc->shift; + return ret; +} + +/** + * timecounter_init - initialize a time counter + * @tc: Pointer to time counter which is to be initialized/reset + * @cc: A cycle counter, ready to be used. + * @start_tstamp: Arbitrary initial time stamp. + * + * After this call the current cycle register (roughly) corresponds to + * the initial time stamp. Every call to timecounter_read() increments + * the time stamp counter by the number of elapsed nanoseconds. + */ +extern void timecounter_init(struct timecounter *tc, + const struct cyclecounter *cc, + u64 start_tstamp); + +/** + * timecounter_read - return nanoseconds elapsed since timecounter_init() + * plus the initial time stamp + * @tc: Pointer to time counter. + * + * In other words, keeps track of time since the same epoch as + * the function which generated the initial time stamp. + */ +extern u64 timecounter_read(struct timecounter *tc); + +/** + * timecounter_cyc2time - convert a cycle counter to same + * time base as values returned by + * timecounter_read() + * @tc: Pointer to time counter. + * @cycle_tstamp: a value returned by tc->cc->read() + * + * Cycle counts that are converted correctly as long as they + * fall into the interval [-1/2 max cycle count, +1/2 max cycle count], + * with "max cycle count" == cs->mask+1. + * + * This allows conversion of cycle counter values which were generated + * in the past. + */ +extern u64 timecounter_cyc2time(struct timecounter *tc, + cycle_t cycle_tstamp); + +#endif diff --git a/include/linux/types.h b/include/linux/types.h index a0bb7048687f..62323825cff9 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -213,5 +213,8 @@ struct callback_head { }; #define rcu_head callback_head +/* clocksource cycle base type */ +typedef u64 cycle_t; + #endif /* __ASSEMBLY__ */ #endif /* _LINUX_TYPES_H */ diff --git a/kernel/time/Makefile b/kernel/time/Makefile index 7347426fa68d..d9ffabea772c 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -1,6 +1,6 @@ obj-y += time.o timer.o hrtimer.o itimer.o posix-timers.o posix-cpu-timers.o obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o -obj-y += timeconv.o posix-clock.o alarmtimer.o +obj-y += timeconv.o timecounter.o posix-clock.o alarmtimer.o obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 2e949cc9c9f1..0f3234b25264 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -34,82 +34,6 @@ #include "tick-internal.h" #include "timekeeping_internal.h" -void timecounter_init(struct timecounter *tc, - const struct cyclecounter *cc, - u64 start_tstamp) -{ - tc->cc = cc; - tc->cycle_last = cc->read(cc); - tc->nsec = start_tstamp; -} -EXPORT_SYMBOL_GPL(timecounter_init); - -/** - * timecounter_read_delta - get nanoseconds since last call of this function - * @tc: Pointer to time counter - * - * When the underlying cycle counter runs over, this will be handled - * correctly as long as it does not run over more than once between - * calls. - * - * The first call to this function for a new time counter initializes - * the time tracking and returns an undefined result. - */ -static u64 timecounter_read_delta(struct timecounter *tc) -{ - cycle_t cycle_now, cycle_delta; - u64 ns_offset; - - /* read cycle counter: */ - cycle_now = tc->cc->read(tc->cc); - - /* calculate the delta since the last timecounter_read_delta(): */ - cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; - - /* convert to nanoseconds: */ - ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta); - - /* update time stamp of timecounter_read_delta() call: */ - tc->cycle_last = cycle_now; - - return ns_offset; -} - -u64 timecounter_read(struct timecounter *tc) -{ - u64 nsec; - - /* increment time by nanoseconds since last call */ - nsec = timecounter_read_delta(tc); - nsec += tc->nsec; - tc->nsec = nsec; - - return nsec; -} -EXPORT_SYMBOL_GPL(timecounter_read); - -u64 timecounter_cyc2time(struct timecounter *tc, - cycle_t cycle_tstamp) -{ - u64 cycle_delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; - u64 nsec; - - /* - * Instead of always treating cycle_tstamp as more recent - * than tc->cycle_last, detect when it is too far in the - * future and treat it as old time stamp instead. - */ - if (cycle_delta > tc->cc->mask / 2) { - cycle_delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; - nsec = tc->nsec - cyclecounter_cyc2ns(tc->cc, cycle_delta); - } else { - nsec = cyclecounter_cyc2ns(tc->cc, cycle_delta) + tc->nsec; - } - - return nsec; -} -EXPORT_SYMBOL_GPL(timecounter_cyc2time); - /** * clocks_calc_mult_shift - calculate mult/shift factors for scaled math of clocks * @mult: pointer to mult variable diff --git a/kernel/time/timecounter.c b/kernel/time/timecounter.c new file mode 100644 index 000000000000..59a1ec3a57cb --- /dev/null +++ b/kernel/time/timecounter.c @@ -0,0 +1,95 @@ +/* + * linux/kernel/time/timecounter.c + * + * based on code that migrated away from + * linux/kernel/time/clocksource.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +void timecounter_init(struct timecounter *tc, + const struct cyclecounter *cc, + u64 start_tstamp) +{ + tc->cc = cc; + tc->cycle_last = cc->read(cc); + tc->nsec = start_tstamp; +} +EXPORT_SYMBOL_GPL(timecounter_init); + +/** + * timecounter_read_delta - get nanoseconds since last call of this function + * @tc: Pointer to time counter + * + * When the underlying cycle counter runs over, this will be handled + * correctly as long as it does not run over more than once between + * calls. + * + * The first call to this function for a new time counter initializes + * the time tracking and returns an undefined result. + */ +static u64 timecounter_read_delta(struct timecounter *tc) +{ + cycle_t cycle_now, cycle_delta; + u64 ns_offset; + + /* read cycle counter: */ + cycle_now = tc->cc->read(tc->cc); + + /* calculate the delta since the last timecounter_read_delta(): */ + cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; + + /* convert to nanoseconds: */ + ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta); + + /* update time stamp of timecounter_read_delta() call: */ + tc->cycle_last = cycle_now; + + return ns_offset; +} + +u64 timecounter_read(struct timecounter *tc) +{ + u64 nsec; + + /* increment time by nanoseconds since last call */ + nsec = timecounter_read_delta(tc); + nsec += tc->nsec; + tc->nsec = nsec; + + return nsec; +} +EXPORT_SYMBOL_GPL(timecounter_read); + +u64 timecounter_cyc2time(struct timecounter *tc, + cycle_t cycle_tstamp) +{ + u64 cycle_delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; + u64 nsec; + + /* + * Instead of always treating cycle_tstamp as more recent + * than tc->cycle_last, detect when it is too far in the + * future and treat it as old time stamp instead. + */ + if (cycle_delta > tc->cc->mask / 2) { + cycle_delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; + nsec = tc->nsec - cyclecounter_cyc2ns(tc->cc, cycle_delta); + } else { + nsec = cyclecounter_cyc2ns(tc->cc, cycle_delta) + tc->nsec; + } + + return nsec; +} +EXPORT_SYMBOL_GPL(timecounter_cyc2time); diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index a09703a2b2c1..5484c6596943 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -15,7 +15,7 @@ #ifndef __SOUND_HDA_PRIV_H #define __SOUND_HDA_PRIV_H -#include +#include #include #include -- GitLab From add4d3e51882a60a4f512f2fa186f23fb6fcd915 Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Fri, 20 May 2016 16:57:21 -0700 Subject: [PATCH 0963/5498] BACKPORT: partial: mm, oom_reaper: do not mmput synchronously from the oom reaper context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit ec8d7c14ea14922fe21945b458a75e39f11dd832) Tetsuo has properly noted that mmput slow path might get blocked waiting for another party (e.g. exit_aio waits for an IO). If that happens the oom_reaper would be put out of the way and will not be able to process next oom victim. We should strive for making this context as reliable and independent on other subsystems as much as possible. Introduce mmput_async which will perform the slow path from an async (WQ) context. This will delay the operation but that shouldn't be a problem because the oom_reaper has reclaimed the victim's address space for most cases as much as possible and the remaining context shouldn't bind too much memory anymore. The only exception is when mmap_sem trylock has failed which shouldn't happen too often. The issue is only theoretical but not impossible. Signed-off-by: Michal Hocko Reported-by: Tetsuo Handa Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Only backports mmput_async. Change-Id: I5fe54abcc629e7d9eab9fe03908903d1174177f1 Signed-off-by: Arve HjønnevÃ¥g --- include/linux/mm_types.h | 2 ++ include/linux/sched.h | 5 ++++ kernel/fork.c | 50 ++++++++++++++++++++++++++++------------ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 6c44170c4aca..546aaf9ecaee 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -459,6 +460,7 @@ struct mm_struct { bool tlb_flush_pending; #endif struct uprobes_state uprobes_state; + struct work_struct async_put_work; }; static inline void mm_init_cpumask(struct mm_struct *mm) diff --git a/include/linux/sched.h b/include/linux/sched.h index e3dff8dc8d14..ebc81d811e0e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2543,6 +2543,11 @@ static inline void mmdrop(struct mm_struct * mm) /* mmput gets rid of the mappings and all user-space */ extern void mmput(struct mm_struct *); +/* same as above but performs the slow path from the async kontext. Can + * be called from the atomic context as well + */ +extern void mmput_async(struct mm_struct *); + /* Grab a reference to a task's mm, if it is not already going away */ extern struct mm_struct *get_task_mm(struct task_struct *task); /* diff --git a/kernel/fork.c b/kernel/fork.c index 9131e562de44..d53e0b5a429a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -662,6 +662,26 @@ void __mmdrop(struct mm_struct *mm) } EXPORT_SYMBOL_GPL(__mmdrop); +static inline void __mmput(struct mm_struct *mm) +{ + VM_BUG_ON(atomic_read(&mm->mm_users)); + + uprobe_clear_state(mm); + exit_aio(mm); + ksm_exit(mm); + khugepaged_exit(mm); /* must run before exit_mmap */ + exit_mmap(mm); + set_mm_exe_file(mm, NULL); + if (!list_empty(&mm->mmlist)) { + spin_lock(&mmlist_lock); + list_del(&mm->mmlist); + spin_unlock(&mmlist_lock); + } + if (mm->binfmt) + module_put(mm->binfmt->module); + mmdrop(mm); +} + /* * Decrement the use count and release all resources for an mm. */ @@ -669,24 +689,24 @@ void mmput(struct mm_struct *mm) { might_sleep(); + if (atomic_dec_and_test(&mm->mm_users)) + __mmput(mm); +} +EXPORT_SYMBOL_GPL(mmput); + +static void mmput_async_fn(struct work_struct *work) +{ + struct mm_struct *mm = container_of(work, struct mm_struct, async_put_work); + __mmput(mm); +} + +void mmput_async(struct mm_struct *mm) +{ if (atomic_dec_and_test(&mm->mm_users)) { - uprobe_clear_state(mm); - exit_aio(mm); - ksm_exit(mm); - khugepaged_exit(mm); /* must run before exit_mmap */ - exit_mmap(mm); - set_mm_exe_file(mm, NULL); - if (!list_empty(&mm->mmlist)) { - spin_lock(&mmlist_lock); - list_del(&mm->mmlist); - spin_unlock(&mmlist_lock); - } - if (mm->binfmt) - module_put(mm->binfmt->module); - mmdrop(mm); + INIT_WORK(&mm->async_put_work, mmput_async_fn); + schedule_work(&mm->async_put_work); } } -EXPORT_SYMBOL_GPL(mmput); void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) { -- GitLab From 9632fcbc950babdd40130808fb45152ed8d8bcb0 Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Fri, 8 Sep 2017 02:09:26 -0400 Subject: [PATCH 0964/5498] FROMLIST: android: binder: Drop lru lock in isolate callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (from https://patchwork.kernel.org/patch/9945123/) Drop the global lru lock in isolate callback before calling zap_page_range which calls cond_resched, and re-acquire the global lru lock before returning. Also change return code to LRU_REMOVED_RETRY. Use mmput_async when fail to acquire mmap sem in an atomic context. Fix "BUG: sleeping function called from invalid context" errors when CONFIG_DEBUG_ATOMIC_SLEEP is enabled. Bug: 63926541 Fixes: f2517eb76f1f2 ("android: binder: Add global lru shrinker to binder") Change-Id: Iacbacd26c0326e20baebe193551324859300f7f6 Reported-by: Kyle Yan Acked-by: Arve HjønnevÃ¥g Signed-off-by: Sherry Yang --- drivers/staging/android/binder_alloc.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index 916ac64de02f..89bbdfcbe34b 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -912,6 +912,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item, struct binder_alloc *alloc; uintptr_t page_addr; size_t index; + struct vm_area_struct *vma; alloc = page->alloc; if (!mutex_trylock(&alloc->mutex)) @@ -922,16 +923,22 @@ enum lru_status binder_alloc_free_page(struct list_head *item, index = page - alloc->pages; page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; - if (alloc->vma) { + vma = alloc->vma; + if (vma) { mm = get_task_mm(alloc->tsk); if (!mm) goto err_get_task_mm_failed; if (!down_write_trylock(&mm->mmap_sem)) goto err_down_write_mmap_sem_failed; + } + + list_del_init(item); + spin_unlock(lock); + if (vma) { trace_binder_unmap_user_start(alloc, index); - zap_page_range(alloc->vma, + zap_page_range(vma, page_addr + alloc->user_buffer_offset, PAGE_SIZE, NULL); @@ -950,13 +957,12 @@ enum lru_status binder_alloc_free_page(struct list_head *item, trace_binder_unmap_kernel_end(alloc, index); - list_del_init(item); - + spin_lock(lock); mutex_unlock(&alloc->mutex); - return LRU_REMOVED; + return LRU_REMOVED_RETRY; err_down_write_mmap_sem_failed: - mmput(mm); + mmput_async(mm); err_get_task_mm_failed: err_page_already_freed: mutex_unlock(&alloc->mutex); -- GitLab From 89ad8f61279253e258e9cc1cf1ca420f684c1131 Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Fri, 15 Sep 2017 20:40:03 -0400 Subject: [PATCH 0965/5498] FROMLIST: android: binder: Remove unused vma argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (from https://patchwork.kernel.org/patch/9954123/) The vma argument in update_page_range is no longer used after 74310e06 ("android: binder: Move buffer out of area shared with user space"), since mmap_handler no longer calls update_page_range with a vma. Test: ran binderLibTest, throughputtest, interfacetest and mempressure Bug: 36007193 Change-Id: Ibd6f24c11750f8f7e6ed56e40dd18c08e02ace25 Acked-by: Arve HjønnevÃ¥g Signed-off-by: Sherry Yang --- drivers/staging/android/binder_alloc.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index 89bbdfcbe34b..84a9ff1d5b5f 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -186,12 +186,12 @@ struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, } static int binder_update_page_range(struct binder_alloc *alloc, int allocate, - void *start, void *end, - struct vm_area_struct *vma) + void *start, void *end) { void *page_addr; unsigned long user_page_addr; struct binder_lru_page *page; + struct vm_area_struct *vma = NULL; struct mm_struct *mm = NULL; bool need_mm = false; @@ -215,7 +215,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, } } - if (!vma && need_mm) + if (need_mm) mm = get_task_mm(alloc->tsk); if (mm) { @@ -442,7 +442,7 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, if (end_page_addr > has_page_addr) end_page_addr = has_page_addr; ret = binder_update_page_range(alloc, 1, - (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL); + (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr); if (ret) return ERR_PTR(ret); @@ -483,7 +483,7 @@ struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, err_alloc_buf_struct_failed: binder_update_page_range(alloc, 0, (void *)PAGE_ALIGN((uintptr_t)buffer->data), - end_page_addr, NULL); + end_page_addr); return ERR_PTR(-ENOMEM); } @@ -567,8 +567,7 @@ static void binder_delete_free_buffer(struct binder_alloc *alloc, alloc->pid, buffer->data, prev->data, next->data); binder_update_page_range(alloc, 0, buffer_start_page(buffer), - buffer_start_page(buffer) + PAGE_SIZE, - NULL); + buffer_start_page(buffer) + PAGE_SIZE); } list_del(&buffer->entry); kfree(buffer); @@ -605,8 +604,7 @@ static void binder_free_buf_locked(struct binder_alloc *alloc, binder_update_page_range(alloc, 0, (void *)PAGE_ALIGN((uintptr_t)buffer->data), - (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), - NULL); + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK)); rb_erase(&buffer->rb_node, &alloc->allocated_buffers); buffer->free = 1; -- GitLab From 2bafaab6dc2513d9e6d6c2f4bfd0b688bcbb324f Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Fri, 15 Sep 2017 21:12:15 -0400 Subject: [PATCH 0966/5498] FROMLIST: android: binder: Don't get mm from task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (from https://patchwork.kernel.org/patch/9954125/) Use binder_alloc struct's mm_struct rather than getting a reference to the mm struct through get_task_mm to avoid a potential deadlock between lru lock, task lock and dentry lock, since a thread can be holding the task lock and the dentry lock while trying to acquire the lru lock. Test: ran binderLibTest, throughputtest, interfacetest and mempressure Bug: 63926541 Change-Id: Icc661404eb7a4a2ecc5234b1bf8f0104665f9b45 Acked-by: Arve HjønnevÃ¥g Signed-off-by: Sherry Yang --- drivers/staging/android/binder_alloc.c | 25 ++++++++++++------------- drivers/staging/android/binder_alloc.h | 1 - 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index 84a9ff1d5b5f..a5ec9709fc54 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -215,17 +215,13 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, } } - if (need_mm) - mm = get_task_mm(alloc->tsk); + /* Same as mmget_not_zero() in later kernel versions */ + if (need_mm && atomic_inc_not_zero(&alloc->vma_vm_mm->mm_users)) + mm = alloc->vma_vm_mm; if (mm) { down_write(&mm->mmap_sem); vma = alloc->vma; - if (vma && mm != alloc->vma_vm_mm) { - pr_err("%d: vma mm and task mm mismatch\n", - alloc->pid); - vma = NULL; - } } if (!vma && need_mm) { @@ -718,6 +714,8 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, barrier(); alloc->vma = vma; alloc->vma_vm_mm = vma->vm_mm; + /* Same as mmgrab() in later kernel versions */ + atomic_inc(&alloc->vma_vm_mm->mm_count); return 0; @@ -793,6 +791,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) vfree(alloc->buffer); } mutex_unlock(&alloc->mutex); + if (alloc->vma_vm_mm) + mmdrop(alloc->vma_vm_mm); binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d buffers %d, pages %d\n", @@ -887,7 +887,6 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) void binder_alloc_vma_close(struct binder_alloc *alloc) { WRITE_ONCE(alloc->vma, NULL); - WRITE_ONCE(alloc->vma_vm_mm, NULL); } /** @@ -923,9 +922,10 @@ enum lru_status binder_alloc_free_page(struct list_head *item, page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; vma = alloc->vma; if (vma) { - mm = get_task_mm(alloc->tsk); - if (!mm) - goto err_get_task_mm_failed; + /* Same as mmget_not_zero() in later kernel versions */ + if (!atomic_inc_not_zero(&alloc->vma_vm_mm->mm_users)) + goto err_mmget; + mm = alloc->vma_vm_mm; if (!down_write_trylock(&mm->mmap_sem)) goto err_down_write_mmap_sem_failed; } @@ -961,7 +961,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item, err_down_write_mmap_sem_failed: mmput_async(mm); -err_get_task_mm_failed: +err_mmget: err_page_already_freed: mutex_unlock(&alloc->mutex); err_get_alloc_mutex_failed: @@ -1000,7 +1000,6 @@ struct shrinker binder_shrinker = { */ void binder_alloc_init(struct binder_alloc *alloc) { - alloc->tsk = current->group_leader; alloc->pid = current->group_leader->pid; mutex_init(&alloc->mutex); INIT_LIST_HEAD(&alloc->buffers); diff --git a/drivers/staging/android/binder_alloc.h b/drivers/staging/android/binder_alloc.h index d864dc60f982..a87913ac200b 100644 --- a/drivers/staging/android/binder_alloc.h +++ b/drivers/staging/android/binder_alloc.h @@ -100,7 +100,6 @@ struct binder_lru_page { */ struct binder_alloc { struct mutex mutex; - struct task_struct *tsk; struct vm_area_struct *vma; struct mm_struct *vma_vm_mm; void *buffer; -- GitLab From cf64391c64b6dae01d78e394e1e2235d50a8f904 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:03 +0100 Subject: [PATCH 0967/5498] UPSTREAM: timecounter: provide a macro to initialize the cyclecounter mask field. (cherry picked from commit 1891172aa5c32f08ad9931b794edd71e91a4a527) There is no need for users of the timecounter/cyclecounter code to include clocksource.h just for a single macro. Change-Id: I809b7690502d3c1fe397903432f0299803522e89 Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- include/linux/timecounter.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/timecounter.h b/include/linux/timecounter.h index 146f07a6651b..e1dae9334ba7 100644 --- a/include/linux/timecounter.h +++ b/include/linux/timecounter.h @@ -19,6 +19,9 @@ #include +/* simplify initialization of mask field */ +#define CYCLECOUNTER_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1) + /** * struct cyclecounter - hardware abstraction for a free running counter * Provides completely state-free accessors to the underlying hardware. @@ -29,7 +32,7 @@ * @read: returns the current cycle value * @mask: bitmask for two's complement * subtraction of non 64 bit counters, - * see CLOCKSOURCE_MASK() helper macro + * see CYCLECOUNTER_MASK() helper macro * @mult: cycle to nanosecond multiplier * @shift: cycle to nanosecond divisor (power of two) */ -- GitLab From a5255b0793f7d17cf44f0417578db6a37adf88dc Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:04 +0100 Subject: [PATCH 0968/5498] UPSTREAM: bnx2x: convert to CYCLECOUNTER_MASK macro. (cherry picked from commit f28ba401dbd9e9fe63d9a7f9101638ca709185b2) Signed-off-by: Richard Cochran Signed-off-by: David S. Miller Change-Id: I0f9e0e58538c579966e8f48357af15615c4c42b8 --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 1217eafb61a4..bd84c4bcd4c8 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -14607,7 +14607,7 @@ static void bnx2x_init_cyclecounter(struct bnx2x *bp) { memset(&bp->cyclecounter, 0, sizeof(bp->cyclecounter)); bp->cyclecounter.read = bnx2x_cyclecounter_read; - bp->cyclecounter.mask = CLOCKSOURCE_MASK(64); + bp->cyclecounter.mask = CYCLECOUNTER_MASK(64); bp->cyclecounter.shift = 1; bp->cyclecounter.mult = 1; } -- GitLab From ac287ef513c0d174ba131438ca1c7493402e9715 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 5 Jan 2015 17:38:41 -0700 Subject: [PATCH 0969/5498] UPSTREAM: arm64: fix missing asm/alternative.h include in kernel/module.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 2c2b282d001e9934adeac93c10eb037b81d532f5) On next-20150105, defconfig compilation breaks with: arch/arm64/kernel/module.c:408:4: error: implicit declaration of function ‘apply_alternatives’ [-Werror=implicit-function-declaration] Fix by including asm/alternative.h, where the apply_alternatives() prototype is declared. This second version incorporates a comment from Mark Rutland to keep the includes in alphabetical order by filename. Change-Id: Ia4ae417f5789524b5ff5749281c10e89a17619ae Signed-off-by: Paul Walmsley Cc: Paul Walmsley Cc: Catalin Marinas Cc: Will Deacon Acked-by: Mark Rutland Signed-off-by: Will Deacon --- arch/arm64/kernel/module.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c index 76015722d5be..4223b0a4136f 100644 --- a/arch/arm64/kernel/module.c +++ b/arch/arm64/kernel/module.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include -- GitLab From e91597f78c6da7a9f8a9b7b43f8530c1a4f84f99 Mon Sep 17 00:00:00 2001 From: Liangwei Dong Date: Thu, 21 Sep 2017 04:45:33 -0400 Subject: [PATCH 0970/5498] ARM: dts: msm: Add new mdm9650 device tree file Add new device tree file for mdm9650 device to support ROME sdio wlan interface. And enable cnss_sdio nodes for ROME sdio wlan. CRs-Fixed: 2113964 Change-Id: Ic83b14e4f7293ef485dc49749b9af0c607a4cbd9 Signed-off-by: Liangwei Dong --- arch/arm/boot/dts/qcom/Makefile | 3 +- .../qcom/mdm9650-v1.1-nand-rome-sdio-mtp.dts | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-rome-sdio-mtp.dts diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 6a476dbaca86..262234e6b385 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -167,7 +167,8 @@ dtb-$(CONFIG_ARCH_MDM9650) += mdm9650-sim.dtb \ mdm9650-v1.1-nand-ccard-v2.dtb \ mdm9650-v1.1-emmc-pcie-ep-mtp.dtb \ mdm9650-v1.1-nand-pcie-ep-mtp.dtb \ - mdm9650-v1.1-nand-cv2x.dtb + mdm9650-v1.1-nand-cv2x.dtb \ + mdm9650-v1.1-nand-rome-sdio-mtp.dtb dtb-$(CONFIG_ARCH_SDX20) += sdx20-emmc-cdp.dtb \ sdx20-emmc-mtp.dtb \ diff --git a/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-rome-sdio-mtp.dts b/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-rome-sdio-mtp.dts new file mode 100644 index 000000000000..0aa0f613ae99 --- /dev/null +++ b/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-rome-sdio-mtp.dts @@ -0,0 +1,57 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "mdm9650-v1.1-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MDM 9650 v1.1 MTP ROME-SDIO"; + compatible = "qcom,mdm9650-mtp", "qcom,mdm9650", + "qcom,mtp"; + qcom,board-id = <8 8>, <8 0x108>; +}; + +&cnss_sdio { + status = "ok"; +}; + +&sdhc_1 { + vdd-supply = <&sdc_vreg>; + vdd-io-supply = <&pmd9650_l7>; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-always-on; + qcom,vdd-io-current-level = <200 10000>; + + qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 + 200000000>; + qcom,bus-width = <4>; + qcom,core_3_0v_support; + qcom,nonremovable; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on + &sdc1_wlan_gpio_active>; + pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off + &sdc1_wlan_gpio_sleep>; + #address-cells = <0>; + interrupt-parent = <&sdhc_1>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 123 0 + 1 &intc 0 138 0 + 2 &tlmm_pinmux 93 0x4>; + interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq"; + + status = "ok"; +}; -- GitLab From 297839a89059708965bbe454f5953acc2729e095 Mon Sep 17 00:00:00 2001 From: Prakash Kamliya Date: Wed, 12 Jul 2017 14:43:49 +0530 Subject: [PATCH 0971/5498] msm: kgsl: Fix leak when preemption init fails For any reason if preemption initialization fails, we do not free allocated memory for preemption. Free allocated memory when it fails. Change-Id: Ie931766f1ec1de7f3a0522054fc1fcb7b9426be6 Signed-off-by: Prakash Kamliya --- drivers/gpu/msm/adreno_a5xx_preempt.c | 43 ++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c index 5ce4b71ebcb9..cc313d27ee07 100644 --- a/drivers/gpu/msm/adreno_a5xx_preempt.c +++ b/drivers/gpu/msm/adreno_a5xx_preempt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -537,13 +537,42 @@ static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED, "smmu_info"); } + +static void a5xx_preemption_iommu_close(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + + kgsl_free_global(device, &iommu->smmu_info); +} + #else static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) { return -ENODEV; } + +static void a5xx_preemption_iommu_close(struct adreno_device *adreno_dev) +{ +} #endif +static void a5xx_preemption_close(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_preemption *preempt = &adreno_dev->preempt; + struct adreno_ringbuffer *rb; + unsigned int i; + + del_timer(&preempt->timer); + kgsl_free_global(device, &preempt->counters); + a5xx_preemption_iommu_close(adreno_dev); + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + kgsl_free_global(device, &rb->preemption_desc); + } +} + int a5xx_preemption_init(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -568,7 +597,7 @@ int a5xx_preemption_init(struct adreno_device *adreno_dev) A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0, "preemption_counters"); if (ret) - return ret; + goto err; addr = preempt->counters.gpuaddr; @@ -576,10 +605,16 @@ int a5xx_preemption_init(struct adreno_device *adreno_dev) FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { ret = a5xx_preemption_ringbuffer_init(adreno_dev, rb, addr); if (ret) - return ret; + goto err; addr += A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE; } - return a5xx_preemption_iommu_init(adreno_dev); + ret = a5xx_preemption_iommu_init(adreno_dev); + +err: + if (ret) + a5xx_preemption_close(device); + + return ret; } -- GitLab From 19ab039994ae050041ccac68da0b7ed9d804fb96 Mon Sep 17 00:00:00 2001 From: Rajesh Kemisetti Date: Fri, 27 Jan 2017 00:04:10 +0530 Subject: [PATCH 0972/5498] msm: kgsl: Check GPMU presence before accessing power counters Some targets do not have GPMU block in GPU and register read or write into those registers is not valid. To prevent this, check GPMU presence and then proceed. Change-Id: I0992125af8cda4b8235aedb64f6ef8868ae95f20 Signed-off-by: Rajesh Kemisetti --- drivers/gpu/msm/adreno_perfcounter.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c index 96922972200f..f2ce02b010d0 100644 --- a/drivers/gpu/msm/adreno_perfcounter.c +++ b/drivers/gpu/msm/adreno_perfcounter.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -654,6 +654,9 @@ static void _perfcounter_enable_vbif_pwr(struct adreno_device *adreno_dev, static void _power_counter_enable_alwayson(struct adreno_device *adreno_dev, struct adreno_perfcounters *counters) { + if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) + return; + kgsl_regwrite(KGSL_DEVICE(adreno_dev), A5XX_GPMU_ALWAYS_ON_COUNTER_RESET, 1); counters->groups[KGSL_PERFCOUNTER_GROUP_ALWAYSON_PWR].regs[0].value = 0; @@ -694,6 +697,9 @@ static void _power_counter_enable_default(struct adreno_device *adreno_dev, struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct adreno_perfcount_register *reg; + if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) + return; + reg = &counters->groups[group].regs[counter]; kgsl_regwrite(device, reg->select, countable); kgsl_regwrite(device, A5XX_GPMU_POWER_COUNTER_ENABLE, 1); @@ -927,6 +933,9 @@ static uint64_t _perfcounter_read_pwrcntr(struct adreno_device *adreno_dev, struct adreno_perfcount_register *reg; unsigned int lo = 0, hi = 0; + if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) + return 0; + reg = &group->regs[counter]; kgsl_regread(device, reg->offset, &lo); -- GitLab From 55c0685b2c81afd32835c0ed5319938e7992c847 Mon Sep 17 00:00:00 2001 From: Anoob Soman Date: Wed, 15 Feb 2017 20:25:39 +0000 Subject: [PATCH 0973/5498] packet: Do not call fanout_release from atomic contexts [ Upstream commit 2bd624b4611ffee36422782d16e1c944d1351e98 ] Commit 6664498280cf ("packet: call fanout_release, while UNREGISTERING a netdev"), unfortunately, introduced the following issues. 1. calling mutex_lock(&fanout_mutex) (fanout_release()) from inside rcu_read-side critical section. rcu_read_lock disables preemption, most often, which prohibits calling sleeping functions. [ ] include/linux/rcupdate.h:560 Illegal context switch in RCU read-side critical section! [ ] [ ] rcu_scheduler_active = 1, debug_locks = 0 [ ] 4 locks held by ovs-vswitchd/1969: [ ] #0: (cb_lock){++++++}, at: [] genl_rcv+0x19/0x40 [ ] #1: (ovs_mutex){+.+.+.}, at: [] ovs_vport_cmd_del+0x4a/0x100 [openvswitch] [ ] #2: (rtnl_mutex){+.+.+.}, at: [] rtnl_lock+0x17/0x20 [ ] #3: (rcu_read_lock){......}, at: [] packet_notifier+0x5/0x3f0 [ ] [ ] Call Trace: [ ] [] dump_stack+0x85/0xc4 [ ] [] lockdep_rcu_suspicious+0x107/0x110 [ ] [] ___might_sleep+0x57/0x210 [ ] [] __might_sleep+0x70/0x90 [ ] [] mutex_lock_nested+0x3c/0x3a0 [ ] [] ? vprintk_default+0x1f/0x30 [ ] [] ? printk+0x4d/0x4f [ ] [] fanout_release+0x1d/0xe0 [ ] [] packet_notifier+0x2f9/0x3f0 2. calling mutex_lock(&fanout_mutex) inside spin_lock(&po->bind_lock). "sleeping function called from invalid context" [ ] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:620 [ ] in_atomic(): 1, irqs_disabled(): 0, pid: 1969, name: ovs-vswitchd [ ] INFO: lockdep is turned off. [ ] Call Trace: [ ] [] dump_stack+0x85/0xc4 [ ] [] ___might_sleep+0x202/0x210 [ ] [] __might_sleep+0x70/0x90 [ ] [] mutex_lock_nested+0x3c/0x3a0 [ ] [] fanout_release+0x1d/0xe0 [ ] [] packet_notifier+0x2f9/0x3f0 3. calling dev_remove_pack(&fanout->prot_hook), from inside spin_lock(&po->bind_lock) or rcu_read-side critical-section. dev_remove_pack() -> synchronize_net(), which might sleep. [ ] BUG: scheduling while atomic: ovs-vswitchd/1969/0x00000002 [ ] INFO: lockdep is turned off. [ ] Call Trace: [ ] [] dump_stack+0x85/0xc4 [ ] [] __schedule_bug+0x64/0x73 [ ] [] __schedule+0x6b/0xd10 [ ] [] schedule+0x6b/0x80 [ ] [] schedule_timeout+0x38d/0x410 [ ] [] synchronize_sched_expedited+0x53d/0x810 [ ] [] synchronize_rcu_expedited+0xe/0x10 [ ] [] synchronize_net+0x35/0x50 [ ] [] dev_remove_pack+0x13/0x20 [ ] [] fanout_release+0xbe/0xe0 [ ] [] packet_notifier+0x2f9/0x3f0 4. fanout_release() races with calls from different CPU. To fix the above problems, remove the call to fanout_release() under rcu_read_lock(). Instead, call __dev_remove_pack(&fanout->prot_hook) and netdev_run_todo will be happy that &dev->ptype_specific list is empty. In order to achieve this, I moved dev_{add,remove}_pack() out of fanout_{add,release} to __fanout_{link,unlink}. So, call to {,__}unregister_prot_hook() will make sure fanout->prot_hook is removed as well. Change-Id: If5c94fcd9ee7fb15fadd50fa05f3a73688be61e8 Fixes: 6664498280cf ("packet: call fanout_release, while UNREGISTERING a netdev") Reported-by: Eric Dumazet Signed-off-by: Anoob Soman Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman Git-commit: 2bd624b4611ffee36422782d16e1c944d1351e98 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [ tejaswit@codeaurora.org: Qcom 3.18 branches do not support classic BPF fanout mode (yet). upstream commit 47dceb8ecdc . So remove fanout_release_data() calls ] Signed-off-by: Tejaswi Tanikella --- net/packet/af_packet.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 1db48a5760b2..3878c34b531b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1380,6 +1380,8 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po) f->arr[f->num_members] = sk; smp_wmb(); f->num_members++; + if (f->num_members == 1) + dev_add_pack(&f->prot_hook); spin_unlock(&f->lock); } @@ -1396,6 +1398,8 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po) BUG_ON(i >= f->num_members); f->arr[i] = f->arr[f->num_members - 1]; f->num_members--; + if (f->num_members == 0) + __dev_remove_pack(&f->prot_hook); spin_unlock(&f->lock); } @@ -1468,7 +1472,6 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) match->prot_hook.func = packet_rcv_fanout; match->prot_hook.af_packet_priv = match; match->prot_hook.id_match = match_fanout_group; - dev_add_pack(&match->prot_hook); list_add(&match->list, &fanout_list); } err = -EINVAL; @@ -1489,7 +1492,14 @@ out: return err; } -static void fanout_release(struct sock *sk) +/* If pkt_sk(sk)->fanout->sk_ref is zero, this function removes + * pkt_sk(sk)->fanout from fanout_list and returns pkt_sk(sk)->fanout. + * It is the responsibility of the caller to free the returned packet_fanout + * (after synchronize_net()) + * This Branch still does not have support classic BPF fanout mode. + * upstream commit 47dceb8ecdc: packet: add classic BPF fanout mode + */ +static struct packet_fanout *fanout_release(struct sock *sk) { struct packet_sock *po = pkt_sk(sk); struct packet_fanout *f; @@ -1499,13 +1509,15 @@ static void fanout_release(struct sock *sk) if (f) { po->fanout = NULL; - if (atomic_dec_and_test(&f->sk_ref)) { + if (atomic_dec_and_test(&f->sk_ref)) list_del(&f->list); - dev_remove_pack(&f->prot_hook); - kfree(f); - } + else + f = NULL; + } mutex_unlock(&fanout_mutex); + + return f; } static bool packet_extra_vlan_len_allowed(const struct net_device *dev, @@ -2587,6 +2599,7 @@ static int packet_release(struct socket *sock) { struct sock *sk = sock->sk; struct packet_sock *po; + struct packet_fanout *f; struct net *net; union tpacket_req_u req_u; @@ -2626,9 +2639,13 @@ static int packet_release(struct socket *sock) packet_set_ring(sk, &req_u, 1, 1); } - fanout_release(sk); + f = fanout_release(sk); synchronize_net(); + + if (f) + kfree(f); + /* * Now the socket is dead. No more input will appear. */ @@ -3567,7 +3584,6 @@ static int packet_notifier(struct notifier_block *this, } if (msg == NETDEV_UNREGISTER) { packet_cached_dev_reset(po); - fanout_release(sk); po->ifindex = -1; if (po->prot_hook.dev) dev_put(po->prot_hook.dev); -- GitLab From f6f9180dc567e15745cceafb46e44d87aea210d2 Mon Sep 17 00:00:00 2001 From: Lynus Vaz Date: Mon, 17 Apr 2017 18:29:58 +0530 Subject: [PATCH 0974/5498] msm: kgsl: Deal with a NULL pointer when creating default pagetable Return an appropriate error code if the default pagetable is NULL. Change-Id: Ic88b066c40a8f840d95fd3fbc9ee9274c428b66a Signed-off-by: Lynus Vaz --- drivers/gpu/msm/kgsl_iommu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index f23d922f6470..0e9fa9ffdf12 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1497,6 +1497,8 @@ static int _setup_user_context(struct kgsl_mmu *mmu) ret = PTR_ERR(mmu->defaultpagetable); mmu->defaultpagetable = NULL; return ret; + } else if (mmu->defaultpagetable == NULL) { + return -ENOMEM; } } -- GitLab From e188d2785e63e210ca6e72e41b43300cb63ef5fd Mon Sep 17 00:00:00 2001 From: Oleg Perelet Date: Tue, 11 Oct 2016 12:19:44 -0700 Subject: [PATCH 0975/5498] msm: kgsl: Remove use of uninitialized variable During certain code execution paths dev_ab can be assigned not initialized value. CRs-Fixed: 1074208 Change-Id: I330a50f6d171497e5b6d3e44e8ce2b09f2d644e3 Signed-off-by: Oleg Perelet --- drivers/devfreq/governor_bw_vbif.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/devfreq/governor_bw_vbif.c b/drivers/devfreq/governor_bw_vbif.c index 83196e1b31d5..ac670ad519fc 100644 --- a/drivers/devfreq/governor_bw_vbif.c +++ b/drivers/devfreq/governor_bw_vbif.c @@ -80,15 +80,13 @@ static int devfreq_vbif_ev_handler(struct devfreq *devfreq, case DEVFREQ_GOV_START: mutex_lock(&df_lock); df = devfreq; - if (df->profile->get_dev_status) - ret = df->profile->get_dev_status(df->dev.parent, - &stat); + if (df->profile->get_dev_status && + !df->profile->get_dev_status(df->dev.parent, &stat) && + stat.private_data) + dev_ab = stat.private_data; else - ret = 0; - if (ret || !stat.private_data) pr_warn("Device doesn't take AB votes!\n"); - else - dev_ab = stat.private_data; + mutex_unlock(&df_lock); ret = devfreq_vbif_update_bw(0, 0); -- GitLab From 479bb3873a352a49b1b0a4ac5e31199b5f41a471 Mon Sep 17 00:00:00 2001 From: Harshdeep Dhatt Date: Thu, 20 Oct 2016 11:25:07 -0600 Subject: [PATCH 0976/5498] msm: kgsl: Declare iomem addresses as void Readl/writel macros expect void pointers so declare the addresses as void and not unsigned int. Change-Id: I67cf15fa918832ebab56cb999265d02880682c5e Signed-off-by: Harshdeep Dhatt --- drivers/gpu/msm/adreno.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 90a393a48f3e..0ae1402e3429 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -2409,9 +2409,9 @@ static void adreno_read(struct kgsl_device *device, void __iomem *base, unsigned int mem_len) { - unsigned int __iomem *reg; + void __iomem *reg; BUG_ON(offsetwords*sizeof(uint32_t) >= mem_len); - reg = (unsigned int __iomem *)(base + (offsetwords << 2)); + reg = (base + (offsetwords << 2)); if (!in_interrupt()) kgsl_pre_hwaccess(device); @@ -2451,7 +2451,7 @@ static void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, unsigned int value) { - unsigned int __iomem *reg; + void __iomem *reg; BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len); @@ -2461,7 +2461,7 @@ static void adreno_regwrite(struct kgsl_device *device, trace_kgsl_regwrite(device, offsetwords, value); kgsl_cffdump_regwrite(device, offsetwords << 2, value); - reg = (unsigned int __iomem *)(device->reg_virt + (offsetwords << 2)); + reg = (device->reg_virt + (offsetwords << 2)); /*ensure previous writes post before this one, * i.e. act like normal writel() */ -- GitLab From c07e54b1e778a36478fd0cadf3780cea7c066866 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Thu, 21 Sep 2017 14:06:15 +0530 Subject: [PATCH 0977/5498] input: synaptics_dsx_2.6: enable wakeup gesture for synaptics driver Enable wakeup gesture for Synaptics V2.6 touch driver. Below changes are added for the same: - Enable the wakeup gesture as it was disabled by default as a macro. - Add change to suspend the touch device for FB_BLANK_VSYNC_SUSPEND fb event, when display goes to dim setting for this event. - Enable wakeup feature and make irq as wake-able only once while going suspend or resume. - Don't put off regulators when wakeup geature is enabled. Change-Id: Icd59de0411d92a791bbeb08ec3d368359edf8993 Signed-off-by: Shantanu Jain --- .../synaptics_dsx_2.6/synaptics_dsx_core.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c index 5e2b8ce5493d..0195dd80fd3f 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c @@ -55,7 +55,7 @@ #define TYPE_B_PROTOCOL #endif -#define WAKEUP_GESTURE false +#define WAKEUP_GESTURE true #define NO_0D_WHILE_2D #define REPORT_2D_Z @@ -4422,7 +4422,8 @@ static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self, synaptics_secure_touch_stop(rmi4_data, false); } else if (event == FB_EVENT_BLANK) { transition = evdata->data; - if (*transition == FB_BLANK_POWERDOWN) { + if (*transition == FB_BLANK_POWERDOWN || + *transition == FB_BLANK_VSYNC_SUSPEND) { flush_work( &(rmi4_data->fb_notify_work)); synaptics_rmi4_suspend( @@ -4563,8 +4564,10 @@ static int synaptics_rmi4_suspend(struct device *dev) synaptics_secure_touch_stop(rmi4_data, true); if (rmi4_data->enable_wakeup_gesture) { - synaptics_rmi4_wakeup_gesture(rmi4_data, true); - enable_irq_wake(rmi4_data->irq); + if (!rmi4_data->suspend) { + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + enable_irq_wake(rmi4_data->irq); + } goto exit; } @@ -4591,7 +4594,7 @@ exit: } mutex_unlock(&exp_data.mutex); - if (!rmi4_data->suspend) { + if (!rmi4_data->suspend && !rmi4_data->enable_wakeup_gesture) { synaptics_rmi4_enable_reg(rmi4_data, false); synaptics_rmi4_get_reg(rmi4_data, false); } @@ -4620,8 +4623,10 @@ static int synaptics_rmi4_resume(struct device *dev) synaptics_secure_touch_stop(rmi4_data, true); if (rmi4_data->enable_wakeup_gesture) { - synaptics_rmi4_wakeup_gesture(rmi4_data, false); - disable_irq_wake(rmi4_data->irq); + if (rmi4_data->suspend) { + synaptics_rmi4_wakeup_gesture(rmi4_data, false); + disable_irq_wake(rmi4_data->irq); + } goto exit; } -- GitLab From 8fa76855253ef796e0ccc2dd5dbf9e9e7c674bc2 Mon Sep 17 00:00:00 2001 From: Archana Sriram Date: Mon, 18 Sep 2017 17:33:31 +0530 Subject: [PATCH 0978/5498] msm: kgsl: Fix calculation of size in _load_regfile During firmware load, there could be data over reads due to calculation of lm_size and lm_sequence from block and block_size. Added bounds checking to prevent this and improved the size calculation. CRs-Fixed: 2107981 Change-Id: Ib4283951b0d6c8fb699af1f85e657981ad4c0318 Signed-off-by: Archana Sriram --- drivers/gpu/msm/adreno_a5xx.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index da0ffa83fdd7..425f6cf80813 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -1195,8 +1195,8 @@ static void _load_regfile(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); const struct firmware *fw; - uint32_t block_size = 0, block_total = 0, fw_size; - uint32_t *block; + uint64_t block_size = 0, block_total = 0; + uint32_t fw_size, *block; int ret = -EINVAL; if (!adreno_dev->gpucore->regfw_name) @@ -1218,7 +1218,8 @@ static void _load_regfile(struct adreno_device *adreno_dev) /* All offset numbers calculated from file description */ while (block_total < fw_size) { block_size = block[0]; - if (block_size >= fw_size || block_size < 2) + if (((block_total + block_size) >= fw_size) + || block_size < 5) goto err; if (block[1] != GPMU_SEQUENCE_ID) goto err; @@ -1233,6 +1234,9 @@ static void _load_regfile(struct adreno_device *adreno_dev) goto err; adreno_dev->lm_fw = fw; + + if (block[2] > (block_size - 2)) + goto err; adreno_dev->lm_sequence = block + block[2] + 3; adreno_dev->lm_size = block_size - block[2] - 2; } @@ -1245,7 +1249,7 @@ static void _load_regfile(struct adreno_device *adreno_dev) err: release_firmware(fw); KGSL_PWR_ERR(device, - "Register file failed to load sz=%d bsz=%d header=%d\n", + "Register file failed to load sz=%d bsz=%llu header=%d\n", fw_size, block_size, ret); return; } -- GitLab From 925d65f0df2276bc162523028f7b7044ea8e9739 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Thu, 21 Sep 2017 13:55:53 -0700 Subject: [PATCH 0979/5498] cnss2: Return proper error code for nonsupported devices Platform driver only forces FW assert for supported devices. Return proper error code for nonsupported devices so that WLAN host driver can handle properly. Change-Id: Iaf88f64564b14425404404db6a7789a7913db74f CRs-fixed: 2059087 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 8277de861d63..46eb3002a15a 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1613,7 +1613,7 @@ int cnss_force_fw_assert(struct device *dev) if (plat_priv->device_id == QCA6174_DEVICE_ID) { cnss_pr_info("Forced FW assert is not supported\n"); - return -EINVAL; + return -EOPNOTSUPP; } if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { -- GitLab From 50cc8324de0239dbac37b9b917e2d349d4325688 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Fri, 22 Sep 2017 12:49:15 -0700 Subject: [PATCH 0980/5498] cnss2: Set debug state when using debugfs dev_boot node When using debugfs dev_boot node to trigger firmware download, debug state should be set as well so that subsequence shutdown and powerup can be handled correctly. Change-Id: Id85d28dca104f8938d78655d682a0a7d1491c04c CRs-fixed: 2059087 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c index c69f0a488c52..047a988736f3 100644 --- a/drivers/net/wireless/cnss2/debug.c +++ b/drivers/net/wireless/cnss2/debug.c @@ -158,6 +158,7 @@ static ssize_t cnss_dev_boot_debug_write(struct file *fp, } else if (sysfs_streq(cmd, "enumerate")) { ret = cnss_pci_init(plat_priv); } else if (sysfs_streq(cmd, "download")) { + set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state); ret = cnss_pci_start_mhi(plat_priv->bus_priv); } else if (sysfs_streq(cmd, "linkup")) { ret = cnss_resume_pci_link(plat_priv->bus_priv); -- GitLab From fbb32aea161a5242b4c0599624416579ce49a0eb Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:07 +0100 Subject: [PATCH 0981/5498] UPSTREAM: ixgbe: convert to CYCLECOUNTER_MASK macro. commit d312da293f787e1b19c57acb58e8c1b171c4a04a upstream. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller Change-Id: I8d713e4ff459ee382228fe24ab36fcebf19dc26e Signed-off-by: Amit Pundir --- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 5fd4b5271f9a..abe176916cf7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -802,7 +802,7 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) memset(&adapter->cc, 0, sizeof(adapter->cc)); adapter->cc.read = ixgbe_ptp_read; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.shift = shift; adapter->cc.mult = 1; -- GitLab From f4fe97815f270a7875fd6e39ba605c6b35dfbe86 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:06 +0100 Subject: [PATCH 0982/5498] BACKPORT: igb: convert to CYCLECOUNTER_MASK macro. commit b57c894040893da41085334d31f159df94c814b4 upstream. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller Change-Id: I2c1c714010737051deb1202aa140871c067fa6d5 Signed-off-by: Amit Pundir --- drivers/net/ethernet/intel/igb/igb_ptp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 794c139f0cc0..5573ca17d3a4 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -770,7 +770,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.settime = igb_ptp_settime_82576; adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->cc.read = igb_ptp_read_82576; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.mult = 1; adapter->cc.shift = IGB_82576_TSYNC_SHIFT; /* Dial the nominal frequency. */ @@ -790,7 +790,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.settime = igb_ptp_settime_82576; adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->cc.read = igb_ptp_read_82580; - adapter->cc.mask = CLOCKSOURCE_MASK(IGB_NBITS_82580); + adapter->cc.mask = CYCLECOUNTER_MASK(IGB_NBITS_82580); adapter->cc.mult = 1; adapter->cc.shift = 0; /* Enable the timer functions by clearing bit 31. */ -- GitLab From b506dcce0411cd941f4eeb81d05240862a26a879 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 22 Jan 2015 20:52:10 +0000 Subject: [PATCH 0983/5498] UPSTREAM: arm64: dump: Fix implicit inclusion of definition for PCI_IOBASE commit 284be28565efe262a81972d12e9264d4824a4ced upstream. Since c9465b4ec37a68425 (arm64: add support to dump the kernel page tables) allmodconfig has failed to build on arm64 as a result of: ../arch/arm64/mm/dump.c:55:20: error: 'PCI_IOBASE' undeclared here (not in a function) Fix this by explicitly including io.h to ensure that a definition is present. Signed-off-by: Mark Brown Signed-off-by: Will Deacon Change-Id: I156b2208eea4f7f81a4ed7aae9062b7e2ef5b6c7 Signed-off-by: Amit Pundir --- arch/arm64/mm/dump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/mm/dump.c b/arch/arm64/mm/dump.c index bf69601be546..d7f71f87c8ed 100644 --- a/arch/arm64/mm/dump.c +++ b/arch/arm64/mm/dump.c @@ -15,6 +15,7 @@ */ #include #include +#include #include #include #include -- GitLab From 5ddd36ccec31a6c4483062eb20580ceb38aa39b2 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:05 +0100 Subject: [PATCH 0984/5498] BACKPORT: e1000e: convert to CYCLECOUNTER_MASK macro. commit 4d045b4c06b1f7d65d8e69d39821dcfaa783feea upstream. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller Change-Id: Ia908fe68139ebf963b2c19d444a1418322bf4151 Signed-off-by: Amit Pundir --- drivers/net/ethernet/intel/e1000e/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 247335d2c7ec..af3df229d47b 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4192,7 +4192,7 @@ static int e1000_sw_init(struct e1000_adapter *adapter) /* Setup hardware time stamping cyclecounter */ if (adapter->flags & FLAG_HAS_HW_TIMESTAMP) { adapter->cc.read = e1000e_cyclecounter_read; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.mult = 1; /* cc.shift set in e1000e_get_base_tininca() */ -- GitLab From ad82ff52644b1120c4e20c7be476dd24b219776e Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 2 Jan 2015 20:22:08 +0100 Subject: [PATCH 0985/5498] UPSTREAM: mlx4: include clocksource.h again commit d9f393734af52b7b09f02439164cc7182e17063c upstream. This driver uses the function, clocksource_khz2mult, and so it really must include clocksource.h. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller Change-Id: I9d7769c54e30581331e75c58deef38791baef3e6 Signed-off-by: Amit Pundir --- drivers/net/ethernet/mellanox/mlx4/en_clock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index 57dda95b67d8..682daf86e74f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -32,6 +32,7 @@ */ #include +#include #include "mlx4_en.h" -- GitLab From ef07fb6b43ef8b3db71589429816597578c03b7b Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Sat, 23 Sep 2017 17:04:28 +0530 Subject: [PATCH 0986/5498] ARM: dts: msm: Add IOT support for SDM450 Add device tree file to support SDM450 on IOT MTP platform. Change-Id: If2ba7a7c75351d633850edc16d6ec103605d0f77 Signed-off-by: Neeraj Upadhyay --- arch/arm/boot/dts/qcom/Makefile | 3 +- arch/arm/boot/dts/qcom/sdm450-iot-mtp.dts | 90 +++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 arch/arm/boot/dts/qcom/sdm450-iot-mtp.dts diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 6a476dbaca86..183b6b93f2ff 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -281,7 +281,8 @@ dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \ sdm450-mtp.dtb \ sdm450-qrd.dtb \ sdm450-pmi8940-mtp.dtb \ - sdm450-pmi8937-mtp.dtb + sdm450-pmi8937-mtp.dtb \ + sdm450-iot-mtp.dtb dtb-$(CONFIG_ARCH_MDM9607) += mdm9607-rumi.dtb \ mdm9607-cdp.dtb \ diff --git a/arch/arm/boot/dts/qcom/sdm450-iot-mtp.dts b/arch/arm/boot/dts/qcom/sdm450-iot-mtp.dts new file mode 100644 index 000000000000..8ceb70db58c8 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm450-iot-mtp.dts @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "sdm450.dtsi" +#include "msm-pmi8950.dtsi" +#include "msm8953-mtp.dtsi" +#include "msm8953-pmi8950.dtsi" +#include "msm8953-camera-sensor-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 IOT MTP"; + compatible = "qcom,sdm450-mtp", "qcom,sdm450", "qcom,mtp"; + qcom,board-id = <8 2>; +}; + +&rpm_bus { + rpm-regulator-ldoa18 { + compatible = "qcom,rpm-smd-regulator-resource"; + qcom,resource-name = "ldoa"; + qcom,resource-id = <18>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + + pm8953_l18: regulator-l18 { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pm8953_l18"; + qcom,set = <3>; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <3300000>; + qcom,init-voltage = <2700000>; + }; + }; +}; + +&modem_mem { + reg = <0x0 0x86c00000 0x0 0x1e00000>; +}; + +&adsp_fw_mem { + reg = <0x0 0x88a00000 0x0 0x1100000>; +}; + +&wcnss_fw_mem { + reg = <0x0 0x89b00000 0x0 0x700000>; +}; + +&soc { + hbtp { + status = "disabled"; + }; +}; + +&gpio_key_suspend { + config { + /delete-property/ bias-pull-up; + bias-pull-down; + }; +}; + +/{ + mtp_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <15>; + #include "batterydata-itech-3000mah.dtsi" + #include "batterydata-ascent-3450mAh.dtsi" + }; +}; + +&pmi8950_fg { + qcom,battery-data = <&mtp_batterydata>; +}; + +&pmi8950_charger { + qcom,battery-data = <&mtp_batterydata>; + qcom,chg-led-sw-controls; + qcom,chg-led-support; + qcom,external-typec; + qcom,typec-psy-name = "typec"; +}; -- GitLab From 03e42ff6fe9b7418b80c13fe9584629c0f095fb7 Mon Sep 17 00:00:00 2001 From: Ray Zhang Date: Thu, 27 Oct 2016 16:15:34 +0800 Subject: [PATCH 0987/5498] ARM: dts: msm: add a new panel driver to enable display for QVR8998 These are the panel settings for s6e3ha3 amoled panel. As QVR8998 will use this panel for display, so add a new file for it. Snapshot of hw-panel-samsung-s6e3ha3x01-5p7-1440p-dual0-cmd.dtsi as of a805a3e1e791ec0fb931d1f44bd70f3307532ec5 to enable display for QVR8998. Rename the file to dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi and modify these panel settings in this file for QVR8998 display. Squash commit messages: commit a805a3e1e791 ("arm64/dts: angler: Don't blank screen when leaving low persistence mode") commit 0e4484ea2ceb ("dts: Reduce low-persistence brightness to 50%") commit 651c6dd35a1f ("dts: msm: Sets the low-persist pulse to be at cycle start") commit 2fc4b8aa55dd ("Add ioctl for low persistence display mode to MDSS driver.") commit 1b0565b24e19 ("Revert "ARM64: dts: angler: Enable AMOLED ACL function."") commit 3200385cb224 ("ARM64: dts: angler: Reduce delay time in Amoled initial code to save the screen on time.") commit de8be2535af7 ("ARM64: dts: angler: Enable qcom,dynamic-dsi-timing-enable for amoled command mode panel") commit 0cce00b5a108 ("Angler: LCD: enable EOT packet transmission") commit 16cd03800bba ("angler: LCD: enable partial update in dtsi") commit f1f63dfc50c9 ("ARM64: dts: angler: Enable AMOLED ACL function.") commit 900833cdd0c9 ("angler: LCD: send display on commands one time") commit 4afd0a7929f1 ("anlger: LCD: enable lcd esd effect recovery function") commit 7ae89bce8295 ("angler: LCD: amend LCD physical width and height dimension") commit 5eff7f8c1c41 ("angler: LCD: fix tearing at the top part of the screen") commit cb8d1be44bcc ("angler: fix flickers when low battery finally") commit 41e4849b13c7 ("angler-kernel: fix bugs of dts before angler-v4") commit 1bd832f295f5 ("ARM64: dts: angler: add dts configuration for v4 device.") Git-commit: a805a3e1e791ec0fb931d1f44bd70f3307532ec5 Git-repo: https://source.codeaurora.org/quic/la/kernel/msm-3.10/ Change-Id: I91a6cfcc3b78159cea505e388749e1610dadeae6 Signed-off-by: Ray Zhang --- ...anel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi diff --git a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi new file mode 100644 index 000000000000..7bd844ae6770 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi @@ -0,0 +1,91 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_dual_s6e3ha3_amoled_cmd: qcom,mdss_dsi_s6e3ha3_amoled_wqhd_cmd{ + qcom,mdss-dsi-panel-name = + "Dual s6e3ha3 amoled cmd mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <100>; + qcom,mdss-dsi-h-back-porch = <100>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <31>; + qcom,mdss-dsi-v-front-porch = <30>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-on-command = [05 01 00 00 05 00 02 11 00 + 39 01 00 00 00 00 05 2a 00 00 05 9f + 39 01 00 00 00 00 05 2b 00 00 09 ff + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 02 b0 10 + 39 01 00 00 00 00 02 b5 a0 + 39 01 00 00 00 00 02 c4 03 + 39 01 00 00 00 00 0a + f6 42 57 37 00 aa cc d0 00 00 + 39 01 00 00 00 00 02 f9 03 + 39 01 00 00 00 00 14 + c2 00 00 d8 d8 00 80 2b 05 08 + 0e 07 0b 05 0d 0a 15 13 20 1e + 39 01 00 00 78 00 03 f0 a5 a5 + 39 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 02 53 20 + 39 01 00 00 00 00 02 51 60 + 05 01 00 00 05 00 02 29 00]; + qcom,mdss-dsi-off-command = [05 01 00 00 3c 00 02 28 00 + 05 01 00 00 b4 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-tx-eot-append; + qcom,dcs-cmd-by-left; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-panel-timings = + [eb 38 26 00 6a 66 32 3c 2f 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x2c>; + qcom,mdss-dsi-t-clk-pre = <0x1c>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <122>; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + }; +}; -- GitLab From 2f06b27460376f952c75fe43e39b896f88622799 Mon Sep 17 00:00:00 2001 From: Yahui Wang Date: Mon, 12 Dec 2016 15:50:38 +0800 Subject: [PATCH 0988/5498] msm: mdss: Add low persistence mode support for display Snapshot of low persistence mode changes as of 26cfe58bd94a ("msm: mdss: Add support for low persistence display mode") and a805a3e1e791 ("arm64/dts: angler: Don't blank screen when leaving low persistence mode"), to add low persistence mode support for display. Also, add some changes to optimize the implementation for LP mode and fix crash issue when VR APP is up. CRs-Fixed: 1104888 1108207 Change-Id: I509fb5c87d93e2416882942a226cb9b48bc1d3bf Signed-off-by: Yahui Wang --- .../devicetree/bindings/fb/mdss-dsi-panel.txt | 27 ++++++ ...anel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi | 56 ++++++++++++- drivers/video/msm/mdss/mdss_dsi.h | 2 + drivers/video/msm/mdss/mdss_dsi_panel.c | 63 ++++++++++++++ drivers/video/msm/mdss/mdss_fb.c | 82 ++++++++++++++++++- drivers/video/msm/mdss/mdss_fb.h | 3 +- drivers/video/msm/mdss/mdss_panel.h | 9 ++ 7 files changed, 237 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 93512a486f1f..444b4104e774 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -61,6 +61,30 @@ Required properties: transmitted byte 5, 6: 16 bits length in network byte order byte 7 and beyond: number byte of payload +- qcom,mdss-dsi-lp-mode-on: This is used to enable display low persistence mode. + A byte stream formed by multiple dcs packets base on + qcom dsi controller protocol. + byte 0: dcs data type + byte 1: set to indicate this is an individual packet + (no chain) + byte 2: virtual channel number + byte 3: expect ack from client (dcs read command) + byte 4: wait number of specified ms after dcs command + transmitted + byte 5, 6: 16 bits length in network byte order + byte 7 and beyond: number byte of payload +- qcom,mdss-dsi-lp-mode-off: This is used to disable display low persistence mode. + A byte stream formed by multiple dcs packets base on + qcom dsi controller protocol. + byte 0: dcs data type + byte 1: set to indicate this is an individual packet + (no chain) + byte 2: virtual channel number + byte 3: expect ack from client (dcs read command) + byte 4: wait number of specified ms after dcs command + transmitted + byte 5, 6: 16 bits length in network byte order + byte 7 and beyond: number byte of payload - qcom,mdss-dsi-post-panel-on-command: same as "qcom,mdss-dsi-on-command" except commands are sent after displaying an image. @@ -611,6 +635,9 @@ Example: qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command = [22 01 00 00 00 00 00]; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-lp-mode-on = [32 01 00 00 00 00 02 00 00 + 29 01 00 00 10 00 02 FF 99]; + qcom,mdss-dsi-lp-mode-off = [22 01 00 00 00 00 00]; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_suspend_resume_mode"; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi index 7bd844ae6770..6d91e72851ec 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -57,6 +57,60 @@ 05 01 00 00 b4 00 02 10 00]; qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-lp-mode-on = [39 00 00 00 05 00 03 f0 5a 5a + 39 00 00 00 05 00 03 f1 5a 5a + 39 00 00 00 05 00 03 fc 5a 5a + 39 00 00 00 05 00 02 b0 17 + 39 00 00 00 05 00 02 cb 10 + 39 00 00 00 05 00 02 b0 2d + 39 00 00 00 05 00 02 cb cd + 39 00 00 00 05 00 02 b0 0e + 39 00 00 00 05 00 02 cb 02 + 39 00 00 00 05 00 02 b0 0f + 39 00 00 00 05 00 02 cb 09 + 39 00 00 00 05 00 02 b0 02 + 39 00 00 00 05 00 02 f2 c9 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f2 c0 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f4 aa + 39 00 00 00 05 00 02 b0 08 + 39 00 00 00 05 00 02 b1 30 + 39 00 00 00 05 00 02 b0 09 + 39 00 00 00 05 00 02 b1 0a + 39 00 00 00 05 00 02 b0 0d + 39 00 00 00 05 00 02 b1 10 + 39 00 00 00 05 00 02 b0 00 + 39 00 00 00 05 00 02 f7 03 + 39 00 00 00 05 00 02 fe 30 + 39 01 00 00 05 00 02 fe b0]; + qcom,mdss-dsi-lp-mode-off = [39 00 00 00 05 00 03 f0 5a 5a + 39 00 00 00 05 00 03 f1 5a 5a + 39 00 00 00 05 00 03 fc 5a 5a + 39 00 00 00 05 00 02 b0 2d + 39 00 00 00 05 00 02 cb 4d + 39 00 00 00 05 00 02 b0 17 + 39 00 00 00 05 00 02 cb 04 + 39 00 00 00 05 00 02 b0 0e + 39 00 00 00 05 00 02 cb 06 + 39 00 00 00 05 00 02 b0 0f + 39 00 00 00 05 00 02 cb 05 + 39 00 00 00 05 00 02 b0 02 + 39 00 00 00 05 00 02 f2 b8 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f2 80 + 39 00 00 00 05 00 02 b0 03 + 39 00 00 00 05 00 02 f4 8a + 39 00 00 00 05 00 02 b0 08 + 39 00 00 00 05 00 02 b1 10 + 39 00 00 00 05 00 02 b0 09 + 39 00 00 00 05 00 02 b1 0a + 39 00 00 00 05 00 02 b0 0d + 39 00 00 00 05 00 02 b1 80 + 39 00 00 00 05 00 02 b0 00 + 39 00 00 00 05 00 02 f7 03 + 39 00 00 00 05 00 02 fe 30 + 39 01 00 00 05 00 02 fe b0]; qcom,mdss-dsi-h-sync-pulse = <0>; qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; qcom,mdss-dsi-lane-map = "lane_map_0123"; diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h index 420e740d0288..d4b3cb3ba17e 100644 --- a/drivers/video/msm/mdss/mdss_dsi.h +++ b/drivers/video/msm/mdss/mdss_dsi.h @@ -470,6 +470,8 @@ struct mdss_dsi_ctrl_pdata { struct dsi_panel_cmds post_dms_on_cmds; struct dsi_panel_cmds post_panel_on_cmds; struct dsi_panel_cmds off_cmds; + struct dsi_panel_cmds lp_on_cmds; + struct dsi_panel_cmds lp_off_cmds; struct dsi_panel_cmds status_cmds; struct dsi_panel_cmds idle_on_cmds; /* for lp mode */ struct dsi_panel_cmds idle_off_cmds; diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c index 0633be15f261..37a4f20cc2af 100644 --- a/drivers/video/msm/mdss/mdss_dsi_panel.c +++ b/drivers/video/msm/mdss/mdss_dsi_panel.c @@ -161,6 +161,25 @@ int mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0, return mdss_dsi_cmdlist_put(ctrl, &cmdreq); } +static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if ((pinfo->dcs_cmd_by_left) && (ctrl->ndx != DSI_CTRL_LEFT)) + return; + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *pcmds, u32 flags) { @@ -665,6 +684,38 @@ end: return 0; } +static int mdss_dsi_panel_apply_display_setting(struct mdss_panel_data *pdata, + u32 mode) +{ + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct dsi_panel_cmds *lp_on_cmds; + struct dsi_panel_cmds *lp_off_cmds; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + lp_on_cmds = &ctrl->lp_on_cmds; + lp_off_cmds = &ctrl->lp_off_cmds; + + /* Apply display settings for low-persistence mode */ + if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_ON) && + (lp_on_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_on_cmds); + else if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_OFF) && + (lp_off_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_off_cmds); + else + return -EINVAL; + + pr_debug("%s: Persistence mode %d applied\n", __func__, mode); + return 0; +} + static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, int mode) { @@ -838,6 +889,9 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) mdss_dsi_panel_on_hdmi(ctrl, pinfo); + /* Ensure low persistence mode is set as before */ + mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode); + end: pr_debug("%s:-\n", __func__); return ret; @@ -2019,6 +2073,12 @@ static int mdss_dsi_parse_panel_features(struct device_node *np, __func__, __LINE__); } + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_on_cmds, + "qcom,mdss-dsi-lp-mode-on", NULL); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_off_cmds, + "qcom,mdss-dsi-lp-mode-off", NULL); + return 0; } @@ -2807,12 +2867,15 @@ int mdss_dsi_panel_init(struct device_node *node, pinfo->dynamic_switch_pending = false; pinfo->is_lpm_mode = false; pinfo->esd_rdy = false; + pinfo->persist_mode = false; ctrl_pdata->on = mdss_dsi_panel_on; ctrl_pdata->post_panel_on = mdss_dsi_post_panel_on; ctrl_pdata->off = mdss_dsi_panel_off; ctrl_pdata->low_power_config = mdss_dsi_panel_low_power_config; ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl; + ctrl_pdata->panel_data.apply_display_setting = + mdss_dsi_panel_apply_display_setting; ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; ctrl_pdata->panel_data.get_idle = mdss_dsi_panel_get_idle_mode; return 0; diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index d55bde099f89..deb0887735c6 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -816,6 +816,73 @@ static ssize_t mdss_fb_get_dfps_mode(struct device *dev, return ret; } +static ssize_t mdss_fb_change_persist_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_panel_info *pinfo = NULL; + struct mdss_panel_data *pdata; + int ret = 0; + u32 persist_mode; + + if (!mfd || !mfd->panel_info) { + pr_err("%s: Panel info is NULL!\n", __func__); + return len; + } + + pinfo = mfd->panel_info; + + if (kstrtouint(buf, 0, &persist_mode)) { + pr_err("kstrtouint buf error!\n"); + return len; + } + + mutex_lock(&mfd->mdss_sysfs_lock); + if (mdss_panel_is_power_off(mfd->panel_power_state)) { + pinfo->persist_mode = persist_mode; + goto end; + } + + mutex_lock(&mfd->bl_lock); + + pdata = dev_get_platdata(&mfd->pdev->dev); + if ((pdata) && (pdata->apply_display_setting)) + ret = pdata->apply_display_setting(pdata, persist_mode); + + mutex_unlock(&mfd->bl_lock); + + if (!ret) { + pr_debug("%s: Persist mode %d\n", __func__, persist_mode); + pinfo->persist_mode = persist_mode; + } + +end: + mutex_unlock(&mfd->mdss_sysfs_lock); + return len; +} + +static ssize_t mdss_fb_get_persist_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_panel_data *pdata; + struct mdss_panel_info *pinfo; + int ret; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("no panel connected!\n"); + return -EINVAL; + } + pinfo = &pdata->panel_info; + + ret = scnprintf(buf, PAGE_SIZE, "%d\n", pinfo->persist_mode); + + return ret; +} + static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL); static DEVICE_ATTR(msm_fb_split, S_IRUGO | S_IWUSR, mdss_fb_show_split, mdss_fb_store_split); @@ -834,6 +901,8 @@ static DEVICE_ATTR(msm_fb_dfps_mode, S_IRUGO | S_IWUSR, mdss_fb_get_dfps_mode, mdss_fb_change_dfps_mode); static DEVICE_ATTR(measured_fps, S_IRUGO | S_IWUSR | S_IWGRP, mdss_fb_get_fps_info, NULL); +static DEVICE_ATTR(msm_fb_persist_mode, S_IRUGO | S_IWUSR, + mdss_fb_get_persist_mode, mdss_fb_change_persist_mode); static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_type.attr, &dev_attr_msm_fb_split.attr, @@ -846,6 +915,7 @@ static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_panel_status.attr, &dev_attr_msm_fb_dfps_mode.attr, &dev_attr_measured_fps.attr, + &dev_attr_msm_fb_persist_mode.attr, NULL, }; @@ -1196,6 +1266,7 @@ static int mdss_fb_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mfd->file_list); mutex_init(&mfd->bl_lock); + mutex_init(&mfd->mdss_sysfs_lock); mutex_init(&mfd->switch_lock); fbi_list[fbi_list_index++] = fbi; @@ -1962,7 +2033,7 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) mfd->index, ret); return ret; } - + mutex_lock(&mfd->mdss_sysfs_lock); if (mfd->op_enable == 0) { if (blank_mode == FB_BLANK_UNBLANK) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_ON; @@ -1972,7 +2043,8 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP1; else mfd->suspend.panel_power_state = MDSS_PANEL_POWER_OFF; - return 0; + ret = 0; + goto end; } pr_debug("mode: %d\n", blank_mode); @@ -1986,7 +2058,11 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) pdata->panel_info.is_lpm_mode = false; } - return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); + ret = mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); + +end: + mutex_unlock(&mfd->mdss_sysfs_lock); + return ret; } static inline int mdss_fb_create_ion_client(struct msm_fb_data_type *mfd) diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h index 205b8f7fac01..b03b3b571f27 100644 --- a/drivers/video/msm/mdss/mdss_fb.h +++ b/drivers/video/msm/mdss/mdss_fb.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -311,6 +311,7 @@ struct msm_fb_data_type { bool allow_bl_update; u32 bl_level_scaled; struct mutex bl_lock; + struct mutex mdss_sysfs_lock; bool ipc_resume; struct platform_device *pdev; diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h index d3256783722c..84e54759154d 100644 --- a/drivers/video/msm/mdss/mdss_panel.h +++ b/drivers/video/msm/mdss/mdss_panel.h @@ -117,6 +117,11 @@ enum { MDSS_PANEL_POWER_LP2, }; +enum { + MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0, + MDSS_PANEL_LOW_PERSIST_MODE_ON, +}; + enum { MODE_GPIO_NOT_VALID = 0, MODE_GPIO_HIGH, @@ -743,6 +748,9 @@ struct mdss_panel_info { /* debugfs structure for the panel */ struct mdss_panel_debugfs_info *debugfs_info; + /* persistence mode on/off */ + bool persist_mode; + /* HDR properties of display panel*/ struct mdss_panel_hdr_properties hdr_properties; }; @@ -784,6 +792,7 @@ struct mdss_panel_timing { struct mdss_panel_data { struct mdss_panel_info panel_info; void (*set_backlight) (struct mdss_panel_data *pdata, u32 bl_level); + int (*apply_display_setting)(struct mdss_panel_data *pdata, u32 mode); unsigned char *mmss_cc_base; /** -- GitLab From dc113aa121730a7aceb603a6ef113cefc5182e54 Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Sat, 23 Sep 2017 17:05:57 +0530 Subject: [PATCH 0989/5498] defconfig: msm: Add CONFIG_ARCH_SDM450 for apq8053 IOT Add CONFIG_ARCH_SDM450 to defconfigs for apq8053 IOT. Change-Id: Ibbf8f9841d99515d1bdccd2bed166f8138b0eae9 Signed-off-by: Neeraj Upadhyay --- arch/arm/configs/apq8053_IoE-perf_defconfig | 1 + arch/arm64/configs/apq8053_IoE-perf_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/apq8053_IoE-perf_defconfig b/arch/arm/configs/apq8053_IoE-perf_defconfig index f6e9620c4027..fc40c2785112 100644 --- a/arch/arm/configs/apq8053_IoE-perf_defconfig +++ b/arch/arm/configs/apq8053_IoE-perf_defconfig @@ -42,6 +42,7 @@ CONFIG_ARCH_MSM8916=y CONFIG_ARCH_MSM8937=y CONFIG_ARCH_MSM8940=y CONFIG_ARCH_MSM8953=y +CONFIG_ARCH_SDM450=y CONFIG_SMP=y CONFIG_SCHED_MC=y CONFIG_NR_CPUS=8 diff --git a/arch/arm64/configs/apq8053_IoE-perf_defconfig b/arch/arm64/configs/apq8053_IoE-perf_defconfig index 684b75be27f5..cc1d1e38c7e2 100644 --- a/arch/arm64/configs/apq8053_IoE-perf_defconfig +++ b/arch/arm64/configs/apq8053_IoE-perf_defconfig @@ -36,6 +36,7 @@ CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_MSM=y CONFIG_ARCH_MSM8953=y +CONFIG_ARCH_SDM450=y CONFIG_PCI_MSM=y CONFIG_SCHED_MC=y CONFIG_PREEMPT=y -- GitLab From adc90fde83f7c89fbfd06aed75d4eee0bc449ce8 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Date: Mon, 19 Sep 2016 20:33:22 +0530 Subject: [PATCH 0990/5498] msm: kgsl: Reschedule idle work in case transition to idle state fails Reschedule the idle work in case transition to idle state is rejected because the GPU is busy. This change avoids the condition where transition to NAP state gets rejected due to a pending IRQ which is currently getting served by IRQ handler because of which GPU remains in active state even when GPU is idle. Change-Id: I472a30b6a0e83cdd6957ed12eaa39d0c7731fcb5 Signed-off-by: Deepak Kumar --- drivers/gpu/msm/kgsl_pwrctrl.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 9ea44997da56..288909cb58f6 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -2334,21 +2334,39 @@ void kgsl_idle_check(struct work_struct *work) { struct kgsl_device *device = container_of(work, struct kgsl_device, idle_check_ws); + int ret = 0; + unsigned int requested_state; + WARN_ON(device == NULL); if (device == NULL) return; mutex_lock(&device->mutex); + requested_state = device->requested_state; + if (device->state == KGSL_STATE_ACTIVE || device->state == KGSL_STATE_NAP || device->state == KGSL_STATE_DEEP_NAP) { - if (!atomic_read(&device->active_cnt)) - kgsl_pwrctrl_change_state(device, + if (!atomic_read(&device->active_cnt)) { + ret = kgsl_pwrctrl_change_state(device, device->requested_state); + if (ret == -EBUSY) { + /* + * If the GPU is currently busy, restore + * the requested state and reschedule + * idle work. + */ + kgsl_pwrctrl_request_state(device, + requested_state); + kgsl_schedule_work(&device->idle_check_ws); + } + } + + if (!ret) + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); - kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); if (device->state == KGSL_STATE_ACTIVE) mod_timer(&device->idle_timer, jiffies + -- GitLab From 4f02d85d8ad9cf4adc098b82532d74446d71f745 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Date: Wed, 21 Jun 2017 13:12:33 +0530 Subject: [PATCH 0991/5498] msm: kgsl: Limit the frequency of logging on memory allocation failure Excessive logging due to several successive memory allocation failure may cause a watchdog bite. Hence, this change adds ratelimit to logging on memory allocation failure. Change-Id: I8e5d78918a32c48ef7fa587f3dc63cbd1f065d5f Signed-off-by: Deepak Kumar --- drivers/gpu/msm/kgsl_sharedmem.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 7f4a5a3b251f..27733b068434 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_sharedmem.h" @@ -699,6 +700,10 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, size_t len; unsigned int align; + static DEFINE_RATELIMIT_STATE(_rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + size = PAGE_ALIGN(size); if (size == 0 || size > UINT_MAX) return -EINVAL; @@ -761,7 +766,8 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, */ memdesc->size = (size - len); - if (sharedmem_noretry_flag != true) + if (sharedmem_noretry_flag != true && + __ratelimit(&_rs)) KGSL_CORE_ERR( "Out of memory: only allocated %lldKB of %lldKB requested\n", (size - len) >> 10, size >> 10); -- GitLab From 3c470fad5101efcebdf66fb6c6c9b279cb647549 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Date: Tue, 20 Jun 2017 16:06:52 +0530 Subject: [PATCH 0992/5498] msm: kgsl: Directly return page size of the supported pool In current code, if a request comes to allocate a page of a size for which pool is not supported EAGAIN is returned with a page size of PAGE_SIZE << --order. This is not efficient as it results in multiple retries in case pool of size = PAGE_SIZE << --order is also not supported. Instead of retrying with lower order page in a sequential manner this change directly returns the page size of the pool that is supported. Change-Id: Ib82ae5be7e4109fdc0a3d72bcbcd4b47cfb2e266 Signed-off-by: Deepak Kumar --- drivers/gpu/msm/kgsl_pool.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index bb92b8b79d93..c31a85b07447 100644 --- a/drivers/gpu/msm/kgsl_pool.c +++ b/drivers/gpu/msm/kgsl_pool.c @@ -280,6 +280,17 @@ static int kgsl_pool_idx_lookup(unsigned int order) return -ENOMEM; } +static int kgsl_pool_get_retry_order(unsigned int order) +{ + int i; + + for (i = kgsl_num_pools-1; i > 0; i--) + if (order >= kgsl_pools[i].pool_order) + return kgsl_pools[i].pool_order; + + return 0; +} + /** * kgsl_pool_alloc_page() - Allocate a page of requested size * @page_size: Size of the page to be allocated @@ -326,7 +337,7 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages, if (pool == NULL) { /* Retry with lower order pages */ if (order > 0) { - size = PAGE_SIZE << --order; + size = PAGE_SIZE << kgsl_pool_get_retry_order(order); goto eagain; } else { /* -- GitLab From e5fa1c0902e56587f654ed170551151618720813 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Date: Wed, 21 Jun 2017 15:16:43 +0530 Subject: [PATCH 0993/5498] msm: kgsl: Return supported page size based on available memory pools In case memory pools are supported return the page size as supported only if corresponding memory pool is available. This will increase the usage of memory pool and will reduce the overall allocation time. Change-Id: Iea84a4259b38fe9cb546419dfcbaf0a9666e7ca9 Signed-off-by: Deepak Kumar --- drivers/gpu/msm/kgsl_pool.c | 18 ++++++++++++++++++ drivers/gpu/msm/kgsl_pool.h | 3 ++- drivers/gpu/msm/kgsl_sharedmem.c | 1 - drivers/gpu/msm/kgsl_sharedmem.h | 11 ++++++++--- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index bb92b8b79d93..c9e106a4f562 100644 --- a/drivers/gpu/msm/kgsl_pool.c +++ b/drivers/gpu/msm/kgsl_pool.c @@ -411,6 +411,24 @@ void kgsl_pool_free_page(struct page *page) __free_pages(page, page_order); } +/* + * Return true if the pool of specified page size is supported + * or no pools are supported otherwise return false. + */ +bool kgsl_pool_avaialable(int page_size) +{ + int i; + + if (!kgsl_num_pools) + return true; + + for (i = 0; i < kgsl_num_pools; i++) + if (ilog2(page_size >> PAGE_SHIFT) == kgsl_pools[i].pool_order) + return true; + + return false; +} + static void kgsl_pool_reserve_pages(void) { int i, j; diff --git a/drivers/gpu/msm/kgsl_pool.h b/drivers/gpu/msm/kgsl_pool.h index 4557bf143297..25e7da10050e 100644 --- a/drivers/gpu/msm/kgsl_pool.h +++ b/drivers/gpu/msm/kgsl_pool.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -40,5 +40,6 @@ void kgsl_exit_page_pools(void); int kgsl_pool_alloc_page(int *page_size, struct page **pages, unsigned int pages_len, unsigned int *align); void kgsl_pool_free_page(struct page *p); +bool kgsl_pool_avaialable(int size); #endif /* __KGSL_POOL_H */ diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 7f4a5a3b251f..3a703d516bdb 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -27,7 +27,6 @@ #include "kgsl_device.h" #include "kgsl_log.h" #include "kgsl_mmu.h" -#include "kgsl_pool.h" /* * The user can set this from debugfs to force failed memory allocations to diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 1409d254cbab..1ec39c221dd1 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -346,6 +346,8 @@ static inline void kgsl_free_sgt(struct sg_table *sgt) } } +#include "kgsl_pool.h" + /** * kgsl_get_page_size() - Get supported pagesize * @size: Size of the page @@ -356,11 +358,14 @@ static inline void kgsl_free_sgt(struct sg_table *sgt) #ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS static inline int kgsl_get_page_size(size_t size, unsigned int align) { - if (align >= ilog2(SZ_1M) && size >= SZ_1M) + if (align >= ilog2(SZ_1M) && size >= SZ_1M && + kgsl_pool_avaialable(SZ_1M)) return SZ_1M; - else if (align >= ilog2(SZ_64K) && size >= SZ_64K) + else if (align >= ilog2(SZ_64K) && size >= SZ_64K && + kgsl_pool_avaialable(SZ_64K)) return SZ_64K; - else if (align >= ilog2(SZ_8K) && size >= SZ_8K) + else if (align >= ilog2(SZ_8K) && size >= SZ_8K && + kgsl_pool_avaialable(SZ_8K)) return SZ_8K; else return PAGE_SIZE; -- GitLab From 677d1809af6499cd18c2961d84a8a186c35f1f62 Mon Sep 17 00:00:00 2001 From: Ashish Garg Date: Thu, 18 May 2017 15:54:39 +0530 Subject: [PATCH 0994/5498] msm: mdss: add recovery if TE is not coming from the panel Enable TE irq for the panel when there is a pingpong timeout. Wait for TE to come for five vsync cycles before starting the recovery for panel dead. If the TE comes from the panel during the wait, call panic for pingpong timeout. Change-Id: I88d06562e3e19880bb327b5f347ed79057f0e8c3 Signed-off-by: Ashish Garg Signed-off-by: Vishnuvardhan Prodduturi --- drivers/video/msm/mdss/mdss_dsi.c | 33 ++++++++++++++++++++++ drivers/video/msm/mdss/mdss_dsi_host.c | 11 ++------ drivers/video/msm/mdss/mdss_fb.c | 11 ++++++++ drivers/video/msm/mdss/mdss_mdp_intf_cmd.c | 25 ++++++++++++++-- drivers/video/msm/mdss/mdss_panel.h | 3 ++ 5 files changed, 71 insertions(+), 12 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c index d4768ece7fc6..33a77bfde0e1 100644 --- a/drivers/video/msm/mdss/mdss_dsi.c +++ b/drivers/video/msm/mdss/mdss_dsi.c @@ -1754,6 +1754,18 @@ static int mdss_dsi_post_panel_on(struct mdss_panel_data *pdata) return 0; } +static irqreturn_t test_hw_vsync_handler(int irq, void *data) +{ + struct mdss_panel_data *pdata = (struct mdss_panel_data *)data; + + pr_debug("HW VSYNC\n"); + MDSS_XLOG(0xaaa, irq); + complete_all(&pdata->te_done); + if (pdata->next) + complete_all(&pdata->next->te_done); + return IRQ_HANDLED; +} + int mdss_dsi_cont_splash_on(struct mdss_panel_data *pdata) { int ret = 0; @@ -3163,6 +3175,8 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) struct device_node *dsi_pan_node = NULL; const char *ctrl_name; struct mdss_util_intf *util; + static int te_irq_registered; + struct mdss_panel_data *pdata; if (!pdev || !pdev->dev.of_node) { pr_err("%s: pdev not found for DSI controller\n", __func__); @@ -3289,6 +3303,23 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); } + pdata = &ctrl_pdata->panel_data; + init_completion(&pdata->te_done); + if (pdata->panel_info.type == MIPI_CMD_PANEL) { + if (!te_irq_registered) { + rc = devm_request_irq(&pdev->dev, + gpio_to_irq(pdata->panel_te_gpio), + test_hw_vsync_handler, IRQF_TRIGGER_FALLING, + "VSYNC_GPIO", &ctrl_pdata->panel_data); + if (rc) { + pr_err("%s: TE request_irq failed\n", __func__); + goto error_shadow_clk_deinit; + } + te_irq_registered = 1; + disable_irq_nosync(gpio_to_irq(pdata->panel_te_gpio)); + } + } + rc = mdss_dsi_get_bridge_chip_params(pinfo, ctrl_pdata, pdev); if (rc) { pr_err("%s: Failed to get bridge params\n", __func__); @@ -4035,6 +4066,7 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, struct mdss_dsi_ctrl_pdata *ctrl_pdata) { struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + struct mdss_panel_data *pdata = &ctrl_pdata->panel_data; /* * If disp_en_gpio has been set previously (disp_en_gpio > 0) @@ -4056,6 +4088,7 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, if (!gpio_is_valid(ctrl_pdata->disp_te_gpio)) pr_err("%s:%d, TE gpio not specified\n", __func__, __LINE__); + pdata->panel_te_gpio = ctrl_pdata->disp_te_gpio; ctrl_pdata->bklt_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, "qcom,platform-bklight-en-gpio", 0); diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c index c0a51211852b..799a4e6ce371 100644 --- a/drivers/video/msm/mdss/mdss_dsi_host.c +++ b/drivers/video/msm/mdss/mdss_dsi_host.c @@ -2481,15 +2481,8 @@ void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl) if (!ctrl->mdp_busy) rc = 1; spin_unlock_irqrestore(&ctrl->mdp_lock, flags); - if (!rc) { - if (mdss_dsi_mdp_busy_tout_check(ctrl)) { - pr_err("%s: timeout error\n", __func__); - MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", - "dsi0_phy", "dsi1_ctrl", "dsi1_phy", - "vbif", "vbif_nrt", "dbg_bus", - "vbif_dbg_bus", "panic"); - } - } + if (!rc && mdss_dsi_mdp_busy_tout_check(ctrl)) + pr_err("%s: timeout error\n", __func__); } pr_debug("%s: done pid=%d\n", __func__, current->pid); MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid, XLOG_FUNC_EXIT); diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index d55bde099f89..8204cacbda06 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -4393,6 +4393,7 @@ static int mdss_fb_atomic_commit_ioctl(struct fb_info *info, struct mdp_output_layer __user *output_layer_user; struct mdp_frc_info *frc_info = NULL; struct mdp_frc_info __user *frc_info_user; + struct msm_fb_data_type *mfd; ret = copy_from_user(&commit, argp, sizeof(struct mdp_layer_commit)); if (ret) { @@ -4400,6 +4401,16 @@ static int mdss_fb_atomic_commit_ioctl(struct fb_info *info, return ret; } + mfd = (struct msm_fb_data_type *)info->par; + if (!mfd) + return -EINVAL; + + if (mfd->panel_info->panel_dead) { + pr_debug("early commit return\n"); + MDSS_XLOG(mfd->panel_info->panel_dead); + return 0; + } + output_layer_user = commit.commit_v1.output_layer; if (output_layer_user) { buffer_size = sizeof(struct mdp_output_layer); diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c index c21389d82e41..1603b01bb8a8 100644 --- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c +++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,6 +21,7 @@ #include "mdss_debug.h" #include "mdss_mdp_trace.h" #include "mdss_dsi_clk.h" +#include #define MAX_RECOVERY_TRIALS 10 #define MAX_SESSIONS 2 @@ -1900,7 +1901,7 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) struct mdss_mdp_cmd_ctx *ctx; struct mdss_panel_data *pdata; unsigned long flags; - int rc = 0; + int rc = 0, te_irq; ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { @@ -1947,7 +1948,21 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) __func__, ctl->num, rc, ctx->pp_timeout_report_cnt, atomic_read(&ctx->koff_cnt)); - if (ctx->pp_timeout_report_cnt == 0) { + + /* enable TE irq to check if it is coming from the panel */ + te_irq = gpio_to_irq(pdata->panel_te_gpio); + enable_irq(te_irq); + + /* wait for 20ms to ensure we are getting the next TE */ + usleep_range(20000, 20010); + + reinit_completion(&pdata->te_done); + rc = wait_for_completion_timeout(&pdata->te_done, KOFF_TIMEOUT); + + if (!rc) { + MDSS_XLOG(0xbac); + mdss_fb_report_panel_dead(ctl->mfd); + } else if (ctx->pp_timeout_report_cnt == 0) { MDSS_XLOG(0xbad); MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt", @@ -1959,6 +1974,10 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) "dbg_bus", "vbif_dbg_bus", "panic"); mdss_fb_report_panel_dead(ctl->mfd); } + + /* disable te irq */ + disable_irq_nosync(te_irq); + ctx->pp_timeout_report_cnt++; rc = -EPERM; diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h index d3256783722c..f60b9fca7553 100644 --- a/drivers/video/msm/mdss/mdss_panel.h +++ b/drivers/video/msm/mdss/mdss_panel.h @@ -809,6 +809,9 @@ struct mdss_panel_data { /* To store dsc cfg name passed by bootloader */ char dsc_cfg_np_name[MDSS_MAX_PANEL_LEN]; struct mdss_panel_data *next; + + int panel_te_gpio; + struct completion te_done; }; struct mdss_panel_debugfs_info { -- GitLab From 21de6d2f253256ab247595e707bd05f1a9db80c1 Mon Sep 17 00:00:00 2001 From: Pranshu Gupta Date: Mon, 18 Sep 2017 13:12:22 +0530 Subject: [PATCH 0995/5498] ARM: dts: msm: Enable verity for system and vendor partition on MSM8909 This feature will help to push content in vendor and system partition at run time after execute disable-verity and reboot command. Change-Id: I65b297584e431f7888ae881bc2b3729e548818ba Signed-off-by: Pranshu Gupta --- arch/arm/boot/dts/qcom/msm8909.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 9b901df3f2b1..60dce8f7f7c7 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -117,7 +117,7 @@ dev = "/dev/block/platform/soc/7824900.sdhci/by-name/vendor"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; - fsmgr_flags = "wait"; + fsmgr_flags = "wait,verify"; status = "ok"; }; system { @@ -125,7 +125,7 @@ dev = "/dev/block/platform/soc/7824900.sdhci/by-name/system"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; - fsmgr_flags = "wait"; + fsmgr_flags = "wait,verify"; status = "ok"; }; }; -- GitLab From 4d9cf901e196032a3cb672fe86a82b73f2dd6be9 Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Tue, 4 Jul 2017 14:33:26 +0530 Subject: [PATCH 0996/5498] msm: kgsl: Add a NULL check for limit pointer KGSL power limit pointer can be error or NULL. Add a NULL check for limit pointer to avoid NULL pointer dereference. Change-Id: I4aacaddd1cd9b34f1befc21807eb7ab577f0a7f1 Signed-off-by: Hareesh Gundu --- drivers/gpu/msm/kgsl_pwrctrl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 9ea44997da56..a1d577dcfe3c 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -3086,7 +3086,7 @@ EXPORT_SYMBOL(kgsl_pwr_limits_add); void kgsl_pwr_limits_del(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; - if (IS_ERR(limit)) + if (IS_ERR_OR_NULL(limit)) return; _update_limits(limit, KGSL_PWR_DEL_LIMIT, 0); @@ -3107,7 +3107,7 @@ int kgsl_pwr_limits_set_freq(void *limit_ptr, unsigned int freq) struct kgsl_pwr_limit *limit = limit_ptr; int level; - if (IS_ERR(limit)) + if (IS_ERR_OR_NULL(limit)) return -EINVAL; pwr = &limit->device->pwrctrl; @@ -3129,7 +3129,7 @@ void kgsl_pwr_limits_set_default(void *limit_ptr) { struct kgsl_pwr_limit *limit = limit_ptr; - if (IS_ERR(limit)) + if (IS_ERR_OR_NULL(limit)) return; _update_limits(limit, KGSL_PWR_SET_LIMIT, 0); -- GitLab From 8d57689ff3cdf446d04b890c46d0166142833a0f Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Mon, 25 Sep 2017 15:30:59 +0530 Subject: [PATCH 0997/5498] clk: msm: clock-gcc-9650: Update vdd conrers for SDX20 Vdd voltage corners need to be updated for SDX20, so define a new corner map table. Change-Id: Icff3f3ca2110d94a9d07fc9130dce542ce6d4a2b Signed-off-by: Odelu Kukatla --- drivers/clk/msm/clock-gcc-9650.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/clk/msm/clock-gcc-9650.c b/drivers/clk/msm/clock-gcc-9650.c index e2912ff869b4..ea7ec78cb249 100644 --- a/drivers/clk/msm/clock-gcc-9650.c +++ b/drivers/clk/msm/clock-gcc-9650.c @@ -96,6 +96,15 @@ static void __iomem *virt_apcsbase; | BVAL(10, 8, s##_source_val), \ } +static int vdd_corner_sdx20[] = { + RPM_REGULATOR_LEVEL_NONE, /* VDD_DIG_NONE */ + RPM_REGULATOR_LEVEL_MIN_SVS, /* VDD_DIG_MIN */ + RPM_REGULATOR_LEVEL_LOW_SVS, /* VDD_DIG_LOWER */ + RPM_REGULATOR_LEVEL_SVS, /* VDD_DIG_LOW */ + RPM_REGULATOR_LEVEL_NOM, /* VDD_DIG_NOMINAL */ + RPM_REGULATOR_LEVEL_TURBO, /* VDD_DIG_HIGH */ +}; + static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL); static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner, NULL); @@ -1666,6 +1675,9 @@ static void msm_clocks_gcc_sdx20_fixup(void) usb30_mock_utmi_clk_src.freq_tbl = ftbl_usb30_mock_utmi_clk_src_sdx20; + vdd_dig.vdd_uv = vdd_corner_sdx20; + vdd_dig_ao.vdd_uv = vdd_corner_sdx20; + sdcc1_apps_clk_src.c.fmax[VDD_DIG_MIN] = 25000000; sdcc1_apps_clk_src.c.fmax[VDD_DIG_LOWER] = 50000000; sdcc1_apps_clk_src.c.fmax[VDD_DIG_LOW] = 1000000000; -- GitLab From 600bda01627de292a9ff6aa17ac9b395cc5618a2 Mon Sep 17 00:00:00 2001 From: Srinivasarao P Date: Mon, 25 Sep 2017 16:24:02 +0530 Subject: [PATCH 0998/5498] defconfig: msm: disable cp_access cpaccess module gives userspace control over system control registers so disable cp_access module. Change-Id: Ibd84fb6262710f730ef86b18e420c8e682b04263 Signed-off-by: Srinivasarao P --- arch/arm64/configs/msmcortex-perf_defconfig | 1 - arch/arm64/configs/msmcortex_defconfig | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 6d2181a2cf62..0a48d66a5ac6 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -529,7 +529,6 @@ CONFIG_MSM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_MSM_IOMMU_V1=y CONFIG_ARM_SMMU=y -CONFIG_CP_ACCESS64=y CONFIG_MSM_ADSP_LOADER=y CONFIG_MSM_MEMORY_DUMP_V2=y CONFIG_MSM_BOOT_STATS=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 387d9795d329..5d2b0cfc233e 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -542,7 +542,6 @@ CONFIG_ARM_SMMU=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y CONFIG_IOMMU_TESTS=y -CONFIG_CP_ACCESS64=y CONFIG_MSM_GLADIATOR_ERP=y CONFIG_PANIC_ON_GLADIATOR_ERROR=y CONFIG_MSM_GLADIATOR_ERP_V2=y -- GitLab From 1a25fb6aaef534c43537838601c33f85acea11e4 Mon Sep 17 00:00:00 2001 From: Arun Kumar Neelakantam Date: Fri, 26 Aug 2016 18:54:09 +0530 Subject: [PATCH 0999/5498] net: ipc_router: Fix remote port conn_info memory leak conn_info is used to connect the local port with destination port address and is freed only for remote server cleanup in SSR case which leads memory leak for other remote ports which valid conn_info. Free the conn_info structure for all remote ports during the SSR and while handling remove client message. CRs-Fixed: 1057219 Change-Id: I164a9eb308f5779d545766b18bb41184c49bbb3d Signed-off-by: Arun Kumar Neelakantam --- net/ipc_router/ipc_router_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c index b0317b688d75..d23799a5b260 100644 --- a/net/ipc_router/ipc_router_core.c +++ b/net/ipc_router/ipc_router_core.c @@ -2176,7 +2176,6 @@ static void cleanup_rmt_server(struct msm_ipc_router_xprt_info *xprt_info, { union rr_control_msg ctl; - ipc_router_reset_conn(rport_ptr); memset(&ctl, 0, sizeof(ctl)); ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER; ctl.srv.service = server->name.service; @@ -2207,6 +2206,7 @@ static void cleanup_rmt_ports(struct msm_ipc_router_xprt_info *xprt_info, server = rport_ptr->server; rport_ptr->server = NULL; mutex_unlock(&rport_ptr->rport_lock_lhb2); + ipc_router_reset_conn(rport_ptr); if (server) { cleanup_rmt_server(xprt_info, rport_ptr, server); @@ -2361,13 +2361,13 @@ static void ipc_router_reset_conn(struct msm_ipc_router_remote_port *rport_ptr) list_for_each_entry_safe(conn_info, tmp_conn_info, &rport_ptr->conn_info_list, list) { port_ptr = ipc_router_get_port_ref(conn_info->port_id); - if (!port_ptr) - continue; - mutex_lock(&port_ptr->port_lock_lhc3); - port_ptr->conn_status = CONNECTION_RESET; - mutex_unlock(&port_ptr->port_lock_lhc3); - wake_up(&port_ptr->port_rx_wait_q); - kref_put(&port_ptr->ref, ipc_router_release_port); + if (port_ptr) { + mutex_lock(&port_ptr->port_lock_lhc3); + port_ptr->conn_status = CONNECTION_RESET; + mutex_unlock(&port_ptr->port_lock_lhc3); + wake_up(&port_ptr->port_rx_wait_q); + kref_put(&port_ptr->ref, ipc_router_release_port); + } list_del(&conn_info->list); kfree(conn_info); @@ -2651,6 +2651,7 @@ static int process_rmv_client_msg(struct msm_ipc_router_xprt_info *xprt_info, server = rport_ptr->server; rport_ptr->server = NULL; mutex_unlock(&rport_ptr->rport_lock_lhb2); + ipc_router_reset_conn(rport_ptr); down_write(&server_list_lock_lha2); if (server) cleanup_rmt_server(NULL, rport_ptr, server); -- GitLab From 2fe2650c65f2a37e079c5c5a0281083f2c719c5b Mon Sep 17 00:00:00 2001 From: Bikshapathi Kothapeta Date: Wed, 13 Sep 2017 15:25:54 +0530 Subject: [PATCH 1000/5498] ARM: dts: msm: use appropriate regulators for sensors on msm8909w msm8909W BG uses pm660 regulator whereas msm8909 do not use pm660 regulator for light and mag sensors. Donot include msm8909-regulator.dtsi for 8909W BG and restrictonly to msm8909. Change-Id: I227e684384fd741c3022c2853bc08f24699b8471 Signed-off-by: Bikshapathi Kothapeta --- arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326.dts | 1 - arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 1 - arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 1 - arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 10 ++++++++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326.dts index 739b17596f3b..dba32a7ddf63 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326.dts @@ -14,7 +14,6 @@ /dts-v1/; #include "msm8909-mtp.dtsi" -#include "msm8909-regulator.dtsi" #include "msm8909-pm8916.dtsi" #include "msm8909-pm8916-mtp.dtsi" #include "apq8009-audio-external_codec.dtsi" diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 940ac8807054..eec1f068d073 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -18,7 +18,6 @@ #include "msm8909w.dtsi" #include "msm8909w-pm660-mtp.dtsi" #include "apq8009w-memory.dtsi" -#include "msm8909-regulator.dtsi" / { model = "Qualcomm Technologies, Inc. APQ8009W-PM660 BLACKGHOST WTP"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 9bff17303dc5..966eece29a49 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -18,7 +18,6 @@ #include "msm8909w.dtsi" #include "msm8909w-memory.dtsi" #include "msm8909w-pm660-mtp.dtsi" -#include "msm8909-regulator.dtsi" / { model = "Qualcomm Technologies, Inc. MSM8909W-PM660 BLACKGHOST WTP"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index 8ee54155f157..c1f83b2d77f3 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -80,6 +80,16 @@ /delete-property/ vdd-supply; /delete-property/ vlogic-supply; }; + + avago@39 { + /delete-property/ vdd-supply; + /delete-property/ vio-supply; + }; + + akm@c { + /delete-property/ vdd-supply; + /delete-property/ vio-supply; + }; }; &i2c_3 { -- GitLab From 1473ba27bc14f3910fa99f5f8a0660e9c41a83d5 Mon Sep 17 00:00:00 2001 From: Ashish Garg Date: Thu, 14 Sep 2017 13:13:16 +0530 Subject: [PATCH 1001/5498] msm: mdss: check the length of the external input buffer properly dchdr->dlen is a short variable controlled by the user-provided data (a string). If the value is negative, the condition does not pass and loop continues, also increasing the value of "len". As a result buffer overflow and overwrite occurs. Change-Id: I5eacec446c9a8b5b82fc3bc6d1281303f336d4de Signed-off-by: Ashish Garg --- drivers/video/msm/mdss/mdss_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c index d4768ece7fc6..e206a32037bd 100644 --- a/drivers/video/msm/mdss/mdss_dsi.c +++ b/drivers/video/msm/mdss/mdss_dsi.c @@ -953,7 +953,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) while (len >= sizeof(*dchdr)) { dchdr = (struct dsi_ctrl_hdr *)bp; dchdr->dlen = ntohs(dchdr->dlen); - if (dchdr->dlen > len) { + if (dchdr->dlen > len || dchdr->dlen < 0) { pr_err("%s: dtsi cmd=%x error, len=%d\n", __func__, dchdr->dtype, dchdr->dlen); kfree(buf); -- GitLab From 414dadd0ad053a16b3fdb0d45a316f826916545f Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Fri, 22 Sep 2017 16:13:01 +0530 Subject: [PATCH 1002/5498] soc: qcom: Flush mapping of cma memory before secure authentication This change flush all stale mapping of cma allocated buffer from cache before passing buffer to secure layer for authentication. Also free the memory allocated for ramdump collection. Change-Id: I4479af296e9fad2c090bc3cef23ad11ad31a7369 Signed-off-by: Avaneesh Kumar Dwivedi --- drivers/soc/qcom/subsys-pil-bg.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/subsys-pil-bg.c b/drivers/soc/qcom/subsys-pil-bg.c index e09801ba2c60..30505b790dc9 100644 --- a/drivers/soc/qcom/subsys-pil-bg.c +++ b/drivers/soc/qcom/subsys-pil-bg.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "peripheral-loader.h" #include "../../misc/qseecom_kernel.h" @@ -311,15 +312,24 @@ static int bg_auth_metadata(struct pil_desc *pil, struct tzapp_bg_req bg_tz_req; void *mdata_buf; dma_addr_t mdata_phys; + DEFINE_DMA_ATTRS(attrs); + struct device dev = {NULL}; int ret; - mdata_buf = dma_alloc_coherent(pil->dev, size, - &mdata_phys, GFP_KERNEL); + dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); + dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &attrs); + mdata_buf = dma_alloc_attrs(&dev, size, + &mdata_phys, GFP_KERNEL, &attrs); + if (!mdata_buf) { pr_err("BG_PIL: Allocation for metadata failed.\n"); return -ENOMEM; } + /* Make sure there are no mappings in PKMAP and fixmap */ + kmap_flush_unused(); + kmap_atomic_flush_unused(); + memcpy(mdata_buf, metadata, size); bg_tz_req.tzapp_bg_cmd = BGPIL_AUTH_MDT; @@ -333,7 +343,7 @@ static int bg_auth_metadata(struct pil_desc *pil, __func__); return bg_data->cmd_status; } - dma_free_coherent(pil->dev, size, mdata_buf, mdata_phys); + dma_free_attrs(&dev, size, mdata_buf, mdata_phys, &attrs); pr_debug("BG MDT Authenticated\n"); return 0; } @@ -359,6 +369,7 @@ static int bg_get_firmware_addr(struct pil_desc *pil, return 0; } + /** * bg_auth_and_xfer() - Called by Peripheral loader framework * to signal tz app to authenticate and boot bg chip. @@ -411,7 +422,6 @@ static int bg_ramdump(int enable, const struct subsys_desc *subsys) struct ramdump_segment *ramdump_segments; struct tzapp_bg_req bg_tz_req; phys_addr_t start_addr; - size_t size = SZ_1M; void *region; int ret; @@ -425,7 +435,7 @@ static int bg_ramdump(int enable, const struct subsys_desc *subsys) if (region == NULL) { dev_dbg(desc.dev, "BG PIL failure to allocate ramdump region of size %zx\n", - size); + BG_RAMDUMP_SZ); return -ENOMEM; } @@ -449,6 +459,8 @@ static int bg_ramdump(int enable, const struct subsys_desc *subsys) do_ramdump(bg_data->ramdump_dev, ramdump_segments, 1); kfree(ramdump_segments); + dma_free_attrs(desc.dev, BG_RAMDUMP_SZ, region, + start_addr, &desc.attrs); return 0; } -- GitLab From 2269b25d5c3dfa56f3af28a58bb2a5dc2c2f29c7 Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Mon, 18 Sep 2017 14:34:11 +0530 Subject: [PATCH 1003/5498] iommu: free io pgtable during domain detach. Pagetable pgd is allocated in arm_smmu_init_domain_context and is freed in arm_smmu_domain_free. Client can attach/detach domain without freeing the same. This causes memory leak. This was introduced with commit fe676929a960 ("iommu/arm-smmu: fix a DOMAIN_ATTR_DYNAMIC memory leak") Free io pgtables in arm_smmu_destroy_domain_context, which is invoked at the time of domain detach. Change-Id: Ib53e4efdd8dca33437596f3f0ccaa8ccaae79ca0 Signed-off-by: Prakash Gupta --- drivers/iommu/arm-smmu.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 24d30978f671..69293aa1e792 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1760,10 +1760,20 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); - arm_smmu_tlb_inv_context(smmu_domain); - arm_smmu_disable_clocks(smmu_domain->smmu); + if (smmu_domain->pgtbl_ops) { + free_io_pgtable_ops(smmu_domain->pgtbl_ops); + /* unassign any freed page table memory */ + if (arm_smmu_is_master_side_secure(smmu_domain)) { + arm_smmu_secure_domain_lock(smmu_domain); + arm_smmu_secure_pool_destroy(smmu_domain); + arm_smmu_unassign_table(smmu_domain); + arm_smmu_secure_domain_unlock(smmu_domain); + } + smmu_domain->pgtbl_ops = NULL; + } + free_irqs: if (cfg->irptndx != INVALID_IRPTNDX) { irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; -- GitLab From 1e65d9b47de1d81f38e628cd655d3ca87b81114a Mon Sep 17 00:00:00 2001 From: Derek Chen Date: Tue, 20 Sep 2016 18:25:13 -0400 Subject: [PATCH 1004/5498] soc: qcom: add audio apr and ion virtualization support Add support for virtualized APR and ION frontend driver for audio. Virtualized APR frontend driver resides in guest VM and supports all legacy APR APIs. It will rely on MSM_HAB to communicate with APR backend driver, which resides in physical VM, where legacy SMD is utilized for communication between APPS and ADSP. Virtualized ION driver currently is designed to extract physical memory through hypervisor abstracted ion layer, which can be shared between APPS and ADSP directly. CRs-fixed: 2047379 Signed-off-by: Derek Chen Change-Id: I7dad5aebfd1db00cd32807dc1219818a7a4e62c5 --- .../bindings/sound/qcom-audio-dev.txt | 20 + drivers/misc/qcom/Kconfig | 2 +- drivers/platform/msm/Kconfig | 3 +- drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/qdsp6v2/Makefile | 2 + drivers/soc/qcom/qdsp6v2/apr_vm.c | 1252 +++++++++++++++++ drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c | 506 +++++++ include/linux/qdsp6v2/apr.h | 6 +- include/linux/qdsp6v2/aprv2_vm.h | 116 ++ 9 files changed, 1913 insertions(+), 3 deletions(-) mode change 100755 => 100644 Documentation/devicetree/bindings/sound/qcom-audio-dev.txt create mode 100644 drivers/soc/qcom/qdsp6v2/apr_vm.c create mode 100644 drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c create mode 100644 include/linux/qdsp6v2/aprv2_vm.h diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt old mode 100755 new mode 100644 index 3c3875083b59..be9df49293f5 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -1211,6 +1211,26 @@ qcom,msm-audio-ion { memory-region = <&audio_mem>; }; +* msm-audio-ion-vm + +Required properties: + - compatible : "qcom,msm-audio-ion-vm" + +Optional properties: + - qcom,smmu-enabled: + It is possible that some MSM have SMMU in ADSP. While other + MSM use no SMMU. Audio lib introduce wrapper for ION APIs. + The wrapper needs presence of SMMU in ADSP to handle ION + APIs differently. Presence of this property means ADSP has + SMMU in it. + +Example: + +qcom,msm-audio-ion-vm { + compatible = "qcom,msm-audio-ion-vm; + qcom,smmu-enabled; +}; + * MSM8994 ASoC Machine driver Required properties: diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig index 8a343d2146cd..7baab66c282a 100644 --- a/drivers/misc/qcom/Kconfig +++ b/drivers/misc/qcom/Kconfig @@ -1,6 +1,6 @@ config MSM_QDSP6V2_CODECS bool "Audio QDSP6V2 APR support" - depends on MSM_SMD + depends on MSM_SMD || MSM_HAB help Enable Audio codecs with APR IPC protocol support between application processor and QDSP6 for B-family. APR is diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 0774fe4d29aa..b4d333666d83 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -3,7 +3,8 @@ menu "Qualcomm MSM specific device drivers" config MSM_AVTIMER tristate "Avtimer Driver" - depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 + depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 || \ + MSM_QDSP6_APRV2_VM help This driver gets the Q6 out of power collapsed state and exposes ioctl control to read avtimer tick. diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c0d99ba847d8..27469bf16ca3 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -41,6 +41,15 @@ config MSM_QDSP6_APRV2 used by audio driver to configure QDSP6's ASM, ADM and AFE. +config MSM_QDSP6_APRV2_VM + bool "Audio QDSP6 APRv2 virtualization support" + depends on MSM_HAB + help + Enable APRv2 IPC protocol support over + HAB between application processor and + QDSP6. APR is used by audio driver to + configure QDSP6's ASM, ADM and AFE. + config MSM_GLADIATOR_ERP tristate "GLADIATOR coherency interconnect error reporting driver" help diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile index d78328191bfe..cec80511cd06 100644 --- a/drivers/soc/qcom/qdsp6v2/Makefile +++ b/drivers/soc/qcom/qdsp6v2/Makefile @@ -2,5 +2,7 @@ obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o voice_svc.o obj-$(CONFIG_MSM_QDSP6_APRV3) += apr.o apr_v3.o apr_tal.o voice_svc.o obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o voice_svc.o obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o voice_svc.o +obj-$(CONFIG_MSM_QDSP6_APRV2_VM) += apr_vm.o apr_v2.o voice_svc.o obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += msm_audio_ion.o +obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += msm_audio_ion_vm.o obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o diff --git a/drivers/soc/qcom/qdsp6v2/apr_vm.c b/drivers/soc/qcom/qdsp6v2/apr_vm.c new file mode 100644 index 000000000000..553e44fd4b44 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_vm.c @@ -0,0 +1,1252 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCM_Q6_NMI_CMD 0x1 +#define APR_PKT_IPC_LOG_PAGE_CNT 2 +#define APR_VM_CB_THREAD_NAME "apr_vm_cb_thread" +#define APR_TX_BUF_SIZE 4096 +#define APR_RX_BUF_SIZE 4096 + +static struct apr_q6 q6; +static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX]; +#ifdef CONFIG_IPC_LOGGING +static void *apr_pkt_ctx; +#define APR_PKT_INFO(x...) \ +do { \ + if (apr_pkt_ctx) \ + ipc_log_string(apr_pkt_ctx, ": "x); \ +} while (0) +#endif +static wait_queue_head_t dsp_wait; +static wait_queue_head_t modem_wait; +static bool is_modem_up; +/* Subsystem restart: QDSP6 data, functions */ +static struct workqueue_struct *apr_reset_workqueue; +static void apr_reset_deregister(struct work_struct *work); +struct apr_reset_work { + void *handle; + struct work_struct work; +}; + +/* hab handle */ +static uint32_t hab_handle_tx; +static uint32_t hab_handle_rx; +static char apr_tx_buf[APR_TX_BUF_SIZE]; +static char apr_rx_buf[APR_RX_BUF_SIZE]; + +/* apr callback thread task */ +static struct task_struct *apr_vm_cb_thread_task; +static int pid; + + +struct apr_svc_table { + char name[64]; + int idx; + int id; + int dest_svc; + int client_id; + int handle; +}; + +/* + * src svc should be assigned dynamically through apr registration: + * 1. replace with a proper string name for registration. + * e.g. "qcom.apps.lnx." + name + * 2. register apr BE, retrieve dynamic src svc address, + * apr handle and store in svc tbl. + */ + +static struct mutex m_lock_tbl_qdsp6; + +static struct apr_svc_table svc_tbl_qdsp6[] = { + { + .name = "AFE", + .idx = 0, + .id = 0, + .dest_svc = APR_SVC_AFE, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "ASM", + .idx = 1, + .id = 0, + .dest_svc = APR_SVC_ASM, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "ADM", + .idx = 2, + .id = 0, + .dest_svc = APR_SVC_ADM, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "CORE", + .idx = 3, + .id = 0, + .dest_svc = APR_SVC_ADSP_CORE, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "TEST", + .idx = 4, + .id = 0, + .dest_svc = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "MVM", + .idx = 5, + .id = 0, + .dest_svc = APR_SVC_ADSP_MVM, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "CVS", + .idx = 6, + .id = 0, + .dest_svc = APR_SVC_ADSP_CVS, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "CVP", + .idx = 7, + .id = 0, + .dest_svc = APR_SVC_ADSP_CVP, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "USM", + .idx = 8, + .id = 0, + .dest_svc = APR_SVC_USM, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, + { + .name = "VIDC", + .idx = 9, + .id = 0, + .dest_svc = APR_SVC_VIDC, + .handle = 0, + }, + { + .name = "LSM", + .idx = 10, + .id = 0, + .dest_svc = APR_SVC_LSM, + .client_id = APR_CLIENT_AUDIO, + .handle = 0, + }, +}; + +static struct mutex m_lock_tbl_voice; + +static struct apr_svc_table svc_tbl_voice[] = { + { + .name = "VSM", + .idx = 0, + .id = 0, + .dest_svc = APR_SVC_VSM, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, + { + .name = "VPM", + .idx = 1, + .id = 0, + .dest_svc = APR_SVC_VPM, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, + { + .name = "MVS", + .idx = 2, + .id = 0, + .dest_svc = APR_SVC_MVS, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, + { + .name = "MVM", + .idx = 3, + .id = 0, + .dest_svc = APR_SVC_MVM, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, + { + .name = "CVS", + .idx = 4, + .id = 0, + .dest_svc = APR_SVC_CVS, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, + { + .name = "CVP", + .idx = 5, + .id = 0, + .dest_svc = APR_SVC_CVP, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, + { + .name = "SRD", + .idx = 6, + .id = 0, + .dest_svc = APR_SVC_SRD, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, + { + .name = "TEST", + .idx = 7, + .id = 0, + .dest_svc = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_VOICE, + .handle = 0, + }, +}; + +enum apr_subsys_state apr_get_modem_state(void) +{ + return atomic_read(&q6.modem_state); +} + +void apr_set_modem_state(enum apr_subsys_state state) +{ + atomic_set(&q6.modem_state, state); +} + +enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev, + enum apr_subsys_state new) +{ + return atomic_cmpxchg(&q6.modem_state, prev, new); +} + +enum apr_subsys_state apr_get_q6_state(void) +{ + return atomic_read(&q6.q6_state); +} +EXPORT_SYMBOL(apr_get_q6_state); + +int apr_set_q6_state(enum apr_subsys_state state) +{ + pr_debug("%s: setting adsp state %d\n", __func__, state); + if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED) + return -EINVAL; + atomic_set(&q6.q6_state, state); + return 0; +} +EXPORT_SYMBOL(apr_set_q6_state); + +enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev, + enum apr_subsys_state new) +{ + return atomic_cmpxchg(&q6.q6_state, prev, new); +} + +int apr_wait_for_device_up(int dest_id) +{ + int rc = -1; + + if (dest_id == APR_DEST_MODEM) + rc = wait_event_interruptible_timeout(modem_wait, + (apr_get_modem_state() == APR_SUBSYS_UP), + (1 * HZ)); + else if (dest_id == APR_DEST_QDSP6) + rc = wait_event_interruptible_timeout(dsp_wait, + (apr_get_q6_state() == APR_SUBSYS_UP), + (1 * HZ)); + else + pr_err("%s: unknown dest_id %d\n", __func__, dest_id); + /* returns left time */ + return rc; +} + +static int apr_vm_nb_receive(int32_t handle, void *dest_buff, + uint32_t *size_bytes, uint32_t timeout) +{ + int rc; + uint32_t dest_buff_bytes = *size_bytes; + unsigned long delay = jiffies + (HZ / 2); + + do { + *size_bytes = dest_buff_bytes; + rc = habmm_socket_recv(handle, + dest_buff, + size_bytes, + timeout, + HABMM_SOCKET_RECV_FLAGS_NON_BLOCKING); + } while (time_before(jiffies, delay) && (rc == HAB_AGAIN) && + (*size_bytes == 0)); + + return rc; +} + +static int apr_vm_cb_process_evt(char *buf, int len) +{ + struct apr_client_data data; + struct apr_client *apr_client; + struct apr_svc *c_svc; + struct apr_hdr *hdr; + uint16_t hdr_size; + uint16_t msg_type; + uint16_t ver; + uint16_t src; + uint16_t svc; + uint16_t clnt; + int i; + int temp_port = 0; + uint32_t *ptr; + uint32_t evt_id; + + pr_debug("APR: len = %d\n", len); + ptr = (uint32_t *)buf; + pr_debug("\n*****************\n"); + for (i = 0; i < len/4; i++) + pr_debug("%x ", ptr[i]); + pr_debug("\n"); + pr_debug("\n*****************\n"); + + if (!buf || len <= APR_HDR_SIZE + sizeof(uint32_t)) { + pr_err("APR: Improper apr pkt received: %p %d\n", buf, len); + return -EINVAL; + } + + evt_id = *((int32_t *)buf); + if (evt_id != APRV2_VM_EVT_RX_PKT_AVAILABLE) { + pr_err("APR: Wrong evt id: %d\n", evt_id); + return -EINVAL; + } + hdr = (struct apr_hdr *)(buf + sizeof(uint32_t)); +#ifdef CONFIG_IPC_LOGGING + APR_PKT_INFO("Rx: dest_svc[%d], opcode[0x%X], size[%d]", + hdr->dest_svc, hdr->opcode, hdr->pkt_size); +#endif + ver = hdr->hdr_field; + ver = (ver & 0x000F); + if (ver > APR_PKT_VER + 1) { + pr_err("APR: Wrong version: %d\n", ver); + return -EINVAL; + } + + hdr_size = hdr->hdr_field; + hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4; + if (hdr_size < APR_HDR_SIZE) { + pr_err("APR: Wrong hdr size:%d\n", hdr_size); + return -EINVAL; + } + + if (hdr->pkt_size < APR_HDR_SIZE) { + pr_err("APR: Wrong paket size\n"); + return -EINVAL; + } + + msg_type = hdr->hdr_field; + msg_type = (msg_type >> 0x08) & 0x0003; + if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) { + pr_err("APR: Wrong message type: %d\n", msg_type); + return -EINVAL; + } + + /* + * dest_svc is dynamic created by apr service + * no need to check the range of dest_svc + */ + if (hdr->src_domain >= APR_DOMAIN_MAX || + hdr->dest_domain >= APR_DOMAIN_MAX || + hdr->src_svc >= APR_SVC_MAX) { + pr_err("APR: Wrong APR header\n"); + return -EINVAL; + } + + svc = hdr->dest_svc; + if (hdr->src_domain == APR_DOMAIN_MODEM) + clnt = APR_CLIENT_VOICE; + else if (hdr->src_domain == APR_DOMAIN_ADSP) + clnt = APR_CLIENT_AUDIO; + else { + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return -EINVAL; + } + + src = apr_get_data_src(hdr); + if (src == APR_DEST_MAX) + return -EINVAL; + + pr_debug("src =%d clnt = %d\n", src, clnt); + apr_client = &client[src][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) + if (apr_client->svc[i].id == svc) { + pr_debug("svc_id = %d\n", apr_client->svc[i].id); + c_svc = &apr_client->svc[i]; + break; + } + + if (i == APR_SVC_MAX) { + pr_err("APR: service is not registered\n"); + return -ENXIO; + } + + pr_debug("svc_idx = %d\n", i); + pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id, + c_svc->client_id, c_svc->fn, c_svc->priv); + + data.payload_size = hdr->pkt_size - hdr_size; + data.opcode = hdr->opcode; + data.src = src; + data.src_port = hdr->src_port; + data.dest_port = hdr->dest_port; + data.token = hdr->token; + data.msg_type = msg_type; + if (data.payload_size > 0) + data.payload = (char *)hdr + hdr_size; + + temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF); + pr_debug("port = %d t_port = %d\n", data.src_port, temp_port); + if (c_svc->port_cnt && c_svc->port_fn[temp_port]) + c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]); + else if (c_svc->fn) + c_svc->fn(&data, c_svc->priv); + else + pr_err("APR: Rxed a packet for NULL callback\n"); + + return 0; +} + +static int apr_vm_cb_thread(void *data) +{ + uint32_t apr_rx_buf_len; + struct aprv2_vm_ack_rx_pkt_available_t apr_ack; + int status = 0; + int ret = 0; + + while (1) { + apr_rx_buf_len = sizeof(apr_rx_buf); + ret = habmm_socket_recv(hab_handle_rx, + (void *)&apr_rx_buf, + &apr_rx_buf_len, + 0xFFFFFFFF, + 0); + if (ret) { + pr_err("%s: habmm_socket_recv failed %d\n", + __func__, ret); + /* + * TODO: depends on the HAB error code, + * may need to implement + * a retry mechanism. + * break if recv failed ? + */ + break; + } + + status = apr_vm_cb_process_evt(apr_rx_buf, apr_rx_buf_len); + + apr_ack.status = status; + ret = habmm_socket_send(hab_handle_rx, + (void *)&apr_ack, + sizeof(apr_ack), + 0); + if (ret) { + pr_err("%s: habmm_socket_send failed %d\n", + __func__, ret); + /* TODO: break if send failed ? */ + break; + } + } + + return ret; +} + +static int apr_vm_get_svc(const char *svc_name, int domain_id, int *client_id, + int *svc_idx, int *svc_id, int *dest_svc, int *handle) +{ + int i; + int size; + struct apr_svc_table *tbl; + struct mutex *lock; + struct aprv2_vm_cmd_register_rsp_t apr_rsp; + uint32_t apr_len; + int ret = 0; + struct { + uint32_t cmd_id; + struct aprv2_vm_cmd_register_t reg_cmd; + } tx_data; + + if (domain_id == APR_DOMAIN_ADSP) { + tbl = svc_tbl_qdsp6; + size = ARRAY_SIZE(svc_tbl_qdsp6); + lock = &m_lock_tbl_qdsp6; + } else { + tbl = svc_tbl_voice; + size = ARRAY_SIZE(svc_tbl_voice); + lock = &m_lock_tbl_voice; + } + + mutex_lock(lock); + for (i = 0; i < size; i++) { + if (!strcmp(svc_name, tbl[i].name)) { + *client_id = tbl[i].client_id; + *svc_idx = tbl[i].idx; + if (!tbl[i].id && !tbl[i].handle) { + /* need to register a new service */ + memset((void *) &tx_data, 0, sizeof(tx_data)); + + apr_len = sizeof(tx_data); + tx_data.cmd_id = APRV2_VM_CMDID_REGISTER; + tx_data.reg_cmd.name_size = snprintf( + tx_data.reg_cmd.name, + APRV2_VM_MAX_DNS_SIZE, + "qcom.apps.lnx.%s", + svc_name); + tx_data.reg_cmd.addr = 0; + ret = habmm_socket_send(hab_handle_tx, + (void *) &tx_data, + apr_len, + 0); + if (ret) { + pr_err("%s: habmm_socket_send failed %d\n", + __func__, ret); + mutex_unlock(lock); + return ret; + } + /* wait for response */ + apr_len = sizeof(apr_rsp); + ret = apr_vm_nb_receive(hab_handle_tx, + (void *)&apr_rsp, + &apr_len, + 0xFFFFFFFF); + if (ret) { + pr_err("%s: apr_vm_nb_receive failed %d\n", + __func__, ret); + mutex_unlock(lock); + return ret; + } + if (apr_rsp.status) { + pr_err("%s: apr_vm_nb_receive status %d\n", + __func__, apr_rsp.status); + ret = apr_rsp.status; + mutex_unlock(lock); + return ret; + } + /* update svc table */ + tbl[i].handle = apr_rsp.handle; + tbl[i].id = apr_rsp.addr & + APRV2_VM_PKT_SERVICE_ID_MASK; + } + *svc_id = tbl[i].id; + *dest_svc = tbl[i].dest_svc; + *handle = tbl[i].handle; + break; + } + } + mutex_unlock(lock); + + pr_debug("%s: svc_name = %s client_id = %d domain_id = %d\n", + __func__, svc_name, *client_id, domain_id); + pr_debug("%s: src_svc = %d dest_svc = %d handle = %d\n", + __func__, *svc_id, *dest_svc, *handle); + + if (i == size) { + pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name); + ret = -EINVAL; + } + + return ret; +} + +static int apr_vm_rel_svc(int domain_id, int svc_id, int handle) +{ + int i; + int size; + struct apr_svc_table *tbl; + struct mutex *lock; + struct aprv2_vm_cmd_deregister_rsp_t apr_rsp; + uint32_t apr_len; + int ret = 0; + struct { + uint32_t cmd_id; + struct aprv2_vm_cmd_deregister_t dereg_cmd; + } tx_data; + + if (domain_id == APR_DOMAIN_ADSP) { + tbl = svc_tbl_qdsp6; + size = ARRAY_SIZE(svc_tbl_qdsp6); + lock = &m_lock_tbl_qdsp6; + } else { + tbl = svc_tbl_voice; + size = ARRAY_SIZE(svc_tbl_voice); + lock = &m_lock_tbl_voice; + } + + mutex_lock(lock); + for (i = 0; i < size; i++) { + if (tbl[i].id == svc_id && tbl[i].handle == handle) { + /* need to deregister a service */ + memset((void *) &tx_data, 0, sizeof(tx_data)); + + apr_len = sizeof(tx_data); + tx_data.cmd_id = APRV2_VM_CMDID_DEREGISTER; + tx_data.dereg_cmd.handle = handle; + ret = habmm_socket_send(hab_handle_tx, + (void *) &tx_data, + apr_len, + 0); + if (ret) + pr_err("%s: habmm_socket_send failed %d\n", + __func__, ret); + /* + * TODO: if send failed, should not wait for recv. + * should clear regardless? + */ + /* wait for response */ + apr_len = sizeof(apr_rsp); + ret = apr_vm_nb_receive(hab_handle_tx, + (void *)&apr_rsp, + &apr_len, + 0xFFFFFFFF); + if (ret) + pr_err("%s: apr_vm_nb_receive failed %d\n", + __func__, ret); + if (apr_rsp.status) { + pr_err("%s: apr_vm_nb_receive status %d\n", + __func__, apr_rsp.status); + ret = apr_rsp.status; + } + /* clear svc table */ + tbl[i].handle = 0; + tbl[i].id = 0; + break; + } + } + mutex_unlock(lock); + + if (i == size) { + pr_err("%s: APR: Wrong svc id %d handle %d\n", + __func__, svc_id, handle); + ret = -EINVAL; + } + + return ret; +} + +int apr_send_pkt(void *handle, uint32_t *buf) +{ + struct apr_svc *svc = handle; + struct apr_hdr *hdr; + unsigned long flags; + uint32_t *cmd_id = (uint32_t *)apr_tx_buf; + struct aprv2_vm_cmd_async_send_t *apr_send = + (struct aprv2_vm_cmd_async_send_t *)(apr_tx_buf + + sizeof(uint32_t)); + uint32_t apr_send_len; + struct aprv2_vm_cmd_async_send_rsp_t apr_rsp; + uint32_t apr_rsp_len; + int ret = 0; + + if (!handle || !buf) { + pr_err("APR: Wrong parameters\n"); + return -EINVAL; + } + if (svc->need_reset) { + pr_err("APR: send_pkt service need reset\n"); + return -ENETRESET; + } + + if ((svc->dest_id == APR_DEST_QDSP6) && + (apr_get_q6_state() != APR_SUBSYS_LOADED)) { + pr_err("%s: Still dsp is not Up\n", __func__); + return -ENETRESET; + } else if ((svc->dest_id == APR_DEST_MODEM) && + (apr_get_modem_state() == APR_SUBSYS_DOWN)) { + pr_err("%s: Still Modem is not Up\n", __func__); + return -ENETRESET; + } + + spin_lock_irqsave(&svc->w_lock, flags); + if (!svc->id || !svc->vm_handle) { + pr_err("APR: Still service is not yet opened\n"); + ret = -EINVAL; + goto done; + } + hdr = (struct apr_hdr *)buf; + + hdr->src_domain = APR_DOMAIN_APPS; + hdr->src_svc = svc->id; + hdr->dest_domain = svc->dest_domain; + hdr->dest_svc = svc->vm_dest_svc; +#ifdef CONFIG_IPC_LOGGING + APR_PKT_INFO("Tx: dest_svc[%d], opcode[0x%X], size[%d]", + hdr->dest_svc, hdr->opcode, hdr->pkt_size); +#endif + memset((void *)&apr_tx_buf, 0, sizeof(apr_tx_buf)); + /* pkt_size + cmd_id + handle */ + apr_send_len = hdr->pkt_size + sizeof(uint32_t) * 2; + *cmd_id = APRV2_VM_CMDID_ASYNC_SEND; + apr_send->handle = svc->vm_handle; + + /* safe check */ + if (hdr->pkt_size > APR_TX_BUF_SIZE - (sizeof(uint32_t) * 2)) { + pr_err("APR: Wrong pkt size %d\n", hdr->pkt_size); + ret = -ENOMEM; + goto done; + } + memcpy(&apr_send->pkt_header, buf, hdr->pkt_size); + + ret = habmm_socket_send(hab_handle_tx, + (void *)&apr_tx_buf, + apr_send_len, + 0); + if (ret) { + pr_err("%s: habmm_socket_send failed %d\n", + __func__, ret); + goto done; + } + /* wait for response */ + apr_rsp_len = sizeof(apr_rsp); + ret = apr_vm_nb_receive(hab_handle_tx, + (void *)&apr_rsp, + &apr_rsp_len, + 0xFFFFFFFF); + if (ret) { + pr_err("%s: apr_vm_nb_receive failed %d\n", + __func__, ret); + goto done; + } + if (apr_rsp.status) { + pr_err("%s: apr_vm_nb_receive status %d\n", + __func__, apr_rsp.status); + /* should translate status properly */ + ret = -ECOMM; + goto done; + } + + /* upon successful send, return packet size */ + ret = hdr->pkt_size; + +done: + spin_unlock_irqrestore(&svc->w_lock, flags); + return ret; +} + +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv) +{ + struct apr_client *clnt; + int client_id = 0; + int svc_idx = 0; + int svc_id = 0; + int dest_id = 0; + int domain_id = 0; + int temp_port = 0; + struct apr_svc *svc = NULL; + int rc = 0; + bool can_open_channel = true; + int dest_svc = 0; + int handle = 0; + + if (!dest || !svc_name || !svc_fn) + return NULL; + + if (!strcmp(dest, "ADSP")) + domain_id = APR_DOMAIN_ADSP; + else if (!strcmp(dest, "MODEM")) { + /* Don't request for SMD channels if destination is MODEM, + * as these channels are no longer used and these clients + * are to listen only for MODEM SSR events + */ + can_open_channel = false; + domain_id = APR_DOMAIN_MODEM; + } else { + pr_err("APR: wrong destination\n"); + goto done; + } + + dest_id = apr_get_dest_id(dest); + + if (dest_id == APR_DEST_QDSP6) { + if (apr_get_q6_state() != APR_SUBSYS_LOADED) { + pr_err("%s: adsp not up\n", __func__); + return NULL; + } + pr_debug("%s: adsp Up\n", __func__); + } else if (dest_id == APR_DEST_MODEM) { + if (apr_get_modem_state() == APR_SUBSYS_DOWN) { + if (is_modem_up) { + pr_err("%s: modem shutdown due to SSR, ret", + __func__); + return NULL; + } + pr_debug("%s: Wait for modem to bootup\n", __func__); + rc = apr_wait_for_device_up(APR_DEST_MODEM); + if (rc == 0) { + pr_err("%s: Modem is not Up\n", __func__); + return NULL; + } + } + pr_debug("%s: modem Up\n", __func__); + } + + if (apr_vm_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id, + &dest_svc, &handle)) { + pr_err("%s: apr_vm_get_svc failed\n", __func__); + goto done; + } + + clnt = &client[dest_id][client_id]; + svc = &clnt->svc[svc_idx]; + mutex_lock(&svc->m_lock); + clnt->id = client_id; + if (svc->need_reset) { + mutex_unlock(&svc->m_lock); + pr_err("APR: Service needs reset\n"); + goto done; + } + svc->id = svc_id; + svc->vm_dest_svc = dest_svc; + svc->dest_id = dest_id; + svc->client_id = client_id; + svc->dest_domain = domain_id; + svc->pkt_owner = APR_PKT_OWNER_DRIVER; + svc->vm_handle = handle; + + if (src_port != 0xFFFFFFFF) { + temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", src_port, temp_port); + if (temp_port >= APR_MAX_PORTS || temp_port < 0) { + pr_err("APR: temp_port out of bounds\n"); + mutex_unlock(&svc->m_lock); + return NULL; + } + if (!svc->port_cnt && !svc->svc_cnt) + clnt->svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + } else { + if (!svc->fn) { + if (!svc->port_cnt && !svc->svc_cnt) + clnt->svc_cnt++; + svc->fn = svc_fn; + if (svc->port_cnt) + svc->svc_cnt++; + svc->priv = priv; + } + } + + mutex_unlock(&svc->m_lock); +done: + return svc; +} + +static void apr_reset_deregister(struct work_struct *work) +{ + struct apr_svc *handle = NULL; + struct apr_reset_work *apr_reset = + container_of(work, struct apr_reset_work, work); + + handle = apr_reset->handle; + pr_debug("%s:handle[%p]\n", __func__, handle); + apr_deregister(handle); + kfree(apr_reset); +} + +int apr_deregister(void *handle) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + uint16_t dest_id; + uint16_t client_id; + + if (!handle) + return -EINVAL; + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + else if (svc->svc_cnt) + svc->svc_cnt--; + if (!svc->port_cnt && !svc->svc_cnt) { + client[dest_id][client_id].svc_cnt--; + svc->need_reset = 0x0; + } + } else if (client[dest_id][client_id].svc_cnt > 0) { + client[dest_id][client_id].svc_cnt--; + if (!client[dest_id][client_id].svc_cnt) { + svc->need_reset = 0x0; + pr_debug("%s: service is reset %p\n", __func__, svc); + } + } + + if (!svc->port_cnt && !svc->svc_cnt) { + if (apr_vm_rel_svc(svc->dest_domain, svc->id, svc->vm_handle)) + pr_err("%s: apr_vm_rel_svc failed\n", __func__); + svc->priv = NULL; + svc->id = 0; + svc->vm_dest_svc = 0; + svc->fn = NULL; + svc->dest_id = 0; + svc->client_id = 0; + svc->need_reset = 0x0; + svc->vm_handle = 0; + } + mutex_unlock(&svc->m_lock); + + return 0; +} + +void apr_reset(void *handle) +{ + struct apr_reset_work *apr_reset_worker = NULL; + + if (!handle) + return; + pr_debug("%s: handle[%p]\n", __func__, handle); + + if (apr_reset_workqueue == NULL) { + pr_err("%s: apr_reset_workqueue is NULL\n", __func__); + return; + } + + apr_reset_worker = kzalloc(sizeof(struct apr_reset_work), + GFP_ATOMIC); + + if (apr_reset_worker == NULL) { + pr_err("%s: mem failure\n", __func__); + return; + } + + apr_reset_worker->handle = handle; + INIT_WORK(&apr_reset_worker->work, apr_reset_deregister); + queue_work(apr_reset_workqueue, &apr_reset_worker->work); +} + +/* Dispatch the Reset events to Modem and audio clients */ +void dispatch_event(unsigned long code, uint16_t proc) +{ + struct apr_client *apr_client; + struct apr_client_data data; + struct apr_svc *svc; + uint16_t clnt; + int i, j; + + data.opcode = RESET_EVENTS; + data.reset_event = code; + + /* Service domain can be different from the processor */ + data.reset_proc = apr_get_reset_domain(proc); + + clnt = APR_CLIENT_AUDIO; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } + + clnt = APR_CLIENT_VOICE; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } +} + +static int modem_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + static int boot_count = 2; + + if (boot_count) { + boot_count--; + return NOTIFY_OK; + } + + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("M-Notify: Shutdown started\n"); + apr_set_modem_state(APR_SUBSYS_DOWN); + dispatch_event(code, APR_DEST_MODEM); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("M-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("M-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) == + APR_SUBSYS_DOWN) + wake_up(&modem_wait); + is_modem_up = 1; + pr_debug("M-Notify: Bootup Completed\n"); + break; + default: + pr_err("M-Notify: General: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block mnb = { + .notifier_call = modem_notifier_cb, +}; + +static bool powered_on; + +static int lpass_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + static int boot_count = 2; + struct notif_data *data = (struct notif_data *)_cmd; + struct scm_desc desc; + + if (boot_count) { + boot_count--; + return NOTIFY_OK; + } + + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("L-Notify: Shutdown started\n"); + apr_set_q6_state(APR_SUBSYS_DOWN); + dispatch_event(code, APR_DEST_QDSP6); + if (data && data->crashed) { + /* Send NMI to QDSP6 via an SCM call. */ + if (!is_scm_armv8()) { + scm_call_atomic1(SCM_SVC_UTIL, + SCM_Q6_NMI_CMD, 0x1); + } else { + desc.args[0] = 0x1; + desc.arginfo = SCM_ARGS(1); + scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL, + SCM_Q6_NMI_CMD), &desc); + } + /* The write should go through before q6 is shutdown */ + mb(); + pr_debug("L-Notify: Q6 NMI was sent.\n"); + } + break; + case SUBSYS_AFTER_SHUTDOWN: + powered_on = false; + pr_debug("L-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("L-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, + APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN) + wake_up(&dsp_wait); + powered_on = true; + pr_debug("L-Notify: Bootup Completed\n"); + break; + default: + pr_err("L-Notify: Generel: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block lnb = { + .notifier_call = lpass_notifier_cb, +}; + +static int panic_handler(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct scm_desc desc; + + if (powered_on) { + /* Send NMI to QDSP6 via an SCM call. */ + if (!is_scm_armv8()) { + scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1); + } else { + desc.args[0] = 0x1; + desc.arginfo = SCM_ARGS(1); + scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL, + SCM_Q6_NMI_CMD), &desc); + } + } + return NOTIFY_DONE; +} + +static struct notifier_block panic_nb = { + .notifier_call = panic_handler, +}; + +static void apr_vm_set_subsys_state(void) +{ + /* set default subsys state in vm env. + Both q6 and modem should be in LOADED state, + since vm boots up at late stage after pm. */ + apr_set_q6_state(APR_SUBSYS_LOADED); + apr_set_modem_state(APR_SUBSYS_LOADED); +} + +static int __init apr_init(void) +{ + int i, j, k; + int ret; + + /* open apr channel tx and rx, store as global */ + ret = habmm_socket_open(&hab_handle_tx, + MM_AUD_1, + 0xFFFFFFFF, + HABMM_SOCKET_OPEN_FLAGS_SINGLE_BE_SINGLE_FE); + if (ret) { + pr_err("%s: habmm_socket_open tx failed %d\n", __func__, ret); + return ret; + } + + ret = habmm_socket_open(&hab_handle_rx, + MM_AUD_2, + 0xFFFFFFFF, + HABMM_SOCKET_OPEN_FLAGS_SINGLE_BE_SINGLE_FE); + if (ret) { + pr_err("%s: habmm_socket_open rx failed %d\n", __func__, ret); + habmm_socket_close(hab_handle_tx); + return ret; + } + pr_info("%s: hab_handle_tx %x hab_handle_rx %x\n", + __func__, hab_handle_tx, hab_handle_rx); + + /* create apr ch rx cb thread */ + apr_vm_cb_thread_task = kthread_run(apr_vm_cb_thread, + NULL, + APR_VM_CB_THREAD_NAME); + if (IS_ERR(apr_vm_cb_thread_task)) { + ret = PTR_ERR(apr_vm_cb_thread_task); + pr_err("%s: kthread_run failed %d\n", __func__, ret); + habmm_socket_close(hab_handle_tx); + habmm_socket_close(hab_handle_rx); + return ret; + } + pid = apr_vm_cb_thread_task->pid; + pr_info("%s: apr_vm_cb_thread started pid %d\n", + __func__, pid); + + mutex_init(&m_lock_tbl_qdsp6); + mutex_init(&m_lock_tbl_voice); + + for (i = 0; i < APR_DEST_MAX; i++) + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_init(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) { + mutex_init(&client[i][j].svc[k].m_lock); + spin_lock_init(&client[i][j].svc[k].w_lock); + } + } + + apr_vm_set_subsys_state(); + mutex_init(&q6.lock); + apr_reset_workqueue = create_singlethread_workqueue("apr_driver"); + if (!apr_reset_workqueue) { + habmm_socket_close(hab_handle_tx); + habmm_socket_close(hab_handle_rx); + kthread_stop(apr_vm_cb_thread_task); + return -ENOMEM; + } + atomic_notifier_chain_register(&panic_notifier_list, &panic_nb); +#ifdef CONFIG_IPC_LOGGING + apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT, + "apr", 0); + if (!apr_pkt_ctx) + pr_err("%s: Unable to create ipc log context\n", __func__); +#endif + return 0; +} +device_initcall(apr_init); + +static int __init apr_late_init(void) +{ + int ret = 0; + + init_waitqueue_head(&dsp_wait); + init_waitqueue_head(&modem_wait); + subsys_notif_register(&mnb, &lnb); + return ret; +} +late_initcall(apr_late_init); + +static void __exit apr_exit(void) +{ + habmm_socket_close(hab_handle_tx); + habmm_socket_close(hab_handle_rx); + kthread_stop(apr_vm_cb_thread_task); +} +__exitcall(apr_exit); diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c new file mode 100644 index 000000000000..c2ab6ef49ffe --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../staging/android/ion/ion_priv.h" +#include "../../../staging/android/ion/ion_hvenv_driver.h" + +#define MSM_AUDIO_ION_PROBED (1 << 0) + +struct msm_audio_ion_private { + bool smmu_enabled; + bool audioheap_enabled; + u8 device_status; +}; + +static struct msm_audio_ion_private msm_audio_ion_data = {0,}; + +static int msm_audio_ion_get_phys(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len); + +int msm_audio_ion_alloc(const char *name, struct ion_client **client, + struct ion_handle **handle, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = -EINVAL; + unsigned long err_ion_ptr = 0; + + if ((msm_audio_ion_data.smmu_enabled == true) && + !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + if (!name || !client || !handle || !paddr || !vaddr + || !bufsz || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + *client = msm_audio_ion_client_create(name); + if (IS_ERR_OR_NULL((void *)(*client))) { + pr_err("%s: ION create client for AUDIO failed\n", __func__); + goto err; + } + + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_AUDIO_HEAP_ID), 0); + if (IS_ERR_OR_NULL((void *) (*handle))) { + if (msm_audio_ion_data.smmu_enabled == true) { + pr_debug("system heap is used"); + msm_audio_ion_data.audioheap_enabled = 0; + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_SYSTEM_HEAP_ID), 0); + } + if (IS_ERR_OR_NULL((void *) (*handle))) { + if (IS_ERR((void *)(*handle))) + err_ion_ptr = PTR_ERR((int *)(*handle)); + pr_err("%s:ION alloc fail err ptr=%ld, smmu_enabled=%d\n", + __func__, err_ion_ptr, msm_audio_ion_data.smmu_enabled); + rc = -ENOMEM; + goto err_ion_client; + } + } else { + pr_debug("audio heap is used"); + msm_audio_ion_data.audioheap_enabled = 1; + } + + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_ion_handle; + } + + *vaddr = ion_map_kernel(*client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + goto err_ion_handle; + } + pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, + *vaddr, bufsz); + + if (bufsz != 0) { + pr_debug("%s: memset to 0 %pK %zd\n", __func__, *vaddr, bufsz); + memset((void *)*vaddr, 0, bufsz); + } + + return rc; + +err_ion_handle: + ion_free(*client, *handle); +err_ion_client: + msm_audio_ion_client_destroy(*client); + *handle = NULL; + *client = NULL; +err: + return rc; +} +EXPORT_SYMBOL(msm_audio_ion_alloc); + +int msm_audio_ion_import(const char *name, struct ion_client **client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = 0; + + if ((msm_audio_ion_data.smmu_enabled == true) && + !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + + if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + rc = -EINVAL; + goto err; + } + + *client = msm_audio_ion_client_create(name); + if (IS_ERR_OR_NULL((void *)(*client))) { + pr_err("%s: ION create client for AUDIO failed\n", __func__); + rc = -EINVAL; + goto err; + } + + /* name should be audio_acdb_client or Audio_Dec_Client, + bufsz should be 0 and fd shouldn't be 0 as of now + */ + *handle = ion_import_dma_buf(*client, fd); + pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__, + name, fd, *handle); + if (IS_ERR_OR_NULL((void *) (*handle))) { + pr_err("%s: ion import dma buffer failed\n", + __func__); + rc = -EINVAL; + goto err_destroy_client; + } + + if (ionflag != NULL) { + rc = ion_handle_get_flags(*client, *handle, ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", + __func__); + goto err_ion_handle; + } + } + + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_ion_handle; + } + + *vaddr = ion_map_kernel(*client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + rc = -ENOMEM; + goto err_ion_handle; + } + pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, + *vaddr, bufsz); + + return 0; + +err_ion_handle: + ion_free(*client, *handle); +err_destroy_client: + msm_audio_ion_client_destroy(*client); + *client = NULL; + *handle = NULL; +err: + return rc; +} + +int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle) +{ + if (!client || !handle) { + pr_err("%s Invalid params\n", __func__); + return -EINVAL; + } + + ion_unmap_kernel(client, handle); + + ion_free(client, handle); + msm_audio_ion_client_destroy(client); + return 0; +} +EXPORT_SYMBOL(msm_audio_ion_free); + +int msm_audio_ion_mmap(struct audio_buffer *ab, + struct vm_area_struct *vma) +{ + struct sg_table *table; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + struct scatterlist *sg; + unsigned int i; + struct page *page; + int ret; + + pr_debug("%s\n", __func__); + + table = ion_sg_table(ab->client, ab->handle); + + if (IS_ERR(table)) { + pr_err("%s: Unable to get sg_table from ion: %ld\n", + __func__, PTR_ERR(table)); + return PTR_ERR(table); + } else if (!table) { + pr_err("%s: sg_list is NULL\n", __func__); + return -EINVAL; + } + + /* uncached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + /* We need to check if a page is associated with this sg list because: + * If the allocation came from a carveout we currently don't have + * pages associated with carved out memory. This might change in the + * future and we can remove this check and the else statement. + */ + page = sg_page(table->sgl); + if (page) { + pr_debug("%s: page is NOT null\n", __func__); + for_each_sg(table->sgl, sg, table->nents, i) { + unsigned long remainder = vma->vm_end - addr; + unsigned long len = sg->length; + + page = sg_page(sg); + + if (offset >= len) { + offset -= len; + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len -= offset; + offset = 0; + } + len = min(len, remainder); + pr_debug("vma=%pK, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%ld\n", + vma, (unsigned int)addr, len, + (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, + (unsigned long int)vma->vm_page_prot); + remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + addr += len; + if (addr >= vma->vm_end) + return 0; + } + } else { + ion_phys_addr_t phys_addr; + size_t phys_len; + size_t va_len = 0; + + pr_debug("%s: page is NULL\n", __func__); + + ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len); + if (ret) { + pr_err("%s: Unable to get phys address from ION buffer: %d\n" + , __func__ , ret); + return ret; + } + pr_debug("phys=%pK len=%zd\n", &phys_addr, phys_len); + pr_debug("vma=%pK, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%ld\n", + vma, (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, vma->vm_pgoff, + (unsigned long int)vma->vm_page_prot); + va_len = vma->vm_end - vma->vm_start; + if ((offset > phys_len) || (va_len > phys_len-offset)) { + pr_err("wrong offset size %ld, lens= %zd, va_len=%zd\n", + offset, phys_len, va_len); + return -EINVAL; + } + ret = remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(phys_addr) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + } + return 0; +} + + +bool msm_audio_ion_is_smmu_available(void) +{ + return msm_audio_ion_data.smmu_enabled; +} + +/* move to static section again */ +struct ion_client *msm_audio_ion_client_create(const char *name) +{ + struct ion_client *pclient = NULL; + + pclient = hvenv_ion_client_create(name); + return pclient; +} + + +void msm_audio_ion_client_destroy(struct ion_client *client) +{ + pr_debug("%s: client = %pK smmu_enabled = %d\n", __func__, + client, msm_audio_ion_data.smmu_enabled); + + ion_client_destroy(client); +} + +int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = 0; + + if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + rc = -EINVAL; + goto err; + } + /* client is already created for legacy and given*/ + /* name should be audio_acdb_client or Audio_Dec_Client, + bufsz should be 0 and fd shouldn't be 0 as of now + */ + *handle = ion_import_dma_buf(client, fd); + pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__, + name, fd, *handle); + if (IS_ERR_OR_NULL((void *)(*handle))) { + pr_err("%s: ion import dma buffer failed\n", + __func__); + rc = -EINVAL; + goto err; + } + + if (ionflag != NULL) { + rc = ion_handle_get_flags(client, *handle, ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", + __func__); + rc = -EINVAL; + goto err_ion_handle; + } + } + + rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + rc = -EINVAL; + goto err_ion_handle; + } + + /*Need to add condition SMMU enable or not */ + *vaddr = ion_map_kernel(client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + rc = -EINVAL; + goto err_ion_handle; + } + + if (bufsz != 0) + memset((void *)*vaddr, 0, bufsz); + + return 0; + +err_ion_handle: + ion_free(client, *handle); +err: + return rc; +} + +int msm_audio_ion_free_legacy(struct ion_client *client, + struct ion_handle *handle) +{ + ion_unmap_kernel(client, handle); + + ion_free(client, handle); + /* no client_destrody in legacy*/ + return 0; +} + +static int msm_audio_ion_get_phys(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len) +{ + int rc = 0; + + pr_debug("%s: smmu_enabled = %d\n", __func__, + msm_audio_ion_data.smmu_enabled); + + if (msm_audio_ion_data.smmu_enabled) { + rc = ion_phys(client, handle, addr, len); + if (rc) { + pr_err("%s: ion_phys failed, err = %d\n", + __func__, rc); + goto err; + } + /* Append the SMMU SID information to the IOVA address */ + *addr |= (1 << 32); + } else { + rc = ion_phys(client, handle, addr, len); + } + + pr_debug("%s: phys=%pK, len=%zd, rc=%d\n", + __func__, &(*addr), *len, rc); +err: + return rc; +} + +static const struct of_device_id msm_audio_ion_dt_match[] = { + { .compatible = "qcom,msm-audio-ion-vm" }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match); + +u32 msm_audio_ion_get_smmu_sid_mode32(void) +{ + if (msm_audio_ion_data.smmu_enabled) + return 1; + else + return 0; +} + +u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa) +{ + if (sizeof(ion_phys_addr_t) == sizeof(u32)) + return msm_audio_ion_get_smmu_sid_mode32(); + else + return upper_32_bits(pa); +} + +static int msm_audio_ion_probe(struct platform_device *pdev) +{ + int rc = 0; + const char *msm_audio_ion_dt = "qcom,smmu-enabled"; + bool smmu_enabled; + struct device *dev = &pdev->dev; + + if (dev->of_node == NULL) { + pr_err("%s: device tree is not found\n", + __func__); + msm_audio_ion_data.smmu_enabled = 0; + return 0; + } + + smmu_enabled = of_property_read_bool(dev->of_node, + msm_audio_ion_dt); + msm_audio_ion_data.smmu_enabled = smmu_enabled; + + pr_info("%s: SMMU is %s\n", __func__, + (smmu_enabled) ? "Enabled" : "Disabled"); + + if (!rc) + msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED; + + return rc; +} + +static int msm_audio_ion_remove(struct platform_device *pdev) +{ + msm_audio_ion_data.smmu_enabled = 0; + msm_audio_ion_data.device_status = 0; + return 0; +} + +static struct platform_driver msm_audio_ion_driver = { + .driver = { + .name = "msm-audio-ion-vm", + .owner = THIS_MODULE, + .of_match_table = msm_audio_ion_dt_match, + }, + .probe = msm_audio_ion_probe, + .remove = msm_audio_ion_remove, +}; + +static int __init msm_audio_ion_init(void) +{ + return platform_driver_register(&msm_audio_ion_driver); +} +module_init(msm_audio_ion_init); + +static void __exit msm_audio_ion_exit(void) +{ + platform_driver_unregister(&msm_audio_ion_driver); +} +module_exit(msm_audio_ion_exit); + +MODULE_DESCRIPTION("MSM Audio ION VM module"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/qdsp6v2/apr.h b/include/linux/qdsp6v2/apr.h index 84c822234e00..5e3b4ef6c993 100644 --- a/include/linux/qdsp6v2/apr.h +++ b/include/linux/qdsp6v2/apr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -137,6 +137,10 @@ struct apr_svc { struct mutex m_lock; spinlock_t w_lock; uint8_t pkt_owner; +#ifdef CONFIG_MSM_QDSP6_APRV2_VM + uint16_t vm_dest_svc; + uint32_t vm_handle; +#endif }; struct apr_client { diff --git a/include/linux/qdsp6v2/aprv2_vm.h b/include/linux/qdsp6v2/aprv2_vm.h new file mode 100644 index 000000000000..2508a199c868 --- /dev/null +++ b/include/linux/qdsp6v2/aprv2_vm.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APRV2_VM_H__ +#define __APRV2_VM_H__ + +#define APRV2_VM_MAX_DNS_SIZE (31) + /* Includes NULL character. */ +#define APRV2_VM_PKT_SERVICE_ID_MASK (0x00FF) + /* Bitmask of the service ID field. */ + +/* Packet Structure Definition */ +struct aprv2_vm_packet_t { + uint32_t header; + uint16_t src_addr; + uint16_t src_port; + uint16_t dst_addr; + uint16_t dst_port; + uint32_t token; + uint32_t opcode; +}; + +/** + * In order to send command/event via MM HAB, the following buffer + * format shall be followed, where the buffer is provided to the + * HAB send API. + * |-----cmd_id or evt_id -----| <- 32 bit, e.g. APRV2_VM_CMDID_REGISTER + * |-----cmd payload ----------| e.g. aprv2_vm_cmd_register_t + * | ... | + * + * In order to receive a command response or event ack, the following + * buffer format shall be followed, where the buffer is provided to + * the HAB receive API. + * |-----cmd response ---------| e.g. aprv2_vm_cmd_register_rsp_t + * | ... | + */ + +/* Registers a service with the backend APR driver. */ +#define APRV2_VM_CMDID_REGISTER (0x00000001) + +struct aprv2_vm_cmd_register_t { + uint32_t name_size; + /**< The service name string size in bytes. */ + char name[APRV2_VM_MAX_DNS_SIZE]; + /**< + * The service name string to register. + * + * A NULL name means the service does not have a name. + */ + uint16_t addr; + /**< + * The address to register. + * + * A zero value means to auto-generate a free dynamic address. + * A non-zero value means to directly use the statically assigned address. + */ +}; + +struct aprv2_vm_cmd_register_rsp_t { + int32_t status; + /**< The status of registration. */ + uint32_t handle; + /**< The registered service handle. */ + uint16_t addr; + /**< The actual registered address. */ +}; + +#define APRV2_VM_CMDID_DEREGISTER (0x00000002) + +struct aprv2_vm_cmd_deregister_t { + uint32_t handle; + /**< The registered service handle. */ +}; + +struct aprv2_vm_cmd_deregister_rsp_t { + int32_t status; + /**< The status of de-registration. */ +}; + +#define APRV2_VM_CMDID_ASYNC_SEND (0x00000003) + +struct aprv2_vm_cmd_async_send_t { + uint32_t handle; + /**< The registered service handle. */ + struct aprv2_vm_packet_t pkt_header; + /**< The packet header. */ + /* The apr packet payload follows */ +}; + +struct aprv2_vm_cmd_async_send_rsp_t { + int32_t status; + /**< The status of send. */ +}; + +#define APRV2_VM_EVT_RX_PKT_AVAILABLE (0x00000004) + +struct aprv2_vm_evt_rx_pkt_available_t { + struct aprv2_vm_packet_t pkt_header; + /**< The packet header. */ + /* The apr packet payload follows */ +}; + +struct aprv2_vm_ack_rx_pkt_available_t { + int32_t status; +}; + +#endif /* __APRV2_VM_H__ */ -- GitLab From 23056bdaa753c6a61d97564d9c430d1a39e5ff79 Mon Sep 17 00:00:00 2001 From: Derek Chen Date: Tue, 20 Sep 2016 20:14:09 -0400 Subject: [PATCH 1005/5498] ASoC: msm: add soc audio snd virtualization support Add soc audio configuration SND_SOC_MSM8996_VM for virtualization support. The new virtualized soc audio is based on legacy soc msm8996, removing dependency on ARCH_MSM8996 and various soc codec driver and pp features. CRs-fixed: 2047379 Change-Id: I930bf78c2ebf2c605331b50a598b5a43ceed0e0f Signed-off-by: Derek Chen --- sound/soc/msm/Kconfig | 29 ++++++++++++++++++++++++++++- sound/soc/msm/Makefile | 6 ++++++ sound/soc/msm/qdsp6v2/Makefile | 2 ++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig index 6e9d3e135d81..8c17ee1b56ee 100644 --- a/sound/soc/msm/Kconfig +++ b/sound/soc/msm/Kconfig @@ -20,6 +20,16 @@ config SND_SOC_QDSP6V2 audio drivers. This includes q6asm, q6adm, q6afe interfaces to DSP using apr. +config SND_SOC_QDSP6V2_VM + tristate "SoC ALSA audio driver for QDSP6V2 virtualization" + depends on MSM_QDSP6_APRV2_VM + help + To add support for MSM QDSP6V2 virtualization + Soc Audio. + This will enable sound soc platform specific + audio drivers. This includes q6asm, q6adm, + q6afe interfaces to DSP using virtualized apr. + config SND_SOC_QDSP_DEBUG bool "QDSP Audio Driver Debug Feature" help @@ -69,7 +79,7 @@ config DTS_SRS_TM config QTI_PP bool "Enable QTI PP" - depends on SND_SOC_MSM_QDSP6V2_INTF + depends on SND_SOC_MSM_QDSP6V2_INTF || SND_SOC_QDSP6V2_VM help To add support for default QTI post processing. This support is to configure the post processing @@ -161,6 +171,23 @@ config SND_SOC_MSM8996 the machine driver and the corresponding DAI-links +config SND_SOC_MSM8996_VM + tristate "SoC Machine driver for MSM8996 virtualization" + select SND_SOC_QDSP6V2_VM + select SND_SOC_MSM_STUB + select SND_SOC_MSM_HOSTLESS_PCM + select SND_DYNAMIC_MINORS + select MSM_QDSP6_APRV2_VM + select MSM_QDSP6V2_CODECS + select QTI_PP + help + To add support for SoC audio on MSM8996 + virtualization platform. + This will enable sound soc drivers which + interfaces with DSP using virtualized apr, + also it will enable the machine driver and + the corresponding DAI-links + config SND_SOC_MSM8X16 tristate "SoC Machine driver for MSM8916" depends on ARCH_MSM8953 || ARCH_MSM8937 diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile index d221bb85db17..cf5f357caab8 100644 --- a/sound/soc/msm/Makefile +++ b/sound/soc/msm/Makefile @@ -4,9 +4,11 @@ snd-soc-hostless-pcm-objs := msm-pcm-hostless.o obj-$(CONFIG_SND_SOC_MSM_HOSTLESS_PCM) += snd-soc-hostless-pcm.o obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += qdsp6v2/ +obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += qdsp6v2/ snd-soc-qdsp6v2-objs := msm-dai-fe.o obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o +obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += snd-soc-qdsp6v2.o #for APQ8084 sound card driver snd-soc-apq8084-objs := apq8084.o @@ -24,6 +26,10 @@ obj-$(CONFIG_SND_SOC_MSM8994) += snd-soc-msm8994.o snd-soc-msm8996-objs := msm8996.o apq8096-auto.o apq8096-i2c.o obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-msm8996.o +# for MSM8996 virtualization sound card driver +snd-soc-msm8996-vm-objs := apq8096-auto.o +obj-$(CONFIG_SND_SOC_MSM8996_VM) += snd-soc-msm8996-vm.o + # for MSM 8x16/8952 sound card driver snd-soc-msm8x16-objs := msm8952.o msm-audio-pinctrl.o snd-soc-msm8x16-objs += msm8952-slimbus.o msm8952-dai-links.o diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile index 7abaaadc0e06..ea8650b15042 100644 --- a/sound/soc/msm/qdsp6v2/Makefile +++ b/sound/soc/msm/qdsp6v2/Makefile @@ -9,6 +9,8 @@ snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \ obj-$(CONFIG_SLIMBUS) += msm-dai-slim.o audio_slimslave.o obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \ msm-dai-stub-v2.o +obj-$(CONFIG_SND_SOC_QDSP6V2_VM) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \ + msm-dai-stub-v2.o obj-$(CONFIG_SND_HWDEP) += msm-pcm-routing-devdep.o obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o -- GitLab From c9f18bfd776c1f2d20e680ee3a2be615e1e55cb7 Mon Sep 17 00:00:00 2001 From: Honghao Liu Date: Fri, 25 Aug 2017 12:38:30 -0400 Subject: [PATCH 1006/5498] soc: qcom: add SMMU support for virtualized audio ION driver Update the virtualized audio ION driver to add SMMU support. The virtualized audio ION driver will provide the ION memory allocated from the guest VM to the physical VM to perform SMMU mapping. The virtualized audio ION driver will receive SMMU mapped device address from physical VM and provide the device address to its clients. Upon its client frees the memory, the virtualized audio ION driver will request the physical VM to perform SMMU un-mapping. CRs-fixed: 2099245 Change-Id: Idca4ecda65910308d7f7af288381866ab4ce4a7a Signed-off-by: Honghao Liu --- drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c | 338 +++++++++++++++++--- 1 file changed, 297 insertions(+), 41 deletions(-) diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c index c2ab6ef49ffe..fef303853958 100644 --- a/drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c +++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion_vm.c @@ -20,22 +20,251 @@ #include #include #include +#include #include "../../../staging/android/ion/ion_priv.h" #include "../../../staging/android/ion/ion_hvenv_driver.h" #define MSM_AUDIO_ION_PROBED (1 << 0) +#define MSM_AUDIO_SMMU_VM_CMD_MAP 0x00000001 +#define MSM_AUDIO_SMMU_VM_CMD_UNMAP 0x00000002 +#define MSM_AUDIO_SMMU_VM_HAB_MINOR_ID 1 + struct msm_audio_ion_private { bool smmu_enabled; bool audioheap_enabled; u8 device_status; + struct list_head smmu_map_list; + struct mutex smmu_map_mutex; +}; + +struct msm_audio_smmu_map_data { + struct ion_client *client; + struct ion_handle *handle; + u32 export_id; + struct list_head list; +}; + +struct msm_audio_smmu_vm_map_cmd { + int cmd_id; + u32 export_id; + u32 buf_size; +}; + +struct msm_audio_smmu_vm_map_cmd_rsp { + int status; + u64 addr; +}; + +struct msm_audio_smmu_vm_unmap_cmd { + int cmd_id; + u32 export_id; +}; + +struct msm_audio_smmu_vm_unmap_cmd_rsp { + int status; }; static struct msm_audio_ion_private msm_audio_ion_data = {0,}; +static u32 msm_audio_ion_hab_handle; static int msm_audio_ion_get_phys(struct ion_client *client, struct ion_handle *handle, - ion_phys_addr_t *addr, size_t *len); + ion_phys_addr_t *addr, size_t *len, + void *vaddr); + +static int msm_audio_ion_smmu_map(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len, void *vaddr) +{ + int rc; + u32 export_id; + u32 cmd_rsp_size; + bool exported = false; + struct msm_audio_smmu_vm_map_cmd_rsp cmd_rsp; + struct msm_audio_smmu_map_data *map_data = NULL; + struct msm_audio_smmu_vm_map_cmd smmu_map_cmd; + + rc = ion_handle_get_size(client, handle, len); + if (rc) { + pr_err("%s: ion_handle_get_size failed, client = %pK, handle = %pK, rc = %d\n", + __func__, client, handle, rc); + goto err; + } + + /* Data required to track per buffer mapping */ + map_data = kzalloc(sizeof(*map_data), GFP_KERNEL); + if (!map_data) { + rc = -ENOMEM; + goto err; + } + + /* Export the buffer to physical VM */ + rc = habmm_export(msm_audio_ion_hab_handle, vaddr, *len, + &export_id, 0); + if (rc) { + pr_err("%s: habmm_export failed vaddr = %pK, len = %zd, rc = %d\n", + __func__, vaddr, *len, rc); + goto err; + } + + exported = true; + smmu_map_cmd.cmd_id = MSM_AUDIO_SMMU_VM_CMD_MAP; + smmu_map_cmd.export_id = export_id; + smmu_map_cmd.buf_size = *len; + + mutex_lock(&(msm_audio_ion_data.smmu_map_mutex)); + rc = habmm_socket_send(msm_audio_ion_hab_handle, + (void *)&smmu_map_cmd, sizeof(smmu_map_cmd), 0); + if (rc) { + pr_err("%s: habmm_socket_send failed %d\n", + __func__, rc); + mutex_unlock(&(msm_audio_ion_data.smmu_map_mutex)); + goto err; + } + + cmd_rsp_size = sizeof(cmd_rsp); + rc = habmm_socket_recv(msm_audio_ion_hab_handle, + (void *)&cmd_rsp, + &cmd_rsp_size, + 0xFFFFFFFF, + 0); + if (rc) { + pr_err("%s: habmm_socket_recv failed %d\n", + __func__, rc); + mutex_unlock(&(msm_audio_ion_data.smmu_map_mutex)); + goto err; + } + mutex_unlock(&(msm_audio_ion_data.smmu_map_mutex)); + + if (cmd_rsp_size != sizeof(cmd_rsp)) { + pr_err("%s: invalid size for cmd rsp %lu, expected %lu\n", + __func__, cmd_rsp_size, sizeof(cmd_rsp)); + rc = -EIO; + goto err; + } + + if (cmd_rsp.status) { + pr_err("%s: SMMU map command failed %d\n", + __func__, cmd_rsp.status); + rc = cmd_rsp.status; + goto err; + } + + *addr = (ion_phys_addr_t)cmd_rsp.addr; + + map_data->client = client; + map_data->handle = handle; + map_data->export_id = export_id; + + mutex_lock(&(msm_audio_ion_data.smmu_map_mutex)); + list_add_tail(&(map_data->list), + &(msm_audio_ion_data.smmu_map_list)); + mutex_unlock(&(msm_audio_ion_data.smmu_map_mutex)); + + return 0; + +err: + if (exported) + (void)habmm_unexport(msm_audio_ion_hab_handle, export_id, 0); + + kfree(map_data); + + return rc; +} + +static int msm_audio_ion_smmu_unmap(struct ion_client *client, + struct ion_handle *handle) +{ + int rc; + bool found = false; + u32 cmd_rsp_size; + struct msm_audio_smmu_vm_unmap_cmd_rsp cmd_rsp; + struct msm_audio_smmu_map_data *map_data, *next; + struct msm_audio_smmu_vm_unmap_cmd smmu_unmap_cmd; + + /* + * Though list_for_each_entry_safe is delete safe, lock + * should be explicitly acquired to avoid race condition + * on adding elements to the list. + */ + mutex_lock(&(msm_audio_ion_data.smmu_map_mutex)); + list_for_each_entry_safe(map_data, next, + &(msm_audio_ion_data.smmu_map_list), list) { + + if (map_data->handle == handle && map_data->client == client) { + found = true; + smmu_unmap_cmd.cmd_id = MSM_AUDIO_SMMU_VM_CMD_UNMAP; + smmu_unmap_cmd.export_id = map_data->export_id; + + rc = habmm_socket_send(msm_audio_ion_hab_handle, + (void *)&smmu_unmap_cmd, + sizeof(smmu_unmap_cmd), 0); + if (rc) { + pr_err("%s: habmm_socket_send failed %d\n", + __func__, rc); + goto err; + } + + cmd_rsp_size = sizeof(cmd_rsp); + rc = habmm_socket_recv(msm_audio_ion_hab_handle, + (void *)&cmd_rsp, + &cmd_rsp_size, + 0xFFFFFFFF, + 0); + if (rc) { + pr_err("%s: habmm_socket_recv failed %d\n", + __func__, rc); + goto err; + } + + if (cmd_rsp_size != sizeof(cmd_rsp)) { + pr_err("%s: invalid size for cmd rsp %lu\n", + __func__, cmd_rsp_size); + rc = -EIO; + goto err; + } + + if (cmd_rsp.status) { + pr_err("%s: SMMU unmap command failed %d\n", + __func__, cmd_rsp.status); + rc = cmd_rsp.status; + goto err; + } + + rc = habmm_unexport(msm_audio_ion_hab_handle, + map_data->export_id, 0xFFFFFFFF); + if (rc) { + pr_err("%s: habmm_unexport failed export_id = %d, rc = %d\n", + __func__, map_data->export_id, rc); + } + + list_del(&(map_data->list)); + kfree(map_data); + break; + } + } + mutex_unlock(&(msm_audio_ion_data.smmu_map_mutex)); + + if (!found) { + pr_err("%s: cannot find map_data ion_handle %pK, ion_client %pK\n", + __func__, handle, client); + rc = -EINVAL; + } + + return rc; + +err: + if (found) { + (void)habmm_unexport(msm_audio_ion_hab_handle, + map_data->export_id, 0xFFFFFFFF); + list_del(&(map_data->list)); + kfree(map_data); + } + + mutex_unlock(&(msm_audio_ion_data.smmu_map_mutex)); + return rc; +} int msm_audio_ion_alloc(const char *name, struct ion_client **client, struct ion_handle **handle, size_t bufsz, @@ -82,13 +311,6 @@ int msm_audio_ion_alloc(const char *name, struct ion_client **client, msm_audio_ion_data.audioheap_enabled = 1; } - rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); - if (rc) { - pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", - __func__, rc); - goto err_ion_handle; - } - *vaddr = ion_map_kernel(*client, *handle); if (IS_ERR_OR_NULL((void *)*vaddr)) { pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); @@ -102,8 +324,17 @@ int msm_audio_ion_alloc(const char *name, struct ion_client **client, memset((void *)*vaddr, 0, bufsz); } + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len, *vaddr); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_get_phys; + } + return rc; +err_get_phys: + ion_unmap_kernel(*client, *handle); err_ion_handle: ion_free(*client, *handle); err_ion_client: @@ -163,13 +394,6 @@ int msm_audio_ion_import(const char *name, struct ion_client **client, } } - rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); - if (rc) { - pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", - __func__, rc); - goto err_ion_handle; - } - *vaddr = ion_map_kernel(*client, *handle); if (IS_ERR_OR_NULL((void *)*vaddr)) { pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); @@ -179,8 +403,17 @@ int msm_audio_ion_import(const char *name, struct ion_client **client, pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, *vaddr, bufsz); + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len, *vaddr); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_get_phys; + } + return 0; +err_get_phys: + ion_unmap_kernel(*client, *handle); err_ion_handle: ion_free(*client, *handle); err_destroy_client: @@ -193,16 +426,25 @@ err: int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle) { + int ret = 0; + if (!client || !handle) { pr_err("%s Invalid params\n", __func__); return -EINVAL; } + if (msm_audio_ion_data.smmu_enabled) { + ret = msm_audio_ion_smmu_unmap(client, handle); + if (ret) + pr_err("%s: smmu unmap failed with ret %d\n", + __func__, ret); + } + ion_unmap_kernel(client, handle); ion_free(client, handle); msm_audio_ion_client_destroy(client); - return 0; + return ret; } EXPORT_SYMBOL(msm_audio_ion_free); @@ -359,14 +601,6 @@ int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, } } - rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len); - if (rc) { - pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", - __func__, rc); - rc = -EINVAL; - goto err_ion_handle; - } - /*Need to add condition SMMU enable or not */ *vaddr = ion_map_kernel(client, *handle); if (IS_ERR_OR_NULL((void *)*vaddr)) { @@ -378,8 +612,17 @@ int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, if (bufsz != 0) memset((void *)*vaddr, 0, bufsz); + rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len, *vaddr); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_get_phys; + } + return 0; +err_get_phys: + ion_unmap_kernel(client, *handle); err_ion_handle: ion_free(client, *handle); err: @@ -398,7 +641,7 @@ int msm_audio_ion_free_legacy(struct ion_client *client, static int msm_audio_ion_get_phys(struct ion_client *client, struct ion_handle *handle, - ion_phys_addr_t *addr, size_t *len) + ion_phys_addr_t *addr, size_t *len, void *vaddr) { int rc = 0; @@ -406,14 +649,12 @@ static int msm_audio_ion_get_phys(struct ion_client *client, msm_audio_ion_data.smmu_enabled); if (msm_audio_ion_data.smmu_enabled) { - rc = ion_phys(client, handle, addr, len); + rc = msm_audio_ion_smmu_map(client, handle, addr, len, vaddr); if (rc) { - pr_err("%s: ion_phys failed, err = %d\n", + pr_err("%s: failed to do smmu map, err = %d\n", __func__, rc); goto err; } - /* Append the SMMU SID information to the IOVA address */ - *addr |= (1 << 32); } else { rc = ion_phys(client, handle, addr, len); } @@ -430,20 +671,9 @@ static const struct of_device_id msm_audio_ion_dt_match[] = { }; MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match); -u32 msm_audio_ion_get_smmu_sid_mode32(void) -{ - if (msm_audio_ion_data.smmu_enabled) - return 1; - else - return 0; -} - u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa) { - if (sizeof(ion_phys_addr_t) == sizeof(u32)) - return msm_audio_ion_get_smmu_sid_mode32(); - else - return upper_32_bits(pa); + return upper_32_bits(pa); } static int msm_audio_ion_probe(struct platform_device *pdev) @@ -467,6 +697,25 @@ static int msm_audio_ion_probe(struct platform_device *pdev) pr_info("%s: SMMU is %s\n", __func__, (smmu_enabled) ? "Enabled" : "Disabled"); + if (smmu_enabled) { + rc = habmm_socket_open(&msm_audio_ion_hab_handle, + HAB_MMID_CREATE(MM_AUD_3, + MSM_AUDIO_SMMU_VM_HAB_MINOR_ID), + 0xFFFFFFFF, + HABMM_SOCKET_OPEN_FLAGS_SINGLE_BE_SINGLE_FE); + if (rc) { + pr_err("%s: habmm_socket_open failed %d\n", + __func__, rc); + return rc; + } + + pr_info("%s: msm_audio_ion_hab_handle %x\n", + __func__, msm_audio_ion_hab_handle); + + INIT_LIST_HEAD(&msm_audio_ion_data.smmu_map_list); + mutex_init(&(msm_audio_ion_data.smmu_map_mutex)); + } + if (!rc) msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED; @@ -475,8 +724,15 @@ static int msm_audio_ion_probe(struct platform_device *pdev) static int msm_audio_ion_remove(struct platform_device *pdev) { + if (msm_audio_ion_data.smmu_enabled) { + if (msm_audio_ion_hab_handle) + habmm_socket_close(msm_audio_ion_hab_handle); + + mutex_destroy(&(msm_audio_ion_data.smmu_map_mutex)); + } msm_audio_ion_data.smmu_enabled = 0; msm_audio_ion_data.device_status = 0; + return 0; } -- GitLab From 0c2d19cf03fc8f2f37704e74a310252a6a460655 Mon Sep 17 00:00:00 2001 From: Derek Chen Date: Wed, 6 Sep 2017 18:34:09 -0400 Subject: [PATCH 1007/5498] ARM: dts: msm: update audio ion device node on msm8996 Update msm audio ion device node to be compatible with virtualized audio ion platform driver on MSM8996 platform. CRs-fixed: 2047379 Signed-off-by: Derek Chen Change-Id: Ibd6b5cfa7e8539a56b6d1650ea0d2ae15ce82073 --- arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts index 8ad7ca7f9743..05c977e1c523 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dts @@ -111,10 +111,9 @@ asoc-codec-names = "msm-stub-codec.1"; }; - qcom,msm-audio-ion { - compatible = "qcom,msm-audio-ion"; + qcom,msm-audio-ion-vm { + compatible = "qcom,msm-audio-ion-vm"; qcom,smmu-enabled; - qcom,smmu-sid = <1>; }; pcm0: qcom,msm-pcm { -- GitLab From 3791990eeeeee8eb1fdc9289dd2d8078474e78d6 Mon Sep 17 00:00:00 2001 From: Hareesh Gundu Date: Wed, 7 Jun 2017 14:50:15 +0530 Subject: [PATCH 1008/5498] msm: kgsl: Defer issue commands to worker thread Currently submit ioctl getting blocked till the commands gets added to ringbuffer incase inflight count is less than context burst count. If the submit command happens in GPU slumber state, it will add the GPU wakeup time to submit IOCTL. This will add latency in preparing next frame in CPU side. Defer commands submission to dispatcher worker, if the GPU is in slumber state. CRs-Fixed: 2055107 Change-Id: I099ba721e02bbcd8ccadb1bc518c7c1ef4fb7e21 Signed-off-by: Hareesh Gundu --- drivers/gpu/msm/adreno_dispatch.c | 25 +++++++++++++++++++++++-- drivers/gpu/msm/kgsl.c | 1 + drivers/gpu/msm/kgsl_device.h | 5 +++++ drivers/gpu/msm/kgsl_pwrctrl.c | 24 +++++++++++++++++++++++- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 98ff434a64e1..48a72fb3c0b3 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -915,6 +915,13 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) spin_unlock(&dispatcher->plist_lock); } +static inline void _decrement_submit_now(struct kgsl_device *device) +{ + spin_lock(&device->submit_lock); + device->submit_now--; + spin_unlock(&device->submit_lock); +} + /** * adreno_dispatcher_issuecmds() - Issue commmands from pending contexts * @adreno_dev: Pointer to the adreno device struct @@ -924,15 +931,29 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) static void adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) { struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + spin_lock(&device->submit_lock); + /* If state transition to SLUMBER, schedule the work for later */ + if (device->slumber == true) { + spin_unlock(&device->submit_lock); + goto done; + } + device->submit_now++; + spin_unlock(&device->submit_lock); /* If the dispatcher is busy then schedule the work for later */ if (!mutex_trylock(&dispatcher->mutex)) { - adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); - return; + _decrement_submit_now(device); + goto done; } _adreno_dispatcher_issuecmds(adreno_dev); mutex_unlock(&dispatcher->mutex); + _decrement_submit_now(device); + return; +done: + adreno_dispatcher_schedule(device); } /** diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index bfcd54ca1a22..a58d6e8b2080 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -3916,6 +3916,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device) device->id, device->reg_phys, device->reg_len); rwlock_init(&device->context_lock); + spin_lock_init(&device->submit_lock); setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device); diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index b38082b19b4e..9114f9b660e4 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -246,6 +246,11 @@ struct kgsl_device { struct kgsl_pwrctrl pwrctrl; int open_count; + /* For GPU inline submission */ + uint32_t submit_now; + spinlock_t submit_lock; + bool slumber; + struct mutex mutex; uint32_t state; uint32_t requested_state; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 9c637abf3d44..4e9a3fe71980 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -2350,9 +2350,24 @@ void kgsl_idle_check(struct work_struct *work) || device->state == KGSL_STATE_DEEP_NAP) { if (!atomic_read(&device->active_cnt)) { + spin_lock(&device->submit_lock); + if (device->submit_now) { + spin_unlock(&device->submit_lock); + goto done; + } + /* Don't allow GPU inline submission in SLUMBER */ + if (requested_state == KGSL_STATE_SLUMBER) + device->slumber = true; + spin_unlock(&device->submit_lock); + ret = kgsl_pwrctrl_change_state(device, device->requested_state); if (ret == -EBUSY) { + if (requested_state == KGSL_STATE_SLUMBER) { + spin_lock(&device->submit_lock); + device->slumber = false; + spin_unlock(&device->submit_lock); + } /* * If the GPU is currently busy, restore * the requested state and reschedule @@ -2363,7 +2378,7 @@ void kgsl_idle_check(struct work_struct *work) kgsl_schedule_work(&device->idle_check_ws); } } - +done: if (!ret) kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); @@ -2879,6 +2894,13 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device, trace_kgsl_pwr_set_state(device, state); device->state = state; device->requested_state = KGSL_STATE_NONE; + + spin_lock(&device->submit_lock); + if (state == KGSL_STATE_SLUMBER || state == KGSL_STATE_SUSPEND) + device->slumber = true; + else + device->slumber = false; + spin_unlock(&device->submit_lock); } static void kgsl_pwrctrl_request_state(struct kgsl_device *device, -- GitLab From d9c7bb67a7c365994ebba2e52ee3e93faa70ce86 Mon Sep 17 00:00:00 2001 From: Kaushal Kumar Date: Tue, 26 Sep 2017 19:58:48 +0530 Subject: [PATCH 1009/5498] soc: qcom: pil: Avoid possible buffer overflow MBA image size comparison check is currently being done with a signed count whose value can possibly be negative. If count value is negative then comparison will always succeed and invoke memcpy with incorrect value of count leading to buffer overflow. Fix this by not using signed comparison. Change-Id: Id2d0cafae01f940f36cfd559d4656fc0f022d6a5 Signed-off-by: Kaushal Kumar --- drivers/soc/qcom/pil-msa.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c index 11031e1bcfb0..f09387c3d5ab 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.c @@ -534,7 +534,7 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) char *fw_name_p; void *mba_dp_virt; dma_addr_t mba_dp_phys, mba_dp_phys_end; - int ret, count; + int ret; const u8 *data; struct device *dma_dev = md->mba_mem_dev_fixed ?: &md->mba_mem_dev; @@ -595,10 +595,9 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) &mba_dp_phys, &mba_dp_phys_end, drv->mba_dp_size); /* Load the MBA image into memory */ - count = fw->size; - if (count <= SZ_1M) { + if (fw->size <= SZ_1M) { /* Ensures memcpy is done for max 1MB fw size */ - memcpy(mba_dp_virt, data, count); + memcpy(mba_dp_virt, data, fw->size); } else { dev_err(pil->dev, "%s fw image loading into memory is failed due to fw size overflow\n", __func__); -- GitLab From 51ef0b663c13cffbc1cc74102122bd4013726c1b Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Fri, 18 Aug 2017 14:40:53 +0200 Subject: [PATCH 1010/5498] ipv6: accept 64k - 1 packet length in ip6_find_1stfragopt() [ Upstream commit 3de33e1ba0506723ab25734e098cf280ecc34756 ] A packet length of exactly IPV6_MAXPLEN is allowed, we should refuse parsing options only if the size is 64KiB or more. While at it, remove one extra variable and one assignment which were also introduced by the commit that introduced the size check. Checking the sum 'offset + len' and only later adding 'len' to 'offset' doesn't provide any advantage over directly summing to 'offset' and checking it. Fixes: 6399f1fae4ec ("ipv6: avoid overflow of offset in ip6_find_1stfragopt") Signed-off-by: Stefano Brivio Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/output_core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index c471baa92b9a..fc6d3e0e4341 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -52,7 +52,6 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) while (offset <= packet_len) { struct ipv6_opt_hdr *exthdr; - unsigned int len; switch (**nexthdr) { @@ -78,10 +77,9 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) + offset); - len = ipv6_optlen(exthdr); - if (len + offset >= IPV6_MAXPLEN) + offset += ipv6_optlen(exthdr); + if (offset > IPV6_MAXPLEN) return -EINVAL; - offset += len; *nexthdr = &exthdr->nexthdr; } -- GitLab From fea532ea08ada35c0700c4c1be878437b023ed00 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 23 Aug 2017 15:59:49 +0200 Subject: [PATCH 1011/5498] qlge: avoid memcpy buffer overflow [ Upstream commit e58f95831e7468d25eb6e41f234842ecfe6f014f ] gcc-8.0.0 (snapshot) points out that we copy a variable-length string into a fixed length field using memcpy() with the destination length, and that ends up copying whatever follows the string: inlined from 'ql_core_dump' at drivers/net/ethernet/qlogic/qlge/qlge_dbg.c:1106:2: drivers/net/ethernet/qlogic/qlge/qlge_dbg.c:708:2: error: 'memcpy' reading 15 bytes from a region of size 14 [-Werror=stringop-overflow=] memcpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1); Changing it to use strncpy() will instead zero-pad the destination, which seems to be the right thing to do here. The bug is probably harmless, but it seems like a good idea to address it in stable kernels as well, if only for the purpose of building with gcc-8 without warnings. Fixes: a61f80261306 ("qlge: Add ethtool register dump function.") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/qlogic/qlge/qlge_dbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c index 829be21f97b2..be258d90de9e 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c @@ -724,7 +724,7 @@ static void ql_build_coredump_seg_header( seg_hdr->cookie = MPI_COREDUMP_COOKIE; seg_hdr->segNum = seg_number; seg_hdr->segSize = seg_size; - memcpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1); + strncpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1); } /* -- GitLab From 39194a40eef5a4771404ea04910b8216f8d2a065 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 30 Aug 2017 17:49:29 -0700 Subject: [PATCH 1012/5498] Revert "net: phy: Correctly process PHY_HALTED in phy_stop_machine()" [ Upstream commit ebc8254aeae34226d0bc8fda309fd9790d4dccfe ] This reverts commit 7ad813f208533cebfcc32d3d7474dc1677d1b09a ("net: phy: Correctly process PHY_HALTED in phy_stop_machine()") because it is creating the possibility for a NULL pointer dereference. David Daney provide the following call trace and diagram of events: When ndo_stop() is called we call: phy_disconnect() +---> phy_stop_interrupts() implies: phydev->irq = PHY_POLL; +---> phy_stop_machine() | +---> phy_state_machine() | +----> queue_delayed_work(): Work queued. +--->phy_detach() implies: phydev->attached_dev = NULL; Now at a later time the queued work does: phy_state_machine() +---->netif_carrier_off(phydev->attached_dev): Oh no! It is NULL: CPU 12 Unable to handle kernel paging request at virtual address 0000000000000048, epc == ffffffff80de37ec, ra == ffffffff80c7c Oops[#1]: CPU: 12 PID: 1502 Comm: kworker/12:1 Not tainted 4.9.43-Cavium-Octeon+ #1 Workqueue: events_power_efficient phy_state_machine task: 80000004021ed100 task.stack: 8000000409d70000 $ 0 : 0000000000000000 ffffffff84720060 0000000000000048 0000000000000004 $ 4 : 0000000000000000 0000000000000001 0000000000000004 0000000000000000 $ 8 : 0000000000000000 0000000000000000 00000000ffff98f3 0000000000000000 $12 : 8000000409d73fe0 0000000000009c00 ffffffff846547c8 000000000000af3b $16 : 80000004096bab68 80000004096babd0 0000000000000000 80000004096ba800 $20 : 0000000000000000 0000000000000000 ffffffff81090000 0000000000000008 $24 : 0000000000000061 ffffffff808637b0 $28 : 8000000409d70000 8000000409d73cf0 80000000271bd300 ffffffff80c7804c Hi : 000000000000002a Lo : 000000000000003f epc : ffffffff80de37ec netif_carrier_off+0xc/0x58 ra : ffffffff80c7804c phy_state_machine+0x48c/0x4f8 Status: 14009ce3 KX SX UX KERNEL EXL IE Cause : 00800008 (ExcCode 02) BadVA : 0000000000000048 PrId : 000d9501 (Cavium Octeon III) Modules linked in: Process kworker/12:1 (pid: 1502, threadinfo=8000000409d70000, task=80000004021ed100, tls=0000000000000000) Stack : 8000000409a54000 80000004096bab68 80000000271bd300 80000000271c1e00 0000000000000000 ffffffff808a1708 8000000409a54000 80000000271bd300 80000000271bd320 8000000409a54030 ffffffff80ff0f00 0000000000000001 ffffffff81090000 ffffffff808a1ac0 8000000402182080 ffffffff84650000 8000000402182080 ffffffff84650000 ffffffff80ff0000 8000000409a54000 ffffffff808a1970 0000000000000000 80000004099e8000 8000000402099240 0000000000000000 ffffffff808a8598 0000000000000000 8000000408eeeb00 8000000409a54000 00000000810a1d00 0000000000000000 8000000409d73de8 8000000409d73de8 0000000000000088 000000000c009c00 8000000409d73e08 8000000409d73e08 8000000402182080 ffffffff808a84d0 8000000402182080 ... Call Trace: [] netif_carrier_off+0xc/0x58 [] phy_state_machine+0x48c/0x4f8 [] process_one_work+0x158/0x368 [] worker_thread+0x150/0x4c0 [] kthread+0xc8/0xe0 [] ret_from_kernel_thread+0x14/0x1c The original motivation for this change originated from Marc Gonzales indicating that his network driver did not have its adjust_link callback executing with phydev->link = 0 while he was expecting it. PHYLIB has never made any such guarantees ever because phy_stop() merely just tells the workqueue to move into PHY_HALTED state which will happen asynchronously. Reported-by: Geert Uytterhoeven Reported-by: David Daney Fixes: 7ad813f20853 ("net: phy: Correctly process PHY_HALTED in phy_stop_machine()") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/phy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index caf4d3469d4a..7ae062743bad 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -509,9 +509,6 @@ void phy_stop_machine(struct phy_device *phydev) if (phydev->state > PHY_UP && phydev->state != PHY_HALTED) phydev->state = PHY_UP; mutex_unlock(&phydev->lock); - - /* Now we can run the state machine synchronously */ - phy_state_machine(&phydev->state_queue.work); } /** -- GitLab From 1722ca90e1b88e6b7f0824908828e2462d7405ac Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 18 May 2017 11:22:33 -0700 Subject: [PATCH 1013/5498] tcp: initialize rcv_mss to TCP_MIN_MSS instead of 0 [ Upstream commit 499350a5a6e7512d9ed369ed63a4244b6536f4f8 ] When tcp_disconnect() is called, inet_csk_delack_init() sets icsk->icsk_ack.rcv_mss to 0. This could potentially cause tcp_recvmsg() => tcp_cleanup_rbuf() => __tcp_select_window() call path to have division by 0 issue. So this patch initializes rcv_mss to TCP_MIN_MSS instead of 0. Reported-by: Andrey Konovalov Signed-off-by: Wei Wang Signed-off-by: Eric Dumazet Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 458a4c046784..567f8860e722 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2265,6 +2265,10 @@ int tcp_disconnect(struct sock *sk, int flags) tcp_set_ca_state(sk, TCP_CA_Open); tcp_clear_retrans(tp); inet_csk_delack_init(sk); + /* Initialize rcv_mss to TCP_MIN_MSS to avoid division by 0 + * issue in __tcp_select_window() + */ + icsk->icsk_ack.rcv_mss = TCP_MIN_MSS; tcp_init_send_head(sk); memset(&tp->rx_opt, 0, sizeof(tp->rx_opt)); __sk_dst_reset(sk); -- GitLab From fbca27ad2916e18251168d7b43e3d185001917fc Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 8 Sep 2017 10:26:19 +0200 Subject: [PATCH 1014/5498] ipv6: fix memory leak with multiple tables during netns destruction [ Upstream commit ba1cc08d9488c94cb8d94f545305688b72a2a300 ] fib6_net_exit only frees the main and local tables. If another table was created with fib6_alloc_table, we leak it when the netns is destroyed. Fix this in the same way ip_fib_net_exit cleans up tables, by walking through the whole hashtable of fib6_table's. We can get rid of the special cases for local and main, since they're also part of the hashtable. Reproducer: ip netns add x ip -net x -6 rule add from 6003:1::/64 table 100 ip netns del x Reported-by: Jianlin Shi Fixes: 58f09b78b730 ("[NETNS][IPV6] ip6_fib - make it per network namespace") Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_fib.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index f1c6d5e98322..49f0d72ab921 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -160,6 +160,12 @@ static void rt6_release(struct rt6_info *rt) dst_free(&rt->dst); } +static void fib6_free_table(struct fib6_table *table) +{ + inetpeer_invalidate_tree(&table->tb6_peers); + kfree(table); +} + static void fib6_link_table(struct net *net, struct fib6_table *tb) { unsigned int h; @@ -1782,15 +1788,22 @@ out_timer: static void fib6_net_exit(struct net *net) { + unsigned int i; + rt6_ifdown(net, NULL); del_timer_sync(&net->ipv6.ip6_fib_timer); -#ifdef CONFIG_IPV6_MULTIPLE_TABLES - inetpeer_invalidate_tree(&net->ipv6.fib6_local_tbl->tb6_peers); - kfree(net->ipv6.fib6_local_tbl); -#endif - inetpeer_invalidate_tree(&net->ipv6.fib6_main_tbl->tb6_peers); - kfree(net->ipv6.fib6_main_tbl); + for (i = 0; i < FIB_TABLE_HASHSZ; i++) { + struct hlist_head *head = &net->ipv6.fib_table_hash[i]; + struct hlist_node *tmp; + struct fib6_table *tb; + + hlist_for_each_entry_safe(tb, tmp, head, tb6_hlist) { + hlist_del(&tb->tb6_hlist); + fib6_free_table(tb); + } + } + kfree(net->ipv6.fib_table_hash); kfree(net->ipv6.rt6_stats); } -- GitLab From 4c97e1c4a67cf5c1150e78de714571a87654ac3d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 8 Sep 2017 15:48:47 -0700 Subject: [PATCH 1015/5498] ipv6: fix typo in fib6_net_exit() [ Upstream commit 32a805baf0fb70b6dbedefcd7249ac7f580f9e3b ] IPv6 FIB should use FIB6_TABLE_HASHSZ, not FIB_TABLE_HASHSZ. Fixes: ba1cc08d9488 ("ipv6: fix memory leak with multiple tables during netns destruction") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_fib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 49f0d72ab921..f6337e18f428 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1793,7 +1793,7 @@ static void fib6_net_exit(struct net *net) rt6_ifdown(net, NULL); del_timer_sync(&net->ipv6.ip6_fib_timer); - for (i = 0; i < FIB_TABLE_HASHSZ; i++) { + for (i = 0; i < FIB6_TABLE_HASHSZ; i++) { struct hlist_head *head = &net->ipv6.fib_table_hash[i]; struct hlist_node *tmp; struct fib6_table *tb; -- GitLab From 70768be91bcc00508038ee743c76fe9f404dfcf9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 12 Aug 2017 21:33:23 -0700 Subject: [PATCH 1016/5498] f2fs: check hot_data for roll-forward recovery commit 125c9fb1ccb53eb2ea9380df40f3c743f3fb2fed upstream. We need to check HOT_DATA to truncate any previous data block when doing roll-forward recovery. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim Signed-off-by: Greg Kroah-Hartman --- fs/f2fs/recovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index ebd013225788..f064ea1bdab2 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -263,7 +263,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, return 0; /* Get the previous summary */ - for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) { + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); if (curseg->segno == segno) { sum = curseg->sum_blk->entries[blkoff]; -- GitLab From c8443922edb7f28ecdfe5d2b43552ebdcb33d1fb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 Sep 2017 12:31:05 +0200 Subject: [PATCH 1017/5498] Revert "usb: musb: fix tx fifo flush handling again" This reverts commit 98b91bfa5e478b9bf332f9f149b1c25ffd58f877 which is commit 45d73860530a14c608f410b91c6c341777bfa85d upstream. It should not have been applied to the 3.18-stable tree at all. Reported-by: Greg Kaiser Cc: Bin Liu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 33223e4b53c6..7da914bc2094 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1294,7 +1294,6 @@ void musb_host_tx(struct musb *musb, u8 epnum) | MUSB_TXCSR_TXPKTRDY); } return; - mdelay(1); } done: -- GitLab From 53c94358a14ffa7f3ba24d9749e8b297cdb0b520 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Wed, 4 Feb 2015 15:25:09 +0100 Subject: [PATCH 1018/5498] ip6_gre: fix endianness errors in ip6gre_err commit d1e158e2d7a0a91110b206653f0e02376e809150 upstream. info is in network byte order, change it back to host byte order before use. In particular, the current code sets the MTU of the tunnel to a wrong (too big) value. Fixes: c12b395a4664 ("gre: Support GRE over IPv6") Signed-off-by: Sabrina Dubroca Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_gre.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index df3633e20458..77adf255d238 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -421,7 +421,7 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (code == ICMPV6_HDR_FIELD) teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data); - if (teli && teli == info - 2) { + if (teli && teli == be32_to_cpu(info) - 2) { tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; if (tel->encap_limit == 0) { net_warn_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n", @@ -433,7 +433,7 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, } break; case ICMPV6_PKT_TOOBIG: - mtu = info - offset; + mtu = be32_to_cpu(info) - offset; if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; t->dev->mtu = mtu; -- GitLab From a6d09b8a6ee3d85cac18a04fad0d219cf94e4526 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 15 Sep 2017 09:36:16 -0700 Subject: [PATCH 1019/5498] Input: i8042 - add Gigabyte P57 to the keyboard reset table commit 697c5d8a36768b36729533fb44622b35d56d6ad0 upstream. Similar to other Gigabyte laptops, the touchpad on P57 requires a keyboard reset to detect Elantech touchpad correctly. BugLink: https://bugs.launchpad.net/bugs/1594214 Signed-off-by: Kai-Heng Feng Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 160b705869f3..3f9ad6414c1b 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -804,6 +804,13 @@ static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "P34"), }, }, + { + /* Gigabyte P57 - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P57"), + }, + }, { /* Schenker XMG C504 - Elantech touchpad */ .matches = { -- GitLab From 1d4ba7f963a93a2207fd103d4a36df1b5aeefea2 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Thu, 21 Sep 2017 10:16:53 +0200 Subject: [PATCH 1020/5498] crypto: AF_ALG - remove SGL terminator indicator when chaining Fixed differently upstream as commit 2d97591ef43d ("crypto: af_alg - consolidation of duplicate code") The SGL is MAX_SGL_ENTS + 1 in size. The last SG entry is used for the chaining and is properly updated with the sg_chain invocation. During the filling-in of the initial SG entries, sg_mark_end is called for each SG entry. This is appropriate as long as no additional SGL is chained with the current SGL. However, when a new SGL is chained and the last SG entry is updated with sg_chain, the last but one entry still contains the end marker from the sg_mark_end. This end marker must be removed as otherwise a walk of the chained SGLs will cause a NULL pointer dereference at the last but one SG entry, because sg_next will return NULL. The patch only applies to all kernels up to and including 4.13. The patch 2d97591ef43d0587be22ad1b0d758d6df4999a0b added to 4.14-rc1 introduced a complete new code base which addresses this bug in a different way. Yet, that patch is too invasive for stable kernels and was therefore not marked for stable. Fixes: 8ff590903d5fc ("crypto: algif_skcipher - User-space interface for skcipher operations") Signed-off-by: Stephan Mueller Acked-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/algif_skcipher.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index d2cacc7f079f..fd1967ecc928 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -92,8 +92,10 @@ static int skcipher_alloc_sgl(struct sock *sk) sg_init_table(sgl->sg, MAX_SGL_ENTS + 1); sgl->cur = 0; - if (sg) + if (sg) { scatterwalk_sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg); + sg_unmark_end(sg + (MAX_SGL_ENTS - 1)); + } list_add_tail(&sgl->list, &ctx->tsgl); } -- GitLab From ab1717dafd8355d3a52ec94deacf806a232ad773 Mon Sep 17 00:00:00 2001 From: "zhangyi (F)" Date: Thu, 24 Aug 2017 15:19:39 -0400 Subject: [PATCH 1021/5498] ext4: fix incorrect quotaoff if the quota feature is enabled commit b0a5a9589decd07db755d6a8d9c0910d96ff7992 upstream. Current ext4 quota should always "usage enabled" if the quota feautre is enabled. But in ext4_orphan_cleanup(), it turn quotas off directly (used for the older journaled quota), so we cannot turn it on again via "quotaon" unless umount and remount ext4. Simple reproduce: mkfs.ext4 -O project,quota /dev/vdb1 mount -o prjquota /dev/vdb1 /mnt chattr -p 123 /mnt chattr +P /mnt touch /mnt/aa /mnt/bb exec 100<>/mnt/aa rm -f /mnt/aa sync echo c > /proc/sysrq-trigger #reboot and mount mount -o prjquota /dev/vdb1 /mnt #query status quotaon -Ppv /dev/vdb1 #output quotaon: Cannot find mountpoint for device /dev/vdb1 quotaon: No correct mountpoint specified. This patch add check for journaled quotas to avoid incorrect quotaoff when ext4 has quota feautre. Signed-off-by: zhangyi (F) Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index a5200023e604..a64e4368bb58 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2205,7 +2205,7 @@ static void ext4_orphan_cleanup(struct super_block *sb, #ifdef CONFIG_QUOTA /* Needed for iput() to work correctly and not trash data */ sb->s_flags |= MS_ACTIVE; - /* Turn on quotas so that they are updated correctly */ + /* Turn on journaled quotas so that they are updated correctly */ for (i = 0; i < EXT4_MAXQUOTAS; i++) { if (EXT4_SB(sb)->s_qf_names[i]) { int ret = ext4_quota_on_mount(sb, i); @@ -2271,9 +2271,9 @@ static void ext4_orphan_cleanup(struct super_block *sb, ext4_msg(sb, KERN_INFO, "%d truncate%s cleaned up", PLURAL(nr_truncates)); #ifdef CONFIG_QUOTA - /* Turn quotas off */ + /* Turn off journaled quotas if they were enabled for orphan cleanup */ for (i = 0; i < EXT4_MAXQUOTAS; i++) { - if (sb_dqopt(sb)->files[i]) + if (EXT4_SB(sb)->s_qf_names[i] && sb_dqopt(sb)->files[i]) dquot_quota_off(sb, i); } #endif -- GitLab From 87e47744e1879b1e57d428ffc5018901d7488f95 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 24 Aug 2017 20:49:57 +1000 Subject: [PATCH 1022/5498] powerpc: Fix DAR reporting when alignment handler faults commit f9effe925039cf54489b5c04e0d40073bb3a123d upstream. Anton noticed that if we fault part way through emulating an unaligned instruction, we don't update the DAR to reflect that. The DAR value is eventually reported back to userspace as the address in the SEGV signal, and if userspace is using that value to demand fault then it can be confused by us not setting the value correctly. This patch is ugly as hell, but is intended to be the minimal fix and back ports easily. Signed-off-by: Michael Ellerman Reviewed-by: Paul Mackerras Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/align.c | 119 ++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index ce068cb2071f..aa3042044118 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -236,6 +236,28 @@ static int emulate_dcbz(struct pt_regs *regs, unsigned char __user *addr) #define SWIZ_PTR(p) ((unsigned char __user *)((p) ^ swiz)) +#define __get_user_or_set_dar(_regs, _dest, _addr) \ + ({ \ + int rc = 0; \ + typeof(_addr) __addr = (_addr); \ + if (__get_user_inatomic(_dest, __addr)) { \ + _regs->dar = (unsigned long)__addr; \ + rc = -EFAULT; \ + } \ + rc; \ + }) + +#define __put_user_or_set_dar(_regs, _src, _addr) \ + ({ \ + int rc = 0; \ + typeof(_addr) __addr = (_addr); \ + if (__put_user_inatomic(_src, __addr)) { \ + _regs->dar = (unsigned long)__addr; \ + rc = -EFAULT; \ + } \ + rc; \ + }) + static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, unsigned int reg, unsigned int nb, unsigned int flags, unsigned int instr, @@ -264,9 +286,10 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, } else { unsigned long pc = regs->nip ^ (swiz & 4); - if (__get_user_inatomic(instr, - (unsigned int __user *)pc)) + if (__get_user_or_set_dar(regs, instr, + (unsigned int __user *)pc)) return -EFAULT; + if (swiz == 0 && (flags & SW)) instr = cpu_to_le32(instr); nb = (instr >> 11) & 0x1f; @@ -310,31 +333,31 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, ((nb0 + 3) / 4) * sizeof(unsigned long)); for (i = 0; i < nb; ++i, ++p) - if (__get_user_inatomic(REG_BYTE(rptr, i ^ bswiz), - SWIZ_PTR(p))) + if (__get_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; if (nb0 > 0) { rptr = ®s->gpr[0]; addr += nb; for (i = 0; i < nb0; ++i, ++p) - if (__get_user_inatomic(REG_BYTE(rptr, - i ^ bswiz), - SWIZ_PTR(p))) + if (__get_user_or_set_dar(regs, + REG_BYTE(rptr, i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; } } else { for (i = 0; i < nb; ++i, ++p) - if (__put_user_inatomic(REG_BYTE(rptr, i ^ bswiz), - SWIZ_PTR(p))) + if (__put_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; if (nb0 > 0) { rptr = ®s->gpr[0]; addr += nb; for (i = 0; i < nb0; ++i, ++p) - if (__put_user_inatomic(REG_BYTE(rptr, - i ^ bswiz), - SWIZ_PTR(p))) + if (__put_user_or_set_dar(regs, + REG_BYTE(rptr, i ^ bswiz), + SWIZ_PTR(p))) return -EFAULT; } } @@ -346,29 +369,32 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr, * Only POWER6 has these instructions, and it does true little-endian, * so we don't need the address swizzling. */ -static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg, - unsigned int flags) +static int emulate_fp_pair(struct pt_regs *regs, unsigned char __user *addr, + unsigned int reg, unsigned int flags) { char *ptr0 = (char *) ¤t->thread.TS_FPR(reg); char *ptr1 = (char *) ¤t->thread.TS_FPR(reg+1); - int i, ret, sw = 0; + int i, sw = 0; if (reg & 1) return 0; /* invalid form: FRS/FRT must be even */ if (flags & SW) sw = 7; - ret = 0; + for (i = 0; i < 8; ++i) { if (!(flags & ST)) { - ret |= __get_user(ptr0[i^sw], addr + i); - ret |= __get_user(ptr1[i^sw], addr + i + 8); + if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i)) + return -EFAULT; + if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8)) + return -EFAULT; } else { - ret |= __put_user(ptr0[i^sw], addr + i); - ret |= __put_user(ptr1[i^sw], addr + i + 8); + if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i)) + return -EFAULT; + if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8)) + return -EFAULT; } } - if (ret) - return -EFAULT; + return 1; /* exception handled and fixed up */ } @@ -378,24 +404,27 @@ static int emulate_lq_stq(struct pt_regs *regs, unsigned char __user *addr, { char *ptr0 = (char *)®s->gpr[reg]; char *ptr1 = (char *)®s->gpr[reg+1]; - int i, ret, sw = 0; + int i, sw = 0; if (reg & 1) return 0; /* invalid form: GPR must be even */ if (flags & SW) sw = 7; - ret = 0; + for (i = 0; i < 8; ++i) { if (!(flags & ST)) { - ret |= __get_user(ptr0[i^sw], addr + i); - ret |= __get_user(ptr1[i^sw], addr + i + 8); + if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i)) + return -EFAULT; + if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8)) + return -EFAULT; } else { - ret |= __put_user(ptr0[i^sw], addr + i); - ret |= __put_user(ptr1[i^sw], addr + i + 8); + if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i)) + return -EFAULT; + if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8)) + return -EFAULT; } } - if (ret) - return -EFAULT; + return 1; /* exception handled and fixed up */ } #endif /* CONFIG_PPC64 */ @@ -688,9 +717,14 @@ static int emulate_vsx(unsigned char __user *addr, unsigned int reg, for (j = 0; j < length; j += elsize) { for (i = 0; i < elsize; ++i) { if (flags & ST) - ret |= __put_user(ptr[i^sw], addr + i); + ret = __put_user_or_set_dar(regs, ptr[i^sw], + addr + i); else - ret |= __get_user(ptr[i^sw], addr + i); + ret = __get_user_or_set_dar(regs, ptr[i^sw], + addr + i); + + if (ret) + return ret; } ptr += elsize; #ifdef __LITTLE_ENDIAN__ @@ -740,7 +774,7 @@ int fix_alignment(struct pt_regs *regs) unsigned int dsisr; unsigned char __user *addr; unsigned long p, swiz; - int ret, i; + int i; union data { u64 ll; double dd; @@ -923,7 +957,7 @@ int fix_alignment(struct pt_regs *regs) if (flags & F) { /* Special case for 16-byte FP loads and stores */ PPC_WARN_ALIGNMENT(fp_pair, regs); - return emulate_fp_pair(addr, reg, flags); + return emulate_fp_pair(regs, addr, reg, flags); } else { #ifdef CONFIG_PPC64 /* Special case for 16-byte loads and stores */ @@ -953,15 +987,12 @@ int fix_alignment(struct pt_regs *regs) } data.ll = 0; - ret = 0; p = (unsigned long)addr; for (i = 0; i < nb; i++) - ret |= __get_user_inatomic(data.v[start + i], - SWIZ_PTR(p++)); - - if (unlikely(ret)) - return -EFAULT; + if (__get_user_or_set_dar(regs, data.v[start + i], + SWIZ_PTR(p++))) + return -EFAULT; } else if (flags & F) { data.ll = current->thread.TS_FPR(reg); @@ -1031,15 +1062,13 @@ int fix_alignment(struct pt_regs *regs) break; } - ret = 0; p = (unsigned long)addr; for (i = 0; i < nb; i++) - ret |= __put_user_inatomic(data.v[start + i], - SWIZ_PTR(p++)); + if (__put_user_or_set_dar(regs, data.v[start + i], + SWIZ_PTR(p++))) + return -EFAULT; - if (unlikely(ret)) - return -EFAULT; } else if (flags & F) current->thread.TS_FPR(reg) = data.ll; else -- GitLab From 617f119a64ce5cc0b9b1eee1e7bcf1e82a671772 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 17 Aug 2017 13:12:44 -0700 Subject: [PATCH 1023/5498] block: Relax a check in blk_start_queue() commit 4ddd56b003f251091a67c15ae3fe4a5c5c5e390a upstream. Calling blk_start_queue() from interrupt context with the queue lock held and without disabling IRQs, as the skd driver does, is safe. This patch avoids that loading the skd driver triggers the following warning: WARNING: CPU: 11 PID: 1348 at block/blk-core.c:283 blk_start_queue+0x84/0xa0 RIP: 0010:blk_start_queue+0x84/0xa0 Call Trace: skd_unquiesce_dev+0x12a/0x1d0 [skd] skd_complete_internal+0x1e7/0x5a0 [skd] skd_complete_other+0xc2/0xd0 [skd] skd_isr_completion_posted.isra.30+0x2a5/0x470 [skd] skd_isr+0x14f/0x180 [skd] irq_forced_thread_fn+0x2a/0x70 irq_thread+0x144/0x1a0 kthread+0x125/0x140 ret_from_fork+0x2a/0x40 Fixes: commit a038e2536472 ("[PATCH] blk_start_queue() must be called with irq disabled - add warning") Signed-off-by: Bart Van Assche Cc: Paolo 'Blaisorblade' Giarrusso Cc: Andrew Morton Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Johannes Thumshirn Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/blk-core.c b/block/blk-core.c index 93f9152fc271..3a099c28a7b5 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -194,7 +194,7 @@ EXPORT_SYMBOL(blk_delay_queue); **/ void blk_start_queue(struct request_queue *q) { - WARN_ON(!irqs_disabled()); + WARN_ON(!in_interrupt() && !irqs_disabled()); queue_flag_clear(QUEUE_FLAG_STOPPED, q); __blk_run_queue(q); -- GitLab From 1074b91bdd4b4ea9b17876c17404b81c19218e49 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 31 Aug 2017 10:23:25 +1000 Subject: [PATCH 1024/5498] md/bitmap: disable bitmap_resize for file-backed bitmaps. commit e8a27f836f165c26f867ece7f31eb5c811692319 upstream. bitmap_resize() does not work for file-backed bitmaps. The buffer_heads are allocated and initialized when the bitmap is read from the file, but resize doesn't read from the file, it loads from the internal bitmap. When it comes time to write the new bitmap, the bh is non-existent and we crash. The common case when growing an array involves making the array larger, and that normally means making the bitmap larger. Doing that inside the kernel is possible, but would need more code. It is probably easier to require people who use file-backed bitmaps to remove them and re-add after a reshape. So this patch disables the resizing of arrays which have file-backed bitmaps. This is better than crashing. Reported-by: Zhilong Liu Fixes: d60b479d177a ("md/bitmap: add bitmap_resize function to allow bitmap resizing.") Signed-off-by: NeilBrown Signed-off-by: Shaohua Li Signed-off-by: Greg Kroah-Hartman --- drivers/md/bitmap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index cced062cab4b..cb5c9d8ea139 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -1802,6 +1802,11 @@ int bitmap_resize(struct bitmap *bitmap, sector_t blocks, long pages; struct bitmap_page *new_bp; + if (bitmap->storage.file && !init) { + pr_info("md: cannot resize file-based bitmap\n"); + return -EINVAL; + } + if (chunksize == 0) { /* If there is enough space, leave the chunk size unchanged, * else increase by factor of two until there is enough space. -- GitLab From 3070df32bfdf79c544f80dd1e1181f7cb4760830 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 17 Aug 2017 13:12:45 -0700 Subject: [PATCH 1025/5498] skd: Avoid that module unloading triggers a use-after-free commit 7277cc67b3916eed47558c64f9c9c0de00a35cda upstream. Since put_disk() triggers a disk_release() call and since that last function calls blk_put_queue() if disk->queue != NULL, clear the disk->queue pointer before calling put_disk(). This avoids that unloading the skd kernel module triggers the following use-after-free: WARNING: CPU: 8 PID: 297 at lib/refcount.c:128 refcount_sub_and_test+0x70/0x80 refcount_t: underflow; use-after-free. CPU: 8 PID: 297 Comm: kworker/8:1 Not tainted 4.11.10-300.fc26.x86_64 #1 Workqueue: events work_for_cpu_fn Call Trace: dump_stack+0x63/0x84 __warn+0xcb/0xf0 warn_slowpath_fmt+0x5a/0x80 refcount_sub_and_test+0x70/0x80 refcount_dec_and_test+0x11/0x20 kobject_put+0x1f/0x50 blk_put_queue+0x15/0x20 disk_release+0xae/0xf0 device_release+0x32/0x90 kobject_release+0x67/0x170 kobject_put+0x2b/0x50 put_disk+0x17/0x20 skd_destruct+0x5c/0x890 [skd] skd_pci_probe+0x124d/0x13a0 [skd] local_pci_probe+0x42/0xa0 work_for_cpu_fn+0x14/0x20 process_one_work+0x19e/0x470 worker_thread+0x1dc/0x4a0 kthread+0x125/0x140 ret_from_fork+0x25/0x30 Signed-off-by: Bart Van Assche Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Johannes Thumshirn Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/skd_main.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c index 1e46eb2305c0..99adff0c6f62 100644 --- a/drivers/block/skd_main.c +++ b/drivers/block/skd_main.c @@ -4679,15 +4679,16 @@ static void skd_free_disk(struct skd_device *skdev) { struct gendisk *disk = skdev->disk; - if (disk != NULL) { - struct request_queue *q = disk->queue; + if (disk && (disk->flags & GENHD_FL_UP)) + del_gendisk(disk); - if (disk->flags & GENHD_FL_UP) - del_gendisk(disk); - if (q) - blk_cleanup_queue(q); - put_disk(disk); + if (skdev->queue) { + blk_cleanup_queue(skdev->queue); + skdev->queue = NULL; + disk->queue = NULL; } + + put_disk(disk); skdev->disk = NULL; } -- GitLab From 2e41a42cf3d1fee9bfc636c5ca991e1112f7f695 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 17 Aug 2017 13:12:46 -0700 Subject: [PATCH 1026/5498] skd: Submit requests to firmware before triggering the doorbell commit 5fbd545cd3fd311ea1d6e8be4cedddd0ee5684c7 upstream. Ensure that the members of struct skd_msg_buf have been transferred to the PCIe adapter before the doorbell is triggered. This patch avoids that I/O fails sporadically and that the following error message is reported: (skd0:STM000196603:[0000:00:09.0]): Completion mismatch comp_id=0x0000 skreq=0x0400 new=0x0000 Signed-off-by: Bart Van Assche Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Johannes Thumshirn Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/skd_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c index 99adff0c6f62..f928e698f659 100644 --- a/drivers/block/skd_main.c +++ b/drivers/block/skd_main.c @@ -2214,6 +2214,9 @@ static void skd_send_fitmsg(struct skd_device *skdev, */ qcmd |= FIT_QCMD_MSGSIZE_64; + /* Make sure skd_msg_buf is written before the doorbell is triggered. */ + smp_wmb(); + SKD_WRITEQ(skdev, qcmd, FIT_Q_COMMAND); } @@ -2260,6 +2263,9 @@ static void skd_send_special_fitmsg(struct skd_device *skdev, qcmd = skspcl->mb_dma_address; qcmd |= FIT_QCMD_QID_NORMAL + FIT_QCMD_MSGSIZE_128; + /* Make sure skd_msg_buf is written before the doorbell is triggered. */ + smp_wmb(); + SKD_WRITEQ(skdev, qcmd, FIT_Q_COMMAND); } -- GitLab From ad385a77e093c6834ce1129ba80dba610320c20b Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Fri, 28 Jul 2017 12:30:51 +0200 Subject: [PATCH 1027/5498] scsi: zfcp: fix queuecommand for scsi_eh commands when DIX enabled commit 71b8e45da51a7b64a23378221c0a5868bd79da4f upstream. Since commit db007fc5e20c ("[SCSI] Command protection operation"), scsi_eh_prep_cmnd() saves scmd->prot_op and temporarily resets it to SCSI_PROT_NORMAL. Other FCP LLDDs such as qla2xxx and lpfc shield their queuecommand() to only access any of scsi_prot_sg...() if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL). Do the same thing for zfcp, which introduced DIX support with commit ef3eb71d8ba4 ("[SCSI] zfcp: Introduce experimental support for DIF/DIX"). Otherwise, TUR SCSI commands as part of scsi_eh likely fail in zfcp, because the regular SCSI command with DIX protection data, that scsi_eh re-uses in scsi_send_eh_cmnd(), of course still has (scsi_prot_sg_count() != 0) and so zfcp sends down bogus requests to the FCP channel hardware. This causes scsi_eh_test_devices() to have (finish_cmds == 0) [not SCSI device is online or not scsi_eh_tur() failed] so regular SCSI commands, that caused / were affected by scsi_eh, are moved to work_q and scsi_eh_test_devices() itself returns false. In turn, it unnecessarily escalates in our case in scsi_eh_ready_devs() beyond host reset to finally scsi_eh_offline_sdevs() which sets affected SCSI devices offline with the following kernel message: "kernel: sd H:0:T:L: Device offlined - not ready after error recovery" Signed-off-by: Steffen Maier Fixes: ef3eb71d8ba4 ("[SCSI] zfcp: Introduce experimental support for DIF/DIX") Reviewed-by: Benjamin Block Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/s390/scsi/zfcp_fsf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 0fe8d5d95119..5dc56db8a893 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -2247,7 +2247,8 @@ int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *scsi_cmnd) fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd, 0); - if (scsi_prot_sg_count(scsi_cmnd)) { + if ((scsi_get_prot_op(scsi_cmnd) != SCSI_PROT_NORMAL) && + scsi_prot_sg_count(scsi_cmnd)) { zfcp_qdio_set_data_div(qdio, &req->qdio_req, scsi_prot_sg_count(scsi_cmnd)); retval = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, -- GitLab From 9799a4d5fc26ede777fd359c2129608af63540e7 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 28 Jul 2017 12:30:52 +0200 Subject: [PATCH 1028/5498] scsi: zfcp: add handling for FCP_RESID_OVER to the fcp ingress path commit a099b7b1fc1f0418ab8d79ecf98153e1e134656e upstream. Up until now zfcp would just ignore the FCP_RESID_OVER flag in the FCP response IU. When this flag is set, it is possible, in regards to the FCP standard, that the storage-server processes the command normally, up to the point where data is missing and simply ignores those. In this case no CHECK CONDITION would be set, and because we ignored the FCP_RESID_OVER flag we resulted in at least a data loss or even -corruption as a follow-up error, depending on how the applications/layers on top behave. To prevent this, we now set the host-byte of the corresponding scsi_cmnd to DID_ERROR. Other storage-behaviors, where the same condition results in a CHECK CONDITION set in the answer, don't need to be changed as they are handled in the mid-layer already. Following is an example trace record decoded with zfcpdbf from the s390-tools package. We forcefully injected a fc_dl which is one byte too small: Timestamp : ... Area : SCSI Subarea : 00 Level : 3 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : rsl_err Request ID : 0x... SCSI ID : 0x... SCSI LUN : 0x... SCSI result : 0x00070000 ^^DID_ERROR SCSI retries : 0x.. SCSI allowed : 0x.. SCSI scribble : 0x... SCSI opcode : 2a000000 00000000 08000000 00000000 FCP rsp inf cod: 0x00 FCP rsp IU : 00000000 00000000 00000400 00000001 ^^fr_flags==FCP_RESID_OVER ^^fr_status==SAM_STAT_GOOD ^^^^^^^^fr_resid 00000000 00000000 As of now, we don't actively handle to possibility that a response IU has both flags - FCP_RESID_OVER and FCP_RESID_UNDER - set at once. Reported-by: Luke M. Hopkins Reviewed-by: Steffen Maier Fixes: 553448f6c483 ("[SCSI] zfcp: Message cleanup") Fixes: ea127f975424 ("[PATCH] s390 (7/7): zfcp host adapter.") (tglx/history.git) Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/s390/scsi/zfcp_fc.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h index b1d2024ed513..c2e40e10b293 100644 --- a/drivers/s390/scsi/zfcp_fc.h +++ b/drivers/s390/scsi/zfcp_fc.h @@ -4,7 +4,7 @@ * Fibre Channel related definitions and inline functions for the zfcp * device driver * - * Copyright IBM Corp. 2009 + * Copyright IBM Corp. 2009, 2017 */ #ifndef ZFCP_FC_H @@ -291,6 +291,10 @@ void zfcp_fc_eval_fcp_rsp(struct fcp_resp_with_ext *fcp_rsp, !(rsp_flags & FCP_SNS_LEN_VAL) && fcp_rsp->resp.fr_status == SAM_STAT_GOOD) set_host_byte(scsi, DID_ERROR); + } else if (unlikely(rsp_flags & FCP_RESID_OVER)) { + /* FCP_DL was not sufficient for SCSI data length */ + if (fcp_rsp->resp.fr_status == SAM_STAT_GOOD) + set_host_byte(scsi, DID_ERROR); } } -- GitLab From 277958c8e3c0cbbab093073266044260dab52161 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Fri, 28 Jul 2017 12:30:55 +0200 Subject: [PATCH 1029/5498] scsi: zfcp: fix missing trace records for early returns in TMF eh handlers commit 1a5d999ebfc7bfe28deb48931bb57faa8e4102b6 upstream. For problem determination we need to see that we were in scsi_eh as well as whether and why we were successful or not. The following commits introduced new early returns without adding a trace record: v2.6.35 commit a1dbfddd02d2 ("[SCSI] zfcp: Pass return code from fc_block_scsi_eh to scsi eh") on fc_block_scsi_eh() returning != 0 which is FAST_IO_FAIL, v2.6.30 commit 63caf367e1c9 ("[SCSI] zfcp: Improve reliability of SCSI eh handlers in zfcp") on not having gotten an FSF request after the maximum number of retry attempts and thus could not issue a TMF and has to return FAILED. Signed-off-by: Steffen Maier Fixes: a1dbfddd02d2 ("[SCSI] zfcp: Pass return code from fc_block_scsi_eh to scsi eh") Fixes: 63caf367e1c9 ("[SCSI] zfcp: Improve reliability of SCSI eh handlers in zfcp") Reviewed-by: Benjamin Block Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/s390/scsi/zfcp_scsi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 7b353647cb90..92f6e9e466b5 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -245,8 +245,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) zfcp_erp_wait(adapter); ret = fc_block_scsi_eh(scpnt); - if (ret) + if (ret) { + zfcp_dbf_scsi_devreset("fiof", scpnt, tm_flags); return ret; + } if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { @@ -254,8 +256,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) return SUCCESS; } } - if (!fsf_req) + if (!fsf_req) { + zfcp_dbf_scsi_devreset("reqf", scpnt, tm_flags); return FAILED; + } wait_for_completion(&fsf_req->completion); -- GitLab From 2a30f9d547687bbf1e06709f03ab07230bc628ef Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Fri, 28 Jul 2017 12:30:56 +0200 Subject: [PATCH 1030/5498] scsi: zfcp: fix payload with full FCP_RSP IU in SCSI trace records commit 12c3e5754c8022a4f2fd1e9f00d19e99ee0d3cc1 upstream. If the FCP_RSP UI has optional parts (FCP_SNS_INFO or FCP_RSP_INFO) and thus does not fit into the fsp_rsp field built into a SCSI trace record, trace the full FCP_RSP UI with all optional parts as payload record instead of just FCP_SNS_INFO as payload and a 1 byte RSP_INFO_CODE part of FCP_RSP_INFO built into the SCSI record. That way we would also get the full FCP_SNS_INFO in case a target would ever send more than min(SCSI_SENSE_BUFFERSIZE==96, ZFCP_DBF_PAY_MAX_REC==256)==96. The mandatory part of FCP_RSP IU is only 24 bytes. PAYload costs at least one full PAY record of 256 bytes anyway. We cap to the hardware response size which is only FSF_FCP_RSP_SIZE==128. So we can just put the whole FCP_RSP IU with any optional parts into PAYload similarly as we do for SAN PAY since v4.9 commit aceeffbb59bb ("zfcp: trace full payload of all SAN records (req,resp,iels)"). This does not cause any additional trace records wasting memory. Decoded trace records were confusing because they showed a hard-coded sense data length of 96 even if the FCP_RSP_IU field FCP_SNS_LEN showed actually less. Since the same commit, we set pl_len for SAN traces to the full length of a request/response even if we cap the corresponding trace. In contrast, here for SCSI traces we set pl_len to the pre-computed length of FCP_RSP IU considering SNS_LEN or RSP_LEN if valid. Nonetheless we trace a hardcoded payload of length FSF_FCP_RSP_SIZE==128 if there were optional parts. This makes it easier for the zfcpdbf tool to format only the relevant part of the long FCP_RSP UI buffer. And any trailing information is still available in the payload trace record just in case. Rename the payload record tag from "fcp_sns" to "fcp_riu" to make the new content explicit to zfcpdbf which can then pick a suitable field name such as "FCP rsp IU all:" instead of "Sense info :" Also, the same zfcpdbf can still be backwards compatible with "fcp_sns". Old example trace record before this fix, formatted with the tool zfcpdbf from s390-tools: Timestamp : ... Area : SCSI Subarea : 00 Level : 3 Exception : - CPU id : .. Caller : 0x... Record id : 1 Tag : rsl_err Request id : 0x SCSI ID : 0x... SCSI LUN : 0x... SCSI result : 0x00000002 SCSI retries : 0x00 SCSI allowed : 0x05 SCSI scribble : 0x SCSI opcode : 00000000 00000000 00000000 00000000 FCP rsp inf cod: 0x00 FCP rsp IU : 00000000 00000000 00000202 00000000 ^^==FCP_SNS_LEN_VALID 00000020 00000000 ^^^^^^^^==FCP_SNS_LEN==32 Sense len : 96 <==min(SCSI_SENSE_BUFFERSIZE,ZFCP_DBF_PAY_MAX_REC) Sense info : 70000600 00000018 00000000 29000000 00000400 00000000 00000000 00000000 00000000 00000000 00000000 00000000<==superfluous 00000000 00000000 00000000 00000000<==superfluous 00000000 00000000 00000000 00000000<==superfluous 00000000 00000000 00000000 00000000<==superfluous New example trace records with this fix: Timestamp : ... Area : SCSI Subarea : 00 Level : 3 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : rsl_err Request ID : 0x SCSI ID : 0x... SCSI LUN : 0x... SCSI result : 0x00000002 SCSI retries : 0x00 SCSI allowed : 0x03 SCSI scribble : 0x SCSI opcode : a30c0112 00000000 02000000 00000000 FCP rsp inf cod: 0x00 FCP rsp IU : 00000000 00000000 00000a02 00000200 00000020 00000000 FCP rsp IU len : 56 FCP rsp IU all : 00000000 00000000 00000a02 00000200 ^^=FCP_RESID_UNDER|FCP_SNS_LEN_VALID 00000020 00000000 70000500 00000018 ^^^^^^^^==FCP_SNS_LEN ^^^^^^^^^^^^^^^^^ 00000000 240000cb 00011100 00000000 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 00000000 00000000 ^^^^^^^^^^^^^^^^^==FCP_SNS_INFO Timestamp : ... Area : SCSI Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : lr_okay Request ID : 0x SCSI ID : 0x... SCSI LUN : 0x... SCSI result : 0x00000000 SCSI retries : 0x00 SCSI allowed : 0x05 SCSI scribble : 0x SCSI opcode : FCP rsp inf cod: 0x00 FCP rsp IU : 00000000 00000000 00000100 00000000 00000000 00000008 FCP rsp IU len : 32 FCP rsp IU all : 00000000 00000000 00000100 00000000 ^^==FCP_RSP_LEN_VALID 00000000 00000008 00000000 00000000 ^^^^^^^^==FCP_RSP_LEN ^^^^^^^^^^^^^^^^^==FCP_RSP_INFO Signed-off-by: Steffen Maier Fixes: 250a1352b95e ("[SCSI] zfcp: Redesign of the debug tracing for SCSI records.") Reviewed-by: Benjamin Block Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/s390/scsi/zfcp_dbf.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 5d7fbe4e907e..0426cba79a27 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -427,19 +427,32 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) if (fsf) { rec->fsf_req_id = fsf->req_id; + rec->pl_len = FCP_RESP_WITH_EXT; fcp_rsp = (struct fcp_resp_with_ext *) &(fsf->qtcb->bottom.io.fcp_rsp); + /* mandatory parts of FCP_RSP IU in this SCSI record */ memcpy(&rec->fcp_rsp, fcp_rsp, FCP_RESP_WITH_EXT); if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) { fcp_rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; rec->fcp_rsp_info = fcp_rsp_info->rsp_code; + rec->pl_len += be32_to_cpu(fcp_rsp->ext.fr_rsp_len); } if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) { - rec->pl_len = min((u16)SCSI_SENSE_BUFFERSIZE, - (u16)ZFCP_DBF_PAY_MAX_REC); - zfcp_dbf_pl_write(dbf, sc->sense_buffer, rec->pl_len, - "fcp_sns", fsf->req_id); + rec->pl_len += be32_to_cpu(fcp_rsp->ext.fr_sns_len); } + /* complete FCP_RSP IU in associated PAYload record + * but only if there are optional parts + */ + if (fcp_rsp->resp.fr_flags != 0) + zfcp_dbf_pl_write( + dbf, fcp_rsp, + /* at least one full PAY record + * but not beyond hardware response field + */ + min_t(u16, max_t(u16, rec->pl_len, + ZFCP_DBF_PAY_MAX_REC), + FSF_FCP_RSP_SIZE), + "fcp_riu", fsf->req_id); } debug_event(dbf->scsi, 1, rec, sizeof(*rec)); -- GitLab From bb5d52954f27b6c6ebc02bbc88e2a55d60d1914c Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Fri, 28 Jul 2017 12:30:57 +0200 Subject: [PATCH 1031/5498] scsi: zfcp: trace HBA FSF response by default on dismiss or timedout late response commit fdb7cee3b9e3c561502e58137a837341f10cbf8b upstream. At the default trace level, we only trace unsuccessful events including FSF responses. zfcp_dbf_hba_fsf_response() only used protocol status and FSF status to decide on an unsuccessful response. However, this is only one of multiple possible sources determining a failed struct zfcp_fsf_req. An FSF request can also "fail" if its response runs into an ERP timeout or if it gets dismissed because a higher level recovery was triggered [trace tags "erscf_1" or "erscf_2" in zfcp_erp_strategy_check_fsfreq()]. FSF requests with ERP timeout are: FSF_QTCB_EXCHANGE_CONFIG_DATA, FSF_QTCB_EXCHANGE_PORT_DATA, FSF_QTCB_OPEN_PORT_WITH_DID or FSF_QTCB_CLOSE_PORT or FSF_QTCB_CLOSE_PHYSICAL_PORT for target ports, FSF_QTCB_OPEN_LUN, FSF_QTCB_CLOSE_LUN. One example is slow queue processing which can cause follow-on errors, e.g. FSF_PORT_ALREADY_OPEN after FSF_QTCB_OPEN_PORT_WITH_DID timed out. In order to see the root cause, we need to see late responses even if the channel presented them successfully with FSF_PROT_GOOD and FSF_GOOD. Example trace records formatted with zfcpdbf from the s390-tools package: Timestamp : ... Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : ... Record ID : 1 Tag : fcegpf1 LUN : 0xffffffffffffffff WWPN : 0x D_ID : 0x00 Adapter status : 0x5400050b Port status : 0x41200000 LUN status : 0x00000000 Ready count : 0x00000001 Running count : 0x... ERP want : 0x02 ZFCP_ERP_ACTION_REOPEN_PORT ERP need : 0x02 ZFCP_ERP_ACTION_REOPEN_PORT | Timestamp : ... 30 seconds later Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : ... Record ID : 2 Tag : erscf_2 LUN : 0xffffffffffffffff WWPN : 0x D_ID : 0x00 Adapter status : 0x5400050b Port status : 0x41200000 LUN status : 0x00000000 Request ID : 0x ERP status : 0x10000000 ZFCP_STATUS_ERP_TIMEDOUT ERP step : 0x0800 ZFCP_ERP_STEP_PORT_OPENING ERP action : 0x02 ZFCP_ERP_ACTION_REOPEN_PORT ERP count : 0x00 | Timestamp : ... later than previous record Area : HBA Subarea : 00 Level : 5 > default level => 3 <= default level Exception : - CPU ID : 00 Caller : ... Record ID : 1 Tag : fs_qtcb => fs_rerr Request ID : 0x Request status : 0x00001010 ZFCP_STATUS_FSFREQ_DISMISSED | ZFCP_STATUS_FSFREQ_CLEANUP FSF cmnd : 0x00000005 FSF sequence no: 0x... FSF issued : ... > 30 seconds ago FSF stat : 0x00000000 FSF_GOOD FSF stat qual : 00000000 00000000 00000000 00000000 Prot stat : 0x00000001 FSF_PROT_GOOD Prot stat qual : 00000000 00000000 00000000 00000000 Port handle : 0x... LUN handle : 0x00000000 QTCB log length: ... QTCB log info : ... In case of problems detecting that new responses are waiting on the input queue, we sooner or later trigger adapter recovery due to an FSF request timeout (trace tag "fsrth_1"). FSF requests with FSF request timeout are: typically FSF_QTCB_ABORT_FCP_CMND; but theoretically also FSF_QTCB_EXCHANGE_CONFIG_DATA or FSF_QTCB_EXCHANGE_PORT_DATA via sysfs, FSF_QTCB_OPEN_PORT_WITH_DID or FSF_QTCB_CLOSE_PORT for WKA ports, FSF_QTCB_FCP_CMND for task management function (LUN / target reset). One or more pending requests can meanwhile have FSF_PROT_GOOD and FSF_GOOD because the channel filled in the response via DMA into the request's QTCB. In a theroretical case, inject code can create an erroneous FSF request on purpose. If data router is enabled, it uses deferred error reporting. A READ SCSI command can succeed with FSF_PROT_GOOD, FSF_GOOD, and SAM_STAT_GOOD. But on writing the read data to host memory via DMA, it can still fail, e.g. if an intentionally wrong scatter list does not provide enough space. Rather than getting an unsuccessful response, we get a QDIO activate check which in turn triggers adapter recovery. One or more pending requests can meanwhile have FSF_PROT_GOOD and FSF_GOOD because the channel filled in the response via DMA into the request's QTCB. Example trace records formatted with zfcpdbf from the s390-tools package: Timestamp : ... Area : HBA Subarea : 00 Level : 6 > default level => 3 <= default level Exception : - CPU ID : .. Caller : ... Record ID : 1 Tag : fs_norm => fs_rerr Request ID : 0x Request status : 0x00001010 ZFCP_STATUS_FSFREQ_DISMISSED | ZFCP_STATUS_FSFREQ_CLEANUP FSF cmnd : 0x00000001 FSF sequence no: 0x... FSF issued : ... FSF stat : 0x00000000 FSF_GOOD FSF stat qual : 00000000 00000000 00000000 00000000 Prot stat : 0x00000001 FSF_PROT_GOOD Prot stat qual : ........ ........ 00000000 00000000 Port handle : 0x... LUN handle : 0x... | Timestamp : ... Area : SCSI Subarea : 00 Level : 3 Exception : - CPU ID : .. Caller : ... Record ID : 1 Tag : rsl_err Request ID : 0x SCSI ID : 0x... SCSI LUN : 0x... SCSI result : 0x000e0000 DID_TRANSPORT_DISRUPTED SCSI retries : 0x00 SCSI allowed : 0x05 SCSI scribble : 0x SCSI opcode : 28... Read(10) FCP rsp inf cod: 0x00 FCP rsp IU : 00000000 00000000 00000000 00000000 ^^ SAM_STAT_GOOD 00000000 00000000 Only with luck in both above cases, we could see a follow-on trace record of an unsuccesful event following a successful but late FSF response with FSF_PROT_GOOD and FSF_GOOD. Typically this was the case for I/O requests resulting in a SCSI trace record "rsl_err" with DID_TRANSPORT_DISRUPTED [On ZFCP_STATUS_FSFREQ_DISMISSED, zfcp_fsf_protstatus_eval() sets ZFCP_STATUS_FSFREQ_ERROR seen by the request handler functions as failure]. However, the reason for this follow-on trace was invisible because the corresponding HBA trace record was missing at the default trace level (by default hidden records with tags "fs_norm", "fs_qtcb", or "fs_open"). On adapter recovery, after we had shut down the QDIO queues, we perform unsuccessful pseudo completions with flag ZFCP_STATUS_FSFREQ_DISMISSED for each pending FSF request in zfcp_fsf_req_dismiss_all(). In order to find the root cause, we need to see all pseudo responses even if the channel presented them successfully with FSF_PROT_GOOD and FSF_GOOD. Therefore, check zfcp_fsf_req.status for ZFCP_STATUS_FSFREQ_DISMISSED or ZFCP_STATUS_FSFREQ_ERROR and trace with a new tag "fs_rerr". It does not matter that there are numerous places which set ZFCP_STATUS_FSFREQ_ERROR after the location where we trace an FSF response early. These cases are based on protocol status != FSF_PROT_GOOD or == FSF_PROT_FSF_STATUS_PRESENTED and are thus already traced by default as trace tag "fs_perr" or "fs_ferr" respectively. NB: The trace record with tag "fssrh_1" for status read buffers on dismiss all remains. zfcp_fsf_req_complete() handles this and returns early. All other FSF request types are handled separately and as described above. Signed-off-by: Steffen Maier Fixes: 8a36e4532ea1 ("[SCSI] zfcp: enhancement of zfcp debug features") Fixes: 2e261af84cdb ("[SCSI] zfcp: Only collect FSF/HBA debug data for matching trace levels") Reviewed-by: Benjamin Block Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/s390/scsi/zfcp_dbf.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 0be3d48681ae..9b8eb8eb8b4c 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -291,7 +291,11 @@ void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req) { struct fsf_qtcb *qtcb = req->qtcb; - if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) && + if (unlikely(req->status & (ZFCP_STATUS_FSFREQ_DISMISSED | + ZFCP_STATUS_FSFREQ_ERROR))) { + zfcp_dbf_hba_fsf_resp("fs_rerr", 3, req); + + } else if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) && (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) { zfcp_dbf_hba_fsf_resp("fs_perr", 1, req); -- GitLab From 9f582840f9e58a34f9f293a8d6da26fedf08f1d8 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Fri, 28 Jul 2017 12:30:58 +0200 Subject: [PATCH 1032/5498] scsi: zfcp: trace high part of "new" 64 bit SCSI LUN commit 5d4a3d0a2ff23799b956e5962b886287614e7fad upstream. Complements debugging aspects of the otherwise functionally complete v3.17 commit 9cb78c16f5da ("scsi: use 64-bit LUNs"). While I don't have access to a target exporting 3 or 4 level LUNs, I did test it by explicitly attaching a non-existent fake 4 level LUN by means of zfcp sysfs attribute "unit_add". In order to see corresponding trace records of otherwise successful events, we had to increase the trace level of area SCSI and HBA to 6. $ echo 6 > /sys/kernel/debug/s390dbf/zfcp_0.0.1880_scsi/level $ echo 6 > /sys/kernel/debug/s390dbf/zfcp_0.0.1880_hba/level $ echo 0x4011402240334044 > \ /sys/bus/ccw/drivers/zfcp/0.0.1880/0x50050763031bd327/unit_add Example output formatted by an updated zfcpdbf from the s390-tools package interspersed with kernel messages at scsi_logging_level=4605: Timestamp : ... Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : scsla_1 LUN : 0x4011402240334044 WWPN : 0x50050763031bd327 D_ID : 0x00...... Adapter status : 0x5400050b Port status : 0x54000001 LUN status : 0x41000000 Ready count : 0x00000001 Running count : 0x00000000 ERP want : 0x01 ERP need : 0x01 scsi 2:0:0:4630896905707208721: scsi scan: INQUIRY pass 1 length 36 scsi 2:0:0:4630896905707208721: scsi scan: INQUIRY successful with code 0x0 Timestamp : ... Area : HBA Subarea : 00 Level : 6 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : fs_norm Request ID : 0x Request status : 0x00000010 FSF cmnd : 0x00000001 FSF sequence no: 0x... FSF issued : ... FSF stat : 0x00000000 FSF stat qual : 00000000 00000000 00000000 00000000 Prot stat : 0x00000001 Prot stat qual : ........ ........ 00000000 00000000 Port handle : 0x... LUN handle : 0x... | Timestamp : ... Area : SCSI Subarea : 00 Level : 6 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : rsl_nor Request ID : 0x SCSI ID : 0x00000000 SCSI LUN : 0x40224011 SCSI LUN high : 0x40444033 <======================= SCSI result : 0x00000000 SCSI retries : 0x00 SCSI allowed : 0x03 SCSI scribble : 0x SCSI opcode : 12000000 a4000000 00000000 00000000 FCP rsp inf cod: 0x00 FCP rsp IU : 00000000 00000000 00000000 00000000 00000000 00000000 scsi 2:0:0:4630896905707208721: scsi scan: INQUIRY pass 2 length 164 scsi 2:0:0:4630896905707208721: scsi scan: INQUIRY successful with code 0x0 scsi 2:0:0:4630896905707208721: scsi scan: peripheral device type of 31, \ no device added Signed-off-by: Steffen Maier Fixes: 9cb78c16f5da ("scsi: use 64-bit LUNs") Reviewed-by: Benjamin Block Reviewed-by: Jens Remus Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/s390/scsi/zfcp_dbf.c | 2 +- drivers/s390/scsi/zfcp_dbf.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 0426cba79a27..296889dc193f 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -418,8 +418,8 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) rec->scsi_retries = sc->retries; rec->scsi_allowed = sc->allowed; rec->scsi_id = sc->device->id; - /* struct zfcp_dbf_scsi needs to be updated to handle 64bit LUNs */ rec->scsi_lun = (u32)sc->device->lun; + rec->scsi_lun_64_hi = (u32)(sc->device->lun >> 32); rec->host_scribble = (unsigned long)sc->host_scribble; memcpy(rec->scsi_opcode, sc->cmnd, diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 9b8eb8eb8b4c..2039e7510a30 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -196,7 +196,7 @@ enum zfcp_dbf_scsi_id { * @id: unique number of recovery record type * @tag: identifier string specifying the location of initiation * @scsi_id: scsi device id - * @scsi_lun: scsi device logical unit number + * @scsi_lun: scsi device logical unit number, low part of 64 bit, old 32 bit * @scsi_result: scsi result * @scsi_retries: current retry number of scsi request * @scsi_allowed: allowed retries @@ -206,6 +206,7 @@ enum zfcp_dbf_scsi_id { * @host_scribble: LLD specific data attached to SCSI request * @pl_len: length of paload stored as zfcp_dbf_pay * @fsf_rsp: response for fsf request + * @scsi_lun_64_hi: scsi device logical unit number, high part of 64 bit */ struct zfcp_dbf_scsi { u8 id; @@ -222,6 +223,7 @@ struct zfcp_dbf_scsi { u64 host_scribble; u16 pl_len; struct fcp_resp_with_ext fcp_rsp; + u32 scsi_lun_64_hi; } __packed; /** -- GitLab From 53a5b214a406fe05bc3cfa2e4efc84e270731f18 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 7 Apr 2017 09:34:13 +0200 Subject: [PATCH 1033/5498] scsi: sg: remove 'save_scat_len' commit 136e57bf43dc4babbfb8783abbf707d483cacbe3 upstream. Unused. Signed-off-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Tested-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index efd7508d7f00..db8e278efd30 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -157,7 +157,6 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ Sg_scatter_hold reserve; /* buffer held for this file descriptor */ - unsigned save_scat_len; /* original length of trunc. scat. element */ Sg_request *headrp; /* head of request slist, NULL->empty */ struct fasync_struct *async_qp; /* used by asynchronous notification */ Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ @@ -2105,7 +2104,6 @@ sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) req_schp->pages = NULL; req_schp->page_order = 0; req_schp->sglist_len = 0; - sfp->save_scat_len = 0; srp->res_used = 0; /* Called without mutex lock to avoid deadlock */ sfp->res_in_use = 0; -- GitLab From a19e985cac6dbdf0571ef3b369631661d5512b89 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 7 Apr 2017 09:34:16 +0200 Subject: [PATCH 1034/5498] scsi: sg: use standard lists for sg_requests commit 109bade9c625c89bb5ea753aaa1a0a97e6fbb548 upstream. 'Sg_request' is using a private list implementation; convert it to standard lists. Signed-off-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Tested-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 147 +++++++++++++++++++--------------------------- 1 file changed, 61 insertions(+), 86 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index db8e278efd30..06dd4c638010 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -133,7 +133,7 @@ struct sg_device; /* forward declarations */ struct sg_fd; typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ - struct sg_request *nextrp; /* NULL -> tail request (slist) */ + struct list_head entry; /* list entry */ struct sg_fd *parentfp; /* NULL -> not in use */ Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ sg_io_hdr_t header; /* scsi command+info, see */ @@ -157,7 +157,7 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ Sg_scatter_hold reserve; /* buffer held for this file descriptor */ - Sg_request *headrp; /* head of request slist, NULL->empty */ + struct list_head rq_list; /* head of request list */ struct fasync_struct *async_qp; /* used by asynchronous notification */ Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ char low_dma; /* as in parent but possibly overridden to 1 */ @@ -965,7 +965,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (!access_ok(VERIFY_WRITE, ip, sizeof (int))) return -EFAULT; read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp; srp; srp = srp->nextrp) { + list_for_each_entry(srp, &sfp->rq_list, entry) { if ((1 == srp->done) && (!srp->sg_io_owned)) { read_unlock_irqrestore(&sfp->rq_list_lock, iflags); @@ -978,7 +978,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return 0; case SG_GET_NUM_WAITING: read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) { + val = 0; + list_for_each_entry(srp, &sfp->rq_list, entry) { if ((1 == srp->done) && (!srp->sg_io_owned)) ++val; } @@ -1053,35 +1054,33 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (!rinfo) return -ENOMEM; read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE; - ++val, srp = srp ? srp->nextrp : srp) { + val = 0; + list_for_each_entry(srp, &sfp->rq_list, entry) { + if (val > SG_MAX_QUEUE) + break; memset(&rinfo[val], 0, SZ_SG_REQ_INFO); - if (srp) { - rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; - if (srp->done) - rinfo[val].duration = - srp->header.duration; - else { - ms = jiffies_to_msecs(jiffies); - rinfo[val].duration = - (ms > srp->header.duration) ? - (ms - srp->header.duration) : 0; - } - rinfo[val].orphan = srp->orphan; - rinfo[val].sg_io_owned = - srp->sg_io_owned; - rinfo[val].pack_id = - srp->header.pack_id; - rinfo[val].usr_ptr = - srp->header.usr_ptr; + rinfo[val].req_state = srp->done + 1; + rinfo[val].problem = + srp->header.masked_status & + srp->header.host_status & + srp->header.driver_status; + if (srp->done) + rinfo[val].duration = + srp->header.duration; + else { + ms = jiffies_to_msecs(jiffies); + rinfo[val].duration = + (ms > srp->header.duration) ? + (ms - srp->header.duration) : 0; } + rinfo[val].orphan = srp->orphan; + rinfo[val].sg_io_owned = srp->sg_io_owned; + rinfo[val].pack_id = srp->header.pack_id; + rinfo[val].usr_ptr = srp->header.usr_ptr; + val++; } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - result = __copy_to_user(p, rinfo, + result = __copy_to_user(p, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE); result = result ? -EFAULT : 0; kfree(rinfo); @@ -1213,7 +1212,7 @@ sg_poll(struct file *filp, poll_table * wait) return POLLERR; poll_wait(filp, &sfp->read_wait, wait); read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp; srp; srp = srp->nextrp) { + list_for_each_entry(srp, &sfp->rq_list, entry) { /* if any read waiting, flag it */ if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned)) res = POLLIN | POLLRDNORM; @@ -2116,7 +2115,7 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id) unsigned long iflags; write_lock_irqsave(&sfp->rq_list_lock, iflags); - for (resp = sfp->headrp; resp; resp = resp->nextrp) { + list_for_each_entry(resp, &sfp->rq_list, entry) { /* look for requests that are ready + not SG_IO owned */ if ((1 == resp->done) && (!resp->sg_io_owned) && ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { @@ -2134,70 +2133,45 @@ sg_add_request(Sg_fd * sfp) { int k; unsigned long iflags; - Sg_request *resp; Sg_request *rp = sfp->req_arr; write_lock_irqsave(&sfp->rq_list_lock, iflags); - resp = sfp->headrp; - if (!resp) { - memset(rp, 0, sizeof (Sg_request)); - rp->parentfp = sfp; - resp = rp; - sfp->headrp = resp; - } else { - if (0 == sfp->cmd_q) - resp = NULL; /* command queuing disallowed */ - else { - for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { - if (!rp->parentfp) - break; - } - if (k < SG_MAX_QUEUE) { - memset(rp, 0, sizeof (Sg_request)); - rp->parentfp = sfp; - while (resp->nextrp) - resp = resp->nextrp; - resp->nextrp = rp; - resp = rp; - } else - resp = NULL; + if (!list_empty(&sfp->rq_list)) { + if (!sfp->cmd_q) + goto out_unlock; + + for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { + if (!rp->parentfp) + break; } + if (k >= SG_MAX_QUEUE) + goto out_unlock; } - if (resp) { - resp->nextrp = NULL; - resp->header.duration = jiffies_to_msecs(jiffies); - } + memset(rp, 0, sizeof (Sg_request)); + rp->parentfp = sfp; + rp->header.duration = jiffies_to_msecs(jiffies); + list_add_tail(&rp->entry, &sfp->rq_list); write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + return rp; +out_unlock: + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return NULL; } /* Return of 1 for found; 0 for not found */ static int sg_remove_request(Sg_fd * sfp, Sg_request * srp) { - Sg_request *prev_rp; - Sg_request *rp; unsigned long iflags; int res = 0; - if ((!sfp) || (!srp) || (!sfp->headrp)) + if (!sfp || !srp || list_empty(&sfp->rq_list)) return res; write_lock_irqsave(&sfp->rq_list_lock, iflags); - prev_rp = sfp->headrp; - if (srp == prev_rp) { - sfp->headrp = prev_rp->nextrp; - prev_rp->parentfp = NULL; + if (!list_empty(&srp->entry)) { + list_del(&srp->entry); + srp->parentfp = NULL; res = 1; - } else { - while ((rp = prev_rp->nextrp)) { - if (srp == rp) { - prev_rp->nextrp = rp->nextrp; - rp->parentfp = NULL; - res = 1; - break; - } - prev_rp = rp; - } } write_unlock_irqrestore(&sfp->rq_list_lock, iflags); return res; @@ -2216,7 +2190,7 @@ sg_add_sfp(Sg_device * sdp) init_waitqueue_head(&sfp->read_wait); rwlock_init(&sfp->rq_list_lock); - + INIT_LIST_HEAD(&sfp->rq_list); kref_init(&sfp->f_ref); mutex_init(&sfp->f_mutex); sfp->timeout = SG_DEFAULT_TIMEOUT; @@ -2257,10 +2231,13 @@ sg_remove_sfp_usercontext(struct work_struct *work) { struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work); struct sg_device *sdp = sfp->parentdp; + Sg_request *srp; /* Cleanup any responses which were never read(). */ - while (sfp->headrp) - sg_finish_rem_req(sfp->headrp); + while (!list_empty(&sfp->rq_list)) { + srp = list_first_entry(&sfp->rq_list, Sg_request, entry); + sg_finish_rem_req(srp); + } if (sfp->reserve.bufflen > 0) { SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp, @@ -2663,7 +2640,7 @@ static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v) /* must be called while holding sg_index_lock */ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) { - int k, m, new_interface, blen, usg; + int k, new_interface, blen, usg; Sg_request *srp; Sg_fd *fp; const sg_io_hdr_t *hp; @@ -2683,13 +2660,11 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", (int) fp->cmd_q, (int) fp->force_packid, (int) fp->keep_orphan); - for (m = 0, srp = fp->headrp; - srp != NULL; - ++m, srp = srp->nextrp) { + list_for_each_entry(srp, &fp->rq_list, entry) { hp = &srp->header; new_interface = (hp->interface_id == '\0') ? 0 : 1; if (srp->res_used) { - if (new_interface && + if (new_interface && (SG_FLAG_MMAP_IO & hp->flags)) cp = " mmap>> "; else @@ -2720,7 +2695,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) seq_printf(s, "ms sgat=%d op=0x%02x\n", usg, (int) srp->data.cmd_opcode); } - if (0 == m) + if (list_empty(&fp->rq_list)) seq_puts(s, " No requests active\n"); read_unlock(&fp->rq_list_lock); } -- GitLab From 9f8cb7be5242f23b01647704da2d274d2b3b8578 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 17 Aug 2017 10:09:54 +0300 Subject: [PATCH 1035/5498] scsi: sg: off by one in sg_ioctl() commit bd46fc406b30d1db1aff8dabaff8d18bb423fdcf upstream. If "val" is SG_MAX_QUEUE then we are one element beyond the end of the "rinfo" array so the > should be >=. Fixes: 109bade9c625 ("scsi: sg: use standard lists for sg_requests") Signed-off-by: Dan Carpenter Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 06dd4c638010..10211e7eb964 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1056,7 +1056,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) read_lock_irqsave(&sfp->rq_list_lock, iflags); val = 0; list_for_each_entry(srp, &sfp->rq_list, entry) { - if (val > SG_MAX_QUEUE) + if (val >= SG_MAX_QUEUE) break; memset(&rinfo[val], 0, SZ_SG_REQ_INFO); rinfo[val].req_state = srp->done + 1; -- GitLab From 691e12db75fb7f55bbbf8c1fea7d462eb1a5e38a Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 15 Sep 2017 14:05:15 +0200 Subject: [PATCH 1036/5498] scsi: sg: factor out sg_fill_request_table() commit 4759df905a474d245752c9dc94288e779b8734dd upstream. Factor out sg_fill_request_table() for better readability. [mkp: typos, applied by hand] Signed-off-by: Hannes Reinecke Reviewed-by: Bart Van Assche Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 61 +++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 10211e7eb964..ab765ca749d4 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -854,6 +854,40 @@ static int max_sectors_bytes(struct request_queue *q) return max_sectors << 9; } +static void +sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) +{ + Sg_request *srp; + int val; + unsigned int ms; + + val = 0; + list_for_each_entry(srp, &sfp->rq_list, entry) { + if (val > SG_MAX_QUEUE) + break; + memset(&rinfo[val], 0, SZ_SG_REQ_INFO); + rinfo[val].req_state = srp->done + 1; + rinfo[val].problem = + srp->header.masked_status & + srp->header.host_status & + srp->header.driver_status; + if (srp->done) + rinfo[val].duration = + srp->header.duration; + else { + ms = jiffies_to_msecs(jiffies); + rinfo[val].duration = + (ms > srp->header.duration) ? + (ms - srp->header.duration) : 0; + } + rinfo[val].orphan = srp->orphan; + rinfo[val].sg_io_owned = srp->sg_io_owned; + rinfo[val].pack_id = srp->header.pack_id; + rinfo[val].usr_ptr = srp->header.usr_ptr; + val++; + } +} + static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { @@ -1047,38 +1081,13 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -EFAULT; else { sg_req_info_t *rinfo; - unsigned int ms; rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, GFP_KERNEL); if (!rinfo) return -ENOMEM; read_lock_irqsave(&sfp->rq_list_lock, iflags); - val = 0; - list_for_each_entry(srp, &sfp->rq_list, entry) { - if (val >= SG_MAX_QUEUE) - break; - memset(&rinfo[val], 0, SZ_SG_REQ_INFO); - rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; - if (srp->done) - rinfo[val].duration = - srp->header.duration; - else { - ms = jiffies_to_msecs(jiffies); - rinfo[val].duration = - (ms > srp->header.duration) ? - (ms - srp->header.duration) : 0; - } - rinfo[val].orphan = srp->orphan; - rinfo[val].sg_io_owned = srp->sg_io_owned; - rinfo[val].pack_id = srp->header.pack_id; - rinfo[val].usr_ptr = srp->header.usr_ptr; - val++; - } + sg_fill_request_table(sfp, rinfo); read_unlock_irqrestore(&sfp->rq_list_lock, iflags); result = __copy_to_user(p, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE); -- GitLab From 9793679d8dc92d1d8a187d023d2d7a17dd9348b5 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 15 Sep 2017 14:05:16 +0200 Subject: [PATCH 1037/5498] scsi: sg: fixup infoleak when using SG_GET_REQUEST_TABLE commit 3e0097499839e0fe3af380410eababe5a47c4cf9 upstream. When calling SG_GET_REQUEST_TABLE ioctl only a half-filled table is returned; the remaining part will then contain stale kernel memory information. This patch zeroes out the entire table to avoid this issue. Signed-off-by: Hannes Reinecke Reviewed-by: Bart Van Assche Reviewed-by: Christoph Hellwig Reviewed-by: Eric Dumazet Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ab765ca749d4..591575587f47 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -865,7 +865,6 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) list_for_each_entry(srp, &sfp->rq_list, entry) { if (val > SG_MAX_QUEUE) break; - memset(&rinfo[val], 0, SZ_SG_REQ_INFO); rinfo[val].req_state = srp->done + 1; rinfo[val].problem = srp->header.masked_status & @@ -1082,8 +1081,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) else { sg_req_info_t *rinfo; - rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, - GFP_KERNEL); + rinfo = kzalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, + GFP_KERNEL); if (!rinfo) return -ENOMEM; read_lock_irqsave(&sfp->rq_list_lock, iflags); -- GitLab From 78d88643d218fccb0fcf7fa957e785d4048e35cb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 30 Aug 2017 16:30:35 +0300 Subject: [PATCH 1038/5498] scsi: qla2xxx: Fix an integer overflow in sysfs code commit e6f77540c067b48dee10f1e33678415bfcc89017 upstream. The value of "size" comes from the user. When we add "start + size" it could lead to an integer overflow bug. It means we vmalloc() a lot more memory than we had intended. I believe that on 64 bit systems vmalloc() can succeed even if we ask it to allocate huge 4GB buffers. So we would get memory corruption and likely a crash when we call ha->isp_ops->write_optrom() and ->read_optrom(). Only root can trigger this bug. Link: https://bugzilla.kernel.org/show_bug.cgi?id=194061 Fixes: b7cc176c9eb3 ("[SCSI] qla2xxx: Allow region-based flash-part accesses.") Reported-by: shqking Signed-off-by: Dan Carpenter Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/qla2xxx/qla_attr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index c1b2e86839ae..e9cd3013dcd0 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -404,6 +404,8 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, return -EINVAL; if (start > ha->optrom_size) return -EINVAL; + if (size > ha->optrom_size - start) + size = ha->optrom_size - start; mutex_lock(&ha->optrom_mutex); switch (val) { @@ -429,8 +431,7 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, } ha->optrom_region_start = start; - ha->optrom_region_size = start + size > ha->optrom_size ? - ha->optrom_size - start : size; + ha->optrom_region_size = start + size; ha->optrom_state = QLA_SREADING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); @@ -503,8 +504,7 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, } ha->optrom_region_start = start; - ha->optrom_region_size = start + size > ha->optrom_size ? - ha->optrom_size - start : size; + ha->optrom_region_size = start + size; ha->optrom_state = QLA_SWRITING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); -- GitLab From 45a521cba61051409ea2b932104ab6767e7b68b4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 1 Sep 2017 12:04:09 -0400 Subject: [PATCH 1039/5498] ftrace: Fix selftest goto location on error commit 46320a6acc4fb58f04bcf78c4c942cc43b20f986 upstream. In the second iteration of trace_selftest_ops(), the error goto label is wrong in the case where trace_selftest_test_global_cnt is off. In the case of error, it leaks the dynamic ops that was allocated. Fixes: 95950c2e ("ftrace: Add self-tests for multiple function trace users") Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace_selftest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index b0f86ea77881..ca70d11b8aa7 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -272,7 +272,7 @@ static int trace_selftest_ops(struct trace_array *tr, int cnt) goto out_free; if (cnt > 1) { if (trace_selftest_test_global_cnt == 0) - goto out; + goto out_free; } if (trace_selftest_test_dyn_cnt == 0) goto out_free; -- GitLab From 88645cf3edc042ef20d70a9f530256fcffb53f0a Mon Sep 17 00:00:00 2001 From: Baohong Liu Date: Tue, 5 Sep 2017 16:57:19 -0500 Subject: [PATCH 1040/5498] tracing: Apply trace_clock changes to instance max buffer commit 170b3b1050e28d1ba0700e262f0899ffa4fccc52 upstream. Currently trace_clock timestamps are applied to both regular and max buffers only for global trace. For instance trace, trace_clock timestamps are applied only to regular buffer. But, regular and max buffers can be swapped, for example, following a snapshot. So, for instance trace, bad timestamps can be seen following a snapshot. Let's apply trace_clock timestamps to instance max buffer as well. Link: http://lkml.kernel.org/r/ebdb168d0be042dcdf51f81e696b17fabe3609c1.1504642143.git.tom.zanussi@linux.intel.com Fixes: 277ba0446 ("tracing: Add interface to allow multiple trace buffers") Signed-off-by: Baohong Liu Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 111dfd986dbf..941b21f7c8a4 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4966,7 +4966,7 @@ static int tracing_set_clock(struct trace_array *tr, const char *clockstr) tracing_reset_online_cpus(&tr->trace_buffer); #ifdef CONFIG_TRACER_MAX_TRACE - if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer) + if (tr->max_buffer.buffer) ring_buffer_set_clock(tr->max_buffer.buffer, trace_clocks[i].func); tracing_reset_online_cpus(&tr->max_buffer); #endif -- GitLab From 13bbb8242ab25fd693f49c43ab560e6f6b85142b Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Fri, 1 Sep 2017 17:00:23 +0100 Subject: [PATCH 1041/5498] ARC: Re-enable MMU upon Machine Check exception commit 1ee55a8f7f6b7ca4c0c59e0b4b4e3584a085c2d3 upstream. I recently came upon a scenario where I would get a double fault machine check exception tiriggered by a kernel module. However the ensuing crash stacktrace (ksym lookup) was not working correctly. Turns out that machine check auto-disables MMU while modules are allocated in kernel vaddr spapce. This patch re-enables the MMU before start printing the stacktrace making stacktracing of modules work upon a fatal exception. Signed-off-by: Jose Abreu Reviewed-by: Alexey Brodkin Signed-off-by: Vineet Gupta [vgupta: moved code into low level handler to avoid in 2 places] Signed-off-by: Greg Kroah-Hartman --- arch/arc/kernel/entry.S | 6 ++++++ arch/arc/mm/tlb.c | 3 --- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 83a046a7cd06..51b419a00def 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -315,6 +315,12 @@ ENTRY(EV_MachineCheck) lr r0, [efa] mov r1, sp + ; hardware auto-disables MMU, re-enable it to allow kernel vaddr + ; access for say stack unwinding of modules for crash dumps + lr r3, [ARC_REG_PID] + or r3, r3, MMU_ENABLE + sr r3, [ARC_REG_PID] + lsr r3, r2, 8 bmsk r3, r3, 7 brne r3, ECR_C_MCHK_DUP_TLB, 1f diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index 7f47d2a56f44..b7a0c44785c1 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -689,9 +689,6 @@ void do_tlb_overlap_fault(unsigned long cause, unsigned long address, local_irq_save(flags); - /* re-enable the MMU */ - write_aux_reg(ARC_REG_PID, MMU_ENABLE | read_aux_reg(ARC_REG_PID)); - /* loop thru all sets of TLB */ for (set = 0; set < mmu->sets; set++) { -- GitLab From 19b86cde515002a32b413964babc0e736478e19e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezzubikov Date: Tue, 18 Jul 2017 17:12:25 +0300 Subject: [PATCH 1042/5498] PCI: shpchp: Enable bridge bus mastering if MSI is enabled commit 48b79a14505349a29b3e20f03619ada9b33c4b17 upstream. An SHPC may generate MSIs to notify software about slot or controller events (SHPC spec r1.0, sec 4.7). A PCI device can only generate an MSI if it has bus mastering enabled. Enable bus mastering if the bridge contains an SHPC that uses MSI for event notifications. Signed-off-by: Aleksandr Bezzubikov [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Reviewed-by: Marcel Apfelbaum Acked-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/hotplug/shpchp_hpc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 7d223e9080ef..77dddee2753a 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -1062,6 +1062,8 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev) if (rc) { ctrl_info(ctrl, "Can't get msi for the hotplug controller\n"); ctrl_info(ctrl, "Use INTx for the hotplug controller\n"); + } else { + pci_set_master(pdev); } rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED, -- GitLab From 6b3412ff96615bab06863c00c371b5601e3b1e1c Mon Sep 17 00:00:00 2001 From: Daniel Mentz Date: Wed, 2 Aug 2017 23:42:17 -0400 Subject: [PATCH 1043/5498] media: v4l2-compat-ioctl32: Fix timespec conversion commit 9c7ba1d7634cef490b85bc64c4091ff004821bfd upstream. Certain syscalls like recvmmsg support 64 bit timespec values for the X32 ABI. The helper function compat_put_timespec converts a timespec value to a 32 bit or 64 bit value depending on what ABI is used. The v4l2 compat layer, however, is not designed to support 64 bit timespec values and always uses 32 bit values. Hence, compat_put_timespec must not be used. Without this patch, user space will be provided with bad timestamp values from the VIDIOC_DQEVENT ioctl. Also, fields of the struct v4l2_event32 that come immediately after timestamp get overwritten, namely the field named id. Fixes: 81993e81a994 ("compat: Get rid of (get|put)_compat_time(val|spec)") Cc: H. Peter Anvin Cc: Laurent Pinchart Cc: Tiffany Lin Cc: Ricardo Ribalda Delgado Cc: Sakari Ailus Signed-off-by: Daniel Mentz Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index e77d3fc50abd..aa11f02c0a8f 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -747,7 +747,8 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u copy_to_user(&up->u, &kp->u, sizeof(kp->u)) || put_user(kp->pending, &up->pending) || put_user(kp->sequence, &up->sequence) || - compat_put_timespec(&kp->timestamp, &up->timestamp) || + put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || + put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) || put_user(kp->id, &up->id) || copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32))) return -EFAULT; -- GitLab From 15ac0595018f5fdfbec2a23574b81a01c73ee5e1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 8 Aug 2017 08:56:21 -0400 Subject: [PATCH 1044/5498] media: uvcvideo: Prevent heap overflow when accessing mapped controls commit 7e09f7d5c790278ab98e5f2c22307ebe8ad6e8ba upstream. The size of uvc_control_mapping is user controlled leading to a potential heap overflow in the uvc driver. This adds a check to verify the user provided size fits within the bounds of the defined buffer size. Originally-from: Richard Simmons Signed-off-by: Guenter Roeck Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/uvc/uvc_ctrl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 3e59b288b8a8..618e4e2b4207 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2001,6 +2001,13 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, goto done; } + /* Validate the user-provided bit-size and offset */ + if (mapping->size > 32 || + mapping->offset + mapping->size > ctrl->info.size * 8) { + ret = -EINVAL; + goto done; + } + list_for_each_entry(map, &ctrl->info.mappings, list) { if (mapping->id == map->id) { uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', " -- GitLab From cfa0386af092a3afcf5edc8930ed7bb4990697e3 Mon Sep 17 00:00:00 2001 From: Tang Junhui Date: Thu, 7 Sep 2017 01:28:53 +0800 Subject: [PATCH 1045/5498] bcache: initialize dirty stripes in flash_dev_run() commit 175206cf9ab63161dec74d9cd7f9992e062491f5 upstream. bcache uses a Proportion-Differentiation Controller algorithm to control writeback rate to cached devices. In the PD controller algorithm, dirty stripes of thin flash device should not be counted in, because flash only volumes never write back dirty data. Currently dirty stripe counter for thin flash device is not initialized when the thin flash device starts. Which means the following calculation in PD controller will reference an undefined dirty stripes number, and all cached devices attached to the same cache set where the thin flash device lies on may have an inaccurate writeback rate. This patch calles bch_sectors_dirty_init() in flash_dev_run(), to correctly initialize dirty stripe counter when the thin flash device starts to run. This patch also does following parameter data type change, -void bch_sectors_dirty_init(struct cached_dev *dc); +void bch_sectors_dirty_init(struct bcache_device *); to call this function conveniently in flash_dev_run(). (Commit log is composed by Coly Li) Signed-off-by: Tang Junhui Reviewed-by: Coly Li Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/super.c | 3 ++- drivers/md/bcache/writeback.c | 8 ++++---- drivers/md/bcache/writeback.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index a7a03a21d78a..2b4fae01381f 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1054,7 +1054,7 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) } if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) { - bch_sectors_dirty_init(dc); + bch_sectors_dirty_init(&dc->disk); atomic_set(&dc->has_dirty, 1); atomic_inc(&dc->count); bch_writeback_queue(dc); @@ -1258,6 +1258,7 @@ static int flash_dev_run(struct cache_set *c, struct uuid_entry *u) goto err; bcache_device_attach(d, c, u - c->uuids); + bch_sectors_dirty_init(d); bch_flash_dev_request_init(d); add_disk(d->disk); diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 540256a0df4f..66433fc40afa 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -488,17 +488,17 @@ static int sectors_dirty_init_fn(struct btree_op *_op, struct btree *b, return MAP_CONTINUE; } -void bch_sectors_dirty_init(struct cached_dev *dc) +void bch_sectors_dirty_init(struct bcache_device *d) { struct sectors_dirty_init op; bch_btree_op_init(&op.op, -1); - op.inode = dc->disk.id; + op.inode = d->id; - bch_btree_map_keys(&op.op, dc->disk.c, &KEY(op.inode, 0, 0), + bch_btree_map_keys(&op.op, d->c, &KEY(op.inode, 0, 0), sectors_dirty_init_fn, 0); - dc->disk.sectors_dirty_last = bcache_dev_sectors_dirty(&dc->disk); + d->sectors_dirty_last = bcache_dev_sectors_dirty(d); } void bch_cached_dev_writeback_init(struct cached_dev *dc) diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h index 073a042aed24..357658b64794 100644 --- a/drivers/md/bcache/writeback.h +++ b/drivers/md/bcache/writeback.h @@ -85,7 +85,7 @@ static inline void bch_writeback_add(struct cached_dev *dc) void bcache_dev_sectors_dirty_add(struct cache_set *, unsigned, uint64_t, int); -void bch_sectors_dirty_init(struct cached_dev *dc); +void bch_sectors_dirty_init(struct bcache_device *); void bch_cached_dev_writeback_init(struct cached_dev *); int bch_cached_dev_writeback_start(struct cached_dev *); -- GitLab From 615f8ab2ffcdd13ad0ce98e62a5268f9954893a6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 6 Sep 2017 14:25:51 +0800 Subject: [PATCH 1046/5498] bcache: Fix leak of bdev reference commit 4b758df21ee7081ab41448d21d60367efaa625b3 upstream. If blkdev_get_by_path() in register_bcache() fails, we try to lookup the block device using lookup_bdev() to detect which situation we are in to properly report error. However we never drop the reference returned to us from lookup_bdev(). Fix that. Signed-off-by: Jan Kara Acked-by: Coly Li Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/super.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 2b4fae01381f..38522cc6b26d 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1997,6 +1997,8 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, else err = "device busy"; mutex_unlock(&bch_register_lock); + if (!IS_ERR(bdev)) + bdput(bdev); if (attr == &ksysfs_register_quiet) goto out; } -- GitLab From 0b312f81db414f2ec82034c317bd2dae58c03fb1 Mon Sep 17 00:00:00 2001 From: Tang Junhui Date: Wed, 6 Sep 2017 14:25:56 +0800 Subject: [PATCH 1047/5498] bcache: correct cache_dirty_target in __update_writeback_rate() commit a8394090a9129b40f9d90dcb7f4a49d60c727ca6 upstream. __update_write_rate() uses a Proportion-Differentiation Controller algorithm to control writeback rate. A dirty target number is used in this PD controller to control writeback rate. A larger target number will make the writeback rate smaller, on the versus, a smaller target number will make the writeback rate larger. bcache uses the following steps to calculate the target number, 1) cache_sectors = all-buckets-of-cache-set * buckets-size 2) cache_dirty_target = cache_sectors * cached-device-writeback_percent 3) target = cache_dirty_target * (sectors-of-cached-device/sectors-of-all-cached-devices-of-this-cache-set) The calculation at step 1) for cache_sectors is incorrect, which does not consider dirty blocks occupied by flash only volume. A flash only volume can be took as a bcache device without cached device. All data sectors allocated for it are persistent on cache device and marked dirty, they are not touched by bcache writeback and garbage collection code. So data blocks of flash only volume should be ignore when calculating cache_sectors of cache set. Current code does not subtract dirty sectors of flash only volume, which results a larger target number from the above 3 steps. And in sequence the cache device's writeback rate is smaller then a correct value, writeback speed is slower on all cached devices. This patch fixes the incorrect slower writeback rate by subtracting dirty sectors of flash only volumes in __update_writeback_rate(). (Commit log composed by Coly Li to pass checkpatch.pl checking) Signed-off-by: Tang Junhui Reviewed-by: Coly Li Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/writeback.c | 3 ++- drivers/md/bcache/writeback.h | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 66433fc40afa..43dc9cb14d28 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -21,7 +21,8 @@ static void __update_writeback_rate(struct cached_dev *dc) { struct cache_set *c = dc->disk.c; - uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size; + uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size - + bcache_flash_devs_sectors_dirty(c); uint64_t cache_dirty_target = div_u64(cache_sectors * dc->writeback_percent, 100); diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h index 357658b64794..daec4fd782ea 100644 --- a/drivers/md/bcache/writeback.h +++ b/drivers/md/bcache/writeback.h @@ -14,6 +14,25 @@ static inline uint64_t bcache_dev_sectors_dirty(struct bcache_device *d) return ret; } +static inline uint64_t bcache_flash_devs_sectors_dirty(struct cache_set *c) +{ + uint64_t i, ret = 0; + + mutex_lock(&bch_register_lock); + + for (i = 0; i < c->nr_uuids; i++) { + struct bcache_device *d = c->devices[i]; + + if (!d || !UUID_FLASH_ONLY(&c->uuids[i])) + continue; + ret += bcache_dev_sectors_dirty(d); + } + + mutex_unlock(&bch_register_lock); + + return ret; +} + static inline unsigned offset_to_stripe(struct bcache_device *d, uint64_t offset) { -- GitLab From c04dc907e590c85d84f252c1de390da1cfd5e3dc Mon Sep 17 00:00:00 2001 From: Tony Asleson Date: Wed, 6 Sep 2017 14:25:57 +0800 Subject: [PATCH 1048/5498] bcache: Correct return value for sysfs attach errors commit 77fa100f27475d08a569b9d51c17722130f089e7 upstream. If you encounter any errors in bch_cached_dev_attach it will return a negative error code. The variable 'v' which stores the result is unsigned, thus user space sees a very large value returned for bytes written which can cause incorrect user space behavior. Utilize 1 signed variable to use throughout the function to preserve error return capability. Signed-off-by: Tony Asleson Acked-by: Coly Li Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index b3ff57d61dde..4fbb5532f24c 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -191,7 +191,7 @@ STORE(__cached_dev) { struct cached_dev *dc = container_of(kobj, struct cached_dev, disk.kobj); - unsigned v = size; + ssize_t v = size; struct cache_set *c; struct kobj_uevent_env *env; @@ -226,7 +226,7 @@ STORE(__cached_dev) bch_cached_dev_run(dc); if (attr == &sysfs_cache_mode) { - ssize_t v = bch_read_string_list(buf, bch_cache_modes + 1); + v = bch_read_string_list(buf, bch_cache_modes + 1); if (v < 0) return v; -- GitLab From 50aee960b0d1f55c2fb682ab54262bcceaf558aa Mon Sep 17 00:00:00 2001 From: Tang Junhui Date: Wed, 6 Sep 2017 14:25:59 +0800 Subject: [PATCH 1049/5498] bcache: fix for gc and write-back race commit 9baf30972b5568d8b5bc8b3c46a6ec5b58100463 upstream. gc and write-back get raced (see the email "bcache get stucked" I sended before): gc thread write-back thread | |bch_writeback_thread() |bch_gc_thread() | | |==>read_dirty() |==>bch_btree_gc() | |==>btree_root() //get btree root | | //node write locker | |==>bch_btree_gc_root() | | |==>read_dirty_submit() | |==>write_dirty() | |==>continue_at(cl, | | write_dirty_finish, | | system_wq); | |==>write_dirty_finish()//excute | | //in system_wq | |==>bch_btree_insert() | |==>bch_btree_map_leaf_nodes() | |==>__bch_btree_map_nodes() | |==>btree_root //try to get btree | | //root node read | | //lock | |-----stuck here |==>bch_btree_set_root() |==>bch_journal_meta() |==>bch_journal() |==>journal_try_write() |==>journal_write_unlocked() //journal_full(&c->journal) | //condition satisfied |==>continue_at(cl, journal_write, system_wq); //try to excute | //journal_write in system_wq | //but work queue is excuting | //write_dirty_finish() |==>closure_sync(); //wait journal_write execute | //over and wake up gc, |-------------stuck here |==>release root node write locker This patch alloc a separate work-queue for write-back thread to avoid such race. (Commit log re-organized by Coly Li to pass checkpatch.pl checking) Signed-off-by: Tang Junhui Acked-by: Coly Li Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/bcache.h | 1 + drivers/md/bcache/super.c | 2 ++ drivers/md/bcache/writeback.c | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 04f7bc28ef83..dfdd1908641c 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -348,6 +348,7 @@ struct cached_dev { /* Limit number of writeback bios in flight */ struct semaphore in_flight; struct task_struct *writeback_thread; + struct workqueue_struct *writeback_write_wq; struct keybuf writeback_keys; diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 38522cc6b26d..8e5666ac8a6a 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1087,6 +1087,8 @@ static void cached_dev_free(struct closure *cl) cancel_delayed_work_sync(&dc->writeback_rate_update); if (!IS_ERR_OR_NULL(dc->writeback_thread)) kthread_stop(dc->writeback_thread); + if (dc->writeback_write_wq) + destroy_workqueue(dc->writeback_write_wq); mutex_lock(&bch_register_lock); diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 43dc9cb14d28..b0667b321a3f 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -191,7 +191,7 @@ static void write_dirty(struct closure *cl) closure_bio_submit(&io->bio, cl, &io->dc->disk); - continue_at(cl, write_dirty_finish, system_wq); + continue_at(cl, write_dirty_finish, io->dc->writeback_write_wq); } static void read_dirty_endio(struct bio *bio, int error) @@ -211,7 +211,7 @@ static void read_dirty_submit(struct closure *cl) closure_bio_submit(&io->bio, cl, &io->dc->disk); - continue_at(cl, write_dirty, system_wq); + continue_at(cl, write_dirty, io->dc->writeback_write_wq); } static void read_dirty(struct cached_dev *dc) @@ -523,6 +523,11 @@ void bch_cached_dev_writeback_init(struct cached_dev *dc) int bch_cached_dev_writeback_start(struct cached_dev *dc) { + dc->writeback_write_wq = alloc_workqueue("bcache_writeback_wq", + WQ_MEM_RECLAIM, 0); + if (!dc->writeback_write_wq) + return -ENOMEM; + dc->writeback_thread = kthread_create(bch_writeback_thread, dc, "bcache_writeback"); if (IS_ERR(dc->writeback_thread)) -- GitLab From 0aad447e99a025ae3a3833c18f94f9700b575367 Mon Sep 17 00:00:00 2001 From: Michael Lyle Date: Wed, 6 Sep 2017 14:26:02 +0800 Subject: [PATCH 1050/5498] bcache: fix bch_hprint crash and improve output commit 9276717b9e297a62d1151a43d1cd286213f68eb7 upstream. Most importantly, solve a crash where %llu was used to format signed numbers. This would cause a buffer overflow when reading sysfs writeback_rate_debug, as only 20 bytes were allocated for this and %llu writes 20 characters plus a null. Always use the units mechanism rather than having different output paths for simplicity. Also, correct problems with display output where 1.10 was a larger number than 1.09, by multiplying by 10 and then dividing by 1024 instead of dividing by 100. (Remainders of >= 1000 would print as .10). Minor changes: Always display the decimal point instead of trying to omit it based on number of digits shown. Decide what units to use based on 1000 as a threshold, not 1024 (in other words, always print at most 3 digits before the decimal point). Signed-off-by: Michael Lyle Reported-by: Dmitry Yu Okunev Acked-by: Kent Overstreet Reviewed-by: Coly Li Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/util.c | 50 ++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c index db3ae4c2b223..6c18e3ec3e48 100644 --- a/drivers/md/bcache/util.c +++ b/drivers/md/bcache/util.c @@ -73,24 +73,44 @@ STRTO_H(strtouint, unsigned int) STRTO_H(strtoll, long long) STRTO_H(strtoull, unsigned long long) +/** + * bch_hprint() - formats @v to human readable string for sysfs. + * + * @v - signed 64 bit integer + * @buf - the (at least 8 byte) buffer to format the result into. + * + * Returns the number of bytes used by format. + */ ssize_t bch_hprint(char *buf, int64_t v) { static const char units[] = "?kMGTPEZY"; - char dec[4] = ""; - int u, t = 0; - - for (u = 0; v >= 1024 || v <= -1024; u++) { - t = v & ~(~0 << 10); - v >>= 10; - } - - if (!u) - return sprintf(buf, "%llu", v); - - if (v < 100 && v > -100) - snprintf(dec, sizeof(dec), ".%i", t / 100); - - return sprintf(buf, "%lli%s%c", v, dec, units[u]); + int u = 0, t; + + uint64_t q; + + if (v < 0) + q = -v; + else + q = v; + + /* For as long as the number is more than 3 digits, but at least + * once, shift right / divide by 1024. Keep the remainder for + * a digit after the decimal point. + */ + do { + u++; + + t = q & ~(~0 << 10); + q >>= 10; + } while (q >= 1000); + + if (v < 0) + /* '-', up to 3 digits, '.', 1 digit, 1 character, null; + * yields 8 bytes. + */ + return sprintf(buf, "-%llu.%i%c", q, t * 10 / 1024, units[u]); + else + return sprintf(buf, "%llu.%i%c", q, t * 10 / 1024, units[u]); } ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[], -- GitLab From 0e13335254d5d54933969dba1d7625f55e657f52 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 27 Sep 2017 10:57:40 +0200 Subject: [PATCH 1051/5498] Linux 3.18.72 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 33feb19e7241..9b82f279ef1d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 71 +SUBLEVEL = 72 EXTRAVERSION = NAME = Diseased Newt -- GitLab From cd3f7a05db070da1ba2371eb7eea1b99c365d97f Mon Sep 17 00:00:00 2001 From: Abhilash Kumar Date: Mon, 17 Jul 2017 01:05:23 +0530 Subject: [PATCH 1052/5498] msm: kgsl: Update total time at right place for accurate GPU busy For calculation of busy time and total time, KGSL relies on perf counters and CPU clock. These can be a bit out of sync and may give GPU busy greater than 100 percent. Updating time at the right place will give more accurate total time and will avoid crossing 100% in GPU busy calculation. Change-Id: I3cc702492325b9dc44ea2b705e4d9014d95abd33 Signed-off-by: Abhilash Kumar --- drivers/gpu/msm/kgsl_pwrscale.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index c68fe136b193..da79ad1cd0ad 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -518,7 +518,7 @@ int kgsl_devfreq_get_dev_status(struct device *dev, struct kgsl_device *device = dev_get_drvdata(dev); struct kgsl_pwrctrl *pwrctrl; struct kgsl_pwrscale *pwrscale; - ktime_t tmp; + ktime_t tmp1, tmp2; if (device == NULL) return -ENODEV; @@ -529,6 +529,8 @@ int kgsl_devfreq_get_dev_status(struct device *dev, pwrctrl = &device->pwrctrl; mutex_lock(&device->mutex); + + tmp1 = ktime_get(); /* * If the GPU clock is on grab the latest power counter * values. Otherwise the most recent ACTIVE values will @@ -536,9 +538,9 @@ int kgsl_devfreq_get_dev_status(struct device *dev, */ kgsl_pwrscale_update_stats(device); - tmp = ktime_get(); - stat->total_time = ktime_us_delta(tmp, pwrscale->time); - pwrscale->time = tmp; + tmp2 = ktime_get(); + stat->total_time = ktime_us_delta(tmp2, pwrscale->time); + pwrscale->time = tmp1; stat->busy_time = pwrscale->accum_stats.busy_time; -- GitLab From 7c43c5e1f7fdae438575093bbaf561528ed0c40c Mon Sep 17 00:00:00 2001 From: Abhilash Kumar Date: Fri, 14 Jul 2017 13:13:44 +0530 Subject: [PATCH 1053/5498] msm: kgsl: Set the abnormal power perf counter value to zero During preemption microcode does save restore for all perf counters. If we read the power counters at preemption boundary we might get abnormal value from the perf counter. This will result in showing incorrect GPU busy percentage. Fix this by setting the abnormal power perf counter value with zero. Change-Id: I96ba367ceeeb92d6adb507d0d917113297b4b58d Signed-off-by: Abhilash Kumar --- drivers/gpu/msm/adreno.c | 25 +++++++++++++++++++++---- drivers/gpu/msm/adreno.h | 23 +++++++++++++++++++++++ drivers/gpu/msm/adreno_a3xx.c | 6 +++++- drivers/gpu/msm/adreno_a4xx.c | 6 +++++- drivers/gpu/msm/adreno_a5xx.c | 4 ++++ 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index faa445ac9d55..829b49927de2 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -2619,21 +2619,38 @@ static inline s64 adreno_ticks_to_us(u32 ticks, u32 freq) return ticks / freq; } -static unsigned int counter_delta(struct kgsl_device *device, +static inline unsigned int counter_delta(struct kgsl_device *device, unsigned int reg, unsigned int *counter) { + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); unsigned int val; unsigned int ret = 0; + bool overflow = true; + static unsigned int perfctr_pwr_hi; /* Read the value */ kgsl_regread(device, reg, &val); + if (adreno_is_a5xx(adreno_dev) && reg == adreno_getreg + (adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO)) + overflow = is_power_counter_overflow(adreno_dev, reg, + *counter, &perfctr_pwr_hi); + /* Return 0 for the first read */ if (*counter != 0) { - if (val < *counter) - ret = (0xFFFFFFFF - *counter) + val; - else + if (val >= *counter) { ret = val - *counter; + } else if (overflow == true) { + ret = (0xFFFFFFFF - *counter) + val; + } else { + /* + * Since KGSL got abnormal value from the counter, + * We will drop the value from being accumulated. + */ + pr_warn_once("KGSL: Abnormal value :0x%x (0x%x) from perf counter : 0x%x\n", + val, *counter, reg); + return 0; + } } *counter = val; diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 80640e123444..eaef1aa9c400 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -565,6 +565,8 @@ enum adreno_regs { ADRENO_REG_RBBM_RBBM_CTL, ADRENO_REG_UCHE_INVALIDATE0, ADRENO_REG_UCHE_INVALIDATE1, + ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, ADRENO_REG_RBBM_SECVID_TRUST_CONTROL, @@ -1490,4 +1492,25 @@ static inline void adreno_ringbuffer_set_pagetable(struct adreno_ringbuffer *rb, spin_unlock_irqrestore(&rb->preempt_lock, flags); } +static inline bool is_power_counter_overflow(struct adreno_device *adreno_dev, + unsigned int reg, unsigned int prev_val, unsigned int *perfctr_pwr_hi) +{ + unsigned int val; + bool ret = false; + + /* + * If prev_val is zero, it is first read after perf counter reset. + * So set perfctr_pwr_hi register to zero. + */ + if (prev_val == 0) { + *perfctr_pwr_hi = 0; + return ret; + } + adreno_readreg(adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, &val); + if (val != *perfctr_pwr_hi) { + *perfctr_pwr_hi = val; + ret = true; + } + return ret; +} #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c index 423071811b43..0e3e5b64bdc7 100644 --- a/drivers/gpu/msm/adreno_a3xx.c +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1530,6 +1530,10 @@ static unsigned int a3xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { A3XX_UCHE_CACHE_INVALIDATE0_REG), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1, A3XX_UCHE_CACHE_INVALIDATE1_REG), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + A3XX_RBBM_PERFCTR_RBBM_0_LO), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, + A3XX_RBBM_PERFCTR_RBBM_0_HI), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, A3XX_RBBM_PERFCTR_LOAD_VALUE_LO), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c index 2bc6d6c15c86..89e90d6a844b 100644 --- a/drivers/gpu/msm/adreno_a4xx.c +++ b/drivers/gpu/msm/adreno_a4xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -806,6 +806,10 @@ static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SW_RESET_CMD, A4XX_RBBM_SW_RESET_CMD), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A4XX_UCHE_INVALIDATE0), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1, A4XX_UCHE_INVALIDATE1), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + A4XX_RBBM_PERFCTR_RBBM_0_LO), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, + A4XX_RBBM_PERFCTR_RBBM_0_HI), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, A4XX_RBBM_PERFCTR_LOAD_VALUE_LO), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index da0ffa83fdd7..306782f71744 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -3042,6 +3042,10 @@ static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_RBBM_BLOCK_SW_RESET_CMD2, A5XX_RBBM_BLOCK_SW_RESET_CMD2), ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A5XX_UCHE_INVALIDATE0), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO, + A5XX_RBBM_PERFCTR_RBBM_0_LO), + ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, + A5XX_RBBM_PERFCTR_RBBM_0_HI), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO, A5XX_RBBM_PERFCTR_LOAD_VALUE_LO), ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI, -- GitLab From fe6e9c94d1c3b5d08ca01c73523f42e60f5f9d06 Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Sat, 23 Sep 2017 01:41:24 +0530 Subject: [PATCH 1054/5498] soc: qcom: glink: Free data memory before freeing intent Data of intent is not freed even in purge_intent_list. This results in memory leak. Kfree is done for data before freeing intent. CRs-Fixed: 2116744 Change-Id: Ib99261208df1cc9b63b4cd0a35ac0c7942efb4a8 Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/glink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index bf33fe5c01ea..49d270591ecf 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -1653,6 +1653,8 @@ void ch_purge_intent_lists(struct channel_ctx *ctx) &ctx->local_rx_intent_list, list) { ctx->notify_rx_abort(ctx, ctx->user_priv, ptr_intent->pkt_priv); + ctx->transport_ptr->ops->deallocate_rx_intent( + ctx->transport_ptr->ops, ptr_intent); list_del(&ptr_intent->list); kfree(ptr_intent); } -- GitLab From b450564b5d4b73a31c0214e212d0d5b69b16bc20 Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Sat, 23 Sep 2017 01:46:02 +0530 Subject: [PATCH 1055/5498] soc: qcom: glink: Free if_ptr before freeing dummy transport Dummy transport is only way to access if_ptr. When dummy transport is freed, if_ptr allocated for dummy transport is not freed. This result in memory leak. kfree of if_ptr is called before freeing dummy transport. CRs-Fixed: 2116744 Change-Id: I832e0fcde418b7c3d992f50e817866bc9075da3c Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/glink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index 49d270591ecf..41dccd0c29ce 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -3669,6 +3669,8 @@ static void glink_dummy_xprt_ctx_release(struct rwref_lock *xprt_st_lock) GLINK_INFO("%s: freeing transport [%s->%s]context\n", __func__, xprt_ctx->name, xprt_ctx->edge); + kfree(xprt_ctx->ops); + xprt_ctx->ops = NULL; kfree(xprt_ctx); } -- GitLab From 704e94032a18264e9ff1e571103db913b6858d4a Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 7 Sep 2017 11:05:56 +0530 Subject: [PATCH 1056/5498] msm:ipa: Fix to slab out of bounds access Accessing of incorrect structure pointer is causing slab-out-of-bounds access, fixed issue by accessing the correct structure pointer. Change-Id: I23d3c9afbbabba88be92ef5cae83c4708a211e88 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c | 14 ++++++++------ drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c | 7 ++++--- drivers/platform/msm/ipa/ipa_v3/ipa_flt.c | 7 +++++++ drivers/platform/msm/ipa/ipa_v3/ipa_rt.c | 7 +++++++ 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index f45420555770..f33205c3e813 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -811,10 +811,11 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, eq = true; } else { rt_tbl = ipa_id_find(entry->rule.rt_tbl_hdl); - if (rt_tbl) - rt_tbl_idx = rt_tbl->idx; + if (rt_tbl == NULL || + rt_tbl->cookie != IPA_RT_TBL_COOKIE) + rt_tbl_idx = ~0; else - rt_tbl_idx = ~0; + rt_tbl_idx = rt_tbl->idx; bitmap = entry->rule.attrib.attrib_mask; eq = false; } @@ -841,10 +842,11 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, eq = true; } else { rt_tbl = ipa_id_find(entry->rule.rt_tbl_hdl); - if (rt_tbl) - rt_tbl_idx = rt_tbl->idx; - else + if (rt_tbl == NULL || + rt_tbl->cookie != IPA_RT_TBL_COOKIE) rt_tbl_idx = ~0; + else + rt_tbl_idx = rt_tbl->idx; bitmap = entry->rule.attrib.attrib_mask; eq = false; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index 503e1c935f05..4baec40f2b7b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -842,10 +842,11 @@ static ssize_t ipa3_read_flt(struct file *file, char __user *ubuf, size_t count, eq = true; } else { rt_tbl = ipa3_id_find(entry->rule.rt_tbl_hdl); - if (rt_tbl) - rt_tbl_idx = rt_tbl->idx; + if (rt_tbl == NULL || + rt_tbl->cookie != IPA_RT_TBL_COOKIE) + rt_tbl_idx = ~0; else - rt_tbl_idx = ~0; + rt_tbl_idx = rt_tbl->idx; bitmap = entry->rule.attrib.attrib_mask; eq = false; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 7839b23aa79f..3ba84463682d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -1501,6 +1501,13 @@ int ipa3_add_flt_rule_after(struct ipa_ioc_add_flt_rule_after *rules) goto bail; } + if (entry->cookie != IPA_FLT_COOKIE) { + IPAERR_RL("Invalid cookie value = %u flt hdl id = %d\n", + entry->cookie, rules->add_after_hdl); + result = -EINVAL; + goto bail; + } + if (entry->tbl != tbl) { IPAERR_RL("given entry does not match the table\n"); result = -EINVAL; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 27a379b3eba6..0072a98bbd6c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -1381,6 +1381,13 @@ int ipa3_add_rt_rule_after(struct ipa_ioc_add_rt_rule_after *rules) goto bail; } + if (entry->cookie != IPA_RT_RULE_COOKIE) { + IPAERR_RL("Invalid cookie value = %u rule %d in rt tbls\n", + entry->cookie, rules->add_after_hdl); + ret = -EINVAL; + goto bail; + } + if (entry->tbl != tbl) { IPAERR_RL("given rt rule does not match the table\n"); ret = -EINVAL; -- GitLab From 545a6b5212fbcd9c66ab524a471af6180e69de15 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Wed, 27 Sep 2017 17:31:23 +0530 Subject: [PATCH 1057/5498] ARM: dts: msm: Use correct qmp phy init sequence for mdm9650 The current qmp phy init sequence for mdm9650 is not aligned with the HPG. Update the sequence to align with the HPG. CRs-Fixed: 2118690 Change-Id: I790b1c3fda02313dc0d737b8b442c12c1b014f83 Signed-off-by: Sriharsha Allenki --- arch/arm/boot/dts/qcom/mdm9650-usb.dtsi | 35 +++++++++---------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650-usb.dtsi b/arch/arm/boot/dts/qcom/mdm9650-usb.dtsi index 7ce2714a09b8..fb665282185c 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-usb.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650-usb.dtsi @@ -162,12 +162,12 @@ qcom,qmp-phy-init-seq = <0xac 0x14 0x1a 0x00 0x34 0x08 0x08 0x00 0x174 0x30 0x30 0x00 - 0x3c 0x06 0x06 0x00 - 0xb4 0x00 0x00 0x00 - 0xb8 0x08 0x08 0x00 - 0x194 0x06 0x06 0x3e8 + 0x70 0x0f 0x0f 0x00 0x19c 0x01 0x01 0x00 0x178 0x00 0x00 0x00 + 0x194 0x06 0x06 0x3e8 + 0x48 0x0f 0x0f 0x00 + 0x3c 0x02 0x02 0x00 0xd0 0x82 0x82 0x00 0xdc 0x55 0x55 0x00 0xe0 0x55 0x55 0x00 @@ -176,12 +176,9 @@ 0x84 0x16 0x16 0x00 0x90 0x28 0x28 0x00 0x108 0x80 0x80 0x00 - 0x10c 0x00 0x00 0x00 - 0x184 0x0a 0x0a 0x00 0x4c 0x15 0x15 0x00 0x50 0x34 0x34 0x00 0x54 0x00 0x00 0x00 - 0xc8 0x00 0x00 0x00 0x18c 0x00 0x00 0x00 0xcc 0x00 0x00 0x00 0x128 0x00 0x00 0x00 @@ -193,37 +190,29 @@ 0x18 0x00 0x00 0x00 0x24 0xde 0xde 0x00 0x28 0x07 0x07 0x00 - 0x48 0x0f 0x0f 0x00 - 0x70 0x0f 0x0f 0x00 - 0x100 0x80 0x80 0x00 - 0x440 0x0b 0x0b 0x00 + 0x41c 0x06 0x06 0x00 0x4d8 0x02 0x02 0x00 - 0x4dc 0x6c 0x6c 0x00 - 0x4e0 0xbb 0xbb 0x00 + 0x4dc 0x4c 0x4c 0x00 + 0x4e0 0xb8 0xb8 0x00 0x508 0x77 0x77 0x00 0x50c 0x80 0x80 0x00 0x514 0x03 0x03 0x00 0x51c 0x16 0x16 0x00 - 0x448 0x75 0x75 0x00 - 0x454 0x00 0x00 0x00 - 0x40c 0x0a 0x0a 0x00 - 0x41c 0x06 0x06 0x00 - 0x510 0x00 0x00 0x00 + 0x510 0x0c 0x0c 0x00 0x268 0x45 0x45 0x00 0x2ac 0x12 0x12 0x00 0x294 0x06 0x06 0x00 - 0x254 0x00 0x00 0x00 + 0x824 0x15 0x15 0x00 + 0x828 0x0e 0x0e 0x00 0x8c8 0x83 0x83 0x00 0x8c4 0x02 0x02 0x00 0x8cc 0x09 0x09 0x00 0x8d0 0xa2 0xa2 0x00 - 0x8d4 0x85 0x85 0x00 + 0x8d4 0x85 0x85 0x00 0x880 0xd1 0xd1 0x00 0x884 0x1f 0x1f 0x00 0x888 0x47 0x47 0x00 - 0x80c 0x9f 0x9f 0x00 - 0x824 0x17 0x17 0x00 - 0x828 0x0f 0x0f 0x00 + 0x864 0x1b 0x1b 0x00 0x8b8 0x75 0x75 0x00 0x8bc 0x13 0x13 0x00 0x8b0 0x86 0x86 0x00 -- GitLab From 0b2108e83eb027c9687e33b058a9ab7ca847e80a Mon Sep 17 00:00:00 2001 From: Gaurav Sonwani Date: Thu, 15 Jun 2017 14:11:23 +0530 Subject: [PATCH 1058/5498] msm: kgsl: Disallow L2PC during wake up from SLUMBER Add a PM QOS request to disallow L2PC during wake up from SLUMBER state. This is required to improve queue to submit time for first set of GPU commands which results in GPU wake up. Change-Id: Iad1a6dfdf9e1fe034eef4fae526138d724bdd3eb Signed-off-by: Gaurav Sonwani Signed-off-by: Abhilash Kumar --- .../devicetree/bindings/gpu/adreno.txt | 3 +++ drivers/gpu/msm/adreno.c | 4 ++++ drivers/gpu/msm/adreno_dispatch.c | 5 ++++- drivers/gpu/msm/kgsl_pwrctrl.c | 17 ++++++++--------- drivers/gpu/msm/kgsl_pwrctrl.h | 18 +++++++++++++++++- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index 1e87cd2729d4..0a5fb5807f00 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -134,6 +134,9 @@ Optional Properties: rendering thread is running on masked CPUs. Bit 0 is for CPU-0, bit 1 is for CPU-1... +- qcom,l2pc-update-queue: + Disables L2PC on masked CPUs at queue time when it's true. + - qcom,snapshot-size: Specify the size of snapshot in bytes. This will override snapshot size defined in the driver code. diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index faa445ac9d55..3f4444fe88a8 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1395,6 +1395,10 @@ static int _adreno_start(struct adreno_device *adreno_dev) /* make sure ADRENO_DEVICE_STARTED is not set here */ BUG_ON(test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)); + /* disallow l2pc during wake up to improve GPU wake up time */ + kgsl_pwrctrl_update_l2pc(&adreno_dev->dev, + KGSL_L2PC_WAKEUP_TIMEOUT); + pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma, pmqos_wakeup_vote); diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 48a72fb3c0b3..12c4c414ee86 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -1006,6 +1006,7 @@ int adreno_dispatcher_queue_cmd(struct adreno_device *adreno_dev, { struct adreno_dispatcher_cmdqueue *dispatch_q = ADRENO_CMDBATCH_DISPATCH_CMDQUEUE(cmdbatch); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); int ret; spin_lock(&drawctxt->lock); @@ -1181,7 +1182,9 @@ int adreno_dispatcher_queue_cmd(struct adreno_device *adreno_dev, spin_unlock(&drawctxt->lock); - kgsl_pwrctrl_update_l2pc(&adreno_dev->dev); + if (device->pwrctrl.l2pc_update_queue) + kgsl_pwrctrl_update_l2pc(&adreno_dev->dev, + KGSL_L2PC_QUEUE_TIMEOUT); /* Add the context to the dispatcher pending list */ dispatcher_queue_context(adreno_dev, drawctxt); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 4e9a3fe71980..a51cbb02f5bd 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -45,13 +45,6 @@ #define DEFAULT_BUS_P 25 -/* - * The effective duration of qos request in usecs. After - * timeout, qos request is cancelled automatically. - * Kept 80ms default, inline with default GPU idle time. - */ -#define KGSL_L2PC_CPU_TIMEOUT (80 * 1000) - /* Order deeply matters here because reasons. New entries go on the end */ static const char * const clocks[] = { "src_clk", @@ -479,12 +472,14 @@ EXPORT_SYMBOL(kgsl_pwrctrl_set_constraint); /** * kgsl_pwrctrl_update_l2pc() - Update existing qos request * @device: Pointer to the kgsl_device struct + * @timeout_us: the effective duration of qos request in usecs. * * Updates an existing qos request to avoid L2PC on the * CPUs (which are selected through dtsi) on which GPU * thread is running. This would help for performance. */ -void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device) +void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device, + unsigned long timeout_us) { int cpu; @@ -498,7 +493,7 @@ void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device) pm_qos_update_request_timeout( &device->pwrctrl.l2pc_cpus_qos, device->pwrctrl.pm_qos_cpu_mask_latency, - KGSL_L2PC_CPU_TIMEOUT); + timeout_us); } } EXPORT_SYMBOL(kgsl_pwrctrl_update_l2pc); @@ -2171,6 +2166,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) kgsl_property_read_u32(device, "qcom,l2pc-cpu-mask", &pwr->l2pc_cpus_mask); + pwr->l2pc_update_queue = of_property_read_bool( + device->pdev->dev.of_node, + "qcom,l2pc-update-queue"); + pm_runtime_enable(&pdev->dev); ocmem_bus_node = of_find_node_by_name( diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 6a589dac4222..38e554d4f8c5 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -48,6 +48,19 @@ #define KGSL_PWR_DEL_LIMIT 1 #define KGSL_PWR_SET_LIMIT 2 +/* + * The effective duration of qos request in usecs at queue time. + * After timeout, qos request is cancelled automatically. + * Kept 80ms default, inline with default GPU idle time. + */ +#define KGSL_L2PC_QUEUE_TIMEOUT (80 * 1000) + +/* + * The effective duration of qos request in usecs at wakeup time. + * After timeout, qos request is cancelled automatically. + */ +#define KGSL_L2PC_WAKEUP_TIMEOUT (10 * 1000) + enum kgsl_pwrctrl_timer_type { KGSL_PWR_IDLE_TIMER, KGSL_PWR_DEEP_NAP_TIMER, @@ -129,6 +142,7 @@ struct kgsl_regulator { * @irq_name - resource name for the IRQ * @clk_stats - structure of clock statistics * @l2pc_cpus_mask - mask to avoid L2PC on masked CPUs + * @l2pc_update_queue - Boolean flag to avoid L2PC on masked CPUs at queue time * @l2pc_cpus_qos - qos structure to avoid L2PC on CPUs * @pm_qos_req_dma - the power management quality of service structure * @pm_qos_active_latency - allowed CPU latency in microseconds when active @@ -185,6 +199,7 @@ struct kgsl_pwrctrl { const char *irq_name; struct kgsl_clk_stats clk_stats; unsigned int l2pc_cpus_mask; + bool l2pc_update_queue; struct pm_qos_request l2pc_cpus_qos; struct pm_qos_request pm_qos_req_dma; unsigned int pm_qos_active_latency; @@ -253,5 +268,6 @@ int kgsl_active_count_wait(struct kgsl_device *device, int count); void kgsl_pwrctrl_busy_time(struct kgsl_device *device, u64 time, u64 busy); void kgsl_pwrctrl_set_constraint(struct kgsl_device *device, struct kgsl_pwr_constraint *pwrc, uint32_t id); -void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device); +void kgsl_pwrctrl_update_l2pc(struct kgsl_device *device, + unsigned long timeout_us); #endif /* __KGSL_PWRCTRL_H */ -- GitLab From 98c0e129c9f077195cae007a21113d750bd48ebf Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Thu, 28 Sep 2017 16:06:18 +0530 Subject: [PATCH 1059/5498] usb: dwc3-msm: Fix NULL pointer dereference in gsi_ring_db If the obtained GSI door bell address in gsi_ring_db is NULL, it will result in NULL pointer derefernce. Fix this by bailing out from the function if the address is NULL. Also change the dev_dbg to dev_err for more visibility in case of error. CRs-Fixed: 2107200 Change-Id: Idfb71cbdbd0b92d54f14951770e0961ab176ad6f Signed-off-by: Sriharsha Allenki --- drivers/usb/dwc3/dwc3-msm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 08b19abb4595..2d8a37124436 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -948,13 +948,17 @@ static void gsi_ring_db(struct usb_ep *ep, struct usb_gsi_request *request) gsi_dbl_address_lsb = devm_ioremap_nocache(mdwc->dev, dbl_lo_addr, sizeof(u32)); - if (!gsi_dbl_address_lsb) - dev_dbg(mdwc->dev, "Failed to get GSI DBL address LSB\n"); + if (!gsi_dbl_address_lsb) { + dev_err(mdwc->dev, "Failed to get GSI DBL address LSB\n"); + return; + } gsi_dbl_address_msb = devm_ioremap_nocache(mdwc->dev, dbl_hi_addr, sizeof(u32)); - if (!gsi_dbl_address_msb) - dev_dbg(mdwc->dev, "Failed to get GSI DBL address MSB\n"); + if (!gsi_dbl_address_msb) { + dev_err(mdwc->dev, "Failed to get GSI DBL address MSB\n"); + return; + } offset = dwc3_trb_dma_offset(dep, &dep->trb_pool[num_trbs-1]); dev_dbg(mdwc->dev, "Writing link TRB addr:%pKa to %pK (%x) for ep:%s\n", -- GitLab From c84700b157f85ee87292b8bcebe424c6658c3abd Mon Sep 17 00:00:00 2001 From: Zhen Kong Date: Mon, 18 Sep 2017 12:59:23 -0700 Subject: [PATCH 1060/5498] crypto: msm: fix authdata copy issue in qcedev_sha_req_cb qcedev_sha_req_cb() is only called by _sha_complete() during sha operation, and will copy byte_count value from authdata array. This array size is two, and only contains two byte_count value that are used for sha operation. So make change to only copy the first two elements from this array. Change-Id: I535f2ec0e358870a9a2163b3c0bf154b2c8d003f Signed-off-by: Zhen Kong --- drivers/crypto/msm/qcedev.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c index 20bf034bb193..a55f236961b8 100644 --- a/drivers/crypto/msm/qcedev.c +++ b/drivers/crypto/msm/qcedev.c @@ -273,8 +273,6 @@ void qcedev_sha_req_cb(void *cookie, unsigned char *digest, if (authdata) { handle->sha_ctxt.auth_data[0] = auth32[0]; handle->sha_ctxt.auth_data[1] = auth32[1]; - handle->sha_ctxt.auth_data[2] = auth32[2]; - handle->sha_ctxt.auth_data[3] = auth32[3]; } tasklet_schedule(&pdev->done_tasklet); -- GitLab From 10da5a28d0f503130634f9825f3d9713c077709f Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Mon, 25 Sep 2017 08:55:09 -0700 Subject: [PATCH 1061/5498] FROMLIST: binder: fix use-after-free in binder_transaction() (from https://patchwork.kernel.org/patch/9978801/) User-space normally keeps the node alive when creating a transaction since it has a reference to the target. The local strong ref keeps it alive if the sending process dies before the target process processes the transaction. If the source process is malicious or has a reference counting bug, this can fail. In this case, when we attempt to decrement the node in the failure path, the node has already been freed. This is fixed by taking a tmpref on the node while constructing the transaction. To avoid re-acquiring the node lock and inner proc lock to increment the proc's tmpref, a helper is used that does the ref increments on both the node and proc. Bug: 66899329 Change-Id: Iad40e1e0bccee88234900494fb52a510a37fe8d7 Signed-off-by: Todd Kjos --- drivers/staging/android/binder.c | 93 ++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 91a40273c971..9af8df96c93e 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2740,6 +2740,48 @@ static bool binder_proc_transaction(struct binder_transaction *t, return true; } +/** + * binder_get_node_refs_for_txn() - Get required refs on node for txn + * @node: struct binder_node for which to get refs + * @proc: returns @node->proc if valid + * @error: if no @proc then returns BR_DEAD_REPLY + * + * User-space normally keeps the node alive when creating a transaction + * since it has a reference to the target. The local strong ref keeps it + * alive if the sending process dies before the target process processes + * the transaction. If the source process is malicious or has a reference + * counting bug, relying on the local strong ref can fail. + * + * Since user-space can cause the local strong ref to go away, we also take + * a tmpref on the node to ensure it survives while we are constructing + * the transaction. We also need a tmpref on the proc while we are + * constructing the transaction, so we take that here as well. + * + * Return: The target_node with refs taken or NULL if no @node->proc is NULL. + * Also sets @proc if valid. If the @node->proc is NULL indicating that the + * target proc has died, @error is set to BR_DEAD_REPLY + */ +static struct binder_node *binder_get_node_refs_for_txn( + struct binder_node *node, + struct binder_proc **procp, + uint32_t *error) +{ + struct binder_node *target_node = NULL; + + binder_node_inner_lock(node); + if (node->proc) { + target_node = node; + binder_inc_node_nilocked(node, 1, 0, NULL); + binder_inc_node_tmpref_ilocked(node); + node->proc->tmp_ref++; + *procp = node->proc; + } else + *error = BR_DEAD_REPLY; + binder_node_inner_unlock(node); + + return target_node; +} + static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply, @@ -2842,43 +2884,35 @@ static void binder_transaction(struct binder_proc *proc, ref = binder_get_ref_olocked(proc, tr->target.handle, true); if (ref) { - binder_inc_node(ref->node, 1, 0, NULL); - target_node = ref->node; - } - binder_proc_unlock(proc); - if (target_node == NULL) { + target_node = binder_get_node_refs_for_txn( + ref->node, &target_proc, + &return_error); + } else { binder_user_error("%d:%d got transaction to invalid handle\n", - proc->pid, thread->pid); + proc->pid, thread->pid); return_error = BR_FAILED_REPLY; - return_error_param = -EINVAL; - return_error_line = __LINE__; - goto err_invalid_target_handle; } + binder_proc_unlock(proc); } else { mutex_lock(&context->context_mgr_node_lock); target_node = context->binder_context_mgr_node; - if (target_node == NULL) { + if (target_node) + target_node = binder_get_node_refs_for_txn( + target_node, &target_proc, + &return_error); + else return_error = BR_DEAD_REPLY; - mutex_unlock(&context->context_mgr_node_lock); - return_error_line = __LINE__; - goto err_no_context_mgr_node; - } - binder_inc_node(target_node, 1, 0, NULL); mutex_unlock(&context->context_mgr_node_lock); } - e->to_node = target_node->debug_id; - binder_node_lock(target_node); - target_proc = target_node->proc; - if (target_proc == NULL) { - binder_node_unlock(target_node); - return_error = BR_DEAD_REPLY; + if (!target_node) { + /* + * return_error is set above + */ + return_error_param = -EINVAL; return_error_line = __LINE__; goto err_dead_binder; } - binder_inner_proc_lock(target_proc); - target_proc->tmp_ref++; - binder_inner_proc_unlock(target_proc); - binder_node_unlock(target_node); + e->to_node = target_node->debug_id; if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { return_error = BR_FAILED_REPLY; @@ -3237,6 +3271,8 @@ static void binder_transaction(struct binder_proc *proc, if (target_thread) binder_thread_dec_tmpref(target_thread); binder_proc_dec_tmpref(target_proc); + if (target_node) + binder_dec_node_tmpref(target_node); /* * write barrier to synchronize with initialization * of log entry @@ -3256,6 +3292,8 @@ err_bad_parent: err_copy_data_failed: trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, t->buffer, offp); + if (target_node) + binder_dec_node_tmpref(target_node); target_node = NULL; t->buffer->transaction = NULL; binder_alloc_free_buf(&target_proc->alloc, t->buffer); @@ -3270,13 +3308,14 @@ err_bad_call_stack: err_empty_call_stack: err_dead_binder: err_invalid_target_handle: -err_no_context_mgr_node: if (target_thread) binder_thread_dec_tmpref(target_thread); if (target_proc) binder_proc_dec_tmpref(target_proc); - if (target_node) + if (target_node) { binder_dec_node(target_node, 1, 0); + binder_dec_node_tmpref(target_node); + } binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n", -- GitLab From 6c0b7d515b92a9a6ebad95ad9f8917be53cc7620 Mon Sep 17 00:00:00 2001 From: Tharun Kumar Merugu Date: Tue, 3 Oct 2017 12:01:24 +0530 Subject: [PATCH 1062/5498] msm: ADSPRPC: Reduce timeout while opening device When opening new device, wait for pending list to be empty for a maximum of half second instead of the existing 5secs. Change-Id: I2d0a96034b71986484da9581331c3989bc619abc Acked-by: Vishnu Karthik D Signed-off-by: Tharun Kumar Merugu --- drivers/char/adsprpc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 2e36becd2eb4..cb98517c5ee6 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -61,6 +61,7 @@ #define VMID_ADSP_Q6 6 #define RPC_TIMEOUT (5 * HZ) +#define OPEN_TIMEOUT (0.5 * HZ) #define BALIGN 128 #define NUM_CHANNELS 3 /*1 compute 1 cpz 1 mdsp*/ #define NUM_SESSIONS 8 /*8 compute*/ @@ -2010,9 +2011,9 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) if (me->pending_free) { event = wait_event_interruptible_timeout(wait_queue, - !me->pending_free, RPC_TIMEOUT); + !me->pending_free, OPEN_TIMEOUT); if (event == 0) - pr_err("timed out..list is still not empty\n"); + pr_err("fastrpc:timed out..list is still not empty\n"); } VERIFY(err, fl = kzalloc(sizeof(*fl), GFP_KERNEL)); -- GitLab From d91526e840afc14ccbce017a5beb8c0be421e5f1 Mon Sep 17 00:00:00 2001 From: Deepak Kushwah Date: Mon, 18 Sep 2017 14:13:00 +0530 Subject: [PATCH 1063/5498] msm: vidc: Increase minimum input buffer count for VP9 decode Increase minimum input buffer count for VP9 decode to 6,as for some vp9 clips which have superframes with more than 4 subframes require more than 4 reference buffers to decode. Change-Id: I561f4c3ad4c4a94c36293c26aab3a9c9423e9268 Signed-off-by: Deepak Kushwah Signed-off-by: Vasantha Balla --- drivers/media/platform/msm/vidc/msm_vdec.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 801e00123aff..86b7b1b6f6c0 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,6 +21,7 @@ #define MSM_VDEC_DVC_NAME "msm_vdec_8974" #define MIN_NUM_OUTPUT_BUFFERS 4 +#define MIN_NUM_OUTPUT_BUFFERS_VP9 6 #define MIN_NUM_CAPTURE_BUFFERS 6 #define MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS 1 #define MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME @@ -1483,6 +1484,17 @@ static int msm_vdec_queue_setup(struct vb2_queue *q, if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS || *num_buffers > MAX_NUM_OUTPUT_BUFFERS) *num_buffers = MIN_NUM_OUTPUT_BUFFERS; + /* + * Increase input buffer count to 6 as for some + * vp9 clips which have superframes with more + * than 4 subframes requires more than 4 + * reference frames to decode. + */ + if (inst->fmts[OUTPUT_PORT].fourcc == + V4L2_PIX_FMT_VP9 && + *num_buffers < MIN_NUM_OUTPUT_BUFFERS_VP9) + *num_buffers = MIN_NUM_OUTPUT_BUFFERS_VP9; + for (i = 0; i < *num_planes; i++) { sizes[i] = get_frame_size(inst, &inst->fmts[OUTPUT_PORT], q->type, i); -- GitLab From 27cc41c5de9541e76b89d493fcce1a7d2c50e2c0 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 27 Sep 2017 17:18:48 -0700 Subject: [PATCH 1064/5498] ANDROID: add script to fetch android kernel config fragments The Android kernel config fragments now live in a separate repository. To prevent others from having to search for this location, add a script to fetch and unpack the fragments. Update .gitignore to include these fragments. Change-Id: If2d4a59b86e4573b0a9b3190025dfe4191870b46 Signed-off-by: Steve Muckle --- .gitignore | 3 +++ android/configs/android-fetch-configs.sh | 4 ++++ 2 files changed, 7 insertions(+) create mode 100755 android/configs/android-fetch-configs.sh diff --git a/.gitignore b/.gitignore index e213b27f3921..6ed8833731da 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,6 @@ x509.genkey # Kconfig presets all.config + +# fetched Android config fragments +android/configs/android-*.cfg diff --git a/android/configs/android-fetch-configs.sh b/android/configs/android-fetch-configs.sh new file mode 100755 index 000000000000..30c544d6ebfd --- /dev/null +++ b/android/configs/android-fetch-configs.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +curl https://android.googlesource.com/kernel/configs/+archive/master/android-3.18.tar.gz | tar xzv + -- GitLab From dd6d8779c62792be4006106092bec4ca47c8cce5 Mon Sep 17 00:00:00 2001 From: Paras Nagda Date: Tue, 3 Oct 2017 10:59:24 +0530 Subject: [PATCH 1065/5498] Revert "msm: vidc: Disable port reconfig for thumbnail session" This reverts commit 5879c11c7ccc6f98d20902e3e904065104087521. This is needed as there are issues in DPB management in firmware. Change-Id: I24fd37aade2133bad8319ef6e531f12a71b6664a CRs-Fixed: 2118440 Signed-off-by: Paras Nagda --- drivers/media/platform/msm/vidc/msm_vidc_common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index b77ba6c9a108..839036352e75 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -1098,8 +1098,7 @@ static void handle_event_change(enum hal_command_response cmd, void *data) rc = msm_comm_g_ctrl_for_id(inst, V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER); - if ((!IS_ERR_VALUE(rc) && rc == true) || - is_thumbnail_session(inst)) { + if (!IS_ERR_VALUE(rc) && rc == true) { event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT; if (msm_comm_get_stream_output_mode(inst) == -- GitLab From 763f2088a93fa281fd630c8118a06f3cf72954ff Mon Sep 17 00:00:00 2001 From: Divya Ojha Date: Thu, 25 Aug 2016 15:25:51 +0530 Subject: [PATCH 1066/5498] ARM: dts: msm: update micbias type for 8937/53/17 CDP 8937/53/17 CDP uses external bias resistor. Hence change micbias type to external. CRs-fixed: 1045680 Change-Id: I7ca26e9b37e5730e8c8b7b1640fb7c55ad2fe532 Signed-off-by: Divya Ojha --- arch/arm/boot/dts/qcom/msm8917-audio-cdp.dtsi | 2 +- arch/arm/boot/dts/qcom/msm8937-audio-cdp.dtsi | 2 +- arch/arm/boot/dts/qcom/msm8953-audio-cdp.dtsi | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8917-audio-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8917-audio-cdp.dtsi index a0de690d101c..93a14bb12ad6 100644 --- a/arch/arm/boot/dts/qcom/msm8917-audio-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8917-audio-cdp.dtsi @@ -13,7 +13,7 @@ &int_codec { status = "okay"; - qcom,msm-hs-micbias-type = "internal"; + qcom,msm-hs-micbias-type = "external"; asoc-wsa-codec-names = "wsa881x-i2c-codec.2-000f"; asoc-wsa-codec-prefixes = "SpkrMono"; diff --git a/arch/arm/boot/dts/qcom/msm8937-audio-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8937-audio-cdp.dtsi index c181a81d7741..b7e57b560a2a 100644 --- a/arch/arm/boot/dts/qcom/msm8937-audio-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8937-audio-cdp.dtsi @@ -13,7 +13,7 @@ &int_codec { status = "okay"; - qcom,msm-hs-micbias-type = "internal"; + qcom,msm-hs-micbias-type = "external"; asoc-wsa-codec-names = "wsa881x-i2c-codec.2-000f"; asoc-wsa-codec-prefixes = "SpkrMono"; diff --git a/arch/arm/boot/dts/qcom/msm8953-audio-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8953-audio-cdp.dtsi index 91eb8c6e2bfe..ff4669e4e46f 100644 --- a/arch/arm/boot/dts/qcom/msm8953-audio-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8953-audio-cdp.dtsi @@ -13,7 +13,7 @@ &int_codec { status = "okay"; - qcom,msm-hs-micbias-type = "internal"; + qcom,msm-hs-micbias-type = "external"; }; &pm8953_diangu_dig { -- GitLab From 03d75a3f37660e639eda0dc1ee179861574a0c62 Mon Sep 17 00:00:00 2001 From: Raghavendra Ambadas Date: Tue, 19 Sep 2017 11:18:27 +0530 Subject: [PATCH 1067/5498] mdss: mdp: Add downscale factor for rotator Newer chipsets supports rotator downscale upto 1/64. Add rotator downscale min and max value in MDP caps, so that SDM can read the value from mdp caps and perform downscale. SDM assumes rotator downscale max as 64, if driver does not populate via mdp caps. Change-Id: I9cc2ffc8a11e9da208e9b7bc67c09b3f57bc7a63 Signed-off-by: Raghavendra Ambadas --- .../devicetree/bindings/fb/mdss-mdp.txt | 6 ++++++ drivers/video/msm/mdss/mdss.h | 2 ++ drivers/video/msm/mdss/mdss_mdp.c | 21 +++++++++++++++++++ drivers/video/msm/mdss/mdss_rotator.c | 8 +++++-- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt index e63b458b878e..1a30747f1036 100644 --- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt @@ -286,6 +286,10 @@ Optional properties: feature is available or not. - qcom,mdss-has-rotator-downscale: Boolean property to indicate if rotator downscale feature is available or not. +- qcom,mdss-rot-downscale-min: This integer value indicates the Minimum + downscale factor supported by rotator. +- qcom,mdss-rot-downscale-max: This integer value indicates the Maximum + downscale factor supported by rotator. - qcom,mdss-ad-off: Array of offset addresses for the available Assertive Display (AD) blocks. These offsets are calculated from the register "mdp_phys" @@ -744,6 +748,8 @@ Example: qcom,mdss-no-hist-vote; qcom,mdss-traffic-shaper-enabled; qcom,mdss-has-rotator-downscale; + qcom,mdss-rot-downscale-min = <2>; + qcom,mdss-rot-downscale-max = <16>; qcom,mdss-has-pingpong-split; qcom,mdss-pipe-vig-xin-id = <0 4 8>; diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h index 6846e68fc6eb..16b4fda2458a 100644 --- a/drivers/video/msm/mdss/mdss.h +++ b/drivers/video/msm/mdss/mdss.h @@ -387,6 +387,8 @@ struct mdss_data_type { u32 *vbif_rt_qos; u32 *vbif_nrt_qos; u32 npriority_lvl; + u32 rot_dwnscale_min; + u32 rot_dwnscale_max; struct mult_factor ab_factor; struct mult_factor ib_factor; diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c index c1f9d3f3afd8..e92c2a310d5d 100644 --- a/drivers/video/msm/mdss/mdss_mdp.c +++ b/drivers/video/msm/mdss/mdss_mdp.c @@ -2382,6 +2382,14 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev, if (mdata->clk_factor.numer) SPRINT("clk_fudge_factor=%u,%u\n", mdata->clk_factor.numer, mdata->clk_factor.denom); + if (mdata->has_rot_dwnscale) { + if (mdata->rot_dwnscale_min) + SPRINT("rot_dwnscale_min=%u\n", + mdata->rot_dwnscale_min); + if (mdata->rot_dwnscale_max) + SPRINT("rot_dwnscale_max=%u\n", + mdata->rot_dwnscale_max); + } SPRINT("features="); if (mdata->has_bwc) SPRINT(" bwc"); @@ -4146,6 +4154,19 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) "qcom,mdss-traffic-shaper-enabled"); mdata->has_rot_dwnscale = of_property_read_bool(pdev->dev.of_node, "qcom,mdss-has-rotator-downscale"); + if (mdata->has_rot_dwnscale) { + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mdss-rot-downscale-min", + &mdata->rot_dwnscale_min); + if (rc) + pr_err("Min rotator downscale property not specified\n"); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mdss-rot-downscale-max", + &mdata->rot_dwnscale_max); + if (rc) + pr_err("Max rotator downscale property not specified\n"); + } rc = of_property_read_u32(pdev->dev.of_node, "qcom,mdss-dram-channels", &mdata->bus_channels); diff --git a/drivers/video/msm/mdss/mdss_rotator.c b/drivers/video/msm/mdss/mdss_rotator.c index 25d56e392842..419b66f8fe99 100644 --- a/drivers/video/msm/mdss/mdss_rotator.c +++ b/drivers/video/msm/mdss/mdss_rotator.c @@ -1199,13 +1199,17 @@ static int mdss_rotator_config_dnsc_factor(struct mdss_rot_mgr *mgr, } entry->dnsc_factor_w = src_w / dst_w; bit = fls(entry->dnsc_factor_w); - if ((entry->dnsc_factor_w & ~BIT(bit - 1)) || (bit > 5)) { + /* + * New Chipsets supports downscale upto 1/64 + * change the Bit check from 5 to 7 to support 1/64 down scale + */ + if ((entry->dnsc_factor_w & ~BIT(bit - 1)) || (bit > 7)) { ret = -EINVAL; goto dnsc_err; } entry->dnsc_factor_h = src_h / dst_h; bit = fls(entry->dnsc_factor_h); - if ((entry->dnsc_factor_h & ~BIT(bit - 1)) || (bit > 5)) { + if ((entry->dnsc_factor_h & ~BIT(bit - 1)) || (bit > 7)) { ret = -EINVAL; goto dnsc_err; } -- GitLab From 202946609f29049bacd5b9314239de82f3717847 Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Tue, 3 Oct 2017 17:16:55 +0530 Subject: [PATCH 1068/5498] ARM: dts: msm: Add rotator downscale factor for MSM8996 Newer chipsets supports rotator downscale upto 1/64. Add rotator downscale min and max value in dts file for MSM8937/53/8996 devices. Change-Id: I61c6846e3d41a0db9d49bfbe005d7eb8876849f9 Signed-off-by: Raghavendra Ambadas --- arch/arm/boot/dts/qcom/msm8937-mdss.dtsi | 4 +++- arch/arm/boot/dts/qcom/msm8953-mdss.dtsi | 2 ++ arch/arm/boot/dts/qcom/msm8996-mdss.dtsi | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8937-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8937-mdss.dtsi index 8426ebb72fc6..fe44b2009755 100644 --- a/arch/arm/boot/dts/qcom/msm8937-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8937-mdss.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -90,6 +90,8 @@ qcom,mdss-has-decimation; qcom,mdss-has-non-scalar-rgb; qcom,mdss-has-rotator-downscale; + qcom,mdss-rot-downscale-min = <2>; + qcom,mdss-rot-downscale-max = <16>; qcom,mdss-idle-power-collapse-enabled; qcom,mdss-rot-block-size = <64>; diff --git a/arch/arm/boot/dts/qcom/msm8953-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8953-mdss.dtsi index 3770b53243a3..2131dc35bc3f 100644 --- a/arch/arm/boot/dts/qcom/msm8953-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8953-mdss.dtsi @@ -89,6 +89,8 @@ qcom,mdss-has-decimation; qcom,mdss-has-non-scalar-rgb; qcom,mdss-has-rotator-downscale; + qcom,mdss-rot-downscale-min = <2>; + qcom,mdss-rot-downscale-max = <16>; qcom,mdss-idle-power-collapse-enabled; qcom,mdss-rot-block-size = <64>; qcom,mdss-ppb-off = <0x00000330>; diff --git a/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi index d77b7c0be370..ebddd6d12793 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -112,6 +112,8 @@ qcom,mdss-highest-bank-bit = <0x2>; qcom,mdss-has-decimation; qcom,mdss-has-rotator-downscale; + qcom,mdss-rot-downscale-min = <2>; + qcom,mdss-rot-downscale-max = <16>; qcom,mdss-idle-power-collapse-enabled; clocks = <&clock_mmss clk_mdss_ahb_clk>, <&clock_mmss clk_mdss_axi_clk>, -- GitLab From 8f8388259ccd972d206137507798d7a40d52f1c2 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Thu, 28 Sep 2017 16:14:52 +0530 Subject: [PATCH 1069/5498] ARM: dts: msm: Disable GPU mempool support for 8909w Wearables are low-memory devices. Reserving memory for GPU mempools on such targets often leads to low-memory condition. So disable GPU mempool on 8909 wearable. Change-Id: I8e4103a77e1a211f7ad0b8a561033645b004c8a9 Signed-off-by: Sunil Khatri --- arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi index 6fb509315362..d1e821053ece 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi @@ -34,6 +34,7 @@ <26 512 0 1536000>, <26 512 0 3070000>; + /delete-node/qcom,gpu-mempools; /delete-node/qcom,gpu-pwrlevels; /* Power levels */ -- GitLab From 422321ca28c3d094947085cbccea155986171dca Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 31 Aug 2017 12:59:47 +0530 Subject: [PATCH 1070/5498] ARM: dts: msm: Enable pmu counters on mdm9x50 Add device tree info to enable pmu counters on mdm9x50. Change-Id: Ic99a973d304630c012e0e72556b181840240e61d Signed-off-by: Mohammed Javid --- arch/arm/boot/dts/qcom/mdm9650.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/boot/dts/qcom/mdm9650.dtsi b/arch/arm/boot/dts/qcom/mdm9650.dtsi index 9da8220dfe35..97c875a1b541 100644 --- a/arch/arm/boot/dts/qcom/mdm9650.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650.dtsi @@ -1358,6 +1358,12 @@ compatible = "qcom,msm-pcm-dtmf"; }; + cpu-pmu { + compatible = "arm,cortex-a7-pmu"; + qcom,irq-is-percpu; + interrupts = <1 8 0x100>; + }; + dai_pri_auxpcm: qcom,msm-pri-auxpcm { compatible = "qcom,msm-auxpcm-dev"; qcom,msm-cpudai-auxpcm-mode = <0>, <0>; -- GitLab From fcbebbaeb24a96e7347ad45a7988bf07c0cca8ab Mon Sep 17 00:00:00 2001 From: Sayali Lokhande Date: Wed, 4 Oct 2017 15:42:18 +0530 Subject: [PATCH 1071/5498] mmc: block: Return error in case of partition switch failure If some error occurs in mmc_blk_cmdq_part_switch() function, the error is not being passed to higher layers to inform the failure. Instead it returns zero even though no request is completed with success. This change returns error to higher layers in case of any failures in mmc_blk_cmdq_part_switch. Change-Id: Icd1d2d9bc6bb5bd16bc24706fb20603cc33cee40 Signed-off-by: Sayali Lokhande --- drivers/mmc/card/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 630ed50a8b3f..e74ee9da2ef8 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -3772,7 +3772,7 @@ static int mmc_blk_cmdq_issue_rq(struct mmc_queue *mq, struct request *req) } else { pr_err("%s: %s: partition switch failed err = %d\n", md->disk->disk_name, __func__, err); - ret = 0; + ret = err; goto out; } } -- GitLab From ec9eeedaaddc68b068f3aa1a6ba2b6dea9bfffc8 Mon Sep 17 00:00:00 2001 From: ramandeep trehan Date: Wed, 4 Oct 2017 18:12:11 +0530 Subject: [PATCH 1072/5498] crypto: ice: Sanitize the ice device return address. Even if ICE device is not found it is possible for list device API to return non NULL pointer which will pass all NULL checks in code. Ensure to return proper address or NULL. Change-Id: Id36bad92fc3c527bdea0b3307210b01cab7197af Signed-off-by: ramandeep trehan --- drivers/crypto/msm/ice.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c index b5596464572b..22027d3baf5e 100644 --- a/drivers/crypto/msm/ice.c +++ b/drivers/crypto/msm/ice.c @@ -833,7 +833,7 @@ static int qcom_ice_restore_config(void) static int qcom_ice_init_clocks(struct ice_device *ice) { int ret = -EINVAL; - struct ice_clk_info *clki; + struct ice_clk_info *clki = NULL; struct device *dev = ice->pdev; struct list_head *head = &ice->clk_list_head; @@ -877,7 +877,7 @@ out: static int qcom_ice_enable_clocks(struct ice_device *ice, bool enable) { int ret = 0; - struct ice_clk_info *clki; + struct ice_clk_info *clki = NULL; struct device *dev = ice->pdev; struct list_head *head = &ice->clk_list_head; @@ -1530,12 +1530,13 @@ struct platform_device *qcom_ice_get_pdevice(struct device_node *node) if (ice_dev->pdev->of_node == node) { pr_info("%s: found ice device %pK\n", __func__, ice_dev); + ice_pdev = to_platform_device(ice_dev->pdev); break; } } - ice_pdev = to_platform_device(ice_dev->pdev); - pr_info("%s: matching platform device %pK\n", __func__, ice_pdev); + if(ice_pdev) + pr_info("%s: matching platform device %pK\n", __func__, ice_pdev); out: return ice_pdev; } @@ -1554,11 +1555,11 @@ static struct ice_device *get_ice_device_from_storage_type list_for_each_entry(ice_dev, &ice_devices, list) { if (!strcmp(ice_dev->ice_instance_type, storage_type)) { pr_info("%s: found ice device %p\n", __func__, ice_dev); - break; + return ice_dev; } } out: - return ice_dev; + return NULL; } static int enable_ice_setup(struct ice_device *ice_dev) -- GitLab From 9969206de5aa6b342956d16d5a05063e8b76ac25 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Thu, 21 Sep 2017 14:51:12 -0600 Subject: [PATCH 1073/5498] netfilter: xt_socket: Restore mark from full sockets only An out of bounds error was detected on an ARM64 target with Android based kernel 4.9. This occurs while trying to restore mark on a skb from an inet request socket. BUG: KASAN: slab-out-of-bounds in socket_match.isra.2+0xc8/0x1f0 net/netfilter/xt_socket.c:248 Read of size 4 at addr ffffffc06a8d824c by task syz-fuzzer/1532 CPU: 7 PID: 1532 Comm: syz-fuzzer Tainted: G W O 4.9.41+ #1 Call trace: [] dump_backtrace+0x0/0x440 arch/arm64/kernel/traps.c:76 [] show_stack+0x28/0x38 arch/arm64/kernel/traps.c:226 [] __dump_stack lib/dump_stack.c:15 [inline] [] dump_stack+0xe4/0x134 lib/dump_stack.c:51 [] print_address_description+0x68/0x258 mm/kasan/report.c:248 [] kasan_report_error mm/kasan/report.c:347 [inline] [] kasan_report.part.2+0x228/0x2f0 mm/kasan/report.c:371 [] kasan_report+0x5c/0x70 mm/kasan/report.c:372 [] check_memory_region_inline mm/kasan/kasan.c:308 [inline] [] __asan_load4+0x88/0xa0 mm/kasan/kasan.c:740 [] socket_match.isra.2+0xc8/0x1f0 net/netfilter/xt_socket.c:248 [] socket_mt4_v1_v2_v3+0x3c/0x48 net/netfilter/xt_socket.c:272 [] ipt_do_table+0x54c/0xad8 net/ipv4/netfilter/ip_tables.c:311 [] iptable_mangle_hook+0x6c/0x220 net/ipv4/netfilter/iptable_mangle.c:90 ... Allocated by task 1532: save_stack_trace_tsk+0x0/0x2a0 arch/arm64/kernel/stacktrace.c:131 save_stack_trace+0x28/0x38 arch/arm64/kernel/stacktrace.c:215 save_stack mm/kasan/kasan.c:495 [inline] set_track mm/kasan/kasan.c:507 [inline] kasan_kmalloc+0xd8/0x188 mm/kasan/kasan.c:599 kasan_slab_alloc+0x14/0x20 mm/kasan/kasan.c:537 slab_post_alloc_hook mm/slab.h:417 [inline] slab_alloc_node mm/slub.c:2728 [inline] slab_alloc mm/slub.c:2736 [inline] kmem_cache_alloc+0x14c/0x2e8 mm/slub.c:2741 reqsk_alloc include/net/request_sock.h:87 [inline] inet_reqsk_alloc+0x4c/0x238 net/ipv4/tcp_input.c:6236 tcp_conn_request+0x2b0/0xea8 net/ipv4/tcp_input.c:6341 tcp_v4_conn_request+0xe0/0x100 net/ipv4/tcp_ipv4.c:1256 tcp_rcv_state_process+0x384/0x18a8 net/ipv4/tcp_input.c:5926 tcp_v4_do_rcv+0x2f0/0x3e0 net/ipv4/tcp_ipv4.c:1430 tcp_v4_rcv+0x1278/0x1350 net/ipv4/tcp_ipv4.c:1709 ip_local_deliver_finish+0x174/0x3e0 net/ipv4/ip_input.c:216 CRs-Fixed: 2113103 Change-Id: I1e81238aa2b8abe768d460674978f554b6c3ca48 Signed-off-by: Subash Abhinov Kasiviswanathan --- net/netfilter/xt_socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 6cf2fd0871eb..a1fad8403a9a 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -229,7 +229,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, inet_twsk(sk)->tw_transparent)); if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && - transparent) + transparent && sk_fullsock(sk)) pskb->mark = sk->sk_mark; sock_gen_put(sk); @@ -414,7 +414,7 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) inet_twsk(sk)->tw_transparent)); if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && - transparent) + transparent && sk_fullsock(sk)) pskb->mark = sk->sk_mark; if (sk != skb->sk) -- GitLab From 164d9de3064c5378c7c601d85c91af9bbf9fb7af Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Fri, 22 Sep 2017 15:30:54 +0530 Subject: [PATCH 1074/5498] msm: ipa: Fix print unwanted debug logs Check of valid clients before getting rm resources and suspending. Change-Id: Ifa9a37606146945fb499d83e1e4b44e42c68c831 Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 52 +++++++++++++++------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 4d35255492b2..e4652e3794a0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -1263,38 +1263,46 @@ int ipa3_get_clients_from_rm_resource( clients->names[i++] = IPA_CLIENT_USB_DPL_CONS; break; case IPA_RM_RESOURCE_HSIC_CONS: - clients->names[i++] = IPA_CLIENT_HSIC1_CONS; + if (ipa3_get_ep_mapping(IPA_CLIENT_HSIC1_CONS) != -1) + clients->names[i++] = IPA_CLIENT_HSIC1_CONS; break; case IPA_RM_RESOURCE_WLAN_CONS: clients->names[i++] = IPA_CLIENT_WLAN1_CONS; clients->names[i++] = IPA_CLIENT_WLAN2_CONS; clients->names[i++] = IPA_CLIENT_WLAN3_CONS; - clients->names[i++] = IPA_CLIENT_WLAN4_CONS; + if (ipa3_get_ep_mapping(IPA_CLIENT_WLAN4_CONS) != -1) + clients->names[i++] = IPA_CLIENT_WLAN4_CONS; break; case IPA_RM_RESOURCE_MHI_CONS: - clients->names[i++] = IPA_CLIENT_MHI_CONS; + if (ipa3_get_ep_mapping(IPA_CLIENT_MHI_CONS) != -1) + clients->names[i++] = IPA_CLIENT_MHI_CONS; break; case IPA_RM_RESOURCE_ODU_ADAPT_CONS: clients->names[i++] = IPA_CLIENT_ODU_EMB_CONS; - clients->names[i++] = IPA_CLIENT_ODU_TETH_CONS; + if (ipa3_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS) != -1) + clients->names[i++] = IPA_CLIENT_ODU_TETH_CONS; break; case IPA_RM_RESOURCE_ETHERNET_CONS: - clients->names[i++] = IPA_CLIENT_ETHERNET_CONS; + if (ipa3_get_ep_mapping(IPA_CLIENT_ETHERNET_CONS) != -1) + clients->names[i++] = IPA_CLIENT_ETHERNET_CONS; break; case IPA_RM_RESOURCE_USB_PROD: clients->names[i++] = IPA_CLIENT_USB_PROD; break; case IPA_RM_RESOURCE_HSIC_PROD: - clients->names[i++] = IPA_CLIENT_HSIC1_PROD; + if (ipa3_get_ep_mapping(IPA_CLIENT_HSIC1_PROD) != -1) + clients->names[i++] = IPA_CLIENT_HSIC1_PROD; break; case IPA_RM_RESOURCE_MHI_PROD: - clients->names[i++] = IPA_CLIENT_MHI_PROD; + if (ipa3_get_ep_mapping(IPA_CLIENT_MHI_PROD) != -1) + clients->names[i++] = IPA_CLIENT_MHI_PROD; break; case IPA_RM_RESOURCE_ODU_ADAPT_PROD: clients->names[i++] = IPA_CLIENT_ODU_PROD; break; case IPA_RM_RESOURCE_ETHERNET_PROD: - clients->names[i++] = IPA_CLIENT_ETHERNET_PROD; + if (ipa3_get_ep_mapping(IPA_CLIENT_ETHERNET_PROD) != -1) + clients->names[i++] = IPA_CLIENT_ETHERNET_PROD; break; default: break; @@ -1327,17 +1335,33 @@ bool ipa3_should_pipe_be_suspended(enum ipa_client_type client) if (ep->keep_ipa_awake) return false; + if (client == IPA_CLIENT_MHI_CONS && + (ipa3_get_ep_mapping(IPA_CLIENT_MHI_CONS) != -1)) + return true; + + if (client == IPA_CLIENT_HSIC1_CONS && + (ipa3_get_ep_mapping(IPA_CLIENT_HSIC1_CONS) != -1)) + return true; + + if (client == IPA_CLIENT_WLAN4_CONS && + (ipa3_get_ep_mapping(IPA_CLIENT_WLAN4_CONS) != -1)) + return true; + + if (client == IPA_CLIENT_ODU_TETH_CONS && + (ipa3_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS) != -1)) + return true; + + if (client == IPA_CLIENT_ETHERNET_CONS && + (ipa3_get_ep_mapping(IPA_CLIENT_ETHERNET_CONS) != -1)) + return true; + + if (client == IPA_CLIENT_USB_CONS || client == IPA_CLIENT_USB_DPL_CONS || - client == IPA_CLIENT_MHI_CONS || - client == IPA_CLIENT_HSIC1_CONS || client == IPA_CLIENT_WLAN1_CONS || client == IPA_CLIENT_WLAN2_CONS || client == IPA_CLIENT_WLAN3_CONS || - client == IPA_CLIENT_WLAN4_CONS || - client == IPA_CLIENT_ODU_EMB_CONS || - client == IPA_CLIENT_ODU_TETH_CONS || - client == IPA_CLIENT_ETHERNET_CONS) + client == IPA_CLIENT_ODU_EMB_CONS) return true; return false; -- GitLab From 454e55becb8e7d54abe8c96a8bb016401e732c83 Mon Sep 17 00:00:00 2001 From: Alok Chauhan Date: Sat, 30 Sep 2017 00:25:03 +0530 Subject: [PATCH 1075/5498] msm: emac: Fix crash during unloading of emac module Due to wrong ephy id check the phy connect/disconnect sequence is going out of sync and causing crash while unloading of emac driver module. Lower MDIO clock rate also causes emac to not communicate properly. Add the correct ephy id check and change MDIO clock rate. Change-Id: If72addc91a6e5ecba1e697338fe76047976e8b38 Signed-off-by: Alok Chauhan --- drivers/net/ethernet/qualcomm/emac/emac_hw.h | 4 +--- drivers/net/ethernet/qualcomm/emac/emac_main.c | 14 ++++++++++---- drivers/net/ethernet/qualcomm/emac/emac_phy.c | 4 ++-- include/linux/qca8337.h | 2 ++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/emac/emac_hw.h b/drivers/net/ethernet/qualcomm/emac/emac_hw.h index b1bd4d686a1b..bcef41d2bfc4 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac_hw.h +++ b/drivers/net/ethernet/qualcomm/emac/emac_hw.h @@ -108,9 +108,7 @@ bool emac_hw_read_tx_tstamp(struct emac_hw *hw, struct emac_hwtxtstamp *ts); #define DMAR_DLY_CNT_DEF 15 #define DMAW_DLY_CNT_DEF 4 -#define MDIO_CLK_25_8 3 -#define MDIO_CLK_25_10 4 -#define MDIO_CLK_25_28 7 +#define MDIO_CLK_25_4 0 #define RXQ0_RSS_HSTYP_IPV6_TCP_EN 0x20 #define RXQ0_RSS_HSTYP_IPV6_EN 0x10 diff --git a/drivers/net/ethernet/qualcomm/emac/emac_main.c b/drivers/net/ethernet/qualcomm/emac/emac_main.c index 3f9844dd1aba..c077330919d8 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac_main.c +++ b/drivers/net/ethernet/qualcomm/emac/emac_main.c @@ -1991,8 +1991,10 @@ void emac_mac_down(struct emac_adapter *adpt, u32 ctrl) if (adpt->irq[i].irq) free_irq(adpt->irq[i].irq, &adpt->irq[i]); - if ((ATH8030_PHY_ID == adpt->phydev->phy_id) && - (adpt->phy.is_ext_phy_connect)) { + if (((ATH8030_PHY_ID == adpt->phydev->phy_id) || + (ATH8031_PHY_ID == adpt->phydev->phy_id) || + (ATH8035_PHY_ID == adpt->phydev->phy_id)) && + (adpt->phy.is_ext_phy_connect)) { phy_disconnect(adpt->phydev); adpt->phy.is_ext_phy_connect = 0; } @@ -3212,10 +3214,14 @@ err_undo_napi: err_init_mdio_gpio: adpt->gpio_off(adpt, true, true); err_clk_init: - if (ATH8030_PHY_ID == adpt->phydev->phy_id) + if ((ATH8030_PHY_ID == adpt->phydev->phy_id) || + (ATH8031_PHY_ID == adpt->phydev->phy_id) || + (ATH8035_PHY_ID == adpt->phydev->phy_id)) emac_disable_clks(adpt); err_ldo_init: - if (ATH8030_PHY_ID == adpt->phydev->phy_id) + if ((ATH8030_PHY_ID == adpt->phydev->phy_id) || + (ATH8031_PHY_ID == adpt->phydev->phy_id) || + (ATH8035_PHY_ID == adpt->phydev->phy_id)) emac_disable_regulator(adpt, EMAC_VREG1, EMAC_VREG5); err_get_resource: free_netdev(netdev); diff --git a/drivers/net/ethernet/qualcomm/emac/emac_phy.c b/drivers/net/ethernet/qualcomm/emac/emac_phy.c index 402c949daf50..e831b744613b 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac_phy.c +++ b/drivers/net/ethernet/qualcomm/emac/emac_phy.c @@ -100,7 +100,7 @@ static int emac_mdio_read(struct mii_bus *bus, int addr, int regnum) reg = reg & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK | MDIO_MODE | MDIO_PR); reg = SUP_PREAMBLE | - ((MDIO_CLK_25_10 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | + ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | MDIO_START | MDIO_RD_NWR; @@ -156,7 +156,7 @@ static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) reg = reg & ~(MDIO_REG_ADDR_BMSK | MDIO_CLK_SEL_BMSK | MDIO_DATA_BMSK | MDIO_MODE | MDIO_PR); reg = SUP_PREAMBLE | - ((MDIO_CLK_25_10 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | + ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | ((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) | MDIO_START; diff --git a/include/linux/qca8337.h b/include/linux/qca8337.h index 7b6bdf143278..b7c7288a6092 100644 --- a/include/linux/qca8337.h +++ b/include/linux/qca8337.h @@ -17,6 +17,8 @@ #define QCA8337_PHY_ID 0x004dd036 #define ATH8030_PHY_ID 0x004dd076 +#define ATH8031_PHY_ID 0x004dd074 +#define ATH8035_PHY_ID 0x004dd072 #define QCA8337_ID_QCA8337 0x13 #define QCA8337_NUM_PORTS 7 /* Make sure that port0 is the cpu port */ -- GitLab From 408be5eb18b30851b0087b0f8fe8258811b20f17 Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Tue, 5 Sep 2017 17:43:35 +0530 Subject: [PATCH 1076/5498] soc: qcom: Reorganize PIL code for reclaiming MSS mem during SSR This change ask hypervisor to remove memory mapping for MSS from IOMMU second stage table and assign the ownership back to HLOS just after MBA is booted. Presently this is being done only after MBA is booted and MDT is authenticated. Change-Id: I724c1bcc664827e666612dd34cd078f3f044498a Signed-off-by: Avaneesh Kumar Dwivedi --- drivers/soc/qcom/peripheral-loader.c | 15 +++------------ drivers/soc/qcom/peripheral-loader.h | 2 +- drivers/soc/qcom/pil-msa.c | 21 ++++++++++++++++++--- drivers/soc/qcom/subsys-pil-bg.c | 3 ++- drivers/soc/qcom/subsys-pil-tz.c | 3 ++- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index f239a43c0264..d0fdce05adc5 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -835,7 +835,9 @@ int pil_boot(struct pil_desc *desc) } if (desc->ops->init_image) - ret = desc->ops->init_image(desc, fw->data, fw->size); + ret = desc->ops->init_image(desc, fw->data, fw->size, + priv->region_start, + priv->region_end - priv->region_start); if (ret) { pil_err(desc, "Invalid firmware metadata\n"); subsys_set_error(desc->subsys_dev, firmware_error_msg); @@ -851,17 +853,6 @@ int pil_boot(struct pil_desc *desc) } if (desc->subsys_vmid > 0) { - /* In case of modem ssr, we need to assign memory back to linux. - * This is not true after cold boot since linux already owns it. - * Also for secure boot devices, modem memory has to be released - * after MBA is booted. */ - if (desc->modem_ssr) { - ret = pil_assign_mem_to_linux(desc, priv->region_start, - (priv->region_end - priv->region_start)); - if (ret) - pil_err(desc, "Failed to assign to linux, ret- %d\n", - ret); - } ret = pil_assign_mem_to_subsys_and_linux(desc, priv->region_start, (priv->region_end - priv->region_start)); diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h index 40aff8ce7e8c..fdc21342e64e 100644 --- a/drivers/soc/qcom/peripheral-loader.h +++ b/drivers/soc/qcom/peripheral-loader.h @@ -88,7 +88,7 @@ struct pil_image_info { */ struct pil_reset_ops { int (*init_image)(struct pil_desc *pil, const u8 *metadata, - size_t size); + size_t size, phys_addr_t addr, size_t sz); int (*mem_setup)(struct pil_desc *pil, phys_addr_t addr, size_t size); int (*verify_blob)(struct pil_desc *pil, phys_addr_t phy_addr, size_t size); diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c index f09387c3d5ab..0e45efb903f1 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.c @@ -651,7 +651,7 @@ err_invalid_fw: } static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata, - size_t size) + size_t size, phys_addr_t phy_addr, size_t phy_sz) { struct modem_data *drv = dev_get_drvdata(pil->dev); void *mdata_virt; @@ -680,6 +680,19 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata, wmb(); if (pil->subsys_vmid > 0) { + /** + * In case of modem ssr, we need to assign memory back to linux. + * This is not true after cold boot since linux already owns + * it. Also for secure boot devices, modem memory has to be + * released after MBA is booted + */ + if (pil->modem_ssr) { + ret = pil_assign_mem_to_linux(pil, phy_addr, phy_sz); + if (ret) + dev_err(pil->dev, + "Failed to assign to linux, ret- %d\n", + ret); + } ret = pil_assign_mem_to_subsys(pil, mdata_phys, ALIGN(size, SZ_4K)); if (ret) { @@ -732,7 +745,8 @@ fail: } static int pil_msa_mss_reset_mba_load_auth_mdt(struct pil_desc *pil, - const u8 *metadata, size_t size) + const u8 *metadata, size_t size, + phys_addr_t modem_reg, size_t sz_modem_reg) { int ret; @@ -740,7 +754,8 @@ static int pil_msa_mss_reset_mba_load_auth_mdt(struct pil_desc *pil, if (ret) return ret; - return pil_msa_auth_modem_mdt(pil, metadata, size); + return pil_msa_auth_modem_mdt(pil, metadata, size, + modem_reg, sz_modem_reg); } static int pil_msa_mba_verify_blob(struct pil_desc *pil, phys_addr_t phy_addr, diff --git a/drivers/soc/qcom/subsys-pil-bg.c b/drivers/soc/qcom/subsys-pil-bg.c index 30505b790dc9..b4e49eb3d85f 100644 --- a/drivers/soc/qcom/subsys-pil-bg.c +++ b/drivers/soc/qcom/subsys-pil-bg.c @@ -306,7 +306,8 @@ static int bg_shutdown(const struct subsys_desc *subsys, bool force_stop) * Return: 0 on success. Error code on failure. */ static int bg_auth_metadata(struct pil_desc *pil, - const u8 *metadata, size_t size) + const u8 *metadata, size_t size, + phys_addr_t addr, size_t sz) { struct pil_bg_data *bg_data = desc_to_data(pil); struct tzapp_bg_req bg_tz_req; diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index c8d615049487..9c82f7a010de 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -582,7 +582,8 @@ static void pil_remove_proxy_vote(struct pil_desc *pil) } static int pil_init_image_trusted(struct pil_desc *pil, - const u8 *metadata, size_t size) + const u8 *metadata, size_t size, + phys_addr_t addr, size_t sz) { struct pil_tz_data *d = desc_to_data(pil); struct pas_init_image_req { -- GitLab From 52c95464e39698681d1e61232df537b1cf8a3f6d Mon Sep 17 00:00:00 2001 From: Amit Nischal Date: Tue, 3 Oct 2017 11:40:26 +0530 Subject: [PATCH 1077/5498] clk: msm: gcc: Add parent for gcc_oxili_timer_clk For gcc_oxili_timer_clk clock, rate setting request of 19.2 MHz is getting failed from KGSL driver so fixing the same by adding the XO as a parent. Change-Id: I2444e4df80336ca42c525e149fd96c18cfe43630 Signed-off-by: Amit Nischal --- drivers/clk/msm/clock-gcc-8953.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/msm/clock-gcc-8953.c b/drivers/clk/msm/clock-gcc-8953.c index 64f409bda771..3cfdafd05b07 100644 --- a/drivers/clk/msm/clock-gcc-8953.c +++ b/drivers/clk/msm/clock-gcc-8953.c @@ -2785,6 +2785,7 @@ static struct branch_clk gcc_oxili_timer_clk = { .base = &virt_bases[GFX_BASE], .c = { .dbg_name = "gcc_oxili_timer_clk", + .parent = &xo_clk_src.c, .ops = &clk_ops_branch, CLK_INIT(gcc_oxili_timer_clk.c), }, -- GitLab From 0c317759e7805e8a615e076186108754093e6d68 Mon Sep 17 00:00:00 2001 From: Shu Wang Date: Thu, 7 Sep 2017 16:03:27 +0800 Subject: [PATCH 1078/5498] cifs: release cifs root_cred after exit_cifs commit 94183331e815617246b1baa97e0916f358c794bb upstream. memory leak was found by kmemleak. exit_cifs_spnego should be called before cifs module removed, or cifs root_cred will not be released. kmemleak report: unreferenced object 0xffff880070a3ce40 (size 192): backtrace: kmemleak_alloc+0x4a/0xa0 kmem_cache_alloc+0xc7/0x1d0 prepare_kernel_cred+0x20/0x120 init_cifs_spnego+0x2d/0x170 [cifs] 0xffffffffc07801f3 do_one_initcall+0x51/0x1b0 do_init_module+0x60/0x1fd load_module+0x161e/0x1b60 SYSC_finit_module+0xa9/0x100 SyS_finit_module+0xe/0x10 Signed-off-by: Shu Wang Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 103e8b6604b1..b8158006e0ff 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1311,7 +1311,7 @@ exit_cifs(void) exit_cifs_idmap(); #endif #ifdef CONFIG_CIFS_UPCALL - unregister_key_type(&cifs_spnego_key_type); + exit_cifs_spnego(); #endif cifs_destroy_request_bufs(); cifs_destroy_mids(); -- GitLab From 456bbdc86d18d95d389cc8012259176c09211d5b Mon Sep 17 00:00:00 2001 From: Shu Wang Date: Fri, 8 Sep 2017 18:48:33 +0800 Subject: [PATCH 1079/5498] cifs: release auth_key.response for reconnect. commit f5c4ba816315d3b813af16f5571f86c8d4e897bd upstream. There is a race that cause cifs reconnect in cifs_mount, - cifs_mount - cifs_get_tcp_session - [ start thread cifs_demultiplex_thread - cifs_read_from_socket: -ECONNABORTED - DELAY_WORK smb2_reconnect_server ] - cifs_setup_session - [ smb2_reconnect_server ] auth_key.response was allocated in cifs_setup_session, and will release when the session destoried. So when session re- connect, auth_key.response should be check and released. Tested with my system: CIFS VFS: Free previous auth_key.response = ffff8800320bbf80 A simple auth_key.response allocation call trace: - cifs_setup_session - SMB2_sess_setup - SMB2_sess_auth_rawntlmssp_authenticate - build_ntlmssp_auth_blob - setup_ntlmv2_rsp Signed-off-by: Shu Wang Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 32c46a5c489e..43df8c3e026c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3958,6 +3958,14 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", server->sec_mode, server->capabilities, server->timeAdj); + if (ses->auth_key.response) { + cifs_dbg(VFS, "Free previous auth_key.response = %p\n", + ses->auth_key.response); + kfree(ses->auth_key.response); + ses->auth_key.response = NULL; + ses->auth_key.len = 0; + } + if (server->ops->sess_setup) rc = server->ops->sess_setup(xid, ses, nls_info); -- GitLab From 998173c7a119102c637506b5ecaa205821755a65 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Fri, 18 Aug 2017 15:33:57 +0300 Subject: [PATCH 1080/5498] mac80211: flush hw_roc_start work before cancelling the ROC commit 6e46d8ce894374fc135c96a8d1057c6af1fef237 upstream. When HW ROC is supported it is possible that after the HW notified that the ROC has started, the ROC was cancelled and another ROC was added while the hw_roc_start worker is waiting on the mutex (since cancelling the ROC and adding another one also holds the same mutex). As a result, the hw_roc_start worker will continue to run after the new ROC is added but before it is actually started by the HW. This may result in notifying userspace that the ROC has started before it actually does, or in case of management tx ROC, in an attempt to tx while not on the right channel. In addition, when the driver will notify mac80211 that the second ROC has started, mac80211 will warn that this ROC has already been notified. Fix this by flushing the hw_roc_start work before cancelling an ROC. Signed-off-by: Avraham Stern Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/offchannel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index ff20b2ebdb30..a914eaa7ff06 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -469,6 +469,8 @@ void ieee80211_roc_purge(struct ieee80211_local *local, struct ieee80211_roc_work *roc, *tmp; LIST_HEAD(tmp_list); + flush_work(&local->hw_roc_start); + mutex_lock(&local->mtx); list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { if (sdata && roc->sdata != sdata) -- GitLab From 9b9ce7484a5f7099435ee2827acd1020edacedef Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 12 Sep 2017 16:00:24 +1000 Subject: [PATCH 1081/5498] KVM: PPC: Book3S: Fix race and leak in kvm_vm_ioctl_create_spapr_tce() commit 47c5310a8dbe7c2cb9f0083daa43ceed76c257fa upstream, with part of commit edd03602d97236e8fea13cd76886c576186aa307 folded in. Nixiaoming pointed out that there is a memory leak in kvm_vm_ioctl_create_spapr_tce() if the call to anon_inode_getfd() fails; the memory allocated for the kvmppc_spapr_tce_table struct is not freed, and nor are the pages allocated for the iommu tables. David Hildenbrand pointed out that there is a race in that the function checks early on that there is not already an entry in the stt->iommu_tables list with the same LIOBN, but an entry with the same LIOBN could get added between then and when the new entry is added to the list. This fixes both problems. To simplify things, we now call anon_inode_getfd() before placing the new entry in the list. The check for an existing entry is done while holding the kvm->lock mutex, immediately before adding the new entry to the list. [paulus@ozlabs.org - folded in that part of edd03602d972 ("KVM: PPC: Book3S HV: Protect updates to spapr_tce_tables list", 2017-08-28) which restructured the code that 47c5310a8dbe modified, to avoid a build failure caused by the absence of put_unused_fd(). Also removed the locked memory accounting, since it doesn't exist in this version, and adjusted the commit message.] Fixes: 54738c097163 ("KVM: PPC: Accelerate H_PUT_TCE by implementing it in real mode") Reported-by: Nixiaoming Reported-by: David Hildenbrand Signed-off-by: Paul Mackerras Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kvm/book3s_64_vio.c | 46 +++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c index 54cf9bc94dad..3a095670b0c4 100644 --- a/arch/powerpc/kvm/book3s_64_vio.c +++ b/arch/powerpc/kvm/book3s_64_vio.c @@ -101,22 +101,17 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, struct kvm_create_spapr_tce *args) { struct kvmppc_spapr_tce_table *stt = NULL; + struct kvmppc_spapr_tce_table *siter; long npages; int ret = -ENOMEM; int i; - /* Check this LIOBN hasn't been previously allocated */ - list_for_each_entry(stt, &kvm->arch.spapr_tce_tables, list) { - if (stt->liobn == args->liobn) - return -EBUSY; - } - npages = kvmppc_stt_npages(args->window_size); stt = kzalloc(sizeof(*stt) + npages * sizeof(struct page *), GFP_KERNEL); if (!stt) - goto fail; + return ret; stt->liobn = args->liobn; stt->window_size = args->window_size; @@ -128,23 +123,36 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, goto fail; } - kvm_get_kvm(kvm); - mutex_lock(&kvm->lock); - list_add(&stt->list, &kvm->arch.spapr_tce_tables); + + /* Check this LIOBN hasn't been previously allocated */ + ret = 0; + list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) { + if (siter->liobn == args->liobn) { + ret = -EBUSY; + break; + } + } + + if (!ret) + ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, + stt, O_RDWR | O_CLOEXEC); + + if (ret >= 0) { + list_add(&stt->list, &kvm->arch.spapr_tce_tables); + kvm_get_kvm(kvm); + } mutex_unlock(&kvm->lock); - return anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, - stt, O_RDWR | O_CLOEXEC); + if (ret >= 0) + return ret; -fail: - if (stt) { - for (i = 0; i < npages; i++) - if (stt->pages[i]) - __free_page(stt->pages[i]); + fail: + for (i = 0; i < npages; i++) + if (stt->pages[i]) + __free_page(stt->pages[i]); - kfree(stt); - } + kfree(stt); return ret; } -- GitLab From 8923bf76c1ea47b16b2d6ccb9e09cdd913ffa06d Mon Sep 17 00:00:00 2001 From: Tahsin Erdogan Date: Sun, 17 Sep 2017 03:23:48 -0700 Subject: [PATCH 1082/5498] tracing: Fix trace_pipe behavior for instance traces commit 75df6e688ccd517e339a7c422ef7ad73045b18a2 upstream. When reading data from trace_pipe, tracing_wait_pipe() performs a check to see if tracing has been turned off after some data was read. Currently, this check always looks at global trace state, but it should be checking the trace instance where trace_pipe is located at. Because of this bug, cat instances/i1/trace_pipe in the following script will immediately exit instead of waiting for data: cd /sys/kernel/debug/tracing echo 0 > tracing_on mkdir -p instances/i1 echo 1 > instances/i1/tracing_on echo 1 > instances/i1/events/sched/sched_process_exec/enable cat instances/i1/trace_pipe Link: http://lkml.kernel.org/r/20170917102348.1615-1-tahsin@google.com Fixes: 10246fa35d4f ("tracing: give easy way to clear trace buffer") Signed-off-by: Tahsin Erdogan Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 941b21f7c8a4..0da1496b2ed1 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4430,7 +4430,7 @@ static int tracing_wait_pipe(struct file *filp) * * iter->pos will be 0 if we haven't read anything. */ - if (!tracing_is_on() && iter->pos) + if (!tracer_tracing_is_on(iter->tr) && iter->pos) break; mutex_unlock(&iter->mutex); -- GitLab From 58061248504a4ab93f802e648daad063857dcac6 Mon Sep 17 00:00:00 2001 From: Bo Yan Date: Mon, 18 Sep 2017 10:03:35 -0700 Subject: [PATCH 1083/5498] tracing: Erase irqsoff trace with empty write commit 8dd33bcb7050dd6f8c1432732f930932c9d3a33e upstream. One convenient way to erase trace is "echo > trace". However, this is currently broken if the current tracer is irqsoff tracer. This is because irqsoff tracer use max_buffer as the default trace buffer. Set the max_buffer as the one to be cleared when it's the trace buffer currently in use. Link: http://lkml.kernel.org/r/1505754215-29411-1-git-send-email-byan@nvidia.com Cc: Fixes: 4acd4d00f ("tracing: give easy way to clear trace buffer") Signed-off-by: Bo Yan Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0da1496b2ed1..e61b862309af 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3168,11 +3168,17 @@ static int tracing_open(struct inode *inode, struct file *file) /* If this file was open for write, then erase contents */ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { int cpu = tracing_get_cpu(inode); + struct trace_buffer *trace_buf = &tr->trace_buffer; + +#ifdef CONFIG_TRACER_MAX_TRACE + if (tr->current_trace->print_max) + trace_buf = &tr->max_buffer; +#endif if (cpu == RING_BUFFER_ALL_CPUS) - tracing_reset_online_cpus(&tr->trace_buffer); + tracing_reset_online_cpus(trace_buf); else - tracing_reset(&tr->trace_buffer, cpu); + tracing_reset(trace_buf, cpu); } if (file->f_mode & FMODE_READ) { -- GitLab From fec4cd33779f9edd7ab2d0d5ad7228f55f3f6887 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 27 Aug 2017 20:25:26 +0800 Subject: [PATCH 1084/5498] scsi: scsi_transport_iscsi: fix the issue that iscsi_if_rx doesn't parse nlmsg properly commit c88f0e6b06f4092995688211a631bb436125d77b upstream. ChunYu found a kernel crash by syzkaller: [ 651.617875] kasan: CONFIG_KASAN_INLINE enabled [ 651.618217] kasan: GPF could be caused by NULL-ptr deref or user memory access [ 651.618731] general protection fault: 0000 [#1] SMP KASAN [ 651.621543] CPU: 1 PID: 9539 Comm: scsi Not tainted 4.11.0.cov #32 [ 651.621938] Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011 [ 651.622309] task: ffff880117780000 task.stack: ffff8800a3188000 [ 651.622762] RIP: 0010:skb_release_data+0x26c/0x590 [...] [ 651.627260] Call Trace: [ 651.629156] skb_release_all+0x4f/0x60 [ 651.629450] consume_skb+0x1a5/0x600 [ 651.630705] netlink_unicast+0x505/0x720 [ 651.632345] netlink_sendmsg+0xab2/0xe70 [ 651.633704] sock_sendmsg+0xcf/0x110 [ 651.633942] ___sys_sendmsg+0x833/0x980 [ 651.637117] __sys_sendmsg+0xf3/0x240 [ 651.638820] SyS_sendmsg+0x32/0x50 [ 651.639048] entry_SYSCALL_64_fastpath+0x1f/0xc2 It's caused by skb_shared_info at the end of sk_buff was overwritten by ISCSI_KEVENT_IF_ERROR when parsing nlmsg info from skb in iscsi_if_rx. During the loop if skb->len == nlh->nlmsg_len and both are sizeof(*nlh), ev = nlmsg_data(nlh) will acutally get skb_shinfo(SKB) instead and set a new value to skb_shinfo(SKB)->nr_frags by ev->type. This patch is to fix it by checking nlh->nlmsg_len properly there to avoid over accessing sk_buff. Reported-by: ChunYu Wang Signed-off-by: Xin Long Acked-by: Chris Leech Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_transport_iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 67d43e35693d..b5a653aed5a4 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -3693,7 +3693,7 @@ iscsi_if_rx(struct sk_buff *skb) uint32_t group; nlh = nlmsg_hdr(skb); - if (nlh->nlmsg_len < sizeof(*nlh) || + if (nlh->nlmsg_len < sizeof(*nlh) + sizeof(*ev) || skb->len < nlh->nlmsg_len) { break; } -- GitLab From 2d9db72fb50e72aafaac2b99f4fcf1013497d8da Mon Sep 17 00:00:00 2001 From: LEROY Christophe Date: Wed, 13 Sep 2017 12:44:51 +0200 Subject: [PATCH 1085/5498] crypto: talitos - fix sha224 commit afd62fa26343be6445479e75de9f07092a061459 upstream. Kernel crypto tests report the following error at startup [ 2.752626] alg: hash: Test 4 failed for sha224-talitos [ 2.757907] 00000000: 30 e2 86 e2 e7 8a dd 0d d7 eb 9f d5 83 fe f1 b0 00000010: 2d 5a 6c a5 f9 55 ea fd 0e 72 05 22 This patch fixes it Signed-off-by: Christophe Leroy Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 00410b319b26..9fa17913941c 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -1581,9 +1581,9 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc, req_ctx->swinit = 0; } else { desc->ptr[1] = zero_entry; - /* Indicate next op is not the first. */ - req_ctx->first = 0; } + /* Indicate next op is not the first. */ + req_ctx->first = 0; /* HMAC key */ if (ctx->keylen) -- GitLab From 50f17a4b9141111a50d9eb127389fa69b2085d7c Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 18 Sep 2017 11:36:45 -0700 Subject: [PATCH 1086/5498] KEYS: fix writing past end of user-supplied buffer in keyring_read() commit e645016abc803dafc75e4b8f6e4118f088900ffb upstream. Userspace can call keyctl_read() on a keyring to get the list of IDs of keys in the keyring. But if the user-supplied buffer is too small, the kernel would write the full list anyway --- which will corrupt whatever userspace memory happened to be past the end of the buffer. Fix it by only filling the space that is available. Fixes: b2a4df200d57 ("KEYS: Expand the capacity of a keyring") Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- security/keys/keyring.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d33437007ad2..b156109742ff 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -416,7 +416,7 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) } struct keyring_read_iterator_context { - size_t qty; + size_t buflen; size_t count; key_serial_t __user *buffer; }; @@ -428,9 +428,9 @@ static int keyring_read_iterator(const void *object, void *data) int ret; kenter("{%s,%d},,{%zu/%zu}", - key->type->name, key->serial, ctx->count, ctx->qty); + key->type->name, key->serial, ctx->count, ctx->buflen); - if (ctx->count >= ctx->qty) + if (ctx->count >= ctx->buflen) return 1; ret = put_user(key->serial, ctx->buffer); @@ -465,16 +465,12 @@ static long keyring_read(const struct key *keyring, return 0; /* Calculate how much data we could return */ - ctx.qty = nr_keys * sizeof(key_serial_t); - if (!buffer || !buflen) - return ctx.qty; - - if (buflen > ctx.qty) - ctx.qty = buflen; + return nr_keys * sizeof(key_serial_t); /* Copy the IDs of the subscribed keys into the buffer */ ctx.buffer = (key_serial_t __user *)buffer; + ctx.buflen = buflen; ctx.count = 0; ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx); if (ret < 0) { -- GitLab From e3b663ba2ddd8f30ba92d4e6898637bb526dba70 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 18 Sep 2017 11:37:03 -0700 Subject: [PATCH 1087/5498] KEYS: prevent creating a different user's keyrings commit 237bbd29f7a049d310d907f4b2716a7feef9abf3 upstream. It was possible for an unprivileged user to create the user and user session keyrings for another user. For example: sudo -u '#3000' sh -c 'keyctl add keyring _uid.4000 "" @u keyctl add keyring _uid_ses.4000 "" @u sleep 15' & sleep 1 sudo -u '#4000' keyctl describe @u sudo -u '#4000' keyctl describe @us This is problematic because these "fake" keyrings won't have the right permissions. In particular, the user who created them first will own them and will have full access to them via the possessor permissions, which can be used to compromise the security of a user's keys: -4: alswrv-----v------------ 3000 0 keyring: _uid.4000 -5: alswrv-----v------------ 3000 0 keyring: _uid_ses.4000 Fix it by marking user and user session keyrings with a flag KEY_FLAG_UID_KEYRING. Then, when searching for a user or user session keyring by name, skip all keyrings that don't have the flag set. Fixes: 69664cf16af4 ("keys: don't generate user and user session keyrings unless they're accessed") Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- include/linux/key.h | 2 ++ security/keys/internal.h | 2 +- security/keys/key.c | 2 ++ security/keys/keyring.c | 23 ++++++++++++++--------- security/keys/process_keys.c | 8 ++++++-- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/include/linux/key.h b/include/linux/key.h index e1d4715f3222..dcc00a7a5b8d 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -172,6 +172,7 @@ struct key { #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ #define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ #define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_UID_KEYRING 12 /* set if key is a user or user session keyring */ /* the key type and key description string * - the desc is used to match a key against search criteria @@ -223,6 +224,7 @@ extern struct key *key_alloc(struct key_type *type, #define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ #define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ #define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ +#define KEY_ALLOC_UID_KEYRING 0x0010 /* allocating a user or user session keyring */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); diff --git a/security/keys/internal.h b/security/keys/internal.h index 200e37867336..b87c92ffae68 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -136,7 +136,7 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx); extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx); -extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check); +extern struct key *find_keyring_by_name(const char *name, bool uid_keyring); extern int install_user_keyrings(void); extern int install_thread_keyring_to_cred(struct cred *); diff --git a/security/keys/key.c b/security/keys/key.c index 37c268fb5c81..8f67cd3bb999 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -298,6 +298,8 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->flags |= 1 << KEY_FLAG_IN_QUOTA; if (flags & KEY_ALLOC_TRUSTED) key->flags |= 1 << KEY_FLAG_TRUSTED; + if (flags & KEY_ALLOC_UID_KEYRING) + key->flags |= 1 << KEY_FLAG_UID_KEYRING; #ifdef KEY_DEBUGGING key->magic = KEY_DEBUG_MAGIC; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index b156109742ff..6d913f40b6f0 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -961,15 +961,15 @@ found: /* * Find a keyring with the specified name. * - * All named keyrings in the current user namespace are searched, provided they - * grant Search permission directly to the caller (unless this check is - * skipped). Keyrings whose usage points have reached zero or who have been - * revoked are skipped. + * Only keyrings that have nonzero refcount, are not revoked, and are owned by a + * user in the current user namespace are considered. If @uid_keyring is %true, + * the keyring additionally must have been allocated as a user or user session + * keyring; otherwise, it must grant Search permission directly to the caller. * * Returns a pointer to the keyring with the keyring's refcount having being * incremented on success. -ENOKEY is returned if a key could not be found. */ -struct key *find_keyring_by_name(const char *name, bool skip_perm_check) +struct key *find_keyring_by_name(const char *name, bool uid_keyring) { struct key *keyring; int bucket; @@ -997,10 +997,15 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) if (strcmp(keyring->description, name) != 0) continue; - if (!skip_perm_check && - key_permission(make_key_ref(keyring, 0), - KEY_NEED_SEARCH) < 0) - continue; + if (uid_keyring) { + if (!test_bit(KEY_FLAG_UID_KEYRING, + &keyring->flags)) + continue; + } else { + if (key_permission(make_key_ref(keyring, 0), + KEY_NEED_SEARCH) < 0) + continue; + } /* we've got a match but we might end up racing with * key_cleanup() if the keyring is currently 'dead' diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 162077db5f81..85b61a3ac981 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -76,7 +76,9 @@ int install_user_keyrings(void) if (IS_ERR(uid_keyring)) { uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_UID_KEYRING | + KEY_ALLOC_IN_QUOTA, + NULL); if (IS_ERR(uid_keyring)) { ret = PTR_ERR(uid_keyring); goto error; @@ -92,7 +94,9 @@ int install_user_keyrings(void) session_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_UID_KEYRING | + KEY_ALLOC_IN_QUOTA, + NULL); if (IS_ERR(session_keyring)) { ret = PTR_ERR(session_keyring); goto error_release; -- GitLab From 6ea8051f42d965e3197ef31d4f54f75525b9439c Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 18 Sep 2017 11:37:23 -0700 Subject: [PATCH 1088/5498] KEYS: prevent KEYCTL_READ on negative key commit 37863c43b2c6464f252862bf2e9768264e961678 upstream. Because keyctl_read_key() looks up the key with no permissions requested, it may find a negatively instantiated key. If the key is also possessed, we went ahead and called ->read() on the key. But the key payload will actually contain the ->reject_error rather than the normal payload. Thus, the kernel oopses trying to read the user_key_payload from memory address (int)-ENOKEY = 0x00000000ffffff82. Fortunately the payload data is stored inline, so it shouldn't be possible to abuse this as an arbitrary memory read primitive... Reproducer: keyctl new_session keyctl request2 user desc '' @s keyctl read $(keyctl show | awk '/user: desc/ {print $1}') It causes a crash like the following: BUG: unable to handle kernel paging request at 00000000ffffff92 IP: user_read+0x33/0xa0 PGD 36a54067 P4D 36a54067 PUD 0 Oops: 0000 [#1] SMP CPU: 0 PID: 211 Comm: keyctl Not tainted 4.14.0-rc1 #337 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-20170228_101828-anatol 04/01/2014 task: ffff90aa3b74c3c0 task.stack: ffff9878c0478000 RIP: 0010:user_read+0x33/0xa0 RSP: 0018:ffff9878c047bee8 EFLAGS: 00010246 RAX: 0000000000000001 RBX: ffff90aa3d7da340 RCX: 0000000000000017 RDX: 0000000000000000 RSI: 00000000ffffff82 RDI: ffff90aa3d7da340 RBP: ffff9878c047bf00 R08: 00000024f95da94f R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 FS: 00007f58ece69740(0000) GS:ffff90aa3e200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000ffffff92 CR3: 0000000036adc001 CR4: 00000000003606f0 Call Trace: keyctl_read_key+0xac/0xe0 SyS_keyctl+0x99/0x120 entry_SYSCALL_64_fastpath+0x1f/0xbe RIP: 0033:0x7f58ec787bb9 RSP: 002b:00007ffc8d401678 EFLAGS: 00000206 ORIG_RAX: 00000000000000fa RAX: ffffffffffffffda RBX: 00007ffc8d402800 RCX: 00007f58ec787bb9 RDX: 0000000000000000 RSI: 00000000174a63ac RDI: 000000000000000b RBP: 0000000000000004 R08: 00007ffc8d402809 R09: 0000000000000020 R10: 0000000000000000 R11: 0000000000000206 R12: 00007ffc8d402800 R13: 00007ffc8d4016e0 R14: 0000000000000000 R15: 0000000000000000 Code: e5 41 55 49 89 f5 41 54 49 89 d4 53 48 89 fb e8 a4 b4 ad ff 85 c0 74 09 80 3d b9 4c 96 00 00 74 43 48 8b b3 20 01 00 00 4d 85 ed <0f> b7 5e 10 74 29 4d 85 e4 74 24 4c 39 e3 4c 89 e2 4c 89 ef 48 RIP: user_read+0x33/0xa0 RSP: ffff9878c047bee8 CR2: 00000000ffffff92 Fixes: 61ea0c0ba904 ("KEYS: Skip key state checks when checking for possession") Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- security/keys/keyctl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 98c5ebc6dad6..9fc289ec0159 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -744,6 +744,11 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { + ret = -ENOKEY; + goto error2; + } + /* see if we can read it directly */ ret = key_permission(key_ref, KEY_NEED_READ); if (ret == 0) -- GitLab From b93becabbaf4c0e3963ebd62e2e094fc68deec08 Mon Sep 17 00:00:00 2001 From: Tyrel Datwyler Date: Wed, 20 Sep 2017 17:02:52 -0400 Subject: [PATCH 1089/5498] powerpc/pseries: Fix parent_dn reference leak in add_dt_node() commit b537ca6fede69a281dc524983e5e633d79a10a08 upstream. A reference to the parent device node is held by add_dt_node() for the node to be added. If the call to dlpar_configure_connector() fails add_dt_node() returns ENOENT and that reference is not freed. Add a call to of_node_put(parent_dn) prior to bailing out after a failed dlpar_configure_connector() call. Fixes: 8d5ff320766f ("powerpc/pseries: Make dlpar_configure_connector parent node aware") Signed-off-by: Tyrel Datwyler Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/mobility.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index f8c9ff7886e1..b86408e91e8b 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -225,8 +225,10 @@ static int add_dt_node(__be32 parent_phandle, __be32 drc_index) return -ENOENT; dn = dlpar_configure_connector(drc_index, parent_dn); - if (!dn) + if (!dn) { + of_node_put(parent_dn); return -ENOENT; + } rc = dlpar_attach_node(dn); if (rc) -- GitLab From b3c1243ba1bc4ab4c632078412381750f94bb649 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 20 Sep 2017 19:57:18 -0500 Subject: [PATCH 1090/5498] SMB: Validate negotiate (to protect against downgrade) even if signing off commit 0603c96f3af50e2f9299fa410c224ab1d465e0f9 upstream. As long as signing is supported (ie not a guest user connection) and connection is SMB3 or SMB3.02, then validate negotiate (protect against man in the middle downgrade attacks). We had been doing this only when signing was required, not when signing was just enabled, but this more closely matches recommended SMB3 behavior and is better security. Suggested by Metze. Signed-off-by: Steve French Reviewed-by: Jeremy Allison Acked-by: Stefan Metzmacher Reviewed-by: Ronnie Sahlberg Signed-off-by: Greg Kroah-Hartman --- fs/cifs/smb2pdu.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2c3047636d4e..fc5809d494ed 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -453,15 +453,22 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) /* * validation ioctl must be signed, so no point sending this if we - * can not sign it. We could eventually change this to selectively + * can not sign it (ie are not known user). Even if signing is not + * required (enabled but not negotiated), in those cases we selectively * sign just this, the first and only signed request on a connection. - * This is good enough for now since a user who wants better security - * would also enable signing on the mount. Having validation of - * negotiate info for signed connections helps reduce attack vectors + * Having validation of negotiate info helps reduce attack vectors. */ - if (tcon->ses->server->sign == false) + if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) return 0; /* validation requires signing */ + if (tcon->ses->user_name == NULL) { + cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); + return 0; /* validation requires signing */ + } + + if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) + cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); + vneg_inbuf.Capabilities = cpu_to_le32(tcon->ses->server->vals->req_capabilities); memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid, -- GitLab From 64304f5ae2f37d54072b208d53fb692ef941d168 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 22 Sep 2017 01:40:27 -0500 Subject: [PATCH 1091/5498] SMB3: Don't ignore O_SYNC/O_DSYNC and O_DIRECT flags commit 1013e760d10e614dc10b5624ce9fc41563ba2e65 upstream. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg Reviewed-by: Pavel Shilovsky Signed-off-by: Greg Kroah-Hartman --- fs/cifs/file.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index a3badede1b32..237c201d6d3e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -225,6 +225,13 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; + /* O_SYNC also has bit for O_DSYNC so following check picks up either */ + if (f_flags & O_SYNC) + create_options |= CREATE_WRITE_THROUGH; + + if (f_flags & O_DIRECT) + create_options |= CREATE_NO_BUFFER; + oparms.tcon = tcon; oparms.cifs_sb = cifs_sb; oparms.desired_access = desired_access; -- GitLab From 9f119f5cc0480e7d1eaa4e16ecbcddcdd5e8db6f Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 25 Sep 2017 12:23:03 +0200 Subject: [PATCH 1092/5498] vfs: Return -ENXIO for negative SEEK_HOLE / SEEK_DATA offsets commit fc46820b27a2d9a46f7e90c9ceb4a64a1bc5fab8 upstream. In generic_file_llseek_size, return -ENXIO for negative offsets as well as offsets beyond EOF. This affects filesystems which don't implement SEEK_HOLE / SEEK_DATA internally, possibly because they don't support holes. Fixes xfstest generic/448. Signed-off-by: Andreas Gruenbacher Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/read_write.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index 7d9318c3d43c..1afb99c33c63 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -116,7 +116,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence, * In the generic case the entire file is data, so as long as * offset isn't at the end of the file then the offset is data. */ - if (offset >= eof) + if ((unsigned long long)offset >= eof) return -ENXIO; break; case SEEK_HOLE: @@ -124,7 +124,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence, * There is a virtual hole at the end of the file, so as long as * offset isn't i_size or larger, return i_size. */ - if (offset >= eof) + if ((unsigned long long)offset >= eof) return -ENXIO; offset = eof; break; -- GitLab From 65e3664fc6087d2941e940e38beb39920e9ae032 Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Wed, 13 Sep 2017 00:21:21 +0200 Subject: [PATCH 1093/5498] nl80211: check for the required netlink attributes presence commit e785fa0a164aa11001cba931367c7f94ffaff888 upstream. nl80211_set_rekey_data() does not check if the required attributes NL80211_REKEY_DATA_{REPLAY_CTR,KEK,KCK} are present when processing NL80211_CMD_SET_REKEY_OFFLOAD request. This request can be issued by users with CAP_NET_ADMIN privilege and may result in NULL dereference and a system crash. Add a check for the required attributes presence. This patch is based on the patch by bo Zhang. This fixes CVE-2017-12153. References: https://bugzilla.redhat.com/show_bug.cgi?id=1491046 Fixes: e5497d766ad ("cfg80211/nl80211: support GTK rekey offload") Reported-by: bo Zhang Signed-off-by: Vladis Dronov Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/nl80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9fb1dd399788..f843f704c17c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9006,6 +9006,9 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) if (err) return err; + if (!tb[NL80211_REKEY_DATA_REPLAY_CTR] || !tb[NL80211_REKEY_DATA_KEK] || + !tb[NL80211_REKEY_DATA_KCK]) + return -EINVAL; if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN) return -ERANGE; if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN) -- GitLab From d9100405a20a71dd620843e0380e38fc50731108 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 7 Sep 2017 13:54:35 +0200 Subject: [PATCH 1094/5498] bsg-lib: don't free job in bsg_prepare_job commit f507b54dccfd8000c517d740bc45f20c74532d18 upstream. The job structure is allocated as part of the request, so we should not free it in the error path of bsg_prepare_job. Signed-off-by: Christoph Hellwig Reviewed-by: Ming Lei Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/bsg-lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/bsg-lib.c b/block/bsg-lib.c index 650f427d915b..341b8d858e67 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -147,7 +147,6 @@ static int bsg_create_job(struct device *dev, struct request *req) failjob_rls_rqst_payload: kfree(job->request_payload.sg_list); failjob_rls_job: - kfree(job); return -ENOMEM; } -- GitLab From 6528968794bc6b4091a7603ae863472c1ef7c393 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Sep 2017 15:57:16 +0100 Subject: [PATCH 1095/5498] arm64: Make sure SPsel is always set commit 5371513fb338fb9989c569dc071326d369d6ade8 upstream. When the kernel is entered at EL2 on an ARMv8.0 system, we construct the EL1 pstate and make sure this uses the the EL1 stack pointer (we perform an exception return to EL1h). But if the kernel is either entered at EL1 or stays at EL2 (because we're on a VHE-capable system), we fail to set SPsel, and use whatever stack selection the higher exception level has choosen for us. Let's not take any chance, and make sure that SPsel is set to one before we decide the mode we're going to run in. Acked-by: Mark Rutland Signed-off-by: Marc Zyngier Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/head.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 2877dd818977..5c4b8d6e8ba0 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -264,6 +264,7 @@ ENDPROC(stext) * booted in EL1 or EL2 respectively. */ ENTRY(el2_setup) + msr SPsel, #1 // We want to use SP_EL{1,2} mrs x0, CurrentEL cmp x0, #CurrentEL_EL2 b.ne 1f -- GitLab From 6ad74630c016ef823f2720671ee4db641d35fd2c Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Tue, 12 Sep 2017 13:02:54 -0700 Subject: [PATCH 1096/5498] kvm: nVMX: Don't allow L2 to access the hardware CR8 commit 51aa68e7d57e3217192d88ce90fd5b8ef29ec94f upstream. If L1 does not specify the "use TPR shadow" VM-execution control in vmcs12, then L0 must specify the "CR8-load exiting" and "CR8-store exiting" VM-execution controls in vmcs02. Failure to do so will give the L2 VM unrestricted read/write access to the hardware CR8. This fixes CVE-2017-12154. Signed-off-by: Jim Mattson Reviewed-by: David Hildenbrand Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 99c004ddefd8..dd3ca375bfdf 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -8275,6 +8275,11 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, page_to_phys(vmx->nested.virtual_apic_page)); vmcs_write32(TPR_THRESHOLD, vmcs12->tpr_threshold); + } else { +#ifdef CONFIG_X86_64 + exec_control |= CPU_BASED_CR8_LOAD_EXITING | + CPU_BASED_CR8_STORE_EXITING; +#endif } /* -- GitLab From 0544aab81d35a0ec852eb63a5e6bfa6eac132848 Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Mon, 11 Sep 2017 09:45:40 +0200 Subject: [PATCH 1097/5498] PCI: Fix race condition with driver_override commit 9561475db680f7144d2223a409dd3d7e322aca03 upstream. The driver_override implementation is susceptible to a race condition when different threads are reading vs. storing a different driver override. Add locking to avoid the race condition. This is in close analogy to commit 6265539776a0 ("driver core: platform: fix race condition with driver_override") from Adrian Salido. Fixes: 782a985d7af2 ("PCI: Introduce new device binding path using pci_dev.driver_override") Signed-off-by: Nicolai Stange Signed-off-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 66ba1ee09a6c..318707870cb2 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -513,7 +513,7 @@ static ssize_t driver_override_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - char *driver_override, *old = pdev->driver_override, *cp; + char *driver_override, *old, *cp; /* We need to keep extra room for a newline */ if (count >= (PAGE_SIZE - 1)) @@ -527,12 +527,15 @@ static ssize_t driver_override_store(struct device *dev, if (cp) *cp = '\0'; + device_lock(dev); + old = pdev->driver_override; if (strlen(driver_override)) { pdev->driver_override = driver_override; } else { kfree(driver_override); pdev->driver_override = NULL; } + device_unlock(dev); kfree(old); @@ -543,8 +546,12 @@ static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pdev = to_pci_dev(dev); + ssize_t len; - return snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); + device_lock(dev); + len = snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); + device_unlock(dev); + return len; } static DEVICE_ATTR_RW(driver_override); -- GitLab From 540125645c70c02e485365a259545a15571e863a Mon Sep 17 00:00:00 2001 From: satoru takeuchi Date: Tue, 12 Sep 2017 22:42:52 +0900 Subject: [PATCH 1098/5498] btrfs: prevent to set invalid default subvolid commit 6d6d282932d1a609e60dc4467677e0e863682f57 upstream. `btrfs sub set-default` succeeds to set an ID which isn't corresponding to any fs/file tree. If such the bad ID is set to a filesystem, we can't mount this filesystem without specifying `subvol` or `subvolid` mount options. Fixes: 6ef5ed0d386b ("Btrfs: add ioctl and incompat flag to set the default mount subvol") Signed-off-by: Satoru Takeuchi Reviewed-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ioctl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index dd8526a659f8..a877e6279b10 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3963,6 +3963,10 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp) ret = PTR_ERR(new_root); goto out; } + if (!is_fstree(new_root->objectid)) { + ret = -ENOENT; + goto out; + } path = btrfs_alloc_path(); if (!path) { -- GitLab From f23ec06d527c9cd90552a68c748bcb5aa942ee87 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 2 Oct 2017 11:10:07 -0700 Subject: [PATCH 1099/5498] x86/fpu: Don't let userspace set bogus xcomp_bv commit 814fb7bb7db5433757d76f4c4502c96fc53b0b5e upstream. [Please apply to 3.18-stable. Note: the backport includes the fpu_finit() call in xstateregs_set(), since fix is useless without it. It was added by commit 91c3dba7dbc1 ("x86/fpu/xstate: Fix PTRACE frames for XSAVES"), but it doesn't make sense to backport that whole commit.] On x86, userspace can use the ptrace() or rt_sigreturn() system calls to set a task's extended state (xstate) or "FPU" registers. ptrace() can set them for another task using the PTRACE_SETREGSET request with NT_X86_XSTATE, while rt_sigreturn() can set them for the current task. In either case, registers can be set to any value, but the kernel assumes that the XSAVE area itself remains valid in the sense that the CPU can restore it. However, in the case where the kernel is using the uncompacted xstate format (which it does whenever the XSAVES instruction is unavailable), it was possible for userspace to set the xcomp_bv field in the xstate_header to an arbitrary value. However, all bits in that field are reserved in the uncompacted case, so when switching to a task with nonzero xcomp_bv, the XRSTOR instruction failed with a #GP fault. This caused the WARN_ON_FPU(err) in copy_kernel_to_xregs() to be hit. In addition, since the error is otherwise ignored, the FPU registers from the task previously executing on the CPU were leaked. Fix the bug by checking that the user-supplied value of xcomp_bv is 0 in the uncompacted case, and returning an error otherwise. The reason for validating xcomp_bv rather than simply overwriting it with 0 is that we want userspace to see an error if it (incorrectly) provides an XSAVE area in compacted format rather than in uncompacted format. Note that as before, in case of error we clear the task's FPU state. This is perhaps non-ideal, especially for PTRACE_SETREGSET; it might be better to return an error before changing anything. But it seems the "clear on error" behavior is fine for now, and it's a little tricky to do otherwise because it would mean we couldn't simply copy the full userspace state into kernel memory in one __copy_from_user(). This bug was found by syzkaller, which hit the above-mentioned WARN_ON_FPU(): WARNING: CPU: 1 PID: 0 at ./arch/x86/include/asm/fpu/internal.h:373 __switch_to+0x5b5/0x5d0 CPU: 1 PID: 0 Comm: swapper/1 Not tainted 4.13.0 #453 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 task: ffff9ba2bc8e42c0 task.stack: ffffa78cc036c000 RIP: 0010:__switch_to+0x5b5/0x5d0 RSP: 0000:ffffa78cc08bbb88 EFLAGS: 00010082 RAX: 00000000fffffffe RBX: ffff9ba2b8bf2180 RCX: 00000000c0000100 RDX: 00000000ffffffff RSI: 000000005cb10700 RDI: ffff9ba2b8bf36c0 RBP: ffffa78cc08bbbd0 R08: 00000000929fdf46 R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000000 R12: ffff9ba2bc8e42c0 R13: 0000000000000000 R14: ffff9ba2b8bf3680 R15: ffff9ba2bf5d7b40 FS: 00007f7e5cb10700(0000) GS:ffff9ba2bf400000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000004005cc CR3: 0000000079fd5000 CR4: 00000000001406e0 Call Trace: Code: 84 00 00 00 00 00 e9 11 fd ff ff 0f ff 66 0f 1f 84 00 00 00 00 00 e9 e7 fa ff ff 0f ff 66 0f 1f 84 00 00 00 00 00 e9 c2 fa ff ff <0f> ff 66 0f 1f 84 00 00 00 00 00 e9 d4 fc ff ff 66 66 2e 0f 1f Here is a C reproducer. The expected behavior is that the program spin forever with no output. However, on a buggy kernel running on a processor with the "xsave" feature but without the "xsaves" feature (e.g. Sandy Bridge through Broadwell for Intel), within a second or two the program reports that the xmm registers were corrupted, i.e. were not restored correctly. With CONFIG_X86_DEBUG_FPU=y it also hits the above kernel warning. #define _GNU_SOURCE #include #include #include #include #include #include #include #include int main(void) { int pid = fork(); uint64_t xstate[512]; struct iovec iov = { .iov_base = xstate, .iov_len = sizeof(xstate) }; if (pid == 0) { bool tracee = true; for (int i = 0; i < sysconf(_SC_NPROCESSORS_ONLN) && tracee; i++) tracee = (fork() != 0); uint32_t xmm0[4] = { [0 ... 3] = tracee ? 0x00000000 : 0xDEADBEEF }; asm volatile(" movdqu %0, %%xmm0\n" " mov %0, %%rbx\n" "1: movdqu %%xmm0, %0\n" " mov %0, %%rax\n" " cmp %%rax, %%rbx\n" " je 1b\n" : "+m" (xmm0) : : "rax", "rbx", "xmm0"); printf("BUG: xmm registers corrupted! tracee=%d, xmm0=%08X%08X%08X%08X\n", tracee, xmm0[0], xmm0[1], xmm0[2], xmm0[3]); } else { usleep(100000); ptrace(PTRACE_ATTACH, pid, 0, 0); wait(NULL); ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov); xstate[65] = -1; ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov); ptrace(PTRACE_CONT, pid, 0, 0); wait(NULL); } return 1; } Note: the program only tests for the bug using the ptrace() system call. The bug can also be reproduced using the rt_sigreturn() system call, but only when called from a 32-bit program, since for 64-bit programs the kernel restores the FPU state from the signal frame by doing XRSTOR directly from userspace memory (with proper error checking). Reported-by: Dmitry Vyukov Signed-off-by: Eric Biggers Reviewed-by: Kees Cook Reviewed-by: Rik van Riel Acked-by: Dave Hansen Cc: Andrew Morton Cc: Andy Lutomirski Cc: Andy Lutomirski Cc: Borislav Petkov Cc: Eric Biggers Cc: Fenghua Yu Cc: Kevin Hao Cc: Linus Torvalds Cc: Michael Halcrow Cc: Oleg Nesterov Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Wanpeng Li Cc: Yu-cheng Yu Cc: kernel-hardening@lists.openwall.com Fixes: 0b29643a5843 ("x86/xsaves: Change compacted format xsave area header") Link: http://lkml.kernel.org/r/20170922174156.16780-2-ebiggers3@gmail.com Link: http://lkml.kernel.org/r/20170923130016.21448-25-mingo@kernel.org Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/i387.c | 11 +++++++++++ arch/x86/kernel/xsave.c | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 8d6e954db2a7..9c9f4c0b0106 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c @@ -388,11 +388,22 @@ int xstateregs_set(struct task_struct *target, const struct user_regset *regset, xsave_hdr = &target->thread.fpu.state->xsave.xsave_hdr; xsave_hdr->xstate_bv &= pcntxt_mask; + + /* xcomp_bv must be 0 when using uncompacted format */ + if (!ret && xsave_hdr->xcomp_bv) + ret = -EINVAL; + /* * These bits must be zero. */ memset(xsave_hdr->reserved, 0, 48); + /* + * In case of failure, mark all states as init: + */ + if (ret) + fpu_finit(&target->thread.fpu); + return ret; } diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index cdc6cf903078..460e72155f51 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -394,7 +394,9 @@ int __restore_xstate_sig(void __user *buf, void __user *buf_fx, int size) drop_fpu(tsk); if (__copy_from_user(&fpu->state->xsave, buf_fx, state_size) || - __copy_from_user(&env, buf, sizeof(env))) { + __copy_from_user(&env, buf, sizeof(env)) || + (state_size > offsetof(struct xsave_struct, xsave_hdr) && + fpu->state->xsave.xsave_hdr.xcomp_bv)) { fpu_finit(fpu); err = -1; } else { -- GitLab From 2d53f0b14066d798104411f13442afc20bdea4d6 Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Mon, 4 Sep 2017 16:00:50 +0200 Subject: [PATCH 1100/5498] video: fbdev: aty: do not leak uninitialized padding in clk to userspace commit 8e75f7a7a00461ef6d91797a60b606367f6e344d upstream. 'clk' is copied to a userland with padding byte(s) after 'vclk_post_div' field unitialized, leaking data from the stack. Fix this ensuring all of 'clk' is initialized to zero. References: https://github.com/torvalds/linux/pull/441 Reported-by: sohu0106 Signed-off-by: Vladis Dronov Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/aty/atyfb_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 37ec09b3fffd..fd38ee820da6 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -1852,7 +1852,7 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT) case ATYIO_CLKR: if (M64_HAS(INTEGRATED)) { - struct atyclk clk; + struct atyclk clk = { 0 }; union aty_pll *pll = &par->pll; u32 dsp_config = pll->ct.dsp_config; u32 dsp_on_off = pll->ct.dsp_on_off; -- GitLab From bc9f6868dfb50ba357d4db5916632c94f8d94a4c Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 7 Feb 2017 19:58:02 +0200 Subject: [PATCH 1101/5498] swiotlb-xen: implement xen_swiotlb_dma_mmap callback commit 7e91c7df29b5e196de3dc6f086c8937973bd0b88 upstream. This function creates userspace mapping for the DMA-coherent memory. Signed-off-by: Stefano Stabellini Signed-off-by: Oleksandr Dmytryshyn Signed-off-by: Andrii Anisov Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman --- arch/arm/xen/mm.c | 1 + drivers/xen/swiotlb-xen.c | 19 +++++++++++++++++++ include/xen/swiotlb-xen.h | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index f8a576b1d9bb..5409d70ffe6f 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -59,6 +59,7 @@ static struct dma_map_ops xen_swiotlb_dma_ops = { .unmap_page = xen_swiotlb_unmap_page, .dma_supported = xen_swiotlb_dma_supported, .set_dma_mask = xen_swiotlb_set_dma_mask, + .mmap = xen_swiotlb_dma_mmap, }; int __init xen_mm_init(void) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index c9d0d5a0e662..a0a819c5fc1f 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -684,3 +684,22 @@ xen_swiotlb_set_dma_mask(struct device *dev, u64 dma_mask) return 0; } EXPORT_SYMBOL_GPL(xen_swiotlb_set_dma_mask); + +/* + * Create userspace mapping for the DMA-coherent memory. + * This function should be called with the pages from the current domain only, + * passing pages mapped from other domains would lead to memory corruption. + */ +int +xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + unsigned long attrs) +{ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + if (__generic_dma_ops(dev)->mmap) + return __generic_dma_ops(dev)->mmap(dev, vma, cpu_addr, + dma_addr, size, attrs); +#endif + return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size); +} +EXPORT_SYMBOL_GPL(xen_swiotlb_dma_mmap); diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h index 8b2eb93ae8ba..fab4fb9c6442 100644 --- a/include/xen/swiotlb-xen.h +++ b/include/xen/swiotlb-xen.h @@ -58,4 +58,9 @@ xen_swiotlb_dma_supported(struct device *hwdev, u64 mask); extern int xen_swiotlb_set_dma_mask(struct device *dev, u64 dma_mask); + +extern int +xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + unsigned long attrs); #endif /* __LINUX_SWIOTLB_XEN_H */ -- GitLab From 376c6f62f8d4f73a0573a0e69323afe39d834893 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 4 Oct 2017 15:51:29 +0200 Subject: [PATCH 1102/5498] fix xen_swiotlb_dma_mmap prototype xen_swiotlb_dma_mmap was backported from v4.10, but older kernels before commit 00085f1efa38 ("dma-mapping: use unsigned long for dma_attrs") use a different signature: arm/xen/mm.c:202:10: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .mmap = xen_swiotlb_dma_mmap, ^~~~~~~~~~~~~~~~~~~~ arm/xen/mm.c:202:10: note: (near initialization for 'xen_swiotlb_dma_ops.mmap') This adapts the patch to the old calling conventions. Fixes: "swiotlb-xen: implement xen_swiotlb_dma_mmap callback" Signed-off-by: Arnd Bergmann Reviewed-by: Stefano Stabellini Signed-off-by: Greg Kroah-Hartman --- drivers/xen/swiotlb-xen.c | 2 +- include/xen/swiotlb-xen.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index a0a819c5fc1f..c6d47e558488 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -693,7 +693,7 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_set_dma_mask); int xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, dma_addr_t dma_addr, size_t size, - unsigned long attrs) + struct dma_attrs *attrs) { #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) if (__generic_dma_ops(dev)->mmap) diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h index fab4fb9c6442..4d7fdbf20eff 100644 --- a/include/xen/swiotlb-xen.h +++ b/include/xen/swiotlb-xen.h @@ -62,5 +62,5 @@ xen_swiotlb_set_dma_mask(struct device *dev, u64 dma_mask); extern int xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, dma_addr_t dma_addr, size_t size, - unsigned long attrs); + struct dma_attrs *attrs); #endif /* __LINUX_SWIOTLB_XEN_H */ -- GitLab From ffc97d4dde1d3c77beebddbbd2a0be5f8f18236a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 5 Oct 2017 09:36:10 +0200 Subject: [PATCH 1103/5498] Linux 3.18.73 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9b82f279ef1d..f5e683464cd4 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 72 +SUBLEVEL = 73 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 4738c4031bb44b961efc41749f2e4e06688d42bb Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Mon, 8 May 2017 09:33:22 -0700 Subject: [PATCH 1104/5498] ANDROID: binder: Add tracing for binder priority inheritance. Bug: 34461621 Change-Id: I5ebb1c0c49fd42a89ee250a1d70221f767c82c7c Signed-off-by: Martijn Coenen --- drivers/staging/android/binder.c | 4 ++++ drivers/staging/android/binder_trace.h | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 9af8df96c93e..7664feda35d4 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -1150,6 +1150,10 @@ static void binder_do_set_priority(struct task_struct *task, task->pid, desired.prio, to_kernel_prio(policy, priority)); + trace_binder_set_priority(task->tgid, task->pid, task->normal_prio, + to_kernel_prio(policy, priority), + desired.prio); + /* Set the actual priority */ if (task->policy != policy || is_rt_policy(policy)) { struct sched_param params; diff --git a/drivers/staging/android/binder_trace.h b/drivers/staging/android/binder_trace.h index 76e3b9c8a8a2..b11dffc521e8 100644 --- a/drivers/staging/android/binder_trace.h +++ b/drivers/staging/android/binder_trace.h @@ -85,6 +85,30 @@ DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done); DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done); +TRACE_EVENT(binder_set_priority, + TP_PROTO(int proc, int thread, unsigned int old_prio, + unsigned int desired_prio, unsigned int new_prio), + TP_ARGS(proc, thread, old_prio, new_prio, desired_prio), + + TP_STRUCT__entry( + __field(int, proc) + __field(int, thread) + __field(unsigned int, old_prio) + __field(unsigned int, new_prio) + __field(unsigned int, desired_prio) + ), + TP_fast_assign( + __entry->proc = proc; + __entry->thread = thread; + __entry->old_prio = old_prio; + __entry->new_prio = new_prio; + __entry->desired_prio = desired_prio; + ), + TP_printk("proc=%d thread=%d old=%d => new=%d desired=%d", + __entry->proc, __entry->thread, __entry->old_prio, + __entry->new_prio, __entry->desired_prio) +); + TRACE_EVENT(binder_wait_for_work, TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), TP_ARGS(proc_work, transaction_stack, thread_todo), -- GitLab From be71309f0f4c10a8cc4b42ad495d649d5581ad45 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Thu, 24 Aug 2017 15:23:36 +0200 Subject: [PATCH 1105/5498] ANDROID: binder: fix transaction leak. If a call to put_user() fails, we failed to properly free a transaction and send a failed reply (if necessary). Bug: 63117588 Test: binderLibTest Change-Id: Ia98db8cd82ce354a4cdc8811c969988d585c7e31 Signed-off-by: Martijn Coenen --- drivers/staging/android/binder.c | 40 +++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 7664feda35d4..60d568b925e8 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2102,6 +2102,26 @@ static void binder_send_failed_reply(struct binder_transaction *t, } } +/** + * binder_cleanup_transaction() - cleans up undelivered transaction + * @t: transaction that needs to be cleaned up + * @reason: reason the transaction wasn't delivered + * @error_code: error to return to caller (if synchronous call) + */ +static void binder_cleanup_transaction(struct binder_transaction *t, + const char *reason, + uint32_t error_code) +{ + if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) { + binder_send_failed_reply(t, error_code); + } else { + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, + "undelivered transaction %d, %s\n", + t->debug_id, reason); + binder_free_transaction(t); + } +} + /** * binder_validate_object() - checks for a valid metadata object in a buffer. * @buffer: binder_buffer that we're parsing. @@ -4184,12 +4204,20 @@ retry: if (put_user(cmd, (uint32_t __user *)ptr)) { if (t_from) binder_thread_dec_tmpref(t_from); + + binder_cleanup_transaction(t, "put_user failed", + BR_FAILED_REPLY); + return -EFAULT; } ptr += sizeof(uint32_t); if (copy_to_user(ptr, &tr, sizeof(tr))) { if (t_from) binder_thread_dec_tmpref(t_from); + + binder_cleanup_transaction(t, "copy_to_user failed", + BR_FAILED_REPLY); + return -EFAULT; } ptr += sizeof(tr); @@ -4259,15 +4287,9 @@ static void binder_release_work(struct binder_proc *proc, struct binder_transaction *t; t = container_of(w, struct binder_transaction, work); - if (t->buffer->target_node && - !(t->flags & TF_ONE_WAY)) { - binder_send_failed_reply(t, BR_DEAD_REPLY); - } else { - binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, - "undelivered transaction %d\n", - t->debug_id); - binder_free_transaction(t); - } + + binder_cleanup_transaction(t, "process died.", + BR_DEAD_REPLY); } break; case BINDER_WORK_RETURN_ERROR: { struct binder_error *e = container_of( -- GitLab From 5a2dc5d35b8644b4db4338f335ce5c31bff3f0e2 Mon Sep 17 00:00:00 2001 From: Archana Obannagari Date: Mon, 18 Sep 2017 12:49:24 +0530 Subject: [PATCH 1106/5498] msm: kgsl: Fix size check issue in _read_fw2_block_header() During firmware data read, it may leads to cross the max range. Add a check to validate data size with in the max range. Change-Id: I12b3c1761214a7d2af2c45ad73bf61954c8398b6 Signed-off-by: Archana Obannagari --- drivers/gpu/msm/adreno_a5xx.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 306782f71744..48f96b1bae00 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -61,8 +61,8 @@ static const struct adreno_vbif_platform a5xx_vbif_platforms[] = { }; static void a5xx_irq_storm_worker(struct work_struct *work); -static int _read_fw2_block_header(uint32_t *header, uint32_t id, - uint32_t major, uint32_t minor); +static int _read_fw2_block_header(uint32_t *header, uint32_t remain, + uint32_t id, uint32_t major, uint32_t minor); static void a5xx_gpmu_reset(struct work_struct *work); static int a5xx_gpmu_init(struct adreno_device *adreno_dev); @@ -659,6 +659,7 @@ static int _load_gpmu_firmware(struct adreno_device *adreno_dev) if (data[1] != GPMU_FIRMWARE_ID) goto err; ret = _read_fw2_block_header(&data[2], + data[0] - 2, GPMU_FIRMWARE_ID, adreno_dev->gpucore->gpmu_major, adreno_dev->gpucore->gpmu_minor); @@ -1120,8 +1121,8 @@ void a5xx_hwcg_set(struct adreno_device *adreno_dev, bool on) kgsl_regwrite(device, A5XX_RBBM_ISDB_CNT, on ? 0x00000182 : 0x00000180); } -static int _read_fw2_block_header(uint32_t *header, uint32_t id, - uint32_t major, uint32_t minor) +static int _read_fw2_block_header(uint32_t *header, uint32_t remain, + uint32_t id, uint32_t major, uint32_t minor) { uint32_t header_size; int i = 1; @@ -1131,7 +1132,8 @@ static int _read_fw2_block_header(uint32_t *header, uint32_t id, header_size = header[0]; /* Headers have limited size and always occur as pairs of words */ - if (header_size > MAX_HEADER_SIZE || header_size % 2) + if (header_size > MAX_HEADER_SIZE || header_size >= remain || + header_size % 2 || header_size == 0) return -EINVAL; /* Sequences must have an identifying id first thing in their header */ if (id == GPMU_SEQUENCE_ID) { @@ -1226,6 +1228,7 @@ static void _load_regfile(struct adreno_device *adreno_dev) /* For now ignore blocks other than the LM sequence */ if (block[4] == LM_SEQUENCE_ID) { ret = _read_fw2_block_header(&block[2], + block_size - 2, GPMU_SEQUENCE_ID, adreno_dev->gpucore->lm_major, adreno_dev->gpucore->lm_minor); -- GitLab From 51b9caf264df59e273dc5e9da3b3fd7a8b5939a1 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Thu, 21 Sep 2017 15:28:04 +0530 Subject: [PATCH 1107/5498] usb: phy: qusb: Add support for the new scm_call2 API The scm library has added support for a new secure world interface that is more aligned to the ARMv8 SCM calling convention. Use the new API while maintaining backward compatibility. Change-Id: I7d1553a82ff5f104ded7028a391fc649c9db4b5b Signed-off-by: Sriharsha Allenki --- drivers/usb/phy/phy-msm-qusb.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index 15efbf6cf08b..59052aeda020 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -173,7 +173,8 @@ struct qusb_phy { static void qusb_phy_update_tcsr_level_shifter(struct qusb_phy *qphy, u32 val) { - int scm_ret, resp_ret; + int scm_ret, resp_ret = 0; + int dummy = 0; dev_dbg(qphy->phy.dev, "%s(): update tcsr lvl shift value:%d\n", __func__, val); @@ -181,8 +182,22 @@ static void qusb_phy_update_tcsr_level_shifter(struct qusb_phy *qphy, u32 val) writel_relaxed(val, qphy->tcsr_phy_lvl_shift_keeper); else if (qphy->scm_lvl_shifter_update) { - scm_ret = scm_call(SCM_SVC_BOOT, QUSB2PHY_LVL_SHIFTER_CMD_ID, - &val, sizeof(val), &resp_ret, sizeof(resp_ret)); + if (!is_scm_armv8()) { + scm_ret = scm_call(SCM_SVC_BOOT, + QUSB2PHY_LVL_SHIFTER_CMD_ID, &val, + sizeof(val), &resp_ret, + sizeof(resp_ret)); + } else { + struct scm_desc desc = {0}; + + desc.arginfo = SCM_ARGS(2); + desc.args[0] = val; + desc.args[1] = dummy; + + scm_ret = scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, + QUSB2PHY_LVL_SHIFTER_CMD_ID), + &desc); + } dev_dbg(qphy->phy.dev, "%s(): scm_ret:%d resp_ret:%d\n", __func__, scm_ret, resp_ret); } -- GitLab From 983672f92a20a0df7645143d3c3a120a68c34362 Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Thu, 24 Nov 2016 13:23:10 +0000 Subject: [PATCH 1108/5498] UPSTREAM: mpi: Fix NULL ptr dereference in mpi_powm() [ver #3] This fixes CVE-2016-8650. If mpi_powm() is given a zero exponent, it wants to immediately return either 1 or 0, depending on the modulus. However, if the result was initalised with zero limb space, no limbs space is allocated and a NULL-pointer exception ensues. Fix this by allocating a minimal amount of limb space for the result when the 0-exponent case when the result is 1 and not touching the limb space when the result is 0. This affects the use of RSA keys and X.509 certificates that carry them. Bug: 33401771 BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] mpi_powm+0x32/0x7e6 PGD 0 Oops: 0002 [#1] SMP Modules linked in: CPU: 3 PID: 3014 Comm: keyctl Not tainted 4.9.0-rc6-fscache+ #278 Hardware name: ASUS All Series/H97-PLUS, BIOS 2306 10/09/2014 task: ffff8804011944c0 task.stack: ffff880401294000 RIP: 0010:[] [] mpi_powm+0x32/0x7e6 RSP: 0018:ffff880401297ad8 EFLAGS: 00010212 RAX: 0000000000000000 RBX: ffff88040868bec0 RCX: ffff88040868bba0 RDX: ffff88040868b260 RSI: ffff88040868bec0 RDI: ffff88040868bee0 RBP: ffff880401297ba8 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000047 R11: ffffffff8183b210 R12: 0000000000000000 R13: ffff8804087c7600 R14: 000000000000001f R15: ffff880401297c50 FS: 00007f7a7918c700(0000) GS:ffff88041fb80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 0000000401250000 CR4: 00000000001406e0 Stack: ffff88040868bec0 0000000000000020 ffff880401297b00 ffffffff81376cd4 0000000000000100 ffff880401297b10 ffffffff81376d12 ffff880401297b30 ffffffff81376f37 0000000000000100 0000000000000000 ffff880401297ba8 Call Trace: [] ? __sg_page_iter_next+0x43/0x66 [] ? sg_miter_get_next_page+0x1b/0x5d [] ? sg_miter_next+0x17/0xbd [] ? mpi_read_raw_from_sgl+0xf2/0x146 [] rsa_verify+0x9d/0xee [] ? pkcs1pad_sg_set_buf+0x2e/0xbb [] pkcs1pad_verify+0xc0/0xe1 [] public_key_verify_signature+0x1b0/0x228 [] x509_check_for_self_signed+0xa1/0xc4 [] x509_cert_parse+0x167/0x1a1 [] x509_key_preparse+0x21/0x1a1 [] asymmetric_key_preparse+0x34/0x61 [] key_create_or_update+0x145/0x399 [] SyS_add_key+0x154/0x19e [] do_syscall_64+0x80/0x191 [] entry_SYSCALL64_slow_path+0x25/0x25 Code: 56 41 55 41 54 53 48 81 ec a8 00 00 00 44 8b 71 04 8b 42 04 4c 8b 67 18 45 85 f6 89 45 80 0f 84 b4 06 00 00 85 c0 75 2f 41 ff ce <49> c7 04 24 01 00 00 00 b0 01 75 0b 48 8b 41 18 48 83 38 01 0f RIP [] mpi_powm+0x32/0x7e6 RSP CR2: 0000000000000000 ---[ end trace d82015255d4a5d8d ]--- Basically, this is a backport of a libgcrypt patch: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=patch;h=6e1adb05d290aeeb1c230c763970695f4a538526 Fixes: cdec9cb5167a ("crypto: GnuPG based MPI lib - source files (part 1)") Signed-off-by: Andrey Ryabinin Signed-off-by: David Howells cc: Dmitry Kasatkin cc: linux-ima-devel@lists.sourceforge.net cc: stable@vger.kernel.org Signed-off-by: James Morris Change-Id: I42a008d34a8ca31406fb545783156fca44fa16b4 (cherry picked from commit f5527fffff3f002b0a6b376163613b82f69de073) --- lib/mpi/mpi-pow.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c index 5464c8744ea9..e24388a863a7 100644 --- a/lib/mpi/mpi-pow.c +++ b/lib/mpi/mpi-pow.c @@ -64,8 +64,13 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) if (!esize) { /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0 * depending on if MOD equals 1. */ - rp[0] = 1; res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1; + if (res->nlimbs) { + if (mpi_resize(res, 1) < 0) + goto enomem; + rp = res->d; + rp[0] = 1; + } res->sign = 0; goto leave; } -- GitLab From eea9447983dccf538ffb105ee0c777355a64b0a0 Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Fri, 30 Jun 2017 15:45:06 +0530 Subject: [PATCH 1109/5498] msm: pcie: add support for switch latency Add support for switch latency property to add additional delay if switch is present. Change-Id: Ia64a79d5ec51d3abb66cebd0a187349711c96af2 Signed-off-by: Rama Krishna Phani A --- .../devicetree/bindings/pci/msm_pcie.txt | 4 +++ drivers/pci/host/pci-msm.c | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt index 968e8c347da2..b8a6ed3991a1 100644 --- a/Documentation/devicetree/bindings/pci/msm_pcie.txt +++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt @@ -97,6 +97,9 @@ Optional Properties: and assign for each endpoint. - qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become stable after power on, before de-assert the PERST to the endpoint. + - qcom,switch-latency: The time (unit: ms) to wait for the PCIe endpoint's link + training with switch downstream port after the link between switch upstream + port and RC is up. - qcom,wr-halt-size: With base 2, this exponent determines the size of the data that PCIe core will halt on for each write transaction. - qcom,cpl-timeout: Completion timeout value. This value specifies the time range @@ -264,6 +267,7 @@ Example: qcom,smmu-exist; qcom,smmu-sid-base = <0x1480>; qcom,ep-latency = <100>; + qcom,switch-latency = <100>; qcom,wr-halt-size = <0xa>; /* 1KB */ qcom,cpl-timeout = <0x2>; diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 435c502076ac..91295670b0fa 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -292,6 +292,7 @@ #define PERST_PROPAGATION_DELAY_US_MIN 1000 #define PERST_PROPAGATION_DELAY_US_MAX 1005 +#define SWITCH_DELAY_MAX 20 #define REFCLK_STABILIZATION_DELAY_US_MIN 1000 #define REFCLK_STABILIZATION_DELAY_US_MAX 1005 #define LINK_UP_TIMEOUT_US_MIN 5000 @@ -626,6 +627,7 @@ struct msm_pcie_dev_t { bool ext_ref_clk; bool common_phy; uint32_t ep_latency; + uint32_t switch_latency; uint32_t wr_halt_size; uint32_t cpl_timeout; uint32_t current_bdf; @@ -1936,6 +1938,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev) dev->common_phy); PCIE_DBG_FS(dev, "ep_latency: %dms\n", dev->ep_latency); + PCIE_DBG_FS(dev, "switch_latency: %dms\n", + dev->switch_latency); PCIE_DBG_FS(dev, "wr_halt_size: 0x%x\n", dev->wr_halt_size); PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n", @@ -4548,6 +4552,16 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) goto link_fail; } + if (dev->switch_latency) { + PCIE_DBG(dev, "switch_latency: %dms\n", + dev->switch_latency); + if (dev->switch_latency <= SWITCH_DELAY_MAX) + usleep_range(dev->switch_latency * 1000, + dev->switch_latency * 1000); + else + msleep(dev->switch_latency); + } + msm_pcie_config_controller(dev); if (!dev->msi_gicm_addr) @@ -6044,6 +6058,20 @@ static int msm_pcie_probe(struct platform_device *pdev) PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n", rc_idx, msm_pcie_dev[rc_idx].ep_latency); + msm_pcie_dev[rc_idx].switch_latency = 0; + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,switch-latency", + &msm_pcie_dev[rc_idx].switch_latency); + + if (ret) + PCIE_DBG(&msm_pcie_dev[rc_idx], + "RC%d: switch-latency does not exist.\n", + rc_idx); + else + PCIE_DBG(&msm_pcie_dev[rc_idx], + "RC%d: switch-latency: 0x%x.\n", + rc_idx, msm_pcie_dev[rc_idx].switch_latency); + msm_pcie_dev[rc_idx].wr_halt_size = 0; ret = of_property_read_u32(pdev->dev.of_node, "qcom,wr-halt-size", -- GitLab From f8787fe60493f71aad5a6158db4e109b6e445578 Mon Sep 17 00:00:00 2001 From: Archana Obannagari Date: Tue, 3 Oct 2017 17:45:56 +0530 Subject: [PATCH 1110/5498] msm: kgsl: Add a check for availability of RBBM timer clock Before calling kgsl_pwrctrl_clk_set_rate(), make sure the RBBM timer clock is available. This avoids warning messages in the driver log. Change-Id: I0cd21b7253c802e8522d570056c8aeda02729267 Signed-off-by: Archana Obannagari --- drivers/gpu/msm/kgsl_pwrctrl.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index a51cbb02f5bd..62356465ce93 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -2153,9 +2153,11 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq, clocks[0]); - kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[6], - clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ), - clocks[6]); + if (pwr->grp_clks[6] != NULL) + kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[6], + clk_round_rate(pwr->grp_clks[6], + KGSL_RBBMTIMER_CLK_FREQ), + clocks[6]); result = get_regulators(device); if (result) -- GitLab From 2d3db6d58527c3a45c91c9d0c213903dd47332a9 Mon Sep 17 00:00:00 2001 From: Prashanth Vadde Date: Thu, 5 Oct 2017 13:35:39 +0530 Subject: [PATCH 1111/5498] ARM: dts: msm: Add i2c_6 LED node only on apq8017 Add TI TLC591xx LED interface i2c6 node support only on APQ8017 platform by creating a new dtsi file for this target configuration. Change-Id: Ie123039b596d18553730dfd455a2632f689d4144 Signed-off-by: Prashanth Vadde --- .../dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts | 1 + .../qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi | 54 +++++++ .../arm/boot/dts/qcom/apq8017-pmi8950-mtp.dts | 146 ------------------ arch/arm/boot/dts/qcom/msm8917.dtsi | 27 ---- 4 files changed, 55 insertions(+), 173 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi diff --git a/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts b/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts index a41d5490494e..16fcc179c03d 100644 --- a/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts +++ b/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dts @@ -17,6 +17,7 @@ #include "msm8917-pmi8950-cdp.dtsi" #include "apq8017-rome.dtsi" #include "apq8017-audio.dtsi" +#include "apq8017-pmi8950-cdp-wcd-rome.dtsi" / { model = "Qualcomm Technologies, Inc. APQ8017-PMI8950 CDP \ diff --git a/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi b/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi new file mode 100644 index 000000000000..b53847a885ed --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm8917.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8017-PMI8950 CDP \ + with WCD codec/Rome card"; + compatible = "qcom,apq8017-cdp", "qcom,apq8017", "qcom,cdp"; + qcom,board-id= <1 2>; + qcom,pmic-id = <0x10019 0x010011 0x0 0x0>; + + aliases { + i2c6 = &i2c_6; + }; +}; + +&soc { + i2c_6: i2c@7af6000 { /* BLSP2 QUP2 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0x7af6000 0x600>; + interrupt-names = "qup_irq"; + interrupts = <0 300 0>; + qcom,clk-freq-out = <400000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, + <&clock_gcc clk_gcc_blsp2_qup2_i2c_apps_clk>; + + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_6_active>; + pinctrl-1 = <&i2c_6_sleep>; + qcom,noise-rjct-scl = <0>; + qcom,noise-rjct-sda = <0>; + qcom,master-id = <84>; + dmas = <&dma_blsp2 6 64 0x20000020 0x20>, + <&dma_blsp2 7 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + status = "disabled"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/apq8017-pmi8950-mtp.dts b/arch/arm/boot/dts/qcom/apq8017-pmi8950-mtp.dts index 91083b45e80d..d152ba3cf9b4 100644 --- a/arch/arm/boot/dts/qcom/apq8017-pmi8950-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8017-pmi8950-mtp.dts @@ -62,149 +62,3 @@ &dsi_adv7533_1080p { qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; }; - -/* Warning, SPI6 & I2C6 cannot be enabled at the same time due to pin usage. */ -&spi_6 { - status = "disabled"; -}; - -&i2c_6 { - status = "ok"; - qcom,clk-freq-out = <100000>; - - /* TI591XX LED Drivers */ - tlc59116@60 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "ti,tlc59116"; - reg = <0x60>; - out0@0 { - label = "ledsec1_g"; - reg = <0x0>; - }; - out1@1 { - label = "ledsec1_r"; - reg = <0x1>; - }; - out2@2 { - label = "ledsec1_b"; - reg = <0x2>; - }; - out3@3 { - label = "ledsec2_g"; - reg = <0x3>; - }; - out4@4 { - label = "ledsec2_r"; - reg = <0x4>; - }; - out5@5 { - label = "ledsec2_b"; - reg = <0x5>; - }; - out6@6 { - label = "ledsec3_g"; - reg = <0x6>; - }; - out7@7 { - label = "ledsec3_r"; - reg = <0x7>; - }; - out8@8 { - label = "ledsec3_b"; - reg = <0x8>; - }; - out9@9 { - label = "ledsec4_g"; - reg = <0x9>; - }; - out10@10 { - label = "ledsec4_r"; - reg = <0xa>; - }; - out11@11 { - label = "ledsec4_b"; - reg = <0xb>; - }; - out12@12 { - label = "ledsec5_g"; - reg = <0xc>; - }; - out13@13 { - label = "ledsec5_r"; - reg = <0xd>; - }; - out14@14 { - label = "ledsec5_b"; - reg = <0xe>; - }; - }; - tlc59116@61 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "ti,tlc59116"; - reg = <0x61>; - out0@0 { - label = "ledsec6_g"; - reg = <0x0>; - }; - out1@1 { - label = "ledsec6_r"; - reg = <0x1>; - }; - out2@2 { - label = "ledsec6_b"; - reg = <0x2>; - }; - out3@3 { - label = "ledsec7_g"; - reg = <0x3>; - }; - out4@4 { - label = "ledsec7_r"; - reg = <0x4>; - }; - out5@5 { - label = "ledsec7_b"; - reg = <0x5>; - }; - out6@6 { - label = "ledsec8_g"; - reg = <0x6>; - }; - out7@7 { - label = "ledsec8_r"; - reg = <0x7>; - }; - out8@8 { - label = "ledsec8_b"; - reg = <0x8>; - }; - out9@9 { - label = "ledsec9_g"; - reg = <0x9>; - }; - out10@10 { - label = "ledsec9_r"; - reg = <0xa>; - }; - out11@11 { - label = "ledsec9_b"; - reg = <0xb>; - }; - }; -}; - -&soc { - pinctrl@1000000 { - i2c6{ - tlc59116_reset: tlc59116_reset { - config { - pins = "gpio20"; - drive-strength = <16>; - bias-pull-up; - }; - }; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/msm8917.dtsi b/arch/arm/boot/dts/qcom/msm8917.dtsi index ccad93ef2747..c529836d6d66 100644 --- a/arch/arm/boot/dts/qcom/msm8917.dtsi +++ b/arch/arm/boot/dts/qcom/msm8917.dtsi @@ -45,7 +45,6 @@ i2c5 = &i2c_5; i2c3 = &i2c_3; i2c4 = &i2c_4; - i2c6 = &i2c_6; sdhc1 = &sdhc_1; /* SDC1 eMMC slot */ sdhc2 = &sdhc_2; /* SDC2 for SD card */ }; @@ -650,32 +649,6 @@ dma-names = "tx", "rx"; }; - i2c_6: i2c@7af6000 { /* BLSP2 QUP2 */ - compatible = "qcom,i2c-msm-v2"; - #address-cells = <1>; - #size-cells = <0>; - reg-names = "qup_phys_addr"; - reg = <0x7af6000 0x600>; - interrupt-names = "qup_irq"; - interrupts = <0 300 0>; - qcom,clk-freq-out = <400000>; - qcom,clk-freq-in = <19200000>; - clock-names = "iface_clk", "core_clk"; - clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, - <&clock_gcc clk_gcc_blsp2_qup2_i2c_apps_clk>; - - pinctrl-names = "i2c_active", "i2c_sleep"; - pinctrl-0 = <&i2c_6_active>; - pinctrl-1 = <&i2c_6_sleep>; - qcom,noise-rjct-scl = <0>; - qcom,noise-rjct-sda = <0>; - qcom,master-id = <84>; - dmas = <&dma_blsp2 6 64 0x20000020 0x20>, - <&dma_blsp2 7 32 0x20000020 0x20>; - dma-names = "tx", "rx"; - status = "disabled"; - }; - rpm_bus: qcom,rpm-smd { compatible = "qcom,rpm-smd"; rpm-channel-name = "rpm_requests"; -- GitLab From fe5f55d7bbf0b7d3669d0872cf2737c087bde052 Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Thu, 14 Sep 2017 16:47:53 +0530 Subject: [PATCH 1112/5498] ARM: enable vmalloc saving For some targets that have less vmalloc space this can be increased by enabling config ENABLE_VMALLOC_SAVING. With this config we can reclaim virtual mappings which remains unused because of non hlos carveout reservations in lowmem. Select the default method of reclaiming virtual memory as vmalloc saving. Change-Id: I05bbae3b554be54f74b740404dda0cdfa89084bd Signed-off-by: Prakash Gupta --- arch/arm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 90ae4a60f7e1..83120cbcea69 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1771,7 +1771,7 @@ source "mm/Kconfig" choice prompt "Virtual Memory Reclaim" - default NO_VM_RECLAIM + default ENABLE_VMALLOC_SAVING help Select the method of reclaiming virtual memory -- GitLab From 5dd18663187f3075536a29c8f1280572fdfef845 Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Wed, 4 Oct 2017 12:37:11 +0530 Subject: [PATCH 1113/5498] iommu: fix smmu_secure_pool access list after free issue secure_pool_list is initialized during domain alloc and freed with domain free. commit e6a18bb617e4 ("iommu: free io pgtable during domain detach.") frees the secure_pool_list as part of iommu detach sequence, and uses the same list head as part of iommu attach. This uncovers an existing bug where list was not being deleted from secure_pool_list and associated memory was being freed. This resulted in invalid secure_pool_list head pointing to a location already freed and resulted in kernel BUG of access after free during iommu attach. Call Trace: arm_smmu_alloc_pages_exact+0x60/0x110 io_pgtable_alloc_pages_exact+0x48/0xb0 __arm_lpae_alloc_pages+0x48/0x1c0 arm_64_lpae_alloc_pgtable_s1+0x100/0x15c alloc_io_pgtable_ops+0x54/0x88 arm_smmu_attach_dev+0x8cc/0x1144 __iommu_attach_device+0x3c/0xf4 [...] Change-Id: I7d1b49030986da7f5d05b7e6cb9dc09079f20a41 Signed-off-by: Prakash Gupta --- drivers/iommu/arm-smmu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 69293aa1e792..5e529fea7a6a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1099,6 +1099,7 @@ static void arm_smmu_secure_pool_destroy(struct arm_smmu_domain *smmu_domain) list_for_each_entry_safe(it, i, &smmu_domain->secure_pool_list, list) { arm_smmu_unprepare_pgtable(smmu_domain, it->addr, it->size); /* pages will be freed later (after being unassigned) */ + list_del(&it->list); kfree(it); } } -- GitLab From dc2ed84e2939baf05c7b084e85b3801a83e95edf Mon Sep 17 00:00:00 2001 From: Hector Marco-Gisbert Date: Thu, 10 Mar 2016 20:51:00 +0100 Subject: [PATCH 1114/5498] UPSTREAM: x86/mm/32: Enable full randomization on i386 and X86_32 Currently on i386 and on X86_64 when emulating X86_32 in legacy mode, only the stack and the executable are randomized but not other mmapped files libraries, vDSO and mmap requests on i386 and in X86_32 in legacy mode. By default on i386 there are 8 bits for the randomization of the libraries, vDSO and mmaps which only uses 1MB of VA. This patch preserves the original randomness, using 1MB of VA out of 3GB or 4GB. We think that 1MB out of 3GB is not a big cost for having the ASLR. The first obvious security benefit is that all objects are randomized (not only the stack and the executable) in legacy mode which highly increases the ASLR effectiveness, otherwise the attackers may use these non-randomized areas. But also sensitive setuid/setgid applications are more secure because currently, attackers can disable the randomization of these applications by setting the ulimit stack to "unlimited". This is a very old and widely known trick to disable the ASLR in i386 which has been allowed for too long. Another trick used to disable the ASLR was to set the ADDR_NO_RANDOMIZE personality flag, but fortunately this doesn't work on setuid/setgid applications because there is security checks which clear Security-relevant flags. This patch always randomizes the mmap_legacy_base address, removing the possibility to disable the ASLR by setting the stack to "unlimited". Signed-off-by: Hector Marco-Gisbert Acked-by: Ismael Ripoll Ripoll Acked-by: Kees Cook Acked-by: Arjan van de Ven Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: akpm@linux-foundation.org Cc: kees Cook Link: http://lkml.kernel.org/r/1457639460-5242-1-git-send-email-hecmargi@upv.es Signed-off-by: Ingo Molnar Bug: 28763575 Change-Id: Icd128489c3c196ade64f79d4ea898d29f8471baf (cherry picked from commit 8b8addf891de8a00e4d39fc32f93f7c5eb8feceb) --- arch/x86/mm/mmap.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c index 084c36f6b4e3..47287ea3f080 100644 --- a/arch/x86/mm/mmap.c +++ b/arch/x86/mm/mmap.c @@ -94,31 +94,24 @@ static unsigned long mmap_base(void) return PAGE_ALIGN(TASK_SIZE - gap - mmap_rnd()); } -/* - * Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64 - * does, but not when emulating X86_32 - */ -static unsigned long mmap_legacy_base(void) -{ - if (mmap_is_ia32()) - return TASK_UNMAPPED_BASE; - else - return TASK_UNMAPPED_BASE + mmap_rnd(); -} - /* * This function, called very early during the creation of a new * process VM image, sets up which VM layout function to use: */ void arch_pick_mmap_layout(struct mm_struct *mm) { - mm->mmap_legacy_base = mmap_legacy_base(); - mm->mmap_base = mmap_base(); + unsigned long random_factor = 0UL; + + if (current->flags & PF_RANDOMIZE) + random_factor = mmap_rnd(); + + mm->mmap_legacy_base = TASK_UNMAPPED_BASE + random_factor; if (mmap_is_legacy()) { mm->mmap_base = mm->mmap_legacy_base; mm->get_unmapped_area = arch_get_unmapped_area; } else { + mm->mmap_base = mmap_base(); mm->get_unmapped_area = arch_get_unmapped_area_topdown; } } -- GitLab From 9907621a601512d45172a6197a576645c509bf98 Mon Sep 17 00:00:00 2001 From: Yuanyuan Liu Date: Tue, 15 Aug 2017 16:57:54 -0700 Subject: [PATCH 1115/5498] cnss2: Remove utils Remove utils as this module is no longer needed by CNSS2 platform driver. CNSS2 has already switched to WLAN common utility module in net/wireless/cnss_utils. Change-Id: I891d3261f7e654dab1136ee1661d15e19fe1d2f5 Signed-off-by: Yuanyuan Liu --- drivers/net/wireless/cnss2/Makefile | 1 - drivers/net/wireless/cnss2/main.c | 25 ------ drivers/net/wireless/cnss2/main.h | 11 --- drivers/net/wireless/cnss2/utils.c | 129 ---------------------------- include/net/cnss2.h | 6 -- 5 files changed, 172 deletions(-) delete mode 100644 drivers/net/wireless/cnss2/utils.c diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index 9d383c8daa43..b49d0898178b 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -5,5 +5,4 @@ cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o cnss2-y += qmi.o -cnss2-y += utils.o cnss2-y += wlan_firmware_service_v01.o diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 46eb3002a15a..6e0e16f53832 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -322,31 +322,6 @@ void cnss_remove_pm_qos(void) } EXPORT_SYMBOL(cnss_remove_pm_qos); -u8 *cnss_common_get_wlan_mac_address(struct device *dev, u32 *num) -{ - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); - struct cnss_wlan_mac_info *wlan_mac_info; - struct cnss_wlan_mac_addr *addr; - - if (!plat_priv) - goto out; - - wlan_mac_info = &plat_priv->wlan_mac_info; - if (!wlan_mac_info->is_wlan_mac_set) { - cnss_pr_info("Platform driver doesn't have any MAC address!\n"); - goto out; - } - - addr = &wlan_mac_info->wlan_mac_addr; - *num = addr->no_of_mac_addr_set; - - return &addr->mac_addr[0][0]; -out: - *num = 0; - return NULL; -} -EXPORT_SYMBOL(cnss_common_get_wlan_mac_address); - int cnss_wlan_enable(struct device *dev, struct cnss_wlan_enable_cfg *config, enum cnss_driver_mode mode, diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 89ada0b2e1f0..f34f4a31560f 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -97,16 +97,6 @@ struct cnss_bus_bw_info { int current_bw_vote; }; -struct cnss_wlan_mac_addr { - u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN]; - u32 no_of_mac_addr_set; -}; - -struct cnss_wlan_mac_info { - struct cnss_wlan_mac_addr wlan_mac_addr; - bool is_wlan_mac_set; -}; - struct cnss_fw_mem { size_t size; void *va; @@ -185,7 +175,6 @@ struct cnss_plat_data { struct cnss_wlan_driver *driver_ops; enum cnss_driver_status driver_status; u32 recovery_count; - struct cnss_wlan_mac_info wlan_mac_info; unsigned long driver_state; struct list_head event_list; spinlock_t event_lock; /* spinlock for driver work event handling */ diff --git a/drivers/net/wireless/cnss2/utils.c b/drivers/net/wireless/cnss2/utils.c deleted file mode 100644 index 9ffe386e3677..000000000000 --- a/drivers/net/wireless/cnss2/utils.c +++ /dev/null @@ -1,129 +0,0 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define CNSS_MAX_CH_NUM 45 - -#include -#include - -static DEFINE_MUTEX(unsafe_channel_list_lock); -static DEFINE_MUTEX(dfs_nol_info_lock); - -static struct cnss_unsafe_channel_list { - u16 unsafe_ch_count; - u16 unsafe_ch_list[CNSS_MAX_CH_NUM]; -} unsafe_channel_list; - -static struct cnss_dfs_nol_info { - void *dfs_nol_info; - u16 dfs_nol_info_len; -} dfs_nol_info; - -int cnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count) -{ - mutex_lock(&unsafe_channel_list_lock); - if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) { - mutex_unlock(&unsafe_channel_list_lock); - return -EINVAL; - } - - unsafe_channel_list.unsafe_ch_count = ch_count; - - if (ch_count != 0) { - memcpy((char *)unsafe_channel_list.unsafe_ch_list, - (char *)unsafe_ch_list, ch_count * sizeof(u16)); - } - mutex_unlock(&unsafe_channel_list_lock); - - return 0; -} -EXPORT_SYMBOL(cnss_set_wlan_unsafe_channel); - -int cnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, - u16 *ch_count, u16 buf_len) -{ - mutex_lock(&unsafe_channel_list_lock); - if (!unsafe_ch_list || !ch_count) { - mutex_unlock(&unsafe_channel_list_lock); - return -EINVAL; - } - - if (buf_len < (unsafe_channel_list.unsafe_ch_count * sizeof(u16))) { - mutex_unlock(&unsafe_channel_list_lock); - return -ENOMEM; - } - - *ch_count = unsafe_channel_list.unsafe_ch_count; - memcpy((char *)unsafe_ch_list, - (char *)unsafe_channel_list.unsafe_ch_list, - unsafe_channel_list.unsafe_ch_count * sizeof(u16)); - mutex_unlock(&unsafe_channel_list_lock); - - return 0; -} -EXPORT_SYMBOL(cnss_get_wlan_unsafe_channel); - -int cnss_wlan_set_dfs_nol(const void *info, u16 info_len) -{ - void *temp; - struct cnss_dfs_nol_info *dfs_info; - - mutex_lock(&dfs_nol_info_lock); - if (!info || !info_len) { - mutex_unlock(&dfs_nol_info_lock); - return -EINVAL; - } - - temp = kmalloc(info_len, GFP_KERNEL); - if (!temp) { - mutex_unlock(&dfs_nol_info_lock); - return -ENOMEM; - } - - memcpy(temp, info, info_len); - dfs_info = &dfs_nol_info; - kfree(dfs_info->dfs_nol_info); - - dfs_info->dfs_nol_info = temp; - dfs_info->dfs_nol_info_len = info_len; - mutex_unlock(&dfs_nol_info_lock); - - return 0; -} -EXPORT_SYMBOL(cnss_wlan_set_dfs_nol); - -int cnss_wlan_get_dfs_nol(void *info, u16 info_len) -{ - int len; - struct cnss_dfs_nol_info *dfs_info; - - mutex_lock(&dfs_nol_info_lock); - if (!info || !info_len) { - mutex_unlock(&dfs_nol_info_lock); - return -EINVAL; - } - - dfs_info = &dfs_nol_info; - - if (!dfs_info->dfs_nol_info || dfs_info->dfs_nol_info_len == 0) { - mutex_unlock(&dfs_nol_info_lock); - return -ENOENT; - } - - len = min(info_len, dfs_info->dfs_nol_info_len); - - memcpy(info, dfs_info->dfs_nol_info, len); - mutex_unlock(&dfs_nol_info_lock); - - return len; -} -EXPORT_SYMBOL(cnss_wlan_get_dfs_nol); diff --git a/include/net/cnss2.h b/include/net/cnss2.h index 5409e1b15a25..f1d321299492 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -153,14 +153,8 @@ extern int cnss_get_platform_cap(struct cnss_platform_cap *cap); extern int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info); extern void cnss_set_driver_status(enum cnss_driver_status driver_status); extern int cnss_request_bus_bandwidth(int bandwidth); -extern int cnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count); -extern int cnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 *ch_count, - u16 buf_len); -extern int cnss_wlan_set_dfs_nol(const void *info, u16 info_len); -extern int cnss_wlan_get_dfs_nol(void *info, u16 info_len); extern int cnss_power_up(struct device *dev); extern int cnss_power_down(struct device *dev); -extern u8 *cnss_common_get_wlan_mac_address(struct device *dev, uint32_t *num); extern void cnss_request_pm_qos(u32 qos_val); extern void cnss_remove_pm_qos(void); extern void cnss_lock_pm_sem(void); -- GitLab From 01be579e3a2b2964df14e8655df18c6d9b429f44 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Thu, 5 Oct 2017 13:58:01 -0700 Subject: [PATCH 1116/5498] usb: gadget: f_qdss: Update usb string table based on gadget transport Currently driver is passing generic string as part of string descriptor for all supported transports. Instead pass transport specific string because USB host may represent the enumerated device using string passed in usb string descriptor. Change-Id: I16c0aa82a62f5a0ccbc61aeea316053b3cb18022 Signed-off-by: Hemant Kumar --- drivers/usb/gadget/function/f_qdss.c | 38 +++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 14c09760a505..a0d8fd793b3b 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -176,12 +176,22 @@ static struct usb_descriptor_header *qdss_ss_data_only_desc[] = { }; /* string descriptors: */ -#define QDSS_DATA_IDX 0 -#define QDSS_CTRL_IDX 1 +#define QDSS_DATA_IDX 0 +#define QDSS_CTRL_IDX 1 +#define MSM_QDSS_DATA_IDX 2 +#define MSM_QDSS_CTRL_IDX 3 +#define MDM_QDSS_DATA_IDX 4 +#define MDM_QDSS_CTRL_IDX 5 +#define DPL_DATA_IDX 6 static struct usb_string qdss_string_defs[] = { - [QDSS_DATA_IDX].s = "QDSS DATA", - [QDSS_CTRL_IDX].s = "QDSS CTRL", + [QDSS_DATA_IDX].s = "QDSS Data", + [QDSS_CTRL_IDX].s = "QDSS Control", + [MSM_QDSS_DATA_IDX].s = "MSM QDSS Data", + [MSM_QDSS_CTRL_IDX].s = "MSM QDSS Control", + [MDM_QDSS_DATA_IDX].s = "MDM QDSS Data", + [MDM_QDSS_CTRL_IDX].s = "MDM QDSS Control", + [DPL_DATA_IDX].s = "DPL Data", {}, /* end of list */ }; @@ -990,6 +1000,7 @@ static int qdss_bind_config(struct usb_configuration *c, unsigned char portno) char *name; enum transport_type dxport; struct usb_function *f; + int str_data_id, str_ctrl_id; dxport = qdss_ports[portno].data_xport; @@ -1001,17 +1012,30 @@ static int qdss_bind_config(struct usb_configuration *c, unsigned char portno) } qdss = qdss_ports[portno].port; - if (qdss_string_defs[QDSS_DATA_IDX].id == 0) { + str_data_id = QDSS_DATA_IDX; + str_ctrl_id = QDSS_CTRL_IDX; + + if (dxport == USB_GADGET_XPORT_BAM2BAM) { + str_data_id = MSM_QDSS_DATA_IDX; + str_ctrl_id = MSM_QDSS_CTRL_IDX; + } else if (dxport == USB_GADGET_XPORT_PCIE) { + str_data_id = MDM_QDSS_DATA_IDX; + str_ctrl_id = MDM_QDSS_CTRL_IDX; + } else if (dxport == USB_GADGET_XPORT_ETHER) { + str_data_id = DPL_DATA_IDX; + } + + if (qdss_string_defs[str_data_id].id == 0) { status = usb_string_id(c->cdev); if (status < 0) return status; - qdss_string_defs[QDSS_DATA_IDX].id = status; + qdss_string_defs[str_data_id].id = status; qdss_data_intf_desc.iInterface = status; if (qdss->debug_inface_enabled) { status = usb_string_id(c->cdev); if (status < 0) return status; - qdss_string_defs[QDSS_CTRL_IDX].id = status; + qdss_string_defs[str_ctrl_id].id = status; qdss_ctrl_intf_desc.iInterface = status; } } -- GitLab From 523a387bb0c02af078137100235ae6ead801e64d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 13 Dec 2016 11:09:16 +0100 Subject: [PATCH 1117/5498] drm: bridge: add DT bindings for TI ths8135 [ Upstream commit 2e644be30fcc08c736f66b60f4898d274d4873ab ] THS8135 is a configurable video DAC. Add DT bindings for this chip. Signed-off-by: Bartosz Golaszewski Reviewed-by: Laurent Pinchart Acked-by: Rob Herring Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1481623759-12786-3-git-send-email-bgolaszewski@baylibre.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../bindings/display/bridge/ti,ths8135.txt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt b/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt new file mode 100644 index 000000000000..6ec1a880ac18 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt @@ -0,0 +1,46 @@ +THS8135 Video DAC +----------------- + +This is the binding for Texas Instruments THS8135 Video DAC bridge. + +Required properties: + +- compatible: Must be "ti,ths8135" + +Required nodes: + +This device has two video ports. Their connections are modelled using the OF +graph bindings specified in Documentation/devicetree/bindings/graph.txt. + +- Video port 0 for RGB input +- Video port 1 for VGA output + +Example +------- + +vga-bridge { + compatible = "ti,ths8135"; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + vga_bridge_in: endpoint { + remote-endpoint = <&lcdc_out_vga>; + }; + }; + + port@1 { + reg = <1>; + + vga_bridge_out: endpoint { + remote-endpoint = <&vga_con_in>; + }; + }; + }; +}; -- GitLab From 23e5fd9c55ca77c5d5557b565b43506ddb1fba74 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 18 Feb 2016 20:06:47 -0800 Subject: [PATCH 1118/5498] RDS: RDMA: Fix the composite message user notification [ Upstream commit 941f8d55f6d613a460a5e080d25a38509f45eb75 ] When application sends an RDS RDMA composite message consist of RDMA transfer to be followed up by non RDMA payload, it expect to be notified *only* when the full message gets delivered. RDS RDMA notification doesn't behave this way though. Thanks to Venkat for debug and root casuing the issue where only first part of the message(RDMA) was successfully delivered but remainder payload delivery failed. In that case, application should not be notified with a false positive of message delivery success. Fix this case by making sure the user gets notified only after the full message delivery. Reviewed-by: Venkat Venkatsubra Signed-off-by: Santosh Shilimkar Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/rds/ib_send.c | 25 +++++++++++++++---------- net/rds/rdma.c | 10 ++++++++++ net/rds/rds.h | 1 + net/rds/send.c | 4 +++- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c index 1dde91e3dc70..5deecf4cce71 100644 --- a/net/rds/ib_send.c +++ b/net/rds/ib_send.c @@ -102,16 +102,6 @@ static void rds_ib_send_complete(struct rds_message *rm, complete(rm, notify_status); } -static void rds_ib_send_unmap_data(struct rds_ib_connection *ic, - struct rm_data_op *op, - int wc_status) -{ - if (op->op_nents) - ib_dma_unmap_sg(ic->i_cm_id->device, - op->op_sg, op->op_nents, - DMA_TO_DEVICE); -} - static void rds_ib_send_unmap_rdma(struct rds_ib_connection *ic, struct rm_rdma_op *op, int wc_status) @@ -172,6 +162,21 @@ static void rds_ib_send_unmap_atomic(struct rds_ib_connection *ic, rds_ib_stats_inc(s_ib_atomic_fadd); } +static void rds_ib_send_unmap_data(struct rds_ib_connection *ic, + struct rm_data_op *op, + int wc_status) +{ + struct rds_message *rm = container_of(op, struct rds_message, data); + + if (op->op_nents) + ib_dma_unmap_sg(ic->i_cm_id->device, + op->op_sg, op->op_nents, + DMA_TO_DEVICE); + + if (rm->rdma.op_active && rm->data.op_notify) + rds_ib_send_unmap_rdma(ic, &rm->rdma, wc_status); +} + /* * Unmap the resources associated with a struct send_work. * diff --git a/net/rds/rdma.c b/net/rds/rdma.c index 40084d843e9f..3738b1920c09 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -625,6 +625,16 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm, } op->op_notifier->n_user_token = args->user_token; op->op_notifier->n_status = RDS_RDMA_SUCCESS; + + /* Enable rmda notification on data operation for composite + * rds messages and make sure notification is enabled only + * for the data operation which follows it so that application + * gets notified only after full message gets delivered. + */ + if (rm->data.op_sg) { + rm->rdma.op_notify = 0; + rm->data.op_notify = !!(args->flags & RDS_RDMA_NOTIFY_ME); + } } /* The cookie contains the R_Key of the remote memory region, and diff --git a/net/rds/rds.h b/net/rds/rds.h index 48f8ffc60f8f..42af715b63fc 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -360,6 +360,7 @@ struct rds_message { } rdma; struct rm_data_op { unsigned int op_active:1; + unsigned int op_notify:1; unsigned int op_nents; unsigned int op_count; struct scatterlist *op_sg; diff --git a/net/rds/send.c b/net/rds/send.c index 0bae8d43b012..45b800c3cc83 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -425,12 +425,14 @@ void rds_rdma_send_complete(struct rds_message *rm, int status) struct rm_rdma_op *ro; struct rds_notifier *notifier; unsigned long flags; + unsigned int notify = 0; spin_lock_irqsave(&rm->m_rs_lock, flags); + notify = rm->rdma.op_notify | rm->data.op_notify; ro = &rm->rdma; if (test_bit(RDS_MSG_ON_SOCK, &rm->m_flags) && - ro->op_active && ro->op_notify && ro->op_notifier) { + ro->op_active && notify && ro->op_notifier) { notifier = ro->op_notifier; rs = rm->m_rs; sock_hold(rds_rs_to_sk(rs)); -- GitLab From 9ff3b35e994da8688786a918a5938668eee94652 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Mon, 7 Nov 2016 11:52:19 +0000 Subject: [PATCH 1119/5498] MIPS: Ensure bss section ends on a long-aligned address [ Upstream commit 3f00f4d8f083bc61005d0a1ef592b149f5c88bbd ] When clearing the .bss section in kernel_entry we do so using LONG_S instructions, and branch whilst the current write address doesn't equal the end of the .bss section minus the size of a long integer. The .bss section always begins at a long-aligned address and we always increment the write pointer by the size of a long integer - we therefore rely upon the .bss section ending at a long-aligned address. If this is not the case then the long-aligned write address can never be equal to the non-long-aligned end address & we will continue to increment past the end of the .bss section, attempting to zero the rest of memory. Despite this requirement that .bss end at a long-aligned address we pass 0 as the end alignment requirement to the BSS_SECTION macro and thus don't guarantee any particular alignment, allowing us to hit the error condition described above. Fix this by instead passing 8 bytes as the end alignment argument to the BSS_SECTION macro, ensuring that the end of the .bss section is always at least long-aligned. Signed-off-by: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/14526/ Signed-off-by: Ralf Baechle Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/vmlinux.lds.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index 3b46f7ce9ca7..77733b403c09 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -141,7 +141,7 @@ SECTIONS * Force .bss to 64K alignment so that .bss..swapper_pg_dir * gets that alignment. .sbss should be empty, so there will be * no holes after __init_end. */ - BSS_SECTION(0, 0x10000, 0) + BSS_SECTION(0, 0x10000, 8) _end = . ; -- GitLab From ff0377a9882504d67b0d4ebf3fa1b480868dc73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Mon, 9 Jan 2017 16:34:04 +0100 Subject: [PATCH 1120/5498] sh_eth: use correct name for ECMR_MPDE bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6dcf45e514974a1ff10755015b5e06746a033e5f ] This bit was wrongly named due to a typo, Sergei checked the SH7734/63 manuals and this bit should be named MPDE. Suggested-by: Sergei Shtylyov Signed-off-by: Niklas Söderlund Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/renesas/sh_eth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 22301bf9c21d..be909fe4fc7a 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -326,7 +326,7 @@ enum FELIC_MODE_BIT { ECMR_DPAD = 0x00200000, ECMR_RZPF = 0x00100000, ECMR_ZPF = 0x00080000, ECMR_PFR = 0x00040000, ECMR_RXF = 0x00020000, ECMR_TXF = 0x00010000, ECMR_MCT = 0x00002000, ECMR_PRCEF = 0x00001000, - ECMR_PMDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020, + ECMR_MPDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020, ECMR_RTM = 0x00000010, ECMR_ILB = 0x00000008, ECMR_ELB = 0x00000004, ECMR_DM = 0x00000002, ECMR_PRM = 0x00000001, }; -- GitLab From f905dad52252c1858327ae9ebed3692f24ee312a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 27 Dec 2016 14:15:07 -0800 Subject: [PATCH 1121/5498] hwmon: (gl520sm) Fix overflows and crash seen when writing into limit attributes [ Upstream commit 87cdfa9d60f4f40e6d71b04b10b36d9df3c89282 ] Writes into limit attributes can overflow due to multplications and additions with unbound input values. Writing into fan limit attributes can result in a crash with a division by zero if very large values are written and the fan divider is larger than 1. Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/gl520sm.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index dee93ec87d02..84e0994aafdd 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -208,11 +208,13 @@ static ssize_t get_cpu_vid(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(cpu0_vid, S_IRUGO, get_cpu_vid, NULL); -#define VDD_FROM_REG(val) (((val) * 95 + 2) / 4) -#define VDD_TO_REG(val) clamp_val((((val) * 4 + 47) / 95), 0, 255) +#define VDD_FROM_REG(val) DIV_ROUND_CLOSEST((val) * 95, 4) +#define VDD_CLAMP(val) clamp_val(val, 0, 255 * 95 / 4) +#define VDD_TO_REG(val) DIV_ROUND_CLOSEST(VDD_CLAMP(val) * 4, 95) -#define IN_FROM_REG(val) ((val) * 19) -#define IN_TO_REG(val) clamp_val((((val) + 9) / 19), 0, 255) +#define IN_FROM_REG(val) ((val) * 19) +#define IN_CLAMP(val) clamp_val(val, 0, 255 * 19) +#define IN_TO_REG(val) DIV_ROUND_CLOSEST(IN_CLAMP(val), 19) static ssize_t get_in_input(struct device *dev, struct device_attribute *attr, char *buf) @@ -349,8 +351,13 @@ static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR, #define DIV_FROM_REG(val) (1 << (val)) #define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) << (div)))) -#define FAN_TO_REG(val, div) ((val) <= 0 ? 0 : \ - clamp_val((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)) + +#define FAN_BASE(div) (480000 >> (div)) +#define FAN_CLAMP(val, div) clamp_val(val, FAN_BASE(div) / 255, \ + FAN_BASE(div)) +#define FAN_TO_REG(val, div) ((val) == 0 ? 0 : \ + DIV_ROUND_CLOSEST(480000, \ + FAN_CLAMP(val, div) << (div))) static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr, char *buf) @@ -513,9 +520,9 @@ static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, static DEVICE_ATTR(fan1_off, S_IRUGO | S_IWUSR, get_fan_off, set_fan_off); -#define TEMP_FROM_REG(val) (((val) - 130) * 1000) -#define TEMP_TO_REG(val) clamp_val(((((val) < 0 ? \ - (val) - 500 : (val) + 500) / 1000) + 130), 0, 255) +#define TEMP_FROM_REG(val) (((val) - 130) * 1000) +#define TEMP_CLAMP(val) clamp_val(val, -130000, 125000) +#define TEMP_TO_REG(val) (DIV_ROUND_CLOSEST(TEMP_CLAMP(val), 1000) + 130) static ssize_t get_temp_input(struct device *dev, struct device_attribute *attr, char *buf) -- GitLab From 8c28f327914153bba849ddefc0d84699471ae295 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Sat, 7 Jan 2017 17:48:10 +0100 Subject: [PATCH 1122/5498] ARM: 8635/1: nommu: allow enabling REMAP_VECTORS_TO_RAM [ Upstream commit 8a792e9afbce84a0fdaf213fe42bb97382487094 ] REMAP_VECTORS_TO_RAM depends on DRAM_BASE, but since DRAM_BASE is a hex, REMAP_VECTORS_TO_RAM could never get enabled. Also depending on DRAM_BASE is redundant as whenever REMAP_VECTORS_TO_RAM makes itself available to Kconfig, DRAM_BASE also is available as the Kconfig gets sourced on !MMU. Signed-off-by: Afzal Mohammed Reviewed-by: Vladimir Murzin Signed-off-by: Russell King Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/Kconfig-nommu | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu index aed66d5df7f1..b7576349528c 100644 --- a/arch/arm/Kconfig-nommu +++ b/arch/arm/Kconfig-nommu @@ -34,8 +34,7 @@ config PROCESSOR_ID used instead of the auto-probing which utilizes the register. config REMAP_VECTORS_TO_RAM - bool 'Install vectors to the beginning of RAM' if DRAM_BASE - depends on DRAM_BASE + bool 'Install vectors to the beginning of RAM' help The kernel needs to change the hardware exception vectors. In nommu mode, the hardware exception vectors are normally -- GitLab From 0961072120f3e40fe98c2bb49c45549ca3f042dc Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 9 Jan 2017 01:26:37 +0100 Subject: [PATCH 1123/5498] tty: goldfish: Fix a parameter of a call to free_irq [ Upstream commit 1a5c2d1de7d35f5eb9793266237903348989502b ] 'request_irq()' and 'free_irq()' should be called with the same dev_id. Signed-off-by: Christophe JAILLET Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/goldfish.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 09495f515fa9..beb3142a1414 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -295,7 +295,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) tty_unregister_device(goldfish_tty_driver, i); err_tty_register_device_failed: - free_irq(irq, pdev); + free_irq(irq, qtty); err_request_irq_failed: goldfish_tty_current_line_count--; if (goldfish_tty_current_line_count == 0) -- GitLab From 6fc261c1e77d6b3cce0a2bcfa95734b908c30615 Mon Sep 17 00:00:00 2001 From: Feras Daoud Date: Wed, 28 Dec 2016 14:47:22 +0200 Subject: [PATCH 1124/5498] IB/ipoib: Fix deadlock over vlan_mutex [ Upstream commit 1c3098cdb05207e740715857df7b0998e372f527 ] This patch fixes Deadlock while executing ipoib_vlan_delete. The function takes the vlan_rwsem semaphore and calls unregister_netdevice. The later function calls ipoib_mcast_stop_thread that cause workqueue flush. When the queue has one of the ipoib_ib_dev_flush_xxx events, a deadlock occur because these events also tries to catch the same vlan_rwsem semaphore. To fix, unregister_netdevice should be called after releasing the semaphore. Fixes: cbbe1efa4972 ("IPoIB: Fix deadlock between ipoib_open() and child interface create") Signed-off-by: Feras Daoud Signed-off-by: Erez Shitrit Reviewed-by: Alex Vesker Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/ipoib/ipoib_vlan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index c995681befee..cae1b8223abe 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -187,7 +187,6 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey) list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) { if (priv->pkey == pkey && priv->child_type == IPOIB_LEGACY_CHILD) { - unregister_netdevice(priv->dev); list_del(&priv->list); dev = priv->dev; break; @@ -195,6 +194,11 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey) } up_write(&ppriv->vlan_rwsem); + if (dev) { + ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name); + unregister_netdevice(dev); + } + rtnl_unlock(); if (dev) { -- GitLab From 8d8a5bbc3976097e472ab4fbc2ccb5c0ff655b7f Mon Sep 17 00:00:00 2001 From: Feras Daoud Date: Wed, 28 Dec 2016 14:47:24 +0200 Subject: [PATCH 1125/5498] IB/ipoib: rtnl_unlock can not come after free_netdev [ Upstream commit 89a3987ab7a923c047c6dec008e60ad6f41fac22 ] The ipoib_vlan_add function calls rtnl_unlock after free_netdev, rtnl_unlock not only releases the lock, but also calls netdev_run_todo. The latter function browses the net_todo_list array and completes the unregistration of all its net_device instances. If we call free_netdev before rtnl_unlock, then netdev_run_todo call over the freed device causes panic. To fix, move rtnl_unlock call before free_netdev call. Fixes: 9baa0b036410 ("IB/ipoib: Add rtnl_link_ops support") Cc: Or Gerlitz Signed-off-by: Feras Daoud Signed-off-by: Erez Shitrit Reviewed-by: Yuval Shaia Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/ipoib/ipoib_vlan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index cae1b8223abe..67182d4e8138 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -162,11 +162,11 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) out: up_write(&ppriv->vlan_rwsem); + rtnl_unlock(); + if (result) free_netdev(priv->dev); - rtnl_unlock(); - return result; } -- GitLab From 7e4194e7e7f4d744b0de96843a3a645c455d60a9 Mon Sep 17 00:00:00 2001 From: Feras Daoud Date: Wed, 28 Dec 2016 14:47:27 +0200 Subject: [PATCH 1126/5498] IB/ipoib: Replace list_del of the neigh->list with list_del_init [ Upstream commit c586071d1dc8227a7182179b8e50ee92cc43f6d2 ] In order to resolve a situation where a few process delete the same list element in sequence and cause panic, list_del is replaced with list_del_init. In this case if the first process that calls list_del releases the lock before acquiring it again, other processes who can acquire the lock will call list_del_init. Fixes: b63b70d87741 ("IPoIB: Use a private hash table for path lookup") Signed-off-by: Feras Daoud Signed-off-by: Erez Shitrit Reviewed-by: Alex Vesker Signed-off-by: Leon Romanovsky Reviewed-by: Yuval Shaia Signed-off-by: Doug Ledford Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/ipoib/ipoib_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 5e08db6f9d8c..eab9eda0e7bd 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -958,7 +958,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from path/mc list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); } else { np = &neigh->hnext; @@ -1121,7 +1121,7 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from parent list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); return; } else { @@ -1206,7 +1206,7 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from parent list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); } else { np = &neigh->hnext; @@ -1248,7 +1248,7 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from path/mc list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); } } -- GitLab From 920027fef71971daf64658c6f6bbfdebde161b0a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:17 +0100 Subject: [PATCH 1127/5498] USB: serial: mos7720: fix control-message error handling [ Upstream commit 0d130367abf582e7cbf60075c2a7ab53817b1d14 ] Make sure to log an error on short transfers when reading a device register. Also clear the provided buffer (which if often an uninitialised automatic variable) on errors as the driver currently does not bother to check for errors. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7720.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index c3b8ae360424..5e3a609fbab4 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -236,11 +236,16 @@ static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, status = usb_control_msg(usbdev, pipe, request, requesttype, value, index, buf, 1, MOS_WDR_TIMEOUT); - if (status == 1) + if (status == 1) { *data = *buf; - else if (status < 0) + } else { dev_err(&usbdev->dev, "mos7720: usb_control_msg() failed: %d\n", status); + if (status >= 0) + status = -EIO; + *data = 0; + } + kfree(buf); return status; -- GitLab From 61e43da985aa188b075e275e1297131df0573aef Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:18 +0100 Subject: [PATCH 1128/5498] USB: serial: mos7840: fix control-message error handling [ Upstream commit cd8db057e93ddaacbec025b567490555d2bca280 ] Make sure to detect short transfers when reading a device register. The modem-status handling had sufficient error checks in place, but move handling of short transfers into the register accessor function itself for consistency. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mos7840.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 0b547d9e2aeb..a0dca81dfaca 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -285,9 +285,15 @@ static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg, ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); + if (ret < VENDOR_READ_LENGTH) { + if (ret >= 0) + ret = -EIO; + goto out; + } + *val = buf[0]; dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val); - +out: kfree(buf); return ret; } @@ -353,8 +359,13 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); + if (ret < VENDOR_READ_LENGTH) { + if (ret >= 0) + ret = -EIO; + goto out; + } *val = buf[0]; - +out: kfree(buf); return ret; } @@ -1518,10 +1529,10 @@ static int mos7840_tiocmget(struct tty_struct *tty) return -ENODEV; status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr); - if (status != 1) + if (status < 0) return -EIO; status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr); - if (status != 1) + if (status < 0) return -EIO; result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) -- GitLab From 40e63fb87a3f9fdf13ee19eafdc52f013068c4a8 Mon Sep 17 00:00:00 2001 From: Alden Tondettar Date: Sun, 15 Jan 2017 15:31:56 -0700 Subject: [PATCH 1129/5498] partitions/efi: Fix integer overflow in GPT size calculation [ Upstream commit c5082b70adfe8e1ea1cf4a8eff92c9f260e364d2 ] If a GUID Partition Table claims to have more than 2**25 entries, the calculation of the partition table size in alloc_read_gpt_entries() will overflow a 32-bit integer and not enough space will be allocated for the table. Nothing seems to get written out of bounds, but later efi_partition() will read up to 32768 bytes from a 128 byte buffer, possibly OOPSing or exposing information to /proc/partitions and uevents. The problem exists on both 64-bit and 32-bit platforms. Fix the overflow and also print a meaningful debug message if the table size is too large. Signed-off-by: Alden Tondettar Acked-by: Ard Biesheuvel Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- block/partitions/efi.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/block/partitions/efi.c b/block/partitions/efi.c index 56d08fd75b1a..8de885e1e0c6 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -293,7 +293,7 @@ static gpt_entry *alloc_read_gpt_entries(struct parsed_partitions *state, if (!gpt) return NULL; - count = le32_to_cpu(gpt->num_partition_entries) * + count = (size_t)le32_to_cpu(gpt->num_partition_entries) * le32_to_cpu(gpt->sizeof_partition_entry); if (!count) return NULL; @@ -352,7 +352,7 @@ static int is_gpt_valid(struct parsed_partitions *state, u64 lba, gpt_header **gpt, gpt_entry **ptes) { u32 crc, origcrc; - u64 lastlba; + u64 lastlba, pt_size; if (!ptes) return 0; @@ -434,13 +434,20 @@ static int is_gpt_valid(struct parsed_partitions *state, u64 lba, goto fail; } + /* Sanity check partition table size */ + pt_size = (u64)le32_to_cpu((*gpt)->num_partition_entries) * + le32_to_cpu((*gpt)->sizeof_partition_entry); + if (pt_size > KMALLOC_MAX_SIZE) { + pr_debug("GUID Partition Table is too large: %llu > %lu bytes\n", + (unsigned long long)pt_size, KMALLOC_MAX_SIZE); + goto fail; + } + if (!(*ptes = alloc_read_gpt_entries(state, *gpt))) goto fail; /* Check the GUID Partition Entry Array CRC */ - crc = efi_crc32((const unsigned char *) (*ptes), - le32_to_cpu((*gpt)->num_partition_entries) * - le32_to_cpu((*gpt)->sizeof_partition_entry)); + crc = efi_crc32((const unsigned char *) (*ptes), pt_size); if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) { pr_debug("GUID Partitition Entry Array CRC check failed.\n"); -- GitLab From 4c963eff7d9721bca8a3bce19b7733c8b2261685 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Tue, 17 Jan 2017 11:07:15 -0500 Subject: [PATCH 1130/5498] audit: log 32-bit socketcalls [ Upstream commit 62bc306e2083436675e33b5bdeb6a77907d35971 ] 32-bit socketcalls were not being logged by audit on x86_64 systems. Log them. This is basically a duplicate of the call from net/socket.c:sys_socketcall(), but it addresses the impedance mismatch between 32-bit userspace process and 64-bit kernel audit. See: https://github.com/linux-audit/audit-kernel/issues/14 Signed-off-by: Richard Guy Briggs Acked-by: David S. Miller Signed-off-by: Paul Moore Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/audit.h | 20 ++++++++++++++++++++ net/compat.c | 17 ++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 10f155b7daf6..ba78e12bef53 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -273,6 +273,20 @@ static inline int audit_socketcall(int nargs, unsigned long *args) return __audit_socketcall(nargs, args); return 0; } + +static inline int audit_socketcall_compat(int nargs, u32 *args) +{ + unsigned long a[AUDITSC_ARGS]; + int i; + + if (audit_dummy_context()) + return 0; + + for (i = 0; i < nargs; i++) + a[i] = (unsigned long)args[i]; + return __audit_socketcall(nargs, a); +} + static inline int audit_sockaddr(int len, void *addr) { if (unlikely(!audit_dummy_context())) @@ -398,6 +412,12 @@ static inline int audit_socketcall(int nargs, unsigned long *args) { return 0; } + +static inline int audit_socketcall_compat(int nargs, u32 *args) +{ + return 0; +} + static inline void audit_fd_pair(int fd1, int fd2) { } static inline int audit_sockaddr(int len, void *addr) diff --git a/net/compat.c b/net/compat.c index 53e933eb78b8..3bb039e92744 100644 --- a/net/compat.c +++ b/net/compat.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -796,14 +797,24 @@ COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) { - int ret; - u32 a[6]; + u32 a[AUDITSC_ARGS]; + unsigned int len; u32 a0, a1; + int ret; if (call < SYS_SOCKET || call > SYS_SENDMMSG) return -EINVAL; - if (copy_from_user(a, args, nas[call])) + len = nas[call]; + if (len > sizeof(a)) + return -EINVAL; + + if (copy_from_user(a, args, len)) return -EFAULT; + + ret = audit_socketcall_compat(len / sizeof(a[0]), a); + if (ret) + return ret; + a0 = a[0]; a1 = a[1]; -- GitLab From 1728786b2b59b21f33c1d48bc6712779c7303c03 Mon Sep 17 00:00:00 2001 From: Myungho Jung Date: Tue, 25 Apr 2017 11:58:15 -0700 Subject: [PATCH 1131/5498] net: core: Prevent from dereferencing null pointer when releasing SKB [ Upstream commit 9899886d5e8ec5b343b1efe44f185a0e68dc6454 ] Added NULL check to make __dev_kfree_skb_irq consistent with kfree family of functions. Link: https://bugzilla.kernel.org/show_bug.cgi?id=195289 Signed-off-by: Myungho Jung Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/core/dev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index 22ad4e433b3a..21b0bd976699 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2225,6 +2225,9 @@ void __dev_kfree_skb_irq(struct sk_buff *skb, enum skb_free_reason reason) { unsigned long flags; + if (unlikely(!skb)) + return; + if (likely(atomic_read(&skb->users) == 1)) { smp_rmb(); atomic_set(&skb->users, 0); -- GitLab From fbbb384f5208cd9076c3619ba702771eed4731cf Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Tue, 25 Apr 2017 18:51:46 +0200 Subject: [PATCH 1132/5498] net/packet: check length in getsockopt() called with PACKET_HDRLEN [ Upstream commit fd2c83b35752f0a8236b976978ad4658df14a59f ] In the case getsockopt() is called with PACKET_HDRLEN and optlen < 4 |val| remains uninitialized and the syscall may behave differently depending on its value, and even copy garbage to userspace on certain architectures. To fix this we now return -EINVAL if optlen is too small. This bug has been detected with KMSAN. Signed-off-by: Alexander Potapenko Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/packet/af_packet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 1c03d83edd7e..7effa07c4491 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3482,6 +3482,8 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, case PACKET_HDRLEN: if (len > sizeof(int)) len = sizeof(int); + if (len < sizeof(int)) + return -EINVAL; if (copy_from_user(&val, optval, len)) return -EFAULT; switch (val) { -- GitLab From eafffc0aabc1431a7b13f9d6fcbc6cf086f513aa Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Mon, 24 Apr 2017 18:29:16 +0800 Subject: [PATCH 1133/5498] team: fix memory leaks [ Upstream commit 72ec0bc64b9a5d8e0efcb717abfc757746b101b7 ] In functions team_nl_send_port_list_get() and team_nl_send_options_get(), pointer skb keeps the return value of nlmsg_new(). When the call to genlmsg_put() fails, the memory is not freed(). This will result in memory leak bugs. Fixes: 9b00cf2d1024 ("team: implement multipart netlink messages for options transfers") Signed-off-by: Pan Bian Acked-by: Jiri Pirko Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/team/team.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 16c580379e38..dae9dcb69c1a 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2331,8 +2331,10 @@ start_again: hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI, TEAM_CMD_OPTIONS_GET); - if (!hdr) + if (!hdr) { + nlmsg_free(skb); return -EMSGSIZE; + } if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) goto nla_put_failure; @@ -2599,8 +2601,10 @@ start_again: hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI, TEAM_CMD_PORT_LIST_GET); - if (!hdr) + if (!hdr) { + nlmsg_free(skb); return -EMSGSIZE; + } if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex)) goto nla_put_failure; -- GitLab From 0ee6c8e7875807fff8f776a50b2299cfa8a7d6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Spycha=C5=82a?= Date: Thu, 20 Apr 2017 12:04:10 +0200 Subject: [PATCH 1134/5498] usb: plusb: Add support for PL-27A1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6f2aee0c0de65013333bbc26fe50c9c7b09a37f7 ] This patch adds support for the PL-27A1 by adding the appropriate USB ID's. This chip is used in the goobay Active USB 3.0 Data Link and Unitek Y-3501 cables. Signed-off-by: Roman SpychaÅ‚a Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/Kconfig | 2 +- drivers/net/usb/plusb.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 37eed4d84e9c..bf7553ed73bc 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -350,7 +350,7 @@ config USB_NET_NET1080 optionally with LEDs that indicate traffic config USB_NET_PLUSB - tristate "Prolific PL-2301/2302/25A1 based cables" + tristate "Prolific PL-2301/2302/25A1/27A1 based cables" # if the handshake/init/reset problems, from original 'plusb', # are ever resolved ... then remove "experimental" depends on USB_USBNET diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c index 1bfe0fcaccf5..7c02231c1a1b 100644 --- a/drivers/net/usb/plusb.c +++ b/drivers/net/usb/plusb.c @@ -102,7 +102,7 @@ static int pl_reset(struct usbnet *dev) } static const struct driver_info prolific_info = { - .description = "Prolific PL-2301/PL-2302/PL-25A1", + .description = "Prolific PL-2301/PL-2302/PL-25A1/PL-27A1", .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT, /* some PL-2302 versions seem to fail usb_set_interface() */ .reset = pl_reset, @@ -139,6 +139,17 @@ static const struct usb_device_id products [] = { * Host-to-Host Cable */ .driver_info = (unsigned long) &prolific_info, + +}, + +/* super speed cables */ +{ + USB_DEVICE(0x067b, 0x27a1), /* PL-27A1, no eeprom + * also: goobay Active USB 3.0 + * Data Link, + * Unitek Y-3501 + */ + .driver_info = (unsigned long) &prolific_info, }, { }, // END @@ -158,5 +169,5 @@ static struct usb_driver plusb_driver = { module_usb_driver(plusb_driver); MODULE_AUTHOR("David Brownell"); -MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver"); +MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1/27A1 USB Host to Host Link Driver"); MODULE_LICENSE("GPL"); -- GitLab From 0fe19e5c661621932a511b36143b25a0294e9ff5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 29 Mar 2017 20:54:37 +0200 Subject: [PATCH 1135/5498] mmc: sdio: fix alignment issue in struct sdio_func [ Upstream commit 5ef1ecf060f28ecef313b5723f1fd39bf5a35f56 ] Certain 64-bit systems (e.g. Amlogic Meson GX) require buffers to be used for DMA to be 8-byte-aligned. struct sdio_func has an embedded small DMA buffer not meeting this requirement. When testing switching to descriptor chain mode in meson-gx driver SDIO is broken therefore. Fix this by allocating the small DMA buffer separately as kmalloc ensures that the returned memory area is properly aligned for every basic data type. Signed-off-by: Heiner Kallweit Tested-by: Helmut Klein Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/sdio_bus.c | 12 +++++++++++- include/linux/mmc/sdio_func.h | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 6da97b170563..8d246f815658 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -265,7 +265,7 @@ static void sdio_release_func(struct device *dev) sdio_free_func_cis(func); kfree(func->info); - + kfree(func->tmpbuf); kfree(func); } @@ -280,6 +280,16 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card) if (!func) return ERR_PTR(-ENOMEM); + /* + * allocate buffer separately to make sure it's properly aligned for + * DMA usage (incl. 64 bit DMA) + */ + func->tmpbuf = kmalloc(4, GFP_KERNEL); + if (!func->tmpbuf) { + kfree(func); + return ERR_PTR(-ENOMEM); + } + func->card = card; device_initialize(&func->dev); diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 50f0bc952328..2d6cc69db1fe 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -53,7 +53,7 @@ struct sdio_func { unsigned int state; /* function state */ #define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */ - u8 tmpbuf[4]; /* DMA:able scratch buffer */ + u8 *tmpbuf; /* DMA:able scratch buffer */ unsigned num_info; /* number of info strings */ const char **info; /* info strings */ -- GitLab From c8cab6f202a1cecb5217706b6df631c87daf39e1 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sat, 25 Mar 2017 08:53:12 +0800 Subject: [PATCH 1136/5498] netfilter: invoke synchronize_rcu after set the _hook_ to NULL [ Upstream commit 3b7dabf029478bb80507a6c4500ca94132a2bc0b ] Otherwise, another CPU may access the invalid pointer. For example: CPU0 CPU1 - rcu_read_lock(); - pfunc = _hook_; _hook_ = NULL; - mod unload - - pfunc(); // invalid, panic - rcu_read_unlock(); So we must call synchronize_rcu() to wait the rcu reader to finish. Also note, in nf_nat_snmp_basic_fini, synchronize_rcu() will be invoked by later nf_conntrack_helper_unregister, but I'm inclined to add a explicit synchronize_rcu after set the nf_nat_snmp_hook to NULL. Depend on such obscure assumptions is not a good idea. Last, in nfnetlink_cttimeout, we use kfree_rcu to free the time object, so in cttimeout_exit, invoking rcu_barrier() is not necessary at all, remove it too. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/nf_nat_snmp_basic.c | 1 + net/netfilter/nf_conntrack_ecache.c | 2 ++ net/netfilter/nf_conntrack_netlink.c | 1 + net/netfilter/nf_nat_core.c | 2 ++ net/netfilter/nfnetlink_cttimeout.c | 1 + 5 files changed, 7 insertions(+) diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c index 7c676671329d..cc626e1b06d3 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c @@ -1304,6 +1304,7 @@ static int __init nf_nat_snmp_basic_init(void) static void __exit nf_nat_snmp_basic_fini(void) { RCU_INIT_POINTER(nf_nat_snmp_hook, NULL); + synchronize_rcu(); nf_conntrack_helper_unregister(&snmp_trap_helper); } diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 4e78c57b818f..f3b92ce463b0 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -200,6 +200,7 @@ void nf_conntrack_unregister_notifier(struct net *net, BUG_ON(notify != new); RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL); mutex_unlock(&nf_ct_ecache_mutex); + /* synchronize_rcu() is called from ctnetlink_exit. */ } EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); @@ -236,6 +237,7 @@ void nf_ct_expect_unregister_notifier(struct net *net, BUG_ON(notify != new); RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL); mutex_unlock(&nf_ct_ecache_mutex); + /* synchronize_rcu() is called from ctnetlink_exit. */ } EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index cccc4aaf09be..de9ea452dd60 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3242,6 +3242,7 @@ static void __exit ctnetlink_exit(void) #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT RCU_INIT_POINTER(nfq_ct_hook, NULL); #endif + synchronize_rcu(); } module_init(ctnetlink_init); diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 4e0b47831d43..56e175efb66e 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -888,6 +888,8 @@ static void __exit nf_nat_cleanup(void) #ifdef CONFIG_XFRM RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL); #endif + synchronize_rcu(); + for (i = 0; i < NFPROTO_NUMPROTO; i++) kfree(nf_nat_l4protos[i]); synchronize_net(); diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 476accd17145..3dfe3b7271e4 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -578,6 +578,7 @@ static void __exit cttimeout_exit(void) #ifdef CONFIG_NF_CONNTRACK_TIMEOUT RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL); RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL); + synchronize_rcu(); #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ } -- GitLab From b05c24d64d4c1a2534a1db469caa17b7ba2f50e1 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 1 Feb 2017 18:05:21 -0200 Subject: [PATCH 1137/5498] exynos-gsc: Do not swap cb/cr for semi planar formats [ Upstream commit d7f3e33df4fbdc9855fb151f4a328ec46447e3ba ] In the case of semi planar formats cb and cr are in the same plane in memory, meaning that will be set to 'cb' whatever the format is, and whatever the (packed) order of those components are. Suggested-by: Nicolas Dufresne Signed-off-by: Thibault Saunier Signed-off-by: Javier Martinez Canillas Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/platform/exynos-gsc/gsc-core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index b4c9f1d08968..fc1716f08777 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -846,9 +846,7 @@ int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, if ((frame->fmt->pixelformat == V4L2_PIX_FMT_VYUY) || (frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) || - (frame->fmt->pixelformat == V4L2_PIX_FMT_NV61) || (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) || - (frame->fmt->pixelformat == V4L2_PIX_FMT_NV21) || (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M)) swap(addr->cb, addr->cr); -- GitLab From 0a1b807cb2b6a5c05e6b22f7d0f3801ab36118d7 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sun, 19 Mar 2017 22:35:59 +0800 Subject: [PATCH 1138/5498] netfilter: nfnl_cthelper: fix incorrect helper->expect_class_max [ Upstream commit ae5c682113f9f94cc5e76f92cf041ee624c173ee ] The helper->expect_class_max must be set to the total number of expect_policy minus 1, since we will use the statement "if (class > helper->expect_class_max)" to validate the CTA_EXPECT_CLASS attr in ctnetlink_alloc_expect. So for compatibility, set the helper->expect_class_max to the NFCTH_POLICY_SET_NUM attr's value minus 1. Also: it's invalid when the NFCTH_POLICY_SET_NUM attr's value is zero. 1. this will result "expect_policy = kzalloc(0, GFP_KERNEL);"; 2. we cannot set the helper->expect_class_max to a proper value. So if nla_get_be32(tb[NFCTH_POLICY_SET_NUM]) is zero, report -EINVAL to the userspace. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 54330fb5efaf..6d10002d23f8 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -161,6 +161,7 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, int i, ret; struct nf_conntrack_expect_policy *expect_policy; struct nlattr *tb[NFCTH_POLICY_SET_MAX+1]; + unsigned int class_max; ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr, nfnl_cthelper_expect_policy_set); @@ -170,19 +171,18 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, if (!tb[NFCTH_POLICY_SET_NUM]) return -EINVAL; - helper->expect_class_max = - ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM])); - - if (helper->expect_class_max != 0 && - helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES) + class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM])); + if (class_max == 0) + return -EINVAL; + if (class_max > NF_CT_MAX_EXPECT_CLASSES) return -EOVERFLOW; expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) * - helper->expect_class_max, GFP_KERNEL); + class_max, GFP_KERNEL); if (expect_policy == NULL) return -ENOMEM; - for (i=0; iexpect_class_max; i++) { + for (i = 0; i < class_max; i++) { if (!tb[NFCTH_POLICY_SET+i]) goto err; @@ -191,6 +191,8 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, if (ret < 0) goto err; } + + helper->expect_class_max = class_max - 1; helper->expect_policy = expect_policy; return 0; err: @@ -377,10 +379,10 @@ nfnl_cthelper_dump_policy(struct sk_buff *skb, goto nla_put_failure; if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM, - htonl(helper->expect_class_max))) + htonl(helper->expect_class_max + 1))) goto nla_put_failure; - for (i=0; iexpect_class_max; i++) { + for (i = 0; i < helper->expect_class_max + 1; i++) { nest_parms2 = nla_nest_start(skb, (NFCTH_POLICY_SET+i) | NLA_F_NESTED); if (nest_parms2 == NULL) -- GitLab From 22126c602203f6c18ec71449d924db92014a6b28 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 14 Mar 2017 15:24:51 +0530 Subject: [PATCH 1139/5498] parisc: perf: Fix potential NULL pointer dereference [ Upstream commit 74e3f6e63da6c8e8246fba1689e040bc926b4a1a ] Fix potential NULL pointer dereference and clean up coding style errors (code indent, trailing whitespaces). Signed-off-by: Arvind Yadav Signed-off-by: Helge Deller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/perf.c | 94 ++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c index ba0c053e25ae..2a2fff407ac4 100644 --- a/arch/parisc/kernel/perf.c +++ b/arch/parisc/kernel/perf.c @@ -39,7 +39,7 @@ * the PDC INTRIGUE calls. This is done to eliminate bugs introduced * in various PDC revisions. The code is much more maintainable * and reliable this way vs having to debug on every version of PDC - * on every box. + * on every box. */ #include @@ -195,8 +195,8 @@ static int perf_config(uint32_t *image_ptr); static int perf_release(struct inode *inode, struct file *file); static int perf_open(struct inode *inode, struct file *file); static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos); -static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, - loff_t *ppos); +static ssize_t perf_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos); static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static void perf_start_counters(void); static int perf_stop_counters(uint32_t *raddr); @@ -222,7 +222,7 @@ extern void perf_intrigue_disable_perf_counters (void); /* * configure: * - * Configure the cpu with a given data image. First turn off the counters, + * Configure the cpu with a given data image. First turn off the counters, * then download the image, then turn the counters back on. */ static int perf_config(uint32_t *image_ptr) @@ -234,7 +234,7 @@ static int perf_config(uint32_t *image_ptr) error = perf_stop_counters(raddr); if (error != 0) { printk("perf_config: perf_stop_counters = %ld\n", error); - return -EINVAL; + return -EINVAL; } printk("Preparing to write image\n"); @@ -242,7 +242,7 @@ printk("Preparing to write image\n"); error = perf_write_image((uint64_t *)image_ptr); if (error != 0) { printk("perf_config: DOWNLOAD = %ld\n", error); - return -EINVAL; + return -EINVAL; } printk("Preparing to start counters\n"); @@ -254,7 +254,7 @@ printk("Preparing to start counters\n"); } /* - * Open the device and initialize all of its memory. The device is only + * Open the device and initialize all of its memory. The device is only * opened once, but can be "queried" by multiple processes that know its * file descriptor. */ @@ -298,8 +298,8 @@ static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t * called on the processor that the download should happen * on. */ -static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, - loff_t *ppos) +static ssize_t perf_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { int err; size_t image_size; @@ -307,11 +307,11 @@ static ssize_t perf_write(struct file *file, const char __user *buf, size_t coun uint32_t interface_type; uint32_t test; - if (perf_processor_interface == ONYX_INTF) + if (perf_processor_interface == ONYX_INTF) image_size = PCXU_IMAGE_SIZE; - else if (perf_processor_interface == CUDA_INTF) + else if (perf_processor_interface == CUDA_INTF) image_size = PCXW_IMAGE_SIZE; - else + else return -EFAULT; if (!capable(CAP_SYS_ADMIN)) @@ -331,22 +331,22 @@ static ssize_t perf_write(struct file *file, const char __user *buf, size_t coun /* First check the machine type is correct for the requested image */ - if (((perf_processor_interface == CUDA_INTF) && - (interface_type != CUDA_INTF)) || - ((perf_processor_interface == ONYX_INTF) && - (interface_type != ONYX_INTF))) + if (((perf_processor_interface == CUDA_INTF) && + (interface_type != CUDA_INTF)) || + ((perf_processor_interface == ONYX_INTF) && + (interface_type != ONYX_INTF))) return -EINVAL; /* Next check to make sure the requested image is valid */ - if (((interface_type == CUDA_INTF) && + if (((interface_type == CUDA_INTF) && (test >= MAX_CUDA_IMAGES)) || - ((interface_type == ONYX_INTF) && - (test >= MAX_ONYX_IMAGES))) + ((interface_type == ONYX_INTF) && + (test >= MAX_ONYX_IMAGES))) return -EINVAL; /* Copy the image into the processor */ - if (interface_type == CUDA_INTF) + if (interface_type == CUDA_INTF) return perf_config(cuda_images[test]); else return perf_config(onyx_images[test]); @@ -360,7 +360,7 @@ static ssize_t perf_write(struct file *file, const char __user *buf, size_t coun static void perf_patch_images(void) { #if 0 /* FIXME!! */ -/* +/* * NOTE: this routine is VERY specific to the current TLB image. * If the image is changed, this routine might also need to be changed. */ @@ -368,9 +368,9 @@ static void perf_patch_images(void) extern void $i_dtlb_miss_2_0(); extern void PA2_0_iva(); - /* + /* * We can only use the lower 32-bits, the upper 32-bits should be 0 - * anyway given this is in the kernel + * anyway given this is in the kernel */ uint32_t itlb_addr = (uint32_t)&($i_itlb_miss_2_0); uint32_t dtlb_addr = (uint32_t)&($i_dtlb_miss_2_0); @@ -378,21 +378,21 @@ static void perf_patch_images(void) if (perf_processor_interface == ONYX_INTF) { /* clear last 2 bytes */ - onyx_images[TLBMISS][15] &= 0xffffff00; + onyx_images[TLBMISS][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[TLBMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[TLBMISS][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[TLBMISS][17] = itlb_addr; /* clear last 2 bytes */ - onyx_images[TLBHANDMISS][15] &= 0xffffff00; + onyx_images[TLBHANDMISS][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[TLBHANDMISS][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[TLBHANDMISS][16] = (dtlb_addr << 8)&0xffffff00; onyx_images[TLBHANDMISS][17] = itlb_addr; /* clear last 2 bytes */ - onyx_images[BIG_CPI][15] &= 0xffffff00; + onyx_images[BIG_CPI][15] &= 0xffffff00; /* set 2 bytes */ onyx_images[BIG_CPI][15] |= (0x000000ff&((dtlb_addr) >> 24)); onyx_images[BIG_CPI][16] = (dtlb_addr << 8)&0xffffff00; @@ -405,24 +405,24 @@ static void perf_patch_images(void) } else if (perf_processor_interface == CUDA_INTF) { /* Cuda interface */ - cuda_images[TLBMISS][16] = + cuda_images[TLBMISS][16] = (cuda_images[TLBMISS][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); - cuda_images[TLBMISS][17] = + cuda_images[TLBMISS][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[TLBMISS][18] = (itlb_addr << 16)&0xffff0000; - cuda_images[TLBHANDMISS][16] = + cuda_images[TLBHANDMISS][16] = (cuda_images[TLBHANDMISS][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); - cuda_images[TLBHANDMISS][17] = + cuda_images[TLBHANDMISS][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[TLBHANDMISS][18] = (itlb_addr << 16)&0xffff0000; - cuda_images[BIG_CPI][16] = + cuda_images[BIG_CPI][16] = (cuda_images[BIG_CPI][16]&0xffff0000) | ((dtlb_addr >> 8)&0x0000ffff); - cuda_images[BIG_CPI][17] = + cuda_images[BIG_CPI][17] = ((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff); cuda_images[BIG_CPI][18] = (itlb_addr << 16)&0xffff0000; } else { @@ -434,7 +434,7 @@ static void perf_patch_images(void) /* * ioctl routine - * All routines effect the processor that they are executed on. Thus you + * All routines effect the processor that they are executed on. Thus you * must be running on the processor that you wish to change. */ @@ -460,7 +460,7 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } /* copy out the Counters */ - if (copy_to_user((void __user *)arg, raddr, + if (copy_to_user((void __user *)arg, raddr, sizeof (raddr)) != 0) { error = -EFAULT; break; @@ -488,7 +488,7 @@ static const struct file_operations perf_fops = { .open = perf_open, .release = perf_release }; - + static struct miscdevice perf_dev = { MISC_DYNAMIC_MINOR, PA_PERF_DEV, @@ -595,7 +595,7 @@ static int perf_stop_counters(uint32_t *raddr) /* OR sticky2 (bit 1496) to counter2 bit 32 */ tmp64 |= (userbuf[23] >> 8) & 0x0000000080000000; raddr[2] = (uint32_t)tmp64; - + /* Counter3 is bits 1497 to 1528 */ tmp64 = (userbuf[23] >> 7) & 0x00000000ffffffff; /* OR sticky3 (bit 1529) to counter3 bit 32 */ @@ -617,7 +617,7 @@ static int perf_stop_counters(uint32_t *raddr) userbuf[22] = 0; userbuf[23] = 0; - /* + /* * Write back the zeroed bytes + the image given * the read was destructive. */ @@ -625,13 +625,13 @@ static int perf_stop_counters(uint32_t *raddr) } else { /* - * Read RDR-15 which contains the counters and sticky bits + * Read RDR-15 which contains the counters and sticky bits */ if (!perf_rdr_read_ubuf(15, userbuf)) { return -13; } - /* + /* * Clear out the counters */ perf_rdr_clear(15); @@ -644,7 +644,7 @@ static int perf_stop_counters(uint32_t *raddr) raddr[2] = (uint32_t)((userbuf[1] >> 32) & 0x00000000ffffffffUL); raddr[3] = (uint32_t)(userbuf[1] & 0x00000000ffffffffUL); } - + return 0; } @@ -682,7 +682,7 @@ static int perf_rdr_read_ubuf(uint32_t rdr_num, uint64_t *buffer) i = tentry->num_words; while (i--) { buffer[i] = 0; - } + } /* Check for bits an even number of 64 */ if ((xbits = width & 0x03f) != 0) { @@ -808,18 +808,22 @@ static int perf_write_image(uint64_t *memaddr) } runway = ioremap_nocache(cpu_device->hpa.start, 4096); + if (!runway) { + pr_err("perf_write_image: ioremap failed!\n"); + return -ENOMEM; + } /* Merge intrigue bits into Runway STATUS 0 */ tmp64 = __raw_readq(runway + RUNWAY_STATUS) & 0xffecfffffffffffful; - __raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul), + __raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul), runway + RUNWAY_STATUS); - + /* Write RUNWAY DEBUG registers */ for (i = 0; i < 8; i++) { __raw_writeq(*memaddr++, runway + RUNWAY_DEBUG); } - return 0; + return 0; } /* @@ -843,7 +847,7 @@ printk("perf_rdr_write\n"); perf_rdr_shift_out_U(rdr_num, buffer[i]); } else { perf_rdr_shift_out_W(rdr_num, buffer[i]); - } + } } printk("perf_rdr_write done\n"); } -- GitLab From 75a12b2fa80c2e4cc40a9f9305f95899850b7426 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Tue, 7 Mar 2017 02:48:36 -0500 Subject: [PATCH 1140/5498] rds: ib: add error handle [ Upstream commit 3b12f73a5c2977153f28a224392fd4729b50d1dc ] In the function rds_ib_setup_qp, the error handle is missing. When some error occurs, it is possible that memory leak occurs. As such, error handle is added. Cc: Joe Jin Reviewed-by: Junxiao Bi Reviewed-by: Guanglei Li Signed-off-by: Zhu Yanjun Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/rds/ib_cm.c | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 31b74f5e61ad..662122470bfa 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -298,7 +298,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) ret = PTR_ERR(ic->i_send_cq); ic->i_send_cq = NULL; rdsdebug("ib_create_cq send failed: %d\n", ret); - goto out; + goto rds_ibdev_out; } ic->i_recv_cq = ib_create_cq(dev, rds_ib_recv_cq_comp_handler, @@ -308,19 +308,19 @@ static int rds_ib_setup_qp(struct rds_connection *conn) ret = PTR_ERR(ic->i_recv_cq); ic->i_recv_cq = NULL; rdsdebug("ib_create_cq recv failed: %d\n", ret); - goto out; + goto send_cq_out; } ret = ib_req_notify_cq(ic->i_send_cq, IB_CQ_NEXT_COMP); if (ret) { rdsdebug("ib_req_notify_cq send failed: %d\n", ret); - goto out; + goto recv_cq_out; } ret = ib_req_notify_cq(ic->i_recv_cq, IB_CQ_SOLICITED); if (ret) { rdsdebug("ib_req_notify_cq recv failed: %d\n", ret); - goto out; + goto recv_cq_out; } /* XXX negotiate max send/recv with remote? */ @@ -344,7 +344,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) ret = rdma_create_qp(ic->i_cm_id, ic->i_pd, &attr); if (ret) { rdsdebug("rdma_create_qp failed: %d\n", ret); - goto out; + goto recv_cq_out; } ic->i_send_hdrs = ib_dma_alloc_coherent(dev, @@ -354,7 +354,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) if (!ic->i_send_hdrs) { ret = -ENOMEM; rdsdebug("ib_dma_alloc_coherent send failed\n"); - goto out; + goto qp_out; } ic->i_recv_hdrs = ib_dma_alloc_coherent(dev, @@ -364,7 +364,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) if (!ic->i_recv_hdrs) { ret = -ENOMEM; rdsdebug("ib_dma_alloc_coherent recv failed\n"); - goto out; + goto send_hdrs_dma_out; } ic->i_ack = ib_dma_alloc_coherent(dev, sizeof(struct rds_header), @@ -372,7 +372,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) if (!ic->i_ack) { ret = -ENOMEM; rdsdebug("ib_dma_alloc_coherent ack failed\n"); - goto out; + goto recv_hdrs_dma_out; } ic->i_sends = vzalloc_node(ic->i_send_ring.w_nr * sizeof(struct rds_ib_send_work), @@ -380,7 +380,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) if (!ic->i_sends) { ret = -ENOMEM; rdsdebug("send allocation failed\n"); - goto out; + goto ack_dma_out; } ic->i_recvs = vzalloc_node(ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work), @@ -388,7 +388,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) if (!ic->i_recvs) { ret = -ENOMEM; rdsdebug("recv allocation failed\n"); - goto out; + goto sends_out; } rds_ib_recv_init_ack(ic); @@ -396,8 +396,33 @@ static int rds_ib_setup_qp(struct rds_connection *conn) rdsdebug("conn %p pd %p mr %p cq %p %p\n", conn, ic->i_pd, ic->i_mr, ic->i_send_cq, ic->i_recv_cq); -out: + return ret; + +sends_out: + vfree(ic->i_sends); +ack_dma_out: + ib_dma_free_coherent(dev, sizeof(struct rds_header), + ic->i_ack, ic->i_ack_dma); +recv_hdrs_dma_out: + ib_dma_free_coherent(dev, ic->i_recv_ring.w_nr * + sizeof(struct rds_header), + ic->i_recv_hdrs, ic->i_recv_hdrs_dma); +send_hdrs_dma_out: + ib_dma_free_coherent(dev, ic->i_send_ring.w_nr * + sizeof(struct rds_header), + ic->i_send_hdrs, ic->i_send_hdrs_dma); +qp_out: + rdma_destroy_qp(ic->i_cm_id); +recv_cq_out: + if (!ib_destroy_cq(ic->i_recv_cq)) + ic->i_recv_cq = NULL; +send_cq_out: + if (!ib_destroy_cq(ic->i_send_cq)) + ic->i_send_cq = NULL; +rds_ibdev_out: + rds_ib_remove_conn(rds_ibdev, conn); rds_ib_dev_put(rds_ibdev); + return ret; } -- GitLab From d0bd573a6543d6f984c19158ce78653aa1f36240 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 23 Feb 2017 12:26:41 -0800 Subject: [PATCH 1141/5498] md/raid10: submit bio directly to replacement disk [ Upstream commit 6d399783e9d4e9bd44931501948059d24ad96ff8 ] Commit 57c67df(md/raid10: submit IO from originating thread instead of md thread) submits bio directly for normal disks but not for replacement disks. There is no point we shouldn't do this for replacement disks. Cc: NeilBrown Signed-off-by: Shaohua Li Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid10.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 1b49827757b5..d207748fdc41 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1514,11 +1514,24 @@ retry_write: mbio->bi_private = r10_bio; atomic_inc(&r10_bio->remaining); + + cb = blk_check_plugged(raid10_unplug, mddev, + sizeof(*plug)); + if (cb) + plug = container_of(cb, struct raid10_plug_cb, + cb); + else + plug = NULL; spin_lock_irqsave(&conf->device_lock, flags); - bio_list_add(&conf->pending_bio_list, mbio); - conf->pending_count++; + if (plug) { + bio_list_add(&plug->pending, mbio); + plug->pending_cnt++; + } else { + bio_list_add(&conf->pending_bio_list, mbio); + conf->pending_count++; + } spin_unlock_irqrestore(&conf->device_lock, flags); - if (!mddev_check_plugged(mddev)) + if (!plug) md_wakeup_thread(mddev->thread); } } -- GitLab From c9103723b7e4bd5046081c7569a73a752972cec9 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 6 Mar 2017 11:58:20 -0800 Subject: [PATCH 1142/5498] xfs: remove kmem_zalloc_greedy [ Upstream commit 08b005f1333154ae5b404ca28766e0ffb9f1c150 ] The sole remaining caller of kmem_zalloc_greedy is bulkstat, which uses it to grab 1-4 pages for staging of inobt records. The infinite loop in the greedy allocation function is causing hangs[1] in generic/269, so just get rid of the greedy allocator in favor of kmem_zalloc_large. This makes bulkstat somewhat more likely to ENOMEM if there's really no pages to spare, but eliminates a source of hangs. [1] http://lkml.kernel.org/r/20170301044634.rgidgdqqiiwsmfpj%40XZHOUW.usersys.redhat.com Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- fs/xfs/kmem.c | 18 ------------------ fs/xfs/kmem.h | 2 -- fs/xfs/xfs_itable.c | 6 ++---- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/fs/xfs/kmem.c b/fs/xfs/kmem.c index 53e95b2a1369..0abc6ae4fa57 100644 --- a/fs/xfs/kmem.c +++ b/fs/xfs/kmem.c @@ -24,24 +24,6 @@ #include "kmem.h" #include "xfs_message.h" -/* - * Greedy allocation. May fail and may return vmalloced memory. - */ -void * -kmem_zalloc_greedy(size_t *size, size_t minsize, size_t maxsize) -{ - void *ptr; - size_t kmsize = maxsize; - - while (!(ptr = vzalloc(kmsize))) { - if ((kmsize >>= 1) <= minsize) - kmsize = minsize; - } - if (ptr) - *size = kmsize; - return ptr; -} - void * kmem_alloc(size_t size, xfs_km_flags_t flags) { diff --git a/fs/xfs/kmem.h b/fs/xfs/kmem.h index 64db0e53edea..2e75933bec1e 100644 --- a/fs/xfs/kmem.h +++ b/fs/xfs/kmem.h @@ -66,8 +66,6 @@ extern void *kmem_realloc(const void *, size_t, size_t, xfs_km_flags_t); extern void kmem_free(const void *); -extern void *kmem_zalloc_greedy(size_t *, size_t, size_t); - static inline void * kmem_zalloc(size_t size, xfs_km_flags_t flags) { diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 894924a5129b..76dd8e7e914f 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -356,7 +356,6 @@ xfs_bulkstat( xfs_agino_t agino; /* inode # in allocation group */ xfs_agnumber_t agno; /* allocation group number */ xfs_btree_cur_t *cur; /* btree cursor for ialloc btree */ - size_t irbsize; /* size of irec buffer in bytes */ xfs_inobt_rec_incore_t *irbuf; /* start of irec buffer */ int nirbuf; /* size of irbuf */ int ubcount; /* size of user's buffer */ @@ -383,11 +382,10 @@ xfs_bulkstat( *ubcountp = 0; *done = 0; - irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4); + irbuf = kmem_zalloc_large(PAGE_SIZE * 4, KM_SLEEP); if (!irbuf) return -ENOMEM; - - nirbuf = irbsize / sizeof(*irbuf); + nirbuf = (PAGE_SIZE * 4) / sizeof(*irbuf); /* * Loop over the allocation groups, starting from the last -- GitLab From b8710e2d77f3088be920b8005a8e5629da98691c Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 3 Mar 2017 09:00:09 -0800 Subject: [PATCH 1143/5498] libata: transport: Remove circular dependency at free time [ Upstream commit d85fc67dd11e9a32966140677d4d6429ca540b25 ] Without this patch, failed probe would not free resources like irq. ata port tdev object currently hold a reference to the ata port object. Therefore the ata port object release function will not get called until the ata_tport_release is called. But that would never happen, releasing the last reference of ata port dev is done by scsi_host_release, which is called by ata_host_release when the ata port object is released. The ata device objects actually do not need to explicitly hold a reference to their real counterpart, given the transport objects are the children of these objects and device_add() is call for each child. We know the parent will not be deleted until we call the child's device_del(). Reported-by: Matthew Whitehead Tested-by: Matthew Whitehead Suggested-by: Tejun Heo Signed-off-by: Gwendal Grignou Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-transport.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index fd29b7224082..43d4fbb377da 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -223,7 +223,6 @@ static DECLARE_TRANSPORT_CLASS(ata_port_class, static void ata_tport_release(struct device *dev) { - put_device(dev->parent); } /** @@ -283,7 +282,7 @@ int ata_tport_add(struct device *parent, device_initialize(dev); dev->type = &ata_port_type; - dev->parent = get_device(parent); + dev->parent = parent; dev->release = ata_tport_release; dev_set_name(dev, "ata%d", ap->print_id); transport_setup_device(dev); @@ -347,7 +346,6 @@ static DECLARE_TRANSPORT_CLASS(ata_link_class, static void ata_tlink_release(struct device *dev) { - put_device(dev->parent); } /** @@ -409,7 +407,7 @@ int ata_tlink_add(struct ata_link *link) int error; device_initialize(dev); - dev->parent = get_device(&ap->tdev); + dev->parent = &ap->tdev; dev->release = ata_tlink_release; if (ata_is_host_link(link)) dev_set_name(dev, "link%d", ap->print_id); @@ -587,7 +585,6 @@ static DECLARE_TRANSPORT_CLASS(ata_dev_class, static void ata_tdev_release(struct device *dev) { - put_device(dev->parent); } /** @@ -660,7 +657,7 @@ static int ata_tdev_add(struct ata_device *ata_dev) int error; device_initialize(dev); - dev->parent = get_device(&link->tdev); + dev->parent = &link->tdev; dev->release = ata_tdev_release; if (ata_is_host_link(link)) dev_set_name(dev, "dev%d.%d", ap->print_id,ata_dev->devno); -- GitLab From e288325e93fbb5163b26f198fd450f9d7b17938f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 14 Mar 2017 13:18:45 +0100 Subject: [PATCH 1144/5498] IB/qib: fix false-postive maybe-uninitialized warning commit f6aafac184a3e46e919769dd4faa8bf0dc436534 upstream. aarch64-linux-gcc-7 complains about code it doesn't fully understand: drivers/infiniband/hw/qib/qib_iba7322.c: In function 'qib_7322_txchk_change': include/asm-generic/bitops/non-atomic.h:105:35: error: 'shadow' may be used uninitialized in this function [-Werror=maybe-uninitialized] The code is right, and despite trying hard, I could not come up with a version that I liked better than just adding a fake initialization here to shut up the warning. Fixes: f931551bafe1 ("IB/qib: Add new qib driver for QLogic PCIe InfiniBand adapters") Signed-off-by: Arnd Bergmann Acked-by: Ira Weiny Signed-off-by: Doug Ledford Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/qib/qib_iba7322.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index 23ca2aca1ad6..5331f8683ef6 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -7078,7 +7078,7 @@ static void qib_7322_txchk_change(struct qib_devdata *dd, u32 start, unsigned long flags; while (wait) { - unsigned long shadow; + unsigned long shadow = 0; int cstart, previ = -1; /* -- GitLab From 0cd9a4647a23243db0d1c45ebd4df4242006b8f6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Mar 2017 16:15:55 +0100 Subject: [PATCH 1145/5498] ALSA: au88x0: avoid theoretical uninitialized access commit 13f99ebdd602ebdafb909e15ec6ffb1e34690167 upstream. The latest gcc-7.0.1 snapshot points out that we if nr_ch is zero, we never initialize some variables: sound/pci/au88x0/au88x0_core.c: In function 'vortex_adb_allocroute': sound/pci/au88x0/au88x0_core.c:2304:68: error: 'mix[0]' may be used uninitialized in this function [-Werror=maybe-uninitialized] sound/pci/au88x0/au88x0_core.c:2305:58: error: 'src[0]' may be used uninitialized in this function [-Werror=maybe-uninitialized] I assume this can never happen in practice, but adding a check here doesn't hurt either and avoids the warning. The code has been unchanged since the start of git history. Signed-off-by: Arnd Bergmann Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/au88x0/au88x0_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 72002382a0db..53713278d19d 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2273,6 +2273,9 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, } else { int src[2], mix[2]; + if (nr_ch < 1) + return -EINVAL; + /* Get SRC and MIXER hardware resources. */ for (i = 0; i < nr_ch; i++) { if ((mix[i] = -- GitLab From 9c4ae8f3e5f778684e0a90c5e83b99cf39d2203c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 2 Feb 2017 12:51:28 -0200 Subject: [PATCH 1146/5498] ttpci: address stringop overflow warning commit 69d3973af1acd4c0989ec8218c05f12d303cd7cf upstream. gcc-7.0.1 warns about old code in ttpci: In file included from drivers/media/pci/ttpci/av7110.c:63:0: In function 'irdebi.isra.2', inlined from 'start_debi_dma' at drivers/media/pci/ttpci/av7110.c:376:3, inlined from 'gpioirq' at drivers/media/pci/ttpci/av7110.c:659:3: drivers/media/pci/ttpci/av7110_hw.h:406:3: warning: 'memcpy': specified size between 18446744071562067968 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=] memcpy(av7110->debi_virt, (char *) &res, count); In function 'irdebi.isra.2', inlined from 'start_debi_dma' at drivers/media/pci/ttpci/av7110.c:376:3, inlined from 'gpioirq' at drivers/media/pci/ttpci/av7110.c:668:3: drivers/media/pci/ttpci/av7110_hw.h:406:3: warning: 'memcpy': specified size between 18446744071562067968 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=] memcpy(av7110->debi_virt, (char *) &res, count); Apparently, 'count' can be negative here, which will then get turned into a giant size argument for memcpy. Changing the sizes to 'unsigned int' instead seems safe as we already check for maximum sizes, and it also simplifies the code a bit. Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/pci/ttpci/av7110_hw.c | 8 ++++---- drivers/media/pci/ttpci/av7110_hw.h | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c index 300bd3c94738..0992bb0e207e 100644 --- a/drivers/media/pci/ttpci/av7110_hw.c +++ b/drivers/media/pci/ttpci/av7110_hw.c @@ -56,11 +56,11 @@ by Nathan Laredo */ int av7110_debiwrite(struct av7110 *av7110, u32 config, - int addr, u32 val, int count) + int addr, u32 val, unsigned int count) { struct saa7146_dev *dev = av7110->dev; - if (count <= 0 || count > 32764) { + if (count > 32764) { printk("%s: invalid count %d\n", __func__, count); return -1; } @@ -78,12 +78,12 @@ int av7110_debiwrite(struct av7110 *av7110, u32 config, return 0; } -u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count) +u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int count) { struct saa7146_dev *dev = av7110->dev; u32 result = 0; - if (count > 32764 || count <= 0) { + if (count > 32764) { printk("%s: invalid count %d\n", __func__, count); return 0; } diff --git a/drivers/media/pci/ttpci/av7110_hw.h b/drivers/media/pci/ttpci/av7110_hw.h index 1634aba5cb84..ccb148059406 100644 --- a/drivers/media/pci/ttpci/av7110_hw.h +++ b/drivers/media/pci/ttpci/av7110_hw.h @@ -377,14 +377,14 @@ extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, /* DEBI (saa7146 data extension bus interface) access */ extern int av7110_debiwrite(struct av7110 *av7110, u32 config, - int addr, u32 val, int count); + int addr, u32 val, unsigned int count); extern u32 av7110_debiread(struct av7110 *av7110, u32 config, - int addr, int count); + int addr, unsigned int count); /* DEBI during interrupt */ /* single word writes */ -static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) { av7110_debiwrite(av7110, config, addr, val, count); } @@ -397,7 +397,7 @@ static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, av7110_debiwrite(av7110, config, addr, 0, count); } -static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) { u32 res; @@ -408,7 +408,7 @@ static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, i } /* DEBI outside interrupts, only for count <= 4! */ -static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) { unsigned long flags; @@ -417,7 +417,7 @@ static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, i spin_unlock_irqrestore(&av7110->debilock, flags); } -static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) { unsigned long flags; u32 res; -- GitLab From 9d5012da8b97a6199fb2ead626be9c81aff649a8 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 16 Jun 2015 23:13:21 +0800 Subject: [PATCH 1147/5498] staging: nvec: remove duplicated const commit 716baa7b430c66187a41e4d41eedf5de01343b21 upstream. Sparse checking warning: "drivers/staging/nvec/nvec_ps2.c:172:14: warning: duplicate const". Remove the duplicated const to fix the warning. Signed-off-by: Peng Fan Acked-by: Marc Dietrich Signed-off-by: Greg Kroah-Hartman --- drivers/staging/nvec/nvec_ps2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c index 3f631c067f54..dd2ebd223867 100644 --- a/drivers/staging/nvec/nvec_ps2.c +++ b/drivers/staging/nvec/nvec_ps2.c @@ -165,8 +165,8 @@ static int nvec_mouse_resume(struct device *dev) } #endif -static const SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend, - nvec_mouse_resume); +static SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend, + nvec_mouse_resume); static struct platform_driver nvec_mouse_driver = { .probe = nvec_mouse_probe, -- GitLab From 36c84b22ac8aa041cbdfbe48a55ebb32e3521704 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 18 Jan 2016 18:46:10 +0800 Subject: [PATCH 1148/5498] crypto: algif_skcipher - Load TX SG list after waiting commit 4f0414e54e4d1893c6f08260693f8ef84c929293 upstream. We need to load the TX SG list in sendmsg(2) after waiting for incoming data, not before. [connoro@google.com: backport to 3.18, where the relevant logic is located in skcipher_recvmsg() rather than skcipher_recvmsg_sync()] Reported-by: Dmitry Vyukov Signed-off-by: Herbert Xu Tested-by: Dmitry Vyukov Signed-off-by: Connor O'Brien Signed-off-by: Greg Kroah-Hartman --- crypto/algif_skcipher.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index fd1967ecc928..110970be0716 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -448,13 +448,6 @@ static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock, char __user *from = iov->iov_base; while (seglen) { - sgl = list_first_entry(&ctx->tsgl, - struct skcipher_sg_list, list); - sg = sgl->sg; - - while (!sg->length) - sg++; - used = ctx->used; if (!used) { err = skcipher_wait_for_data(sk, flags); @@ -476,6 +469,13 @@ static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock, if (!used) goto free; + sgl = list_first_entry(&ctx->tsgl, + struct skcipher_sg_list, list); + sg = sgl->sg; + + while (!sg->length) + sg++; + ablkcipher_request_set_crypt(&ctx->req, sg, ctx->rsgl.sg, used, ctx->iv); -- GitLab From 703937f005ea09ac2b6da593a9cec0befcb7b22a Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Thu, 24 Nov 2016 13:23:10 +0000 Subject: [PATCH 1149/5498] mpi: Fix NULL ptr dereference in mpi_powm() [ver #3] commit f5527fffff3f002b0a6b376163613b82f69de073 upstream. This fixes CVE-2016-8650. If mpi_powm() is given a zero exponent, it wants to immediately return either 1 or 0, depending on the modulus. However, if the result was initalised with zero limb space, no limbs space is allocated and a NULL-pointer exception ensues. Fix this by allocating a minimal amount of limb space for the result when the 0-exponent case when the result is 1 and not touching the limb space when the result is 0. This affects the use of RSA keys and X.509 certificates that carry them. BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] mpi_powm+0x32/0x7e6 PGD 0 Oops: 0002 [#1] SMP Modules linked in: CPU: 3 PID: 3014 Comm: keyctl Not tainted 4.9.0-rc6-fscache+ #278 Hardware name: ASUS All Series/H97-PLUS, BIOS 2306 10/09/2014 task: ffff8804011944c0 task.stack: ffff880401294000 RIP: 0010:[] [] mpi_powm+0x32/0x7e6 RSP: 0018:ffff880401297ad8 EFLAGS: 00010212 RAX: 0000000000000000 RBX: ffff88040868bec0 RCX: ffff88040868bba0 RDX: ffff88040868b260 RSI: ffff88040868bec0 RDI: ffff88040868bee0 RBP: ffff880401297ba8 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000047 R11: ffffffff8183b210 R12: 0000000000000000 R13: ffff8804087c7600 R14: 000000000000001f R15: ffff880401297c50 FS: 00007f7a7918c700(0000) GS:ffff88041fb80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 0000000401250000 CR4: 00000000001406e0 Stack: ffff88040868bec0 0000000000000020 ffff880401297b00 ffffffff81376cd4 0000000000000100 ffff880401297b10 ffffffff81376d12 ffff880401297b30 ffffffff81376f37 0000000000000100 0000000000000000 ffff880401297ba8 Call Trace: [] ? __sg_page_iter_next+0x43/0x66 [] ? sg_miter_get_next_page+0x1b/0x5d [] ? sg_miter_next+0x17/0xbd [] ? mpi_read_raw_from_sgl+0xf2/0x146 [] rsa_verify+0x9d/0xee [] ? pkcs1pad_sg_set_buf+0x2e/0xbb [] pkcs1pad_verify+0xc0/0xe1 [] public_key_verify_signature+0x1b0/0x228 [] x509_check_for_self_signed+0xa1/0xc4 [] x509_cert_parse+0x167/0x1a1 [] x509_key_preparse+0x21/0x1a1 [] asymmetric_key_preparse+0x34/0x61 [] key_create_or_update+0x145/0x399 [] SyS_add_key+0x154/0x19e [] do_syscall_64+0x80/0x191 [] entry_SYSCALL64_slow_path+0x25/0x25 Code: 56 41 55 41 54 53 48 81 ec a8 00 00 00 44 8b 71 04 8b 42 04 4c 8b 67 18 45 85 f6 89 45 80 0f 84 b4 06 00 00 85 c0 75 2f 41 ff ce <49> c7 04 24 01 00 00 00 b0 01 75 0b 48 8b 41 18 48 83 38 01 0f RIP [] mpi_powm+0x32/0x7e6 RSP CR2: 0000000000000000 ---[ end trace d82015255d4a5d8d ]--- Basically, this is a backport of a libgcrypt patch: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=patch;h=6e1adb05d290aeeb1c230c763970695f4a538526 Fixes: cdec9cb5167a ("crypto: GnuPG based MPI lib - source files (part 1)") Signed-off-by: Andrey Ryabinin Signed-off-by: David Howells cc: Dmitry Kasatkin cc: linux-ima-devel@lists.sourceforge.net Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- lib/mpi/mpi-pow.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c index 5464c8744ea9..e24388a863a7 100644 --- a/lib/mpi/mpi-pow.c +++ b/lib/mpi/mpi-pow.c @@ -64,8 +64,13 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) if (!esize) { /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0 * depending on if MOD equals 1. */ - rp[0] = 1; res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1; + if (res->nlimbs) { + if (mpi_resize(res, 1) < 0) + goto enomem; + rp = res->d; + rp[0] = 1; + } res->sign = 0; goto leave; } -- GitLab From ac0058305d83e8e50a9652a003bc2ec468df9f87 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 8 Oct 2017 10:11:47 +0200 Subject: [PATCH 1150/5498] Linux 3.18.74 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f5e683464cd4..bece4a07e07a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 73 +SUBLEVEL = 74 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 600d77e43d4c8365c25db049f18c1007d0a16f1b Mon Sep 17 00:00:00 2001 From: Swetha Chikkaboraiah Date: Wed, 13 Sep 2017 12:36:44 +0530 Subject: [PATCH 1151/5498] defconfig: Disable few configs for msm8909 perf This change disables below configs for the msm8909 perf config CONFIG_CGROUP_DEBUG CONFIG_SLUB_DEBUG CONFIG_VT Change-Id: Id8b3f8bccc37e6089ec48655d26d81db9a01c889 CRs-Fixed: 2105947 Signed-off-by: Swetha Chikkaboraiah --- arch/arm/configs/msm8909-perf_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index f1ae76976931..6bbc3d9db7f2 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -11,7 +11,6 @@ CONFIG_RCU_FAST_NO_HZ=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y -CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y @@ -29,6 +28,7 @@ CONFIG_RD_LZMA=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set CONFIG_PROFILING=y CONFIG_CC_STACKPROTECTOR_REGULAR=y CONFIG_ARCH_MMAP_RND_BITS=16 @@ -294,6 +294,7 @@ CONFIG_SENSORS_MPU6050=y CONFIG_SENSORS_AKM8963=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=m +# CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVMEM is not set # CONFIG_DEVKMEM is not set -- GitLab From dbc2016b0d1ce89e06487a05dd9968c74e4a4b49 Mon Sep 17 00:00:00 2001 From: Rashi Bindra Date: Mon, 5 Jun 2017 13:02:59 +0530 Subject: [PATCH 1152/5498] msm: mdss: Do not attempt to request TE irq again if already requested If the TE irq is alredy requested and registred with a GPIO, then setting it to true and not continuing with further requesting of the same. Change-Id: Iacd1677127c2663d88826e58f1b72704b58db939 Signed-off-by: Rashi Bindra --- drivers/video/msm/mdss/mdss_dsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c index 395aef305d80..cf4ae206c97a 100644 --- a/drivers/video/msm/mdss/mdss_dsi.c +++ b/drivers/video/msm/mdss/mdss_dsi.c @@ -3297,9 +3297,10 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) hw_vsync_handler, IRQF_TRIGGER_FALLING, "VSYNC_GPIO", ctrl_pdata); if (rc) { - pr_err("TE request_irq failed.\n"); + pr_err("%s: TE request_irq failed for ESD\n", __func__); goto error_shadow_clk_deinit; } + te_irq_registered = 1; disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); } -- GitLab From e78c57c1235c7c2fac63b802b37e08aefaf5d62e Mon Sep 17 00:00:00 2001 From: Amit Nischal Date: Thu, 5 Oct 2017 11:22:41 +0530 Subject: [PATCH 1153/5498] clk: msm: clock-gcc-9650: Add support to use Max uV for GCC clocks GCC clocks need to vote for max uV so add support for the same by setting the use_max_uV variable of clk_vdd_class struct to true for MDM9650. Change-Id: I0bdf5f5f700c6cfcc03d67438b020ada4c415f52 Signed-off-by: Amit Nischal --- drivers/clk/msm/clock-gcc-9650.c | 17 +++++------------ drivers/clk/msm/vdd-level-9650.h | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/clk/msm/clock-gcc-9650.c b/drivers/clk/msm/clock-gcc-9650.c index ea7ec78cb249..e454707952b0 100644 --- a/drivers/clk/msm/clock-gcc-9650.c +++ b/drivers/clk/msm/clock-gcc-9650.c @@ -96,15 +96,6 @@ static void __iomem *virt_apcsbase; | BVAL(10, 8, s##_source_val), \ } -static int vdd_corner_sdx20[] = { - RPM_REGULATOR_LEVEL_NONE, /* VDD_DIG_NONE */ - RPM_REGULATOR_LEVEL_MIN_SVS, /* VDD_DIG_MIN */ - RPM_REGULATOR_LEVEL_LOW_SVS, /* VDD_DIG_LOWER */ - RPM_REGULATOR_LEVEL_SVS, /* VDD_DIG_LOW */ - RPM_REGULATOR_LEVEL_NOM, /* VDD_DIG_NOMINAL */ - RPM_REGULATOR_LEVEL_TURBO, /* VDD_DIG_HIGH */ -}; - static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL); static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner, NULL); @@ -1675,9 +1666,6 @@ static void msm_clocks_gcc_sdx20_fixup(void) usb30_mock_utmi_clk_src.freq_tbl = ftbl_usb30_mock_utmi_clk_src_sdx20; - vdd_dig.vdd_uv = vdd_corner_sdx20; - vdd_dig_ao.vdd_uv = vdd_corner_sdx20; - sdcc1_apps_clk_src.c.fmax[VDD_DIG_MIN] = 25000000; sdcc1_apps_clk_src.c.fmax[VDD_DIG_LOWER] = 50000000; sdcc1_apps_clk_src.c.fmax[VDD_DIG_LOW] = 1000000000; @@ -1746,6 +1734,11 @@ static int msm_gcc_9650_probe(struct platform_device *pdev) if (ret) return ret; + if (of_device_is_compatible(pdev->dev.of_node, "qcom,gcc-9650")) { + vdd_dig.use_max_uV = true; + vdd_dig_ao.use_max_uV = true; + } + for_sdx20 = of_device_is_compatible(pdev->dev.of_node, "qcom,gcc-sdx20"); diff --git a/drivers/clk/msm/vdd-level-9650.h b/drivers/clk/msm/vdd-level-9650.h index 6d496d4a0240..d8f95b062fc2 100644 --- a/drivers/clk/msm/vdd-level-9650.h +++ b/drivers/clk/msm/vdd-level-9650.h @@ -90,7 +90,7 @@ static int vdd_corner[] = { RPM_REGULATOR_LEVEL_LOW_SVS, /* VDD_DIG_LOWER */ RPM_REGULATOR_LEVEL_SVS, /* VDD_DIG_LOW */ RPM_REGULATOR_LEVEL_NOM, /* VDD_DIG_NOMINAL */ - RPM_REGULATOR_LEVEL_TURBO_NO_CPR, /* VDD_DIG_HIGH */ + RPM_REGULATOR_LEVEL_TURBO, /* VDD_DIG_HIGH */ }; #endif -- GitLab From 0b25a2f065e7f313b8c62f86d0abcf3d229e84eb Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Mon, 9 Oct 2017 16:35:41 +0530 Subject: [PATCH 1154/5498] USB: HCD: Add dummy functions when host mode disabled If CONFIG_USB is disabled in the defconfig and some hcd function is invoked by other drivers, then it can lead to compilation failure. Fix this by adding appropriate dummy functions in case of CONFIG_USB disabled. Change-Id: I3e4110cd90c926af9ee81b6f981889c53ae5b039 Signed-off-by: Ajay Agarwal --- include/linux/usb/hcd.h | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 4f83dc8e7923..a83ef102150c 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -434,9 +434,6 @@ extern struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd); extern void usb_put_hcd(struct usb_hcd *hcd); extern int usb_hcd_is_primary_hcd(struct usb_hcd *hcd); -extern int usb_add_hcd(struct usb_hcd *hcd, - unsigned int irqnum, unsigned long irqflags); -extern void usb_remove_hcd(struct usb_hcd *hcd); extern int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1); struct platform_device; @@ -636,15 +633,6 @@ extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg); extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg); #endif /* CONFIG_PM */ -#ifdef CONFIG_PM_RUNTIME -extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd); -#else -static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) -{ - return; -} -#endif /* CONFIG_PM_RUNTIME */ - /*-------------------------------------------------------------------------*/ #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) @@ -710,6 +698,29 @@ extern struct rw_semaphore ehci_cf_port_reset_rwsem; #define USB_EHCI_LOADED 2 extern unsigned long usb_hcds_loaded; +#ifdef CONFIG_USB +extern int usb_add_hcd(struct usb_hcd *hcd, + unsigned int irqnum, unsigned long irqflags); +extern void usb_remove_hcd(struct usb_hcd *hcd); +#ifdef CONFIG_PM_RUNTIME +extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd); +#else +static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) {} +#endif /* CONFIG_PM_RUNTIME */ + +#else /* CONFIG_USB */ + +static inline int usb_add_hcd(struct usb_hcd *hcd, + unsigned int irqnum, unsigned long irqflags) +{ + return 0; +} + +static inline void usb_remove_hcd(struct usb_hcd *hcd) {} +static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) {} + +#endif /* CONFIG_USB */ + #endif /* __KERNEL__ */ #endif /* __USB_CORE_HCD_H */ -- GitLab From fa0f1baf276ecce0cb6d0de9b812f928734b95c4 Mon Sep 17 00:00:00 2001 From: Srinivasarao P Date: Mon, 4 Sep 2017 15:28:15 +0530 Subject: [PATCH 1155/5498] defconfig: msm8909: disable KSM KSM is not needed on 8909 so disabling it. Change-Id: I1b4b3784d32b18df5c54ebf04294f86ecc3bdac0 Signed-off-by: Srinivasarao P --- arch/arm/configs/msm8909-perf_defconfig | 1 - arch/arm/configs/msm8909_defconfig | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index f1ae76976931..6812c23f6a61 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -45,7 +45,6 @@ CONFIG_SCHED_MC=y CONFIG_PREEMPT=y CONFIG_AEABI=y CONFIG_HIGHMEM=y -CONFIG_KSM=y CONFIG_CMA=y CONFIG_ZSMALLOC=y CONFIG_BALANCE_ANON_FILE_RECLAIM=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index a273170404ce..0c62dab092a8 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -49,7 +49,6 @@ CONFIG_SCHED_MC=y CONFIG_PREEMPT=y CONFIG_AEABI=y CONFIG_HIGHMEM=y -CONFIG_KSM=y CONFIG_CMA=y CONFIG_ZSMALLOC=y CONFIG_BALANCE_ANON_FILE_RECLAIM=y -- GitLab From cadaa74fc7dd79b12aead8dffa93e4e289efd49e Mon Sep 17 00:00:00 2001 From: Amit Nischal Date: Thu, 5 Oct 2017 12:19:32 +0530 Subject: [PATCH 1156/5498] ARM: dts: msm: Change the voltage level for cpu clock on MDM9650 For MDM9650, CPU clocks need to vote for Turbo level so change the voltage corner. Change-Id: I202c6178cceb378cc7f368fe7b6a1c97f4aafdf1 Signed-off-by: Amit Nischal --- arch/arm/boot/dts/qcom/mdm9650.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650.dtsi b/arch/arm/boot/dts/qcom/mdm9650.dtsi index 97c875a1b541..f8824c5cf846 100644 --- a/arch/arm/boot/dts/qcom/mdm9650.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650.dtsi @@ -216,7 +216,7 @@ < 200000000 RPM_SMD_REGULATOR_LEVEL_LOW_SVS>, < 384000000 RPM_SMD_REGULATOR_LEVEL_SVS>, < 787200000 RPM_SMD_REGULATOR_LEVEL_NOM>, - <1286400000 RPM_SMD_REGULATOR_LEVEL_TURBO_NO_CPR>; + <1286400000 RPM_SMD_REGULATOR_LEVEL_TURBO>; cpu-vdd-supply = <&pmd9650_s5_level_ao>; qcom,a7ssmux-opp-store-vcorner = <&CPU0>; -- GitLab From 78a2a6f47bffb3d6888a089e2a4ea62e14f6c135 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Sat, 16 Sep 2017 00:39:46 +0530 Subject: [PATCH 1157/5498] msm: ipa: Set ep delay on rmnet/mbim tether pipe As soon we start a gsi channel, there is a chance of getting packets from USB to IPA before even we setup flt/rt fules. To avoid this race condition set ep delay before start of gsi channel in case of rmnet/mbim tether. This delay should be cleared by Q6 once required flt/rt rules in place. Change-Id: Iac16ba067d4454ffd3f191009ea6eb5998473977 Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v3/ipa_client.c | 61 +++++++++++++++++++- drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 1 + 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 00ef52d0223e..d8ed2aef802b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1351,6 +1351,7 @@ int ipa3_set_usb_max_packet_size( return 0; } +/* This function called as part of usb pipe resume */ int ipa3_xdci_connect(u32 clnt_hdl) { int result; @@ -1390,11 +1391,14 @@ exit: return result; } + +/* This function called as part of usb pipe connect */ int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid) { struct ipa3_ep_context *ep; int result = -EFAULT; enum gsi_status gsi_res; + struct ipa_ep_cfg_ctrl ep_cfg_ctrl; IPADBG("entry\n"); if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || @@ -1416,6 +1420,22 @@ int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid) goto write_chan_scratch_fail; } } + + if (IPA_CLIENT_IS_PROD(ep->client) && ep->skip_ep_cfg) { + memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); + ep_cfg_ctrl.ipa_ep_delay = true; + ep->ep_delay_set = true; + + result = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); + if (result) + IPAERR("client (ep: %d) failed result=%d\n", + clnt_hdl, result); + else + IPADBG("client (ep: %d) success\n", clnt_hdl); + } else { + ep->ep_delay_set = false; + } + gsi_res = gsi_start_channel(ep->gsi_chan_hdl); if (gsi_res != GSI_STATUS_SUCCESS) { IPAERR("Error starting channel: %d\n", gsi_res); @@ -1620,13 +1640,15 @@ static int ipa3_xdci_stop_gsi_ch_brute_force(u32 clnt_hdl, /* Clocks should be voted for before invoking this function */ static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id, - u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl) + u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl, + bool remove_delay) { int result; bool is_empty = false; int i; bool stop_in_proc; struct ipa3_ep_context *ep; + struct ipa_ep_cfg_ctrl ep_cfg_ctrl; if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { @@ -1647,6 +1669,22 @@ static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id, if (!stop_in_proc) goto exit; + if (remove_delay && ep->ep_delay_set == true) { + memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); + ep_cfg_ctrl.ipa_ep_delay = false; + result = ipa3_cfg_ep_ctrl(clnt_hdl, + &ep_cfg_ctrl); + if (result) { + IPAERR + ("client (ep: %d) failed to remove delay result=%d\n", + clnt_hdl, result); + } else { + IPADBG("client (ep: %d) delay removed\n", + clnt_hdl); + ep->ep_delay_set = false; + } + } + /* if stop_in_proc, lets wait for emptiness */ for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) { result = ipa3_is_xdci_channel_empty(ep, &is_empty); @@ -1712,6 +1750,21 @@ disable_force_clear_and_exit: if (should_force_clear) ipa3_disable_force_clear(qmi_req_id); exit: + if (remove_delay && ep->ep_delay_set == true) { + memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); + ep_cfg_ctrl.ipa_ep_delay = false; + result = ipa3_cfg_ep_ctrl(clnt_hdl, + &ep_cfg_ctrl); + if (result) { + IPAERR + ("client (ep: %d) failed to remove delay result=%d\n", + clnt_hdl, result); + } else { + IPADBG("client (ep: %d) delay removed\n", + clnt_hdl); + ep->ep_delay_set = false; + } + } return result; } @@ -1741,7 +1794,8 @@ int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id) source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ep->client); result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id, - source_pipe_bitmask, should_force_clear, clnt_hdl); + source_pipe_bitmask, should_force_clear, clnt_hdl, + true); if (result) { IPAERR("Fail to stop UL channel with data drain\n"); BUG(); @@ -1916,7 +1970,8 @@ int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, if (!is_dpl) { source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ul_ep->client); result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id, - source_pipe_bitmask, should_force_clear, ul_clnt_hdl); + source_pipe_bitmask, should_force_clear, ul_clnt_hdl, + false); if (result) { IPAERR("Error stopping UL channel: result = %d\n", result); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 295cdfe85dd5..b50b9e403f0a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -610,6 +610,7 @@ struct ipa3_ep_context { u32 uc_offload_state; bool disconnect_in_progress; u32 qmi_request_sent; + bool ep_delay_set; /* sys MUST be the last element of this struct */ struct ipa3_sys_context *sys; -- GitLab From 6da5199e20cef7ce91af51991f8a4fa21eada27d Mon Sep 17 00:00:00 2001 From: Siba Prasad Date: Mon, 18 Sep 2017 19:32:45 +0530 Subject: [PATCH 1158/5498] ARM: dts: msm: Move PMIC GPIO4 configuration on mdm9607 On MDM9X07 platforms where SD card is not present, the PMIC GPIO4 is causing power leakage. PMIC GPIO4 is connected to external regulator which is used by SD card. This GPIO configurations needs to be enabled only for platform which has SD card. So move this GPIO configuration from generic mtp/cdp DT files to SD card specific DT file. Change-Id: I6365353532130d5503764ac37637444efca020d7 Signed-off-by: Siba Prasad --- arch/arm/boot/dts/qcom/mdm9607-cdp.dtsi | 14 +------------- arch/arm/boot/dts/qcom/mdm9607-mtp-sdcard.dts | 12 ++++++++++++ arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi | 12 ------------ 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/arch/arm/boot/dts/qcom/mdm9607-cdp.dtsi b/arch/arm/boot/dts/qcom/mdm9607-cdp.dtsi index 698a93526816..d959db9d5918 100644 --- a/arch/arm/boot/dts/qcom/mdm9607-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9607-cdp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -104,18 +104,6 @@ }; }; -&pm8019_gpios { - gpio@c300 { /* GPIO 4 */ - qcom,mode = <1>; /* Digital output */ - qcom,output-type = <0>; /* CMOS logic */ - qcom,invert = <0>; /* Output high */ - qcom,vin-sel = <0>; /* VPH_PWR */ - qcom,src-sel = <0>; /* GPIO */ - qcom,out-strength = <1>; /* Low drive strength */ - qcom,master-en = <1>; /* Enable GPIO */ - }; -}; - /* MPP pin 4 configs for SGMII */ &pm8019_mpps { mpp@a300 { /* MPP 4 */ diff --git a/arch/arm/boot/dts/qcom/mdm9607-mtp-sdcard.dts b/arch/arm/boot/dts/qcom/mdm9607-mtp-sdcard.dts index 96bce37b41bb..f2250a36a4de 100644 --- a/arch/arm/boot/dts/qcom/mdm9607-mtp-sdcard.dts +++ b/arch/arm/boot/dts/qcom/mdm9607-mtp-sdcard.dts @@ -43,3 +43,15 @@ status = "ok"; }; +&pm8019_gpios { + gpio@c300 { /* GPIO 4 */ + qcom,mode = <1>; /* Digital Output */ + qcom,output-type = <0>; /* CMOS Logic */ + qcom,invert = <0>; /* Output High */ + qcom,vin-sel = <0>; /* VPH_PWR */ + qcom,src-sel = <0>; /* GPIO */ + qcom,out-strength = <1>; /* Low Drive Strength */ + qcom,master-en = <1>; /* Enable GPIO */ + }; +}; + diff --git a/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi b/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi index b3e66eeeb957..89753a29d6d6 100644 --- a/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi @@ -141,18 +141,6 @@ status = "ok"; }; -&pm8019_gpios { - gpio@c300 { /* GPIO 4 */ - qcom,mode = <1>; /* Digital output */ - qcom,output-type = <0>; /* CMOS logic */ - qcom,invert = <0>; /* Output high */ - qcom,vin-sel = <0>; /* VPH_PWR */ - qcom,src-sel = <0>; /* GPIO */ - qcom,out-strength = <1>; /* Low drive strength */ - qcom,master-en = <1>; /* Enable GPIO */ - }; -}; - /* MPP pin 4 configs for SGMII */ &pm8019_mpps { mpp@a300 { /* MPP 4 */ -- GitLab From 66cf2fb3e85b9e048c60dc89c0dba7cbfd9e6f9e Mon Sep 17 00:00:00 2001 From: Brahmaji K Date: Tue, 21 Jun 2016 03:37:51 -0700 Subject: [PATCH 1159/5498] Revert "ARM: dts: msm: update heap size for msm8909w" This reverts commit 7fbdc231fef21b8e304a04aa19ff9d22e20e8627. Change-Id: Iffec9fbd97d93b0b0f7bd00b7427ba80039e632d Signed-off-by: Brahmaji K --- arch/arm/boot/dts/qcom/msm8909w.dtsi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w.dtsi b/arch/arm/boot/dts/qcom/msm8909w.dtsi index 7fc0edd8c741..02f3252a6ccf 100644 --- a/arch/arm/boot/dts/qcom/msm8909w.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w.dtsi @@ -101,10 +101,6 @@ <55 512 393600 393600>; }; -&venus_qseecom_mem { - size = <0 0x0400000>; -}; - &mdss_dsi0 { qcom,dsi-pref-prim-pan = <&dsi_auo_cx_qvga_cmd>; pinctrl-names = "mdss_default", "mdss_sleep"; -- GitLab From 2a2af8b4a411e8c99191b31bf6320c98dc304667 Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Mon, 25 Sep 2017 17:38:46 -0700 Subject: [PATCH 1160/5498] msm: ipa: fix to unlock the DDR region after FW loading Changes to unlock the DDR region at the end of FW pil loading. Change-Id: I6f64f3d19efc1a52b7013fc15db67e70b9ac559c Acked-by: Jyothi Jayanthi Signed-off-by: Michael Adisumarta --- drivers/platform/msm/ipa/ipa_v3/ipa.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 0f3f2e536f55..7f18d2b6f4e6 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4557,6 +4557,8 @@ static int ipa3_pil_load_ipa_fws(void) if (IS_ERR_OR_NULL(subsystem_get_retval)) { IPAERR("Unable to trigger PIL process for FW loading\n"); return -EINVAL; + } else { + subsystem_put(subsystem_get_retval); } IPADBG("PIL FW loading process is complete\n"); -- GitLab From d91e8d4bea3028360100b712015a20f5843c29c7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 11 Aug 2017 02:11:33 +0900 Subject: [PATCH 1161/5498] BACKPORT: net: xfrm: support setting an output mark. On systems that use mark-based routing it may be necessary for routing lookups to use marks in order for packets to be routed correctly. An example of such a system is Android, which uses socket marks to route packets via different networks. Currently, routing lookups in tunnel mode always use a mark of zero, making routing incorrect on such systems. This patch adds a new output_mark element to the xfrm state and a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output mark differs from the existing xfrm mark in two ways: 1. The xfrm mark is used to match xfrm policies and states, while the xfrm output mark is used to set the mark (and influence the routing) of the packets emitted by those states. 2. The existing mark is constrained to be a subset of the bits of the originating socket or transformed packet, but the output mark is arbitrary and depends only on the state. The use of a separate mark provides additional flexibility. For example: - A packet subject to two transforms (e.g., transport mode inside tunnel mode) can have two different output marks applied to it, one for the transport mode SA and one for the tunnel mode SA. - On a system where socket marks determine routing, the packets emitted by an IPsec tunnel can be routed based on a mark that is determined by the tunnel, not by the marks of the unencrypted packets. - Support for setting the output marks can be introduced without breaking any existing setups that employ both mark-based routing and xfrm tunnel mode. Simply changing the code to use the xfrm mark for routing output packets could xfrm mark could change behaviour in a way that breaks these setups. If the output mark is unspecified or set to zero, the mark is not set or changed. [backport of upstream 077fbac405bfc6d41419ad6c1725804ad4e9887c] Bug: 63589535 Test: https://android-review.googlesource.com/452776/ passes Tested: make allyesconfig; make -j64 Tested: https://android-review.googlesource.com/452776 Signed-off-by: Lorenzo Colitti Signed-off-by: Steffen Klassert Change-Id: I76120fba036e21780ced31ad390faf491ea81e52 --- include/net/xfrm.h | 7 +++++-- include/uapi/linux/xfrm.h | 3 +++ net/ipv4/xfrm4_policy.c | 14 +++++++++----- net/ipv6/xfrm6_policy.c | 9 ++++++--- net/xfrm/xfrm_output.c | 3 +++ net/xfrm/xfrm_policy.c | 17 +++++++++-------- net/xfrm/xfrm_user.c | 11 +++++++++++ 7 files changed, 46 insertions(+), 18 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 2f00125131ca..af749f07db9b 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -161,6 +161,7 @@ struct xfrm_state { int header_len; int trailer_len; u32 extra_flags; + u32 output_mark; } props; struct xfrm_lifetime_cfg lft; @@ -288,8 +289,10 @@ struct xfrm_policy_afinfo { void (*garbage_collect)(struct net *net); struct dst_entry *(*dst_lookup)(struct net *net, int tos, const xfrm_address_t *saddr, - const xfrm_address_t *daddr); - int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr); + const xfrm_address_t *daddr, + u32 mark); + int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr, + u32 mark); void (*decode_session)(struct sk_buff *skb, struct flowi *fl, int reverse); diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 02d5125a5ee8..528db8161c98 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -300,6 +300,9 @@ enum xfrm_attr_type_t { XFRMA_SA_EXTRA_FLAGS, /* __u32 */ XFRMA_PROTO, /* __u8 */ XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ + XFRMA_PAD, + XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ + XFRMA_OUTPUT_MARK, /* __u32 */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 94fc16dad6c6..f18d7487d484 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -21,13 +21,15 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo; static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, int tos, const xfrm_address_t *saddr, - const xfrm_address_t *daddr) + const xfrm_address_t *daddr, + u32 mark) { struct rtable *rt; memset(fl4, 0, sizeof(*fl4)); fl4->daddr = daddr->a4; fl4->flowi4_tos = tos; + fl4->flowi4_mark = mark; if (saddr) fl4->saddr = saddr->a4; @@ -40,20 +42,22 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, - const xfrm_address_t *daddr) + const xfrm_address_t *daddr, + u32 mark) { struct flowi4 fl4; - return __xfrm4_dst_lookup(net, &fl4, tos, saddr, daddr); + return __xfrm4_dst_lookup(net, &fl4, tos, saddr, daddr, mark); } static int xfrm4_get_saddr(struct net *net, - xfrm_address_t *saddr, xfrm_address_t *daddr) + xfrm_address_t *saddr, xfrm_address_t *daddr, + u32 mark) { struct dst_entry *dst; struct flowi4 fl4; - dst = __xfrm4_dst_lookup(net, &fl4, 0, NULL, daddr); + dst = __xfrm4_dst_lookup(net, &fl4, 0, NULL, daddr, mark); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index d2425cef8a38..13457c950eee 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -28,13 +28,15 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo; static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, - const xfrm_address_t *daddr) + const xfrm_address_t *daddr, + u32 mark) { struct flowi6 fl6; struct dst_entry *dst; int err; memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_mark = mark; memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); if (saddr) memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); @@ -51,12 +53,13 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, } static int xfrm6_get_saddr(struct net *net, - xfrm_address_t *saddr, xfrm_address_t *daddr) + xfrm_address_t *saddr, xfrm_address_t *daddr, + u32 mark) { struct dst_entry *dst; struct net_device *dev; - dst = xfrm6_dst_lookup(net, 0, NULL, daddr); + dst = xfrm6_dst_lookup(net, 0, NULL, daddr, mark); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 7c532856b398..a26882c2a6d8 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -54,6 +54,9 @@ static int xfrm_output_one(struct sk_buff *skb, int err) goto error_nolock; } + if (x->props.output_mark) + skb->mark = x->props.output_mark; + err = x->outer_mode->output(x, skb); if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 253e7dda287b..b9481872d3fa 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -117,7 +117,7 @@ static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr, - int family) + int family, u32 mark) { struct xfrm_policy_afinfo *afinfo; struct dst_entry *dst; @@ -126,7 +126,7 @@ static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, if (unlikely(afinfo == NULL)) return ERR_PTR(-EAFNOSUPPORT); - dst = afinfo->dst_lookup(net, tos, saddr, daddr); + dst = afinfo->dst_lookup(net, tos, saddr, daddr, mark); xfrm_policy_put_afinfo(afinfo); @@ -136,7 +136,7 @@ static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, xfrm_address_t *prev_saddr, xfrm_address_t *prev_daddr, - int family) + int family, u32 mark) { struct net *net = xs_net(x); xfrm_address_t *saddr = &x->props.saddr; @@ -152,7 +152,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, daddr = x->coaddr; } - dst = __xfrm_dst_lookup(net, tos, saddr, daddr, family); + dst = __xfrm_dst_lookup(net, tos, saddr, daddr, family, mark); if (!IS_ERR(dst)) { if (prev_saddr != saddr) @@ -1370,14 +1370,14 @@ int __xfrm_sk_clone_policy(struct sock *sk) static int xfrm_get_saddr(struct net *net, xfrm_address_t *local, xfrm_address_t *remote, - unsigned short family) + unsigned short family, u32 mark) { int err; struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EINVAL; - err = afinfo->get_saddr(net, local, remote); + err = afinfo->get_saddr(net, local, remote, mark); xfrm_policy_put_afinfo(afinfo); return err; } @@ -1406,7 +1406,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl, remote = &tmpl->id.daddr; local = &tmpl->saddr; if (xfrm_addr_any(local, tmpl->encap_family)) { - error = xfrm_get_saddr(net, &tmp, remote, tmpl->encap_family); + error = xfrm_get_saddr(net, &tmp, remote, tmpl->encap_family, 0); if (error) goto fail; local = &tmp; @@ -1687,7 +1687,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { family = xfrm[i]->props.family; dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr, - family); + family, + xfrm[i]->props.output_mark); err = PTR_ERR(dst); if (IS_ERR(dst)) goto put_states; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 1c474637b5d9..212733f453a4 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -561,6 +561,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, xfrm_mark_get(attrs, &x->mark); + if (attrs[XFRMA_OUTPUT_MARK]) + x->props.output_mark = nla_get_u32(attrs[XFRMA_OUTPUT_MARK]); + err = __xfrm_init_state(x, false); if (err) goto error; @@ -840,6 +843,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x, } if (x->security) ret = copy_sec_ctx(x->security, skb); + if (x->props.output_mark) { + ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark); + if (ret) + goto out; + } out: return ret; } @@ -2388,6 +2396,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, [XFRMA_PROTO] = { .type = NLA_U8 }, [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, + [XFRMA_OUTPUT_MARK] = { .len = NLA_U32 }, }; static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { @@ -2596,6 +2605,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x) l += nla_total_size(sizeof(*x->coaddr)); if (x->props.extra_flags) l += nla_total_size(sizeof(x->props.extra_flags)); + if (x->props.output_mark) + l += nla_total_size(sizeof(x->props.output_mark)); /* Must count x->lastused as it may become non-zero behind our back. */ l += nla_total_size(sizeof(u64)); -- GitLab From d2bc87ae09c8ee959a6778e8ccb551444acfcc32 Mon Sep 17 00:00:00 2001 From: Robb Glasser Date: Fri, 24 Mar 2017 16:23:37 -0700 Subject: [PATCH 1162/5498] Prevent potential double frees in sg driver sg_ioctl could be spammed by requests, leading to a double free in __free_pages. This protects the entry points of sg_ioctl where the memory could be corrupted by a double call to __free_pages if multiple requests are happening concurrently. Bug:35644812 Change-Id: Ie13f65beb6974430f90292e2742841b26aecb8b1 Signed-off-by: Robb Glasser Git-commit: 22d8e80738b5ce8784d59b48b0b051a520da4bec Git-repo: https://android.googlesource.com/kernel/msm [srkupp@codeaurora.org: Resolved trivial conflicts] Signed-off-by: Srinivasa Rao Kuppala --- drivers/scsi/sg.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 7f7f3c0b560a..73c116ba4aba 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -511,7 +511,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) old_hdr->result = EIO; break; case DID_ERROR: - old_hdr->result = (srp->sense_b[0] == 0 && + old_hdr->result = (srp->sense_b[0] == 0 && hp->masked_status == GOOD) ? 0 : EIO; break; default: @@ -881,8 +881,10 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -ENXIO; if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR)) return -EFAULT; + mutex_lock(&sfp->parentdp->open_rel_lock); result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, 1, read_only, 1, &srp); + mutex_unlock(&sfp->parentdp->open_rel_lock); if (result < 0) return result; result = wait_event_interruptible(sfp->read_wait, @@ -924,6 +926,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) val = (int) sfp->reserve.bufflen; sg_remove_scat(sfp, &sfp->reserve); sg_build_reserve(sfp, val); + mutex_unlock(&sfp->parentdp->open_rel_lock); } } else { if (atomic_read(&sdp->detaching)) @@ -991,8 +994,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) result = get_user(val, ip); if (result) return result; - if (val < 0) - return -EINVAL; + if (val < 0) + return -EINVAL; val = min_t(int, val, max_sectors_bytes(sdp->device->request_queue)); mutex_lock(&sfp->f_mutex); @@ -1005,6 +1008,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) sg_remove_scat(sfp, &sfp->reserve); sg_build_reserve(sfp, val); + mutex_unlock(&sfp->parentdp->open_rel_lock); } mutex_unlock(&sfp->f_mutex); return 0; @@ -1060,8 +1064,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (srp) { rinfo[val].req_state = srp->done + 1; rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & + srp->header.masked_status & + srp->header.host_status & srp->header.driver_status; if (srp->done) rinfo[val].duration = @@ -1082,7 +1086,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) } } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - result = __copy_to_user(p, rinfo, + result = __copy_to_user(p, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE); result = result ? -EFAULT : 0; kfree(rinfo); @@ -1184,14 +1188,14 @@ static long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned lon return -ENXIO; sdev = sdp->device; - if (sdev->host->hostt->compat_ioctl) { + if (sdev->host->hostt->compat_ioctl) { int ret; ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg); return ret; } - + return -ENOIOCTLCMD; } #endif @@ -1681,7 +1685,7 @@ init_sg(void) else def_reserved_size = sg_big_buff; - rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), + rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS, "sg"); if (rc) return rc; @@ -2367,7 +2371,7 @@ static const struct file_operations adio_fops = { }; static int sg_proc_single_open_dressz(struct inode *inode, struct file *file); -static ssize_t sg_proc_write_dressz(struct file *filp, +static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off); static const struct file_operations dressz_fops = { .owner = THIS_MODULE, @@ -2507,7 +2511,7 @@ static int sg_proc_single_open_adio(struct inode *inode, struct file *file) return single_open(file, sg_proc_seq_show_int, &sg_allow_dio); } -static ssize_t +static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { @@ -2528,7 +2532,7 @@ static int sg_proc_single_open_dressz(struct inode *inode, struct file *file) return single_open(file, sg_proc_seq_show_int, &sg_big_buff); } -static ssize_t +static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { @@ -2688,7 +2692,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) hp = &srp->header; new_interface = (hp->interface_id == '\0') ? 0 : 1; if (srp->res_used) { - if (new_interface && + if (new_interface && (SG_FLAG_MMAP_IO & hp->flags)) cp = " mmap>> "; else @@ -2705,6 +2709,9 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) seq_puts(s, srp->done ? ((1 == srp->done) ? "rcv:" : "fin:") : "act:"); + seq_printf(s, srp->done ? + ((1 == srp->done) ? "rcv:" : "fin:") + : "act:"); seq_printf(s, " id=%d blen=%d", srp->header.pack_id, blen); if (srp->done) -- GitLab From 7ccce5be5015cf3fceee928e2baf267f2c82881d Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Mon, 9 Oct 2017 16:29:24 +0530 Subject: [PATCH 1163/5498] defconfig: 8909w: Disable USB HOST drivers Remove USB Host drivers to reduce USB memory footprint. Change-Id: I99630a878a3024a6225f3efdbf25fe34e8798676 Signed-off-by: Ajay Agarwal --- arch/arm/configs/msm8909w-perf_defconfig | 10 ---------- arch/arm/configs/msm8909w_defconfig | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 46f72f4ef0f4..774d09d43e5d 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -234,7 +234,6 @@ CONFIG_PPPOLAC=y CONFIG_PPPOPNS=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y -CONFIG_USB_USBNET=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y @@ -338,18 +337,9 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_BACKLIGHT_GENERIC is not set CONFIG_SOUND=y CONFIG_SND=y -CONFIG_SND_USB_AUDIO=y CONFIG_SND_SOC=y CONFIG_SND_SOC_MSM8909=y CONFIG_UHID=y -CONFIG_USB=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_MON=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_MSM=y -CONFIG_USB_ACM=y -CONFIG_USB_SERIAL=y -CONFIG_USB_EHSET_TEST_FIXTURE=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_DEBUG_FILES=y CONFIG_USB_GADGET_DEBUG_FS=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 8c6129203aef..ec898e7f1c13 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -237,7 +237,6 @@ CONFIG_PPPOLAC=y CONFIG_PPPOPNS=y CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y -CONFIG_USB_USBNET=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y @@ -344,18 +343,9 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_BACKLIGHT_GENERIC is not set CONFIG_SOUND=y CONFIG_SND=y -CONFIG_SND_USB_AUDIO=y CONFIG_SND_SOC=y CONFIG_SND_SOC_MSM8909=y CONFIG_UHID=y -CONFIG_USB=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_MON=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_MSM=y -CONFIG_USB_ACM=y -CONFIG_USB_SERIAL=y -CONFIG_USB_EHSET_TEST_FIXTURE=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_DEBUG_FILES=y CONFIG_USB_GADGET_DEBUG_FS=y -- GitLab From 6f8bf76dccca648f65ce1305e50c5168f8921183 Mon Sep 17 00:00:00 2001 From: kunleiz Date: Fri, 29 Sep 2017 14:22:38 +0800 Subject: [PATCH 1164/5498] rtac: add size check when reading cal data kvaddr buffer Add size check to ensure cal data bytes size fits inside the cal date when copying to user space buffer. CRs-Fixed: 2110256 Change-Id: I511999984684a9db4aaf1cf2c65eb1495c36980f Signed-off-by: kunleiz --- sound/soc/msm/qdsp6v2/rtac.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c index 641a17bded64..9b5e72f94d1b 100644 --- a/sound/soc/msm/qdsp6v2/rtac.c +++ b/sound/soc/msm/qdsp6v2/rtac.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -883,6 +883,14 @@ int send_adm_apr(void *buf, u32 opcode) bytes_returned = ((u32 *)rtac_cal[ADM_RTAC_CAL].cal_data. kvaddr)[2] + 3 * sizeof(u32); + if (bytes_returned > rtac_cal[ADM_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + if (bytes_returned > user_buf_size) { pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", __func__, user_buf_size, bytes_returned); @@ -1105,6 +1113,14 @@ int send_rtac_asm_apr(void *buf, u32 opcode) bytes_returned = ((u32 *)rtac_cal[ASM_RTAC_CAL].cal_data. kvaddr)[2] + 3 * sizeof(u32); + if (bytes_returned > rtac_cal[ASM_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + if (bytes_returned > user_buf_size) { pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", __func__, user_buf_size, bytes_returned); @@ -1364,6 +1380,14 @@ static int send_rtac_afe_apr(void *buf, uint32_t opcode) bytes_returned = get_resp->param_size + sizeof(struct afe_port_param_data_v2); + if (bytes_returned > rtac_cal[AFE_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + if (bytes_returned > user_afe_buf.buf_size) { pr_err("%s: user size = 0x%x, returned size = 0x%x\n", __func__, user_afe_buf.buf_size, @@ -1586,6 +1610,14 @@ int send_voice_apr(u32 mode, void *buf, u32 opcode) bytes_returned = ((u32 *)rtac_cal[VOICE_RTAC_CAL].cal_data. kvaddr)[2] + 3 * sizeof(u32); + if (bytes_returned > rtac_cal[VOICE_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + if (bytes_returned > user_buf_size) { pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", __func__, user_buf_size, bytes_returned); -- GitLab From 3d1593e8f0e69fa9808ab1d0e4f7801b4d72a2d5 Mon Sep 17 00:00:00 2001 From: Vidyasagar Pasmel Date: Tue, 10 Oct 2017 15:30:37 +0530 Subject: [PATCH 1165/5498] ARM: dts: msm: Move IO-Expander to specific dtsi file for APQ8017 Move IO-expander nodes configuration to dtsi specific file for resolving the build conflict. Change-Id: Ia1655cf473689e2e2123f959979f8bb5b86100df Signed-off-by: Vidyasagar Pasmel --- .../dts/qcom/apq8017-io-expander-cdp.dtsi | 95 +++++++++++++++++++ .../dts/qcom/apq8017-no-pmi-wcd-rome-cdp.dts | 1 + arch/arm/boot/dts/qcom/msm8917-cdp.dtsi | 77 --------------- 3 files changed, 96 insertions(+), 77 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/apq8017-io-expander-cdp.dtsi diff --git a/arch/arm/boot/dts/qcom/apq8017-io-expander-cdp.dtsi b/arch/arm/boot/dts/qcom/apq8017-io-expander-cdp.dtsi new file mode 100644 index 000000000000..e24d3ff126ff --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8017-io-expander-cdp.dtsi @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm8917-pinctrl.dtsi" +#include "msm8917-camera-sensor-cdp.dtsi" + +&soc { + usbhub_vbus_vreg: usbhub_vbus_vreg { + compatible = "regulator-fixed"; + regulator-name = "usbhub_vbus_vreg"; + gpio = <&ioexp_gpios 3 0>; + enable-active-high; + }; + + usbeth_vbus_vreg: usbeth_vbus_vreg { + compatible = "regulator-fixed"; + regulator-name = "usbeth_vbus_vreg"; + gpio = <&ioexp_gpios 7 0>; + enable-active-high; + vin-supply = <&usbhub_vbus_vreg>; + }; +}; + +/* SX1509q i/o expander */ +&i2c_2 { + status = "ok"; + ioexp2_gpios: sx150x@3e { + status = "ok"; + compatible = "sx1509q"; + #gpio-cells = <2>; + gpio-controller; + reg = <0x3e>; /* I2C Client Addr */ + sx150x,pullup_ena = <0x0>; + sx150x,pulldn_ena = <0x0>; + sx150x,float_ena = <0x00>; + sx150x,polarity = <0x0>; + sx150x,gpio_base= <300>; + }; +}; + +/* SX1508q i/o expander */ +&i2c_4 { + status = "ok"; + ioexp_gpios: sx150x@20 { + status = "ok"; + compatible = "sx1508q"; + #gpio-cells = <2>; + gpio-controller; + reg = <0x20>; /* I2C Client Addr */ + sx150x,pullup_ena = <0x0>; + sx150x,pulldn_ena = <0x0>; + sx150x,float_ena = <0x00>; + sx150x,polarity = <0x0>; + sx150x,gpio_base = <200>; + vdd-supply = <&pm8917_l6>; + qcom,vdd-max-voltage = <1800000>; + qcom,vdd-min-voltage = <1800000>; + }; + + /* usb2533 flex hub */ + usb2533@2c { + status = "disabled"; + compatible = "qcom,usb2533-flex-hub"; + reg = <0x2d>; + vbus-supply = <&usbeth_vbus_vreg>; + pinctrl-names = "default"; + pinctrl-0 = <&usb2533_hub_reset>; + qcom,hub-reset-gpio = <&tlmm 100 0>; + qcom,usbeth-reset-gpio = <&ioexp_gpios 5 0>; + }; +}; + +/* KS8851 eth over spi */ +&spi_6 { + status = "ok"; + ethernet@0 { + status = "ok"; + compatible = "micrel,ks8851"; + reg = <1>; /* CS-1 */ + interrupt-parent = <&tlmm>; + interrupts = <97 0>; + reset-gpios = <&ioexp_gpios 2 0>; + spi-max-frequency = <40000000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/apq8017-no-pmi-wcd-rome-cdp.dts b/arch/arm/boot/dts/qcom/apq8017-no-pmi-wcd-rome-cdp.dts index 47341abd6155..cf60299fd983 100644 --- a/arch/arm/boot/dts/qcom/apq8017-no-pmi-wcd-rome-cdp.dts +++ b/arch/arm/boot/dts/qcom/apq8017-no-pmi-wcd-rome-cdp.dts @@ -17,6 +17,7 @@ #include "apq8017-no-pmi-cdp.dtsi" #include "apq8017-rome.dtsi" #include "apq8017-audio.dtsi" +#include "apq8017-io-expander-cdp.dtsi" / { model = "Qualcomm Technologies, Inc. APQ8017 CDP no-PMI \ diff --git a/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi index 32da1aa920e5..9ddbf75c4042 100644 --- a/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi @@ -78,21 +78,6 @@ qcom,notify-host-mode; status = "disabled"; }; - - usbhub_vbus_vreg: usbhub_vbus_vreg { - compatible = "regulator-fixed"; - regulator-name = "usbhub_vbus_vreg"; - gpio = <&ioexp_gpios 3 0>; - enable-active-high; - }; - - usbeth_vbus_vreg: usbeth_vbus_vreg { - compatible = "regulator-fixed"; - regulator-name = "usbeth_vbus_vreg"; - gpio = <&ioexp_gpios 7 0>; - enable-active-high; - vin-supply = <&usbhub_vbus_vreg>; - }; }; &spi_3 { /* BLSP1 QUP3 */ @@ -264,65 +249,3 @@ status = "ok"; }; -/* SX1509q i/o expander */ -&i2c_2 { - status = "ok"; - ioexp2_gpios: sx150x@3e { - status = "ok"; - compatible = "sx1509q"; - #gpio-cells = <2>; - gpio-controller; - reg = <0x3E>; /* I2C Client Addr */ - sx150x,pullup_ena = <0x0>; - sx150x,pulldn_ena = <0x0>; - sx150x,float_ena = <0x00>; - sx150x,polarity = <0x0>; - sx150x,gpio_base= <300>; - }; -}; - -/* SX1508q i/o expander */ -&i2c_4 { - status = "ok"; - ioexp_gpios: sx150x@20 { - status = "ok"; - compatible = "sx1508q"; - #gpio-cells = <2>; - gpio-controller; - reg = <0x20>; /* I2C Client Addr */ - sx150x,pullup_ena = <0x0>; - sx150x,pulldn_ena = <0x0>; - sx150x,float_ena = <0x00>; - sx150x,polarity = <0x0>; - sx150x,gpio_base = <200>; - vdd-supply = <&pm8917_l6>; - qcom,vdd-max-voltage = <1800000>; - qcom,vdd-min-voltage = <1800000>; - }; - /* usb2533 flex hub */ - usb2533@2c { - status = "disabled"; - compatible = "qcom,usb2533-flex-hub"; - reg = <0x2d>; - vbus-supply = <&usbeth_vbus_vreg>; - pinctrl-names = "default"; - pinctrl-0 = <&usb2533_hub_reset>; - qcom,hub-reset-gpio = <&tlmm 100 0>; - qcom,usbeth-reset-gpio = <&ioexp_gpios 5 0>; - }; -}; - -/* KS8851 eth over spi */ -&spi_6 { - status = "ok"; - ethernet@0 { - status = "ok"; - compatible = "micrel,ks8851"; - reg = <1>; /* CS-1 */ - interrupt-parent = <&tlmm>; - interrupts = <97 0>; - reset-gpios = <&ioexp_gpios 2 0>; - spi-max-frequency = <40000000>; - }; -}; - -- GitLab From 20dc293da05450d105899e407c5a782ea2f12d44 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Tue, 26 Sep 2017 12:51:14 +0530 Subject: [PATCH 1166/5498] msm: ipa: prevent string buffer overflows In WAN ioctls user-supplied data structures contain string members,but there's no guarantee they're null-terminated, add the string terminator to prevent vulnerability of string buffer overflows. Change-Id: I17c06c94aa619a2cd3a678c495a31541a65a7741 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 6 ++++++ drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 77f1d87cc266..8ea8233715d8 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2596,6 +2596,12 @@ int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, struct ipa_get_data_stats_resp_msg_v01 *resp; int pipe_len, rc; + if (data != NULL) { + /* prevent string buffer overflows */ + data->upstreamIface[IFNAMSIZ-1] = '\0'; + data->tetherIface[IFNAMSIZ-1] = '\0'; + } + req = kzalloc(sizeof(struct ipa_get_data_stats_req_msg_v01), GFP_KERNEL); if (!req) { diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 42e243f46a7f..97c95a45ce3d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -2754,6 +2754,12 @@ int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, struct ipa_get_data_stats_resp_msg_v01 *resp; int pipe_len, rc; + if (data != NULL) { + /* prevent string buffer overflows */ + data->upstreamIface[IFNAMSIZ-1] = '\0'; + data->tetherIface[IFNAMSIZ-1] = '\0'; + } + req = kzalloc(sizeof(struct ipa_get_data_stats_req_msg_v01), GFP_KERNEL); if (!req) { -- GitLab From 3367f6310bb523bd0725c9b503a5d3b9bb747814 Mon Sep 17 00:00:00 2001 From: Shihuan Liu Date: Fri, 6 Oct 2017 20:38:02 -0700 Subject: [PATCH 1167/5498] msm: ipa: add null terminator Add null terminator at the end of string extend_ioctl_data.u.rmnet_mux_val.vchannel_name to avoid potential security issue. Change-Id: I57fe3a9f7e3ad6a499b62a9cfc49bc6b2f3b42e0 Acked-by: Shihuan Liu Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 2 ++ drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 77f1d87cc266..2b5581d0d407 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1423,6 +1423,8 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) mutex_unlock(&add_mux_channel_lock); return -EFAULT; } + extend_ioctl_data.u.rmnet_mux_val.vchannel_name + [IFNAMSIZ-1] = '\0'; IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n", extend_ioctl_data.u.rmnet_mux_val.mux_id, extend_ioctl_data.u.rmnet_mux_val.vchannel_name); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 42e243f46a7f..417da7c7c743 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1562,6 +1562,8 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) add_mux_channel_lock); return -EFAULT; } + extend_ioctl_data.u.rmnet_mux_val.vchannel_name + [IFNAMSIZ-1] = '\0'; IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n", extend_ioctl_data.u.rmnet_mux_val.mux_id, extend_ioctl_data.u.rmnet_mux_val.vchannel_name); -- GitLab From 88f5c1f0bcfc813911e4bb0b5a83b4740de1fce4 Mon Sep 17 00:00:00 2001 From: bings Date: Wed, 27 Sep 2017 08:36:03 +0800 Subject: [PATCH 1168/5498] ARM: dts: msm: Enable sdio bus power for sdx20 v2 single wifi MTP To make TF LTE Coex work, enable sdio bus power for sdx20 v2 single wifi MTP CRs-Fixed: 2117453 Change-Id: Icead0c27fdc7009aa0e26b2100a60727c9ed8385 Signed-off-by: bings --- .../boot/dts/qcom/sdx20-v2-nand-singlewifi-dualwificonf-mtp.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/sdx20-v2-nand-singlewifi-dualwificonf-mtp.dts b/arch/arm/boot/dts/qcom/sdx20-v2-nand-singlewifi-dualwificonf-mtp.dts index 78c152bf5339..a6f8544493dc 100644 --- a/arch/arm/boot/dts/qcom/sdx20-v2-nand-singlewifi-dualwificonf-mtp.dts +++ b/arch/arm/boot/dts/qcom/sdx20-v2-nand-singlewifi-dualwificonf-mtp.dts @@ -24,7 +24,7 @@ &sdhc_1 { vdd-supply = <&sdc_vreg>; - vdd-io-supply = <&pmd9650_l6>; + vdd-io-supply = <&pmd9650_l7>; qcom,vdd-io-voltage-level = <1800000 1800000>; qcom,vdd-io-always-on; qcom,vdd-io-current-level = <200 10000>; -- GitLab From 98c492009239b82e3e9829400f3ddf986c86fe86 Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Thu, 14 Sep 2017 16:53:00 +0800 Subject: [PATCH 1169/5498] drivers: input: add general hall sensor driver This change adds general hall sensor driver. This snapshot is taken as of msm-3.10 'commit 1227f5746677 ("soc: qcom: smp2p: spinlock_test: Initialize work item")' with minor errors fixed. Change-Id: Icd048fea42facca48b5d6b387218d6847f730d58 Signed-off-by: Bingzhe Cai --- drivers/input/Kconfig | 7 + drivers/input/Makefile | 2 +- drivers/input/hall_sensor.c | 333 ++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 drivers/input/hall_sensor.c diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 2a4c51377f86..1819769051f3 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -187,6 +187,13 @@ config INPUT_KEYCOMBO ---help--- Say Y here if you want to take action when some keys are pressed; +config SENSORS_HALL + bool "Hall sensor" + depends on INPUT + ---help--- + Say y here if you want to use this hall sensor driver, it + is like a switch. For example, lid. + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index ee4c06520bb4..93289a43536e 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -27,4 +27,4 @@ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o obj-$(CONFIG_INPUT_KEYCOMBO) += keycombo.o - +obj-$(CONFIG_SENSORS_HALL) += hall_sensor.o diff --git a/drivers/input/hall_sensor.c b/drivers/input/hall_sensor.c new file mode 100644 index 000000000000..7f0a3d3f821c --- /dev/null +++ b/drivers/input/hall_sensor.c @@ -0,0 +1,333 @@ +/* + * + * Copyright (c) 2014-2015,2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LID_DEV_NAME "hall_sensor" +#define HALL_INPUT "/dev/input/hall_dev" + +struct hall_data { + int gpio; /* device use gpio number */ + int irq; /* device request irq number */ + int active_low; /* gpio active high or low for valid value */ + bool wakeup; /* device can wakeup system or not */ + struct input_dev *hall_dev; + struct regulator *vddio; + u32 min_uv; /* device allow minimum voltage */ + u32 max_uv; /* device allow max voltage */ +}; + +static irqreturn_t hall_interrupt_handler(int irq, void *dev) +{ + int value; + struct hall_data *data = dev; + + value = (gpio_get_value_cansleep(data->gpio) ? 1 : 0) ^ + data->active_low; + if (value) { + input_report_switch(data->hall_dev, SW_LID, 0); + dev_dbg(&data->hall_dev->dev, "far\n"); + } else { + input_report_switch(data->hall_dev, SW_LID, 1); + dev_dbg(&data->hall_dev->dev, "near\n"); + } + input_sync(data->hall_dev); + + return IRQ_HANDLED; +} + +static int hall_input_init(struct platform_device *pdev, + struct hall_data *data) +{ + int err = -1; + + data->hall_dev = devm_input_allocate_device(&pdev->dev); + if (!data->hall_dev) { + dev_err(&data->hall_dev->dev, + "input device allocation failed\n"); + return -EINVAL; + } + data->hall_dev->name = LID_DEV_NAME; + data->hall_dev->phys = HALL_INPUT; + __set_bit(EV_SW, data->hall_dev->evbit); + __set_bit(SW_LID, data->hall_dev->swbit); + + err = input_register_device(data->hall_dev); + if (err < 0) { + dev_err(&data->hall_dev->dev, + "unable to register input device %s\n", + LID_DEV_NAME); + return err; + } + + return 0; +} + +static int hall_config_regulator(struct platform_device *dev, bool on) +{ + struct hall_data *data = dev_get_drvdata(&dev->dev); + int rc = 0; + + if (on) { + data->vddio = devm_regulator_get(&dev->dev, "vddio"); + if (IS_ERR(data->vddio)) { + rc = PTR_ERR(data->vddio); + dev_err(&dev->dev, "Regulator vddio get failed rc=%d\n", + rc); + data->vddio = NULL; + return rc; + } + + if (regulator_count_voltages(data->vddio) > 0) { + rc = regulator_set_voltage( + data->vddio, + data->min_uv, + data->max_uv); + if (rc) { + dev_err(&dev->dev, "Regulator vddio Set voltage failed rc=%d\n", + rc); + goto deinit_vregs; + } + } + return rc; + } + +deinit_vregs: + if (regulator_count_voltages(data->vddio) > 0) + regulator_set_voltage(data->vddio, 0, data->max_uv); + + return rc; +} + +static int hall_set_regulator(struct platform_device *dev, bool on) +{ + struct hall_data *data = dev_get_drvdata(&dev->dev); + int rc = 0; + + if (on) { + if (!IS_ERR_OR_NULL(data->vddio)) { + rc = regulator_enable(data->vddio); + if (rc) { + dev_err(&dev->dev, "Enable regulator vddio failed rc=%d\n", + rc); + goto disable_regulator; + } + } + return rc; + } + if (!IS_ERR_OR_NULL(data->vddio)) { + rc = regulator_disable(data->vddio); + if (rc) + dev_err(&dev->dev, "Disable regulator vddio failed rc=%d\n", + rc); + } + return 0; + +disable_regulator: + if (!IS_ERR_OR_NULL(data->vddio)) + regulator_disable(data->vddio); + return rc; +} + +#ifdef CONFIG_OF +static int hall_parse_dt(struct device *dev, struct hall_data *data) +{ + unsigned int tmp; + u32 tempval; + int rc; + struct device_node *np = dev->of_node; + + data->gpio = of_get_named_gpio_flags(dev->of_node, + "linux,gpio-int", 0, &tmp); + if (!gpio_is_valid(data->gpio)) { + dev_err(dev, "hall gpio is not valid\n"); + return -EINVAL; + } + data->active_low = tmp & OF_GPIO_ACTIVE_LOW ? 0 : 1; + + data->wakeup = of_property_read_bool(np, "linux,wakeup"); + + rc = of_property_read_u32(np, "linux,max-uv", &tempval); + if (rc) { + dev_err(dev, "unable to read max-uv\n"); + return -EINVAL; + } + data->max_uv = tempval; + + rc = of_property_read_u32(np, "linux,min-uv", &tempval); + if (rc) { + dev_err(dev, "unable to read min-uv\n"); + return -EINVAL; + } + data->min_uv = tempval; + + return 0; +} +#else +static int hall_parse_dt(struct device *dev, struct hall_data *data) +{ + return -EINVAL; +} +#endif + +static int hall_driver_probe(struct platform_device *dev) +{ + struct hall_data *data; + int err = 0; + int irq_flags; + + dev_dbg(&dev->dev, "hall_driver probe\n"); + data = devm_kzalloc(&dev->dev, sizeof(struct hall_data), GFP_KERNEL); + if (data == NULL) { + err = -ENOMEM; + dev_err(&dev->dev, + "failed to allocate memory %d\n", err); + goto exit; + } + dev_set_drvdata(&dev->dev, data); + if (dev->dev.of_node) { + err = hall_parse_dt(&dev->dev, data); + if (err < 0) { + dev_err(&dev->dev, "Failed to parse device tree\n"); + goto exit; + } + } else if (dev->dev.platform_data != NULL) { + memcpy(data, dev->dev.platform_data, sizeof(*data)); + } else { + dev_err(&dev->dev, "No valid platform data.\n"); + err = -ENODEV; + goto exit; + } + + err = hall_input_init(dev, data); + if (err < 0) { + dev_err(&dev->dev, "input init failed\n"); + goto exit; + } + + if (!gpio_is_valid(data->gpio)) { + dev_err(&dev->dev, "gpio is not valid\n"); + err = -EINVAL; + goto exit; + } + + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT; + err = gpio_request_one(data->gpio, GPIOF_DIR_IN, "hall_sensor_irq"); + if (err) { + dev_err(&dev->dev, "unable to request gpio %d\n", data->gpio); + goto exit; + } + + data->irq = gpio_to_irq(data->gpio); + err = devm_request_threaded_irq(&dev->dev, data->irq, NULL, + hall_interrupt_handler, + irq_flags, "hall_sensor", data); + if (err < 0) { + dev_err(&dev->dev, "request irq failed : %d\n", data->irq); + goto free_gpio; + } + + device_init_wakeup(&dev->dev, data->wakeup); + enable_irq_wake(data->irq); + + err = hall_config_regulator(dev, true); + if (err < 0) { + dev_err(&dev->dev, "Configure power failed: %d\n", err); + goto free_irq; + } + + err = hall_set_regulator(dev, true); + if (err < 0) { + dev_err(&dev->dev, "power on failed: %d\n", err); + goto err_regulator_init; + } + + return 0; + +err_regulator_init: + hall_config_regulator(dev, false); +free_irq: + disable_irq_wake(data->irq); + device_init_wakeup(&dev->dev, 0); +free_gpio: + gpio_free(data->gpio); +exit: + return err; +} + +static int hall_driver_remove(struct platform_device *dev) +{ + struct hall_data *data = dev_get_drvdata(&dev->dev); + + disable_irq_wake(data->irq); + device_init_wakeup(&dev->dev, 0); + if (data->gpio) + gpio_free(data->gpio); + hall_set_regulator(dev, false); + hall_config_regulator(dev, false); + + return 0; +} + +static struct platform_device_id hall_id[] = { + {LID_DEV_NAME, 0 }, + { }, +}; + + +#ifdef CONFIG_OF +static struct of_device_id hall_match_table[] = { + {.compatible = "hall-switch", }, + { }, +}; +#endif + +static struct platform_driver hall_driver = { + .driver = { + .name = LID_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(hall_match_table), + }, + .probe = hall_driver_probe, + .remove = hall_driver_remove, + .id_table = hall_id, +}; + +static int __init hall_init(void) +{ + return platform_driver_register(&hall_driver); +} + +static void __exit hall_exit(void) +{ + platform_driver_unregister(&hall_driver); +} + +module_init(hall_init); +module_exit(hall_exit); +MODULE_DESCRIPTION("Hall sensor driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From a80e57bc84eac7c1e547c1274e46d129b7169df0 Mon Sep 17 00:00:00 2001 From: Sayali Lokhande Date: Thu, 5 Oct 2017 11:36:17 +0530 Subject: [PATCH 1170/5498] scsi: ufs: Fix stack overflow read in ufs debugfs driver When getting string from userspace by simple_write_to_buffer in ufs_qcom_dbg_testbus_cfg_write() function, the string copied to configuration is not terminated with '\0'. Thus stack overflow read may occur while copying configuration to host->testbus.select_major, which will result in information leak later while printing error message. This change adds null character at the end of the input string to avoid information leak. Change-Id: Ic9a9204def4bd6976f42f5f80ae5c0a9730afeb1 Signed-off-by: Sayali Lokhande --- drivers/scsi/ufs/ufs-qcom-debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c index 8532439c392d..4e0c5b7d2d3f 100644 --- a/drivers/scsi/ufs/ufs-qcom-debugfs.c +++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c @@ -104,7 +104,7 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, loff_t *ppos) { struct ufs_qcom_host *host = file->f_mapping->host->i_private; - char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {0}; + char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {'\0'}; loff_t buff_pos = 0; char *comma; int ret = 0; @@ -118,6 +118,7 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, __func__); goto out; } + configuration[ret] = '\0'; comma = strnchr(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, ','); if (!comma || comma == configuration) { -- GitLab From ab236f280c2c1a68983f9485ca74e713428d4a28 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Mon, 29 Aug 2016 16:14:00 +0530 Subject: [PATCH 1171/5498] defconfig: msm8909w: Enable core control core control enables you to save power by controlling how many cores are being used depending on the load in the system. Change-Id: I3e9f2df46014e4269a1ae5394b5d3f8c6fa39c15 Signed-off-by: Pavankumar Kondeti --- arch/arm/configs/msm8909w-1gb-perf_defconfig | 1 + arch/arm/configs/msm8909w-1gb_defconfig | 1 + arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/configs/msm8909w-1gb-perf_defconfig b/arch/arm/configs/msm8909w-1gb-perf_defconfig index 0d1df056a490..eeeae22338be 100644 --- a/arch/arm/configs/msm8909w-1gb-perf_defconfig +++ b/arch/arm/configs/msm8909w-1gb-perf_defconfig @@ -13,6 +13,7 @@ CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y +CONFIG_SCHED_CORE_CTL=y CONFIG_SCHED_QHMP=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set diff --git a/arch/arm/configs/msm8909w-1gb_defconfig b/arch/arm/configs/msm8909w-1gb_defconfig index ab8dedc49ad4..ada44b8677bf 100644 --- a/arch/arm/configs/msm8909w-1gb_defconfig +++ b/arch/arm/configs/msm8909w-1gb_defconfig @@ -13,6 +13,7 @@ CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y +CONFIG_SCHED_CORE_CTL=y CONFIG_SCHED_QHMP=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 774d09d43e5d..cdfa9bf5a467 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -13,6 +13,7 @@ CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y +CONFIG_SCHED_CORE_CTL=y CONFIG_SCHED_QHMP=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index ec898e7f1c13..170bbcd92206 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -13,6 +13,7 @@ CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y +CONFIG_SCHED_CORE_CTL=y CONFIG_SCHED_QHMP=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set -- GitLab From 2c270650d749b8a945427278ad38e61f77564b66 Mon Sep 17 00:00:00 2001 From: Sayali Lokhande Date: Wed, 4 Oct 2017 11:56:14 +0530 Subject: [PATCH 1172/5498] scsi: ufs: Fix race condition in ufs qcom debugfs driver In function ufs_qcom_dbg_testbus_cfg_write(), the global variable ufs_qcom_host (host) is not protected by lock. In function ufs_qcom_testbug_config(), we are checking this variable in switch case and there is possibility of race condition while accessing host variable in both of these functions. This change fixes the possible race scenario using spin_lock on host_lock. Change-Id: I4e3fa1c3b80b92a648965371e12e52352cf80ce5 Signed-off-by: Sayali Lokhande --- drivers/scsi/ufs/ufs-qcom-debugfs.c | 12 ++++++++- drivers/scsi/ufs/ufs-qcom.c | 42 +++++++++++++++++------------ drivers/scsi/ufs/ufs-qcom.h | 6 +++-- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c index 8532439c392d..3b4553d425c5 100644 --- a/drivers/scsi/ufs/ufs-qcom-debugfs.c +++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Linux Foundation. All rights reserved. + * Copyright (c) 2015,2017, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -110,6 +110,9 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, int ret = 0; int major; int minor; + unsigned long flags; + struct ufs_hba *hba = host->hba; + ret = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, &buff_pos, ubuf, cnt); @@ -135,8 +138,15 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, goto out; } + if (!ufs_qcom_testbus_cfg_is_ok(host, major, minor)) { + ret = -EPERM; + goto out; + } + + spin_lock_irqsave(hba->host->host_lock, flags); host->testbus.select_major = (u8)major; host->testbus.select_minor = (u8)minor; + spin_unlock_irqrestore(hba->host->host_lock, flags); /* * Sanity check of the {major, minor} tuple is done in the diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 634e7ea98903..661fb437f608 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2193,12 +2193,13 @@ static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host) host->testbus.select_minor = 1; } -static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) +bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host, + u8 select_major, u8 select_minor) { - if (host->testbus.select_major >= TSTBUS_MAX) { + if (select_major >= TSTBUS_MAX) { dev_err(host->hba->dev, "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n", - __func__, host->testbus.select_major); + __func__, select_major); return false; } @@ -2207,10 +2208,10 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) * mappings of select_minor, since there is no harm in * configuring a non-existent select_minor */ - if (host->testbus.select_minor > 0x1F) { + if (select_minor > 0x1F) { dev_err(host->hba->dev, "%s: 0x%05X is not a legal testbus option\n", - __func__, host->testbus.select_minor); + __func__, select_minor); return false; } @@ -2219,16 +2220,16 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) int ufs_qcom_testbus_config(struct ufs_qcom_host *host) { - int reg; - int offset; + int reg = 0; + int offset, ret = 0, testbus_sel_offset = 19; u32 mask = TEST_BUS_SUB_SEL_MASK; + unsigned long flags; + struct ufs_hba *hba; if (!host) return -EINVAL; - - if (!ufs_qcom_testbus_cfg_is_ok(host)) - return -EPERM; - + hba = host->hba; + spin_lock_irqsave(hba->host->host_lock, flags); switch (host->testbus.select_major) { case TSTBUS_UAWM: reg = UFS_TEST_BUS_CTRL_0; @@ -2285,20 +2286,27 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host) */ } mask <<= offset; - + spin_unlock_irqrestore(hba->host->host_lock, flags); pm_runtime_get_sync(host->hba->dev); ufshcd_hold(host->hba, false); - ufshcd_rmwl(host->hba, TEST_BUS_SEL, - (u32)host->testbus.select_major << 19, + if (reg) { + ufshcd_rmwl(host->hba, TEST_BUS_SEL, + (u32)host->testbus.select_major << testbus_sel_offset, REG_UFS_CFG1); - ufshcd_rmwl(host->hba, mask, + ufshcd_rmwl(host->hba, mask, (u32)host->testbus.select_minor << offset, reg); + } else { + dev_err(hba->dev, "%s: Problem setting minor\n", __func__); + ret = -EINVAL; + goto out; + } ufs_qcom_enable_test_bus(host); +out: ufshcd_release(host->hba, false); pm_runtime_put_sync(host->hba->dev); - return 0; + return ret; } static void ufs_qcom_testbus_read(struct ufs_hba *hba) diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index 04d2639f703c..63b075006e1b 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -99,7 +99,7 @@ enum { /* bit definitions for REG_UFS_CFG1 register */ #define QUNIPRO_SEL UFS_BIT(0) #define TEST_BUS_EN BIT(18) -#define TEST_BUS_SEL GENMASK(22, 19) +#define TEST_BUS_SEL 0x780000 /* bit definitions for REG_UFS_CFG2 register */ #define UAWM_HW_CGC_EN (1 << 0) @@ -355,6 +355,8 @@ ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg) #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba) #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) +bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host, u8 select_major, + u8 select_minor); int ufs_qcom_testbus_config(struct ufs_qcom_host *host); void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv, void (*print_fn)(struct ufs_hba *hba, int offset, int num_regs, -- GitLab From abb540b5397674243994c5327146b6fed7339b71 Mon Sep 17 00:00:00 2001 From: David Eccher Date: Fri, 11 Dec 2015 22:13:55 +0100 Subject: [PATCH 1173/5498] usb: gadget: inode.c: fix unbalanced spin_lock in ep0_write commit b7bd98b7db9fc8fe19da1a5ff0215311c6b95e46 upstream. Fix bad unlock balance: ep0_write enter with the locks locked from inode.c:1769, hence it must exit with spinlock held to avoid double unlock in dev_config. Signed-off-by: David Eccher Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index fe45311f243e..3b8af19c6da0 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1224,10 +1224,9 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) dev->gadget->ep0, dev->req, GFP_KERNEL); } + spin_lock_irq(&dev->lock); if (retval < 0) { - spin_lock_irq (&dev->lock); clean_req (dev->gadget->ep0, dev->req); - spin_unlock_irq (&dev->lock); } else retval = len; -- GitLab From c2b87de9b5bfe61b9babd47f840050375284dde6 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 21 Sep 2017 13:23:58 -0400 Subject: [PATCH 1174/5498] USB: gadgetfs: Fix crash caused by inadequate synchronization commit 520b72fc64debf8a86c3853b8e486aa5982188f0 upstream. The gadgetfs driver (drivers/usb/gadget/legacy/inode.c) was written before the UDC and composite frameworks were adopted; it is a legacy driver. As such, it expects that once bound to a UDC controller, it will not be unbound until it unregisters itself. However, the UDC framework does unbind function drivers while they are still registered. When this happens, it can cause the gadgetfs driver to misbehave or crash. For example, userspace can cause a crash by opening the device file and doing an ioctl call before setting up a configuration (found by Andrey Konovalov using the syzkaller fuzzer). This patch adds checks and synchronization to prevent these bad behaviors. It adds a udc_usage counter that the driver increments at times when it is using a gadget interface without holding the private spinlock. The unbind routine waits for this counter to go to 0 before returning, thereby ensuring that the UDC is no longer in use. The patch also adds a check in the dev_ioctl() routine to make sure the driver is bound to a UDC before dereferencing the gadget pointer, and it makes destroy_ep_files() synchronize with the endpoint I/O routines, to prevent the user from accessing an endpoint data structure after it has been removed. Signed-off-by: Alan Stern Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/inode.c | 41 +++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 3b8af19c6da0..a73d68fa9dad 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -26,7 +26,7 @@ #include #include #include - +#include #include #include @@ -113,6 +113,7 @@ enum ep0_state { struct dev_data { spinlock_t lock; atomic_t count; + int udc_usage; enum ep0_state state; /* P: lock */ struct usb_gadgetfs_event event [N_EVENT]; unsigned ev_next; @@ -620,9 +621,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) priv->actual = req->actual; schedule_work(&priv->work); } - spin_unlock(&epdata->dev->lock); usb_ep_free_request(ep, req); + spin_unlock(&epdata->dev->lock); put_ep(epdata); } @@ -1020,9 +1021,11 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) struct usb_request *req = dev->req; if ((retval = setup_req (ep, req, 0)) == 0) { + ++dev->udc_usage; spin_unlock_irq (&dev->lock); retval = usb_ep_queue (ep, req, GFP_KERNEL); spin_lock_irq (&dev->lock); + --dev->udc_usage; } dev->state = STATE_DEV_CONNECTED; @@ -1214,6 +1217,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) retval = setup_req (dev->gadget->ep0, dev->req, len); if (retval == 0) { dev->state = STATE_DEV_CONNECTED; + ++dev->udc_usage; spin_unlock_irq (&dev->lock); if (copy_from_user (dev->req->buf, buf, len)) retval = -EFAULT; @@ -1225,6 +1229,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) GFP_KERNEL); } spin_lock_irq(&dev->lock); + --dev->udc_usage; if (retval < 0) { clean_req (dev->gadget->ep0, dev->req); } else @@ -1321,9 +1326,21 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) struct usb_gadget *gadget = dev->gadget; long ret = -ENOTTY; - if (gadget->ops->ioctl) + spin_lock_irq(&dev->lock); + if (dev->state == STATE_DEV_OPENED || + dev->state == STATE_DEV_UNBOUND) { + /* Not bound to a UDC */ + } else if (gadget->ops->ioctl) { + ++dev->udc_usage; + spin_unlock_irq(&dev->lock); + ret = gadget->ops->ioctl (gadget, code, value); + spin_lock_irq(&dev->lock); + --dev->udc_usage; + } + spin_unlock_irq(&dev->lock); + return ret; } @@ -1554,10 +1571,12 @@ delegate: if (value < 0) break; + ++dev->udc_usage; spin_unlock (&dev->lock); value = usb_ep_queue (gadget->ep0, dev->req, GFP_KERNEL); spin_lock (&dev->lock); + --dev->udc_usage; if (value < 0) { clean_req (gadget->ep0, dev->req); break; @@ -1581,8 +1600,12 @@ delegate: req->length = value; req->zero = value < w_length; + ++dev->udc_usage; spin_unlock (&dev->lock); value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL); + spin_lock(&dev->lock); + --dev->udc_usage; + spin_unlock(&dev->lock); if (value < 0) { DBG (dev, "ep_queue --> %d\n", value); req->status = 0; @@ -1609,21 +1632,24 @@ static void destroy_ep_files (struct dev_data *dev) /* break link to FS */ ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles); list_del_init (&ep->epfiles); + spin_unlock_irq (&dev->lock); + dentry = ep->dentry; ep->dentry = NULL; parent = dentry->d_parent->d_inode; /* break link to controller */ + mutex_lock(&ep->lock); if (ep->state == STATE_EP_ENABLED) (void) usb_ep_disable (ep->ep); ep->state = STATE_EP_UNBOUND; usb_ep_free_request (ep->ep, ep->req); ep->ep = NULL; + mutex_unlock(&ep->lock); + wake_up (&ep->wait); put_ep (ep); - spin_unlock_irq (&dev->lock); - /* break link to dcache */ mutex_lock (&parent->i_mutex); d_delete (dentry); @@ -1694,6 +1720,11 @@ gadgetfs_unbind (struct usb_gadget *gadget) spin_lock_irq (&dev->lock); dev->state = STATE_DEV_UNBOUND; + while (dev->udc_usage > 0) { + spin_unlock_irq(&dev->lock); + usleep_range(1000, 2000); + spin_lock_irq(&dev->lock); + } spin_unlock_irq (&dev->lock); destroy_ep_files (dev); -- GitLab From 59bf5c20792e4842e063890dc08ad38960d87eca Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 21 Sep 2017 16:12:01 -0400 Subject: [PATCH 1175/5498] USB: gadgetfs: fix copy_to_user while holding spinlock commit 6e76c01e71551cb221c1f3deacb9dcd9a7346784 upstream. The gadgetfs driver as a long-outstanding FIXME, regarding a call of copy_to_user() made while holding a spinlock. This patch fixes the issue by dropping the spinlock and using the dev->udc_usage mechanism introduced by another recent patch to guard against status changes while the lock isn't held. Signed-off-by: Alan Stern Reported-by: Andrey Konovalov Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/inode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index a73d68fa9dad..352c59cff5b6 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1067,11 +1067,14 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) retval = -EIO; else { len = min (len, (size_t)dev->req->actual); -// FIXME don't call this with the spinlock held ... + ++dev->udc_usage; + spin_unlock_irq(&dev->lock); if (copy_to_user (buf, dev->req->buf, len)) retval = -EFAULT; else retval = len; + spin_lock_irq(&dev->lock); + --dev->udc_usage; clean_req (dev->gadget->ep0, dev->req); /* NOTE userspace can't yet choose to stall */ } -- GitLab From c57e55706abeffafbc52d28155297650dae6e2c0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 21 Sep 2017 15:59:30 -0400 Subject: [PATCH 1176/5498] usb-storage: unusual_devs entry to fix write-access regression for Seagate external drives commit 113f6eb6d50cfa5e2a1cdcf1678b12661fa272ab upstream. Kris Lindgren reports that without the NO_WP_DETECT flag, his Seagate external disk drive fails all write accesses. This regresssion dates back approximately to the start of the 4.x kernel releases. Signed-off-by: Alan Stern Reported-by: Kris Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 36584e1f15c5..4fefd76d2320 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1379,6 +1379,13 @@ UNUSUAL_DEV( 0x0bc2, 0x3010, 0x0000, 0x0000, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_SANE_SENSE ), +/* Reported by Kris Lindgren */ +UNUSUAL_DEV( 0x0bc2, 0x3332, 0x0000, 0x9999, + "Seagate", + "External", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT ), + UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999, "Maxtor", "USB to SATA", -- GitLab From 98578949897f4f46338870e1995230627fb42081 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 27 Sep 2017 18:47:12 +0900 Subject: [PATCH 1177/5498] usb: renesas_usbhs: fix the BCLR setting condition for non-DCP pipe commit 6124607acc88fffeaadf3aacfeb3cc1304c87387 upstream. This patch fixes an issue that the driver sets the BCLR bit of {C,Dn}FIFOCTR register to 1 even when it's non-DCP pipe and the FRDY bit of {C,Dn}FIFOCTR register is set to 1. Fixes: e8d548d54968 ("usb: renesas_usbhs: fifo became independent from pipe.") Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/fifo.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 96bc405a5821..e391f9a7c964 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -287,11 +287,17 @@ static void usbhsf_fifo_clear(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + int ret = 0; if (!usbhs_pipe_is_dcp(pipe)) - usbhsf_fifo_barrier(priv, fifo); + ret = usbhsf_fifo_barrier(priv, fifo); - usbhs_write(priv, fifo->ctr, BCLR); + /* + * if non-DCP pipe, this driver should set BCLR when + * usbhsf_fifo_barrier() returns 0. + */ + if (!ret) + usbhs_write(priv, fifo->ctr, BCLR); } static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv, -- GitLab From be8077a03a279b4f3ab9dda3d64959d626e3044f Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 27 Sep 2017 18:47:13 +0900 Subject: [PATCH 1178/5498] usb: renesas_usbhs: fix usbhsf_fifo_clear() for RX direction commit 0a2ce62b61f2c76d0213edf4e37aaf54a8ddf295 upstream. This patch fixes an issue that the usbhsf_fifo_clear() is possible to cause 10 msec delay if the pipe is RX direction and empty because the FRDY bit will never be set to 1 in such case. Fixes: e8d548d54968 ("usb: renesas_usbhs: fifo became independent from pipe.") Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/fifo.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index e391f9a7c964..75d9ba91e7c5 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -289,8 +289,17 @@ static void usbhsf_fifo_clear(struct usbhs_pipe *pipe, struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); int ret = 0; - if (!usbhs_pipe_is_dcp(pipe)) - ret = usbhsf_fifo_barrier(priv, fifo); + if (!usbhs_pipe_is_dcp(pipe)) { + /* + * This driver checks the pipe condition first to avoid -EBUSY + * from usbhsf_fifo_barrier() with about 10 msec delay in + * the interrupt handler if the pipe is RX direction and empty. + */ + if (usbhs_pipe_is_dir_in(pipe)) + ret = usbhs_pipe_is_accessible(pipe); + if (!ret) + ret = usbhsf_fifo_barrier(priv, fifo); + } /* * if non-DCP pipe, this driver should set BCLR when -- GitLab From ee44ff5445ae449570427baa534bfba2abba7545 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 22 Sep 2017 16:18:53 +0200 Subject: [PATCH 1179/5498] ALSA: usb-audio: Check out-of-bounds access by corrupted buffer descriptor commit bfc81a8bc18e3c4ba0cbaa7666ff76be2f998991 upstream. When a USB-audio device receives a maliciously adjusted or corrupted buffer descriptor, the USB-audio driver may access an out-of-bounce value at its parser. This was detected by syzkaller, something like: BUG: KASAN: slab-out-of-bounds in usb_audio_probe+0x27b2/0x2ab0 Read of size 1 at addr ffff88006b83a9e8 by task kworker/0:1/24 CPU: 0 PID: 24 Comm: kworker/0:1 Not tainted 4.14.0-rc1-42251-gebb2c2437d80 #224 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Workqueue: usb_hub_wq hub_event Call Trace: __dump_stack lib/dump_stack.c:16 dump_stack+0x292/0x395 lib/dump_stack.c:52 print_address_description+0x78/0x280 mm/kasan/report.c:252 kasan_report_error mm/kasan/report.c:351 kasan_report+0x22f/0x340 mm/kasan/report.c:409 __asan_report_load1_noabort+0x19/0x20 mm/kasan/report.c:427 snd_usb_create_streams sound/usb/card.c:248 usb_audio_probe+0x27b2/0x2ab0 sound/usb/card.c:605 usb_probe_interface+0x35d/0x8e0 drivers/usb/core/driver.c:361 really_probe drivers/base/dd.c:413 driver_probe_device+0x610/0xa00 drivers/base/dd.c:557 __device_attach_driver+0x230/0x290 drivers/base/dd.c:653 bus_for_each_drv+0x161/0x210 drivers/base/bus.c:463 __device_attach+0x26e/0x3d0 drivers/base/dd.c:710 device_initial_probe+0x1f/0x30 drivers/base/dd.c:757 bus_probe_device+0x1eb/0x290 drivers/base/bus.c:523 device_add+0xd0b/0x1660 drivers/base/core.c:1835 usb_set_configuration+0x104e/0x1870 drivers/usb/core/message.c:1932 generic_probe+0x73/0xe0 drivers/usb/core/generic.c:174 usb_probe_device+0xaf/0xe0 drivers/usb/core/driver.c:266 really_probe drivers/base/dd.c:413 driver_probe_device+0x610/0xa00 drivers/base/dd.c:557 __device_attach_driver+0x230/0x290 drivers/base/dd.c:653 bus_for_each_drv+0x161/0x210 drivers/base/bus.c:463 __device_attach+0x26e/0x3d0 drivers/base/dd.c:710 device_initial_probe+0x1f/0x30 drivers/base/dd.c:757 bus_probe_device+0x1eb/0x290 drivers/base/bus.c:523 device_add+0xd0b/0x1660 drivers/base/core.c:1835 usb_new_device+0x7b8/0x1020 drivers/usb/core/hub.c:2457 hub_port_connect drivers/usb/core/hub.c:4903 hub_port_connect_change drivers/usb/core/hub.c:5009 port_event drivers/usb/core/hub.c:5115 hub_event+0x194d/0x3740 drivers/usb/core/hub.c:5195 process_one_work+0xc7f/0x1db0 kernel/workqueue.c:2119 worker_thread+0x221/0x1850 kernel/workqueue.c:2253 kthread+0x3a1/0x470 kernel/kthread.c:231 ret_from_fork+0x2a/0x40 arch/x86/entry/entry_64.S:431 This patch adds the checks of out-of-bounce accesses at appropriate places and bails out when it goes out of the given buffer. Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/card.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/usb/card.c b/sound/usb/card.c index f61ebb17cc64..f7dbdc10bf77 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -220,6 +220,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) struct usb_interface_descriptor *altsd; void *control_header; int i, protocol; + int rest_bytes; /* find audiocontrol interface */ host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; @@ -234,6 +235,15 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return -EINVAL; } + rest_bytes = (void *)(host_iface->extra + host_iface->extralen) - + control_header; + + /* just to be sure -- this shouldn't hit at all */ + if (rest_bytes <= 0) { + dev_err(&dev->dev, "invalid control header\n"); + return -EINVAL; + } + switch (protocol) { default: dev_warn(&dev->dev, @@ -244,11 +254,21 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) case UAC_VERSION_1: { struct uac1_ac_header_descriptor *h1 = control_header; + if (rest_bytes < sizeof(*h1)) { + dev_err(&dev->dev, "too short v1 buffer descriptor\n"); + return -EINVAL; + } + if (!h1->bInCollection) { dev_info(&dev->dev, "skipping empty audio interface (v1)\n"); return -EINVAL; } + if (rest_bytes < h1->bLength) { + dev_err(&dev->dev, "invalid buffer length (v1)\n"); + return -EINVAL; + } + if (h1->bLength < sizeof(*h1) + h1->bInCollection) { dev_err(&dev->dev, "invalid UAC_HEADER (v1)\n"); return -EINVAL; -- GitLab From 4b01fcab0516e40d2fb53eeeb513bf8e2ba01982 Mon Sep 17 00:00:00 2001 From: Jim Dickerson Date: Mon, 18 Sep 2017 17:39:14 +0300 Subject: [PATCH 1180/5498] usb: pci-quirks.c: Corrected timeout values used in handshake commit 114ec3a6f9096d211a4aff4277793ba969a62c73 upstream. Servers were emitting failed handoff messages but were not waiting the full 1 second as designated in section 4.22.1 of the eXtensible Host Controller Interface specifications. The handshake was using wrong units so calls were made with milliseconds not microseconds. Comments referenced 5 seconds not 1 second as in specs. The wrong units were also corrected in a second handshake call. Signed-off-by: Jim Dickerson Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 651636e947a3..6a767e2842fb 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -970,7 +970,7 @@ EXPORT_SYMBOL_GPL(usb_disable_xhci_ports); * * Takes care of the handoff between the Pre-OS (i.e. BIOS) and the OS. * It signals to the BIOS that the OS wants control of the host controller, - * and then waits 5 seconds for the BIOS to hand over control. + * and then waits 1 second for the BIOS to hand over control. * If we timeout, assume the BIOS is broken and take control anyway. */ static void quirk_usb_handoff_xhci(struct pci_dev *pdev) @@ -1016,9 +1016,9 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev) if (val & XHCI_HC_BIOS_OWNED) { writel(val | XHCI_HC_OS_OWNED, base + ext_cap_offset); - /* Wait for 5 seconds with 10 microsecond polling interval */ + /* Wait for 1 second with 10 microsecond polling interval */ timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED, - 0, 5000, 10); + 0, 1000000, 10); /* Assume a buggy BIOS and take HC ownership anyway */ if (timeout) { @@ -1046,7 +1046,7 @@ hc_init: * operational or runtime registers. Wait 5 seconds and no more. */ timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_CNR, 0, - 5000, 10); + 5000000, 10); /* Assume a buggy HC and start HC initialization anyway */ if (timeout) { val = readl(op_reg_base + XHCI_STS_OFFSET); -- GitLab From f47d4026e5d73186e7b218c2a3e23aa09ab2fa06 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 26 Sep 2017 15:15:22 -0400 Subject: [PATCH 1181/5498] USB: dummy-hcd: fix connection failures (wrong speed) commit fe659bcc9b173bcfdd958ce2aec75e47651e74e1 upstream. The dummy-hcd UDC driver is not careful about the way it handles connection speeds. It ignores the module parameter that is supposed to govern the maximum connection speed and it doesn't set the HCD flags properly for the case where it ends up running at full speed. The result is that in many cases, gadget enumeration over dummy-hcd fails because the bMaxPacketSize byte in the device descriptor is set incorrectly. For example, the default settings call for a high-speed connection, but the maxpacket value for ep0 ends up being set for a Super-Speed connection. This patch fixes the problem by initializing the gadget's max_speed and the HCD flags correctly. Signed-off-by: Alan Stern Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index a2bf16b6b930..e86d67626c4c 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -976,7 +976,12 @@ static int dummy_udc_probe(struct platform_device *pdev) memzero_explicit(&dum->gadget, sizeof(struct usb_gadget)); dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; - dum->gadget.max_speed = USB_SPEED_SUPER; + if (mod_data.is_super_speed) + dum->gadget.max_speed = USB_SPEED_SUPER; + else if (mod_data.is_high_speed) + dum->gadget.max_speed = USB_SPEED_HIGH; + else + dum->gadget.max_speed = USB_SPEED_FULL; dum->gadget.dev.parent = &pdev->dev; init_dummy_udc_hw(dum); @@ -2492,8 +2497,6 @@ static struct hc_driver dummy_hcd = { .product_desc = "Dummy host controller", .hcd_priv_size = sizeof(struct dummy_hcd), - .flags = HCD_USB3 | HCD_SHARED, - .reset = dummy_setup, .start = dummy_start, .stop = dummy_stop, @@ -2522,8 +2525,12 @@ static int dummy_hcd_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); dum = *((void **)dev_get_platdata(&pdev->dev)); - if (!mod_data.is_super_speed) + if (mod_data.is_super_speed) + dummy_hcd.flags = HCD_USB3 | HCD_SHARED; + else if (mod_data.is_high_speed) dummy_hcd.flags = HCD_USB2; + else + dummy_hcd.flags = HCD_USB11; hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); if (!hs_hcd) return -ENOMEM; -- GitLab From 36283d676804ee88f343207b573950da7c857b1a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 26 Sep 2017 15:15:40 -0400 Subject: [PATCH 1182/5498] USB: dummy-hcd: fix infinite-loop resubmission bug commit 0173a68bfb0ad1c72a6ee39cc485aa2c97540b98 upstream. The dummy-hcd HCD/UDC emulator tries not to do too much work during each timer interrupt. But it doesn't try very hard; currently all it does is limit the total amount of bulk data transferred. Other transfer types aren't limited, and URBs that transfer no data (because of an error, perhaps) don't count toward the limit, even though on a real USB bus they would consume at least a minimum overhead. This means it's possible to get the driver stuck in an infinite loop, for example, if the host class driver resubmits an URB every time it completes (which is common for interrupt URBs). Each time the URB is resubmitted it gets added to the end of the pending-URBs list, and dummy-hcd doesn't stop until that list is empty. Andrey Konovalov was able to trigger this failure mode using the syzkaller fuzzer. This patch fixes the infinite-loop problem by restricting the URBs handled during each timer interrupt to those that were already on the pending list when the interrupt routine started. Newly added URBs won't be processed until the next timer interrupt. The problem of properly accounting for non-bulk bandwidth (as well as packet and transaction overhead) is not addressed here. Signed-off-by: Alan Stern Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index e86d67626c4c..783715a9e399 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -173,6 +173,8 @@ struct dummy_hcd { struct usb_device *udev; struct list_head urbp_list; + struct urbp *next_frame_urbp; + u32 stream_en_ep; u8 num_stream[30 / 2]; @@ -1191,6 +1193,8 @@ static int dummy_urb_enqueue( list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list); urb->hcpriv = urbp; + if (!dum_hcd->next_frame_urbp) + dum_hcd->next_frame_urbp = urbp; if (usb_pipetype(urb->pipe) == PIPE_CONTROL) urb->error_count = 1; /* mark as a new urb */ @@ -1694,6 +1698,7 @@ static void dummy_timer(unsigned long _dum_hcd) spin_unlock_irqrestore(&dum->lock, flags); return; } + dum_hcd->next_frame_urbp = NULL; for (i = 0; i < DUMMY_ENDPOINTS; i++) { if (!ep_name[i]) @@ -1710,6 +1715,10 @@ restart: int type; int status = -EINPROGRESS; + /* stop when we reach URBs queued after the timer interrupt */ + if (urbp == dum_hcd->next_frame_urbp) + break; + urb = urbp->urb; if (urb->unlinked) goto return_urb; -- GitLab From d55567406fbd0c484bd7361d5d35feb037960592 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 22 Sep 2017 23:43:46 +0300 Subject: [PATCH 1183/5498] USB: devio: Don't corrupt user memory commit fa1ed74eb1c233be6131ec92df21ab46499a15b6 upstream. The user buffer has "uurb->buffer_length" bytes. If the kernel has more information than that, we should truncate it instead of writing past the end of the user's buffer. I added a WARN_ONCE() to help the user debug the issue. Reported-by: Alan Stern Signed-off-by: Dan Carpenter Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index d7edec160a6d..839a5022efd9 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1413,7 +1413,11 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb totlen += isopkt[u].length; } u *= sizeof(struct usb_iso_packet_descriptor); - uurb->buffer_length = totlen; + if (totlen <= uurb->buffer_length) + uurb->buffer_length = totlen; + else + WARN_ONCE(1, "uurb->buffer_length is too short %d vs %d", + totlen, uurb->buffer_length); break; default: -- GitLab From 8f0a7703ff9e3a5553134c3d9a20573624802541 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 22 Sep 2017 11:56:49 -0400 Subject: [PATCH 1184/5498] USB: uas: fix bug in handling of alternate settings commit 786de92b3cb26012d3d0f00ee37adf14527f35c4 upstream. The uas driver has a subtle bug in the way it handles alternate settings. The uas_find_uas_alt_setting() routine returns an altsetting value (the bAlternateSetting number in the descriptor), but uas_use_uas_driver() then treats that value as an index to the intf->altsetting array, which it isn't. Normally this doesn't cause any problems because the various alternate settings have bAlternateSetting values 0, 1, 2, ..., so the value is equal to the index in the array. But this is not guaranteed, and Andrey Konovalov used the syzkaller fuzzer with KASAN to get a slab-out-of-bounds error by violating this assumption. This patch fixes the bug by making uas_find_uas_alt_setting() return a pointer to the altsetting entry rather than either the value or the index. Pointers are less subject to misinterpretation. Signed-off-by: Alan Stern Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov CC: Oliver Neukum Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas-detect.h | 15 ++++++++------- drivers/usb/storage/uas.c | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index a451903a6857..a0b92096f6f1 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -9,7 +9,8 @@ static int uas_is_interface(struct usb_host_interface *intf) intf->desc.bInterfaceProtocol == USB_PR_UAS); } -static int uas_find_uas_alt_setting(struct usb_interface *intf) +static struct usb_host_interface *uas_find_uas_alt_setting( + struct usb_interface *intf) { int i; @@ -17,10 +18,10 @@ static int uas_find_uas_alt_setting(struct usb_interface *intf) struct usb_host_interface *alt = &intf->altsetting[i]; if (uas_is_interface(alt)) - return alt->desc.bAlternateSetting; + return alt; } - return -ENODEV; + return NULL; } static int uas_find_endpoints(struct usb_host_interface *alt, @@ -58,14 +59,14 @@ static int uas_use_uas_driver(struct usb_interface *intf, struct usb_device *udev = interface_to_usbdev(intf); struct usb_hcd *hcd = bus_to_hcd(udev->bus); unsigned long flags = id->driver_info; - int r, alt; - + struct usb_host_interface *alt; + int r; alt = uas_find_uas_alt_setting(intf); - if (alt < 0) + if (!alt) return 0; - r = uas_find_endpoints(&intf->altsetting[alt], eps); + r = uas_find_endpoints(alt, eps); if (r < 0) return 0; diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e390d1d11baf..017278ec4e03 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -866,14 +866,14 @@ MODULE_DEVICE_TABLE(usb, uas_usb_ids); static int uas_switch_interface(struct usb_device *udev, struct usb_interface *intf) { - int alt; + struct usb_host_interface *alt; alt = uas_find_uas_alt_setting(intf); - if (alt < 0) - return alt; + if (!alt) + return -ENODEV; - return usb_set_interface(udev, - intf->altsetting[0].desc.bInterfaceNumber, alt); + return usb_set_interface(udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); } static int uas_configure_endpoints(struct uas_dev_info *devinfo) -- GitLab From 8c7124a5295a10043daf2154bc01a437ece9d0f9 Mon Sep 17 00:00:00 2001 From: Dmitry Fleytman Date: Tue, 5 Sep 2017 11:40:56 +0300 Subject: [PATCH 1185/5498] usb: Increase quirk delay for USB devices commit b2a542bbb3081dbd64acc8929c140d196664c406 upstream. Commit e0429362ab15 ("usb: Add device quirk for Logitech HD Pro Webcams C920 and C930e") introduced quirk to workaround an issue with some Logitech webcams. The workaround is introducing delay for some USB operations. According to our testing, delay introduced by original commit is not long enough and in rare cases we still see issues described by the aforementioned commit. This patch increases delays introduced by original commit. Having this patch applied we do not see those problems anymore. Signed-off-by: Dmitry Fleytman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 2 +- drivers/usb/core/hub.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 65e2cec1ca2a..69dfaea4dede 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -747,7 +747,7 @@ int usb_get_configuration(struct usb_device *dev) } if (dev->quirks & USB_QUIRK_DELAY_INIT) - msleep(100); + msleep(200); result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 32d904ea494c..5ff2e776bc5c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4719,7 +4719,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, goto loop; if (udev->quirks & USB_QUIRK_DELAY_INIT) - msleep(1000); + msleep(2000); /* consecutive bus-powered hubs aren't reliable; they can * violate the voltage drop budget. if the new child has -- GitLab From 0502bf54bd01e8dbf4a057fe76f974074b5fb7e6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 19 Sep 2017 15:07:17 +0200 Subject: [PATCH 1186/5498] USB: fix out-of-bounds in usb_set_configuration commit bd7a3fe770ebd8391d1c7d072ff88e9e76d063eb upstream. Andrey Konovalov reported a possible out-of-bounds problem for a USB interface association descriptor. He writes: It seems there's no proper size check of a USB_DT_INTERFACE_ASSOCIATION descriptor. It's only checked that the size is >= 2 in usb_parse_configuration(), so find_iad() might do out-of-bounds access to intf_assoc->bInterfaceCount. And he's right, we don't check for crazy descriptors of this type very well, so resolve this problem. Yet another issue found by syzkaller... Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 14 +++++++++++--- include/uapi/linux/usb/ch9.h | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 69dfaea4dede..49e66fb1ce87 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -538,15 +538,23 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, } else if (header->bDescriptorType == USB_DT_INTERFACE_ASSOCIATION) { + struct usb_interface_assoc_descriptor *d; + + d = (struct usb_interface_assoc_descriptor *)header; + if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) { + dev_warn(ddev, + "config %d has an invalid interface association descriptor of length %d, skipping\n", + cfgno, d->bLength); + continue; + } + if (iad_num == USB_MAXIADS) { dev_warn(ddev, "found more Interface " "Association Descriptors " "than allocated for in " "configuration %d\n", cfgno); } else { - config->intf_assoc[iad_num] = - (struct usb_interface_assoc_descriptor - *)header; + config->intf_assoc[iad_num] = d; iad_num++; } diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index aa33fd1b2d4f..400196c45b3c 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -705,6 +705,7 @@ struct usb_interface_assoc_descriptor { __u8 iFunction; } __attribute__ ((packed)); +#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8 /*-------------------------------------------------------------------------*/ -- GitLab From 9a7370be51a44373d4279a2566a9c988fe07aaf5 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Mon, 18 Sep 2017 17:39:13 +0300 Subject: [PATCH 1187/5498] xhci: fix finding correct bus_state structure for USB 3.1 hosts commit 5a838a13c9b4e5dd188b7a6eaeb894e9358ead0c upstream. xhci driver keeps a bus_state structure for each hcd (usb2 and usb3) The structure is picked based on hcd speed, but driver only compared for HCD_USB3 speed, returning the wrong bus_state for HCD_USB31 hosts. This caused null pointer dereference errors in bus_resume function. Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3850cb2af7a9..2ea272a3503c 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1434,7 +1434,7 @@ struct xhci_bus_state { static inline unsigned int hcd_index(struct usb_hcd *hcd) { - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) return 0; else return 1; -- GitLab From 440edb76708ff2eefe035c9f3f9457b3b8a93dd6 Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Tue, 5 Sep 2017 15:14:45 +0300 Subject: [PATCH 1188/5498] iio: ad_sigma_delta: Implement a dedicated reset function commit 7fc10de8d49a748c476532c9d8e8fe19e548dd67 upstream. Since most of the SD ADCs have the option of reseting the serial interface by sending a number of SCLKs with CS = 0 and DIN = 1, a dedicated function that can do this is usefull. Needed for the patch: iio: ad7793: Fix the serial interface reset Signed-off-by: Dragos Bogdan Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad_sigma_delta.c | 28 ++++++++++++++++++++++++++ include/linux/iio/adc/ad_sigma_delta.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index d10bd0c97233..22c4c17cd996 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -177,6 +177,34 @@ out: } EXPORT_SYMBOL_GPL(ad_sd_read_reg); +/** + * ad_sd_reset() - Reset the serial interface + * + * @sigma_delta: The sigma delta device + * @reset_length: Number of SCLKs with DIN = 1 + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_reset(struct ad_sigma_delta *sigma_delta, + unsigned int reset_length) +{ + uint8_t *buf; + unsigned int size; + int ret; + + size = DIV_ROUND_UP(reset_length, 8); + buf = kcalloc(size, sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memset(buf, 0xff, size); + ret = spi_write(sigma_delta->spi, buf, size); + kfree(buf); + + return ret; +} +EXPORT_SYMBOL_GPL(ad_sd_reset); + static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index e7fdec4db9da..6cc48ac55fd2 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -111,6 +111,9 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, unsigned int *val); +int ad_sd_reset(struct ad_sigma_delta *sigma_delta, + unsigned int reset_length); + int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val); int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, -- GitLab From 923f18a8aacdf4b23a452360346755b7cf21c252 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Thu, 14 Sep 2017 16:50:28 +0300 Subject: [PATCH 1189/5498] staging: iio: ad7192: Fix - use the dedicated reset function avoiding dma from stack. commit f790923f146140a261ad211e5baf75d169f16fb2 upstream. Depends on: 691c4b95d1 ("iio: ad_sigma_delta: Implement a dedicated reset function") SPI host drivers can use DMA to transfer data, so the buffer should be properly allocated. Keeping it on the stack could cause an undefined behavior. The dedicated reset function solves this issue. Signed-off-by: Stefan Popa Acked-by: Lars-Peter Clausen Acked-by: Michael Hennerich Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/adc/ad7192.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index 8343a780f63d..dbf30524fa73 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -206,11 +206,9 @@ static int ad7192_setup(struct ad7192_state *st, struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi); unsigned long long scale_uv; int i, ret, id; - u8 ones[6]; /* reset the serial interface */ - memset(&ones, 0xFF, 6); - ret = spi_write(st->sd.spi, &ones, 6); + ret = ad_sd_reset(&st->sd, 48); if (ret < 0) goto out; usleep_range(500, 1000); /* Wait for at least 500us */ -- GitLab From e1087d24eff5f5cc3f82f55bec8848e6d910cf9c Mon Sep 17 00:00:00 2001 From: Matt Fornero Date: Tue, 5 Sep 2017 16:34:10 +0200 Subject: [PATCH 1190/5498] iio: core: Return error for failed read_reg commit 3d62c78a6eb9a7d67bace9622b66ad51e81c5f9b upstream. If an IIO device returns an error code for a read access via debugfs, it is currently ignored by the IIO core (other than emitting an error message). Instead, return this error code to user space, so upper layers can detect it correctly. Signed-off-by: Matt Fornero Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index f009d053384a..1ccac43e1795 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -188,8 +188,10 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, ret = indio_dev->info->debugfs_reg_access(indio_dev, indio_dev->cached_reg_addr, 0, &val); - if (ret) + if (ret) { dev_err(indio_dev->dev.parent, "%s: read failed\n", __func__); + return ret; + } len = snprintf(buf, sizeof(buf), "0x%X\n", val); -- GitLab From 85a14a6de4f7256a81c6c2b06df61216e2fcf9a9 Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Tue, 5 Sep 2017 15:16:13 +0300 Subject: [PATCH 1191/5498] iio: ad7793: Fix the serial interface reset commit 7ee3b7ebcb74714df6d94c8f500f307e1ee5dda5 upstream. The serial interface can be reset by writing 32 consecutive 1s to the device. 'ret' was initialized correctly but its value was overwritten when ad7793_check_platform_data() was called. Since a dedicated reset function is present now, it should be used instead. Fixes: 2edb769d246e ("iio:ad7793: Add support for the ad7798 and ad7799") Signed-off-by: Dragos Bogdan Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad7793.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 4dddeabdfbb0..555cb74fbd49 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -257,7 +257,7 @@ static int ad7793_setup(struct iio_dev *indio_dev, unsigned int vref_mv) { struct ad7793_state *st = iio_priv(indio_dev); - int i, ret = -1; + int i, ret; unsigned long long scale_uv; u32 id; @@ -266,7 +266,7 @@ static int ad7793_setup(struct iio_dev *indio_dev, return ret; /* reset the serial interface */ - ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret)); + ret = ad_sd_reset(&st->sd, 32); if (ret < 0) goto out; usleep_range(500, 2000); /* Wait for at least 500us */ -- GitLab From 6fd03e48dc8ad207a6f2f1c36c8b773599ec0280 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 22 Aug 2017 15:33:00 +0200 Subject: [PATCH 1192/5498] iio: adc: mcp320x: Fix oops on module unload commit 0964e40947a630a2a6f724e968246992f97bcf1c upstream. The driver calls spi_get_drvdata() in its ->remove hook even though it has never called spi_set_drvdata(). Stack trace for posterity: Unable to handle kernel NULL pointer dereference at virtual address 00000220 Internal error: Oops: 5 [#1] SMP ARM [<8072f564>] (mutex_lock) from [<7f1400d0>] (iio_device_unregister+0x24/0x7c [industrialio]) [<7f1400d0>] (iio_device_unregister [industrialio]) from [<7f15e020>] (mcp320x_remove+0x20/0x30 [mcp320x]) [<7f15e020>] (mcp320x_remove [mcp320x]) from [<8055a8cc>] (spi_drv_remove+0x2c/0x44) [<8055a8cc>] (spi_drv_remove) from [<805087bc>] (__device_release_driver+0x98/0x134) [<805087bc>] (__device_release_driver) from [<80509180>] (driver_detach+0xdc/0xe0) [<80509180>] (driver_detach) from [<8050823c>] (bus_remove_driver+0x5c/0xb0) [<8050823c>] (bus_remove_driver) from [<80509ab0>] (driver_unregister+0x38/0x58) [<80509ab0>] (driver_unregister) from [<7f15e69c>] (mcp320x_driver_exit+0x14/0x1c [mcp320x]) [<7f15e69c>] (mcp320x_driver_exit [mcp320x]) from [<801a78d0>] (SyS_delete_module+0x184/0x1d0) [<801a78d0>] (SyS_delete_module) from [<80108100>] (ret_fast_syscall+0x0/0x1c) Fixes: f5ce4a7a9291 ("iio: adc: add driver for MCP3204/08 12-bit ADC") Cc: Oskar Andero Signed-off-by: Lukas Wunner Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/mcp320x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index 28a086e48776..662c930fb1c3 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -180,6 +180,7 @@ static int mcp320x_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mcp320x_info; + spi_set_drvdata(spi, indio_dev); chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data]; indio_dev->channels = chip_info->channels; -- GitLab From 4a8d502989094b0825bb11240bc12e4d9cc5c65b Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 14 Sep 2017 14:30:55 +0200 Subject: [PATCH 1193/5498] uwb: properly check kthread_run return value commit bbf26183b7a6236ba602f4d6a2f7cade35bba043 upstream. uwbd_start() calls kthread_run() and checks that the return value is not NULL. But the return value is not NULL in case kthread_run() fails, it takes the form of ERR_PTR(-EINTR). Use IS_ERR() instead. Also add a check to uwbd_stop(). Signed-off-by: Andrey Konovalov Signed-off-by: Greg Kroah-Hartman --- drivers/uwb/uwbd.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c index bdcb13cc1d54..5c9828370217 100644 --- a/drivers/uwb/uwbd.c +++ b/drivers/uwb/uwbd.c @@ -303,18 +303,22 @@ static int uwbd(void *param) /** Start the UWB daemon */ void uwbd_start(struct uwb_rc *rc) { - rc->uwbd.task = kthread_run(uwbd, rc, "uwbd"); - if (rc->uwbd.task == NULL) + struct task_struct *task = kthread_run(uwbd, rc, "uwbd"); + if (IS_ERR(task)) { + rc->uwbd.task = NULL; printk(KERN_ERR "UWB: Cannot start management daemon; " "UWB won't work\n"); - else + } else { + rc->uwbd.task = task; rc->uwbd.pid = rc->uwbd.task->pid; + } } /* Stop the UWB daemon and free any unprocessed events */ void uwbd_stop(struct uwb_rc *rc) { - kthread_stop(rc->uwbd.task); + if (rc->uwbd.task) + kthread_stop(rc->uwbd.task); uwbd_flush(rc); } -- GitLab From fce8db2c2cbd58fb9b37f1ff476442ee07c7c803 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 14 Sep 2017 16:52:59 +0200 Subject: [PATCH 1194/5498] uwb: ensure that endpoint is interrupt commit 70e743e4cec3733dc13559f6184b35d358b9ef3f upstream. hwarc_neep_init() assumes that endpoint 0 is interrupt, but there's no check for that, which results in a WARNING in USB core code, when a bad USB descriptor is provided from a device: usb 1-1: BOGUS urb xfer, pipe 1 != type 3 ------------[ cut here ]------------ WARNING: CPU: 0 PID: 3 at drivers/usb/core/urb.c:449 usb_submit_urb+0xf8a/0x11d0 Modules linked in: CPU: 0 PID: 3 Comm: kworker/0:0 Not tainted 4.13.0+ #111 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Workqueue: usb_hub_wq hub_event task: ffff88006bdc1a00 task.stack: ffff88006bde8000 RIP: 0010:usb_submit_urb+0xf8a/0x11d0 drivers/usb/core/urb.c:448 RSP: 0018:ffff88006bdee3c0 EFLAGS: 00010282 RAX: 0000000000000029 RBX: ffff8800672a7200 RCX: 0000000000000000 RDX: 0000000000000029 RSI: ffff88006c815c78 RDI: ffffed000d7bdc6a RBP: ffff88006bdee4c0 R08: fffffbfff0fe00ff R09: fffffbfff0fe00ff R10: 0000000000000018 R11: fffffbfff0fe00fe R12: 1ffff1000d7bdc7f R13: 0000000000000003 R14: 0000000000000001 R15: ffff88006b02cc90 FS: 0000000000000000(0000) GS:ffff88006c800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fe4daddf000 CR3: 000000006add6000 CR4: 00000000000006f0 Call Trace: hwarc_neep_init+0x4ce/0x9c0 drivers/uwb/hwa-rc.c:710 uwb_rc_add+0x2fb/0x730 drivers/uwb/lc-rc.c:361 hwarc_probe+0x34e/0x9b0 drivers/uwb/hwa-rc.c:858 usb_probe_interface+0x351/0x8d0 drivers/usb/core/driver.c:361 really_probe drivers/base/dd.c:385 driver_probe_device+0x610/0xa00 drivers/base/dd.c:529 __device_attach_driver+0x230/0x290 drivers/base/dd.c:625 bus_for_each_drv+0x15e/0x210 drivers/base/bus.c:463 __device_attach+0x269/0x3c0 drivers/base/dd.c:682 device_initial_probe+0x1f/0x30 drivers/base/dd.c:729 bus_probe_device+0x1da/0x280 drivers/base/bus.c:523 device_add+0xcf9/0x1640 drivers/base/core.c:1703 usb_set_configuration+0x1064/0x1890 drivers/usb/core/message.c:1932 generic_probe+0x73/0xe0 drivers/usb/core/generic.c:174 usb_probe_device+0xaf/0xe0 drivers/usb/core/driver.c:266 really_probe drivers/base/dd.c:385 driver_probe_device+0x610/0xa00 drivers/base/dd.c:529 __device_attach_driver+0x230/0x290 drivers/base/dd.c:625 bus_for_each_drv+0x15e/0x210 drivers/base/bus.c:463 __device_attach+0x269/0x3c0 drivers/base/dd.c:682 device_initial_probe+0x1f/0x30 drivers/base/dd.c:729 bus_probe_device+0x1da/0x280 drivers/base/bus.c:523 device_add+0xcf9/0x1640 drivers/base/core.c:1703 usb_new_device+0x7b8/0x1020 drivers/usb/core/hub.c:2457 hub_port_connect drivers/usb/core/hub.c:4890 hub_port_connect_change drivers/usb/core/hub.c:4996 port_event drivers/usb/core/hub.c:5102 hub_event+0x23c8/0x37c0 drivers/usb/core/hub.c:5182 process_one_work+0x9fb/0x1570 kernel/workqueue.c:2097 worker_thread+0x1e4/0x1350 kernel/workqueue.c:2231 kthread+0x324/0x3f0 kernel/kthread.c:231 ret_from_fork+0x25/0x30 arch/x86/entry/entry_64.S:425 Code: 48 8b 85 30 ff ff ff 48 8d b8 98 00 00 00 e8 8e 93 07 ff 45 89 e8 44 89 f1 4c 89 fa 48 89 c6 48 c7 c7 a0 e5 55 86 e8 20 08 8f fd <0f> ff e9 9b f7 ff ff e8 4a 04 d6 fd e9 80 f7 ff ff e8 60 11 a6 ---[ end trace 55d741234124cfc3 ]--- Check that endpoint is interrupt. Found by syzkaller. Signed-off-by: Andrey Konovalov Signed-off-by: Greg Kroah-Hartman --- drivers/uwb/hwa-rc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c index e75bbe5a10cd..1212b4b3c5a9 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/uwb/hwa-rc.c @@ -827,6 +827,8 @@ static int hwarc_probe(struct usb_interface *iface, if (iface->cur_altsetting->desc.bNumEndpoints < 1) return -ENODEV; + if (!usb_endpoint_xfer_int(&iface->cur_altsetting->endpoint[0].desc)) + return -ENODEV; result = -ENOMEM; uwb_rc = uwb_rc_alloc(); -- GitLab From c52be88a5d675c67e02008ab4ebd54ce0d01c683 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Tue, 19 Sep 2017 09:39:08 -0700 Subject: [PATCH 1195/5498] lsm: fix smack_inode_removexattr and xattr_getsecurity memleak commit 57e7ba04d422c3d41c8426380303ec9b7533ded9 upstream. security_inode_getsecurity() provides the text string value of a security attribute. It does not provide a "secctx". The code in xattr_getsecurity() that calls security_inode_getsecurity() and then calls security_release_secctx() happened to work because SElinux and Smack treat the attribute and the secctx the same way. It fails for cap_inode_getsecurity(), because that module has no secctx that ever needs releasing. It turns out that Smack is the one that's doing things wrong by not allocating memory when instructed to do so by the "alloc" parameter. The fix is simple enough. Change the security_release_secctx() to kfree() because it isn't a secctx being returned by security_inode_getsecurity(). Change Smack to allocate the string when told to do so. Note: this also fixes memory leaks for LSMs which implement inode_getsecurity but not release_secctx, such as capabilities. Signed-off-by: Casey Schaufler Reported-by: Konstantin Khlebnikov Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- fs/xattr.c | 2 +- security/smack/smack_lsm.c | 55 +++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/fs/xattr.c b/fs/xattr.c index 2ac964e32daf..d536edbd377e 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -163,7 +163,7 @@ xattr_getsecurity(struct inode *inode, const char *name, void *value, } memcpy(value, buffer, len); out: - security_release_secctx(buffer, len); + kfree(buffer); out_noalloc: return len; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index cddf5d17bbc4..a72b516b319e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1229,7 +1229,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) * @inode: the object * @name: attribute name * @buffer: where to put the result - * @alloc: unused + * @alloc: duplicate memory * * Returns the size of the attribute or an error code */ @@ -1242,43 +1242,38 @@ static int smack_inode_getsecurity(const struct inode *inode, struct super_block *sbp; struct inode *ip = (struct inode *)inode; struct smack_known *isp; - int ilen; - int rc = 0; - if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { + if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) isp = smk_of_inode(inode); - ilen = strlen(isp->smk_known); - *buffer = isp->smk_known; - return ilen; - } + else { + /* + * The rest of the Smack xattrs are only on sockets. + */ + sbp = ip->i_sb; + if (sbp->s_magic != SOCKFS_MAGIC) + return -EOPNOTSUPP; - /* - * The rest of the Smack xattrs are only on sockets. - */ - sbp = ip->i_sb; - if (sbp->s_magic != SOCKFS_MAGIC) - return -EOPNOTSUPP; + sock = SOCKET_I(ip); + if (sock == NULL || sock->sk == NULL) + return -EOPNOTSUPP; - sock = SOCKET_I(ip); - if (sock == NULL || sock->sk == NULL) - return -EOPNOTSUPP; - - ssp = sock->sk->sk_security; + ssp = sock->sk->sk_security; - if (strcmp(name, XATTR_SMACK_IPIN) == 0) - isp = ssp->smk_in; - else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) - isp = ssp->smk_out; - else - return -EOPNOTSUPP; + if (strcmp(name, XATTR_SMACK_IPIN) == 0) + isp = ssp->smk_in; + else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) + isp = ssp->smk_out; + else + return -EOPNOTSUPP; + } - ilen = strlen(isp->smk_known); - if (rc == 0) { - *buffer = isp->smk_known; - rc = ilen; + if (alloc) { + *buffer = kstrdup(isp->smk_known, GFP_KERNEL); + if (*buffer == NULL) + return -ENOMEM; } - return rc; + return strlen(isp->smk_known); } -- GitLab From e29b6128817a06e6b12e5fad724ff23f6d43c62d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Oct 2017 14:06:43 +0200 Subject: [PATCH 1196/5498] ALSA: usx2y: Suppress kernel warning at page allocation failures commit 7682e399485fe19622b6fd82510b1f4551e48a25 upstream. The usx2y driver allocates the stream read/write buffers in continuous pages depending on the stream setup, and this may spew the kernel warning messages with a stack trace like: WARNING: CPU: 1 PID: 1846 at mm/page_alloc.c:3883 __alloc_pages_slowpath+0x1ef2/0x2d70 Modules linked in: CPU: 1 PID: 1846 Comm: kworker/1:2 Not tainted .... It may confuse user as if it were any serious error, although this is no fatal error and the driver handles the error case gracefully. Since the driver has already some sanity check of the given size (128 and 256 pages), it can't pass any crazy value. So it's merely page fragmentation. This patch adds __GFP_NOWARN to each caller for suppressing such kernel warnings. The original issue was spotted by syzkaller. Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/usx2y/usb_stream.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c index bf618e1500ac..e7b934f4d837 100644 --- a/sound/usb/usx2y/usb_stream.c +++ b/sound/usb/usx2y/usb_stream.c @@ -191,7 +191,8 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk, } pg = get_order(read_size); - sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg); + sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO| + __GFP_NOWARN, pg); if (!sk->s) { snd_printk(KERN_WARNING "couldn't __get_free_pages()\n"); goto out; @@ -211,7 +212,8 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk, pg = get_order(write_size); sk->write_page = - (void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg); + (void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO| + __GFP_NOWARN, pg); if (!sk->write_page) { snd_printk(KERN_WARNING "couldn't __get_free_pages()\n"); usb_stream_free(sk); -- GitLab From 389721fabce2457414c4f128bc0b3a7d77c8f833 Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Mon, 11 Sep 2017 09:45:42 +0200 Subject: [PATCH 1197/5498] driver core: platform: Don't read past the end of "driver_override" buffer commit bf563b01c2895a4bfd1a29cc5abc67fe706ecffd upstream. When printing the driver_override parameter when it is 4095 and 4094 bytes long, the printing code would access invalid memory because we need count+1 bytes for printing. Reject driver_override values of these lengths in driver_override_store(). This is in close analogy to commit 4efe874aace5 ("PCI: Don't read past the end of sysfs "driver_override" buffer") from Sasha Levin. Fixes: 3d713e0e382e ("driver core: platform: add device binding path 'driver_override'") Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ef1d816090c3..a33ae377728f 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -729,7 +729,8 @@ static ssize_t driver_override_store(struct device *dev, struct platform_device *pdev = to_platform_device(dev); char *driver_override, *old, *cp; - if (count > PATH_MAX) + /* We need to keep extra room for a newline */ + if (count >= (PAGE_SIZE - 1)) return -EINVAL; driver_override = kstrndup(buf, count, GFP_KERNEL); -- GitLab From 884d5129c355a200103c49089b420873241daa04 Mon Sep 17 00:00:00 2001 From: Adrian Salido Date: Fri, 8 Sep 2017 10:55:27 -0700 Subject: [PATCH 1198/5498] HID: i2c-hid: allocate hid buffers for real worst case commit 8320caeeffdefec3b58b9d4a7ed8e1079492fe7b upstream. The buffer allocation is not currently accounting for an extra byte for the report id. This can cause an out of bounds access in function i2c_hid_set_or_send_report() with reportID > 15. Signed-off-by: Adrian Salido Reviewed-by: Benson Leung Signed-off-by: Guenter Roeck Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/i2c-hid/i2c-hid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index fdcce357f395..462b0a383353 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -526,7 +526,8 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size) { /* the worst case is computed from the set_report command with a * reportID > 15 and the maximum report length */ - int args_len = sizeof(__u8) + /* optional ReportID byte */ + int args_len = sizeof(__u8) + /* ReportID */ + sizeof(__u8) + /* optional ReportID byte */ sizeof(__u16) + /* data register */ sizeof(__u16) + /* size of the report */ report_size; /* report */ -- GitLab From f5a35eb7a803973c45fb2245a2290591a4d33c77 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 21 Sep 2017 17:19:20 +0300 Subject: [PATCH 1199/5498] drm/i915/bios: ignore HDMI on port A MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2ba7d7e0437127314864238f8bfcb8369d81075c upstream. The hardware state readout oopses after several warnings when trying to use HDMI on port A, if such a combination is configured in VBT. Filter the combo out already at the VBT parsing phase. v2: also ignore DVI (Ville) Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=102889 Cc: Imre Deak Reviewed-by: Ville Syrjälä Tested-by: Daniel Drake Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20170921141920.18172-1-jani.nikula@intel.com (cherry picked from commit d27ffc1d00327c29b3aa97f941b42f0949f9e99f) Signed-off-by: Rodrigo Vivi Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_bios.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index d96b152a6e04..2f205dff372e 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -940,6 +940,13 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port, is_hdmi = is_dvi && (child->common.device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0; is_edp = is_dp && (child->common.device_type & DEVICE_TYPE_INTERNAL_CONNECTOR); + if (port == PORT_A && is_dvi) { + DRM_DEBUG_KMS("VBT claims port A supports DVI%s, ignoring\n", + is_hdmi ? "/HDMI" : ""); + is_dvi = false; + is_hdmi = false; + } + info->supports_dvi = is_dvi; info->supports_hdmi = is_hdmi; info->supports_dp = is_dp; -- GitLab From 9d3562bbfd343009c1541727e83fe7c1634b5722 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 22 Jun 2015 00:31:26 -0400 Subject: [PATCH 1200/5498] ext4: only call ext4_truncate when size <= isize commit 3da40c7b089810ac9cf2bb1e59633f619f3a7312 upstream. At LSF we decided that if we truncate up from isize we shouldn't trim fallocated blocks that were fallocated with KEEP_SIZE and are past the new i_size. This patch fixes ext4 to do this. [ Completely reworked patch so that i_disksize would actually get set when truncating up. Also reworked the code for handling truncate so that it's easier to handle. -- tytso ] Signed-off-by: Josef Bacik Signed-off-by: Theodore Ts'o Reviewed-by: Lukas Czerner Signed-off-by: Greg Kroah-Hartman --- fs/ext4/inode.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 39a263ecca18..876cd195995a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4534,8 +4534,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) ext4_journal_stop(handle); } - if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) { + if (attr->ia_valid & ATTR_SIZE) { handle_t *handle; + loff_t oldsize = inode->i_size; + int shrink = (attr->ia_size <= inode->i_size); if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); @@ -4543,24 +4545,26 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_size > sbi->s_bitmap_maxbytes) return -EFBIG; } + if (!S_ISREG(inode->i_mode)) + return -EINVAL; if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size) inode_inc_iversion(inode); - if (S_ISREG(inode->i_mode) && + if (ext4_should_order_data(inode) && (attr->ia_size < inode->i_size)) { - if (ext4_should_order_data(inode)) { - error = ext4_begin_ordered_truncate(inode, + error = ext4_begin_ordered_truncate(inode, attr->ia_size); - if (error) - goto err_out; - } + if (error) + goto err_out; + } + if (attr->ia_size != inode->i_size) { handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto err_out; } - if (ext4_handle_valid(handle)) { + if (ext4_handle_valid(handle) && shrink) { error = ext4_orphan_add(handle, inode); orphan = 1; } @@ -4579,15 +4583,13 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) up_write(&EXT4_I(inode)->i_data_sem); ext4_journal_stop(handle); if (error) { - ext4_orphan_del(NULL, inode); + if (orphan) + ext4_orphan_del(NULL, inode); goto err_out; } - } else { - loff_t oldsize = inode->i_size; - - i_size_write(inode, attr->ia_size); - pagecache_isize_extended(inode, oldsize, inode->i_size); } + if (!shrink) + pagecache_isize_extended(inode, oldsize, inode->i_size); /* * Blocks are going to be removed from the inode. Wait @@ -4606,14 +4608,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) * Truncate pagecache after we've waited for commit * in data=journal mode to make pages freeable. */ - truncate_pagecache(inode, inode->i_size); + truncate_pagecache(inode, inode->i_size); + if (shrink) + ext4_truncate(inode); } - /* - * We want to call ext4_truncate() even if attr->ia_size == - * inode->i_size for cases like truncation of fallocated space - */ - if (attr->ia_valid & ATTR_SIZE) - ext4_truncate(inode); if (!rc) { setattr_copy(inode, attr); -- GitLab From 7798893bcb2f565f03eecc1ded9eb79d57929b9e Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 26 Sep 2016 18:07:48 +0200 Subject: [PATCH 1201/5498] fs/super.c: fix race between freeze_super() and thaw_super() commit 89f39af129382a40d7cd1f6914617282cfeee28e upstream. Change thaw_super() to check frozen != SB_FREEZE_COMPLETE rather than frozen == SB_UNFROZEN, otherwise it can race with freeze_super() which drops sb->s_umount after SB_FREEZE_WRITE to preserve the lock ordering. In this case thaw_super() will wrongly call s_op->unfreeze_fs() before it was actually frozen, and call sb_freeze_unlock() which leads to the unbalanced percpu_up_write(). Unfortunately lockdep can't detect this, so this triggers misc BUG_ON()'s in kernel/rcu/sync.c. Reported-and-tested-by: Nikolay Borisov Signed-off-by: Oleg Nesterov Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/super.c b/fs/super.c index eae088f6aaae..19fafe86071a 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1346,8 +1346,8 @@ int freeze_super(struct super_block *sb) } } /* - * This is just for debugging purposes so that fs can warn if it - * sees write activity when frozen is set to SB_FREEZE_COMPLETE. + * For debugging purposes so that fs can warn if it sees write activity + * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super(). */ sb->s_writers.frozen = SB_FREEZE_COMPLETE; up_write(&sb->s_umount); @@ -1366,7 +1366,7 @@ int thaw_super(struct super_block *sb) int error; down_write(&sb->s_umount); - if (sb->s_writers.frozen == SB_UNFROZEN) { + if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) { up_write(&sb->s_umount); return -EINVAL; } -- GitLab From 5ca606ad34f7c5e8c8366014a5df0eb645c2efaf Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 26 May 2017 17:45:45 -0400 Subject: [PATCH 1202/5498] ext4: fix data corruption for mmap writes commit a056bdaae7a181f7dcc876cfab2f94538e508709 upstream. mpage_submit_page() can race with another process growing i_size and writing data via mmap to the written-back page. As mpage_submit_page() samples i_size too early, it may happen that ext4_bio_write_page() zeroes out too large tail of the page and thus corrupts user data. Fix the problem by sampling i_size only after the page has been write-protected in page tables by clear_page_dirty_for_io() call. Reported-by: Michael Zimmer CC: stable@vger.kernel.org Fixes: cb20d5188366f04d96d2e07b1240cc92170ade40 Signed-off-by: Jan Kara Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/inode.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 876cd195995a..a9117695da6c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1780,15 +1780,29 @@ static int ext4_writepage(struct page *page, static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page) { int len; - loff_t size = i_size_read(mpd->inode); + loff_t size; int err; BUG_ON(page->index != mpd->first_page); - if (page->index == size >> PAGE_CACHE_SHIFT) - len = size & ~PAGE_CACHE_MASK; - else - len = PAGE_CACHE_SIZE; clear_page_dirty_for_io(page); + /* + * We have to be very careful here! Nothing protects writeback path + * against i_size changes and the page can be writeably mapped into + * page tables. So an application can be growing i_size and writing + * data through mmap while writeback runs. clear_page_dirty_for_io() + * write-protects our page in page tables and the page cannot get + * written to again until we release page lock. So only after + * clear_page_dirty_for_io() we are safe to sample i_size for + * ext4_bio_write_page() to zero-out tail of the written page. We rely + * on the barrier provided by TestClearPageDirty in + * clear_page_dirty_for_io() to make sure i_size is really sampled only + * after page tables are updated. + */ + size = i_size_read(mpd->inode); + if (page->index == size >> PAGE_SHIFT) + len = size & ~PAGE_MASK; + else + len = PAGE_SIZE; err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false); if (!err) mpd->wbc->nr_to_write--; -- GitLab From 79012f0216a18d043cbcb54362d6812e8e21a82e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Sun, 30 Jul 2017 23:33:01 -0400 Subject: [PATCH 1203/5498] ext4: Don't clear SGID when inheriting ACLs commit a3bb2d5587521eea6dab2d05326abb0afb460abd upstream. When new directory 'DIR1' is created in a directory 'DIR0' with SGID bit set, DIR1 is expected to have SGID bit set (and owning group equal to the owning group of 'DIR0'). However when 'DIR0' also has some default ACLs that 'DIR1' inherits, setting these ACLs will result in SGID bit on 'DIR1' to get cleared if user is not member of the owning group. Fix the problem by moving posix_acl_update_mode() out of __ext4_set_acl() into ext4_set_acl(). That way the function will not be called when inheriting ACLs which is what we want as it prevents SGID bit clearing and the mode has been properly set by posix_acl_create() anyway. Fixes: 073931017b49d9458aa351605b43a7e34598caef Signed-off-by: Theodore Ts'o Signed-off-by: Jan Kara Reviewed-by: Andreas Gruenbacher Signed-off-by: Greg Kroah-Hartman --- fs/ext4/acl.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index 87d9bbf6a53f..89863e1e7c32 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -200,13 +200,6 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type, switch (type) { case ACL_TYPE_ACCESS: name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; - if (acl) { - error = posix_acl_update_mode(inode, &inode->i_mode, &acl); - if (error) - return error; - inode->i_ctime = ext4_current_time(inode); - ext4_mark_inode_dirty(handle, inode); - } break; case ACL_TYPE_DEFAULT: @@ -239,6 +232,8 @@ ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type) { handle_t *handle; int error, retries = 0; + umode_t mode = inode->i_mode; + int update_mode = 0; retry: handle = ext4_journal_start(inode, EXT4_HT_XATTR, @@ -246,7 +241,20 @@ retry: if (IS_ERR(handle)) return PTR_ERR(handle); + if ((type == ACL_TYPE_ACCESS) && acl) { + error = posix_acl_update_mode(inode, &mode, &acl); + if (error) + goto out_stop; + update_mode = 1; + } + error = __ext4_set_acl(handle, inode, type, acl); + if (!error && update_mode) { + inode->i_mode = mode; + inode->i_ctime = ext4_current_time(inode); + ext4_mark_inode_dirty(handle, inode); + } +out_stop: ext4_journal_stop(handle); if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) goto retry; -- GitLab From 2ce649fdacd53afc430565e18124d7d72be92cb3 Mon Sep 17 00:00:00 2001 From: Eryu Guan Date: Thu, 1 Dec 2016 15:08:37 -0500 Subject: [PATCH 1204/5498] ext4: validate s_first_meta_bg at mount time commit 3a4b77cd47bb837b8557595ec7425f281f2ca1fe upstream. Ralf Spenneberg reported that he hit a kernel crash when mounting a modified ext4 image. And it turns out that kernel crashed when calculating fs overhead (ext4_calculate_overhead()), this is because the image has very large s_first_meta_bg (debug code shows it's 842150400), and ext4 overruns the memory in count_overhead() when setting bitmap buffer, which is PAGE_SIZE. ext4_calculate_overhead(): buf = get_zeroed_page(GFP_NOFS); <=== PAGE_SIZE buffer blks = count_overhead(sb, i, buf); count_overhead(): for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) { <=== j = 842150400 ext4_set_bit(EXT4_B2C(sbi, s++), buf); <=== buffer overrun count++; } This can be reproduced easily for me by this script: #!/bin/bash rm -f fs.img mkdir -p /mnt/ext4 fallocate -l 16M fs.img mke2fs -t ext4 -O bigalloc,meta_bg,^resize_inode -F fs.img debugfs -w -R "ssv first_meta_bg 842150400" fs.img mount -o loop fs.img /mnt/ext4 Fix it by validating s_first_meta_bg first at mount time, and refusing to mount if its value exceeds the largest possible meta_bg number. Reported-by: Ralf Spenneberg Signed-off-by: Eryu Guan Signed-off-by: Theodore Ts'o Reviewed-by: Andreas Dilger Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index a64e4368bb58..b7e0370171f9 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3931,6 +3931,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / EXT4_DESC_PER_BLOCK(sb); + if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG)) { + if (le32_to_cpu(es->s_first_meta_bg) >= db_count) { + ext4_msg(sb, KERN_WARNING, + "first meta block group too large: %u " + "(group descriptor block count %u)", + le32_to_cpu(es->s_first_meta_bg), db_count); + goto failed_mount; + } + } sbi->s_group_desc = ext4_kvmalloc(db_count * sizeof(struct buffer_head *), GFP_KERNEL); -- GitLab From b7ff160a8ba4747edb774b1b2dde5417b7d4cf7a Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 15 Feb 2017 01:26:39 -0500 Subject: [PATCH 1205/5498] ext4: fix fencepost in s_first_meta_bg validation commit 2ba3e6e8afc9b6188b471f27cf2b5e3cf34e7af2 upstream. It is OK for s_first_meta_bg to be equal to the number of block group descriptor blocks. (It rarely happens, but it shouldn't cause any problems.) https://bugzilla.kernel.org/show_bug.cgi?id=194567 Fixes: 3a4b77cd47bb837b8557595ec7425f281f2ca1fe Signed-off-by: Theodore Ts'o Cc: Eryu Guan Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b7e0370171f9..51454eb4e4cb 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3932,7 +3932,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / EXT4_DESC_PER_BLOCK(sb); if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG)) { - if (le32_to_cpu(es->s_first_meta_bg) >= db_count) { + if (le32_to_cpu(es->s_first_meta_bg) > db_count) { ext4_msg(sb, KERN_WARNING, "first meta block group too large: %u " "(group descriptor block count %u)", -- GitLab From 3e2bb7d281edae5a0732023061402e4a7231dffc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 12 Oct 2017 09:18:18 +0200 Subject: [PATCH 1206/5498] Linux 3.18.75 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bece4a07e07a..63a173e41f22 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 74 +SUBLEVEL = 75 EXTRAVERSION = NAME = Diseased Newt -- GitLab From c8d3b871c4fbab4bc92ebbc3a1de180af00359c4 Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Wed, 27 Sep 2017 15:12:25 +0800 Subject: [PATCH 1207/5498] ANDROID: binder: init desired_prio.sched_policy before use it In function binder_transaction_priority(), we access desired_prio before initialzing it. This patch fix this. Change-Id: I9d14d50f9a128010476a65b52631630899a44633 Signed-off-by: Ganesh Mahendran --- drivers/staging/android/binder.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 60d568b925e8..f6d012ee2c93 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -1185,7 +1185,7 @@ static void binder_transaction_priority(struct task_struct *task, struct binder_priority node_prio, bool inherit_rt) { - struct binder_priority desired_prio; + struct binder_priority desired_prio = t->priority; if (t->set_priority_called) return; @@ -1197,9 +1197,6 @@ static void binder_transaction_priority(struct task_struct *task, if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) { desired_prio.prio = NICE_TO_PRIO(0); desired_prio.sched_policy = SCHED_NORMAL; - } else { - desired_prio.prio = t->priority.prio; - desired_prio.sched_policy = t->priority.sched_policy; } if (node_prio.prio < t->priority.prio || -- GitLab From 7c4059a482627e47e3c8ca363caf4b6812189622 Mon Sep 17 00:00:00 2001 From: Ashwanth Goli Date: Thu, 12 Oct 2017 13:14:13 +0530 Subject: [PATCH 1208/5498] defconfig: cleaning up NET related configs for LW Removing IMS, VPN, NAT related configs since these features are not required on wearable targets. Change-Id: Ia44eeb31a62a1146362b8be5847cf563d1481c3d Signed-off-by: Ashwanth Goli --- arch/arm/configs/msm8909w-perf_defconfig | 70 ++---------------------- arch/arm/configs/msm8909w_defconfig | 64 ---------------------- 2 files changed, 4 insertions(+), 130 deletions(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 774d09d43e5d..372fea208d0b 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -62,13 +62,9 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y -CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_INET_AH=y @@ -77,54 +73,24 @@ CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set CONFIG_IPV6=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_IPV6_MIP6=y -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_SUBTREES=y +# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET6_XFRM_MODE_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_BEET is not set +# CONFIG_IPV6_SIT is not set CONFIG_NETFILTER=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SIP=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NETMAP=y CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_REDIRECT=y CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -137,14 +103,11 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y -CONFIG_NF_NAT_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -158,20 +121,10 @@ CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_NF_LOG_IPV6=y -CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y -CONFIG_L2TP=y -CONFIG_L2TP_DEBUGFS=y -CONFIG_L2TP_V3=y -CONFIG_L2TP_IP=y -CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -179,7 +132,6 @@ CONFIG_NET_CLS_FW=y CONFIG_NET_CLS_U32=y CONFIG_NET_CLS_FLOW=y CONFIG_NET_EMATCH=y -CONFIG_NET_EMATCH_NBYTE=y CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y @@ -217,23 +169,9 @@ CONFIG_BLK_DEV_DM=y CONFIG_DM_VERITY=y CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y -CONFIG_DUMMY=y CONFIG_IFB=y -CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y -CONFIG_PPP=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_FILTER=y -CONFIG_PPP_MPPE=y -CONFIG_PPP_MULTILINK=y -CONFIG_PPPOE=y -CONFIG_PPPOL2TP=y -CONFIG_PPPOLAC=y -CONFIG_PPPOPNS=y -CONFIG_PPP_ASYNC=y -CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index ec898e7f1c13..924b5cbb0b00 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -66,13 +66,9 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y -CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_INET_AH=y @@ -81,36 +77,8 @@ CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set CONFIG_IPV6=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_IPV6_MIP6=y -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SIP=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y @@ -121,12 +89,8 @@ CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -139,14 +103,11 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y -CONFIG_NF_NAT_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -160,20 +121,10 @@ CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_NF_LOG_IPV6=y -CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y -CONFIG_L2TP=y -CONFIG_L2TP_DEBUGFS=y -CONFIG_L2TP_V3=y -CONFIG_L2TP_IP=y -CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -181,7 +132,6 @@ CONFIG_NET_CLS_FW=y CONFIG_NET_CLS_U32=y CONFIG_NET_CLS_FLOW=y CONFIG_NET_EMATCH=y -CONFIG_NET_EMATCH_NBYTE=y CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y @@ -220,23 +170,9 @@ CONFIG_BLK_DEV_DM=y CONFIG_DM_VERITY=y CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y -CONFIG_DUMMY=y CONFIG_IFB=y -CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y -CONFIG_PPP=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_FILTER=y -CONFIG_PPP_MPPE=y -CONFIG_PPP_MULTILINK=y -CONFIG_PPPOE=y -CONFIG_PPPOL2TP=y -CONFIG_PPPOLAC=y -CONFIG_PPPOPNS=y -CONFIG_PPP_ASYNC=y -CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y -- GitLab From 017e3d74fd142620e13e393d6ad801902e2682de Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 11 Oct 2017 12:50:24 +0530 Subject: [PATCH 1209/5498] ASoC: msm: qdsp6v2: add route from Voice to PRI_TDM interface Add dai-links in routing driver to support voice call over Primary TDM interface. Change-Id: I6dc53905b595d31979e0706718d857230f32497c Signed-off-by: Ashish Jain --- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 253 +++++++++++++++++++++ 1 file changed, 253 insertions(+) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 501b26e3970c..1df7be78319f 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -6832,6 +6832,162 @@ static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = { msm_routing_put_voice_mixer), }; +static const struct snd_kcontrol_new pri_tdm_rx_0_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_1_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_2_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_3_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + static const struct snd_kcontrol_new quat_tdm_rx_2_voice_mixer_controls[] = { SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUAT_TDM_RX_2, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, @@ -6896,6 +7052,9 @@ static const struct snd_kcontrol_new tx_voice_mixer_controls[] = { SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3_Voice", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), SOC_SINGLE_EXT("PRI_MI2S_TX_Voice", MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -6932,6 +7091,9 @@ static const struct snd_kcontrol_new tx_voice2_mixer_controls[] = { SOC_SINGLE_EXT("PRI_MI2S_TX_Voice2", MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3_Voice2", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), SOC_SINGLE_EXT("TERT_MI2S_TX_Voice2", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -6962,6 +7124,9 @@ static const struct snd_kcontrol_new tx_volte_mixer_controls[] = { SOC_SINGLE_EXT("PRI_MI2S_TX_VoLTE", MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3_VoLTE", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), SOC_SINGLE_EXT("TERT_MI2S_TX_VoLTE", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -6992,6 +7157,9 @@ static const struct snd_kcontrol_new tx_vowlan_mixer_controls[] = { SOC_SINGLE_EXT("PRI_MI2S_TX_VoWLAN", MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3_VoWLAN", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), SOC_SINGLE_EXT("TERT_MI2S_TX_VoWLAN", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -7028,6 +7196,9 @@ static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_TDM_TX_0_MMode1", MSM_BACKEND_DAI_QUAT_TDM_TX_0, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3_MMode1", + MSM_BACKEND_DAI_PRI_TDM_TX_3, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), SOC_SINGLE_EXT("SEC_MI2S_TX_MMode1", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -7061,6 +7232,9 @@ static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = { SOC_SINGLE_EXT("TERT_MI2S_TX_MMode2", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3_MMode2", + MSM_BACKEND_DAI_PRI_TDM_TX_3, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), SOC_SINGLE_EXT("SEC_MI2S_TX_MMode2", MSM_BACKEND_DAI_SECONDARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -10463,6 +10637,22 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_NOPM, 0, 0, quin_mi2s_rx_voice_mixer_controls, ARRAY_SIZE(quin_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_0_Voice Mixer", + SND_SOC_NOPM, 0, 0, + pri_tdm_rx_0_voice_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_0_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_1_Voice Mixer", + SND_SOC_NOPM, 0, 0, + pri_tdm_rx_1_voice_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_1_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_2_Voice Mixer", + SND_SOC_NOPM, 0, 0, + pri_tdm_rx_2_voice_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_2_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_3_Voice Mixer", + SND_SOC_NOPM, 0, 0, + pri_tdm_rx_3_voice_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_3_voice_mixer_controls)), SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2_Voice Mixer", SND_SOC_NOPM, 0, 0, quat_tdm_rx_2_voice_mixer_controls, @@ -11844,6 +12034,62 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_Voice Mixer"}, + {"PRI_TDM_RX_0_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"PRI_TDM_RX_0_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_TDM_RX_0_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_TDM_RX_0_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0_Voice Mixer"}, + + {"PRI_TDM_RX_1_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"PRI_TDM_RX_1_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_TDM_RX_1_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_TDM_RX_1_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1_Voice Mixer"}, + + {"PRI_TDM_RX_2_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"PRI_TDM_RX_2_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_TDM_RX_2_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_TDM_RX_2_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2_Voice Mixer"}, + + {"PRI_TDM_RX_3_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"PRI_TDM_RX_3_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_TDM_RX_3_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_TDM_RX_3_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3_Voice Mixer"}, + {"QUAT_TDM_RX_2_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_Voice Mixer"}, @@ -11938,6 +12184,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"}, {"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"}, {"Voice_Tx Mixer", "SEC_MI2S_TX_Voice", "SEC_MI2S_TX"}, + {"Voice_Tx Mixer", "PRI_TDM_TX_3_Voice", "PRI_TDM_TX_3"}, {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"}, {"Voice2_Tx Mixer", "PRI_TX_Voice2", "PRI_I2S_TX"}, @@ -11949,6 +12196,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"Voice2_Tx Mixer", "AFE_PCM_TX_Voice2", "PCM_TX"}, {"Voice2_Tx Mixer", "AUX_PCM_TX_Voice2", "AUX_PCM_TX"}, {"Voice2_Tx Mixer", "SEC_AUX_PCM_TX_Voice2", "SEC_AUX_PCM_TX"}, + {"Voice2_Tx Mixer", "PRI_TDM_TX_3_Voice2", "PRI_TDM_TX_3"}, {"VOICE2_UL", NULL, "Voice2_Tx Mixer"}, {"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"}, @@ -11960,6 +12208,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"VoLTE_Tx Mixer", "MI2S_TX_VoLTE", "MI2S_TX"}, {"VoLTE_Tx Mixer", "PRI_MI2S_TX_VoLTE", "PRI_MI2S_TX"}, {"VoLTE_Tx Mixer", "TERT_MI2S_TX_VoLTE", "TERT_MI2S_TX"}, + {"VoLTE_Tx Mixer", "PRI_TDM_TX_3_VoLTE", "PRI_TDM_TX_3"}, {"VoLTE_UL", NULL, "VoLTE_Tx Mixer"}, {"VoWLAN_Tx Mixer", "PRI_TX_VoWLAN", "PRI_I2S_TX"}, @@ -11971,6 +12220,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"VoWLAN_Tx Mixer", "MI2S_TX_VoWLAN", "MI2S_TX"}, {"VoWLAN_Tx Mixer", "PRI_MI2S_TX_VoWLAN", "PRI_MI2S_TX"}, {"VoWLAN_Tx Mixer", "TERT_MI2S_TX_VoWLAN", "TERT_MI2S_TX"}, + {"VoWLAN_Tx Mixer", "PRI_TDM_TX_3_VoWLAN", "PRI_TDM_TX_3"}, {"VoWLAN_UL", NULL, "VoWLAN_Tx Mixer"}, {"VoiceMMode1_Tx Mixer", "PRI_TX_MMode1", "PRI_I2S_TX"}, @@ -11984,6 +12234,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"VoiceMMode1_Tx Mixer", "SEC_AUX_PCM_TX_MMode1", "SEC_AUX_PCM_TX"}, {"VoiceMMode1_Tx Mixer", "QUAT_TDM_TX_0_MMode1", "QUAT_TDM_TX_0"}, {"VoiceMMode1_Tx Mixer", "SEC_MI2S_TX_MMode1", "SEC_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "PRI_TDM_TX_3_MMode1", "PRI_TDM_TX_3"}, {"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"}, {"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"}, @@ -11996,6 +12247,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"VoiceMMode2_Tx Mixer", "AUX_PCM_TX_MMode2", "AUX_PCM_TX"}, {"VoiceMMode2_Tx Mixer", "SEC_AUX_PCM_TX_MMode2", "SEC_AUX_PCM_TX"}, {"VoiceMMode2_Tx Mixer", "SEC_MI2S_TX_MMode2", "SEC_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "PRI_TDM_TX_3_MMode2", "PRI_TDM_TX_3"}, {"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"}, {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"}, @@ -12007,6 +12259,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"}, {"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"}, {"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"}, + {"Voip_Tx Mixer", "PRI_TDM_TX_3_Voip", "PRI_TDM_TX_3"}, {"VOIP_UL", NULL, "Voip_Tx Mixer"}, {"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"}, -- GitLab From 06c3fa2058c152d85f0cb16501c15d5c8e0270b9 Mon Sep 17 00:00:00 2001 From: Lior David Date: Thu, 28 Sep 2017 13:44:54 +0300 Subject: [PATCH 1210/5498] wil6210: potential buffer overflow in wmi_evt_aoa_meas The code in wmi_evt_aoa_meas can potentially overflow a data buffer if the len parameter is smaller than offsetof(struct wmi_aoa_meas_event, meas_data). Add protection against this case. Change-Id: I3f86f231e0c6b5ac54abbb9865cba0cbbd8d0448 Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/ftm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/ftm.c b/drivers/net/wireless/ath/wil6210/ftm.c index 5cf07343a33c..87e1115bc081 100644 --- a/drivers/net/wireless/ath/wil6210/ftm.c +++ b/drivers/net/wireless/ath/wil6210/ftm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -650,6 +650,10 @@ void wil_aoa_evt_meas(struct wil6210_priv *wil, int data_len = len - offsetof(struct wmi_aoa_meas_event, meas_data); struct wil_aoa_meas_result *res; + if (data_len < 0) { + wil_err(wil, "AOA event too short (%d)\n", len); + return; + } data_len = min_t(int, le16_to_cpu(evt->length), data_len); res = kmalloc(sizeof(*res) + data_len, GFP_KERNEL); -- GitLab From a13f8956be693c9334095a4f7b2855f46e49502d Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Tue, 26 Sep 2017 17:56:25 +0800 Subject: [PATCH 1211/5498] ANDROID: binder: fix node sched policy calculation We should use FLAT_BINDER_FLAG_SCHED_POLICY_MASK as the mask to calculate sched policy. Change-Id: Ic252fd7c68495830690130d792802c02f99fc8fc Signed-off-by: Ganesh Mahendran --- drivers/staging/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index f6d012ee2c93..030b6da9e8cf 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -1299,7 +1299,7 @@ static struct binder_node *binder_init_node_ilocked( node->cookie = cookie; node->work.type = BINDER_WORK_NODE; priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; - node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >> + node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); -- GitLab From c89830461759e63f65fde467ec9bfe3759e510e3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 2 Oct 2017 02:50:16 +0800 Subject: [PATCH 1212/5498] f2fs: fix potential panic during fstrim As Ju Hyung Park reported: "When 'fstrim' is called for manual trim, a BUG() can be triggered randomly with this patch. I'm seeing this issue on both x86 Desktop and arm64 Android phone. On x86 Desktop, this was caused during Ubuntu boot-up. I have a cronjob installed which calls 'fstrim -v /' during boot. On arm64 Android, this was caused during GC looping with 1ms gc_min_sleep_time & gc_max_sleep_time." Root cause of this issue is that f2fs_wait_discard_bios can only be used by f2fs_put_super, because during put_super there must be no other referrers, so it can ignore discard entry's reference count when removing the entry, otherwise in other caller we will hit bug_on in __remove_discard_cmd as there may be other issuer added reference count in discard entry. Thread A Thread B - issue_discard_thread - f2fs_ioc_fitrim - f2fs_trim_fs - f2fs_wait_discard_bios - __issue_discard_cmd - __submit_discard_cmd - __wait_discard_cmd - dc->ref++ - __wait_one_discard_bio - __wait_discard_cmd - __remove_discard_cmd - f2fs_bug_on(sbi, dc->ref) Fixes: 969d1b180d987c2be02de890d0fff0f66a0e80de Reported-by: Ju Hyung Park Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/segment.c | 6 +++--- fs/f2fs/super.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 726a71c872fd..8193223fc146 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2639,7 +2639,7 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); void stop_discard_thread(struct f2fs_sb_info *sbi); -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void release_discard_addrs(struct f2fs_sb_info *sbi); int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7dd2f612ec81..bb159226bf36 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1290,11 +1290,11 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) } /* This comes from f2fs_put_super and f2fs_trim_fs */ -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount) { __issue_discard_cmd(sbi, false); __drop_discard_cmd(sbi); - __wait_discard_cmd(sbi, false); + __wait_discard_cmd(sbi, !umount); } static void mark_discard_range_all(struct f2fs_sb_info *sbi) @@ -2324,7 +2324,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) } /* It's time to issue all the filed discards */ mark_discard_range_all(sbi); - f2fs_wait_discard_bios(sbi); + f2fs_wait_discard_bios(sbi, false); out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return err; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 80bb604b4f54..9472358cc9e9 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -816,7 +816,7 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - f2fs_wait_discard_bios(sbi); + f2fs_wait_discard_bios(sbi, true); if (f2fs_discard_en(sbi) && !sbi->discard_blks) { struct cp_control cpc = { -- GitLab From 23bee11152e408a6950f7e981024413e7add26d9 Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Fri, 15 Sep 2017 15:48:30 -0700 Subject: [PATCH 1213/5498] soc: qcom: fix out of bounds read issue If userspace sends non-null terminated channel name then out of bounds read is possible while printing channel name. Fix this issue by validating channel name before use. Change-Id: I418b7f446c6a849450dd4314592c1d3810163bdc Signed-off-by: Vidyakumar Athota --- drivers/soc/qcom/wcd-dsp-glink.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index e9da395bb8ad..03eef82918d6 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -604,6 +604,21 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, memcpy(&ch[i]->ch_cfg, payload, ch_cfg_size); payload += ch_cfg_size; + /* check ch name is valid string or not */ + for (j = 0; j < WDSP_CH_NAME_MAX_LEN; j++) { + if (ch[i]->ch_cfg.name[j] == '\0') + break; + } + + if (j == WDSP_CH_NAME_MAX_LEN) { + dev_err_ratelimited(wpriv->dev, "%s: Wrong channel name\n", + __func__); + kfree(ch[i]); + ch[i] = NULL; + ret = -EINVAL; + goto err_ch_mem; + } + mutex_init(&ch[i]->mutex); ch[i]->wpriv = wpriv; INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk); @@ -876,8 +891,6 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, ret = -EINVAL; goto free_buf; } - dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n", - __func__, cpkt->ch_name, pkt_max_size); for (i = 0; i < wpriv->no_of_channels; i++) { if (wpriv->ch && wpriv->ch[i] && (!strcmp(cpkt->ch_name, @@ -892,6 +905,8 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, ret = -EINVAL; goto free_buf; } + dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n", + __func__, cpkt->ch_name, pkt_max_size); ret = wait_event_timeout(tx_buf->ch->ch_connect_wait, (tx_buf->ch->channel_state == -- GitLab From eec71a71334aa4aeb3d4fe607f318b38521f11af Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 2 Oct 2017 02:50:16 +0800 Subject: [PATCH 1214/5498] UPSTREAM: f2fs: fix potential panic during fstrim As Ju Hyung Park reported: "When 'fstrim' is called for manual trim, a BUG() can be triggered randomly with this patch. I'm seeing this issue on both x86 Desktop and arm64 Android phone. On x86 Desktop, this was caused during Ubuntu boot-up. I have a cronjob installed which calls 'fstrim -v /' during boot. On arm64 Android, this was caused during GC looping with 1ms gc_min_sleep_time & gc_max_sleep_time." Root cause of this issue is that f2fs_wait_discard_bios can only be used by f2fs_put_super, because during put_super there must be no other referrers, so it can ignore discard entry's reference count when removing the entry, otherwise in other caller we will hit bug_on in __remove_discard_cmd as there may be other issuer added reference count in discard entry. Thread A Thread B - issue_discard_thread - f2fs_ioc_fitrim - f2fs_trim_fs - f2fs_wait_discard_bios - __issue_discard_cmd - __submit_discard_cmd - __wait_discard_cmd - dc->ref++ - __wait_one_discard_bio - __wait_discard_cmd - __remove_discard_cmd - f2fs_bug_on(sbi, dc->ref) Change-Id: I8fb5c8215e6222ae853e7781218d5084e1f11166 Fixes: 969d1b180d987c2be02de890d0fff0f66a0e80de Reported-by: Ju Hyung Park Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim (cherry picked from commit 638164a2718f337ea224b747cf5977ef143166a4) --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/segment.c | 6 +++--- fs/f2fs/super.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5711b8f0c963..226934652d08 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2639,7 +2639,7 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); void stop_discard_thread(struct f2fs_sb_info *sbi); -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void release_discard_addrs(struct f2fs_sb_info *sbi); int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7dd2f612ec81..bb159226bf36 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1290,11 +1290,11 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) } /* This comes from f2fs_put_super and f2fs_trim_fs */ -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount) { __issue_discard_cmd(sbi, false); __drop_discard_cmd(sbi); - __wait_discard_cmd(sbi, false); + __wait_discard_cmd(sbi, !umount); } static void mark_discard_range_all(struct f2fs_sb_info *sbi) @@ -2324,7 +2324,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) } /* It's time to issue all the filed discards */ mark_discard_range_all(sbi); - f2fs_wait_discard_bios(sbi); + f2fs_wait_discard_bios(sbi, false); out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return err; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 80bb604b4f54..9472358cc9e9 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -816,7 +816,7 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - f2fs_wait_discard_bios(sbi); + f2fs_wait_discard_bios(sbi, true); if (f2fs_discard_en(sbi) && !sbi->discard_blks) { struct cp_control cpc = { -- GitLab From ad902ef5151001c81e7a0282e4001102fab55579 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 5 Oct 2017 07:03:21 +0530 Subject: [PATCH 1215/5498] ARM: dts: msm: Specify throughput threshold for IPA on SDX20 Different target supports different throughput threshold bandwidth for NOM and TURBO clocks. To support this add a new device tree node to set tput threshold bandwidth. With this change we can support to update a new threshold BW for NOM and TURBO clocks during driver init. Change-Id: I8f2850ee4db482997da856d3eefae9d89bf7a6a2 Signed-off-by: Mohammed Javid --- arch/arm/boot/dts/qcom/sdx20.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/sdx20.dtsi b/arch/arm/boot/dts/qcom/sdx20.dtsi index c045800e24dc..226cc2d0c0d7 100644 --- a/arch/arm/boot/dts/qcom/sdx20.dtsi +++ b/arch/arm/boot/dts/qcom/sdx20.dtsi @@ -88,6 +88,7 @@ ipa_hw: qcom,ipa@07B00000 { qcom,ipa-hw-ver = <12>; /* IPA core version = IPAv3.5 */ /delete-property/qcom,use-rg10-limitation-mitigation; + qcom,throughput-threshold = <600 1200>; /* NOM TURBO */ }; qcom,ipa_fws { -- GitLab From 7fcec1bf02c9ab7cefce45c2aa6acea62dbd7fc6 Mon Sep 17 00:00:00 2001 From: Prashanth Vadde Date: Fri, 13 Oct 2017 16:34:29 +0530 Subject: [PATCH 1216/5498] ARM: dts: msm: Do not include msm8917 dtsi for apq8017 Do not include msm8917 dtsi in apq8017 specific dtsi as it will cause the conflict in board id. Change-Id: I9ba96892ebc7f6538ad55fefc15357fae6665026 Signed-off-by: Prashanth Vadde --- arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi b/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi index b53847a885ed..0228ca936a09 100644 --- a/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi +++ b/arch/arm/boot/dts/qcom/apq8017-pmi8950-cdp-wcd-rome.dtsi @@ -11,8 +11,6 @@ * GNU General Public License for more details. */ -#include "msm8917.dtsi" - / { model = "Qualcomm Technologies, Inc. APQ8017-PMI8950 CDP \ with WCD codec/Rome card"; -- GitLab From f7a3d5d10ca78ed3a52c96a55bf4356dc9becf39 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Kuppala Date: Fri, 13 Oct 2017 18:22:38 +0530 Subject: [PATCH 1217/5498] defconfig: msm8909w-perf_defconfig changes Add CONFIG_CRYPTO_SHA256_ARM=y Remove CONFIG_DM_VERITY_FEC to remove FEC support as it is taking lot of memory. Change-Id: I0463b3fec9f1fd946f336bf5dbe586cdd8a9594b Signed-off-by: Srinivasa Rao Kuppala --- arch/arm/configs/msm8909w-perf_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 46f72f4ef0f4..10e0ccec57b6 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -215,7 +215,6 @@ CONFIG_UID_SYS_STATS=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y CONFIG_IFB=y @@ -482,6 +481,7 @@ CONFIG_CRYPTO_GF128MUL=y CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y CONFIG_ARM_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM_NEON=y +CONFIG_CRYPTO_SHA256_ARM=y CONFIG_CRYPTO_SHA512_ARM_NEON=y CONFIG_CRYPTO_AES_ARM_BS=y CONFIG_QMI_ENCDEC=y -- GitLab From d280746bd5e82d4933c20536077e881af3a3fc12 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Kuppala Date: Fri, 13 Oct 2017 19:08:19 +0530 Subject: [PATCH 1218/5498] defconfig: msm8909w_defconfig Add CONFIG_CRYPTO_SHA256_ARM=y Remove CONFIG_DM_VERITY_FEC to remove FEC support as it is taking lot of memory. Change-Id: Ia472b0af48b4f424cc57e3a1276a1e09e07ad5dd Signed-off-by: Srinivasa Rao Kuppala --- arch/arm/configs/msm8909w_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 8c6129203aef..6d6285b6f2b6 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -218,7 +218,6 @@ CONFIG_QPNP_MISC=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y CONFIG_IFB=y @@ -517,6 +516,7 @@ CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y CONFIG_ARM_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM_NEON=y +CONFIG_CRYPTO_SHA256_ARM=y CONFIG_CRYPTO_SHA512_ARM_NEON=y CONFIG_CRYPTO_AES_ARM_BS=y CONFIG_QMI_ENCDEC=y -- GitLab From 362adf0236746af350485f1e9f7a2e5375ffc751 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 13 Oct 2017 10:27:45 -0700 Subject: [PATCH 1219/5498] FROMLIST: f2fs: expose some sectors to user in inline data or dentry case (from https://patchwork.kernel.org/patch/10005409/) If there's some data written through inline data or dentry, we need to shouw st_blocks. This fixes reporting zero blocks even though there is small written data. Bug: 67651285 Bug: 67600404 Change-Id: I9ad5cb137eb627b9fd22740d2ab98e0221433c95 Cc: stable@vger.kernel.org Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e5a47b59d1dd..0bc56822ffc3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -665,6 +665,11 @@ int f2fs_getattr(struct vfsmount *mnt, { struct inode *inode = d_inode(dentry); generic_fillattr(inode, stat); + + /* we need to show initial sectors used for inline_data/dentries */ + if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) + stat->blocks += (stat->size + 511) >> 9; + return 0; } -- GitLab From 60b47004f1781b5b7d6846181d3089303a85e7e0 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Mon, 16 Oct 2017 09:42:52 +0530 Subject: [PATCH 1220/5498] defconfig: Enable QPNP modules in 8909w perf-defconfig Enable the QPNP modules supported by PM660. Change-Id: I2cf97eea3ebaeacb51340c5b33fd545b2bae4858 Signed-off-by: Anirudh Ghayal --- arch/arm/configs/msm8909w-perf_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 1465a706686c..f9ce52aac61a 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -165,6 +165,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_QSEECOM=y CONFIG_UID_SYS_STATS=y +CONFIG_QPNP_MISC=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_VERITY=y @@ -235,6 +236,7 @@ CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_THERMAL=y CONFIG_THERMAL_TSENS8974=y CONFIG_THERMAL_MONITOR=y +CONFIG_THERMAL_QPNP=y CONFIG_THERMAL_QPNP_ADC_TM=y CONFIG_WCD9335_CODEC=y CONFIG_REGULATOR=y @@ -315,6 +317,7 @@ CONFIG_MSM_BUS_SCALING=y CONFIG_BUS_TOPOLOGY_ADHOC=y CONFIG_QPNP_POWER_ON=y CONFIG_QPNP_REVID=y +CONFIG_QPNP_COINCELL=y CONFIG_SPS=y CONFIG_USB_BAM=y CONFIG_SPS_SUPPORT_NDP_BAM=y @@ -336,6 +339,7 @@ CONFIG_MSM_RUN_QUEUE_STATS=y CONFIG_MSM_SCM=y CONFIG_MSM_MPM_OF=y CONFIG_MSM_SMEM=y +CONFIG_QPNP_PBS=y CONFIG_MSM_SMD=y CONFIG_MSM_SMD_DEBUG=y CONFIG_MSM_GLINK=y -- GitLab From 1d036889ba2110f9e9b5ae6185f8c7fda440407b Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 22 Sep 2017 18:19:36 +0530 Subject: [PATCH 1221/5498] ASoC: msm: qdsp6v2: Add TDM changes in dai driver Update dai driver with TDM dai related changes. Add a property to help not configure the AFE EBIT on platforms where it is not supported. Change-Id: Ic10cbb58f31698ceb03a07d94c8834515e828a93 Signed-off-by: Yeleswarapu Nagaradhesh Signed-off-by: Ashish Jain --- .../bindings/sound/qcom-audio-dev.txt | 7 + sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 193 ++++++++++++++++-- 2 files changed, 182 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index be9df49293f5..bbfbd2d6a6b6 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -1386,6 +1386,13 @@ Required properties: When clock rate is set to zero, then external clock is assumed. + - qcom,msm-cpudai-tdm-afe-ebit-unsupported: Notify if ebit setting is needed + When this is set , alongwith + clock rate as zero then afe + is not configured for clock. + + + [Second Level Nodes] Required properties: diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 80b03f9a02aa..4b1159d3774b 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -190,17 +190,22 @@ struct msm_dai_q6_auxpcm_dai_data { struct msm_dai_q6_dai_data bdai_data; /* incoporate base DAI data */ }; +static union afe_port_group_config group_cfg_tx; + struct msm_dai_q6_tdm_dai_data { DECLARE_BITMAP(status_mask, STATUS_MAX); u32 rate; u32 channels; u32 bitwidth; u32 num_group_ports; + bool afe_ebit_unsupported; struct afe_clk_set clk_set; /* hold LPASS clock config. */ union afe_port_group_config group_cfg; /* hold tdm group config */ struct afe_tdm_port_config port_cfg; /* hold tdm config */ }; +static bool afe_ebit_unsupported; + /* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command * 0: linear PCM * 1: non-linear PCM @@ -2049,7 +2054,7 @@ static struct snd_soc_dai_driver msm_dai_q6_afe_lb_tx_dai[] = { .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 48000, }, .id = AFE_LOOPBACK_TX, .probe = msm_dai_q6_dai_probe, @@ -4131,6 +4136,11 @@ static int msm_dai_tdm_q6_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s: Clk Attribute from DT file %d\n", __func__, tdm_clk_set.clk_attri); + afe_ebit_unsupported = of_property_read_bool(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-afe-ebit-unsupported"); + + dev_dbg(&pdev->dev, "afe_ebit_unsupported %d\n", afe_ebit_unsupported); + /* other initializations within device group */ atomic_set(&tdm_group_ref[group_idx], 0); @@ -4906,6 +4916,9 @@ static int msm_dai_q6_tdm_set_clk( { int rc = 0; + pr_debug("dai_data->group_cfg.tdm_cfg.group_id = %d : %d\n", + dai_data->group_cfg.tdm_cfg.group_id, + dai_data->clk_set.clk_freq_in_hz); switch (dai_data->group_cfg.tdm_cfg.group_id) { case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: @@ -5118,6 +5131,10 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, case 16: cap_mask = 0xFFFF; break; + case 4: + cap_mask = 0xF; + break; + default: dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); @@ -5160,6 +5177,11 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, tdm_group->nslots_per_frame = slots; tdm_group->slot_width = slot_width; tdm_group->slot_mask = rx_mask & cap_mask; + dev_dbg(dai->dev, "%s:Rx:tdm_group->nslots_per_frame %d\n" + "tdm_group->slot_width %d\n" + "tdm_group->slot_mask %d\n", __func__, + tdm_group->nslots_per_frame, + tdm_group->slot_width, tdm_group->slot_mask); break; case AFE_PORT_ID_PRIMARY_TDM_TX: case AFE_PORT_ID_PRIMARY_TDM_TX_1: @@ -5196,6 +5218,10 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, tdm_group->nslots_per_frame = slots; tdm_group->slot_width = slot_width; tdm_group->slot_mask = tx_mask & cap_mask; + dev_dbg(dai->dev, "%s:Tx:tdm_group->nslots_per_frame %d\n" + "tdm_group->slot_width %d,tdm_group->slot_mask %d\n" + "tx%d\n", __func__, tdm_group->nslots_per_frame, + tdm_group->slot_width, tdm_group->slot_mask, tx_mask); break; default: dev_err(dai->dev, "%s: invalid dai id 0x%x\n", @@ -5497,6 +5523,23 @@ static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, custom_tdm_header->header[7]); } + memcpy(&group_cfg_tx.group_cfg, &dai_data->group_cfg.group_cfg, + sizeof(dai_data->group_cfg.group_cfg)); + memcpy(&group_cfg_tx.group_enable, &dai_data->group_cfg.group_enable, + sizeof(dai_data->group_cfg.group_enable)); + memcpy(&group_cfg_tx.tdm_cfg, &dai_data->group_cfg.tdm_cfg, + sizeof(dai_data->group_cfg.tdm_cfg)); + pr_debug("%s: TDM GROUP:\n" + "num_channels=%d sample_rate=%d bit_width=%d\n" + "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n", + __func__, + group_cfg_tx.tdm_cfg.num_channels, + group_cfg_tx.tdm_cfg.sample_rate, + group_cfg_tx.tdm_cfg.bit_width, + group_cfg_tx.tdm_cfg.nslots_per_frame, + group_cfg_tx.tdm_cfg.slot_width, + group_cfg_tx.tdm_cfg.slot_mask); + return 0; } @@ -5508,6 +5551,8 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, dev_get_drvdata(dai->dev); u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; int group_idx = 0; + u16 prim_port_id = 0; + u16 sec_port_id = 0; atomic_t *group_ref = NULL; group_idx = msm_dai_q6_get_group_idx(dai->id); @@ -5528,41 +5573,104 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, /* TX and RX share the same clk. AFE clk is enabled per group to simplify the logic. DSP will monitor the clk count. */ - rc = msm_dai_q6_tdm_set_clk(dai_data, - dai->id, true); - if (IS_ERR_VALUE(rc)) { - dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n", - __func__, dai->id); - goto rtn; + if (!(dai_data->afe_ebit_unsupported && + !dai_data->clk_set.clk_freq_in_hz)) { + rc = msm_dai_q6_tdm_set_clk(dai_data, + dai->id, true); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n", + __func__, dai->id); + goto rtn; + } } if (dai_data->num_group_ports > 1) { + dev_dbg(dai->dev, "%s:enable RX afe group\n", + __func__); rc = afe_port_group_enable(group_id, &dai_data->group_cfg, true); if (IS_ERR_VALUE(rc)) { - dev_err(dai->dev, "%s: fail to enable AFE group 0x%x\n", + dev_err(dai->dev, + "%s:failed to enable grp %x\n", __func__, group_id); goto rtn; } } } - - rc = afe_tdm_port_start(dai->id, &dai_data->port_cfg, + /* + * 8909 HW has a dependency where for Rx/Tx to work in TDM mode + * We need to start a Tx or Rx port in the same group. + * Hence for BG use TDM_TX when a RX session is requested and + * use TDM_RX port when a TX session is requested as these ports + * are unused as of now. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + prim_port_id = dai->id; + if (dai_data->afe_ebit_unsupported) + sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; + } else { + prim_port_id = dai->id; + if (dai_data->afe_ebit_unsupported) + sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; + } + dev_dbg(dai->dev, "\n%s:open prim port id %d TDM rate: %d\n" + "dai_data->port_cfg.tdm.slot_mask %x\n" + "dai_data->port_cfg.tdm.nslots_per_frame:%x\n", + __func__, prim_port_id, + dai_data->port_cfg.tdm.num_channels, + dai_data->port_cfg.tdm.slot_mask, + dai_data->port_cfg.tdm.nslots_per_frame); + + rc = afe_tdm_port_start(prim_port_id, &dai_data->port_cfg, dai_data->rate, dai_data->num_group_ports); + if (IS_ERR_VALUE(rc)) { if (atomic_read(group_ref) == 0) { afe_port_group_enable(group_id, NULL, false); - msm_dai_q6_tdm_set_clk(dai_data, - dai->id, false); + if (!(dai_data->afe_ebit_unsupported && + !dai_data->clk_set.clk_freq_in_hz)) + msm_dai_q6_tdm_set_clk(dai_data, + dai->id, false); } - dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n", - __func__, dai->id); + dev_err(dai->dev, "%s: open AFE port 0x%x\n", + __func__, prim_port_id); } else { set_bit(STATUS_PORT_STARTED, dai_data->status_mask); atomic_inc(group_ref); } + dai_data->port_cfg.tdm.num_channels = 1; + dai_data->port_cfg.tdm.slot_mask = 1; + dai_data->port_cfg.tdm.nslots_per_frame = 4; + + dev_dbg(dai->dev, "\n%s:open sec port id %d TDM rate: %d\n" + "dai_data->port_cfg.tdm.slot_mask %x\n" + "dai_data->port_cfg.tdm.nslotsper_frame:%x\n", __func__, + sec_port_id, dai_data->port_cfg.tdm.num_channels, + dai_data->port_cfg.tdm.slot_mask, + dai_data->port_cfg.tdm.nslots_per_frame); + + if (sec_port_id != 0) { + rc = afe_tdm_port_start(sec_port_id, + &dai_data->port_cfg, + dai_data->rate, 4); + if (IS_ERR_VALUE(rc)) { + if (atomic_read(group_ref) == 0) { + afe_port_group_enable(group_id, + NULL, false); + msm_dai_q6_tdm_set_clk(dai_data, + dai->id, false); + } + dev_err(dai->dev, "%s: fail AFE port 0x%x\n", + __func__, sec_port_id); + } else { + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + atomic_inc(group_ref); + } + } + /* TODO: need to monitor PCM/MI2S/TDM HW status */ /* NOTE: AFE should error out if HW resource contention */ @@ -5581,6 +5689,8 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, dev_get_drvdata(dai->dev); u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; int group_idx = 0; + u16 prim_port_id = 0; + u16 sec_port_id = 0; atomic_t *group_ref = NULL; group_idx = msm_dai_q6_get_group_idx(dai->id); @@ -5595,11 +5705,30 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, group_ref = &tdm_group_ref[group_idx]; if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { - rc = afe_close(dai->id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + prim_port_id = dai->id; + if (dai_data->afe_ebit_unsupported) + sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; + } else { + prim_port_id = dai->id; + if (dai_data->afe_ebit_unsupported) + sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; + } + + rc = afe_close(prim_port_id); if (IS_ERR_VALUE(rc)) { dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n", - __func__, dai->id); + __func__, prim_port_id); } + + if (sec_port_id != 0) { + rc = afe_close(sec_port_id); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail AFE port 0x%x\n", + __func__, sec_port_id); + } + } + atomic_dec(group_ref); clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); @@ -5608,13 +5737,15 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, rc = afe_port_group_enable(group_id, NULL, false); if (IS_ERR_VALUE(rc)) { - dev_err(dai->dev, "%s: fail to disable AFE group 0x%x\n", + dev_err(dai->dev, + "%s: failed to disable grp 0x%x\n", __func__, group_id); } rc = msm_dai_q6_tdm_set_clk(dai_data, dai->id, false); if (IS_ERR_VALUE(rc)) { - dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n", + dev_err(dai->dev, + "%s: fail to disable AFE clk 0x%x\n", __func__, dai->id); } } @@ -6985,6 +7116,32 @@ static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) dai_data->group_cfg.tdm_cfg = tdm_group_cfg; /* copy static num group ports per parent node */ dai_data->num_group_ports = num_tdm_group_ports[group_idx]; + dev_dbg(&pdev->dev, "TX group configuration tdm_dev_id 0x%x\n", + tdm_dev_id); + + dai_data->afe_ebit_unsupported = afe_ebit_unsupported; + + if (tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX || + tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX_1 || + tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX_2 || + tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX_3) { + dev_dbg(&pdev->dev, "Copy TX group config id %d\n", tdm_dev_id); + /*memcpy (&group_cfg_tx,&dai_data->group_cfg , + sizeof(dai_data->group_cfg));*/ + memcpy(&group_cfg_tx.group_cfg, + &dai_data->group_cfg.group_cfg , + sizeof(dai_data->group_cfg.group_cfg)); + memcpy(&group_cfg_tx.group_enable, + &dai_data->group_cfg.group_enable, + sizeof(dai_data->group_cfg.group_enable)); + memcpy(&group_cfg_tx.tdm_cfg, + &dai_data->group_cfg.tdm_cfg, + sizeof(dai_data->group_cfg.tdm_cfg)); + dev_dbg(&pdev->dev, + "Copy TX group configuration Successfully tdm_id %d\n", + tdm_dev_id); + } + dev_set_drvdata(&pdev->dev, dai_data); -- GitLab From ba687c80730a46b7209c6d4bb4317a3762f39f50 Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Mon, 18 Jan 2016 12:07:42 +0530 Subject: [PATCH 1222/5498] msm: mdss: Fix NULL pointer dereferences Fix potential Null pointer dereference while reading panel register in mdp3 driver. Change-Id: I09f35b9b86f2365f8f9e02460e2cdeeb60c7f399 CRs-Fixed: 959057 Signed-off-by: Raghavendra Ambadas --- drivers/video/msm/mdss/mdp3.c | 2 ++ drivers/video/msm/mdss/mdss.h | 1 + drivers/video/msm/mdss/mdss_debug.c | 35 +++++++++++++++++++++++------ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index 7a64d9aea0c0..4d4f859be3ff 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -2356,6 +2356,7 @@ static int mdp3_panel_register_done(struct mdss_panel_data *pdata) if (pdata->panel_info.cont_splash_enabled == false) mdp3_res->allow_iommu_update = true; + mdss_res->pdata = pdata; return rc; } @@ -2441,6 +2442,7 @@ static int mdp3_debug_init(struct platform_device *pdev) mdss_res->mdss_util = mdp3_res->mdss_util; mdata->debug_inf.debug_enable_clock = mdp3_debug_enable_clock; + mdata->mdp_rev = mdp3_res->mdp_rev; rc = mdss_debugfs_init(mdata); if (rc) diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h index 16b4fda2458a..e44ca678cd22 100644 --- a/drivers/video/msm/mdss/mdss.h +++ b/drivers/video/msm/mdss/mdss.h @@ -280,6 +280,7 @@ struct mdss_data_type { bool en_svs_high; u32 max_mdp_clk_rate; struct mdss_util_intf *mdss_util; + struct mdss_panel_data *pdata; struct platform_device *pdev; struct dss_io_data mdss_io; diff --git a/drivers/video/msm/mdss/mdss_debug.c b/drivers/video/msm/mdss/mdss_debug.c index 80c74c48d7f4..1d453b6b9ead 100644 --- a/drivers/video/msm/mdss/mdss_debug.c +++ b/drivers/video/msm/mdss/mdss_debug.c @@ -38,6 +38,8 @@ #define PANEL_TX_MAX_BUF 256 #define PANEL_CMD_MIN_TX_COUNT 2 #define PANEL_DATA_NODE_LEN 80 +/* MDP3 HW Version */ +#define MDP_CORE_HW_VERSION 0x03050306 /* Hex number + whitespace */ #define NEXT_VALUE_OFFSET 3 @@ -148,8 +150,10 @@ static ssize_t panel_debug_base_reg_write(struct file *file, char *bufp; struct mdss_data_type *mdata = mdss_res; - struct mdss_mdp_ctl *ctl; - struct mdss_dsi_ctrl_pdata *ctrl_pdata; + struct mdss_mdp_ctl *ctl = mdata->ctl_off + 0; + struct mdss_panel_data *panel_data = NULL; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct dsi_cmd_desc dsi_write_cmd = { {0/*data type*/, 1, 0, 0, 0, 0/* len */}, reg}; struct dcs_cmd_req cmdreq; @@ -164,6 +168,15 @@ static ssize_t panel_debug_base_reg_write(struct file *file, if (copy_from_user(buf, user_buf, count)) return -EFAULT; + if ((mdata->mdp_rev <= MDSS_MDP_HW_REV_105) || + (mdata->mdp_rev == MDP_CORE_HW_VERSION)) + panel_data = mdss_res->pdata; + else + panel_data = ctl->panel_data; + + ctrl_pdata = container_of(panel_data, + struct mdss_dsi_ctrl_pdata, panel_data); + buf[count] = 0; /* end of string */ bufp = buf; @@ -224,9 +237,8 @@ static ssize_t panel_debug_base_reg_read(struct file *file, char *panel_reg_buf, *rx_buf; struct mdss_data_type *mdata = mdss_res; struct mdss_mdp_ctl *ctl = mdata->ctl_off + 0; - struct mdss_panel_data *panel_data = ctl->panel_data; - struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(panel_data, - struct mdss_dsi_ctrl_pdata, panel_data); + struct mdss_panel_data *panel_data = NULL; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; int rc = -EFAULT; if (!dbg) @@ -259,8 +271,17 @@ static ssize_t panel_debug_base_reg_read(struct file *file, mdata->debug_inf.debug_enable_clock(1); panel_reg[0] = dbg->off; - mdss_dsi_panel_cmd_read(ctrl_pdata, panel_reg[0], panel_reg[1], - NULL, rx_buf, dbg->cnt); + if ((mdata->mdp_rev <= MDSS_MDP_HW_REV_105) || + (mdata->mdp_rev == MDP_CORE_HW_VERSION)) + panel_data = mdss_res->pdata; + else + panel_data = ctl->panel_data; + + ctrl_pdata = container_of(panel_data, + struct mdss_dsi_ctrl_pdata, panel_data); + + mdss_dsi_panel_cmd_read(ctrl_pdata, panel_reg[0], + panel_reg[1], NULL, rx_buf, dbg->cnt); len = scnprintf(panel_reg_buf, reg_buf_len, "0x%02zx: ", dbg->off); -- GitLab From 04ddcb095f245d807113248910120769e00c739f Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 12 Oct 2017 14:42:42 +0530 Subject: [PATCH 1223/5498] USB: phy: msm: Turn off HSUSB vdd LDO on OTG suspend Currently we keep voting for hsusb_vdd LDO on msm_otg_suspend. As a result, LDO remains ON after suspend. Fix this by adding disable and enable of the LDO during OTG suspend and resume respectively. Change-Id: Ifcd754df29fe8e67d42bad0dabc3f848497afdd8 Signed-off-by: Ajay Agarwal --- drivers/usb/phy/phy-msm-usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 3ada46301ff5..2598709866f0 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1462,6 +1462,7 @@ phcd_retry: if (motg->lpm_flags & PHY_RETENTIONED || (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED)) { + regulator_disable(hsusb_vdd); msm_hsusb_config_vddcx(0); } @@ -1587,6 +1588,8 @@ static int msm_otg_resume(struct msm_otg *motg) if (motg->lpm_flags & PHY_RETENTIONED || (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED)) { msm_hsusb_config_vddcx(1); + ret = regulator_enable(hsusb_vdd); + WARN(ret, "hsusb_vdd LDO enable failed\n"); msm_otg_disable_phy_hv_int(motg); msm_otg_exit_phy_retention(motg); motg->lpm_flags &= ~PHY_RETENTIONED; -- GitLab From 95d3f4326842b676c40c4e5380b008c8d45f1995 Mon Sep 17 00:00:00 2001 From: Mao Jinlong Date: Thu, 12 Oct 2017 11:27:10 +0800 Subject: [PATCH 1224/5498] rtc: Disable alarm irq if alarm time is in the past If device is boot up by rtc alarm, the alarm irq will still be enabled and the alarm time is smaller than current rtc time before any alarm is set or canceled. If device is powered off now, it will boot up automatically as the alarm irq is enabled. So disable alarm irq if alarm is enabled and alarm time is in the past. CRs-Fixed: 2109666 Change-Id: Ie60bd1222a400cd45a6c5a385faa70190fbe7e3c Signed-off-by: Mao Jinlong --- drivers/rtc/interface.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 5b2717f5dafa..b12f1694ad00 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -394,6 +394,14 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } EXPORT_SYMBOL_GPL(rtc_set_alarm); +static void rtc_alarm_disable(struct rtc_device *rtc) +{ + if (!rtc->ops || !rtc->ops->alarm_irq_enable) + return; + + rtc->ops->alarm_irq_enable(rtc->dev.parent, false); +} + /* Called once per device from rtc_device_register */ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { @@ -421,7 +429,11 @@ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) rtc->aie_timer.enabled = 1; timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node); + } else if (alarm->enabled && (rtc_tm_to_ktime(now).tv64 >= + rtc->aie_timer.node.expires.tv64)){ + rtc_alarm_disable(rtc); } + mutex_unlock(&rtc->ops_lock); return err; } @@ -800,14 +812,6 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) return 0; } -static void rtc_alarm_disable(struct rtc_device *rtc) -{ - if (!rtc->ops || !rtc->ops->alarm_irq_enable) - return; - - rtc->ops->alarm_irq_enable(rtc->dev.parent, false); -} - /** * rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue * @rtc rtc device -- GitLab From a7f6d23730e905dea38373297c59ade22b25aadd Mon Sep 17 00:00:00 2001 From: Kevin Brodsky Date: Fri, 4 Aug 2017 10:17:00 -0700 Subject: [PATCH 1225/5498] UPSTREAM: arm64: compat: Remove leftover variable declaration (cherry picked from commit 82d24d114f249d919b918ff8eefde4117db8f088) Commit a1d5ebaf8ccd ("arm64: big-endian: don't treat code as data when copying sigret code") moved the 32-bit sigreturn trampoline code from the aarch32_sigret_code array to kuser32.S. The commit removed the array definition from signal32.c, but not its declaration in signal32.h. Remove the leftover declaration. Signed-off-by: Kevin Brodsky Signed-off-by: Mark Salyzyn Signed-off-by: Catalin Marinas Bug: 20045882 Bug: 63737556 Change-Id: Ic8a5f0e367f0ecd5c5ddd9e3885d0285f91cf89e --- arch/arm64/include/asm/signal32.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/include/asm/signal32.h b/arch/arm64/include/asm/signal32.h index eeaa97559bab..81abea0b7650 100644 --- a/arch/arm64/include/asm/signal32.h +++ b/arch/arm64/include/asm/signal32.h @@ -22,8 +22,6 @@ #define AARCH32_KERN_SIGRET_CODE_OFFSET 0x500 -extern const compat_ulong_t aarch32_sigret_code[6]; - int compat_setup_frame(int usig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs); int compat_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, -- GitLab From 3c528f27ce8fb63b511fd013322d4a48291d16d4 Mon Sep 17 00:00:00 2001 From: Pranshu Gupta Date: Tue, 17 Oct 2017 11:13:50 +0530 Subject: [PATCH 1226/5498] ARM: dts: msm: Enable crypto related configs for msm8909w Enable crypto related configs for msm8909w_defconfig and msm8909w-perf_defconfig. Change-Id: Ia2852a1f717c1a943291ba69eda943ab24b6f9c8 Signed-off-by: Pranshu Gupta --- arch/arm/configs/msm8909w-perf_defconfig | 7 +++++++ arch/arm/configs/msm8909w_defconfig | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index f9ce52aac61a..5dc067d89bc0 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -412,7 +412,14 @@ CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 CONFIG_SECURITY_SELINUX=y CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=y CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y CONFIG_ARM_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM_NEON=y CONFIG_CRYPTO_SHA512_ARM_NEON=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index a620386cb059..1273e704da7c 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -440,8 +440,14 @@ CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 CONFIG_SECURITY_SELINUX=y -CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_DEV_QCRYPTO=y CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y CONFIG_ARM_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM_NEON=y CONFIG_CRYPTO_SHA512_ARM_NEON=y -- GitLab From 13f68039a5ea83868ab82ba0afda6d19ffde8849 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 5 Oct 2017 06:47:00 +0530 Subject: [PATCH 1227/5498] msm: ipa3: Add support to take data of tput threshold BW from dtsi Different target supports different throughput threshold bandwidth for NOM and TURBO clocks. To support this, add a support in driver code to accept new tput threshold bandwidth values from device tree property. With this change we support to change the tput bandwidth on driver init. Change-Id: I9ca83f808613c4c265ac265f6b112500541c6347 Signed-off-by: Mohammed Javid --- .../devicetree/bindings/platform/msm/ipa.txt | 1 + drivers/platform/msm/ipa/ipa_v3/ipa.c | 16 ++++++++++++++++ drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 3 +++ 3 files changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt index edc33b401e36..c8bc8d4c87c9 100644 --- a/Documentation/devicetree/bindings/platform/msm/ipa.txt +++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt @@ -58,6 +58,7 @@ memory allocation over a PCIe bridge a pipe reset via the IPA uC is required - qcom,ipa-wdi2: Boolean context flag to indicate whether using wdi-2.0 or not +- qcom,throughput-threshold: An array of throughput threshold for NOM and TURBO. - qcom,use-64-bit-dma-mask: Boolean context flag to indicate whether using 64bit dma mask or not - qcom,use-dma-zone: Boolean context flag to indicate whether memory diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 6e078675fc62..39dd57dbed1a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4888,6 +4888,14 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, goto fail_bind; } + if (resource_p->default_threshold[0] > 0) + ipa3_ctx->ctrl->clock_scaling_bw_threshold_nominal = + resource_p->default_threshold[0]; + + if (resource_p->default_threshold[1] > 0) + ipa3_ctx->ctrl->clock_scaling_bw_threshold_turbo = + resource_p->default_threshold[1]; + if (ipa3_bus_scale_table) { IPADBG("Use bus scaling info from device tree\n"); ipa3_ctx->ctrl->msm_bus_data_ptr = ipa3_bus_scale_table; @@ -5447,6 +5455,14 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->ipa_wdi2 ? "True" : "False"); + /* Updat BW for NOM and TURBO TPUT threshold from Device Tree*/ + result = of_property_read_u32_array(pdev->dev.of_node, + "qcom,throughput-threshold", + ipa_drv_res->default_threshold, + IPA_PM_THRESHOLD_MAX); + if (result) + IPAERR("failed to read qcom,throughput-thresholds\n"); + ipa_drv_res->use_64_bit_dma_mask = of_property_read_bool(pdev->dev.of_node, "qcom,use-64-bit-dma-mask"); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 295cdfe85dd5..e20303378a29 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -65,6 +65,8 @@ #define IPA_IPC_LOG_PAGES 50 +#define IPA_PM_THRESHOLD_MAX 2 + #define IPADBG(fmt, args...) \ do { \ pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\ @@ -1360,6 +1362,7 @@ struct ipa3_plat_drv_res { bool ipa_bam_remote_mode; bool modem_cfg_emb_pipe_flt; bool ipa_wdi2; + u32 default_threshold[IPA_PM_THRESHOLD_MAX]; bool use_64_bit_dma_mask; u32 wan_rx_ring_size; u32 lan_rx_ring_size; -- GitLab From 9cad9446f58ad9b3710b8d899e9544b879690d85 Mon Sep 17 00:00:00 2001 From: Samyukta Mogily Date: Mon, 16 Oct 2017 16:45:49 +0530 Subject: [PATCH 1228/5498] ARM: dts: msm: Reset GPIO configuration for flash on 8909 MTP Renaming the flash nodes according to the new convention and changing the GPIO configuration, to enable i2c flash on 8909MTP. Change-Id: Id772a9b5d52dcbab3c32cb680780b49b3218e2ed Signed-off-by: Samyukta Mogily --- .../dts/qcom/msm8909-camera-sensor-mtp.dtsi | 10 +++--- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 31 ++++++++++++------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909-camera-sensor-mtp.dtsi index a5f2d4087283..3734b2e2f142 100644 --- a/arch/arm/boot/dts/qcom/msm8909-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-camera-sensor-mtp.dtsi @@ -105,13 +105,15 @@ pinctrl-0 = <&cam_sensor_flash_default>; pinctrl-1 = <&cam_sensor_flash_sleep>; gpios = <&msm_gpio 31 0>, - <&msm_gpio 32 0>; + <&msm_gpio 32 0>, + <&msm_gpio 36 0>; qcom,gpio-flash-en = <0>; qcom,gpio-flash-now = <1>; - qcom,gpio-req-tbl-num = <0 1>; - qcom,gpio-req-tbl-flags = <0 0>; + qcom,gpio-flash-rst = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <0 0 0>; qcom,gpio-req-tbl-label = "FLASH_EN", - "FLASH_NOW"; + "FLASH_NOW", "FLASH_RST"; qcom,cci-master = <0>; }; diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index b51ad8be6317..c064cfec9d64 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -137,21 +137,28 @@ }; }; - cam_sensor_flash { - /* FLASH_RESET,FLASH_EN,FLASH_NOW */ - qcom,pins = "gpio 36", "gpio 31", "gpio 32"; - qcom,num-grp-pins = <3>; - qcom,pin-func = <0>; - label = "cam_sensor_flash"; - /* active state */ - cam_sensor_flash_default: default { + cam_sensor_flash_default: cam_sensor_flash_default { + /* FLASH_RESET,FLASH_EN,FLASH_NOW */ + mux { + pins = "gpio36", "gpio31", "gpio32"; + function = "gpio"; + }; + config { + pins = "gpio36", "gpio31", "gpio32"; + bias-disable; /* No PULL */ drive-strength = <2>; /* 2 MA */ - bias-disable = <0>; /* No PULL */ }; - /* suspended state */ - cam_sensor_flash_sleep: sleep { + }; + + cam_sensor_flash_sleep: cam_sensor_flash_sleep { + mux { + pins = "gpio36", "gpio31", "gpio32"; + function = "gpio"; + }; + config { + pins = "gpio36", "gpio31", "gpio32"; + bias-disable; /* No PULL */ drive-strength = <2>; /* 2 MA */ - bias-pull-down = <0>; /* PULL DOWN */ }; }; -- GitLab From 634a896732715701a542e5c9b376f96e10815891 Mon Sep 17 00:00:00 2001 From: Gaurav Singhal Date: Tue, 17 Oct 2017 12:40:45 +0530 Subject: [PATCH 1229/5498] ARM: dts: msm: Add NFC device node for 8909w BG2 Device node changes required on 8909w BG2 HW describing the GPIO configuration for Nfc controller chip. Change-Id: If953dacf46daa97ef2c3cb7ec1630a6cc613299b Signed-off-by: Gaurav Singhal --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 19 +++++++- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 48 +++++++++++++++++++ arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 19 +++++++- .../arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 9 ++++ 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index eec1f068d073..065ae3e42e99 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -139,7 +139,24 @@ }; &i2c_1 { - status = "disabled"; + status = "okay"; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&msm_gpio 50 0x00>; + qcom,nq-ven = <&msm_gpio 36 0x00>; + qcom,nq-firm = <&msm_gpio 38 0x00>; + qcom,nq-esepwr = <&msm_gpio 49 0x00>; + qcom,nq-clkreq = <&pm660_gpios 4 0x00>; + qcom,clk-src = "BBCLK3"; + interrupt-parent = <&msm_gpio>; + interrupts = <50 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active","nfc_suspend"; + pinctrl-0 = <&nfcw_int_active &nfcw_disable_active>; + pinctrl-1 = <&nfcw_int_suspend &nfcw_disable_suspend>; + clock-names = "ref_clk"; + }; }; &spi_0 { diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index b51ad8be6317..d932b1f71a6c 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -514,6 +514,54 @@ }; nfc { + nfcw_int_active: nfcw_int_active { + mux { + pins = "gpio50"; + function = "gpio"; + }; + config { + pins = "gpio50"; + drive-strength = <6>; + bias-pull-up; + }; + }; + + nfcw_int_suspend: nfcw_int_suspend { + mux { + pins = "gpio50"; + function = "gpio"; + }; + config { + pins = "gpio50"; + drive-strength = <6>; + bias-pull-up; + }; + }; + + nfcw_disable_active: nfcw_disable_active { + mux { + pins = "gpio36"; + function = "gpio"; + }; + config { + pins = "gpio36"; + drive-strength = <6>; + bias-pull-up; + }; + }; + + nfcw_disable_suspend: nfcw_disable_suspend { + mux { + pins = "gpio36"; + function = "gpio"; + }; + config { + pins = "gpio36"; + drive-strength = <6>; + bias-disable; + }; + }; + nfc_int_active: nfc_int_active { mux { pins = "gpio21"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 966eece29a49..1f60271bc1ed 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -169,7 +169,24 @@ }; &i2c_1 { - status = "disabled"; + status = "okay"; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&msm_gpio 50 0x00>; + qcom,nq-ven = <&msm_gpio 36 0x00>; + qcom,nq-firm = <&msm_gpio 38 0x00>; + qcom,nq-esepwr = <&msm_gpio 49 0x00>; + qcom,nq-clkreq = <&pm660_gpios 4 0x00>; + qcom,clk-src = "BBCLK3"; + interrupt-parent = <&msm_gpio>; + interrupts = <50 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active","nfc_suspend"; + pinctrl-0 = <&nfcw_int_active &nfcw_disable_active>; + pinctrl-1 = <&nfcw_int_suspend &nfcw_disable_suspend>; + clock-names = "ref_clk"; + }; }; &spi_0 { diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index c1f83b2d77f3..e0b156703f23 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -216,6 +216,15 @@ }; &pm660_gpios { + /* GPIO 4 (NFC_CLK_REQ) */ + gpio@c300 { + qcom,mode = <0>; + qcom,vin-sel = <1>; + qcom,src-sel = <0>; + qcom,master-en = <1>; + status = "okay"; + }; + gpio@cb00 { status = "ok"; qcom,mode = <1>; -- GitLab From 79255eb0b61b6886ce2383f0e799777ef07f1c44 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 15 Sep 2017 19:05:13 +0530 Subject: [PATCH 1230/5498] ASoC: msm: add machine driver for BG codec Add machine driver for BG codec platform with the required dailinks. TDM interface is used to communicate with BG codec. Change-Id: I08eae7a357429207aae8b61eba531c9f9f9c19f1 Signed-off-by: Yeleswarapu Nagaradhesh Signed-off-by: Ashish Jain --- .../bindings/sound/qcom-audio-dev.txt | 97 + sound/soc/msm/Kconfig | 2 + sound/soc/msm/Makefile | 2 +- sound/soc/msm/msm_bg.c | 2223 +++++++++++++++++ 4 files changed, 2323 insertions(+), 1 deletion(-) create mode 100644 sound/soc/msm/msm_bg.c diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index be9df49293f5..d97a889d9380 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2064,6 +2064,103 @@ Example: qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight"; }; +* MSM8909 BG ASoC Machine driver + +Required properties: +- compatible : "qcom,msm-bg-audio-codec" +- qcom,model : The user-visible name of this sound card. +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets + mentioned in qcom,msm-gpios. Say we have 2^N combinations for N GPIOs, + this would list all the 2^N combinations. +- pinctrl-names : The combinations of gpio sets from above that are supported in + the flavor. This can be sometimes same as qcom,pinctrl-names i.e with 2^N + combinations or will have less incase if some combination is not supported. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. +- qcom,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. + +Optional properties: +- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given + in "asoc-cpu". The cpu names are in the form of "%s.%d" form, + where the id (%d) field represents the back-end AFE port id that + this CPU dai is associated with. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". + +Example: + sound { + status = "disabled"; + compatible = "qcom,msm-bg-audio-codec"; + qcom,model = "msm-bg-snd-card"; + reg = <0x7702000 0x4>, + <0x7702004 0x4>, + <0x7702008 0x4>, + <0x770200c 0x4>; + reg-names = "csr_gp_io_mux_mic_ctl", + "csr_gp_io_mux_spkr_ctl", + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel", + "csr_gp_io_lpaif_sec_pcm_sec_mode_muxsel"; + qcom,msm-snd-card-id = <0>; + qcom,msm-ext-pa = "primary"; + qcom,tdm-audio-intf; + qcom,msm-afe-clk-ver = <1>; + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>, + <&voice_svc>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-pcm-lpa", + "msm-voice-svc"; + asoc-cpu = <&dai_pri_auxpcm>, + <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, + <&dai_mi2s3>, <&dai_mi2s5>, <&dai_mi2s6>, + <&bt_sco_rx>, <&bt_sco_tx>, <&bt_a2dp_rx>, + <&int_fm_rx>, <&int_fm_tx>, <&afe_pcm_rx>, + <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, + <&incall_music_rx>, <&incall_music_2_rx>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>, + <&dai_pri_tdm_rx_1>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_tx_2>, + <&dai_pri_tdm_rx_3>, <&dai_pri_tdm_tx_3>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-mi2s.5", "msm-dai-q6-mi2s.6", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12290", "msm-dai-q6-dev.12292", + "msm-dai-q6-dev.12293", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770", "msm-dai-q6-tdm.36864", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36867", "msm-dai-q6-tdm.36868", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36870", + "msm-dai-q6-tdm.36871"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + }; + + + * MDM9607 ASoC Machine driver Required properties: diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig index 8c17ee1b56ee..f7db6b7a98d9 100644 --- a/sound/soc/msm/Kconfig +++ b/sound/soc/msm/Kconfig @@ -273,6 +273,8 @@ config SND_SOC_MSM8909 select SND_SOC_WCD9335 select SND_SOC_CPE select SND_HWDEP + select MSM_BG_GLINK + select MSM_BG_CODEC select QTI_PP help To add support for SoC audio on MSM8909 boards. diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile index cf5f357caab8..7a16dca8b4a3 100644 --- a/sound/soc/msm/Makefile +++ b/sound/soc/msm/Makefile @@ -44,7 +44,7 @@ snd-soc-mdm9607-objs := mdm9607.o obj-$(CONFIG_SND_SOC_MDM9607) += snd-soc-mdm9607.o # for MSM 8909 sound card driver -snd-soc-msm8909-objs := msm8952.o msm-audio-pinctrl.o apq8009-i2s-ext-codec.o +snd-soc-msm8909-objs := msm8952.o msm-audio-pinctrl.o apq8009-i2s-ext-codec.o msm_bg.o obj-$(CONFIG_SND_SOC_MSM8909) += snd-soc-msm8909.o # for MDM9640 sound card driver diff --git a/sound/soc/msm/msm_bg.c b/sound/soc/msm/msm_bg.c new file mode 100644 index 000000000000..b190680d7ae5 --- /dev/null +++ b/sound/soc/msm/msm_bg.c @@ -0,0 +1,2223 @@ + +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt "\n", __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" + + +#define DRV_NAME "msm-bg-asoc-wcd" + +#define BTSCO_RATE_8KHZ 8000 +#define BTSCO_RATE_16KHZ 16000 + +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_192KHZ 192000 + +#define PRI_MI2S_ID (1 << 0) +#define SEC_MI2S_ID (1 << 1) +#define TER_MI2S_ID (1 << 2) +#define QUAT_MI2S_ID (1 << 3) +#define QUIN_MI2S_ID (1 << 4) + +#define DEFAULT_MCLK_RATE 9600000 + +#define TDM_SLOT_OFFSET_MAX 4 + +enum btsco_rates { + RATE_8KHZ_ID, + RATE_16KHZ_ID, +}; + +enum { + PRIMARY_TDM_RX_0, + PRIMARY_TDM_RX_1, + PRIMARY_TDM_RX_2, + PRIMARY_TDM_RX_3, + PRIMARY_TDM_TX_0, + PRIMARY_TDM_TX_1, + PRIMARY_TDM_TX_2, + PRIMARY_TDM_TX_3, + TDM_MAX, +}; + +static int msm_btsco_rate = BTSCO_RATE_8KHZ; +static int msm_proxy_rx_ch = 2; + +/* TDM default channels */ +static int msm_pri_tdm_rx_0_ch = 1; +static int msm_pri_tdm_tx_0_ch = 1; + +static int msm_sec_tdm_rx_0_ch = 4; +static int msm_sec_tdm_tx_0_ch = 4; + +/* TDM default bit format */ +static int msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +static int msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +/* TDM default sampling rate */ +static int msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ; +static int msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ; + +static int msm_sec_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ; +static int msm_sec_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ; + +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_16", "KHZ_48"}; + +/* TDM default offset for individual MIC */ +static unsigned int tdm_slot_offset[TDM_MAX][TDM_SLOT_OFFSET_MAX] = { + /* PRI_TDM_RX */ + {0, 0xFFFF}, + {2, 0xFFFF}, + {4, 0xFFFF}, + {6, 0xFFFF}, + /* PRI_TDM_TX */ + {0, 0xFFFF}, + {2, 0xFFFF}, + {4, 0xFFFF}, + {6, 0xFFFF}, +}; + +/* TDM default offset for stereo MIC */ +static unsigned int tdm_slot_offset1[TDM_MAX][TDM_SLOT_OFFSET_MAX] = { + /* PRI_TDM_RX */ + {0, 0xFFFF}, + {4, 0xFFFF}, + {8, 0xFFFF}, + {12, 0xFFFF}, + /* PRI_TDM_TX */ + {0, 0xFFFF}, + {4, 0xFFFF}, + {8, 0xFFFF}, + {0xFFFF}, +}; + +static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static const char *const loopback_mclk_text[] = {"DISABLE", "ENABLE"}; +static const char *const btsco_rate_text[] = {"BTSCO_RATE_8KHZ", + "BTSCO_RATE_16KHZ"}; +static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; + + +struct msm8916_asoc_mach_data { + int ext_pa; + int afe_clk_ver; + void __iomem *vaddr_gpio_mux_spkr_ctl; + void __iomem *vaddr_gpio_mux_mic_ctl; + void __iomem *vaddr_gpio_mux_quin_ctl; + void __iomem *vaddr_gpio_mux_pcm_ctl; + void __iomem *vaddr_gpio_mux_sec_pcm_ctl; + struct device_node *pri_mi2s_gpio_p; +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static const struct snd_soc_dapm_widget msm_bg_dapm_widgets[] = { + + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), +}; + +static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_proxy_rx_ch = %d", msm_proxy_rx_ch); + ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1; + return 0; +} + +static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("msm_proxy_rx_ch = %d", msm_proxy_rx_ch); + return 0; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("enter"); + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + +static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_btsco_rate = %d", msm_btsco_rate); + ucontrol->value.integer.value[0] = msm_btsco_rate; + return 0; +} + +static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case RATE_8KHZ_ID: + msm_btsco_rate = BTSCO_RATE_8KHZ; + break; + case RATE_16KHZ_ID: + msm_btsco_rate = BTSCO_RATE_16KHZ; + break; + default: + msm_btsco_rate = BTSCO_RATE_8KHZ; + break; + } + + pr_debug("msm_btsco_rate = %d", msm_btsco_rate); + return 0; +} + +static int msm_pri_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_pri_tdm_rx_0_ch = %d", + msm_pri_tdm_rx_0_ch); + ucontrol->value.integer.value[0] = msm_pri_tdm_rx_0_ch - 1; + return 0; +} + +static int msm_pri_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_pri_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("msm_pri_tdm_rx_0_ch = %d", + msm_pri_tdm_rx_0_ch); + return 0; +} + +static int msm_pri_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_pri_tdm_tx_0_ch = %d", + msm_pri_tdm_tx_0_ch); + ucontrol->value.integer.value[0] = msm_pri_tdm_tx_0_ch - 1; + return 0; +} + +static int msm_pri_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_pri_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("msm_pri_tdm_tx_0_ch = %d", + msm_pri_tdm_tx_0_ch); + return 0; +} + +static int msm_sec_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_sec_tdm_rx_0_ch = %d", + msm_sec_tdm_rx_0_ch); + ucontrol->value.integer.value[0] = msm_sec_tdm_rx_0_ch - 1; + return 0; +} + +static int msm_sec_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_sec_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("msm_sec_tdm_rx_0_ch = %d", + msm_sec_tdm_rx_0_ch); + return 0; +} + +static int msm_sec_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_sec_tdm_tx_0_ch = %d", + msm_sec_tdm_tx_0_ch); + ucontrol->value.integer.value[0] = msm_sec_tdm_tx_0_ch - 1; + return 0; +} + +static int msm_sec_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_sec_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("msm_sec_tdm_tx_0_ch = %d", + msm_sec_tdm_tx_0_ch); + return 0; +} + +static int msm_pri_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_rx_0_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("msm_pri_tdm_rx_0_bit_format = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("msm_pri_tdm_rx_0_bit_format = %d", + msm_pri_tdm_rx_0_bit_format); + return 0; +} + +static int msm_pri_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_tx_0_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("msm_pri_tdm_tx_0_bit_format = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("msm_pri_tdm_tx_0_bit_format = %d", + msm_pri_tdm_tx_0_bit_format); + return 0; +} + +static int msm_sec_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_rx_0_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("msm_sec_tdm_rx_0_bit_format = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_sec_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("msm_sec_tdm_rx_0_bit_format = %d", + msm_sec_tdm_rx_0_bit_format); + return 0; +} + +static int msm_sec_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_tx_0_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("msm_sec_tdm_tx_0_bit_format = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("msm_sec_tdm_tx_0_bit_format = %d", + msm_sec_tdm_tx_0_bit_format); + return 0; +} + +static int msm_pri_tdm_rx_0_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_rx_0_sample_rate) { + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 0; + break; + case SAMPLING_RATE_48KHZ: + default: + ucontrol->value.integer.value[0] = 1; + break; + } + pr_debug("msm_pri_tdm_rx_0_sample_rate = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_rx_0_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + default: + msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("msm_pri_tdm_rx_0_sample_rate = %d", + msm_pri_tdm_rx_0_sample_rate); + return 0; +} + +static int msm_sec_tdm_rx_0_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_rx_0_sample_rate) { + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 0; + break; + case SAMPLING_RATE_48KHZ: + default: + ucontrol->value.integer.value[0] = 1; + break; + } + pr_debug("msm_sec_tdm_rx_0_sample_rate = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_rx_0_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_sec_tdm_rx_0_sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + default: + msm_sec_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("msm_sec_tdm_rx_0_sample_rate = %d", + msm_sec_tdm_rx_0_sample_rate); + return 0; +} + +static int msm_pri_tdm_tx_0_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_tx_0_sample_rate) { + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 0; + break; + case SAMPLING_RATE_48KHZ: + default: + ucontrol->value.integer.value[0] = 1; + break; + } + pr_debug("msm_pri_tdm_tx_0_sample_rate = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_tx_0_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + default: + msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("msm_pri_tdm_tx_0_sample_rate = %d", + msm_pri_tdm_tx_0_sample_rate); + return 0; +} + +static int msm_sec_tdm_tx_0_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_sec_tdm_tx_0_sample_rate) { + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 0; + break; + case SAMPLING_RATE_48KHZ: + default: + ucontrol->value.integer.value[0] = 1; + break; + } + pr_debug("msm_sec_tdm_tx_0_sample_rate = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_sec_tdm_tx_0_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_sec_tdm_tx_0_sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + default: + msm_sec_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("msm_sec_tdm_tx_0_sample_rate = %d", + msm_sec_tdm_tx_0_sample_rate); + return 0; +} + +static const struct soc_enum msm_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_bit_format_text), + rx_bit_format_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(btsco_rate_text), + btsco_rate_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(proxy_rx_ch_text), + proxy_rx_ch_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_ch_text), + tdm_ch_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_bit_format_text), + tdm_bit_format_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_sample_rate_text), + tdm_sample_rate_text), +}; + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_snd_enum[1], + msm_btsco_rate_get, msm_btsco_rate_put), + SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[2], + msm_proxy_rx_ch_get, msm_proxy_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", msm_snd_enum[3], + msm_pri_tdm_rx_0_ch_get, msm_pri_tdm_rx_0_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", msm_snd_enum[3], + msm_pri_tdm_tx_0_ch_get, msm_pri_tdm_tx_0_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", msm_snd_enum[3], + msm_sec_tdm_rx_0_ch_get, msm_sec_tdm_rx_0_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", msm_snd_enum[3], + msm_sec_tdm_tx_0_ch_get, msm_sec_tdm_tx_0_ch_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Bit Format", msm_snd_enum[4], + msm_pri_tdm_rx_0_bit_format_get, + msm_pri_tdm_rx_0_bit_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Bit Format", msm_snd_enum[4], + msm_pri_tdm_tx_0_bit_format_get, + msm_pri_tdm_tx_0_bit_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Bit Format", msm_snd_enum[4], + msm_sec_tdm_rx_0_bit_format_get, + msm_sec_tdm_rx_0_bit_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Bit Format", msm_snd_enum[4], + msm_sec_tdm_tx_0_bit_format_get, + msm_sec_tdm_tx_0_bit_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", msm_snd_enum[5], + msm_pri_tdm_rx_0_sample_rate_get, + msm_pri_tdm_rx_0_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", msm_snd_enum[5], + msm_pri_tdm_tx_0_sample_rate_get, + msm_pri_tdm_tx_0_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", msm_snd_enum[5], + msm_sec_tdm_rx_0_sample_rate_get, + msm_sec_tdm_rx_0_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", msm_snd_enum[5], + msm_sec_tdm_tx_0_sample_rate_get, + msm_sec_tdm_tx_0_sample_rate_put), +}; + + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + channels->min = channels->max = msm_pri_tdm_rx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_pri_tdm_rx_0_bit_format); + rate->min = rate->max = msm_pri_tdm_rx_0_sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + channels->min = channels->max = msm_pri_tdm_tx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_pri_tdm_tx_0_bit_format); + rate->min = rate->max = msm_pri_tdm_tx_0_sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + channels->min = channels->max = msm_sec_tdm_rx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_sec_tdm_rx_0_bit_format); + rate->min = rate->max = msm_sec_tdm_rx_0_sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + channels->min = channels->max = msm_sec_tdm_tx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_sec_tdm_tx_0_bit_format); + rate->min = rate->max = msm_sec_tdm_tx_0_sample_rate; + break; + default: + pr_err("dai id 0x%x not supported", cpu_dai->id); + return -EINVAL; + } + + pr_debug("dai id = 0x%x channels = %d rate = %d", + cpu_dai->id, channels->max, rate->max); + + return 0; +} + +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots, unsigned int tdm_slot_offset2[][TDM_SLOT_OFFSET_MAX]) +{ + unsigned int slot_mask = 0; + int upper, lower, i, j; + unsigned int *slot_offset; + + switch (port_id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + lower = PRIMARY_TDM_RX_0; + upper = PRIMARY_TDM_RX_3; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + lower = PRIMARY_TDM_TX_0; + upper = PRIMARY_TDM_TX_3; + break; + default: + return slot_mask; + } + + for (i = lower; i <= upper; i++) { + slot_offset = tdm_slot_offset2[i]; + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + pr_debug("j=%d slot_offst[j]= %x", j, slot_offset[j]); + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) { + /* + * set the mask of active slot according to + * the offset table for the group of devices + */ + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + pr_debug("slot_mask = %x", slot_mask); + } else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels; + int slot_width = 16; + int slots = 0; + unsigned int slot_mask = 0; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + unsigned int tdm_slot_offset2[TDM_MAX][TDM_SLOT_OFFSET_MAX]; + + pr_debug("dai id = 0x%x", cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 2: + /* If no.of channels is 2 in record session then use + * tdm_slot_offset1 + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + memcpy(tdm_slot_offset2, tdm_slot_offset1, + sizeof(tdm_slot_offset2)); + else + memcpy(tdm_slot_offset2, tdm_slot_offset, + sizeof(tdm_slot_offset2)); + break; + case 1: + case 3: + case 4: + case 6: + case 8: + memcpy(tdm_slot_offset2, tdm_slot_offset, + sizeof(tdm_slot_offset2)); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channel HW configuration should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 16; + break; + default: + pr_err("invalid param format 0x%x", + params_format(params)); + return -EINVAL; + } + slots = 4; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, slots, tdm_slot_offset2); + if (!slot_mask) { + pr_err("invalid slot_mask 0x%x", slot_mask); + return -EINVAL; + } + break; + default: + pr_err("invalid param channels %d", channels); + return -EINVAL; + } + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_RX_0]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_RX_1]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_RX_2]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_RX_3]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_TX_0]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_TX_1]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_TX_2]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + slot_offset = tdm_slot_offset2[PRIMARY_TDM_TX_3]; + break; + default: + pr_err("dai id 0x%x not supported", cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("slot offset not supported, offset_channels %d", + offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("channels %d exceed offset_channels %d", + channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("failed to set tdm slot, err:%d", ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("failed to set channel map, err:%d", ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("failed to set tdm slot, err:%d", ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("failed to set channel map, err:%d", ret); + goto end; + } + } + +end: + return ret; +} + +static int msm_tdm_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm8916_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0, val = 0; + + pr_debug("substream = %s stream = %d", + substream->name, substream->stream); + pr_debug("dai id = 0x%x", cpu_dai->id); + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + /* Configure mux for Primary TDM */ + if (pdata->vaddr_gpio_mux_pcm_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); + val = val | 0x00000001; + iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); + } else { + return -EINVAL; + } + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + /*0x02020002 Use this value for master mode*/ + val = val | 0x1808000; /*this is for slave mode*/ + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } else { + return -EINVAL; + } + break; + default: + pr_err("dai id 0x%x not supported", cpu_dai->id); + break; + return -EINVAL; + } + return ret; +} + +static void msm_tdm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm8916_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int val = 0; + + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + /* Reset Configuration of mux for Primary TDM */ + if (pdata->vaddr_gpio_mux_pcm_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); + val = val & (~0x00000001); + iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); + } else { + return; + } + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + /*0x02020002 Use this value for master mode*/ + val = val & (~0x1808000); /*this is for slave mode*/ + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } else { + return; + } + break; + default: + break; + } +} + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + pr_debug("dev_name%s", dev_name(cpu_dai->dev)); + + snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + return 0; +} + +static struct snd_soc_ops msm_tdm_be_ops = { + .startup = msm_tdm_startup, + .hw_params = msm_tdm_snd_hw_params, + .shutdown = msm_tdm_shutdown, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_bg_dai[] = { + /* FrontEnd DAI Links */ + {/* hw:x,0 */ + .name = "MSM8952 Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + {/* hw:x,1 */ + .name = "MSM8952 Media2", + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + {/* hw:x,2 */ + .name = "Circuit-Switch Voice", + .stream_name = "CS-Voice", + .cpu_dai_name = "CS-VOICE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_CS_VOICE, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,3 */ + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_VOIP, + }, + {/* hw:x,4 */ + .name = "MSM8X16 ULL", + .stream_name = "ULL", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + {/* hw:x,5 */ + .name = "Primary MI2S_RX Hostless", + .stream_name = "Primary MI2S_RX Hostless", + .cpu_dai_name = "PRI_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,6 */ + .name = "INT_FM Hostless", + .stream_name = "INT_FM Hostless", + .cpu_dai_name = "INT_FM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,7 */ + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + {/* hw:x,8 */ + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + {/* hw:x,9 */ + .name = "MSM8952 Compress1", + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + {/* hw:x,10 */ + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,11 */ + .name = "Tertiary MI2S_TX Hostless", + .stream_name = "Tertiary MI2S_TX Hostless", + .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,12 */ + .name = "MSM8x16 LowLatency", + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + }, + {/* hw:x,13 */ + .name = "Voice2", + .stream_name = "Voice2", + .cpu_dai_name = "Voice2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICE2, + }, + {/* hw:x,14 */ + .name = "MSM8x16 Media9", + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* This dailink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { /* hw:x,15 */ + .name = "VoLTE", + .stream_name = "VoLTE", + .cpu_dai_name = "VoLTE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOLTE, + }, + { /* hw:x,16 */ + .name = "VoWLAN", + .stream_name = "VoWLAN", + .cpu_dai_name = "VoWLAN", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOWLAN, + }, + {/* hw:x,17 */ + .name = "INT_HFP_BT Hostless", + .stream_name = "INT_HFP_BT Hostless", + .cpu_dai_name = "INT_HFP_BT_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,18 */ + .name = "MSM8916 HFP", + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + /* LSM FE */ + {/* hw:x,19 */ + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM1, + }, + {/* hw:x,20 */ + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM2, + }, + {/* hw:x,21 */ + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM3, + }, + {/* hw:x,22 */ + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM4, + }, + {/* hw:x,23 */ + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM5, + }, + { /* hw:x,24 */ + .name = "MSM8X16 Compress2", + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { /* hw:x,25 */ + .name = "QUAT_MI2S Hostless", + .stream_name = "QUAT_MI2S Hostless", + .cpu_dai_name = "QUAT_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,27 */ + .name = "MSM8X16 Compress3", + .stream_name = "Compress3", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + {/* hw:x,28 */ + .name = "MSM8X16 Compress4", + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + {/* hw:x,29 */ + .name = "MSM8X16 Compress5", + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + {/* hw:x,30 */ + .name = "MSM8X16 Compress6", + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + {/* hw:x,31 */ + .name = "MSM8X16 Compress7", + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + {/* hw:x,32 */ + .name = "MSM8X16 Compress8", + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + {/* hw:x,33 */ + .name = "MSM8X16 Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + {/* hw:x,34 */ + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + {/* hw:x,35 */ + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + {/* hw:x,36 */ + .name = "MSM8916 HFP Loopback2", + .stream_name = "MultiMedia8", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + {/* hw:x,37 */ + .name = "QCHAT", + .stream_name = "QCHAT", + .cpu_dai_name = "QCHAT", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_QCHAT, + }, + {/* hw:x,38 */ + .name = "MSM8X16 Compress10", + .stream_name = "Compress10", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, + {/* hw:x,39 */ + .name = "MSM8X16 Compress11", + .stream_name = "Compress11", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, + {/* hw:x,40 */ + .name = "MSM8X16 Compress12", + .stream_name = "Compress12", + .cpu_dai_name = "MultiMedia19", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA19, + }, +}; + +static struct snd_soc_dai_link msm_bg_tdm_fe_dai[] = { + /* FE TDM DAI links */ + { + .name = "Primary TDM RX 0 Hostless", + .stream_name = "Primary TDM RX 0 Hostless", + .cpu_dai_name = "PRI_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .init = &msm_audrx_init, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Primary TDM TX 0 Hostless", + .stream_name = "Primary TDM TX 0 Hostless", + .cpu_dai_name = "PRI_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary TDM RX 0 Hostless", + .stream_name = "Secondary TDM RX 0 Hostless", + .cpu_dai_name = "SEC_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary TDM TX 0 Hostless", + .stream_name = "Secondary TDM TX 0 Hostless", + .cpu_dai_name = "SEC_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_bg_tdm_be_dai[] = { + /* TDM be dai links */ + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_1, + .stream_name = "Primary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36866", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_1, + .stream_name = "Primary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36867", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_tx2", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_2, + .stream_name = "Primary TDM2 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36868", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_2, + .stream_name = "Primary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36869", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_3, + .stream_name = "Primary TDM3 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36870", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_3, + .stream_name = "Primary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36871", + .platform_name = "msm-pcm-routing", + .codec_name = "soc:bg_codec", + .codec_dai_name = "bg_cdc_tx4", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_bg_split_a2dp_dai_link[] = { + { + .name = LPASS_BE_INT_BT_A2DP_RX, + .stream_name = "Internal BT-A2DP Playback", + .cpu_dai_name = "msm-dai-q6-dev.12290", + .platform_name = "msm-pcm-routing", + .codec_dai_name = "msm-stub-rx", + .codec_name = "msm-stub-codec.1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_INT_BT_A2DP_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_bg_dai_links[ +ARRAY_SIZE(msm_bg_dai) + +ARRAY_SIZE(msm_bg_tdm_fe_dai) + +ARRAY_SIZE(msm_bg_tdm_be_dai) + +ARRAY_SIZE(msm_bg_split_a2dp_dai_link)]; + +static struct snd_soc_card bear_card = { + /* snd_soc_card_msm_bg */ + .name = "msm_bg-snd-card", + .dai_link = msm_bg_dai, + .num_links = ARRAY_SIZE(msm_bg_dai), +}; + +static int msm_bg_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *phandle; + + if (!cdev) { + pr_err("Sound card device memory NULL"); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("No match found for platform name: %s", + dai_link[i].platform_name); + ret = index; + goto cpu_dai; + } + phandle = of_parse_phandle(cdev->of_node, + "asoc-platform", + index); + if (!phandle) { + pr_err("retrieving phandle for platform %s, index %d failed", + dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = phandle; + dai_link[i].platform_name = NULL; + } +cpu_dai: + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index < 0) + goto codec_dai; + phandle = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!phandle) { + pr_err("retrieving phandle for cpu dai %s failed", + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = phandle; + dai_link[i].cpu_dai_name = NULL; + } +codec_dai: + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + phandle = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!phandle) { + pr_err("retrieving phandle for codec dai %s failed", + dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = phandle; + dai_link[i].codec_name = NULL; + } + } +err: + return ret; +} + +static struct snd_soc_card *msm_bg_populate_sndcard_dailinks( + struct device *dev) +{ + struct snd_soc_card *card = &bear_card; + struct snd_soc_dai_link *dailink; + int len1; + + card->name = dev_name(dev); + len1 = ARRAY_SIZE(msm_bg_dai); + memcpy(msm_bg_dai_links, msm_bg_dai, sizeof(msm_bg_dai)); + dailink = msm_bg_dai_links; + + if (of_property_read_bool(dev->of_node, + "qcom,tdm-audio-intf")) { + memcpy(dailink + len1, msm_bg_tdm_fe_dai, + sizeof(msm_bg_tdm_fe_dai)); + len1 += ARRAY_SIZE(msm_bg_tdm_fe_dai); + memcpy(dailink + len1, msm_bg_tdm_be_dai, + sizeof(msm_bg_tdm_be_dai)); + len1 += ARRAY_SIZE(msm_bg_tdm_be_dai); + } + + if (of_property_read_bool(dev->of_node, + "qcom,split-a2dp")) { + dev_dbg(dev, "%s: split a2dp support present\n", __func__); + memcpy(dailink + len1, msm_bg_split_a2dp_dai_link, + sizeof(msm_bg_split_a2dp_dai_link)); + len1 += ARRAY_SIZE(msm_bg_split_a2dp_dai_link); + } + card->dai_link = dailink; + card->num_links = len1; + return card; +} + +static int msm_bg_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm8916_asoc_mach_data *pdata = NULL; + const char *ext_pa = "qcom,msm-ext-pa"; + int num_strings; + int ret, /*id,*/ val; + struct resource *muxsel; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_mux_mic_ctl"); + if (!muxsel) { + dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); + ret = -ENODEV; + goto err1; + } + pdata->vaddr_gpio_mux_mic_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_mic_ctl == NULL) { + pr_err("ioremap failure for muxsel virt addr"); + ret = -ENOMEM; + goto err1; + } + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_mux_spkr_ctl"); + if (!muxsel) { + dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); + ret = -ENODEV; + goto err; + } + pdata->vaddr_gpio_mux_spkr_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_spkr_ctl == NULL) { + pr_err("ioremap failure for muxsel virt addr"); + ret = -ENOMEM; + goto err; + } + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel"); + if (!muxsel) { + dev_err(&pdev->dev, "pri pcm addr invalid for MI2S\n"); + ret = -ENODEV; + goto err; + } + pdata->vaddr_gpio_mux_pcm_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_pcm_ctl == NULL) { + pr_err("ioremap failure for muxsel virt addr"); + ret = -ENOMEM; + goto err; + } + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_lpaif_sec_pcm_sec_mode_muxsel"); + if (!muxsel) { + dev_err(&pdev->dev, "sec pcm addr invalid for MI2S\n"); + ret = -ENODEV; + goto err; + } + pdata->vaddr_gpio_mux_sec_pcm_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_sec_pcm_ctl == NULL) { + pr_err("ioremap failure for sec pcm muxsel virt addr"); + ret = -ENOMEM; + goto err; + } + + pdata->pri_mi2s_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,pri-mi2s-gpios", 0); + if (!pdata->pri_mi2s_gpio_p) + dev_err(&pdev->dev, " invalid phandle pri-mi2s-gpios\n"); + + card = msm_bg_populate_sndcard_dailinks(&pdev->dev); + dev_err(&pdev->dev, "default codec configured\n"); + num_strings = of_property_count_strings(pdev->dev.of_node, + ext_pa); + if (num_strings < 0) { + dev_err(&pdev->dev, + "%s: missing %s in dt node or length is incorrect\n", + __func__, ext_pa); + ret = -EINVAL; + goto err; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-afe-clk-ver", &val); + if (ret) + pdata->afe_clk_ver = AFE_CLK_VERSION_V2; + else + pdata->afe_clk_ver = val; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) + goto err; + + ret = msm_bg_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + return 0; +err: + if (pdata->vaddr_gpio_mux_spkr_ctl) + iounmap(pdata->vaddr_gpio_mux_spkr_ctl); + if (pdata->vaddr_gpio_mux_mic_ctl) + iounmap(pdata->vaddr_gpio_mux_mic_ctl); + if (pdata->vaddr_gpio_mux_pcm_ctl) + iounmap(pdata->vaddr_gpio_mux_pcm_ctl); + if (pdata->vaddr_gpio_mux_sec_pcm_ctl) + iounmap(pdata->vaddr_gpio_mux_sec_pcm_ctl); +err1: + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_bg_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm8916_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->vaddr_gpio_mux_spkr_ctl) + iounmap(pdata->vaddr_gpio_mux_spkr_ctl); + if (pdata->vaddr_gpio_mux_mic_ctl) + iounmap(pdata->vaddr_gpio_mux_mic_ctl); + if (pdata->vaddr_gpio_mux_pcm_ctl) + iounmap(pdata->vaddr_gpio_mux_pcm_ctl); + if (pdata->vaddr_gpio_mux_sec_pcm_ctl) + iounmap(pdata->vaddr_gpio_mux_sec_pcm_ctl); + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id msm_bg_asoc_machine_of_match[] = { + { .compatible = "qcom,msm-bg-audio-codec", }, + {}, +}; + +static struct platform_driver msm_bg_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = msm_bg_asoc_machine_of_match, + }, + .probe = msm_bg_asoc_machine_probe, + .remove = msm_bg_asoc_machine_remove, +}; + +static int __init msm_bg_machine_init(void) +{ + return platform_driver_register(&msm_bg_asoc_machine_driver); +} +late_initcall(msm_bg_machine_init); + +static void __exit msm_bg_machine_exit(void) +{ + return platform_driver_unregister(&msm_bg_asoc_machine_driver); +} +module_exit(msm_bg_machine_exit); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, msm_bg_asoc_machine_of_match); -- GitLab From 8289585837b5e38bdacd03058a15e3c13af2eda7 Mon Sep 17 00:00:00 2001 From: Samyukta Mogily Date: Thu, 31 Aug 2017 19:50:40 +0530 Subject: [PATCH 1231/5498] msm: camera: Sensor driver changes to enable flash on 8909 MTP Addition of flash related files and drivers, to enable i2c flash on 8909MTP. Change-Id: I581598a00cf5a237a1f6de970377ac9440e52650 Signed-off-by: Samyukta Mogily --- .../msm/camera_v2/sensor/flash/Makefile | 5 + .../msm/camera_v2/sensor/flash/adp1660.c | 217 +++++ .../msm/camera_v2/sensor/flash/msm_flash.c | 110 +++ .../camera_v2/sensor/flash/msm_led_flash.c | 128 +++ .../camera_v2/sensor/flash/msm_led_flash.h | 97 ++ .../sensor/flash/msm_led_i2c_trigger.c | 883 ++++++++++++++++++ .../camera_v2/sensor/flash/msm_led_torch.c | 82 ++ .../camera_v2/sensor/flash/msm_led_trigger.c | 329 +++++++ .../camera_v2/sensor/io/msm_camera_dt_util.c | 94 +- .../camera_v2/sensor/io/msm_camera_dt_util.h | 6 +- .../msm/camera_v2/sensor/msm_sensor.c | 3 +- .../msm/camera_v2/sensor/msm_sensor_driver.c | 44 + include/soc/qcom/camera2.h | 5 +- 13 files changed, 1998 insertions(+), 5 deletions(-) create mode 100644 drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c create mode 100644 drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c create mode 100644 drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h create mode 100644 drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_i2c_trigger.c create mode 100644 drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_torch.c create mode 100644 drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile b/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile index 6a28da5926c7..61301355558d 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/Makefile @@ -3,3 +3,8 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2 ccflags-y += -Idrivers/media/platform/msm/camera_v2/common ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io obj-$(CONFIG_MSMB_CAMERA) += msm_flash.o +obj-$(CONFIG_MSMB_CAMERA) += msm_led_flash.o +obj-$(CONFIG_MSMB_CAMERA) += msm_led_trigger.o +obj-$(CONFIG_MSMB_CAMERA) += msm_led_i2c_trigger.o +obj-$(CONFIG_MSMB_CAMERA) += adp1660.o +obj-$(CONFIG_MSMB_CAMERA) += msm_led_torch.o \ No newline at end of file diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c b/drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c new file mode 100644 index 000000000000..8606fe4441a4 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c @@ -0,0 +1,217 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include "msm_led_flash.h" + +#define FLASH_NAME "qcom,led-flash" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +static struct msm_led_flash_ctrl_t fctrl; +static struct i2c_driver adp1660_i2c_driver; + +static struct msm_camera_i2c_reg_array adp1660_init_array[] = { + {0x01, 0x03}, + {0x02, 0x0F}, + {0x09, 0x28}, + {0x03, 0x09}, +}; + +static struct msm_camera_i2c_reg_array adp1660_off_array[] = { + {0x0f, 0x00}, +}; + +static struct msm_camera_i2c_reg_array adp1660_release_array[] = { + {0x0f, 0x00}, +}; + +static struct msm_camera_i2c_reg_array adp1660_low_array[] = { + {0x08, 0x04}, + {0x06, 0x28}, + {0x01, 0xBD}, + {0x0f, 0x01}, +}; + +static struct msm_camera_i2c_reg_array adp1660_high_array[] = { + {0x02, 0x4F}, + {0x06, 0x3C}, + {0x09, 0x3C}, + {0x0f, 0x01}, + {0x01, 0xBB}, +}; + +static void __exit msm_flash_adp1660_i2c_remove(void) +{ + i2c_del_driver(&adp1660_i2c_driver); +} + +static const struct of_device_id adp1660_trigger_dt_match[] = { + {.compatible = "qcom,led-flash", .data = &fctrl}, + {} +}; + +MODULE_DEVICE_TABLE(of, adp1660_trigger_dt_match); + +static const struct i2c_device_id flash_i2c_id[] = { + {"qcom,led-flash", (kernel_ulong_t)&fctrl}, + { } +}; + +static const struct i2c_device_id adp1660_i2c_id[] = { + {FLASH_NAME, (kernel_ulong_t)&fctrl}, + { } +}; + +static int msm_flash_adp1660_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (!id) { + pr_err("msm_flash_adp1660_i2c_probe: id is NULL"); + id = adp1660_i2c_id; + } + + return msm_flash_i2c_probe(client, id); +} + +static struct i2c_driver adp1660_i2c_driver = { + .id_table = adp1660_i2c_id, + .probe = msm_flash_adp1660_i2c_probe, + .remove = __exit_p(msm_flash_adp1660_i2c_remove), + .driver = { + .name = FLASH_NAME, + .owner = THIS_MODULE, + .of_match_table = adp1660_trigger_dt_match, + }, +}; + +static int msm_flash_adp1660_platform_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + + match = of_match_device(adp1660_trigger_dt_match, &pdev->dev); + + if (!match) + return -EFAULT; + return msm_flash_probe(pdev, match->data); +} + +static struct platform_driver adp1660_platform_driver = { + .probe = msm_flash_adp1660_platform_probe, + .driver = { + .name = "qcom,led-flash", + .owner = THIS_MODULE, + .of_match_table = adp1660_trigger_dt_match, + }, +}; + +static int __init msm_flash_adp1660_init_module(void) +{ + int32_t rc = 0; + + rc = platform_driver_register(&adp1660_platform_driver); + + if (fctrl.pdev != NULL && rc == 0) + pr_err("adp1660 platform_driver_register success"); + else if (rc != 0) + pr_err("adp1660 platform_driver_register failed"); + else { + rc = i2c_add_driver(&adp1660_i2c_driver); + if (!rc) + pr_err("adp1660 i2c_add_driver success"); + } + return rc; +} + +static void __exit msm_flash_adp1660_exit_module(void) +{ + if (fctrl.pdev) + platform_driver_unregister(&adp1660_platform_driver); + else + i2c_del_driver(&adp1660_i2c_driver); +} + +static struct msm_camera_i2c_client adp1660_i2c_client = { + .addr_type = MSM_CAMERA_I2C_BYTE_ADDR, +}; + +static struct msm_camera_i2c_reg_setting adp1660_init_setting = { + .reg_setting = adp1660_init_array, + .size = ARRAY_SIZE(adp1660_init_array), + .addr_type = MSM_CAMERA_I2C_BYTE_ADDR, + .data_type = MSM_CAMERA_I2C_BYTE_DATA, + .delay = 0, +}; + +static struct msm_camera_i2c_reg_setting adp1660_off_setting = { + .reg_setting = adp1660_off_array, + .size = ARRAY_SIZE(adp1660_off_array), + .addr_type = MSM_CAMERA_I2C_BYTE_ADDR, + .data_type = MSM_CAMERA_I2C_BYTE_DATA, + .delay = 0, +}; + +static struct msm_camera_i2c_reg_setting adp1660_release_setting = { + .reg_setting = adp1660_release_array, + .size = ARRAY_SIZE(adp1660_release_array), + .addr_type = MSM_CAMERA_I2C_BYTE_ADDR, + .data_type = MSM_CAMERA_I2C_BYTE_DATA, + .delay = 0, +}; + +static struct msm_camera_i2c_reg_setting adp1660_low_setting = { + .reg_setting = adp1660_low_array, + .size = ARRAY_SIZE(adp1660_low_array), + .addr_type = MSM_CAMERA_I2C_BYTE_ADDR, + .data_type = MSM_CAMERA_I2C_BYTE_DATA, + .delay = 0, +}; + +static struct msm_camera_i2c_reg_setting adp1660_high_setting = { + .reg_setting = adp1660_high_array, + .size = ARRAY_SIZE(adp1660_high_array), + .addr_type = MSM_CAMERA_I2C_BYTE_ADDR, + .data_type = MSM_CAMERA_I2C_BYTE_DATA, + .delay = 0, +}; + +static struct msm_led_flash_reg_t adp1660_regs = { + .init_setting = &adp1660_init_setting, + .off_setting = &adp1660_off_setting, + .low_setting = &adp1660_low_setting, + .high_setting = &adp1660_high_setting, + .release_setting = &adp1660_release_setting, +}; + +static struct msm_flash_fn_t adp1660_func_tbl = { + .flash_get_subdev_id = msm_led_i2c_trigger_get_subdev_id, + .flash_led_config = msm_led_i2c_trigger_config, + .flash_led_init = msm_flash_led_init, + .flash_led_release = msm_flash_led_release, + .flash_led_off = msm_flash_led_off, + .flash_led_low = msm_flash_led_low, + .flash_led_high = msm_flash_led_high, +}; + +static struct msm_led_flash_ctrl_t fctrl = { + .flash_i2c_client = &adp1660_i2c_client, + .reg_setting = &adp1660_regs, + .func_tbl = &adp1660_func_tbl, +}; + +/*subsys_initcall(msm_flash_i2c_add_driver);*/ +module_init(msm_flash_adp1660_init_module); +module_exit(msm_flash_adp1660_exit_module); +MODULE_DESCRIPTION("adp1660 FLASH"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c index 1670fa315800..2a32a4a02287 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c @@ -27,6 +27,16 @@ DEFINE_MSM_MUTEX(msm_flash_mutex); static struct v4l2_file_operations msm_flash_v4l2_subdev_fops; static struct led_trigger *torch_trigger; +static const struct of_device_id msm_flash_i2c_dt_match[] = { + {.compatible = "qcom,camera-flash"}, + {} +}; + +static const struct i2c_device_id msm_flash_i2c_id[] = { + {"qcom,camera-flash", (kernel_ulong_t)NULL}, + { } +}; + static const struct of_device_id msm_flash_dt_match[] = { {.compatible = "qcom,camera-flash", .data = NULL}, {} @@ -42,6 +52,16 @@ static struct msm_flash_table *flash_table[] = { &msm_pmic_flash_table }; +static struct msm_camera_i2c_fn_t msm_flash_qup_func_tbl = { + .i2c_read = msm_camera_qup_i2c_read, + .i2c_read_seq = msm_camera_qup_i2c_read_seq, + .i2c_write = msm_camera_qup_i2c_write, + .i2c_write_table = msm_camera_qup_i2c_write_table, + .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_qup_i2c_write_table_w_microdelay, +}; + static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { .i2c_read = msm_camera_cci_i2c_read, .i2c_read_seq = msm_camera_cci_i2c_read_seq, @@ -1111,6 +1131,75 @@ static long msm_flash_subdev_fops_ioctl(struct file *file, return video_usercopy(file, cmd, arg, msm_flash_subdev_do_ioctl); } #endif + +static int msm_camera_flash_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t rc = 0; + struct msm_flash_ctrl_t *flash_ctrl = NULL; + + CDBG("Enter\n"); + + if (client == NULL) { + pr_err("msm_flash_i2c_probe: client is null\n"); + return -EINVAL; + } + + flash_ctrl = kzalloc(sizeof(struct msm_flash_ctrl_t), GFP_KERNEL); + if (!flash_ctrl) + return -ENOMEM; + + memset(flash_ctrl, 0, sizeof(struct msm_flash_ctrl_t)); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("i2c_check_functionality failed\n"); + kfree(flash_ctrl); + return -EINVAL; + } + + rc = msm_flash_get_dt_data(client->dev.of_node, flash_ctrl); + if (rc < 0) { + pr_err("%s:%d msm_flash_get_dt_data failed\n", + __func__, __LINE__); + kfree(flash_ctrl); + return -EINVAL; + } + + flash_ctrl->flash_state = MSM_CAMERA_FLASH_RELEASE; + flash_ctrl->power_info.dev = &client->dev; + flash_ctrl->flash_device_type = MSM_CAMERA_I2C_DEVICE; + flash_ctrl->flash_mutex = &msm_flash_mutex; + flash_ctrl->flash_i2c_client.i2c_func_tbl = &msm_flash_qup_func_tbl; + flash_ctrl->flash_i2c_client.client = client; + + /* Initialize sub device */ + v4l2_subdev_init(&flash_ctrl->msm_sd.sd, &msm_flash_subdev_ops); + v4l2_set_subdevdata(&flash_ctrl->msm_sd.sd, flash_ctrl); + + flash_ctrl->msm_sd.sd.internal_ops = &msm_flash_internal_ops; + flash_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(flash_ctrl->msm_sd.sd.name, + ARRAY_SIZE(flash_ctrl->msm_sd.sd.name), + "msm_camera_flash"); + media_entity_init(&flash_ctrl->msm_sd.sd.entity, 0, NULL, 0); + flash_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + flash_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_FLASH; + flash_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1; + msm_sd_register(&flash_ctrl->msm_sd); + + CDBG("%s:%d flash sd name = %s", __func__, __LINE__, + flash_ctrl->msm_sd.sd.entity.name); + msm_flash_v4l2_subdev_fops = v4l2_subdev_fops; +#ifdef CONFIG_COMPAT + msm_flash_v4l2_subdev_fops.compat_ioctl32 = + msm_flash_subdev_fops_ioctl; +#endif + flash_ctrl->msm_sd.sd.devnode->fops = &msm_flash_v4l2_subdev_fops; + + CDBG("probe success\n"); + return rc; +} + static int32_t msm_flash_platform_probe(struct platform_device *pdev) { int32_t rc = 0; @@ -1189,6 +1278,19 @@ static int32_t msm_flash_platform_probe(struct platform_device *pdev) return rc; } +MODULE_DEVICE_TABLE(of, msm_flash_i2c_dt_match); + +static struct i2c_driver msm_flash_i2c_driver = { + .id_table = msm_flash_i2c_id, + .probe = msm_camera_flash_i2c_probe, + .remove = __exit_p(msm_camera_flash_i2c_remove), + .driver = { + .name = "qcom,camera-flash", + .owner = THIS_MODULE, + .of_match_table = msm_flash_i2c_dt_match, + }, +}; + MODULE_DEVICE_TABLE(of, msm_flash_dt_match); static struct platform_driver msm_flash_platform_driver = { @@ -1205,6 +1307,13 @@ static int __init msm_flash_init_module(void) int32_t rc = 0; CDBG("Enter\n"); rc = platform_driver_register(&msm_flash_platform_driver); + if (!rc) + return rc; + + pr_err("platform probe for flash failed"); + + /* Perform i2c probe if platform probe fails. */ + rc = i2c_add_driver(&msm_flash_i2c_driver); if (rc) pr_err("platform probe for flash failed"); @@ -1214,6 +1323,7 @@ static int __init msm_flash_init_module(void) static void __exit msm_flash_exit_module(void) { platform_driver_unregister(&msm_flash_platform_driver); + i2c_del_driver(&msm_flash_i2c_driver); return; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c new file mode 100644 index 000000000000..2d067a241884 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c @@ -0,0 +1,128 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include "msm_led_flash.h" + +#undef CDBG +#define CDBG(fmt, args...) pr_err(fmt, ##args) + +static struct v4l2_file_operations msm_led_flash_v4l2_subdev_fops; + +static long msm_led_flash_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct msm_led_flash_ctrl_t *fctrl = NULL; + void *argp = (void *)arg; + + if (!sd) { + pr_err("sd NULL\n"); + return -EINVAL; + } + fctrl = v4l2_get_subdevdata(sd); + if (!fctrl) { + pr_err("fctrl NULL\n"); + return -EINVAL; + } + switch (cmd) { + case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID: + return fctrl->func_tbl->flash_get_subdev_id(fctrl, argp); + case VIDIOC_MSM_FLASH_LED_DATA_CFG: + return fctrl->func_tbl->flash_led_config(fctrl, argp); + case MSM_SD_NOTIFY_FREEZE: + return 0; + case MSM_SD_SHUTDOWN: + return fctrl->func_tbl->flash_led_release(fctrl); + default: + pr_err_ratelimited("invalid cmd %d\n", cmd); + return -ENOIOCTLCMD; + } +} + +static struct v4l2_subdev_core_ops msm_flash_subdev_core_ops = { + .ioctl = msm_led_flash_subdev_ioctl, +}; + +static struct v4l2_subdev_ops msm_flash_subdev_ops = { + .core = &msm_flash_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops msm_flash_internal_ops; + +int32_t msm_led_flash_create_v4lsubdev(struct platform_device *pdev, void *data) +{ + struct msm_led_flash_ctrl_t *fctrl = + (struct msm_led_flash_ctrl_t *)data; + CDBG("Enter\n"); + + if (!fctrl) { + pr_err("fctrl NULL\n"); + return -EINVAL; + } + + /* Initialize sub device */ + v4l2_subdev_init(&fctrl->msm_sd.sd, &msm_flash_subdev_ops); + v4l2_set_subdevdata(&fctrl->msm_sd.sd, fctrl); + + fctrl->pdev = pdev; + fctrl->msm_sd.sd.internal_ops = &msm_flash_internal_ops; + fctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(fctrl->msm_sd.sd.name, ARRAY_SIZE(fctrl->msm_sd.sd.name), + "msm_flash"); + media_entity_init(&fctrl->msm_sd.sd.entity, 0, NULL, 0); + fctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + fctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_FLASH; + fctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1; + msm_sd_register(&fctrl->msm_sd); + + msm_led_flash_v4l2_subdev_fops = v4l2_subdev_fops; +#ifdef CONFIG_COMPAT + msm_led_flash_v4l2_subdev_fops.compat_ioctl32 = + msm_led_flash_v4l2_subdev_fops.unlocked_ioctl; +#endif + fctrl->msm_sd.sd.devnode->fops = &msm_led_flash_v4l2_subdev_fops; + CDBG("probe success\n"); + return 0; +} + +int32_t msm_led_i2c_flash_create_v4lsubdev(void *data) +{ + struct msm_led_flash_ctrl_t *fctrl = + (struct msm_led_flash_ctrl_t *)data; + CDBG("Enter\n"); + + if (!fctrl) { + pr_err("fctrl NULL\n"); + return -EINVAL; + } + + /* Initialize sub device */ + v4l2_subdev_init(&fctrl->msm_sd.sd, &msm_flash_subdev_ops); + v4l2_set_subdevdata(&fctrl->msm_sd.sd, fctrl); + + fctrl->msm_sd.sd.internal_ops = &msm_flash_internal_ops; + fctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(fctrl->msm_sd.sd.name, ARRAY_SIZE(fctrl->msm_sd.sd.name), + "msm_flash"); + media_entity_init(&fctrl->msm_sd.sd.entity, 0, NULL, 0); + fctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + fctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_LED_FLASH; + msm_sd_register(&fctrl->msm_sd); + + msm_led_flash_v4l2_subdev_fops = v4l2_subdev_fops; + fctrl->msm_sd.sd.devnode->fops = &msm_led_flash_v4l2_subdev_fops; + + CDBG("probe success\n"); + return 0; +} diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h new file mode 100644 index 000000000000..b954429af6a1 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef MSM_LED_FLASH_H +#define MSM_LED_FLASH_H + +#include +#include +#include +#include +#include +#include "msm_camera_i2c.h" +#include "msm_sd.h" + + +struct msm_led_flash_ctrl_t; + +struct msm_flash_fn_t { + int32_t (*flash_get_subdev_id)(struct msm_led_flash_ctrl_t *, void *); + int32_t (*flash_led_config)(struct msm_led_flash_ctrl_t *, void *); + int32_t (*flash_led_init)(struct msm_led_flash_ctrl_t *); + int32_t (*flash_led_release)(struct msm_led_flash_ctrl_t *); + int32_t (*flash_led_off)(struct msm_led_flash_ctrl_t *); + int32_t (*flash_led_low)(struct msm_led_flash_ctrl_t *); + int32_t (*flash_led_high)(struct msm_led_flash_ctrl_t *); +}; + +struct msm_led_flash_reg_t { + struct msm_camera_i2c_reg_setting *init_setting; + struct msm_camera_i2c_reg_setting *off_setting; + struct msm_camera_i2c_reg_setting *release_setting; + struct msm_camera_i2c_reg_setting *low_setting; + struct msm_camera_i2c_reg_setting *high_setting; +}; + +struct msm_led_flash_ctrl_t { + struct msm_camera_i2c_client *flash_i2c_client; + struct msm_sd_subdev msm_sd; + struct platform_device *pdev; + struct msm_flash_fn_t *func_tbl; + struct msm_camera_sensor_board_info *flashdata; + struct msm_led_flash_reg_t *reg_setting; + /* Flash */ + const char *flash_trigger_name[MAX_LED_TRIGGERS]; + struct led_trigger *flash_trigger[MAX_LED_TRIGGERS]; + uint32_t flash_num_sources; + uint32_t flash_op_current[MAX_LED_TRIGGERS]; + uint32_t flash_max_current[MAX_LED_TRIGGERS]; + uint32_t flash_max_duration[MAX_LED_TRIGGERS]; + /* Torch */ + const char *torch_trigger_name[MAX_LED_TRIGGERS]; + struct led_trigger *torch_trigger[MAX_LED_TRIGGERS]; + uint32_t torch_num_sources; + uint32_t torch_op_current[MAX_LED_TRIGGERS]; + uint32_t torch_max_current[MAX_LED_TRIGGERS]; + + void *data; + enum msm_camera_device_type_t flash_device_type; + enum cci_i2c_master_t cci_i2c_master; + enum msm_camera_led_config_t led_state; + uint32_t subdev_id; + struct msm_pinctrl_info pinctrl_info; +}; + +int msm_flash_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id); + +int msm_flash_probe(struct platform_device *pdev, const void *data); + +int32_t msm_led_flash_create_v4lsubdev(struct platform_device *pdev, + void *data); +int32_t msm_led_i2c_flash_create_v4lsubdev(void *data); + +int32_t msm_led_i2c_trigger_get_subdev_id(struct msm_led_flash_ctrl_t *fctrl, + void *arg); + +int32_t msm_led_i2c_trigger_config(struct msm_led_flash_ctrl_t *fctrl, + void *data); + +int32_t msm_led_torch_create_classdev(struct platform_device *pdev, + void *data); + +int msm_flash_led_init(struct msm_led_flash_ctrl_t *fctrl); +int msm_flash_led_release(struct msm_led_flash_ctrl_t *fctrl); +int msm_flash_led_off(struct msm_led_flash_ctrl_t *fctrl); +int msm_flash_led_low(struct msm_led_flash_ctrl_t *fctrl); +int msm_flash_led_high(struct msm_led_flash_ctrl_t *fctrl); +#endif diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_i2c_trigger.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_i2c_trigger.c new file mode 100644 index 000000000000..68ac3aedcced --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_i2c_trigger.c @@ -0,0 +1,883 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include +#include "msm_led_flash.h" +#include "msm_camera_io_util.h" +#include "../msm_sensor.h" +#include "msm_led_flash.h" +#include "../cci/msm_cci.h" +#include + +#define FLASH_NAME "camera-led-flash" +#define CAM_FLASH_PINCTRL_STATE_SLEEP "cam_flash_suspend" +#define CAM_FLASH_PINCTRL_STATE_DEFAULT "cam_flash_default" +/*#define CONFIG_MSMB_CAMERA_DEBUG*/ +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +static void *g_fctrl; +int32_t msm_led_i2c_trigger_get_subdev_id(struct msm_led_flash_ctrl_t *fctrl, + void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + + if (!subdev_id) { + pr_err("failed\n"); + return -EINVAL; + } + *subdev_id = fctrl->subdev_id; + + CDBG("subdev_id %d\n", *subdev_id); + return 0; +} + +int32_t msm_led_i2c_trigger_config(struct msm_led_flash_ctrl_t *fctrl, + void *data) +{ + int rc = 0; + int i = 0; + struct msm_camera_led_cfg_t *cfg = (struct msm_camera_led_cfg_t *)data; + + CDBG("called led_state %d\n", cfg->cfgtype); + + if (!fctrl->func_tbl) { + pr_err("failed\n"); + return -EINVAL; + } + switch (cfg->cfgtype) { + + case MSM_CAMERA_LED_INIT: + if (fctrl->func_tbl->flash_led_init) + rc = fctrl->func_tbl->flash_led_init(fctrl); + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + cfg->flash_current[i] = + fctrl->flash_max_current[i]; + cfg->flash_duration[i] = + fctrl->flash_max_duration[i]; + cfg->torch_current[i] = + fctrl->torch_max_current[i]; + } + break; + + case MSM_CAMERA_LED_RELEASE: + if (fctrl->func_tbl->flash_led_release) + rc = fctrl->func_tbl-> + flash_led_release(fctrl); + break; + + case MSM_CAMERA_LED_OFF: + if (fctrl->func_tbl->flash_led_off) + rc = fctrl->func_tbl->flash_led_off(fctrl); + break; + + case MSM_CAMERA_LED_LOW: + for (i = 0; i < fctrl->torch_num_sources; i++) { + if (fctrl->torch_max_current[i] > 0) { + fctrl->torch_op_current[i] = + (cfg->torch_current[i] < + fctrl->torch_max_current[i]) ? + cfg->torch_current[i] : + fctrl->torch_max_current[i]; + CDBG("i %d: op_current %d max_current %d\n", + i, fctrl->torch_op_current[i], + fctrl->torch_max_current[i]); + } + } + if (fctrl->func_tbl->flash_led_low) + rc = fctrl->func_tbl->flash_led_low(fctrl); + break; + + case MSM_CAMERA_LED_HIGH: + for (i = 0; i < fctrl->flash_num_sources; i++) { + if (fctrl->flash_max_current[i] > 0) { + fctrl->flash_op_current[i] = + (cfg->flash_current[i] < + fctrl->flash_max_current[i]) ? + cfg->flash_current[i] : + fctrl->flash_max_current[i]; + CDBG(" i %d: op_current %d max_current %d\n", + i, fctrl->flash_op_current[i], + fctrl->flash_max_current[i]); + } + } + if (fctrl->func_tbl->flash_led_high) + rc = fctrl->func_tbl->flash_led_high(fctrl); + break; + default: + rc = -EFAULT; + break; + } + CDBG("flash_set_led_state: return %d\n", rc); + return rc; +} +static int msm_flash_pinctrl_init(struct msm_led_flash_ctrl_t *ctrl) +{ + struct msm_pinctrl_info *flash_pctrl = NULL; + + flash_pctrl = &ctrl->pinctrl_info; + + if (ctrl->pdev != NULL) + flash_pctrl->pinctrl = devm_pinctrl_get(&ctrl->pdev->dev); + else + flash_pctrl->pinctrl = devm_pinctrl_get(&ctrl-> + flash_i2c_client-> + client->dev); + if (IS_ERR_OR_NULL(flash_pctrl->pinctrl)) { + pr_err("%s:%d Getting pinctrl handle failed\n", + __func__, __LINE__); + return -EINVAL; + } + flash_pctrl->gpio_state_active = pinctrl_lookup_state( + flash_pctrl->pinctrl, + CAM_FLASH_PINCTRL_STATE_DEFAULT); + + if (IS_ERR_OR_NULL(flash_pctrl->gpio_state_active)) { + pr_err("%s:%d Failed to get the active state pinctrl handle\n", + __func__, __LINE__); + return -EINVAL; + } + flash_pctrl->gpio_state_suspend = pinctrl_lookup_state( + flash_pctrl->pinctrl, + CAM_FLASH_PINCTRL_STATE_SLEEP); + + if (IS_ERR_OR_NULL(flash_pctrl->gpio_state_suspend)) { + pr_err("%s:%d Failed to get the suspend state pinctrl handle\n", + __func__, __LINE__); + return -EINVAL; + } + return 0; +} + + +int msm_flash_led_init(struct msm_led_flash_ctrl_t *fctrl) +{ + int rc = 0; + struct msm_camera_sensor_board_info *flashdata = NULL; + struct msm_camera_power_ctrl_t *power_info = NULL; + + CDBG("%s:%d called\n", __func__, __LINE__); + + flashdata = fctrl->flashdata; + power_info = &flashdata->power_info; + fctrl->led_state = MSM_CAMERA_LED_RELEASE; + if (power_info->gpio_conf->cam_gpiomux_conf_tbl != NULL) + pr_err("%s:%d mux install\n", __func__, __LINE__); + + /* CCI Init */ + if (fctrl->flash_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + rc = fctrl->flash_i2c_client->i2c_func_tbl->i2c_util( + fctrl->flash_i2c_client, MSM_CCI_INIT); + if (rc < 0) { + pr_err("cci_init failed\n"); + return rc; + } + } + rc = msm_camera_request_gpio_table( + power_info->gpio_conf->cam_gpio_req_tbl, + power_info->gpio_conf->cam_gpio_req_tbl_size, 1); + if (rc < 0) { + pr_err("%s: request gpio failed\n", __func__); + return rc; + } + + if (fctrl->pinctrl_info.use_pinctrl == true) { + CDBG("%s:%d PC:: flash pins setting to active state", + __func__, __LINE__); + rc = pinctrl_select_state(fctrl->pinctrl_info.pinctrl, + fctrl->pinctrl_info.gpio_state_active); + if (rc < 0) { + devm_pinctrl_put(fctrl->pinctrl_info.pinctrl); + pr_err("%s:%d cannot set pin to active state", + __func__, __LINE__); + } + } + msleep(20); + + CDBG("before FL_RESET\n"); + if (power_info->gpio_conf->gpio_num_info-> + valid[SENSOR_GPIO_FL_RESET] == 1) + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_RESET], + GPIO_OUT_HIGH); + + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_EN], + GPIO_OUT_HIGH); + + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_NOW], + GPIO_OUT_HIGH); + + if (fctrl->flash_i2c_client && fctrl->reg_setting) { + rc = fctrl->flash_i2c_client->i2c_func_tbl->i2c_write_table( + fctrl->flash_i2c_client, + fctrl->reg_setting->init_setting); + if (rc < 0) + pr_err("%s:%d failed\n", __func__, __LINE__); + } + fctrl->led_state = MSM_CAMERA_LED_INIT; + return rc; +} + +int msm_flash_led_release(struct msm_led_flash_ctrl_t *fctrl) +{ + int rc = 0, ret = 0; + struct msm_camera_sensor_board_info *flashdata = NULL; + struct msm_camera_power_ctrl_t *power_info = NULL; + + CDBG("%s:%d called\n", __func__, __LINE__); + if (!fctrl) { + pr_err("%s:%d fctrl NULL\n", __func__, __LINE__); + return -EINVAL; + } + flashdata = fctrl->flashdata; + power_info = &flashdata->power_info; + + if (fctrl->led_state != MSM_CAMERA_LED_INIT) { + pr_err("%s:%d invalid led state\n", __func__, __LINE__); + return -EINVAL; + } + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_EN], + GPIO_OUT_LOW); + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_NOW], + GPIO_OUT_LOW); + if (power_info->gpio_conf->gpio_num_info-> + valid[SENSOR_GPIO_FL_RESET] == 1) + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_RESET], + GPIO_OUT_LOW); + + if (fctrl->pinctrl_info.use_pinctrl == true) { + ret = pinctrl_select_state(fctrl->pinctrl_info.pinctrl, + fctrl->pinctrl_info.gpio_state_suspend); + if (ret < 0) { + devm_pinctrl_put(fctrl->pinctrl_info.pinctrl); + pr_err("%s:%d cannot set pin to suspend state", + __func__, __LINE__); + } + } + rc = msm_camera_request_gpio_table( + power_info->gpio_conf->cam_gpio_req_tbl, + power_info->gpio_conf->cam_gpio_req_tbl_size, 0); + if (rc < 0) { + pr_err("%s: request gpio failed\n", __func__); + return rc; + } + + fctrl->led_state = MSM_CAMERA_LED_RELEASE; + /* CCI deInit */ + if (fctrl->flash_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + rc = fctrl->flash_i2c_client->i2c_func_tbl->i2c_util( + fctrl->flash_i2c_client, MSM_CCI_RELEASE); + if (rc < 0) + pr_err("cci_deinit failed\n"); + } + + return 0; +} + +int msm_flash_led_off(struct msm_led_flash_ctrl_t *fctrl) +{ + int rc = 0; + struct msm_camera_sensor_board_info *flashdata = NULL; + struct msm_camera_power_ctrl_t *power_info = NULL; + + if (!fctrl) { + pr_err("%s:%d fctrl NULL\n", __func__, __LINE__); + return -EINVAL; + } + flashdata = fctrl->flashdata; + power_info = &flashdata->power_info; + CDBG("%s:%d called\n", __func__, __LINE__); + if (fctrl->flash_i2c_client && fctrl->reg_setting) { + rc = fctrl->flash_i2c_client->i2c_func_tbl->i2c_write_table( + fctrl->flash_i2c_client, + fctrl->reg_setting->off_setting); + if (rc < 0) + pr_err("%s:%d failed\n", __func__, __LINE__); + } + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_NOW], + GPIO_OUT_LOW); + + return rc; +} + +int msm_flash_led_low(struct msm_led_flash_ctrl_t *fctrl) +{ + int rc = 0; + struct msm_camera_sensor_board_info *flashdata = NULL; + struct msm_camera_power_ctrl_t *power_info = NULL; + + CDBG("%s:%d called\n", __func__, __LINE__); + + flashdata = fctrl->flashdata; + power_info = &flashdata->power_info; + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_EN], + GPIO_OUT_HIGH); + + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_NOW], + GPIO_OUT_HIGH); + + + if (fctrl->flash_i2c_client && fctrl->reg_setting) { + rc = fctrl->flash_i2c_client->i2c_func_tbl->i2c_write_table( + fctrl->flash_i2c_client, + fctrl->reg_setting->low_setting); + if (rc < 0) + pr_err("%s:%d failed\n", __func__, __LINE__); + } + + return rc; +} + +int msm_flash_led_high(struct msm_led_flash_ctrl_t *fctrl) +{ + int rc = 0; + struct msm_camera_sensor_board_info *flashdata = NULL; + struct msm_camera_power_ctrl_t *power_info = NULL; + + CDBG("%s:%d called\n", __func__, __LINE__); + + flashdata = fctrl->flashdata; + power_info = &flashdata->power_info; + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_EN], + GPIO_OUT_HIGH); + + gpio_set_value_cansleep( + power_info->gpio_conf->gpio_num_info-> + gpio_num[SENSOR_GPIO_FL_NOW], + GPIO_OUT_HIGH); + + if (fctrl->flash_i2c_client && fctrl->reg_setting) { + rc = fctrl->flash_i2c_client->i2c_func_tbl->i2c_write_table( + fctrl->flash_i2c_client, + fctrl->reg_setting->high_setting); + if (rc < 0) + pr_err("%s:%d failed\n", __func__, __LINE__); + } + + return rc; +} + +static int32_t msm_led_get_dt_data(struct device_node *of_node, + struct msm_led_flash_ctrl_t *fctrl) +{ + int32_t rc = 0, i = 0; + struct msm_camera_gpio_conf *gconf = NULL; + struct device_node *flash_src_node = NULL; + struct msm_camera_sensor_board_info *flashdata = NULL; + struct msm_camera_power_ctrl_t *power_info = NULL; + uint32_t count = 0; + uint16_t *gpio_array = NULL; + uint16_t gpio_array_size = 0; + uint32_t id_info[3]; + + CDBG("called\n"); + + if (!of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + fctrl->flashdata = kzalloc(sizeof(fctrl->flashdata), + GFP_KERNEL); + if (!fctrl->flashdata) + return -ENOMEM; + + flashdata = fctrl->flashdata; + power_info = &flashdata->power_info; + + rc = of_property_read_u32(of_node, "cell-index", &fctrl->subdev_id); + if (rc < 0) { + pr_err("failed\n"); + return -EINVAL; + } + + CDBG("subdev id %d\n", fctrl->subdev_id); + + rc = of_property_read_string(of_node, "label", + &flashdata->sensor_name); + CDBG("%s label %s, rc %d\n", __func__, + flashdata->sensor_name, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR1; + } + + rc = of_property_read_u32(of_node, "qcom,cci-master", + &fctrl->cci_i2c_master); + CDBG("%s qcom,cci-master %d, rc %d\n", __func__, fctrl->cci_i2c_master, + rc); + if (rc < 0) { + /* Set default master 0 */ + fctrl->cci_i2c_master = MASTER_0; + rc = 0; + } + + fctrl->pinctrl_info.use_pinctrl = of_property_read_bool(of_node, + "qcom,enable_pinctrl"); + if (of_get_property(of_node, "qcom,flash-source", &count)) { + count /= sizeof(uint32_t); + CDBG("count %d\n", count); + if (count > MAX_LED_TRIGGERS) { + pr_err("failed\n"); + return -EINVAL; + } + for (i = 0; i < count; i++) { + flash_src_node = of_parse_phandle(of_node, + "qcom,flash-source", i); + if (!flash_src_node) { + pr_err("flash_src_node NULL\n"); + continue; + } + + rc = of_property_read_string(flash_src_node, + "linux,default-trigger", + &fctrl->flash_trigger_name[i]); + if (rc < 0) { + pr_err("failed\n"); + of_node_put(flash_src_node); + continue; + } + + CDBG("default trigger %s\n", + fctrl->flash_trigger_name[i]); + + rc = of_property_read_u32(flash_src_node, + "qcom,max-current", + &fctrl->flash_op_current[i]); + if (rc < 0) { + pr_err("failed rc %d\n", rc); + of_node_put(flash_src_node); + continue; + } + + of_node_put(flash_src_node); + + CDBG("max_current[%d] %d\n", + i, fctrl->flash_op_current[i]); + + led_trigger_register_simple( + fctrl->flash_trigger_name[i], + &fctrl->flash_trigger[i]); + } + } else { /*Handle LED Flash Ctrl by GPIO*/ + power_info->gpio_conf = + kzalloc(sizeof(struct msm_camera_gpio_conf), + GFP_KERNEL); + if (!power_info->gpio_conf) + return -ENOMEM; + gconf = power_info->gpio_conf; + + gpio_array_size = of_gpio_count(of_node); + CDBG("%s gpio count %d\n", __func__, gpio_array_size); + + if (gpio_array_size) { + gpio_array = + kcalloc(gpio_array_size, sizeof(uint16_t), + GFP_KERNEL); + if (!gpio_array) { + rc = -ENOMEM; + goto ERROR4; + } + for (i = 0; i < gpio_array_size; i++) { + gpio_array[i] = of_get_gpio(of_node, i); + CDBG("%s gpio_array[%d] = %d\n", __func__, i, + gpio_array[i]); + } + + rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR4; + } + + rc = msm_camera_get_dt_gpio_set_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR5; + } + + rc = msm_camera_init_gpio_pin_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR6; + } + } + + /* Read the max current for an LED if present */ + if (of_get_property(of_node, "qcom,max-current", &count)) { + count /= sizeof(uint32_t); + + if (count > MAX_LED_TRIGGERS) { + pr_err("failed\n"); + rc = -EINVAL; + goto ERROR8; + } + + fctrl->flash_num_sources = count; + fctrl->torch_num_sources = count; + + rc = of_property_read_u32_array(of_node, + "qcom,max-current", + fctrl->flash_max_current, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR8; + } + + for (; count < MAX_LED_TRIGGERS; count++) + fctrl->flash_max_current[count] = 0; + + for (count = 0; count < MAX_LED_TRIGGERS; count++) + fctrl->torch_max_current[count] = + fctrl->flash_max_current[count] >> 1; + } + + /* Read the max duration for an LED if present */ + if (of_get_property(of_node, "qcom,max-duration", &count)) { + count /= sizeof(uint32_t); + + if (count > MAX_LED_TRIGGERS) { + pr_err("failed\n"); + rc = -EINVAL; + goto ERROR8; + } + + rc = of_property_read_u32_array(of_node, + "qcom,max-duration", + fctrl->flash_max_duration, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR8; + } + + for (; count < MAX_LED_TRIGGERS; count++) + fctrl->flash_max_duration[count] = 0; + } + + flashdata->slave_info = + kzalloc(sizeof(struct msm_camera_slave_info), + GFP_KERNEL); + if (!flashdata->slave_info) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR8; + } + + rc = of_property_read_u32_array(of_node, "qcom,slave-id", + id_info, 3); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR9; + } + fctrl->flashdata->slave_info->sensor_slave_addr = id_info[0]; + fctrl->flashdata->slave_info->sensor_id_reg_addr = id_info[1]; + fctrl->flashdata->slave_info->sensor_id = id_info[2]; + + kfree(gpio_array); + return rc; +ERROR9: + kfree(fctrl->flashdata->slave_info); +ERROR8: + kfree(fctrl->flashdata->power_info.gpio_conf->gpio_num_info); +ERROR6: + kfree(gconf->cam_gpio_set_tbl); +ERROR5: + kfree(gconf->cam_gpio_req_tbl); +ERROR4: + kfree(gconf); +ERROR1: + kfree(fctrl->flashdata); + kfree(gpio_array); + } + return rc; +} + +static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = { + .i2c_read = msm_camera_qup_i2c_read, + .i2c_read_seq = msm_camera_qup_i2c_read_seq, + .i2c_write = msm_camera_qup_i2c_write, + .i2c_write_table = msm_camera_qup_i2c_write_table, + .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_qup_i2c_write_table_w_microdelay, +}; + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_write_conf_tbl = msm_camera_cci_i2c_write_conf_tbl, +}; + +#ifdef CONFIG_DEBUG_FS +static int set_led_status(void *data, u64 val) +{ + struct msm_led_flash_ctrl_t *fctrl = + (struct msm_led_flash_ctrl_t *)data; + int rc = -1; + + pr_debug("set_led_status: Enter val: %llu", val); + if (!fctrl) { + pr_err("set_led_status: fctrl is NULL"); + return rc; + } + if (!fctrl->func_tbl) { + pr_err("set_led_status: fctrl->func_tbl is NULL"); + return rc; + } + if (val == 0) { + pr_debug("set_led_status: val is disable"); + rc = msm_flash_led_off(fctrl); + if (rc < 0) { + pr_err("%s led_off failed line %d\n", + __func__, __LINE__); + return rc; + } + rc = msm_flash_led_release(fctrl); + if (rc < 0) { + pr_err("%s led_release failed line %d\n", + __func__, __LINE__); + return rc; + } + } else { + pr_debug("set_led_status: val is enable"); + rc = msm_flash_led_init(fctrl); + if (rc < 0) { + pr_err("%s led_init failed line %d\n", + __func__, __LINE__); + return rc; + } + rc = msm_flash_led_low(fctrl); + if (rc < 0) { + pr_err("%s led_low failed line %d\n", + __func__, __LINE__); + return rc; + } + } + + return rc; +} + +DEFINE_SIMPLE_ATTRIBUTE(ledflashdbg_fops, + NULL, set_led_status, "%llu\n"); +#endif + +static void msm_led_i2c_torch_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct msm_led_flash_ctrl_t *fctrl = NULL; + + if (g_fctrl == NULL) + return; + + fctrl = (struct msm_led_flash_ctrl_t *) g_fctrl; + + if (value > LED_OFF) { + if (fctrl->func_tbl->flash_led_init) + fctrl->func_tbl->flash_led_init(fctrl); + if (fctrl->func_tbl->flash_led_low) + fctrl->func_tbl->flash_led_low(fctrl); + } else { + if (fctrl->func_tbl->flash_led_off) + fctrl->func_tbl->flash_led_off(fctrl); + if (fctrl->func_tbl->flash_led_release) + fctrl->func_tbl->flash_led_release(fctrl); + } +}; + +static struct led_classdev msm_torch_i2c_led = { + .name = "torch-light", + .brightness_set = msm_led_i2c_torch_brightness_set, + .brightness = LED_OFF, +}; + +static int32_t msm_i2c_torch_create_classdev(struct device *dev , + void *data) +{ + int rc; + + msm_led_i2c_torch_brightness_set(&msm_torch_i2c_led, LED_OFF); + rc = led_classdev_register(dev, &msm_torch_i2c_led); + if (rc) { + pr_err("Failed to register led dev. rc = %d\n", rc); + return rc; + } + + return 0; +}; + +int msm_flash_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct msm_led_flash_ctrl_t *fctrl = NULL; +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("i2c_check_functionality failed\n"); + goto probe_failure; + } + + fctrl = (struct msm_led_flash_ctrl_t *)(id->driver_data); + if (fctrl->flash_i2c_client) + fctrl->flash_i2c_client->client = client; + /* Set device type as I2C */ + fctrl->flash_device_type = MSM_CAMERA_I2C_DEVICE; + + /* Assign name for sub device */ + snprintf(fctrl->msm_sd.sd.name, sizeof(fctrl->msm_sd.sd.name), + "%s", id->name); + + rc = msm_led_get_dt_data(client->dev.of_node, fctrl); + if (rc < 0) { + pr_err("%s failed line %d\n", __func__, __LINE__); + return rc; + } + + if (fctrl->pinctrl_info.use_pinctrl == true) + msm_flash_pinctrl_init(fctrl); + + if (fctrl->flash_i2c_client != NULL) { + fctrl->flash_i2c_client->client = client; + if (fctrl->flashdata->slave_info->sensor_slave_addr) + fctrl->flash_i2c_client->client->addr = + fctrl->flashdata->slave_info-> + sensor_slave_addr; + } else { + pr_err("%s %s sensor_i2c_client NULL\n", + __func__, client->name); + rc = -EFAULT; + return rc; + } + + if (!fctrl->flash_i2c_client->i2c_func_tbl) + fctrl->flash_i2c_client->i2c_func_tbl = + &msm_sensor_qup_func_tbl; + + rc = msm_led_i2c_flash_create_v4lsubdev(fctrl); +#ifdef CONFIG_DEBUG_FS + dentry = debugfs_create_file("ledflash", S_IRUGO, NULL, (void *)fctrl, + &ledflashdbg_fops); + if (!dentry) + pr_err("Failed to create the debugfs ledflash file"); +#endif + /* Assign Global flash control struture for local usage */ + g_fctrl = (void *) fctrl; + rc = msm_i2c_torch_create_classdev(&(client->dev), NULL); + if (rc) { + pr_err("%s failed to create classdev %d\n", __func__, __LINE__); + return rc; + } + CDBG("%s:%d probe success\n", __func__, __LINE__); + return 0; + +probe_failure: + CDBG("%s:%d probe failed\n", __func__, __LINE__); + return rc; +} + +int msm_flash_probe(struct platform_device *pdev, + const void *data) +{ + int rc = 0; + struct msm_led_flash_ctrl_t *fctrl = + (struct msm_led_flash_ctrl_t *)data; + struct device_node *of_node = pdev->dev.of_node; + struct msm_camera_cci_client *cci_client = NULL; + + if (!of_node) { + pr_err("of_node NULL\n"); + goto probe_failure; + } + fctrl->pdev = pdev; + + rc = msm_led_get_dt_data(pdev->dev.of_node, fctrl); + if (rc < 0) { + pr_err("%s failed line %d rc = %d\n", __func__, __LINE__, rc); + return rc; + } + + if (fctrl->pinctrl_info.use_pinctrl == true) + msm_flash_pinctrl_init(fctrl); + + /* Assign name for sub device */ + snprintf(fctrl->msm_sd.sd.name, sizeof(fctrl->msm_sd.sd.name), + "%s", fctrl->flashdata->sensor_name); + /* Set device type as Platform*/ + fctrl->flash_device_type = MSM_CAMERA_PLATFORM_DEVICE; + + if (NULL == fctrl->flash_i2c_client) { + pr_err("%s flash_i2c_client NULL\n", + __func__); + rc = -EFAULT; + goto probe_failure; + } + + fctrl->flash_i2c_client->cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + if (!fctrl->flash_i2c_client->cci_client) + return rc; + + cci_client = fctrl->flash_i2c_client->cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + cci_client->cci_i2c_master = fctrl->cci_i2c_master; + if (fctrl->flashdata->slave_info->sensor_slave_addr) + cci_client->sid = + fctrl->flashdata->slave_info->sensor_slave_addr >> 1; + cci_client->retries = 3; + cci_client->id_map = 0; + + if (!fctrl->flash_i2c_client->i2c_func_tbl) + fctrl->flash_i2c_client->i2c_func_tbl = + &msm_sensor_cci_func_tbl; + + rc = msm_led_flash_create_v4lsubdev(pdev, fctrl); + + CDBG("%s: probe success\n", __func__); + return 0; + +probe_failure: + CDBG("%s probe failed\n", __func__); + return rc; +} + diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_torch.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_torch.c new file mode 100644 index 000000000000..c4ed4fa17805 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_torch.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include +#include "msm_led_flash.h" + +static struct led_trigger *torch_trigger; + +static void msm_led_torch_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (!torch_trigger) { + pr_err("No torch trigger found, can't set brightness\n"); + return; + } + + led_trigger_event(torch_trigger, value); +}; + +static struct led_classdev msm_torch_led[MAX_LED_TRIGGERS] = { + { + .name = "torch-light0", + .brightness_set = msm_led_torch_brightness_set, + .brightness = LED_OFF, + }, + { + .name = "torch-light1", + .brightness_set = msm_led_torch_brightness_set, + .brightness = LED_OFF, + }, + { + .name = "torch-light2", + .brightness_set = msm_led_torch_brightness_set, + .brightness = LED_OFF, + }, +}; + +int32_t msm_led_torch_create_classdev(struct platform_device *pdev, + void *data) +{ + int32_t i, rc = 0; + struct msm_led_flash_ctrl_t *fctrl = + (struct msm_led_flash_ctrl_t *)data; + + if (!fctrl) { + pr_err("Invalid fctrl\n"); + return -EINVAL; + } + + for (i = 0; i < fctrl->torch_num_sources; i++) { + if (fctrl->torch_trigger[i]) { + torch_trigger = fctrl->torch_trigger[i]; + msm_led_torch_brightness_set(&msm_torch_led[i], + LED_OFF); + + rc = led_classdev_register(&pdev->dev, + &msm_torch_led[i]); + if (rc) { + pr_err("Failed to register %d led dev. rc = %d\n", + i, rc); + return rc; + } + } else { + pr_err("Invalid fctrl->torch_trigger[%d]\n", i); + return -EINVAL; + } + } + + return 0; +}; diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c new file mode 100644 index 000000000000..ff80801a21cd --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_trigger.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include +#include "msm_led_flash.h" + +#define FLASH_NAME "camera-led-flash" + +#undef CDBG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) + +static enum flash_type flashtype; +static struct msm_led_flash_ctrl_t fctrl; + +static int32_t msm_led_trigger_get_subdev_id(struct msm_led_flash_ctrl_t *fctrl, + void *arg) +{ + uint32_t *subdev_id = (uint32_t *)arg; + + if (!subdev_id) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EINVAL; + } + *subdev_id = fctrl->pdev->id; + CDBG("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id); + return 0; +} + +static int32_t msm_led_trigger_config(struct msm_led_flash_ctrl_t *fctrl, + void *data) +{ + int rc = 0; + struct msm_camera_led_cfg_t *cfg = (struct msm_camera_led_cfg_t *)data; + uint32_t i; + uint32_t curr_l, max_curr_l; + + CDBG("called led_state %d\n", cfg->cfgtype); + + if (!fctrl) { + pr_err("failed\n"); + return -EINVAL; + } + + switch (cfg->cfgtype) { + case MSM_CAMERA_LED_OFF: + /* Flash off */ + for (i = 0; i < fctrl->flash_num_sources; i++) + if (fctrl->flash_trigger[i]) + led_trigger_event(fctrl->flash_trigger[i], 0); + /* Torch off */ + for (i = 0; i < fctrl->torch_num_sources; i++) + if (fctrl->torch_trigger[i]) + led_trigger_event(fctrl->torch_trigger[i], 0); + break; + + case MSM_CAMERA_LED_LOW: + for (i = 0; i < fctrl->torch_num_sources; i++) + if (fctrl->torch_trigger[i]) { + max_curr_l = fctrl->torch_max_current[i]; + if (cfg->torch_current[i] >= 0 && + cfg->torch_current[i] < max_curr_l) { + curr_l = cfg->torch_current[i]; + } else { + curr_l = fctrl->torch_op_current[i]; + pr_debug("LED torch %d clamped %d\n", + i, curr_l); + } + led_trigger_event(fctrl->torch_trigger[i], + curr_l); + } + break; + + case MSM_CAMERA_LED_HIGH: + /* Torch off */ + for (i = 0; i < fctrl->torch_num_sources; i++) + if (fctrl->torch_trigger[i]) + led_trigger_event(fctrl->torch_trigger[i], 0); + + for (i = 0; i < fctrl->flash_num_sources; i++) + if (fctrl->flash_trigger[i]) { + max_curr_l = fctrl->flash_max_current[i]; + if (cfg->flash_current[i] >= 0 && + cfg->flash_current[i] < max_curr_l) { + curr_l = cfg->flash_current[i]; + } else { + curr_l = fctrl->flash_op_current[i]; + pr_debug("LED flash %d clamped %d\n", + i, curr_l); + } + led_trigger_event(fctrl->flash_trigger[i], + curr_l); + } + break; + + case MSM_CAMERA_LED_INIT: + case MSM_CAMERA_LED_RELEASE: + /* Flash off */ + for (i = 0; i < fctrl->flash_num_sources; i++) + if (fctrl->flash_trigger[i]) + led_trigger_event(fctrl->flash_trigger[i], 0); + /* Torch off */ + for (i = 0; i < fctrl->torch_num_sources; i++) + if (fctrl->torch_trigger[i]) + led_trigger_event(fctrl->torch_trigger[i], 0); + break; + + default: + rc = -EFAULT; + break; + } + CDBG("flash_set_led_state: return %d\n", rc); + return rc; +} + +static const struct of_device_id msm_led_trigger_dt_match[] = { + {.compatible = "qcom,camera-led-flash"}, + {} +}; + +static int32_t msm_led_trigger_probe(struct platform_device *pdev) +{ + int32_t rc = 0, rc_1 = 0, i = 0; + struct device_node *of_node = pdev->dev.of_node; + struct device_node *flash_src_node = NULL; + uint32_t count = 0; + struct led_trigger *temp = NULL; + + CDBG("called\n"); + + if (!of_node) { + pr_err("of_node NULL\n"); + return -EINVAL; + } + + fctrl.pdev = pdev; + fctrl.flash_num_sources = 0; + fctrl.torch_num_sources = 0; + + rc = of_property_read_u32(of_node, "cell-index", &pdev->id); + if (rc < 0) { + pr_err("failed\n"); + return -EINVAL; + } + CDBG("pdev id %d\n", pdev->id); + + rc = of_property_read_u32(of_node, + "qcom,flash-type", &flashtype); + if (rc < 0) { + pr_err("flash-type: read failed\n"); + return -EINVAL; + } + + /* Flash source */ + if (of_get_property(of_node, "qcom,flash-source", &count)) { + count /= sizeof(uint32_t); + CDBG("qcom,flash-source count %d\n", count); + if (count > MAX_LED_TRIGGERS) { + pr_err("invalid count qcom,flash-source %d\n", count); + return -EINVAL; + } + fctrl.flash_num_sources = count; + for (i = 0; i < fctrl.flash_num_sources; i++) { + flash_src_node = of_parse_phandle(of_node, + "qcom,flash-source", i); + if (!flash_src_node) { + pr_err("flash_src_node %d NULL\n", i); + continue; + } + + rc = of_property_read_string(flash_src_node, + "linux,default-trigger", + &fctrl.flash_trigger_name[i]); + + rc_1 = of_property_read_string(flash_src_node, + "qcom,default-led-trigger", + &fctrl.flash_trigger_name[i]); + if ((rc < 0) && (rc_1 < 0)) { + pr_err("default-trigger: read failed\n"); + of_node_put(flash_src_node); + continue; + } + + CDBG("default trigger %s\n", + fctrl.flash_trigger_name[i]); + + if (flashtype == GPIO_FLASH) { + /* use fake current */ + fctrl.flash_op_current[i] = LED_FULL; + } else { + rc = of_property_read_u32(flash_src_node, + "qcom,current", + &fctrl.flash_op_current[i]); + rc_1 = of_property_read_u32(flash_src_node, + "qcom,max-current", + &fctrl.flash_max_current[i]); + if ((rc < 0) || (rc_1 < 0)) { + pr_err("current: read failed\n"); + of_node_put(flash_src_node); + continue; + } + } + + of_node_put(flash_src_node); + + CDBG("max_current[%d] %d\n", + i, fctrl.flash_op_current[i]); + + led_trigger_register_simple(fctrl.flash_trigger_name[i], + &fctrl.flash_trigger[i]); + + if (flashtype == GPIO_FLASH) + if (fctrl.flash_trigger[i]) + temp = fctrl.flash_trigger[i]; + } + + } + /* Torch source */ + if (of_get_property(of_node, "qcom,torch-source", &count)) { + count /= sizeof(uint32_t); + CDBG("qcom,torch-source count %d\n", count); + if (count > MAX_LED_TRIGGERS) { + pr_err("invalid count qcom,torch-source %d\n", count); + return -EINVAL; + } + fctrl.torch_num_sources = count; + + for (i = 0; i < fctrl.torch_num_sources; i++) { + flash_src_node = of_parse_phandle(of_node, + "qcom,torch-source", i); + if (!flash_src_node) { + pr_err("torch_src_node %d NULL\n", i); + continue; + } + + rc = of_property_read_string(flash_src_node, + "linux,default-trigger", + &fctrl.torch_trigger_name[i]); + + rc_1 = of_property_read_string(flash_src_node, + "qcom,default-led-trigger", + &fctrl.torch_trigger_name[i]); + if ((rc < 0) && (rc_1 < 0)) { + pr_err("default-trigger: read failed\n"); + of_node_put(flash_src_node); + continue; + } + + CDBG("default trigger %s\n", + fctrl.torch_trigger_name[i]); + + if (flashtype == GPIO_FLASH) { + /* use fake current */ + fctrl.torch_op_current[i] = LED_HALF; + } else { + rc = of_property_read_u32(flash_src_node, + "qcom,current", + &fctrl.torch_op_current[i]); + rc_1 = of_property_read_u32(flash_src_node, + "qcom,max-current", + &fctrl.torch_max_current[i]); + if ((rc < 0) || (rc_1 < 0)) { + pr_err("current: read failed\n"); + of_node_put(flash_src_node); + continue; + } + } + + of_node_put(flash_src_node); + + CDBG("torch max_current[%d] %d\n", + i, fctrl.torch_op_current[i]); + + led_trigger_register_simple(fctrl.torch_trigger_name[i], + &fctrl.torch_trigger[i]); + + if (flashtype == GPIO_FLASH) + if (temp && !fctrl.torch_trigger[i]) + fctrl.torch_trigger[i] = temp; + } + } + + rc = msm_led_flash_create_v4lsubdev(pdev, &fctrl); + if (!rc) + msm_led_torch_create_classdev(pdev, &fctrl); + + return rc; +} + +MODULE_DEVICE_TABLE(of, msm_led_trigger_dt_match); + +static struct platform_driver msm_led_trigger_driver = { + .probe = msm_led_trigger_probe, + .driver = { + .name = FLASH_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_led_trigger_dt_match, + }, +}; + +static int __init msm_led_trigger_add_driver(void) +{ + CDBG("called\n"); + return platform_driver_register(&msm_led_trigger_driver); +} + +static struct msm_flash_fn_t msm_led_trigger_func_tbl = { + .flash_get_subdev_id = msm_led_trigger_get_subdev_id, + .flash_led_config = msm_led_trigger_config, +}; + +static struct msm_led_flash_ctrl_t fctrl = { + .func_tbl = &msm_led_trigger_func_tbl, +}; + +module_init(msm_led_trigger_add_driver); +MODULE_DESCRIPTION("LED TRIGGER FLASH"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c index b0fd8277e203..7d7969f36adb 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -781,6 +781,89 @@ ERROR1: gconf->cam_gpio_req_tbl_size = 0; return rc; } +int msm_camera_get_dt_gpio_set_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size) +{ + int rc = 0, i = 0; + uint32_t count = 0; + uint32_t *val_array = NULL; + + rc = of_property_read_u32(of_node, "qcom,gpio-set-tbl-num", &count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return 0; + } + + count /= sizeof(uint32_t); + if (!count) { + pr_err("%s qcom,gpio-set-tbl-num 0\n", __func__); + return 0; + } + + val_array = kzalloc((sizeof(uint32_t) * count), GFP_KERNEL); + if (!val_array) + return -ENOMEM; + + gconf->cam_gpio_set_tbl = kzalloc(sizeof(struct msm_gpio_set_tbl) * + count, GFP_KERNEL); + if (!gconf->cam_gpio_set_tbl) { + rc = -ENOMEM; + goto ERROR1; + } + gconf->cam_gpio_set_tbl_size = count; + + rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-num", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + if (val_array[i] >= gpio_array_size) { + pr_err("%s gpio set tbl index %d invalid\n", + __func__, val_array[i]); + return -EINVAL; + } + gconf->cam_gpio_set_tbl[i].gpio = gpio_array[val_array[i]]; + CDBG("%s cam_gpio_set_tbl[%d].gpio = %d\n", __func__, i, + gconf->cam_gpio_set_tbl[i].gpio); + } + + rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-flags", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + gconf->cam_gpio_set_tbl[i].flags = val_array[i]; + CDBG("%s cam_gpio_set_tbl[%d].flags = %ld\n", __func__, i, + gconf->cam_gpio_set_tbl[i].flags); + } + + rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-delay", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + gconf->cam_gpio_set_tbl[i].delay = val_array[i]; + CDBG("%s cam_gpio_set_tbl[%d].delay = %d\n", __func__, i, + gconf->cam_gpio_set_tbl[i].delay); + } + + kfree(val_array); + return rc; + +ERROR2: + kfree(gconf->cam_gpio_set_tbl); +ERROR1: + kfree(val_array); + gconf->cam_gpio_set_tbl_size = 0; + return rc; +} int msm_camera_init_gpio_pin_tbl(struct device_node *of_node, struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, @@ -1388,6 +1471,7 @@ int32_t msm_sensor_driver_get_gpio_data( gpio_array[i] = of_get_gpio(of_node, i); CDBG("gpio_array[%d] = %d", i, gpio_array[i]); } + rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf, gpio_array, gpio_array_size); if (rc < 0) { @@ -1395,6 +1479,13 @@ int32_t msm_sensor_driver_get_gpio_data( goto FREE_GPIO_CONF; } + rc = msm_camera_get_dt_gpio_set_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_GPIO_REQ_TBL; + } + rc = msm_camera_init_gpio_pin_tbl(of_node, gconf, gpio_array, gpio_array_size); if (rc < 0) { @@ -1403,7 +1494,6 @@ int32_t msm_sensor_driver_get_gpio_data( } kfree(gpio_array); return rc; - FREE_GPIO_REQ_TBL: kfree(gconf->cam_gpio_req_tbl); FREE_GPIO_CONF: diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h index 220915511cce..78c34126e95b 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -36,6 +36,10 @@ int msm_camera_get_dt_power_setting_data(struct device_node *of_node, struct camera_vreg_t *cam_vreg, int num_vreg, struct msm_camera_power_ctrl_t *power_info); +int msm_camera_get_dt_gpio_set_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size); + int msm_camera_get_dt_gpio_req_tbl(struct device_node *of_node, struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, uint16_t gpio_array_size); diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c index 87848350255a..b0d521587193 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -88,6 +88,7 @@ int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl) kfree(s_ctrl->sensordata->actuator_info); kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info); kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl); + kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_set_tbl); kfree(s_ctrl->sensordata->power_info.gpio_conf); kfree(s_ctrl->sensordata->power_info.cam_vreg); kfree(s_ctrl->sensordata->power_info.power_setting); diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 8a0e310ca92a..20d7b9f9b1cb 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -344,6 +344,42 @@ static int32_t msm_sensor_fill_ois_subdevid_by_name( return rc; } +static int32_t msm_sensor_fill_flash_subdevid_by_name( + struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct device_node *src_node = NULL; + uint32_t val = 0; + int32_t *flash_subdev_id; + struct msm_sensor_info_t *sensor_info; + struct device_node *of_node = s_ctrl->of_node; + + if (!of_node) + return -EINVAL; + + sensor_info = s_ctrl->sensordata->sensor_info; + flash_subdev_id = &sensor_info->subdev_id[SUB_MODULE_LED_FLASH]; + + *flash_subdev_id = -1; + + src_node = of_parse_phandle(of_node, "qcom,led-flash-src", 0); + if (!src_node) { + CDBG("%s:%d src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CDBG("%s qcom,flash cell index %d, rc %d\n", __func__, + val, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -EINVAL; + } + *flash_subdev_id = val; + of_node_put(src_node); + src_node = NULL; + } + return rc; +} + static int32_t msm_sensor_fill_slave_info_init_params( struct msm_camera_sensor_slave_info *slave_info, struct msm_sensor_info_t *sensor_info) @@ -893,6 +929,7 @@ CSID_TG: s_ctrl->sensordata->eeprom_name = slave_info->eeprom_name; s_ctrl->sensordata->actuator_name = slave_info->actuator_name; s_ctrl->sensordata->ois_name = slave_info->ois_name; + s_ctrl->sensordata->flash_name = slave_info->flash_name; /* * Update eeporm subdevice Id by input eeprom name */ @@ -916,6 +953,12 @@ CSID_TG: goto free_camera_info; } + rc = msm_sensor_fill_flash_subdevid_by_name(s_ctrl); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto free_camera_info; + } + /* Power up and probe sensor */ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); if (rc < 0) { @@ -1183,6 +1226,7 @@ static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl) FREE_DT_DATA: kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info); kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl); + kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_set_tbl); kfree(s_ctrl->sensordata->power_info.gpio_conf); kfree(s_ctrl->sensordata->power_info.cam_vreg); kfree(s_ctrl->sensordata); diff --git a/include/soc/qcom/camera2.h b/include/soc/qcom/camera2.h index 70d00b6ddd26..c0dac0324020 100644 --- a/include/soc/qcom/camera2.h +++ b/include/soc/qcom/camera2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -85,6 +85,8 @@ struct msm_camera_gpio_conf { struct gpio *cam_gpio_common_tbl; uint8_t cam_gpio_common_tbl_size; struct gpio *cam_gpio_req_tbl; + struct msm_gpio_set_tbl *cam_gpio_set_tbl; + uint8_t cam_gpio_set_tbl_size; uint8_t cam_gpio_req_tbl_size; uint32_t gpio_no_mux; uint32_t *camera_off_table; @@ -146,6 +148,7 @@ struct msm_camera_sensor_board_info { const char *sensor_name; const char *eeprom_name; const char *actuator_name; + const char *flash_name; const char *ois_name; struct msm_camera_slave_info *slave_info; struct msm_camera_csi_lane_params *csi_lane_params; -- GitLab From 213710ba42efefc31849d4b7e3ad08cd84e22554 Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Tue, 3 Oct 2017 17:32:18 +0300 Subject: [PATCH 1232/5498] msm: ipa3: Explicitly enable IPA DMA for IPA MHI Enable IPA DMA explicitly from IPA MHI as IPA MHI uses IPA DMA block. Change-Id: I3daebf7c0b353612502523799d3b396cdde1f1d0 Signed-off-by: Ghanim Fodi --- .../msm/ipa/ipa_clients/ipa_mhi_client.c | 27 +- drivers/platform/msm/ipa/ipa_v3/ipa.c | 36 ++- drivers/platform/msm/ipa/ipa_v3/ipa_dma.c | 233 +++++++++++++++--- drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 2 + drivers/platform/msm/ipa/test/ipa_test_dma.c | 104 +++++++- drivers/platform/msm/ipa/test/ipa_test_mhi.c | 28 +-- 6 files changed, 342 insertions(+), 88 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c index 2e92a0e2b358..de944ec0fd09 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c @@ -182,6 +182,12 @@ static int ipa_mhi_read_write_host(enum ipa_mhi_dma_dir dir, void *dev_addr, return -ENOMEM; } + res = ipa_dma_enable(); + if (res) { + IPA_MHI_ERR("failed to enable IPA DMA rc=%d\n", res); + goto fail_dma_enable; + } + if (dir == IPA_MHI_DMA_FROM_HOST) { res = ipa_dma_sync_memcpy(mem.phys_base, host_addr, size); @@ -203,8 +209,7 @@ static int ipa_mhi_read_write_host(enum ipa_mhi_dma_dir dir, void *dev_addr, goto fail_memcopy; } } - dma_free_coherent(pdev, mem.size, mem.base, - mem.phys_base); + goto dma_succeed; } else { void *host_ptr; @@ -227,9 +232,14 @@ static int ipa_mhi_read_write_host(enum ipa_mhi_dma_dir dir, void *dev_addr, IPA_MHI_FUNC_EXIT(); return 0; +dma_succeed: + IPA_MHI_FUNC_EXIT(); + res = 0; fail_memcopy: - dma_free_coherent(ipa_get_dma_dev(), mem.size, mem.base, - mem.phys_base); + if (ipa_dma_disable()) + IPA_MHI_ERR("failed to disable IPA DMA\n"); +fail_dma_enable: + dma_free_coherent(pdev, mem.size, mem.base, mem.phys_base); return res; } @@ -2408,6 +2418,7 @@ void ipa_mhi_destroy(void) goto fail; } + ipa_dma_destroy(); ipa_mhi_debugfs_destroy(); destroy_workqueue(ipa_mhi_client_ctx->wq); kfree(ipa_mhi_client_ctx); @@ -2500,6 +2511,12 @@ int ipa_mhi_init(struct ipa_mhi_init_params *params) goto fail_create_wq; } + res = ipa_dma_init(); + if (res) { + IPA_MHI_ERR("failed to init ipa dma %d\n", res); + goto fail_dma_init; + } + /* Create PROD in IPA RM */ memset(&mhi_prod_params, 0, sizeof(mhi_prod_params)); mhi_prod_params.name = IPA_RM_RESOURCE_MHI_PROD; @@ -2557,6 +2574,8 @@ fail_create_rm_cons: fail_perf_rm_prod: ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD); fail_create_rm_prod: + ipa_dma_destroy(); +fail_dma_init: destroy_workqueue(ipa_mhi_client_ctx->wq); fail_create_wq: kfree(ipa_mhi_client_ctx); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 0f3f2e536f55..9e87a69571eb 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -5190,7 +5190,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, if (result) { IPAERR("Failed to alloc pkt_init payload\n"); result = -ENODEV; - goto fail_create_apps_resource; + goto fail_allok_pkt_init; } if (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5) @@ -5201,6 +5201,13 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, init_completion(&ipa3_ctx->init_completion_obj); init_completion(&ipa3_ctx->uc_loaded_completion_obj); + result = ipa3_dma_setup(); + if (result) { + IPAERR("Failed to setup IPA DMA\n"); + result = -ENODEV; + goto fail_ipa_dma_setup; + } + /* * For GSI, we can't register the GSI driver yet, as it expects * the GSI FW to be up and running before the registration. @@ -5215,7 +5222,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, if (result) { IPAERR("gsi pre FW loading config failed\n"); result = -ENODEV; - goto fail_ipa_init_interrupts; + goto fail_gsi_pre_fw_load_init; } } } else { @@ -5226,7 +5233,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, result = ipa3_post_init(resource_p, ipa_dev); if (result) { IPAERR("ipa3_post_init failed\n"); - goto fail_ipa_post_init; + goto fail_gsi_pre_fw_load_init; } } @@ -5246,13 +5253,13 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, return 0; + + fail_cdev_add: -fail_ipa_post_init: - if (ipa3_bus_scale_table) { - msm_bus_cl_clear_pdata(ipa3_bus_scale_table); - ipa3_bus_scale_table = NULL; - } -fail_ipa_init_interrupts: +fail_gsi_pre_fw_load_init: + ipa3_dma_shutdown(); +fail_ipa_dma_setup: +fail_allok_pkt_init: ipa_rm_delete_resource(IPA_RM_RESOURCE_APPS_CONS); fail_create_apps_resource: ipa_rm_exit(); @@ -5297,18 +5304,21 @@ fail_flt_rule_cache: fail_create_transport_wq: destroy_workqueue(ipa3_ctx->power_mgmt_wq); fail_init_hw: + ipahal_destroy(); +fail_ipahal: iounmap(ipa3_ctx->mmio); fail_remap: ipa3_disable_clks(); -fail_init_active_client: ipa3_active_clients_log_destroy(); +fail_init_active_client: fail_clk: if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_VIRTUAL) msm_bus_scale_unregister_client(ipa3_ctx->ipa_bus_hdl); -fail_ipahal: - ipa3_bus_scale_table = NULL; fail_bus_reg: - ipahal_destroy(); + if (ipa3_bus_scale_table) { + msm_bus_cl_clear_pdata(ipa3_bus_scale_table); + ipa3_bus_scale_table = NULL; + } fail_bind: kfree(ipa3_ctx->ctrl); fail_mem_ctrl: diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c index 51c11d6e6b76..feeb7361fd27 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c @@ -81,7 +81,7 @@ static void ipa3_dma_debugfs_destroy(void) {} /** * struct ipa3_dma_ctx -IPADMA driver context information - * @is_enabled:is ipa_dma enabled? + * @enable_ref_cnt: ipa dma enable reference count * @destroy_pending: destroy ipa_dma after handling all pending memcpy * @ipa_dma_xfer_wrapper_cache: cache of ipa3_dma_xfer_wrapper structs * @sync_lock: lock for synchronisation in sync_memcpy @@ -100,7 +100,7 @@ static void ipa3_dma_debugfs_destroy(void) {} * @total_uc_memcpy: total number of uc memcpy (statistics) */ struct ipa3_dma_ctx { - bool is_enabled; + unsigned enable_ref_cnt; bool destroy_pending; struct kmem_cache *ipa_dma_xfer_wrapper_cache; struct mutex sync_lock; @@ -125,6 +125,70 @@ struct ipa3_dma_ctx { }; static struct ipa3_dma_ctx *ipa3_dma_ctx; +/** + * struct ipa3_dma_init_refcnt_ctrl -IPADMA driver init control information + * @ref_cnt: reference count for initialization operations + * @lock: lock for the reference count + */ +struct ipa3_dma_init_refcnt_ctrl { + unsigned ref_cnt; + struct mutex lock; +}; +static struct ipa3_dma_init_refcnt_ctrl *ipa3_dma_init_refcnt_ctrl; + +/** + * ipa3_dma_setup() - One time setup for IPA DMA + * + * This function should be called once to setup ipa dma + * by creating the init reference count controller + * + * Return codes: 0: success + * Negative value: failure + */ +int ipa3_dma_setup(void) +{ + IPADMA_FUNC_ENTRY(); + + if (ipa3_dma_init_refcnt_ctrl) { + IPADMA_ERR("Setup already done\n"); + return -EFAULT; + } + + ipa3_dma_init_refcnt_ctrl = + kzalloc(sizeof(*(ipa3_dma_init_refcnt_ctrl)), GFP_KERNEL); + + if (!ipa3_dma_init_refcnt_ctrl) { + IPADMA_ERR("kzalloc error.\n"); + return -ENOMEM; + } + + mutex_init(&ipa3_dma_init_refcnt_ctrl->lock); + + IPADMA_FUNC_EXIT(); + return 0; +} + +/** + * ipa3_dma_shutdown() - Clear setup operations. + * + * Cleanup for the setup function. + * Should be called during IPA driver unloading. + * It assumes all ipa_dma operations are done and ipa_dma is destroyed. + * + * Return codes: None. + */ +void ipa3_dma_shutdown(void) +{ + IPADMA_FUNC_ENTRY(); + + if (!ipa3_dma_init_refcnt_ctrl) + return; + + kfree(ipa3_dma_init_refcnt_ctrl); + ipa3_dma_init_refcnt_ctrl = NULL; + + IPADMA_FUNC_EXIT(); +} /** * ipa3_dma_init() -Initialize IPADMA. @@ -133,8 +197,10 @@ static struct ipa3_dma_ctx *ipa3_dma_ctx; * MEMCPY_DMA_SYNC_PROD ->MEMCPY_DMA_SYNC_CONS * MEMCPY_DMA_ASYNC_PROD->MEMCPY_DMA_SYNC_CONS * + * Can be executed several times (re-entrant) + * * Return codes: 0: success - * -EFAULT: IPADMA is already initialized + * -EFAULT: Mismatch between context existence and init ref_cnt * -EINVAL: IPA driver is not initialized * -ENOMEM: allocating memory error * -EPERM: pipe connection failed @@ -149,21 +215,43 @@ int ipa3_dma_init(void) IPADMA_FUNC_ENTRY(); + if (!ipa3_dma_init_refcnt_ctrl) { + IPADMA_ERR("Setup isn't done yet!\n"); + return -EINVAL; + } + + mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock); + if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 0) { + IPADMA_DBG("Already initialized refcnt=%d\n", + ipa3_dma_init_refcnt_ctrl->ref_cnt); + if (!ipa3_dma_ctx) { + IPADMA_ERR("Context missing. refcnt=%d\n", + ipa3_dma_init_refcnt_ctrl->ref_cnt); + res = -EFAULT; + } else { + ipa3_dma_init_refcnt_ctrl->ref_cnt++; + } + goto init_unlock; + } + if (ipa3_dma_ctx) { - IPADMA_ERR("Already initialized.\n"); - return -EFAULT; + IPADMA_ERR("Context already exist\n"); + res = -EFAULT; + goto init_unlock; } if (!ipa3_is_ready()) { IPADMA_ERR("IPA is not ready yet\n"); - return -EINVAL; + res = -EINVAL; + goto init_unlock; } ipa_dma_ctx_t = kzalloc(sizeof(*(ipa3_dma_ctx)), GFP_KERNEL); if (!ipa_dma_ctx_t) { IPADMA_ERR("kzalloc error.\n"); - return -ENOMEM; + res = -ENOMEM; + goto init_unlock; } ipa_dma_ctx_t->ipa_dma_xfer_wrapper_cache = @@ -180,7 +268,7 @@ int ipa3_dma_init(void) mutex_init(&ipa_dma_ctx_t->sync_lock); spin_lock_init(&ipa_dma_ctx_t->pending_lock); init_completion(&ipa_dma_ctx_t->done); - ipa_dma_ctx_t->is_enabled = false; + ipa_dma_ctx_t->enable_ref_cnt = 0; ipa_dma_ctx_t->destroy_pending = false; atomic_set(&ipa_dma_ctx_t->async_memcpy_pending_cnt, 0); atomic_set(&ipa_dma_ctx_t->sync_memcpy_pending_cnt, 0); @@ -294,10 +382,12 @@ int ipa3_dma_init(void) } ipa3_dma_debugfs_init(); ipa3_dma_ctx = ipa_dma_ctx_t; + ipa3_dma_init_refcnt_ctrl->ref_cnt = 1; IPADMA_DBG("ASYNC MEMCPY pipes are connected\n"); IPADMA_FUNC_EXIT(); - return res; + goto init_unlock; + fail_async_cons: ipa3_teardown_sys_pipe(ipa_dma_ctx_t->ipa_dma_async_prod_hdl); fail_async_prod: @@ -313,6 +403,8 @@ fail_alloc_dummy: fail_mem_ctrl: kfree(ipa_dma_ctx_t); ipa3_dma_ctx = NULL; +init_unlock: + mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock); return res; } @@ -320,26 +412,29 @@ fail_mem_ctrl: /** * ipa3_dma_enable() -Vote for IPA clocks. * + * Can be executed several times (re-entrant) + * *Return codes: 0: success * -EINVAL: IPADMA is not initialized - * -EPERM: Operation not permitted as ipa_dma is already - * enabled */ int ipa3_dma_enable(void) { IPADMA_FUNC_ENTRY(); - if (ipa3_dma_ctx == NULL) { + if ((ipa3_dma_ctx == NULL) || + (ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) { IPADMA_ERR("IPADMA isn't initialized, can't enable\n"); - return -EPERM; + return -EINVAL; } mutex_lock(&ipa3_dma_ctx->enable_lock); - if (ipa3_dma_ctx->is_enabled) { - IPADMA_ERR("Already enabled.\n"); + if (ipa3_dma_ctx->enable_ref_cnt > 0) { + IPADMA_ERR("Already enabled refcnt=%d\n", + ipa3_dma_ctx->enable_ref_cnt); + ipa3_dma_ctx->enable_ref_cnt++; mutex_unlock(&ipa3_dma_ctx->enable_lock); - return -EPERM; + return 0; } IPA_ACTIVE_CLIENTS_INC_SPECIAL("DMA"); - ipa3_dma_ctx->is_enabled = true; + ipa3_dma_ctx->enable_ref_cnt = 1; mutex_unlock(&ipa3_dma_ctx->enable_lock); IPADMA_FUNC_EXIT(); @@ -379,32 +474,45 @@ static bool ipa3_dma_work_pending(void) int ipa3_dma_disable(void) { unsigned long flags; + int res = 0; + bool dec_clks = false; IPADMA_FUNC_ENTRY(); - if (ipa3_dma_ctx == NULL) { + if ((ipa3_dma_ctx == NULL) || + (ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) { IPADMA_ERR("IPADMA isn't initialized, can't disable\n"); - return -EPERM; + return -EINVAL; } mutex_lock(&ipa3_dma_ctx->enable_lock); spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags); - if (!ipa3_dma_ctx->is_enabled) { - IPADMA_ERR("Already disabled.\n"); - spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags); - mutex_unlock(&ipa3_dma_ctx->enable_lock); - return -EPERM; + if (ipa3_dma_ctx->enable_ref_cnt > 1) { + IPADMA_DBG("Multiple enablement done. refcnt=%d\n", + ipa3_dma_ctx->enable_ref_cnt); + ipa3_dma_ctx->enable_ref_cnt--; + goto completed; + } + + if (ipa3_dma_ctx->enable_ref_cnt == 0) { + IPADMA_ERR("Already disabled\n"); + res = -EPERM; + goto completed; } + if (ipa3_dma_work_pending()) { IPADMA_ERR("There is pending work, can't disable.\n"); - spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags); - mutex_unlock(&ipa3_dma_ctx->enable_lock); - return -EFAULT; + res = -EFAULT; + goto completed; } - ipa3_dma_ctx->is_enabled = false; + ipa3_dma_ctx->enable_ref_cnt = 0; + dec_clks = true; + IPADMA_FUNC_EXIT(); + +completed: spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags); - IPA_ACTIVE_CLIENTS_DEC_SPECIAL("DMA"); + if (dec_clks) + IPA_ACTIVE_CLIENTS_DEC_SPECIAL("DMA"); mutex_unlock(&ipa3_dma_ctx->enable_lock); - IPADMA_FUNC_EXIT(); - return 0; + return res; } /** @@ -460,7 +568,7 @@ int ipa3_dma_sync_memcpy(u64 dest, u64 src, int len) } } spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags); - if (!ipa3_dma_ctx->is_enabled) { + if (!ipa3_dma_ctx->enable_ref_cnt) { IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n"); spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags); return -EPERM; @@ -751,7 +859,7 @@ int ipa3_dma_async_memcpy(u64 dest, u64 src, int len, return -EINVAL; } spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags); - if (!ipa3_dma_ctx->is_enabled) { + if (!ipa3_dma_ctx->enable_ref_cnt) { IPADMA_ERR("can't memcpy, IPA_DMA isn't enabled\n"); spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags); return -EPERM; @@ -956,7 +1064,7 @@ int ipa3_dma_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len) } spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags); - if (!ipa3_dma_ctx->is_enabled) { + if (!ipa3_dma_ctx->enable_ref_cnt) { IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n"); spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags); return -EPERM; @@ -990,17 +1098,36 @@ void ipa3_dma_destroy(void) int res = 0; IPADMA_FUNC_ENTRY(); - if (!ipa3_dma_ctx) { - IPADMA_ERR("IPADMA isn't initialized\n"); + + if (!ipa3_dma_init_refcnt_ctrl) { + IPADMA_ERR("Setup isn't done\n"); return; } + mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock); + if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 1) { + IPADMA_DBG("Multiple initialization done. refcnt=%d\n", + ipa3_dma_init_refcnt_ctrl->ref_cnt); + ipa3_dma_init_refcnt_ctrl->ref_cnt--; + goto completed; + } + + if ((!ipa3_dma_ctx) || (ipa3_dma_init_refcnt_ctrl->ref_cnt == 0)) { + IPADMA_ERR("IPADMA isn't initialized ctx=%pK\n", ipa3_dma_ctx); + goto completed; + } + if (ipa3_dma_work_pending()) { ipa3_dma_ctx->destroy_pending = true; IPADMA_DBG("There are pending memcpy, wait for completion\n"); wait_for_completion(&ipa3_dma_ctx->done); } + if (ipa3_dma_ctx->enable_ref_cnt > 0) { + IPADMA_ERR("IPADMA still enabled\n"); + goto completed; + } + res = ipa3_teardown_sys_pipe(ipa3_dma_ctx->ipa_dma_async_cons_hdl); if (res) IPADMA_ERR("teardown IPADMA ASYNC CONS failed\n"); @@ -1026,7 +1153,11 @@ void ipa3_dma_destroy(void) kfree(ipa3_dma_ctx); ipa3_dma_ctx = NULL; + ipa3_dma_init_refcnt_ctrl->ref_cnt = 0; IPADMA_FUNC_EXIT(); + +completed: + mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock); } /** @@ -1089,15 +1220,31 @@ static ssize_t ipa3_dma_debugfs_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { int nbytes = 0; + + if (!ipa3_dma_init_refcnt_ctrl) { + nbytes += scnprintf(&dbg_buff[nbytes], + IPADMA_MAX_MSG_LEN - nbytes, + "Setup was not done\n"); + goto completed; + + } + if (!ipa3_dma_ctx) { nbytes += scnprintf(&dbg_buff[nbytes], IPADMA_MAX_MSG_LEN - nbytes, - "Not initialized\n"); + "Status:\n Not initialized (ref_cnt=%d)\n", + ipa3_dma_init_refcnt_ctrl->ref_cnt); } else { nbytes += scnprintf(&dbg_buff[nbytes], IPADMA_MAX_MSG_LEN - nbytes, - "Status:\n IPADMA is %s\n", - (ipa3_dma_ctx->is_enabled) ? "Enabled" : "Disabled"); + "Status:\n Initialized (ref_cnt=%d)\n", + ipa3_dma_init_refcnt_ctrl->ref_cnt); + nbytes += scnprintf(&dbg_buff[nbytes], + IPADMA_MAX_MSG_LEN - nbytes, + " %s (ref_cnt=%d)\n", + (ipa3_dma_ctx->enable_ref_cnt > 0) ? + "Enabled" : "Disabled", + ipa3_dma_ctx->enable_ref_cnt); nbytes += scnprintf(&dbg_buff[nbytes], IPADMA_MAX_MSG_LEN - nbytes, "Statistics:\n total sync memcpy: %d\n ", @@ -1106,19 +1253,25 @@ static ssize_t ipa3_dma_debugfs_read(struct file *file, char __user *ubuf, IPADMA_MAX_MSG_LEN - nbytes, "total async memcpy: %d\n ", atomic_read(&ipa3_dma_ctx->total_async_memcpy)); + nbytes += scnprintf(&dbg_buff[nbytes], + IPADMA_MAX_MSG_LEN - nbytes, + "total uc memcpy: %d\n ", + atomic_read(&ipa3_dma_ctx->total_uc_memcpy)); nbytes += scnprintf(&dbg_buff[nbytes], IPADMA_MAX_MSG_LEN - nbytes, "pending sync memcpy jobs: %d\n ", atomic_read(&ipa3_dma_ctx->sync_memcpy_pending_cnt)); nbytes += scnprintf(&dbg_buff[nbytes], IPADMA_MAX_MSG_LEN - nbytes, - "pending async memcpy jobs: %d\n", + "pending async memcpy jobs: %d\n ", atomic_read(&ipa3_dma_ctx->async_memcpy_pending_cnt)); nbytes += scnprintf(&dbg_buff[nbytes], IPADMA_MAX_MSG_LEN - nbytes, "pending uc memcpy jobs: %d\n", atomic_read(&ipa3_dma_ctx->uc_memcpy_pending_cnt)); } + +completed: return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index b50b9e403f0a..45280c57d682 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -2002,6 +2002,8 @@ void ipa3_uc_register_handlers(enum ipa3_hw_features feature, struct ipa3_uc_hdlrs *hdlrs); int ipa3_create_nat_device(void); int ipa3_uc_notify_clk_state(bool enabled); +int ipa3_dma_setup(void); +void ipa3_dma_shutdown(void); void ipa3_dma_async_memcpy_notify_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data); diff --git a/drivers/platform/msm/ipa/test/ipa_test_dma.c b/drivers/platform/msm/ipa/test/ipa_test_dma.c index a7762c2a2c94..3e06b86265ba 100644 --- a/drivers/platform/msm/ipa/test/ipa_test_dma.c +++ b/drivers/platform/msm/ipa/test/ipa_test_dma.c @@ -364,11 +364,11 @@ static int ipa_test_dma_sync_async_memcpy(int size) } /** - * TEST: test control API - enable/disable dma + * TEST: test enable/disable dma * 1. enable dma * 2. disable dma */ -static int ipa_test_dma_control_api(void *priv) +static int ipa_test_dma_enable_disable(void *priv) { int rc; @@ -391,6 +391,92 @@ static int ipa_test_dma_control_api(void *priv) return 0; } +/** + * TEST: test init/enable/disable/destroy dma + * 1. init dma + * 2. enable dma + * 3. disable dma + * 4. destroy dma + */ +static int ipa_test_dma_init_enbl_disable_destroy(void *priv) +{ + int rc; + + IPA_UT_LOG("Test Start\n"); + + rc = ipa_dma_init(); + if (rc) { + IPA_UT_LOG("DMA Init failed rc=%d\n", rc); + IPA_UT_TEST_FAIL_REPORT("fail init dma"); + return rc; + } + + rc = ipa_dma_enable(); + if (rc) { + ipa_dma_destroy(); + IPA_UT_LOG("DMA enable failed rc=%d\n", rc); + IPA_UT_TEST_FAIL_REPORT("fail enable dma"); + return rc; + } + + rc = ipa_dma_disable(); + if (rc) { + IPA_UT_LOG("DMA disable failed rc=%d\n", rc); + IPA_UT_TEST_FAIL_REPORT("fail disable dma"); + return rc; + } + + ipa_dma_destroy(); + + return 0; +} + +/** + * TEST: test enablex2/disablex2 dma + * 1. enable dma + * 2. enable dma + * 3. disable dma + * 4. disable dma + */ +static int ipa_test_dma_enblx2_disablex2(void *priv) +{ + int rc; + + IPA_UT_LOG("Test Start\n"); + + rc = ipa_dma_enable(); + if (rc) { + ipa_dma_destroy(); + IPA_UT_LOG("DMA enable failed rc=%d\n", rc); + IPA_UT_TEST_FAIL_REPORT("fail enable dma"); + return rc; + } + + rc = ipa_dma_enable(); + if (rc) { + ipa_dma_destroy(); + IPA_UT_LOG("DMA enable failed rc=%d\n", rc); + IPA_UT_TEST_FAIL_REPORT("fail enable dma"); + return rc; + } + + rc = ipa_dma_disable(); + if (rc) { + IPA_UT_LOG("DMA disable failed rc=%d\n", rc); + IPA_UT_TEST_FAIL_REPORT("fail disable dma"); + return rc; + } + + rc = ipa_dma_disable(); + if (rc) { + IPA_UT_LOG("DMA disable failed rc=%d\n", rc); + IPA_UT_TEST_FAIL_REPORT("fail disable dma"); + return rc; + } + + return 0; +} + /** * TEST: memcpy before dma enable * @@ -999,9 +1085,17 @@ static int ipa_test_dma_sync_memcpy_max_pkt_size(void *priv) IPA_UT_DEFINE_SUITE_START(dma, "DMA for GSI", ipa_test_dma_setup, ipa_test_dma_teardown) { - IPA_UT_ADD_TEST(control_api, - "Control API", - ipa_test_dma_control_api, + IPA_UT_ADD_TEST(init_enable_disable_destroy, + "Init->Enable->Disable->Destroy", + ipa_test_dma_enable_disable, + true, IPA_HW_v3_0, IPA_HW_MAX), + IPA_UT_ADD_TEST(initx2_enable_disable_destroyx2, + "Initx2->Enable->Disable->Destroyx2", + ipa_test_dma_init_enbl_disable_destroy, + true, IPA_HW_v3_0, IPA_HW_MAX), + IPA_UT_ADD_TEST(init_enablex2_disablex2_destroy, + "Init->Enablex2->Disablex2->Destroy", + ipa_test_dma_enblx2_disablex2, true, IPA_HW_v3_0, IPA_HW_MAX), IPA_UT_ADD_TEST(memcpy_before_enable, "Call memcpy before dma enable and expect it to fail", diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c index 462d5bbe1f40..43c8b3a818c3 100644 --- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c +++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c @@ -868,7 +868,7 @@ static int ipa_test_mhi_suite_teardown(void *priv) * * To be run during tests * 1. MHI init (Ready state) - * 2. Conditional MHO start and connect (M0 state) + * 2. Conditional MHI start and connect (M0 state) */ static int ipa_mhi_test_initialize_driver(bool skip_start_and_conn) { @@ -879,7 +879,6 @@ static int ipa_mhi_test_initialize_driver(bool skip_start_and_conn) struct ipa_mhi_connect_params cons_params; struct ipa_mhi_mmio_register_set *p_mmio; struct ipa_mhi_channel_context_array *p_ch_ctx_array; - bool is_dma; u64 phys_addr; IPA_UT_LOG("Entry\n"); @@ -912,29 +911,6 @@ static int ipa_mhi_test_initialize_driver(bool skip_start_and_conn) return -ETIME; } - if (ipa_mhi_is_using_dma(&is_dma)) { - IPA_UT_LOG("is_dma checkign failed. Is MHI loaded?\n"); - IPA_UT_TEST_FAIL_REPORT("failed checking using dma"); - return -EPERM; - } - - if (is_dma) { - IPA_UT_LOG("init ipa_dma\n"); - rc = ipa_dma_init(); - if (rc && rc != -EFAULT) { - IPA_UT_LOG("ipa_dma_init failed, %d\n", rc); - IPA_UT_TEST_FAIL_REPORT("failed init dma"); - return rc; - } - IPA_UT_LOG("enable ipa_dma\n"); - rc = ipa_dma_enable(); - if (rc && rc != -EPERM) { - IPA_UT_LOG("ipa_dma_enable failed, %d\n", rc); - IPA_UT_TEST_FAIL_REPORT("failed enable dma"); - return rc; - } - } - if (!skip_start_and_conn) { memset(&start_params, 0, sizeof(start_params)); start_params.channel_context_array_addr = p_mmio->ccabap; @@ -1545,7 +1521,7 @@ static int ipa_mhi_test_suspend(bool force, bool should_success) } if (!should_success && rc != -EAGAIN) { - IPA_UT_LOG("ipa_mhi_suspenddid not return -EAGAIN fail %d\n", + IPA_UT_LOG("ipa_mhi_suspend did not return -EAGAIN fail %d\n", rc); IPA_UT_TEST_FAIL_REPORT("suspend succeeded unexpectedly"); return -EFAULT; -- GitLab From 314b4f55d56f8b4b8f389c993f4ffc3929677e79 Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Wed, 16 Aug 2017 11:20:06 -0700 Subject: [PATCH 1233/5498] msm: ipa: fix race condition around sys->len Changes to gaurd sys->len with a spinlock to avoid race condition between incrementing and decrementing this value. Change-Id: I638fea5a1a9cdda10b471fb4fa500079024e24c7 CRs-Fixed: 2093851 Acked-by: Jyothi Jayanthi Signed-off-by: Michael Adisumarta --- drivers/platform/msm/ipa/ipa_v3/ipa_dp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 4ce83e81aec9..c362b69d8784 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -2966,6 +2966,7 @@ static void ipa3_wq_rx_common(struct ipa3_sys_context *sys, u32 size) WARN_ON(1); return; } + spin_lock_bh(&sys->spinlock); rx_pkt_expected = list_first_entry(&sys->head_desc_list, struct ipa3_rx_pkt_wrapper, link); @@ -2973,6 +2974,7 @@ static void ipa3_wq_rx_common(struct ipa3_sys_context *sys, u32 size) sys->len--; if (size) rx_pkt_expected->len = size; + spin_unlock_bh(&sys->spinlock); rx_skb = rx_pkt_expected->data.skb; dma_unmap_single(ipa3_ctx->pdev, rx_pkt_expected->data.dma_addr, sys->rx_buff_sz, DMA_FROM_DEVICE); -- GitLab From fccbd2533ba3fafa3a922c01cb1e6a4a553f5b7c Mon Sep 17 00:00:00 2001 From: Srinivasarao P Date: Wed, 4 Oct 2017 13:21:31 +0530 Subject: [PATCH 1234/5498] power: reset: Do hard reset when user reboots from UI Reboot initiated by user from UI should be hard reset. For UI reboots, userspace passes 'userrequested' as argument along with reboot command so use this info to do hard reset. Change-Id: I6503ea8b12efed2af62ee2269043ef4dc4bebfbf Signed-off-by: Srinivasarao P --- drivers/power/reset/msm-poweroff.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index ee3e72b3537a..cb66ee5e3a9c 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -287,7 +287,8 @@ static void msm_restart_prepare(const char *cmd) need_warm_reset = true; } else { need_warm_reset = (get_dload_mode() || - (cmd != NULL && cmd[0] != '\0')); + ((cmd != NULL && cmd[0] != '\0') && + strcmp(cmd, "userrequested"))); } /* Hard reset the PMIC unless memory contents must be maintained. */ -- GitLab From 259ac4ede3d9719729e6b43fbee3a9a420a55054 Mon Sep 17 00:00:00 2001 From: Srikanth Uyyala Date: Mon, 16 Oct 2017 02:42:27 -0700 Subject: [PATCH 1235/5498] Revert "ARM: dts: msm: Add VFE efuse support for 8953" commit 66aabc842571 ("ARM: dts: msm: Add VFE efuse support for 8953") VFE efuse support for 8953 dtsi is not required anymore as now 8953 can support fullsize upto 24mp Change-Id: Id6e4866f80e4f55b0ce0c3d5dbe344d4633a7329 Signed-off-by: Srikanth Uyyala --- arch/arm/boot/dts/qcom/msm8953-camera.dtsi | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8953-camera.dtsi b/arch/arm/boot/dts/qcom/msm8953-camera.dtsi index 760efda246a6..32c9f92937b0 100644 --- a/arch/arm/boot/dts/qcom/msm8953-camera.dtsi +++ b/arch/arm/boot/dts/qcom/msm8953-camera.dtsi @@ -232,9 +232,8 @@ cell-index = <0>; compatible = "qcom,vfe40"; reg = <0x1b10000 0x1000>, - <0x1b40000 0x200>, - <0x00a4174 0x8>; - reg-names = "vfe", "vfe_vbif", "vfe_fuse"; + <0x1b40000 0x200>; + reg-names = "vfe", "vfe_vbif"; interrupts = <0 57 0>; interrupt-names = "vfe"; vdd-supply = <&gdsc_vfe>; @@ -283,9 +282,8 @@ cell-index = <1>; compatible = "qcom,vfe40"; reg = <0x1b14000 0x1000>, - <0x1ba0000 0x200>, - <0x00a4174 0x8>; - reg-names = "vfe", "vfe_vbif", "vfe_fuse"; + <0x1ba0000 0x200>; + reg-names = "vfe", "vfe_vbif"; interrupts = <0 29 0>; interrupt-names = "vfe"; vdd-supply = <&gdsc_vfe1>; -- GitLab From 6d1c3f7ed291c3139eccc3248d908aa7e0ac9e91 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 24 Aug 2017 13:22:06 -0400 Subject: [PATCH 1236/5498] ext4: in ext4_seek_{hole,data}, return -ENXIO for negative offsets commit 1bd8d6cd3e413d64e543ec3e69ff43e75a1cf1ea upstream. In the ext4 implementations of SEEK_HOLE and SEEK_DATA, make sure we return -ENXIO for negative offsets instead of banging around inside the extent code and returning -EFSCORRUPTED. Reported-by: Mateusz S Signed-off-by: Darrick J. Wong Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 9dfb324de119..64623537f3b0 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -399,7 +399,7 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize) mutex_lock(&inode->i_mutex); isize = i_size_read(inode); - if (offset >= isize) { + if (offset < 0 || offset >= isize) { mutex_unlock(&inode->i_mutex); return -ENXIO; } @@ -472,7 +472,7 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize) mutex_lock(&inode->i_mutex); isize = i_size_read(inode); - if (offset >= isize) { + if (offset < 0 || offset >= isize) { mutex_unlock(&inode->i_mutex); return -ENXIO; } -- GitLab From f74c7fb21ee597e6ba5ff7ecb2b1021fa2a6212c Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Sat, 8 Jul 2017 14:32:00 -0700 Subject: [PATCH 1237/5498] CIFS: Reconnect expired SMB sessions commit 511c54a2f69195b28afb9dd119f03787b1625bb4 upstream. According to the MS-SMB2 spec (3.2.5.1.6) once the client receives STATUS_NETWORK_SESSION_EXPIRED error code from a server it should reconnect the current SMB session. Currently the client doesn't do that. This can result in subsequent client requests failing by the server. The patch adds an additional logic to the demultiplex thread to identify expired sessions and reconnect them. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsglob.h | 2 ++ fs/cifs/cifssmb.c | 7 +++++++ fs/cifs/connect.c | 7 +++++++ fs/cifs/smb2ops.c | 15 +++++++++++++++ 4 files changed, 31 insertions(+) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 99745da9cd90..0a359af59dd8 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -347,6 +347,8 @@ struct smb_version_operations { unsigned int (*calc_smb_size)(void *); /* check for STATUS_PENDING and process it in a positive case */ bool (*is_status_pending)(char *, struct TCP_Server_Info *, int); + /* check for STATUS_NETWORK_SESSION_EXPIRED */ + bool (*is_session_expired)(char *); /* send oplock break response */ int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *, struct cifsInodeInfo *); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 8e1a17c64ddd..b2218b755dab 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1458,6 +1458,13 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) return length; server->total_read += length; + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + cifs_reconnect(server); + wake_up(&server->response_q); + return -1; + } + if (server->ops->is_status_pending && server->ops->is_status_pending(buf, server, 0)) { discard_remaining_data(server); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 43df8c3e026c..43bb9e2c81a5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -839,6 +839,13 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_dump_mem("Bad SMB: ", buf, min_t(unsigned int, server->total_read, 48)); + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + cifs_reconnect(server); + wake_up(&server->response_q); + return -1; + } + if (server->ops->is_status_pending && server->ops->is_status_pending(buf, server, length)) return -1; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 881af94ac68f..d23a41652b4c 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -896,6 +896,18 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length) return true; } +static bool +smb2_is_session_expired(char *buf) +{ + struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + + if (hdr->Status != STATUS_NETWORK_SESSION_EXPIRED) + return false; + + cifs_dbg(FYI, "Session expired\n"); + return true; +} + static int smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, struct cifsInodeInfo *cinode) @@ -1424,6 +1436,7 @@ struct smb_version_operations smb20_operations = { .close_dir = smb2_close_dir, .calc_smb_size = smb2_calc_size, .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, .oplock_response = smb2_oplock_response, .queryfs = smb2_queryfs, .mand_lock = smb2_mand_lock, @@ -1505,6 +1518,7 @@ struct smb_version_operations smb21_operations = { .close_dir = smb2_close_dir, .calc_smb_size = smb2_calc_size, .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, .oplock_response = smb2_oplock_response, .queryfs = smb2_queryfs, .mand_lock = smb2_mand_lock, @@ -1587,6 +1601,7 @@ struct smb_version_operations smb30_operations = { .close_dir = smb2_close_dir, .calc_smb_size = smb2_calc_size, .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, .oplock_response = smb2_oplock_response, .queryfs = smb2_queryfs, .mand_lock = smb2_mand_lock, -- GitLab From f4cf5d75416ae3d79e03179fe6f4b9f1231ae42c Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Thu, 28 Sep 2017 19:16:30 +0900 Subject: [PATCH 1238/5498] HID: usbhid: fix out-of-bounds bug commit f043bfc98c193c284e2cd768fefabe18ac2fed9b upstream. The hid descriptor identifies the length and type of subordinate descriptors for a device. If the received hid descriptor is smaller than the size of the struct hid_descriptor, it is possible to cause out-of-bounds. In addition, if bNumDescriptors of the hid descriptor have an incorrect value, this can also cause out-of-bounds while approaching hdesc->desc[n]. So check the size of hid descriptor and bNumDescriptors. BUG: KASAN: slab-out-of-bounds in usbhid_parse+0x9b1/0xa20 Read of size 1 at addr ffff88006c5f8edf by task kworker/1:2/1261 CPU: 1 PID: 1261 Comm: kworker/1:2 Not tainted 4.14.0-rc1-42251-gebb2c2437d80 #169 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Workqueue: usb_hub_wq hub_event Call Trace: __dump_stack lib/dump_stack.c:16 dump_stack+0x292/0x395 lib/dump_stack.c:52 print_address_description+0x78/0x280 mm/kasan/report.c:252 kasan_report_error mm/kasan/report.c:351 kasan_report+0x22f/0x340 mm/kasan/report.c:409 __asan_report_load1_noabort+0x19/0x20 mm/kasan/report.c:427 usbhid_parse+0x9b1/0xa20 drivers/hid/usbhid/hid-core.c:1004 hid_add_device+0x16b/0xb30 drivers/hid/hid-core.c:2944 usbhid_probe+0xc28/0x1100 drivers/hid/usbhid/hid-core.c:1369 usb_probe_interface+0x35d/0x8e0 drivers/usb/core/driver.c:361 really_probe drivers/base/dd.c:413 driver_probe_device+0x610/0xa00 drivers/base/dd.c:557 __device_attach_driver+0x230/0x290 drivers/base/dd.c:653 bus_for_each_drv+0x161/0x210 drivers/base/bus.c:463 __device_attach+0x26e/0x3d0 drivers/base/dd.c:710 device_initial_probe+0x1f/0x30 drivers/base/dd.c:757 bus_probe_device+0x1eb/0x290 drivers/base/bus.c:523 device_add+0xd0b/0x1660 drivers/base/core.c:1835 usb_set_configuration+0x104e/0x1870 drivers/usb/core/message.c:1932 generic_probe+0x73/0xe0 drivers/usb/core/generic.c:174 usb_probe_device+0xaf/0xe0 drivers/usb/core/driver.c:266 really_probe drivers/base/dd.c:413 driver_probe_device+0x610/0xa00 drivers/base/dd.c:557 __device_attach_driver+0x230/0x290 drivers/base/dd.c:653 bus_for_each_drv+0x161/0x210 drivers/base/bus.c:463 __device_attach+0x26e/0x3d0 drivers/base/dd.c:710 device_initial_probe+0x1f/0x30 drivers/base/dd.c:757 bus_probe_device+0x1eb/0x290 drivers/base/bus.c:523 device_add+0xd0b/0x1660 drivers/base/core.c:1835 usb_new_device+0x7b8/0x1020 drivers/usb/core/hub.c:2457 hub_port_connect drivers/usb/core/hub.c:4903 hub_port_connect_change drivers/usb/core/hub.c:5009 port_event drivers/usb/core/hub.c:5115 hub_event+0x194d/0x3740 drivers/usb/core/hub.c:5195 process_one_work+0xc7f/0x1db0 kernel/workqueue.c:2119 worker_thread+0x221/0x1850 kernel/workqueue.c:2253 kthread+0x3a1/0x470 kernel/kthread.c:231 ret_from_fork+0x2a/0x40 arch/x86/entry/entry_64.S:431 Reported-by: Andrey Konovalov Signed-off-by: Jaejoong Kim Tested-by: Andrey Konovalov Acked-by: Alan Stern Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/usbhid/hid-core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index d3fd973e8776..c96fbb8c8046 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -959,6 +959,8 @@ static int usbhid_parse(struct hid_device *hid) unsigned int rsize = 0; char *rdesc; int ret, n; + int num_descriptors; + size_t offset = offsetof(struct hid_descriptor, desc); quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); @@ -981,10 +983,18 @@ static int usbhid_parse(struct hid_device *hid) return -ENODEV; } + if (hdesc->bLength < sizeof(struct hid_descriptor)) { + dbg_hid("hid descriptor is too short\n"); + return -EINVAL; + } + hid->version = le16_to_cpu(hdesc->bcdHID); hid->country = hdesc->bCountryCode; - for (n = 0; n < hdesc->bNumDescriptors; n++) + num_descriptors = min_t(int, hdesc->bNumDescriptors, + (hdesc->bLength - offset) / sizeof(struct hid_class_descriptor)); + + for (n = 0; n < num_descriptors; n++) if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); -- GitLab From 43c9d794871f95464fe6d1c6c1f5adc4c23b047d Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 9 Oct 2017 23:30:02 +0800 Subject: [PATCH 1239/5498] crypto: shash - Fix zero-length shash ahash digest crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b61907bb42409adf9b3120f741af7c57dd7e3db2 upstream. The shash ahash digest adaptor function may crash if given a zero-length input together with a null SG list. This is because it tries to read the SG list before looking at the length. This patch fixes it by checking the length first. Reported-by: Stephan Müller Signed-off-by: Herbert Xu Tested-by: Stephan Müller Signed-off-by: Greg Kroah-Hartman --- crypto/shash.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crypto/shash.c b/crypto/shash.c index 03fbcd4a82c4..17510eaf0a36 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -274,12 +274,14 @@ static int shash_async_finup(struct ahash_request *req) int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc) { - struct scatterlist *sg = req->src; - unsigned int offset = sg->offset; unsigned int nbytes = req->nbytes; + struct scatterlist *sg; + unsigned int offset; int err; - if (nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset)) { + if (nbytes && + (sg = req->src, offset = sg->offset, + nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset))) { void *data; data = kmap_atomic(sg_page(sg)); -- GitLab From e4a25d305457fcb7119ceaa94f85c1a82d1e0076 Mon Sep 17 00:00:00 2001 From: Haozhong Zhang Date: Tue, 10 Oct 2017 15:01:22 +0800 Subject: [PATCH 1240/5498] KVM: nVMX: fix guest CR4 loading when emulating L2 to L1 exit commit 8eb3f87d903168bdbd1222776a6b1e281f50513e upstream. When KVM emulates an exit from L2 to L1, it loads L1 CR4 into the guest CR4. Before this CR4 loading, the guest CR4 refers to L2 CR4. Because these two CR4's are in different levels of guest, we should vmx_set_cr4() rather than kvm_set_cr4() here. The latter, which is used to handle guest writes to its CR4, checks the guest change to CR4 and may fail if the change is invalid. The failure may cause trouble. Consider we start a L1 guest with non-zero L1 PCID in use, (i.e. L1 CR4.PCIDE == 1 && L1 CR3.PCID != 0) and a L2 guest with L2 PCID disabled, (i.e. L2 CR4.PCIDE == 0) and following events may happen: 1. If kvm_set_cr4() is used in load_vmcs12_host_state() to load L1 CR4 into guest CR4 (in VMCS01) for L2 to L1 exit, it will fail because of PCID check. As a result, the guest CR4 recorded in L0 KVM (i.e. vcpu->arch.cr4) is left to the value of L2 CR4. 2. Later, if L1 attempts to change its CR4, e.g., clearing VMXE bit, kvm_set_cr4() in L0 KVM will think L1 also wants to enable PCID, because the wrong L2 CR4 is used by L0 KVM as L1 CR4. As L1 CR3.PCID != 0, L0 KVM will inject GP to L1 guest. Fixes: 4704d0befb072 ("KVM: nVMX: Exiting from L2 to L1") Cc: qemu-stable@nongnu.org Signed-off-by: Haozhong Zhang Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index dd3ca375bfdf..a9ca10baf936 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -8904,7 +8904,7 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, * (KVM doesn't change it)- no reason to call set_cr4_guest_host_mask(); */ vcpu->arch.cr4_guest_owned_bits = ~vmcs_readl(CR4_GUEST_HOST_MASK); - kvm_set_cr4(vcpu, vmcs12->host_cr4); + vmx_set_cr4(vcpu, vmcs12->host_cr4); nested_ept_uninit_mmu_context(vcpu); -- GitLab From 2b15a69dc20c9c7d29074038ad16a118abb40d7f Mon Sep 17 00:00:00 2001 From: Kazuya Mizuguchi Date: Mon, 2 Oct 2017 14:01:41 +0900 Subject: [PATCH 1241/5498] usb: renesas_usbhs: Fix DMAC sequence for receiving zero-length packet commit 29c7f3e68eec4ae94d85ad7b5dfdafdb8089f513 upstream. The DREQE bit of the DnFIFOSEL should be set to 1 after the DE bit of USB-DMAC on R-Car SoCs is set to 1 after the USB-DMAC received a zero-length packet. Otherwise, a transfer completion interruption of USB-DMAC doesn't happen. Even if the driver changes the sequence, normal operations (transmit/receive without zero-length packet) will not cause any side-effects. So, this patch fixes the sequence anyway. Signed-off-by: Kazuya Mizuguchi [shimoda: revise the commit log] Fixes: e73a9891b3a1 ("usb: renesas_usbhs: add DMAEngine support") Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/fifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 75d9ba91e7c5..a8230472d66c 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -880,9 +880,9 @@ static void xfer_work(struct work_struct *work) fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); usbhs_pipe_running(pipe, 1); - usbhsf_dma_start(pipe, fifo); usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); dma_async_issue_pending(chan); + usbhsf_dma_start(pipe, fifo); usbhs_pipe_enable(pipe); xfer_work_end: -- GitLab From 4207aada9b813b36d876ab497953e46c94d7b215 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 13 Oct 2017 14:32:37 +0200 Subject: [PATCH 1242/5498] iommu/amd: Finish TLB flush in amd_iommu_unmap() commit ce76353f169a6471542d999baf3d29b121dce9c0 upstream. The function only sends the flush command to the IOMMU(s), but does not wait for its completion when it returns. Fix that. Fixes: 601367d76bd1 ('x86/amd-iommu: Remove iommu_flush_domain function') Signed-off-by: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/amd_iommu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index af3daf89c77d..5cf388ad1555 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3381,6 +3381,7 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, mutex_unlock(&domain->api_lock); domain_flush_tlb_pde(domain); + domain_flush_complete(domain); return unmap_size; } -- GitLab From 9d263dba25284b31f2544970964decf65b79a2e8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Oct 2017 14:10:32 +0200 Subject: [PATCH 1243/5498] ALSA: usb-audio: Kill stray URB at exiting commit 124751d5e63c823092060074bd0abaae61aaa9c4 upstream. USB-audio driver may leave a stray URB for the mixer interrupt when it exits by some error during probe. This leads to a use-after-free error as spotted by syzkaller like: ================================================================== BUG: KASAN: use-after-free in snd_usb_mixer_interrupt+0x604/0x6f0 Call Trace: __dump_stack lib/dump_stack.c:16 dump_stack+0x292/0x395 lib/dump_stack.c:52 print_address_description+0x78/0x280 mm/kasan/report.c:252 kasan_report_error mm/kasan/report.c:351 kasan_report+0x23d/0x350 mm/kasan/report.c:409 __asan_report_load8_noabort+0x19/0x20 mm/kasan/report.c:430 snd_usb_mixer_interrupt+0x604/0x6f0 sound/usb/mixer.c:2490 __usb_hcd_giveback_urb+0x2e0/0x650 drivers/usb/core/hcd.c:1779 .... Allocated by task 1484: save_stack_trace+0x1b/0x20 arch/x86/kernel/stacktrace.c:59 save_stack+0x43/0xd0 mm/kasan/kasan.c:447 set_track mm/kasan/kasan.c:459 kasan_kmalloc+0xad/0xe0 mm/kasan/kasan.c:551 kmem_cache_alloc_trace+0x11e/0x2d0 mm/slub.c:2772 kmalloc ./include/linux/slab.h:493 kzalloc ./include/linux/slab.h:666 snd_usb_create_mixer+0x145/0x1010 sound/usb/mixer.c:2540 create_standard_mixer_quirk+0x58/0x80 sound/usb/quirks.c:516 snd_usb_create_quirk+0x92/0x100 sound/usb/quirks.c:560 create_composite_quirk+0x1c4/0x3e0 sound/usb/quirks.c:59 snd_usb_create_quirk+0x92/0x100 sound/usb/quirks.c:560 usb_audio_probe+0x1040/0x2c10 sound/usb/card.c:618 .... Freed by task 1484: save_stack_trace+0x1b/0x20 arch/x86/kernel/stacktrace.c:59 save_stack+0x43/0xd0 mm/kasan/kasan.c:447 set_track mm/kasan/kasan.c:459 kasan_slab_free+0x72/0xc0 mm/kasan/kasan.c:524 slab_free_hook mm/slub.c:1390 slab_free_freelist_hook mm/slub.c:1412 slab_free mm/slub.c:2988 kfree+0xf6/0x2f0 mm/slub.c:3919 snd_usb_mixer_free+0x11a/0x160 sound/usb/mixer.c:2244 snd_usb_mixer_dev_free+0x36/0x50 sound/usb/mixer.c:2250 __snd_device_free+0x1ff/0x380 sound/core/device.c:91 snd_device_free_all+0x8f/0xe0 sound/core/device.c:244 snd_card_do_free sound/core/init.c:461 release_card_device+0x47/0x170 sound/core/init.c:181 device_release+0x13f/0x210 drivers/base/core.c:814 .... Actually such a URB is killed properly at disconnection when the device gets probed successfully, and what we need is to apply it for the error-path, too. In this patch, we apply snd_usb_mixer_disconnect() at releasing. Also introduce a new flag, disconnected, to struct usb_mixer_interface for not performing the disconnection procedure twice. Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer.c | 12 ++++++++++-- sound/usb/mixer.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 1ceeb9e875a4..d0bbc7b32190 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2157,6 +2157,9 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) static void snd_usb_mixer_free(struct usb_mixer_interface *mixer) { + /* kill pending URBs */ + snd_usb_mixer_disconnect(&mixer->list); + kfree(mixer->id_elems); if (mixer->urb) { kfree(mixer->urb->transfer_buffer); @@ -2493,8 +2496,13 @@ void snd_usb_mixer_disconnect(struct list_head *p) struct usb_mixer_interface *mixer; mixer = list_entry(p, struct usb_mixer_interface, list); - usb_kill_urb(mixer->urb); - usb_kill_urb(mixer->rc_urb); + if (mixer->disconnected) + return; + if (mixer->urb) + usb_kill_urb(mixer->urb); + if (mixer->rc_urb) + usb_kill_urb(mixer->rc_urb); + mixer->disconnected = true; } #ifdef CONFIG_PM diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 73b1f649447b..cdff31de12d4 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -23,6 +23,7 @@ struct usb_mixer_interface { u8 audigy2nx_leds[3]; u8 xonar_u1_status; + bool disconnected; }; #define MAX_CHANNELS 16 /* max logical channels */ -- GitLab From 035e6d0b5b192ff5e168ed322304d29db108d790 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 9 Oct 2017 11:09:20 +0200 Subject: [PATCH 1244/5498] ALSA: seq: Fix use-after-free at creating a port commit 71105998845fb012937332fe2e806d443c09e026 upstream. There is a potential race window opened at creating and deleting a port via ioctl, as spotted by fuzzing. snd_seq_create_port() creates a port object and returns its pointer, but it doesn't take the refcount, thus it can be deleted immediately by another thread. Meanwhile, snd_seq_ioctl_create_port() still calls the function snd_seq_system_client_ev_port_start() with the created port object that is being deleted, and this triggers use-after-free like: BUG: KASAN: use-after-free in snd_seq_ioctl_create_port+0x504/0x630 [snd_seq] at addr ffff8801f2241cb1 ============================================================================= BUG kmalloc-512 (Tainted: G B ): kasan: bad access detected ----------------------------------------------------------------------------- INFO: Allocated in snd_seq_create_port+0x94/0x9b0 [snd_seq] age=1 cpu=3 pid=4511 ___slab_alloc+0x425/0x460 __slab_alloc+0x20/0x40 kmem_cache_alloc_trace+0x150/0x190 snd_seq_create_port+0x94/0x9b0 [snd_seq] snd_seq_ioctl_create_port+0xd1/0x630 [snd_seq] snd_seq_do_ioctl+0x11c/0x190 [snd_seq] snd_seq_ioctl+0x40/0x80 [snd_seq] do_vfs_ioctl+0x54b/0xda0 SyS_ioctl+0x79/0x90 entry_SYSCALL_64_fastpath+0x16/0x75 INFO: Freed in port_delete+0x136/0x1a0 [snd_seq] age=1 cpu=2 pid=4717 __slab_free+0x204/0x310 kfree+0x15f/0x180 port_delete+0x136/0x1a0 [snd_seq] snd_seq_delete_port+0x235/0x350 [snd_seq] snd_seq_ioctl_delete_port+0xc8/0x180 [snd_seq] snd_seq_do_ioctl+0x11c/0x190 [snd_seq] snd_seq_ioctl+0x40/0x80 [snd_seq] do_vfs_ioctl+0x54b/0xda0 SyS_ioctl+0x79/0x90 entry_SYSCALL_64_fastpath+0x16/0x75 Call Trace: [] dump_stack+0x63/0x82 [] print_trailer+0xfb/0x160 [] object_err+0x34/0x40 [] kasan_report.part.2+0x223/0x520 [] ? snd_seq_ioctl_create_port+0x504/0x630 [snd_seq] [] __asan_report_load1_noabort+0x2e/0x30 [] snd_seq_ioctl_create_port+0x504/0x630 [snd_seq] [] ? snd_seq_ioctl_delete_port+0x180/0x180 [snd_seq] [] ? taskstats_exit+0xbc0/0xbc0 [] snd_seq_do_ioctl+0x11c/0x190 [snd_seq] [] snd_seq_ioctl+0x40/0x80 [snd_seq] [] ? acct_account_cputime+0x63/0x80 [] do_vfs_ioctl+0x54b/0xda0 ..... We may fix this in a few different ways, and in this patch, it's fixed simply by taking the refcount properly at snd_seq_create_port() and letting the caller unref the object after use. Also, there is another potential use-after-free by sprintf() call in snd_seq_create_port(), and this is moved inside the lock. This fix covers CVE-2017-15265. Reported-and-tested-by: Michael23 Yu Suggested-by: Linus Torvalds Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_clientmgr.c | 6 +++++- sound/core/seq/seq_ports.c | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 4a240416b830..41347ad1c4d5 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1260,6 +1260,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, struct snd_seq_client_port *port; struct snd_seq_port_info info; struct snd_seq_port_callback *callback; + int port_idx; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; @@ -1273,7 +1274,9 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, return -ENOMEM; if (client->type == USER_CLIENT && info.kernel) { - snd_seq_delete_port(client, port->addr.port); + port_idx = port->addr.port; + snd_seq_port_unlock(port); + snd_seq_delete_port(client, port_idx); return -EINVAL; } if (client->type == KERNEL_CLIENT) { @@ -1295,6 +1298,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, snd_seq_set_port_info(port, &info); snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); + snd_seq_port_unlock(port); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 9c1c8d50f593..1ddae911255c 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -122,7 +122,9 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp) } -/* create a port, port number is returned (-1 on failure) */ +/* create a port, port number is returned (-1 on failure); + * the caller needs to unref the port via snd_seq_port_unlock() appropriately + */ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, int port) { @@ -151,6 +153,7 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, snd_use_lock_init(&new_port->use_lock); port_subs_info_init(&new_port->c_src); port_subs_info_init(&new_port->c_dest); + snd_use_lock_use(&new_port->use_lock); num = port >= 0 ? port : 0; mutex_lock(&client->ports_mutex); @@ -165,9 +168,9 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, list_add_tail(&new_port->list, &p->list); client->num_ports++; new_port->addr.port = num; /* store the port number in the port */ + sprintf(new_port->name, "port-%d", num); write_unlock_irqrestore(&client->ports_lock, flags); mutex_unlock(&client->ports_mutex); - sprintf(new_port->name, "port-%d", num); return new_port; } -- GitLab From f12baf790258076b7f1f5b12e809124eafb730b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 9 Oct 2017 10:02:56 +0200 Subject: [PATCH 1245/5498] ALSA: seq: Fix copy_from_user() call inside lock commit 5803b023881857db32ffefa0d269c90280a67ee0 upstream. The event handler in the virmidi sequencer code takes a read-lock for the linked list traverse, while it's calling snd_seq_dump_var_event() in the loop. The latter function may expand the user-space data depending on the event type. It eventually invokes copy_from_user(), which might be a potential dead-lock. The sequencer core guarantees that the user-space data is passed only with atomic=0 argument, but snd_virmidi_dev_receive_event() ignores it and always takes read-lock(). For avoiding the problem above, this patch introduces rwsem for non-atomic case, while keeping rwlock for atomic case. Also while we're at it: the superfluous irq flags is dropped in snd_virmidi_input_open(). Reported-by: Jia-Ju Bai Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- include/sound/seq_virmidi.h | 1 + sound/core/seq/seq_virmidi.c | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h index a03acd0d398a..695257ae64ac 100644 --- a/include/sound/seq_virmidi.h +++ b/include/sound/seq_virmidi.h @@ -60,6 +60,7 @@ struct snd_virmidi_dev { int port; /* created/attached port */ unsigned int flags; /* SNDRV_VIRMIDI_* */ rwlock_t filelist_lock; + struct rw_semaphore filelist_sem; struct list_head filelist; }; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 81134e067184..3b126af4a026 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -77,13 +77,17 @@ static void snd_virmidi_init_event(struct snd_virmidi *vmidi, * decode input event and put to read buffer of each opened file */ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, - struct snd_seq_event *ev) + struct snd_seq_event *ev, + bool atomic) { struct snd_virmidi *vmidi; unsigned char msg[4]; int len; - read_lock(&rdev->filelist_lock); + if (atomic) + read_lock(&rdev->filelist_lock); + else + down_read(&rdev->filelist_sem); list_for_each_entry(vmidi, &rdev->filelist, list) { if (!vmidi->trigger) continue; @@ -97,7 +101,10 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, snd_rawmidi_receive(vmidi->substream, msg, len); } } - read_unlock(&rdev->filelist_lock); + if (atomic) + read_unlock(&rdev->filelist_lock); + else + up_read(&rdev->filelist_sem); return 0; } @@ -115,7 +122,7 @@ int snd_virmidi_receive(struct snd_rawmidi *rmidi, struct snd_seq_event *ev) struct snd_virmidi_dev *rdev; rdev = rmidi->private_data; - return snd_virmidi_dev_receive_event(rdev, ev); + return snd_virmidi_dev_receive_event(rdev, ev, true); } #endif /* 0 */ @@ -130,7 +137,7 @@ static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct, rdev = private_data; if (!(rdev->flags & SNDRV_VIRMIDI_USE)) return 0; /* ignored */ - return snd_virmidi_dev_receive_event(rdev, ev); + return snd_virmidi_dev_receive_event(rdev, ev, atomic); } /* @@ -209,7 +216,6 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream) struct snd_virmidi_dev *rdev = substream->rmidi->private_data; struct snd_rawmidi_runtime *runtime = substream->runtime; struct snd_virmidi *vmidi; - unsigned long flags; vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL); if (vmidi == NULL) @@ -223,9 +229,11 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream) vmidi->client = rdev->client; vmidi->port = rdev->port; runtime->private_data = vmidi; - write_lock_irqsave(&rdev->filelist_lock, flags); + down_write(&rdev->filelist_sem); + write_lock_irq(&rdev->filelist_lock); list_add_tail(&vmidi->list, &rdev->filelist); - write_unlock_irqrestore(&rdev->filelist_lock, flags); + write_unlock_irq(&rdev->filelist_lock); + up_write(&rdev->filelist_sem); vmidi->rdev = rdev; return 0; } @@ -264,9 +272,11 @@ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream) struct snd_virmidi_dev *rdev = substream->rmidi->private_data; struct snd_virmidi *vmidi = substream->runtime->private_data; + down_write(&rdev->filelist_sem); write_lock_irq(&rdev->filelist_lock); list_del(&vmidi->list); write_unlock_irq(&rdev->filelist_lock); + up_write(&rdev->filelist_sem); snd_midi_event_free(vmidi->parser); substream->runtime->private_data = NULL; kfree(vmidi); @@ -520,6 +530,7 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi rdev->rmidi = rmidi; rdev->device = device; rdev->client = -1; + init_rwsem(&rdev->filelist_sem); rwlock_init(&rdev->filelist_lock); INIT_LIST_HEAD(&rdev->filelist); rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; -- GitLab From c57f813cb8807f07eab5f3048aa3445386c78387 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Oct 2017 16:39:02 +0200 Subject: [PATCH 1246/5498] ALSA: caiaq: Fix stray URB at probe error path commit 99fee508245825765ff60155fed43f970ff83a8f upstream. caiaq driver doesn't kill the URB properly at its error path during the probe, which may lead to a use-after-free error later. This patch addresses it. Reported-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/caiaq/device.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index b871ba407e4e..4458190149d1 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -469,10 +469,12 @@ static int init_card(struct snd_usb_caiaqdev *cdev) err = snd_usb_caiaq_send_command(cdev, EP1_CMD_GET_DEVICE_INFO, NULL, 0); if (err) - return err; + goto err_kill_urb; - if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ)) - return -ENODEV; + if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ)) { + err = -ENODEV; + goto err_kill_urb; + } usb_string(usb_dev, usb_dev->descriptor.iManufacturer, cdev->vendor_name, CAIAQ_USB_STR_LEN); @@ -507,6 +509,10 @@ static int init_card(struct snd_usb_caiaqdev *cdev) setup_card(cdev); return 0; + + err_kill_urb: + usb_kill_urb(&cdev->ep1_in_urb); + return err; } static int snd_probe(struct usb_interface *intf, -- GitLab From a2b295db33d4371bf80cadede1965138a90883db Mon Sep 17 00:00:00 2001 From: Andrew Gabbasov Date: Sat, 30 Sep 2017 08:55:55 -0700 Subject: [PATCH 1247/5498] usb: gadget: composite: Fix use-after-free in usb_composite_overwrite_options commit aec17e1e249567e82b26dafbb86de7d07fde8729 upstream. KASAN enabled configuration reports an error BUG: KASAN: use-after-free in usb_composite_overwrite_options+... [libcomposite] at addr ... Read of size 1 by task ... when some driver is un-bound and then bound again. For example, this happens with FunctionFS driver when "ffs-test" test application is run several times in a row. If the driver has empty manufacturer ID string in initial static data, it is then replaced with generated string. After driver unbinding the generated string is freed, but the driver data still keep that pointer. And if the driver is then bound again, that pointer is re-used for string emptiness check. The fix is to clean up the driver string data upon its unbinding to drop the pointer to freed memory. Fixes: cc2683c318a5 ("usb: gadget: Provide a default implementation of default manufacturer string") Signed-off-by: Andrew Gabbasov Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 7b2ba34cf5e7..f24adf48b0cc 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1806,6 +1806,8 @@ static DEVICE_ATTR_RO(suspended); static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) { struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_gadget_strings *gstr = cdev->driver->strings[0]; + struct usb_string *dev_str = gstr->strings; /* composite_disconnect() must already have been called * by the underlying peripheral controller driver! @@ -1825,6 +1827,9 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) composite_dev_cleanup(cdev); + if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer) + dev_str[USB_GADGET_MANUFACTURER_IDX].s = ""; + kfree(cdev->def_manufacturer); kfree(cdev); set_gadget_data(gadget, NULL); -- GitLab From 2159252e70e95ba59e926423a006b95c313a0eea Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 9 Oct 2017 11:13:18 +0200 Subject: [PATCH 1248/5498] direct-io: Prevent NULL pointer access in submit_page_section commit 899f0429c7d3eed886406cd72182bee3b96aa1f9 upstream. In the code added to function submit_page_section by commit b1058b981, sdio->bio can currently be NULL when calling dio_bio_submit. This then leads to a NULL pointer access in dio_bio_submit, so check for a NULL bio in submit_page_section before trying to submit it instead. Fixes xfstest generic/250 on gfs2. Signed-off-by: Andreas Gruenbacher Reviewed-by: Jan Kara Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/direct-io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/direct-io.c b/fs/direct-io.c index e181b6b2e297..a325d8c5ec2d 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -812,7 +812,8 @@ out: */ if (sdio->boundary) { ret = dio_send_cur_page(dio, sdio, map_bh); - dio_bio_submit(dio, sdio); + if (sdio->bio) + dio_bio_submit(dio, sdio); page_cache_release(sdio->cur_page); sdio->cur_page = NULL; } -- GitLab From d7795bf6a9d8b004dbbd1f081f3eff18ff49276d Mon Sep 17 00:00:00 2001 From: Vitaly Mayatskikh Date: Fri, 22 Sep 2017 01:18:39 -0400 Subject: [PATCH 1249/5498] fix unbalanced page refcounting in bio_map_user_iov commit 95d78c28b5a85bacbc29b8dba7c04babb9b0d467 upstream. bio_map_user_iov and bio_unmap_user do unbalanced pages refcounting if IO vector has small consecutive buffers belonging to the same page. bio_add_pc_page merges them into one, but the page reference is never dropped. Signed-off-by: Vitaly Mayatskikh Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- block/bio.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/block/bio.c b/block/bio.c index 78803e99c154..140c45b2b717 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1338,6 +1338,7 @@ static struct bio *__bio_map_user_iov(struct request_queue *q, offset = uaddr & ~PAGE_MASK; for (j = cur_page; j < page_limit; j++) { unsigned int bytes = PAGE_SIZE - offset; + unsigned short prev_bi_vcnt = bio->bi_vcnt; if (len <= 0) break; @@ -1352,6 +1353,13 @@ static struct bio *__bio_map_user_iov(struct request_queue *q, bytes) break; + /* + * check if vector was merged with previous + * drop page reference if needed + */ + if (bio->bi_vcnt == prev_bi_vcnt) + put_page(pages[j]); + len -= bytes; offset = 0; } -- GitLab From 704e5f08e7af2410fe7b07f568944a8e24966fa4 Mon Sep 17 00:00:00 2001 From: Andreas Engel Date: Mon, 18 Sep 2017 21:11:57 +0200 Subject: [PATCH 1250/5498] USB: serial: cp210x: add support for ELV TFD500 commit c496ad835c31ad639b6865714270b3003df031f6 upstream. Add the USB device id for the ELV TFD500 data logger. Signed-off-by: Andreas Engel Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index a8051aad885c..c454c2fec2f7 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -168,6 +168,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */ + { USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */ { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */ { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */ -- GitLab From 740ffa11b45a27415204f674e975f2b3dae1afc1 Mon Sep 17 00:00:00 2001 From: Henryk Heisig Date: Mon, 11 Sep 2017 17:57:34 +0200 Subject: [PATCH 1251/5498] USB: serial: option: add support for TP-Link LTE module commit 837ddc4793a69b256ac5e781a5e729b448a8d983 upstream. This commit adds support for TP-Link LTE mPCIe module is used in in TP-Link MR200v1, MR6400v1 and v2 routers. Signed-off-by: Henryk Heisig Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fdd5eff72c31..71d032822cd9 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -515,6 +515,7 @@ static void option_instat_callback(struct urb *urb); /* TP-LINK Incorporated products */ #define TPLINK_VENDOR_ID 0x2357 +#define TPLINK_PRODUCT_LTE 0x000D #define TPLINK_PRODUCT_MA180 0x0201 /* Changhong products */ @@ -2006,6 +2007,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) }, { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600A) }, { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) }, + { USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) }, /* TP-Link LTE Module */ { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(TPLINK_VENDOR_ID, 0x9000), /* TP-Link MA260 */ -- GitLab From 2d0ffe43f2b9c99ca1dd67d4911e62c83487362c Mon Sep 17 00:00:00 2001 From: Shrirang Bagul Date: Fri, 29 Sep 2017 12:39:51 +0800 Subject: [PATCH 1252/5498] USB: serial: qcserial: add Dell DW5818, DW5819 commit f5d9644c5fca7d8e8972268598bb516a7eae17f9 upstream. Dell Wireless 5819/5818 devices are re-branded Sierra Wireless MC74 series which will by default boot with vid 0x413c and pid's 0x81cf, 0x81d0, 0x81d1, 0x81d2. Signed-off-by: Shrirang Bagul Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index ec8d1410fe5a..2a2ff0ba9f7f 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -168,6 +168,10 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */ {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */ {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */ + {DEVICE_SWI(0x413c, 0x81cf)}, /* Dell Wireless 5819 */ + {DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */ + {DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */ + {DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */ /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ -- GitLab From 8ae04f638496b7a9b0381aecf5c47dcb5a3bd6fc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Oct 2017 11:01:13 +0200 Subject: [PATCH 1253/5498] USB: serial: console: fix use-after-free after failed setup commit 299d7572e46f98534033a9e65973f13ad1ce9047 upstream. Make sure to reset the USB-console port pointer when console setup fails in order to avoid having the struct usb_serial be prematurely freed by the console code when the device is later disconnected. Fixes: 73e487fdb75f ("[PATCH] USB console: fix disconnection issues") Acked-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/console.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 29fa1c3d0089..926c1f2dc962 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -187,6 +187,7 @@ static int usb_console_setup(struct console *co, char *options) tty_kref_put(tty); reset_open_count: port->port.count = 0; + info->port = NULL; usb_autopm_put_interface(serial->interface); error_get_interface: usb_serial_put(serial); -- GitLab From 954e2ed41fec684048824230eb51bed0eae449ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Jan 2016 12:38:02 +0100 Subject: [PATCH 1254/5498] ALSA: seq: Fix missing NULL check at remove_events ioctl commit 030e2c78d3a91dd0d27fef37e91950dde333eba1 upstream. snd_seq_ioctl_remove_events() calls snd_seq_fifo_clear() unconditionally even if there is no FIFO assigned, and this leads to an Oops due to NULL dereference. The fix is just to add a proper NULL check. Reported-by: Dmitry Vyukov Tested-by: Dmitry Vyukov Signed-off-by: Takashi Iwai Cc: Mark Salyzyn Cc: Dmitry Vyukov Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_clientmgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 41347ad1c4d5..a3988a4bcfd6 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1970,7 +1970,7 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client, * No restrictions so for a user client we can clear * the whole fifo */ - if (client->type == USER_CLIENT) + if (client->type == USER_CLIENT && client->data.user.fifo) snd_seq_fifo_clear(client->data.user.fifo); } -- GitLab From cbe4d8c077e7ad4345c12bdded0c54f9e4979a9f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 17 Oct 2017 16:12:49 +0200 Subject: [PATCH 1255/5498] Revert "usb: gadget: inode.c: fix unbalanced spin_lock in ep0_write" This reverts commit abb540b5397674243994c5327146b6fed7339b71 which is commit b7bd98b7db9f upstream. I had added it to make another patch apply cleanly, but as Ben points out, that was wrong. Reported-by: Ben Hutchings Reported-by: kbuild test robot Cc: David Eccher Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/usb/gadget/legacy/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 352c59cff5b6..368d07937848 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1231,10 +1231,11 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) dev->gadget->ep0, dev->req, GFP_KERNEL); } - spin_lock_irq(&dev->lock); --dev->udc_usage; if (retval < 0) { + spin_lock_irq (&dev->lock); clean_req (dev->gadget->ep0, dev->req); + spin_unlock_irq (&dev->lock); } else retval = len; -- GitLab From 1e8e6b30d014eb77278edb4cd9bee72f0255996f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 18 Oct 2017 09:15:17 +0200 Subject: [PATCH 1256/5498] Linux 3.18.76 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 63a173e41f22..22c320078a98 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 75 +SUBLEVEL = 76 EXTRAVERSION = NAME = Diseased Newt -- GitLab From f1f639e29aa33aa77855153b5aa984656e78c389 Mon Sep 17 00:00:00 2001 From: Prashanth Vadde Date: Wed, 18 Oct 2017 12:58:00 +0530 Subject: [PATCH 1257/5498] defconfig: msm: Enable TLC591xx LED Driver on APQ8017-perf Enable TLC591xx LED Driver on APQ8017 perf build as well Change-Id: Ic417907e8d755af1ad182d18899282087ea6f3ad Signed-off-by: Prashanth Vadde --- arch/arm64/configs/msm8937-perf_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig index 8b8d1b2810ea..88d6f93335e8 100644 --- a/arch/arm64/configs/msm8937-perf_defconfig +++ b/arch/arm64/configs/msm8937-perf_defconfig @@ -490,6 +490,7 @@ CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_MSM=y CONFIG_MMC_SDHCI_MSM_ICE=y CONFIG_MMC_CQ_HCI=y +CONFIG_LEDS_TLC591XX=y CONFIG_LEDS_QPNP=y CONFIG_LEDS_QPNP_FLASH=y CONFIG_LEDS_QPNP_WLED=y -- GitLab From c2fd7892ca420e587f63d27676d84db631cf678f Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Thu, 24 Aug 2017 11:15:12 -0700 Subject: [PATCH 1258/5498] msm: mhi_dev: Support device reset When host issues a MHI (modem host interface) device reset the existing cache context for channel, events and command needs to be invalidated. IPA needs to be released including rest of hardware channels and device needs to prepare to perform a fresh start. Abort all pending software data transfers, reset the MHI state and channels, reset MMIO. Once host detects complete device reset it re-initializes MHI MMIO and device needs to cache the new context and prepare for data transfers. Change-Id: I20c4feb03cbcc56da070848e92294c70c31e0575 Signed-off-by: Siddartha Mohanadoss --- drivers/platform/msm/mhi_dev/mhi.c | 279 ++++++++++++++++++++++-- drivers/platform/msm/mhi_dev/mhi.h | 6 +- drivers/platform/msm/mhi_dev/mhi_mmio.c | 6 +- drivers/platform/msm/mhi_dev/mhi_sm.c | 23 +- drivers/platform/msm/mhi_dev/mhi_sm.h | 3 +- 5 files changed, 292 insertions(+), 25 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index 2e95f6c0a5cc..075f9331a73a 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -37,8 +37,8 @@ /* Wait time on the device for Host to set M0 state */ #define MHI_DEV_M0_MAX_CNT 30 /* Wait time before suspend/resume is complete */ -#define MHI_SUSPEND_MIN 10000 -#define MHI_SUSPEND_TIMEOUT 3000 +#define MHI_SUSPEND_MIN 100 +#define MHI_SUSPEND_TIMEOUT 600 #define MHI_MASK_CH_EV_LEN 32 #define MHI_RING_CMD_ID 0 #define MHI_RING_PRIMARY_EVT_ID 1 @@ -58,6 +58,8 @@ #define MHI_IPC_LOG_PAGES (100) #define MHI_REGLEN 0x100 +#define MHI_INIT 0 +#define MHI_REINIT 1 enum mhi_msg_level mhi_msg_lvl = MHI_MSG_ERROR; enum mhi_msg_level mhi_ipc_msg_lvl = MHI_MSG_VERBOSE; @@ -68,6 +70,8 @@ static void mhi_hwc_cb(void *priv, enum ipa_mhi_event_type event, unsigned long data); static void mhi_ring_init_cb(void *user_data); static void mhi_update_state_info(uint32_t info); +static int mhi_deinit(struct mhi_dev *mhi); +static void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify); void mhi_dev_read_from_host(struct mhi_dev *mhi, struct mhi_addr *transfer) { @@ -386,6 +390,9 @@ static void mhi_hwc_cb(void *priv, enum ipa_mhi_event_type event, } mhi_update_state_info(MHI_STATE_CONNECTED); + + ep_pcie_mask_irq_event(mhi_ctx->phandle, + EP_PCIE_INT_EVT_MHI_A7, true); break; case IPA_MHI_EVENT_DATA_AVAILABLE: rc = mhi_dev_notify_sm_event(MHI_DEV_EVENT_HW_ACC_WAKEUP); @@ -1087,6 +1094,82 @@ static void mhi_dev_check_channel_interrupt(struct mhi_dev *mhi) } } +static int mhi_dev_abort(struct mhi_dev *mhi) +{ + struct mhi_dev_channel *ch; + struct mhi_dev_ring *ring; + int ch_id = 0, rc = 0; + + /* Hard stop all the channels */ + for (ch_id = 0; ch_id < mhi->cfg.channels; ch_id++) { + ring = &mhi->ring[ch_id + mhi->ch_ring_start]; + if (ring->state == RING_STATE_UINT) + continue; + + ch = &mhi->ch[ch_id]; + mutex_lock(&ch->ch_lock); + mhi->ch[ch_id].state = MHI_DEV_CH_STOPPED; + mutex_unlock(&ch->ch_lock); + } + + /* Update ctrl node */ + mhi_update_state_info(MHI_STATE_DISCONNECTED); + + flush_workqueue(mhi->ring_init_wq); + flush_workqueue(mhi->pending_ring_wq); + + /* Initiate MHI IPA reset */ + ipa_mhi_destroy(); + + /* Clean up initialized channels */ + rc = mhi_deinit(mhi); + if (rc) { + pr_err("Error during mhi_deinit with %d\n", rc); + return rc; + } + + rc = mhi_dev_mmio_mask_chdb_interrupts(mhi_ctx); + if (rc) { + pr_err("Failed to enable channel db\n"); + return rc; + } + + rc = mhi_dev_mmio_disable_ctrl_interrupt(mhi_ctx); + if (rc) { + pr_err("Failed to enable control interrupt\n"); + return rc; + } + + rc = mhi_dev_mmio_disable_cmdb_interrupt(mhi_ctx); + if (rc) { + pr_err("Failed to enable command db\n"); + return rc; + } + + + atomic_set(&mhi_ctx->re_init_done, 0); + + mhi_log(MHI_MSG_INFO, + "Register a PCIe callback during re-init\n"); + mhi_ctx->event_reg.events = EP_PCIE_EVENT_LINKUP; + mhi_ctx->event_reg.user = mhi_ctx; + mhi_ctx->event_reg.mode = EP_PCIE_TRIGGER_CALLBACK; + mhi_ctx->event_reg.callback = mhi_dev_resume_init_with_link_up; + mhi_ctx->event_reg.options = MHI_REINIT; + + rc = ep_pcie_register_event(mhi_ctx->phandle, + &mhi_ctx->event_reg); + if (rc) { + pr_err("Failed to register for events from PCIe\n"); + return rc; + } + + /* Set RESET field to 0 */ + mhi_dev_mmio_reset(mhi_ctx); + + return rc; +} + static void mhi_dev_scheduler(struct work_struct *work) { struct mhi_dev *mhi = container_of(work, @@ -1096,6 +1179,7 @@ static void mhi_dev_scheduler(struct work_struct *work) struct mhi_dev_ring *ring; enum mhi_dev_state state; enum mhi_dev_event event = 0; + bool mhi_reset = false; mutex_lock(&mhi_ctx->mhi_lock); /* Check for interrupts */ @@ -1104,25 +1188,34 @@ static void mhi_dev_scheduler(struct work_struct *work) if (int_value & MHI_MMIO_CTRL_INT_STATUS_A7_MSK) { mhi_log(MHI_MSG_VERBOSE, "processing ctrl interrupt with %d\n", int_value); - rc = mhi_dev_mmio_get_mhi_state(mhi, &state); + rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset); if (rc) { pr_err("%s: get mhi state failed\n", __func__); mutex_unlock(&mhi_ctx->mhi_lock); return; } + if (mhi_reset) { + mhi_log(MHI_MSG_VERBOSE, + "processing mhi device reset\n"); + rc = mhi_dev_abort(mhi); + if (rc) + pr_err("device reset failed:%d\n", rc); + mutex_unlock(&mhi_ctx->mhi_lock); + queue_work(mhi->ring_init_wq, &mhi->re_init); + return; + } + rc = mhi_dev_get_event_notify(state, &event); if (rc) { pr_err("unsupported state :%d\n", state); - mutex_unlock(&mhi_ctx->mhi_lock); - return; + goto fail; } rc = mhi_dev_notify_sm_event(event); if (rc) { pr_err("error sending SM event\n"); - mutex_unlock(&mhi_ctx->mhi_lock); - return; + goto fail; } } @@ -1137,6 +1230,7 @@ static void mhi_dev_scheduler(struct work_struct *work) /* get the specific channel interrupts */ mhi_dev_check_channel_interrupt(mhi); +fail: mutex_unlock(&mhi_ctx->mhi_lock); if (mhi->config_iatu || mhi->mhi_int) @@ -1389,7 +1483,6 @@ int mhi_dev_suspend(struct mhi_dev *mhi) mhi_dev_write_to_host(mhi, &data_transfer); } - mhi_update_state_info(MHI_STATE_DISCONNECTED); atomic_set(&mhi->mhi_dev_wake, 0); pm_relax(mhi->dev); @@ -1616,6 +1709,11 @@ int mhi_dev_read_channel(struct mhi_dev_client *handle_client, return -ENXIO; } + if (mhi_ctx->ctrl_info != MHI_STATE_CONNECTED) { + pr_err("Channel not connected:%d\n", mhi_ctx->ctrl_info); + return -ENODEV; + } + ch = handle_client->channel; ring = ch->ring; ch_id = ch->ch_id; @@ -1735,6 +1833,11 @@ int mhi_dev_write_channel(struct mhi_dev_client *handle_client, return -ENXIO; } + if (mhi_ctx->ctrl_info != MHI_STATE_CONNECTED) { + pr_err("Channel not connected:%d\n", mhi_ctx->ctrl_info); + return -ENODEV; + } + mutex_lock(&mhi_ctx->mhi_write_test); if (atomic_read(&mhi_ctx->is_suspended)) { @@ -1850,7 +1953,7 @@ static void mhi_dev_enable(struct work_struct *work) struct ep_pcie_msi_config msi_cfg; struct mhi_dev *mhi = container_of(work, struct mhi_dev, ring_init_cb_work); - + bool mhi_reset; enum mhi_dev_state state; uint32_t max_cnt = 0, bhi_intvec = 0; @@ -1898,7 +2001,7 @@ static void mhi_dev_enable(struct work_struct *work) } } - rc = mhi_dev_mmio_get_mhi_state(mhi, &state); + rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset); if (rc) { pr_err("%s: get mhi state failed\n", __func__); return; @@ -1907,7 +2010,7 @@ static void mhi_dev_enable(struct work_struct *work) while (state != MHI_DEV_M0_STATE && max_cnt < MHI_SUSPEND_TIMEOUT) { /* Wait for Host to set the M0 state */ msleep(MHI_SUSPEND_MIN); - rc = mhi_dev_mmio_get_mhi_state(mhi, &state); + rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset); if (rc) { pr_err("%s: get mhi state failed\n", __func__); return; @@ -2096,6 +2199,39 @@ static int get_device_tree_data(struct platform_device *pdev) return 0; } +static int mhi_deinit(struct mhi_dev *mhi) +{ + int rc = 0, i = 0, ring_id = 0; + struct mhi_dev_ring *ring; + struct platform_device *pdev = mhi->pdev; + + ring_id = mhi->cfg.channels + mhi->cfg.event_rings + 1; + + for (i = 0; i < ring_id; i++) { + ring = &mhi->ring[i]; + if (ring->state == RING_STATE_UINT) + continue; + + dma_free_coherent(mhi->dev, ring->ring_size * + sizeof(union mhi_dev_ring_element_type), + ring->ring_cache, + ring->ring_cache_dma_handle); + } + + for (i = 0; i < mhi->cfg.channels; i++) + mutex_destroy(&mhi->ch[i].ch_lock); + + devm_kfree(&pdev->dev, mhi->mmio_backup); + devm_kfree(&pdev->dev, mhi->ch); + devm_kfree(&pdev->dev, mhi->ring); + + mhi_dev_sm_exit(mhi); + + mhi->mmio_initialized = false; + + return rc; +} + static int mhi_init(struct mhi_dev *mhi) { int rc = 0, i = 0; @@ -2129,13 +2265,99 @@ static int mhi_init(struct mhi_dev *mhi) if (!mhi->mmio_backup) return -ENOMEM; - mhi_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, "mhi", 0); - if (mhi_ipc_log == NULL) { - dev_err(&pdev->dev, - "Failed to create IPC logging context\n"); + return 0; +} + +static int mhi_dev_resume_mmio_mhi_reinit(struct mhi_dev *mhi_ctx) +{ + int rc = 0; + + mutex_lock(&mhi_ctx->mhi_lock); + if (atomic_read(&mhi_ctx->re_init_done)) { + mhi_log(MHI_MSG_INFO, "Re_init done, return\n"); + mutex_unlock(&mhi_ctx->mhi_lock); + return 0; } - return 0; + rc = mhi_init(mhi_ctx); + if (rc) { + pr_err("Error initializing MHI MMIO with %d\n", rc); + goto fail; + } + + mhi_ctx->event_reg.events = EP_PCIE_EVENT_PM_D3_HOT | + EP_PCIE_EVENT_PM_D3_COLD | + EP_PCIE_EVENT_PM_D0 | + EP_PCIE_EVENT_PM_RST_DEAST | + EP_PCIE_EVENT_MHI_A7 | + EP_PCIE_EVENT_LINKDOWN; + mhi_ctx->event_reg.user = mhi_ctx; + mhi_ctx->event_reg.mode = EP_PCIE_TRIGGER_CALLBACK; + mhi_ctx->event_reg.callback = mhi_dev_sm_pcie_handler; + + rc = ep_pcie_register_event(mhi_ctx->phandle, &mhi_ctx->event_reg); + if (rc) { + pr_err("Failed to register for events from PCIe\n"); + goto fail; + } + + rc = ipa_register_ipa_ready_cb(mhi_ring_init_cb, mhi_ctx); + if (rc < 0) { + if (rc == -EEXIST) { + mhi_ring_init_cb(mhi_ctx); + } else { + pr_err("Error calling IPA cb with %d\n", rc); + goto fail; + } + } + + /* Invoke MHI SM when device is in RESET state */ + rc = mhi_dev_sm_init(mhi_ctx); + if (rc) { + pr_err("%s: Error during SM init\n", __func__); + goto fail; + } + + /* set the env before setting the ready bit */ + rc = mhi_dev_mmio_set_env(mhi_ctx, MHI_ENV_VALUE); + if (rc) { + pr_err("%s: env setting failed\n", __func__); + goto fail; + } + + /* All set, notify the host */ + rc = mhi_dev_sm_set_ready(); + if (rc) { + pr_err("%s: unable to set ready bit\n", __func__); + goto fail; + } + + atomic_set(&mhi_ctx->is_suspended, 0); + +fail: + atomic_set(&mhi_ctx->re_init_done, 1); + mutex_unlock(&mhi_ctx->mhi_lock); + return rc; +} + +static void mhi_dev_reinit(struct work_struct *work) +{ + struct mhi_dev *mhi_ctx = container_of(work, + struct mhi_dev, re_init); + enum ep_pcie_link_status link_state; + int rc = 0; + + link_state = ep_pcie_get_linkstatus(mhi_ctx->phandle); + if (link_state == EP_PCIE_LINK_ENABLED) { + /* PCIe link is up with BME set */ + rc = mhi_dev_resume_mmio_mhi_reinit(mhi_ctx); + if (rc) { + pr_err("Failed to register for events from PCIe\n"); + return; + } + } + + mhi_log(MHI_MSG_VERBOSE, "Wait for PCIe linkup\n"); } static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx) @@ -2158,6 +2380,8 @@ static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx) INIT_WORK(&mhi_ctx->ring_init_cb_work, mhi_dev_enable); + INIT_WORK(&mhi_ctx->re_init, mhi_dev_reinit); + mhi_ctx->ring_init_wq = alloc_workqueue("mhi_ring_init_cb_wq", WQ_HIGHPRI, 0); if (!mhi_ctx->ring_init_wq) { @@ -2281,10 +2505,18 @@ void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify) return; } - rc = mhi_dev_resume_mmio_mhi_init(mhi_ctx); - if (rc) { - pr_err("Error during MHI device initialization\n"); - return; + if (notify->options == MHI_INIT) { + rc = mhi_dev_resume_mmio_mhi_init(mhi_ctx); + if (rc) { + pr_err("Error during MHI device initialization\n"); + return; + } + } else if (notify->options == MHI_REINIT) { + rc = mhi_dev_resume_mmio_mhi_reinit(mhi_ctx); + if (rc) { + pr_err("Error during MHI device re-initialization\n"); + return; + } } } @@ -2298,6 +2530,12 @@ static int mhi_dev_probe(struct platform_device *pdev) pr_err("Error reading MHI Dev DT\n"); return rc; } + mhi_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, + "mhi", 0); + if (mhi_ipc_log == NULL) { + dev_err(&pdev->dev, + "Failed to create IPC logging context\n"); + } mhi_uci_init(); mhi_update_state_info(MHI_STATE_CONFIGURED); } @@ -2316,6 +2554,7 @@ static int mhi_dev_probe(struct platform_device *pdev) mhi_ctx->event_reg.user = mhi_ctx; mhi_ctx->event_reg.mode = EP_PCIE_TRIGGER_CALLBACK; mhi_ctx->event_reg.callback = mhi_dev_resume_init_with_link_up; + mhi_ctx->event_reg.options = MHI_INIT; rc = ep_pcie_register_event(mhi_ctx->phandle, &mhi_ctx->event_reg); diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index 4cc67478cabb..39b7a37bd233 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -520,6 +520,7 @@ struct mhi_dev { u32 ipa_clnt_hndl[4]; struct workqueue_struct *ring_init_wq; struct work_struct ring_init_cb_work; + struct work_struct re_init; /* EP PCIe registration */ struct ep_pcie_register_event event_reg; @@ -529,6 +530,7 @@ struct mhi_dev { atomic_t write_active; atomic_t is_suspended; atomic_t mhi_dev_wake; + atomic_t re_init_done; struct mutex mhi_write_test; u32 device_local_pa_base; u32 mhi_ep_msi_num; @@ -1050,8 +1052,10 @@ int mhi_dev_get_mhi_addr(struct mhi_dev *dev); * mhi_dev_get_mhi_state() - Fetches the MHI state such as M0/M1/M2/M3. * @dev: MHI device structure. * @state: Pointer of type mhi_dev_state + * @mhi_reset: MHI device reset from host. */ -int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state); +int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state, + bool *mhi_reset); /** * mhi_dev_mmio_init() - Initializes the MMIO and reads the Number of event diff --git a/drivers/platform/msm/mhi_dev/mhi_mmio.c b/drivers/platform/msm/mhi_dev/mhi_mmio.c index 42355c8f72d3..7aab9efaa82c 100644 --- a/drivers/platform/msm/mhi_dev/mhi_mmio.c +++ b/drivers/platform/msm/mhi_dev/mhi_mmio.c @@ -258,7 +258,8 @@ int mhi_dev_mmio_disable_erdb_a7(struct mhi_dev *dev, uint32_t erdb_id) } EXPORT_SYMBOL(mhi_dev_mmio_disable_erdb_a7); -int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state) +int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state, + bool *mhi_reset) { uint32_t reg_value = 0; int rc = 0; @@ -277,6 +278,9 @@ int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state) if (rc) return rc; + if (reg_value & MHICTRL_RESET_MASK) + *mhi_reset = true; + pr_debug("MHICTRL is 0x%x\n", reg_value); return 0; diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.c b/drivers/platform/msm/mhi_dev/mhi_sm.c index 726cf34aad98..e35d35fc54bf 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.c +++ b/drivers/platform/msm/mhi_dev/mhi_sm.c @@ -397,7 +397,8 @@ static bool mhi_sm_is_legal_pcie_event_on_state(enum mhi_dev_state curr_mstate, res = true; break; case EP_PCIE_EVENT_PM_D3_HOT: - res = (curr_mstate == MHI_DEV_M3_STATE && + res = ((curr_mstate == MHI_DEV_M3_STATE || + curr_mstate == MHI_DEV_READY_STATE) && curr_dstate != MHI_SM_EP_PCIE_LINK_DISABLE); break; case EP_PCIE_EVENT_PM_D3_COLD: @@ -937,6 +938,24 @@ fail_init_wq: } EXPORT_SYMBOL(mhi_dev_sm_init); +int mhi_dev_sm_exit(struct mhi_dev *mhi_dev) +{ + MHI_SM_FUNC_ENTRY(); + + atomic_set(&mhi_sm_ctx->pending_device_events, 0); + atomic_set(&mhi_sm_ctx->pending_pcie_events, 0); + mhi_sm_debugfs_destroy(); + flush_workqueue(mhi_sm_ctx->mhi_sm_wq); + destroy_workqueue(mhi_sm_ctx->mhi_sm_wq); + ipa_dma_destroy(); + mutex_destroy(&mhi_sm_ctx->mhi_state_lock); + devm_kfree(mhi_dev->dev, mhi_sm_ctx); + mhi_sm_ctx = NULL; + + return 0; +} +EXPORT_SYMBOL(mhi_dev_sm_exit); + /** * mhi_dev_sm_get_mhi_state() -Get current MHI state. * @state: return param @@ -983,7 +1002,7 @@ EXPORT_SYMBOL(mhi_dev_sm_get_mhi_state); */ int mhi_dev_sm_set_ready(void) { - int res = -EAGAIN; + int res = 0; int is_ready; enum mhi_dev_state state; diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.h b/drivers/platform/msm/mhi_dev/mhi_sm.h index a6a9d89df18e..2fa6fa001677 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.h +++ b/drivers/platform/msm/mhi_dev/mhi_sm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015,2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -41,6 +41,7 @@ enum mhi_dev_event { }; int mhi_dev_sm_init(struct mhi_dev *dev); +int mhi_dev_sm_exit(struct mhi_dev *dev); int mhi_dev_sm_set_ready(void); int mhi_dev_notify_sm_event(enum mhi_dev_event event); int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state); -- GitLab From 8af708e199526d73d592cd93e48477c7a1ebdd57 Mon Sep 17 00:00:00 2001 From: Jun Fu Date: Thu, 19 Oct 2017 11:00:01 +0800 Subject: [PATCH 1259/5498] ARM: dts: msm: disable red and green LED for apq8096 drone Disable red and green LED on APQ8096 drone to save power. Change-Id: I8c0e0ffc2728c0c27c8a9724f718cc5d533138e7 Signed-off-by: Jun Fu --- arch/arm/boot/dts/qcom/apq8096-drone.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8096-drone.dtsi b/arch/arm/boot/dts/qcom/apq8096-drone.dtsi index 7f23f47f7b5d..a259c0a47258 100644 --- a/arch/arm/boot/dts/qcom/apq8096-drone.dtsi +++ b/arch/arm/boot/dts/qcom/apq8096-drone.dtsi @@ -582,7 +582,7 @@ qcom,cs-out = <3>; /* 20 MA */ qcom,master-en = <1>; /* Enable GPIO */ qcom,invert = <1>; - status = "okay"; + status = "disabled"; }; mpp@a600 { /* MPP 7 - TP83 */ @@ -601,7 +601,7 @@ qcom,cs-out = <3>; /* 20 MA */ qcom,master-en = <1>; /* Enable GPIO */ qcom,invert = <1>; - status = "okay"; + status = "disabled"; }; }; -- GitLab From e547e7ee9a29c044a4305c718922bc2e56acb717 Mon Sep 17 00:00:00 2001 From: gaolez Date: Tue, 17 Oct 2017 18:00:55 +0800 Subject: [PATCH 1260/5498] msm: wlan: Disable DFS flag for JM Disable DFS flag for JM since the corresponding 5Ghz regulatory is set as FCC13 which don't support DFS channel. Change-Id: Ia4ba1975426ac1525edb4be611baaa468eb1df70 Signed-off-by: Gaole Zhang --- net/wireless/db.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 8db574196d26..6a7b522f1f46 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -718,8 +718,8 @@ country IT: DFS-ETSI country JM: DFS-FCC (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (5250 - 5330 @ 80), (24), AUTO-BW + (5490 - 5730 @ 160), (24) (5735 - 5835 @ 80), (30) country JO: -- GitLab From 73c254c029c851de3e4c4eec1a9a83e26527c646 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Wed, 11 Oct 2017 11:25:17 -0700 Subject: [PATCH 1261/5498] cnss2: Add multiple recoveries support without WLAN host driver In order to support multiple recoveries without WLAN host driver, clear recovery flag properly for debug state and also move WLAN host driver ops check to proper place during powerup and shutdown. Change-Id: I8cb17a495f6106bd07fe29dba282d4168c9f7411 CRs-fixed: 2124999 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 36 ++++++++++++------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 6e0e16f53832..06685086b88b 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -564,9 +564,15 @@ out: static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv) { - int ret; + int ret = 0; struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + cnss_pr_dbg("Skip driver probe\n"); + goto out; + } + if (!plat_priv->driver_ops) { cnss_pr_err("driver_ops is NULL\n"); ret = -EINVAL; @@ -604,6 +610,13 @@ static int cnss_driver_call_remove(struct cnss_plat_data *plat_priv) { struct cnss_pci_data *pci_priv = plat_priv->bus_priv; + if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || + test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { + cnss_pr_dbg("Skip driver remove\n"); + return 0; + } + if (!plat_priv->driver_ops) { cnss_pr_err("driver_ops is NULL\n"); return -EINVAL; @@ -997,11 +1010,6 @@ static int cnss_qca6174_powerup(struct cnss_plat_data *plat_priv) return -ENODEV; } - if (!plat_priv->driver_ops) { - cnss_pr_err("driver_ops is NULL!\n"); - return -EINVAL; - } - ret = cnss_power_on_device(plat_priv); if (ret) { cnss_pr_err("Failed to power on device, err = %d\n", ret); @@ -1035,15 +1043,8 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv) if (!pci_priv) return -ENODEV; - if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) - goto skip_driver_remove; - - if (!plat_priv->driver_ops) - return -EINVAL; - cnss_driver_call_remove(plat_priv); -skip_driver_remove: cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); cnss_pci_set_monitor_wake_intr(pci_priv, false); cnss_pci_set_auto_suspended(pci_priv, 0); @@ -1140,17 +1141,8 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) if (!pci_priv) return -ENODEV; - if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || - test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || - test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) - goto skip_driver_remove; - - if (!plat_priv->driver_ops) - return -EINVAL; - cnss_driver_call_remove(plat_priv); -skip_driver_remove: cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); cnss_pci_set_monitor_wake_intr(pci_priv, false); cnss_pci_set_auto_suspended(pci_priv, 0); -- GitLab From 604e869d2f89ae399c8ec45f4dcb84b77ad009c3 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Fri, 13 Oct 2017 10:56:42 -0700 Subject: [PATCH 1262/5498] cnss2: Support multiple recoveries when using CORE only firmware After MHI power on is completed, CORE component only firmware boot can be considered as success. Add a debug parameter in order to support multiple recoveries when using the firmware without WLAN component. Change-Id: I1b6a67a578ff7ffa15dff733b8d691910003ac6f CRs-fixed: 2124999 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 6e0e16f53832..935cdd4356c7 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -58,6 +58,7 @@ MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest"); enum cnss_debug_quirks { LINK_DOWN_SELF_RECOVERY, SKIP_DEVICE_BOOT, + USE_CORE_ONLY_FW, }; unsigned long quirks; @@ -1110,6 +1111,12 @@ static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv) return 0; } + if (test_bit(USE_CORE_ONLY_FW, &quirks)) { + clear_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + return 0; + } + cnss_set_pin_connect_status(plat_priv); if (qmi_bypass) { -- GitLab From f93014528b9f0bf22356e16e83902949520608dc Mon Sep 17 00:00:00 2001 From: Satyajit Desai Date: Mon, 19 Jun 2017 19:12:18 -0700 Subject: [PATCH 1263/5498] soc: qcom: Add support for QDSS bridge driver Add support to route QDSS data received from MDM via MHI to USB. The driver will help route diag traffic over the QDSS sub-system to USB on APPS side. It acts as a bridge between the PCIE MHI and USB interface. Change-Id: I18ca278cec2b03659fabcdaf5095c310d1744b72 Signed-off-by: Satyajit Desai --- .../devicetree/bindings/arm/msm/qdss_mhi.txt | 15 + drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qdss_bridge.c | 461 ++++++++++++++++++ drivers/soc/qcom/qdss_bridge.h | 37 ++ 5 files changed, 523 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/msm/qdss_mhi.txt create mode 100644 drivers/soc/qcom/qdss_bridge.c create mode 100644 drivers/soc/qcom/qdss_bridge.h diff --git a/Documentation/devicetree/bindings/arm/msm/qdss_mhi.txt b/Documentation/devicetree/bindings/arm/msm/qdss_mhi.txt new file mode 100644 index 000000000000..928a4f4269a3 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/qdss_mhi.txt @@ -0,0 +1,15 @@ +Qualcomm Technologies, Inc. QDSS bridge Driver + +This device will enable routing debug data from modem +subsystem to APSS host. + +Required properties: +-compatible : "qcom,qdss-mhi". +-qcom,mhi : phandle of MHI Device to connect to. + +Example: + qcom,qdss-mhi { + compatible = "qcom,qdss-mhi"; + qcom,mhi = <&mhi_0>; + }; + diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 27469bf16ca3..a167416214c1 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -901,6 +901,15 @@ config QCOM_EARLY_RANDOM may not be truly random. Select this option to make an early call to get some random data to put in the pool. If unsure, say N. +config QCOM_QDSS_BRIDGE + bool "Configure bridge driver for Qualcomm MDM" + depends on MSM_MHI + help + The driver will help route diag traffic over the QDSS sub-system to + USB on APPS side. The driver acts as a bridge between the PCIE MHI + and USB interface. The amount of buffer memory allocated at runtime + can be modified via module params. If unsure, say N. + config WCD_DSP_GLINK tristate "WCD DSP GLINK Driver" depends on MSM_GLINK diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index fb0ae9187ab7..643d1c7bcd37 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -108,3 +108,4 @@ obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o obj-$(CONFIG_MSM_HAB) += hab/ +obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o diff --git a/drivers/soc/qcom/qdss_bridge.c b/drivers/soc/qcom/qdss_bridge.c new file mode 100644 index 000000000000..5294be9f0bd3 --- /dev/null +++ b/drivers/soc/qcom/qdss_bridge.c @@ -0,0 +1,461 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define KMSG_COMPONENT "QDSS diag bridge" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdss_bridge.h" + +#define MODULE_NAME "qdss_bridge" + +#define QDSS_BUF_SIZE (16*1024) +#define MHI_CLIENT_QDSS_IN 9 + +/* Max number of objects needed */ +static int poolsize = 32; +module_param(poolsize, int, 0644); + +/* Size of single buffer */ +static int itemsize = QDSS_BUF_SIZE; +module_param(itemsize, int, 0644); + +static int qdss_destroy_buf_tbl(struct qdss_bridge_drvdata *drvdata) +{ + struct list_head *start, *temp; + struct qdss_buf_tbl_lst *entry = NULL; + + list_for_each_safe(start, temp, &drvdata->buf_tbl) { + entry = list_entry(start, struct qdss_buf_tbl_lst, link); + list_del(&entry->link); + kfree(entry->buf); + kfree(entry->usb_req); + kfree(entry); + } + + return 0; +} + +static int qdss_create_buf_tbl(struct qdss_bridge_drvdata *drvdata) +{ + struct qdss_buf_tbl_lst *entry; + void *buf; + struct qdss_request *usb_req; + int i; + + for (i = 0; i < poolsize; i++) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + goto err; + + buf = kzalloc(QDSS_BUF_SIZE, GFP_KERNEL); + usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL); + + entry->buf = buf; + entry->usb_req = usb_req; + atomic_set(&entry->available, 1); + list_add_tail(&entry->link, &drvdata->buf_tbl); + + if (!buf || !usb_req) + goto err; + } + + return 0; +err: + qdss_destroy_buf_tbl(drvdata); + return -ENOMEM; +} + +struct qdss_buf_tbl_lst *qdss_get_buf_tbl_entry( + struct qdss_bridge_drvdata *drvdata, + void *buf) +{ + struct qdss_buf_tbl_lst *entry; + + list_for_each_entry(entry , &drvdata->buf_tbl, link) { + if (atomic_read(&entry->available)) + continue; + if (entry->buf == buf) + return entry; + } + + return NULL; +} + +struct qdss_buf_tbl_lst *qdss_get_entry(struct qdss_bridge_drvdata *drvdata) +{ + struct qdss_buf_tbl_lst *item; + + list_for_each_entry(item, &drvdata->buf_tbl, link) + if (atomic_cmpxchg(&item->available, 1, 0) == 1) + return item; + + return NULL; +} + +static void qdss_buf_tbl_remove(struct qdss_bridge_drvdata *drvdata, + void *buf) +{ + struct qdss_buf_tbl_lst *entry = NULL; + + list_for_each_entry(entry, &drvdata->buf_tbl, link) { + if (entry->buf != buf) + continue; + atomic_set(&entry->available, 1); + return; + } + + pr_err_ratelimited("Failed to find buffer for removal\n"); +} + +static void mhi_ch_close(struct qdss_bridge_drvdata *drvdata) +{ + flush_workqueue(drvdata->mhi_wq); + qdss_destroy_buf_tbl(drvdata); + mhi_close_channel(drvdata->hdl); +} + +static void mhi_close_work_fn(struct work_struct *work) +{ + struct qdss_bridge_drvdata *drvdata = + container_of(work, + struct qdss_bridge_drvdata, + close_work); + + usb_qdss_close(drvdata->usb_ch); + mhi_ch_close(drvdata); +} + +static void mhi_read_work_fn(struct work_struct *work) +{ + int err = 0; + enum MHI_FLAGS mhi_flags = MHI_EOT; + struct qdss_buf_tbl_lst *entry; + + struct qdss_bridge_drvdata *drvdata = + container_of(work, + struct qdss_bridge_drvdata, + read_work); + + do { + if (!drvdata->opened) + break; + entry = qdss_get_entry(drvdata); + if (!entry) + break; + + err = mhi_queue_xfer(drvdata->hdl, entry->buf, QDSS_BUF_SIZE, + mhi_flags); + if (err) { + pr_err_ratelimited("Unable to read from MHI buffer err:%d", + err); + goto fail; + } + } while (entry); + + return; +fail: + qdss_buf_tbl_remove(drvdata, entry->buf); + queue_work(drvdata->mhi_wq, &drvdata->read_work); +} + +static int mhi_queue_read(struct qdss_bridge_drvdata *drvdata) +{ + queue_work(drvdata->mhi_wq, &(drvdata->read_work)); + return 0; +} + +static int usb_write(struct qdss_bridge_drvdata *drvdata, + struct mhi_result *result) +{ + int ret = 0; + struct qdss_buf_tbl_lst *entry; + + entry = qdss_get_buf_tbl_entry(drvdata, result->buf_addr); + if (!entry) + return -EINVAL; + + entry->usb_req->buf = result->buf_addr; + entry->usb_req->length = result->bytes_xferd; + ret = usb_qdss_data_write(drvdata->usb_ch, entry->usb_req); + + return ret; +} + +static void mhi_read_done_work_fn(struct work_struct *work) +{ + unsigned char *buf = NULL; + struct mhi_result result; + int err = 0; + struct qdss_bridge_drvdata *drvdata = + container_of(work, + struct qdss_bridge_drvdata, + read_done_work); + + do { + err = mhi_poll_inbound(drvdata->hdl, &result); + if (err) { + pr_debug("MHI poll failed err:%d\n", err); + break; + } + buf = result.buf_addr; + if (!buf) + break; + err = usb_write(drvdata, &result); + if (err) + qdss_buf_tbl_remove(drvdata, buf); + } while (1); +} + +static void usb_write_done(struct qdss_bridge_drvdata *drvdata, + struct qdss_request *d_req) +{ + if (d_req->status) { + pr_err_ratelimited("USB write failed err:%d\n", d_req->status); + mhi_queue_read(drvdata); + return; + } + qdss_buf_tbl_remove(drvdata, d_req->buf); + mhi_queue_read(drvdata); +} + +static void usb_notifier(void *priv, unsigned int event, + struct qdss_request *d_req, struct usb_qdss_ch *ch) +{ + struct qdss_bridge_drvdata *drvdata = priv; + + if (!drvdata) + return; + + switch (event) { + case USB_QDSS_CONNECT: + usb_qdss_alloc_req(drvdata->usb_ch, poolsize, 0); + mhi_queue_read(drvdata); + break; + + case USB_QDSS_DISCONNECT: + /* Leave MHI/USB open.Only close on MHI disconnect */ + break; + + case USB_QDSS_DATA_WRITE_DONE: + usb_write_done(drvdata, d_req); + break; + + default: + break; + } +} + +static int mhi_ch_open(struct qdss_bridge_drvdata *drvdata) +{ + int ret; + + if (drvdata->opened) + return 0; + + ret = mhi_open_channel(drvdata->hdl); + if (ret) { + pr_err("Unable to open MHI channel\n"); + return ret; + } + + ret = mhi_get_free_desc(drvdata->hdl); + if (ret <= 0) + return -EIO; + + drvdata->opened = 1; + return 0; +} + +static void qdss_bridge_open_work_fn(struct work_struct *work) +{ + struct qdss_bridge_drvdata *drvdata = + container_of(work, + struct qdss_bridge_drvdata, + open_work); + int ret; + + ret = mhi_ch_open(drvdata); + if (ret) + goto err_open; + + ret = qdss_create_buf_tbl(drvdata); + if (ret) + goto err; + + drvdata->usb_ch = usb_qdss_open("PCIE", drvdata, usb_notifier); + if (IS_ERR_OR_NULL(drvdata->usb_ch)) { + ret = PTR_ERR(drvdata->usb_ch); + goto err; + } + + return; +err: + mhi_ch_close(drvdata); +err_open: + pr_err("Open work failed with err:%d\n", ret); +} + +static void mhi_notifier(struct mhi_cb_info *cb_info) +{ + struct mhi_result *result; + struct qdss_bridge_drvdata *drvdata; + + if (!cb_info) + return; + + result = cb_info->result; + if (!result) { + pr_err_ratelimited("Failed to obtain MHI result\n"); + return; + } + + drvdata = (struct qdss_bridge_drvdata *)cb_info->result->user_data; + if (!drvdata) { + pr_err_ratelimited("MHI returned invalid drvdata\n"); + return; + } + + switch (cb_info->cb_reason) { + case MHI_CB_MHI_ENABLED: + queue_work(drvdata->mhi_wq, &drvdata->open_work); + break; + + case MHI_CB_XFER: + if (!drvdata->opened) + break; + + queue_work(drvdata->mhi_wq, &drvdata->read_done_work); + break; + + case MHI_CB_MHI_DISABLED: + if (!drvdata->opened) + break; + + drvdata->opened = 0; + queue_work(drvdata->mhi_wq, &drvdata->close_work); + break; + + default: + pr_err_ratelimited("MHI returned invalid cb reason 0x%x\n", + cb_info->cb_reason); + break; + } +} + +static int qdss_mhi_register_ch(struct qdss_bridge_drvdata *drvdata) +{ + struct mhi_client_info_t *client_info; + int ret; + struct mhi_client_info_t *mhi_info; + + client_info = devm_kzalloc(drvdata->dev, sizeof(*client_info), + GFP_KERNEL); + if (!client_info) + return -ENOMEM; + + client_info->mhi_client_cb = mhi_notifier; + drvdata->client_info = client_info; + + mhi_info = client_info; + mhi_info->chan = MHI_CLIENT_QDSS_IN; + mhi_info->dev = drvdata->dev; + mhi_info->node_name = "qcom,mhi"; + mhi_info->user_data = drvdata; + + ret = mhi_register_channel(&drvdata->hdl, mhi_info); + return ret; +} + +int qdss_mhi_init(struct qdss_bridge_drvdata *drvdata) +{ + int ret; + + drvdata->mhi_wq = create_singlethread_workqueue(MODULE_NAME); + if (!drvdata->mhi_wq) + return -ENOMEM; + + INIT_WORK(&(drvdata->read_work), mhi_read_work_fn); + INIT_WORK(&(drvdata->read_done_work), mhi_read_done_work_fn); + INIT_WORK(&(drvdata->open_work), qdss_bridge_open_work_fn); + INIT_WORK(&(drvdata->close_work), mhi_close_work_fn); + INIT_LIST_HEAD(&drvdata->buf_tbl); + drvdata->opened = 0; + + ret = qdss_mhi_register_ch(drvdata); + if (ret) { + destroy_workqueue(drvdata->mhi_wq); + pr_err("Unable to register MHI read channel err:%d\n", ret); + return ret; + } + + return 0; +} + +static int qdss_mhi_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + struct qdss_bridge_drvdata *drvdata; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + return ret; + } + + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + ret = qdss_mhi_init(drvdata); + if (ret) + goto err; + + return 0; +err: + pr_err("Device probe failed err:%d\n", ret); + return ret; +} + +static const struct of_device_id qdss_mhi_table[] = { + {.compatible = "qcom,qdss-mhi"}, + {}, +}; + +static struct platform_driver qdss_mhi_driver = { + .probe = qdss_mhi_probe, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = qdss_mhi_table, + }, +}; + +static int __init qdss_bridge_init(void) +{ + return platform_driver_register(&qdss_mhi_driver); +} + +static void __exit qdss_bridge_exit(void) +{ + platform_driver_unregister(&qdss_mhi_driver); +} + +module_init(qdss_bridge_init); +module_exit(qdss_bridge_exit); diff --git a/drivers/soc/qcom/qdss_bridge.h b/drivers/soc/qcom/qdss_bridge.h new file mode 100644 index 000000000000..97b9c4099141 --- /dev/null +++ b/drivers/soc/qcom/qdss_bridge.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _QDSS_BRIDGE_H +#define _QDSS_BRIDGE_H + +struct qdss_buf_tbl_lst { + struct list_head link; + unsigned char *buf; + struct qdss_request *usb_req; + atomic_t available; +}; + +struct qdss_bridge_drvdata { + struct device *dev; + bool opened; + struct work_struct read_work; + struct work_struct read_done_work; + struct work_struct open_work; + struct work_struct close_work; + struct workqueue_struct *mhi_wq; + struct mhi_client_handle *hdl; + struct mhi_client_info_t *client_info; + struct list_head buf_tbl; + struct usb_qdss_ch *usb_ch; +}; + +#endif -- GitLab From 896124c88ff2d89321f8f2b2dde3e5e5f5196095 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Wed, 20 Sep 2017 14:58:34 -0700 Subject: [PATCH 1264/5498] cnss2: Request runtime PM resume when shutdown happens Request runtime PM resume in platform driver as soon as shutdown happens. This can make sure device is resumed while shutdown is proceeding. Change-Id: I0aa15b9713347288f4954bd767ec9243d22153ed CRs-fixed: 2124999 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 4 ++++ drivers/net/wireless/cnss2/pci.c | 14 ++++++++++++++ drivers/net/wireless/cnss2/pci.h | 1 + 3 files changed, 19 insertions(+) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 06685086b88b..5798cb378da2 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1043,6 +1043,8 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv) if (!pci_priv) return -ENODEV; + cnss_pm_request_resume(pci_priv); + cnss_driver_call_remove(plat_priv); cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); @@ -1141,6 +1143,8 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) if (!pci_priv) return -ENODEV; + cnss_pm_request_resume(pci_priv); + cnss_driver_call_remove(plat_priv); cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 5007d03c0c7b..10149eeb93f9 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -685,6 +685,20 @@ out: } EXPORT_SYMBOL(cnss_auto_resume); +int cnss_pm_request_resume(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev; + + if (!pci_priv) + return -ENODEV; + + pci_dev = pci_priv->pci_dev; + if (!pci_dev) + return -ENODEV; + + return pm_request_resume(&pci_dev->dev); +} + int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index 4dc29c3c1f10..ac549685c2e3 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -137,5 +137,6 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv); void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv); void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv); void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv); +int cnss_pm_request_resume(struct cnss_pci_data *pci_priv); #endif /* _CNSS_PCI_H */ -- GitLab From cd47f9a19170d783f65542cf78da715e34fde8d5 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 20 Oct 2017 11:55:33 +0900 Subject: [PATCH 1265/5498] ANDROID: net: xfrm: fix long lines. Test: xfrm_test.py passes Change-Id: I90e08f5b7ae56320cb86cd9b2184721ee6bd48e1 Signed-off-by: Lorenzo Colitti --- include/net/xfrm.h | 4 +++- net/xfrm/xfrm_policy.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index af749f07db9b..a08d8bcd145c 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -291,7 +291,9 @@ struct xfrm_policy_afinfo { const xfrm_address_t *saddr, const xfrm_address_t *daddr, u32 mark); - int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr, + int (*get_saddr)(struct net *net, + xfrm_address_t *saddr, + xfrm_address_t *daddr, u32 mark); void (*decode_session)(struct sk_buff *skb, struct flowi *fl, diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b9481872d3fa..f4f17fb057ad 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1406,7 +1406,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl, remote = &tmpl->id.daddr; local = &tmpl->saddr; if (xfrm_addr_any(local, tmpl->encap_family)) { - error = xfrm_get_saddr(net, &tmp, remote, tmpl->encap_family, 0); + error = xfrm_get_saddr(net, &tmp, remote, + tmpl->encap_family, 0); if (error) goto fail; local = &tmp; -- GitLab From fd92bfe6aec225f66206bb7fe4be72f9b3d6b071 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Sun, 8 Oct 2017 22:13:10 +0530 Subject: [PATCH 1266/5498] usb: gadget: Support G-Link for GPS function driver The existing GPS function driver uses SMD transport to communicate with the modem. SMD is deprecated on newer targets. Add support for G-Link transport for the GPS function driver to communicate with the modem. Stats of the G-Link channels can be seen by using the below command cat /sys/kernel/debug/usb_glink_ctrl/status The stats can be reset by writing to the same node echo 0 > /sys/kernel/debug/usb_glink_ctrl/status CRs-Fixed: 2121744 Change-Id: Id2e84292e1cd1a08499c23604f9c9efe02f5fbe5 Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/android.c | 32 +- drivers/usb/gadget/function/f_gps.c | 35 +- drivers/usb/gadget/function/u_glink.c | 673 ++++++++++++++++++ .../usb/gadget/function/usb_gadget_xport.h | 5 + 4 files changed, 737 insertions(+), 8 deletions(-) create mode 100644 drivers/usb/gadget/function/u_glink.c diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 7366e57b29de..9b33fdb1d75c 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -1064,6 +1064,32 @@ static struct android_usb_function rmnet_function = { .attributes = rmnet_function_attributes, }; +static char gps_transport[MAX_XPORT_STR_LEN]; + +static ssize_t gps_transport_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", gps_transport); +} + +static ssize_t gps_transport_store( + struct device *device, struct device_attribute *attr, + const char *buff, size_t size) +{ + strlcpy(gps_transport, buff, sizeof(gps_transport)); + + return size; +} + +static struct device_attribute dev_attr_gps_transport = + __ATTR(transport, S_IRUGO | S_IWUSR, + gps_transport_show, + gps_transport_store); + +static struct device_attribute *gps_function_attrbitutes[] = { + &dev_attr_gps_transport, + NULL }; + static void gps_function_cleanup(struct android_usb_function *f) { gps_cleanup(); @@ -1074,10 +1100,13 @@ static int gps_function_bind_config(struct android_usb_function *f, { int err; static int gps_initialized; + char buf[MAX_XPORT_STR_LEN], *b; if (!gps_initialized) { + strlcpy(buf, gps_transport, sizeof(buf)); + b = strim(buf); gps_initialized = 1; - err = gps_init_port(); + err = gps_init_port(b); if (err) { pr_err("gps: Cannot init gps port"); return err; @@ -1102,6 +1131,7 @@ static struct android_usb_function gps_function = { .name = "gps", .cleanup = gps_function_cleanup, .bind_config = gps_function_bind_config, + .attributes = gps_function_attrbitutes, }; /* ncm */ diff --git a/drivers/usb/gadget/function/f_gps.c b/drivers/usb/gadget/function/f_gps.c index 073915cc40d6..7c97ff237361 100644 --- a/drivers/usb/gadget/function/f_gps.c +++ b/drivers/usb/gadget/function/f_gps.c @@ -19,6 +19,7 @@ #include "usb_gadget_xport.h" #include "u_rmnet.h" #include "gadget_chips.h" +#include "u_glink.c" #define GPS_NOTIFY_INTERVAL 5 #define GPS_MAX_NOTIFY_SIZE 64 @@ -222,20 +223,39 @@ static int gps_gport_setup(void) u8 base; int res; - res = gsmd_ctrl_setup(GPS_CTRL_CLIENT, 1, &base); - gps_port.port->port_num = base; + switch (gps_port.ctrl_xport) { + case USB_GADGET_XPORT_GLINK: + res = glink_ctrl_setup(GPS_CTRL_CLIENT, 1, &base); + gps_port.port->port_num = base; + break; + default: + res = gsmd_ctrl_setup(GPS_CTRL_CLIENT, 1, &base); + gps_port.port->port_num = base; + break; + } return res; } static int gport_ctrl_connect(struct f_gps *dev) { - return gsmd_ctrl_connect(&dev->port, dev->port_num); + switch (gps_port.ctrl_xport) { + case USB_GADGET_XPORT_GLINK: + return glink_ctrl_connect(&dev->port, dev->port_num); + default: + return gsmd_ctrl_connect(&dev->port, dev->port_num); + } } static int gport_gps_disconnect(struct f_gps *dev) { - gsmd_ctrl_disconnect(&dev->port, dev->port_num); - return 0; + switch (gps_port.ctrl_xport) { + case USB_GADGET_XPORT_GLINK: + glink_ctrl_disconnect(&dev->port, dev->port_num); + return 0; + default: + gsmd_ctrl_disconnect(&dev->port, dev->port_num); + return 0; + } } static void gps_unbind(struct usb_configuration *c, struct usb_function *f) @@ -910,7 +930,7 @@ static void gps_cleanup(void) kfree(gps_port.port); } -static int gps_init_port(void) +static int gps_init_port(const char *transport_name) { struct f_gps *dev; @@ -925,7 +945,8 @@ static int gps_init_port(void) dev->port_num = 0; gps_port.port = dev; - gps_port.ctrl_xport = USB_GADGET_XPORT_SMD; + gps_port.ctrl_xport = str_to_xport(transport_name); + pr_debug("%s: init GPS with transport: %s\n", __func__, transport_name); return 0; } diff --git a/drivers/usb/gadget/function/u_glink.c b/drivers/usb/gadget/function/u_glink.c new file mode 100644 index 000000000000..109226243af3 --- /dev/null +++ b/drivers/usb/gadget/function/u_glink.c @@ -0,0 +1,673 @@ +/* Copyright (c) 2017, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "u_rmnet.h" + +static struct workqueue_struct *glink_ctrl_wq; + +struct gl_link_info { + void *handle; + enum glink_link_state link_state; +}; + +struct glink_channel { + struct grmnet *port; + void *handle; + struct glink_open_config open_cfg; + unsigned channel_state; + + struct list_head tx_q; + unsigned tx_q_count; + uint32_t cbits_to_modem; + + spinlock_t port_lock; + struct work_struct write_w; + + struct work_struct connect_w; + struct work_struct disconnect_w; + + unsigned long to_modem; + unsigned long to_host; +}; + +#define MAX_CHANNELS 2 +#define MAX_INTENTS 20 +#define RX_INTENT_SIZE 2048 + +struct glink_ctrl_intent_work { + struct glink_channel *ch_info; + struct work_struct work; +}; + +static struct gl_link_info link_info; +static struct glink_ctrl_intent_work intent_work; +static struct glink_channel *glink_channels[MAX_CHANNELS]; + +static struct rmnet_ctrl_pkt *alloc_rmnet_cpkt(unsigned len, gfp_t flags) +{ + struct rmnet_ctrl_pkt *pkt; + + pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags); + if (!pkt) + return ERR_PTR(-ENOMEM); + + pkt->buf = kmalloc(len, flags); + if (!pkt->buf) { + kfree(pkt); + return ERR_PTR(-ENOMEM); + } + + pkt->len = len; + + return pkt; +} + +static void glink_ctrl_connect_w(struct work_struct *w) +{ + struct glink_channel *ch_info = container_of(w, struct glink_channel, + connect_w); + unsigned long flags; + int set_bits = 0; + int clear_bits = 0; + uint32_t sig = 0; + + pr_debug("%s\n", __func__); + if (!ch_info->port) { + pr_err("%s: no port\n", __func__); + return; + } + + /** + * If the channel is not opened yet, open the channel and queue + * connect work again once the remote client has also opened the + * channel. + */ + if (!ch_info->handle) { + ch_info->handle = glink_open(&ch_info->open_cfg); + if (IS_ERR(ch_info->handle)) { + pr_err("%s failed to open GLINK channel %lu\n", + __func__, PTR_ERR(ch_info->handle)); + ch_info->handle = NULL; + return; + } + } + + if (ch_info->channel_state != GLINK_CONNECTED) { + pr_debug("%s: remote not yet connected wait for notification\n", + __func__); + return; + } + + set_bits = ch_info->cbits_to_modem; + clear_bits = ~(ch_info->cbits_to_modem | TIOCM_RTS); + + sig |= set_bits; + sig &= ~clear_bits; + + spin_lock_irqsave(&ch_info->port_lock, flags); + glink_sigs_set(ch_info->handle, sig); + spin_unlock_irqrestore(&ch_info->port_lock, flags); + + queue_work(glink_ctrl_wq, &ch_info->write_w); +} + +static void glink_ctrl_disconnect_w(struct work_struct *w) +{ + struct glink_channel *ch_info = container_of(w, struct glink_channel, + disconnect_w); + + if (ch_info->handle) { + pr_debug("%s: close glink channel\n", __func__); + glink_close(ch_info->handle); + } +} + +static void glink_ctrl_intent_worker(struct work_struct *w) +{ + struct glink_ctrl_intent_work *intent_work = container_of(w, + struct glink_ctrl_intent_work, work); + struct glink_channel *ch_info = intent_work->ch_info; + int i; + + for (i = 0; i < MAX_INTENTS; i++) { + glink_queue_rx_intent(ch_info->handle, (void *)ch_info, + RX_INTENT_SIZE); + } +} + +static void glink_ctrl_write_w(struct work_struct *w) +{ + struct glink_channel *ch_info = container_of(w, + struct glink_channel, write_w); + struct rmnet_ctrl_pkt *cpkt; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ch_info->port_lock, flags); + pr_debug("%s: Tx Q count:%u\n", __func__, ch_info->tx_q_count); + while (ch_info->channel_state == GLINK_CONNECTED) { + if (list_empty(&ch_info->tx_q)) { + pr_debug("%s: list is empty\n", __func__); + break; + } + cpkt = list_first_entry(&ch_info->tx_q, + struct rmnet_ctrl_pkt, list); + list_del(&cpkt->list); + spin_unlock_irqrestore(&ch_info->port_lock, flags); + + ret = glink_tx(ch_info->handle, (void *)ch_info, + (void *)cpkt->buf, cpkt->len, + GLINK_TX_REQ_INTENT); + + spin_lock_irqsave(&ch_info->port_lock, flags); + + if (ret < 0) { + pr_err("%s: G-link Tx fail\n", __func__); + kfree(cpkt->buf); + } + + /* + * If glink_tx succeeds then the buffer(cpkt->buf) is now owned + * by the remote glink client/glink core and should not be + * freed. The buffer should be freed once the tx_done + * notification is received which is when the ownership is + * returned back to the client. + */ + kfree(cpkt); + ch_info->tx_q_count--; + } + spin_unlock_irqrestore(&ch_info->port_lock, flags); +} + +#define GLINK_CTRL_PKT_Q_LIMIT 50 + +static int glink_send_cpkt_tomodem(u8 client_num, void *buf, size_t len) +{ + struct glink_channel *ch_info; + struct rmnet_ctrl_pkt *cpkt; + unsigned long flags; + + if (client_num >= MAX_CHANNELS) { + pr_err("%s: Invalid client number\n", __func__); + return -EINVAL; + } + + ch_info = glink_channels[client_num]; + if (!ch_info) { + pr_err("%s: channel not set up\n", __func__); + return -EINVAL; + } + + cpkt = alloc_rmnet_cpkt(len, GFP_ATOMIC); + + memcpy(cpkt->buf, buf, len); + cpkt->len = len; + + spin_lock_irqsave(&ch_info->port_lock, flags); + if (ch_info->tx_q_count > GLINK_CTRL_PKT_Q_LIMIT) { + pr_err_ratelimited("%s Dropping GLINK CTRL Pkt: limit: %u\n", + __func__, ch_info->tx_q_count); + spin_unlock_irqrestore(&ch_info->port_lock, flags); + kfree(cpkt->buf); + kfree(cpkt); + return 0; + } + + list_add_tail(&cpkt->list, &ch_info->tx_q); + ch_info->tx_q_count++; + spin_unlock_irqrestore(&ch_info->port_lock, flags); + + if (ch_info->handle && (ch_info->channel_state == GLINK_CONNECTED)) + queue_work(glink_ctrl_wq, &ch_info->write_w); + + return 0; +} + +void glink_ctrl_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + struct glink_channel *c = (struct glink_channel *)priv; + + c->to_modem++; + kfree(ptr); +} + +#define RMNET_CTRL_DTR 0x01 + +static void glink_send_cbits_to_modem(void *gptr, u8 client_num, int cbits) +{ + struct glink_channel *ch_info; + int set_bits = 0; + int clear_bits = 0; + uint32_t sig = 0; + + if (client_num >= MAX_CHANNELS) { + pr_err("%s: Invalid client number\n", __func__); + return; + } + + ch_info = glink_channels[client_num]; + if (!ch_info) { + pr_err("%s: channel not set up\n", __func__); + return; + } + + cbits = cbits & RMNET_CTRL_DTR; + if (cbits & RMNET_CTRL_DTR) + set_bits |= TIOCM_DTR; + else + clear_bits |= TIOCM_DTR; + + if (!ch_info->handle) + return; + + glink_sigs_local_get(ch_info->handle, &sig); + sig |= set_bits; + sig &= ~clear_bits; + + if (sig == ch_info->cbits_to_modem) + return; + + ch_info->cbits_to_modem = sig; + + glink_sigs_set(ch_info->handle, sig); +} + +static void glink_ctrl_notify_rx(void *handle, const void *priv, + const void *pkt_priv, const void *ptr, + size_t size) +{ + struct glink_channel *ch_info = (struct glink_channel *)priv; + unsigned long flags; + + spin_lock_irqsave(&ch_info->port_lock, flags); + if (ch_info->port && ch_info->port->send_cpkt_response) + ch_info->port->send_cpkt_response(ch_info->port, + (void *)ptr, size); + spin_unlock_irqrestore(&ch_info->port_lock, flags); + + ch_info->to_host++; + glink_rx_done(ch_info->handle, ptr, true); +} + +void glink_purge_tx_q(struct glink_channel *ch_info) +{ + struct rmnet_ctrl_pkt *cpkt; + + pr_debug("%s\n", __func__); + while (!list_empty(&ch_info->tx_q)) { + cpkt = list_first_entry(&ch_info->tx_q, + struct rmnet_ctrl_pkt, list); + list_del(&cpkt->list); + kfree(cpkt->buf); + kfree(cpkt); + } + + ch_info->tx_q_count = 0; +} + +/** + * Callback function notifying the change in the channel state. + * + * @handle: handle corresponding to the channel. + * @priv: private data passed as part of glink_open. + * @event: state event sent by the glink core. + * + * The event indicates the current state of the channel. + * + * GLINK_CONNECTED: + * The channel is opened by both client and remote and is ready for + * transfers. + * + * GLINK_LOCAL_DISCONNECTED: + * The channel is closed by the local client and no transfers + * should be initiated on this channel. + * + * GLINK_REMOTE_DISCONNECTED: + * The channel is closed by the remote client and no transfers should + * initiated further. + */ +static void glink_notify_state(void *handle, const void *priv, unsigned event) +{ + struct glink_channel *ch_info = (struct glink_channel *)priv; + struct grmnet *gr; + unsigned long flags; + + ch_info->channel_state = event; + + spin_lock_irqsave(&ch_info->port_lock, flags); + gr = ch_info->port; + pr_debug("%s: notify link state: %u\n", __func__, event); + switch (event) { + case GLINK_CONNECTED: + if (gr && gr->connect) { + gr->connect(gr); + queue_work(glink_ctrl_wq, &ch_info->connect_w); + intent_work.ch_info = ch_info; + queue_work(glink_ctrl_wq, &intent_work.work); + } + break; + case GLINK_LOCAL_DISCONNECTED: + ch_info->handle = NULL; + case GLINK_REMOTE_DISCONNECTED: + if (gr && gr->disconnect) + gr->disconnect(gr); + glink_purge_tx_q(ch_info); + break; + default: + pr_err("%s: invalid channel state notification\n", __func__); + } + spin_unlock_irqrestore(&ch_info->port_lock, flags); +} + +int glink_ctrl_connect(struct grmnet *gr, u8 client_num) +{ + struct glink_channel *ch_info; + unsigned long flags; + + if (client_num >= MAX_CHANNELS) { + pr_err("%s: Invalid client number\n", __func__); + return -EINVAL; + } + + ch_info = glink_channels[client_num]; + if (!ch_info) { + pr_err("%s: channel not set up\n", __func__); + return -EINVAL; + } + + if (!gr) { + pr_err("%s: no control port\n", __func__); + return -EINVAL; + } + + memset(&ch_info->open_cfg, 0, sizeof(struct glink_open_config)); + ch_info->open_cfg.options = GLINK_OPT_INITIAL_XPORT; + ch_info->open_cfg.edge = "mpss"; + ch_info->open_cfg.name = "DATA39_CNTL"; + ch_info->open_cfg.transport = "smem"; + ch_info->open_cfg.notify_rx = glink_ctrl_notify_rx; + ch_info->open_cfg.notify_tx_done = glink_ctrl_tx_done; + ch_info->open_cfg.notify_state = glink_notify_state; + ch_info->open_cfg.priv = ch_info; + + ch_info->channel_state = GLINK_REMOTE_DISCONNECTED; + ch_info->handle = NULL; + + spin_lock_irqsave(&ch_info->port_lock, flags); + ch_info->port = gr; + gr->send_encap_cmd = glink_send_cpkt_tomodem; + gr->notify_modem = glink_send_cbits_to_modem; + spin_unlock_irqrestore(&ch_info->port_lock, flags); + + queue_work(glink_ctrl_wq, &ch_info->connect_w); + return 0; +} + +void glink_ctrl_disconnect(struct grmnet *gr, u8 client_num) +{ + struct glink_channel *ch_info; + unsigned long flags; + + pr_debug("%s: glink ctrl disconnect\n", __func__); + if (client_num >= MAX_CHANNELS) { + pr_err("%s: Invalid client number\n", __func__); + return; + } + + ch_info = glink_channels[client_num]; + if (!ch_info) { + pr_err("%s: channel not set up\n", __func__); + return; + } + + spin_lock_irqsave(&ch_info->port_lock, flags); + ch_info->port->send_encap_cmd = 0; + ch_info->port->notify_modem = 0; + ch_info->port = 0; + + glink_purge_tx_q(ch_info); + spin_unlock_irqrestore(&ch_info->port_lock, flags); + + if (ch_info->handle) { + pr_debug("%s queue disconnect work\n", __func__); + queue_work(glink_ctrl_wq, &ch_info->disconnect_w); + } +} + +/** + * Callback function notifying the change in transport(smem) link state. + * G-Link acts as a wrapper around the underlying transport. This callback + * notifies any change in the state of that underlying transport. + * + * @cb_info: Structure containing the info of transport, + * edge and the link state. + * @priv: Private Data passed as part of link state callback register. + * + * GLINK_LINK_STATE_DOWN: The underlying transport link is down and no + * channels can be opened over this transport. + * + * GLINK_LINK_STATE_UP: The underlying transport link is up and the + * glink channels can be opened over this + * transport. + */ +static void glink_link_state_cb(struct glink_link_state_cb_info *cb_info, + void *priv) +{ + int i = 0; + + link_info.link_state = cb_info->link_state; + + switch (link_info.link_state) { + case GLINK_LINK_STATE_DOWN: + pr_debug("%s: %s link is down\n", __func__, cb_info->transport); + for (i = 0; i < MAX_CHANNELS; i++) { + if (glink_channels[i] && glink_channels[i]->port) { + queue_work(glink_ctrl_wq, + &glink_channels[i]->disconnect_w); + } + } + break; + case GLINK_LINK_STATE_UP: + pr_debug("%s: %s link is up\n", __func__, cb_info->transport); + for (i = 0; i < MAX_CHANNELS; i++) { + if (glink_channels[i] && glink_channels[i]->port) { + queue_work(glink_ctrl_wq, + &glink_channels[i]->connect_w); + } + } + break; + default: + pr_err("%s: invalid link state notification\n", __func__); + } +} + +static struct glink_link_info link_cb_info = { + .transport = "smem", + .edge = "mpss", + .glink_link_state_notif_cb = glink_link_state_cb, +}; + +int glink_ctrl_setup(enum ctrl_client client_num, unsigned int count, + u8 *port_idx) +{ + struct glink_channel *ch_info; + + pr_debug("%s: G-Link ctrl setup\n", __func__); + + if (client_num >= MAX_CHANNELS) { + pr_err("%s: Invalid client number\n", __func__); + return -EINVAL; + } + + ch_info = kzalloc(sizeof(struct glink_channel), GFP_ATOMIC); + if (!ch_info) + return -ENOMEM; + + glink_channels[client_num] = ch_info; + + link_info.link_state = GLINK_LINK_STATE_DOWN; + link_info.handle = glink_register_link_state_cb(&link_cb_info, NULL); + if (IS_ERR(link_info.handle)) { + pr_err("%s: Unable to register link cb %lu\n", __func__, + PTR_ERR(link_info.handle)); + kfree(ch_info); + glink_channels[client_num] = NULL; + return PTR_ERR(link_info.handle); + } + + glink_ctrl_wq = alloc_workqueue("glink_ctrl", + WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!glink_ctrl_wq) { + pr_err("%s: Unable to create workqueue glink_ctrl\n", + __func__); + kfree(ch_info); + glink_channels[client_num] = NULL; + glink_unregister_link_state_cb(link_info.handle); + link_info.handle = NULL; + return -ENOMEM; + } + + INIT_WORK(&intent_work.work, glink_ctrl_intent_worker); + + spin_lock_init(&ch_info->port_lock); + INIT_LIST_HEAD(&ch_info->tx_q); + INIT_WORK(&ch_info->write_w, glink_ctrl_write_w); + INIT_WORK(&ch_info->connect_w, glink_ctrl_connect_w); + INIT_WORK(&ch_info->disconnect_w, glink_ctrl_disconnect_w); + *port_idx = client_num; + + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +#define DEBUG_BUF_SIZE 1024 +static ssize_t glink_ctrl_read_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct glink_channel *c; + char *buf; + unsigned long flags; + int ret; + int i; + int temp = 0; + + buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < MAX_CHANNELS; i++) { + if (!glink_channels[i] || !glink_channels[i]->port) + continue; + + c = glink_channels[i]; + spin_lock_irqsave(&c->port_lock, flags); + + temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp, + "#CHANNEL:%d CHANNEL:%s ctrl_ch:%pK#\n" + "to_usbhost: %lu\n" + "to_modem: %lu\n" + "Tx_q_count: %u\n" + "DTR: %s\n" + "LINK UP: %d\n" + "ch_open: %d\n" + "ch_ready: %d\n", + i, c->open_cfg.name, c->handle, + c->to_host, c->to_modem, c->tx_q_count, + c->cbits_to_modem ? "HIGH" : "LOW", + (link_info.link_state) ? 0 : 1, + (c->handle) ? 1 : 0, + (c->channel_state == GLINK_CONNECTED) ? 1 : 0); + + spin_unlock_irqrestore(&c->port_lock, flags); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp); + + kfree(buf); + + return ret; +} + +static ssize_t glink_ctrl_reset_stats(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct glink_channel *c; + int i; + unsigned long flags; + + for (i = 0; i < MAX_CHANNELS; i++) { + if (!glink_channels[i] || !glink_channels[i]->port) + continue; + + c = glink_channels[i]; + + spin_lock_irqsave(&c->port_lock, flags); + + c->to_host = 0; + c->to_modem = 0; + + spin_unlock_irqrestore(&c->port_lock, flags); + } + return count; +} + +const struct file_operations glink_ctrl_stats_ops = { + .read = glink_ctrl_read_stats, + .write = glink_ctrl_reset_stats, +}; + +static struct dentry *glink_ctrl_dent; +static struct dentry *glink_ctrl_dfile; +static void glink_ctrl_debugfs_init(void) +{ + glink_ctrl_dent = debugfs_create_dir("usb_glink_ctrl", 0); + if (IS_ERR(glink_ctrl_dent)) + return; + + glink_ctrl_dfile = debugfs_create_file("status", 0444, glink_ctrl_dent, + 0, &glink_ctrl_stats_ops); + if (!glink_ctrl_dfile || IS_ERR(glink_ctrl_dfile)) + debugfs_remove(glink_ctrl_dent); +} + +static void glink_ctrl_debugfs_exit(void) +{ + debugfs_remove(glink_ctrl_dfile); + debugfs_remove(glink_ctrl_dent); +} + +#else +static void glink_ctrl_debugfs_init(void) { } +static void glink_ctrl_debugfs_exit(void) { } +#endif + +static int __init glink_ctrl_init(void) +{ + glink_ctrl_debugfs_init(); + + return 0; +} +module_init(glink_ctrl_init); + +static void __exit glink_ctrl_exit(void) +{ + glink_ctrl_debugfs_exit(); +} +module_exit(glink_ctrl_exit); diff --git a/drivers/usb/gadget/function/usb_gadget_xport.h b/drivers/usb/gadget/function/usb_gadget_xport.h index 47d92879b667..768ef84e1234 100644 --- a/drivers/usb/gadget/function/usb_gadget_xport.h +++ b/drivers/usb/gadget/function/usb_gadget_xport.h @@ -25,6 +25,7 @@ enum transport_type { USB_GADGET_XPORT_HSUART, USB_GADGET_XPORT_ETHER, USB_GADGET_XPORT_CHAR_BRIDGE, + USB_GADGET_XPORT_GLINK, USB_GADGET_XPORT_BAM_DMUX, USB_GADGET_XPORT_PCIE, USB_GADGET_XPORT_NONE, @@ -53,6 +54,8 @@ static char *xport_to_str(enum transport_type t) return "ETHER"; case USB_GADGET_XPORT_CHAR_BRIDGE: return "CHAR_BRIDGE"; + case USB_GADGET_XPORT_GLINK: + return "GLINK"; case USB_GADGET_XPORT_BAM_DMUX: return "BAM_DMUX"; case USB_GADGET_XPORT_PCIE: @@ -88,6 +91,8 @@ static enum transport_type str_to_xport(const char *name) return USB_GADGET_XPORT_ETHER; if (!strncasecmp("CHAR_BRIDGE", name, XPORT_STR_LEN)) return USB_GADGET_XPORT_CHAR_BRIDGE; + if (!strncasecmp("GLINK", name, XPORT_STR_LEN)) + return USB_GADGET_XPORT_GLINK; if (!strncasecmp("BAM_DMUX", name, XPORT_STR_LEN)) return USB_GADGET_XPORT_BAM_DMUX; if (!strncasecmp("PCIE", name, XPORT_STR_LEN)) -- GitLab From 292b67082dfef1a74d184fef448a96d7c9567d27 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Wed, 18 Oct 2017 11:41:24 +0530 Subject: [PATCH 1267/5498] usb: gadget: f_gps: Clear is_suspended flag on disable In cases where host resets the function after suspend, the resume is never called. In this case the is_suspended flag is never cleared and if remote wakeup is not enabled the function driver would never queue any response available notifications. Fix this by clearing the is_suspended flag on disable. CRs-Fixed: 2128747 Change-Id: I9e627756009a53eba987b669acab7de004e347a0 Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/function/f_gps.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_gps.c b/drivers/usb/gadget/function/f_gps.c index 073915cc40d6..d7da52ae630c 100644 --- a/drivers/usb/gadget/function/f_gps.c +++ b/drivers/usb/gadget/function/f_gps.c @@ -413,6 +413,7 @@ static void gps_disable(struct usb_function *f) usb_ep_disable(dev->notify); dev->notify->driver_data = NULL; + dev->is_suspended = false; atomic_set(&dev->online, 0); -- GitLab From ad856a7924b599ec658ae4aa56e4df691fdc60de Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Thu, 14 Sep 2017 18:00:58 +0800 Subject: [PATCH 1268/5498] input: sensors: add Lite-on ltr553 ALPS sensor driver MSM8909 SKUE uses ltr553 as ambient light and proximity sensor. Add ltr553 sensor driver to enable it. This snapshot is taken as of msm-3.10 'commit 1227f5746677 ("soc: qcom: smp2p: spinlock_test: Initialize work item")'with minor compile error fixed. Change-Id: Ib5462672d8029396acaca5999b0c14a11a68382b Signed-off-by: Bingzhe Cai --- drivers/input/misc/Kconfig | 10 + drivers/input/misc/Makefile | 1 + drivers/input/misc/ltr553.c | 2260 +++++++++++++++++++++++++++++++++++ 3 files changed, 2271 insertions(+) create mode 100644 drivers/input/misc/ltr553.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 3e357d813cf0..8f3b77eea781 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -779,4 +779,14 @@ config INPUT_DRV2667_HAPTICS source "drivers/input/misc/ots_pat9125/Kconfig" +config SENSORS_LTR553 + tristate "LTR553 light and proximity device driver" + depends on I2C + help + Say Y here if you want to enable the LTR553 light and + proximity sensor driver. + + To compile this driver as a module, choose M here: the + module will be called ltr553. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 58080ac8202d..5903b5c1aeb6 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -72,5 +72,6 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_SENSORS_LTR553) += ltr553.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/ diff --git a/drivers/input/misc/ltr553.c b/drivers/input/misc/ltr553.c new file mode 100644 index 000000000000..e3281f74d34d --- /dev/null +++ b/drivers/input/misc/ltr553.c @@ -0,0 +1,2260 @@ +/* Copyright (c) 2014-2015,2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTR553_I2C_NAME "ltr553" +#define LTR553_LIGHT_INPUT_NAME "ltr553-light" +#define LTR553_PROXIMITY_INPUT_NAME "ltr553-proximity" + +#define LTR553_REG_ALS_CTL 0x80 +#define LTR553_REG_PS_CTL 0x81 +#define LTR553_REG_PS_LED 0x82 +#define LTR553_REG_PS_N_PULSES 0x83 +#define LTR553_REG_PS_MEAS_RATE 0x84 +#define LTR553_REG_ALS_MEAS_RATE 0x85 +#define LTR553_REG_PART_ID 0x86 +#define LTR553_REG_ALS_DATA_CH1_0 0x88 +#define LTR553_REG_ALS_DATA_CH1_1 0x89 +#define LTR553_REG_ALS_DATA_CH0_0 0x8A +#define LTR553_REG_ALS_DATA_CH0_1 0x8B +#define LTR553_REG_ALS_PS_STATUS 0x8C +#define LTR553_REG_PS_DATA_0 0x8D +#define LTR553_REG_INTERRUPT 0x8F +#define LTR553_REG_PS_THRES_UP_0 0x90 +#define LTR553_REG_PS_OFFSET_1 0x94 +#define LTR553_REG_PS_OFFSET_0 0x95 +#define LTR553_REG_ALS_THRES_UP_0 0x97 +#define LTR553_REG_INTERRUPT_PERSIST 0x9E +#define LTR553_REG_MAGIC 0xFF + +#define LTR553_PART_ID 0x92 + +#define LTR553_ALS_SENSITIVITY 70 + +#define LTR553_BOOT_TIME_MS 120 +#define LTR553_WAKE_TIME_MS 10 + +#define LTR553_PS_SATURATE_MASK 0x8000 +#define LTR553_ALS_INT_MASK 0x08 +#define LTR553_PS_INT_MASK 0x02 + +#define LTR553_ALS_MEASURE_MASK 0x38 +#define LTR553_ALS_GAIN_MASK 0x1c + +/* default measurement rate is 100 ms */ +#define LTR553_ALS_DEFAULT_MEASURE_RATE 0x01 +#define LTR553_PS_MEASUREMENT_RATE_10MS 0x08 + +#define LTR553_CALIBRATE_SAMPLES 15 + +#define ALS_GAIN_SWITCH_THRESHOLD 60000 + +#define LTR553_ALS_INVALID(value) (value & 0x80) + +/* LTR553 ALS data is 16 bit */ +#define ALS_DATA_MASK 0xffff +#define ALS_LOW_BYTE(data) ((data) & 0xff) +#define ALS_HIGH_BYTE(data) (((data) >> 8) & 0xff) + +/* LTR553 PS data is 11 bit */ +#define PS_DATA_MASK 0x7ff +#define PS_LOW_BYTE(data) ((data) & 0xff) +#define PS_HIGH_BYTE(data) (((data) >> 8) & 0x7) + +/* Calculated by 10% transmittance */ +#define LTR553_MAX_LUX (ALS_DATA_MASK * 10) + +/* both als and ps interrupt are enabled */ +#define LTR553_INTERRUPT_SETTING 0x03 + +/* Any proximity distance change will wakeup SoC */ +#define LTR553_WAKEUP_ANY_CHANGE 0xff + +#define CAL_BUF_LEN 16 +enum { + CMD_WRITE = 0, + CMD_READ = 1, +}; + +struct regulator_map { + struct regulator *regulator; + int min_uv; + int max_uv; + char *supply; +}; + +struct pinctrl_config { + struct pinctrl *pinctrl; + struct pinctrl_state *state[2]; + char *name[2]; +}; + +struct ltr553_data { + struct i2c_client *i2c; + struct regmap *regmap; + struct regulator *config; + struct input_dev *input_light; + struct input_dev *input_proximity; + struct workqueue_struct *workqueue; + + struct sensors_classdev als_cdev; + struct sensors_classdev ps_cdev; + struct mutex ops_lock; + ktime_t last_als_ts; + ktime_t last_ps_ts; + struct work_struct report_work; + struct work_struct als_enable_work; + struct work_struct als_disable_work; + struct work_struct ps_enable_work; + struct work_struct ps_disable_work; + atomic_t wake_count; + + int irq_gpio; + int irq; + bool als_enabled; + bool ps_enabled; + u32 irq_flags; + int als_delay; + int ps_delay; + int als_cal; + int ps_cal; + int als_gain; + int als_persist; + int als_integration_time; + int als_measure_rate; + int ps_led; + int ps_pulses; + int ps_measure_rate; + int als_ps_persist; + int ps_wakeup_threshold; + + int last_als; + int last_ps; + int flush_count; + int power_enabled; + + unsigned int reg_addr; + char calibrate_buf[CAL_BUF_LEN]; + unsigned int bias; +}; + +struct als_coeff { + int ch0_coeff_i; + int ch1_coeff_i; + int ch0_coeff_f; + int ch1_coeff_f; + int win_fac; + int sign; +} __attribute__((__packed__)); + +static struct regulator_map power_config[] = { + {.supply = "vdd", .min_uv = 2000000, .max_uv = 3300000, }, + {.supply = "vio", .min_uv = 1750000, .max_uv = 1950000, }, +}; + +static struct pinctrl_config pin_config = { + .name = { "default", "sleep" }, +}; + +static struct als_coeff eqtn_map[] = { + { + .ch0_coeff_i = 1, + .ch1_coeff_i = 1, + .ch0_coeff_f = 7743, + .ch1_coeff_f = 1059, + .win_fac = 44, + .sign = 1, + }, + { + .ch0_coeff_i = 4, + .ch1_coeff_i = 1, + .ch0_coeff_f = 2785, + .ch1_coeff_f = 9548, + .win_fac = 50, + .sign = -1, + }, + { + .ch0_coeff_i = 0, + .ch1_coeff_i = 0, + .ch0_coeff_f = 5926, + .ch1_coeff_f = 1185, + .win_fac = 40, + .sign = 1, + }, + { + .ch0_coeff_i = 0, + .ch1_coeff_i = 0, + .ch0_coeff_f = 0, + .ch1_coeff_f = 0, + .win_fac = 1, + .sign = 1, + }, +}; + +/* ALS integration time in 10ms */ +static int als_int_fac_table[] = { 10, 5, 20, 40, 15, 25, 30, 35 }; +/* ALS gain table, index 4 & 5 are reserved */ +static int als_gain_table[] = {1, 2, 4, 8, 1, 1, 48, 96}; +/* ALS measurement repeat rate in ms */ +static int als_mrr_table[] = {50, 100, 200, 500, 1000, 2000, 2000, 2000}; +/* PS measurement repeat rate in ms */ +static int ps_mrr_table[] = { 50, 70, 100, 200, 500, 1000, 2000, 10, + 10, 10, 10, 10, 10, 10, 10, 10}; + +/* Tuned for devices with rubber */ +static int ps_distance_table[] = { 790, 337, 195, 114, 78, 62, 50 }; + +static int sensitivity_table[] = {150, 150, 100, 100, 0, 0, 100, 1}; + +static struct sensors_classdev als_cdev = { + .name = "ltr553-light", + .vendor = "Lite-On Technology Corp", + .version = 1, + .handle = SENSORS_LIGHT_HANDLE, + .type = SENSOR_TYPE_LIGHT, + .max_range = "65536", + .resolution = "1.0", + .sensor_power = "0.25", + .min_delay = 50000, + .max_delay = 2000, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .flags = 2, + .enabled = 0, + .delay_msec = 50, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; + +static struct sensors_classdev ps_cdev = { + .name = "ltr553-proximity", + .vendor = "Lite-On Technology Corp", + .version = 1, + .handle = SENSORS_PROXIMITY_HANDLE, + .type = SENSOR_TYPE_PROXIMITY, + .max_range = "7", + .resolution = "1.0", + .sensor_power = "0.25", + .min_delay = 10000, + .max_delay = 2000, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .flags = 3, + .enabled = 0, + .delay_msec = 50, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; + +static int sensor_power_init(struct device *dev, struct regulator_map *map, + int size) +{ + int rc; + int i; + + for (i = 0; i < size; i++) { + map[i].regulator = devm_regulator_get(dev, map[i].supply); + if (IS_ERR(map[i].regulator)) { + rc = PTR_ERR(map[i].regulator); + dev_err(dev, "Regualtor get failed vdd rc=%d\n", rc); + goto exit; + } + if (regulator_count_voltages(map[i].regulator) > 0) { + rc = regulator_set_voltage(map[i].regulator, + map[i].min_uv, map[i].max_uv); + if (rc) { + dev_err(dev, "Regulator set failed vdd rc=%d\n", + rc); + goto exit; + } + } + } + + return 0; + +exit: + /* Regulator not set correctly */ + for (i = i - 1; i >= 0; i--) { + if (regulator_count_voltages(map[i].regulator)) + regulator_set_voltage(map[i].regulator, 0, + map[i].max_uv); + } + + return rc; +} + +static int sensor_power_deinit(struct device *dev, struct regulator_map *map, + int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (!IS_ERR_OR_NULL(map[i].regulator)) { + if (regulator_count_voltages(map[i].regulator) > 0) + regulator_set_voltage(map[i].regulator, 0, + map[i].max_uv); + } + } + + return 0; +} + +static int sensor_power_config(struct device *dev, struct regulator_map *map, + int size, bool enable) +{ + int i; + int rc = 0; + + if (enable) { + for (i = 0; i < size; i++) { + rc = regulator_enable(map[i].regulator); + if (rc) { + dev_err(dev, "enable %s failed.\n", + map[i].supply); + goto exit_enable; + } + } + } else { + for (i = 0; i < size; i++) { + rc = regulator_disable(map[i].regulator); + if (rc) { + dev_err(dev, "disable %s failed.\n", + map[i].supply); + goto exit_disable; + } + } + } + + return 0; + +exit_enable: + for (i = i - 1; i >= 0; i--) + regulator_disable(map[i].regulator); + + return rc; + +exit_disable: + for (i = i - 1; i >= 0; i--) + if (regulator_enable(map[i].regulator)) + dev_err(dev, "enable %s failed\n", map[i].supply); + + return rc; +} + +static int sensor_pinctrl_init(struct device *dev, + struct pinctrl_config *config) +{ + config->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(config->pinctrl)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_ERR(config->pinctrl); + } + + config->state[0] = + pinctrl_lookup_state(config->pinctrl, config->name[0]); + if (IS_ERR_OR_NULL(config->state[0])) { + dev_err(dev, "Failed to look up %s\n", config->name[0]); + return PTR_ERR(config->state[0]); + } + + config->state[1] = + pinctrl_lookup_state(config->pinctrl, config->name[1]); + if (IS_ERR_OR_NULL(config->state[1])) { + dev_err(dev, "Failed to look up %s\n", config->name[1]); + return PTR_ERR(config->state[1]); + } + + return 0; +} + +static int ltr553_parse_dt(struct device *dev, struct ltr553_data *ltr) +{ + struct device_node *dp = dev->of_node; + u32 value; + int rc; + int i; + + rc = of_get_named_gpio_flags(dp, "liteon,irq-gpio", 0, + <r->irq_flags); + if (rc < 0) { + dev_err(dev, "unable to read irq gpio\n"); + return rc; + } + ltr->irq_gpio = rc; + + /* als ps persist */ + rc = of_property_read_u32(dp, "liteon,als-ps-persist", &value); + if (rc) { + dev_err(dev, "read liteon,als-ps-persist failed\n"); + return rc; + } + ltr->als_ps_persist = value; + + /* ps led */ + rc = of_property_read_u32(dp, "liteon,ps-led", &value); + if (rc) { + dev_err(dev, "read liteon,ps-led failed\n"); + return rc; + } + ltr->ps_led = value; + + /* ps pulses */ + rc = of_property_read_u32(dp, "liteon,ps-pulses", &value); + if (rc) { + dev_err(dev, "read liteon,ps-pulses failed\n"); + return rc; + } + if (value > 0x7) { + dev_err(dev, "liteon,ps-pulses out of range\n"); + return -EINVAL; + } + ltr->ps_pulses = value; + + /* als integration time */ + rc = of_property_read_u32(dp, "liteon,als-integration-time", &value); + if (rc) { + dev_err(dev, "read liteon,als-integration-time failed\n"); + return rc; + } + if (value > 0x7) { + dev_err(dev, "liteon,als-integration-time out of range\n"); + return -EINVAL; + } + ltr->als_integration_time = value; + + /* ps wakeup threshold */ + rc = of_property_read_u32(dp, "liteon,wakeup-threshold", &value); + if (rc) { + dev_err(dev, "liteon,wakeup-threshold incorrect, drop to default\n"); + value = LTR553_WAKEUP_ANY_CHANGE; + } + if ((value >= ARRAY_SIZE(ps_distance_table)) && + (value != LTR553_WAKEUP_ANY_CHANGE)) { + dev_err(dev, "wakeup threshold too big\n"); + return -EINVAL; + } + ltr->ps_wakeup_threshold = value; + + /* ps distance table */ + rc = of_property_read_u32_array(dp, "liteon,ps-distance-table", + ps_distance_table, ARRAY_SIZE(ps_distance_table)); + if ((rc == -ENODATA) || (rc == -EOVERFLOW)) { + dev_warn(dev, "liteon,ps-distance-table not correctly set\n"); + return rc; + } + + for (i = 1; i < ARRAY_SIZE(ps_distance_table); i++) { + if (ps_distance_table[i - 1] < ps_distance_table[i]) { + dev_err(dev, "ps distance table should in descend order\n"); + return -EINVAL; + } + } + + if (ps_distance_table[0] > PS_DATA_MASK) { + dev_err(dev, "distance table out of range\n"); + return -EINVAL; + } + + /* als gain */ + rc = of_property_read_u32(dp, "liteon,als-gain", &value); + if (rc) { + dev_err(dev, "read liteon,als-gain failed. Drop to default\n"); + value = 0; + } + /* 4 & 5 are reserved */ + if ((value > 0x7) || (value == 0x4) || (value == 0x5)) { + dev_err(dev, "liteon,als-gain invalid\n"); + return -EINVAL; + } + ltr->als_gain = value; + + /* als sensitivity */ + rc = of_property_read_u32_array(dp, "liteon,als-sensitivity", + sensitivity_table, ARRAY_SIZE(sensitivity_table)); + if (rc) + dev_info(dev, "read liteon,als-sensitivity failed. Drop to default\n"); + + /* als equation map */ + rc = of_property_read_u32_array(dp, "liteon,als-equation-0", + &eqtn_map[0].ch0_coeff_i, 6); + if (rc) + dev_warn(dev, "read liteon,als-equation-0 failed. Drop to default\n"); + + rc = of_property_read_u32_array(dp, "liteon,als-equation-0", + &eqtn_map[1].ch0_coeff_i, 6); + if (rc) + dev_warn(dev, "read liteon,als-equation-1 failed. Drop to default\n"); + + rc = of_property_read_u32_array(dp, "liteon,als-equation-0", + &eqtn_map[2].ch0_coeff_i, 6); + if (rc) + dev_warn(dev, "read liteon,als-equation-2 failed. Drop to default\n"); + + rc = of_property_read_u32_array(dp, "liteon,als-equation-3", + &eqtn_map[3].ch0_coeff_i, 6); + if (rc) + dev_warn(dev, "read liteon,als-equation-3 failed. Drop to default\n"); + + return 0; +} + +static int ltr553_check_device(struct ltr553_data *ltr) +{ + unsigned int part_id; + int rc; + + rc = regmap_read(ltr->regmap, LTR553_REG_PART_ID, &part_id); + if (rc) { + dev_err(<r->i2c->dev, "read reg %d failed.(%d)\n", + LTR553_REG_PART_ID, rc); + return rc; + } + + if (part_id != LTR553_PART_ID) + return -ENODEV; + + return 0; +} + +static int ltr553_init_input(struct ltr553_data *ltr) +{ + struct input_dev *input; + int status; + + input = devm_input_allocate_device(<r->i2c->dev); + if (!input) { + dev_err(<r->i2c->dev, "allocate light input device failed\n"); + return -ENOMEM; + } + + input->name = LTR553_LIGHT_INPUT_NAME; + input->phys = "ltr553/input0"; + input->id.bustype = BUS_I2C; + + input_set_capability(input, EV_ABS, ABS_MISC); + input_set_abs_params(input, ABS_MISC, 0, LTR553_MAX_LUX, 0, 0); + + status = input_register_device(input); + if (status) { + dev_err(<r->i2c->dev, "register light input device failed.\n"); + return status; + } + + ltr->input_light = input; + + input = devm_input_allocate_device(<r->i2c->dev); + if (!input) { + dev_err(<r->i2c->dev, "allocate proximity input device failed\n"); + return -ENOMEM; + } + + input->name = LTR553_PROXIMITY_INPUT_NAME; + input->phys = "ltr553/input1"; + input->id.bustype = BUS_I2C; + + input_set_capability(input, EV_ABS, ABS_DISTANCE); + input_set_abs_params(input, ABS_DISTANCE, 0, + ARRAY_SIZE(ps_distance_table), 0, 0); + + status = input_register_device(input); + if (status) { + dev_err(<r->i2c->dev, "register proxmity input device failed.\n"); + return status; + } + + ltr->input_proximity = input; + + return 0; +} + +static int ltr553_init_device(struct ltr553_data *ltr) +{ + int rc; + unsigned int tmp; + + /* Enable als/ps interrupt */ + rc = regmap_write(ltr->regmap, LTR553_REG_INTERRUPT, + LTR553_INTERRUPT_SETTING); + if (rc) { + dev_err(<r->i2c->dev, "write %d register failed\n", + LTR553_REG_INTERRUPT); + return rc; + } + + rc = regmap_write(ltr->regmap, LTR553_REG_INTERRUPT_PERSIST, + ltr->als_ps_persist); + if (rc) { + dev_err(<r->i2c->dev, "write %d register failed\n", + LTR553_REG_INTERRUPT_PERSIST); + return rc; + } + + rc = regmap_write(ltr->regmap, LTR553_REG_PS_N_PULSES, ltr->ps_pulses); + if (rc) { + dev_err(<r->i2c->dev, "write %d register failed\n", + LTR553_REG_PS_N_PULSES); + return rc; + } + + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_MEAS_RATE, + (ltr->als_integration_time << 3) | (ltr->als_measure_rate)); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed\n", + LTR553_REG_ALS_MEAS_RATE); + return rc; + } + + rc = regmap_write(ltr->regmap, LTR553_REG_PS_MEAS_RATE, + ltr->ps_measure_rate); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed\n", + LTR553_REG_PS_MEAS_RATE); + return rc; + } + + /* Set calibration parameter low byte */ + rc = regmap_write(ltr->regmap, LTR553_REG_PS_OFFSET_0, + PS_LOW_BYTE(ltr->bias)); + if (rc) { + dev_err(<r->i2c->dev, "write %d register failed\n", + LTR553_REG_PS_OFFSET_0); + return rc; + } + + /* Set calibration parameter high byte */ + rc = regmap_write(ltr->regmap, LTR553_REG_PS_OFFSET_1, + PS_HIGH_BYTE(ltr->bias)); + if (rc) { + dev_err(<r->i2c->dev, "write %d register failed\n", + LTR553_REG_PS_OFFSET_1); + return rc; + } + + /* set up als gain */ + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, &tmp); + if (rc) { + dev_err(<r->i2c->dev, "read %d register failed\n", + LTR553_REG_ALS_CTL); + return rc; + } + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + (tmp & (~0x1c)) | (ltr->als_gain << 2)); + if (rc) { + dev_err(<r->i2c->dev, "write %d register failed\n", + LTR553_REG_ALS_CTL); + return rc; + } + + return 0; +} + +/* Calculate the lux value based on ADC data */ +static int ltr553_calc_lux(int ch0data, int ch1data, int gain, int als_int_fac) +{ + int ratio; + int lux_i; + int lux_f; + int lux; + struct als_coeff *eqtn; + + /* avoid divided by 0 */ + if ((ch0data == 0) && (ch1data == 0)) + return 0; + + ratio = ch1data * 100 / (ch0data + ch1data); + if (ratio < 45) + eqtn = &eqtn_map[0]; + else if ((ratio >= 45) && (ratio < 68)) + eqtn = &eqtn_map[1]; + else if ((ratio >= 68) && (ratio < 99)) + eqtn = &eqtn_map[2]; + else + eqtn = &eqtn_map[3]; + + lux_i = (ch0data * eqtn->ch0_coeff_i + ch1data * eqtn->ch1_coeff_i * + eqtn->sign) * eqtn->win_fac; + lux_f = (ch0data * eqtn->ch0_coeff_f + ch1data * eqtn->ch1_coeff_f * + eqtn->sign) / 100 * eqtn->win_fac; + + lux = (lux_i + abs(lux_f) / 100) / (gain * als_int_fac); + + return lux; +} + +/* Calculate adc value based on lux. Return value is positive */ +static int ltr553_calc_adc(int ratio, int lux, int gain, int als_int_fac) +{ + int divisor_i; + int divisor_f; + int dividend; + struct als_coeff *eqtn; + int result; + + /* avoid divided by 0 */ + if (ratio == 0) + return 0; + + if (ratio < 45) + eqtn = &eqtn_map[0]; + else if ((ratio >= 45) && (ratio < 68)) + eqtn = &eqtn_map[1]; + else if ((ratio >= 68) && (ratio < 99)) + eqtn = &eqtn_map[2]; + else + eqtn = &eqtn_map[3]; + + dividend = lux * gain * als_int_fac; + divisor_i = ((100 - ratio) * eqtn->ch0_coeff_i / ratio + + eqtn->ch1_coeff_i * eqtn->sign) * eqtn->win_fac; + divisor_f = abs((100 - ratio) * eqtn->ch0_coeff_f / ratio + + eqtn->ch1_coeff_f * eqtn->sign) * eqtn->win_fac / 10000; + + /* avoid divided by 0 */ + if ((divisor_i + divisor_f) == 0) + return 0; + + result = dividend / (divisor_i + divisor_f); + + return result <= 0 ? 1 : result; +} + +/* update als gain and threshold */ +static int ltr553_als_update_setting(struct ltr553_data *ltr, + int ch0data, int ch1data, int als_int_fac) +{ + int gain_index; + unsigned int config; + unsigned int ratio; + unsigned int adc_base; + int rc; + int adc; + int i; + u8 als_data[4]; + + for (i = ARRAY_SIZE(als_gain_table) - 1; i >= 0; i--) { + if ((i == 4) || (i == 5)) + continue; + + if ((ch0data + ch1data) * als_gain_table[i] / + als_gain_table[ltr->als_gain] < + ALS_GAIN_SWITCH_THRESHOLD) + break; + } + + gain_index = i < 0 ? 0 : i; + + /* + * Disable als and enable it again to avoid incorrect value. + * Updating als gain during als measurement cycle will cause + * incorrect light sensor adc value. The logic here is to handle + * this scenario. + */ + + if (ltr->als_gain != gain_index) { + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, &config); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_CTL, rc); + return rc; + } + + /* disable als sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + config & (~0x1)); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_CTL, rc); + return rc; + } + + /* write new als gain */ + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + (config & (~0x1c)) | (gain_index << 2)); + if (rc) { + dev_err(<r->i2c->dev, "write %d register failed\n", + LTR553_REG_ALS_CTL); + return rc; + } + } + + if ((ch0data == 0) && (ch1data == 0)) { + adc = 1; + } else { + ratio = ch1data * 100 / (ch0data + ch1data); + dev_dbg(<r->i2c->dev, "ratio:%d\n", ratio); + adc = ltr553_calc_adc(ratio, sensitivity_table[gain_index], + als_gain_table[gain_index], als_int_fac); + } + + dev_dbg(<r->i2c->dev, "adc:%d\n", adc); + + /* catch'ya! */ + adc_base = ch0data * als_gain_table[gain_index] / + als_gain_table[ltr->als_gain]; + + /* upper threshold */ + if (adc_base + adc > ALS_DATA_MASK) { + als_data[0] = 0xff; + als_data[1] = 0xff; + } else { + als_data[0] = ALS_LOW_BYTE(adc_base + adc); + als_data[1] = ALS_HIGH_BYTE(adc_base + adc); + } + + /* lower threshold */ + if (adc_base < adc) { + als_data[2] = 0x0; + als_data[3] = 0x0; + } else { + als_data[2] = ALS_LOW_BYTE(adc_base - adc); + als_data[3] = ALS_HIGH_BYTE(adc_base - adc); + } + + rc = regmap_bulk_write(ltr->regmap, LTR553_REG_ALS_THRES_UP_0, + als_data, 4); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_THRES_UP_0, rc); + return rc; + } + + if (ltr->als_gain != gain_index) { + /* enable als_sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + (config & (~0x1c)) | (gain_index << 2) | 0x1); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_CTL, rc); + return rc; + } + + ltr->als_gain = gain_index; + } + + return 0; +} + +static int ltr553_process_data(struct ltr553_data *ltr, int als_ps) +{ + int als_int_fac; + ktime_t timestamp; + int rc = 0; + + unsigned int tmp; + u8 als_data[4]; + int lux; + int ch0data; + int ch1data; + + u8 ps_data[4]; + int i; + int distance; + + timestamp = ktime_get_boottime(); + + if (als_ps) { /* process als data */ + /* Read data */ + rc = regmap_bulk_read(ltr->regmap, LTR553_REG_ALS_DATA_CH1_0, + als_data, 4); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_DATA_CH1_0, rc); + goto exit; + } + ch0data = als_data[2] | (als_data[3] << 8); + ch1data = als_data[0] | (als_data[1] << 8); + + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_MEAS_RATE, &tmp); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_MEAS_RATE, rc); + goto exit; + } + + tmp = (tmp & LTR553_ALS_MEASURE_MASK) >> 3; + als_int_fac = als_int_fac_table[tmp]; + lux = ltr553_calc_lux(ch0data, ch1data, + als_gain_table[ltr->als_gain], als_int_fac); + + dev_dbg(<r->i2c->dev, "lux:%d als_data:0x%x-0x%x-0x%x-0x%x\n", + lux, als_data[0], als_data[1], + als_data[2], als_data[3]); + + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_PS_STATUS, &tmp); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_PS_STATUS, rc); + goto exit; + } + + + if ((lux != ltr->last_als) && (!LTR553_ALS_INVALID(tmp))) { + input_report_abs(ltr->input_light, ABS_MISC, lux); + input_event(ltr->input_light, EV_SYN, SYN_TIME_SEC, + ktime_to_timespec(timestamp).tv_sec); + input_event(ltr->input_light, EV_SYN, SYN_TIME_NSEC, + ktime_to_timespec(timestamp).tv_nsec); + input_sync(ltr->input_light); + + ltr->last_als_ts = timestamp; + } + + ltr->last_als = lux; + + dev_dbg(<r->i2c->dev, "previous als_gain:%d\n", + ltr->als_gain); + + rc = ltr553_als_update_setting(ltr, ch0data, ch1data, + als_int_fac); + if (rc) { + dev_err(<r->i2c->dev, "update setting failed\n"); + goto exit; + } + + dev_dbg(<r->i2c->dev, "new als_gain:%d\n", + ltr->als_gain); + + + } else { /* process ps value */ + rc = regmap_bulk_read(ltr->regmap, LTR553_REG_PS_DATA_0, + ps_data, 2); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_PS_DATA_0, rc); + goto exit; + } + + dev_dbg(<r->i2c->dev, "ps data: 0x%x 0x%x\n", + ps_data[0], ps_data[1]); + + tmp = (ps_data[1] << 8) | ps_data[0]; + if (tmp & LTR553_PS_SATURATE_MASK) + distance = 0; + else { + for (i = 0; i < ARRAY_SIZE(ps_distance_table); i++) { + if (tmp > ps_distance_table[i]) { + distance = i; + break; + } + } + distance = i; + } + + if (distance != ltr->last_ps) { + input_report_abs(ltr->input_proximity, ABS_DISTANCE, + distance); + input_event(ltr->input_proximity, EV_SYN, SYN_TIME_SEC, + ktime_to_timespec(timestamp).tv_sec); + input_event(ltr->input_proximity, EV_SYN, SYN_TIME_NSEC, + ktime_to_timespec(timestamp).tv_nsec); + input_sync(ltr->input_proximity); + + ltr->last_ps_ts = timestamp; + } + + ltr->last_ps = distance; + + /* lower threshold */ + if (distance < ARRAY_SIZE(ps_distance_table)) + tmp = ps_distance_table[distance]; + else + tmp = 0; + + ps_data[2] = PS_LOW_BYTE(tmp); + ps_data[3] = PS_HIGH_BYTE(tmp); + + /* upper threshold */ + if (distance > 0) + tmp = ps_distance_table[distance - 1]; + else + tmp = PS_DATA_MASK; + + ps_data[0] = PS_LOW_BYTE(tmp); + ps_data[1] = PS_HIGH_BYTE(tmp); + + dev_dbg(<r->i2c->dev, "ps threshold: 0x%x 0x%x 0x%x 0x%x\n", + ps_data[0], ps_data[1], ps_data[2], ps_data[3]); + + rc = regmap_bulk_write(ltr->regmap, LTR553_REG_PS_THRES_UP_0, + ps_data, 4); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_THRES_UP_0, rc); + goto exit; + } + } +exit: + return rc; +} + +static irqreturn_t ltr553_irq_handler(int irq, void *data) +{ + struct ltr553_data *ltr = data; + bool rc; + + rc = queue_work(ltr->workqueue, <r->report_work); + /* wake up event should hold a wake lock until reported */ + if (rc && (atomic_inc_return(<r->wake_count) == 1)) + pm_stay_awake(<r->i2c->dev); + + + return IRQ_HANDLED; +} + +static void ltr553_report_work(struct work_struct *work) +{ + struct ltr553_data *ltr = container_of(work, struct ltr553_data, + report_work); + int rc; + unsigned int status; + u8 buf[7]; + int fake_interrupt = 0; + + mutex_lock(<r->ops_lock); + + /* avoid fake interrupt */ + if (!ltr->power_enabled) { + dev_dbg(<r->i2c->dev, "fake interrupt triggered\n"); + fake_interrupt = 1; + goto exit; + } + + /* read status */ + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_PS_STATUS, &status); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_PS_STATUS, rc); + status |= LTR553_PS_INT_MASK; + goto exit; + } + + dev_dbg(<r->i2c->dev, "interrupt issued status=0x%x.\n", status); + + /* als interrupt issueed */ + if ((status & LTR553_ALS_INT_MASK) && (ltr->als_enabled)) { + rc = ltr553_process_data(ltr, 1); + if (rc) + goto exit; + dev_dbg(<r->i2c->dev, "process als done!\n"); + } + + if ((status & LTR553_PS_INT_MASK) && (ltr->ps_enabled)) { + rc = ltr553_process_data(ltr, 0); + if (rc) + goto exit; + dev_dbg(<r->i2c->dev, "process ps data done!\n"); + pm_wakeup_event(<r->input_proximity->dev, 200); + } + +exit: + if (atomic_dec_and_test(<r->wake_count)) { + pm_relax(<r->i2c->dev); + dev_dbg(<r->i2c->dev, "wake lock released\n"); + } + + /* clear interrupt */ + if (!fake_interrupt) { + if (regmap_bulk_read(ltr->regmap, LTR553_REG_ALS_DATA_CH1_0, + buf, ARRAY_SIZE(buf))) + dev_err(<r->i2c->dev, "clear interrupt failed\n"); + } + + mutex_unlock(<r->ops_lock); +} + +static int ltr553_enable_ps(struct ltr553_data *ltr, int enable) +{ + unsigned int config; + unsigned int tmp; + int rc = 0; + u8 buf[7]; + + rc = regmap_read(ltr->regmap, LTR553_REG_PS_CTL, &config); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_PS_CTL, rc); + return rc; + } + + if (enable) { + /* Enable ps sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_PS_CTL, + config | 0x02); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_CTL, rc); + goto exit; + } + + rc = regmap_read(ltr->regmap, LTR553_REG_PS_MEAS_RATE, &tmp); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_PS_MEAS_RATE, rc); + goto exit; + } + + /* Wait for data ready */ + msleep(ps_mrr_table[tmp & 0xf] + LTR553_WAKE_TIME_MS); + + /* clear last ps value */ + ltr->last_ps = -1; + + rc = ltr553_process_data(ltr, 0); + if (rc) { + dev_err(<r->i2c->dev, "process ps data failed\n"); + goto exit; + } + + /* clear interrupt */ + rc = regmap_bulk_read(ltr->regmap, LTR553_REG_ALS_DATA_CH1_0, + buf, ARRAY_SIZE(buf)); + if (rc) { + dev_err(<r->i2c->dev, "clear interrupt failed\n"); + goto exit; + } + + ltr->ps_enabled = true; + + } else { + /* disable ps_sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_PS_CTL, + config & (~0x02)); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_CTL, rc); + goto exit; + } + + ltr->ps_enabled = false; + } +exit: + return rc; +} + +static int ltr553_enable_als(struct ltr553_data *ltr, int enable) +{ + int rc = 0; + unsigned int config; + unsigned int tmp; + u8 buf[7]; + + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, &config); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_CTL, rc); + goto exit; + } + + if (enable) { + /* enable als_sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + config | 0x1); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_CTL, rc); + goto exit; + } + + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_MEAS_RATE, &tmp); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_MEAS_RATE, rc); + goto exit; + } + + /* Wait for data ready */ + msleep(als_mrr_table[tmp & 0x7] + LTR553_WAKE_TIME_MS); + + /* Clear last value and report even not change. */ + ltr->last_als = -1; + + rc = ltr553_process_data(ltr, 1); + if (rc) { + dev_err(<r->i2c->dev, "process als data failed\n"); + goto exit; + } + + /* clear interrupt */ + rc = regmap_bulk_read(ltr->regmap, LTR553_REG_ALS_DATA_CH1_0, + buf, ARRAY_SIZE(buf)); + if (rc) { + dev_err(<r->i2c->dev, "clear interrupt failed\n"); + goto exit; + } + + ltr->als_enabled = true; + } else { + /* disable als sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + config & (~0x1)); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_CTL, rc); + goto exit; + } + + ltr->als_enabled = false; + } + +exit: + return rc; +} + +static int ltr553_als_sync_delay(struct ltr553_data *ltr, + unsigned int als_delay) +{ + int index = 0; + int i; + unsigned int val; + int rc = 0; + int min; + + if (!ltr->power_enabled) { + dev_dbg(<r->i2c->dev, "power is not enabled\n"); + return 0; + } + + min = abs(als_delay - als_mrr_table[0]); + for (i = 0; i < ARRAY_SIZE(als_mrr_table); i++) { + if (als_mrr_table[i] >= 10 * + als_int_fac_table[ltr->als_integration_time]) { + if (als_delay == als_mrr_table[i]) { + index = i; + break; + } + if (min > abs(als_delay - als_mrr_table[i])) { + index = i; + min = abs(als_delay - als_mrr_table[i]); + } + } + } + + dev_dbg(<r->i2c->dev, "als delay %d ms\n", als_mrr_table[index]); + + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_MEAS_RATE, &val); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed\n", + LTR553_REG_ALS_MEAS_RATE); + goto exit; + } + val &= ~0x7; + + ltr->als_measure_rate = index; + rc = regmap_write(ltr->regmap, LTR553_REG_ALS_MEAS_RATE, val | index); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed\n", + LTR553_REG_ALS_MEAS_RATE); + goto exit; + } + +exit: + return rc; +} + + +static int ltr553_ps_sync_delay(struct ltr553_data *ltr, unsigned int ps_delay) +{ + int index = 0; + int i; + int rc = 0; + int min; + + if (!ltr->power_enabled) { + dev_dbg(<r->i2c->dev, "power is not enabled\n"); + return 0; + } + + min = abs(ps_delay - ps_mrr_table[0]); + for (i = 0; i < ARRAY_SIZE(ps_mrr_table); i++) { + if (ps_delay == ps_mrr_table[i]) { + index = i; + break; + } + if (min > abs(ps_delay - ps_mrr_table[i])) { + min = abs(ps_delay - ps_mrr_table[i]); + index = i; + } + } + + ltr->ps_measure_rate = index; + dev_dbg(<r->i2c->dev, "ps delay %d ms\n", ps_mrr_table[index]); + + rc = regmap_write(ltr->regmap, LTR553_REG_PS_MEAS_RATE, index); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed\n", + LTR553_REG_PS_MEAS_RATE); + goto exit; + } + +exit: + return rc; +} + +static void ltr553_als_enable_work(struct work_struct *work) +{ + struct ltr553_data *ltr = container_of(work, struct ltr553_data, + als_enable_work); + + mutex_lock(<r->ops_lock); + if (!ltr->power_enabled) { /* new HAL? */ + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), true)) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + + msleep(LTR553_BOOT_TIME_MS); + ltr->power_enabled = true; + if (ltr553_init_device(ltr)) { + dev_err(<r->i2c->dev, "init device failed\n"); + goto exit_power_off; + } + + ltr553_als_sync_delay(ltr, ltr->als_delay); + } + + if (ltr553_enable_als(ltr, 1)) { + dev_err(<r->i2c->dev, "enable als failed\n"); + goto exit_power_off; + } + +exit_power_off: + if ((!ltr->als_enabled) && (!ltr->ps_enabled) && + ltr->power_enabled) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), false)) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + ltr->power_enabled = false; + } +exit: + mutex_unlock(<r->ops_lock); +} + + +static void ltr553_als_disable_work(struct work_struct *work) +{ + struct ltr553_data *ltr = container_of(work, struct ltr553_data, + als_disable_work); + + mutex_lock(<r->ops_lock); + + if (ltr553_enable_als(ltr, 0)) { + dev_err(<r->i2c->dev, "disable als failed\n"); + goto exit; + } + + if ((!ltr->als_enabled) && (!ltr->ps_enabled) && ltr->power_enabled) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), false)) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + + ltr->power_enabled = false; + } + +exit: + mutex_unlock(<r->ops_lock); +} + +static void ltr553_ps_enable_work(struct work_struct *work) +{ + struct ltr553_data *ltr = container_of(work, struct ltr553_data, + ps_enable_work); + + mutex_lock(<r->ops_lock); + if (!ltr->power_enabled) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), true)) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + + msleep(LTR553_BOOT_TIME_MS); + ltr->power_enabled = true; + + if (ltr553_init_device(ltr)) { + dev_err(<r->i2c->dev, "init device failed\n"); + goto exit_power_off; + } + + ltr553_ps_sync_delay(ltr, ltr->ps_delay); + } + + if (ltr553_enable_ps(ltr, 1)) { + dev_err(<r->i2c->dev, "enable ps failed\n"); + goto exit_power_off; + } + +exit_power_off: + if ((!ltr->als_enabled) && (!ltr->ps_enabled) && + ltr->power_enabled) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), false)) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + ltr->power_enabled = false; + } + +exit: + mutex_unlock(<r->ops_lock); +} + +static void ltr553_ps_disable_work(struct work_struct *work) +{ + struct ltr553_data *ltr = container_of(work, struct ltr553_data, + ps_disable_work); + + mutex_lock(<r->ops_lock); + + if (ltr553_enable_ps(ltr, 0)) { + dev_err(<r->i2c->dev, "ltrsable ps failed\n"); + goto exit; + } + + if ((!ltr->als_enabled) && (!ltr->ps_enabled) && ltr->power_enabled) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), false)) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + + ltr->power_enabled = false; + } +exit: + mutex_unlock(<r->ops_lock); +} + + +static struct regmap_config ltr553_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ltr553_cdev_enable_als(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, als_cdev); + + mutex_lock(<r->ops_lock); + + if (enable) + queue_work(ltr->workqueue, <r->als_enable_work); + else + queue_work(ltr->workqueue, <r->als_disable_work); + + mutex_unlock(<r->ops_lock); + + return 0; +} + +static int ltr553_cdev_enable_ps(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, ps_cdev); + + mutex_lock(<r->ops_lock); + + if (enable) + queue_work(ltr->workqueue, <r->ps_enable_work); + else + queue_work(ltr->workqueue, <r->ps_disable_work); + + mutex_unlock(<r->ops_lock); + + return 0; +} + +static int ltr553_cdev_set_als_delay(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec) +{ + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, als_cdev); + int rc; + + mutex_lock(<r->ops_lock); + + ltr->als_delay = delay_msec; + rc = ltr553_als_sync_delay(ltr, delay_msec); + + mutex_unlock(<r->ops_lock); + + return rc; +} + +static int ltr553_cdev_set_ps_delay(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec) +{ + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, ps_cdev); + int rc; + + mutex_lock(<r->ops_lock); + + ltr->ps_delay = delay_msec; + rc = ltr553_ps_sync_delay(ltr, delay_msec); + + mutex_unlock(<r->ops_lock); + + return 0; +} + +static int ltr553_cdev_ps_flush(struct sensors_classdev *sensors_cdev) +{ + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, ps_cdev); + + input_event(ltr->input_proximity, EV_SYN, SYN_CONFIG, + ltr->flush_count++); + input_sync(ltr->input_proximity); + + return 0; +} + +static int ltr553_cdev_als_flush(struct sensors_classdev *sensors_cdev) +{ + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, als_cdev); + + input_event(ltr->input_light, EV_SYN, SYN_CONFIG, ltr->flush_count++); + input_sync(ltr->input_light); + + return 0; +} + +/* This function should be called when sensor is disabled */ +static int ltr553_cdev_ps_calibrate(struct sensors_classdev *sensors_cdev, + int axis, int apply_now) +{ + int rc; + int power; + unsigned int config; + unsigned int interrupt; + u16 min = PS_DATA_MASK; + u8 ps_data[2]; + int count = LTR553_CALIBRATE_SAMPLES; + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, ps_cdev); + + + if (axis != AXIS_BIAS) + return 0; + + mutex_lock(<r->ops_lock); + + /* Ensure only be called when sensors in standy mode */ + if (ltr->als_enabled || ltr->ps_enabled) { + rc = -EPERM; + goto exit; + } + + power = ltr->power_enabled; + if (!power) { + rc = sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), true); + if (rc) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + + msleep(LTR553_BOOT_TIME_MS); + + rc = ltr553_init_device(ltr); + if (rc) { + dev_err(<r->i2c->dev, "init ltr553 failed\n"); + goto exit; + } + } + + rc = regmap_read(ltr->regmap, LTR553_REG_INTERRUPT, &interrupt); + if (rc) { + dev_err(<r->i2c->dev, "read interrupt configuration failed\n"); + goto exit_power_off; + } + + /* disable interrupt */ + rc = regmap_write(ltr->regmap, LTR553_REG_INTERRUPT, 0x0); + if (rc) { + dev_err(<r->i2c->dev, "disable interrupt failed\n"); + goto exit_power_off; + } + + rc = regmap_read(ltr->regmap, LTR553_REG_PS_CTL, &config); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_PS_CTL, rc); + goto exit_enable_interrupt; + } + + /* clear offset */ + ps_data[0] = 0; + ps_data[1] = 0; + rc = regmap_bulk_write(ltr->regmap, LTR553_REG_PS_OFFSET_1, + ps_data, 2); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_OFFSET_1, rc); + goto exit_enable_interrupt; + } + + /* enable ps sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_PS_CTL, config | 0x02); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_CTL, rc); + goto exit_enable_interrupt; + } + + /* ps measurement rate set to fastest rate */ + rc = regmap_write(ltr->regmap, LTR553_REG_PS_MEAS_RATE, + LTR553_PS_MEASUREMENT_RATE_10MS); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_PS_MEAS_RATE, rc); + goto exit_enable_interrupt; + } + + msleep(LTR553_WAKE_TIME_MS); + + while (--count) { + /* the measurement rate is 10 ms */ + usleep_range(11000, 12000); + rc = regmap_bulk_read(ltr->regmap, LTR553_REG_PS_DATA_0, + ps_data, 2); + if (rc) { + dev_err(<r->i2c->dev, "read PS data failed\n"); + break; + } + if (min > ((ps_data[1] << 8) | ps_data[0])) + min = (ps_data[1] << 8) | ps_data[0]; + } + + /* disable ps sensor */ + rc = regmap_write(ltr->regmap, LTR553_REG_PS_CTL, config); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_CTL, rc); + goto exit_enable_interrupt; + } + + if (!count) { + if (min > (PS_DATA_MASK >> 1)) { + dev_err(<r->i2c->dev, "ps data out of range, check if shield\n"); + rc = -EINVAL; + goto exit_enable_interrupt; + } + + if (apply_now) { + ps_data[1] = PS_LOW_BYTE(min); + ps_data[0] = PS_HIGH_BYTE(min); + rc = regmap_bulk_write(ltr->regmap, + LTR553_REG_PS_OFFSET_1, ps_data, 2); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_OFFSET_1, rc); + goto exit_enable_interrupt; + } + ltr->bias = min; + } + + snprintf(ltr->calibrate_buf, sizeof(ltr->calibrate_buf), + "0,0,%d", min); + dev_dbg(<r->i2c->dev, "result: %s\n", ltr->calibrate_buf); + } else { + dev_err(<r->i2c->dev, "calibration failed\n"); + rc = -EINVAL; + } + +exit_enable_interrupt: + if (regmap_write(ltr->regmap, LTR553_REG_INTERRUPT, interrupt)) { + dev_err(<r->i2c->dev, "enable interrupt failed\n"); + goto exit_power_off; + } + +exit_power_off: + if (!power) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), false)) { + dev_err(<r->i2c->dev, "power off sensor failed.\n"); + goto exit; + } + } +exit: + mutex_unlock(<r->ops_lock); + return rc; +} + +static int ltr553_cdev_ps_write_cal(struct sensors_classdev *sensors_cdev, + struct cal_result_t *cal_result) +{ + int power; + u8 ps_data[2]; + int rc = 0; + struct ltr553_data *ltr = container_of(sensors_cdev, + struct ltr553_data, ps_cdev); + + mutex_lock(<r->ops_lock); + power = ltr->power_enabled; + if (!power) { + rc = sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), true); + if (rc) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + } + + ltr->bias = cal_result->bias; + ps_data[1] = PS_LOW_BYTE(cal_result->bias); + ps_data[0] = PS_HIGH_BYTE(cal_result->bias); + rc = regmap_bulk_write(ltr->regmap, LTR553_REG_PS_OFFSET_1, + ps_data, 2); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_PS_OFFSET_1, rc); + goto exit_power_off; + } + + snprintf(ltr->calibrate_buf, sizeof(ltr->calibrate_buf), "0,0,%d", + ltr->bias); + +exit_power_off: + if (!power) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), false)) { + dev_err(<r->i2c->dev, "power off sensor failed.\n"); + goto exit; + } + } +exit: + + mutex_unlock(<r->ops_lock); + return rc; +}; + +static ssize_t ltr553_register_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ltr553_data *ltr = dev_get_drvdata(dev); + unsigned int val; + int rc; + ssize_t count = 0; + int i; + + if (ltr->reg_addr == LTR553_REG_MAGIC) { + for (i = 0; i <= 0x1f; i++) { + rc = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL + i, + &val); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed\n", + LTR553_REG_ALS_CTL + i); + break; + } + count += snprintf(&buf[count], PAGE_SIZE, + "0x%x: 0x%x\n", LTR553_REG_ALS_CTL + i, + val); + } + } else { + rc = regmap_read(ltr->regmap, ltr->reg_addr, &val); + if (rc) { + dev_err(<r->i2c->dev, "read %d failed\n", + ltr->reg_addr); + return rc; + } + count += snprintf(&buf[count], PAGE_SIZE, "0x%x:0x%x\n", + ltr->reg_addr, val); + } + + return count; +} + +static ssize_t ltr553_register_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ltr553_data *ltr = dev_get_drvdata(dev); + unsigned int reg; + unsigned int val; + unsigned int cmd; + int rc; + + if (sscanf(buf, "%u %u %u\n", &cmd, ®, &val) < 2) { + dev_err(<r->i2c->dev, "argument error\n"); + return -EINVAL; + } + + if (cmd == CMD_WRITE) { + rc = regmap_write(ltr->regmap, reg, val); + if (rc) { + dev_err(<r->i2c->dev, "write %d failed\n", reg); + return rc; + } + } else if (cmd == CMD_READ) { + ltr->reg_addr = reg; + dev_dbg(<r->i2c->dev, "register address set to 0x%x\n", reg); + } + + return size; +} + +static DEVICE_ATTR(register, S_IWUSR | S_IRUGO, + ltr553_register_show, + ltr553_register_store); + +static struct attribute *ltr553_attr[] = { + &dev_attr_register.attr, + NULL +}; + +static const struct attribute_group ltr553_attr_group = { + .attrs = ltr553_attr, +}; + +static int ltr553_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltr553_data *ltr; + int res = 0; + + dev_dbg(&client->dev, "probling ltr553...\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "ltr553 i2c check failed.\n"); + return -ENODEV; + } + + ltr = devm_kzalloc(&client->dev, sizeof(struct ltr553_data), + GFP_KERNEL); + if (!ltr) + return -ENOMEM; + + ltr->i2c = client; + + if (client->dev.of_node) { + res = ltr553_parse_dt(&client->dev, ltr); + if (res) { + dev_err(&client->dev, + "unable to parse device tree.(%d)\n", res); + goto out; + } + } else { + dev_err(&client->dev, "device tree not found.\n"); + res = -ENODEV; + goto out; + } + + dev_set_drvdata(&client->dev, ltr); + mutex_init(<r->ops_lock); + + ltr->regmap = devm_regmap_init_i2c(client, <r553_regmap_config); + if (IS_ERR(ltr->regmap)) { + dev_err(&client->dev, "init regmap failed.(%ld)\n", + PTR_ERR(ltr->regmap)); + res = PTR_ERR(ltr->regmap); + goto out; + } + + res = sensor_power_init(&client->dev, power_config, + ARRAY_SIZE(power_config)); + if (res) { + dev_err(&client->dev, "init power failed.\n"); + goto out; + } + + res = sensor_power_config(&client->dev, power_config, + ARRAY_SIZE(power_config), true); + if (res) { + dev_err(&client->dev, "power up sensor failed.\n"); + goto err_power_config; + } + + res = sensor_pinctrl_init(&client->dev, &pin_config); + if (res) { + dev_err(&client->dev, "init pinctrl failed.\n"); + goto err_pinctrl_init; + } + + msleep(LTR553_BOOT_TIME_MS); + + res = ltr553_check_device(ltr); + if (res) { + dev_err(&client->dev, "check device failed.\n"); + goto err_check_device; + } + + ltr->als_measure_rate = LTR553_ALS_DEFAULT_MEASURE_RATE; + + res = ltr553_init_device(ltr); + if (res) { + dev_err(&client->dev, "check device failed.\n"); + goto err_init_device; + } + + /* configure interrupt */ + if (gpio_is_valid(ltr->irq_gpio)) { + res = gpio_request(ltr->irq_gpio, "ltr553_interrupt"); + if (res) { + dev_err(&client->dev, + "unable to request interrupt gpio %d\n", + ltr->irq_gpio); + goto err_request_gpio; + } + + res = gpio_direction_input(ltr->irq_gpio); + if (res) { + dev_err(&client->dev, + "unable to set direction for gpio %d\n", + ltr->irq_gpio); + goto err_set_direction; + } + + ltr->irq = gpio_to_irq(ltr->irq_gpio); + + res = devm_request_irq(&client->dev, ltr->irq, + ltr553_irq_handler, + ltr->irq_flags | IRQF_ONESHOT, + "ltr553", ltr); + + if (res) { + dev_err(&client->dev, + "request irq %d failed(%d),\n", + ltr->irq, res); + goto err_request_irq; + } + + /* device wakeup initialization */ + device_init_wakeup(&client->dev, 1); + + ltr->workqueue = alloc_workqueue("ltr553_workqueue", + WQ_FREEZABLE, 0); + INIT_WORK(<r->report_work, ltr553_report_work); + INIT_WORK(<r->als_enable_work, ltr553_als_enable_work); + INIT_WORK(<r->als_disable_work, ltr553_als_disable_work); + INIT_WORK(<r->ps_enable_work, ltr553_ps_enable_work); + INIT_WORK(<r->ps_disable_work, ltr553_ps_disable_work); + + } else { + res = -ENODEV; + goto err_init_device; + } + + res = sysfs_create_group(&client->dev.kobj, <r553_attr_group); + if (res) { + dev_err(&client->dev, "sysfs create group failed\n"); + goto err_create_group; + } + + res = ltr553_init_input(ltr); + if (res) { + dev_err(&client->dev, "init input failed.\n"); + goto err_init_input; + } + + ltr->als_cdev = als_cdev; + ltr->als_cdev.sensors_enable = ltr553_cdev_enable_als; + ltr->als_cdev.sensors_poll_delay = ltr553_cdev_set_als_delay; + ltr->als_cdev.sensors_flush = ltr553_cdev_als_flush; + res = sensors_classdev_register(<r->input_light->dev, <r->als_cdev); + if (res) { + dev_err(&client->dev, "sensors class register failed.\n"); + goto err_register_als_cdev; + } + + ltr->ps_cdev = ps_cdev; + ltr->ps_cdev.sensors_enable = ltr553_cdev_enable_ps; + ltr->ps_cdev.sensors_poll_delay = ltr553_cdev_set_ps_delay; + ltr->ps_cdev.sensors_flush = ltr553_cdev_ps_flush; + ltr->ps_cdev.sensors_calibrate = ltr553_cdev_ps_calibrate; + ltr->ps_cdev.sensors_write_cal_params = ltr553_cdev_ps_write_cal; + ltr->ps_cdev.params = ltr->calibrate_buf; + res = sensors_classdev_register(<r->input_proximity->dev, + <r->ps_cdev); + if (res) { + dev_err(&client->dev, "sensors class register failed.\n"); + goto err_register_ps_cdev; + } + + sensor_power_config(&client->dev, power_config, + ARRAY_SIZE(power_config), false); + + dev_dbg(&client->dev, "ltr553 successfully probed!\n"); + + return 0; + +err_register_ps_cdev: + sensors_classdev_unregister(<r->als_cdev); +err_register_als_cdev: +err_init_input: + sysfs_remove_group(&client->dev.kobj, <r553_attr_group); +err_create_group: +err_request_irq: +err_set_direction: + gpio_free(ltr->irq_gpio); +err_request_gpio: +err_init_device: + device_init_wakeup(&client->dev, 0); +err_check_device: +err_pinctrl_init: + sensor_power_config(&client->dev, power_config, + ARRAY_SIZE(power_config), false); +err_power_config: + sensor_power_deinit(&client->dev, power_config, + ARRAY_SIZE(power_config)); +out: + return res; +} + +static int ltr553_remove(struct i2c_client *client) +{ + struct ltr553_data *ltr = dev_get_drvdata(&client->dev); + + sensors_classdev_unregister(<r->ps_cdev); + sensors_classdev_unregister(<r->als_cdev); + + if (ltr->input_light) + input_unregister_device(ltr->input_light); + + if (ltr->input_proximity) + input_unregister_device(ltr->input_proximity); + + destroy_workqueue(ltr->workqueue); + device_init_wakeup(<r->i2c->dev, 0); + sensor_power_config(&client->dev, power_config, + ARRAY_SIZE(power_config), false); + sensor_power_deinit(&client->dev, power_config, + ARRAY_SIZE(power_config)); + return 0; +} + +static int ltr553_suspend(struct device *dev) +{ + int res = 0; + struct ltr553_data *ltr = dev_get_drvdata(dev); + u8 ps_data[4]; + unsigned int config; + int idx = ltr->ps_wakeup_threshold; + + dev_dbg(dev, "suspending ltr553..."); + + mutex_lock(<r->ops_lock); + + /* proximity is enabled */ + if (ltr->ps_enabled) { + /* disable als sensor to avoid wake up by als interrupt */ + if (ltr->als_enabled) { + res = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, + &config); + if (res) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_CTL, res); + return res; + } + + res = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + config & (~0x1)); + if (res) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_CTL, res); + goto exit; + } + } + + /* Don't power off sensor because proximity is a + * wake up sensor. + */ + if (device_may_wakeup(<r->i2c->dev)) { + dev_dbg(<r->i2c->dev, "enable irq wake\n"); + enable_irq_wake(ltr->irq); + } + + /* Setup threshold to avoid frequent wakeup */ + if (device_may_wakeup(<r->i2c->dev) && + (idx != LTR553_WAKEUP_ANY_CHANGE)) { + dev_dbg(<r->i2c->dev, "last ps: %d\n", ltr->last_ps); + if (ltr->last_ps > idx) { + ps_data[2] = 0x0; + ps_data[3] = 0x0; + ps_data[0] = + PS_LOW_BYTE(ps_distance_table[idx]); + ps_data[1] = + PS_HIGH_BYTE(ps_distance_table[idx]); + } else { + ps_data[2] = + PS_LOW_BYTE(ps_distance_table[idx]); + ps_data[3] = + PS_HIGH_BYTE(ps_distance_table[idx]); + ps_data[0] = PS_LOW_BYTE(PS_DATA_MASK); + ps_data[1] = PS_HIGH_BYTE(PS_DATA_MASK); + } + + res = regmap_bulk_write(ltr->regmap, + LTR553_REG_PS_THRES_UP_0, ps_data, 4); + if (res) { + dev_err(<r->i2c->dev, "set up threshold failed\n"); + goto exit; + } + } + } else { + /* power off */ + disable_irq(ltr->irq); + if (ltr->power_enabled) { + res = sensor_power_config(dev, power_config, + ARRAY_SIZE(power_config), false); + if (res) { + dev_err(dev, "failed to suspend ltr553\n"); + enable_irq(ltr->irq); + goto exit; + } + } + pinctrl_select_state(pin_config.pinctrl, pin_config.state[1]); + } +exit: + mutex_unlock(<r->ops_lock); + return res; +} + +static int ltr553_resume(struct device *dev) +{ + int res = 0; + struct ltr553_data *ltr = dev_get_drvdata(dev); + unsigned int config; + + dev_dbg(dev, "resuming ltr553..."); + if (ltr->ps_enabled) { + if (device_may_wakeup(<r->i2c->dev)) { + dev_dbg(<r->i2c->dev, "disable irq wake\n"); + disable_irq_wake(ltr->irq); + } + + if (ltr->als_enabled) { + res = regmap_read(ltr->regmap, LTR553_REG_ALS_CTL, + &config); + if (res) { + dev_err(<r->i2c->dev, "read %d failed.(%d)\n", + LTR553_REG_ALS_CTL, res); + goto exit; + } + + res = regmap_write(ltr->regmap, LTR553_REG_ALS_CTL, + config | 0x1); + if (res) { + dev_err(<r->i2c->dev, "write %d failed.(%d)\n", + LTR553_REG_ALS_CTL, res); + goto exit; + } + } + } else { + pinctrl_select_state(pin_config.pinctrl, pin_config.state[0]); + /* Power up sensor */ + if (ltr->power_enabled) { + res = sensor_power_config(dev, power_config, + ARRAY_SIZE(power_config), true); + if (res) { + dev_err(dev, "failed to power up ltr553\n"); + goto exit; + } + msleep(LTR553_BOOT_TIME_MS); + + res = ltr553_init_device(ltr); + if (res) { + dev_err(dev, "failed to init ltr553\n"); + goto exit_power_off; + } + } + + if (ltr->als_enabled) { + res = ltr553_enable_als(ltr, ltr->als_enabled); + if (res) { + dev_err(dev, "failed to enable ltr553\n"); + goto exit_power_off; + } + } + + enable_irq(ltr->irq); + } + + return res; + +exit_power_off: + if ((!ltr->als_enabled) && (!ltr->ps_enabled) && + ltr->power_enabled) { + if (sensor_power_config(<r->i2c->dev, power_config, + ARRAY_SIZE(power_config), false)) { + dev_err(<r->i2c->dev, "power up sensor failed.\n"); + goto exit; + } + ltr->power_enabled = false; + } + +exit: + return res; +} + +static const struct i2c_device_id ltr553_id[] = { + { LTR553_I2C_NAME, 0 }, + { } +}; + +static struct of_device_id ltr553_match_table[] = { + { .compatible = "liteon,ltr553", }, + { }, +}; + +static const struct dev_pm_ops ltr553_pm_ops = { + .suspend = ltr553_suspend, + .resume = ltr553_resume, +}; + +static struct i2c_driver ltr553_driver = { + .probe = ltr553_probe, + .remove = ltr553_remove, + .id_table = ltr553_id, + .driver = { + .owner = THIS_MODULE, + .name = LTR553_I2C_NAME, + .of_match_table = ltr553_match_table, + .pm = <r553_pm_ops, + }, +}; + +module_i2c_driver(ltr553_driver); + +MODULE_DESCRIPTION("LTR-553ALPS Driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From dbe95c110b016ebd68f3d860badeff8864ba057b Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Thu, 14 Sep 2017 17:35:48 +0800 Subject: [PATCH 1269/5498] input: sensors: add BOSCH bma2x2 accelerometer driver Add BOSCH bma2x2 accelerometer driver to support sensors on MSM8909. This snapshot is taken as of msm-3.10 'commit 1227f5746677 ("soc: qcom: smp2p: spinlock_test: Initialize work item")' Change-Id: I0169725857887307f755b6fdf899e407d8ad74ad Signed-off-by: Bingzhe Cai --- drivers/input/misc/Kconfig | 35 + drivers/input/misc/Makefile | 10 + drivers/input/misc/bma2x2.c | 8958 +++++++++++++++++++++++++++++++++ drivers/input/misc/bstclass.c | 270 + drivers/input/misc/bstclass.h | 78 + 5 files changed, 9351 insertions(+) create mode 100644 drivers/input/misc/bma2x2.c create mode 100644 drivers/input/misc/bstclass.c create mode 100644 drivers/input/misc/bstclass.h diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 8f3b77eea781..4069df138c05 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -789,4 +789,39 @@ config SENSORS_LTR553 To compile this driver as a module, choose M here: the module will be called ltr553. +config SENSORS_BMA2X2 + tristate "BMA2x2 acceleration sensor support" + depends on I2C + help + If you say yes here, you get support for Bosch Sensortec's + acceleration sensors BMA255/BMA254/BMA355/BMA250E/BMA222E/BMA280. + +config SENSORS_BMA2X2_ENABLE_INT1 + tristate "BMA2X2 acceleration sensor interrupt INT1 support" + depends on SENSORS_BMA2X2 + help + If you say yes here, you get INT1 support for Bosch Sensortec + acceleration sensors BMA255/BMA254/BMA355/BMA250E/BMA222E/BMA280. + Select it will disable interrupt INT2 support + +config SENSORS_BMA2X2_ENABLE_INT2 + tristate "BMA2X2 acceleration sensor interrupt INT2 support" + depends on SENSORS_BMA2X2 && !SENSORS_BMA2X2_ENABLE_INT1 + help + If you say yes here, you get INT2 support for Bosch Sensortec + acceleration sensors BMA255/BMA254/BMA355/BMA250E/BMA222E/BMA280. + Can only open if you do NOT open interrupt INT1 support + +config SIG_MOTION + tristate "support significant motion sensor function" + depends on SENSORS_BMA2X2 && ( SENSORS_BMA2X2_ENABLE_INT1 || SENSORS_BMA2X2_ENABLE_INT2) + help + If you say yes here, if you want to support Bosch significant motion sensor function + +config DOUBLE_TAP + tristate "support double tap sensor function" + depends on SENSORS_BMA2X2 && ( SENSORS_BMA2X2_ENABLE_INT1 || SENSORS_BMA2X2_ENABLE_INT2) + help + If you say yes here, you get support Bosch double tap sensor function + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 5903b5c1aeb6..1456a017577f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -73,5 +73,15 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_SENSORS_LTR553) += ltr553.o +obj-$(CONFIG_SENSORS_BMA2X2) += bstclass.o + +obj-$(CONFIG_SENSORS_BMA2X2) += bma2x2.o +ifeq ($(CONFIG_SENSORS_BMA2X2_ENABLE_INT1),y) + EXTRA_CFLAGS += -DBMA2X2_ENABLE_INT1 +endif + +ifeq ($(CONFIG_BOSCH_BMA2X2_ENABLE_INT2),y) + EXTRA_CFLAGS += -DBMA2X2_ENABLE_INT2 +endif obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/ diff --git a/drivers/input/misc/bma2x2.c b/drivers/input/misc/bma2x2.c new file mode 100644 index 000000000000..c31bbf52f935 --- /dev/null +++ b/drivers/input/misc/bma2x2.c @@ -0,0 +1,8958 @@ +/*! + * @section LICENSE + * (C) Copyright 2013 Bosch Sensortec GmbH All Rights Reserved + * + * This software program is licensed subject to the GNU General + * Public License (GPL).Version 2,June 1991, + * available at http://www.fsf.org/copyleft/gpl.html + * + * @filename bma2x2.c + * @date 2014/02/13 15:50 + * @id "564eaab" + * @version 2.0 + * + * @brief + * This file contains all function implementations for the BMA2X2 in linux +*/ + +#define BMA2X2_ENABLE_INT2 + +#if !defined(BMA2X2_ENABLE_INT1) && !defined(BMA2X2_ENABLE_INT2) +#if defined(CONFIG_BMA_ENABLE_NEWDATA_INT) || defined(CONFIG_SIG_MOTION) +#error Please enable INT1 or INT2 to support new data int and sig-motion int! +#endif +#endif + +#ifdef CONFIG_SIG_MOTION +#undef CONFIG_HAS_EARLYSUSPEND +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#else +#include +#include +#include +#endif + +#include "bstclass.h" + +#define ACC_NAME "ACC" + +#ifdef ENABLE_ISR_DEBUG_MSG +#define ISR_INFO(dev, fmt, arg...) dev_info(dev, fmt, ##arg) +#else +#define ISR_INFO(dev, fmt, arg...) +#endif + +#define BMA2X2_SENSOR_IDENTIFICATION_ENABLE + +#define SENSOR_NAME "bma2x2-accel" +#define ABSMIN -512 +#define ABSMAX 512 +#define SLOPE_THRESHOLD_VALUE 32 +#define SLOPE_DURATION_VALUE 1 +#define INTERRUPT_LATCH_MODE 13 +#define INTERRUPT_ENABLE 1 +#define INTERRUPT_DISABLE 0 +#define MAP_SLOPE_INTERRUPT 2 +#define SLOPE_X_INDEX 5 +#define SLOPE_Y_INDEX 6 +#define SLOPE_Z_INDEX 7 +#define BMA2X2_RANGE_SET 3 /* +/- 2G */ +#define BMA2X2_RANGE_SHIFT 4 /* shift 4 bits for 2G */ +#define BMA2X2_BW_SET 12 /* 125HZ */ + +#define I2C_RETRY_DELAY() usleep_range(1000, 2000) +/* wait 2ms for calibration ready */ +#define WAIT_CAL_READY() usleep_range(2000, 2500) +/* >3ms wait device ready */ +#define WAIT_DEVICE_READY() usleep_range(3000, 5000) +/* >5ms for device reset */ +#define RESET_DELAY() usleep_range(5000, 10000) +/* wait 10ms for self test done */ +#define SELF_TEST_DELAY() usleep_range(10000, 15000) + +#ifdef USE_BMA_INTERRUPT +#define LOW_G_INTERRUPT REL_Z +#define HIGH_G_INTERRUPT REL_HWHEEL +#define SLOP_INTERRUPT REL_DIAL +#define DOUBLE_TAP_INTERRUPT REL_WHEEL +#define SINGLE_TAP_INTERRUPT REL_MISC +#define ORIENT_INTERRUPT ABS_PRESSURE +#define FLAT_INTERRUPT ABS_DISTANCE +#define SLOW_NO_MOTION_INTERRUPT REL_Y +#else +/* AndroidM didn't use the dev-interrupt,bypass above defines */ +#define LOW_G_INTERRUPT REL_Z +#define HIGH_G_INTERRUPT REL_Z +#define SLOP_INTERRUPT REL_Z +#define DOUBLE_TAP_INTERRUPT REL_Z +#define SINGLE_TAP_INTERRUPT REL_Z +#define ORIENT_INTERRUPT REL_Z +#define FLAT_INTERRUPT REL_Z +#define SLOW_NO_MOTION_INTERRUPT REL_Z +#endif + +#define HIGH_G_INTERRUPT_X_HAPPENED 1 +#define HIGH_G_INTERRUPT_Y_HAPPENED 2 +#define HIGH_G_INTERRUPT_Z_HAPPENED 3 +#define HIGH_G_INTERRUPT_X_NEGATIVE_HAPPENED 4 +#define HIGH_G_INTERRUPT_Y_NEGATIVE_HAPPENED 5 +#define HIGH_G_INTERRUPT_Z_NEGATIVE_HAPPENED 6 +#define SLOPE_INTERRUPT_X_HAPPENED 7 +#define SLOPE_INTERRUPT_Y_HAPPENED 8 +#define SLOPE_INTERRUPT_Z_HAPPENED 9 +#define SLOPE_INTERRUPT_X_NEGATIVE_HAPPENED 10 +#define SLOPE_INTERRUPT_Y_NEGATIVE_HAPPENED 11 +#define SLOPE_INTERRUPT_Z_NEGATIVE_HAPPENED 12 +#define DOUBLE_TAP_INTERRUPT_HAPPENED 13 +#define SINGLE_TAP_INTERRUPT_HAPPENED 14 +#define UPWARD_PORTRAIT_UP_INTERRUPT_HAPPENED 15 +#define UPWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED 16 +#define UPWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED 17 +#define UPWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED 18 +#define DOWNWARD_PORTRAIT_UP_INTERRUPT_HAPPENED 19 +#define DOWNWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED 20 +#define DOWNWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED 21 +#define DOWNWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED 22 +#define FLAT_INTERRUPT_TRUE_HAPPENED 23 +#define FLAT_INTERRUPT_FALSE_HAPPENED 24 +#define LOW_G_INTERRUPT_HAPPENED 25 +#define SLOW_NO_MOTION_INTERRUPT_HAPPENED 26 + +#define PAD_LOWG 0 +#define PAD_HIGHG 1 +#define PAD_SLOP 2 +#define PAD_DOUBLE_TAP 3 +#define PAD_SINGLE_TAP 4 +#define PAD_ORIENT 5 +#define PAD_FLAT 6 +#define PAD_SLOW_NO_MOTION 7 + +#define BMA2X2_EEP_OFFSET 0x16 +#define BMA2X2_IMAGE_BASE 0x38 +#define BMA2X2_IMAGE_LEN 22 + +#define BMA2X2_CHIP_ID_REG 0x00 +#define BMA2X2_VERSION_REG 0x01 +#define BMA2X2_X_AXIS_LSB_REG 0x02 +#define BMA2X2_X_AXIS_MSB_REG 0x03 +#define BMA2X2_Y_AXIS_LSB_REG 0x04 +#define BMA2X2_Y_AXIS_MSB_REG 0x05 +#define BMA2X2_Z_AXIS_LSB_REG 0x06 +#define BMA2X2_Z_AXIS_MSB_REG 0x07 +#define BMA2X2_TEMPERATURE_REG 0x08 +#define BMA2X2_STATUS1_REG 0x09 +#define BMA2X2_STATUS2_REG 0x0A +#define BMA2X2_STATUS_TAP_SLOPE_REG 0x0B +#define BMA2X2_STATUS_ORIENT_HIGH_REG 0x0C +#define BMA2X2_STATUS_FIFO_REG 0x0E +#define BMA2X2_RANGE_SEL_REG 0x0F +#define BMA2X2_BW_SEL_REG 0x10 +#define BMA2X2_MODE_CTRL_REG 0x11 +#define BMA2X2_LOW_NOISE_CTRL_REG 0x12 +#define BMA2X2_DATA_CTRL_REG 0x13 +#define BMA2X2_RESET_REG 0x14 +#define BMA2X2_INT_ENABLE1_REG 0x16 +#define BMA2X2_INT_ENABLE2_REG 0x17 +#define BMA2X2_INT_SLO_NO_MOT_REG 0x18 +#define BMA2X2_INT1_PAD_SEL_REG 0x19 +#define BMA2X2_INT_DATA_SEL_REG 0x1A +#define BMA2X2_INT2_PAD_SEL_REG 0x1B +#define BMA2X2_INT_SRC_REG 0x1E +#define BMA2X2_INT_SET_REG 0x20 +#define BMA2X2_INT_CTRL_REG 0x21 +#define BMA2X2_LOW_DURN_REG 0x22 +#define BMA2X2_LOW_THRES_REG 0x23 +#define BMA2X2_LOW_HIGH_HYST_REG 0x24 +#define BMA2X2_HIGH_DURN_REG 0x25 +#define BMA2X2_HIGH_THRES_REG 0x26 +#define BMA2X2_SLOPE_DURN_REG 0x27 +#define BMA2X2_SLOPE_THRES_REG 0x28 +#define BMA2X2_SLO_NO_MOT_THRES_REG 0x29 +#define BMA2X2_TAP_PARAM_REG 0x2A +#define BMA2X2_TAP_THRES_REG 0x2B +#define BMA2X2_ORIENT_PARAM_REG 0x2C +#define BMA2X2_THETA_BLOCK_REG 0x2D +#define BMA2X2_THETA_FLAT_REG 0x2E +#define BMA2X2_FLAT_HOLD_TIME_REG 0x2F +#define BMA2X2_FIFO_WML_TRIG 0x30 +#define BMA2X2_SELF_TEST_REG 0x32 +#define BMA2X2_EEPROM_CTRL_REG 0x33 +#define BMA2X2_SERIAL_CTRL_REG 0x34 +#define BMA2X2_EXTMODE_CTRL_REG 0x35 +#define BMA2X2_OFFSET_CTRL_REG 0x36 +#define BMA2X2_OFFSET_PARAMS_REG 0x37 +#define BMA2X2_OFFSET_X_AXIS_REG 0x38 +#define BMA2X2_OFFSET_Y_AXIS_REG 0x39 +#define BMA2X2_OFFSET_Z_AXIS_REG 0x3A +#define BMA2X2_GP0_REG 0x3B +#define BMA2X2_GP1_REG 0x3C +#define BMA2X2_FIFO_MODE_REG 0x3E +#define BMA2X2_FIFO_DATA_OUTPUT_REG 0x3F + +#define BMA2X2_CHIP_ID__POS 0 +#define BMA2X2_CHIP_ID__MSK 0xFF +#define BMA2X2_CHIP_ID__LEN 8 +#define BMA2X2_CHIP_ID__REG BMA2X2_CHIP_ID_REG + +#define BMA2X2_VERSION__POS 0 +#define BMA2X2_VERSION__LEN 8 +#define BMA2X2_VERSION__MSK 0xFF +#define BMA2X2_VERSION__REG BMA2X2_VERSION_REG + +#define BMA2x2_SLO_NO_MOT_DUR__POS 2 +#define BMA2x2_SLO_NO_MOT_DUR__LEN 6 +#define BMA2x2_SLO_NO_MOT_DUR__MSK 0xFC +#define BMA2x2_SLO_NO_MOT_DUR__REG BMA2X2_SLOPE_DURN_REG + +#define BMA2X2_NEW_DATA_X__POS 0 +#define BMA2X2_NEW_DATA_X__LEN 1 +#define BMA2X2_NEW_DATA_X__MSK 0x01 +#define BMA2X2_NEW_DATA_X__REG BMA2X2_X_AXIS_LSB_REG + +#define BMA2X2_ACC_X14_LSB__POS 2 +#define BMA2X2_ACC_X14_LSB__LEN 6 +#define BMA2X2_ACC_X14_LSB__MSK 0xFC +#define BMA2X2_ACC_X14_LSB__REG BMA2X2_X_AXIS_LSB_REG + +#define BMA2X2_ACC_X12_LSB__POS 4 +#define BMA2X2_ACC_X12_LSB__LEN 4 +#define BMA2X2_ACC_X12_LSB__MSK 0xF0 +#define BMA2X2_ACC_X12_LSB__REG BMA2X2_X_AXIS_LSB_REG + +#define BMA2X2_ACC_X10_LSB__POS 6 +#define BMA2X2_ACC_X10_LSB__LEN 2 +#define BMA2X2_ACC_X10_LSB__MSK 0xC0 +#define BMA2X2_ACC_X10_LSB__REG BMA2X2_X_AXIS_LSB_REG + +#define BMA2X2_ACC_X8_LSB__POS 0 +#define BMA2X2_ACC_X8_LSB__LEN 0 +#define BMA2X2_ACC_X8_LSB__MSK 0x00 +#define BMA2X2_ACC_X8_LSB__REG BMA2X2_X_AXIS_LSB_REG + +#define BMA2X2_ACC_X_MSB__POS 0 +#define BMA2X2_ACC_X_MSB__LEN 8 +#define BMA2X2_ACC_X_MSB__MSK 0xFF +#define BMA2X2_ACC_X_MSB__REG BMA2X2_X_AXIS_MSB_REG + +#define BMA2X2_NEW_DATA_Y__POS 0 +#define BMA2X2_NEW_DATA_Y__LEN 1 +#define BMA2X2_NEW_DATA_Y__MSK 0x01 +#define BMA2X2_NEW_DATA_Y__REG BMA2X2_Y_AXIS_LSB_REG + +#define BMA2X2_ACC_Y14_LSB__POS 2 +#define BMA2X2_ACC_Y14_LSB__LEN 6 +#define BMA2X2_ACC_Y14_LSB__MSK 0xFC +#define BMA2X2_ACC_Y14_LSB__REG BMA2X2_Y_AXIS_LSB_REG + +#define BMA2X2_ACC_Y12_LSB__POS 4 +#define BMA2X2_ACC_Y12_LSB__LEN 4 +#define BMA2X2_ACC_Y12_LSB__MSK 0xF0 +#define BMA2X2_ACC_Y12_LSB__REG BMA2X2_Y_AXIS_LSB_REG + +#define BMA2X2_ACC_Y10_LSB__POS 6 +#define BMA2X2_ACC_Y10_LSB__LEN 2 +#define BMA2X2_ACC_Y10_LSB__MSK 0xC0 +#define BMA2X2_ACC_Y10_LSB__REG BMA2X2_Y_AXIS_LSB_REG + +#define BMA2X2_ACC_Y8_LSB__POS 0 +#define BMA2X2_ACC_Y8_LSB__LEN 0 +#define BMA2X2_ACC_Y8_LSB__MSK 0x00 +#define BMA2X2_ACC_Y8_LSB__REG BMA2X2_Y_AXIS_LSB_REG + +#define BMA2X2_ACC_Y_MSB__POS 0 +#define BMA2X2_ACC_Y_MSB__LEN 8 +#define BMA2X2_ACC_Y_MSB__MSK 0xFF +#define BMA2X2_ACC_Y_MSB__REG BMA2X2_Y_AXIS_MSB_REG + +#define BMA2X2_NEW_DATA_Z__POS 0 +#define BMA2X2_NEW_DATA_Z__LEN 1 +#define BMA2X2_NEW_DATA_Z__MSK 0x01 +#define BMA2X2_NEW_DATA_Z__REG BMA2X2_Z_AXIS_LSB_REG + +#define BMA2X2_ACC_Z14_LSB__POS 2 +#define BMA2X2_ACC_Z14_LSB__LEN 6 +#define BMA2X2_ACC_Z14_LSB__MSK 0xFC +#define BMA2X2_ACC_Z14_LSB__REG BMA2X2_Z_AXIS_LSB_REG + +#define BMA2X2_ACC_Z12_LSB__POS 4 +#define BMA2X2_ACC_Z12_LSB__LEN 4 +#define BMA2X2_ACC_Z12_LSB__MSK 0xF0 +#define BMA2X2_ACC_Z12_LSB__REG BMA2X2_Z_AXIS_LSB_REG + +#define BMA2X2_ACC_Z10_LSB__POS 6 +#define BMA2X2_ACC_Z10_LSB__LEN 2 +#define BMA2X2_ACC_Z10_LSB__MSK 0xC0 +#define BMA2X2_ACC_Z10_LSB__REG BMA2X2_Z_AXIS_LSB_REG + +#define BMA2X2_ACC_Z8_LSB__POS 0 +#define BMA2X2_ACC_Z8_LSB__LEN 0 +#define BMA2X2_ACC_Z8_LSB__MSK 0x00 +#define BMA2X2_ACC_Z8_LSB__REG BMA2X2_Z_AXIS_LSB_REG + +#define BMA2X2_ACC_Z_MSB__POS 0 +#define BMA2X2_ACC_Z_MSB__LEN 8 +#define BMA2X2_ACC_Z_MSB__MSK 0xFF +#define BMA2X2_ACC_Z_MSB__REG BMA2X2_Z_AXIS_MSB_REG + +#define BMA2X2_TEMPERATURE__POS 0 +#define BMA2X2_TEMPERATURE__LEN 8 +#define BMA2X2_TEMPERATURE__MSK 0xFF +#define BMA2X2_TEMPERATURE__REG BMA2X2_TEMP_RD_REG + +#define BMA2X2_LOWG_INT_S__POS 0 +#define BMA2X2_LOWG_INT_S__LEN 1 +#define BMA2X2_LOWG_INT_S__MSK 0x01 +#define BMA2X2_LOWG_INT_S__REG BMA2X2_STATUS1_REG + +#define BMA2X2_HIGHG_INT_S__POS 1 +#define BMA2X2_HIGHG_INT_S__LEN 1 +#define BMA2X2_HIGHG_INT_S__MSK 0x02 +#define BMA2X2_HIGHG_INT_S__REG BMA2X2_STATUS1_REG + +#define BMA2X2_SLOPE_INT_S__POS 2 +#define BMA2X2_SLOPE_INT_S__LEN 1 +#define BMA2X2_SLOPE_INT_S__MSK 0x04 +#define BMA2X2_SLOPE_INT_S__REG BMA2X2_STATUS1_REG + + +#define BMA2X2_SLO_NO_MOT_INT_S__POS 3 +#define BMA2X2_SLO_NO_MOT_INT_S__LEN 1 +#define BMA2X2_SLO_NO_MOT_INT_S__MSK 0x08 +#define BMA2X2_SLO_NO_MOT_INT_S__REG BMA2X2_STATUS1_REG + +#define BMA2X2_DOUBLE_TAP_INT_S__POS 4 +#define BMA2X2_DOUBLE_TAP_INT_S__LEN 1 +#define BMA2X2_DOUBLE_TAP_INT_S__MSK 0x10 +#define BMA2X2_DOUBLE_TAP_INT_S__REG BMA2X2_STATUS1_REG + +#define BMA2X2_SINGLE_TAP_INT_S__POS 5 +#define BMA2X2_SINGLE_TAP_INT_S__LEN 1 +#define BMA2X2_SINGLE_TAP_INT_S__MSK 0x20 +#define BMA2X2_SINGLE_TAP_INT_S__REG BMA2X2_STATUS1_REG + +#define BMA2X2_ORIENT_INT_S__POS 6 +#define BMA2X2_ORIENT_INT_S__LEN 1 +#define BMA2X2_ORIENT_INT_S__MSK 0x40 +#define BMA2X2_ORIENT_INT_S__REG BMA2X2_STATUS1_REG + +#define BMA2X2_FLAT_INT_S__POS 7 +#define BMA2X2_FLAT_INT_S__LEN 1 +#define BMA2X2_FLAT_INT_S__MSK 0x80 +#define BMA2X2_FLAT_INT_S__REG BMA2X2_STATUS1_REG + +#define BMA2X2_FIFO_FULL_INT_S__POS 5 +#define BMA2X2_FIFO_FULL_INT_S__LEN 1 +#define BMA2X2_FIFO_FULL_INT_S__MSK 0x20 +#define BMA2X2_FIFO_FULL_INT_S__REG BMA2X2_STATUS2_REG + +#define BMA2X2_FIFO_WM_INT_S__POS 6 +#define BMA2X2_FIFO_WM_INT_S__LEN 1 +#define BMA2X2_FIFO_WM_INT_S__MSK 0x40 +#define BMA2X2_FIFO_WM_INT_S__REG BMA2X2_STATUS2_REG + +#define BMA2X2_DATA_INT_S__POS 7 +#define BMA2X2_DATA_INT_S__LEN 1 +#define BMA2X2_DATA_INT_S__MSK 0x80 +#define BMA2X2_DATA_INT_S__REG BMA2X2_STATUS2_REG + +#define BMA2X2_SLOPE_FIRST_X__POS 0 +#define BMA2X2_SLOPE_FIRST_X__LEN 1 +#define BMA2X2_SLOPE_FIRST_X__MSK 0x01 +#define BMA2X2_SLOPE_FIRST_X__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_SLOPE_FIRST_Y__POS 1 +#define BMA2X2_SLOPE_FIRST_Y__LEN 1 +#define BMA2X2_SLOPE_FIRST_Y__MSK 0x02 +#define BMA2X2_SLOPE_FIRST_Y__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_SLOPE_FIRST_Z__POS 2 +#define BMA2X2_SLOPE_FIRST_Z__LEN 1 +#define BMA2X2_SLOPE_FIRST_Z__MSK 0x04 +#define BMA2X2_SLOPE_FIRST_Z__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_SLOPE_SIGN_S__POS 3 +#define BMA2X2_SLOPE_SIGN_S__LEN 1 +#define BMA2X2_SLOPE_SIGN_S__MSK 0x08 +#define BMA2X2_SLOPE_SIGN_S__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_TAP_FIRST_X__POS 4 +#define BMA2X2_TAP_FIRST_X__LEN 1 +#define BMA2X2_TAP_FIRST_X__MSK 0x10 +#define BMA2X2_TAP_FIRST_X__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_TAP_FIRST_Y__POS 5 +#define BMA2X2_TAP_FIRST_Y__LEN 1 +#define BMA2X2_TAP_FIRST_Y__MSK 0x20 +#define BMA2X2_TAP_FIRST_Y__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_TAP_FIRST_Z__POS 6 +#define BMA2X2_TAP_FIRST_Z__LEN 1 +#define BMA2X2_TAP_FIRST_Z__MSK 0x40 +#define BMA2X2_TAP_FIRST_Z__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_TAP_SIGN_S__POS 7 +#define BMA2X2_TAP_SIGN_S__LEN 1 +#define BMA2X2_TAP_SIGN_S__MSK 0x80 +#define BMA2X2_TAP_SIGN_S__REG BMA2X2_STATUS_TAP_SLOPE_REG + +#define BMA2X2_HIGHG_FIRST_X__POS 0 +#define BMA2X2_HIGHG_FIRST_X__LEN 1 +#define BMA2X2_HIGHG_FIRST_X__MSK 0x01 +#define BMA2X2_HIGHG_FIRST_X__REG BMA2X2_STATUS_ORIENT_HIGH_REG + +#define BMA2X2_HIGHG_FIRST_Y__POS 1 +#define BMA2X2_HIGHG_FIRST_Y__LEN 1 +#define BMA2X2_HIGHG_FIRST_Y__MSK 0x02 +#define BMA2X2_HIGHG_FIRST_Y__REG BMA2X2_STATUS_ORIENT_HIGH_REG + +#define BMA2X2_HIGHG_FIRST_Z__POS 2 +#define BMA2X2_HIGHG_FIRST_Z__LEN 1 +#define BMA2X2_HIGHG_FIRST_Z__MSK 0x04 +#define BMA2X2_HIGHG_FIRST_Z__REG BMA2X2_STATUS_ORIENT_HIGH_REG + +#define BMA2X2_HIGHG_SIGN_S__POS 3 +#define BMA2X2_HIGHG_SIGN_S__LEN 1 +#define BMA2X2_HIGHG_SIGN_S__MSK 0x08 +#define BMA2X2_HIGHG_SIGN_S__REG BMA2X2_STATUS_ORIENT_HIGH_REG + +#define BMA2X2_ORIENT_S__POS 4 +#define BMA2X2_ORIENT_S__LEN 3 +#define BMA2X2_ORIENT_S__MSK 0x70 +#define BMA2X2_ORIENT_S__REG BMA2X2_STATUS_ORIENT_HIGH_REG + +#define BMA2X2_FLAT_S__POS 7 +#define BMA2X2_FLAT_S__LEN 1 +#define BMA2X2_FLAT_S__MSK 0x80 +#define BMA2X2_FLAT_S__REG BMA2X2_STATUS_ORIENT_HIGH_REG + +#define BMA2X2_FIFO_FRAME_COUNTER_S__POS 0 +#define BMA2X2_FIFO_FRAME_COUNTER_S__LEN 7 +#define BMA2X2_FIFO_FRAME_COUNTER_S__MSK 0x7F +#define BMA2X2_FIFO_FRAME_COUNTER_S__REG BMA2X2_STATUS_FIFO_REG + +#define BMA2X2_FIFO_OVERRUN_S__POS 7 +#define BMA2X2_FIFO_OVERRUN_S__LEN 1 +#define BMA2X2_FIFO_OVERRUN_S__MSK 0x80 +#define BMA2X2_FIFO_OVERRUN_S__REG BMA2X2_STATUS_FIFO_REG + +#define BMA2X2_RANGE_SEL__POS 0 +#define BMA2X2_RANGE_SEL__LEN 4 +#define BMA2X2_RANGE_SEL__MSK 0x0F +#define BMA2X2_RANGE_SEL__REG BMA2X2_RANGE_SEL_REG + +#define BMA2X2_BANDWIDTH__POS 0 +#define BMA2X2_BANDWIDTH__LEN 5 +#define BMA2X2_BANDWIDTH__MSK 0x1F +#define BMA2X2_BANDWIDTH__REG BMA2X2_BW_SEL_REG + +#define BMA2X2_SLEEP_DUR__POS 1 +#define BMA2X2_SLEEP_DUR__LEN 4 +#define BMA2X2_SLEEP_DUR__MSK 0x1E +#define BMA2X2_SLEEP_DUR__REG BMA2X2_MODE_CTRL_REG + +#define BMA2X2_MODE_CTRL__POS 5 +#define BMA2X2_MODE_CTRL__LEN 3 +#define BMA2X2_MODE_CTRL__MSK 0xE0 +#define BMA2X2_MODE_CTRL__REG BMA2X2_MODE_CTRL_REG + +#define BMA2X2_DEEP_SUSPEND__POS 5 +#define BMA2X2_DEEP_SUSPEND__LEN 1 +#define BMA2X2_DEEP_SUSPEND__MSK 0x20 +#define BMA2X2_DEEP_SUSPEND__REG BMA2X2_MODE_CTRL_REG + +#define BMA2X2_EN_LOW_POWER__POS 6 +#define BMA2X2_EN_LOW_POWER__LEN 1 +#define BMA2X2_EN_LOW_POWER__MSK 0x40 +#define BMA2X2_EN_LOW_POWER__REG BMA2X2_MODE_CTRL_REG + +#define BMA2X2_EN_SUSPEND__POS 7 +#define BMA2X2_EN_SUSPEND__LEN 1 +#define BMA2X2_EN_SUSPEND__MSK 0x80 +#define BMA2X2_EN_SUSPEND__REG BMA2X2_MODE_CTRL_REG + +#define BMA2X2_SLEEP_TIMER__POS 5 +#define BMA2X2_SLEEP_TIMER__LEN 1 +#define BMA2X2_SLEEP_TIMER__MSK 0x20 +#define BMA2X2_SLEEP_TIMER__REG BMA2X2_LOW_NOISE_CTRL_REG + +#define BMA2X2_LOW_POWER_MODE__POS 6 +#define BMA2X2_LOW_POWER_MODE__LEN 1 +#define BMA2X2_LOW_POWER_MODE__MSK 0x40 +#define BMA2X2_LOW_POWER_MODE__REG BMA2X2_LOW_NOISE_CTRL_REG + +#define BMA2X2_EN_LOW_NOISE__POS 7 +#define BMA2X2_EN_LOW_NOISE__LEN 1 +#define BMA2X2_EN_LOW_NOISE__MSK 0x80 +#define BMA2X2_EN_LOW_NOISE__REG BMA2X2_LOW_NOISE_CTRL_REG + +#define BMA2X2_DIS_SHADOW_PROC__POS 6 +#define BMA2X2_DIS_SHADOW_PROC__LEN 1 +#define BMA2X2_DIS_SHADOW_PROC__MSK 0x40 +#define BMA2X2_DIS_SHADOW_PROC__REG BMA2X2_DATA_CTRL_REG + +#define BMA2X2_EN_DATA_HIGH_BW__POS 7 +#define BMA2X2_EN_DATA_HIGH_BW__LEN 1 +#define BMA2X2_EN_DATA_HIGH_BW__MSK 0x80 +#define BMA2X2_EN_DATA_HIGH_BW__REG BMA2X2_DATA_CTRL_REG + +#define BMA2X2_EN_SOFT_RESET__POS 0 +#define BMA2X2_EN_SOFT_RESET__LEN 8 +#define BMA2X2_EN_SOFT_RESET__MSK 0xFF +#define BMA2X2_EN_SOFT_RESET__REG BMA2X2_RESET_REG + +#define BMA2X2_EN_SOFT_RESET_VALUE 0xB6 + +#define BMA2X2_EN_SLOPE_X_INT__POS 0 +#define BMA2X2_EN_SLOPE_X_INT__LEN 1 +#define BMA2X2_EN_SLOPE_X_INT__MSK 0x01 +#define BMA2X2_EN_SLOPE_X_INT__REG BMA2X2_INT_ENABLE1_REG + +#define BMA2X2_EN_SLOPE_Y_INT__POS 1 +#define BMA2X2_EN_SLOPE_Y_INT__LEN 1 +#define BMA2X2_EN_SLOPE_Y_INT__MSK 0x02 +#define BMA2X2_EN_SLOPE_Y_INT__REG BMA2X2_INT_ENABLE1_REG + +#define BMA2X2_EN_SLOPE_Z_INT__POS 2 +#define BMA2X2_EN_SLOPE_Z_INT__LEN 1 +#define BMA2X2_EN_SLOPE_Z_INT__MSK 0x04 +#define BMA2X2_EN_SLOPE_Z_INT__REG BMA2X2_INT_ENABLE1_REG + +#define BMA2X2_EN_DOUBLE_TAP_INT__POS 4 +#define BMA2X2_EN_DOUBLE_TAP_INT__LEN 1 +#define BMA2X2_EN_DOUBLE_TAP_INT__MSK 0x10 +#define BMA2X2_EN_DOUBLE_TAP_INT__REG BMA2X2_INT_ENABLE1_REG + +#define BMA2X2_EN_SINGLE_TAP_INT__POS 5 +#define BMA2X2_EN_SINGLE_TAP_INT__LEN 1 +#define BMA2X2_EN_SINGLE_TAP_INT__MSK 0x20 +#define BMA2X2_EN_SINGLE_TAP_INT__REG BMA2X2_INT_ENABLE1_REG + +#define BMA2X2_EN_ORIENT_INT__POS 6 +#define BMA2X2_EN_ORIENT_INT__LEN 1 +#define BMA2X2_EN_ORIENT_INT__MSK 0x40 +#define BMA2X2_EN_ORIENT_INT__REG BMA2X2_INT_ENABLE1_REG + +#define BMA2X2_EN_FLAT_INT__POS 7 +#define BMA2X2_EN_FLAT_INT__LEN 1 +#define BMA2X2_EN_FLAT_INT__MSK 0x80 +#define BMA2X2_EN_FLAT_INT__REG BMA2X2_INT_ENABLE1_REG + +#define BMA2X2_EN_HIGHG_X_INT__POS 0 +#define BMA2X2_EN_HIGHG_X_INT__LEN 1 +#define BMA2X2_EN_HIGHG_X_INT__MSK 0x01 +#define BMA2X2_EN_HIGHG_X_INT__REG BMA2X2_INT_ENABLE2_REG + +#define BMA2X2_EN_HIGHG_Y_INT__POS 1 +#define BMA2X2_EN_HIGHG_Y_INT__LEN 1 +#define BMA2X2_EN_HIGHG_Y_INT__MSK 0x02 +#define BMA2X2_EN_HIGHG_Y_INT__REG BMA2X2_INT_ENABLE2_REG + +#define BMA2X2_EN_HIGHG_Z_INT__POS 2 +#define BMA2X2_EN_HIGHG_Z_INT__LEN 1 +#define BMA2X2_EN_HIGHG_Z_INT__MSK 0x04 +#define BMA2X2_EN_HIGHG_Z_INT__REG BMA2X2_INT_ENABLE2_REG + +#define BMA2X2_EN_LOWG_INT__POS 3 +#define BMA2X2_EN_LOWG_INT__LEN 1 +#define BMA2X2_EN_LOWG_INT__MSK 0x08 +#define BMA2X2_EN_LOWG_INT__REG BMA2X2_INT_ENABLE2_REG + +#define BMA2X2_EN_NEW_DATA_INT__POS 4 +#define BMA2X2_EN_NEW_DATA_INT__LEN 1 +#define BMA2X2_EN_NEW_DATA_INT__MSK 0x10 +#define BMA2X2_EN_NEW_DATA_INT__REG BMA2X2_INT_ENABLE2_REG + +#define BMA2X2_INT_FFULL_EN_INT__POS 5 +#define BMA2X2_INT_FFULL_EN_INT__LEN 1 +#define BMA2X2_INT_FFULL_EN_INT__MSK 0x20 +#define BMA2X2_INT_FFULL_EN_INT__REG BMA2X2_INT_ENABLE2_REG + +#define BMA2X2_INT_FWM_EN_INT__POS 6 +#define BMA2X2_INT_FWM_EN_INT__LEN 1 +#define BMA2X2_INT_FWM_EN_INT__MSK 0x40 +#define BMA2X2_INT_FWM_EN_INT__REG BMA2X2_INT_ENABLE2_REG + +#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__POS 0 +#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__LEN 1 +#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__MSK 0x01 +#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__REG BMA2X2_INT_SLO_NO_MOT_REG + +#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__POS 1 +#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__LEN 1 +#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__MSK 0x02 +#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__REG BMA2X2_INT_SLO_NO_MOT_REG + +#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__POS 2 +#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__LEN 1 +#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__MSK 0x04 +#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__REG BMA2X2_INT_SLO_NO_MOT_REG + +#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__POS 3 +#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__LEN 1 +#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__MSK 0x08 +#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__REG BMA2X2_INT_SLO_NO_MOT_REG + +#define BMA2X2_EN_INT1_PAD_LOWG__POS 0 +#define BMA2X2_EN_INT1_PAD_LOWG__LEN 1 +#define BMA2X2_EN_INT1_PAD_LOWG__MSK 0x01 +#define BMA2X2_EN_INT1_PAD_LOWG__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_HIGHG__POS 1 +#define BMA2X2_EN_INT1_PAD_HIGHG__LEN 1 +#define BMA2X2_EN_INT1_PAD_HIGHG__MSK 0x02 +#define BMA2X2_EN_INT1_PAD_HIGHG__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_SLOPE__POS 2 +#define BMA2X2_EN_INT1_PAD_SLOPE__LEN 1 +#define BMA2X2_EN_INT1_PAD_SLOPE__MSK 0x04 +#define BMA2X2_EN_INT1_PAD_SLOPE__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__POS 3 +#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__LEN 1 +#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__MSK 0x08 +#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_DB_TAP__POS 4 +#define BMA2X2_EN_INT1_PAD_DB_TAP__LEN 1 +#define BMA2X2_EN_INT1_PAD_DB_TAP__MSK 0x10 +#define BMA2X2_EN_INT1_PAD_DB_TAP__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_SNG_TAP__POS 5 +#define BMA2X2_EN_INT1_PAD_SNG_TAP__LEN 1 +#define BMA2X2_EN_INT1_PAD_SNG_TAP__MSK 0x20 +#define BMA2X2_EN_INT1_PAD_SNG_TAP__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_ORIENT__POS 6 +#define BMA2X2_EN_INT1_PAD_ORIENT__LEN 1 +#define BMA2X2_EN_INT1_PAD_ORIENT__MSK 0x40 +#define BMA2X2_EN_INT1_PAD_ORIENT__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_FLAT__POS 7 +#define BMA2X2_EN_INT1_PAD_FLAT__LEN 1 +#define BMA2X2_EN_INT1_PAD_FLAT__MSK 0x80 +#define BMA2X2_EN_INT1_PAD_FLAT__REG BMA2X2_INT1_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_LOWG__POS 0 +#define BMA2X2_EN_INT2_PAD_LOWG__LEN 1 +#define BMA2X2_EN_INT2_PAD_LOWG__MSK 0x01 +#define BMA2X2_EN_INT2_PAD_LOWG__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_HIGHG__POS 1 +#define BMA2X2_EN_INT2_PAD_HIGHG__LEN 1 +#define BMA2X2_EN_INT2_PAD_HIGHG__MSK 0x02 +#define BMA2X2_EN_INT2_PAD_HIGHG__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_SLOPE__POS 2 +#define BMA2X2_EN_INT2_PAD_SLOPE__LEN 1 +#define BMA2X2_EN_INT2_PAD_SLOPE__MSK 0x04 +#define BMA2X2_EN_INT2_PAD_SLOPE__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__POS 3 +#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__LEN 1 +#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__MSK 0x08 +#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_DB_TAP__POS 4 +#define BMA2X2_EN_INT2_PAD_DB_TAP__LEN 1 +#define BMA2X2_EN_INT2_PAD_DB_TAP__MSK 0x10 +#define BMA2X2_EN_INT2_PAD_DB_TAP__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_SNG_TAP__POS 5 +#define BMA2X2_EN_INT2_PAD_SNG_TAP__LEN 1 +#define BMA2X2_EN_INT2_PAD_SNG_TAP__MSK 0x20 +#define BMA2X2_EN_INT2_PAD_SNG_TAP__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_ORIENT__POS 6 +#define BMA2X2_EN_INT2_PAD_ORIENT__LEN 1 +#define BMA2X2_EN_INT2_PAD_ORIENT__MSK 0x40 +#define BMA2X2_EN_INT2_PAD_ORIENT__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT2_PAD_FLAT__POS 7 +#define BMA2X2_EN_INT2_PAD_FLAT__LEN 1 +#define BMA2X2_EN_INT2_PAD_FLAT__MSK 0x80 +#define BMA2X2_EN_INT2_PAD_FLAT__REG BMA2X2_INT2_PAD_SEL_REG + +#define BMA2X2_EN_INT1_PAD_NEWDATA__POS 0 +#define BMA2X2_EN_INT1_PAD_NEWDATA__LEN 1 +#define BMA2X2_EN_INT1_PAD_NEWDATA__MSK 0x01 +#define BMA2X2_EN_INT1_PAD_NEWDATA__REG BMA2X2_INT_DATA_SEL_REG + +#define BMA2X2_EN_INT1_PAD_FWM__POS 1 +#define BMA2X2_EN_INT1_PAD_FWM__LEN 1 +#define BMA2X2_EN_INT1_PAD_FWM__MSK 0x02 +#define BMA2X2_EN_INT1_PAD_FWM__REG BMA2X2_INT_DATA_SEL_REG + +#define BMA2X2_EN_INT1_PAD_FFULL__POS 2 +#define BMA2X2_EN_INT1_PAD_FFULL__LEN 1 +#define BMA2X2_EN_INT1_PAD_FFULL__MSK 0x04 +#define BMA2X2_EN_INT1_PAD_FFULL__REG BMA2X2_INT_DATA_SEL_REG + +#define BMA2X2_EN_INT2_PAD_FFULL__POS 5 +#define BMA2X2_EN_INT2_PAD_FFULL__LEN 1 +#define BMA2X2_EN_INT2_PAD_FFULL__MSK 0x20 +#define BMA2X2_EN_INT2_PAD_FFULL__REG BMA2X2_INT_DATA_SEL_REG + +#define BMA2X2_EN_INT2_PAD_FWM__POS 6 +#define BMA2X2_EN_INT2_PAD_FWM__LEN 1 +#define BMA2X2_EN_INT2_PAD_FWM__MSK 0x40 +#define BMA2X2_EN_INT2_PAD_FWM__REG BMA2X2_INT_DATA_SEL_REG + +#define BMA2X2_EN_INT2_PAD_NEWDATA__POS 7 +#define BMA2X2_EN_INT2_PAD_NEWDATA__LEN 1 +#define BMA2X2_EN_INT2_PAD_NEWDATA__MSK 0x80 +#define BMA2X2_EN_INT2_PAD_NEWDATA__REG BMA2X2_INT_DATA_SEL_REG + +#define BMA2X2_UNFILT_INT_SRC_LOWG__POS 0 +#define BMA2X2_UNFILT_INT_SRC_LOWG__LEN 1 +#define BMA2X2_UNFILT_INT_SRC_LOWG__MSK 0x01 +#define BMA2X2_UNFILT_INT_SRC_LOWG__REG BMA2X2_INT_SRC_REG + +#define BMA2X2_UNFILT_INT_SRC_HIGHG__POS 1 +#define BMA2X2_UNFILT_INT_SRC_HIGHG__LEN 1 +#define BMA2X2_UNFILT_INT_SRC_HIGHG__MSK 0x02 +#define BMA2X2_UNFILT_INT_SRC_HIGHG__REG BMA2X2_INT_SRC_REG + +#define BMA2X2_UNFILT_INT_SRC_SLOPE__POS 2 +#define BMA2X2_UNFILT_INT_SRC_SLOPE__LEN 1 +#define BMA2X2_UNFILT_INT_SRC_SLOPE__MSK 0x04 +#define BMA2X2_UNFILT_INT_SRC_SLOPE__REG BMA2X2_INT_SRC_REG + +#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__POS 3 +#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__LEN 1 +#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__MSK 0x08 +#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__REG BMA2X2_INT_SRC_REG + +#define BMA2X2_UNFILT_INT_SRC_TAP__POS 4 +#define BMA2X2_UNFILT_INT_SRC_TAP__LEN 1 +#define BMA2X2_UNFILT_INT_SRC_TAP__MSK 0x10 +#define BMA2X2_UNFILT_INT_SRC_TAP__REG BMA2X2_INT_SRC_REG + +#define BMA2X2_UNFILT_INT_SRC_DATA__POS 5 +#define BMA2X2_UNFILT_INT_SRC_DATA__LEN 1 +#define BMA2X2_UNFILT_INT_SRC_DATA__MSK 0x20 +#define BMA2X2_UNFILT_INT_SRC_DATA__REG BMA2X2_INT_SRC_REG + +#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__POS 0 +#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__LEN 1 +#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__MSK 0x01 +#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__REG BMA2X2_INT_SET_REG + +#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__POS 2 +#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__LEN 1 +#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__MSK 0x04 +#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__REG BMA2X2_INT_SET_REG + +#define BMA2X2_INT1_PAD_OUTPUT_TYPE__POS 1 +#define BMA2X2_INT1_PAD_OUTPUT_TYPE__LEN 1 +#define BMA2X2_INT1_PAD_OUTPUT_TYPE__MSK 0x02 +#define BMA2X2_INT1_PAD_OUTPUT_TYPE__REG BMA2X2_INT_SET_REG + +#define BMA2X2_INT2_PAD_OUTPUT_TYPE__POS 3 +#define BMA2X2_INT2_PAD_OUTPUT_TYPE__LEN 1 +#define BMA2X2_INT2_PAD_OUTPUT_TYPE__MSK 0x08 +#define BMA2X2_INT2_PAD_OUTPUT_TYPE__REG BMA2X2_INT_SET_REG + +#define BMA2X2_INT_MODE_SEL__POS 0 +#define BMA2X2_INT_MODE_SEL__LEN 4 +#define BMA2X2_INT_MODE_SEL__MSK 0x0F +#define BMA2X2_INT_MODE_SEL__REG BMA2X2_INT_CTRL_REG + +#define BMA2X2_RESET_INT__POS 7 +#define BMA2X2_RESET_INT__LEN 1 +#define BMA2X2_RESET_INT__MSK 0x80 +#define BMA2X2_RESET_INT__REG BMA2X2_INT_CTRL_REG + +#define BMA2X2_LOWG_DUR__POS 0 +#define BMA2X2_LOWG_DUR__LEN 8 +#define BMA2X2_LOWG_DUR__MSK 0xFF +#define BMA2X2_LOWG_DUR__REG BMA2X2_LOW_DURN_REG + +#define BMA2X2_LOWG_THRES__POS 0 +#define BMA2X2_LOWG_THRES__LEN 8 +#define BMA2X2_LOWG_THRES__MSK 0xFF +#define BMA2X2_LOWG_THRES__REG BMA2X2_LOW_THRES_REG + +#define BMA2X2_LOWG_HYST__POS 0 +#define BMA2X2_LOWG_HYST__LEN 2 +#define BMA2X2_LOWG_HYST__MSK 0x03 +#define BMA2X2_LOWG_HYST__REG BMA2X2_LOW_HIGH_HYST_REG + +#define BMA2X2_LOWG_INT_MODE__POS 2 +#define BMA2X2_LOWG_INT_MODE__LEN 1 +#define BMA2X2_LOWG_INT_MODE__MSK 0x04 +#define BMA2X2_LOWG_INT_MODE__REG BMA2X2_LOW_HIGH_HYST_REG + +#define BMA2X2_HIGHG_DUR__POS 0 +#define BMA2X2_HIGHG_DUR__LEN 8 +#define BMA2X2_HIGHG_DUR__MSK 0xFF +#define BMA2X2_HIGHG_DUR__REG BMA2X2_HIGH_DURN_REG + +#define BMA2X2_HIGHG_THRES__POS 0 +#define BMA2X2_HIGHG_THRES__LEN 8 +#define BMA2X2_HIGHG_THRES__MSK 0xFF +#define BMA2X2_HIGHG_THRES__REG BMA2X2_HIGH_THRES_REG + +#define BMA2X2_HIGHG_HYST__POS 6 +#define BMA2X2_HIGHG_HYST__LEN 2 +#define BMA2X2_HIGHG_HYST__MSK 0xC0 +#define BMA2X2_HIGHG_HYST__REG BMA2X2_LOW_HIGH_HYST_REG + +#define BMA2X2_SLOPE_DUR__POS 0 +#define BMA2X2_SLOPE_DUR__LEN 2 +#define BMA2X2_SLOPE_DUR__MSK 0x03 +#define BMA2X2_SLOPE_DUR__REG BMA2X2_SLOPE_DURN_REG + +#define BMA2X2_SLO_NO_MOT_DUR__POS 2 +#define BMA2X2_SLO_NO_MOT_DUR__LEN 6 +#define BMA2X2_SLO_NO_MOT_DUR__MSK 0xFC +#define BMA2X2_SLO_NO_MOT_DUR__REG BMA2X2_SLOPE_DURN_REG + +#define BMA2X2_SLOPE_THRES__POS 0 +#define BMA2X2_SLOPE_THRES__LEN 8 +#define BMA2X2_SLOPE_THRES__MSK 0xFF +#define BMA2X2_SLOPE_THRES__REG BMA2X2_SLOPE_THRES_REG + +#define BMA2X2_SLO_NO_MOT_THRES__POS 0 +#define BMA2X2_SLO_NO_MOT_THRES__LEN 8 +#define BMA2X2_SLO_NO_MOT_THRES__MSK 0xFF +#define BMA2X2_SLO_NO_MOT_THRES__REG BMA2X2_SLO_NO_MOT_THRES_REG + +#define BMA2X2_TAP_DUR__POS 0 +#define BMA2X2_TAP_DUR__LEN 3 +#define BMA2X2_TAP_DUR__MSK 0x07 +#define BMA2X2_TAP_DUR__REG BMA2X2_TAP_PARAM_REG + +#define BMA2X2_TAP_SHOCK_DURN__POS 6 +#define BMA2X2_TAP_SHOCK_DURN__LEN 1 +#define BMA2X2_TAP_SHOCK_DURN__MSK 0x40 +#define BMA2X2_TAP_SHOCK_DURN__REG BMA2X2_TAP_PARAM_REG + +#define BMA2X2_ADV_TAP_INT__POS 5 +#define BMA2X2_ADV_TAP_INT__LEN 1 +#define BMA2X2_ADV_TAP_INT__MSK 0x20 +#define BMA2X2_ADV_TAP_INT__REG BMA2X2_TAP_PARAM_REG + +#define BMA2X2_TAP_QUIET_DURN__POS 7 +#define BMA2X2_TAP_QUIET_DURN__LEN 1 +#define BMA2X2_TAP_QUIET_DURN__MSK 0x80 +#define BMA2X2_TAP_QUIET_DURN__REG BMA2X2_TAP_PARAM_REG + +#define BMA2X2_TAP_THRES__POS 0 +#define BMA2X2_TAP_THRES__LEN 5 +#define BMA2X2_TAP_THRES__MSK 0x1F +#define BMA2X2_TAP_THRES__REG BMA2X2_TAP_THRES_REG + +#define BMA2X2_TAP_SAMPLES__POS 6 +#define BMA2X2_TAP_SAMPLES__LEN 2 +#define BMA2X2_TAP_SAMPLES__MSK 0xC0 +#define BMA2X2_TAP_SAMPLES__REG BMA2X2_TAP_THRES_REG + +#define BMA2X2_ORIENT_MODE__POS 0 +#define BMA2X2_ORIENT_MODE__LEN 2 +#define BMA2X2_ORIENT_MODE__MSK 0x03 +#define BMA2X2_ORIENT_MODE__REG BMA2X2_ORIENT_PARAM_REG + +#define BMA2X2_ORIENT_BLOCK__POS 2 +#define BMA2X2_ORIENT_BLOCK__LEN 2 +#define BMA2X2_ORIENT_BLOCK__MSK 0x0C +#define BMA2X2_ORIENT_BLOCK__REG BMA2X2_ORIENT_PARAM_REG + +#define BMA2X2_ORIENT_HYST__POS 4 +#define BMA2X2_ORIENT_HYST__LEN 3 +#define BMA2X2_ORIENT_HYST__MSK 0x70 +#define BMA2X2_ORIENT_HYST__REG BMA2X2_ORIENT_PARAM_REG + +#define BMA2X2_ORIENT_AXIS__POS 7 +#define BMA2X2_ORIENT_AXIS__LEN 1 +#define BMA2X2_ORIENT_AXIS__MSK 0x80 +#define BMA2X2_ORIENT_AXIS__REG BMA2X2_THETA_BLOCK_REG + +#define BMA2X2_ORIENT_UD_EN__POS 6 +#define BMA2X2_ORIENT_UD_EN__LEN 1 +#define BMA2X2_ORIENT_UD_EN__MSK 0x40 +#define BMA2X2_ORIENT_UD_EN__REG BMA2X2_THETA_BLOCK_REG + +#define BMA2X2_THETA_BLOCK__POS 0 +#define BMA2X2_THETA_BLOCK__LEN 6 +#define BMA2X2_THETA_BLOCK__MSK 0x3F +#define BMA2X2_THETA_BLOCK__REG BMA2X2_THETA_BLOCK_REG + +#define BMA2X2_THETA_FLAT__POS 0 +#define BMA2X2_THETA_FLAT__LEN 6 +#define BMA2X2_THETA_FLAT__MSK 0x3F +#define BMA2X2_THETA_FLAT__REG BMA2X2_THETA_FLAT_REG + +#define BMA2X2_FLAT_HOLD_TIME__POS 4 +#define BMA2X2_FLAT_HOLD_TIME__LEN 2 +#define BMA2X2_FLAT_HOLD_TIME__MSK 0x30 +#define BMA2X2_FLAT_HOLD_TIME__REG BMA2X2_FLAT_HOLD_TIME_REG + +#define BMA2X2_FLAT_HYS__POS 0 +#define BMA2X2_FLAT_HYS__LEN 3 +#define BMA2X2_FLAT_HYS__MSK 0x07 +#define BMA2X2_FLAT_HYS__REG BMA2X2_FLAT_HOLD_TIME_REG + +#define BMA2X2_FIFO_WML_TRIG_RETAIN__POS 0 +#define BMA2X2_FIFO_WML_TRIG_RETAIN__LEN 6 +#define BMA2X2_FIFO_WML_TRIG_RETAIN__MSK 0x3F +#define BMA2X2_FIFO_WML_TRIG_RETAIN__REG BMA2X2_FIFO_WML_TRIG + +#define BMA2X2_EN_SELF_TEST__POS 0 +#define BMA2X2_EN_SELF_TEST__LEN 2 +#define BMA2X2_EN_SELF_TEST__MSK 0x03 +#define BMA2X2_EN_SELF_TEST__REG BMA2X2_SELF_TEST_REG + +#define BMA2X2_NEG_SELF_TEST__POS 2 +#define BMA2X2_NEG_SELF_TEST__LEN 1 +#define BMA2X2_NEG_SELF_TEST__MSK 0x04 +#define BMA2X2_NEG_SELF_TEST__REG BMA2X2_SELF_TEST_REG + +#define BMA2X2_SELF_TEST_AMP__POS 4 +#define BMA2X2_SELF_TEST_AMP__LEN 1 +#define BMA2X2_SELF_TEST_AMP__MSK 0x10 +#define BMA2X2_SELF_TEST_AMP__REG BMA2X2_SELF_TEST_REG + + +#define BMA2X2_UNLOCK_EE_PROG_MODE__POS 0 +#define BMA2X2_UNLOCK_EE_PROG_MODE__LEN 1 +#define BMA2X2_UNLOCK_EE_PROG_MODE__MSK 0x01 +#define BMA2X2_UNLOCK_EE_PROG_MODE__REG BMA2X2_EEPROM_CTRL_REG + +#define BMA2X2_START_EE_PROG_TRIG__POS 1 +#define BMA2X2_START_EE_PROG_TRIG__LEN 1 +#define BMA2X2_START_EE_PROG_TRIG__MSK 0x02 +#define BMA2X2_START_EE_PROG_TRIG__REG BMA2X2_EEPROM_CTRL_REG + +#define BMA2X2_EE_PROG_READY__POS 2 +#define BMA2X2_EE_PROG_READY__LEN 1 +#define BMA2X2_EE_PROG_READY__MSK 0x04 +#define BMA2X2_EE_PROG_READY__REG BMA2X2_EEPROM_CTRL_REG + +#define BMA2X2_UPDATE_IMAGE__POS 3 +#define BMA2X2_UPDATE_IMAGE__LEN 1 +#define BMA2X2_UPDATE_IMAGE__MSK 0x08 +#define BMA2X2_UPDATE_IMAGE__REG BMA2X2_EEPROM_CTRL_REG + +#define BMA2X2_EE_REMAIN__POS 4 +#define BMA2X2_EE_REMAIN__LEN 4 +#define BMA2X2_EE_REMAIN__MSK 0xF0 +#define BMA2X2_EE_REMAIN__REG BMA2X2_EEPROM_CTRL_REG + +#define BMA2X2_EN_SPI_MODE_3__POS 0 +#define BMA2X2_EN_SPI_MODE_3__LEN 1 +#define BMA2X2_EN_SPI_MODE_3__MSK 0x01 +#define BMA2X2_EN_SPI_MODE_3__REG BMA2X2_SERIAL_CTRL_REG + +#define BMA2X2_I2C_WATCHDOG_PERIOD__POS 1 +#define BMA2X2_I2C_WATCHDOG_PERIOD__LEN 1 +#define BMA2X2_I2C_WATCHDOG_PERIOD__MSK 0x02 +#define BMA2X2_I2C_WATCHDOG_PERIOD__REG BMA2X2_SERIAL_CTRL_REG + +#define BMA2X2_EN_I2C_WATCHDOG__POS 2 +#define BMA2X2_EN_I2C_WATCHDOG__LEN 1 +#define BMA2X2_EN_I2C_WATCHDOG__MSK 0x04 +#define BMA2X2_EN_I2C_WATCHDOG__REG BMA2X2_SERIAL_CTRL_REG + +#define BMA2X2_EXT_MODE__POS 7 +#define BMA2X2_EXT_MODE__LEN 1 +#define BMA2X2_EXT_MODE__MSK 0x80 +#define BMA2X2_EXT_MODE__REG BMA2X2_EXTMODE_CTRL_REG + +#define BMA2X2_ALLOW_UPPER__POS 6 +#define BMA2X2_ALLOW_UPPER__LEN 1 +#define BMA2X2_ALLOW_UPPER__MSK 0x40 +#define BMA2X2_ALLOW_UPPER__REG BMA2X2_EXTMODE_CTRL_REG + +#define BMA2X2_MAP_2_LOWER__POS 5 +#define BMA2X2_MAP_2_LOWER__LEN 1 +#define BMA2X2_MAP_2_LOWER__MSK 0x20 +#define BMA2X2_MAP_2_LOWER__REG BMA2X2_EXTMODE_CTRL_REG + +#define BMA2X2_MAGIC_NUMBER__POS 0 +#define BMA2X2_MAGIC_NUMBER__LEN 5 +#define BMA2X2_MAGIC_NUMBER__MSK 0x1F +#define BMA2X2_MAGIC_NUMBER__REG BMA2X2_EXTMODE_CTRL_REG + +#define BMA2X2_UNLOCK_EE_WRITE_TRIM__POS 4 +#define BMA2X2_UNLOCK_EE_WRITE_TRIM__LEN 4 +#define BMA2X2_UNLOCK_EE_WRITE_TRIM__MSK 0xF0 +#define BMA2X2_UNLOCK_EE_WRITE_TRIM__REG BMA2X2_CTRL_UNLOCK_REG + +#define BMA2X2_EN_SLOW_COMP_X__POS 0 +#define BMA2X2_EN_SLOW_COMP_X__LEN 1 +#define BMA2X2_EN_SLOW_COMP_X__MSK 0x01 +#define BMA2X2_EN_SLOW_COMP_X__REG BMA2X2_OFFSET_CTRL_REG + +#define BMA2X2_EN_SLOW_COMP_Y__POS 1 +#define BMA2X2_EN_SLOW_COMP_Y__LEN 1 +#define BMA2X2_EN_SLOW_COMP_Y__MSK 0x02 +#define BMA2X2_EN_SLOW_COMP_Y__REG BMA2X2_OFFSET_CTRL_REG + +#define BMA2X2_EN_SLOW_COMP_Z__POS 2 +#define BMA2X2_EN_SLOW_COMP_Z__LEN 1 +#define BMA2X2_EN_SLOW_COMP_Z__MSK 0x04 +#define BMA2X2_EN_SLOW_COMP_Z__REG BMA2X2_OFFSET_CTRL_REG + +#define BMA2X2_FAST_CAL_RDY_S__POS 4 +#define BMA2X2_FAST_CAL_RDY_S__LEN 1 +#define BMA2X2_FAST_CAL_RDY_S__MSK 0x10 +#define BMA2X2_FAST_CAL_RDY_S__REG BMA2X2_OFFSET_CTRL_REG + +#define BMA2X2_CAL_TRIGGER__POS 5 +#define BMA2X2_CAL_TRIGGER__LEN 2 +#define BMA2X2_CAL_TRIGGER__MSK 0x60 +#define BMA2X2_CAL_TRIGGER__REG BMA2X2_OFFSET_CTRL_REG + +#define BMA2X2_RESET_OFFSET_REGS__POS 7 +#define BMA2X2_RESET_OFFSET_REGS__LEN 1 +#define BMA2X2_RESET_OFFSET_REGS__MSK 0x80 +#define BMA2X2_RESET_OFFSET_REGS__REG BMA2X2_OFFSET_CTRL_REG + +#define BMA2X2_COMP_CUTOFF__POS 0 +#define BMA2X2_COMP_CUTOFF__LEN 1 +#define BMA2X2_COMP_CUTOFF__MSK 0x01 +#define BMA2X2_COMP_CUTOFF__REG BMA2X2_OFFSET_PARAMS_REG + +#define BMA2X2_COMP_TARGET_OFFSET_X__POS 1 +#define BMA2X2_COMP_TARGET_OFFSET_X__LEN 2 +#define BMA2X2_COMP_TARGET_OFFSET_X__MSK 0x06 +#define BMA2X2_COMP_TARGET_OFFSET_X__REG BMA2X2_OFFSET_PARAMS_REG + +#define BMA2X2_COMP_TARGET_OFFSET_Y__POS 3 +#define BMA2X2_COMP_TARGET_OFFSET_Y__LEN 2 +#define BMA2X2_COMP_TARGET_OFFSET_Y__MSK 0x18 +#define BMA2X2_COMP_TARGET_OFFSET_Y__REG BMA2X2_OFFSET_PARAMS_REG + +#define BMA2X2_COMP_TARGET_OFFSET_Z__POS 5 +#define BMA2X2_COMP_TARGET_OFFSET_Z__LEN 2 +#define BMA2X2_COMP_TARGET_OFFSET_Z__MSK 0x60 +#define BMA2X2_COMP_TARGET_OFFSET_Z__REG BMA2X2_OFFSET_PARAMS_REG + +#define BMA2X2_FIFO_DATA_SELECT__POS 0 +#define BMA2X2_FIFO_DATA_SELECT__LEN 2 +#define BMA2X2_FIFO_DATA_SELECT__MSK 0x03 +#define BMA2X2_FIFO_DATA_SELECT__REG BMA2X2_FIFO_MODE_REG + +#define BMA2X2_FIFO_TRIGGER_SOURCE__POS 2 +#define BMA2X2_FIFO_TRIGGER_SOURCE__LEN 2 +#define BMA2X2_FIFO_TRIGGER_SOURCE__MSK 0x0C +#define BMA2X2_FIFO_TRIGGER_SOURCE__REG BMA2X2_FIFO_MODE_REG + +#define BMA2X2_FIFO_TRIGGER_ACTION__POS 4 +#define BMA2X2_FIFO_TRIGGER_ACTION__LEN 2 +#define BMA2X2_FIFO_TRIGGER_ACTION__MSK 0x30 +#define BMA2X2_FIFO_TRIGGER_ACTION__REG BMA2X2_FIFO_MODE_REG + +#define BMA2X2_FIFO_MODE__POS 6 +#define BMA2X2_FIFO_MODE__LEN 2 +#define BMA2X2_FIFO_MODE__MSK 0xC0 +#define BMA2X2_FIFO_MODE__REG BMA2X2_FIFO_MODE_REG + + +#define BMA2X2_STATUS1 0 +#define BMA2X2_STATUS2 1 +#define BMA2X2_STATUS3 2 +#define BMA2X2_STATUS4 3 +#define BMA2X2_STATUS5 4 + + +#define BMA2X2_RANGE_2G 3 +#define BMA2X2_RANGE_4G 5 +#define BMA2X2_RANGE_8G 8 +#define BMA2X2_RANGE_16G 12 + + +#define BMA2X2_BW_7_81HZ 0x08 +#define BMA2X2_BW_15_63HZ 0x09 +#define BMA2X2_BW_31_25HZ 0x0A +#define BMA2X2_BW_62_50HZ 0x0B +#define BMA2X2_BW_125HZ 0x0C +#define BMA2X2_BW_250HZ 0x0D +#define BMA2X2_BW_500HZ 0x0E +#define BMA2X2_BW_1000HZ 0x0F + +#define BMA2X2_SLEEP_DUR_0_5MS 0x05 +#define BMA2X2_SLEEP_DUR_1MS 0x06 +#define BMA2X2_SLEEP_DUR_2MS 0x07 +#define BMA2X2_SLEEP_DUR_4MS 0x08 +#define BMA2X2_SLEEP_DUR_6MS 0x09 +#define BMA2X2_SLEEP_DUR_10MS 0x0A +#define BMA2X2_SLEEP_DUR_25MS 0x0B +#define BMA2X2_SLEEP_DUR_50MS 0x0C +#define BMA2X2_SLEEP_DUR_100MS 0x0D +#define BMA2X2_SLEEP_DUR_500MS 0x0E +#define BMA2X2_SLEEP_DUR_1S 0x0F + +#define BMA2X2_LATCH_DUR_NON_LATCH 0x00 +#define BMA2X2_LATCH_DUR_250MS 0x01 +#define BMA2X2_LATCH_DUR_500MS 0x02 +#define BMA2X2_LATCH_DUR_1S 0x03 +#define BMA2X2_LATCH_DUR_2S 0x04 +#define BMA2X2_LATCH_DUR_4S 0x05 +#define BMA2X2_LATCH_DUR_8S 0x06 +#define BMA2X2_LATCH_DUR_LATCH 0x07 +#define BMA2X2_LATCH_DUR_NON_LATCH1 0x08 +#define BMA2X2_LATCH_DUR_250US 0x09 +#define BMA2X2_LATCH_DUR_500US 0x0A +#define BMA2X2_LATCH_DUR_1MS 0x0B +#define BMA2X2_LATCH_DUR_12_5MS 0x0C +#define BMA2X2_LATCH_DUR_25MS 0x0D +#define BMA2X2_LATCH_DUR_50MS 0x0E +#define BMA2X2_LATCH_DUR_LATCH1 0x0F + +#define BMA2X2_MODE_NORMAL 0 +#define BMA2X2_MODE_LOWPOWER1 1 +#define BMA2X2_MODE_SUSPEND 2 +#define BMA2X2_MODE_DEEP_SUSPEND 3 +#define BMA2X2_MODE_LOWPOWER2 4 +#define BMA2X2_MODE_STANDBY 5 + +#define BMA2X2_X_AXIS 0 +#define BMA2X2_Y_AXIS 1 +#define BMA2X2_Z_AXIS 2 + +#define BMA2X2_Low_G_Interrupt 0 +#define BMA2X2_High_G_X_Interrupt 1 +#define BMA2X2_High_G_Y_Interrupt 2 +#define BMA2X2_High_G_Z_Interrupt 3 +#define BMA2X2_DATA_EN 4 +#define BMA2X2_Slope_X_Interrupt 5 +#define BMA2X2_Slope_Y_Interrupt 6 +#define BMA2X2_Slope_Z_Interrupt 7 +#define BMA2X2_Single_Tap_Interrupt 8 +#define BMA2X2_Double_Tap_Interrupt 9 +#define BMA2X2_Orient_Interrupt 10 +#define BMA2X2_Flat_Interrupt 11 +#define BMA2X2_FFULL_INTERRUPT 12 +#define BMA2X2_FWM_INTERRUPT 13 + +#define BMA2X2_INT1_LOWG 0 +#define BMA2X2_INT2_LOWG 1 +#define BMA2X2_INT1_HIGHG 0 +#define BMA2X2_INT2_HIGHG 1 +#define BMA2X2_INT1_SLOPE 0 +#define BMA2X2_INT2_SLOPE 1 +#define BMA2X2_INT1_SLO_NO_MOT 0 +#define BMA2X2_INT2_SLO_NO_MOT 1 +#define BMA2X2_INT1_DTAP 0 +#define BMA2X2_INT2_DTAP 1 +#define BMA2X2_INT1_STAP 0 +#define BMA2X2_INT2_STAP 1 +#define BMA2X2_INT1_ORIENT 0 +#define BMA2X2_INT2_ORIENT 1 +#define BMA2X2_INT1_FLAT 0 +#define BMA2X2_INT2_FLAT 1 +#define BMA2X2_INT1_NDATA 0 +#define BMA2X2_INT2_NDATA 1 +#define BMA2X2_INT1_FWM 0 +#define BMA2X2_INT2_FWM 1 +#define BMA2X2_INT1_FFULL 0 +#define BMA2X2_INT2_FFULL 1 + +#define BMA2X2_SRC_LOWG 0 +#define BMA2X2_SRC_HIGHG 1 +#define BMA2X2_SRC_SLOPE 2 +#define BMA2X2_SRC_SLO_NO_MOT 3 +#define BMA2X2_SRC_TAP 4 +#define BMA2X2_SRC_DATA 5 + +#define BMA2X2_INT1_OUTPUT 0 +#define BMA2X2_INT2_OUTPUT 1 +#define BMA2X2_INT1_LEVEL 0 +#define BMA2X2_INT2_LEVEL 1 + +#define BMA2X2_LOW_DURATION 0 +#define BMA2X2_HIGH_DURATION 1 +#define BMA2X2_SLOPE_DURATION 2 +#define BMA2X2_SLO_NO_MOT_DURATION 3 + +#define BMA2X2_LOW_THRESHOLD 0 +#define BMA2X2_HIGH_THRESHOLD 1 +#define BMA2X2_SLOPE_THRESHOLD 2 +#define BMA2X2_SLO_NO_MOT_THRESHOLD 3 + + +#define BMA2X2_LOWG_HYST 0 +#define BMA2X2_HIGHG_HYST 1 + +#define BMA2X2_ORIENT_THETA 0 +#define BMA2X2_FLAT_THETA 1 + +#define BMA2X2_I2C_SELECT 0 +#define BMA2X2_I2C_EN 1 + +#define BMA2X2_SLOW_COMP_X 0 +#define BMA2X2_SLOW_COMP_Y 1 +#define BMA2X2_SLOW_COMP_Z 2 + +#define BMA2X2_CUT_OFF 0 +#define BMA2X2_OFFSET_TRIGGER_X 1 +#define BMA2X2_OFFSET_TRIGGER_Y 2 +#define BMA2X2_OFFSET_TRIGGER_Z 3 + +#define BMA2X2_GP0 0 +#define BMA2X2_GP1 1 + +#define BMA2X2_SLO_NO_MOT_EN_X 0 +#define BMA2X2_SLO_NO_MOT_EN_Y 1 +#define BMA2X2_SLO_NO_MOT_EN_Z 2 +#define BMA2X2_SLO_NO_MOT_EN_SEL 3 + +#define BMA2X2_WAKE_UP_DUR_20MS 0 +#define BMA2X2_WAKE_UP_DUR_80MS 1 +#define BMA2X2_WAKE_UP_DUR_320MS 2 +#define BMA2X2_WAKE_UP_DUR_2560MS 3 + +#define BMA2X2_SELF_TEST0_ON 1 +#define BMA2X2_SELF_TEST1_ON 2 + +#define BMA2X2_EE_W_OFF 0 +#define BMA2X2_EE_W_ON 1 + +#define BMA2X2_LOW_TH_IN_G(gthres, range) ((256 * gthres) / range) + + +#define BMA2X2_HIGH_TH_IN_G(gthres, range) ((256 * gthres) / range) + + +#define BMA2X2_LOW_HY_IN_G(ghyst, range) ((32 * ghyst) / range) + + +#define BMA2X2_HIGH_HY_IN_G(ghyst, range) ((32 * ghyst) / range) + + +#define BMA2X2_SLOPE_TH_IN_G(gthres, range) ((128 * gthres) / range) + + +#define BMA2X2_GET_BITSLICE(regvar, bitname)\ + ((regvar & bitname##__MSK) >> bitname##__POS) + + +#define BMA2X2_SET_BITSLICE(regvar, bitname, val)\ + ((regvar & ~bitname##__MSK) | ((val<> (16 - bitwidth)) +#endif + +#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT +#define BMA2x2_IS_NEWDATA_INT_ENABLED() (true) +#else +#define BMA2x2_IS_NEWDATA_INT_ENABLED() (false) +#endif + +#ifdef BMA2X2_ENABLE_INT1 +#define BMA2x2_IS_INT1_ENABLED() (true) +#else +#define BMA2x2_IS_INT1_ENABLED() (false) +#endif + +#ifdef BMA2X2_ENABLE_INT2 +#define BMA2x2_IS_INT2_ENABLED() (true) +#else +#define BMA2x2_IS_INT2_ENABLED() (false) +#endif + +#define CHECK_CHIP_ID_TIME_MAX 5 +#define BMA255_CHIP_ID 0XFA +#define BMA250E_CHIP_ID 0XF9 +#define BMA222E_CHIP_ID 0XF8 +#define BMA280_CHIP_ID 0XFB +#define BMA355_CHIP_ID 0XEA + +#define BMA255_TYPE 0 +#define BMA250E_TYPE 1 +#define BMA222E_TYPE 2 +#define BMA280_TYPE 3 + +#define MAX_FIFO_F_LEVEL 32 +#define MAX_FIFO_F_BYTES 6 +#define FIFO_FRAMESIZE_3_AXIS 6 +#define FIFO_FRAMESIZE_1_AXIS 2 +#define BMA_MAX_RETRY_I2C_XFER (100) + +#ifdef CONFIG_DOUBLE_TAP +#define DEFAULT_TAP_JUDGE_PERIOD 1000 /* default judge in 1 second */ +#endif + +#define BMA2X2_SMD_DET_TIME_NS (2200UL * 1000 * 1000) +#define BMA2X2_SMD_DET_CNT (7) +#define BMA2X2_SMD_SLOPE_DUR 0x3 +#define BMA2X2_SMD_SLOPE_TH 0x20 +#define BMA2X2_SMD_NO_MOT_DUR 0x1 +#define BMA2X2_SMD_NO_MOT_TH 0x20 + +/*! Bosch sensor unknown place*/ +#define BOSCH_SENSOR_PLACE_UNKNOWN (-1) +/*! Bosch sensor remapping table size P0~P7*/ +#define MAX_AXIS_REMAP_TAB_SZ 8 +#define BOSCH_SENSOR_PLANE 0 +#define BOSCH_SENSOR_UP 1 +#define BOSCH_SENSOR_DOWN 2 +#define RETRY_TIME 50 +/*! + * @brief:BMI058 feature + * macro definition +*/ + +#define BMA2X2_IS_NEWDATA_INT BMA2X2_DATA_INT_S__MSK +#define BMA2X2_FIFO_MODE_BYPASS 0x0 +#define BMA2X2_FIFO_MODE_FIFO 0x1 +#define BMA2X2_FIFO_MODE_STREAM 0x2 + +#define BMA2X2_FIFO_DAT_SEL_XYZ 0 +#define BMA2X2_FIFO_DAT_SEL_X 1 +#define BMA2X2_FIFO_DAT_SEL_Y 2 +#define BMA2X2_FIFO_DAT_SEL_Z 3 + +#define BMA2X2_FIFO_WM_INT_FLAG BMA2X2_FIFO_WM_INT_S__MSK +#define BMA2X2_FIFO_FULL_INT_FLAG BMA2X2_FIFO_FULL_INT_S__MSK +#define BMA2X2_IS_FIFO_INT\ + (BMA2X2_FIFO_WM_INT_FLAG | BMA2X2_FIFO_FULL_INT_FLAG) + +#define BMA2x2_NEWDATA_INT_MASK 0x80 +#define BMA2x2_NEWDATA_INT_FLAG 0x80 + +#ifdef CONFIG_SENSORS_BMI058 +#define C_BMI058_One_U8X 1 +#define C_BMI058_Two_U8X 2 +#define BMI058_OFFSET_TRIGGER_X BMA2X2_OFFSET_TRIGGER_Y +#define BMI058_OFFSET_TRIGGER_Y BMA2X2_OFFSET_TRIGGER_X + +/*! BMI058 X AXIS OFFSET REG definition*/ +#define BMI058_OFFSET_X_AXIS_REG BMA2X2_OFFSET_Y_AXIS_REG +/*! BMI058 Y AXIS OFFSET REG definition*/ +#define BMI058_OFFSET_Y_AXIS_REG BMA2X2_OFFSET_X_AXIS_REG + +#define BMI058_FIFO_DAT_SEL_X BMA2X2_FIFO_DAT_SEL_Y +#define BMI058_FIFO_DAT_SEL_Y BMA2X2_FIFO_DAT_SEL_X + +/*! BMA2x2 common slow no motion X interrupt type definition*/ +#define BMA2X2_SLOW_NO_MOT_X_INT 12 +/*! BMA2x2 common slow no motion Y interrupt type definition*/ +#define BMA2X2_SLOW_NO_MOT_Y_INT 13 +/*! BMA2x2 common High G X interrupt type definition*/ +#define BMA2X2_HIGHG_X_INT 1 +/*! BMA2x2 common High G Y interrupt type definition*/ +#define BMA2X2_HIGHG_Y_INT 2 +/*! BMA2x2 common slope X interrupt type definition*/ +#define BMA2X2_SLOPE_X_INT 5 +/*! BMA2x2 common slope Y interrupt type definition*/ +#define BMA2X2_SLOPE_Y_INT 6 + +/*! this structure holds some interrupt types difference +**between BMA2x2 and BMI058. +*/ +struct interrupt_map_t { + int x; + int y; +}; +/*!*Need to use BMA2x2 Common interrupt type definition to +* instead of Some of BMI058 reversed Interrupt type +* because of HW Register. +* The reversed Interrupt types contain: +* slow_no_mot_x_int && slow_not_mot_y_int +* highg_x_int && highg_y_int +* slope_x_int && slope_y_int +**/ +static const struct interrupt_map_t int_map[] = { + {BMA2X2_SLOW_NO_MOT_X_INT, BMA2X2_SLOW_NO_MOT_Y_INT}, + {BMA2X2_HIGHG_X_INT, BMA2X2_HIGHG_Y_INT}, + {BMA2X2_SLOPE_X_INT, BMA2X2_SLOPE_Y_INT} +}; + +/*! high g or slope interrupt type definition for BMI058*/ +/*! High G interrupt of x, y, z axis happened */ +#define HIGH_G_INTERRUPT_X HIGH_G_INTERRUPT_Y_HAPPENED +#define HIGH_G_INTERRUPT_Y HIGH_G_INTERRUPT_X_HAPPENED +#define HIGH_G_INTERRUPT_Z HIGH_G_INTERRUPT_Z_HAPPENED +/*! High G interrupt of x, y, z negative axis happened */ +#define HIGH_G_INTERRUPT_X_N HIGH_G_INTERRUPT_Y_NEGATIVE_HAPPENED +#define HIGH_G_INTERRUPT_Y_N HIGH_G_INTERRUPT_X_NEGATIVE_HAPPENED +#define HIGH_G_INTERRUPT_Z_N HIGH_G_INTERRUPT_Z_NEGATIVE_HAPPENED +/*! Slope interrupt of x, y, z axis happened */ +#define SLOPE_INTERRUPT_X SLOPE_INTERRUPT_Y_HAPPENED +#define SLOPE_INTERRUPT_Y SLOPE_INTERRUPT_X_HAPPENED +#define SLOPE_INTERRUPT_Z SLOPE_INTERRUPT_Z_HAPPENED +/*! Slope interrupt of x, y, z negative axis happened */ +#define SLOPE_INTERRUPT_X_N SLOPE_INTERRUPT_Y_NEGATIVE_HAPPENED +#define SLOPE_INTERRUPT_Y_N SLOPE_INTERRUPT_X_NEGATIVE_HAPPENED +#define SLOPE_INTERRUPT_Z_N SLOPE_INTERRUPT_Z_NEGATIVE_HAPPENED + + +#else + +/*! high g or slope interrupt type definition*/ +/*! High G interrupt of x, y, z axis happened */ +#define HIGH_G_INTERRUPT_X HIGH_G_INTERRUPT_X_HAPPENED +#define HIGH_G_INTERRUPT_Y HIGH_G_INTERRUPT_Y_HAPPENED +#define HIGH_G_INTERRUPT_Z HIGH_G_INTERRUPT_Z_HAPPENED +/*! High G interrupt of x, y, z negative axis happened */ +#define HIGH_G_INTERRUPT_X_N HIGH_G_INTERRUPT_X_NEGATIVE_HAPPENED +#define HIGH_G_INTERRUPT_Y_N HIGH_G_INTERRUPT_Y_NEGATIVE_HAPPENED +#define HIGH_G_INTERRUPT_Z_N HIGH_G_INTERRUPT_Z_NEGATIVE_HAPPENED +/*! Slope interrupt of x, y, z axis happened */ +#define SLOPE_INTERRUPT_X SLOPE_INTERRUPT_X_HAPPENED +#define SLOPE_INTERRUPT_Y SLOPE_INTERRUPT_Y_HAPPENED +#define SLOPE_INTERRUPT_Z SLOPE_INTERRUPT_Z_HAPPENED +/*! Slope interrupt of x, y, z negative axis happened */ +#define SLOPE_INTERRUPT_X_N SLOPE_INTERRUPT_X_NEGATIVE_HAPPENED +#define SLOPE_INTERRUPT_Y_N SLOPE_INTERRUPT_Y_NEGATIVE_HAPPENED +#define SLOPE_INTERRUPT_Z_N SLOPE_INTERRUPT_Z_NEGATIVE_HAPPENED + + +#endif/*End of CONFIG_SENSORS_BMI058*/ + +/*BMA power supply VDD 1.62V-3.6V VIO 1.2-3.6V */ +#define BMA2x2_VDD_MIN_UV 2000000 +#define BMA2x2_VDD_MAX_UV 3400000 +#define BMA2x2_VIO_MIN_UV 1500000 +#define BMA2x2_VIO_MAX_UV 3400000 + +/* Polling delay in msecs */ +#define POLL_INTERVAL_MIN_MS 10 +#define POLL_INTERVAL_MAX_MS 4000 +#define POLL_DEFAULT_INTERVAL_MS 200 + +#define POLL_MS_100HZ 10 + +/* Interrupt delay in msecs */ +#define BMA_INT_MAX_DELAY 64 + +#define MAX_RANGE_MAP 4 + +#define BMA_CAL_BUF_SIZE 99 + +struct bma2x2_type_map_t { + + /*! bma2x2 sensor chip id */ + uint16_t chip_id; + + /*! bma2x2 sensor type */ + uint16_t sensor_type; + + /*! bma2x2 sensor name */ + const char *sensor_name; + + /*! bma2x2 sensor resolution */ + const char *resolution; +}; + +static const struct bma2x2_type_map_t sensor_type_map[] = { + + {BMA255_CHIP_ID, BMA255_TYPE, "BMA255/254", "0.00957031"}, + {BMA355_CHIP_ID, BMA255_TYPE, "BMA355", "0.00957031"}, + {BMA250E_CHIP_ID, BMA250E_TYPE, "BMA250E", "0.03828125"}, + {BMA222E_CHIP_ID, BMA222E_TYPE, "BMA222E", "0.153125"}, + {BMA280_CHIP_ID, BMA280_TYPE, "BMA280", "0.00239258"}, + +}; + +/*! + * we use a typedef to hide the detail, + * because this type might be changed + */ +struct bosch_sensor_axis_remap { + /* src means which source will be mapped to target x, y, z axis */ + /* if an target OS axis is remapped from (-)x, + * src is 0, sign_* is (-)1 */ + /* if an target OS axis is remapped from (-)y, + * src is 1, sign_* is (-)1 */ + /* if an target OS axis is remapped from (-)z, + * src is 2, sign_* is (-)1 */ + int src_x:3; + int src_y:3; + int src_z:3; + + int sign_x:2; + int sign_y:2; + int sign_z:2; +}; + +struct bosch_sensor_data { + union { + int16_t v[3]; + struct { + int16_t x; + int16_t y; + int16_t z; + }; + }; +}; + +struct bma2x2acc { + s16 x; + s16 y; + s16 z; +}; + +struct bma2x2_platform_data { + int poll_interval; + int gpio_int1; + int gpio_int2; + unsigned int int1_flag; + unsigned int int2_flag; + s8 place; + bool int_en; + bool use_int2; /* Use interrupt pin2 */ + bool use_smd; + bool use_hrtimer; +}; + +struct bma2x2_suspend_state { + bool powerEn; +}; + +struct bma2x2_pinctrl_data { + struct pinctrl *pctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; +}; + +struct bma2x2_data { + struct i2c_client *bma2x2_client; + struct sensors_classdev cdev; + struct sensors_classdev smd_cdev; + atomic_t delay; + atomic_t enable; + atomic_t selftest_result; + atomic_t cal_status; + atomic_t fifo_enabled; + atomic_t en_sig_motion; + char calibrate_buf[BMA_CAL_BUF_SIZE]; + unsigned int chip_id; + unsigned int chip_type; + unsigned char mode; + signed char sensor_type; + unsigned char fifo_datasel; + unsigned int fifo_count; + signed char *fifo_buf; + s64 fifo_start_ns; + unsigned int max_latency_ms; + struct input_dev *input; + + struct bst_dev *bst_acc; + + struct bma2x2acc value; + struct mutex value_mutex; + struct mutex enable_mutex; + struct mutex mode_mutex; + struct mutex op_lock; + + struct workqueue_struct *data_wq; + struct delayed_work work; + struct work_struct irq_work; + struct hrtimer accel_timer; + int accel_wkp_flag; + struct task_struct *accel_task; + bool accel_delay_change; + wait_queue_head_t accel_wq; + struct regulator *vdd; + struct regulator *vio; + bool power_enabled; + unsigned char bandwidth; + unsigned char range; + unsigned int int_flag; + int sensitivity; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + int IRQ; + struct bma2x2_platform_data *pdata; + struct bma2x2_suspend_state suspend_state; + struct bma2x2_pinctrl_data *pctrl_data; + + int ref_count; + struct input_dev *dev_interrupt; + +#ifdef CONFIG_SIG_MOTION + struct class *g_sensor_class; + struct device *g_sensor_dev; + struct input_dev *smd_input; + + /*struct bma250_platform_data *pdata;*/ + unsigned int smd_count; +#endif + +#ifdef CONFIG_DOUBLE_TAP + struct class *g_sensor_class_doubletap; + struct device *g_sensor_dev_doubletap; + atomic_t en_double_tap; + unsigned char tap_times; + struct mutex tap_mutex; + struct timer_list tap_timer; + int tap_time_period; +#endif +}; + +struct bma2x2_delay2bw { + unsigned int delay_ms; + unsigned int bw_config; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void bma2x2_early_suspend(struct early_suspend *h); +static void bma2x2_late_resume(struct early_suspend *h); +#endif + +static int bma2x2_open_init(struct i2c_client *client, + struct bma2x2_data *data); +static int bma2x2_set_mode(struct i2c_client *client, u8 mode); +static int bma2x2_get_mode(struct i2c_client *client, u8 *mode); +static int bma2x2_get_fifo_mode(struct i2c_client *client, u8 *fifo_mode); +static int bma2x2_set_fifo_mode(struct i2c_client *client, u8 fifo_mode); +static int bma2x2_normal_to_suspend(struct bma2x2_data *bma2x2, + unsigned char data1, unsigned char data2); +static int bma2x2_store_state(struct i2c_client *client, + struct bma2x2_data *data); +static int bma2x2_power_ctl(struct bma2x2_data *data, bool on); +static int bma2x2_eeprom_prog(struct i2c_client *client); +static int bma2x2_get_sensitivity(struct bma2x2_data *bma2x2, int range); +static void bma2x2_pinctrl_state(struct bma2x2_data *data, bool active); +static int bma2x2_flush_fifo(struct bma2x2_data *bma2x2); + +static struct sensors_classdev sensors_cdev = { + .name = "bma2x2-accel", + .vendor = "bosch", + .version = 1, + .handle = SENSORS_ACCELERATION_HANDLE, + .type = SENSOR_TYPE_ACCELEROMETER, + .max_range = "156.8", /* 16g */ + .resolution = "0.153125", /* 15.6mg */ + .sensor_power = "0.13", /* typical value */ + .min_delay = POLL_INTERVAL_MIN_MS * 1000, /* in microseconds */ + .max_delay = POLL_INTERVAL_MAX_MS, + .max_latency = POLL_INTERVAL_MAX_MS, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = POLL_DEFAULT_INTERVAL_MS, /* in millisecond */ + .sensors_enable = NULL, + .sensors_poll_delay = NULL, + .sensors_self_test = NULL, +}; + +#ifdef CONFIG_SIG_MOTION +static struct sensors_classdev smd_cdev = { + .name = "bma2x2-smd", + .vendor = "bosch", + .version = 1, + .handle = 0, + .type = SENSOR_TYPE_SIGNIFICANT_MOTION, + .max_range = "1", + .resolution = "1.0", + .sensor_power = "0.25", + .min_delay = -1, + .max_delay = 0, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .flags = 5, + .enabled = 0, + .delay_msec = 0, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + +/*Remapping for BMA2X2*/ + +static const struct bosch_sensor_axis_remap +bst_axis_remap_tab_dft[MAX_AXIS_REMAP_TAB_SZ] = { + /* src_x src_y src_z sign_x sign_y sign_z */ + { 0, 1, 2, 1, 1, 1 }, /* P0 */ + { 1, 0, 2, 1, -1, 1 }, /* P1 */ + { 0, 1, 2, -1, -1, 1 }, /* P2 */ + { 1, 0, 2, -1, 1, 1 }, /* P3 */ + + { 0, 1, 2, -1, 1, -1 }, /* P4 */ + { 1, 0, 2, -1, -1, -1 }, /* P5 */ + { 0, 1, 2, 1, -1, -1 }, /* P6 */ + { 1, 0, 2, 1, 1, -1 }, /* P7 */ +}; + +static const int bosch_sensor_range_map[MAX_RANGE_MAP] = { + 0, /*2G range*/ + 1, /*4G range*/ + 2, /*8G range*/ + 3 /*16G range*/ +}; + +/* Convert bandwidth to sampling delay */ +static const struct bma2x2_delay2bw bma2x2_delay2bw_table[] = { + { 1, BMA2X2_BW_500HZ }, + { 2, BMA2X2_BW_250HZ }, + { 4, BMA2X2_BW_125HZ }, + { 8, BMA2X2_BW_62_50HZ }, + { 16, BMA2X2_BW_31_25HZ }, + { 32, BMA2X2_BW_15_63HZ }, + { 64, BMA2X2_BW_7_81HZ }, +}; + + +static inline bool bma2x2_use_data_polling(const struct bma2x2_data *bma2x2) +{ + return !bma2x2->pdata->int_en || + ((bma2x2->pdata->int_en) && + !BMA2x2_IS_NEWDATA_INT_ENABLED()); +} + +static inline void bma2x2_set_fifo_start_time(struct bma2x2_data *bma2x2) +{ + struct timespec ts; + + get_monotonic_boottime(&ts); + bma2x2->fifo_start_ns = timespec_to_ns(&ts); +} + +static void bst_remap_sensor_data(struct bosch_sensor_data *data, + const struct bosch_sensor_axis_remap *remap) +{ + struct bosch_sensor_data tmp; + + tmp.x = data->v[remap->src_x] * remap->sign_x; + tmp.y = data->v[remap->src_y] * remap->sign_y; + tmp.z = data->v[remap->src_z] * remap->sign_z; + + memcpy(data, &tmp, sizeof(*data)); +} + + +static void bst_remap_sensor_data_dft_tab(struct bosch_sensor_data *data, + int place) +{ + /* sensor with place 0 needs not to be remapped */ + if ((place <= 0) || (place >= MAX_AXIS_REMAP_TAB_SZ)) + return; + + bst_remap_sensor_data(data, &bst_axis_remap_tab_dft[place]); +} + +static void bma2x2_remap_sensor_data(struct bma2x2acc *val, + struct bma2x2_data *client_data) +{ + struct bosch_sensor_data bsd; + +#ifdef CONFIG_SENSORS_BMI058 +/*x,y need to be invesed becase of HW Register for BMI058*/ + bsd.y = val->x; + bsd.x = val->y; + bsd.z = val->z; +#else + bsd.x = val->x; + bsd.y = val->y; + bsd.z = val->z; +#endif + + bst_remap_sensor_data_dft_tab(&bsd, + client_data->pdata->place); + + val->x = bsd.x; + val->y = bsd.y; + val->z = bsd.z; + +} + + +static int bma2x2_smbus_read_byte(struct i2c_client *client, + unsigned char reg_addr, unsigned char *data) +{ + s32 dummy; + + dummy = i2c_smbus_read_byte_data(client, reg_addr); + if (dummy < 0) + return -EIO; + *data = dummy & 0x000000ff; + + return 0; +} + +static int bma2x2_smbus_write_byte(struct i2c_client *client, + unsigned char reg_addr, unsigned char *data) +{ + s32 dummy; + + dummy = i2c_smbus_write_byte_data(client, reg_addr, *data); + if (dummy < 0) + return -EIO; + udelay(2); + return 0; +} + +static int bma2x2_smbus_read_byte_block(struct i2c_client *client, + unsigned char reg_addr, unsigned char *data, unsigned char len) +{ + s32 dummy; + + dummy = i2c_smbus_read_i2c_block_data(client, reg_addr, len, data); + if (dummy < 0) + return -EIO; + return 0; +} + +static int bma_i2c_burst_read(struct i2c_client *client, u8 reg_addr, + u8 *data, u16 len) +{ + int retry; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®_addr, + }, + + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + }, + }; + + for (retry = 0; retry < BMA_MAX_RETRY_I2C_XFER; retry++) { + if (i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)) > 0) + break; + + I2C_RETRY_DELAY(); + } + + if (BMA_MAX_RETRY_I2C_XFER <= retry) { + dev_err(&client->dev, "I2C xfer error"); + return -EIO; + } + + return 0; +} + +static int bma2x2_check_chip_id(struct i2c_client *client, + struct bma2x2_data *data) +{ + int i = 0; + int err = 0; + unsigned char chip_id; + unsigned char read_count = 0; + unsigned char bma2x2_sensor_type_count = 0; + + bma2x2_sensor_type_count = + sizeof(sensor_type_map) / sizeof(struct bma2x2_type_map_t); + + while (read_count++ < CHECK_CHIP_ID_TIME_MAX) { + err = bma2x2_smbus_read_byte(client, BMA2X2_CHIP_ID_REG, + &chip_id); + if (err < 0) { + dev_err(&client->dev, + "Bosch Sensortec Device not foundi2c bus read error, read chip_id:%d\n", + chip_id); + err = -ENODEV; + return err; + } + for (i = 0; i < bma2x2_sensor_type_count; i++) { + if (sensor_type_map[i].chip_id == chip_id) { + data->sensor_type = + sensor_type_map[i].sensor_type; + data->chip_id = chip_id; + dev_dbg(&client->dev, + "Bosch Sensortec Device detected, HW IC name: %s\n", + sensor_type_map[i].sensor_name); + data->chip_type = i; + return err; + } + } + if (i < bma2x2_sensor_type_count) { + return err; + } else if (read_count == CHECK_CHIP_ID_TIME_MAX) { + dev_err(&client->dev, + "Failed!Bosch Sensortec Device not found, mismatch chip_id:%d\n", + chip_id); + err = -ENODEV; + return err; + } + I2C_RETRY_DELAY(); + } + return err; +} + +#if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2) +static int bma2x2_set_newdata(struct i2c_client *client, + unsigned char channel, unsigned char int_newdata) +{ + + unsigned char data; + int comres = 0; + + switch (channel) { + case BMA2X2_INT1_NDATA: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_NEWDATA__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_EN_INT1_PAD_NEWDATA, int_newdata); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_NEWDATA__REG, &data); + break; + case BMA2X2_INT2_NDATA: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_NEWDATA__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_EN_INT2_PAD_NEWDATA, int_newdata); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_NEWDATA__REG, &data); + break; + default: + comres = -1; + break; + } + + return comres; + +} + +static int bma2x2_set_fwm_int_pad_sel(struct i2c_client *client, + unsigned char channel, unsigned char fifo_int) +{ + unsigned char data; + int comres = 0; + + switch (channel) { + case BMA2X2_INT1_FWM: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_FWM__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_EN_INT1_PAD_FWM, fifo_int); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_FWM__REG, &data); + break; + case BMA2X2_INT2_FWM: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_FWM__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_EN_INT2_PAD_FWM, fifo_int); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_FWM__REG, &data); + break; + default: + comres = -1; + break; + } + + return comres; +} + +static int bma2x2_set_ffull_int_pad_sel(struct i2c_client *client, + unsigned char channel, unsigned char fifo_int) +{ + unsigned char data; + int comres = 0; + + switch (channel) { + case BMA2X2_INT1_FFULL: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_FFULL__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_EN_INT1_PAD_FFULL, fifo_int); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_FFULL__REG, &data); + break; + case BMA2X2_INT2_FFULL: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_FFULL__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_EN_INT2_PAD_FFULL, fifo_int); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_FFULL__REG, &data); + break; + default: + comres = -1; + break; + } + + return comres; +} +#endif + +#ifdef BMA2X2_ENABLE_INT1 +static int bma2x2_set_int1_pad_sel(struct i2c_client *client, unsigned char + int1sel) +{ + int comres = 0; + unsigned char data; + unsigned char state = 0x01; + + switch (int1sel) { + case 0: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_LOWG__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_LOWG, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_LOWG__REG, &data); + break; + case 1: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_HIGHG__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_HIGHG, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_HIGHG__REG, &data); + break; + case 2: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_SLOPE__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_SLOPE, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_SLOPE__REG, &data); + break; + case 3: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_DB_TAP__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_DB_TAP, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_DB_TAP__REG, &data); + break; + case 4: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_SNG_TAP__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_SNG_TAP, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_SNG_TAP__REG, &data); + break; + case 5: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_ORIENT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_ORIENT, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_ORIENT__REG, &data); + break; + case 6: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_FLAT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_FLAT, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_FLAT__REG, &data); + break; + case 7: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT1_PAD_SLO_NO_MOT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_SLO_NO_MOT, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT1_PAD_SLO_NO_MOT__REG, &data); + break; + + default: + break; + } + + return comres; +} +#endif /* BMA2X2_ENABLE_INT1 */ + +#ifdef BMA2X2_ENABLE_INT2 +static int bma2x2_set_int2_pad_sel(struct i2c_client *client, unsigned char + int2sel) +{ + int comres = 0; + unsigned char data; + unsigned char state = 0x01; + + switch (int2sel) { + case 0: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_LOWG__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_LOWG, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_LOWG__REG, &data); + break; + case 1: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_HIGHG__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_HIGHG, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_HIGHG__REG, &data); + break; + case 2: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_SLOPE__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_SLOPE, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_SLOPE__REG, &data); + break; + case 3: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_DB_TAP__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_DB_TAP, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_DB_TAP__REG, &data); + break; + case 4: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_SNG_TAP__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_SNG_TAP, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_SNG_TAP__REG, &data); + break; + case 5: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_ORIENT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_ORIENT, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_ORIENT__REG, &data); + break; + case 6: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_FLAT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_FLAT, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_FLAT__REG, &data); + break; + case 7: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_EN_INT2_PAD_SLO_NO_MOT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_SLO_NO_MOT, + state); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_EN_INT2_PAD_SLO_NO_MOT__REG, &data); + break; + default: + break; + } + + return comres; +} +#endif /* BMA2X2_ENABLE_INT2 */ + +static int bma2x2_set_Int_Enable(struct i2c_client *client, unsigned char + InterruptType , unsigned char value) +{ + int comres = 0; + unsigned char data1, data2; + + if ((11 < InterruptType) && (InterruptType < 16)) { + switch (InterruptType) { + case 12: + /* slow/no motion X Interrupt */ + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_X_INT__REG, &data1); + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_INT_SLO_NO_MOT_EN_X_INT, value); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_X_INT__REG, &data1); + break; + case 13: + /* slow/no motion Y Interrupt */ + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__REG, &data1); + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_INT_SLO_NO_MOT_EN_Y_INT, value); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__REG, &data1); + break; + case 14: + /* slow/no motion Z Interrupt */ + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__REG, &data1); + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_INT_SLO_NO_MOT_EN_Z_INT, value); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__REG, &data1); + break; + case 15: + /* slow / no motion Interrupt select */ + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__REG, &data1); + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT, value); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__REG, &data1); + } + + return comres; + } + + + comres = bma2x2_smbus_read_byte(client, BMA2X2_INT_ENABLE1_REG, &data1); + comres = bma2x2_smbus_read_byte(client, BMA2X2_INT_ENABLE2_REG, &data2); + + value = value & 1; + switch (InterruptType) { + case 0: + /* Low G Interrupt */ + data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_LOWG_INT, value); + break; + + case 1: + /* High G X Interrupt */ + data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_HIGHG_X_INT, + value); + break; + + case 2: + /* High G Y Interrupt */ + data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_HIGHG_Y_INT, + value); + break; + + case 3: + /* High G Z Interrupt */ + data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_HIGHG_Z_INT, + value); + break; + + case 4: + /* New Data Interrupt */ + data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_NEW_DATA_INT, + value); + break; + + case 5: + /* Slope X Interrupt */ + data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SLOPE_X_INT, + value); + break; + + case 6: + /* Slope Y Interrupt */ + data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SLOPE_Y_INT, + value); + break; + + case 7: + /* Slope Z Interrupt */ + data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SLOPE_Z_INT, + value); + break; + + case 8: + /* Single Tap Interrupt */ + data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SINGLE_TAP_INT, + value); + break; + + case 9: + /* Double Tap Interrupt */ + data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_DOUBLE_TAP_INT, + value); + break; + + case 10: + /* Orient Interrupt */ + data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_ORIENT_INT, value); + break; + + case 11: + /* Flat Interrupt */ + data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_FLAT_INT, value); + break; + + default: + break; + } + comres = bma2x2_smbus_write_byte(client, BMA2X2_INT_ENABLE1_REG, + &data1); + comres = bma2x2_smbus_write_byte(client, BMA2X2_INT_ENABLE2_REG, + &data2); + + return comres; +} + +static int bma2x2_set_watermark_int(struct i2c_client *client, bool enable) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_FWM_EN_INT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_INT_FWM_EN_INT, (unsigned char)(enable ? 1 : 0)); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT_FWM_EN_INT__REG, &data); + + return comres; +} + +static int bma2x2_set_fifo_full_int(struct i2c_client *client, bool enable) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_FFULL_EN_INT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_INT_FFULL_EN_INT, (unsigned char)(enable ? 1 : 0)); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT_FFULL_EN_INT__REG, &data); + + return comres; +} + +#if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2) +static int bma2x2_get_HIGH_first(struct i2c_client *client, unsigned char + param, unsigned char *intstatus) +{ + int comres = 0; + unsigned char data; + + switch (param) { + case 0: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_STATUS_ORIENT_HIGH_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_FIRST_X); + *intstatus = data; + break; + case 1: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_STATUS_ORIENT_HIGH_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_FIRST_Y); + *intstatus = data; + break; + case 2: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_STATUS_ORIENT_HIGH_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_FIRST_Z); + *intstatus = data; + break; + default: + break; + } + + return comres; +} + +static int bma2x2_get_HIGH_sign(struct i2c_client *client, unsigned char + *intstatus) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG, + &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_SIGN_S); + *intstatus = data; + + return comres; +} + +#ifndef CONFIG_SIG_MOTION +static int bma2x2_get_slope_first(struct i2c_client *client, unsigned char + param, unsigned char *intstatus) +{ + int comres = 0; + unsigned char data; + + switch (param) { + case 0: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_STATUS_TAP_SLOPE_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_FIRST_X); + *intstatus = data; + break; + case 1: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_STATUS_TAP_SLOPE_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_FIRST_Y); + *intstatus = data; + break; + case 2: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_STATUS_TAP_SLOPE_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_FIRST_Z); + *intstatus = data; + break; + default: + break; + } + + return comres; +} + +static int bma2x2_get_slope_sign(struct i2c_client *client, unsigned char + *intstatus) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_TAP_SLOPE_REG, + &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_SIGN_S); + *intstatus = data; + + return comres; +} +#endif /* CONFIG_SIG_MOTION */ + +static int bma2x2_get_orient_status(struct i2c_client *client, unsigned char + *intstatus) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG, + &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_S); + *intstatus = data; + + return comres; +} + +static int bma2x2_get_orient_flat_status(struct i2c_client *client, unsigned + char *intstatus) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG, + &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_FLAT_S); + *intstatus = data; + + return comres; +} +#endif /* defined(BMA2X2_ENABLE_INT1)||defined(BMA2X2_ENABLE_INT2) */ + +static int bma2x2_set_Int_Mode(struct i2c_client *client, unsigned char Mode) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_MODE_SEL__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_INT_MODE_SEL, Mode); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT_MODE_SEL__REG, &data); + + return comres; +} + +static int bma2x2_set_int1_active_lvl(struct i2c_client *client, + bool activeHigh) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT1_PAD_ACTIVE_LEVEL__REG, &data); + if (comres) + return comres; + + data = BMA2X2_SET_BITSLICE(data, BMA2X2_INT1_PAD_ACTIVE_LEVEL, + (unsigned char)(activeHigh ? 1 : 0)); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT1_PAD_ACTIVE_LEVEL__REG, &data); + + return comres; +} + +static int bma2x2_set_int2_active_lvl(struct i2c_client *client, + bool activeHigh) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT2_PAD_ACTIVE_LEVEL__REG, &data); + if (comres) + return comres; + + data = BMA2X2_SET_BITSLICE(data, BMA2X2_INT2_PAD_ACTIVE_LEVEL, + (unsigned char)(activeHigh ? 1 : 0)); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_INT2_PAD_ACTIVE_LEVEL__REG, &data); + + return comres; +} + +static int bma2x2_get_Int_Mode(struct i2c_client *client, unsigned char *Mode) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_INT_MODE_SEL__REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_INT_MODE_SEL); + *Mode = data; + + + return comres; +} +static int bma2x2_set_slope_duration(struct i2c_client *client, unsigned char + duration) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_SLOPE_DUR__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_SLOPE_DUR, duration); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_SLOPE_DUR__REG, &data); + + return comres; +} + +static int bma2x2_get_slope_duration(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_SLOPE_DURN_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_DUR); + *status = data; + + + return comres; +} + +static int bma2x2_set_slope_no_mot_duration(struct i2c_client *client, + unsigned char duration) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2x2_SLO_NO_MOT_DUR__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2x2_SLO_NO_MOT_DUR, duration); + comres = bma2x2_smbus_write_byte(client, + BMA2x2_SLO_NO_MOT_DUR__REG, &data); + + + return comres; +} + +static int bma2x2_get_slope_no_mot_duration(struct i2c_client *client, + unsigned char *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2x2_SLO_NO_MOT_DUR__REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2x2_SLO_NO_MOT_DUR); + *status = data; + + + return comres; +} + +static int bma2x2_set_slope_threshold(struct i2c_client *client, + unsigned char threshold) +{ + int comres = 0; + unsigned char data; + + data = threshold; + comres = bma2x2_smbus_write_byte(client, + BMA2X2_SLOPE_THRES__REG, &data); + + return comres; +} + +static int bma2x2_get_slope_threshold(struct i2c_client *client, + unsigned char *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_SLOPE_THRES_REG, &data); + *status = data; + + return comres; +} + +static int bma2x2_set_slope_no_mot_threshold(struct i2c_client *client, + unsigned char threshold) +{ + int comres = 0; + unsigned char data; + + data = threshold; + comres = bma2x2_smbus_write_byte(client, + BMA2X2_SLO_NO_MOT_THRES_REG, &data); + + return comres; +} + +static int bma2x2_get_slope_no_mot_threshold(struct i2c_client *client, + unsigned char *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_SLO_NO_MOT_THRES_REG, &data); + *status = data; + + return comres; +} + + +static int bma2x2_set_low_g_duration(struct i2c_client *client, unsigned char + duration) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_LOWG_DUR__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_LOWG_DUR, duration); + comres = bma2x2_smbus_write_byte(client, BMA2X2_LOWG_DUR__REG, &data); + + return comres; +} + +static int bma2x2_get_low_g_duration(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_DURN_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_LOWG_DUR); + *status = data; + + return comres; +} + +static int bma2x2_set_low_g_threshold(struct i2c_client *client, unsigned char + threshold) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_LOWG_THRES__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_LOWG_THRES, threshold); + comres = bma2x2_smbus_write_byte(client, BMA2X2_LOWG_THRES__REG, &data); + + return comres; +} + +static int bma2x2_get_low_g_threshold(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_THRES_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_LOWG_THRES); + *status = data; + + return comres; +} + +static int bma2x2_set_high_g_duration(struct i2c_client *client, unsigned char + duration) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_DUR__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_HIGHG_DUR, duration); + comres = bma2x2_smbus_write_byte(client, BMA2X2_HIGHG_DUR__REG, &data); + + return comres; +} + +static int bma2x2_get_high_g_duration(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGH_DURN_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_DUR); + *status = data; + + return comres; +} + +static int bma2x2_set_high_g_threshold(struct i2c_client *client, unsigned char + threshold) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_THRES__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_HIGHG_THRES, threshold); + comres = bma2x2_smbus_write_byte(client, BMA2X2_HIGHG_THRES__REG, + &data); + + return comres; +} + +static int bma2x2_get_high_g_threshold(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGH_THRES_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_THRES); + *status = data; + + return comres; +} + + +static int bma2x2_set_tap_duration(struct i2c_client *client, unsigned char + duration) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_DUR__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_DUR, duration); + comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_DUR__REG, &data); + + return comres; +} + +static int bma2x2_get_tap_duration(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_DUR); + *status = data; + + return comres; +} + +static int bma2x2_set_tap_shock(struct i2c_client *client, unsigned char setval) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_SHOCK_DURN__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_SHOCK_DURN, setval); + comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_SHOCK_DURN__REG, + &data); + + return comres; +} + +static int bma2x2_get_tap_shock(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_SHOCK_DURN); + *status = data; + + return comres; +} + +static int bma2x2_set_tap_quiet(struct i2c_client *client, unsigned char + duration) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_QUIET_DURN__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_QUIET_DURN, duration); + comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_QUIET_DURN__REG, + &data); + + return comres; +} + +static int bma2x2_get_tap_quiet(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_QUIET_DURN); + *status = data; + + return comres; +} + +static int bma2x2_set_tap_threshold(struct i2c_client *client, unsigned char + threshold) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_THRES, threshold); + comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_THRES__REG, &data); + + return comres; +} + +static int bma2x2_get_tap_threshold(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_THRES); + *status = data; + + return comres; +} + +static int bma2x2_set_tap_samp(struct i2c_client *client, unsigned char samp) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_SAMPLES__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_SAMPLES, samp); + comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_SAMPLES__REG, + &data); + + return comres; +} + +static int bma2x2_get_tap_samp(struct i2c_client *client, unsigned char *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_SAMPLES); + *status = data; + + return comres; +} + +static int bma2x2_set_orient_mode(struct i2c_client *client, unsigned char mode) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_MODE__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_MODE, mode); + comres = bma2x2_smbus_write_byte(client, BMA2X2_ORIENT_MODE__REG, + &data); + + return comres; +} + +static int bma2x2_get_orient_mode(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_MODE); + *status = data; + + return comres; +} + +static int bma2x2_set_orient_blocking(struct i2c_client *client, unsigned char + samp) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_BLOCK__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_BLOCK, samp); + comres = bma2x2_smbus_write_byte(client, BMA2X2_ORIENT_BLOCK__REG, + &data); + + return comres; +} + +static int bma2x2_get_orient_blocking(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_BLOCK); + *status = data; + + return comres; +} + +static int bma2x2_set_orient_hyst(struct i2c_client *client, unsigned char + orienthyst) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_HYST__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_HYST, orienthyst); + comres = bma2x2_smbus_write_byte(client, BMA2X2_ORIENT_HYST__REG, + &data); + + return comres; +} + +static int bma2x2_get_orient_hyst(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_HYST); + *status = data; + + return comres; +} +static int bma2x2_set_theta_blocking(struct i2c_client *client, unsigned char + thetablk) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_BLOCK__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_THETA_BLOCK, thetablk); + comres = bma2x2_smbus_write_byte(client, BMA2X2_THETA_BLOCK__REG, + &data); + + return comres; +} + +static int bma2x2_get_theta_blocking(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_BLOCK_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_THETA_BLOCK); + *status = data; + + return comres; +} + +static int bma2x2_set_theta_flat(struct i2c_client *client, unsigned char + thetaflat) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_FLAT__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_THETA_FLAT, thetaflat); + comres = bma2x2_smbus_write_byte(client, BMA2X2_THETA_FLAT__REG, &data); + + return comres; +} + +static int bma2x2_get_theta_flat(struct i2c_client *client, unsigned char + *status) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_FLAT_REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_THETA_FLAT); + *status = data; + + return comres; +} + +static int bma2x2_set_flat_hold_time(struct i2c_client *client, unsigned char + holdtime) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_FLAT_HOLD_TIME__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_FLAT_HOLD_TIME, holdtime); + comres = bma2x2_smbus_write_byte(client, BMA2X2_FLAT_HOLD_TIME__REG, + &data); + + return comres; +} + +static int bma2x2_get_flat_hold_time(struct i2c_client *client, unsigned char + *holdtime) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_FLAT_HOLD_TIME_REG, + &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_FLAT_HOLD_TIME); + *holdtime = data; + + return comres; +} + +/*! + * brief: bma2x2 switch from normal to suspend mode + * @param[i] bma2x2 + * @param[i] data1, write to PMU_LPW + * @param[i] data2, write to PMU_LOW_NOSIE + * + * @return zero success, none-zero failed + */ +static int bma2x2_normal_to_suspend(struct bma2x2_data *bma2x2, + unsigned char data1, unsigned char data2) +{ + unsigned char current_fifo_mode; + unsigned char current_op_mode; + + if (bma2x2 == NULL) + return -EINVAL; + /* get current op mode from mode register */ + if (bma2x2_get_mode(bma2x2->bma2x2_client, ¤t_op_mode) < 0) + return -EIO; + /* only aimed at operatiom mode chang from normal/lpw1 mode + * to suspend state. + */ + if (current_op_mode == BMA2X2_MODE_NORMAL || + current_op_mode == BMA2X2_MODE_LOWPOWER1) { + /* get current fifo mode from fifo config register */ + if (bma2x2_get_fifo_mode(bma2x2->bma2x2_client, + ¤t_fifo_mode) < 0) + return -EIO; + + bma2x2_smbus_write_byte(bma2x2->bma2x2_client, + BMA2X2_LOW_NOISE_CTRL_REG, &data2); + bma2x2_smbus_write_byte(bma2x2->bma2x2_client, + BMA2X2_MODE_CTRL_REG, &data1); + bma2x2_smbus_write_byte(bma2x2->bma2x2_client, + BMA2X2_FIFO_MODE__REG, ¤t_fifo_mode); + WAIT_DEVICE_READY(); + + return 0; + } + + bma2x2_smbus_write_byte(bma2x2->bma2x2_client, + BMA2X2_LOW_NOISE_CTRL_REG, &data2); + bma2x2_smbus_write_byte(bma2x2->bma2x2_client, + BMA2X2_MODE_CTRL_REG, &data1); + WAIT_DEVICE_READY(); + + return 0; +} + +static int bma2x2_set_mode(struct i2c_client *client, unsigned char mode) +{ + int comres = 0; + unsigned char data1, data2; + int ret = 0; + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + mutex_lock(&bma2x2->mode_mutex); + if (BMA2X2_MODE_SUSPEND == mode) { + if (bma2x2->ref_count > 0) { + bma2x2->ref_count--; + if (0 < bma2x2->ref_count) { + mutex_unlock(&bma2x2->mode_mutex); + return 0; + } + } + } else { + bma2x2->ref_count++; + if (1 < bma2x2->ref_count) { + mutex_unlock(&bma2x2->mode_mutex); + return 0; + } + } + mutex_unlock(&bma2x2->mode_mutex); + + if (mode < 6) { + comres = bma2x2_smbus_read_byte(client, BMA2X2_MODE_CTRL_REG, + &data1); + comres = bma2x2_smbus_read_byte(client, + BMA2X2_LOW_NOISE_CTRL_REG, + &data2); + switch (mode) { + case BMA2X2_MODE_NORMAL: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_MODE_CTRL, 0); + data2 = BMA2X2_SET_BITSLICE(data2, + BMA2X2_LOW_POWER_MODE, 0); + bma2x2_smbus_write_byte(client, + BMA2X2_MODE_CTRL_REG, &data1); + WAIT_DEVICE_READY(); + bma2x2_smbus_write_byte(client, + BMA2X2_LOW_NOISE_CTRL_REG, &data2); + break; + case BMA2X2_MODE_LOWPOWER1: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_MODE_CTRL, 2); + data2 = BMA2X2_SET_BITSLICE(data2, + BMA2X2_LOW_POWER_MODE, 0); + bma2x2_smbus_write_byte(client, + BMA2X2_MODE_CTRL_REG, &data1); + WAIT_DEVICE_READY(); + bma2x2_smbus_write_byte(client, + BMA2X2_LOW_NOISE_CTRL_REG, &data2); + break; + case BMA2X2_MODE_SUSPEND: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_MODE_CTRL, 4); + data2 = BMA2X2_SET_BITSLICE(data2, + BMA2X2_LOW_POWER_MODE, 0); + /*aimed at anomaly resolution when switch to suspend*/ + ret = bma2x2_normal_to_suspend(bma2x2, data1, data2); + if (ret < 0) + dev_err(&client->dev, + "Error switching to suspend"); + break; + case BMA2X2_MODE_DEEP_SUSPEND: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_MODE_CTRL, 1); + data2 = BMA2X2_SET_BITSLICE(data2, + BMA2X2_LOW_POWER_MODE, 1); + bma2x2_smbus_write_byte(client, + BMA2X2_MODE_CTRL_REG, &data1); + WAIT_DEVICE_READY(); + bma2x2_smbus_write_byte(client, + BMA2X2_LOW_NOISE_CTRL_REG, &data2); + break; + case BMA2X2_MODE_LOWPOWER2: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_MODE_CTRL, 2); + data2 = BMA2X2_SET_BITSLICE(data2, + BMA2X2_LOW_POWER_MODE, 1); + bma2x2_smbus_write_byte(client, + BMA2X2_MODE_CTRL_REG, &data1); + WAIT_DEVICE_READY(); + bma2x2_smbus_write_byte(client, + BMA2X2_LOW_NOISE_CTRL_REG, &data2); + break; + case BMA2X2_MODE_STANDBY: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_MODE_CTRL, 4); + data2 = BMA2X2_SET_BITSLICE(data2, + BMA2X2_LOW_POWER_MODE, 1); + bma2x2_smbus_write_byte(client, + BMA2X2_LOW_NOISE_CTRL_REG, &data2); + WAIT_DEVICE_READY(); + bma2x2_smbus_write_byte(client, + BMA2X2_MODE_CTRL_REG, &data1); + break; + } + } else { + comres = -1; + } + + return comres; +} + + +static int bma2x2_get_mode(struct i2c_client *client, unsigned char *mode) +{ + int comres = 0; + unsigned char data1, data2; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_MODE_CTRL_REG, &data1); + comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_NOISE_CTRL_REG, + &data2); + + data1 = (data1 & 0xE0) >> 5; + data2 = (data2 & 0x40) >> 6; + + if (data2 == 0x00) { + switch (data1) { + case 0: + *mode = BMA2X2_MODE_NORMAL; + break; + case 1: + *mode = BMA2X2_MODE_DEEP_SUSPEND; + break; + case 2: + *mode = BMA2X2_MODE_LOWPOWER1; + break; + case 4: + case 6: + *mode = BMA2X2_MODE_SUSPEND; + break; + default: + comres = -ENODEV; + break; + } + } else if (data2 == 0x01) { + switch (data1) { + case 0: + case 1: + case 6: + *mode = BMA2X2_MODE_DEEP_SUSPEND; + break; + case 2: + *mode = BMA2X2_MODE_LOWPOWER2; + break; + case 4: + *mode = BMA2X2_MODE_STANDBY; + break; + default: + comres = -ENODEV; + break; + } + } else { + comres = -ENODEV; + } + + return comres; +} + +static int bma2x2_set_range(struct i2c_client *client, unsigned char Range) +{ + int comres = 0; + unsigned char data1; + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if ((Range == 3) || (Range == 5) || (Range == 8) || (Range == 12)) { + comres = bma2x2_smbus_read_byte(client, BMA2X2_RANGE_SEL_REG, + &data1); + switch (Range) { + case BMA2X2_RANGE_2G: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_RANGE_SEL, 3); + break; + case BMA2X2_RANGE_4G: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_RANGE_SEL, 5); + break; + case BMA2X2_RANGE_8G: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_RANGE_SEL, 8); + break; + case BMA2X2_RANGE_16G: + data1 = BMA2X2_SET_BITSLICE(data1, + BMA2X2_RANGE_SEL, 12); + break; + default: + break; + } + comres += bma2x2_smbus_write_byte(client, BMA2X2_RANGE_SEL_REG, + &data1); + bma2x2_get_sensitivity(bma2x2, Range); + } else { + comres = -1; + } + + return comres; +} + +static int bma2x2_get_range(struct i2c_client *client, unsigned char *Range) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_RANGE_SEL__REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_RANGE_SEL); + *Range = data; + + return comres; +} + +static int bma2x2_set_watermark_lvl(struct i2c_client *client, + unsigned char watermark) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_WML_TRIG_RETAIN__REG, &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_FIFO_WML_TRIG_RETAIN, watermark); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_FIFO_WML_TRIG_RETAIN__REG, &data); + + return comres; +} + +static int bma2x2_set_bandwidth(struct i2c_client *client, unsigned char BW) +{ + int comres = 0; + unsigned char data; + int Bandwidth = 0; + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (BW > 7 && BW < 16) { + switch (BW) { + case BMA2X2_BW_7_81HZ: + Bandwidth = BMA2X2_BW_7_81HZ; + + /* 7.81 Hz 64000 uS */ + break; + case BMA2X2_BW_15_63HZ: + Bandwidth = BMA2X2_BW_15_63HZ; + + /* 15.63 Hz 32000 uS */ + break; + case BMA2X2_BW_31_25HZ: + Bandwidth = BMA2X2_BW_31_25HZ; + + /* 31.25 Hz 16000 uS */ + break; + case BMA2X2_BW_62_50HZ: + Bandwidth = BMA2X2_BW_62_50HZ; + + /* 62.50 Hz 8000 uS */ + break; + case BMA2X2_BW_125HZ: + Bandwidth = BMA2X2_BW_125HZ; + + /* 125 Hz 4000 uS */ + break; + case BMA2X2_BW_250HZ: + Bandwidth = BMA2X2_BW_250HZ; + + /* 250 Hz 2000 uS */ + break; + case BMA2X2_BW_500HZ: + Bandwidth = BMA2X2_BW_500HZ; + + /* 500 Hz 1000 uS */ + break; + case BMA2X2_BW_1000HZ: + Bandwidth = BMA2X2_BW_1000HZ; + + /* 1000 Hz 500 uS */ + break; + default: + break; + } + comres = bma2x2_smbus_read_byte(client, BMA2X2_BANDWIDTH__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_BANDWIDTH, Bandwidth); + comres += bma2x2_smbus_write_byte(client, BMA2X2_BANDWIDTH__REG, + &data); + if (comres == 0) + bma2x2->bandwidth = Bandwidth; + } else { + comres = -1; + } + + return comres; +} + +static int bma2x2_get_bandwidth(struct i2c_client *client, unsigned char *BW) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_BANDWIDTH__REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_BANDWIDTH); + *BW = data; + + return comres; +} + +int bma2x2_get_sleep_duration(struct i2c_client *client, unsigned char + *sleep_dur) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_SLEEP_DUR__REG, &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLEEP_DUR); + *sleep_dur = data; + + return comres; +} + +int bma2x2_set_sleep_duration(struct i2c_client *client, unsigned char + sleep_dur) +{ + int comres = 0; + unsigned char data; + int sleep_duration = 0; + + if (sleep_dur > 4 && sleep_dur < 16) { + switch (sleep_dur) { + case BMA2X2_SLEEP_DUR_0_5MS: + sleep_duration = BMA2X2_SLEEP_DUR_0_5MS; + + /* 0.5 MS */ + break; + case BMA2X2_SLEEP_DUR_1MS: + sleep_duration = BMA2X2_SLEEP_DUR_1MS; + + /* 1 MS */ + break; + case BMA2X2_SLEEP_DUR_2MS: + sleep_duration = BMA2X2_SLEEP_DUR_2MS; + + /* 2 MS */ + break; + case BMA2X2_SLEEP_DUR_4MS: + sleep_duration = BMA2X2_SLEEP_DUR_4MS; + + /* 4 MS */ + break; + case BMA2X2_SLEEP_DUR_6MS: + sleep_duration = BMA2X2_SLEEP_DUR_6MS; + + /* 6 MS */ + break; + case BMA2X2_SLEEP_DUR_10MS: + sleep_duration = BMA2X2_SLEEP_DUR_10MS; + + /* 10 MS */ + break; + case BMA2X2_SLEEP_DUR_25MS: + sleep_duration = BMA2X2_SLEEP_DUR_25MS; + + /* 25 MS */ + break; + case BMA2X2_SLEEP_DUR_50MS: + sleep_duration = BMA2X2_SLEEP_DUR_50MS; + + /* 50 MS */ + break; + case BMA2X2_SLEEP_DUR_100MS: + sleep_duration = BMA2X2_SLEEP_DUR_100MS; + + /* 100 MS */ + break; + case BMA2X2_SLEEP_DUR_500MS: + sleep_duration = BMA2X2_SLEEP_DUR_500MS; + + /* 500 MS */ + break; + case BMA2X2_SLEEP_DUR_1S: + sleep_duration = BMA2X2_SLEEP_DUR_1S; + + /* 1 SECS */ + break; + default: + break; + } + comres = bma2x2_smbus_read_byte(client, BMA2X2_SLEEP_DUR__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_SLEEP_DUR, + sleep_duration); + comres = bma2x2_smbus_write_byte(client, BMA2X2_SLEEP_DUR__REG, + &data); + } else { + comres = -1; + } + + return comres; +} + +static int bma2x2_get_fifo_mode(struct i2c_client *client, unsigned char + *fifo_mode) +{ + int comres; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_MODE__REG, &data); + *fifo_mode = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_MODE); + + return comres; +} + +static int bma2x2_set_fifo_mode(struct i2c_client *client, unsigned char + fifo_mode) +{ + unsigned char data; + int comres = 0; + + if (fifo_mode < 4) { + comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_MODE__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_MODE, fifo_mode); + comres = bma2x2_smbus_write_byte(client, BMA2X2_FIFO_MODE__REG, + &data); + } else { + comres = -1; + } + + return comres; +} + +static int bma2x2_get_fifo_trig(struct i2c_client *client, unsigned char + *fifo_trig) +{ + int comres; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_TRIGGER_ACTION__REG, &data); + *fifo_trig = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_ACTION); + + return comres; +} + +static int bma2x2_set_fifo_trig(struct i2c_client *client, unsigned char + fifo_trig) +{ + unsigned char data; + int comres = 0; + + if (fifo_trig < 4) { + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_TRIGGER_ACTION__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_ACTION, + fifo_trig); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_FIFO_TRIGGER_ACTION__REG, &data); + } else { + comres = -1; + } + + return comres; +} + +static int bma2x2_get_fifo_trig_src(struct i2c_client *client, unsigned char + *trig_src) +{ + int comres; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_TRIGGER_SOURCE__REG, &data); + *trig_src = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_SOURCE); + + return comres; +} + +static int bma2x2_set_fifo_trig_src(struct i2c_client *client, unsigned char + trig_src) +{ + unsigned char data; + int comres = 0; + + if (trig_src < 4) { + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_TRIGGER_SOURCE__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_SOURCE, + trig_src); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_FIFO_TRIGGER_SOURCE__REG, &data); + } else { + comres = -1; + } + + return comres; +} + +static int bma2x2_get_fifo_framecount(struct i2c_client *client, unsigned char + *framecount) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_FRAME_COUNTER_S__REG, &data); + *framecount = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_FRAME_COUNTER_S); + + return comres; +} + +static int bma2x2_get_fifo_data_sel(struct i2c_client *client, unsigned char + *data_sel) +{ + int comres; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_DATA_SELECT__REG, &data); + *data_sel = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_DATA_SELECT); + + return comres; +} + +static int bma2x2_set_fifo_data_sel(struct i2c_client *client, unsigned char + data_sel) +{ + unsigned char data; + int comres = 0; + + if (data_sel < 4) { + comres = bma2x2_smbus_read_byte(client, + BMA2X2_FIFO_DATA_SELECT__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_DATA_SELECT, + data_sel); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_FIFO_DATA_SELECT__REG, + &data); + } else { + comres = -1; + } + + return comres; +} + + +static int bma2x2_get_offset_target(struct i2c_client *client, unsigned char + channel, unsigned char *offset) +{ + unsigned char data; + int comres = 0; + + switch (channel) { + case BMA2X2_CUT_OFF: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_CUTOFF__REG, &data); + *offset = BMA2X2_GET_BITSLICE(data, BMA2X2_COMP_CUTOFF); + break; + case BMA2X2_OFFSET_TRIGGER_X: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_TARGET_OFFSET_X__REG, &data); + *offset = BMA2X2_GET_BITSLICE(data, + BMA2X2_COMP_TARGET_OFFSET_X); + break; + case BMA2X2_OFFSET_TRIGGER_Y: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_TARGET_OFFSET_Y__REG, &data); + *offset = BMA2X2_GET_BITSLICE(data, + BMA2X2_COMP_TARGET_OFFSET_Y); + break; + case BMA2X2_OFFSET_TRIGGER_Z: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_TARGET_OFFSET_Z__REG, &data); + *offset = BMA2X2_GET_BITSLICE(data, + BMA2X2_COMP_TARGET_OFFSET_Z); + break; + default: + comres = -1; + break; + } + + return comres; +} + +static int bma2x2_set_offset_target(struct i2c_client *client, unsigned char + channel, unsigned char offset) +{ + unsigned char data; + int comres = 0; + + switch (channel) { + case BMA2X2_CUT_OFF: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_CUTOFF__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_COMP_CUTOFF, + offset); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_COMP_CUTOFF__REG, &data); + break; + case BMA2X2_OFFSET_TRIGGER_X: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_TARGET_OFFSET_X__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_COMP_TARGET_OFFSET_X, + offset); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_COMP_TARGET_OFFSET_X__REG, + &data); + break; + case BMA2X2_OFFSET_TRIGGER_Y: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_TARGET_OFFSET_Y__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_COMP_TARGET_OFFSET_Y, + offset); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_COMP_TARGET_OFFSET_Y__REG, + &data); + break; + case BMA2X2_OFFSET_TRIGGER_Z: + comres = bma2x2_smbus_read_byte(client, + BMA2X2_COMP_TARGET_OFFSET_Z__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, + BMA2X2_COMP_TARGET_OFFSET_Z, + offset); + comres = bma2x2_smbus_write_byte(client, + BMA2X2_COMP_TARGET_OFFSET_Z__REG, + &data); + break; + default: + comres = -1; + break; + } + + return comres; +} + +static int bma2x2_get_cal_ready(struct i2c_client *client, + unsigned char *calrdy) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_FAST_CAL_RDY_S__REG, + &data); + data = BMA2X2_GET_BITSLICE(data, BMA2X2_FAST_CAL_RDY_S); + *calrdy = data; + + return comres; +} + +static int bma2x2_set_cal_trigger(struct i2c_client *client, unsigned char + caltrigger) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_CAL_TRIGGER__REG, &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_CAL_TRIGGER, caltrigger); + comres = bma2x2_smbus_write_byte(client, BMA2X2_CAL_TRIGGER__REG, + &data); + + return comres; +} + +static int bma2x2_write_reg(struct i2c_client *client, unsigned char addr, + unsigned char *data) +{ + int comres = 0; + + comres = bma2x2_smbus_write_byte(client, addr, data); + + return comres; +} + + +static int bma2x2_set_offset_x(struct i2c_client *client, unsigned char + offsetfilt) +{ + int comres = 0; + unsigned char data; + + data = offsetfilt; + +#ifdef CONFIG_SENSORS_BMI058 + comres = bma2x2_smbus_write_byte(client, BMI058_OFFSET_X_AXIS_REG, + &data); +#else + comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_X_AXIS_REG, + &data); +#endif + + return comres; +} + + +static int bma2x2_get_offset_x(struct i2c_client *client, unsigned char + *offsetfilt) +{ + int comres = 0; + unsigned char data; + +#ifdef CONFIG_SENSORS_BMI058 + comres = bma2x2_smbus_read_byte(client, BMI058_OFFSET_X_AXIS_REG, + &data); +#else + comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_X_AXIS_REG, + &data); +#endif + *offsetfilt = data; + + return comres; +} + +static int bma2x2_set_offset_y(struct i2c_client *client, unsigned char + offsetfilt) +{ + int comres = 0; + unsigned char data; + + data = offsetfilt; + +#ifdef CONFIG_SENSORS_BMI058 + comres = bma2x2_smbus_write_byte(client, BMI058_OFFSET_Y_AXIS_REG, + &data); +#else + comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_Y_AXIS_REG, + &data); +#endif + return comres; +} + +static int bma2x2_get_offset_y(struct i2c_client *client, unsigned char + *offsetfilt) +{ + int comres = 0; + unsigned char data; + +#ifdef CONFIG_SENSORS_BMI058 + comres = bma2x2_smbus_read_byte(client, BMI058_OFFSET_Y_AXIS_REG, + &data); +#else + comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_Y_AXIS_REG, + &data); +#endif + *offsetfilt = data; + + return comres; +} + +static int bma2x2_set_offset_z(struct i2c_client *client, unsigned char + offsetfilt) +{ + int comres = 0; + unsigned char data; + + data = offsetfilt; + comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_Z_AXIS_REG, + &data); + + return comres; +} + +static int bma2x2_get_offset_z(struct i2c_client *client, unsigned char + *offsetfilt) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_Z_AXIS_REG, + &data); + *offsetfilt = data; + + return comres; +} + + +static int bma2x2_set_selftest_st(struct i2c_client *client, unsigned char + selftest) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_EN_SELF_TEST__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_SELF_TEST, selftest); + comres = bma2x2_smbus_write_byte(client, BMA2X2_EN_SELF_TEST__REG, + &data); + + return comres; +} + +static int bma2x2_set_selftest_stn(struct i2c_client *client, unsigned char stn) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_NEG_SELF_TEST__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_NEG_SELF_TEST, stn); + comres = bma2x2_smbus_write_byte(client, BMA2X2_NEG_SELF_TEST__REG, + &data); + + return comres; +} + +static int bma2x2_set_selftest_amp(struct i2c_client *client, unsigned char amp) +{ + int comres = 0; + unsigned char data; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_SELF_TEST_AMP__REG, + &data); + data = BMA2X2_SET_BITSLICE(data, BMA2X2_SELF_TEST_AMP, amp); + comres = bma2x2_smbus_write_byte(client, BMA2X2_SELF_TEST_AMP__REG, + &data); + + return comres; +} + +static int bma2x2_read_accel_x(struct i2c_client *client, + signed char sensor_type, short *a_x) +{ + int comres = 0; + unsigned char data[2]; + + switch (sensor_type) { + case 0: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_X12_LSB__REG, data, 2); + *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X12_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X12_LSB__LEN)); + *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X12_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X12_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + break; + case 1: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_X10_LSB__REG, data, 2); + *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X10_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X10_LSB__LEN)); + *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X10_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X10_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + break; + case 2: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_X8_LSB__REG, data, 2); + *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X8_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X8_LSB__LEN)); + *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X8_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X8_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + break; + case 3: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_X14_LSB__REG, data, 2); + *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X14_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X14_LSB__LEN)); + *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X14_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X14_LSB__LEN + + BMA2X2_ACC_X_MSB__LEN)); + break; + default: + break; + } + + return comres; +} + +static int bma2x2_soft_reset(struct i2c_client *client) +{ + int comres = 0; + unsigned char data = BMA2X2_EN_SOFT_RESET_VALUE; + + comres = bma2x2_smbus_write_byte(client, BMA2X2_EN_SOFT_RESET__REG, + &data); + + return comres; +} + +static int bma2x2_read_accel_y(struct i2c_client *client, + signed char sensor_type, short *a_y) +{ + int comres = 0; + unsigned char data[2]; + + switch (sensor_type) { + case 0: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Y12_LSB__REG, data, 2); + *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y12_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y12_LSB__LEN)); + *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y12_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y12_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + break; + case 1: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Y10_LSB__REG, data, 2); + *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y10_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y10_LSB__LEN)); + *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y10_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y10_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + break; + case 2: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Y8_LSB__REG, data, 2); + *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y8_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y8_LSB__LEN)); + *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y8_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y8_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + break; + case 3: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Y14_LSB__REG, data, 2); + *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y14_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y14_LSB__LEN)); + *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y14_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y14_LSB__LEN + + BMA2X2_ACC_Y_MSB__LEN)); + break; + default: + break; + } + + return comres; +} + +static int bma2x2_read_accel_z(struct i2c_client *client, + signed char sensor_type, short *a_z) +{ + int comres = 0; + unsigned char data[2]; + + switch (sensor_type) { + case 0: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Z12_LSB__REG, data, 2); + *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z12_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z12_LSB__LEN)); + *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z12_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z12_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + break; + case 1: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Z10_LSB__REG, data, 2); + *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z10_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z10_LSB__LEN)); + *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z10_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z10_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + break; + case 2: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Z8_LSB__REG, data, 2); + *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z8_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z8_LSB__LEN)); + *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z8_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z8_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + break; + case 3: + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_Z14_LSB__REG, data, 2); + *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z14_LSB)| + (BMA2X2_GET_BITSLICE(data[1], + BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z14_LSB__LEN)); + *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z14_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z14_LSB__LEN + + BMA2X2_ACC_Z_MSB__LEN)); + break; + default: + break; + } + + return comres; +} + + +static int bma2x2_read_temperature(struct i2c_client *client, + signed char *temperature) +{ + unsigned char data; + int comres = 0; + + comres = bma2x2_smbus_read_byte(client, BMA2X2_TEMPERATURE_REG, &data); + *temperature = (signed char)data; + + return comres; +} + +static ssize_t bma2x2_enable_int_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int type, value; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); +#ifdef CONFIG_SENSORS_BMI058 + int i; +#endif + + if (sscanf(buf, "%3d %3d", &type, &value) != 2) + return -EINVAL; + +#ifdef CONFIG_SENSORS_BMI058 + for (i = 0; i < sizeof(int_map) / sizeof(struct interrupt_map_t); i++) { + if (int_map[i].x == type) { + type = int_map[i].y; + break; + } + if (int_map[i].y == type) { + type = int_map[i].x; + break; + } + } +#endif + + if (bma2x2_set_Int_Enable(bma2x2->bma2x2_client, type, value) < 0) + return -EINVAL; + + return count; +} + +static int bma2x2_update_bandwidth(const struct bma2x2_data *bma2x2) +{ + int err = 0; + int i; + int delay = atomic_read(&bma2x2->delay); + + for (i = ARRAY_SIZE(bma2x2_delay2bw_table) - 1; i > 0; i--) { + if (bma2x2_delay2bw_table[i].delay_ms <= delay) + break; + } + + err = bma2x2_set_bandwidth(bma2x2->bma2x2_client, + bma2x2_delay2bw_table[i].bw_config); + if (err) + dev_err(&bma2x2->bma2x2_client->dev, + "Update bandwidth not success,delay=%d err=%d\n", + delay, err); + + dev_dbg(&bma2x2->bma2x2_client->dev, + "Update bandwidth success,delay=%d config=%u\n", + delay, bma2x2_delay2bw_table[i].bw_config); + return err; +} + +static int bma2x2_update_delay(struct bma2x2_data *bma2x2, unsigned int delay) +{ + int pre_enable = atomic_read(&bma2x2->enable); + int err = 0; + ktime_t ktime; + + atomic_set(&bma2x2->delay, delay); + + if (!pre_enable) + return 0; + + /* + * Flush fifo data as ODR is about to change. + * Data acquisition and fifo buffering is not disabled during this + * configuration changing process, timestamp of sensor + * event my not correct. Set sensor to standby state during + * configuration update if accurate timestamp is required. + */ + if (atomic_read(&bma2x2->fifo_enabled)) { + bma2x2_flush_fifo(bma2x2); + err = bma2x2_update_bandwidth(bma2x2); + } else if (bma2x2_use_data_polling(bma2x2)) { + if (!bma2x2->pdata->use_hrtimer) { + if (cancel_delayed_work_sync(&bma2x2->work)) + queue_delayed_work(bma2x2->data_wq, + &bma2x2->work, + msecs_to_jiffies(delay)); + } else { + hrtimer_cancel(&bma2x2->accel_timer); + ktime = ktime_set(0, + atomic_read(&bma2x2->delay) * NSEC_PER_MSEC); + hrtimer_start(&bma2x2->accel_timer, + ktime, + HRTIMER_MODE_REL); + } + } else if (bma2x2->pdata->int_en && BMA2x2_IS_NEWDATA_INT_ENABLED()) { + err = bma2x2_update_bandwidth(bma2x2); + } else { + dev_err(&bma2x2->bma2x2_client->dev, + "Incorrect state! enable=%d, fifoEnable=%d, delay=%d, latency=%d\n", + pre_enable, atomic_read(&bma2x2->fifo_enabled), + delay, bma2x2->max_latency_ms); + } + return err; +} + +#if defined(BMA2X2_ENABLE_INT1) +static int bma2x2_sel_int1_pad(const struct bma2x2_data *data) +{ + struct i2c_client *client = data->bma2x2_client; + int err = 0; + + /* maps interrupt to INT1 pin */ + err |= bma2x2_set_int1_pad_sel(client, PAD_LOWG); + err |= bma2x2_set_int1_pad_sel(client, PAD_HIGHG); + err |= bma2x2_set_int1_pad_sel(client, PAD_SLOP); + err |= bma2x2_set_int1_pad_sel(client, PAD_DOUBLE_TAP); + err |= bma2x2_set_int1_pad_sel(client, PAD_SINGLE_TAP); + err |= bma2x2_set_int1_pad_sel(client, PAD_ORIENT); + err |= bma2x2_set_int1_pad_sel(client, PAD_FLAT); + err |= bma2x2_set_int1_pad_sel(client, PAD_SLOW_NO_MOTION); + err |= bma2x2_set_newdata(client, BMA2X2_INT1_NDATA, 1); + err |= bma2x2_set_newdata(client, BMA2X2_INT2_NDATA, 0); + err |= bma2x2_set_fwm_int_pad_sel(client, BMA2X2_INT1_FWM, 1); + err |= bma2x2_set_fwm_int_pad_sel(client, BMA2X2_INT2_FWM, 0); + err |= bma2x2_set_ffull_int_pad_sel(client, BMA2X2_INT1_FFULL, 1); + err |= bma2x2_set_ffull_int_pad_sel(client, BMA2X2_INT2_FFULL, 0); + + if (err) { + dev_err(&client->dev, "select pad int1 error, ret=%d\n", err); + err = -EIO; + } + return err; +} +#else +static int bma2x2_sel_int1_pad(const struct bma2x2_data *data) +{ + return -EPERM; +} +#endif /* BMA2X2_ENABLE_INT1 */ + +#if defined(BMA2X2_ENABLE_INT2) +static int bma2x2_sel_int2_pad(const struct bma2x2_data *data) +{ + struct i2c_client *client = data->bma2x2_client; + int err = 0; + + /* maps interrupt to INT2 pin */ + err |= bma2x2_set_int2_pad_sel(client, PAD_LOWG); + err |= bma2x2_set_int2_pad_sel(client, PAD_HIGHG); + err |= bma2x2_set_int2_pad_sel(client, PAD_SLOP); + err |= bma2x2_set_int2_pad_sel(client, PAD_DOUBLE_TAP); + err |= bma2x2_set_int2_pad_sel(client, PAD_SINGLE_TAP); + err |= bma2x2_set_int2_pad_sel(client, PAD_ORIENT); + err |= bma2x2_set_int2_pad_sel(client, PAD_FLAT); + err |= bma2x2_set_int2_pad_sel(client, PAD_SLOW_NO_MOTION); + err |= bma2x2_set_newdata(client, BMA2X2_INT1_NDATA, 0); + err |= bma2x2_set_newdata(client, BMA2X2_INT2_NDATA, 1); + err |= bma2x2_set_fwm_int_pad_sel(client, BMA2X2_INT1_FWM, 0); + err |= bma2x2_set_fwm_int_pad_sel(client, BMA2X2_INT2_FWM, 1); + err |= bma2x2_set_ffull_int_pad_sel(client, BMA2X2_INT1_FFULL, 0); + err |= bma2x2_set_ffull_int_pad_sel(client, BMA2X2_INT2_FFULL, 1); + + if (err) { + dev_err(&client->dev, "select pad int1 error, ret=%d\n", err); + err = -EIO; + } + return err; +} +#else +static int bma2x2_sel_int2_pad(const struct bma2x2_data *data) +{ + return -EPERM; +} +#endif /* BMA2X2_ENABLE_INT2 */ + +static ssize_t bma2x2_int_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_Int_Mode(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); +} + +static ssize_t bma2x2_int_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_Int_Mode(bma2x2->bma2x2_client, (unsigned char)data) < 0) + return -EINVAL; + + return count; +} +static ssize_t bma2x2_slope_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_slope_duration(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_slope_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_slope_duration(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_slope_no_mot_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_slope_no_mot_duration(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_slope_no_mot_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_slope_no_mot_duration(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + + +static ssize_t bma2x2_slope_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_slope_threshold(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_slope_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_slope_threshold(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_slope_no_mot_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_slope_no_mot_threshold(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_slope_no_mot_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_slope_no_mot_threshold(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_high_g_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_high_g_duration(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_high_g_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_high_g_duration(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_high_g_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_high_g_threshold(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_high_g_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_high_g_threshold(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_low_g_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_low_g_duration(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_low_g_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_low_g_duration(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_low_g_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_low_g_threshold(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_low_g_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_low_g_threshold(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} +static ssize_t bma2x2_tap_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_tap_threshold(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_tap_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_tap_threshold(bma2x2->bma2x2_client, (unsigned char)data) + < 0) + return -EINVAL; + + return count; +} +static ssize_t bma2x2_tap_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_tap_duration(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_tap_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_tap_duration(bma2x2->bma2x2_client, (unsigned char)data) + < 0) + return -EINVAL; + + return count; +} +static ssize_t bma2x2_tap_quiet_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_tap_quiet(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_tap_quiet_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_tap_quiet(bma2x2->bma2x2_client, (unsigned char)data) < + 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_tap_shock_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_tap_shock(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_tap_shock_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_tap_shock(bma2x2->bma2x2_client, (unsigned char)data) < + 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_tap_samp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_tap_samp(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_tap_samp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_tap_samp(bma2x2->bma2x2_client, (unsigned char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_orient_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_orient_mode(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_orient_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_orient_mode(bma2x2->bma2x2_client, (unsigned char)data) < + 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_orient_blocking_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_orient_blocking(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_orient_blocking_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_orient_blocking(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} +static ssize_t bma2x2_orient_hyst_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_orient_hyst(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_orient_hyst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_orient_hyst(bma2x2->bma2x2_client, (unsigned char)data) < + 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_orient_theta_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_theta_blocking(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_orient_theta_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_theta_blocking(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_flat_theta_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_theta_flat(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_flat_theta_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_theta_flat(bma2x2->bma2x2_client, (unsigned char)data) < + 0) + return -EINVAL; + + return count; +} +static ssize_t bma2x2_flat_hold_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_flat_hold_time(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} +static ssize_t bma2x2_selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&bma2x2->selftest_result)); + +} + +static ssize_t bma2x2_softreset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_soft_reset(bma2x2->bma2x2_client) < 0) + return -EINVAL; + + return count; +} +static ssize_t bma2x2_selftest_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + unsigned long data; + unsigned char clear_value = 0; + int error; + short value1 = 0; + short value2 = 0; + short diff = 0; + unsigned long result = 0; + unsigned char test_result_branch = 0; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + bma2x2_soft_reset(bma2x2->bma2x2_client); + RESET_DELAY(); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (data != 1) + return -EINVAL; + + bma2x2_write_reg(bma2x2->bma2x2_client, 0x32, &clear_value); + + if ((bma2x2->sensor_type == BMA280_TYPE) || + (bma2x2->sensor_type == BMA255_TYPE)) { +#ifdef CONFIG_SENSORS_BMI058 + /*set self test amp */ + if (bma2x2_set_selftest_amp(bma2x2->bma2x2_client, 1) < 0) + return -EINVAL; + /* set to 8 G range */ + if (bma2x2_set_range(bma2x2->bma2x2_client, + BMA2X2_RANGE_8G) < 0) + return -EINVAL; +#else + /* set to 4 G range */ + if (bma2x2_set_range(bma2x2->bma2x2_client, + BMA2X2_RANGE_4G) < 0) + return -EINVAL; +#endif + } + + if ((bma2x2->sensor_type == BMA250E_TYPE) || + (bma2x2->sensor_type == BMA222E_TYPE)) { + /* set to 8 G range */ + if (bma2x2_set_range(bma2x2->bma2x2_client, 8) < 0) + return -EINVAL; + if (bma2x2_set_selftest_amp(bma2x2->bma2x2_client, 1) < 0) + return -EINVAL; + } + + /* 1 for x-axis(but BMI058 is 1 for y-axis )*/ + bma2x2_set_selftest_st(bma2x2->bma2x2_client, 1); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); + SELF_TEST_DELAY(); + bma2x2_read_accel_x(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value1); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); + SELF_TEST_DELAY(); + bma2x2_read_accel_x(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value2); + diff = value1-value2; + +#ifdef CONFIG_SENSORS_BMI058 + dev_dbg(dev, "diff y is %d,value1 is %d, value2 is %d\n", diff, + value1, value2); + test_result_branch = 2; +#else + dev_dbg(dev, "diff x is %d,value1 is %d, value2 is %d\n", diff, + value1, value2); + test_result_branch = 1; +#endif + + if (bma2x2->sensor_type == BMA280_TYPE) { +#ifdef CONFIG_SENSORS_BMI058 + if (abs(diff) < 819) + result |= test_result_branch; +#else + if (abs(diff) < 1638) + result |= test_result_branch; +#endif + } + if (bma2x2->sensor_type == BMA255_TYPE) { + if (abs(diff) < 409) + result |= 1; + } + if (bma2x2->sensor_type == BMA250E_TYPE) { + if (abs(diff) < 51) + result |= 1; + } + if (bma2x2->sensor_type == BMA222E_TYPE) { + if (abs(diff) < 12) + result |= 1; + } + + /* 2 for y-axis but BMI058 is 1*/ + bma2x2_set_selftest_st(bma2x2->bma2x2_client, 2); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); + SELF_TEST_DELAY(); + bma2x2_read_accel_y(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value1); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); + SELF_TEST_DELAY(); + bma2x2_read_accel_y(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value2); + diff = value1-value2; + +#ifdef CONFIG_SENSORS_BMI058 + dev_dbg(dev, "diff x is %d,value1 is %d, value2 is %d\n", diff, + value1, value2); + test_result_branch = 1; +#else + dev_dbg(dev, "diff y is %d,value1 is %d, value2 is %d\n", diff, + value1, value2); + test_result_branch = 2; +#endif + + if (bma2x2->sensor_type == BMA280_TYPE) { +#ifdef CONFIG_SENSORS_BMI058 + if (abs(diff) < 819) + result |= test_result_branch; +#else + if (abs(diff) < 1638) + result |= test_result_branch; +#endif + } + if (bma2x2->sensor_type == BMA255_TYPE) { + if (abs(diff) < 409) + result |= test_result_branch; + } + if (bma2x2->sensor_type == BMA250E_TYPE) { + if (abs(diff) < 51) + result |= test_result_branch; + } + if (bma2x2->sensor_type == BMA222E_TYPE) { + if (abs(diff) < 12) + result |= test_result_branch; + } + + + bma2x2_set_selftest_st(bma2x2->bma2x2_client, 3); /* 3 for z-axis*/ + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); + SELF_TEST_DELAY(); + bma2x2_read_accel_z(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value1); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); + SELF_TEST_DELAY(); + bma2x2_read_accel_z(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value2); + diff = value1-value2; + + dev_dbg(dev, "diff z is %d,value1 is %d, value2 is %d\n", diff, + value1, value2); + + if (bma2x2->sensor_type == BMA280_TYPE) { +#ifdef CONFIG_SENSORS_BMI058 + if (abs(diff) < 409) + result |= 4; +#else + if (abs(diff) < 819) + result |= 4; +#endif + } + if (bma2x2->sensor_type == BMA255_TYPE) { + if (abs(diff) < 204) + result |= 4; + } + if (bma2x2->sensor_type == BMA250E_TYPE) { + if (abs(diff) < 25) + result |= 4; + } + if (bma2x2->sensor_type == BMA222E_TYPE) { + if (abs(diff) < 6) + result |= 4; + } + + /* self test for bma254 */ + if ((bma2x2->sensor_type == BMA255_TYPE) && (result > 0)) { + result = 0; + bma2x2_soft_reset(bma2x2->bma2x2_client); + RESET_DELAY(); + bma2x2_write_reg(bma2x2->bma2x2_client, 0x32, &clear_value); + /* set to 8 G range */ + if (bma2x2_set_range(bma2x2->bma2x2_client, 8) < 0) + return -EINVAL; + if (bma2x2_set_selftest_amp(bma2x2->bma2x2_client, 1) < 0) + return -EINVAL; + + bma2x2_set_selftest_st(bma2x2->bma2x2_client, 1); /* 1 + for x-axis*/ + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); /* + positive direction*/ + SELF_TEST_DELAY(); + bma2x2_read_accel_x(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value1); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); /* + negative direction*/ + SELF_TEST_DELAY(); + bma2x2_read_accel_x(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value2); + diff = value1-value2; + + dev_dbg(dev, "diff x is %d,value1 is %d, value2 is %d\n", + diff, value1, value2); + if (abs(diff) < 204) + result |= 1; + + bma2x2_set_selftest_st(bma2x2->bma2x2_client, 2); /* 2 + for y-axis*/ + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); /* + positive direction*/ + SELF_TEST_DELAY(); + bma2x2_read_accel_y(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value1); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); /* + negative direction*/ + SELF_TEST_DELAY(); + bma2x2_read_accel_y(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value2); + diff = value1-value2; + dev_dbg(dev, "diff y is %d,value1 is %d, value2 is %d\n", + diff, value1, value2); + + if (abs(diff) < 204) + result |= 2; + + bma2x2_set_selftest_st(bma2x2->bma2x2_client, 3); /* 3 + for z-axis*/ + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); /* + positive direction*/ + SELF_TEST_DELAY(); + bma2x2_read_accel_z(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value1); + bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); /* + negative direction*/ + SELF_TEST_DELAY(); + bma2x2_read_accel_z(bma2x2->bma2x2_client, + bma2x2->sensor_type, &value2); + diff = value1-value2; + + dev_dbg(dev, "diff z is %d,value1 is %d, value2 is %d\n", + diff, value1, value2); + if (abs(diff) < 102) + result |= 4; + } + + atomic_set(&bma2x2->selftest_result, (unsigned int)result); + + bma2x2_soft_reset(bma2x2->bma2x2_client); + RESET_DELAY(); + dev_dbg(dev, "self test finished\n"); + + return count; +} + + + +static ssize_t bma2x2_flat_hold_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_flat_hold_time(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +const int bma2x2_sensor_bitwidth[] = { + 12, 10, 8, 14 +}; + +static int bma2x2_get_sensitivity(struct bma2x2_data *bma2x2, int range) +{ + + switch (range) { + case BMA2X2_RANGE_2G: + bma2x2->sensitivity = bosch_sensor_range_map[0]; + break; + case BMA2X2_RANGE_4G: + bma2x2->sensitivity = bosch_sensor_range_map[1]; + break; + case BMA2X2_RANGE_8G: + bma2x2->sensitivity = bosch_sensor_range_map[2]; + break; + case BMA2X2_RANGE_16G: + bma2x2->sensitivity = bosch_sensor_range_map[3]; + break; + default: + bma2x2->sensitivity = bosch_sensor_range_map[0]; + break; + } + return 0; +} + +static int bma2x2_read_accel_xyz(struct i2c_client *client, + signed char sensor_type, struct bma2x2acc *acc) +{ + int comres = 0; + unsigned char data[6]; + struct bma2x2_data *client_data = i2c_get_clientdata(client); + int bitwidth; + + comres = bma2x2_smbus_read_byte_block(client, + BMA2X2_ACC_X12_LSB__REG, data, 6); + if (sensor_type >= 4) + return -EINVAL; + + acc->x = (data[1]<<8)|data[0]; + acc->y = (data[3]<<8)|data[2]; + acc->z = (data[5]<<8)|data[4]; + + bitwidth = bma2x2_sensor_bitwidth[sensor_type]; + BMA2X2_SHIFT_BITWIDTH(acc->x, bitwidth); + BMA2X2_SHIFT_BITWIDTH(acc->y, bitwidth); + BMA2X2_SHIFT_BITWIDTH(acc->z, bitwidth); + + bma2x2_remap_sensor_data(acc, client_data); + return comres; +} + +static void bma2x2_report_axis_data(struct bma2x2_data *bma2x2, + struct bma2x2acc *value) +{ + ktime_t ts; + int err; + + ts = ktime_get_boottime(); + err = bma2x2_read_accel_xyz(bma2x2->bma2x2_client, + bma2x2->sensor_type, value); + if (err < 0) { + dev_err(&bma2x2->bma2x2_client->dev, + "read accel data failed! err = %d\n", err); + return; + } + input_report_abs(bma2x2->input, ABS_X, + (int)value->x << bma2x2->sensitivity); + input_report_abs(bma2x2->input, ABS_Y, + (int)value->y << bma2x2->sensitivity); + input_report_abs(bma2x2->input, ABS_Z, + (int)value->z << bma2x2->sensitivity); + input_event(bma2x2->input, EV_SYN, SYN_TIME_SEC, + ktime_to_timespec(ts).tv_sec); + input_event(bma2x2->input, EV_SYN, SYN_TIME_NSEC, + ktime_to_timespec(ts).tv_nsec); + input_sync(bma2x2->input); +} + +static void bma2x2_work_func(struct work_struct *work) +{ + struct bma2x2_data *bma2x2 = container_of((struct delayed_work *)work, + struct bma2x2_data, work); + struct bma2x2acc value; + unsigned long delay = msecs_to_jiffies(atomic_read(&bma2x2->delay)); + + bma2x2_report_axis_data(bma2x2, &value); + mutex_lock(&bma2x2->value_mutex); + bma2x2->value = value; + mutex_unlock(&bma2x2->value_mutex); + queue_delayed_work(bma2x2->data_wq, &bma2x2->work, delay); +} + +static enum hrtimer_restart accel_timer_handle(struct hrtimer *hrtimer) +{ + struct bma2x2_data *bma2x2; + ktime_t ktime; + + bma2x2 = container_of(hrtimer, struct bma2x2_data, accel_timer); + ktime = ktime_set(0, atomic_read(&bma2x2->delay) * NSEC_PER_MSEC); + hrtimer_forward_now(&bma2x2->accel_timer, ktime); + bma2x2->accel_wkp_flag = 1; + wake_up_interruptible(&bma2x2->accel_wq); + return HRTIMER_RESTART; +} + +static int accel_poll_thread(void *data) +{ + struct bma2x2_data *bma2x2 = data; + struct bma2x2acc value; + + while (1) { + wait_event_interruptible(bma2x2->accel_wq, + ((bma2x2->accel_wkp_flag != 0) || + kthread_should_stop())); + bma2x2->accel_wkp_flag = 0; + if (kthread_should_stop()) + break; + + mutex_lock(&bma2x2->op_lock); + if (bma2x2->accel_delay_change) { + if (atomic_read(&bma2x2->delay) <= POLL_MS_100HZ) + set_wake_up_idle(true); + else + set_wake_up_idle(false); + bma2x2->accel_delay_change = false; + } + mutex_unlock(&bma2x2->op_lock); + + bma2x2_report_axis_data(bma2x2, &value); + mutex_lock(&bma2x2->value_mutex); + bma2x2->value = value; + mutex_unlock(&bma2x2->value_mutex); + } + + return 0; +} + +static ssize_t bma2x2_register_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int address, value; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (sscanf(buf, "%3d %3d", &address, &value) != 2) + return -EINVAL; + + if (bma2x2_write_reg(bma2x2->bma2x2_client, (unsigned char)address, + (unsigned char *)&value) < 0) + return -EINVAL; + return count; +} +static ssize_t bma2x2_register_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + size_t count = 0; + u8 reg[0x40]; + int i; + + for (i = 0; i < 0x40; i++) { + bma2x2_smbus_read_byte(bma2x2->bma2x2_client, i, reg+i); + + count += snprintf(&buf[count], PAGE_SIZE, + "0x%x: 0x%x\n", i, reg[i]); + } + return count; + + +} + +static ssize_t bma2x2_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_range(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); +} + +static ssize_t bma2x2_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_range(bma2x2->bma2x2_client, (unsigned char) data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_bandwidth_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_bandwidth(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_bandwidth_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2->sensor_type == BMA280_TYPE) + if ((unsigned char) data > 14) + return -EINVAL; + + if (bma2x2_set_bandwidth(bma2x2->bma2x2_client, + (unsigned char) data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_mode(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d %d\n", data, bma2x2->ref_count); +} + +static ssize_t bma2x2_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_mode(bma2x2->bma2x2_client, (unsigned char) data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_value_cache_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct bma2x2_data *bma2x2 = input_get_drvdata(input); + struct bma2x2acc acc_value; + + mutex_lock(&bma2x2->value_mutex); + acc_value = bma2x2->value; + mutex_unlock(&bma2x2->value_mutex); + + return snprintf(buf, PAGE_SIZE, "%d %d %d\n", acc_value.x, acc_value.y, + acc_value.z); +} + +static ssize_t bma2x2_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct bma2x2_data *bma2x2 = input_get_drvdata(input); + struct bma2x2acc acc_value; + + bma2x2_read_accel_xyz(bma2x2->bma2x2_client, bma2x2->sensor_type, + &acc_value); + + return snprintf(buf, PAGE_SIZE, "%d %d %d\n", acc_value.x, acc_value.y, + acc_value.z); +} + +static ssize_t bma2x2_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&bma2x2->delay)); + +} + +static ssize_t bma2x2_chip_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", bma2x2->chip_id); + +} + + +static ssize_t bma2x2_place_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + int place = BOSCH_SENSOR_PLACE_UNKNOWN; + + place = bma2x2->pdata->place; + + return snprintf(buf, PAGE_SIZE, "%d\n", place); +} + + +static ssize_t bma2x2_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + data = clamp_val(data, POLL_INTERVAL_MIN_MS, POLL_INTERVAL_MAX_MS); + + error = bma2x2_update_delay(bma2x2, (unsigned int)data); + if (error) + return -EBUSY; + else + return count; +} + + +static ssize_t bma2x2_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&bma2x2->enable)); + +} + +static int bma2x2_config_interrupt(struct bma2x2_data *data, int enable) +{ + struct i2c_client *client = data->bma2x2_client; + int err = 0; + bool act_high; + + if (!enable) + /* No need reset these interrupt configurations */ + goto exit; + + if ((data->int_flag & IRQF_TRIGGER_RISING) || + (data->int_flag & IRQF_TRIGGER_HIGH)) + act_high = true; + else + act_high = false; + + if (data->pdata->use_int2) { + err = bma2x2_sel_int2_pad(data); + if (err) { + dev_err(&client->dev, + "Failed to select int2 pad, err=%d\n", + err); + goto exit; + } + + err = bma2x2_set_int2_active_lvl(client, act_high); + if (err) { + dev_err(&client->dev, + "Failed to select int2 level, err=%d\n", + err); + goto exit; + } + } else { + err = bma2x2_sel_int1_pad(data); + if (err) { + dev_err(&client->dev, + "Failed to select int1 pad, err=%d\n", + err); + goto exit; + } + err = bma2x2_set_int1_active_lvl(client, act_high); + if (err) { + dev_err(&client->dev, + "Failed to select int2 level, err=%d\n", + err); + goto exit; + } + } + + err = bma2x2_set_Int_Mode(client, BMA2X2_LATCH_DUR_NON_LATCH); + if (err) { + dev_err(&client->dev, + "Failed to set interrupt latch, err=%d\n", + err); + goto exit; + } + +exit: + return err; +} + +static unsigned int bma2x2_bandwidth_to_interval(struct bma2x2_data *bma2x2) +{ + unsigned int i; + + for (i = ARRAY_SIZE(bma2x2_delay2bw_table) - 1; i > 0; i--) { + if (bma2x2_delay2bw_table[i].bw_config == bma2x2->bandwidth) + break; + } + + return bma2x2_delay2bw_table[i].delay_ms; +} + +static int bma2x2_set_fifo_enable(struct bma2x2_data *bma2x2, bool enable) +{ + struct i2c_client *client = bma2x2->bma2x2_client; + unsigned int interval, wml; + unsigned int latency = bma2x2->max_latency_ms; + int delay = atomic_read(&bma2x2->delay); + int fifo_en = atomic_read(&bma2x2->fifo_enabled); + int err = 0; + + dev_dbg(&client->dev, + "bma2x2_set_fifo_enable latency=%d,delay=%d,enable=%d,fifo_en=%d\n", + latency, delay, enable, fifo_en); + if (enable && !fifo_en) { + if ((latency == 0) || (latency < delay)) { + dev_err(&client->dev, + "Invalid parameter! latency=%d delay=%d\n", + latency, delay); + err = -EINVAL; + goto exit; + } + if (IS_ERR_OR_NULL(bma2x2->fifo_buf)) { + dev_err(&client->dev, + "Not enough memory for sensor FIFO\n"); + err = -ENOMEM; + goto exit; + } + err = bma2x2_update_bandwidth(bma2x2); + if (err) + goto print_error; + + interval = bma2x2_bandwidth_to_interval(bma2x2); + wml = (unsigned int)(latency / interval); + if (wml > MAX_FIFO_F_LEVEL) + wml = MAX_FIFO_F_LEVEL; + err = bma2x2_set_watermark_lvl(client, wml); + if (err) + goto print_error; + + bma2x2->fifo_datasel = BMA2X2_FIFO_DAT_SEL_XYZ; + err = bma2x2_set_fifo_data_sel(bma2x2->bma2x2_client, + (unsigned char)BMA2X2_FIFO_DAT_SEL_XYZ); + if (err) + goto print_error; + + err = bma2x2_set_watermark_int(client, true); + if (err) + goto print_error; + + err = bma2x2_set_fifo_full_int(client, true); + if (err) + goto print_error; + + err = bma2x2_set_fifo_mode(client, BMA2X2_FIFO_MODE_FIFO); + if (err) + goto print_error; + bma2x2_set_fifo_start_time(bma2x2); + atomic_set(&bma2x2->fifo_enabled, 1); + } else if (!enable && fifo_en) { + bma2x2_flush_fifo(bma2x2); + bma2x2_set_watermark_int(client, false); + bma2x2_set_fifo_full_int(client, false); + err = bma2x2_set_fifo_mode(client, BMA2X2_FIFO_MODE_BYPASS); + if (err) + goto print_error; + atomic_set(&bma2x2->fifo_enabled, 0); + } else { + dev_err(&client->dev, + "FIFO state incorrect! enable=%d, fifo enable=%d\n", + enable, fifo_en); + err = -EINVAL; + goto exit; + } + +print_error: + if (err) + dev_err(&client->dev, + "Set fifo error! enable=%d latency=%d, err=%d\n", + enable, latency, err); +exit: + return err; +} + +static int bma2x2_set_enable(struct device *dev, int enable) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + int pre_enable = atomic_read(&bma2x2->enable); + int en_sig_motion = atomic_read(&bma2x2->en_sig_motion); + int err = 0; + ktime_t ktime; + int delay_ms; + + dev_dbg(&client->dev, + "set enable: en=%d, en_state=%d en_SMD=%d\n", + enable, pre_enable, en_sig_motion); + + if (atomic_read(&bma2x2->cal_status)) { + dev_err(dev, "can not enable or disable when calibration\n"); + return -EBUSY; + } + + mutex_lock(&bma2x2->enable_mutex); + if (enable && !pre_enable) { + if (!en_sig_motion) { + if (bma2x2_power_ctl(bma2x2, true)) { + dev_err(dev, "power failed\n"); + goto mutex_exit; + } + if (bma2x2_open_init(client, bma2x2) < 0) { + dev_err(dev, "set init failed\n"); + goto mutex_exit; + } + bma2x2_set_mode(bma2x2->bma2x2_client, + BMA2X2_MODE_NORMAL); + } + + if ((bma2x2->pdata->int_en) && + (bma2x2->max_latency_ms > 0)) { + err = bma2x2_set_fifo_enable(bma2x2, true); + if (err) + goto mutex_exit; + } else if ((bma2x2->pdata->int_en) && + (BMA2x2_IS_NEWDATA_INT_ENABLED())) { + if (bma2x2_set_Int_Enable(client, BMA2X2_DATA_EN, 1)) { + dev_err(&client->dev, + "enable interrupt failed\n"); + goto mutex_exit; + } + } else { + if (!bma2x2->pdata->use_hrtimer) { + delay_ms = atomic_read(&bma2x2->delay); + queue_delayed_work(bma2x2->data_wq, + &bma2x2->work, + msecs_to_jiffies(delay_ms)); + } else { + ktime = ktime_set(0, + atomic_read(&bma2x2->delay) + * NSEC_PER_MSEC); + hrtimer_start(&bma2x2->accel_timer, + ktime, HRTIMER_MODE_REL); + } + } + if (!en_sig_motion && bma2x2->pdata->int_en) { + err = bma2x2_config_interrupt(bma2x2, true); + if (err) { + dev_err(&client->dev, + "Config interrupt failed\n"); + goto mutex_exit; + } + bma2x2_pinctrl_state(bma2x2, true); + enable_irq(bma2x2->IRQ); + } + atomic_set(&bma2x2->enable, 1); + } else if (!enable && pre_enable) { + if (!en_sig_motion && bma2x2->pdata->int_en) { + if (bma2x2_store_state(client, bma2x2) < 0) { + dev_err(dev, "set state failed\n"); + goto mutex_exit; + } + bma2x2_set_mode(bma2x2->bma2x2_client, + BMA2X2_MODE_SUSPEND); + disable_irq(bma2x2->IRQ); + bma2x2_pinctrl_state(bma2x2, false); + if (bma2x2_config_interrupt(bma2x2, false)) { + dev_err(&client->dev, + "Deconfig interrupt failed\n"); + goto mutex_exit; + } + } + + if (atomic_read(&bma2x2->fifo_enabled)) { + err = bma2x2_set_fifo_enable(bma2x2, false); + if (err) + goto mutex_exit; + } else if ((bma2x2->pdata->int_en) && + (BMA2x2_IS_NEWDATA_INT_ENABLED())) { + if (bma2x2_set_Int_Enable(client, BMA2X2_DATA_EN, 0)) { + dev_err(&client->dev, + "disable interrupt failed\n"); + goto mutex_exit; + } + } else { + if (!bma2x2->pdata->use_hrtimer) + cancel_delayed_work_sync(&bma2x2->work); + else + hrtimer_cancel(&bma2x2->accel_timer); + } + + atomic_set(&bma2x2->enable, 0); + if (!en_sig_motion) { + if (bma2x2_power_ctl(bma2x2, false)) { + dev_err(dev, "power failed\n"); + goto mutex_exit; + } + } + } else { + dev_err(&client->dev, + "Incorrect enable state! enable=%d, state=%d\n", + enable, pre_enable); + } +mutex_exit: + mutex_unlock(&bma2x2->enable_mutex); + if (!!enable != atomic_read(&bma2x2->enable)) { + dev_err(&client->dev, + "Set enable failed! en=%d, en_state=%d, use_int=%d\n", + enable, atomic_read(&bma2x2->enable), + bma2x2->pdata->int_en); + err = -EBUSY; + } + return err; +} + +static ssize_t bma2x2_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if ((data == 0) || (data == 1)) { + error = bma2x2_set_enable(dev, data); + if (error) + return -EBUSY; + } + + return count; +} + +static int bma2x2_cdev_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + struct bma2x2_data *data = container_of(sensors_cdev, + struct bma2x2_data, cdev); + + bma2x2_set_enable(&data->bma2x2_client->dev, enable); + return 0; +} + +static int bma2x2_is_power_enabled(struct bma2x2_data *data) +{ + return atomic_read(&data->enable); +} + +static int bma2x2_cdev_poll_delay(struct sensors_classdev *sensors_cdev, + unsigned int delay_ms) +{ + struct bma2x2_data *data = container_of(sensors_cdev, + struct bma2x2_data, cdev); + int err; + + delay_ms = clamp_val(delay_ms, + POLL_INTERVAL_MIN_MS, POLL_INTERVAL_MAX_MS); + + dev_dbg(&data->bma2x2_client->dev, + "bma2x2_cdev_poll_delay delay_ms=%d\n", + delay_ms); + err = bma2x2_update_delay(data, delay_ms); + if (err) + return -EBUSY; + else + return 0; +} + +static int bma2x2_cdev_set_latency(struct sensors_classdev *sensors_cdev, + unsigned int max_latency_ms) +{ + struct bma2x2_data *bma2x2 = container_of(sensors_cdev, + struct bma2x2_data, cdev); + struct i2c_client *client = bma2x2->bma2x2_client; + unsigned int delay = atomic_read(&bma2x2->delay); + int err = 0; + + dev_dbg(&client->dev, + "bma2x2_cdev_set_latency latency=%d\n", + max_latency_ms); + if ((max_latency_ms > 0) && (max_latency_ms < delay)) { + dev_err(&client->dev, + "Latency should not less than delay! latency=%d, delay=%d\n", + max_latency_ms, delay); + return -EINVAL; + } + + bma2x2->max_latency_ms = max_latency_ms; + if (!atomic_read(&bma2x2->enable)) + return 0; + + if (max_latency_ms > 0) { + /* Data polling and batching should not work at same time */ + if (!bma2x2->pdata->use_hrtimer) + cancel_delayed_work_sync(&bma2x2->work); + else + hrtimer_cancel(&bma2x2->accel_timer); + + err = bma2x2_set_fifo_enable(bma2x2, true); + } else { + err = bma2x2_set_fifo_enable(bma2x2, false); + if (!err && bma2x2_use_data_polling(bma2x2)) + mod_delayed_work(bma2x2->data_wq, &bma2x2->work, + msecs_to_jiffies(delay)); + } + if (err) + dev_err(&client->dev, + "Set latency error! latency=%d, err=%d\n", + max_latency_ms, err); + return err; +} + +static int bma2x2_cdev_flush(struct sensors_classdev *sensors_cdev) +{ + struct bma2x2_data *bma2x2 = container_of(sensors_cdev, + struct bma2x2_data, cdev); + + if (atomic_read(&bma2x2->fifo_enabled)) + return bma2x2_flush_fifo(bma2x2); + else + return 0; +} + +#ifdef CONFIG_SENSORS_BMI058 +static int bma2x2_select_chanel(struct i2c_client *client) +{ + unsigned char data_ore[3] = { BOSCH_SENSOR_PLANE }; + signed char tmp; + int error, i; + int timeout; + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + unsigned char bmi058_channel_tb = {BMI058_OFFSET_TRIGGER_X, + BMI058_OFFSET_TRIGGER_Y, BMI058_OFFSET_TRIGGER_Z}; + + if (bma2x2->pdata->place > 3 && bma2x2->pdata->place < 8) + data_ore[2] = BOSCH_SENSOR_DOWN; + else if (bma2x2->pdata->place >= 0 && bma2x2->pdata->place < 4) + data_ore[2] = BOSCH_SENSOR_UP; + else { + dev_err(&client->dev, "unknown sensor place\n"); + return -EINVAL; + } + if (bma2x2_set_mode(client, BMA2X2_MODE_NORMAL) < 0) { + dev_err(&client->dev, "set calibrate mode error\n"); + return -EINVAL; + } + if (bma2x2_set_bandwidth(client, BMA2X2_BW_1000HZ) < 0) { + dev_err(&client->dev, "set calibrate bandwidth error\n"); + return -EINVAL; + } + if (bma2x2_set_range(client, BMA2X2_RANGE_SET) < 0) { + dev_err(&client->dev, "set calibrate range error\n"); + return -EINVAL; + } + for (i = 0; i < 3; i++) { + if (bma2x2_set_offset_target(client, bmi058_channel_tb[i], + (unsigned char)data_ore[i]) < 0) { + dev_err(&client->dev, + "set offset target error\n"); + return -EINVAL; + } + if (bma2x2_set_cal_trigger(client, (i + 1)) < 0) { + dev_err(&client->dev, + "read calibration state error\n"); + return -EINVAL; + } + timeout = 0; + do { + WAIT_CAL_READY(); + error = bma2x2_get_cal_ready(client, &tmp); + if (error < 0) { + dev_err(&client->dev, + "read cal_ready error\n"); + return error; + } + timeout++; + if (timeout == RETRY_TIME) { + dev_err(&client->dev, + "get fast calibration ready error\n"); + return -EINVAL; + }; + + } while (tmp == 0); + } + bma2x2_set_bandwidth(client, bma2x2->bandwidth); + if (error < 0) { + dev_err(&client->dev, "restore calibrate bandwidth error\n"); + return error; + } + return 0; +} +#else +static int bma2x2_select_chanel(struct i2c_client *client) +{ + unsigned char data_ore[3] = { BOSCH_SENSOR_PLANE }; + signed char tmp; + int error, i; + int timeout; + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + unsigned char channel_tab[] = {BMA2X2_OFFSET_TRIGGER_X, + BMA2X2_OFFSET_TRIGGER_Y, BMA2X2_OFFSET_TRIGGER_Z}; + + if (bma2x2->pdata->place > 3 && bma2x2->pdata->place < 8) + data_ore[2] = BOSCH_SENSOR_DOWN; + else if (bma2x2->pdata->place >= 0 && bma2x2->pdata->place < 4) + data_ore[2] = BOSCH_SENSOR_UP; + else { + dev_err(&client->dev, "unknown sensor place\n"); + return -EINVAL; + } + + if (bma2x2_set_mode(client, BMA2X2_MODE_NORMAL) < 0) { + dev_err(&client->dev, "set calibrate mode error\n"); + return -EINVAL; + } + if (bma2x2_set_bandwidth(client, BMA2X2_BW_1000HZ) < 0) { + dev_err(&client->dev, "set calibrate bandwidth error\n"); + return -EINVAL; + } + if (bma2x2_set_range(client, BMA2X2_RANGE_SET) < 0) { + dev_err(&client->dev, "set calibrate range error\n"); + return -EINVAL; + } + + for (i = 0; i < 3; i++) { + if (bma2x2_set_offset_target(client, channel_tab[i], + (unsigned char)data_ore[i]) < 0) { + dev_err(&client->dev, + "set offset target error\n"); + return -EINVAL; + } + if (bma2x2_set_cal_trigger(client, (i + 1)) < 0) { + dev_err(&client->dev, + "read calibration state error\n"); + return -EINVAL; + } + timeout = 0; + do { + WAIT_CAL_READY(); + error = bma2x2_get_cal_ready(client, &tmp); + if (error < 0) { + dev_err(&client->dev, + "read cal_ready error\n"); + return error; + } + timeout++; + if (timeout == RETRY_TIME) { + dev_err(&client->dev, + "get fast calibration ready error\n"); + return -EINVAL; + }; + + } while (tmp == 0); + } + + error = bma2x2_set_bandwidth(client, bma2x2->bandwidth); + if (error < 0) { + dev_err(&client->dev, "restore calibrate bandwidth error\n"); + return error; + } + return 0; +} +#endif + +static int bma2x2_self_calibration_xyz(struct sensors_classdev *sensors_cdev, + int axis, int apply_now) +{ + int error; + bool pre_enable; + struct bma2x2_data *data = container_of(sensors_cdev, + struct bma2x2_data, cdev); + struct i2c_client *client = data->bma2x2_client; + + pre_enable = atomic_read(&data->enable); + if (pre_enable) + bma2x2_set_enable(&client->dev, 0); + if (atomic_cmpxchg(&data->cal_status, 0, 1)) { + dev_err(&client->dev, "do calibration error\n"); + return -EBUSY; + } + + error = bma2x2_power_ctl(data, true); + if (error) { + dev_err(&client->dev, "Failed to enable sensor power\n"); + error = -EINVAL; + goto exit; + } + error = bma2x2_select_chanel(client); + if (error < 0) { + dev_err(&client->dev, "xyz calibration error\n"); + goto exit; + } + dev_dbg(&client->dev, "xyz axis fast calibration finished\n"); + error = bma2x2_eeprom_prog(client); + if (error < 0) { + dev_err(&client->dev, "write calibration to eeprom failed\n"); + goto exit; + } + snprintf(data->calibrate_buf, sizeof(data->calibrate_buf), + "%d,%d,%d", 0, 0, 0); + sensors_cdev->params = data->calibrate_buf; + + error = bma2x2_power_ctl(data, false); + if (error) { + dev_err(&client->dev, "Failed to disable sensor power\n"); + goto exit; + } + +exit: + atomic_set(&data->cal_status, 0); + if (pre_enable) + bma2x2_set_enable(&client->dev, 1); + + return error; +} + +static int bma2x2_eeprom_prog(struct i2c_client *client) +{ + int res = 0, timeout = 0; + unsigned char databuf; + + res = bma2x2_smbus_read_byte(client, BMA2X2_EEPROM_CTRL_REG, + &databuf); + if (res < 0) { + dev_err(&client->dev, "read eeprom control reg error1\n"); + return res; + } + databuf |= 0x01; + res = bma2x2_smbus_write_byte(client, BMA2X2_EEPROM_CTRL_REG, + &databuf); + if (res < 0) { + dev_err(&client->dev, "write eeprom control reg error1\n"); + return res; + } + + res = bma2x2_smbus_read_byte(client, BMA2X2_EEPROM_CTRL_REG, + &databuf); + if (res < 0) { + dev_err(&client->dev, "read eeprom control reg error2\n"); + return res; + } + databuf |= 0x02; + res = bma2x2_smbus_write_byte(client, BMA2X2_EEPROM_CTRL_REG, + &databuf); + if (res < 0) { + dev_err(&client->dev, "write eeprom control reg error2\n"); + return res; + } + do { + WAIT_CAL_READY(); + res = bma2x2_smbus_read_byte(client, BMA2X2_EEPROM_CTRL_REG, + &databuf); + if (res < 0) { + dev_err(&client->dev, "read nvm_rdy error\n"); + return res; + } + databuf = (databuf >> 2) & 0x01; + if (++timeout == 50) { + dev_err(&client->dev, "check nvm_rdy time out\n"); + break; + } + } while (databuf == 0); + + res = bma2x2_smbus_read_byte(client, BMA2X2_EEPROM_CTRL_REG, + &databuf); + if (res < 0) { + dev_err(&client->dev, "read eeprom control reg error3\n"); + return res; + } + databuf &= 0xFE; + res = bma2x2_smbus_write_byte(client, BMA2X2_EEPROM_CTRL_REG, + &databuf); + if (res < 0) { + dev_err(&client->dev, "write eeprom control reg error3\n"); + return res; + } + return res; +} + +static int bma2x2_write_cal_params(struct sensors_classdev *sensors_cdev, + struct cal_result_t *cal_result) +{ + struct bma2x2_data *data = container_of(sensors_cdev, + struct bma2x2_data, cdev); + + snprintf(data->calibrate_buf, sizeof(data->calibrate_buf), + "%d,%d,%d", 0, 0, 0); + sensors_cdev->params = data->calibrate_buf; + return 0; +} + +static ssize_t bma2x2_fast_calibration_x_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + +#ifdef CONFIG_SENSORS_BMI058 + if (bma2x2_get_offset_target(bma2x2->bma2x2_client, + BMI058_OFFSET_TRIGGER_X, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); +#else + if (bma2x2_get_offset_target(bma2x2->bma2x2_client, + BMA2X2_OFFSET_TRIGGER_X, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); +#endif + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fast_calibration_x_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + signed char tmp; + unsigned char timeout = 0; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + +#ifdef CONFIG_SENSORS_BMI058 + if (bma2x2_set_offset_target(bma2x2->bma2x2_client, + BMI058_OFFSET_TRIGGER_X, (unsigned char)data) < 0) + return -EINVAL; +#else + if (bma2x2_set_offset_target(bma2x2->bma2x2_client, + BMA2X2_OFFSET_TRIGGER_X, (unsigned char)data) < 0) + return -EINVAL; +#endif + + if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 1) < 0) + return -EINVAL; + + do { + WAIT_CAL_READY(); + bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp); + + timeout++; + if (timeout == 50) { + dev_err(&client->dev, "get fast calibration ready error\n"); + return -EINVAL; + }; + + } while (tmp == 0); + + dev_dbg(&client->dev, "x axis fast calibration finished\n"); + return count; +} + +static ssize_t bma2x2_fast_calibration_y_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + +#ifdef CONFIG_SENSORS_BMI058 + if (bma2x2_get_offset_target(bma2x2->bma2x2_client, + BMI058_OFFSET_TRIGGER_Y, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); +#else + if (bma2x2_get_offset_target(bma2x2->bma2x2_client, + BMA2X2_OFFSET_TRIGGER_Y, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); +#endif + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fast_calibration_y_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + signed char tmp; + unsigned char timeout = 0; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + +#ifdef CONFIG_SENSORS_BMI058 + if (bma2x2_set_offset_target(bma2x2->bma2x2_client, + BMI058_OFFSET_TRIGGER_Y, (unsigned char)data) < 0) + return -EINVAL; +#else + if (bma2x2_set_offset_target(bma2x2->bma2x2_client, + BMA2X2_OFFSET_TRIGGER_Y, (unsigned char)data) < 0) + return -EINVAL; +#endif + + if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 2) < 0) + return -EINVAL; + + do { + WAIT_CAL_READY(); + bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp); + + timeout++; + if (timeout == 50) { + dev_err(&client->dev, "get fast calibration ready error\n"); + return -EINVAL; + }; + + } while (tmp == 0); + + dev_dbg(&client->dev, "y axis fast calibration finished\n"); + return count; +} + +static ssize_t bma2x2_fast_calibration_z_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_offset_target(bma2x2->bma2x2_client, 3, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fast_calibration_z_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + signed char tmp; + unsigned char timeout = 0; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_offset_target(bma2x2->bma2x2_client, 3, (unsigned + char)data) < 0) + return -EINVAL; + + if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 3) < 0) + return -EINVAL; + + do { + WAIT_CAL_READY(); + bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp); + + timeout++; + if (timeout == 50) { + dev_err(&client->dev, "get fast calibration ready error\n"); + return -EINVAL; + }; + + } while (tmp == 0); + + dev_dbg(&client->dev, "z axis fast calibration finished\n"); + return count; +} + + +static ssize_t bma2x2_SleepDur_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_sleep_duration(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_SleepDur_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_sleep_duration(bma2x2->bma2x2_client, + (unsigned char) data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_fifo_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_fifo_mode(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fifo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_fifo_mode(bma2x2->bma2x2_client, + (unsigned char) data) < 0) + return -EINVAL; + return count; +} + + + +static ssize_t bma2x2_fifo_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_fifo_trig(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fifo_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_fifo_trig(bma2x2->bma2x2_client, + (unsigned char) data) < 0) + return -EINVAL; + + return count; +} + + + +static ssize_t bma2x2_fifo_trig_src_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_fifo_trig_src(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fifo_trig_src_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + if (bma2x2_set_fifo_trig_src(bma2x2->bma2x2_client, + (unsigned char) data) < 0) + return -EINVAL; + + return count; +} + + +/*! + * @brief show fifo_data_sel axis definition(Android definition, not sensor HW reg). + * 0--> x, y, z axis fifo data for every frame + * 1--> only x axis fifo data for every frame + * 2--> only y axis fifo data for every frame + * 3--> only z axis fifo data for every frame + */ +static ssize_t bma2x2_fifo_data_sel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + signed char place = BOSCH_SENSOR_PLACE_UNKNOWN; + + if (bma2x2_get_fifo_data_sel(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + +#ifdef CONFIG_SENSORS_BMI058 +/*Update BMI058 fifo_data_sel to the BMA2x2 common definition*/ + if (BMI058_FIFO_DAT_SEL_X == data) + data = BMA2X2_FIFO_DAT_SEL_X; + else if (BMI058_FIFO_DAT_SEL_Y == data) + data = BMA2X2_FIFO_DAT_SEL_Y; +#endif + + /*remaping fifo_dat_sel if define virtual place in BSP files*/ + place = bma2x2->pdata->place; + /* sensor with place 0 needs not to be remapped */ + if ((place > 0) && (place < MAX_AXIS_REMAP_TAB_SZ)) { + /* BMA2X2_FIFO_DAT_SEL_X: 1, Y:2, Z:3; + * but bst_axis_remap_tab_dft[i].src_x:0, y:1, z:2 + * so we need to +1*/ + if (BMA2X2_FIFO_DAT_SEL_X == data) + data = bst_axis_remap_tab_dft[place].src_x + 1; + else if (BMA2X2_FIFO_DAT_SEL_Y == data) + data = bst_axis_remap_tab_dft[place].src_y + 1; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fifo_framecount_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_fifo_framecount(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_fifo_framecount_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + bma2x2->fifo_count = (unsigned int) data; + + return count; +} + +static ssize_t bma2x2_temperature_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_read_temperature(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +/*! + * @brief store fifo_data_sel axis definition(Android definition, not sensor HW reg). + * 0--> x, y, z axis fifo data for every frame + * 1--> only x axis fifo data for every frame + * 2--> only y axis fifo data for every frame + * 3--> only z axis fifo data for every frame + */ +static ssize_t bma2x2_fifo_data_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + signed char place; + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + /*save fifo_data_sel(android definition)*/ + bma2x2->fifo_datasel = (unsigned char) data; + + /*remaping fifo_dat_sel if define virtual place*/ + place = bma2x2->pdata->place; + /* sensor with place 0 needs not to be remapped */ + if ((place > 0) && (place < MAX_AXIS_REMAP_TAB_SZ)) { + /*Need X Y axis revesal sensor place: P1, P3, P5, P7 */ + /* BMA2X2_FIFO_DAT_SEL_X: 1, Y:2, Z:3; + * but bst_axis_remap_tab_dft[i].src_x:0, y:1, z:2 + * so we need to +1*/ + if (BMA2X2_FIFO_DAT_SEL_X == data) + data = bst_axis_remap_tab_dft[place].src_x + 1; + else if (BMA2X2_FIFO_DAT_SEL_Y == data) + data = bst_axis_remap_tab_dft[place].src_y + 1; + } +#ifdef CONFIG_SENSORS_BMI058 + /*Update BMI058 fifo_data_sel to the BMA2x2 common definition*/ + if (BMA2X2_FIFO_DAT_SEL_X == data) + data = BMI058_FIFO_DAT_SEL_X; + else if (BMA2X2_FIFO_DAT_SEL_Y == data) + data = BMI058_FIFO_DAT_SEL_Y; + +#endif + if (bma2x2_set_fifo_data_sel(bma2x2->bma2x2_client, + (unsigned char) data) < 0) + return -EINVAL; + + return count; +} + + +/*! + * brief: bma2x2 single axis data remaping + * @param[i] fifo_datasel fifo axis data select setting + * @param[i/o] remap_dir remapping direction + * @param[i] client_data to transfer sensor place + * + * @return none + */ +static void bma2x2_single_axis_remaping(unsigned char fifo_datasel, + unsigned char *remap_dir, struct bma2x2_data *client_data) +{ + signed char place = client_data->pdata->place; + /* sensor with place 0 needs not to be remapped */ + if ((place <= 0) || (place >= MAX_AXIS_REMAP_TAB_SZ)) + return; + + if (fifo_datasel < 1 || fifo_datasel > 3) + return; + + switch (fifo_datasel) { + /*P2, P3, P4, P5 X axis(andorid) need to reverse*/ + case BMA2X2_FIFO_DAT_SEL_X: + if (-1 == bst_axis_remap_tab_dft[place].sign_x) + *remap_dir = 1; + else + *remap_dir = 0; + break; + /*P1, P2, P5, P6 Y axis(andorid) need to reverse*/ + case BMA2X2_FIFO_DAT_SEL_Y: + if (-1 == bst_axis_remap_tab_dft[place].sign_y) + *remap_dir = 1; + else + *remap_dir = 0; + break; + case BMA2X2_FIFO_DAT_SEL_Z: + /*P4, P5, P6, P7 Z axis(andorid) need to reverse*/ + if (-1 == bst_axis_remap_tab_dft[place].sign_z) + *remap_dir = 1; + else + *remap_dir = 0; + break; + default: + break; + } +} + +static int bma2x2_flush_fifo(struct bma2x2_data *bma2x2) +{ + struct i2c_client *client = bma2x2->bma2x2_client; + int bitwidth, i, err, ns; + unsigned char f_len = 0; + unsigned char fifo_count; + s64 interval_ns, ts_ns, sec; + struct bma2x2acc acc; + + err = bma2x2_get_fifo_framecount(bma2x2->bma2x2_client, &fifo_count); + if (err) + dev_err(&client->dev, + "Get fifo count error! err=%d\n", err); + + dev_dbg(&client->dev, + "bma2x2_flush_fifo fifo_count=%d\n", fifo_count); + if (fifo_count > MAX_FIFO_F_LEVEL) { + dev_err(&client->dev, + "Invalid fifo framecount: %d\n", fifo_count); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(bma2x2->fifo_buf)) { + dev_err(&client->dev, + "Not enough memory for fifo data\n"); + return -ENOMEM; + } + interval_ns = bma2x2_bandwidth_to_interval(bma2x2) * NSEC_PER_MSEC; + ts_ns = bma2x2->fifo_start_ns + interval_ns; + bma2x2_set_fifo_start_time(bma2x2); + f_len = bma2x2->fifo_datasel ? + FIFO_FRAMESIZE_1_AXIS : FIFO_FRAMESIZE_3_AXIS; + if (f_len != FIFO_FRAMESIZE_3_AXIS) { + /* Reset FIFO if it doesn't contain X Y Z three axis data */ + dev_err(&client->dev, + "Incorrect FIFO data select (0x%x)!\n", + bma2x2->fifo_datasel); + err = -EBUSY; + goto reset_fifo; + } + err = bma_i2c_burst_read(client, + BMA2X2_FIFO_DATA_OUTPUT_REG, bma2x2->fifo_buf, + fifo_count * f_len); + if (err < 0) { + dev_err(&client->dev, + "Read byte block error ret=%d\n", err); + err = -EIO; + goto reset_fifo; + } + + for (i = 0; i < fifo_count; i++) { + acc.x = + ((unsigned char)bma2x2->fifo_buf[i * f_len + 1] << 8 | + (unsigned char)bma2x2->fifo_buf[i * f_len + 0]); + acc.y = + ((unsigned char)bma2x2->fifo_buf[i * f_len + 3] << 8 | + (unsigned char)bma2x2->fifo_buf[i * f_len + 2]); + acc.z = + ((unsigned char)bma2x2->fifo_buf[i * f_len + 5] << 8 | + (unsigned char)bma2x2->fifo_buf[i * f_len + 4]); + bitwidth = bma2x2_sensor_bitwidth[bma2x2->sensor_type]; + BMA2X2_SHIFT_BITWIDTH(acc.x, bitwidth); + BMA2X2_SHIFT_BITWIDTH(acc.y, bitwidth); + BMA2X2_SHIFT_BITWIDTH(acc.z, bitwidth); + + bma2x2_remap_sensor_data(&acc, bma2x2); + + sec = ts_ns; + ns = do_div(sec, NSEC_PER_SEC); + ts_ns += interval_ns; + input_report_abs(bma2x2->input, ABS_X, + (int)acc.x << bma2x2->sensitivity); + input_report_abs(bma2x2->input, ABS_Y, + (int)acc.y << bma2x2->sensitivity); + input_report_abs(bma2x2->input, ABS_Z, + (int)acc.z << bma2x2->sensitivity); + input_event(bma2x2->input, EV_SYN, SYN_TIME_SEC, + (int)sec); + input_event(bma2x2->input, EV_SYN, SYN_TIME_NSEC, + (int)ns); + input_sync(bma2x2->input); + } + return 0; + +reset_fifo: + /* Clear FIFO content and reset interrupt */ + bma2x2_set_fifo_mode(client, BMA2X2_FIFO_MODE_FIFO); + return err; +} + +static ssize_t bma2x2_fifo_data_out_frame_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int err, i, len; + signed char fifo_data_out[MAX_FIFO_F_LEVEL * MAX_FIFO_F_BYTES] = {0}; + unsigned char f_len = 0; + s16 value; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + struct bma2x2acc acc_lsb; + unsigned char axis_dir_remap = 0; + + if (bma2x2->fifo_datasel) { + /*Select one axis data output for every fifo frame*/ + f_len = FIFO_FRAMESIZE_1_AXIS; + } else { + /*Select X Y Z axis data output for every fifo frame*/ + f_len = FIFO_FRAMESIZE_3_AXIS; + } + + if (bma2x2->fifo_count == 0) + return -EINVAL; + + if (bma_i2c_burst_read(bma2x2->bma2x2_client, + BMA2X2_FIFO_DATA_OUTPUT_REG, fifo_data_out, + bma2x2->fifo_count * f_len) < 0) + return snprintf(buf, PAGE_SIZE, "Read byte block error\n"); + + + err = 0; + +/* please give attation for the fifo output data format*/ + if (f_len == FIFO_FRAMESIZE_3_AXIS) { + /* Select X Y Z axis data output for every frame */ + for (i = 0; i < bma2x2->fifo_count; i++) { + acc_lsb.x = + ((unsigned char)fifo_data_out[i * f_len + 1] << 8 | + (unsigned char)fifo_data_out[i * f_len + 0]); + acc_lsb.y = + ((unsigned char)fifo_data_out[i * f_len + 3] << 8 | + (unsigned char)fifo_data_out[i * f_len + 2]); + acc_lsb.z = + ((unsigned char)fifo_data_out[i * f_len + 5] << 8 | + (unsigned char)fifo_data_out[i * f_len + 4]); +#ifndef BMA2X2_SENSOR_IDENTIFICATION_ENABLE + acc_lsb.x >>= + (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]); + acc_lsb.y >>= + (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]); + acc_lsb.z >>= + (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]); +#endif + bma2x2_remap_sensor_data(&acc_lsb, bma2x2); + len = snprintf(buf, PAGE_SIZE, "%d %d %d ", + acc_lsb.x, acc_lsb.y, acc_lsb.z); + buf += len; + err += len; + } + } else { + /* single axis data output for every frame */ + bma2x2_single_axis_remaping(bma2x2->fifo_datasel, + &axis_dir_remap, bma2x2); + for (i = 0; i < bma2x2->fifo_count * f_len / 2; i++) { + value = ((unsigned char)fifo_data_out[2 * i + 1] << 8 | + (unsigned char)fifo_data_out[2 * i]); +#ifndef BMA2X2_SENSOR_IDENTIFICATION_ENABLE + value >>= + (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]); +#endif + if (axis_dir_remap) + value = 0 - value; + len = snprintf(buf, PAGE_SIZE, "%d ", value); + buf += len; + err += len; + } + } + + return err; +} + +static ssize_t bma2x2_offset_x_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_offset_x(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_offset_x_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_offset_x(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_offset_y_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_offset_y(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_offset_y_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_offset_y(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +static ssize_t bma2x2_offset_z_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char data; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + if (bma2x2_get_offset_z(bma2x2->bma2x2_client, &data) < 0) + return snprintf(buf, PAGE_SIZE, "Read error\n"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data); + +} + +static ssize_t bma2x2_offset_z_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if (bma2x2_set_offset_z(bma2x2->bma2x2_client, (unsigned + char)data) < 0) + return -EINVAL; + + return count; +} + +#ifdef CONFIG_SIG_MOTION +static int bma2x2_set_en_slope_int(struct bma2x2_data *bma2x2, + int en) +{ + int err; + struct i2c_client *client = bma2x2->bma2x2_client; + + if (en) { + /* Set the related parameters which needs to be fine tuned by + * interfaces: slope_threshold and slope_duration + */ + /*dur: 192 samples ~= 3s*/ + err = bma2x2_set_slope_duration(client, BMA2X2_SMD_SLOPE_DUR); + err += bma2x2_set_slope_threshold(client, BMA2X2_SMD_SLOPE_TH); + + /*Enable the interrupts*/ + err += bma2x2_set_Int_Enable(client, 5, 1);/*Slope X*/ + err += bma2x2_set_Int_Enable(client, 6, 1);/*Slope Y*/ + err += bma2x2_set_Int_Enable(client, 7, 1);/*Slope Z*/ + } else { + err = bma2x2_set_Int_Enable(client, 5, 0);/*Slope X*/ + err += bma2x2_set_Int_Enable(client, 6, 0);/*Slope Y*/ + err += bma2x2_set_Int_Enable(client, 7, 0);/*Slope Z*/ + } + dev_dbg(&bma2x2->bma2x2_client->dev, + "bma2x2_set_en_slope_int en=%d, err=%d\n", en, err); + return err; +} + +static ssize_t bma2x2_en_sig_motion_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&bma2x2->en_sig_motion)); +} + +static int bma2x2_set_en_sig_motion(struct bma2x2_data *bma2x2, + int en) +{ + int err = 0; + + en = (en >= 1) ? 1 : 0; /* set sig motion sensor status */ + + if (atomic_read(&bma2x2->en_sig_motion) != en) { + if (en) { + err = bma2x2_set_mode(bma2x2->bma2x2_client, + BMA2X2_MODE_NORMAL); + err = bma2x2_set_en_slope_int(bma2x2, en); + enable_irq_wake(bma2x2->IRQ); + } else { + disable_irq_wake(bma2x2->IRQ); + err = bma2x2_set_en_slope_int(bma2x2, en); + err = bma2x2_set_mode(bma2x2->bma2x2_client, + BMA2X2_MODE_SUSPEND); + } + atomic_set(&bma2x2->en_sig_motion, en); + } + return err; +} + +static int bma2x2_smd_enable(struct bma2x2_data *bma2x2, bool enable) +{ + struct i2c_client *client = bma2x2->bma2x2_client; + int acc_enable = atomic_read(&bma2x2->enable); + int smd_enable = atomic_read(&bma2x2->en_sig_motion); + int err = 0; + + mutex_lock(&bma2x2->enable_mutex); + if (enable && !smd_enable) { + if (!acc_enable) { + if (bma2x2_power_ctl(bma2x2, true)) { + err = -EBUSY; + goto mutex_exit; + } + if (bma2x2_open_init(client, bma2x2) < 0) { + bma2x2_power_ctl(bma2x2, false); + err = -EBUSY; + goto mutex_exit; + } + bma2x2_pinctrl_state(bma2x2, true); + bma2x2_set_mode(client, BMA2X2_MODE_NORMAL); + } + err = bma2x2_set_en_slope_int(bma2x2, enable); + if (err) { + bma2x2_set_mode(client, BMA2X2_MODE_SUSPEND); + bma2x2_pinctrl_state(bma2x2, false); + bma2x2_power_ctl(bma2x2, false); + goto mutex_exit; + } + if (!acc_enable) { + bma2x2_config_interrupt(bma2x2, true); + enable_irq(bma2x2->IRQ); + } + enable_irq_wake(bma2x2->IRQ); + atomic_set(&bma2x2->en_sig_motion, 1); + } else if (!enable && smd_enable) { + disable_irq_wake(bma2x2->IRQ); + if (!acc_enable) + disable_irq(bma2x2->IRQ); + + err = bma2x2_set_en_slope_int(bma2x2, enable); + if (err) + goto mutex_exit; + if (!acc_enable) { + bma2x2_store_state(client, bma2x2); + bma2x2_set_mode(client, BMA2X2_MODE_SUSPEND); + bma2x2_pinctrl_state(bma2x2, false); + bma2x2_power_ctl(bma2x2, false); + } + atomic_set(&bma2x2->en_sig_motion, 0); + } else { + dev_err(&bma2x2->bma2x2_client->dev, + "SMD state incorrect! enable=%d, state=%d\n", + enable, smd_enable); + } + +mutex_exit: + mutex_unlock(&bma2x2->enable_mutex); + if (err) + dev_err(&client->dev, "Set SMD error! enable=%d, err=%d\n", + enable, err); + return err; +} + +static int bma2x2_smd_cdev_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + struct bma2x2_data *bma2x2 = container_of(sensors_cdev, + struct bma2x2_data, smd_cdev); + + return bma2x2_smd_enable(bma2x2, !!enable); +} + +static ssize_t bma2x2_en_sig_motion_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if ((data == 0) || (data == 1)) + bma2x2_set_en_sig_motion(bma2x2, data); + + return count; +} +#endif /* CONFIG_SIG_MOTION */ + +#ifdef CONFIG_DOUBLE_TAP +static int bma2x2_set_en_single_tap_int(struct bma2x2_data *bma2x2, int en) +{ + int err; + struct i2c_client *client = bma2x2->bma2x2_client; + + if (en) { + /* set tap interruption parameter here if needed. + bma2x2_set_tap_duration(client, 0xc0); + bma2x2_set_tap_threshold(client, 0x16); + */ + + /*Enable the single tap interrupts*/ + err = bma2x2_set_Int_Enable(client, 8, 1); + #ifdef BMA2X2_ENABLE_INT1 + err += bma2x2_set_int1_pad_sel(client, PAD_SINGLE_TAP); + #else + err += bma2x2_set_int2_pad_sel(client, PAD_SINGLE_TAP); + #endif + } else { + err = bma2x2_set_Int_Enable(client, 8, 0); + } + return err; +} + +static ssize_t bma2x2_tap_time_period_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", bma2x2->tap_time_period); +} + +static ssize_t bma2x2_tap_time_period_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + bma2x2->tap_time_period = data; + + return count; +} + +static ssize_t bma2x2_en_double_tap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&bma2x2->en_double_tap)); +} + +static int bma2x2_set_en_double_tap(struct bma2x2_data *bma2x2, + int en) +{ + int err = 0; + + en = (en >= 1) ? 1 : 0; + + if (atomic_read(&bma2x2->en_double_tap) != en) { + if (en) { + err = bma2x2_set_mode(bma2x2->bma2x2_client, + BMA2X2_MODE_NORMAL); + err = bma2x2_set_en_single_tap_int(bma2x2, en); + } else { + err = bma2x2_set_en_single_tap_int(bma2x2, en); + err = bma2x2_set_mode(bma2x2->bma2x2_client, + BMA2X2_MODE_SUSPEND); + } + atomic_set(&bma2x2->en_double_tap, en); + } + return err; +} + +static ssize_t bma2x2_en_double_tap_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct i2c_client *client = to_i2c_client(dev); + struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); + + error = kstrtoul(buf, 10, &data); + if (error) + return error; + + if ((data == 0) || (data == 1)) + bma2x2_set_en_double_tap(bma2x2, data); + + return count; +} + +static void bma2x2_tap_timeout_handle(unsigned long data) +{ + struct bma2x2_data *bma2x2 = (struct bma2x2_data *)data; + + dev_dbg(&bma2x2->bma2x2_client->dev, + "tap interrupt handle, timeout\n"); + mutex_lock(&bma2x2->tap_mutex); + bma2x2->tap_times = 0; + mutex_unlock(&bma2x2->tap_mutex); + + /* if a single tap need to report, open the define */ +#ifdef REPORT_SINGLE_TAP_WHEN_DOUBLE_TAP_SENSOR_ENABLED + input_report_rel(bma2x2->dev_interrupt, + SINGLE_TAP_INTERRUPT, + SINGLE_TAP_INTERRUPT_HAPPENED); + input_sync(bma2x2->dev_interrupt); +#endif + +} +#endif + +static DEVICE_ATTR(range, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_range_show, bma2x2_range_store); +static DEVICE_ATTR(bandwidth, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_bandwidth_show, bma2x2_bandwidth_store); +static DEVICE_ATTR(op_mode, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_mode_show, bma2x2_mode_store); +static DEVICE_ATTR(value, S_IRUSR|S_IRGRP, + bma2x2_value_show, NULL); +static DEVICE_ATTR(value_cache, S_IRUSR|S_IRGRP, + bma2x2_value_cache_show, NULL); +static DEVICE_ATTR(delay, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP, + bma2x2_delay_show, bma2x2_delay_store); +static DEVICE_ATTR(enable, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP, + bma2x2_enable_show, bma2x2_enable_store); +static DEVICE_ATTR(SleepDur, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_SleepDur_show, bma2x2_SleepDur_store); +static DEVICE_ATTR(fast_calibration_x, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fast_calibration_x_show, + bma2x2_fast_calibration_x_store); +static DEVICE_ATTR(fast_calibration_y, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fast_calibration_y_show, + bma2x2_fast_calibration_y_store); +static DEVICE_ATTR(fast_calibration_z, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fast_calibration_z_show, + bma2x2_fast_calibration_z_store); +static DEVICE_ATTR(fifo_mode, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fifo_mode_show, bma2x2_fifo_mode_store); +static DEVICE_ATTR(fifo_framecount, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fifo_framecount_show, bma2x2_fifo_framecount_store); +static DEVICE_ATTR(fifo_trig, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fifo_trig_show, bma2x2_fifo_trig_store); +static DEVICE_ATTR(fifo_trig_src, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fifo_trig_src_show, bma2x2_fifo_trig_src_store); +static DEVICE_ATTR(fifo_data_sel, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_fifo_data_sel_show, bma2x2_fifo_data_sel_store); +static DEVICE_ATTR(fifo_data_frame, S_IRUSR|S_IRGRP, + bma2x2_fifo_data_out_frame_show, NULL); +static DEVICE_ATTR(reg, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_register_show, bma2x2_register_store); +static DEVICE_ATTR(chip_id, S_IRUSR|S_IRGRP, + bma2x2_chip_id_show, NULL); +static DEVICE_ATTR(offset_x, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_offset_x_show, + bma2x2_offset_x_store); +static DEVICE_ATTR(offset_y, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_offset_y_show, + bma2x2_offset_y_store); +static DEVICE_ATTR(offset_z, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_offset_z_show, + bma2x2_offset_z_store); +static DEVICE_ATTR(enable_int, S_IWUSR, + NULL, bma2x2_enable_int_store); +static DEVICE_ATTR(int_mode, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_int_mode_show, bma2x2_int_mode_store); +static DEVICE_ATTR(slope_duration, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_slope_duration_show, bma2x2_slope_duration_store); +static DEVICE_ATTR(slope_threshold, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_slope_threshold_show, bma2x2_slope_threshold_store); +static DEVICE_ATTR(slope_no_mot_duration, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_slope_no_mot_duration_show, + bma2x2_slope_no_mot_duration_store); +static DEVICE_ATTR(slope_no_mot_threshold, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_slope_no_mot_threshold_show, + bma2x2_slope_no_mot_threshold_store); +static DEVICE_ATTR(high_g_duration, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_high_g_duration_show, bma2x2_high_g_duration_store); +static DEVICE_ATTR(high_g_threshold, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_high_g_threshold_show, bma2x2_high_g_threshold_store); +static DEVICE_ATTR(low_g_duration, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_low_g_duration_show, bma2x2_low_g_duration_store); +static DEVICE_ATTR(low_g_threshold, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_low_g_threshold_show, bma2x2_low_g_threshold_store); +static DEVICE_ATTR(tap_duration, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_tap_duration_show, bma2x2_tap_duration_store); +static DEVICE_ATTR(tap_threshold, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_tap_threshold_show, bma2x2_tap_threshold_store); +static DEVICE_ATTR(tap_quiet, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_tap_quiet_show, bma2x2_tap_quiet_store); +static DEVICE_ATTR(tap_shock, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_tap_shock_show, bma2x2_tap_shock_store); +static DEVICE_ATTR(tap_samp, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_tap_samp_show, bma2x2_tap_samp_store); +static DEVICE_ATTR(orient_mode, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_orient_mode_show, bma2x2_orient_mode_store); +static DEVICE_ATTR(orient_blocking, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_orient_blocking_show, bma2x2_orient_blocking_store); +static DEVICE_ATTR(orient_hyst, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_orient_hyst_show, bma2x2_orient_hyst_store); +static DEVICE_ATTR(orient_theta, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_orient_theta_show, bma2x2_orient_theta_store); +static DEVICE_ATTR(flat_theta, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_flat_theta_show, bma2x2_flat_theta_store); +static DEVICE_ATTR(flat_hold_time, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_flat_hold_time_show, bma2x2_flat_hold_time_store); +static DEVICE_ATTR(selftest, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_selftest_show, bma2x2_selftest_store); +static DEVICE_ATTR(softreset, S_IWUSR, + NULL, bma2x2_softreset_store); +static DEVICE_ATTR(temperature, S_IRUSR|S_IRGRP, + bma2x2_temperature_show, NULL); +static DEVICE_ATTR(place, S_IRUSR|S_IRGRP, + bma2x2_place_show, NULL); +#ifdef CONFIG_SIG_MOTION +static DEVICE_ATTR(en_sig_motion, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_en_sig_motion_show, bma2x2_en_sig_motion_store); +#endif +#ifdef CONFIG_DOUBLE_TAP +static DEVICE_ATTR(tap_time_period, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_tap_time_period_show, bma2x2_tap_time_period_store); +static DEVICE_ATTR(en_double_tap, S_IRUSR|S_IRGRP|S_IWUSR, + bma2x2_en_double_tap_show, bma2x2_en_double_tap_store); +#endif + +static struct attribute *bma2x2_attributes[] = { + &dev_attr_range.attr, + &dev_attr_bandwidth.attr, + &dev_attr_op_mode.attr, + &dev_attr_value.attr, + &dev_attr_value_cache.attr, + &dev_attr_delay.attr, + &dev_attr_enable.attr, + &dev_attr_SleepDur.attr, + &dev_attr_reg.attr, + &dev_attr_fast_calibration_x.attr, + &dev_attr_fast_calibration_y.attr, + &dev_attr_fast_calibration_z.attr, + &dev_attr_fifo_mode.attr, + &dev_attr_fifo_framecount.attr, + &dev_attr_fifo_trig.attr, + &dev_attr_fifo_trig_src.attr, + &dev_attr_fifo_data_sel.attr, + &dev_attr_fifo_data_frame.attr, + &dev_attr_chip_id.attr, + &dev_attr_offset_x.attr, + &dev_attr_offset_y.attr, + &dev_attr_offset_z.attr, + &dev_attr_enable_int.attr, + &dev_attr_int_mode.attr, + &dev_attr_slope_duration.attr, + &dev_attr_slope_threshold.attr, + &dev_attr_slope_no_mot_duration.attr, + &dev_attr_slope_no_mot_threshold.attr, + &dev_attr_high_g_duration.attr, + &dev_attr_high_g_threshold.attr, + &dev_attr_low_g_duration.attr, + &dev_attr_low_g_threshold.attr, + &dev_attr_tap_threshold.attr, + &dev_attr_tap_duration.attr, + &dev_attr_tap_quiet.attr, + &dev_attr_tap_shock.attr, + &dev_attr_tap_samp.attr, + &dev_attr_orient_mode.attr, + &dev_attr_orient_blocking.attr, + &dev_attr_orient_hyst.attr, + &dev_attr_orient_theta.attr, + &dev_attr_flat_theta.attr, + &dev_attr_flat_hold_time.attr, + &dev_attr_selftest.attr, + &dev_attr_softreset.attr, + &dev_attr_temperature.attr, + &dev_attr_place.attr, +#ifdef CONFIG_SIG_MOTION + &dev_attr_en_sig_motion.attr, +#endif +#ifdef CONFIG_DOUBLE_TAP + &dev_attr_en_double_tap.attr, +#endif + + NULL +}; + +static struct attribute_group bma2x2_attribute_group = { + .attrs = bma2x2_attributes +}; + +#ifdef CONFIG_SIG_MOTION +static struct attribute *bma2x2_sig_motion_attributes[] = { + &dev_attr_slope_duration.attr, + &dev_attr_slope_threshold.attr, + &dev_attr_en_sig_motion.attr, + NULL +}; +static struct attribute_group bma2x2_sig_motion_attribute_group = { + .attrs = bma2x2_sig_motion_attributes +}; +#endif + +#ifdef CONFIG_DOUBLE_TAP +static struct attribute *bma2x2_double_tap_attributes[] = { + &dev_attr_tap_threshold.attr, + &dev_attr_tap_duration.attr, + &dev_attr_tap_quiet.attr, + &dev_attr_tap_shock.attr, + &dev_attr_tap_samp.attr, + &dev_attr_tap_time_period.attr, + &dev_attr_en_double_tap.attr, + NULL +}; +static struct attribute_group bma2x2_double_tap_attribute_group = { + .attrs = bma2x2_double_tap_attributes +}; +#endif + + +#if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2) +unsigned char *orient[] = {"upward looking portrait upright", + "upward looking portrait upside-down", + "upward looking landscape left", + "upward looking landscape right", + "downward looking portrait upright", + "downward looking portrait upside-down", + "downward looking landscape left", + "downward looking landscape right"}; + + +static void bma2x2_high_g_interrupt_handle(struct bma2x2_data *bma2x2) +{ + unsigned char first_value = 0; + unsigned char sign_value = 0; + int i; + + for (i = 0; i < 3; i++) { + bma2x2_get_HIGH_first(bma2x2->bma2x2_client, i, &first_value); + if (first_value == 1) { + bma2x2_get_HIGH_sign(bma2x2->bma2x2_client, + &sign_value); + if (sign_value == 1) { + if (i == 0) + input_report_rel(bma2x2->dev_interrupt, + HIGH_G_INTERRUPT, + HIGH_G_INTERRUPT_X_N); + if (i == 1) + input_report_rel(bma2x2->dev_interrupt, + HIGH_G_INTERRUPT, + HIGH_G_INTERRUPT_Y_N); + if (i == 2) + input_report_rel(bma2x2->dev_interrupt, + HIGH_G_INTERRUPT, + HIGH_G_INTERRUPT_Z_N); + } else { + if (i == 0) + input_report_rel(bma2x2->dev_interrupt, + HIGH_G_INTERRUPT, + HIGH_G_INTERRUPT_X); + if (i == 1) + input_report_rel(bma2x2->dev_interrupt, + HIGH_G_INTERRUPT, + HIGH_G_INTERRUPT_Y); + if (i == 2) + input_report_rel(bma2x2->dev_interrupt, + HIGH_G_INTERRUPT, + HIGH_G_INTERRUPT_Z); + } + } + + ISR_INFO(&bma2x2->bma2x2_client->dev, + "High G interrupt happened,exis is %d, first is %d,sign is %d\n", + i, first_value, sign_value); + } +} + +#ifndef CONFIG_SIG_MOTION +static void bma2x2_slope_interrupt_handle(struct bma2x2_data *bma2x2) +{ + unsigned char first_value = 0; + unsigned char sign_value = 0; + int i; + + for (i = 0; i < 3; i++) { + bma2x2_get_slope_first(bma2x2->bma2x2_client, i, &first_value); + if (first_value == 1) { + bma2x2_get_slope_sign(bma2x2->bma2x2_client, + &sign_value); + if (sign_value == 1) { + if (i == 0) + input_report_rel(bma2x2->dev_interrupt, + SLOP_INTERRUPT, + SLOPE_INTERRUPT_X_N); + if (i == 1) + input_report_rel(bma2x2->dev_interrupt, + SLOP_INTERRUPT, + SLOPE_INTERRUPT_Y_N); + if (i == 2) + input_report_rel(bma2x2->dev_interrupt, + SLOP_INTERRUPT, + SLOPE_INTERRUPT_Z_N); + } else { + if (i == 0) + input_report_rel(bma2x2->dev_interrupt, + SLOP_INTERRUPT, + SLOPE_INTERRUPT_X); + if (i == 1) + input_report_rel(bma2x2->dev_interrupt, + SLOP_INTERRUPT, + SLOPE_INTERRUPT_Y); + if (i == 2) + input_report_rel(bma2x2->dev_interrupt, + SLOP_INTERRUPT, + SLOPE_INTERRUPT_Z); + + } + } + + ISR_INFO(&bma2x2->bma2x2_client->dev, + "Slop interrupt happened,exis is %d, first is %d,sign is %d\n", + i, first_value, sign_value); + } +} +#endif + +#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT +static void bma2x2_read_new_data(struct bma2x2_data *bma2x2) +{ + struct bma2x2acc value; + + bma2x2_report_axis_data(bma2x2, &value); + mutex_lock(&bma2x2->value_mutex); + bma2x2->value = value; + mutex_unlock(&bma2x2->value_mutex); +} +#else +static void bma2x2_read_new_data(struct bma2x2_data *bma2x2) +{ + +} +#endif + +#ifdef CONFIG_SIG_MOTION +static int bma2x2_register_smd(struct bma2x2_data *bma2x2, bool enable) +{ + struct input_dev *smd_input; + int err; + + if (!enable) { + sensors_classdev_unregister(&bma2x2->smd_cdev); + return 0; + } + + smd_input = devm_input_allocate_device(&bma2x2->bma2x2_client->dev); + if (IS_ERR_OR_NULL(smd_input)) { + dev_err(&bma2x2->bma2x2_client->dev, + "Cannot allocate SMD device\n"); + return -ENOMEM; + } + + smd_input->name = "bma2x2-smd"; + smd_input->id.bustype = BUS_I2C; + input_set_capability(smd_input, EV_ABS, ABS_MISC); + input_set_drvdata(smd_input, bma2x2); + + err = input_register_device(smd_input); + if (err < 0) { + dev_err(&bma2x2->bma2x2_client->dev, + "Cannot register input interrupt device\n"); + input_free_device(smd_input); + return err; + } + + if (!bma2x2->pdata->int_en) { + dev_err(&bma2x2->bma2x2_client->dev, + "SMD need interrupt for wakeup!\n"); + input_unregister_device(smd_input); + return -EINVAL; + } + + bma2x2->smd_cdev = smd_cdev; + bma2x2->smd_cdev.sensors_enable = bma2x2_smd_cdev_enable; + err = sensors_classdev_register(&bma2x2->smd_input->dev, + &bma2x2->smd_cdev); + if (err) + dev_err(&bma2x2->bma2x2_client->dev, + "Create SMD device file failed!\n"); + + bma2x2->smd_input = smd_input; + + return err; +} +#else +static inline int bma2x2_register_smd(struct bma2x2_data *bma2x2, bool enable) +{ + dev_err(&bma2x2->bma2x2_client->dev, + "SMD feature is not enabled!\n"); + return -EINVAL; +} +#endif + +#ifdef CONFIG_SIG_MOTION +static void bma2x2_report_sig_motion(struct bma2x2_data *bma2x2) +{ + ktime_t ts; + + ts = ktime_get_boottime(); + + ISR_INFO(&bma2x2->bma2x2_client->dev, + "REPORT Significant motion interrupt\n"); + pm_wakeup_event(&bma2x2->smd_input->dev, 200); + /* report SMD event */ + input_report_abs(bma2x2->smd_input, ABS_MISC, + bma2x2->smd_count++); + input_event(bma2x2->smd_input, EV_SYN, SYN_TIME_SEC, + ktime_to_timespec(ts).tv_sec); + input_event(bma2x2->smd_input, EV_SYN, SYN_TIME_NSEC, + ktime_to_timespec(ts).tv_nsec); + input_sync(bma2x2->smd_input); +} + +#ifdef BMA2X2_SMD_SW_ENHANCE + +static bool bma2x2_detect_sig_motion(struct bma2x2_data *bma2x2) +{ + static int det_cnt; + static s64 last_ns; + ktime_t ts; + + ts = ktime_get_boottime(); + dev_dbg(&bma2x2->bma2x2_client->dev, + "ts=%lld, lastns=%lld, delta=%lld, det_cnt=%d\n", + ts.tv64, last_ns, (ts.tv64 - last_ns), det_cnt); + if (last_ns == 0) { + last_ns = ts.tv64; + return false; + } + if (ts.tv64 - last_ns < BMA2X2_SMD_DET_TIME_NS) + det_cnt++; + else + det_cnt = 0; + + last_ns = ts.tv64; + + if (det_cnt >= BMA2X2_SMD_DET_CNT) { + det_cnt = 0; + return true; + } else { + return false; + } +} +#else +static bool bma2x2_detect_sig_motion(struct bma2x2_data *bma2x2) +{ + return true; +} +#endif /* !BMA2X2_SMD_SW_ENHANCE */ +#endif + +static void bma2x2_irq_work_func(struct work_struct *work) +{ + struct bma2x2_data *bma2x2 = container_of((struct work_struct *)work, + struct bma2x2_data, irq_work); + struct i2c_client *client = bma2x2->bma2x2_client; + unsigned char intstatus[2] = {0}; + unsigned char first_value = 0; + unsigned char sign_value = 0; + int ret; + + ret = bma2x2_smbus_read_byte_block(client, + BMA2X2_STATUS1_REG, intstatus, ARRAY_SIZE(intstatus)); + if (ret) { + dev_err(&client->dev, + "read interrupt status2 err, err=%d\n", ret); + return; + } + + ISR_INFO(&client->dev, + "bma2x2_irq_work_func, intstatus=0x%x,0x%x\n", + intstatus[0], intstatus[1]); + + if (intstatus[1] & BMA2X2_IS_FIFO_INT) + bma2x2_flush_fifo(bma2x2); + if (intstatus[1] & BMA2X2_IS_NEWDATA_INT) + bma2x2_read_new_data(bma2x2); + if ((intstatus[1] == 0) && (intstatus[0] == 0)) { + /* + * Read new data if no other interrupt is triggered. + * BMA2x2 data ready flag will be cleared if new data + * acquisition is started, sometimes we cannot get that flag. + */ + bma2x2_read_new_data(bma2x2); + return; + } + +#ifdef CONFIG_SIG_MOTION + if (intstatus[0] & 0x04) { + if (atomic_read(&bma2x2->en_sig_motion) == 1) { + ISR_INFO(&bma2x2->bma2x2_client->dev, + "Significant motion interrupt happened\n"); + if (bma2x2_detect_sig_motion(bma2x2)) { + /* + * Close signification motion sensor, + * it will be open again if APP wants + */ + + bma2x2_smd_enable(bma2x2, false); + bma2x2_report_sig_motion(bma2x2); + } + } + } +#endif + +#ifdef CONFIG_DOUBLE_TAP + if (intstatus[0] & 0x20) { + if (atomic_read(&bma2x2->en_double_tap) == 1) { + ISR_INFO(&bma2x2->bma2x2_client->dev, + "single tap interrupt happened\n"); + bma2x2_set_Int_Enable(client, 8, 0); + if (bma2x2->tap_times == 0) { + mod_timer(&bma2x2->tap_timer, jiffies + + msecs_to_jiffies(bma2x2->tap_time_period)); + bma2x2->tap_times = 1; + } else { + /* only double tap is judged */ + ISR_INFO(&bma2x2->bma2x2_client->dev, + "double tap\n"); + mutex_lock(&bma2x2->tap_mutex); + bma2x2->tap_times = 0; + del_timer(&bma2x2->tap_timer); + mutex_unlock(&bma2x2->tap_mutex); + input_report_rel(bma2x2->dev_interrupt, + DOUBLE_TAP_INTERRUPT, + DOUBLE_TAP_INTERRUPT_HAPPENED); + input_sync(bma2x2->dev_interrupt); + } + bma2x2_set_Int_Enable(client, 8, 1); + } + } +#endif + + switch (intstatus[0]) { + case 0x01: + ISR_INFO(&bma2x2->bma2x2_client->dev, + "Low G interrupt happened\n"); + input_report_rel(bma2x2->dev_interrupt, LOW_G_INTERRUPT, + LOW_G_INTERRUPT_HAPPENED); + break; + + case 0x02: + bma2x2_high_g_interrupt_handle(bma2x2); + break; + +#ifndef CONFIG_SIG_MOTION + case 0x04: + bma2x2_slope_interrupt_handle(bma2x2); + break; +#endif + + case 0x08: + ISR_INFO(&bma2x2->bma2x2_client->dev, + "slow/ no motion interrupt happened\n"); + input_report_rel(bma2x2->dev_interrupt, + SLOW_NO_MOTION_INTERRUPT, + SLOW_NO_MOTION_INTERRUPT_HAPPENED); + break; + +#ifndef CONFIG_DOUBLE_TAP + case 0x10: + ISR_INFO(&bma2x2->bma2x2_client->dev, + "double tap interrupt happened\n"); + input_report_rel(bma2x2->dev_interrupt, + DOUBLE_TAP_INTERRUPT, + DOUBLE_TAP_INTERRUPT_HAPPENED); + break; + case 0x20: + ISR_INFO(&bma2x2->bma2x2_client->dev, + "single tap interrupt happened\n"); + input_report_rel(bma2x2->dev_interrupt, + SINGLE_TAP_INTERRUPT, + SINGLE_TAP_INTERRUPT_HAPPENED); + break; +#endif + + case 0x40: + bma2x2_get_orient_status(bma2x2->bma2x2_client, + &first_value); + ISR_INFO(&bma2x2->bma2x2_client->dev, + "orient interrupt happened,%s\n", + orient[first_value]); + if (first_value == 0) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + UPWARD_PORTRAIT_UP_INTERRUPT_HAPPENED); + else if (first_value == 1) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + UPWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED); + else if (first_value == 2) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + UPWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED); + else if (first_value == 3) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + UPWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED); + else if (first_value == 4) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + DOWNWARD_PORTRAIT_UP_INTERRUPT_HAPPENED); + else if (first_value == 5) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + DOWNWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED); + else if (first_value == 6) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + DOWNWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED); + else if (first_value == 7) + input_report_abs(bma2x2->dev_interrupt, + ORIENT_INTERRUPT, + DOWNWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED); + break; + case 0x80: + bma2x2_get_orient_flat_status(bma2x2->bma2x2_client, + &sign_value); + ISR_INFO(&bma2x2->bma2x2_client->dev, + "flat interrupt happened,flat status is %d\n", + sign_value); + if (sign_value == 1) { + input_report_abs(bma2x2->dev_interrupt, + FLAT_INTERRUPT, + FLAT_INTERRUPT_TRUE_HAPPENED); + } else { + input_report_abs(bma2x2->dev_interrupt, + FLAT_INTERRUPT, + FLAT_INTERRUPT_FALSE_HAPPENED); + } + break; + + default: + break; + } +} + +static irqreturn_t bma2x2_irq_handler(int irq, void *handle) +{ + struct bma2x2_data *data = handle; + + if (data == NULL) + return IRQ_HANDLED; + if (data->bma2x2_client == NULL) + return IRQ_HANDLED; + + queue_work(data->data_wq, &data->irq_work); + + return IRQ_HANDLED; +} +#else +static void bma2x2_irq_work_func(struct work_struct *work) +{ + struct bma2x2_data *bma2x2 = container_of((struct work_struct *)work, + struct bma2x2_data, irq_work); + + dev_dbg(&bma2x2->bma2x2_client->dev, + "Interrupt feature is not enabled!\n"); +} + +static irqreturn_t bma2x2_irq_handler(int irq, void *handle) +{ + struct bma2x2_data *bma2x2 = handle; + + dev_dbg(&bma2x2->bma2x2_client->dev, + "Interrupt feature is not enabled!\n"); + return IRQ_HANDLED; +} +#endif /* defined(BMA2X2_ENABLE_INT1)||defined(BMA2X2_ENABLE_INT2) */ + +static int bma2x2_power_ctl(struct bma2x2_data *data, bool on) +{ + int ret = 0; + int err = 0; + + if (!on && data->power_enabled) { + ret = regulator_disable(data->vdd); + if (ret) { + dev_err(&data->bma2x2_client->dev, + "Regulator vdd disable failed ret=%d\n", ret); + return ret; + } + + ret = regulator_disable(data->vio); + if (ret) { + dev_err(&data->bma2x2_client->dev, + "Regulator vio disable failed ret=%d\n", ret); + err = regulator_enable(data->vdd); + return ret; + } + data->power_enabled = on; + } else if (on && !data->power_enabled) { + ret = regulator_enable(data->vdd); + if (ret) { + dev_err(&data->bma2x2_client->dev, + "Regulator vdd enable failed ret=%d\n", ret); + return ret; + } + + ret = regulator_enable(data->vio); + if (ret) { + dev_err(&data->bma2x2_client->dev, + "Regulator vio enable failed ret=%d\n", ret); + err = regulator_disable(data->vdd); + return ret; + } + data->power_enabled = on; + } else { + dev_info(&data->bma2x2_client->dev, + "Power on=%d. enabled=%d\n", + on, data->power_enabled); + } + + return ret; +} + +static int bma2x2_power_init(struct bma2x2_data *data) +{ + int ret; + + data->vdd = regulator_get(&data->bma2x2_client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + ret = PTR_ERR(data->vdd); + dev_err(&data->bma2x2_client->dev, + "Regulator get failed vdd ret=%d\n", ret); + return ret; + } + + if (regulator_count_voltages(data->vdd) > 0) { + ret = regulator_set_voltage(data->vdd, + BMA2x2_VDD_MIN_UV, + BMA2x2_VDD_MAX_UV); + if (ret) { + dev_err(&data->bma2x2_client->dev, + "Regulator set failed vdd ret=%d\n", + ret); + goto reg_vdd_put; + } + } + + data->vio = regulator_get(&data->bma2x2_client->dev, "vio"); + if (IS_ERR(data->vio)) { + ret = PTR_ERR(data->vio); + dev_err(&data->bma2x2_client->dev, + "Regulator get failed vio ret=%d\n", ret); + goto reg_vdd_set; + } + + if (regulator_count_voltages(data->vio) > 0) { + ret = regulator_set_voltage(data->vio, + BMA2x2_VIO_MIN_UV, + BMA2x2_VIO_MAX_UV); + if (ret) { + dev_err(&data->bma2x2_client->dev, + "Regulator set failed vio ret=%d\n", ret); + goto reg_vio_put; + } + } + + return 0; + +reg_vio_put: + regulator_put(data->vio); +reg_vdd_set: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, BMA2x2_VDD_MAX_UV); +reg_vdd_put: + regulator_put(data->vdd); + return ret; +} + +static int bma2x2_power_deinit(struct bma2x2_data *data) +{ + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, + 0, BMA2x2_VDD_MAX_UV); + + regulator_put(data->vdd); + + if (regulator_count_voltages(data->vio) > 0) + regulator_set_voltage(data->vio, + 0, BMA2x2_VIO_MAX_UV); + + regulator_put(data->vio); + + return 0; +} + +#ifdef CONFIG_OF +static int bma2x2_parse_dt(struct device *dev, + struct bma2x2_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + u32 temp_val; + int rc; + + rc = of_property_read_u32(np, "bosch,init-interval", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read init-interval\n"); + return rc; + } + + pdata->poll_interval = temp_val; + + rc = of_property_read_u32(np, "bosch,place", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read sensor place parameter\n"); + return rc; + } + if (temp_val > 7 || temp_val < 0) { + dev_err(dev, "Invalid place parameter, use default value 0\n"); + pdata->place = 0; + } else { + pdata->place = temp_val; + } + + pdata->int_en = of_property_read_bool(np, "bosch,use-interrupt"); + + pdata->use_int2 = of_property_read_bool(np, "bosch,use-int2"); + + pdata->use_smd = of_property_read_bool(np, "bosch,use-smd"); + + pdata->use_hrtimer = of_property_read_bool(np, "bosch,use-hrtimer"); + + pdata->gpio_int1 = of_get_named_gpio_flags(dev->of_node, + "bosch,gpio-int1", 0, &pdata->int1_flag); + + pdata->gpio_int2 = of_get_named_gpio_flags(dev->of_node, + "bosch,gpio-int2", 0, &pdata->int2_flag); + + return 0; +} +#else +static int bma2x2_parse_dt(struct device *dev, + struct bma2x2_platform_data *pdata) +{ + return -EINVAL; +} +#endif + +#ifdef CONFIG_DOUBLE_TAP +static void bma2x2_double_tap_disable(struct bma2x2_data *data) +{ + if (data->g_sensor_dev_doubletap) { + sysfs_remove_group(&data->g_sensor_dev_doubletap->kobj, + &bma2x2_double_tap_attribute_group); + device_destroy(data->g_sensor_dev_doubletap); + class_destroy(data->g_sensor_class_doubletap); + } +} +#else +static void bma2x2_double_tap_disable(struct bma2x2_data *data) +{ + +} +#endif + +#ifdef CONFIG_SIG_MOTION +static void bma2x2_sig_motion_disable(struct bma2x2_data *data) +{ + if (data->g_sensor_dev) { + sysfs_remove_group(&data->g_sensor_dev->kobj, + &bma2x2_sig_motion_attribute_group); + device_destroy(data->g_sensor_class, 0); + class_destroy(data->g_sensor_class); + } +} +#else +static void bma2x2_sig_motion_disable(struct bma2x2_data *data) +{ + +} +#endif + +static int bma2x2_open_init(struct i2c_client *client, + struct bma2x2_data *data) +{ + int err; + + err = bma2x2_set_bandwidth(client, data->bandwidth); + if (err < 0) { + dev_err(&client->dev, "init bandwidth error\n"); + return err; + } + err = bma2x2_set_range(client, data->range); + if (err < 0) { + dev_err(&client->dev, "init bandwidth error\n"); + return err; + } + return 0; +} + +static int bma2x2_get_interrupt_gpio(const struct bma2x2_data *data, + const unsigned int gpio) +{ + struct i2c_client *client = data->bma2x2_client; + int err; + + if (!gpio_is_valid(gpio)) { + dev_err(&client->dev, + "gpio(%d) is invalid,\n", gpio); + return -EINVAL; + } + + err = gpio_request(gpio, "bma2x2_gpio_int"); + if (err) { + dev_err(&client->dev, + "Unable to request gpio %d, err=%d\n", + gpio, err); + return err; + } + + err = gpio_direction_input(gpio); + if (err) { + dev_err(&client->dev, + "Unable to set gpio direction %d, err=%d\n", + gpio, err); + gpio_free(gpio); + return err; + } + + client->irq = gpio_to_irq(gpio); + dev_dbg(&client->dev, "Interrupt gpio=%d, irq=%d\n", + gpio, client->irq); + + return 0; +} + +static int bma2x2_pinctrl_init(struct bma2x2_data *data) +{ + struct i2c_client *client = data->bma2x2_client; + struct bma2x2_pinctrl_data *pctrl_data; + struct pinctrl *pctrl; + int ret = 0; + + pctrl = devm_pinctrl_get(&client->dev); + if (IS_ERR_OR_NULL(pctrl)) { + ret = PTR_ERR(pctrl); + dev_err(&client->dev, + "Failed to get pin pinctrl, err:%d\n", ret); + goto exit; + } + pctrl_data = devm_kzalloc(&client->dev, + sizeof(*pctrl_data), GFP_KERNEL); + if (!pctrl_data) { + ret = -ENOMEM; + goto exit; + } + pctrl_data->pctrl = pctrl; + + pctrl_data->pins_default = pinctrl_lookup_state(pctrl, "default"); + if (IS_ERR_OR_NULL(pctrl_data->pins_default)) { + ret = PTR_ERR(pctrl_data->pins_default); + dev_err(&client->dev, + "Could not get default pinstate, err:%d\n", ret); + goto exit; + } + /* "sleep" state is optional to compatible with old config */ + pctrl_data->pins_sleep = pinctrl_lookup_state(pctrl, "sleep"); + if (IS_ERR_OR_NULL(pctrl_data->pins_sleep)) { + dev_info(&client->dev, + "Could not get sleep pinstate, err:%ld\n", + PTR_ERR(pctrl_data->pins_sleep)); + pctrl_data->pins_sleep = NULL; + } + data->pctrl_data = pctrl_data; + +exit: + return ret; +} + +static void bma2x2_pinctrl_state(struct bma2x2_data *data, + bool active) +{ + struct device dev = data->bma2x2_client->dev; + int ret; + + if (!data->pctrl_data) + return; + + if (active) { + ret = pinctrl_select_state(data->pctrl_data->pctrl, + data->pctrl_data->pins_default); + if (ret) + dev_info(&dev, + "Select default pinstate err:%d\n", ret); + } else { + if (!data->pctrl_data->pins_sleep) { + dev_dbg(&dev, "Pinstate 'sleep' is not defined\n"); + } else { + ret = pinctrl_select_state(data->pctrl_data->pctrl, + data->pctrl_data->pins_sleep); + if (ret) + dev_info(&dev, "Select sleep pinstate err:%d\n", + ret); + } + } + dev_dbg(&dev, "Select pinctrl state=%d\n", active); +} + +static int bma2x2_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct bma2x2_data *data; + struct input_dev *dev; + struct bst_dev *dev_acc; + struct bma2x2_platform_data *pdata; + struct input_dev *dev_interrupt; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c_check_functionality error\n"); + err = -EPERM; + goto exit; + } + data = kzalloc(sizeof(struct bma2x2_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(*data)); + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + err = -ENOMEM; + goto kfree_exit; + } + err = bma2x2_parse_dt(&client->dev, pdata); + if (err) { + dev_err(&client->dev, "Failed to parse device tree\n"); + err = -EINVAL; + goto pdata_free_exit; + } + } else { + pdata = client->dev.platform_data; + dev_err(&client->dev, "Use platform data\n"); + } + + if (!pdata) { + dev_err(&client->dev, "Cannot get device platform data\n"); + err = -EINVAL; + goto kfree_exit; + } + data->pdata = pdata; + i2c_set_clientdata(client, data); + data->bma2x2_client = client; + + err = bma2x2_power_init(data); + if (err) { + dev_err(&client->dev, "Failed to get sensor regulators\n"); + err = -EINVAL; + goto free_i2c_clientdata_exit; + } + err = bma2x2_power_ctl(data, true); + if (err) { + dev_err(&client->dev, "Failed to enable sensor power\n"); + err = -EINVAL; + goto deinit_power_exit; + } + + RESET_DELAY(); + if (bma2x2_soft_reset(client) < 0) { + dev_err(&client->dev, + "i2c bus write error, pls check HW connection\n"); + err = -EINVAL; + goto disable_power_exit; + } + RESET_DELAY(); + /* read and check chip id */ + if (bma2x2_check_chip_id(client, data) < 0) { + err = -EINVAL; + goto disable_power_exit; + } + mutex_init(&data->value_mutex); + mutex_init(&data->mode_mutex); + mutex_init(&data->enable_mutex); + mutex_init(&data->op_lock); + data->bandwidth = BMA2X2_BW_SET; + data->range = BMA2X2_RANGE_SET; + data->sensitivity = bosch_sensor_range_map[0]; + atomic_set(&data->cal_status, 0); + data->fifo_buf = NULL; + err = bma2x2_open_init(client, data); + if (err < 0) { + err = -EINVAL; + goto disable_power_exit; + } + + if (pdata->int_en) { + /* check interrupt feature enable state */ + err = bma2x2_pinctrl_init(data); + if (err) { + dev_err(&client->dev, + "Failed to init pinctrl err=%d\n", err); + err = -EINVAL; + goto disable_power_exit; + } + + if ((pdata->use_int2 && (!BMA2x2_IS_INT2_ENABLED())) || + (!pdata->use_int2 && (!BMA2x2_IS_INT1_ENABLED()))) { + dev_err(&client->dev, + "Interrupt support is not enabled, int1=%d, int2=%d use_int2=%d\n", + BMA2x2_IS_INT1_ENABLED(), + BMA2x2_IS_INT2_ENABLED(), + pdata->use_int2); + err = -EINVAL; + goto disable_power_exit; + } + + if (pdata->use_int2) { + data->int_flag = pdata->int2_flag; + err = bma2x2_get_interrupt_gpio(data, + pdata->gpio_int2); + } else { + data->int_flag = pdata->int1_flag; + err = bma2x2_get_interrupt_gpio(data, + pdata->gpio_int1); + } + if (err) { + dev_err(&client->dev, + "Failed to get interrupt gpio, err=%d\n", + err); + err = -EINVAL; + goto set_pinctrl_sleep; + } + + data->IRQ = client->irq; + if (!data->int_flag) + data->int_flag = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + dev_dbg(&client->dev, "IRQ=%d, use_int2=%d, int_flag=0x%x\n", + data->IRQ, pdata->use_int2, data->int_flag); + err = request_irq(data->IRQ, bma2x2_irq_handler, + data->int_flag, "bma2x2", data); + if (err) { + dev_err(&client->dev, "Could not request irq\n"); + goto free_interrupt_gpio; + } + disable_irq(data->IRQ); + device_init_wakeup(&client->dev, 1); + + INIT_WORK(&data->irq_work, bma2x2_irq_work_func); + data->fifo_buf = devm_kmalloc(&client->dev, + (MAX_FIFO_F_LEVEL * MAX_FIFO_F_BYTES), GFP_KERNEL); + } + + if (!pdata->use_hrtimer) { + INIT_DELAYED_WORK(&data->work, bma2x2_work_func); + + if (!pdata->int_en || !BMA2x2_IS_NEWDATA_INT_ENABLED()) + INIT_DELAYED_WORK(&data->work, bma2x2_work_func); + + data->data_wq = create_freezable_workqueue("bma2x2_data_work"); + if (!data->data_wq) { + dev_err(&client->dev, "Cannot get create workqueue!\n"); + goto free_irq_exit; + } + } else { + hrtimer_init(&data->accel_timer, + CLOCK_BOOTTIME, HRTIMER_MODE_REL); + data->accel_timer.function = accel_timer_handle; + + init_waitqueue_head(&data->accel_wq); + data->accel_wkp_flag = 0; + data->accel_task = kthread_run(accel_poll_thread, data, + "bma_accel"); + } + + atomic_set(&data->delay, POLL_DEFAULT_INTERVAL_MS); + atomic_set(&data->enable, 0); + + dev = devm_input_allocate_device(&client->dev); + if (!dev) { + dev_err(&client->dev, + "Cannot allocate input device\n"); + err = -ENOMEM; + goto destroy_workqueue_exit; + } + + dev_interrupt = devm_input_allocate_device(&client->dev); + if (!dev_interrupt) { + dev_err(&client->dev, + "Cannot allocate input interrupt device\n"); + err = -ENOMEM; + goto destroy_workqueue_exit; + } + + /* only value events reported */ + dev->name = SENSOR_NAME; + dev->id.bustype = BUS_I2C; + input_set_capability(dev, EV_ABS, ABS_MISC); + input_set_abs_params(dev, ABS_X, ABSMIN, ABSMAX, 0, 0); + input_set_abs_params(dev, ABS_Y, ABSMIN, ABSMAX, 0, 0); + input_set_abs_params(dev, ABS_Z, ABSMIN, ABSMAX, 0, 0); + + input_set_drvdata(dev, data); + err = input_register_device(dev); + if (err < 0) { + dev_err(&client->dev, + "Cannot register input device\n"); + goto free_irq_exit; + } + + /* all interrupt generated events are moved to interrupt input devices*/ + dev_interrupt->name = "bma_interrupt"; + dev_interrupt->id.bustype = BUS_I2C; + input_set_capability(dev_interrupt, EV_REL, + SLOW_NO_MOTION_INTERRUPT); + input_set_capability(dev_interrupt, EV_REL, + LOW_G_INTERRUPT); + input_set_capability(dev_interrupt, EV_REL, + HIGH_G_INTERRUPT); + input_set_capability(dev_interrupt, EV_REL, + SLOP_INTERRUPT); + input_set_capability(dev_interrupt, EV_REL, + DOUBLE_TAP_INTERRUPT); + input_set_capability(dev_interrupt, EV_REL, + SINGLE_TAP_INTERRUPT); + input_set_capability(dev_interrupt, EV_ABS, + ORIENT_INTERRUPT); + input_set_capability(dev_interrupt, EV_ABS, + FLAT_INTERRUPT); + input_set_drvdata(dev_interrupt, data); + + err = input_register_device(dev_interrupt); + if (err < 0) { + dev_err(&client->dev, + "Cannot register input interrupt device\n"); + goto free_irq_exit; + } + + data->dev_interrupt = dev_interrupt; + data->input = dev; + +#ifdef CONFIG_SIG_MOTION + data->g_sensor_class = class_create(THIS_MODULE, "sig_sensor"); + if (IS_ERR(data->g_sensor_class)) { + err = PTR_ERR(data->g_sensor_class); + data->g_sensor_class = NULL; + dev_err(&client->dev, "could not allocate g_sensor_class\n"); + goto free_irq_exit; + } + + data->g_sensor_dev = device_create(data->g_sensor_class, + NULL, 0, "%s", "g_sensor"); + if (unlikely(IS_ERR(data->g_sensor_dev))) { + err = PTR_ERR(data->g_sensor_dev); + data->g_sensor_dev = NULL; + + dev_err(&client->dev, "could not allocate g_sensor_dev\n"); + goto destroy_g_sensor_class_exit; + } + + dev_set_drvdata(data->g_sensor_dev, data); + + err = sysfs_create_group(&data->g_sensor_dev->kobj, + &bma2x2_sig_motion_attribute_group); + if (err < 0) { + dev_err(&client->dev, + "could not create sysfs for sig motion sensor\n"); + goto free_g_sensor_dev_exit; + } +#endif + +#ifdef CONFIG_DOUBLE_TAP + data->g_sensor_class_doubletap = + class_create(THIS_MODULE, "dtap_sensor"); + if (IS_ERR(data->g_sensor_class_doubletap)) { + err = PTR_ERR(data->g_sensor_class_doubletap); + data->g_sensor_class_doubletap = NULL; + dev_err(&client->dev, "could not allocate g_sensor_class_doubletap\n"); + goto remove_sig_motion_sysfs_exit; + } + + data->g_sensor_dev_doubletap = device_create( + data->g_sensor_class_doubletap, + NULL, 0, "%s", "g_sensor"); + if (unlikely(IS_ERR(data->g_sensor_dev_doubletap))) { + err = PTR_ERR(data->g_sensor_dev_doubletap); + data->g_sensor_dev_doubletap = NULL; + + dev_err(&client->dev, "could not allocate g_sensor_dev_doubletap\n"); + goto destroy_dtap_class_exit; + } + + dev_set_drvdata(data->g_sensor_dev_doubletap, data); + + err = sysfs_create_group(&data->g_sensor_dev_doubletap->kobj, + &bma2x2_double_tap_attribute_group); + if (err < 0) { + dev_err(&client->dev, + "could not create sysfs for double tap sensor\n"); + goto destroy_dtap_dev_exit; + } +#endif + + err = sysfs_create_group(&data->input->dev.kobj, + &bma2x2_attribute_group); + if (err < 0) { + dev_err(&client->dev, + "Cannot create sysfs for bma2x2\n"); + goto remove_dtap_sysfs_exit; + } + + dev_acc = bst_allocate_device(); + if (!dev_acc) { + dev_err(&client->dev, + "Cannot allocate bst device\n"); + err = -ENOMEM; + goto remove_bma2x2_sysfs_exit; + } + dev_acc->name = ACC_NAME; + + bst_set_drvdata(dev_acc, data); + + err = bst_register_device(dev_acc); + if (err < 0) { + dev_err(&client->dev, + "Cannot register bst device\n"); + goto bst_free_acc_exit; + } + + data->bst_acc = dev_acc; + err = sysfs_create_group(&data->bst_acc->dev.kobj, + &bma2x2_attribute_group); + + if (err < 0) { + dev_err(&client->dev, + "Cannot create sysfs for bst_acc.\n"); + goto bst_free_exit; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + data->early_suspend.suspend = bma2x2_early_suspend; + data->early_suspend.resume = bma2x2_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + data->ref_count = 0; + data->fifo_datasel = 0; + data->fifo_count = 0; +#ifdef CONFIG_SIG_MOTION + atomic_set(&data->en_sig_motion, 0); +#endif +#ifdef CONFIG_DOUBLE_TAP + atomic_set(&data->en_double_tap, 0); + data->tap_times = 0; + data->tap_time_period = DEFAULT_TAP_JUDGE_PERIOD; + mutex_init(&data->tap_mutex); + setup_timer(&data->tap_timer, bma2x2_tap_timeout_handle, + (unsigned long)data); +#endif + + data->cdev = sensors_cdev; + data->cdev.min_delay = POLL_INTERVAL_MIN_MS * 1000; + data->cdev.delay_msec = pdata->poll_interval; + data->cdev.sensors_enable = bma2x2_cdev_enable; + data->cdev.sensors_poll_delay = bma2x2_cdev_poll_delay; + data->cdev.sensors_calibrate = bma2x2_self_calibration_xyz; + data->cdev.sensors_write_cal_params = bma2x2_write_cal_params; + data->cdev.resolution = sensor_type_map[data->chip_type].resolution; + if (pdata->int_en) { + if (BMA2x2_IS_NEWDATA_INT_ENABLED()) + data->cdev.max_delay = BMA_INT_MAX_DELAY; + data->cdev.sensors_set_latency = bma2x2_cdev_set_latency; + data->cdev.sensors_flush = bma2x2_cdev_flush; + data->cdev.fifo_max_event_count = MAX_FIFO_F_LEVEL; + data->cdev.fifo_reserved_event_count = MAX_FIFO_F_LEVEL; + } + err = sensors_classdev_register(&data->input->dev, &data->cdev); + if (err) { + dev_err(&client->dev, "Create class device file failed!\n"); + err = -EINVAL; + goto remove_bst_acc_sysfs_exit; + } + + if (pdata->use_smd) { + err = bma2x2_register_smd(data, true); + if (err) + dev_err(&client->dev, "Register SMD device failed!\n"); + } + + dev_notice(&client->dev, "BMA2x2 driver probe successfully"); + + bma2x2_pinctrl_state(data, false); + bma2x2_power_ctl(data, false); + return 0; + +remove_bst_acc_sysfs_exit: + sysfs_remove_group(&data->bst_acc->dev.kobj, + &bma2x2_attribute_group); +bst_free_exit: + bst_unregister_device(dev_acc); + +bst_free_acc_exit: + bst_free_device(dev_acc); + +remove_bma2x2_sysfs_exit: + sysfs_remove_group(&data->input->dev.kobj, + &bma2x2_attribute_group); +remove_dtap_sysfs_exit: +#ifdef CONFIG_DOUBLE_TAP +sysfs_remove_group(&data->g_sensor_dev_doubletap->kobj, + &bma2x2_double_tap_attribute_group); +destroy_dtap_dev_exit: + device_destroy(data->g_sensor_dev_doubletap); +destroy_dtap_class_exit: + class_destroy(data->g_sensor_class_doubletap); +remove_sig_motion_sysfs_exit: +#endif + +#ifdef CONFIG_SIG_MOTION +sysfs_remove_group(&data->g_sensor_dev->kobj, + &bma2x2_sig_motion_attribute_group); +free_g_sensor_dev_exit: + device_destroy(data->g_sensor_class, 0); +destroy_g_sensor_class_exit: + class_destroy(data->g_sensor_class); +#endif + +destroy_workqueue_exit: + if (!pdata->use_hrtimer) { + destroy_workqueue(data->data_wq); + } else { + hrtimer_cancel(&data->accel_timer); + kthread_stop(data->accel_task); + } +free_irq_exit: +free_interrupt_gpio: + if (pdata->int_en) { + if (pdata->use_int2) + gpio_free(pdata->gpio_int2); + else + gpio_free(pdata->gpio_int1); + } +set_pinctrl_sleep: + if (pdata->int_en) + bma2x2_pinctrl_state(data, false); +disable_power_exit: + bma2x2_power_ctl(data, false); +deinit_power_exit: + bma2x2_power_deinit(data); +free_i2c_clientdata_exit: + i2c_set_clientdata(client, NULL); +pdata_free_exit: + if (pdata && (client->dev.of_node)) + devm_kfree(&client->dev, pdata); + data->pdata = NULL; +kfree_exit: + kfree(data); +exit: + return err; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void bma2x2_early_suspend(struct early_suspend *h) +{ + struct bma2x2_data *data = + container_of(h, struct bma2x2_data, early_suspend); + + mutex_lock(&data->enable_mutex); + if (atomic_read(&data->enable) == 1) { + bma2x2_set_mode(data->bma2x2_client, BMA2X2_MODE_SUSPEND); + if (!data->pdata->int_en) { + if (!data->pdata->use_hrtimer) + cancel_delayed_work_sync(&data->work); + else + hrtimer_cancel(&data->accel_timer); + } + } + mutex_unlock(&data->enable_mutex); +} + +static void bma2x2_late_resume(struct early_suspend *h) +{ + struct bma2x2_data *data = + container_of(h, struct bma2x2_data, early_suspend); + + mutex_lock(&data->enable_mutex); + if (atomic_read(&data->enable) == 1) { + bma2x2_set_mode(data->bma2x2_client, BMA2X2_MODE_NORMAL); + if (!data->pdata->int_en) { + if (!data->pdata->use_hrtimer) { + queue_delayed_work(data->data_wq, + &data->work, + msecs_to_jiffies(atomic_read(&data->delay))); + } else { + ktime = ktime_set(0, + atomic_read(&bma2x2->delay) * NSEC_PER_MSEC); + hrtimer_start(&bma2x2->accle_timer, + ktime, HRTIMER_MODE_REL); + } + } + } + mutex_unlock(&data->enable_mutex); +} +#endif + +static int bma2x2_remove(struct i2c_client *client) +{ + struct bma2x2_data *data = i2c_get_clientdata(client); + + sensors_classdev_unregister(&data->cdev); + if (data->pdata && data->pdata->use_smd) + bma2x2_register_smd(data, false); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&data->early_suspend); +#endif + + if (data->bst_acc) { + bst_unregister_device(data->bst_acc); + bst_free_device(data->bst_acc); + } + + bma2x2_double_tap_disable(data); + + bma2x2_sig_motion_disable(data); + + if (data->input) + sysfs_remove_group(&data->input->dev.kobj, + &bma2x2_attribute_group); + + bma2x2_set_enable(&client->dev, 0); + if (!data->pdata->use_hrtimer) { + destroy_workqueue(data->data_wq); + } else { + hrtimer_cancel(&data->accel_timer); + kthread_stop(data->accel_task); + } + bma2x2_power_deinit(data); + i2c_set_clientdata(client, NULL); + if (data->pdata && (client->dev.of_node)) + devm_kfree(&client->dev, data->pdata); + data->pdata = NULL; + + kfree(data); + + return 0; +} + +void bma2x2_shutdown(struct i2c_client *client) +{ + struct bma2x2_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->enable_mutex); + bma2x2_set_mode(data->bma2x2_client, BMA2X2_MODE_DEEP_SUSPEND); + mutex_unlock(&data->enable_mutex); +} + +static int bma2x2_store_state(struct i2c_client *client, + struct bma2x2_data *data) +{ + int err; + + err = bma2x2_get_bandwidth(client, &(data->bandwidth)); + if (err < 0) { + dev_err(&client->dev, "get state bandwidth failed\n"); + return err; + } + err = bma2x2_get_range(client, &(data->range)); + if (err < 0) { + dev_err(&client->dev, "get state range failed\n"); + return err; + } + return err; +} + +#ifdef CONFIG_PM +static int bma2x2_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct bma2x2_data *data = i2c_get_clientdata(client); + + data->suspend_state.powerEn = bma2x2_is_power_enabled(data); + bma2x2_set_enable(&client->dev, 0); + return 0; +} + +static int bma2x2_resume(struct i2c_client *client) +{ + struct bma2x2_data *data = i2c_get_clientdata(client); + + if (data->suspend_state.powerEn) + bma2x2_set_enable(&client->dev, 1); + + return 0; +} + +#else + +#define bma2x2_suspend NULL +#define bma2x2_resume NULL + +#endif /* CONFIG_PM */ + +static const struct i2c_device_id bma2x2_id[] = { + { SENSOR_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, bma2x2_id); + +static const struct of_device_id bma2x2_of_match[] = { + { .compatible = "bosch,bma2x2", }, + { }, +}; + +static struct i2c_driver bma2x2_driver = { + .driver = { + .owner = THIS_MODULE, + .name = SENSOR_NAME, + .of_match_table = bma2x2_of_match, + }, + .suspend = bma2x2_suspend, + .resume = bma2x2_resume, + .id_table = bma2x2_id, + .probe = bma2x2_probe, + .remove = bma2x2_remove, + .shutdown = bma2x2_shutdown, +}; + +static int __init BMA2X2_init(void) +{ + return i2c_add_driver(&bma2x2_driver); +} + +static void __exit BMA2X2_exit(void) +{ + i2c_del_driver(&bma2x2_driver); +} + +MODULE_AUTHOR("contact@bosch-sensortec.com"); +MODULE_DESCRIPTION("BMA2X2 ACCELEROMETER SENSOR DRIVER"); +MODULE_LICENSE("GPL v2"); + +module_init(BMA2X2_init); +module_exit(BMA2X2_exit); + diff --git a/drivers/input/misc/bstclass.c b/drivers/input/misc/bstclass.c new file mode 100644 index 000000000000..042eeff89ad5 --- /dev/null +++ b/drivers/input/misc/bstclass.c @@ -0,0 +1,270 @@ +/*! + * @section LICENSE + * (C) Copyright 2013 Bosch Sensortec GmbH All Rights Reserved + * + * This software program is licensed subject to the GNU General + * Public License (GPL).Version 2,June 1991, + * available at http://www.fsf.org/copyleft/gpl.html + * + * @filename bstclass.c + * @date "Wed Feb 19 13:22:52 2014 +0800" + * @id "6d7c0bb" + * + * @brief + * The core code of bst device driver +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bstclass.h" + +static LIST_HEAD(bst_dev_list); + +/* + * bst_mutex protects access to both bst_dev_list and input_handler_list. + * This also causes bst_[un]register_device and bst_[un]register_handler + * be mutually exclusive which simplifies locking in drivers implementing + * input handlers. + */ +static DEFINE_MUTEX(bst_mutex); + + +static void bst_dev_release(struct device *device) +{ + struct bst_dev *dev = to_bst_dev(device); + + if (NULL != dev) + kfree(dev); + module_put(THIS_MODULE); +} + + +#ifdef CONFIG_PM +static int bst_dev_suspend(struct device *dev) +{ + return 0; +} + +static int bst_dev_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops bst_dev_pm_ops = { + .suspend = bst_dev_suspend, + .resume = bst_dev_resume, + .poweroff = bst_dev_suspend, + .restore = bst_dev_resume, +}; +#endif /* CONFIG_PM */ + +static const struct attribute_group *bst_dev_attr_groups[] = { + NULL +}; + +static struct device_type bst_dev_type = { + .groups = bst_dev_attr_groups, + .release = bst_dev_release, +#ifdef CONFIG_PM + .pm = &bst_dev_pm_ops, +#endif +}; + + + +static char *bst_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} + +struct class bst_class = { + .name = "bst", + .owner = THIS_MODULE, + .devnode = bst_devnode, + .dev_release = bst_dev_release, +}; +EXPORT_SYMBOL_GPL(bst_class); + +/** + * bst_allocate_device - allocate memory for new input device + * + * Returns prepared struct bst_dev or NULL. + * + * NOTE: Use bst_free_device() to free devices that have not been + * registered; bst_unregister_device() should be used for already + * registered devices. + */ +struct bst_dev *bst_allocate_device(void) +{ + struct bst_dev *dev; + + dev = kzalloc(sizeof(struct bst_dev), GFP_KERNEL); + if (dev) { + dev->dev.type = &bst_dev_type; + dev->dev.class = &bst_class; + device_initialize(&dev->dev); + mutex_init(&dev->mutex); + INIT_LIST_HEAD(&dev->node); + __module_get(THIS_MODULE); + } + return dev; +} +EXPORT_SYMBOL(bst_allocate_device); + + + +/** + * bst_free_device - free memory occupied by bst_dev structure + * @dev: input device to free + * + * This function should only be used if bst_register_device() + * was not called yet or if it failed. Once device was registered + * use bst_unregister_device() and memory will be freed once last + * reference to the device is dropped. + * + * Device should be allocated by bst_allocate_device(). + * + * NOTE: If there are references to the input device then memory + * will not be freed until last reference is dropped. + */ +void bst_free_device(struct bst_dev *dev) +{ + if (dev) + bst_put_device(dev); +} +EXPORT_SYMBOL(bst_free_device); + +/** + * bst_register_device - register device with input core + * @dev: device to be registered + * + * This function registers device with input core. The device must be + * allocated with bst_allocate_device() and all it's capabilities + * set up before registering. + * If function fails the device must be freed with bst_free_device(). + * Once device has been successfully registered it can be unregistered + * with bst_unregister_device(); bst_free_device() should not be + * called in this case. + */ +int bst_register_device(struct bst_dev *dev) +{ + const char *path; + int error; + + + /* + * If delay and period are pre-set by the driver, then autorepeating + * is handled by the driver itself and we don't do it in input.c. + */ + dev_set_name(&dev->dev, dev->name); + + error = device_add(&dev->dev); + if (error) + return error; + + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); + dev_dbg(&dev->dev, "%s as %s\n", + dev->name ? dev->name : "Unspecified device", + path ? path : "N/A"); + kfree(path); + error = mutex_lock_interruptible(&bst_mutex); + if (error) { + device_del(&dev->dev); + return error; + } + + list_add_tail(&dev->node, &bst_dev_list); + + mutex_unlock(&bst_mutex); + return 0; +} +EXPORT_SYMBOL(bst_register_device); + +/** + * bst_unregister_device - unregister previously registered device + * @dev: device to be unregistered + * + * This function unregisters an input device. Once device is unregistered + * the caller should not try to access it as it may get freed at any moment. + */ +void bst_unregister_device(struct bst_dev *dev) +{ + int error; + + error = mutex_lock_interruptible(&bst_mutex); + list_del_init(&dev->node); + if (!error) + mutex_unlock(&bst_mutex); + device_unregister(&dev->dev); +} +EXPORT_SYMBOL(bst_unregister_device); + +static int bst_open_file(struct inode *inode, struct file *file) +{ + const struct file_operations *old_fops, *new_fops = NULL; + int err; + + /* + * That's _really_ odd. Usually NULL ->open means "nothing special", + * not "no device". Oh, well... + */ + if (!new_fops || !new_fops->open) { + fops_put(new_fops); + err = -ENODEV; + goto out; + } + + old_fops = file->f_op; + file->f_op = new_fops; + + err = new_fops->open(inode, file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); +out: + return err; +} + +static const struct file_operations bst_fops = { + .owner = THIS_MODULE, + .open = bst_open_file, + /*.llseek = noop_llseek,*/ +}; + +static int __init bst_init(void) +{ + int err; + /*bst class register*/ + err = class_register(&bst_class); + if (err) { + pr_err("unable to register bst_dev class\n"); + return err; + } + return err; +} + +static void __exit bst_exit(void) +{ + /*bst class*/ + class_unregister(&bst_class); +} + +/*subsys_initcall(bst_init);*/ + +MODULE_AUTHOR("contact@bosch-sensortec.com"); +MODULE_DESCRIPTION("BST CLASS CORE"); +MODULE_LICENSE("GPL V2"); + +module_init(bst_init); +module_exit(bst_exit); diff --git a/drivers/input/misc/bstclass.h b/drivers/input/misc/bstclass.h new file mode 100644 index 000000000000..50ae68852855 --- /dev/null +++ b/drivers/input/misc/bstclass.h @@ -0,0 +1,78 @@ +/*! + * @section LICENSE + * (C) Copyright 2013 Bosch Sensortec GmbH All Rights Reserved + * + * This software program is licensed subject to the GNU General + * Public License (GPL).Version 2,June 1991, + * available at http://www.fsf.org/copyleft/gpl.html + * + * @filename bstclass.h + * @date "Fri Aug 2 17:41:45 2013 +0800" + * @id "644147c" + * + * @brief + * The core code of bst device driver +*/ + +#ifndef _BSTCLASS_H +#define _BSTCLASS_H + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include + +struct bst_dev { + const char *name; + + int (*open)(struct bst_dev *dev); + void (*close)(struct bst_dev *dev); + struct mutex mutex; + struct device dev; + struct list_head node; +}; + +#define to_bst_dev(d) container_of(d, struct bst_dev, dev) + +struct bst_dev *bst_allocate_device(void); +void bst_free_device(struct bst_dev *dev); + +static inline struct bst_dev *bst_get_device(struct bst_dev *dev) +{ + return dev ? to_bst_dev(get_device(&dev->dev)) : NULL; +} + +static inline void bst_put_device(struct bst_dev *dev) +{ + if (dev) + put_device(&dev->dev); +} + +static inline void *bst_get_drvdata(struct bst_dev *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void bst_set_drvdata(struct bst_dev *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +int __must_check bst_register_device(struct bst_dev *); +void bst_unregister_device(struct bst_dev *); + +void bst_reset_device(struct bst_dev *); + + +extern struct class bst_class; + +#endif -- GitLab From 608c3080b00944769a3cf898d5e54dca64511dea Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 5 Aug 2016 15:37:39 +0200 Subject: [PATCH 1270/5498] x86/mm: Disable preemption during CR3 read+write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5cf0791da5c162ebc14b01eb01631cfa7ed4fa6e upstream. There's a subtle preemption race on UP kernels: Usually current->mm (and therefore mm->pgd) stays the same during the lifetime of a task so it does not matter if a task gets preempted during the read and write of the CR3. But then, there is this scenario on x86-UP: TaskA is in do_exit() and exit_mm() sets current->mm = NULL followed by: -> mmput() -> exit_mmap() -> tlb_finish_mmu() -> tlb_flush_mmu() -> tlb_flush_mmu_tlbonly() -> tlb_flush() -> flush_tlb_mm_range() -> __flush_tlb_up() -> __flush_tlb() -> __native_flush_tlb() At this point current->mm is NULL but current->active_mm still points to the "old" mm. Let's preempt taskA _after_ native_read_cr3() by taskB. TaskB has its own mm so CR3 has changed. Now preempt back to taskA. TaskA has no ->mm set so it borrows taskB's mm and so CR3 remains unchanged. Once taskA gets active it continues where it was interrupted and that means it writes its old CR3 value back. Everything is fine because userland won't need its memory anymore. Now the fun part: Let's preempt taskA one more time and get back to taskB. This time switch_mm() won't do a thing because oldmm (->active_mm) is the same as mm (as per context_switch()). So we remain with a bad CR3 / PGD and return to userland. The next thing that happens is handle_mm_fault() with an address for the execution of its code in userland. handle_mm_fault() realizes that it has a PTE with proper rights so it returns doing nothing. But the CPU looks at the wrong PGD and insists that something is wrong and faults again. And again. And one more time… This pagefault circle continues until the scheduler gets tired of it and puts another task on the CPU. It gets little difficult if the task is a RT task with a high priority. The system will either freeze or it gets fixed by the software watchdog thread which usually runs at RT-max prio. But waiting for the watchdog will increase the latency of the RT task which is no good. Fix this by disabling preemption across the critical code section. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Peter Zijlstra (Intel) Acked-by: Rik van Riel Acked-by: Andy Lutomirski Cc: Borislav Petkov Cc: Borislav Petkov Cc: Brian Gerst Cc: Denys Vlasenko Cc: H. Peter Anvin Cc: Josh Poimboeuf Cc: Linus Torvalds Cc: Mel Gorman Cc: Peter Zijlstra Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-mm@kvack.org Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1470404259-26290-1-git-send-email-bigeasy@linutronix.de [ Prettified the changelog. ] Signed-off-by: Ingo Molnar Cc: Bernhard Kaindl Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/tlbflush.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h index 7e459b7ee708..ac56615c14f2 100644 --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -86,7 +86,14 @@ static inline void cr4_set_bits_and_update_boot(unsigned long mask) static inline void __native_flush_tlb(void) { + /* + * If current->mm == NULL then we borrow a mm which may change during a + * task switch and therefore we must not be preempted while we write CR3 + * back: + */ + preempt_disable(); native_write_cr3(native_read_cr3()); + preempt_enable(); } static inline void __native_flush_tlb_global_irq_disabled(void) -- GitLab From 9b7e3d75f8c3a704d8bba9ee9049e41371489815 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 7 Dec 2015 13:55:52 -0500 Subject: [PATCH 1271/5498] drm/dp/mst: save vcpi with payloads commit 6cecdf7a161d2b909dc7c8979176bbc4f0669968 upstream. This makes it possibly for drivers to find the associated mst_port by looking at the payload allocation table. Signed-off-by: Harry Wentland Reviewed-by: Alex Deucher Link: http://patchwork.freedesktop.org/patch/msgid/1449514552-10236-3-git-send-email-harry.wentland@amd.com Signed-off-by: Daniel Vetter Cc: Kai Heng Feng Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_dp_mst_topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index e8075400fc1b..7113c95f5ad0 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1707,6 +1707,7 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) return -EINVAL; } req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots; + req_payload.vcpi = mgr->proposed_vcpis[i]->vcpi; } else { port = NULL; req_payload.num_slots = 0; @@ -1722,6 +1723,7 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) if (req_payload.num_slots) { drm_dp_create_payload_step1(mgr, mgr->proposed_vcpis[i]->vcpi, &req_payload); mgr->payloads[i].num_slots = req_payload.num_slots; + mgr->payloads[i].vcpi = req_payload.vcpi; } else if (mgr->payloads[i].num_slots) { mgr->payloads[i].num_slots = 0; drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]); -- GitLab From 1512129452b835f342c7e0bffac71172d0ac39f3 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 11 Aug 2016 12:38:55 -0400 Subject: [PATCH 1272/5498] ext4: avoid deadlock when expanding inode size [ Upstream commit 2e81a4eeedcaa66e35f58b81e0755b87057ce392 ] When we need to move xattrs into external xattr block, we call ext4_xattr_block_set() from ext4_expand_extra_isize_ea(). That may end up calling ext4_mark_inode_dirty() again which will recurse back into the inode expansion code leading to deadlocks. Protect from recursion using EXT4_STATE_NO_EXPAND inode flag and move its management into ext4_expand_extra_isize_ea() since its manipulation is safe there (due to xattr_sem) from possible races with ext4_xattr_set_handle() which plays with it as well. Signed-off-by: Jan Kara Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/inode.c | 2 -- fs/ext4/xattr.c | 19 +++++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index a9117695da6c..c2434d72681e 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4895,8 +4895,6 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) sbi->s_want_extra_isize, iloc, handle); if (ret) { - ext4_set_inode_state(inode, - EXT4_STATE_NO_EXPAND); if (mnt_count != le16_to_cpu(sbi->s_es->s_mnt_count)) { ext4_warning(inode->i_sb, diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index c71329f05b26..c57c83806fb9 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1291,11 +1291,13 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize); down_write(&EXT4_I(inode)->xattr_sem); + /* + * Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty + */ + ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); retry: - if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) { - up_write(&EXT4_I(inode)->xattr_sem); - return 0; - } + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) + goto out; header = IHDR(inode, raw_inode); entry = IFIRST(header); @@ -1324,8 +1326,7 @@ retry: (void *)header, total_ino, inode->i_sb->s_blocksize); EXT4_I(inode)->i_extra_isize = new_extra_isize; - error = 0; - goto cleanup; + goto out; } /* @@ -1485,6 +1486,8 @@ retry: kfree(bs); } brelse(bh); +out: + ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); up_write(&EXT4_I(inode)->xattr_sem); return 0; @@ -1496,6 +1499,10 @@ cleanup: kfree(is); kfree(bs); brelse(bh); + /* + * We deliberately leave EXT4_STATE_NO_EXPAND set here since inode + * size expansion failed. + */ up_write(&EXT4_I(inode)->xattr_sem); return error; } -- GitLab From db27397942aa3f3c7b02a28d52878468923834bd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 14 Sep 2017 02:00:54 +0300 Subject: [PATCH 1273/5498] sctp: potential read out of bounds in sctp_ulpevent_type_enabled() [ Upstream commit fa5f7b51fc3080c2b195fa87c7eca7c05e56f673 ] This code causes a static checker warning because Smatch doesn't trust anything that comes from skb->data. I've reviewed this code and I do think skb->data can be controlled by the user here. The sctp_event_subscribe struct has 13 __u8 fields and we want to see if ours is non-zero. sn_type can be any value in the 0-USHRT_MAX range. We're subtracting SCTP_SN_TYPE_BASE which is 1 << 15 so we could read either before the start of the struct or after the end. This is a very old bug and it's surprising that it would go undetected for so long but my theory is that it just doesn't have a big impact so it would be hard to notice. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/sctp/ulpevent.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index cccdcfd14973..f348c736e6e0 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -141,8 +141,12 @@ __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event); static inline int sctp_ulpevent_type_enabled(__u16 sn_type, struct sctp_event_subscribe *mask) { + int offset = sn_type - SCTP_SN_TYPE_BASE; char *amask = (char *) mask; - return amask[sn_type - SCTP_SN_TYPE_BASE]; + + if (offset >= sizeof(struct sctp_event_subscribe)) + return 0; + return amask[offset]; } /* Given an event subscription, is this event enabled? */ -- GitLab From 63692f8b33bd8167169e600350b1d5a3102a71ad Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Fri, 15 Sep 2017 14:37:38 +0100 Subject: [PATCH 1274/5498] bpf/verifier: reject BPF_ALU64|BPF_END [ Upstream commit e67b8a685c7c984e834e3181ef4619cd7025a136 ] Neither ___bpf_prog_run nor the JITs accept it. Also adds a new test case. Fixes: 17a5267067f3 ("bpf: verifier (add verifier core)") Signed-off-by: Edward Cree Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index eac45b688c08..76cd19dcb7f0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -932,7 +932,8 @@ static int check_alu_op(struct reg_state *regs, struct bpf_insn *insn) } } else { if (insn->src_reg != BPF_REG_0 || insn->off != 0 || - (insn->imm != 16 && insn->imm != 32 && insn->imm != 64)) { + (insn->imm != 16 && insn->imm != 32 && insn->imm != 64) || + BPF_CLASS(insn->code) == BPF_ALU64) { verbose("BPF_END uses reserved fields\n"); return -EINVAL; } -- GitLab From e4ffdf9ead59a909f2824a4270356909d6d64380 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 14 Sep 2017 17:14:41 -0400 Subject: [PATCH 1275/5498] packet: hold bind lock when rebinding to fanout hook [ Upstream commit 008ba2a13f2d04c947adc536d19debb8fe66f110 ] Packet socket bind operations must hold the po->bind_lock. This keeps po->running consistent with whether the socket is actually on a ptype list to receive packets. fanout_add unbinds a socket and its packet_rcv/tpacket_rcv call, then binds the fanout object to receive through packet_rcv_fanout. Make it hold the po->bind_lock when testing po->running and rebinding. Else, it can race with other rebind operations, such as that in packet_set_ring from packet_rcv to tpacket_rcv. Concurrent updates can result in a socket being added to a fanout group twice, causing use-after-free KASAN bug reports, among others. Reported independently by both trinity and syzkaller. Verified that the syzkaller reproducer passes after this patch. Fixes: dc99f600698d ("packet: Add fanout support.") Reported-by: nixioaming Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/packet/af_packet.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 7effa07c4491..9f1820e3c8b1 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1429,9 +1429,6 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) return -EINVAL; } - if (!po->running) - return -EINVAL; - if (po->fanout) return -EALREADY; @@ -1469,7 +1466,10 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) list_add(&match->list, &fanout_list); } err = -EINVAL; - if (match->type == type && + + spin_lock(&po->bind_lock); + if (po->running && + match->type == type && match->prot_hook.type == po->prot_hook.type && match->prot_hook.dev == po->prot_hook.dev) { err = -ENOSPC; @@ -1481,6 +1481,13 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) err = 0; } } + spin_unlock(&po->bind_lock); + + if (err && !atomic_read(&match->sk_ref)) { + list_del(&match->list); + kfree(match); + } + out: mutex_unlock(&fanout_mutex); return err; -- GitLab From ba554fc7dc2b4189ccfc412853c578cf0d613c3b Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 19 Sep 2017 21:49:55 -0400 Subject: [PATCH 1276/5498] isdn/i4l: fetch the ppp_write buffer in one shot [ Upstream commit 02388bf87f72e1d47174cd8f81c34443920eb5a0 ] In isdn_ppp_write(), the header (i.e., protobuf) of the buffer is fetched twice from userspace. The first fetch is used to peek at the protocol of the message and reset the huptimer if necessary; while the second fetch copies in the whole buffer. However, given that buf resides in userspace memory, a user process can race to change its memory content across fetches. By doing so, we can either avoid resetting the huptimer for any type of packets (by first setting proto to PPP_LCP and later change to the actual type) or force resetting the huptimer for LCP packets. This patch changes this double-fetch behavior into two single fetches decided by condition (lp->isdn_device < 0 || lp->isdn_channel <0). A more detailed discussion can be found at https://marc.info/?l=linux-kernel&m=150586376926123&w=2 Signed-off-by: Meng Xu Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_ppp.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index bf3fbd00a091..64b586458d3d 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -828,7 +828,6 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) isdn_net_local *lp; struct ippp_struct *is; int proto; - unsigned char protobuf[4]; is = file->private_data; @@ -842,24 +841,28 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) if (!lp) printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n"); else { - /* - * Don't reset huptimer for - * LCP packets. (Echo requests). - */ - if (copy_from_user(protobuf, buf, 4)) - return -EFAULT; - proto = PPP_PROTOCOL(protobuf); - if (proto != PPP_LCP) - lp->huptimer = 0; + if (lp->isdn_device < 0 || lp->isdn_channel < 0) { + unsigned char protobuf[4]; + /* + * Don't reset huptimer for + * LCP packets. (Echo requests). + */ + if (copy_from_user(protobuf, buf, 4)) + return -EFAULT; + + proto = PPP_PROTOCOL(protobuf); + if (proto != PPP_LCP) + lp->huptimer = 0; - if (lp->isdn_device < 0 || lp->isdn_channel < 0) return 0; + } if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && lp->dialstate == 0 && (lp->flags & ISDN_NET_CONNECTED)) { unsigned short hl; struct sk_buff *skb; + unsigned char *cpy_buf; /* * we need to reserve enough space in front of * sk_buff. old call to dev_alloc_skb only reserved @@ -872,11 +875,21 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) return count; } skb_reserve(skb, hl); - if (copy_from_user(skb_put(skb, count), buf, count)) + cpy_buf = skb_put(skb, count); + if (copy_from_user(cpy_buf, buf, count)) { kfree_skb(skb); return -EFAULT; } + + /* + * Don't reset huptimer for + * LCP packets. (Echo requests). + */ + proto = PPP_PROTOCOL(cpy_buf); + if (proto != PPP_LCP) + lp->huptimer = 0; + if (is->debug & 0x40) { printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); -- GitLab From 6132b4ff8fbd56613a8342e75a8b82b6b011a837 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Tue, 26 Sep 2017 15:14:29 +0300 Subject: [PATCH 1277/5498] vti: fix use after free in vti_tunnel_xmit/vti6_tnl_xmit [ Upstream commit 36f6ee22d2d66046e369757ec6bbe1c482957ba6 ] When running LTP IPsec tests, KASan might report: BUG: KASAN: use-after-free in vti_tunnel_xmit+0xeee/0xff0 [ip_vti] Read of size 4 at addr ffff880dc6ad1980 by task swapper/0/0 ... Call Trace: dump_stack+0x63/0x89 print_address_description+0x7c/0x290 kasan_report+0x28d/0x370 ? vti_tunnel_xmit+0xeee/0xff0 [ip_vti] __asan_report_load4_noabort+0x19/0x20 vti_tunnel_xmit+0xeee/0xff0 [ip_vti] ? vti_init_net+0x190/0x190 [ip_vti] ? save_stack_trace+0x1b/0x20 ? save_stack+0x46/0xd0 dev_hard_start_xmit+0x147/0x510 ? icmp_echo.part.24+0x1f0/0x210 __dev_queue_xmit+0x1394/0x1c60 ... Freed by task 0: save_stack_trace+0x1b/0x20 save_stack+0x46/0xd0 kasan_slab_free+0x70/0xc0 kmem_cache_free+0x81/0x1e0 kfree_skbmem+0xb1/0xe0 kfree_skb+0x75/0x170 kfree_skb_list+0x3e/0x60 __dev_queue_xmit+0x1298/0x1c60 dev_queue_xmit+0x10/0x20 neigh_resolve_output+0x3a8/0x740 ip_finish_output2+0x5c0/0xe70 ip_finish_output+0x4ba/0x680 ip_output+0x1c1/0x3a0 xfrm_output_resume+0xc65/0x13d0 xfrm_output+0x1e4/0x380 xfrm4_output_finish+0x5c/0x70 Can be fixed if we get skb->len before dst_output(). Fixes: b9959fd3b0fa ("vti: switch to new ip tunnel code") Fixes: 22e1b23dafa8 ("vti6: Support inter address family tunneling.") Signed-off-by: Alexey Kodanev Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_vti.c | 3 ++- net/ipv6/ip6_vti.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 15046aec8484..1b8a82f0f6ec 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -156,6 +156,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, struct ip_tunnel_parm *parms = &tunnel->parms; struct dst_entry *dst = skb_dst(skb); struct net_device *tdev; /* Device to other host */ + int pkt_len = skb->len; int err; if (!dst) { @@ -199,7 +200,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, err = dst_output(skb); if (net_xmit_eval(err) == 0) - err = skb->len; + err = pkt_len; iptunnel_xmit_stats(err, &dev->stats, dev->tstats); return NETDEV_TX_OK; diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 591e2355cc9e..a11083d37789 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -416,6 +416,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) struct net_device_stats *stats = &t->dev->stats; struct dst_entry *dst = skb_dst(skb); struct net_device *tdev; + int pkt_len = skb->len; int err = -1; if (!dst) @@ -450,7 +451,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); u64_stats_update_begin(&tstats->syncp); - tstats->tx_bytes += skb->len; + tstats->tx_bytes += pkt_len; tstats->tx_packets++; u64_stats_update_end(&tstats->syncp); } else { -- GitLab From 88db37f8e4de97702da5f53c09cdbff0382f9bdd Mon Sep 17 00:00:00 2001 From: Ridge Kennedy Date: Wed, 22 Feb 2017 14:59:49 +1300 Subject: [PATCH 1278/5498] l2tp: Avoid schedule while atomic in exit_net [ Upstream commit 12d656af4e3d2781b9b9f52538593e1717e7c979 ] While destroying a network namespace that contains a L2TP tunnel a "BUG: scheduling while atomic" can be observed. Enabling lockdep shows that this is happening because l2tp_exit_net() is calling l2tp_tunnel_closeall() (via l2tp_tunnel_delete()) from within an RCU critical section. l2tp_exit_net() takes rcu_read_lock_bh() << list_for_each_entry_rcu() >> l2tp_tunnel_delete() l2tp_tunnel_closeall() __l2tp_session_unhash() synchronize_rcu() << Illegal inside RCU critical section >> BUG: sleeping function called from invalid context in_atomic(): 1, irqs_disabled(): 0, pid: 86, name: kworker/u16:2 INFO: lockdep is turned off. CPU: 2 PID: 86 Comm: kworker/u16:2 Tainted: G W O 4.4.6-at1 #2 Hardware name: Xen HVM domU, BIOS 4.6.1-xs125300 05/09/2016 Workqueue: netns cleanup_net 0000000000000000 ffff880202417b90 ffffffff812b0013 ffff880202410ac0 ffffffff81870de8 ffff880202417bb8 ffffffff8107aee8 ffffffff81870de8 0000000000000c51 0000000000000000 ffff880202417be0 ffffffff8107b024 Call Trace: [] dump_stack+0x85/0xc2 [] ___might_sleep+0x148/0x240 [] __might_sleep+0x44/0x80 [] synchronize_sched+0x2d/0xe0 [] ? trace_hardirqs_on+0xd/0x10 [] ? __local_bh_enable_ip+0x6b/0xc0 [] ? _raw_spin_unlock_bh+0x30/0x40 [] __l2tp_session_unhash+0x172/0x220 [] ? __l2tp_session_unhash+0x87/0x220 [] l2tp_tunnel_closeall+0x9b/0x140 [] l2tp_tunnel_delete+0x14/0x60 [] l2tp_exit_net+0x110/0x270 [] ? l2tp_exit_net+0x9c/0x270 [] ops_exit_list.isra.6+0x33/0x60 [] cleanup_net+0x1b6/0x280 ... This bug can easily be reproduced with a few steps: $ sudo unshare -n bash # Create a shell in a new namespace # ip link set lo up # ip addr add 127.0.0.1 dev lo # ip l2tp add tunnel remote 127.0.0.1 local 127.0.0.1 tunnel_id 1 \ peer_tunnel_id 1 udp_sport 50000 udp_dport 50000 # ip l2tp add session name foo tunnel_id 1 session_id 1 \ peer_session_id 1 # ip link set foo up # exit # Exit the shell, in turn exiting the namespace $ dmesg ... [942121.089216] BUG: scheduling while atomic: kworker/u16:3/13872/0x00000200 ... To fix this, move the call to l2tp_tunnel_closeall() out of the RCU critical section, and instead call it from l2tp_tunnel_del_work(), which is running from the l2tp_wq workqueue. Fixes: 2b551c6e7d5b ("l2tp: close sessions before initiating tunnel delete") Signed-off-by: Ridge Kennedy Acked-by: Guillaume Nault Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/l2tp/l2tp_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 508154a04558..3da8fe8ccf96 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1317,6 +1317,9 @@ static void l2tp_tunnel_del_work(struct work_struct *work) struct sock *sk = NULL; tunnel = container_of(work, struct l2tp_tunnel, del_work); + + l2tp_tunnel_closeall(tunnel); + sk = l2tp_tunnel_sock_lookup(tunnel); if (!sk) goto out; @@ -1642,7 +1645,6 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) { l2tp_tunnel_inc_refcount(tunnel); - l2tp_tunnel_closeall(tunnel); if (false == queue_work(l2tp_wq, &tunnel->del_work)) { l2tp_tunnel_dec_refcount(tunnel); return 1; -- GitLab From 5eaafe649af94c12164bb43f81d85cbc7cfe6778 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Tue, 26 Sep 2017 16:16:43 +0200 Subject: [PATCH 1279/5498] l2tp: fix race condition in l2tp_tunnel_delete [ Upstream commit 62b982eeb4589b2e6d7c01a90590e3a4c2b2ca19 ] If we try to delete the same tunnel twice, the first delete operation does a lookup (l2tp_tunnel_get), finds the tunnel, calls l2tp_tunnel_delete, which queues it for deletion by l2tp_tunnel_del_work. The second delete operation also finds the tunnel and calls l2tp_tunnel_delete. If the workqueue has already fired and started running l2tp_tunnel_del_work, then l2tp_tunnel_delete will queue the same tunnel a second time, and try to free the socket again. Add a dead flag to prevent firing the workqueue twice. Then we can remove the check of queue_work's result that was meant to prevent that race but doesn't. Reproducer: ip l2tp add tunnel tunnel_id 3000 peer_tunnel_id 4000 local 192.168.0.2 remote 192.168.0.1 encap udp udp_sport 5000 udp_dport 6000 ip l2tp add session name l2tp1 tunnel_id 3000 session_id 1000 peer_session_id 2000 ip link set l2tp1 up ip l2tp del tunnel tunnel_id 3000 ip l2tp del tunnel tunnel_id 3000 Fixes: f8ccac0e4493 ("l2tp: put tunnel socket release on a workqueue") Reported-by: Jianlin Shi Signed-off-by: Sabrina Dubroca Acked-by: Guillaume Nault Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/l2tp/l2tp_core.c | 10 ++++------ net/l2tp/l2tp_core.h | 5 ++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 3da8fe8ccf96..b5f3c175331c 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1642,14 +1642,12 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); /* This function is used by the netlink TUNNEL_DELETE command. */ -int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) +void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) { - l2tp_tunnel_inc_refcount(tunnel); - if (false == queue_work(l2tp_wq, &tunnel->del_work)) { - l2tp_tunnel_dec_refcount(tunnel); - return 1; + if (!test_and_set_bit(0, &tunnel->dead)) { + l2tp_tunnel_inc_refcount(tunnel); + queue_work(l2tp_wq, &tunnel->del_work); } - return 0; } EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index e9ec7d2cc357..1c1a033a546a 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -169,6 +169,9 @@ struct l2tp_tunnel_cfg { struct l2tp_tunnel { int magic; /* Should be L2TP_TUNNEL_MAGIC */ + + unsigned long dead; + struct rcu_head rcu; rwlock_t hlist_lock; /* protect session_hlist */ struct hlist_head session_hlist[L2TP_HASH_SIZE]; @@ -252,7 +255,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp); void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); -int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); +void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, -- GitLab From b0763909b4538894bb47614656c75f2a233c40d2 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 26 Sep 2017 12:19:37 -0400 Subject: [PATCH 1280/5498] packet: in packet_do_bind, test fanout with bind_lock held [ Upstream commit 4971613c1639d8e5f102c4e797c3bf8f83a5a69e ] Once a socket has po->fanout set, it remains a member of the group until it is destroyed. The prot_hook must be constant and identical across sockets in the group. If fanout_add races with packet_do_bind between the test of po->fanout and taking the lock, the bind call may make type or dev inconsistent with that of the fanout group. Hold po->bind_lock when testing po->fanout to avoid this race. I had to introduce artificial delay (local_bh_enable) to actually observe the race. Fixes: dc99f600698d ("packet: Add fanout support.") Signed-off-by: Willem de Bruijn Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/packet/af_packet.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9f1820e3c8b1..dc474fd81d71 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2662,13 +2662,15 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, int ret = 0; bool unlisted = false; - if (po->fanout) - return -EINVAL; - lock_sock(sk); spin_lock(&po->bind_lock); rcu_read_lock(); + if (po->fanout) { + ret = -EINVAL; + goto out_unlock; + } + if (name) { dev = dev_get_by_name_rcu(sock_net(sk), name); if (!dev) { -- GitLab From bc8a5a45208d335de143643e51358c8299bce0f3 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Tue, 26 Sep 2017 17:38:50 -0700 Subject: [PATCH 1281/5498] net: Set sk_prot_creator when cloning sockets to the right proto [ Upstream commit 9d538fa60bad4f7b23193c89e843797a1cf71ef3 ] sk->sk_prot and sk->sk_prot_creator can differ when the app uses IPV6_ADDRFORM (transforming an IPv6-socket to an IPv4-one). Which is why sk_prot_creator is there to make sure that sk_prot_free() does the kmem_cache_free() on the right kmem_cache slab. Now, if such a socket gets transformed back to a listening socket (using connect() with AF_UNSPEC) we will allocate an IPv4 tcp_sock through sk_clone_lock() when a new connection comes in. But sk_prot_creator will still point to the IPv6 kmem_cache (as everything got copied in sk_clone_lock()). When freeing, we will thus put this memory back into the IPv6 kmem_cache although it was allocated in the IPv4 cache. I have seen memory corruption happening because of this. With slub-debugging and MEMCG_KMEM enabled this gives the warning "cache_from_obj: Wrong slab cache. TCPv6 but object is from TCP" A C-program to trigger this: void main(void) { int fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); int new_fd, newest_fd, client_fd; struct sockaddr_in6 bind_addr; struct sockaddr_in bind_addr4, client_addr1, client_addr2; struct sockaddr unsp; int val; memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin6_family = AF_INET6; bind_addr.sin6_port = ntohs(42424); memset(&client_addr1, 0, sizeof(client_addr1)); client_addr1.sin_family = AF_INET; client_addr1.sin_port = ntohs(42424); client_addr1.sin_addr.s_addr = inet_addr("127.0.0.1"); memset(&client_addr2, 0, sizeof(client_addr2)); client_addr2.sin_family = AF_INET; client_addr2.sin_port = ntohs(42421); client_addr2.sin_addr.s_addr = inet_addr("127.0.0.1"); memset(&unsp, 0, sizeof(unsp)); unsp.sa_family = AF_UNSPEC; bind(fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); listen(fd, 5); client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); connect(client_fd, (struct sockaddr *)&client_addr1, sizeof(client_addr1)); new_fd = accept(fd, NULL, NULL); close(fd); val = AF_INET; setsockopt(new_fd, SOL_IPV6, IPV6_ADDRFORM, &val, sizeof(val)); connect(new_fd, &unsp, sizeof(unsp)); memset(&bind_addr4, 0, sizeof(bind_addr4)); bind_addr4.sin_family = AF_INET; bind_addr4.sin_port = ntohs(42421); bind(new_fd, (struct sockaddr *)&bind_addr4, sizeof(bind_addr4)); listen(new_fd, 5); client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); connect(client_fd, (struct sockaddr *)&client_addr2, sizeof(client_addr2)); newest_fd = accept(new_fd, NULL, NULL); close(new_fd); close(client_fd); close(new_fd); } As far as I can see, this bug has been there since the beginning of the git-days. Signed-off-by: Christoph Paasch Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/sock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/sock.c b/net/core/sock.c index 8c8fb6ab8a26..55f35765c6e0 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1488,6 +1488,8 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) sock_copy(newsk, sk); + newsk->sk_prot_creator = sk->sk_prot; + /* SANITY */ get_net(sock_net(newsk)); sk_node_init(&newsk->sk_node); -- GitLab From 4a8a916d932e719fba2725ea144e5c5803ee6cb0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 19 Oct 2017 14:55:29 +0200 Subject: [PATCH 1282/5498] Revert "bsg-lib: don't free job in bsg_prepare_job" This reverts commit d9100405a20a71dd620843e0380e38fc50731108 which was commit f507b54dccfd8000c517d740bc45f20c74532d18 upstream. Ben reports: That function doesn't exist here (it was introduced in 4.13). Instead, this backport has modified bsg_create_job(), creating a leak. Please revert this on the 3.18, 4.4 and 4.9 stable branches. So I'm dropping it from here. Reported-by: Ben Hutchings Cc: Christoph Hellwig Cc: Ming Lei Cc: Jens Axboe Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- block/bsg-lib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/bsg-lib.c b/block/bsg-lib.c index 341b8d858e67..650f427d915b 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -147,6 +147,7 @@ static int bsg_create_job(struct device *dev, struct request *req) failjob_rls_rqst_payload: kfree(job->request_payload.sg_list); failjob_rls_job: + kfree(job); return -ENOMEM; } -- GitLab From 981199076d96effa8f26321372240a2dbaa3561e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 1 Mar 2017 16:23:30 +0100 Subject: [PATCH 1283/5498] locking/lockdep: Add nest_lock integrity test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7fb4a2cea6b18dab56d609530d077f168169ed6b ] Boqun reported that hlock->references can overflow. Add a debug test for that to generate a clear error when this happens. Without this, lockdep is likely to report a mysterious failure on unlock. Reported-by: Boqun Feng Signed-off-by: Peter Zijlstra (Intel) Cc: Andrew Morton Cc: Chris Wilson Cc: Linus Torvalds Cc: Nicolai Hähnle Cc: Paul E. McKenney Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/locking/lockdep.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 88d0d4420ad2..3467618cc0ea 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -3107,10 +3107,17 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, if (depth) { hlock = curr->held_locks + depth - 1; if (hlock->class_idx == class_idx && nest_lock) { - if (hlock->references) + if (hlock->references) { + /* + * Check: unsigned int references:12, overflow. + */ + if (DEBUG_LOCKS_WARN_ON(hlock->references == (1 << 12)-1)) + return 0; + hlock->references++; - else + } else { hlock->references = 2; + } return 1; } -- GitLab From 5961adefd0f361deaf925d3c4bff80bc9e87de50 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 1 Mar 2017 10:15:29 +0100 Subject: [PATCH 1284/5498] watchdog: kempld: fix gcc-4.3 build [ Upstream commit 3736d4eb6af37492aeded7fec0072dedd959c842 ] gcc-4.3 can't decide whether the constant value in kempld_prescaler[PRESCALER_21] is built-time constant or not, and gets confused by the logic in do_div(): drivers/watchdog/kempld_wdt.o: In function `kempld_wdt_set_stage_timeout': kempld_wdt.c:(.text.kempld_wdt_set_stage_timeout+0x130): undefined reference to `__aeabi_uldivmod' This adds a call to ACCESS_ONCE() to force it to not consider it to be constant, and leaves the more efficient normal case in place for modern compilers, using an #ifdef to annotate why we do this hack. Signed-off-by: Arnd Bergmann Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/kempld_wdt.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c index d9c1a1601926..e88d1346be5c 100644 --- a/drivers/watchdog/kempld_wdt.c +++ b/drivers/watchdog/kempld_wdt.c @@ -140,12 +140,19 @@ static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data, unsigned int timeout) { struct kempld_device_data *pld = wdt_data->pld; - u32 prescaler = kempld_prescaler[PRESCALER_21]; + u32 prescaler; u64 stage_timeout64; u32 stage_timeout; u32 remainder; u8 stage_cfg; +#if GCC_VERSION < 40400 + /* work around a bug compiling do_div() */ + prescaler = READ_ONCE(kempld_prescaler[PRESCALER_21]); +#else + prescaler = kempld_prescaler[PRESCALER_21]; +#endif + if (!stage) return -EINVAL; -- GitLab From ef0423167be6c11d68bfcbd1190f508727f1d042 Mon Sep 17 00:00:00 2001 From: Franck Demathieu Date: Thu, 23 Feb 2017 10:48:55 +0100 Subject: [PATCH 1285/5498] irqchip/crossbar: Fix incorrect type of local variables [ Upstream commit b28ace12661fbcfd90959c1e84ff5a85113a82a1 ] The max and entry variables are unsigned according to the dt-bindings. Fix following 3 sparse issues (-Wtypesign): drivers/irqchip/irq-crossbar.c:222:52: warning: incorrect type in argument 3 (different signedness) drivers/irqchip/irq-crossbar.c:222:52: expected unsigned int [usertype] *out_value drivers/irqchip/irq-crossbar.c:222:52: got int * drivers/irqchip/irq-crossbar.c:245:56: warning: incorrect type in argument 4 (different signedness) drivers/irqchip/irq-crossbar.c:245:56: expected unsigned int [usertype] *out_value drivers/irqchip/irq-crossbar.c:245:56: got int * drivers/irqchip/irq-crossbar.c:263:56: warning: incorrect type in argument 4 (different signedness) drivers/irqchip/irq-crossbar.c:263:56: expected unsigned int [usertype] *out_value drivers/irqchip/irq-crossbar.c:263:56: got int * Signed-off-by: Franck Demathieu Cc: marc.zyngier@arm.com Cc: jason@lakedaemon.net Link: http://lkml.kernel.org/r/20170223094855.6546-1-fdemathieu@gmail.com Signed-off-by: Thomas Gleixner Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-crossbar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index bbbaf5de65d2..c03773bf02c7 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -175,7 +175,8 @@ static const struct irq_domain_ops routable_irq_domain_ops = { static int __init crossbar_of_init(struct device_node *node) { - int i, size, max = 0, reserved = 0, entry; + int i, size, reserved = 0; + u32 max = 0, entry; const __be32 *irqsr; int ret = -ENOMEM; -- GitLab From 9a5043741109f5318638cc79de0a1a28b78b25b9 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Thu, 23 Feb 2017 17:08:54 -0800 Subject: [PATCH 1286/5498] netfilter: nf_ct_expect: Change __nf_ct_expect_check() return value. [ Upstream commit 4b86c459c7bee3acaf92f0e2b4c6ac803eaa1a58 ] Commit 4dee62b1b9b4 ("netfilter: nf_ct_expect: nf_ct_expect_insert() returns void") inadvertently changed the successful return value of nf_ct_expect_related_report() from 0 to 1 due to __nf_ct_expect_check() returning 1 on success. Prevent this regression in the future by changing the return value of __nf_ct_expect_check() to 0 on success. Signed-off-by: Jarno Rajahalme Acked-by: Joe Stringer Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_expect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 26af45193ab7..7ebdd7ff8ec0 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -392,7 +392,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect) struct net *net = nf_ct_exp_net(expect); struct hlist_node *next; unsigned int h; - int ret = 1; + int ret = 0; if (!master_help) { ret = -ESHUTDOWN; @@ -442,7 +442,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, spin_lock_bh(&nf_conntrack_expect_lock); ret = __nf_ct_expect_check(expect); - if (ret <= 0) + if (ret < 0) goto out; ret = nf_ct_expect_insert(expect); -- GitLab From 21bf570719a5e5c9c8082601175fa32be83e43d5 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 21 Feb 2017 07:34:00 +0100 Subject: [PATCH 1287/5498] iio: adc: xilinx: Fix error handling [ Upstream commit ca1c39ef76376b67303d01f94fe98bb68bb3861a ] Reorder error handling labels in order to match the way resources have been allocated. Signed-off-by: Christophe JAILLET Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/xilinx-xadc-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index ce93bd8e3f68..a483747cdc9b 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1223,7 +1223,7 @@ static int xadc_probe(struct platform_device *pdev) ret = xadc->ops->setup(pdev, indio_dev, irq); if (ret) - goto err_free_samplerate_trigger; + goto err_clk_disable_unprepare; ret = request_threaded_irq(irq, xadc->ops->interrupt_handler, xadc->ops->threaded_interrupt_handler, @@ -1284,6 +1284,8 @@ static int xadc_probe(struct platform_device *pdev) err_free_irq: free_irq(irq, indio_dev); +err_clk_disable_unprepare: + clk_disable_unprepare(xadc->clk); err_free_samplerate_trigger: if (xadc->ops->flags & XADC_FLAGS_BUFFERED) iio_trigger_free(xadc->samplerate_trigger); @@ -1293,8 +1295,6 @@ err_free_convst_trigger: err_triggered_buffer_cleanup: if (xadc->ops->flags & XADC_FLAGS_BUFFERED) iio_triggered_buffer_cleanup(indio_dev); -err_clk_disable_unprepare: - clk_disable_unprepare(xadc->clk); err_device_free: kfree(indio_dev->channels); -- GitLab From 2d9e4b59c8fa8e0983bff7432ddf26c42762cf6f Mon Sep 17 00:00:00 2001 From: Robbie Ko Date: Thu, 5 Jan 2017 16:24:55 +0800 Subject: [PATCH 1288/5498] Btrfs: send, fix failure to rename top level inode due to name collision [ Upstream commit 4dd9920d991745c4a16f53a8f615f706fbe4b3f7 ] Under certain situations, an incremental send operation can fail due to a premature attempt to create a new top level inode (a direct child of the subvolume/snapshot root) whose name collides with another inode that was removed from the send snapshot. Consider the following example scenario. Parent snapshot: . (ino 256, gen 8) |---- a1/ (ino 257, gen 9) |---- a2/ (ino 258, gen 9) Send snapshot: . (ino 256, gen 3) |---- a2/ (ino 257, gen 7) In this scenario, when receiving the incremental send stream, the btrfs receive command fails like this (ran in verbose mode, -vv argument): rmdir a1 mkfile o257-7-0 rename o257-7-0 -> a2 ERROR: rename o257-7-0 -> a2 failed: Is a directory What happens when computing the incremental send stream is: 1) An operation to remove the directory with inode number 257 and generation 9 is issued. 2) An operation to create the inode with number 257 and generation 7 is issued. This creates the inode with an orphanized name of "o257-7-0". 3) An operation rename the new inode 257 to its final name, "a2", is issued. This is incorrect because inode 258, which has the same name and it's a child of the same parent (root inode 256), was not yet processed and therefore no rmdir operation for it was yet issued. The rename operation is issued because we fail to detect that the name of the new inode 257 collides with inode 258, because their parent, a subvolume/snapshot root (inode 256) has a different generation in both snapshots. So fix this by ignoring the generation value of a parent directory that matches a root inode (number 256) when we are checking if the name of the inode currently being processed collides with the name of some other inode that was not yet processed. We can achieve this scenario of different inodes with the same number but different generation values either by mounting a filesystem with the inode cache option (-o inode_cache) or by creating and sending snapshots across different filesystems, like in the following example: $ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt $ mkdir /mnt/a1 $ mkdir /mnt/a2 $ btrfs subvolume snapshot -r /mnt /mnt/snap1 $ btrfs send /mnt/snap1 -f /tmp/1.snap $ umount /mnt $ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt $ touch /mnt/a2 $ btrfs subvolume snapshot -r /mnt /mnt/snap2 $ btrfs receive /mnt -f /tmp/1.snap # Take note that once the filesystem is created, its current # generation has value 7 so the inode from the second snapshot has # a generation value of 7. And after receiving the first snapshot # the filesystem is at a generation value of 10, because the call to # create the second snapshot bumps the generation to 8 (the snapshot # creation ioctl does a transaction commit), the receive command calls # the snapshot creation ioctl to create the first snapshot, which bumps # the filesystem's generation to 9, and finally when the receive # operation finishes it calls an ioctl to transition the first snapshot # (snap1) from RW mode to RO mode, which does another transaction commit # and bumps the filesystem's generation to 10. $ rm -f /tmp/1.snap $ btrfs send /mnt/snap1 -f /tmp/1.snap $ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/2.snap $ umount /mnt $ mkfs.btrfs -f /dev/sdd $ mount /dev/sdd /mnt $ btrfs receive /mnt /tmp/1.snap # Receive of snapshot snap2 used to fail. $ btrfs receive /mnt /tmp/2.snap Signed-off-by: Robbie Ko Reviewed-by: Filipe Manana [Rewrote changelog to be more precise and clear] Signed-off-by: Filipe Manana Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/send.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 3cc2d1dfd7bf..fc2472ef5011 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -1640,6 +1640,9 @@ static int is_inode_existent(struct send_ctx *sctx, u64 ino, u64 gen) { int ret; + if (ino == BTRFS_FIRST_FREE_OBJECTID) + return 1; + ret = get_cur_inode_state(sctx, ino, gen); if (ret < 0) goto out; @@ -1825,7 +1828,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen, * not delted and then re-created, if it was then we have no overwrite * and we can just unlink this entry. */ - if (sctx->parent_root) { + if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) { ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL, NULL, NULL, NULL); if (ret < 0 && ret != -ENOENT) -- GitLab From bcd17067a2311847ea05bc62aae0ba297e740e0d Mon Sep 17 00:00:00 2001 From: Majd Dibbiny Date: Thu, 23 Feb 2017 12:02:43 +0200 Subject: [PATCH 1289/5498] net/mlx4_core: Fix VF overwrite of module param which disables DMFS on new probed PFs [ Upstream commit 95f1ba9a24af9769f6e20dfe9a77c863f253f311 ] In the VF driver, module parameter mlx4_log_num_mgm_entry_size was mistakenly overwritten -- and in a manner which overrode the device-managed flow steering option encoded in the parameter. log_num_mgm_entry_size is a global module parameter which affects all ConnectX-3 PFs installed on that host. If a VF changes log_num_mgm_entry_size, this will affect all PFs which are probed subsequent to the change (by disabling DMFS for those PFs). Fixes: 3c439b5586e9 ("mlx4_core: Allow choosing flow steering mode") Signed-off-by: Majd Dibbiny Reviewed-by: Jack Morgenstein Signed-off-by: Tariq Toukan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx4/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 90de6e1ad06e..7fea86c06783 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -648,8 +648,6 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) return -ENOSYS; } - mlx4_log_num_mgm_entry_size = hca_param.log_mc_entry_sz; - dev->caps.hca_core_clock = hca_param.hca_core_clock; memset(&dev_cap, 0, sizeof(dev_cap)); -- GitLab From 7cab7260362320e26243925c60d731552a0280a7 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Thu, 23 Feb 2017 08:38:26 +0100 Subject: [PATCH 1290/5498] crypto: xts - Add ECB dependency [ Upstream commit 12cb3a1c4184f891d965d1f39f8cfcc9ef617647 ] Since the commit f1c131b45410a202eb45cc55980a7a9e4e4b4f40 crypto: xts - Convert to skcipher the XTS mode is based on ECB, so the mode must select ECB otherwise it can fail to initialize. Signed-off-by: Milan Broz Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- crypto/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/Kconfig b/crypto/Kconfig index 87bbc9c1e681..7bd153fd68fe 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -290,6 +290,7 @@ config CRYPTO_XTS select CRYPTO_BLKCIPHER select CRYPTO_MANAGER select CRYPTO_GF128MUL + select CRYPTO_ECB help XTS: IEEE1619/D16 narrow block cipher use with aes-xts-plain, key size 256, 384 or 512 bits. This implementation currently -- GitLab From b370a0378091e31f8c6a4eb708dfd22be314f7c0 Mon Sep 17 00:00:00 2001 From: Eric Ren Date: Wed, 22 Feb 2017 15:40:41 -0800 Subject: [PATCH 1291/5498] ocfs2/dlmglue: prepare tracking logic to avoid recursive cluster lock [ Upstream commit 439a36b8ef38657f765b80b775e2885338d72451 ] We are in the situation that we have to avoid recursive cluster locking, but there is no way to check if a cluster lock has been taken by a precess already. Mostly, we can avoid recursive locking by writing code carefully. However, we found that it's very hard to handle the routines that are invoked directly by vfs code. For instance: const struct inode_operations ocfs2_file_iops = { .permission = ocfs2_permission, .get_acl = ocfs2_iop_get_acl, .set_acl = ocfs2_iop_set_acl, }; Both ocfs2_permission() and ocfs2_iop_get_acl() call ocfs2_inode_lock(PR): do_sys_open may_open inode_permission ocfs2_permission ocfs2_inode_lock() <=== first time generic_permission get_acl ocfs2_iop_get_acl ocfs2_inode_lock() <=== recursive one A deadlock will occur if a remote EX request comes in between two of ocfs2_inode_lock(). Briefly describe how the deadlock is formed: On one hand, OCFS2_LOCK_BLOCKED flag of this lockres is set in BAST(ocfs2_generic_handle_bast) when downconvert is started on behalf of the remote EX lock request. Another hand, the recursive cluster lock (the second one) will be blocked in in __ocfs2_cluster_lock() because of OCFS2_LOCK_BLOCKED. But, the downconvert never complete, why? because there is no chance for the first cluster lock on this node to be unlocked - we block ourselves in the code path. The idea to fix this issue is mostly taken from gfs2 code. 1. introduce a new field: struct ocfs2_lock_res.l_holders, to keep track of the processes' pid who has taken the cluster lock of this lock resource; 2. introduce a new flag for ocfs2_inode_lock_full: OCFS2_META_LOCK_GETBH; it means just getting back disk inode bh for us if we've got cluster lock. 3. export a helper: ocfs2_is_locked_by_me() is used to check if we have got the cluster lock in the upper code path. The tracking logic should be used by some of the ocfs2 vfs's callbacks, to solve the recursive locking issue cuased by the fact that vfs routines can call into each other. The performance penalty of processing the holder list should only be seen at a few cases where the tracking logic is used, such as get/set acl. You may ask what if the first time we got a PR lock, and the second time we want a EX lock? fortunately, this case never happens in the real world, as far as I can see, including permission check, (get|set)_(acl|attr), and the gfs2 code also do so. [sfr@canb.auug.org.au remove some inlines] Link: http://lkml.kernel.org/r/20170117100948.11657-2-zren@suse.com Signed-off-by: Eric Ren Reviewed-by: Junxiao Bi Reviewed-by: Joseph Qi Cc: Stephen Rothwell Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/dlmglue.c | 105 +++++++++++++++++++++++++++++++++++++++++++-- fs/ocfs2/dlmglue.h | 18 ++++++++ fs/ocfs2/ocfs2.h | 1 + 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 12fe56b2e870..2402353f290c 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -531,6 +531,7 @@ void ocfs2_lock_res_init_once(struct ocfs2_lock_res *res) init_waitqueue_head(&res->l_event); INIT_LIST_HEAD(&res->l_blocked_list); INIT_LIST_HEAD(&res->l_mask_waiters); + INIT_LIST_HEAD(&res->l_holders); } void ocfs2_inode_lock_res_init(struct ocfs2_lock_res *res, @@ -748,6 +749,50 @@ void ocfs2_lock_res_free(struct ocfs2_lock_res *res) res->l_flags = 0UL; } +/* + * Keep a list of processes who have interest in a lockres. + * Note: this is now only uesed for check recursive cluster locking. + */ +static inline void ocfs2_add_holder(struct ocfs2_lock_res *lockres, + struct ocfs2_lock_holder *oh) +{ + INIT_LIST_HEAD(&oh->oh_list); + oh->oh_owner_pid = get_pid(task_pid(current)); + + spin_lock(&lockres->l_lock); + list_add_tail(&oh->oh_list, &lockres->l_holders); + spin_unlock(&lockres->l_lock); +} + +static inline void ocfs2_remove_holder(struct ocfs2_lock_res *lockres, + struct ocfs2_lock_holder *oh) +{ + spin_lock(&lockres->l_lock); + list_del(&oh->oh_list); + spin_unlock(&lockres->l_lock); + + put_pid(oh->oh_owner_pid); +} + +static inline int ocfs2_is_locked_by_me(struct ocfs2_lock_res *lockres) +{ + struct ocfs2_lock_holder *oh; + struct pid *pid; + + /* look in the list of holders for one with the current task as owner */ + spin_lock(&lockres->l_lock); + pid = task_pid(current); + list_for_each_entry(oh, &lockres->l_holders, oh_list) { + if (oh->oh_owner_pid == pid) { + spin_unlock(&lockres->l_lock); + return 1; + } + } + spin_unlock(&lockres->l_lock); + + return 0; +} + static inline void ocfs2_inc_holders(struct ocfs2_lock_res *lockres, int level) { @@ -2313,8 +2358,9 @@ int ocfs2_inode_lock_full_nested(struct inode *inode, goto getbh; } - if (ocfs2_mount_local(osb)) - goto local; + if ((arg_flags & OCFS2_META_LOCK_GETBH) || + ocfs2_mount_local(osb)) + goto update; if (!(arg_flags & OCFS2_META_LOCK_RECOVERY)) ocfs2_wait_for_recovery(osb); @@ -2343,7 +2389,7 @@ int ocfs2_inode_lock_full_nested(struct inode *inode, if (!(arg_flags & OCFS2_META_LOCK_RECOVERY)) ocfs2_wait_for_recovery(osb); -local: +update: /* * We only see this flag if we're being called from * ocfs2_read_locked_inode(). It means we're locking an inode @@ -2485,6 +2531,59 @@ void ocfs2_inode_unlock(struct inode *inode, ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level); } +/* + * This _tracker variantes are introduced to deal with the recursive cluster + * locking issue. The idea is to keep track of a lock holder on the stack of + * the current process. If there's a lock holder on the stack, we know the + * task context is already protected by cluster locking. Currently, they're + * used in some VFS entry routines. + * + * return < 0 on error, return == 0 if there's no lock holder on the stack + * before this call, return == 1 if this call would be a recursive locking. + */ +int ocfs2_inode_lock_tracker(struct inode *inode, + struct buffer_head **ret_bh, + int ex, + struct ocfs2_lock_holder *oh) +{ + int status; + int arg_flags = 0, has_locked; + struct ocfs2_lock_res *lockres; + + lockres = &OCFS2_I(inode)->ip_inode_lockres; + has_locked = ocfs2_is_locked_by_me(lockres); + /* Just get buffer head if the cluster lock has been taken */ + if (has_locked) + arg_flags = OCFS2_META_LOCK_GETBH; + + if (likely(!has_locked || ret_bh)) { + status = ocfs2_inode_lock_full(inode, ret_bh, ex, arg_flags); + if (status < 0) { + if (status != -ENOENT) + mlog_errno(status); + return status; + } + } + if (!has_locked) + ocfs2_add_holder(lockres, oh); + + return has_locked; +} + +void ocfs2_inode_unlock_tracker(struct inode *inode, + int ex, + struct ocfs2_lock_holder *oh, + int had_lock) +{ + struct ocfs2_lock_res *lockres; + + lockres = &OCFS2_I(inode)->ip_inode_lockres; + if (!had_lock) { + ocfs2_remove_holder(lockres, oh); + ocfs2_inode_unlock(inode, ex); + } +} + int ocfs2_orphan_scan_lock(struct ocfs2_super *osb, u32 *seqno) { struct ocfs2_lock_res *lockres; diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h index d293a22c32c5..a7fc18ba0dc1 100644 --- a/fs/ocfs2/dlmglue.h +++ b/fs/ocfs2/dlmglue.h @@ -70,6 +70,11 @@ struct ocfs2_orphan_scan_lvb { __be32 lvb_os_seqno; }; +struct ocfs2_lock_holder { + struct list_head oh_list; + struct pid *oh_owner_pid; +}; + /* ocfs2_inode_lock_full() 'arg_flags' flags */ /* don't wait on recovery. */ #define OCFS2_META_LOCK_RECOVERY (0x01) @@ -77,6 +82,8 @@ struct ocfs2_orphan_scan_lvb { #define OCFS2_META_LOCK_NOQUEUE (0x02) /* don't block waiting for the downconvert thread, instead return -EAGAIN */ #define OCFS2_LOCK_NONBLOCK (0x04) +/* just get back disk inode bh if we've got cluster lock. */ +#define OCFS2_META_LOCK_GETBH (0x08) /* Locking subclasses of inode cluster lock */ enum { @@ -170,4 +177,15 @@ void ocfs2_put_dlm_debug(struct ocfs2_dlm_debug *dlm_debug); /* To set the locking protocol on module initialization */ void ocfs2_set_locking_protocol(void); + +/* The _tracker pair is used to avoid cluster recursive locking */ +int ocfs2_inode_lock_tracker(struct inode *inode, + struct buffer_head **ret_bh, + int ex, + struct ocfs2_lock_holder *oh); +void ocfs2_inode_unlock_tracker(struct inode *inode, + int ex, + struct ocfs2_lock_holder *oh, + int had_lock); + #endif /* DLMGLUE_H */ diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index bbec539230fd..6a90e364f648 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -166,6 +166,7 @@ struct ocfs2_lock_res { struct list_head l_blocked_list; struct list_head l_mask_waiters; + struct list_head l_holders; unsigned long l_flags; char l_name[OCFS2_LOCK_ID_MAX_LEN]; -- GitLab From ffc669a3af00c8c3a9ceeb1b30a55f89cf38d2ce Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 21 Feb 2017 21:46:37 +0300 Subject: [PATCH 1292/5498] scsi: scsi_dh_emc: return success in clariion_std_inquiry() [ Upstream commit 4d7d39a18b8b81511f0b893b7d2203790bf8a58b ] We accidentally return an uninitialized variable on success. Fixes: b6ff1b14cdf4 ("[SCSI] scsi_dh: Update EMC handler") Signed-off-by: Dan Carpenter Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/device_handler/scsi_dh_emc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 84765384c47c..d03f0568bfda 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -464,7 +464,7 @@ static int clariion_prep_fn(struct scsi_device *sdev, struct request *req) static int clariion_std_inquiry(struct scsi_device *sdev, struct clariion_dh_data *csdev) { - int err; + int err = SCSI_DH_OK; char *sp_model; err = send_inquiry_cmd(sdev, 0, csdev); -- GitLab From d090846a538dc4081140b4b76fbbe6b806611f7f Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 16 Feb 2017 18:05:45 +0300 Subject: [PATCH 1293/5498] uapi: fix linux/rds.h userspace compilation errors [ Upstream commit feb0869d90e51ce8b6fd8a46588465b1b5a26d09 ] Consistently use types from linux/types.h to fix the following linux/rds.h userspace compilation errors: /usr/include/linux/rds.h:106:2: error: unknown type name 'uint8_t' uint8_t name[32]; /usr/include/linux/rds.h:107:2: error: unknown type name 'uint64_t' uint64_t value; /usr/include/linux/rds.h:117:2: error: unknown type name 'uint64_t' uint64_t next_tx_seq; /usr/include/linux/rds.h:118:2: error: unknown type name 'uint64_t' uint64_t next_rx_seq; /usr/include/linux/rds.h:121:2: error: unknown type name 'uint8_t' uint8_t transport[TRANSNAMSIZ]; /* null term ascii */ /usr/include/linux/rds.h:122:2: error: unknown type name 'uint8_t' uint8_t flags; /usr/include/linux/rds.h:129:2: error: unknown type name 'uint64_t' uint64_t seq; /usr/include/linux/rds.h:130:2: error: unknown type name 'uint32_t' uint32_t len; /usr/include/linux/rds.h:135:2: error: unknown type name 'uint8_t' uint8_t flags; /usr/include/linux/rds.h:139:2: error: unknown type name 'uint32_t' uint32_t sndbuf; /usr/include/linux/rds.h:144:2: error: unknown type name 'uint32_t' uint32_t rcvbuf; /usr/include/linux/rds.h:145:2: error: unknown type name 'uint64_t' uint64_t inum; /usr/include/linux/rds.h:153:2: error: unknown type name 'uint64_t' uint64_t hdr_rem; /usr/include/linux/rds.h:154:2: error: unknown type name 'uint64_t' uint64_t data_rem; /usr/include/linux/rds.h:155:2: error: unknown type name 'uint32_t' uint32_t last_sent_nxt; /usr/include/linux/rds.h:156:2: error: unknown type name 'uint32_t' uint32_t last_expected_una; /usr/include/linux/rds.h:157:2: error: unknown type name 'uint32_t' uint32_t last_seen_una; /usr/include/linux/rds.h:164:2: error: unknown type name 'uint8_t' uint8_t src_gid[RDS_IB_GID_LEN]; /usr/include/linux/rds.h:165:2: error: unknown type name 'uint8_t' uint8_t dst_gid[RDS_IB_GID_LEN]; /usr/include/linux/rds.h:167:2: error: unknown type name 'uint32_t' uint32_t max_send_wr; /usr/include/linux/rds.h:168:2: error: unknown type name 'uint32_t' uint32_t max_recv_wr; /usr/include/linux/rds.h:169:2: error: unknown type name 'uint32_t' uint32_t max_send_sge; /usr/include/linux/rds.h:170:2: error: unknown type name 'uint32_t' uint32_t rdma_mr_max; /usr/include/linux/rds.h:171:2: error: unknown type name 'uint32_t' uint32_t rdma_mr_size; /usr/include/linux/rds.h:212:9: error: unknown type name 'uint64_t' typedef uint64_t rds_rdma_cookie_t; /usr/include/linux/rds.h:215:2: error: unknown type name 'uint64_t' uint64_t addr; /usr/include/linux/rds.h:216:2: error: unknown type name 'uint64_t' uint64_t bytes; /usr/include/linux/rds.h:221:2: error: unknown type name 'uint64_t' uint64_t cookie_addr; /usr/include/linux/rds.h:222:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:228:2: error: unknown type name 'uint64_t' uint64_t cookie_addr; /usr/include/linux/rds.h:229:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:234:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:240:2: error: unknown type name 'uint64_t' uint64_t local_vec_addr; /usr/include/linux/rds.h:241:2: error: unknown type name 'uint64_t' uint64_t nr_local; /usr/include/linux/rds.h:242:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:243:2: error: unknown type name 'uint64_t' uint64_t user_token; /usr/include/linux/rds.h:248:2: error: unknown type name 'uint64_t' uint64_t local_addr; /usr/include/linux/rds.h:249:2: error: unknown type name 'uint64_t' uint64_t remote_addr; /usr/include/linux/rds.h:252:4: error: unknown type name 'uint64_t' uint64_t compare; /usr/include/linux/rds.h:253:4: error: unknown type name 'uint64_t' uint64_t swap; /usr/include/linux/rds.h:256:4: error: unknown type name 'uint64_t' uint64_t add; /usr/include/linux/rds.h:259:4: error: unknown type name 'uint64_t' uint64_t compare; /usr/include/linux/rds.h:260:4: error: unknown type name 'uint64_t' uint64_t swap; /usr/include/linux/rds.h:261:4: error: unknown type name 'uint64_t' uint64_t compare_mask; /usr/include/linux/rds.h:262:4: error: unknown type name 'uint64_t' uint64_t swap_mask; /usr/include/linux/rds.h:265:4: error: unknown type name 'uint64_t' uint64_t add; /usr/include/linux/rds.h:266:4: error: unknown type name 'uint64_t' uint64_t nocarry_mask; /usr/include/linux/rds.h:269:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:270:2: error: unknown type name 'uint64_t' uint64_t user_token; /usr/include/linux/rds.h:274:2: error: unknown type name 'uint64_t' uint64_t user_token; /usr/include/linux/rds.h:275:2: error: unknown type name 'int32_t' int32_t status; Signed-off-by: Dmitry V. Levin Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/rds.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h index 91950950aa59..25e865320288 100644 --- a/include/uapi/linux/rds.h +++ b/include/uapi/linux/rds.h @@ -35,6 +35,7 @@ #define _LINUX_RDS_H #include +#include /* For __kernel_sockaddr_storage. */ #define RDS_IB_ABI_VERSION 0x301 @@ -213,7 +214,7 @@ struct rds_get_mr_args { }; struct rds_get_mr_for_dest_args { - struct sockaddr_storage dest_addr; + struct __kernel_sockaddr_storage dest_addr; struct rds_iovec vec; uint64_t cookie_addr; uint64_t flags; -- GitLab From 1405b8e763e6634f0c7503c47184509ba3911387 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 16 Feb 2017 18:04:29 +0300 Subject: [PATCH 1294/5498] uapi: fix linux/mroute6.h userspace compilation errors [ Upstream commit 72aa107df6a275cf03359934ca5799a2be7a1bf7 ] Include to fix the following linux/mroute6.h userspace compilation errors: /usr/include/linux/mroute6.h:80:22: error: field 'mf6cc_origin' has incomplete type struct sockaddr_in6 mf6cc_origin; /* Origin of mcast */ /usr/include/linux/mroute6.h:81:22: error: field 'mf6cc_mcastgrp' has incomplete type struct sockaddr_in6 mf6cc_mcastgrp; /* Group in question */ /usr/include/linux/mroute6.h:91:22: error: field 'src' has incomplete type struct sockaddr_in6 src; /usr/include/linux/mroute6.h:92:22: error: field 'grp' has incomplete type struct sockaddr_in6 grp; /usr/include/linux/mroute6.h:132:18: error: field 'im6_src' has incomplete type struct in6_addr im6_src, im6_dst; /usr/include/linux/mroute6.h:132:27: error: field 'im6_dst' has incomplete type struct in6_addr im6_src, im6_dst; Signed-off-by: Dmitry V. Levin Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/mroute6.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h index ce91215cf7e6..e0b566dc72ef 100644 --- a/include/uapi/linux/mroute6.h +++ b/include/uapi/linux/mroute6.h @@ -3,6 +3,7 @@ #include #include +#include /* For struct sockaddr_in6. */ /* * Based on the MROUTING 3.5 defines primarily to keep -- GitLab From e524bfab417cb2c109eb9b8e900f98a944dbf494 Mon Sep 17 00:00:00 2001 From: Varun Prakash Date: Fri, 20 Jan 2017 16:44:33 +0530 Subject: [PATCH 1295/5498] target/iscsi: Fix unsolicited data seq_end_offset calculation [ Upstream commit 4d65491c269729a1e3b375c45e73213f49103d33 ] In case of unsolicited data for the first sequence seq_end_offset must be set to minimum of total data length and FirstBurstLength, so do not add cmd->write_data_done to the min of total data length and FirstBurstLength. This patch avoids that with ImmediateData=Yes, InitialR2T=No, MaxXmitDataSegmentLength < FirstBurstLength that a WRITE command with IO size above FirstBurstLength triggers sequence error messages, for example Set following parameters on target (linux-4.8.12) ImmediateData = Yes InitialR2T = No MaxXmitDataSegmentLength = 8k FirstBurstLength = 64k Log in from Open iSCSI initiator and execute dd if=/dev/zero of=/dev/sdb bs=128k count=1 oflag=direct Error messages on target Command ITT: 0x00000035 with Offset: 65536, Length: 8192 outside of Sequence 73728:131072 while DataSequenceInOrder=Yes. Command ITT: 0x00000035, received DataSN: 0x00000001 higher than expected 0x00000000. Unable to perform within-command recovery while ERL=0. Signed-off-by: Varun Prakash [ bvanassche: Use min() instead of open-coding it / edited patch description ] Signed-off-by: Bart Van Assche Signed-off-by: Nicholas Bellinger Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target_erl0.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index f900de64e267..b58c97ab1f8e 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -45,10 +45,8 @@ void iscsit_set_dataout_sequence_values( */ if (cmd->unsolicited_data) { cmd->seq_start_offset = cmd->write_data_done; - cmd->seq_end_offset = (cmd->write_data_done + - ((cmd->se_cmd.data_length > - conn->sess->sess_ops->FirstBurstLength) ? - conn->sess->sess_ops->FirstBurstLength : cmd->se_cmd.data_length)); + cmd->seq_end_offset = min(cmd->se_cmd.data_length, + conn->sess->sess_ops->FirstBurstLength); return; } -- GitLab From 3cde552980e99d537ff88352202932f8c0bc12e4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 19 Oct 2017 15:28:08 +0200 Subject: [PATCH 1296/5498] Revert "tty: goldfish: Fix a parameter of a call to free_irq" This reverts commit 0961072120f3e40fe98c2bb49c45549ca3f042dc which is commit 1a5c2d1de7d35f5eb9793266237903348989502b upstream. Ben writes: This fixes a bug introduced in 4.6 by commit 465893e18878 "tty: goldfish: support platform_device with id -1". For earlier kernel versions, it *introduces* a bug. So let's drop it. Reported-by: Ben Hutchings Cc: Christophe JAILLET Cc: Sasha Levin Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/tty/goldfish.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index beb3142a1414..09495f515fa9 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -295,7 +295,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) tty_unregister_device(goldfish_tty_driver, i); err_tty_register_device_failed: - free_irq(irq, qtty); + free_irq(irq, pdev); err_request_irq_failed: goldfish_tty_current_line_count--; if (goldfish_tty_current_line_count == 0) -- GitLab From 6f457819e8343ae097b7122a57694d26d4ce13b0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 21 Oct 2017 17:07:27 +0200 Subject: [PATCH 1297/5498] Linux 3.18.77 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 22c320078a98..6f71303316d5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 76 +SUBLEVEL = 77 EXTRAVERSION = NAME = Diseased Newt -- GitLab From b0149269bb3ff99099bc76a1137e79d520da9bd8 Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Tue, 25 Jul 2017 21:54:03 +0530 Subject: [PATCH 1298/5498] usb: phy: msm: Disable runtime PM for root hub upon usb disconnect There is a possibility of runtime idle thread for root hub being scheduled out inside autosuspend_check() during root hub device disconnect. Device interface pointer is set to NULL by usb_disconnect as part of device disconnect. This results into NULL pointer dereference when runtime idle thread gets a chance to run and access it later in side autosuspend_check(). Hence disable runtime PM for root hub and finish any pending runtime PM request synchronously before disconnecting root hub device. Change-Id: I24e290e8061a676ea04c5028d531da865be2d937 Signed-off-by: Sai Krishna Juturi --- drivers/usb/phy/phy-msm-usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 2598709866f0..684c0dc1d5d3 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1967,6 +1967,8 @@ static void msm_otg_start_host(struct usb_otg *otg, int on) msm_otg_perf_vote_update(motg, false); pm_qos_remove_request(&motg->pm_qos_req_dma); + pm_runtime_disable(&hcd->self.root_hub->dev); + pm_runtime_barrier(&hcd->self.root_hub->dev); usb_remove_hcd(hcd); msm_otg_reset(&motg->phy); -- GitLab From 59cc2b450f884260ede75dfbf85c2daaa61df28a Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Tue, 17 Oct 2017 11:28:17 +0530 Subject: [PATCH 1299/5498] ALSA: usb-audio: Add length check after string desc copy It might be possible that negative error code is returned in 'len', when we try to copy USB string desc into the ID name buf of snd_kcontrol instance. But even in that case, we are terminating buf with 0 at the 'len' index, which leads to memory corruption. And for good case where 'len' is non-negative, usb_string func is terminating the buf with 0. Fix this by removing the termination of buf with '0' in the caller function. Change-Id: Ie32d395b0fc91d6c3e1cfdbafb76304e21e40577 Signed-off-by: Ajay Agarwal --- sound/usb/mixer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 537ac978c69f..d533d2a0aa5d 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -199,7 +199,6 @@ static int snd_usb_copy_string_desc(struct mixer_build *state, int index, char *buf, int maxlen) { int len = usb_string(state->chip->dev, index, buf, maxlen - 1); - buf[len] = 0; return len; } -- GitLab From 4f12e51391f675205a44c01f3f6c104d9bbd3b2a Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Wed, 4 May 2016 19:30:23 -0700 Subject: [PATCH 1300/5498] usb: gadget: composite: Handle OS descriptor request properly In case w_index or w_value of an OS descriptor does not match for a device or an interface, value remains set to -EOPNOTSUPP. This is assigned to an unsigned request length and becomes a large integer value. When driver tries to allocate a buffer of this large integer value DMA allocator complaints for out of SW-IOMMU space. Hence check this variable for negative value and return without queuing ep0 request. CRs-Fixed: 1013316 Change-Id: I705d0d54fb17ca3042533f0106f91912215bd52a Signed-off-by: Hemant Kumar --- drivers/usb/gadget/composite.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f038204d949e..22b0b9a42f91 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1921,6 +1921,16 @@ unknown: } break; } + + if (value < 0) { + DBG(cdev, "%s: unhandled os desc request\n", + __func__); + DBG(cdev, "req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; + } + req->length = value; req->zero = value < w_length; value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); -- GitLab From 3598c8e0d7839d82992e860f522ffcae87299ccc Mon Sep 17 00:00:00 2001 From: Viraja Kommaraju Date: Fri, 13 Oct 2017 16:07:39 +0530 Subject: [PATCH 1301/5498] ASoC: msm: qdsp6v2: Update routing and FE DAI drivers Update routing driver with port mixer commands between primary and secondary interfaces to enable AFE lopback between them. Update FE DAI driver to add hostless FEs for primary/secondary Auxpcm and secondary MI2S. CRs-fixed: 2105222 Change-Id: I4bab442dae76d2a81f52883c4b0171253f6d9fe9 Signed-off-by: Viraja Kommaraju --- sound/soc/msm/msm-dai-fe.c | 30 ++++++++++++++++++++++ sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 22 ++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index 73d678afbc7d..dec64067f2cf 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -628,6 +628,36 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .name = "AUXPCM_HOSTLESS", .probe = fe_dai_probe, }, + { + .playback = { + .stream_name = "SEC_AUXPCM_HOSTLESS Playback", + .aif_name = "SEC_AUXPCM_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_AUXPCM_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "SEC_AUXPCM_HOSTLESS Capture", + .aif_name = "SEC_AUXPCM_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_AUXPCM_TX_HOSTLESS", + .probe = fe_dai_probe, + }, { .playback = { .stream_name = "VOICE_STUB Playback", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 501b26e3970c..4d82871f0263 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -7294,6 +7294,9 @@ static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_AUXPCM_RX, MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = { @@ -7306,6 +7309,9 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX, MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = { @@ -7439,6 +7445,9 @@ static const struct snd_kcontrol_new primary_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new quat_mi2s_rx_port_mixer_controls[] = { @@ -8572,6 +8581,9 @@ static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new lsm1_mixer_controls[] = { @@ -9791,6 +9803,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_AUXPCM_DL_HL", "SEC_AUXPCM_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_AUXPCM_UL_HL", "SEC_AUXPCM_HOSTLESS Capture", + 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("TERT_MI2S_UL_HL", @@ -12121,7 +12137,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"HFP_INT_UL_HL", "Switch", "INT_BT_SCO_TX"}, {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_DL_HL"}, {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"}, + {"SEC_AUXPCM_UL_HL", NULL, "SEC_AUX_PCM_TX"}, {"MI2S_RX", NULL, "MI2S_DL_HL"}, {"MI2S_UL_HL", NULL, "MI2S_TX"}, {"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"}, @@ -12487,11 +12505,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, {"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, {"AUX_PCM_RX Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"AUX_PCM_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"}, {"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"SEC_AUXPCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, {"SEC_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SEC_AUXPCM_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"}, {"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, @@ -12592,6 +12612,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"PRI_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"PRI_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_MI2S_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"}, {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -12599,6 +12620,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"SEC_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"SEC_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SEC_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"}, {"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, -- GitLab From 76a10ddf3962db6df43468cf1892eaee8228b387 Mon Sep 17 00:00:00 2001 From: Viraja Kommaraju Date: Tue, 26 Sep 2017 15:56:22 +0530 Subject: [PATCH 1302/5498] ASoC: msm: Changes for on-the-fly switch between master/slave modes Update machine driver to use msm-audio-pinctrl driver and have single pinctrl handle for all states master, slave and sleep. This is applicable to both MI2S and AUXPCM. Also, update FE DAIs for SEC MI2S TX/RX hostless, PRI AUXPCM TX/RX hostless, SEC AUXPCM TX/RX hostless session. CRs-fixed: 2105222 Change-Id: I9c4f3eae488c8632a762f36a3055f16c772dc1c2 Signed-off-by: Viraja Kommaraju --- .../bindings/sound/qcom-audio-dev.txt | 91 ++++++++- sound/soc/msm/Makefile | 2 +- sound/soc/msm/mdm9650.c | 172 ++++++++++++++---- 3 files changed, 216 insertions(+), 49 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index be9df49293f5..9ad5992ae13e 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2155,10 +2155,14 @@ sound-9330 { - qcom,model : The user-visible name of this sound card. - qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec and node is "sound-9335" -- qcom,prim_mi2s_aux_master : Handle to prim_master pinctrl configurations -- qcom,prim_mi2s_aux_slave : Handle to prim_slave pinctrl configurations -- qcom,sec_mi2s_aux_master : Handle to sec_master pinctrl configurations -- qcom,sec_mi2s_aux_slave : Handle to sec_slave pinctrl configurations +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets + mentioned in qcom,msm-gpios. Say we have 2^N combinations + for N GPIOs, this would list all the 2^N combinations. +- pinctrl-names : The combinations of gpio sets from above that are supported in + the flavor. This can be sometimes same as qcom,pinctrl-names + i.e with 2^N combinations or will have less incase if some + combination is not supported or invalid. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. - qcom,audio-routing : A list of the connections between audio components. - asoc-platform: This is phandle list containing the references to platform device nodes that are used as part of the sound card dai-links. @@ -2217,10 +2221,81 @@ Example: "MIC BIAS4 External", "Digital Mic6"; qcom,tasha-mclk-clk-freq = <12288000>; - qcom,prim_mi2s_aux_master = <&prim_master>; - qcom,prim_mi2s_aux_slave = <&prim_slave>; - qcom,sec_mi2s_aux_master = <&sec_master>; - qcom,sec_mi2s_aux_slave = <&sec_slave>; + + qcom,msm-gpios = + "pri_mi2s_aux_master", + "pri_mi2s_aux_slave", + "sec_mi2s_aux_master", + "sec_mi2s_aux_slave"; + qcom,pinctrl-names = + "all_off", + "pri_mi2s_aux_master_active", + "pri_mi2s_aux_slave_active", + "invalid_state_1", + "sec_mi2s_aux_master_active", + "pri_master_active_sec_master_active", + "pri_slave_active_sec_master_active", + "invalid_state_2", + "sec_mi2s_aux_slave_active", + "pri_master_active_sec_slave_active", + "pri_slave_active_sec_slave_active"; + pinctrl-names = + "all_off", + "pri_mi2s_aux_master_active", + "pri_mi2s_aux_slave_active", + "invalid_state_1", + "sec_mi2s_aux_master_active", + "pri_master_active_sec_master_active", + "pri_slave_active_sec_master_active", + "invalid_state_2", + "sec_mi2s_aux_slave_active", + "pri_master_active_sec_slave_active", + "pri_slave_active_sec_slave_active"; + pinctrl-0 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-1 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-2 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-3 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-4 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-5 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-6 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-7 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-8 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + pinctrl-9 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + pinctrl-10 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, <&loopback>, <&hostless>, <&afe>, <&routing>, <&pcm_dtmf>, <&host_pcm>; diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile index cf5f357caab8..a844306686d8 100644 --- a/sound/soc/msm/Makefile +++ b/sound/soc/msm/Makefile @@ -36,7 +36,7 @@ snd-soc-msm8x16-objs += msm8952-slimbus.o msm8952-dai-links.o obj-$(CONFIG_SND_SOC_MSM8X16) += snd-soc-msm8x16.o # for MDM 9650 sound card driver -snd-soc-mdm9650-objs := mdm9650.o +snd-soc-mdm9650-objs := mdm9650.o msm-audio-pinctrl.o obj-$(CONFIG_SND_SOC_MDM9650) += snd-soc-mdm9650.o # for MDM 9607 sound card driver diff --git a/sound/soc/msm/mdm9650.c b/sound/soc/msm/mdm9650.c index 34a8c7a1ec81..ee469f10b78a 100644 --- a/sound/soc/msm/mdm9650.c +++ b/sound/soc/msm/mdm9650.c @@ -35,6 +35,7 @@ #include "../codecs/wcd9335.h" #include "../codecs/wsa881x.h" #include "../codecs/tlv320aic3x.h" +#include "msm-audio-pinctrl.h" /* Spk control */ #define MDM_SPK_ON 1 @@ -369,11 +370,11 @@ static void mdm_mi2s_shutdown(struct snd_pcm_substream *substream) pr_err("%s Clock disable failed\n", __func__); if (pdata->prim_mi2s_mode == 1) - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->prim_master_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "pri_mi2s_aux_master"); else - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->prim_slave_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "pri_mi2s_aux_slave"); if (ret) pr_err("%s: failed to set pri gpios to sleep: %d\n", __func__, ret); @@ -427,8 +428,8 @@ static int mdm_mi2s_startup(struct snd_pcm_substream *substream) * 0 means external clock slave mode. */ if (pdata->prim_mi2s_mode == 1) { - ret = wcd_gpio_ctrl_select_active_state - (pdata->prim_master_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "pri_mi2s_aux_master"); if (ret < 0) { pr_err("%s pinctrl set failed\n", __func__); goto err; @@ -471,8 +472,8 @@ static int mdm_mi2s_startup(struct snd_pcm_substream *substream) * Disable bit clk in slave mode for QC codec. * Enable only mclk. */ - ret = wcd_gpio_ctrl_select_active_state - (pdata->prim_slave_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "pri_mi2s_aux_slave"); if (ret < 0) { pr_err("%s pinctrl set failed\n", __func__); goto err; @@ -533,11 +534,11 @@ static void mdm_sec_mi2s_shutdown(struct snd_pcm_substream *substream) pr_err("%s Clock disable failed\n", __func__); if (pdata->sec_mi2s_mode == 1) - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->sec_master_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "sec_mi2s_aux_master"); else - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->sec_slave_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "sec_mi2s_aux_slave"); if (ret) pr_err("%s: failed to set sec gpios to sleep: %d\n", __func__, ret); @@ -591,8 +592,8 @@ static int mdm_sec_mi2s_startup(struct snd_pcm_substream *substream) * 0 means external clock slave mode. */ if (pdata->sec_mi2s_mode == 1) { - ret = wcd_gpio_ctrl_select_active_state - (pdata->sec_master_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "sec_mi2s_aux_master"); if (ret < 0) { pr_err("%s pinctrl set failed\n", __func__); goto err; @@ -619,8 +620,8 @@ static int mdm_sec_mi2s_startup(struct snd_pcm_substream *substream) * Enable mclk here, if needed for external codecs. * Optional. Refer primary mi2s slave interface. */ - ret = wcd_gpio_ctrl_select_active_state - (pdata->sec_slave_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "sec_mi2s_aux_slave"); if (ret < 0) { pr_err("%s pinctrl set failed\n", __func__); goto err; @@ -1130,11 +1131,11 @@ static void mdm_auxpcm_shutdown(struct snd_pcm_substream *substream) struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); if (pdata->prim_auxpcm_mode == 1) - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->prim_master_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "pri_mi2s_aux_master"); else - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->prim_slave_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "pri_mi2s_aux_slave"); if (ret) pr_err("%s: failed to set prim gpios to sleep: %d\n", __func__, ret); @@ -1176,13 +1177,13 @@ static int mdm_auxpcm_startup(struct snd_pcm_substream *substream) } if (pdata->prim_auxpcm_mode == 1) { - ret = wcd_gpio_ctrl_select_active_state - (pdata->prim_master_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "pri_mi2s_aux_master"); if (ret < 0) pr_err("%s pinctrl set failed\n", __func__); } else { - ret = wcd_gpio_ctrl_select_active_state - (pdata->prim_slave_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "pri_mi2s_aux_slave"); if (ret < 0) pr_err("%s pinctrl set failed\n", __func__); } @@ -1204,11 +1205,11 @@ static void mdm_sec_auxpcm_shutdown(struct snd_pcm_substream *substream) struct mdm_machine_data *pdata = snd_soc_card_get_drvdata(card); if (pdata->sec_auxpcm_mode == 1) - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->sec_master_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "sec_mi2s_aux_master"); else - ret = wcd_gpio_ctrl_select_sleep_state - (pdata->sec_slave_p); + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, + "sec_mi2s_aux_slave"); if (ret) pr_err("%s: failed to set sec gpios to sleep: %d\n", __func__, ret); @@ -1251,13 +1252,13 @@ static int mdm_sec_auxpcm_startup(struct snd_pcm_substream *substream) } if (pdata->sec_auxpcm_mode == 1) { - ret = wcd_gpio_ctrl_select_active_state - (pdata->sec_master_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "sec_mi2s_aux_master"); if (ret < 0) pr_err("%s pinctrl set failed\n", __func__); } else { - ret = wcd_gpio_ctrl_select_active_state - (pdata->sec_slave_p); + ret = msm_gpioset_activate(CLIENT_WCD_EXT, + "sec_mi2s_aux_slave"); if (ret < 0) pr_err("%s pinctrl set failed\n", __func__); } @@ -1951,6 +1952,99 @@ static struct snd_soc_dai_link mdm_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, }, + { + .name = "Secondary MI2S RX Hostless", + .stream_name = "Secondary MI2S_RX Hostless Playback", + .cpu_dai_name = "SEC_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary MI2S TX Hostless", + .stream_name = "Secondary MI2S_TX Hostless Playback", + .cpu_dai_name = "SEC_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Primary AUXPCM RX Hostless", + .stream_name = "AUXPCM_HOSTLESS Playback", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Primary AUXPCM TX Hostless", + .stream_name = "AUXPCM_HOSTLESS Capture", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary AUXPCM RX Hostless", + .stream_name = "SEC_AUXPCM_HOSTLESS Playback", + .cpu_dai_name = "SEC_AUXPCM_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary AUXPCM TX Hostless", + .stream_name = "SEC_AUXPCM_HOSTLESS Capture", + .cpu_dai_name = "SEC_AUXPCM_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, /* Backend DAI Links */ { .name = LPASS_BE_AFE_PCM_RX, @@ -2548,14 +2642,12 @@ static int mdm_asoc_machine_probe(struct platform_device *pdev) goto err; } - pdata->prim_master_p = of_parse_phandle(pdev->dev.of_node, - "qcom,prim_mi2s_aux_master", 0); - pdata->prim_slave_p = of_parse_phandle(pdev->dev.of_node, - "qcom,prim_mi2s_aux_slave", 0); - pdata->sec_master_p = of_parse_phandle(pdev->dev.of_node, - "qcom,sec_mi2s_aux_master", 0); - pdata->sec_slave_p = of_parse_phandle(pdev->dev.of_node, - "qcom,sec_mi2s_aux_slave", 0); + ret = msm_gpioset_initialize(CLIENT_WCD_EXT, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Error reading dtsi file for gpios\n"); + return -EINVAL; + } + mutex_init(&cdc_mclk_mutex); atomic_set(&mi2s_ref_count, 0); atomic_set(&sec_mi2s_ref_count, 0); -- GitLab From 129b83dd590b0806812343ddc62dfd265410292d Mon Sep 17 00:00:00 2001 From: Viraja Kommaraju Date: Tue, 26 Sep 2017 18:40:17 +0530 Subject: [PATCH 1303/5498] ARM: dts: msm: Add gpio configuration for master/slave modes on mdm9650 Add gpio configurations for different states of master and slave modes of MI2S and AUXPCM interfaces - both primary and secondary. CRs-fixed: 2105222 Change-Id: I947a290b7a8f8587b138ea9aa8e4d9858607ba18 Signed-off-by: Viraja Kommaraju --- arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi | 78 +++++++++++- arch/arm/boot/dts/qcom/mdm9650-ttp.dts | 82 +++++++++---- arch/arm/boot/dts/qcom/mdm9650.dtsi | 137 ++++++++++++---------- 3 files changed, 207 insertions(+), 90 deletions(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi b/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi index bf7099914e4e..03b3bd4cfcd0 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650-ccard.dtsi @@ -30,10 +30,80 @@ compatible = "qcom,mdm-audio-auto"; qcom,model = "mdm-auto-i2s-snd-card"; - qcom,prim_mi2s_aux_master = <&prim_master>; - qcom,prim_mi2s_aux_slave = <&prim_slave>; - qcom,sec_mi2s_aux_master = <&sec_master>; - qcom,sec_mi2s_aux_slave = <&sec_slave>; + qcom,msm-gpios = + "pri_mi2s_aux_master", + "pri_mi2s_aux_slave", + "sec_mi2s_aux_master", + "sec_mi2s_aux_slave"; + qcom,pinctrl-names = + "all_off", + "pri_mi2s_aux_master_active", + "pri_mi2s_aux_slave_active", + "invalid_state_1", + "sec_mi2s_aux_master_active", + "pri_master_active_sec_master_active", + "pri_slave_active_sec_master_active", + "invalid_state_2", + "sec_mi2s_aux_slave_active", + "pri_master_active_sec_slave_active", + "pri_slave_active_sec_slave_active"; + pinctrl-names = + "all_off", + "pri_mi2s_aux_master_active", + "pri_mi2s_aux_slave_active", + "invalid_state_1", + "sec_mi2s_aux_master_active", + "pri_master_active_sec_master_active", + "pri_slave_active_sec_master_active", + "invalid_state_2", + "sec_mi2s_aux_slave_active", + "pri_master_active_sec_slave_active", + "pri_slave_active_sec_slave_active"; + pinctrl-0 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-1 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-2 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-3 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-4 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-5 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-6 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-7 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-8 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + pinctrl-9 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + pinctrl-10 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, <&loopback>, <&hostless>, <&afe>, <&routing>, <&pcm_dtmf>, <&host_pcm>, <&compress>; diff --git a/arch/arm/boot/dts/qcom/mdm9650-ttp.dts b/arch/arm/boot/dts/qcom/mdm9650-ttp.dts index e2d4d1d00cdb..8db7dc55dfae 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-ttp.dts +++ b/arch/arm/boot/dts/qcom/mdm9650-ttp.dts @@ -82,30 +82,62 @@ &blsp1_uart2_hs { status = "disabled"; }; -&sec_master { - compatible = "qcom,wcd-gpio-ctrl"; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&sec_ws_b_active_master - &sec_sck_b_active_master - &sec_dout_b_active - &sec_din_b_active>; - pinctrl-1 = <&sec_ws_b_sleep - &sec_sck_b_sleep - &sec_dout_b_sleep - &sec_din_b_sleep>; - qcom,mi2s-auxpcm-cdc-gpios; -}; -&sec_slave { - compatible = "qcom,wcd-gpio-ctrl"; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&sec_ws_b_active_slave - &sec_sck_b_active_slave - &sec_dout_b_active - &sec_din_b_active>; - pinctrl-1 = <&sec_ws_b_sleep - &sec_sck_b_sleep - &sec_dout_b_sleep - &sec_din_b_sleep>; - qcom,mi2s-auxpcm-cdc-gpios; +&snd_tasha { + pinctrl-names = + "all_off", + "pri_mi2s_aux_master_active", + "pri_mi2s_aux_slave_active", + "invalid_state_1", + "sec_mi2s_aux_master_active", + "pri_master_active_sec_master_active", + "pri_slave_active_sec_master_active", + "invalid_state_2", + "sec_mi2s_aux_slave_active", + "pri_master_active_sec_slave_active", + "pri_slave_active_sec_slave_active"; + pinctrl-0 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_b_sleep &sec_sck_b_sleep + &sec_dout_b_sleep &sec_din_b_sleep>; + pinctrl-1 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_b_sleep &sec_sck_b_sleep + &sec_dout_b_sleep &sec_din_b_sleep>; + pinctrl-2 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_b_sleep &sec_sck_b_sleep + &sec_dout_b_sleep &sec_din_b_sleep>; + pinctrl-3 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_b_sleep &sec_sck_b_sleep + &sec_dout_b_sleep &sec_din_b_sleep>; + pinctrl-4 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_b_active_master &sec_sck_b_active_master + &sec_dout_b_active &sec_din_b_active>; + pinctrl-5 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_b_active_master &sec_sck_b_active_master + &sec_dout_b_active &sec_din_b_active>; + pinctrl-6 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_b_active_master &sec_sck_b_active_master + &sec_dout_b_active &sec_din_b_active>; + pinctrl-7 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_b_sleep &sec_sck_b_sleep + &sec_dout_b_sleep &sec_din_b_sleep>; + pinctrl-8 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_b_active_slave &sec_sck_b_active_slave + &sec_dout_b_active &sec_din_b_active>; + pinctrl-9 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_b_active_slave &sec_sck_b_active_slave + &sec_dout_b_active &sec_din_b_active>; + pinctrl-10 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_b_active_slave &sec_sck_b_active_slave + &sec_dout_b_active &sec_din_b_active>; }; diff --git a/arch/arm/boot/dts/qcom/mdm9650.dtsi b/arch/arm/boot/dts/qcom/mdm9650.dtsi index f8824c5cf846..3f4915f018ff 100644 --- a/arch/arm/boot/dts/qcom/mdm9650.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650.dtsi @@ -1145,7 +1145,7 @@ #clock-cells = <1>; }; - sound { + snd_tasha: sound { compatible = "qcom,mdm-audio-tasha"; qcom,model = "mdm-tasha-i2s-snd-card"; @@ -1179,10 +1179,81 @@ "SpkrRight IN", "SPK2 OUT"; qcom,tasha-mclk-clk-freq = <12288000>; - qcom,prim_mi2s_aux_master = <&prim_master>; - qcom,prim_mi2s_aux_slave = <&prim_slave>; - qcom,sec_mi2s_aux_master = <&sec_master>; - qcom,sec_mi2s_aux_slave = <&sec_slave>; + + qcom,msm-gpios = + "pri_mi2s_aux_master", + "pri_mi2s_aux_slave", + "sec_mi2s_aux_master", + "sec_mi2s_aux_slave"; + qcom,pinctrl-names = + "all_off", + "pri_mi2s_aux_master_active", + "pri_mi2s_aux_slave_active", + "invalid_state_1", + "sec_mi2s_aux_master_active", + "pri_master_active_sec_master_active", + "pri_slave_active_sec_master_active", + "invalid_state_2", + "sec_mi2s_aux_slave_active", + "pri_master_active_sec_slave_active", + "pri_slave_active_sec_slave_active"; + pinctrl-names = + "all_off", + "pri_mi2s_aux_master_active", + "pri_mi2s_aux_slave_active", + "invalid_state_1", + "sec_mi2s_aux_master_active", + "pri_master_active_sec_master_active", + "pri_slave_active_sec_master_active", + "invalid_state_2", + "sec_mi2s_aux_slave_active", + "pri_master_active_sec_slave_active", + "pri_slave_active_sec_slave_active"; + pinctrl-0 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-1 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-2 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-3 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-4 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-5 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-6 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_active_master &sec_sck_active_master + &sec_dout_active &sec_din_active>; + pinctrl-7 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_sleep &sec_sck_sleep + &sec_dout_sleep &sec_din_sleep>; + pinctrl-8 = <&pri_ws_sleep &pri_sck_sleep + &pri_dout_sleep &pri_din_sleep + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + pinctrl-9 = <&pri_ws_active_master &pri_sck_active_master + &pri_dout_active &pri_din_active + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + pinctrl-10 = <&pri_ws_active_slave &pri_sck_active_slave + &pri_dout_active &pri_din_active + &sec_ws_active_slave &sec_sck_active_slave + &sec_dout_active &sec_din_active>; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, <&loopback>, <&hostless>, <&afe>, <&routing>, <&pcm_dtmf>, <&host_pcm>, <&compress>; @@ -1409,62 +1480,6 @@ }; - prim_master: prim_master_pinctrl { - compatible = "qcom,wcd-gpio-ctrl"; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&pri_ws_active_master - &pri_sck_active_master - &pri_dout_active - &pri_din_active>; - pinctrl-1 = <&pri_ws_sleep - &pri_sck_sleep - &pri_dout_sleep - &pri_din_sleep>; - qcom,mi2s-auxpcm-cdc-gpios; - }; - - prim_slave: prim_slave_pinctrl { - compatible = "qcom,wcd-gpio-ctrl"; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&pri_ws_active_slave - &pri_sck_active_slave - &pri_dout_active - &pri_din_active>; - pinctrl-1 = <&pri_ws_sleep - &pri_sck_sleep - &pri_dout_sleep - &pri_din_sleep>; - qcom,mi2s-auxpcm-cdc-gpios; - }; - - sec_master: sec_master_pinctrl { - compatible = "qcom,wcd-gpio-ctrl"; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&sec_ws_active_master - &sec_sck_active_master - &sec_dout_active - &sec_din_active>; - pinctrl-1 = <&sec_ws_sleep - &sec_sck_sleep - &sec_dout_sleep - &sec_din_sleep>; - qcom,mi2s-auxpcm-cdc-gpios; - }; - - sec_slave: sec_slave_pinctrl { - compatible = "qcom,wcd-gpio-ctrl"; - pinctrl-names = "aud_active", "aud_sleep"; - pinctrl-0 = <&sec_ws_active_slave - &sec_sck_active_slave - &sec_dout_active - &sec_din_active>; - pinctrl-1 = <&sec_ws_sleep - &sec_sck_sleep - &sec_dout_sleep - &sec_din_sleep>; - qcom,mi2s-auxpcm-cdc-gpios; - }; - sdhc_1: sdhci@7824000 { compatible = "qcom,sdhci-msm"; reg = <0x07824900 0x500>, <0x07824000 0x800>; -- GitLab From 5c502df8de34604bddba5263790d93c111cff56b Mon Sep 17 00:00:00 2001 From: Srinivas Rao L Date: Fri, 1 Sep 2017 17:57:51 +0530 Subject: [PATCH 1304/5498] lpm-levels: Add support for APC off with L2RAM ON mode To support APCS_PMIC_OFF_L2RAM_ON mode, add support for device tree entry to specify if L2 cache need not be flushed. Change-Id: Id67c97359f73ea364d10f81b1d2623f2dcfe820a Signed-off-by: Srinivas Rao L --- .../bindings/arm/msm/lpm-levels.txt | 2 ++ drivers/cpuidle/lpm-levels-of.c | 5 ++++- drivers/cpuidle/lpm-levels.c | 19 ++++++++++++++----- drivers/cpuidle/lpm-levels.h | 15 ++++++++++----- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt index ecbf5a731940..b2b7151f1bd0 100644 --- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt +++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt @@ -96,6 +96,8 @@ Optional properties: or not for the drivers to prepare for cluster collapse. - qcom,hyp-psci: This property is used to determine if the cpu enters the low power mode within hypervisor. + - qcom,no-cache-flush: This boolean property will specify that cache + will not be flushed and will be in retention for this mode. - qcom,reset-level: This property is used to determine in this low power mode only control logic power collapse happens or memory logic power collapse aswell happens or retention state. diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 3710c52587a1..4d40849abb94 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -358,7 +358,8 @@ static int parse_legacy_cluster_params(struct device_node *node, int ret; struct lpm_match { char *devname; - int (*set_mode)(struct low_power_ops *, int, bool); + int (*set_mode)(struct low_power_ops *, int, + struct lpm_cluster_level *); }; struct lpm_match match_tbl[] = { {"l2", set_l2_mode}, @@ -595,6 +596,8 @@ static int parse_cluster_level(struct device_node *node, "qcom,disable-dynamic-int-routing"); level->last_core_only = of_property_read_bool(node, "qcom,last-core-only"); + level->no_cache_flush = of_property_read_bool(node, + "qcom,no-cache-flush"); key = "parse_power_params"; ret = parse_power_params(node, &level->pwr); diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index b0c65c761e16..3c8a6d924b50 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -354,10 +354,12 @@ static void msm_pm_set_timer(uint32_t modified_time_us) hrtimer_start(&lpm_hrtimer, modified_ktime, HRTIMER_MODE_REL_PINNED); } -int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm) +int set_l2_mode(struct low_power_ops *ops, int mode, + struct lpm_cluster_level *level) { int lpm = mode; int rc = 0; + bool notify_rpm = level->notify_rpm; struct low_power_ops *cpu_ops = per_cpu(cpu_cluster, smp_processor_id())->lpm_dev; @@ -369,7 +371,10 @@ int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm) case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE: case MSM_SPM_MODE_POWER_COLLAPSE: case MSM_SPM_MODE_FASTPC: - cpu_ops->tz_flag = MSM_SCM_L2_OFF; + if (level->no_cache_flush) + cpu_ops->tz_flag = MSM_SCM_L2_GDHS; + else + cpu_ops->tz_flag = MSM_SCM_L2_OFF; coresight_cti_ctx_save(); break; case MSM_SPM_MODE_GDHS: @@ -400,8 +405,10 @@ int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm) return rc; } -int set_l3_mode(struct low_power_ops *ops, int mode, bool notify_rpm) +int set_l3_mode(struct low_power_ops *ops, int mode, + struct lpm_cluster_level *level) { + bool notify_rpm = level->notify_rpm; struct low_power_ops *cpu_ops = per_cpu(cpu_cluster, smp_processor_id())->lpm_dev; @@ -418,8 +425,10 @@ int set_l3_mode(struct low_power_ops *ops, int mode, bool notify_rpm) } -int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm) +int set_system_mode(struct low_power_ops *ops, int mode, + struct lpm_cluster_level *level) { + bool notify_rpm = level->notify_rpm; return msm_spm_config_low_power_mode(ops->spm, mode, notify_rpm); } @@ -434,7 +443,7 @@ static int set_device_mode(struct lpm_cluster *cluster, int ndevice, ops = &cluster->lpm_dev[ndevice]; if (ops && ops->set_mode) return ops->set_mode(ops, level->mode[ndevice], - level->notify_rpm); + level); else return -EINVAL; } diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index f6979c4d4d9f..e91a16d04fa4 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -76,11 +76,13 @@ struct lpm_cluster_level { unsigned int psci_id; bool is_reset; int reset_level; + bool no_cache_flush; }; struct low_power_ops { struct msm_spm_device *spm; - int (*set_mode)(struct low_power_ops *ops, int mode, bool notify_rpm); + int (*set_mode)(struct low_power_ops *ops, int mode, + struct lpm_cluster_level *level); enum msm_pm_l2_scm_flag tz_flag; }; @@ -110,9 +112,12 @@ struct lpm_cluster { bool no_saw_devices; }; -int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm); -int set_system_mode(struct low_power_ops *ops, int mode, bool notify_rpm); -int set_l3_mode(struct low_power_ops *ops, int mode, bool notify_rpm); +int set_l2_mode(struct low_power_ops *ops, int mode, + struct lpm_cluster_level *level); +int set_system_mode(struct low_power_ops *ops, int mode, + struct lpm_cluster_level *level); +int set_l3_mode(struct low_power_ops *ops, int mode, + struct lpm_cluster_level *level); void lpm_suspend_wake_time(uint64_t wakeup_time); struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev); -- GitLab From fccd67185cb398ec7d91cd243073c519aee1979d Mon Sep 17 00:00:00 2001 From: Srinivas Rao L Date: Mon, 23 Oct 2017 14:16:37 +0530 Subject: [PATCH 1305/5498] ARM: dts: msm: support APC off with L2RAM on state for MSM8909W Support APCS_PMIC_OFF_L2RAM_ON mode for MSM8909W + PM660. Change-Id: I092627926654b83ffb7b2bef10c01a37cf7b9229 Signed-off-by: Srinivas Rao L --- arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi b/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi index 3e09b6f41c74..c1980bb99ac4 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi @@ -175,9 +175,9 @@ }; qcom,mode2 { qcom,label = "qcom,saw2-spm-cmd-pc"; - qcom,sequence = [00 32 b0 10 e0 d0 6b c0 42 f0 11 - 07 01 b0 50 4e 02 02 c0 d0 12 e0 6b 02 32 - 50 f0 0f]; /*APC_L2RAM_OFF */ + qcom,sequence = [00 20 32 b0 6b c0 e0 d0 42 11 07 + 01 b0 50 4e 02 02 d0 e0 c0 22 6b 02 32 52 + 0f]; /*APC_L2RAM_ON */ qcom,spm_en; qcom,pc_mode; }; @@ -228,6 +228,7 @@ qcom,time-overhead = <2500>; qcom,min-child-idx = <2>; qcom,notify-rpm; + qcom,no-cache-flush; qcom,reset-level = ; }; -- GitLab From 717459c6b6f2bde6380ee917bd785d5748177851 Mon Sep 17 00:00:00 2001 From: mraja Date: Tue, 17 Oct 2017 21:20:46 +0530 Subject: [PATCH 1306/5498] ARM: dts: msm: increase modem size to 115MB on SDX20 Increase the modem region size to 115MB on sdx20 as per the latest memory map. Change-Id: I6da1df5306e36e25bc7d25003a25519b3f569197 Signed-off-by: mraja Signed-off-by: phanendra yarra --- arch/arm/boot/dts/qcom/mdm9650.dtsi | 2 +- arch/arm/boot/dts/qcom/sdx20.dtsi | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650.dtsi b/arch/arm/boot/dts/qcom/mdm9650.dtsi index f8824c5cf846..1f618ea8c36e 100644 --- a/arch/arm/boot/dts/qcom/mdm9650.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650.dtsi @@ -42,7 +42,7 @@ }; mss_mem: mss_region@88000000 { compatible = "removed-dma-pool"; - no-map; + no-map-fixup; reg = <0x88000000 0x6E00000>; label = "mss_mem"; }; diff --git a/arch/arm/boot/dts/qcom/sdx20.dtsi b/arch/arm/boot/dts/qcom/sdx20.dtsi index 6484148608ad..54c55d7ba88a 100644 --- a/arch/arm/boot/dts/qcom/sdx20.dtsi +++ b/arch/arm/boot/dts/qcom/sdx20.dtsi @@ -35,6 +35,10 @@ qcom,sensor-id = <0 1 2 3 4>; }; +&mss_mem { + reg = <0x88000000 0x7300000>; +}; + &soc { /* SD card 2.95 V supply */ sdc_vreg: sdc_vreg { -- GitLab From c45bd7d945deafa28eff5d503674026d29ad0cc0 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Mon, 23 Oct 2017 17:30:27 +0530 Subject: [PATCH 1307/5498] msm: rmnet_ipa: do not release resource on pending data In case there is pending data submitted to IPA, IPA RM resource should not be released. The resource release happens only when all packets were processed by IPA. Change-Id: I9a9ede39785d0e5e75d81c02233182460d0d17a6 Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 46841e11c462..b5fc1c92aebe 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1109,8 +1109,9 @@ send: dev->stats.tx_bytes += skb->len; ret = NETDEV_TX_OK; out: - ipa_rm_inactivity_timer_release_resource( - IPA_RM_RESOURCE_WWAN_0_PROD); + if (atomic_read(&wwan_ptr->outstanding_pkts) == 0) + ipa_rm_inactivity_timer_release_resource( + IPA_RM_RESOURCE_WWAN_0_PROD); return ret; } @@ -1162,10 +1163,12 @@ static void apps_ipa_tx_complete_notify(void *priv, wwan_ptr->outstanding_low); netif_wake_queue(wwan_ptr->net); } + + if (atomic_read(&wwan_ptr->outstanding_pkts) == 0) + ipa_rm_inactivity_timer_release_resource( + IPA_RM_RESOURCE_WWAN_0_PROD); __netif_tx_unlock_bh(netdev_get_tx_queue(dev, 0)); dev_kfree_skb_any(skb); - ipa_rm_inactivity_timer_release_resource( - IPA_RM_RESOURCE_WWAN_0_PROD); } /** -- GitLab From 30b6ca9bfb061507f0d1628349a578458a5c8749 Mon Sep 17 00:00:00 2001 From: Surajit Podder Date: Wed, 6 Sep 2017 21:57:56 +0530 Subject: [PATCH 1308/5498] media: v4l2-compat-ioctl32: memset stack union in compat ioctl memset karg in do_video_ioctl to ensure that stack memory is not copied to user memory. Change-Id: Ib892f8cabff1e0076c670496ee6353d00afdf85e Signed-off-by: Surajit Podder Signed-off-by: Sanjay Singh --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index cade82fbebcf..823d8b0f27ee 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -851,6 +851,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar int compatible_arg = 1; long err = 0; + memset(&karg, 0, sizeof(karg)); /* First, convert the command. */ switch (cmd) { case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; -- GitLab From f5eff5d925912ac7cfca07843f96a5cec6787eac Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Thu, 14 Sep 2017 19:07:09 +0800 Subject: [PATCH 1309/5498] ARM: dts: msm: add sensor device node for msm8909 SKUE Add device node for BMA2x2 accelerometer sensor and LTR553 ambient light and proximity sensor. Change-Id: I706c89457bf60b8eaaeac6f6d83c0019f26c1b05 Signed-off-by: Bingzhe Cai --- arch/arm/boot/dts/qcom/msm8909-qrd-skue.dtsi | 113 +++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909-qrd-skue.dtsi b/arch/arm/boot/dts/qcom/msm8909-qrd-skue.dtsi index d3dc8f909f55..062ac1b684f7 100644 --- a/arch/arm/boot/dts/qcom/msm8909-qrd-skue.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-qrd-skue.dtsi @@ -109,6 +109,56 @@ qcom,y-offset = <0>; }; + i2c@78b5000 { /* BLSP1 QUP1 */ + liteon@23 { + compatible = "liteon,ltr553"; + reg = <0x23>; + vdd-supply = <&pm8909_l17>; + vio-supply = <&pm8909_l6>; + interrupt-parent = <&msm_gpio>; + interrupts = <94 0x2002>; + pinctrl-names = "default","sleep"; + pinctrl-0 = <<r553_default>; + pinctrl-1 = <<r553_sleep>; + liteon,irq-gpio = <&msm_gpio 94 0x2002>; + liteon,als-ps-persist = <0>; + liteon,ps-led = <0x7f>; + liteon,ps-pulses = <4>; + liteon,wakeup-threshold = <4>; + liteon,als-integration-time = <0>; + liteon,ps-distance-table = \ + <1376 566 343 287 200 170 155>; + }; + + bosch@18 { /* Accelerometer sensor */ + compatible = "bosch,bma2x2"; + reg = <0x18>; + pinctrl-names = "default"; + pinctrl-0 = <&bma2x2_int1_default &bma2x2_int2_default>; + interrupt-parent = <&msm_gpio>; + interrupts = <96 0x2002>; + vdd-supply = <&pm8909_l17>; + vio-supply = <&pm8909_l6>; + bosch,init-interval = <200>; + bosch,place = <4>; + bosch,gpio-int1 = <&msm_gpio 96 0x2002>; + bosch,gpio-int2 = <&msm_gpio 65 0x2002>; + }; + }; + + hall { + compatible = "hall-switch"; + pinctrl-names = "default"; + pinctrl-0 = <&hall_sensor_int_default>; + interrupt-parent = <&msm_gpio>; + interrupts = <36 0x2003>; + vddio-supply = <&pm8909_l5>; + linux,gpio-int = <&msm_gpio 36 0x1>; + linux,wakeup; + linux,min-uv = <1650000>; + linux,max-uv = <3300000>; + }; + sound { compatible = "qcom,msm8952-audio-codec"; qcom,model = "msm8909-skue-snd-card"; @@ -312,6 +362,69 @@ }; }; }; + bma2x2_int1_pin { + bma2x2_int1_default: int1_default { + mux { + pins = "gpio96"; + function = "gpio"; + }; + config { + pins = "gpio96"; + drive-dtrength = <6>; + bias-pull-up; + }; + }; + }; + bma2x2_int2_pin { + bma2x2_int2_default: int2_default { + mux { + pins = "gpio65"; + function = "gpio"; + }; + config { + pins = "gpio65"; + drive-dtrength = <6>; + bias-pull-up; + }; + }; + }; + ltr553_int_pin { + ltr553_default: ltr553_default { + mux { + pins = "gpio94"; + function = "gpio"; + }; + config { + pins = "gpio94"; + drive-dtrength = <6>; + bias-pull-up; + }; + }; + ltr553_sleep: ltr553_sleep { + mux { + pins = "gpio94"; + function = "gpio"; + }; + config { + pins = "gpio94"; + drive-dtrength = <2>; + bias-pull-down; + }; + }; + }; + hall_int_pin { + hall_sensor_int_default: hall_sensor_int_default { + mux { + pins = "gpio36"; + function = "gpio"; + }; + config { + pins = "gpio36"; + drive-dtrength = <6>; + bias-pull-up; + }; + }; + }; }; &mdss_dsi0 { -- GitLab From 19b0911d4344072f6131bdb281a19df34aaeb027 Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Sat, 21 Oct 2017 16:36:02 -0700 Subject: [PATCH 1310/5498] pps-gpio: Add new property to use system time timestamp for PPS GPIO If the device tree property "use-system-time-ts" is defined then the timestamp used for PPS events will be the system time, otherwise it will be the monotonic boot time. Change-Id: I92fab1d8861e1d652fcb68066b417f44c1f8e29b Signed-off-by: Gustavo Solaira --- Documentation/devicetree/bindings/pps/pps-gpio.txt | 3 +++ drivers/pps/clients/pps-gpio.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pps/pps-gpio.txt b/Documentation/devicetree/bindings/pps/pps-gpio.txt index 40bf9c3564a5..a834868ba6a2 100644 --- a/Documentation/devicetree/bindings/pps/pps-gpio.txt +++ b/Documentation/devicetree/bindings/pps/pps-gpio.txt @@ -10,6 +10,9 @@ Required properties: Optional properties: - assert-falling-edge: when present, assert is indicated by a falling edge (instead of by a rising edge) +- use-system-time-ts: use the system time via pps_get_ts as the timestamp + if this is not defined then the timestamp will come + from the monotonic boot time Example: pps { diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index 331daf5f6d3b..f74dd4e339db 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -44,6 +44,7 @@ struct pps_gpio_device_data { bool assert_falling_edge; bool capture_clear; unsigned int gpio_pin; + bool use_system_time_ts; }; /* @@ -56,11 +57,14 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data) struct pps_event_time ts; int rising_edge; - /* Get the time stamp first */ - get_monotonic_boottime(&ts.ts_real); - info = data; + /* Get the time stamp first */ + if (!info->use_system_time_ts) + get_monotonic_boottime(&ts.ts_real); + else + pps_get_ts(&ts); + rising_edge = gpio_get_value(info->gpio_pin); if ((rising_edge && !info->assert_falling_edge) || (!rising_edge && info->assert_falling_edge)) @@ -119,6 +123,9 @@ static int pps_gpio_probe(struct platform_device *pdev) if (of_get_property(np, "assert-falling-edge", NULL)) data->assert_falling_edge = true; + + if (of_get_property(np, "use-system-time-ts", NULL)) + data->use_system_time_ts = true; } /* GPIO setup */ -- GitLab From 86c001af8c56b0dea869e8135fb99f05c2d7c735 Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Sat, 21 Oct 2017 16:57:45 -0700 Subject: [PATCH 1311/5498] ARM: dts: msm: Use system time timestamp for PPS on mdm9650 CV2X Use system time timestamp for the PPS GPIO driver on mdm9650 CV2X boards. Change-Id: I8aa7a19819921d44e53ee49602e6d6a93ec884ef Signed-off-by: Gustavo Solaira --- arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi b/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi index 59b85ec93548..0dde4a007dfe 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi @@ -52,6 +52,10 @@ enable-active-high; regulator-always-on; }; + + pps { + use-system-time-ts; + }; }; &cnss_pcie { -- GitLab From e8dcaae263b4fd20c5f2242e6352629b39450f33 Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Thu, 5 Oct 2017 17:00:57 -0400 Subject: [PATCH 1312/5498] FROMLIST: android: binder: Change binder_shrinker to static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (from https://patchwork.kernel.org/patch/9990321/) binder_shrinker struct is not used anywhere outside of binder_alloc.c and should be static. Bug: 63926541 Change-Id: I7a13d4ddbaaf3721cddfe1d860e34c7be80dd082 Acked-by: Arve HjønnevÃ¥g Signed-off-by: Sherry Yang --- drivers/staging/android/binder_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index a5ec9709fc54..2ef2dadb0c6e 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -985,7 +985,7 @@ binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) return ret; } -struct shrinker binder_shrinker = { +static struct shrinker binder_shrinker = { .count_objects = binder_shrink_count, .scan_objects = binder_shrink_scan, .seeks = DEFAULT_SEEKS, -- GitLab From 6e9ad2f2d5064a84f1151626064f60ecb27c7a14 Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Thu, 5 Oct 2017 17:13:47 -0400 Subject: [PATCH 1313/5498] FROMLIST: android: binder: Fix null ptr dereference in debug msg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (from https://patchwork.kernel.org/patch/9990323/) Don't access next->data in kernel debug message when the next buffer is null. Bug: 36007193 Change-Id: Ib8240d7e9a7087a2256e88c0ae84b9df0f2d0224 Acked-by: Arve HjønnevÃ¥g Signed-off-by: Sherry Yang --- drivers/staging/android/binder_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index 2ef2dadb0c6e..8309da048a8c 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -561,7 +561,7 @@ static void binder_delete_free_buffer(struct binder_alloc *alloc, binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, "%d: merge free, buffer %pK do not share page with %pK or %pK\n", alloc->pid, buffer->data, - prev->data, next->data); + prev->data, next ? next->data : NULL); binder_update_page_range(alloc, 0, buffer_start_page(buffer), buffer_start_page(buffer) + PAGE_SIZE); } -- GitLab From 47ccaa2f5a59304c12240c941a1b4a9e457afb3d Mon Sep 17 00:00:00 2001 From: Peng Xu Date: Tue, 3 Oct 2017 23:21:51 +0300 Subject: [PATCH 1314/5498] nl80211: Define policy for packet pattern attributes Define a policy for packet pattern attributes in order to fix a potential read over the end of the buffer during nla_get_u32() of the NL80211_PKTPAT_OFFSET attribute. Note that the data there can always be read due to SKB allocation (with alignment and struct skb_shared_info at the end), but the data might be uninitialized. This could be used to leak some data from uninitialized vmalloc() memory, but most drivers don't allow an offset (so you'd just get -EINVAL if the data is non-zero) or just allow it with a fixed value - 100 or 128 bytes, so anything above that would get -EINVAL. With brcmfmac the limit is 1500 so (at least) one byte could be obtained. Cc: stable@kernel.org Signed-off-by: Peng Xu Signed-off-by: Jouni Malinen [rewrite description based on SKB allocation knowledge] Signed-off-by: Johannes Berg Conflicts: net/wireless/nl80211.c Git-commit: ad670233c9e1d5feb365d870e30083ef1b889177 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git CRs-fixed: 2116387 Change-Id: Ia84ca10f85507fe3ddbbb518388ca7b453fd8453 [Backport: Fix conflicts] Signed-off-by: Vidyullatha Kanchanapally Signed-off-by: Peng Xu --- net/wireless/nl80211.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ba48a327b2ac..cc3f9195a76f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -495,6 +495,14 @@ nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = { }, }; +/* policy for packet pattern attributes */ +static const struct nla_policy +nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = { + [NL80211_PKTPAT_MASK] = { .type = NLA_BINARY, }, + [NL80211_PKTPAT_PATTERN] = { .type = NLA_BINARY, }, + [NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 }, +}; + static int nl80211_prepare_wdev_dump(struct sk_buff *skb, struct netlink_callback *cb, struct cfg80211_registered_device **rdev, @@ -9295,7 +9303,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) u8 *mask_pat; nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), - nla_len(pat), NULL); + nla_len(pat), nl80211_packet_pattern_policy); err = -EINVAL; if (!pat_tb[NL80211_PKTPAT_MASK] || !pat_tb[NL80211_PKTPAT_PATTERN]) @@ -9523,7 +9531,7 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, u8 *mask_pat; nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), - nla_len(pat), NULL); + nla_len(pat), nl80211_packet_pattern_policy); if (!pat_tb[NL80211_PKTPAT_MASK] || !pat_tb[NL80211_PKTPAT_PATTERN]) return -EINVAL; -- GitLab From 2ddc554c1268521fccb6e6742e47359ba43db202 Mon Sep 17 00:00:00 2001 From: gaolez Date: Fri, 20 Oct 2017 16:33:18 +0800 Subject: [PATCH 1315/5498] msm: wlan: Enable DFS flag for TT Enable DFS flag for TT since the corresponding 5Ghz regulatory is set as FCC3 which shall support DFS channel. Change-Id: I1bff90af2ffcd3ee3e1c3a8b5eeb3939c6cd51e6 Signed-off-by: Gaole Zhang --- net/wireless/db.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 6a7b522f1f46..e423dd9e931c 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -1342,10 +1342,11 @@ country TR: DFS-ETSI # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR -country TT: +country TT: DFS-FCC (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (24) - (5490 - 5730 @ 160), (24) + (5170 - 5250 @ 80), (24) + (5250 - 5330 @ 80), (24), DFS + (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country TW: DFS-FCC -- GitLab From c1feda94c177ac9f0388759072824fec6dcfb47f Mon Sep 17 00:00:00 2001 From: Siba Prasad Date: Wed, 13 Sep 2017 20:43:57 +0530 Subject: [PATCH 1316/5498] mmc: core: Prevent accessing user space buffer directly In mmc_wr_pack_stats_read(), userspace buffer is directly accessed which is violating PAN (Privilege Access Never). So to prevent this, data which needs to be copied to user buffer is gathered into a local buffer and at the end, this local buffer contents are copied back to buffer supplied from user space using copy_to_user(). Change-Id: Id772613fe54c47d64906c18d3d0249eee2f6ac26 Signed-off-by: Siba Prasad --- drivers/mmc/core/debugfs.c | 53 ++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index e02301e3c96c..f71863715909 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -568,9 +568,10 @@ static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf, { struct mmc_card *card = filp->private_data; struct mmc_wr_pack_stats *pack_stats; - int i; + int i, ret = 0; int max_num_of_packed_reqs = 0; - char *temp_buf; + char *temp_buf, *temp_ubuf; + size_t tubuf_cnt = 0; if (!card) return cnt; @@ -596,15 +597,24 @@ static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf, max_num_of_packed_reqs = card->ext_csd.max_packed_writes; - temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL); + if (cnt <= (strlen_user(ubuf) + 1)) + goto exit; + + temp_buf = kzalloc(TEMP_BUF_SIZE, GFP_KERNEL); if (!temp_buf) goto exit; + tubuf_cnt = cnt - strlen_user(ubuf) - 1; + + temp_ubuf = kzalloc(tubuf_cnt, GFP_KERNEL); + if (!temp_ubuf) + goto cleanup; + spin_lock(&pack_stats->lock); snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n", mmc_hostname(card->host)); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) { if (pack_stats->packing_events[i]) { @@ -612,63 +622,63 @@ static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf, "%s: Packed %d reqs - %d times\n", mmc_hostname(card->host), i, pack_stats->packing_events[i]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } } snprintf(temp_buf, TEMP_BUF_SIZE, "%s: stopped packing due to the following reasons:\n", mmc_hostname(card->host)); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: exceed max num of segments\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: exceed max num of sectors\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[EXCEEDS_SECTORS]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: wrong data direction\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[WRONG_DATA_DIR]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: flush or discard\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: empty queue\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[EMPTY_QUEUE]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[REL_WRITE]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: rel write\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[REL_WRITE]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[THRESHOLD]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: Threshold\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[THRESHOLD]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) { @@ -676,25 +686,36 @@ static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf, "%s: %d times: Large sector alignment\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[RANDOM]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: random request\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[RANDOM]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } if (pack_stats->pack_stop_reason[FUA]) { snprintf(temp_buf, TEMP_BUF_SIZE, "%s: %d times: fua request\n", mmc_hostname(card->host), pack_stats->pack_stop_reason[FUA]); - strlcat(ubuf, temp_buf, cnt); + strlcat(temp_ubuf, temp_buf, tubuf_cnt); } + if (strlen_user(ubuf) < cnt - strlen(temp_ubuf)) + ret = copy_to_user((ubuf + strlen_user(ubuf)), + temp_ubuf, tubuf_cnt); + else + ret = -EFAULT; + if (ret) + pr_err("%s: %s: Copy to userspace failed: %s\n", + mmc_hostname(card->host), __func__, ubuf); spin_unlock(&pack_stats->lock); + kfree(temp_ubuf); + +cleanup: kfree(temp_buf); pr_info("%s", ubuf); -- GitLab From 7ccfaadc92d2d490536489c1107dc3e304f27c94 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 24 Oct 2017 11:49:12 +0530 Subject: [PATCH 1317/5498] ASoC: msm8x16-wcd: Fix lineout pop issue At lineout of analog codec, observe pop during powerdown. Handle teardown sequence to turn off registers as per HW sequence. Change-Id: Ic3a7dc34416b5242d15c5b4cd494b380dc7de7de Signed-off-by: Laxminath Kasam --- sound/soc/codecs/msm8x16-wcd.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c index 91e8257222b8..034a2c4ac9f7 100644 --- a/sound/soc/codecs/msm8x16-wcd.c +++ b/sound/soc/codecs/msm8x16-wcd.c @@ -4242,8 +4242,6 @@ static int msm8x16_wcd_lo_dac_event(struct snd_soc_dapm_widget *w, MSM8X16_WCD_A_ANALOG_RX_LO_DAC_CTL, 0x80, 0x80); snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_LO_EN_CTL, 0x40, 0x40); break; case SND_SOC_DAPM_POST_PMD: usleep_range(20000, 20100); @@ -4255,8 +4253,6 @@ static int msm8x16_wcd_lo_dac_event(struct snd_soc_dapm_widget *w, MSM8X16_WCD_A_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_LO_EN_CTL, 0x80, 0x00); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_LO_EN_CTL, 0x40, 0x00); snd_soc_update_bits(codec, MSM8X16_WCD_A_ANALOG_RX_LO_EN_CTL, 0x20, 0x00); snd_soc_update_bits(codec, @@ -4913,9 +4909,9 @@ static int msm8x16_wcd_codec_enable_lo_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, MSM8X16_WCD_A_CDC_RX3_B6_CTL, 0x01, 0x00); break; - case SND_SOC_DAPM_POST_PMD: + case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_RX3_B6_CTL, 0x01, 0x00); + MSM8X16_WCD_A_CDC_RX3_B6_CTL, 0x01, 0x01); break; } @@ -5099,8 +5095,8 @@ static const struct snd_soc_dapm_widget msm8x16_wcd_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("LINEOUT PA", MSM8X16_WCD_A_ANALOG_RX_LO_EN_CTL, - 5, 0 , NULL, 0, msm8x16_wcd_codec_enable_lo_pa, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + 6, 0 , NULL, 0, msm8x16_wcd_codec_enable_lo_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0, msm89xx_wcd_codec_enable_vdd_spkr, -- GitLab From 0485d9db4a2d3f8caa158754be405712e66c5b99 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 15 Sep 2017 19:01:59 +0530 Subject: [PATCH 1318/5498] ASoC: Add drivers to support BG codec driver Add audio codec driver to configure BG audio codec. Add glink client driver to send audio commands to audio client. BG audio codec driver communicates with the BG using the glink client driver. Change-Id: I2795e75a522478e65e79c19dcf062d3e157ca26a Signed-off-by: Yeleswarapu Nagaradhesh Signed-off-by: Ashish Jain --- .../bindings/sound/qcom-audio-dev.txt | 21 + drivers/soc/qcom/Kconfig | 10 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/bg_glink.c | 494 +++++++++++++ include/soc/qcom/bg_glink.h | 36 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/bg_codec.c | 654 ++++++++++++++++++ sound/soc/codecs/pktzr.c | 280 ++++++++ sound/soc/codecs/pktzr.h | 71 ++ 10 files changed, 1573 insertions(+) create mode 100644 drivers/soc/qcom/bg_glink.c create mode 100644 include/soc/qcom/bg_glink.h create mode 100644 sound/soc/codecs/bg_codec.c create mode 100644 sound/soc/codecs/pktzr.c create mode 100644 sound/soc/codecs/pktzr.h diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 6db7f7180740..3a820c87bed6 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2667,3 +2667,24 @@ sound { asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; }; + +* BG Codec Driver. + +Required properties: +- compatible : "qcom,bg-codec" +- qcom,bg-glink : Glink component required for the BG codec communication. + - compatible :"qcom,bg-cdc-glink" +- qcom,msm-glink-channels: Number of glink channels available to communicate + with the glink client + + +Example: + + bg_cdc: bg_codec { + status = "disabled"; + compatible = "qcom,bg-codec"; + qcom,bg-glink { + compatible = "qcom,bg-cdc-glink"; + qcom,msm-glink-channels = <4>; + }; + }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a167416214c1..e78a75221a93 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -910,6 +910,16 @@ config QCOM_QDSS_BRIDGE and USB interface. The amount of buffer memory allocated at runtime can be modified via module params. If unsure, say N. +config MSM_BG_GLINK + tristate "WCD DSP GLINK Driver" + depends on MSM_GLINK + default y if MSM_BGCOM=y + help + This option enables driver which provides communication interface + between MSM and BG over glink transport protocol. This driver + provides read and write interface via char device.This driver is + used by the audio codec driver to interface with BG. + config WCD_DSP_GLINK tristate "WCD DSP GLINK Driver" depends on MSM_GLINK diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 643d1c7bcd37..fe78445ee8af 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT) += glink_smem_native_xprt.o obj-$(CONFIG_MSM_GLINK_BGCOM_XPRT) += glink_bgcom_xprt.o obj-$(CONFIG_MSM_SPCOM) += spcom.o obj-$(CONFIG_MSM_SMEM_LOGGING) += smem_log.o +obj-$(CONFIG_MSM_BG_GLINK) += bg_glink.o obj-$(CONFIG_MSM_SMP2P) += smp2p.o smp2p_debug.o smp2p_sleepstate.o obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o obj-$(CONFIG_MSM_QMI_INTERFACE) += qmi_interface.o diff --git a/drivers/soc/qcom/bg_glink.c b/drivers/soc/qcom/bg_glink.c new file mode 100644 index 000000000000..c6bb8599b353 --- /dev/null +++ b/drivers/soc/qcom/bg_glink.c @@ -0,0 +1,494 @@ +/* Copyright (c) 2017 The Linux Foundation.All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GLINK_LINK_STATE_UP_WAIT_TIMEOUT 5000 +#define APR_MAXIMUM_NUM_OF_RETRIES 2 +#define BG_GLINK_NAME "bg-cdc-glink" +#define BG_GLINK_EDGE "bg" +#define BG_MAX_NO_OF_INTENTS 20 + +struct bg_cdc_glink_drvdata { + struct device *dev; + struct platform_device *pdev; + struct bg_cdc_glink_ch_info *ch_info; + void *handle; + wait_queue_head_t wait; + u8 num_channels; + u8 active_channel; + enum glink_link_state link_state; +}; + +struct bg_cdc_glink_ch_info { + void *handle; + struct mutex w_lock; + struct mutex r_lock; + struct mutex m_lock; + bg_glink_cb_fn func; + wait_queue_head_t wait; + unsigned channel_state; + bool if_remote_intent_ready; +}; +static int __bg_cdc_glink_write(struct bg_cdc_glink_ch_info *ch_info, + void *data, char *tx_buf, int len) +{ + int rc = 0; + + if (!ch_info) + return -EINVAL; + mutex_lock(&ch_info->w_lock); + rc = glink_tx(ch_info->handle, tx_buf, data, len, GLINK_TX_REQ_INTENT); + mutex_unlock(&ch_info->w_lock); + + if (rc) + pr_err("%s: glink_tx failed, rc[%d]\n", __func__, rc); + else + rc = len; + + return rc; +} + +int bg_cdc_glink_write(void *ch_info, void *data, + int len) +{ + int rc = 0; + char *tx_buf = NULL; + + if (!((struct bg_cdc_glink_ch_info *)ch_info)->handle || !data) + return -EINVAL; + + /* check if channel is connected before proceeding */ + if (((struct bg_cdc_glink_ch_info *)ch_info)->channel_state + != GLINK_CONNECTED) { + pr_err("channel is not connected\n"); + return -EINVAL; + } + + tx_buf = kzalloc((sizeof(char) * len), GFP_KERNEL); + if (IS_ERR_OR_NULL(tx_buf)) { + rc = -EINVAL; + goto exit; + } + memcpy(tx_buf, data, len); + + rc = __bg_cdc_glink_write((struct bg_cdc_glink_ch_info *)ch_info, + tx_buf, tx_buf, len); + + if (rc < 0) { + pr_err("%s: Unable to send the packet, rc:%d\n", __func__, rc); + kfree(tx_buf); + } +exit: + return rc; +} +EXPORT_SYMBOL(bg_cdc_glink_write); + +static void bg_cdc_glink_notify_rx(void *handle, const void *priv, + const void *pkt_priv, const void *ptr, + size_t size) +{ + struct bg_cdc_glink_ch_info *ch_info = + (struct bg_cdc_glink_ch_info *)priv; + + if (!ch_info || !ptr) { + pr_err("%s: Invalid ch_info or ptr\n", __func__); + return; + } + + pr_debug("%s: Rx packet received\n", __func__); + + mutex_lock(&ch_info->w_lock); + if (ch_info->func) + ch_info->func((void *)ptr, size); + mutex_unlock(&ch_info->w_lock); + glink_rx_done(ch_info->handle, ptr, true); +} + +static void bg_cdc_glink_notify_tx_abort(void *handle, const void *priv, + const void *pkt_priv) +{ + pr_debug("%s: tx_abort received for pkt_priv:%pK\n", + __func__, pkt_priv); + kfree(pkt_priv); +} + +static void bg_cdc_glink_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + pr_debug("%s: tx_done received for pkt_priv:%pK\n", + __func__, pkt_priv); + kfree(pkt_priv); +} + +static bool bg_cdc_glink_notify_rx_intent_req(void *handle, const void *priv, + size_t req_size) +{ + struct bg_cdc_glink_ch_info *ch_info = + (struct bg_cdc_glink_ch_info *)priv; + + if (!ch_info) { + pr_err("%s: Invalid ch_info\n", __func__); + return false; + } + + pr_debug("%s: No rx intents queued, unable to receive\n", __func__); + return false; +} + +static void bg_cdc_glink_notify_remote_rx_intent(void *handle, const void *priv, + size_t size) +{ + struct bg_cdc_glink_ch_info *ch_info = + (struct bg_cdc_glink_ch_info *)priv; + + if (!ch_info) { + pr_err("%s: Invalid ch_info\n", __func__); + return; + } + /* + * This is to make sure that the far end has queued at least one intent + * before we attempt any IPC. + */ + pr_debug("%s: remote queued an intent\n", __func__); + ch_info->if_remote_intent_ready = true; + wake_up(&ch_info->wait); +} + +static void bg_cdc_glink_notify_state(void *handle, const void *priv, + unsigned event) +{ + struct bg_cdc_glink_ch_info *ch_info = + (struct bg_cdc_glink_ch_info *)priv; + + if (!ch_info) { + pr_err("%s: Invalid ch_info\n", __func__); + return; + } + + ch_info->channel_state = event; + pr_debug("%s: Channel state[%d]\n", __func__, event); + + if (event == GLINK_CONNECTED) + wake_up(&ch_info->wait); +} + +static int bg_cdc_glink_rx_intents_config(struct bg_cdc_glink_ch_info *ch_info, + int num_of_intents, uint32_t *size) +{ + int i; + int rc = 0; + + if (!ch_info || !num_of_intents || !size) { + pr_err("%s: Invalid parameter\n", __func__); + return -EINVAL; + } + if (num_of_intents > BG_MAX_NO_OF_INTENTS) { + pr_err("%s: Invalid no_of_intents = %d\n", + __func__, num_of_intents); + return -EINVAL; + } + + for (i = 0; i < num_of_intents; i++) { + rc = glink_queue_rx_intent(ch_info->handle, ch_info, *(size+i)); + if (rc) { + pr_err("%s: Failed to queue rx intent, iteration[%d]\n", + __func__, i); + break; + } + } + + return rc; +} +/* + * bg_cdc_channel_open - API to open Glink channel. + * ch_cfg: glink channel configuration + * func: callback function to notify client. + */ +void *bg_cdc_channel_open(struct platform_device *pdev, + struct bg_glink_ch_cfg *ch_cfg, + bg_glink_cb_fn func) +{ + int rc; + struct bg_cdc_glink_drvdata *bg_cdc_glink; + struct glink_open_config open_cfg; + struct bg_cdc_glink_ch_info *ch_info; + + if (!pdev) { + pr_err("%s: invalid platform device\n", __func__); + return NULL; + } + bg_cdc_glink = platform_get_drvdata(pdev); + + if (!bg_cdc_glink) { + dev_err(&pdev->dev, "%s: driver data not found\n", + __func__); + return NULL; + } + if (bg_cdc_glink->active_channel > bg_cdc_glink->num_channels) { + dev_err(bg_cdc_glink->dev, "%s: invalid channel number\n", + __func__); + return NULL; + } + + ch_info = &bg_cdc_glink->ch_info[bg_cdc_glink->active_channel]; + mutex_lock(&ch_info->m_lock); + if (ch_info->handle) { + dev_err(&pdev->dev, "%s: This channel is already opened\n", + __func__); + rc = -EBUSY; + goto unlock; + } + + if (bg_cdc_glink->link_state != GLINK_LINK_STATE_UP) { + rc = wait_event_timeout(bg_cdc_glink->wait, + bg_cdc_glink->link_state == GLINK_LINK_STATE_UP, + msecs_to_jiffies(GLINK_LINK_STATE_UP_WAIT_TIMEOUT)); + if (rc == 0) { + dev_err(bg_cdc_glink->dev, "%s: Open timeout\n", + __func__); + rc = -ETIMEDOUT; + goto unlock; + } + dev_dbg(bg_cdc_glink->dev, "%s: Wakeup done\n", __func__); + } + + memset(&open_cfg, 0, sizeof(struct glink_open_config)); + open_cfg.options = GLINK_OPT_INITIAL_XPORT; + open_cfg.edge = BG_GLINK_EDGE; + open_cfg.name = ch_cfg->ch_name; + open_cfg.notify_rx = bg_cdc_glink_notify_rx; + open_cfg.notify_tx_done = bg_cdc_glink_notify_tx_done; + open_cfg.notify_state = bg_cdc_glink_notify_state; + open_cfg.notify_rx_intent_req = bg_cdc_glink_notify_rx_intent_req; + open_cfg.notify_remote_rx_intent = bg_cdc_glink_notify_remote_rx_intent; + open_cfg.notify_tx_abort = bg_cdc_glink_notify_tx_abort; + open_cfg.priv = ch_info; + + ch_info->channel_state = GLINK_REMOTE_DISCONNECTED; + ch_info->handle = glink_open(&open_cfg); + if (IS_ERR_OR_NULL(ch_info->handle)) { + dev_err(bg_cdc_glink->dev, "%s: glink_open failed %s\n", + __func__, ch_cfg->ch_name); + ch_info->handle = NULL; + rc = -EINVAL; + goto unlock; + } + bg_cdc_glink->active_channel++; + rc = wait_event_timeout(ch_info->wait, + (ch_info->channel_state == GLINK_CONNECTED), 5 * HZ); + if (rc == 0) { + dev_err(bg_cdc_glink->dev, "%s: TIMEOUT for OPEN event\n", + __func__); + rc = -ETIMEDOUT; + goto close_link; + } + + /* + * Remote intent is not required for GLINK <--> BG + * designed not to fail the open call. + */ + rc = wait_event_timeout(ch_info->wait, + ch_info->if_remote_intent_ready, 5 * HZ); + if (rc == 0) + dev_err(&pdev->dev, "%s: TIMEOUT for remote intent readiness\n", + __func__); + + pr_err("Remote is ready !!!\n"); + rc = bg_cdc_glink_rx_intents_config(ch_info, + ch_cfg->num_of_intents, ch_cfg->intents_size); + if (rc) { + dev_err(bg_cdc_glink->dev, "%s: Unable to queue intents\n", + __func__); + goto close_link; + } + + ch_info->func = func; + +close_link: + if (rc) { + if (bg_cdc_glink->active_channel > 0) + bg_cdc_glink->active_channel--; + glink_close(ch_info->handle); + ch_info->handle = NULL; + } +unlock: + mutex_unlock(&ch_info->m_lock); + + return rc ? NULL : (void *)ch_info; +} +EXPORT_SYMBOL(bg_cdc_channel_open); + + +int bg_cdc_channel_close(struct platform_device *pdev, + void *ch_info) +{ + struct bg_cdc_glink_drvdata *bg_cdc_glink; + int rc; + struct bg_cdc_glink_ch_info *channel_info + = (struct bg_cdc_glink_ch_info *)ch_info; + bg_cdc_glink = platform_get_drvdata(pdev); + if (!channel_info || !channel_info->handle) { + rc = -EINVAL; + goto exit; + } + + mutex_lock(&channel_info->m_lock); + if (bg_cdc_glink->active_channel > 0) + bg_cdc_glink->active_channel--; + rc = glink_close(channel_info->handle); + channel_info->handle = NULL; + channel_info->func = NULL; + channel_info->if_remote_intent_ready = false; + mutex_unlock(&channel_info->m_lock); +exit: + return rc; +} +EXPORT_SYMBOL(bg_cdc_channel_close); + +static void bg_cdc_glink_link_state_cb(struct glink_link_state_cb_info *cb_info, + void *priv) +{ + struct bg_cdc_glink_drvdata *bg_cdc_glink = + (struct bg_cdc_glink_drvdata *) priv; + + if (!cb_info) { + pr_err("%s: Invalid cb_info\n", __func__); + return; + } + + dev_dbg(bg_cdc_glink->dev, "%s: edge[%s] link state[%d]\n", __func__, + cb_info->edge, cb_info->link_state); + + bg_cdc_glink->link_state = cb_info->link_state; + if (bg_cdc_glink->link_state == GLINK_LINK_STATE_UP) + wake_up(&bg_cdc_glink->wait); +} + +static int bg_cdc_glink_probe(struct platform_device *pdev) +{ + struct bg_cdc_glink_drvdata *bg_cdc_glink; + struct glink_link_info link_info; + u32 num_channels; + int ret = 0; + int i; + + ret = of_property_read_u32(pdev->dev.of_node, "qcom,msm-glink-channels", + &num_channels); + + if (ret) { + dev_err(&pdev->dev, "%s: glink channels from DT file %s\n", + __func__, "qcom,msm-glink-channels"); + return -EINVAL; + } + + /* Allocate BG codec Glink structure */ + bg_cdc_glink = kzalloc(sizeof(struct bg_cdc_glink_drvdata), GFP_KERNEL); + if (!bg_cdc_glink) + return -ENOMEM; + + bg_cdc_glink->ch_info = kzalloc((sizeof(struct bg_cdc_glink_ch_info) * + num_channels), GFP_KERNEL); + if (!bg_cdc_glink->ch_info) { + ret = -ENOMEM; + goto err_memory_fail; + } + bg_cdc_glink->dev = &pdev->dev; + bg_cdc_glink->pdev = pdev; + bg_cdc_glink->num_channels = num_channels; + platform_set_drvdata(pdev, bg_cdc_glink); + + init_waitqueue_head(&bg_cdc_glink->wait); + + /* Register glink link_state notification */ + link_info.glink_link_state_notif_cb = bg_cdc_glink_link_state_cb; + link_info.transport = NULL; + link_info.edge = BG_GLINK_EDGE; + bg_cdc_glink->link_state = GLINK_LINK_STATE_DOWN; + bg_cdc_glink->handle = + glink_register_link_state_cb(&link_info, bg_cdc_glink); + if (!bg_cdc_glink->handle) { + dev_err(&pdev->dev, "%s: Unable to register link state\n", + __func__); + ret = -EINVAL; + goto err_glink_register_fail; + } + + for (i = 0; i < num_channels; i++) { + mutex_init(&bg_cdc_glink->ch_info[i].w_lock); + mutex_init(&bg_cdc_glink->ch_info[i].r_lock); + mutex_init(&bg_cdc_glink->ch_info[i].m_lock); + init_waitqueue_head(&bg_cdc_glink->ch_info[i].wait); + } + return ret; +err_glink_register_fail: + kfree(bg_cdc_glink->ch_info); + +err_memory_fail: + kfree(bg_cdc_glink); + + return ret; +} + +static const struct of_device_id bg_cdc_glink_of_match[] = { + { .compatible = "qcom,bg-cdc-glink", }, + {}, +}; + +static int bg_cdc_glink_remove(struct platform_device *pdev) +{ + struct bg_cdc_glink_drvdata *bg_cdc_glink = platform_get_drvdata(pdev); + int i; + + if (!bg_cdc_glink) { + dev_err(&pdev->dev, "%s: invalid data\n", + __func__); + return -EINVAL; + } + if (bg_cdc_glink->handle) + glink_unregister_link_state_cb(bg_cdc_glink->handle); + + for (i = 0; i < bg_cdc_glink->num_channels; i++) { + mutex_destroy(&bg_cdc_glink->ch_info[i].w_lock); + mutex_destroy(&bg_cdc_glink->ch_info[i].r_lock); + mutex_destroy(&bg_cdc_glink->ch_info[i].m_lock); + } + kfree(bg_cdc_glink->ch_info); + kfree(bg_cdc_glink); + return 0; +} + +static struct platform_driver msm_bg_cdc_glink_driver = { + .driver = { + .owner = THIS_MODULE, + .name = BG_GLINK_NAME, + .of_match_table = bg_cdc_glink_of_match, + }, + .probe = bg_cdc_glink_probe, + .remove = bg_cdc_glink_remove, +}; +module_platform_driver(msm_bg_cdc_glink_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BG Glink driver"); diff --git a/include/soc/qcom/bg_glink.h b/include/soc/qcom/bg_glink.h new file mode 100644 index 000000000000..f97592ab410f --- /dev/null +++ b/include/soc/qcom/bg_glink.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __BG_GLINK_H_ +#define __BG_GLINK_H_ + +#include +#include +#include +#include + +typedef int (*bg_glink_cb_fn)(void *buf, int len); + +struct bg_glink_ch_cfg { + char ch_name[100]; + int num_of_intents; + uint32_t *intents_size; +}; + +int bg_cdc_glink_write(void *ch_info, void *data, + int len); +void *bg_cdc_channel_open(struct platform_device *pdev, + struct bg_glink_ch_cfg *ch_cfg, + bg_glink_cb_fn func); +int bg_cdc_channel_close(struct platform_device *pdev, + void *ch_info); +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index aae1b9a86eb6..3dd0ecca92a1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -632,6 +632,10 @@ config SND_SOC_WCD9330 config SND_SOC_WCD9335 tristate +config SND_SOC_BG_CODEC + tristate + default m if MSM_BG_GLINK=y + config SND_SOC_WSA881X_SENSORS tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8c9db9b1a62a..1836dde6403c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -117,6 +117,7 @@ snd-soc-wcd9306-objs := wcd9306.o wcd9306-tables.o snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o snd-soc-wcd9330-objs := wcd9330.o wcd9330-tables.o snd-soc-wcd9335-objs := wcd9335.o +bg-codec-objs := bg_codec.o pktzr.o snd-soc-wcd9xxx-objs := wcd9xxx-resmgr.o wcd9xxx-mbhc.o wcd9xxx-common.o snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o audio-ext-clock-objs := audio-ext-clk.o @@ -291,6 +292,7 @@ obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o +obj-$(CONFIG_SND_SOC_BG_CODEC) += bg-codec.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c new file mode 100644 index 000000000000..5d2a4278820e --- /dev/null +++ b/sound/soc/codecs/bg_codec.c @@ -0,0 +1,654 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pktzr.h" + + +#define TASHA_RX_PORT_START_NUMBER 16 + +#define BG_RATES_MAX (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define BG_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + + + +enum { + BG_AIF1_PB = 0, + BG_AIF2_PB, + BG_AIF3_PB, + BG_AIF4_PB, + BG_AIF1_CAP, + BG_AIF2_CAP, + BG_AIF3_CAP, + BG_AIF4_CAP, + NUM_CODEC_DAIS, +}; + + +enum { + PLAYBACK = 0, + CAPTURE, +}; + +struct bg_hw_params { + u32 active_session; + u32 rx_sample_rate; + u32 rx_bit_width; + u32 rx_num_channels; + u32 tx_sample_rate; + u32 tx_bit_width; + u32 tx_num_channels; +}; + +struct bg_cdc_priv { + struct device *dev; + struct snd_soc_codec *codec; + struct platform_device *pdev_child; + struct work_struct bg_cdc_add_child_devices_work; + struct delayed_work bg_cdc_pktzr_init_work; + unsigned long status_mask; + struct bg_hw_params hw_params; +}; + +struct graphite_basic_rsp_result { + /* Valid Graphite error code or completion status */ + uint32_t status; +}; + +static int bg_cdc_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s: substream = %s stream = %d\n" , __func__, + substream->name, substream->stream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + set_bit(PLAYBACK, &bg_cdc->status_mask); + else + set_bit(CAPTURE, &bg_cdc->status_mask); + + return 0; +} + +static void bg_cdc_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s: substream = %s stream = %d\n" , __func__, + substream->name, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + clear_bit(PLAYBACK, &bg_cdc->status_mask); + else + clear_bit(PLAYBACK, &bg_cdc->status_mask); + +} + +static int bg_cdc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); + struct bg_hw_params hw_params; + struct pktzr_cmd_rsp rsp; + int ret = 0; + + rsp.buf = NULL; + + pr_debug("%s: dai_name = %s DAI-ID %x rate %d width %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_width(params), params_channels(params)); + + switch (dai->id) { + case BG_AIF1_PB: + bg_cdc->hw_params.active_session = 0x0001; + break; + case BG_AIF1_CAP: + bg_cdc->hw_params.active_session = 0x00010000; + break; + case BG_AIF2_PB: + bg_cdc->hw_params.active_session = 0x0001; + break; + case BG_AIF2_CAP: + bg_cdc->hw_params.active_session = 0x00020000; + break; + case BG_AIF3_PB: + break; + case BG_AIF3_CAP: + bg_cdc->hw_params.active_session = 0x00010000; + break; + case BG_AIF4_PB: + break; + case BG_AIF4_CAP: + bg_cdc->hw_params.active_session = 0x00020000; + break; + default: + pr_err("%s:Invalid dai id %d", __func__, dai->id); + ret = -EINVAL; + goto exit; + } + + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + bg_cdc->hw_params.rx_sample_rate = params_rate(params); + bg_cdc->hw_params.rx_bit_width = params_width(params); + bg_cdc->hw_params.rx_num_channels = params_channels(params); + } else { + bg_cdc->hw_params.tx_sample_rate = params_rate(params); + bg_cdc->hw_params.tx_bit_width = params_width(params); + bg_cdc->hw_params.tx_num_channels = params_channels(params); + } + + /* check if RX, TX sampling freq is same if not return error. */ + /* Send command to BG for HW params */ + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return -ENOMEM; + memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); + /* Send command to BG to start session */ + ret = pktzr_cmd_set_params(&hw_params, sizeof(hw_params), &rsp); + if (ret < 0) { + pr_err("pktzr cmd set params failed\n"); + ret = 0; + goto exit; + } +exit: + if (rsp.buf) + kzfree(rsp.buf); + return ret; +} + +static int bg_cdc_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct pktzr_cmd_rsp rsp; + int ret = 0; + uint32_t active_session = 0; + + pr_debug("%s: dai_name = %s DAI-ID %x\n", __func__, dai->name, dai->id); + + rsp.buf = NULL; + + switch (dai->id) { + case BG_AIF1_PB: + active_session = 0x0001; + break; + case BG_AIF1_CAP: + active_session = 0x00010000; + break; + case BG_AIF2_PB: + active_session = 0x0001; + break; + case BG_AIF2_CAP: + active_session = 0x00020000; + break; + case BG_AIF3_PB: + break; + case BG_AIF3_CAP: + active_session = 0x00010000; + break; + case BG_AIF4_PB: + break; + case BG_AIF4_CAP: + active_session = 0x00020000; + break; + default: + pr_err("%s:Invalid dai id %d", __func__, dai->id); + ret = -EINVAL; + goto exit; + } + + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return -ENOMEM; + /* Send command to BG to start session */ + ret = pktzr_cmd_stop(&active_session, + sizeof(active_session), &rsp); + if (ret < 0) { + pr_err("pktzr cmd close failed\n"); + ret = 0; + goto exit; + } +exit: + if (rsp.buf) + kzfree(rsp.buf); + return ret; +} + +static int bg_cdc_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct pktzr_cmd_rsp rsp; + int ret = 0; + uint32_t active_session = 0; + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); + + rsp.buf = NULL; + + + if (test_bit(PLAYBACK, &bg_cdc->status_mask) && + test_bit(CAPTURE, &bg_cdc->status_mask)) { + if ((bg_cdc->hw_params.rx_sample_rate != + bg_cdc->hw_params.tx_sample_rate) || + (bg_cdc->hw_params.rx_bit_width != + bg_cdc->hw_params.rx_bit_width)) { + pr_err("%s diff rx and tx configuration %d:%d:%d:%d\n", + __func__, bg_cdc->hw_params.rx_sample_rate, + bg_cdc->hw_params.tx_sample_rate, + bg_cdc->hw_params.rx_bit_width, + bg_cdc->hw_params.rx_bit_width); + return -EINVAL; + } + } + + switch (dai->id) { + case BG_AIF1_PB: + active_session = 0x0001; + break; + case BG_AIF1_CAP: + active_session = 0x00010000; + break; + case BG_AIF2_PB: + active_session = 0x0001; + break; + case BG_AIF2_CAP: + active_session = 0x00020000; + break; + case BG_AIF3_PB: + break; + case BG_AIF3_CAP: + active_session = 0x00010000; + break; + case BG_AIF4_PB: + break; + case BG_AIF4_CAP: + active_session = 0x00020000; + break; + default: + pr_err("%s:Invalid dai id %d", __func__, dai->id); + ret = -EINVAL; + goto exit; + } + + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return -ENOMEM; + /* Send command to BG to start session */ + ret = pktzr_cmd_start(&active_session, sizeof(active_session), &rsp); + if (ret < 0) { + pr_err("pktzr cmd start failed\n"); + ret = 0; + goto exit; + } +exit: + if (rsp.buf) + kzfree(rsp.buf); + return ret; + +} + +static int bg_cdc_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + pr_debug("in func %s", __func__); + return 0; +} + + +static int bg_cdc_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + pr_debug("in func %s", __func__); + return 0; +} + +static struct snd_soc_dai_ops bg_cdc_dai_ops = { + .startup = bg_cdc_startup, + .shutdown = bg_cdc_shutdown, + .hw_params = bg_cdc_hw_params, + .hw_free = bg_cdc_hw_free, + .prepare = bg_cdc_prepare, + .set_channel_map = bg_cdc_set_channel_map, + .get_channel_map = bg_cdc_get_channel_map, +}; + +static struct snd_soc_dai_driver bg_cdc_dai[] = { + { + .name = "bg_cdc_rx1", + .id = BG_AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &bg_cdc_dai_ops, + }, + { + .name = "bg_cdc_rx2", + .id = BG_AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &bg_cdc_dai_ops, + }, + { + .name = "bg_cdc_rx3", + .id = BG_AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &bg_cdc_dai_ops, + }, + { + .name = "bg_cdc_rx4", + .id = BG_AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &bg_cdc_dai_ops, + }, + { + .name = "bg_cdc_tx1", + .id = BG_AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &bg_cdc_dai_ops, + }, + { + .name = "bg_cdc_tx2", + .id = BG_AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &bg_cdc_dai_ops, + }, + { + .name = "bg_cdc_tx3", + .id = BG_AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &bg_cdc_dai_ops, + }, + { + .name = "bg_cdc_tx4", + .id = BG_AIF4_CAP, + .capture = { + .stream_name = "AIF4 Capture", + .rates = BG_RATES_MAX, + .formats = BG_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &bg_cdc_dai_ops, + }, +}; + +static int data_cmd_rsp(void *buf, uint32_t len, void *priv_data, + bool *is_basic_rsp) +{ + struct graphite_basic_rsp_result *resp; + + pr_debug("in data_cmd_rsp"); + + if (buf != NULL) { + resp = buf; + pr_err("%s: status = %d\n", __func__, resp->status); + } + + return 0; +} + +static void bg_cdc_pktzr_init(struct work_struct *work) +{ + int ret; + struct bg_cdc_priv *bg_cdc; + struct delayed_work *dwork; + int num_of_intents = 2; + uint32_t size[2] = {2048, 2048}; + struct bg_glink_ch_cfg ch_info[1] = { + {"CODEC_CHANNEL", num_of_intents, size} + }; + struct bg_hw_params hw_params; + struct pktzr_cmd_rsp rsp; + + pr_debug("%s\n", __func__); + dwork = to_delayed_work(work); + bg_cdc = container_of(dwork, struct bg_cdc_priv, + bg_cdc_pktzr_init_work); + + ret = pktzr_init(bg_cdc->pdev_child, ch_info, 1, data_cmd_rsp); + if (ret < 0) { + dev_err(bg_cdc->dev, "%s: failed in pktzr_init\n", __func__); + /*return ret*/; + } + + /* Send open command */ + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); + /* Send command to BG to start session */ + ret = pktzr_cmd_open(&hw_params, sizeof(hw_params), &rsp); + if (ret < 0) + pr_err("pktzr cmd open failed\n"); + + if (rsp.buf) + kzfree(rsp.buf); +} + +static int bg_cdc_codec_probe(struct snd_soc_codec *codec) +{ + struct bg_cdc_priv *bg_cdc = dev_get_drvdata(codec->dev); + + schedule_delayed_work(&bg_cdc->bg_cdc_pktzr_init_work, + msecs_to_jiffies(400)); + + return 0; +} + +static int bg_cdc_codec_remove(struct snd_soc_codec *codec) +{ + pr_debug("In func %s\n", __func__); + pktzr_deinit(); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_bg_cdc = { + .probe = bg_cdc_codec_probe, + .remove = bg_cdc_codec_remove, +}; + +static void bg_cdc_add_child_devices(struct work_struct *work) +{ + struct platform_device *pdev = NULL; + struct device_node *node; + char plat_dev_name[50] = "bg-cdc"; + struct bg_cdc_priv *bg_cdc; + int ret; + + pr_debug("%s\n", __func__); + bg_cdc = container_of(work, struct bg_cdc_priv, + bg_cdc_add_child_devices_work); + if (!bg_cdc) { + pr_err("%s: Memory for BG codec does not exist\n", + __func__); + return; + } + for_each_available_child_of_node(bg_cdc->dev->of_node, node) { + pr_debug("hnode->name = %s\n", node->name); + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(bg_cdc->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = bg_cdc->dev; + pdev->dev.of_node = node; + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + bg_cdc->pdev_child = pdev; + } +fail_pdev_add: + if (pdev) + platform_device_put(pdev); +err: + return; +} + +static int bg_cdc_probe(struct platform_device *pdev) +{ + struct bg_cdc_priv *bg_cdc; + int ret = 0; + + bg_cdc = kzalloc(sizeof(struct bg_cdc_priv), + GFP_KERNEL); + if (!bg_cdc) + return -ENOMEM; + + bg_cdc->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, bg_cdc); + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bg_cdc, + bg_cdc_dai, ARRAY_SIZE(bg_cdc_dai)); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n", + __func__, ret); + goto err_cdc_reg; + } + + INIT_WORK(&bg_cdc->bg_cdc_add_child_devices_work, + bg_cdc_add_child_devices); + INIT_DELAYED_WORK(&bg_cdc->bg_cdc_pktzr_init_work, + bg_cdc_pktzr_init); + schedule_work(&bg_cdc->bg_cdc_add_child_devices_work); + + dev_dbg(&pdev->dev, "%s: BG driver probe done\n", __func__); + return ret; + +err_cdc_reg: + + kfree(bg_cdc); + return ret; +} + +static int bg_cdc_remove(struct platform_device *pdev) +{ + struct bg_cdc_priv *bg_cdc; + + bg_cdc = platform_get_drvdata(pdev); + + snd_soc_unregister_codec(&pdev->dev); + kfree(bg_cdc); + return 0; +} + + +#define MODULE_NAME "bg_codec" + +static const struct of_device_id audio_codec_of_match[] = { + { .compatible = "qcom,bg-codec", }, + {}, +}; + +static struct platform_driver bg_codec_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = audio_codec_of_match, + }, + .probe = bg_cdc_probe, + .remove = bg_cdc_remove, +}; +module_platform_driver(bg_codec_driver); + +MODULE_DESCRIPTION("BG Codec driver Loader module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pktzr.c b/sound/soc/codecs/pktzr.c new file mode 100644 index 000000000000..33c2b4eddf5f --- /dev/null +++ b/sound/soc/codecs/pktzr.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pktzr.h" + +#define MSM_BG_THREAD_TIMEOUT (9 * (HZ/10)) +#define PKTZR_MAX_CHANNELS 4 + +struct pktzr_hdr { + uint8_t version; + uint8_t reserved; + uint16_t opcode; + uint8_t client_id; + uint8_t domain_id; + uint16_t token; + uint32_t payload_size; +}; + +/* pkt format */ +struct pktzr_pkt { + struct pktzr_hdr hdr; + uint8_t payload[0]; +}; + +struct pktzr_node { + struct list_head list; + uint16_t token; + uint16_t cmd; + void *priv; +}; + +struct pktzr_priv { + void *ch_info[PKTZR_MAX_CHANNELS]; + struct completion thread_complete; + struct mutex pktzr_lock; + uint16_t token; + struct list_head ch_list; + struct platform_device *pdev; + pktzr_data_cmd_cb_fn data_cmd_cb; + int num_channels; +}; + +static struct pktzr_priv *ppriv; + +static int pktzr_resp_cb(void *payload, int size) +{ + int rc = 0; + struct pktzr_pkt *pkt; + struct pktzr_hdr *pkt_hdr; + + if (!payload) { + pr_err("%s: payload is NULL\n", __func__); + rc = -EINVAL; + goto done; + } + pr_debug("%s: entry\n", __func__); + + pkt = (struct pktzr_pkt *)payload; + pkt_hdr = &pkt->hdr; + + if (pkt_hdr->opcode == PKTZR_BASIC_RESPONSE_RESULT) { + pr_debug("%s: Command response: success\n", __func__); + complete(&ppriv->thread_complete); + } else + pr_err("%s: Command response: fail\n", __func__); +done: + return rc; +} + +static int pktzr_send_pkt(void *payload, uint32_t size, void *rsp, + uint16_t cmd, bool sync_cmd) +{ + struct pktzr_pkt *pkt_hdr; + struct pktzr_node *pnode; + int pkt_size = 0; + int rc = 0; + + pr_debug("%s: cmd=%d sync=%d size=%d\n", __func__, cmd, sync_cmd, size); + if (!ppriv) { + pr_err("packetizer not initialized"); + return -EINVAL; + } + mutex_lock(&ppriv->pktzr_lock); + if (++ppriv->token == 0) + ppriv->token = 1; + + pkt_size = sizeof(struct pktzr_pkt) + size; + pkt_hdr = kzalloc(pkt_size, GFP_KERNEL); + if (!pkt_hdr) { + pr_err("%s: Failed to initialise pkt header\n", __func__); + return -ENOMEM; + } + + pnode = kzalloc(sizeof(struct pktzr_node), GFP_KERNEL); + if (!pnode) { + kfree(pkt_hdr); + return -ENOMEM; + } + + pnode->priv = rsp; + pnode->token = ppriv->token; + pnode->cmd = cmd; + pkt_hdr->hdr.version = VERSION_ID; + pkt_hdr->hdr.opcode = cmd; + pkt_hdr->hdr.client_id = CLIENT_ID_AUDIO; + pkt_hdr->hdr.domain_id = DOMAIN_ID_APPS; + pkt_hdr->hdr.token = ppriv->token; + pkt_hdr->hdr.payload_size = size; + memcpy(pkt_hdr->payload, (char *)payload, size); + INIT_LIST_HEAD(&pnode->list); + pr_debug("ppriv->token = %d\n", ppriv->token); + list_add_tail(&pnode->list, &ppriv->ch_list); + + if (cmd == PKTZR_CMD_DATA) + rc = bg_cdc_glink_write(ppriv->ch_info[1], pkt_hdr, pkt_size); + else + rc = bg_cdc_glink_write(ppriv->ch_info[0], pkt_hdr, pkt_size); + if (rc < 0) { + pr_err("%s: Failed to send command over glink\n", __func__); + list_del(&pnode->list); + goto exit; + } + + + if (sync_cmd) { + pr_debug("%s: command sent waiting!\n", __func__); + mutex_unlock(&ppriv->pktzr_lock); + rc = wait_for_completion_timeout(&ppriv->thread_complete, + MSM_BG_THREAD_TIMEOUT); + if (!rc) { + pr_err("%s: Wait for thread timedout\n", __func__); + rc = -ETIMEDOUT; + goto exit; + } + mutex_lock(&ppriv->pktzr_lock); + list_del(&pnode->list); + kfree(pnode); + pnode = NULL; + mutex_unlock(&ppriv->pktzr_lock); + } + pr_debug("%s: command processing done\n", __func__); +exit: + /* Free memory */ + kfree(pkt_hdr); + if (pnode) { + list_del(&pnode->list); + kfree(pnode); + } + return rc; +} + +int pktzr_cmd_open(void *payload, uint32_t size, struct pktzr_cmd_rsp *rsp) +{ + return pktzr_send_pkt(payload, size, rsp, PKTZR_CMD_OPEN, true); +} + +int pktzr_cmd_close(void *payload, uint32_t size, struct pktzr_cmd_rsp *rsp) +{ + return pktzr_send_pkt(payload, size, rsp, PKTZR_CMD_CLOSE, true); +} + +int pktzr_cmd_start(void *payload, uint32_t size, struct pktzr_cmd_rsp *rsp) +{ + return pktzr_send_pkt(payload, size, rsp, PKTZR_CMD_START, true); +} + +int pktzr_cmd_stop(void *payload, uint32_t size, struct pktzr_cmd_rsp *rsp) +{ + return pktzr_send_pkt(payload, size, rsp, PKTZR_CMD_STOP, true); +} + +int pktzr_cmd_set_params(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp) +{ + return pktzr_send_pkt(payload, size, rsp, PKTZR_CMD_SET_CONFIG, true); +} + +int pktzr_cmd_data(void *payload, uint32_t size, void *priv_data) +{ + return pktzr_send_pkt(payload, size, priv_data, PKTZR_CMD_DATA, false); +} + +int pktzr_init(void *pdev, struct bg_glink_ch_cfg *ch_info, int num_channels, + pktzr_data_cmd_cb_fn func) +{ + int i; + + if (num_channels > 4) { + pr_err("%s: Invalid num channels:%d\n", __func__, num_channels); + return -EINVAL; + } + + if (!ppriv) { + ppriv = kzalloc((sizeof(struct pktzr_priv)), GFP_KERNEL); + if (!ppriv) { + pr_err("%s:Failed to allocate memory for ppriv\n", + __func__); + return -ENOMEM; + } + } else { + pr_debug("%s: Already initialized\n", __func__); + goto done; + } + + for (i = 0; i < num_channels; i++) { + ppriv->ch_info[i] = bg_cdc_channel_open(pdev, + &ch_info[i], + pktzr_resp_cb); + if (!ppriv->ch_info[i]) { + pr_err("%s: Failed to open channel\n", __func__); + goto err; + } + ppriv->token = i; + } + ppriv->num_channels = num_channels; + ppriv->data_cmd_cb = func; + ppriv->token = 0; + ppriv->pdev = pdev; + init_completion(&ppriv->thread_complete); + mutex_init(&ppriv->pktzr_lock); + INIT_LIST_HEAD(&ppriv->ch_list); + +done: + return 0; +err: + for (i = 0; i < ppriv->token; i++) + bg_cdc_channel_close(ppriv->pdev, ppriv->ch_info[i]); + ppriv->token = 0; + if (ppriv) + kzfree(ppriv); + ppriv = NULL; + return -EINVAL; +} + +void pktzr_deinit(void) +{ + int rc; + int i; + + if (!ppriv) + return; + + for (i = 0; i < ppriv->num_channels; i++) { + rc = bg_cdc_channel_close(ppriv->pdev, ppriv->ch_info[i]); + if (rc) + pr_err("%s:Failed to close channel\n", __func__); + } + reinit_completion(&ppriv->thread_complete); + mutex_destroy(&ppriv->pktzr_lock); + kzfree(ppriv); + ppriv = NULL; +} + diff --git a/sound/soc/codecs/pktzr.h b/sound/soc/codecs/pktzr.h new file mode 100644 index 000000000000..efeee3f7576c --- /dev/null +++ b/sound/soc/codecs/pktzr.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __PKTZR_H_ +#define __PKTZR_H_ + +#include +#include +#include +#include +#include + +#define VERSION_ID 0x80 +#define CLIENT_ID_AUDIO 0X01 +#define DOMAIN_ID_APPS 0x01 + +#define PKTZR_BASIC_RESPONSE_RESULT 0x0002 +#define PKTZR_CMD_OPEN 0x0021 +#define PKTZR_CMD_SET_CONFIG 0x0022 +#define PKTZR_CMD_START 0x0023 +#define PKTZR_CMD_STOP 0x0024 +#define PKTZR_CMD_CLOSE 0x0027 +#define PKTZR_CMDRSP_GET_CONFIG 0x0007 +#define PKTZR_CMD_LOAD_DATA 0x0008 +#define PKTZR_CMDRSP_LOAD_DATA 0x0009 +#define PKTZR_CMD_UNLOAD_DATA 0x000A +#define PKTZR_CMD_EVENT 0x000B +#define PKTZR_CMD_DATA 0x000C +#define PKTZR_CMDRSP_DATA 0x000D + + +typedef int (*pktzr_data_cmd_cb_fn)(void *buf, uint32_t len, void *priv_data, + bool *is_basic_rsp); + +struct pktzr_cmd_rsp { + /* Requested resp buffer */ + void *buf; + /* Requested resp buffer size */ + uint32_t buf_size; + /* Received resp buffer size */ + uint32_t rsp_size; + /* Basic response or command response */ + bool is_basic_rsp; +}; + +extern int pktzr_init(void *pdev, struct bg_glink_ch_cfg *ch_info, + int num_channels, pktzr_data_cmd_cb_fn func); +extern void pktzr_deinit(void); +extern int pktzr_cmd_open(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp); +extern int pktzr_cmd_close(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp); +extern int pktzr_cmd_start(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp); +extern int pktzr_cmd_stop(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp); +extern int pktzr_cmd_data(void *payload, uint32_t size, void *priv_data); + +extern int pktzr_cmd_set_params(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp); +#endif -- GitLab From f90cc0206a6e1e7c936a7c2497b6e017d6924d87 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 15 Sep 2017 19:03:24 +0530 Subject: [PATCH 1319/5498] ARM: dts: msm: enable audio for BG codec based 8909w devices Enable bg codec driver and glink client driver for msm8909w based msm and apq variants. Change-Id: I654ccbfe324c91b42f8107dec7e8063c5ca5bdcf Signed-off-by: Yeleswarapu Nagaradhesh Signed-off-by: Ashish Jain --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 12 ++ arch/arm/boot/dts/qcom/msm8909-mtp.dtsi | 68 +++++++++++ arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 2 +- arch/arm/boot/dts/qcom/msm8909.dtsi | 106 ++++++++++++++++++ arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts | 12 ++ arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 12 ++ .../arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 3 - 7 files changed, 211 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 065ae3e42e99..52e3e49952a5 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -179,6 +179,18 @@ status = "disabled"; }; +&audio_codec_mtp { + status = "disabled"; +}; + +&audio_codec_bg { + status = "ok"; +}; + +&bg_cdc { + status = "ok"; +}; + &blsp1_uart1 { status = "ok"; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi index e1620532654c..32aff172e74d 100644 --- a/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi @@ -285,6 +285,74 @@ asoc-codec = <&stub_codec>, <&pm8909_conga_dig>; asoc-codec-names = "msm-stub-codec.1", "cajon_codec"; }; + + bg_cdc: bg_codec { + status = "disabled"; + compatible = "qcom,bg-codec"; + qcom,bg-glink { + compatible = "qcom,bg-cdc-glink"; + qcom,msm-glink-channels = <4>; + }; + }; + + audio_codec_bg: sound { + status = "disabled"; + compatible = "qcom,msm-bg-audio-codec"; + qcom,model = "msm-bg-snd-card"; + reg = <0x7702000 0x4>, + <0x7702004 0x4>, + <0x7702008 0x4>, + <0x770200c 0x4>; + reg-names = "csr_gp_io_mux_mic_ctl", + "csr_gp_io_mux_spkr_ctl", + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel", + "csr_gp_io_lpaif_sec_pcm_sec_mode_muxsel"; + + qcom,msm-snd-card-id = <0>; + qcom,msm-ext-pa = "primary"; + qcom,tdm-audio-intf; + qcom,msm-afe-clk-ver = <1>; + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>, + <&voice_svc>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-pcm-lpa", + "msm-voice-svc"; + asoc-cpu = <&dai_pri_auxpcm>, + <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, + <&dai_mi2s3>, <&dai_mi2s5>, <&dai_mi2s6>, + <&bt_sco_rx>, <&bt_sco_tx>, <&bt_a2dp_rx>, + <&int_fm_rx>, <&int_fm_tx>, <&afe_pcm_rx>, + <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, + <&incall_music_rx>, <&incall_music_2_rx>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>, + <&dai_pri_tdm_rx_1>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_tx_2>, + <&dai_pri_tdm_rx_3>, <&dai_pri_tdm_tx_3>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-mi2s.5", "msm-dai-q6-mi2s.6", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12290", "msm-dai-q6-dev.12292", + "msm-dai-q6-dev.12293", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770", "msm-dai-q6-tdm.36864", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36867", "msm-dai-q6-tdm.36868", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36870", + "msm-dai-q6-tdm.36871"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + }; }; &blsp1_uart1 { diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index d932b1f71a6c..737d48cf09a4 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -2039,7 +2039,6 @@ pins = "gpio0", "gpio1"; drive-strength = <8>; /* 8 MA */ bias-disable; /* No PULL */ - output-high; }; }; quat_mi2s_sleep: quat_mi2s_sleep { @@ -2066,6 +2065,7 @@ pins = "gpio2", "gpio3"; drive-strength = <8>; /* 8 MA */ bias-disable; /* No PULL */ + output-high; }; }; quat_mi2s_din_sleep: quat_mi2s_din_sleep { diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 60dce8f7f7c7..a6f0e9243aa4 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -1787,6 +1787,112 @@ qcom,clk-div = <27>; }; + pri_tdm_rx: qcom,msm-dai-tdm-pri-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37120>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>; + qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-afe-ebit-unsupported; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>; + pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>; + dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36864>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36866>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36868>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36870>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + pri_tdm_tx: qcom,msm-dai-tdm-pri-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37121>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>; + qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-afe-ebit-unsupported; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>; + pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>; + dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36865>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36867>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36869>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36871>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + qcom,memshare { compatible = "qcom,memshare"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts index 6812cb546905..2b4f45023862 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts @@ -210,3 +210,15 @@ pins = "gpio98", "gpio16"; }; }; + +&audio_codec_mtp { + status = "disabled"; +}; + +&audio_codec_bg { + status = "ok"; +}; + +&bg_cdc { + status = "ok"; +}; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 1f60271bc1ed..36644ba7d92c 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -168,6 +168,18 @@ }; }; +&audio_codec_mtp { + status = "disabled"; +}; + +&audio_codec_bg { + status = "ok"; +}; + +&bg_cdc { + status = "ok"; +}; + &i2c_1 { status = "okay"; nq@28 { diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index e0b156703f23..9db086835a04 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -117,9 +117,6 @@ }; }; -&audio_codec_mtp { - /delete-property/ asoc-codec; -}; &sdhc_2 { /delete-property/ vdd-supply; -- GitLab From 7db646494c24403f5cd3c0a554a60ed2ef777adb Mon Sep 17 00:00:00 2001 From: Sai Krishna Juturi Date: Wed, 18 Oct 2017 19:58:43 +0530 Subject: [PATCH 1320/5498] usb: dwc3-msm: Assign voltage_max after on charger identification voltage_max shows the max voltage that the charger can negotiate up to. Assign voltage_max value accordingly based on charger type. Change-Id: I7a127e9470de04b79df90dc37f65aa4b0d8195b6 Signed-off-by: Sai Krishna Juturi --- drivers/usb/dwc3/dwc3-msm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 2d8a37124436..3b553decbf43 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -56,6 +56,9 @@ #define DWC3_HVDCP_CHG_MAX 1800 #define DWC3_WAKEUP_SRC_TIMEOUT 5000 +#define MICRO_5V 5000000 +#define MICRO_9V 9000000 + /* AHB2PHY register offsets */ #define PERIPH_SS_AHB2PHY_TOP_CFG 0x10 @@ -2622,16 +2625,20 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, switch (psy->type) { case POWER_SUPPLY_TYPE_USB: mdwc->chg_type = DWC3_SDP_CHARGER; + mdwc->voltage_max = MICRO_5V; break; case POWER_SUPPLY_TYPE_USB_DCP: mdwc->chg_type = DWC3_DCP_CHARGER; + mdwc->voltage_max = MICRO_5V; break; case POWER_SUPPLY_TYPE_USB_HVDCP: mdwc->chg_type = DWC3_DCP_CHARGER; + mdwc->voltage_max = MICRO_9V; dwc3_msm_gadget_vbus_draw(mdwc, hvdcp_max_current); break; case POWER_SUPPLY_TYPE_USB_CDP: mdwc->chg_type = DWC3_CDP_CHARGER; + mdwc->voltage_max = MICRO_5V; break; case POWER_SUPPLY_TYPE_USB_ACA: mdwc->chg_type = DWC3_PROPRIETARY_CHARGER; -- GitLab From 66bfd3a0cfd1a498b89aa7f20cdea984d27bfb8a Mon Sep 17 00:00:00 2001 From: Shadab Naseem Date: Tue, 24 Oct 2017 20:27:00 +0530 Subject: [PATCH 1321/5498] ARM: dts: msm: Enable msm8909 512mb QRD SKUE DT Enable msm8909 512mb QRD SKUE device tree in Makefile. Change-Id: Id044a7ae54faa131205659cc5e30c397bda7fbbe Signed-off-by: Shadab Naseem --- arch/arm/boot/dts/qcom/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 92aad57dc494..25902b673a6c 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -304,6 +304,7 @@ dtb-$(CONFIG_ARCH_MSM8916) += msm8952-qrd-skum.dtb \ dtb-$(CONFIG_ARCH_MSM8909) += msm8909-pm8916-mtp.dtb \ msm8909-1gb-qrd-skuc.dtb \ msm8909-1gb-qrd-skue.dtb \ + msm8909-qrd-skue.dtb \ msm8909w-wtp.dtb \ apq8009w-wtp.dtb \ apq8009w-cdp.dtb \ -- GitLab From d5f24cf615de190d75ed5ca01d95593b5103d5fe Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Fri, 20 Oct 2017 09:54:57 -0700 Subject: [PATCH 1322/5498] cnss2: Decouple WLAN host driver and PCI/MHI for suspend/resume Decouple WLAN host driver suspend/resume and PCI link and MHI suspend/resume so that PCI link and MHI can be suspended/resumeed separately without WLAN host driver. Change-Id: I82cb7b8b54c61657c5d0bbaead0ffc1c55ecee8d Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/pci.c | 85 +++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 10149eeb93f9..ec5a6d21ab2b 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -134,6 +134,7 @@ int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv) if (!pci_priv) return -ENODEV; + cnss_pr_dbg("Suspending PCI link\n"); if (!pci_priv->pci_link_state) { cnss_pr_info("PCI link is already suspended!\n"); goto out; @@ -167,6 +168,7 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv) if (!pci_priv) return -ENODEV; + cnss_pr_dbg("Resuming PCI link\n"); if (pci_priv->pci_link_state) { cnss_pr_info("PCI link is already resumed!\n"); goto out; @@ -385,27 +387,37 @@ static int cnss_pci_suspend(struct device *dev) driver_ops = plat_priv->driver_ops; if (driver_ops && driver_ops->suspend) { ret = driver_ops->suspend(pci_dev, state); - if (pci_priv->pci_link_state) { - if (cnss_pci_set_mhi_state(pci_priv, - CNSS_MHI_SUSPEND)) { + if (ret) { + cnss_pr_err("Failed to suspend host driver, err = %d\n", + ret); + ret = -EAGAIN; + goto out; + } + } + + if (pci_priv->pci_link_state) { + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND); + if (ret) { + if (driver_ops && driver_ops->resume) driver_ops->resume(pci_dev); - ret = -EAGAIN; - goto out; - } + ret = -EAGAIN; + goto out; + } - cnss_set_pci_config_space(pci_priv, - SAVE_PCI_CONFIG_SPACE); - pci_disable_device(pci_dev); + cnss_set_pci_config_space(pci_priv, + SAVE_PCI_CONFIG_SPACE); + pci_disable_device(pci_dev); - ret = pci_set_power_state(pci_dev, PCI_D3hot); - if (ret) - cnss_pr_err("Failed to set D3Hot, err = %d\n", - ret); - } + ret = pci_set_power_state(pci_dev, PCI_D3hot); + if (ret) + cnss_pr_err("Failed to set D3Hot, err = %d\n", + ret); } cnss_pci_set_monitor_wake_intr(pci_priv, false); + return 0; + out: return ret; } @@ -425,23 +437,30 @@ static int cnss_pci_resume(struct device *dev) if (!plat_priv) goto out; - driver_ops = plat_priv->driver_ops; - if (driver_ops && driver_ops->resume && !pci_priv->pci_link_down_ind) { - ret = pci_enable_device(pci_dev); - if (ret) - cnss_pr_err("Failed to enable PCI device, err = %d\n", - ret); + if (pci_priv->pci_link_down_ind) + goto out; - if (pci_priv->saved_state) - cnss_set_pci_config_space(pci_priv, - RESTORE_PCI_CONFIG_SPACE); + ret = pci_enable_device(pci_dev); + if (ret) + cnss_pr_err("Failed to enable PCI device, err = %d\n", ret); - pci_set_master(pci_dev); - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); + if (pci_priv->saved_state) + cnss_set_pci_config_space(pci_priv, + RESTORE_PCI_CONFIG_SPACE); + + pci_set_master(pci_dev); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); + driver_ops = plat_priv->driver_ops; + if (driver_ops && driver_ops->resume) { ret = driver_ops->resume(pci_dev); + if (ret) + cnss_pr_err("Failed to resume host driver, err = %d\n", + ret); } + return 0; + out: return ret; } @@ -617,14 +636,17 @@ int cnss_auto_suspend(void) ret = pci_set_power_state(pci_dev, PCI_D3hot); if (ret) cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); + + cnss_pr_dbg("Suspending PCI link\n"); if (cnss_set_pci_link(pci_priv, PCI_LINK_DOWN)) { - cnss_pr_err("Failed to shutdown PCI link!\n"); + cnss_pr_err("Failed to suspend PCI link!\n"); ret = -EAGAIN; goto resume_mhi; } + + pci_priv->pci_link_state = PCI_LINK_DOWN; } - pci_priv->pci_link_state = PCI_LINK_DOWN; cnss_pci_set_auto_suspended(pci_priv, 1); cnss_pci_set_monitor_wake_intr(pci_priv, true); @@ -660,21 +682,24 @@ int cnss_auto_resume(void) pci_dev = pci_priv->pci_dev; if (!pci_priv->pci_link_state) { + cnss_pr_dbg("Resuming PCI link\n"); if (cnss_set_pci_link(pci_priv, PCI_LINK_UP)) { cnss_pr_err("Failed to resume PCI link!\n"); ret = -EAGAIN; goto out; } pci_priv->pci_link_state = PCI_LINK_UP; + ret = pci_enable_device(pci_dev); if (ret) cnss_pr_err("Failed to enable PCI device, err = %d\n", ret); + + cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE); + pci_set_master(pci_dev); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); } - cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE); - pci_set_master(pci_dev); - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); cnss_pci_set_auto_suspended(pci_priv, 0); bus_bw_info = &plat_priv->bus_bw_info; -- GitLab From fbef5bf5a091b565f5efb03567acee7df037c50c Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Wed, 25 Oct 2017 10:33:19 +0530 Subject: [PATCH 1323/5498] usb: f_gps: Ratelimit the error and debug messages During very high throughput if there is an issue of low memory or if the dynamic debugging is enabled, the target crashes because of the heavy logging of these messages. Ratelimit these messages to prevent the crash. CRs-Fixed: 2130811 Change-Id: I014685d19541b571194cd9fa50f62b168d005c12 Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/function/f_gps.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_gps.c b/drivers/usb/gadget/function/f_gps.c index ccf9dee8fbc7..9ea5ae948868 100644 --- a/drivers/usb/gadget/function/f_gps.c +++ b/drivers/usb/gadget/function/f_gps.c @@ -590,7 +590,8 @@ gps_send_cpkt_response(void *gr, void *buf, size_t len) } cpkt = gps_alloc_ctrl_pkt(len, GFP_ATOMIC); if (IS_ERR(cpkt)) { - pr_err("%s: Unable to allocate ctrl pkt\n", __func__); + pr_err_ratelimited("%s: Unable to allocate ctrl pkt\n", + __func__); return -ENOMEM; } memcpy(cpkt->buf, buf, len); @@ -598,7 +599,7 @@ gps_send_cpkt_response(void *gr, void *buf, size_t len) dev = port_to_gps(gr); - pr_debug("%s: dev:%pK\n", __func__, dev); + pr_debug_ratelimited("%s: dev:%pK\n", __func__, dev); if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) { gps_free_ctrl_pkt(cpkt); -- GitLab From 3e065b68a7881364d8d252b55beab79b9d142af9 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Wed, 25 Oct 2017 10:36:59 +0530 Subject: [PATCH 1324/5498] usb: gadget: Fix support for 2 serial ports over char_bridge Currently, u_data_bridge driver specifies that we can have 2 port instances supported over it. But there is a bug in the setup function to check the number of ports. Fix the bug and also add corresponding port_num support in f_serial driver. Change-Id: I564914ff737bee8bd89e08153190d65219d002cd Signed-off-by: Ajay Agarwal --- drivers/usb/gadget/function/f_serial.c | 1 + drivers/usb/gadget/function/u_data_bridge.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 65497407f64d..b26fa9b9a79a 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -1196,6 +1196,7 @@ int gserial_init_port(int port_num, const char *name, no_smd_ports++; break; case USB_GADGET_XPORT_CHAR_BRIDGE: + gserial_ports[port_num].client_port_num = no_char_bridge_ports; no_char_bridge_ports++; break; case USB_GADGET_XPORT_HSIC: diff --git a/drivers/usb/gadget/function/u_data_bridge.c b/drivers/usb/gadget/function/u_data_bridge.c index f5ecf3e27d65..a5bd9fda32e9 100644 --- a/drivers/usb/gadget/function/u_data_bridge.c +++ b/drivers/usb/gadget/function/u_data_bridge.c @@ -888,7 +888,7 @@ static void gbridge_debugfs_init(void) {} int gbridge_setup(void *gptr, u8 no_ports) { pr_debug("gptr:%pK, no_bridge_ports:%d\n", gptr, no_ports); - if (no_ports >= num_of_instance) { + if (no_ports > num_of_instance) { pr_err("More ports are requested\n"); return -EINVAL; } -- GitLab From 30146105ea88bc4dbde2cd59be0a2135e1bb820d Mon Sep 17 00:00:00 2001 From: Swetha Chikkaboraiah Date: Wed, 4 Oct 2017 11:08:44 +0530 Subject: [PATCH 1325/5498] defconfig: Enable few configs for msm8996 This change enables below configs to address the VTS KernelConfig test case failures. Enable configs: CONFIG_CP15_BARRIER_EMULATION CONFIG_SETEND_EMULATION Change-Id: I22dd320bf7a82fa11ef7b751969ecff8479ac17c Signed-off-by: Swetha Chikkaboraiah --- arch/arm64/configs/msm-perf_defconfig | 2 ++ arch/arm64/configs/msm_defconfig | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 9cba4d54d3b9..4fc259bfe75d 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -56,6 +56,8 @@ CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y CONFIG_SECCOMP=y CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_COMPAT=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 279316fe94c7..f2ace01d5aef 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -55,6 +55,8 @@ CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y CONFIG_SECCOMP=y CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_COMPAT=y -- GitLab From 18a2460a08381999d3f47bc3d549cf045092d777 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Thu, 19 Oct 2017 11:20:01 +0800 Subject: [PATCH 1326/5498] camera isp: fix VIO camera buffers drop issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case of undelivered_request_cnt more than 1 for ISP, do not config ISP to drop frames because it will directly return empty buffers. This change can help to ensure the number of output buffers matching the number of request, to solve the issue of VIO camera (RDI mode) buffers lost.  Change-Id: Idbc47a5681e218b1b6345d0a4cc2d8673926814c Signed-off-by: Shuai Wang --- drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 6a339a97a671..364b74cca822 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -678,7 +678,7 @@ void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev, MSM_VFE_STREAM_STOP_PERIOD; } - if (stream_info->undelivered_request_cnt > 0) + if (stream_info->undelivered_request_cnt == 1) stream_info->current_framedrop_period = MSM_VFE_STREAM_STOP_PERIOD; -- GitLab From 34b2d3b88651a1b1ffdd3962d82f6f83787ac430 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Wed, 25 Oct 2017 15:16:11 +0530 Subject: [PATCH 1327/5498] USB: gadget: Replace %pM with %pm commit I29364891fbdf38 ("USB: gadget: Replace %pK with %pM") modified the MAC address format specifier to %pM. So when the host PC asks for the MAC address, the device sends the address with colons present. Linux Host machines cannot handle this and ECM function fails to bind. Fix this by changing the format specifier to %pm which removes the colons and sends the MAC address. Change-Id: Id3eebfa371bd2cf976ee0f44e8a7b54690f43e77 Signed-off-by: Ajay Agarwal --- drivers/usb/gadget/function/u_ether.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index a240e5530cf3..b39b31cd7879 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1941,7 +1941,7 @@ int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len) return -EINVAL; dev = netdev_priv(net); - snprintf(host_addr, len, "%pM", dev->host_mac); + snprintf(host_addr, len, "%pm", dev->host_mac); return strlen(host_addr); } -- GitLab From 41ea89e3c2427fd8a817d465c700445f8533334d Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Tue, 24 Oct 2017 17:50:41 +0530 Subject: [PATCH 1328/5498] usb: glink: Fix crash on quick disconnect and connect On very quick USB disconnect and connect we are trying to open the G-Link channel before the previous session was properly closed, resulting in a crash. Prevent this by making sure the G-Link channel is opened only once the previous session was closed properly. CRs-Fixed: 2130811 Change-Id: Id8ab16a3154a9c8e987f560f0b8a6dd0ce80a8a6 Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/function/u_glink.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/function/u_glink.c b/drivers/usb/gadget/function/u_glink.c index 109226243af3..b09ceb31a8d5 100644 --- a/drivers/usb/gadget/function/u_glink.c +++ b/drivers/usb/gadget/function/u_glink.c @@ -42,6 +42,8 @@ struct glink_channel { struct work_struct connect_w; struct work_struct disconnect_w; + struct completion close_done; + unsigned long to_modem; unsigned long to_host; }; @@ -132,9 +134,12 @@ static void glink_ctrl_disconnect_w(struct work_struct *w) struct glink_channel *ch_info = container_of(w, struct glink_channel, disconnect_w); + pr_debug("%s: close glink channel %pK\n", __func__, ch_info->handle); if (ch_info->handle) { - pr_debug("%s: close glink channel\n", __func__); + reinit_completion(&ch_info->close_done); glink_close(ch_info->handle); + wait_for_completion(&ch_info->close_done); + pr_debug("%s: glink channel closed\n", __func__); } } @@ -365,6 +370,7 @@ static void glink_notify_state(void *handle, const void *priv, unsigned event) break; case GLINK_LOCAL_DISCONNECTED: ch_info->handle = NULL; + complete(&ch_info->close_done); case GLINK_REMOTE_DISCONNECTED: if (gr && gr->disconnect) gr->disconnect(gr); @@ -407,9 +413,6 @@ int glink_ctrl_connect(struct grmnet *gr, u8 client_num) ch_info->open_cfg.notify_state = glink_notify_state; ch_info->open_cfg.priv = ch_info; - ch_info->channel_state = GLINK_REMOTE_DISCONNECTED; - ch_info->handle = NULL; - spin_lock_irqsave(&ch_info->port_lock, flags); ch_info->port = gr; gr->send_encap_cmd = glink_send_cpkt_tomodem; @@ -532,8 +535,7 @@ int glink_ctrl_setup(enum ctrl_client client_num, unsigned int count, return PTR_ERR(link_info.handle); } - glink_ctrl_wq = alloc_workqueue("glink_ctrl", - WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + glink_ctrl_wq = alloc_ordered_workqueue("glink_ctrl", WQ_MEM_RECLAIM); if (!glink_ctrl_wq) { pr_err("%s: Unable to create workqueue glink_ctrl\n", __func__); @@ -546,11 +548,15 @@ int glink_ctrl_setup(enum ctrl_client client_num, unsigned int count, INIT_WORK(&intent_work.work, glink_ctrl_intent_worker); + ch_info->channel_state = GLINK_LOCAL_DISCONNECTED; + ch_info->handle = NULL; + spin_lock_init(&ch_info->port_lock); INIT_LIST_HEAD(&ch_info->tx_q); INIT_WORK(&ch_info->write_w, glink_ctrl_write_w); INIT_WORK(&ch_info->connect_w, glink_ctrl_connect_w); INIT_WORK(&ch_info->disconnect_w, glink_ctrl_disconnect_w); + init_completion(&ch_info->close_done); *port_idx = client_num; return 0; -- GitLab From d9b682f0a731978a0dc0cf810a449d5178a58c00 Mon Sep 17 00:00:00 2001 From: Dhananjay Kumar Date: Thu, 28 Sep 2017 23:23:56 +0530 Subject: [PATCH 1329/5498] ASoC: msm: qdsp6v2: Add MM28 and MM29 for record usecase Add two more MM entries to increase supported compress record session to five. Change-Id: I3d8a65c478bc38d4a382bd4d7ee4172e429a4d80 Signed-off-by: Dhananjay Kumar --- sound/soc/msm/msm-dai-fe.c | 42 +++- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 226 ++++++++++++++++++++- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h | 6 +- 3 files changed, 269 insertions(+), 5 deletions(-) diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index 73d678afbc7d..4f569f19fcbf 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -2487,7 +2487,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_fe_Multimedia_dai_ops, .compress_dai = 1, @@ -2525,7 +2525,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_fe_Multimedia_dai_ops, .compress_dai = 1, @@ -2598,6 +2598,44 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .name = "MultiMedia21", .probe = fe_dai_probe, }, + { + .capture = { + .stream_name = "MultiMedia28 Capture", + .aif_name = "MM_UL28", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_dai = 1, + .name = "MultiMedia28", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia29 Capture", + .aif_name = "MM_UL29", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_dai = 1, + .name = "MultiMedia29", + .probe = fe_dai_probe, + }, }; static int msm_fe_dai_dev_probe(struct platform_device *pdev) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 1df7be78319f..21705eb76acf 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -516,6 +516,12 @@ static struct msm_pcm_routing_fdai_data {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* MULTIMEDIA21 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA28 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA29 */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* CS_VOICE */ @@ -3185,6 +3191,16 @@ static const struct snd_kcontrol_new ext_ec_ref_mux_ul19 = msm_route_ec_ref_rx_enum[0], msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); +static const struct snd_kcontrol_new ext_ec_ref_mux_ul28 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL28 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul29 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL29 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -3328,6 +3344,12 @@ static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_I2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { @@ -3388,6 +3410,12 @@ static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_I2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = { @@ -3448,7 +3476,12 @@ static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SPDIF_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), - + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { @@ -3509,6 +3542,12 @@ static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_5_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { @@ -3569,6 +3608,12 @@ static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_0_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { @@ -3629,6 +3674,12 @@ static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { @@ -3689,6 +3740,12 @@ static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { @@ -3749,6 +3806,12 @@ static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUINARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { @@ -3797,6 +3860,12 @@ static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = { @@ -3863,6 +3932,12 @@ static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new mi2s_hl_mixer_controls[] = { @@ -3932,6 +4007,12 @@ static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_MI2S_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new hdmi_mixer_controls[] = { @@ -3992,6 +4073,12 @@ static const struct snd_kcontrol_new hdmi_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_HDMI_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; /* incall music delivery mixer */ static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = { @@ -4148,6 +4235,12 @@ static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_BT_SCO_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = { @@ -4259,6 +4352,12 @@ static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_FM_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { @@ -4319,6 +4418,12 @@ static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AFE_PCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { @@ -4382,6 +4487,12 @@ static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_AUXPCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { @@ -4445,6 +4556,12 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_SEC_AUXPCM_RX, MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { @@ -6095,6 +6212,9 @@ static const struct snd_kcontrol_new mmul17_mixer_controls[] = { SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), @@ -6125,6 +6245,9 @@ static const struct snd_kcontrol_new mmul18_mixer_controls[] = { SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), @@ -6155,6 +6278,9 @@ static const struct snd_kcontrol_new mmul19_mixer_controls[] = { SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), @@ -6292,6 +6418,66 @@ static const struct snd_kcontrol_new mmul21_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new mmul28_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul29_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = { SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer, @@ -10564,6 +10750,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { mmul20_mixer_controls, ARRAY_SIZE(mmul20_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia21 Mixer", SND_SOC_NOPM, 0, 0, mmul21_mixer_controls, ARRAY_SIZE(mmul21_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia28 Mixer", SND_SOC_NOPM, 0, 0, + mmul28_mixer_controls, ARRAY_SIZE(mmul28_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia29 Mixer", SND_SOC_NOPM, 0, 0, + mmul29_mixer_controls, ARRAY_SIZE(mmul29_mixer_controls)), SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)), SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, @@ -11009,6 +11199,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia18 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia19 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia28 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia29 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia8 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia4 Mixer", "SLIM_2_TX", "SLIMBUS_2_TX"}, {"MultiMedia17 Mixer", "SLIM_2_TX", "SLIMBUS_2_TX"}, @@ -11020,7 +11212,19 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia18 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia19 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia28 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia29 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia17 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia18 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia19 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia28 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia29 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia17 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia18 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia19 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia28 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia29 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia3 Mixer", "SLIM_2_TX", "SLIMBUS_2_TX"}, @@ -11764,6 +11968,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia18 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia19 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia28 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia29 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia8 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, @@ -11771,6 +11977,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia28 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia29 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia8 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, @@ -11781,6 +11989,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia18 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia28 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia29 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MM_UL1", NULL, "MultiMedia1 Mixer"}, @@ -11797,6 +12007,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL19", NULL, "MultiMedia19 Mixer"}, {"MM_UL20", NULL, "MultiMedia20 Mixer"}, {"MM_UL21", NULL, "MultiMedia21 Mixer"}, + {"MM_UL28", NULL, "MultiMedia28 Mixer"}, + {"MM_UL29", NULL, "MultiMedia29 Mixer"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, @@ -12162,6 +12374,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUDIO_REF_EC_UL19 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"}, {"AUDIO_REF_EC_UL19 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL29 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"}, {"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"}, {"MM_UL3", NULL, "AUDIO_REF_EC_UL3 MUX"}, @@ -12173,6 +12395,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"}, {"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"}, {"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"}, + {"MM_UL28", NULL, "AUDIO_REF_EC_UL28 MUX"}, + {"MM_UL29", NULL, "AUDIO_REF_EC_UL29 MUX"}, {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"}, {"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"}, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h index bc8b6f299178..a47377ab38d3 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h @@ -160,6 +160,8 @@ enum { MSM_FRONTEND_DAI_MULTIMEDIA19, MSM_FRONTEND_DAI_MULTIMEDIA20, MSM_FRONTEND_DAI_MULTIMEDIA21, + MSM_FRONTEND_DAI_MULTIMEDIA28, + MSM_FRONTEND_DAI_MULTIMEDIA29, MSM_FRONTEND_DAI_CS_VOICE, MSM_FRONTEND_DAI_VOIP, MSM_FRONTEND_DAI_AFE_RX, @@ -185,8 +187,8 @@ enum { MSM_FRONTEND_DAI_MAX, }; -#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA21 + 1) -#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA21 +#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA29 + 1) +#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA29 enum { MSM_BACKEND_DAI_PRI_I2S_RX = 0, -- GitLab From 154092128e6210f81155e6868e81c2318bc3424b Mon Sep 17 00:00:00 2001 From: Dhananjay Kumar Date: Thu, 28 Sep 2017 23:41:13 +0530 Subject: [PATCH 1330/5498] ASoC: msm8952: Add more dai links to support compress capture Add two more dai links to support upto five compress capture streams. Change-Id: Ibdc06ea343ebd9fe20eb3260be9c932c684315e1 Signed-off-by: Dhananjay Kumar --- sound/soc/msm/msm8952-dai-links.c | 34 ++++++++++++++++++++++++++++++- sound/soc/msm/msm8952.c | 32 ++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/sound/soc/msm/msm8952-dai-links.c b/sound/soc/msm/msm8952-dai-links.c index 43a07e0126d9..b304ba0d1b00 100644 --- a/sound/soc/msm/msm8952-dai-links.c +++ b/sound/soc/msm/msm8952-dai-links.c @@ -141,7 +141,7 @@ static struct snd_soc_dai_link msm8952_tasha_fe_dai[] = { .codec_name = "snd-soc-dummy", }, /* QCHAT */ - {/* hw:x,42 */ + {/* hw:x,45 */ .name = "QCHAT", .stream_name = "QCHAT", .cpu_dai_name = "QCHAT", @@ -158,6 +158,38 @@ static struct snd_soc_dai_link msm8952_tasha_fe_dai[] = { .codec_name = "snd-soc-dummy", .be_id = MSM_FRONTEND_DAI_QCHAT, }, + {/* hw:x,46 */ + .name = "MSM8X16 Compress13", + .stream_name = "Compress13", + .cpu_dai_name = "MultiMedia28", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has capture support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA28, + }, + {/* hw:x,47 */ + .name = "MSM8X16 Compress14", + .stream_name = "Compress14", + .cpu_dai_name = "MultiMedia29", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has capture support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA29, + }, }; static struct snd_soc_dai_link msm8952_tasha_be_dai[] = { diff --git a/sound/soc/msm/msm8952.c b/sound/soc/msm/msm8952.c index f504885dc2f2..880677fc39a9 100644 --- a/sound/soc/msm/msm8952.c +++ b/sound/soc/msm/msm8952.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2333,6 +2333,36 @@ static struct snd_soc_dai_link msm8952_dai[] = { .ignore_pmdown_time = 1, .be_id = MSM_FRONTEND_DAI_MULTIMEDIA19, }, + {/* hw:x,41 */ + .name = "MSM8X16 Compress13", + .stream_name = "Compress13", + .cpu_dai_name = "MultiMedia28", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA28, + }, + {/* hw:x,42 */ + .name = "MSM8X16 Compress14", + .stream_name = "Compress14", + .cpu_dai_name = "MultiMedia29", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA29, + }, /* Backend I2S DAI Links */ { .name = LPASS_BE_PRI_MI2S_RX, -- GitLab From 6e80ff47b2884814419d9c9daee41ed6b44ea237 Mon Sep 17 00:00:00 2001 From: Ashwanth Goli Date: Fri, 9 Jun 2017 14:24:58 +0530 Subject: [PATCH 1331/5498] net: rps: send out pending IPI's on CPU hotplug IPI's from the victim cpu are not handled in dev_cpu_callback. So these pending IPI's would be sent to the remote cpu only when NET_RX is scheduled on the victim cpu and since this trigger is unpredictable it would result in packet latencies on the remote cpu. This patch add support to send the pending ipi's of victim cpu. Change-Id: I6e688bf0d09a952468eec18f80ce6b21bf370ef1 Signed-off-by: Ashwanth Goli Signed-off-by: David S. Miller --- net/core/dev.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index e8194572ddef..58b78f1852df 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4410,6 +4410,19 @@ __sum16 __skb_gro_checksum_complete(struct sk_buff *skb) } EXPORT_SYMBOL(__skb_gro_checksum_complete); +static void net_rps_send_ipi(struct softnet_data *remsd) +{ +#ifdef CONFIG_RPS + while (remsd) { + struct softnet_data *next = remsd->rps_ipi_next; + + if (cpu_online(remsd->cpu)) + smp_call_function_single_async(remsd->cpu, &remsd->csd); + remsd = next; + } +#endif +} + /* * net_rps_action_and_irq_enable sends any pending IPI's for rps. * Note: called with local irq disabled, but exits with local irq enabled. @@ -4425,20 +4438,7 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd) local_irq_enable(); /* Send pending IPI's to kick RPS processing on remote cpus. */ - while (remsd) { - struct softnet_data *next = remsd->rps_ipi_next; - - if (cpu_online(remsd->cpu)) { - smp_call_function_single_async(remsd->cpu, - &remsd->csd); - } else { - pr_err("%s() cpu offline\n", __func__); - rps_lock(remsd); - remsd->backlog.state = 0; - rps_unlock(remsd); - } - remsd = next; - } + net_rps_send_ipi(remsd); } else #endif local_irq_enable(); @@ -7106,7 +7106,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, struct sk_buff **list_skb; struct sk_buff *skb; unsigned int cpu, oldcpu = (unsigned long)ocpu; - struct softnet_data *sd, *oldsd; + struct softnet_data *sd, *oldsd, *remsd; if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) return NOTIFY_OK; @@ -7150,6 +7150,13 @@ static int dev_cpu_callback(struct notifier_block *nfb, raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_enable(); +#ifdef CONFIG_RPS + remsd = oldsd->rps_ipi_list; + oldsd->rps_ipi_list = NULL; +#endif + /* send out pending IPI's on offline CPU */ + net_rps_send_ipi(remsd); + /* Process offline CPU's input_pkt_queue */ while ((skb = __skb_dequeue(&oldsd->process_queue))) { netif_rx_internal(skb); -- GitLab From 94e47ddaf713850026ed3eacc5593cccc0f7f44e Mon Sep 17 00:00:00 2001 From: Prateek Sood Date: Sat, 14 Oct 2017 04:33:08 -0700 Subject: [PATCH 1332/5498] Revert "osq_lock: fix osq_lock queue corruption" This reverts commit e0f258dfae88 ("osq_lock: fix osq_lock queue corruption") Perparing for taking more optimized patch suggested to upstream for using smp_wmb() instead of smp_mb(). Change-Id: Ide9bdd60ec5935ba78bc57b7bdedf7952072c580 Signed-off-by: Prateek Sood --- kernel/locking/mcs_spinlock.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c index 1ac5ee932dc0..620759f24da5 100644 --- a/kernel/locking/mcs_spinlock.c +++ b/kernel/locking/mcs_spinlock.c @@ -102,32 +102,6 @@ bool osq_lock(struct optimistic_spin_queue *lock) prev = decode_cpu(old); node->prev = prev; - - /* - * We need to avoid reordering of link updation sequence of osq. - * A case in which the status of optimistic spin queue is - * CPU6->CPU2 in which CPU6 has acquired the lock. At this point - * if CPU0 comes in to acquire osq_lock, it will update the tail - * count. After tail count update if CPU2 starts to unqueue itself - * from optimistic spin queue, it will find updated tail count with - * CPU0 and update CPU2 node->next to NULL in osq_wait_next(). If - * reordering of following stores happen then prev->next where prev - * being CPU2 would be updated to point to CPU0 node: - * node->prev = prev; - * WRITE_ONCE(prev->next, node); - * - * At this point if next instruction - * WRITE_ONCE(next->prev, prev); - * in CPU2 path is committed before the update of CPU0 node->prev = - * prev then CPU0 node->prev will point to CPU6 node. At this point - * if CPU0 path's node->prev = prev is committed resulting in change - * of CPU0 prev back to CPU2 node. CPU2 node->next is NULL, so if - * CPU0 gets into unqueue path of osq_lock it will keep spinning - * in infinite loop as condition prev->next == node will never be - * true. - */ - smp_mb(); - ACCESS_ONCE(prev->next) = node; /* -- GitLab From 64d0dcce1b5628286962e4f68a4b29928b16d430 Mon Sep 17 00:00:00 2001 From: Trishansh Bhardwaj Date: Fri, 6 Oct 2017 12:48:29 +0530 Subject: [PATCH 1333/5498] msm: camera: Prevent use-after-free in v4l2_event_subscribe. If same event is unsubscribed before v4l2_event_subscribe returned, Then function v4l2_event_subscribe have possibility use-after-free. Serialize msm_subscribe_event and msm_unsubscribe_event to prevent parallel invocation of v4l2_event_subscribe and v4l2_event_unsubscribe. Change-Id: I321fc66a9acbfb6e511b553be4cedffd26a558db Signed-off-by: Trishansh Bhardwaj --- drivers/media/platform/msm/camera_v2/msm.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index e685d50d1e6c..828eca671c42 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -35,6 +35,7 @@ static struct v4l2_device *msm_v4l2_dev; static struct list_head ordered_sd_list; static struct mutex ordered_sd_mtx; +static struct mutex v4l2_event_mtx; static struct pm_qos_request msm_v4l2_pm_qos_request; @@ -837,13 +838,25 @@ static long msm_private_ioctl(struct file *file, void *fh, static int msm_unsubscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { - return v4l2_event_unsubscribe(fh, sub); + int rc; + + mutex_lock(&v4l2_event_mtx); + rc = v4l2_event_unsubscribe(fh, sub); + mutex_unlock(&v4l2_event_mtx); + + return rc; } static int msm_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { - return v4l2_event_subscribe(fh, sub, 5, NULL); + int rc; + + mutex_lock(&v4l2_event_mtx); + rc = v4l2_event_subscribe(fh, sub, 5, NULL); + mutex_unlock(&v4l2_event_mtx); + + return rc; } static const struct v4l2_ioctl_ops g_msm_ioctl_ops = { @@ -1361,6 +1374,7 @@ static int msm_probe(struct platform_device *pdev) spin_lock_init(&msm_eventq_lock); spin_lock_init(&msm_pid_lock); mutex_init(&ordered_sd_mtx); + mutex_init(&v4l2_event_mtx); INIT_LIST_HEAD(&ordered_sd_list); cam_debugfs_root = debugfs_create_dir(MSM_CAM_LOGSYNC_FILE_BASEDIR, -- GitLab From 71ef62d66cfc338d831a48a977063a995443b14c Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 26 Oct 2017 15:14:39 +0530 Subject: [PATCH 1334/5498] msm: ipa: Fix pointer checked for NULL may be used Data pointer may be NULL, check for reset value to be false in that case and return EINVAL for invalid argument. Change-Id: I05a4aa96724c123516a7965bd0e939bdf0c86553 Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 3 +++ drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index cf39f80e0845..9255ccc95bee 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2602,6 +2602,9 @@ int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, /* prevent string buffer overflows */ data->upstreamIface[IFNAMSIZ-1] = '\0'; data->tetherIface[IFNAMSIZ-1] = '\0'; + } else if (reset != false) { + /* Data can be NULL for reset stats, checking reset != False */ + return -EINVAL; } req = kzalloc(sizeof(struct ipa_get_data_stats_req_msg_v01), diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 190edacd28a3..eb128faa1933 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -2760,6 +2760,9 @@ int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, /* prevent string buffer overflows */ data->upstreamIface[IFNAMSIZ-1] = '\0'; data->tetherIface[IFNAMSIZ-1] = '\0'; + } else if (reset != false) { + /* Data can be NULL for reset stats, checking reset != False */ + return -EINVAL; } req = kzalloc(sizeof(struct ipa_get_data_stats_req_msg_v01), -- GitLab From 925b850cc0501faec5ced843def0d75fbd00b2f2 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Wed, 25 Oct 2017 17:25:20 +0100 Subject: [PATCH 1335/5498] ANDROID: sched/fair: Select correct capacity state for energy_diff The util returned from group_max_util is not capped at the max util present in the group, so it can be larger than the capacity stored in the array. Ensure that when this happens, we always use the last entry in the array to fetch energy from. Tested with synthetics on Juno board. Change-Id: I89fb52fb7e68fa3e682e308acc232596672d03f7 Signed-off-by: Chris Redpath --- kernel/sched/fair.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c76b4d5ee05e..37ec09b3269a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4712,17 +4712,20 @@ long group_norm_util(struct energy_env *eenv, struct sched_group *sg) static int find_new_capacity(struct energy_env *eenv, const struct sched_group_energy * const sge) { - int idx; + int idx, max_idx = sge->nr_cap_states - 1; unsigned long util = group_max_util(eenv); + /* default is max_cap if we don't find a match */ + eenv->cap_idx = max_idx; + for (idx = 0; idx < sge->nr_cap_states; idx++) { - if (sge->cap_states[idx].cap >= util) + if (sge->cap_states[idx].cap >= util) { + eenv->cap_idx = idx; break; + } } - eenv->cap_idx = idx; - - return idx; + return eenv->cap_idx; } static int group_idle_state(struct sched_group *sg) -- GitLab From 531d8630caef27d3ff6ccdd0d7c26ecdb133542c Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Thu, 26 Oct 2017 12:27:57 -0700 Subject: [PATCH 1336/5498] ARM: dts: msm: Add flag to stop PIL from force shutdown on SDX20M This change adds a new flag, "qcom,pil-force-shutdown", to stop PIL from trying to trigger a shutdown after IPA FW loading. Change-Id: I9e9f8fe9942ab9b1989ec7c50d903ac4b0b3d1e1 Signed-off-by: Michael Adisumarta Acked-by: Jyothi Jayanthi --- arch/arm/boot/dts/qcom/sdx20.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/sdx20.dtsi b/arch/arm/boot/dts/qcom/sdx20.dtsi index 54c55d7ba88a..bf9e1eac5771 100644 --- a/arch/arm/boot/dts/qcom/sdx20.dtsi +++ b/arch/arm/boot/dts/qcom/sdx20.dtsi @@ -99,6 +99,7 @@ compatible = "qcom,pil-tz-generic"; qcom,pas-id = <0xf>; qcom,firmware-name = "ipa_fws"; + qcom,pil-force-shutdown; }; qcom,msm-cpufreq { -- GitLab From a690bd88a9c88da8aa6146d4327f1ab921d68cbf Mon Sep 17 00:00:00 2001 From: Yan He Date: Thu, 14 Sep 2017 16:31:04 -0700 Subject: [PATCH 1337/5498] msm: ep_pcie: notify client about link up event Notify client about link up event when the link is re-enabled by host. Change-Id: I3f800807c219105ccaf9bda2c68e0e9a50492a34 Signed-off-by: Yan He --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index fb187dac37b3..d820f0f88ef6 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -1414,8 +1414,9 @@ static irqreturn_t ep_pcie_handle_bme_irq(int irq, void *data) schedule_work(&dev->handle_bme_work); } else { EP_PCIE_DBG(dev, - "PCIe V%d:BME is set again after the enumeration has completed\n", + "PCIe V%d:BME is set again after the enumeration has completed; callback client for link ready.\n", dev->rev); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP); } } else { EP_PCIE_DBG(dev, -- GitLab From 077726c80fb79dabf6a03a1a53c1201c410d9705 Mon Sep 17 00:00:00 2001 From: haihez Date: Thu, 19 Oct 2017 09:47:00 +0800 Subject: [PATCH 1338/5498] regulator: cpr3-hmss: Increase fuse combo count To accommodate speed bin 3 and fuse revisions associated with it increase the fuse combo count from 25 to 31. At present, this is needed for msm8996-v3 SOC. Change-Id: I89f50583b4896430e06547af9d7d738e2d678dcf Signed-off-by: Haihe Zhang --- drivers/regulator/cpr3-hmss-regulator.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index a9259058e0e2..e3df844afe56 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -88,8 +88,9 @@ struct cpr3_msm8996_hmss_fuses { * Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0. * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1. * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2. + * Fuse combos 24 - 31 map to CPR fusing revision 0 - 7 with speed bin fuse = 3. */ -#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 24 +#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 32 /* * Constants which define the name of each fuse corner. Note that no actual -- GitLab From 6f4d1d1e5e664412869c6e6dc62c4427104ded50 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Tue, 24 Oct 2017 22:43:38 +0530 Subject: [PATCH 1339/5498] msm: msm_bus: Add mutex lock for floor vote data Floor vote data needs to be protected with mutex lock to avoid double free of memory due to race condtion. Change-Id: Ifaa01a14d273ccba6b9463aff3a41c0038b05f06 Signed-off-by: Odelu Kukatla --- drivers/platform/msm/msm_bus/msm_bus_dbg_voter.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/platform/msm/msm_bus/msm_bus_dbg_voter.c b/drivers/platform/msm/msm_bus/msm_bus_dbg_voter.c index a876484859eb..ba1adb8acea7 100644 --- a/drivers/platform/msm/msm_bus/msm_bus_dbg_voter.c +++ b/drivers/platform/msm/msm_bus/msm_bus_dbg_voter.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is Mree software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,6 +27,7 @@ struct msm_bus_floor_client_type { }; static struct class *bus_floor_class; +static DEFINE_RT_MUTEX(msm_bus_floor_vote_lock); #define MAX_VOTER_NAME (50) #define DEFAULT_NODE_WIDTH (8) #define DBG_NAME(s) (strnstr(s, "-", 7) + 1) @@ -64,18 +65,22 @@ static ssize_t bus_floor_active_only_store(struct device *dev, { struct msm_bus_floor_client_type *cl; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (sscanf(buf, "%d", &cl->active_only) != 1) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } @@ -100,20 +105,24 @@ static ssize_t bus_floor_vote_store(struct device *dev, struct msm_bus_floor_client_type *cl; int ret = 0; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (sscanf(buf, "%llu", &cl->cur_vote_hz) != 1) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } ret = msm_bus_floor_vote_context(dev_name(dev), cl->cur_vote_hz, cl->active_only); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } @@ -126,15 +135,18 @@ static ssize_t bus_floor_vote_store_api(struct device *dev, char name[10]; u64 vote_khz = 0; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (sscanf(buf, "%9s %llu", name, &vote_khz) != 2) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } @@ -142,6 +154,7 @@ static ssize_t bus_floor_vote_store_api(struct device *dev, __func__, name, vote_khz); ret = msm_bus_floor_vote(name, vote_khz); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } -- GitLab From bc92f5701a0ba1217d2adc963a770b8fb3fbe17e Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Mon, 25 Sep 2017 08:55:09 -0700 Subject: [PATCH 1340/5498] FROMLIST: binder: fix use-after-free in binder_transaction() (from https://patchwork.kernel.org/patch/9978801/) User-space normally keeps the node alive when creating a transaction since it has a reference to the target. The local strong ref keeps it alive if the sending process dies before the target process processes the transaction. If the source process is malicious or has a reference counting bug, this can fail. In this case, when we attempt to decrement the node in the failure path, the node has already been freed. This is fixed by taking a tmpref on the node while constructing the transaction. To avoid re-acquiring the node lock and inner proc lock to increment the proc's tmpref, a helper is used that does the ref increments on both the node and proc. Bug: 66899329 Change-Id: Iad40e1e0bccee88234900494fb52a510a37fe8d7 Signed-off-by: Todd Kjos Git-commit: 10da5a28d0f503130634f9825f3d9713c077709f Git-repo: https://android.googlesource.com/kernel/common/ Signed-off-by: Srinivasarao P --- drivers/staging/android/binder.c | 93 ++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index df02fd0ce8d9..a1667ea6f69e 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2741,6 +2741,48 @@ static bool binder_proc_transaction(struct binder_transaction *t, return true; } +/** + * binder_get_node_refs_for_txn() - Get required refs on node for txn + * @node: struct binder_node for which to get refs + * @proc: returns @node->proc if valid + * @error: if no @proc then returns BR_DEAD_REPLY + * + * User-space normally keeps the node alive when creating a transaction + * since it has a reference to the target. The local strong ref keeps it + * alive if the sending process dies before the target process processes + * the transaction. If the source process is malicious or has a reference + * counting bug, relying on the local strong ref can fail. + * + * Since user-space can cause the local strong ref to go away, we also take + * a tmpref on the node to ensure it survives while we are constructing + * the transaction. We also need a tmpref on the proc while we are + * constructing the transaction, so we take that here as well. + * + * Return: The target_node with refs taken or NULL if no @node->proc is NULL. + * Also sets @proc if valid. If the @node->proc is NULL indicating that the + * target proc has died, @error is set to BR_DEAD_REPLY + */ +static struct binder_node *binder_get_node_refs_for_txn( + struct binder_node *node, + struct binder_proc **procp, + uint32_t *error) +{ + struct binder_node *target_node = NULL; + + binder_node_inner_lock(node); + if (node->proc) { + target_node = node; + binder_inc_node_nilocked(node, 1, 0, NULL); + binder_inc_node_tmpref_ilocked(node); + node->proc->tmp_ref++; + *procp = node->proc; + } else + *error = BR_DEAD_REPLY; + binder_node_inner_unlock(node); + + return target_node; +} + static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply, @@ -2843,43 +2885,35 @@ static void binder_transaction(struct binder_proc *proc, ref = binder_get_ref_olocked(proc, tr->target.handle, true); if (ref) { - binder_inc_node(ref->node, 1, 0, NULL); - target_node = ref->node; - } - binder_proc_unlock(proc); - if (target_node == NULL) { + target_node = binder_get_node_refs_for_txn( + ref->node, &target_proc, + &return_error); + } else { binder_user_error("%d:%d got transaction to invalid handle\n", - proc->pid, thread->pid); + proc->pid, thread->pid); return_error = BR_FAILED_REPLY; - return_error_param = -EINVAL; - return_error_line = __LINE__; - goto err_invalid_target_handle; } + binder_proc_unlock(proc); } else { mutex_lock(&context->context_mgr_node_lock); target_node = context->binder_context_mgr_node; - if (target_node == NULL) { + if (target_node) + target_node = binder_get_node_refs_for_txn( + target_node, &target_proc, + &return_error); + else return_error = BR_DEAD_REPLY; - mutex_unlock(&context->context_mgr_node_lock); - return_error_line = __LINE__; - goto err_no_context_mgr_node; - } - binder_inc_node(target_node, 1, 0, NULL); mutex_unlock(&context->context_mgr_node_lock); } - e->to_node = target_node->debug_id; - binder_node_lock(target_node); - target_proc = target_node->proc; - if (target_proc == NULL) { - binder_node_unlock(target_node); - return_error = BR_DEAD_REPLY; + if (!target_node) { + /* + * return_error is set above + */ + return_error_param = -EINVAL; return_error_line = __LINE__; goto err_dead_binder; } - binder_inner_proc_lock(target_proc); - target_proc->tmp_ref++; - binder_inner_proc_unlock(target_proc); - binder_node_unlock(target_node); + e->to_node = target_node->debug_id; if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { return_error = BR_FAILED_REPLY; @@ -3238,6 +3272,8 @@ static void binder_transaction(struct binder_proc *proc, if (target_thread) binder_thread_dec_tmpref(target_thread); binder_proc_dec_tmpref(target_proc); + if (target_node) + binder_dec_node_tmpref(target_node); /* * write barrier to synchronize with initialization * of log entry @@ -3257,6 +3293,8 @@ err_bad_parent: err_copy_data_failed: trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, t->buffer, offp); + if (target_node) + binder_dec_node_tmpref(target_node); target_node = NULL; t->buffer->transaction = NULL; binder_alloc_free_buf(&target_proc->alloc, t->buffer); @@ -3271,13 +3309,14 @@ err_bad_call_stack: err_empty_call_stack: err_dead_binder: err_invalid_target_handle: -err_no_context_mgr_node: if (target_thread) binder_thread_dec_tmpref(target_thread); if (target_proc) binder_proc_dec_tmpref(target_proc); - if (target_node) + if (target_node) { binder_dec_node(target_node, 1, 0); + binder_dec_node_tmpref(target_node); + } binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n", -- GitLab From 469371cfc452745ad3f7dc5e3338941a491ff7a9 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Thu, 24 Aug 2017 15:23:36 +0200 Subject: [PATCH 1341/5498] ANDROID: binder: fix transaction leak. If a call to put_user() fails, we failed to properly free a transaction and send a failed reply (if necessary). Bug: 63117588 Test: binderLibTest Change-Id: Ia98db8cd82ce354a4cdc8811c969988d585c7e31 Signed-off-by: Martijn Coenen Git-commit: be71309f0f4c10a8cc4b42ad495d649d5581ad45 Git-repo: https://android.googlesource.com/kernel/common/ Signed-off-by: Srinivasarao P --- drivers/staging/android/binder.c | 40 +++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index a1667ea6f69e..ff378cf44610 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2099,6 +2099,26 @@ static void binder_send_failed_reply(struct binder_transaction *t, } } +/** + * binder_cleanup_transaction() - cleans up undelivered transaction + * @t: transaction that needs to be cleaned up + * @reason: reason the transaction wasn't delivered + * @error_code: error to return to caller (if synchronous call) + */ +static void binder_cleanup_transaction(struct binder_transaction *t, + const char *reason, + uint32_t error_code) +{ + if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) { + binder_send_failed_reply(t, error_code); + } else { + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, + "undelivered transaction %d, %s\n", + t->debug_id, reason); + binder_free_transaction(t); + } +} + /** * binder_validate_object() - checks for a valid metadata object in a buffer. * @buffer: binder_buffer that we're parsing. @@ -4181,12 +4201,20 @@ retry: if (put_user(cmd, (uint32_t __user *)ptr)) { if (t_from) binder_thread_dec_tmpref(t_from); + + binder_cleanup_transaction(t, "put_user failed", + BR_FAILED_REPLY); + return -EFAULT; } ptr += sizeof(uint32_t); if (copy_to_user(ptr, &tr, sizeof(tr))) { if (t_from) binder_thread_dec_tmpref(t_from); + + binder_cleanup_transaction(t, "copy_to_user failed", + BR_FAILED_REPLY); + return -EFAULT; } ptr += sizeof(tr); @@ -4256,15 +4284,9 @@ static void binder_release_work(struct binder_proc *proc, struct binder_transaction *t; t = container_of(w, struct binder_transaction, work); - if (t->buffer->target_node && - !(t->flags & TF_ONE_WAY)) { - binder_send_failed_reply(t, BR_DEAD_REPLY); - } else { - binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, - "undelivered transaction %d\n", - t->debug_id); - binder_free_transaction(t); - } + + binder_cleanup_transaction(t, "process died.", + BR_DEAD_REPLY); } break; case BINDER_WORK_RETURN_ERROR: { struct binder_error *e = container_of( -- GitLab From 3d5af600c03ab81d30b3f1a1045a8753d7b2bf3b Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Wed, 25 May 2016 16:05:22 +0530 Subject: [PATCH 1342/5498] USB: gadget: smd: Retry usb_req submission on resume Controller driver might fail any usb_req submission when BUS is in suspend state. If all usb_reqs are pending at the driver level then on resume they never get queued again and RX stall will be seen. Take care of this by attempting to submit any idle rx usb_req on resume. Change-Id: I4981da77fcb8359225c2f80510f924dc55c1e07b CRs-fixed: 1017623 Signed-off-by: Manu Gautam --- drivers/usb/gadget/function/u_smd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/u_smd.c b/drivers/usb/gadget/function/u_smd.c index 3efaaf27f636..a3e03b4562f3 100644 --- a/drivers/usb/gadget/function/u_smd.c +++ b/drivers/usb/gadget/function/u_smd.c @@ -1061,6 +1061,7 @@ void gsmd_resume(struct gserial *gser, u8 portno) port->is_suspended = false; spin_unlock(&port->port_lock); queue_work(gsmd_wq, &port->pull); + queue_work(gsmd_wq, &port->push); } void gsmd_cleanup(struct usb_gadget *g, unsigned count) -- GitLab From e60f4ea0ba5346be09a356f3a0981c5933842014 Mon Sep 17 00:00:00 2001 From: haihez Date: Wed, 18 Oct 2017 12:29:55 +0800 Subject: [PATCH 1343/5498] ARM: dts: msm: Add support of speed-bin 3 for MSM8996v3 Add speed-bin 3 to support the fmax of 1.8GHz for perf clusters respectively based on speed bin0. Change-Id: I96d301005c5d00a46dcc20eb321b5552249b84c7 Signed-off-by: Haihe Zhang --- arch/arm/boot/dts/qcom/apq8096-v3-drone.dts | 71 ++++ arch/arm/boot/dts/qcom/msm8996-regulator.dtsi | 328 +++++++++++++++++- arch/arm/boot/dts/qcom/msm8996-v3.dtsi | 155 ++++++++- 3 files changed, 538 insertions(+), 16 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8096-v3-drone.dts b/arch/arm/boot/dts/qcom/apq8096-v3-drone.dts index eaea61d7d5a3..100ca90892a4 100644 --- a/arch/arm/boot/dts/qcom/apq8096-v3-drone.dts +++ b/arch/arm/boot/dts/qcom/apq8096-v3-drone.dts @@ -51,6 +51,15 @@ < 401800000 6 5 >, < 510000000 6 5 >, < 560000000 7 7 >; + qcom,gfxfreq-speedbin3 = + < 0 0 0 >, + < 133000000 6 4 >, + < 214000000 6 4 >, + < 315000000 6 4 >, + < 401800000 6 5 >, + < 510000000 6 5 >, + < 560000000 7 7 >, + < 624000000 8 7 >; }; @@ -105,6 +114,24 @@ < 1324800000 13 >, < 1401600000 14 >, < 1497600000 15 >; + qcom,pwrcl-speedbin3-v0 = + < 0 0 >, + < 307200000 12 >, + < 422400000 12 >, + < 480000000 12 >, + < 556800000 12 >, + < 652800000 12 >, + < 729600000 12 >, + < 844800000 12 >, + < 960000000 12 >, + < 1036800000 12 >, + < 1113600000 12 >, + < 1190400000 12 >, + < 1228800000 12 >, + < 1324800000 13 >, + < 1401600000 14 >, + < 1478400000 15 >, + < 1593600000 16 >; qcom,perfcl-speedbin0-v0 = < 0 0 >, < 307200000 13 >, @@ -179,6 +206,29 @@ < 1785600000 20 >, < 1804800000 21 >, < 1900800000 22 >; + qcom,perfcl-speedbin3-v0 = + < 0 0 >, + < 307200000 13 >, + < 403200000 13 >, + < 480000000 13 >, + < 556800000 13 >, + < 652800000 13 >, + < 729600000 13 >, + < 806400000 13 >, + < 883200000 13 >, + < 940800000 13 >, + < 1036800000 13 >, + < 1113600000 13 >, + < 1190400000 13 >, + < 1248000000 13 >, + < 1324800000 14 >, + < 1401600000 15 >, + < 1478400000 16 >, + < 1555200000 17 >, + < 1632000000 18 >, + < 1708800000 19 >, + < 1785600000 20 >, + < 1804800000 21 >; qcom,cbf-speedbin0-v0 = < 0 0 >, < 307200000 13 >, @@ -234,6 +284,27 @@ < 1190400000 13 >, < 1228800000 14 >, < 1305600000 15 >; + qcom,cbf-speedbin3-v0 = + < 0 0 >, + < 307200000 13 >, + < 384000000 13 >, + < 460800000 13 >, + < 537600000 13 >, + < 595200000 13 >, + < 672000000 13 >, + < 748800000 13 >, + < 825600000 13 >, + < 902400000 13 >, + < 979200000 13 >, + < 1056000000 13 >, + < 1132800000 13 >, + < 1190400000 13 >, + < 1228800000 14 >, + < 1305600000 15 >, + < 1382400000 16 >, + < 1459200000 17 >, + < 1536000000 18 >, + < 1593600000 19 >; }; &blsp1_uart2 { diff --git a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi index a546bcb5a0e7..41d04e4c9632 100644 --- a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi @@ -623,9 +623,9 @@ qcom,cpr-pd-bypass-mask = <0x07>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <24>; - qcom,cpr-speed-bins = <3>; - qcom,cpr-speed-bin-corners = <16 13 16>; + qcom,cpr-fuse-combos = <32>; + qcom,cpr-speed-bins = <4>; + qcom,cpr-speed-bin-corners = <16 13 16 16>; qcom,cpr-corners = /* Speed bin 0 */ <16 16 16 16 16 16 16 16>, @@ -634,6 +634,9 @@ <13 13 13 13 13 13 13 13>, /* Speed bin 2 */ + <16 16 16 16 16 16 16 16>, + + /* Speed bin 3 */ <16 16 16 16 16 16 16 16>; qcom,ldo-min-headroom-voltage = <150000>; @@ -649,8 +652,10 @@ <1 2 7 12 13>, /* Speed bin 2 */ - <1 2 7 12 16>; + <1 2 7 12 16>, + /* Speed bin 3 */ + <1 2 7 12 16>; qcom,cpr-voltage-ceiling = /* Speed bin 0 */ <670000 670000 745000 745000 745000 @@ -664,6 +669,12 @@ 905000 905000 1140000>, /* Speed bin 2 */ + <670000 670000 745000 745000 745000 + 745000 745000 905000 905000 905000 + 905000 905000 1140000 1140000 1140000 + 1140000>, + + /* Speed bin 3 */ <670000 670000 745000 745000 745000 745000 745000 905000 905000 905000 905000 905000 1140000 1140000 1140000 @@ -731,6 +742,40 @@ 470000 470000 470000>, /* Speed bin 2 */ + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000>, + + /* Speed bin 3 */ <625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 @@ -777,6 +822,12 @@ 80000 80000 80000>, /* Speed bin 2 */ + <50000 50000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000>, + + /* Speed bin 3 */ <50000 50000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 @@ -804,6 +855,14 @@ 844800000 960000000 1036800000 1113600000 1190400000 1228800000 1324800000 1401600000 1478400000 + 1593600000>, + + /* Speed bin 3 */ + <307200000 422400000 480000000 + 556800000 652800000 729600000 + 844800000 960000000 1036800000 + 1113600000 1190400000 1228800000 + 1324800000 1401600000 1478400000 1593600000>; qcom,cpr-ro-scaling-factor = @@ -847,6 +906,16 @@ <35000 0 40000 10000 5000>, <35000 0 40000 10000 5000>, <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, + + /* Speed bin 3 */ + <20000 0 25000 (-5000) (-10000)>, + <20000 0 25000 (-5000) (-10000)>, + <20000 0 25000 (-5000) (-10000)>, + <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, + <35000 0 40000 10000 5000>, <35000 0 40000 10000 5000>; qcom,cpr-closed-loop-voltage-fuse-adjustment = @@ -878,6 +947,16 @@ <20000 10000 5000 (-5000) (-5000)>, <20000 10000 5000 (-5000) (-5000)>, <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + + /* Speed bin 3 */ + <35000 35000 40000 40000 40000>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, + <20000 10000 5000 (-5000) (-5000)>, <20000 10000 5000 (-5000) (-5000)>; qcom,cpr-open-loop-voltage-adjustment = @@ -894,6 +973,12 @@ (-26000)>, /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-13000) (-14000) (-15000) (-18000) + (-20000) (-22000) (-24000) (-25000) + (-26000) (-27000) (-28000) (-30000)>, + + /* Speed bin 3 */ <(-15000) (-15000) (-15000) (-15000) (-13000) (-14000) (-15000) (-18000) (-20000) (-22000) (-24000) (-25000) @@ -907,6 +992,9 @@ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0>, /* Speed bin 2 */ + <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0>, + + /* Speed bin 3 */ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0>; qcom,cpr-closed-loop-voltage-adjustment = @@ -923,6 +1011,12 @@ (-26000)>, /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-13000) (-14000) (-15000) (-18000) + (-20000) (-22000) (-24000) (-25000) + (-26000) (-27000) (-28000) (-30000)>, + + /* Speed bin 3 */ <(-15000) (-15000) (-15000) (-15000) (-13000) (-14000) (-15000) (-18000) (-20000) (-22000) (-24000) (-25000) @@ -933,7 +1027,7 @@ qcom,cpr-scaled-open-loop-voltage-as-ceiling; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <12 12 12>; + qcom,cpr-aging-ref-corner = <12 12 12 12>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = /* Speed bin 0 */ @@ -943,6 +1037,9 @@ <0 0 0 1 1 1 1 1>, /* Speed bin 2 */ + <0 0 0 1 1 1 1 1>, + + /* Speed bin 3 */ <0 0 0 1 1 1 1 1>; }; @@ -956,9 +1053,9 @@ qcom,cpr-pd-bypass-mask = <0x18>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <24>; - qcom,cpr-speed-bins = <3>; - qcom,cpr-speed-bin-corners = <19 15 19>; + qcom,cpr-fuse-combos = <32>; + qcom,cpr-speed-bins = <4>; + qcom,cpr-speed-bin-corners = <19 15 19 19>; qcom,cpr-corners = /* Speed bin 0 */ <19 19 19 19 19 19 19 19>, @@ -967,6 +1064,9 @@ <15 15 15 15 15 15 15 15>, /* Speed bin 2 */ + <19 19 19 19 19 19 19 19>, + + /* Speed bin 3 */ <19 19 19 19 19 19 19 19>; qcom,cpr-corner-fmax-map = @@ -977,6 +1077,9 @@ <1 2 5 13 15>, /* Speed bin 2 */ + <1 2 5 13 19>, + + /* Speed bin 3 */ <1 2 5 13 19>; qcom,cpr-voltage-ceiling = @@ -995,8 +1098,13 @@ <670000 670000 745000 745000 745000 905000 905000 905000 905000 905000 905000 905000 905000 1140000 1140000 - 1140000 1140000 1140000 1140000>; + 1140000 1140000 1140000 1140000>, + /* Speed bin 3 */ + <670000 670000 745000 745000 745000 + 905000 905000 905000 905000 905000 + 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000 1140000>; qcom,cpr-voltage-floor = /* Speed bin 0 */ <625000 625000 625000 625000 625000 @@ -1059,6 +1167,40 @@ 470000 470000 470000 470000 470000>, /* Speed bin 2 */ + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000>, + + /* Speed bin 3 */ <625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 @@ -1105,6 +1247,12 @@ 80000 80000 80000 80000 80000>, /* Speed bin 2 */ + <50000 50000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000>, + + /* Speed bin 3 */ <50000 50000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 @@ -1134,6 +1282,15 @@ 979200000 1056000000 1132800000 1190400000 1228800000 1305600000 1382400000 1459200000 1536000000 + 1593600000>, + + /* Speed bin 3 */ + <307200000 384000000 460800000 + 537600000 595200000 672000000 + 748800000 825600000 902400000 + 979200000 1056000000 1132800000 + 1190400000 1228800000 1305600000 + 1382400000 1459200000 1536000000 1593600000>; qcom,cpr-ro-scaling-factor = @@ -1177,6 +1334,16 @@ <45000 0 5000 5000 (-25000)>, <45000 0 5000 5000 (-25000)>, <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, + + /* Speed bin 3 */ + <30000 0 (-10000) (-10000) (-40000)>, + <30000 0 (-10000) (-10000) (-40000)>, + <30000 0 (-10000) (-10000) (-40000)>, + <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, + <45000 0 5000 5000 (-25000)>, <45000 0 5000 5000 (-25000)>; qcom,cpr-closed-loop-voltage-fuse-adjustment = @@ -1208,6 +1375,16 @@ <10000 5000 (-20000) 0 (-35000)>, <10000 5000 (-20000) 0 (-35000)>, <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + + /* Speed bin 3 */ + <10000 5000 0 0 0>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, + <10000 5000 (-20000) 0 (-35000)>, <10000 5000 (-20000) 0 (-35000)>; qcom,allow-voltage-interpolation; @@ -1215,7 +1392,7 @@ qcom,cpr-scaled-open-loop-voltage-as-ceiling; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <13 13 13>; + qcom,cpr-aging-ref-corner = <13 13 13 13>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = /* Speed bin 0 */ @@ -1225,6 +1402,9 @@ <0 0 0 1 1 1 1 1>, /* Speed bin 2 */ + <0 0 0 1 1 1 1 1>, + + /* Speed bin 3 */ <0 0 0 1 1 1 1 1>; }; }; @@ -1243,9 +1423,9 @@ qcom,cpr-pd-bypass-mask = <0xe0>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <24>; - qcom,cpr-speed-bins = <3>; - qcom,cpr-speed-bin-corners = <25 21 25>; + qcom,cpr-fuse-combos = <32>; + qcom,cpr-speed-bins = <4>; + qcom,cpr-speed-bin-corners = <25 21 25 25>; qcom,cpr-corners = /* Speed bin 0 */ <25 25 25 25 25 25 25 25>, @@ -1253,7 +1433,10 @@ /* Speed bin 1 */ <21 21 21 21 21 21 21 21>, - /* Speed bin 0 */ + /* Speed bin 2 */ + <25 25 25 25 25 25 25 25>, + + /* Speed bin 3 */ <25 25 25 25 25 25 25 25>; qcom,ldo-min-headroom-voltage = <150000>; @@ -1269,6 +1452,9 @@ <1 4 9 13 21>, /* Speed bin 2 */ + <1 4 9 13 25>, + + /* Speed bin 3 */ <1 4 9 13 25>; qcom,cpr-voltage-ceiling = @@ -1291,6 +1477,13 @@ 745000 745000 745000 745000 905000 905000 905000 905000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 + 1140000 1140000 1140000 1140000 1140000>, + + /* Speed bin 3 */ + <670000 670000 670000 670000 745000 + 745000 745000 745000 745000 905000 + 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000>; @@ -1380,6 +1573,48 @@ 470000>, /* Speed bin 2 */ + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000 + 625000 625000 625000 625000 625000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000>, + <470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000>, + + /* Speed bin 3 */ <625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 625000 @@ -1437,6 +1672,13 @@ 80000>, /* Speed bin 2 */ + <50000 50000 50000 50000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000 + 80000 80000 80000 80000 80000>, + + /* Speed bin 3 */ <50000 50000 50000 50000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 80000 @@ -1473,6 +1715,17 @@ 1478400000 1555200000 1632000000 1708800000 1785600000 1824000000 1920000000 1996800000 2073600000 + 2150400000>, + + /* Speed bin 3 */ + <307200000 403200000 480000000 + 556800000 652800000 729600000 + 806400000 883200000 940800000 + 1036800000 1113600000 1190400000 + 1248000000 1324800000 1401600000 + 1478400000 1555200000 1632000000 + 1708800000 1785600000 1824000000 + 1920000000 1996800000 2073600000 2150400000>; qcom,cpr-ro-scaling-factor = @@ -1516,6 +1769,16 @@ <35000 0 30000 15000 15000>, <35000 0 30000 15000 15000>, <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, + + /* Speed bin 3 */ + <20000 0 15000 (-55000) 0>, + <20000 0 15000 (-55000) 0>, + <20000 0 15000 0 0>, + <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, + <35000 0 30000 15000 15000>, <35000 0 30000 15000 15000>; qcom,cpr-closed-loop-voltage-fuse-adjustment = @@ -1547,6 +1810,16 @@ < 0 0 0 0 0>, < 0 0 0 0 0>, < 0 0 0 0 0>, + < 0 0 0 0 0>, + + /* Speed bin 3 */ + <35000 35000 40000 (-30000) 40000>, + < 0 0 0 (-70000) 0>, + < 0 0 0 0 0>, + < 0 0 0 0 0>, + < 0 0 0 0 0>, + < 0 0 0 0 0>, + < 0 0 0 0 0>, < 0 0 0 0 0>; qcom,cpr-open-loop-voltage-adjustment = @@ -1568,6 +1841,15 @@ (-28000)>, /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-11000) (-12000) (-13000) (-14000) + (-15000) (-18000) (-21000) (-23000) + (-25000) (-25000) (-26000) (-26000) + (-27000) (-27000) (-28000) (-28000) + (-28000) (-29000) (-29000) (-30000) + (-30000)>, + + /* Speed bin 3 */ <(-15000) (-15000) (-15000) (-15000) (-11000) (-12000) (-13000) (-14000) (-15000) (-18000) (-21000) (-23000) @@ -1586,6 +1868,10 @@ 0 0 0 0 0>, /* Speed bin 2 */ + <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0>, + + /* Speed bin 3 */ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; @@ -1608,6 +1894,15 @@ (-28000)>, /* Speed bin 2 */ + <(-15000) (-15000) (-15000) (-15000) + (-11000) (-12000) (-13000) (-14000) + (-15000) (-18000) (-21000) (-23000) + (-25000) (-25000) (-26000) (-26000) + (-27000) (-27000) (-28000) (-28000) + (-28000) (-29000) (-29000) (-30000) + (-30000)>, + + /* Speed bin 3 */ <(-15000) (-15000) (-15000) (-15000) (-11000) (-12000) (-13000) (-14000) (-15000) (-18000) (-21000) (-23000) @@ -1621,7 +1916,7 @@ qcom,cpr-scaled-open-loop-voltage-as-ceiling; qcom,cpr-aging-max-voltage-adjustment = <15000>; - qcom,cpr-aging-ref-corner = <13 13 13>; + qcom,cpr-aging-ref-corner = <13 13 13 13>; qcom,cpr-aging-ro-scaling-factor = <3200>; qcom,allow-aging-voltage-adjustment = /* Speed bin 0 */ @@ -1631,6 +1926,9 @@ <0 0 0 1 1 1 1 1>, /* Speed bin 2 */ + <0 0 0 1 1 1 1 1>, + + /* Speed bin 3 */ <0 0 0 1 1 1 1 1>; qcom,cpr-dynamic-floor-corner = <1>; diff --git a/arch/arm/boot/dts/qcom/msm8996-v3.dtsi b/arch/arm/boot/dts/qcom/msm8996-v3.dtsi index 8e46ce5277b3..689de30b4d57 100644 --- a/arch/arm/boot/dts/qcom/msm8996-v3.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-v3.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -90,6 +90,24 @@ < 401800000 5 >, < 510000000 5 >, < 560000000 7 >; + qcom,gfxfreq-speedbin3 = + < 0 0 0 >, + < 133000000 2 4 >, + < 214000000 3 4 >, + < 315000000 4 4 >, + < 401800000 5 5 >, + < 510000000 6 5 >, + < 560000000 7 7 >, + < 624000000 8 7 >; + qcom,gfxfreq-mx-speedbin3 = + < 0 0 >, + < 133000000 4 >, + < 214000000 4 >, + < 315000000 4 >, + < 401800000 5 >, + < 510000000 5 >, + < 560000000 7 >, + < 624000000 7 >; }; &gdsc_gpu_gx { @@ -324,6 +342,79 @@ }; }; + + qcom,gpu-pwrlevels-3 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <3>; + + qcom,initial-pwrlevel = <5>; + + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <624000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <11>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <560000000>; + qcom,bus-freq = <11>; + qcom,bus-min = <9>; + qcom,bus-max = <12>; + }; + + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <510000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <11>; + }; + + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <401800000>; + qcom,bus-freq = <8>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <315000000>; + qcom,bus-freq = <6>; + qcom,bus-min = <5>; + qcom,bus-max = <7>; + }; + + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <214000000>; + qcom,bus-freq = <4>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <133000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <2>; + qcom,bus-max = <4>; + }; + + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <27000000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; + }; }; }; @@ -515,6 +606,24 @@ < 1324800000 13 >, < 1401600000 14 >, < 1497600000 15 >; + qcom,pwrcl-speedbin3-v0 = + < 0 0 >, + < 307200000 1 >, + < 422400000 2 >, + < 480000000 3 >, + < 556800000 4 >, + < 652800000 5 >, + < 729600000 6 >, + < 844800000 7 >, + < 960000000 8 >, + < 1036800000 9 >, + < 1113600000 10 >, + < 1190400000 11 >, + < 1228800000 12 >, + < 1324800000 13 >, + < 1401600000 14 >, + < 1478400000 15 >, + < 1593600000 16 >; qcom,perfcl-speedbin0-v0 = < 0 0 >, < 307200000 1 >, @@ -589,6 +698,29 @@ < 1785600000 20 >, < 1804800000 21 >, < 1900800000 22 >; + qcom,perfcl-speedbin3-v0 = + < 0 0 >, + < 307200000 1 >, + < 403200000 2 >, + < 480000000 3 >, + < 556800000 4 >, + < 652800000 5 >, + < 729600000 6 >, + < 806400000 7 >, + < 883200000 8 >, + < 940800000 9 >, + < 1036800000 10 >, + < 1113600000 11 >, + < 1190400000 12 >, + < 1248000000 13 >, + < 1324800000 14 >, + < 1401600000 15 >, + < 1478400000 16 >, + < 1555200000 17 >, + < 1632000000 18 >, + < 1708800000 19 >, + < 1785600000 20 >, + < 1804800000 21 >; qcom,cbf-speedbin0-v0 = < 0 0 >, < 307200000 1 >, @@ -644,6 +776,27 @@ < 1190400000 13 >, < 1228800000 14 >, < 1305600000 15 >; + qcom,cbf-speedbin3-v0 = + < 0 0 >, + < 307200000 1 >, + < 384000000 2 >, + < 460800000 3 >, + < 537600000 4 >, + < 595200000 5 >, + < 672000000 6 >, + < 748800000 7 >, + < 825600000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1190400000 13 >, + < 1228800000 14 >, + < 1305600000 15 >, + < 1382400000 16 >, + < 1459200000 17 >, + < 1536000000 18 >, + < 1593600000 19 >; }; &msm_cpufreq { -- GitLab From 827b310400582d0638562aaf5a9fd2bc1388343f Mon Sep 17 00:00:00 2001 From: Sachin Bhayare Date: Fri, 16 Sep 2016 15:00:12 +0530 Subject: [PATCH 1344/5498] defconfig: msm8909w: Enable xlog support for display Enable xlog support for display driver. This helps in debugging display issues faster. Change-Id: Icda32a795ba8d11e46a8569d6903a1df7a064f37 Signed-off-by: Sachin Bhayare --- arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 778645c09403..550ec4aadf52 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -271,6 +271,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 7fda81c9616e..be093a7d1d47 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -273,6 +273,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y -- GitLab From d9fbd49c6dfa21738c503e3965bd6e28727652d3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 16 Oct 2017 16:21:19 +0200 Subject: [PATCH 1345/5498] USB: devio: Revert "USB: devio: Don't corrupt user memory" commit 845d584f41eac3475c21e4a7d5e88d0f6e410cf7 upstream. Taking the uurb->buffer_length userspace passes in as a maximum for the actual urbs transfer_buffer_length causes 2 serious issues: 1) It breaks isochronous support for all userspace apps using libusb, as existing libusb versions pass in 0 for uurb->buffer_length, relying on the kernel using the lenghts of the usbdevfs_iso_packet_desc descriptors passed in added together as buffer length. This for example causes redirection of USB audio and Webcam's into virtual machines using qemu-kvm to no longer work. This is a userspace ABI break and as such must be reverted. Note that the original commit does not protect other users / the kernels memory, it only stops the userspace process making the call from shooting itself in the foot. 2) It may cause the kernel to program host controllers to DMA over random memory. Just as the devio code used to only look at the iso_packet_desc lenghts, the host drivers do the same, relying on the submitter of the urbs to make sure the entire buffer is large enough and not checking transfer_buffer_length. But the "USB: devio: Don't corrupt user memory" commit now takes the userspace provided uurb->buffer_length for the buffer-size while copying over the user-provided iso_packet_desc lengths 1:1, allowing the user to specify a small buffer size while programming the host controller to dma a lot more data. (Atleast the ohci, uhci, xhci and fhci drivers do not check transfer_buffer_length for isoc transfers.) This reverts commit fa1ed74eb1c2 ("USB: devio: Don't corrupt user memory") fixing both these issues. Cc: Dan Carpenter Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 839a5022efd9..d7edec160a6d 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1413,11 +1413,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb totlen += isopkt[u].length; } u *= sizeof(struct usb_iso_packet_descriptor); - if (totlen <= uurb->buffer_length) - uurb->buffer_length = totlen; - else - WARN_ONCE(1, "uurb->buffer_length is too short %d vs %d", - totlen, uurb->buffer_length); + uurb->buffer_length = totlen; break; default: -- GitLab From 7c1c88e160ed14dfb02cb35369e27abe01eb2ca5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 18 Oct 2017 12:49:38 -0400 Subject: [PATCH 1346/5498] USB: core: fix out-of-bounds access bug in usb_get_bos_descriptor() commit 1c0edc3633b56000e18d82fc241e3995ca18a69e upstream. Andrey used the syzkaller fuzzer to find an out-of-bounds memory access in usb_get_bos_descriptor(). The code wasn't checking that the next usb_dev_cap_header structure could fit into the remaining buffer space. This patch fixes the error and also reduces the bNumDeviceCaps field in the header to match the actual number of capabilities found, in cases where there are fewer than expected. Reported-by: Andrey Konovalov Signed-off-by: Alan Stern Tested-by: Andrey Konovalov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 49e66fb1ce87..1c62d31ed896 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -855,10 +855,12 @@ int usb_get_bos_descriptor(struct usb_device *dev) for (i = 0; i < num; i++) { buffer += length; cap = (struct usb_dev_cap_header *)buffer; - length = cap->bLength; - if (total_len < length) + if (total_len < sizeof(*cap) || total_len < cap->bLength) { + dev->bos->desc->bNumDeviceCaps = i; break; + } + length = cap->bLength; total_len -= length; if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { -- GitLab From 7238612a72e12402dbaaa4afdce49d24072fdf74 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Oct 2017 14:50:46 +0200 Subject: [PATCH 1347/5498] USB: serial: metro-usb: add MS7820 device id commit 31dc3f819bac28a0990b36510197560258ab7421 upstream. Add device-id entry for (Honeywell) Metrologic MS7820 bar code scanner. The device has two interfaces (in this mode?); a vendor-specific interface with two interrupt endpoints and a second HID interface, which we do not bind to. Reported-by: Ladislav Dobrovsky Tested-by: Ladislav Dobrovsky Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/metro-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 39e683096e94..45182c65fa1f 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -45,6 +45,7 @@ struct metrousb_private { static const struct usb_device_id id_table[] = { { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) }, { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) }, + { USB_DEVICE_INTERFACE_CLASS(0x0c2e, 0x0730, 0xff) }, /* MS7820 */ { }, /* Terminating entry. */ }; MODULE_DEVICE_TABLE(usb, id_table); -- GitLab From d3d493cf256700fe39876f3d785180e3c022b134 Mon Sep 17 00:00:00 2001 From: Maksim Salau Date: Wed, 11 Oct 2017 11:10:52 +0300 Subject: [PATCH 1348/5498] usb: cdc_acm: Add quirk for Elatec TWN3 commit 765fb2f181cad669f2beb87842a05d8071f2be85 upstream. Elatec TWN3 has the union descriptor on data interface. This results in failure to bind the device to the driver with the following log: usb 1-1.2: new full speed USB device using streamplug-ehci and address 4 usb 1-1.2: New USB device found, idVendor=09d8, idProduct=0320 usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 1-1.2: Product: RFID Device (COM) usb 1-1.2: Manufacturer: OEM cdc_acm 1-1.2:1.0: Zero length descriptor references cdc_acm: probe of 1-1.2:1.0 failed with error -22 Adding the NO_UNION_NORMAL quirk for the device fixes the issue. `lsusb -v` of the device: Bus 001 Device 003: ID 09d8:0320 Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 2 Communications bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 32 idVendor 0x09d8 idProduct 0x0320 bcdDevice 3.00 iManufacturer 1 OEM iProduct 2 RFID Device (COM) iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 67 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 250mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 1 AT-commands (v.25ter) iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0020 1x 32 bytes bInterval 2 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 Unused bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0020 1x 32 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0020 1x 32 bytes bInterval 0 CDC Header: bcdCDC 1.10 CDC Call Management: bmCapabilities 0x03 call management use DataInterface bDataInterface 1 CDC ACM: bmCapabilities 0x06 sends break line coding and serial state CDC Union: bMasterInterface 0 bSlaveInterface 1 Device Status: 0x0000 (Bus Powered) Signed-off-by: Maksim Salau Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 15ce4dc6eee6..d67484b3dcfa 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1779,6 +1779,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0xfff0, 0x0100), /* DATECS FP-2000 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0x09d8, 0x0320), /* Elatec GmbH TWN3 */ + .driver_info = NO_UNION_NORMAL, /* has misplaced union descriptor */ + }, { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ .driver_info = CLEAR_HALT_CONDITIONS, -- GitLab From 6e500be96d0c5ef7d60807001f75f638ec086c7d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 3 Oct 2017 11:16:43 +0300 Subject: [PATCH 1349/5498] usb: quirks: add quirk for WORLDE MINI MIDI keyboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2811501e6d8f5747d08f8e25b9ecf472d0dc4c7d upstream. This keyboard doesn't implement Get String descriptors properly even though string indexes are valid. What happens is that when requesting for the String descriptor, the device disconnects and reconnects. Without this quirk, this loop will continue forever. Cc: Alan Stern Reported-by: Владимир МартьÑнов Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index ddc5e8833772..76d92d84e0d6 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -214,6 +214,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* Corsair Strafe RGB */ { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT }, + /* MIDI keyboard WORLDE MINI */ + { USB_DEVICE(0x1c75, 0x0204), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Acer C120 LED Projector */ { USB_DEVICE(0x1de1, 0xc102), .driver_info = USB_QUIRK_NO_LPM }, -- GitLab From e9aeba8003dcf81d527d7169c67117d7d994872e Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 17 Oct 2017 16:07:33 +0300 Subject: [PATCH 1350/5498] usb: hub: Allow reset retry for USB2 devices on connect bounce commit 1ac7db63333db1eeff901bfd6bbcd502b4634fa4 upstream. If the connect status change is set during reset signaling, but the status remains connected just retry port reset. This solves an issue with connecting a 90W HP Thunderbolt 3 dock with a Lenovo Carbon x1 (5th generation) which causes a 30min loop of a high speed device being re-discovererd before usb ports starts working. [...] [ 389.023845] usb 3-1: new high-speed USB device number 55 using xhci_hcd [ 389.491841] usb 3-1: new high-speed USB device number 56 using xhci_hcd [ 389.959928] usb 3-1: new high-speed USB device number 57 using xhci_hcd [...] This is caused by a high speed device that doesn't successfully go to the enabled state after the second port reset. Instead the connection bounces (connected, with connect status change), bailing out completely from enumeration just to restart from scratch. Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1716332 Signed-off-by: Mathias Nyman Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5ff2e776bc5c..955d2ea37dc6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2667,13 +2667,16 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; - /* bomb out completely if the connection bounced. A USB 3.0 - * connection may bounce if multiple warm resets were issued, + /* Retry if connect change is set but status is still connected. + * A USB 3.0 connection may bounce if multiple warm resets were issued, * but the device may have successfully re-connected. Ignore it. */ if (!hub_is_superspeed(hub->hdev) && - (portchange & USB_PORT_STAT_C_CONNECTION)) - return -ENOTCONN; + (portchange & USB_PORT_STAT_C_CONNECTION)) { + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + return -EAGAIN; + } if (!(portstatus & USB_PORT_STAT_ENABLE)) return -EBUSY; -- GitLab From 405c43ceb0aeadecf7d13ef7c4e0528962287a2c Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Thu, 14 Sep 2017 18:37:14 +0200 Subject: [PATCH 1351/5498] can: gs_usb: fix busy loop if no more TX context is available commit 97819f943063b622eca44d3644067c190dc75039 upstream. If sending messages with no cable connected, it quickly happens that there is no more TX context available. Then "gs_can_start_xmit()" returns with "NETDEV_TX_BUSY" and the upper layer does retry immediately keeping the CPU busy. To fix that issue, I moved "atomic_dec(&dev->active_tx_urbs)" from "gs_usb_xmit_callback()" to the TX done handling in "gs_usb_receive_bulk_callback()". Renaming "active_tx_urbs" to "active_tx_contexts" and moving it into "gs_[alloc|free]_tx_context()" would also make sense. Signed-off-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/gs_usb.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 991aff587f63..94adbcf74e7c 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -356,6 +356,8 @@ static void gs_usb_recieve_bulk_callback(struct urb *urb) gs_free_tx_context(txc); + atomic_dec(&dev->active_tx_urbs); + netif_wake_queue(netdev); } @@ -444,14 +446,6 @@ static void gs_usb_xmit_callback(struct urb *urb) urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); - - atomic_dec(&dev->active_tx_urbs); - - if (!netif_device_present(netdev)) - return; - - if (netif_queue_stopped(netdev)) - netif_wake_queue(netdev); } static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, struct net_device *netdev) -- GitLab From 34b9ecf806f8637ebba15d6dd977c61122326dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=A4tje?= Date: Wed, 18 Oct 2017 13:25:17 +0200 Subject: [PATCH 1352/5498] can: esd_usb2: Fix can_dlc value for received RTR, frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 72d92e865d1560723e1957ee3f393688c49ca5bf upstream. The dlc member of the struct rx_msg contains also the ESD_RTR flag to mark received RTR frames. Without the fix the can_dlc value for received RTR frames would always be set to 8 by get_can_dlc() instead of the received value. Fixes: 96d8e90382dc ("can: Add driver for esd CAN-USB/2 device") Signed-off-by: Stefan Mätje Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/esd_usb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index c063a54ab8dd..30a082842620 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -334,7 +334,7 @@ static void esd_usb2_rx_can_msg(struct esd_usb2_net_priv *priv, } cf->can_id = id & ESD_IDMASK; - cf->can_dlc = get_can_dlc(msg->msg.rx.dlc); + cf->can_dlc = get_can_dlc(msg->msg.rx.dlc & ~ESD_RTR); if (id & ESD_EXTID) cf->can_id |= CAN_EFF_FLAG; -- GitLab From 38b463456f980caa60134dd118c51ce2f6897d1a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 18 Oct 2017 00:45:49 +0100 Subject: [PATCH 1353/5498] ALSA: seq: Enable 'use' locking in all configurations commit 8009d506a1dd00cf436b0c4cca0dcec130580a21 upstream. The 'use' locking macros are no-ops if neither SMP or SND_DEBUG is enabled. This might once have been OK in non-preemptible configurations, but even in that case snd_seq_read() may sleep while relying on a 'use' lock. So always use the proper implementations. Signed-off-by: Ben Hutchings Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_lock.c | 4 ---- sound/core/seq/seq_lock.h | 12 ------------ 2 files changed, 16 deletions(-) diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index 12ba83367b1b..ba5752ee9af3 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -23,8 +23,6 @@ #include #include "seq_lock.h" -#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG) - /* wait until all locks are released */ void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) { @@ -42,5 +40,3 @@ void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) } EXPORT_SYMBOL(snd_use_lock_sync_helper); - -#endif diff --git a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h index 54044bc2c9ef..ac38031c370e 100644 --- a/sound/core/seq/seq_lock.h +++ b/sound/core/seq/seq_lock.h @@ -3,8 +3,6 @@ #include -#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG) - typedef atomic_t snd_use_lock_t; /* initialize lock */ @@ -20,14 +18,4 @@ typedef atomic_t snd_use_lock_t; void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line); #define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__) -#else /* SMP || CONFIG_SND_DEBUG */ - -typedef spinlock_t snd_use_lock_t; /* dummy */ -#define snd_use_lock_init(lockp) /**/ -#define snd_use_lock_use(lockp) /**/ -#define snd_use_lock_free(lockp) /**/ -#define snd_use_lock_sync(lockp) /**/ - -#endif /* SMP || CONFIG_SND_DEBUG */ - #endif /* __SND_SEQ_LOCK_H */ -- GitLab From e2c0ddbe64bdeb7f68013305943192a5b95c2590 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 17 Oct 2017 11:58:17 +0200 Subject: [PATCH 1354/5498] ALSA: hda: Remove superfluous '-' added by printk conversion commit 6bf88a343db2b3c160edf9b82a74966b31cc80bd upstream. While converting the error messages to the standard macros in the commit 4e76a8833fac ("ALSA: hda - Replace with standard printk"), a superfluous '-' slipped in the code mistakenly. Its influence is almost negligible, merely shows a dB value as negative integer instead of positive integer (or vice versa) in the rare error message. So let's kill this embarrassing byte to show more correct value. Fixes: 4e76a8833fac ("ALSA: hda - Replace with standard printk") Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 6bd95f95e48f..33f4aead22d4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2824,7 +2824,7 @@ static int get_kctl_0dB_offset(struct hda_codec *codec, return -1; if (*step_to_check && *step_to_check != step) { codec_err(codec, "Mismatching dB step for vmaster slave (%d!=%d)\n", -- *step_to_check, step); + *step_to_check, step); return -1; } *step_to_check = step; -- GitLab From d35801d437f5aaed4b45f4a054c403794dc67020 Mon Sep 17 00:00:00 2001 From: Pontus Andersson Date: Mon, 2 Oct 2017 14:45:19 +0200 Subject: [PATCH 1355/5498] i2c: ismt: Separate I2C block read from SMBus block read commit c6ebcedbab7ca78984959386012a17b21183e1a3 upstream. Commit b6c159a9cb69 ("i2c: ismt: Don't duplicate the receive length for block reads") broke I2C block reads. It aimed to fix normal SMBus block read, but changed the correct behavior of I2C block read in the process. According to Documentation/i2c/smbus-protocol, one vital difference between normal SMBus block read and I2C block read is that there is no byte count prefixed in the data sent on the wire: SMBus Block Read: i2c_smbus_read_block_data() S Addr Wr [A] Comm [A] S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P I2C Block Read: i2c_smbus_read_i2c_block_data() S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P Therefore the two transaction types need to be processed differently in the driver by copying of the dma_buffer as done previously for the I2C_SMBUS_I2C_BLOCK_DATA case. Fixes: b6c159a9cb69 ("i2c: ismt: Don't duplicate the receive length for block reads") Signed-off-by: Pontus Andersson Tested-by: Stephen Douthit Signed-off-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-ismt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 35ec24aa1e56..f771c6afbab5 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -339,12 +339,15 @@ static int ismt_process_desc(const struct ismt_desc *desc, data->word = dma_buffer[0] | (dma_buffer[1] << 8); break; case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_I2C_BLOCK_DATA: if (desc->rxbytes != dma_buffer[0] + 1) return -EMSGSIZE; memcpy(data->block, dma_buffer, desc->rxbytes); break; + case I2C_SMBUS_I2C_BLOCK_DATA: + memcpy(&data->block[1], dma_buffer, desc->rxbytes); + data->block[0] = desc->rxbytes; + break; } return 0; } -- GitLab From c47889a4006e6fdbee5d90eef5b595758cf8c1dd Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 22 Sep 2017 23:29:12 +0200 Subject: [PATCH 1356/5498] brcmsmac: make some local variables 'static const' to reduce stack size commit c503dd38f850be28867ef7a42d9abe5ade81a9bd upstream. With KASAN and a couple of other patches applied, this driver is one of the few remaining ones that actually use more than 2048 bytes of kernel stack: broadcom/brcm80211/brcmsmac/phy/phy_n.c: In function 'wlc_phy_workarounds_nphy_gainctrl': broadcom/brcm80211/brcmsmac/phy/phy_n.c:16065:1: warning: the frame size of 3264 bytes is larger than 2048 bytes [-Wframe-larger-than=] broadcom/brcm80211/brcmsmac/phy/phy_n.c: In function 'wlc_phy_workarounds_nphy': broadcom/brcm80211/brcmsmac/phy/phy_n.c:17138:1: warning: the frame size of 2864 bytes is larger than 2048 bytes [-Wframe-larger-than=] Here, I'm reducing the stack size by marking as many local variables as 'static const' as I can without changing the actual code. This is the first of three patches to improve the stack usage in this driver. It would be good to have this backported to stabl kernels to get all drivers in 'allmodconfig' below the 2048 byte limit so we can turn on the frame warning again globally, but I realize that the patch is larger than the normal limit for stable backports. The other two patches do not need to be backported. Acked-by: Arend van Spriel Signed-off-by: Arnd Bergmann Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- .../wireless/brcm80211/brcmsmac/phy/phy_n.c | 197 +++++++++--------- 1 file changed, 97 insertions(+), 100 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c index 084f18f4f950..498f437e7738 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c @@ -14764,8 +14764,8 @@ static void wlc_phy_ipa_restore_tx_digi_filts_nphy(struct brcms_phy *pi) } static void -wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, u8 *events, u8 *dlys, - u8 len) +wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, const u8 *events, + const u8 *dlys, u8 len) { u32 t1_offset, t2_offset; u8 ctr; @@ -15240,16 +15240,16 @@ static void wlc_phy_workarounds_nphy_gainctrl_2057_rev5(struct brcms_phy *pi) static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi) { u16 currband; - s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 }; - s8 *lna1_gain_db = NULL; - s8 *lna1_gain_db_2 = NULL; - s8 *lna2_gain_db = NULL; - s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 }; - s8 *tia_gain_db; - s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 }; - s8 *tia_gainbits; - u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f }; - u16 *rfseq_init_gain; + static const s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 }; + const s8 *lna1_gain_db = NULL; + const s8 *lna1_gain_db_2 = NULL; + const s8 *lna2_gain_db = NULL; + static const s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 }; + const s8 *tia_gain_db; + static const s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 }; + const s8 *tia_gainbits; + static const u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f }; + const u16 *rfseq_init_gain; u16 init_gaincode; u16 clip1hi_gaincode; u16 clip1md_gaincode = 0; @@ -15310,10 +15310,9 @@ static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi) if ((freq <= 5080) || (freq == 5825)) { - s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 }; - s8 lna1A_gain_db_2_rev7[] = { - 11, 17, 22, 25}; - s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; + static const s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 }; + static const s8 lna1A_gain_db_2_rev7[] = { 11, 17, 22, 25}; + static const s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; crsminu_th = 0x3e; lna1_gain_db = lna1A_gain_db_rev7; @@ -15321,10 +15320,9 @@ static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi) lna2_gain_db = lna2A_gain_db_rev7; } else if ((freq >= 5500) && (freq <= 5700)) { - s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 }; - s8 lna1A_gain_db_2_rev7[] = { - 12, 18, 22, 26}; - s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 }; + static const s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 }; + static const s8 lna1A_gain_db_2_rev7[] = { 12, 18, 22, 26}; + static const s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 }; crsminu_th = 0x45; clip1md_gaincode_B = 0x14; @@ -15335,10 +15333,9 @@ static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi) lna2_gain_db = lna2A_gain_db_rev7; } else { - s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 }; - s8 lna1A_gain_db_2_rev7[] = { - 12, 18, 22, 26}; - s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; + static const s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 }; + static const s8 lna1A_gain_db_2_rev7[] = { 12, 18, 22, 26}; + static const s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; crsminu_th = 0x41; lna1_gain_db = lna1A_gain_db_rev7; @@ -15450,65 +15447,65 @@ static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi) NPHY_RFSEQ_CMD_CLR_HIQ_DIS, NPHY_RFSEQ_CMD_SET_HPF_BW }; - u8 rfseq_updategainu_dlys[] = { 10, 30, 1 }; - s8 lna1G_gain_db[] = { 7, 11, 16, 23 }; - s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 }; - s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 }; - s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 }; - s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 }; - s8 lna1A_gain_db[] = { 7, 11, 17, 23 }; - s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 }; - s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 }; - s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 }; - s8 *lna1_gain_db = NULL; - s8 lna2G_gain_db[] = { -5, 6, 10, 14 }; - s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 }; - s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 }; - s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 }; - s8 lna2A_gain_db[] = { -6, 2, 6, 10 }; - s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 }; - s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 }; - s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 }; - s8 *lna2_gain_db = NULL; - s8 tiaG_gain_db[] = { + static const u8 rfseq_updategainu_dlys[] = { 10, 30, 1 }; + static const s8 lna1G_gain_db[] = { 7, 11, 16, 23 }; + static const s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 }; + static const s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 }; + static const s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 }; + static const s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 }; + static const s8 lna1A_gain_db[] = { 7, 11, 17, 23 }; + static const s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 }; + static const s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 }; + static const s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 }; + const s8 *lna1_gain_db = NULL; + static const s8 lna2G_gain_db[] = { -5, 6, 10, 14 }; + static const s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 }; + static const s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 }; + static const s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 }; + static const s8 lna2A_gain_db[] = { -6, 2, 6, 10 }; + static const s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 }; + static const s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 }; + static const s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 }; + const s8 *lna2_gain_db = NULL; + static const s8 tiaG_gain_db[] = { 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }; - s8 tiaA_gain_db[] = { + static const s8 tiaA_gain_db[] = { 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 }; - s8 tiaA_gain_db_rev4[] = { + static const s8 tiaA_gain_db_rev4[] = { 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; - s8 tiaA_gain_db_rev5[] = { + static const s8 tiaA_gain_db_rev5[] = { 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; - s8 tiaA_gain_db_rev6[] = { + static const s8 tiaA_gain_db_rev6[] = { 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; - s8 *tia_gain_db; - s8 tiaG_gainbits[] = { + const s8 *tia_gain_db; + static const s8 tiaG_gainbits[] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }; - s8 tiaA_gainbits[] = { + static const s8 tiaA_gainbits[] = { 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 }; - s8 tiaA_gainbits_rev4[] = { + static const s8 tiaA_gainbits_rev4[] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; - s8 tiaA_gainbits_rev5[] = { + static const s8 tiaA_gainbits_rev5[] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; - s8 tiaA_gainbits_rev6[] = { + static const s8 tiaA_gainbits_rev6[] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; - s8 *tia_gainbits; - s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 }; - s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 }; - u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f }; - u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f }; - u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f }; - u16 rfseqG_init_gain_rev5_elna[] = { + const s8 *tia_gainbits; + static const s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 }; + static const s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 }; + static const u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f }; + static const u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f }; + static const u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f }; + static const u16 rfseqG_init_gain_rev5_elna[] = { 0x013f, 0x013f, 0x013f, 0x013f }; - u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f }; - u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f }; - u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f }; - u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f }; - u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f }; - u16 rfseqA_init_gain_rev4_elna[] = { + static const u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f }; + static const u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f }; + static const u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f }; + static const u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f }; + static const u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f }; + static const u16 rfseqA_init_gain_rev4_elna[] = { 0x314f, 0x314f, 0x314f, 0x314f }; - u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f }; - u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f }; - u16 *rfseq_init_gain; + static const u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f }; + static const u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f }; + const u16 *rfseq_init_gain; u16 initG_gaincode = 0x627e; u16 initG_gaincode_rev4 = 0x527e; u16 initG_gaincode_rev5 = 0x427e; @@ -15538,10 +15535,10 @@ static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi) u16 clip1mdA_gaincode_rev6 = 0x2084; u16 clip1md_gaincode = 0; u16 clip1loG_gaincode = 0x0074; - u16 clip1loG_gaincode_rev5[] = { + static const u16 clip1loG_gaincode_rev5[] = { 0x0062, 0x0064, 0x006a, 0x106a, 0x106c, 0x1074, 0x107c, 0x207c }; - u16 clip1loG_gaincode_rev6[] = { + static const u16 clip1loG_gaincode_rev6[] = { 0x106a, 0x106c, 0x1074, 0x107c, 0x007e, 0x107e, 0x207e, 0x307e }; u16 clip1loG_gaincode_rev6_224B0 = 0x1074; @@ -16066,7 +16063,7 @@ static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi) static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) { - u8 rfseq_rx2tx_events[] = { + static const u8 rfseq_rx2tx_events[] = { NPHY_RFSEQ_CMD_NOP, NPHY_RFSEQ_CMD_RXG_FBW, NPHY_RFSEQ_CMD_TR_SWITCH, @@ -16076,7 +16073,7 @@ static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) NPHY_RFSEQ_CMD_EXT_PA }; u8 rfseq_rx2tx_dlys[] = { 8, 6, 6, 2, 4, 60, 1 }; - u8 rfseq_tx2rx_events[] = { + static const u8 rfseq_tx2rx_events[] = { NPHY_RFSEQ_CMD_NOP, NPHY_RFSEQ_CMD_EXT_PA, NPHY_RFSEQ_CMD_TX_GAIN, @@ -16085,8 +16082,8 @@ static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) NPHY_RFSEQ_CMD_RXG_FBW, NPHY_RFSEQ_CMD_CLR_HIQ_DIS }; - u8 rfseq_tx2rx_dlys[] = { 8, 6, 2, 4, 4, 6, 1 }; - u8 rfseq_tx2rx_events_rev3[] = { + static const u8 rfseq_tx2rx_dlys[] = { 8, 6, 2, 4, 4, 6, 1 }; + static const u8 rfseq_tx2rx_events_rev3[] = { NPHY_REV3_RFSEQ_CMD_EXT_PA, NPHY_REV3_RFSEQ_CMD_INT_PA_PU, NPHY_REV3_RFSEQ_CMD_TX_GAIN, @@ -16096,7 +16093,7 @@ static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, NPHY_REV3_RFSEQ_CMD_END }; - u8 rfseq_tx2rx_dlys_rev3[] = { 8, 4, 2, 2, 4, 4, 6, 1 }; + static const u8 rfseq_tx2rx_dlys_rev3[] = { 8, 4, 2, 2, 4, 4, 6, 1 }; u8 rfseq_rx2tx_events_rev3[] = { NPHY_REV3_RFSEQ_CMD_NOP, NPHY_REV3_RFSEQ_CMD_RXG_FBW, @@ -16110,7 +16107,7 @@ static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) }; u8 rfseq_rx2tx_dlys_rev3[] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; - u8 rfseq_rx2tx_events_rev3_ipa[] = { + static const u8 rfseq_rx2tx_events_rev3_ipa[] = { NPHY_REV3_RFSEQ_CMD_NOP, NPHY_REV3_RFSEQ_CMD_RXG_FBW, NPHY_REV3_RFSEQ_CMD_TR_SWITCH, @@ -16121,15 +16118,15 @@ static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) NPHY_REV3_RFSEQ_CMD_INT_PA_PU, NPHY_REV3_RFSEQ_CMD_END }; - u8 rfseq_rx2tx_dlys_rev3_ipa[] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; - u16 rfseq_rx2tx_dacbufpu_rev7[] = { 0x10f, 0x10f }; + static const u8 rfseq_rx2tx_dlys_rev3_ipa[] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; + static const u16 rfseq_rx2tx_dacbufpu_rev7[] = { 0x10f, 0x10f }; s16 alpha0, alpha1, alpha2; s16 beta0, beta1, beta2; u32 leg_data_weights, ht_data_weights, nss1_data_weights, stbc_data_weights; u8 chan_freq_range = 0; - u16 dac_control = 0x0002; + static const u16 dac_control = 0x0002; u16 aux_adc_vmid_rev7_core0[] = { 0x8e, 0x96, 0x96, 0x96 }; u16 aux_adc_vmid_rev7_core1[] = { 0x8f, 0x9f, 0x9f, 0x96 }; u16 aux_adc_vmid_rev4[] = { 0xa2, 0xb4, 0xb4, 0x89 }; @@ -16139,8 +16136,8 @@ static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) u16 aux_adc_gain_rev4[] = { 0x02, 0x02, 0x02, 0x00 }; u16 aux_adc_gain_rev3[] = { 0x02, 0x02, 0x02, 0x00 }; u16 *aux_adc_gain; - u16 sk_adc_vmid[] = { 0xb4, 0xb4, 0xb4, 0x24 }; - u16 sk_adc_gain[] = { 0x02, 0x02, 0x02, 0x02 }; + static const u16 sk_adc_vmid[] = { 0xb4, 0xb4, 0xb4, 0x24 }; + static const u16 sk_adc_gain[] = { 0x02, 0x02, 0x02, 0x02 }; s32 min_nvar_val = 0x18d; s32 min_nvar_offset_6mbps = 20; u8 pdetrange; @@ -16151,9 +16148,9 @@ static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) u16 rfseq_rx2tx_lpf_h_hpc_rev7 = 0x77; u16 rfseq_tx2rx_lpf_h_hpc_rev7 = 0x77; u16 rfseq_pktgn_lpf_h_hpc_rev7 = 0x77; - u16 rfseq_htpktgn_lpf_hpc_rev7[] = { 0x77, 0x11, 0x11 }; - u16 rfseq_pktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; - u16 rfseq_cckpktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; + static const u16 rfseq_htpktgn_lpf_hpc_rev7[] = { 0x77, 0x11, 0x11 }; + static const u16 rfseq_pktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; + static const u16 rfseq_cckpktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; u16 ipalvlshift_3p3_war_en = 0; u16 rccal_bcap_val, rccal_scap_val; u16 rccal_tx20_11b_bcap = 0; @@ -24298,13 +24295,13 @@ static void wlc_phy_update_txcal_ladder_nphy(struct brcms_phy *pi, u16 core) u16 bbmult; u16 tblentry; - struct nphy_txiqcal_ladder ladder_lo[] = { + static const struct nphy_txiqcal_ladder ladder_lo[] = { {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, {25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5}, {25, 6}, {25, 7}, {35, 7}, {50, 7}, {71, 7}, {100, 7} }; - struct nphy_txiqcal_ladder ladder_iq[] = { + static const struct nphy_txiqcal_ladder ladder_iq[] = { {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, {25, 0}, {35, 0}, {50, 0}, {71, 0}, {100, 0}, {100, 1}, {100, 2}, {100, 3}, {100, 4}, {100, 5}, {100, 6}, {100, 7} @@ -25780,67 +25777,67 @@ wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, u16 cal_gain[2]; struct nphy_iqcal_params cal_params[2]; u32 tbl_len; - void *tbl_ptr; + const void *tbl_ptr; bool ladder_updated[2]; u8 mphase_cal_lastphase = 0; int bcmerror = 0; bool phyhang_avoid_state = false; - u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { + static const u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { 0x0300, 0x0500, 0x0700, 0x0900, 0x0d00, 0x1100, 0x1900, 0x1901, 0x1902, 0x1903, 0x1904, 0x1905, 0x1906, 0x1907, 0x2407, 0x3207, 0x4607, 0x6407 }; - u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { + static const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { 0x0200, 0x0300, 0x0600, 0x0900, 0x0d00, 0x1100, 0x1900, 0x2400, 0x3200, 0x4600, 0x6400, 0x6401, 0x6402, 0x6403, 0x6404, 0x6405, 0x6406, 0x6407 }; - u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { + static const u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { 0x0200, 0x0300, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1201, 0x1202, 0x1203, 0x1204, 0x1205, 0x1206, 0x1207, 0x1907, 0x2307, 0x3207, 0x4707 }; - u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { + static const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { 0x0100, 0x0200, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1900, 0x2300, 0x3200, 0x4700, 0x4701, 0x4702, 0x4703, 0x4704, 0x4705, 0x4706, 0x4707 }; - u16 tbl_tx_iqlo_cal_startcoefs[] = { + static const u16 tbl_tx_iqlo_cal_startcoefs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; - u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { + static const u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { 0x8123, 0x8264, 0x8086, 0x8245, 0x8056, 0x9123, 0x9264, 0x9086, 0x9245, 0x9056 }; - u16 tbl_tx_iqlo_cal_cmds_recal[] = { + static const u16 tbl_tx_iqlo_cal_cmds_recal[] = { 0x8101, 0x8253, 0x8053, 0x8234, 0x8034, 0x9101, 0x9253, 0x9053, 0x9234, 0x9034 }; - u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[] = { + static const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; - u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { + static const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234, 0x9434, 0x9334, 0x9084, 0x9267, 0x9056, 0x9234 }; - u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { + static const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { 0x8423, 0x8323, 0x8073, 0x8256, 0x8045, 0x8223, 0x9423, 0x9323, 0x9073, 0x9256, 0x9045, 0x9223 }; -- GitLab From 20f5c5dc8381111ee197c6e8fe5a52dad4c6172a Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Mon, 28 Aug 2017 17:25:16 +0200 Subject: [PATCH 1357/5498] bus: mbus: fix window size calculation for 4GB windows commit 2bbbd96357ce76cc45ec722c00f654aa7b189112 upstream. At least the Armada XP SoC supports 4GB on a single DRAM window. Because the size register values contain the actual size - 1, the MSB is set in that case. For example, the SDRAM window's control register's value is 0xffffffe1 for 4GB (bits 31 to 24 contain the size). The MBUS driver reads back each window's size from registers and calculates the actual size as (control_reg | ~DDR_SIZE_MASK) + 1, which overflows for 32 bit values, resulting in other miscalculations further on (a bad RAM window for the CESA crypto engine calculated by mvebu_mbus_setup_cpu_target_nooverlap() in my case). This patch changes the type in 'struct mbus_dram_window' from u32 to u64, which allows us to keep using the same register calculation code in most MBUS-using drivers (which calculate ->size - 1 again). Fixes: fddddb52a6c4 ("bus: introduce an Marvell EBU MBus driver") Signed-off-by: Jan Luebbe Signed-off-by: Gregory CLEMENT Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mvebu-mbus.c | 2 +- include/linux/mbus.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index d29f5ffdb0f4..d729c1cc7542 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -523,7 +523,7 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) if (mbus->hw_io_coherency) w->mbus_attr |= ATTR_HW_COHERENCY; w->base = base & DDR_BASE_CS_LOW_MASK; - w->size = (size | ~DDR_SIZE_MASK) + 1; + w->size = (u64)(size | ~DDR_SIZE_MASK) + 1; } } mvebu_mbus_dram_info.num_cs = cs; diff --git a/include/linux/mbus.h b/include/linux/mbus.h index 550c88fb0267..0ae20395bf5a 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -29,8 +29,8 @@ struct mbus_dram_target_info struct mbus_dram_window { u8 cs_index; u8 mbus_attr; - u32 base; - u32 size; + u64 base; + u64 size; } cs[4]; }; -- GitLab From 400f063f4dbc1210eea6aa6e18d95ea6139c3aae Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:37:49 -0700 Subject: [PATCH 1358/5498] KEYS: encrypted: fix dereference of NULL user_key_payload commit 13923d0865ca96312197962522e88bc0aedccd74 upstream. A key of type "encrypted" references a "master key" which is used to encrypt and decrypt the encrypted key's payload. However, when we accessed the master key's payload, we failed to handle the case where the master key has been revoked, which sets the payload pointer to NULL. Note that request_key() *does* skip revoked keys, but there is still a window where the key can be revoked before we acquire its semaphore. Fix it by checking for a NULL payload, treating it like a key which was already revoked at the time it was requested. This was an issue for master keys of type "user" only. Master keys can also be of type "trusted", but those cannot be revoked. Fixes: 7e70cb497850 ("keys: add new key-type encrypted") Reviewed-by: James Morris Cc: Mimi Zohar Cc: David Safford Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- security/keys/encrypted-keys/encrypted.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 8e1c0099bb66..89d5695c51cd 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -315,6 +315,13 @@ static struct key *request_user_key(const char *master_desc, u8 **master_key, down_read(&ukey->sem); upayload = ukey->payload.data; + if (!upayload) { + /* key was revoked before we acquired its semaphore */ + up_read(&ukey->sem); + key_put(ukey); + ukey = ERR_PTR(-EKEYREVOKED); + goto error; + } *master_key = upayload->data; *master_keylen = upayload->datalen; error: -- GitLab From f6a2c3d4b0ced8b34c46cf99bbd0c395e7e9bc58 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:43:20 -0700 Subject: [PATCH 1359/5498] lib/digsig: fix dereference of NULL user_key_payload commit 192cabd6a296cbc57b3d8c05c4c89d87fc102506 upstream. digsig_verify() requests a user key, then accesses its payload. However, a revoked key has a NULL payload, and we failed to check for this. request_key() *does* skip revoked keys, but there is still a window where the key can be revoked before we acquire its semaphore. Fix it by checking for a NULL payload, treating it like a key which was already revoked at the time it was requested. Fixes: 051dbb918c7f ("crypto: digital signature verification support") Reviewed-by: James Morris Cc: Dmitry Kasatkin Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- lib/digsig.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/digsig.c b/lib/digsig.c index ae05ea393fc8..4b8ef0bd315b 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -86,6 +86,12 @@ static int digsig_verify_rsa(struct key *key, down_read(&key->sem); ukp = key->payload.data; + if (!ukp) { + /* key was revoked before we acquired its semaphore */ + err = -EKEYREVOKED; + goto err1; + } + if (ukp->datalen < sizeof(*pkh)) goto err1; -- GitLab From 98c4e5cae5204c1114390219331ddd649d78a5a7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 12 Oct 2017 16:00:41 +0100 Subject: [PATCH 1360/5498] KEYS: don't let add_key() update an uninstantiated key commit 60ff5b2f547af3828aebafd54daded44cfb0807a upstream. Currently, when passed a key that already exists, add_key() will call the key's ->update() method if such exists. But this is heavily broken in the case where the key is uninstantiated because it doesn't call __key_instantiate_and_link(). Consequently, it doesn't do most of the things that are supposed to happen when the key is instantiated, such as setting the instantiation state, clearing KEY_FLAG_USER_CONSTRUCT and awakening tasks waiting on it, and incrementing key->user->nikeys. It also never takes key_construction_mutex, which means that ->instantiate() can run concurrently with ->update() on the same key. In the case of the "user" and "logon" key types this causes a memory leak, at best. Maybe even worse, the ->update() methods of the "encrypted" and "trusted" key types actually just dereference a NULL pointer when passed an uninstantiated key. Change key_create_or_update() to wait interruptibly for the key to finish construction before continuing. This patch only affects *uninstantiated* keys. For now we still allow a negatively instantiated key to be updated (thereby positively instantiating it), although that's broken too (the next patch fixes it) and I'm not sure that anyone actually uses that functionality either. Here is a simple reproducer for the bug using the "encrypted" key type (requires CONFIG_ENCRYPTED_KEYS=y), though as noted above the bug pertained to more than just the "encrypted" key type: #include #include #include int main(void) { int ringid = keyctl_join_session_keyring(NULL); if (fork()) { for (;;) { const char payload[] = "update user:foo 32"; usleep(rand() % 10000); add_key("encrypted", "desc", payload, sizeof(payload), ringid); keyctl_clear(ringid); } } else { for (;;) request_key("encrypted", "desc", "callout_info", ringid); } } It causes: BUG: unable to handle kernel NULL pointer dereference at 0000000000000018 IP: encrypted_update+0xb0/0x170 PGD 7a178067 P4D 7a178067 PUD 77269067 PMD 0 PREEMPT SMP CPU: 0 PID: 340 Comm: reproduce Tainted: G D 4.14.0-rc1-00025-g428490e38b2e #796 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 task: ffff8a467a39a340 task.stack: ffffb15c40770000 RIP: 0010:encrypted_update+0xb0/0x170 RSP: 0018:ffffb15c40773de8 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff8a467a275b00 RCX: 0000000000000000 RDX: 0000000000000005 RSI: ffff8a467a275b14 RDI: ffffffffb742f303 RBP: ffffb15c40773e20 R08: 0000000000000000 R09: ffff8a467a275b17 R10: 0000000000000020 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: ffff8a4677057180 R15: ffff8a467a275b0f FS: 00007f5d7fb08700(0000) GS:ffff8a467f200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000018 CR3: 0000000077262005 CR4: 00000000001606f0 Call Trace: key_create_or_update+0x2bc/0x460 SyS_add_key+0x10c/0x1d0 entry_SYSCALL_64_fastpath+0x1f/0xbe RIP: 0033:0x7f5d7f211259 RSP: 002b:00007ffed03904c8 EFLAGS: 00000246 ORIG_RAX: 00000000000000f8 RAX: ffffffffffffffda RBX: 000000003b2a7955 RCX: 00007f5d7f211259 RDX: 00000000004009e4 RSI: 00000000004009ff RDI: 0000000000400a04 RBP: 0000000068db8bad R08: 000000003b2a7955 R09: 0000000000000004 R10: 000000000000001a R11: 0000000000000246 R12: 0000000000400868 R13: 00007ffed03905d0 R14: 0000000000000000 R15: 0000000000000000 Code: 77 28 e8 64 34 1f 00 45 31 c0 31 c9 48 8d 55 c8 48 89 df 48 8d 75 d0 e8 ff f9 ff ff 85 c0 41 89 c4 0f 88 84 00 00 00 4c 8b 7d c8 <49> 8b 75 18 4c 89 ff e8 24 f8 ff ff 85 c0 41 89 c4 78 6d 49 8b RIP: encrypted_update+0xb0/0x170 RSP: ffffb15c40773de8 CR2: 0000000000000018 Reported-by: Eric Biggers Signed-off-by: David Howells cc: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- security/keys/key.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/security/keys/key.c b/security/keys/key.c index 8f67cd3bb999..783738ea20ee 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -909,6 +909,16 @@ error: */ __key_link_end(keyring, &index_key, edit); + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) { + ret = wait_for_key_construction(key, true); + if (ret < 0) { + key_ref_put(key_ref); + key_ref = ERR_PTR(ret); + goto error_free_prep; + } + } + key_ref = __key_update(key_ref, &prep); goto error_free_prep; } -- GitLab From 9918b51157fdf22c430ddec6b4d772aa1cbc7775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacy=20Gaw=C4=99dzki?= Date: Tue, 3 Feb 2015 19:05:18 +0100 Subject: [PATCH 1361/5498] cls_api.c: Fix dumping of non-existing actions' stats. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b057df24a7536cce6c372efe9d0e3d1558afedf4 upstream. In tcf_exts_dump_stats(), ensure that exts->actions is not empty before accessing the first element of that list and calling tcf_action_copy_stats() on it. This fixes some random segvs when adding filters of type "basic" with no particular action. This also fixes the dumping of those "no-action" filters, which more often than not made calls to tcf_action_copy_stats() fail and consequently netlink attributes added by the caller to be removed by a call to nla_nest_cancel(). Fixes: 33be62715991 ("net_sched: act: use standard struct list_head") Signed-off-by: Ignacy GawÄ™dzki Acked-by: Cong Wang Signed-off-by: David S. Miller Cc: J Pommnitz Signed-off-by: Greg Kroah-Hartman --- net/sched/cls_api.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index e50272592724..3a01c73497a3 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -563,8 +563,9 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, } EXPORT_SYMBOL(tcf_exts_change); -#define tcf_exts_first_act(ext) \ - list_first_entry(&(exts)->actions, struct tc_action, list) +#define tcf_exts_first_act(ext) \ + list_first_entry_or_null(&(exts)->actions, \ + struct tc_action, list) int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) { @@ -610,7 +611,7 @@ int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) { #ifdef CONFIG_NET_CLS_ACT struct tc_action *a = tcf_exts_first_act(exts); - if (tcf_action_copy_stats(skb, a, 1) < 0) + if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0) return -1; #endif return 0; -- GitLab From e967f7593831621560d2ae2a5e74af94035b0f06 Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Fri, 28 Oct 2016 22:13:42 +0200 Subject: [PATCH 1362/5498] parisc: Avoid trashing sr2 and sr3 in LWS code commit f4125cfdb3008363137f744c101e5d76ead760ba upstream. There is no need to trash sr2 and sr3 in the Light-weight syscall (LWS). sr2 already points to kernel space (it's zero in userspace, otherwise syscalls wouldn't work), and since the LWS code is executed in userspace, we can simply ignore to preload sr3. Signed-off-by: John David Anglin Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/syscall.S | 53 ++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index 8f13c7facdd7..a6d7de8cc47d 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -479,11 +479,6 @@ lws_start: comiclr,>> __NR_lws_entries, %r20, %r0 b,n lws_exit_nosys - /* WARNING: Trashing sr2 and sr3 */ - mfsp %sr7,%r1 /* get userspace into sr3 */ - mtsp %r1,%sr3 - mtsp %r0,%sr2 /* get kernel space into sr2 */ - /* Load table start */ ldil L%lws_table, %r1 ldo R%lws_table(%r1), %r28 /* Scratch use of r28 */ @@ -632,9 +627,9 @@ cas_action: stw %r1, 4(%sr2,%r20) #endif /* The load and store could fail */ -1: ldw,ma 0(%sr3,%r26), %r28 +1: ldw,ma 0(%r26), %r28 sub,<> %r28, %r25, %r0 -2: stw,ma %r24, 0(%sr3,%r26) +2: stw,ma %r24, 0(%r26) /* Free lock */ stw,ma %r20, 0(%sr2,%r20) #if ENABLE_LWS_DEBUG @@ -711,9 +706,9 @@ lws_compare_and_swap_2: nop /* 8bit load */ -4: ldb 0(%sr3,%r25), %r25 +4: ldb 0(%r25), %r25 b cas2_lock_start -5: ldb 0(%sr3,%r24), %r24 +5: ldb 0(%r24), %r24 nop nop nop @@ -721,9 +716,9 @@ lws_compare_and_swap_2: nop /* 16bit load */ -6: ldh 0(%sr3,%r25), %r25 +6: ldh 0(%r25), %r25 b cas2_lock_start -7: ldh 0(%sr3,%r24), %r24 +7: ldh 0(%r24), %r24 nop nop nop @@ -731,9 +726,9 @@ lws_compare_and_swap_2: nop /* 32bit load */ -8: ldw 0(%sr3,%r25), %r25 +8: ldw 0(%r25), %r25 b cas2_lock_start -9: ldw 0(%sr3,%r24), %r24 +9: ldw 0(%r24), %r24 nop nop nop @@ -742,14 +737,14 @@ lws_compare_and_swap_2: /* 64bit load */ #ifdef CONFIG_64BIT -10: ldd 0(%sr3,%r25), %r25 -11: ldd 0(%sr3,%r24), %r24 +10: ldd 0(%r25), %r25 +11: ldd 0(%r24), %r24 #else /* Load new value into r22/r23 - high/low */ -10: ldw 0(%sr3,%r25), %r22 -11: ldw 4(%sr3,%r25), %r23 +10: ldw 0(%r25), %r22 +11: ldw 4(%r25), %r23 /* Load new value into fr4 for atomic store later */ -12: flddx 0(%sr3,%r24), %fr4 +12: flddx 0(%r24), %fr4 #endif cas2_lock_start: @@ -799,30 +794,30 @@ cas2_action: ldo 1(%r0),%r28 /* 8bit CAS */ -13: ldb,ma 0(%sr3,%r26), %r29 +13: ldb,ma 0(%r26), %r29 sub,= %r29, %r25, %r0 b,n cas2_end -14: stb,ma %r24, 0(%sr3,%r26) +14: stb,ma %r24, 0(%r26) b cas2_end copy %r0, %r28 nop nop /* 16bit CAS */ -15: ldh,ma 0(%sr3,%r26), %r29 +15: ldh,ma 0(%r26), %r29 sub,= %r29, %r25, %r0 b,n cas2_end -16: sth,ma %r24, 0(%sr3,%r26) +16: sth,ma %r24, 0(%r26) b cas2_end copy %r0, %r28 nop nop /* 32bit CAS */ -17: ldw,ma 0(%sr3,%r26), %r29 +17: ldw,ma 0(%r26), %r29 sub,= %r29, %r25, %r0 b,n cas2_end -18: stw,ma %r24, 0(%sr3,%r26) +18: stw,ma %r24, 0(%r26) b cas2_end copy %r0, %r28 nop @@ -830,22 +825,22 @@ cas2_action: /* 64bit CAS */ #ifdef CONFIG_64BIT -19: ldd,ma 0(%sr3,%r26), %r29 +19: ldd,ma 0(%r26), %r29 sub,*= %r29, %r25, %r0 b,n cas2_end -20: std,ma %r24, 0(%sr3,%r26) +20: std,ma %r24, 0(%r26) copy %r0, %r28 #else /* Compare first word */ -19: ldw,ma 0(%sr3,%r26), %r29 +19: ldw,ma 0(%r26), %r29 sub,= %r29, %r22, %r0 b,n cas2_end /* Compare second word */ -20: ldw,ma 4(%sr3,%r26), %r29 +20: ldw,ma 4(%r26), %r29 sub,= %r29, %r23, %r0 b,n cas2_end /* Perform the store */ -21: fstdx %fr4, 0(%sr3,%r26) +21: fstdx %fr4, 0(%r26) copy %r0, %r28 #endif -- GitLab From 9a8835679e67ae4b031405ab32a690c5d98e94a6 Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Sat, 30 Sep 2017 17:24:23 -0400 Subject: [PATCH 1363/5498] parisc: Fix double-word compare and exchange in LWS code on 32-bit kernels commit 374b3bf8e8b519f61eb9775888074c6e46b3bf0c upstream. As discussed on the debian-hppa list, double-wordcompare and exchange operations fail on 32-bit kernels. Looking at the code, I realized that the ",ma" completer does the wrong thing in the "ldw,ma 4(%r26), %r29" instruction. This increments %r26 and causes the following store to write to the wrong location. Note by Helge Deller: The patch applies cleanly to stable kernel series if this upstream commit is merged in advance: f4125cfdb300 ("parisc: Avoid trashing sr2 and sr3 in LWS code"). Signed-off-by: John David Anglin Tested-by: Christoph Biedl Fixes: 89206491201c ("parisc: Implement new LWS CAS supporting 64 bit operations.") Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/syscall.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index a6d7de8cc47d..95f090fe385a 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -740,7 +740,7 @@ lws_compare_and_swap_2: 10: ldd 0(%r25), %r25 11: ldd 0(%r24), %r24 #else - /* Load new value into r22/r23 - high/low */ + /* Load old value into r22/r23 - high/low */ 10: ldw 0(%r25), %r22 11: ldw 4(%r25), %r23 /* Load new value into fr4 for atomic store later */ @@ -832,11 +832,11 @@ cas2_action: copy %r0, %r28 #else /* Compare first word */ -19: ldw,ma 0(%r26), %r29 +19: ldw 0(%r26), %r29 sub,= %r29, %r22, %r0 b,n cas2_end /* Compare second word */ -20: ldw,ma 4(%r26), %r29 +20: ldw 4(%r26), %r29 sub,= %r29, %r23, %r0 b,n cas2_end /* Perform the store */ -- GitLab From 38118617a584b70aa14988397f0041fd067f9de3 Mon Sep 17 00:00:00 2001 From: Alexander Drozdov Date: Tue, 24 Feb 2015 08:18:28 +0300 Subject: [PATCH 1364/5498] af_packet: don't pass empty blocks for PACKET_V3 commit 41a50d621a321b4c15273cc1b5ed41437f4acdfb upstream. Before da413eec729d ("packet: Fixed TPACKET V3 to signal poll when block is closed rather than every packet") poll listening for an af_packet socket was not signaled if there was no packets to process. After the patch poll is signaled evety time when block retire timer expires. That happens because af_packet closes the current block on timeout even if the block is empty. Passing empty blocks to the user not only wastes CPU but also wastes ring buffer space increasing probability of packets dropping on small timeouts. Signed-off-by: Alexander Drozdov Cc: Dan Collins Cc: Willem de Bruijn Cc: Guy Harris Signed-off-by: David S. Miller Cc: Christoph Biedl Signed-off-by: Greg Kroah-Hartman --- net/packet/af_packet.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index dc474fd81d71..d872be097c60 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -697,6 +697,10 @@ static void prb_retire_rx_blk_timer_expired(unsigned long data) if (pkc->last_kactive_blk_num == pkc->kactive_blk_num) { if (!frozen) { + if (!BLOCK_NUM_PKTS(pbd)) { + /* An empty block. Just refresh the timer. */ + goto refresh_timer; + } prb_retire_current_block(pkc, po, TP_STATUS_BLK_TMO); if (!prb_dispatch_next_block(pkc, po)) goto refresh_timer; @@ -796,7 +800,11 @@ static void prb_close_block(struct tpacket_kbdq_core *pkc1, h1->ts_last_pkt.ts_sec = last_pkt->tp_sec; h1->ts_last_pkt.ts_nsec = last_pkt->tp_nsec; } else { - /* Ok, we tmo'd - so get the current time */ + /* Ok, we tmo'd - so get the current time. + * + * It shouldn't really happen as we don't close empty + * blocks. See prb_retire_rx_blk_timer_expired(). + */ struct timespec ts; getnstimeofday(&ts); h1->ts_last_pkt.ts_sec = ts.tv_sec; -- GitLab From 114dff8ef64e4b72cb97a613179db64096bc79dd Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:40:00 -0700 Subject: [PATCH 1365/5498] FS-Cache: fix dereference of NULL user_key_payload commit d124b2c53c7bee6569d2a2d0b18b4a1afde00134 upstream. When the file /proc/fs/fscache/objects (available with CONFIG_FSCACHE_OBJECT_LIST=y) is opened, we request a user key with description "fscache:objlist", then access its payload. However, a revoked key has a NULL payload, and we failed to check for this. request_key() *does* skip revoked keys, but there is still a window where the key can be revoked before we access its payload. Fix it by checking for a NULL payload, treating it like a key which was already revoked at the time it was requested. Fixes: 4fbf4291aa15 ("FS-Cache: Allow the current state of all objects to be dumped") Reviewed-by: James Morris Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- fs/fscache/object-list.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c index 51dde817e1f2..11cf71ac1cb5 100644 --- a/fs/fscache/object-list.c +++ b/fs/fscache/object-list.c @@ -330,6 +330,13 @@ static void fscache_objlist_config(struct fscache_objlist_data *data) rcu_read_lock(); confkey = key->payload.data; + if (!confkey) { + /* key was revoked */ + rcu_read_unlock(); + key_put(key); + goto no_config; + } + buf = confkey->data; for (len = confkey->datalen - 1; len >= 0; len--) { -- GitLab From b44ef85f9033720e7ec6aa7bc9536b9e4b09719a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 27 Oct 2017 10:17:24 +0200 Subject: [PATCH 1366/5498] Linux 3.18.78 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6f71303316d5..527627294778 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 77 +SUBLEVEL = 78 EXTRAVERSION = NAME = Diseased Newt -- GitLab From a6ae48a8728a2f8add8a3c5b147a81baef5b2100 Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Wed, 25 Oct 2017 09:59:37 +0800 Subject: [PATCH 1367/5498] input: sensors: fix code scan error in bma2x2 driver Fix code scan error in BOSCH bma2x2 accelerometer driver. Change-Id: I8b1277c0f204ec5e8df02ebef4452efa91e10cd2 Signed-off-by: Bingzhe Cai --- drivers/input/misc/bma2x2.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/input/misc/bma2x2.c b/drivers/input/misc/bma2x2.c index c31bbf52f935..7b526277a862 100644 --- a/drivers/input/misc/bma2x2.c +++ b/drivers/input/misc/bma2x2.c @@ -3385,7 +3385,7 @@ static int bma2x2_get_bandwidth(struct i2c_client *client, unsigned char *BW) return comres; } -int bma2x2_get_sleep_duration(struct i2c_client *client, unsigned char +static int bma2x2_get_sleep_duration(struct i2c_client *client, unsigned char *sleep_dur) { int comres = 0; @@ -3399,7 +3399,7 @@ int bma2x2_get_sleep_duration(struct i2c_client *client, unsigned char return comres; } -int bma2x2_set_sleep_duration(struct i2c_client *client, unsigned char +static int bma2x2_set_sleep_duration(struct i2c_client *client, unsigned char sleep_dur) { int comres = 0; @@ -4956,7 +4956,7 @@ static ssize_t bma2x2_selftest_store(struct device *dev, short value2 = 0; short diff = 0; unsigned long result = 0; - unsigned char test_result_branch = 0; + unsigned long test_result_branch = 0; struct i2c_client *client = to_i2c_client(dev); struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); @@ -5225,7 +5225,7 @@ static ssize_t bma2x2_flat_hold_time_store(struct device *dev, return count; } -const int bma2x2_sensor_bitwidth[] = { +static const int bma2x2_sensor_bitwidth[] = { 12, 10, 8, 14 }; @@ -7528,7 +7528,8 @@ static struct attribute_group bma2x2_double_tap_attribute_group = { #if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2) -unsigned char *orient[] = {"upward looking portrait upright", +#ifdef ENABLE_ISR_DEBUG_MSG +static unsigned char *orient[] = {"upward looking portrait upright", "upward looking portrait upside-down", "upward looking landscape left", "upward looking landscape right", @@ -7536,6 +7537,7 @@ unsigned char *orient[] = {"upward looking portrait upright", "downward looking portrait upside-down", "downward looking landscape left", "downward looking landscape right"}; +#endif static void bma2x2_high_g_interrupt_handle(struct bma2x2_data *bma2x2) @@ -8125,7 +8127,7 @@ static int bma2x2_parse_dt(struct device *dev, dev_err(dev, "Unable to read sensor place parameter\n"); return rc; } - if (temp_val > 7 || temp_val < 0) { + if (temp_val > 7) { dev_err(dev, "Invalid place parameter, use default value 0\n"); pdata->place = 0; } else { @@ -8842,7 +8844,7 @@ static int bma2x2_remove(struct i2c_client *client) &bma2x2_attribute_group); bma2x2_set_enable(&client->dev, 0); - if (!data->pdata->use_hrtimer) { + if (data->pdata && !data->pdata->use_hrtimer) { destroy_workqueue(data->data_wq); } else { hrtimer_cancel(&data->accel_timer); @@ -8859,7 +8861,7 @@ static int bma2x2_remove(struct i2c_client *client) return 0; } -void bma2x2_shutdown(struct i2c_client *client) +static void bma2x2_shutdown(struct i2c_client *client) { struct bma2x2_data *data = i2c_get_clientdata(client); @@ -8955,4 +8957,3 @@ MODULE_LICENSE("GPL v2"); module_init(BMA2X2_init); module_exit(BMA2X2_exit); - -- GitLab From 226555507bc4316723092d9c7b7b2a757f39a39f Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 27 Oct 2017 11:49:01 +0530 Subject: [PATCH 1368/5498] mm: vmalloc: don't print lowmem information With CONFIG_ENABLE_VMALLOC_SAVING enabled, carve-outs are reused as vmalloc space. Since vmalloc space is now spread across whole of kernel virtual memory, vmallocinfo prints lowmem information as well. Remove lowmem information from vmallocinfo as it isn't expected. Change-Id: I3ec2995eb5460fd44cef84207a960fe5a3bd04db Signed-off-by: Shiraz Hashim --- mm/vmalloc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 89d5abbe5e04..80ece7cedc9f 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -2694,6 +2694,9 @@ static int s_show(struct seq_file *m, void *p) v = va->vm; + if (v->flags & VM_LOWMEM) + return 0; + seq_printf(m, "0x%pK-0x%pK %7ld", v->addr, v->addr + v->size, v->size); @@ -2721,9 +2724,6 @@ static int s_show(struct seq_file *m, void *p) if (v->flags & VM_VPAGES) seq_puts(m, " vpages"); - if (v->flags & VM_LOWMEM) - seq_puts(m, " lowmem"); - show_numa_info(m, v); seq_putc(m, '\n'); return 0; -- GitLab From cd3fa6c2bd5f8272065975e1781d738908ac7c2f Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Thu, 14 Sep 2017 18:51:12 +0800 Subject: [PATCH 1369/5498] defconfig: arm: msm: enable sensors for msm8909 SKUE Add config to enable build of accelerometer, ambient light and proximity sensor drivers. Change-Id: I57315c93db488760c8addcff7aef3d3ac0c7ac02 Signed-off-by: Bingzhe Cai --- arch/arm/configs/msm8909-perf_defconfig | 2 ++ arch/arm/configs/msm8909_defconfig | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index 0e3e2908904e..7de592f49679 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -293,6 +293,8 @@ CONFIG_SENSORS_MPU6050=y CONFIG_SENSORS_AKM8963=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=m +CONFIG_SENSORS_LTR553=y +CONFIG_SENSORS_BMA2X2=y # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVMEM is not set diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index 0c62dab092a8..0eda04c77b5b 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -299,6 +299,8 @@ CONFIG_SENSORS_MPU6050=y CONFIG_SENSORS_AKM8963=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=m +CONFIG_SENSORS_LTR553=y +CONFIG_SENSORS_BMA2X2=y # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVMEM is not set # CONFIG_DEVKMEM is not set -- GitLab From 4caa90efc8cbba2df46d0a73328b1e1d4c9b0b64 Mon Sep 17 00:00:00 2001 From: Sai Krishna Juturi Date: Wed, 25 Oct 2017 12:19:39 +0530 Subject: [PATCH 1370/5498] USB: phy: msm: Update voltage_max and current_max properties To avoid cts test failures write voltage_max and current_max based on charger type. volatge_max is the max voltage that charger can negotiate upto. current_max is the max current that charger can negotiate upto. Change-Id: Iaae8ae615778b2e13fcaf273177059d8f02c8174 Signed-off-by: Sai Krishna Juturi --- drivers/usb/phy/phy-msm-usb.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 684c0dc1d5d3..ab1a1029b738 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -77,6 +77,14 @@ #define PM_QOS_SAMPLE_SEC 2 #define PM_QOS_THRESHOLD 400 +#define MICRO_5V 5000000 +#define MICRO_9V 9000000 + +#define SDP_CURRENT_UA 500000 +#define CDP_CURRENT_UA 1500000 +#define DCP_CURRENT_UA 1500000 +#define HVDCP_CURRENT_UA 3000000 + enum msm_otg_phy_reg_mode { USB_PHY_REG_OFF, USB_PHY_REG_ON, @@ -3611,16 +3619,24 @@ static int otg_power_set_property_usb(struct power_supply *psy, switch (psy->type) { case POWER_SUPPLY_TYPE_USB: motg->chg_type = USB_SDP_CHARGER; + motg->voltage_max = MICRO_5V; + motg->current_max = SDP_CURRENT_UA; break; case POWER_SUPPLY_TYPE_USB_DCP: motg->chg_type = USB_DCP_CHARGER; + motg->voltage_max = MICRO_5V; + motg->current_max = DCP_CURRENT_UA; break; case POWER_SUPPLY_TYPE_USB_HVDCP: motg->chg_type = USB_DCP_CHARGER; + motg->voltage_max = MICRO_9V; + motg->current_max = HVDCP_CURRENT_UA; msm_otg_notify_charger(motg, hvdcp_max_current); break; case POWER_SUPPLY_TYPE_USB_CDP: motg->chg_type = USB_CDP_CHARGER; + motg->voltage_max = MICRO_5V; + motg->current_max = CDP_CURRENT_UA; break; case POWER_SUPPLY_TYPE_USB_ACA: motg->chg_type = USB_PROPRIETARY_CHARGER; -- GitLab From 60bd5e7cbe84441a4bf16e5a2d258a2b803653fe Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Thu, 19 Oct 2017 15:04:46 +0200 Subject: [PATCH 1371/5498] ANDROID: binder: Add thread->process_todo flag. This flag determines whether the thread should currently process the work in the thread->todo worklist. The prime usecase for this is improving the performance of synchronous transactions: all synchronous transactions post a BR_TRANSACTION_COMPLETE to the calling thread, but there's no reason to return that command to userspace right away - userspace anyway needs to wait for the reply. Likewise, a synchronous transaction that contains a binder object can cause a BC_ACQUIRE/BC_INCREFS to be returned to userspace; since the caller must anyway hold a strong/weak ref for the duration of the call, postponing these commands until the reply comes in is not a problem. Note that this flag is not used to determine whether a thread can handle process work; a thread should never pick up process work when thread work is still pending. Before patch: ------------------------------------------------------------------ Benchmark Time CPU Iterations ------------------------------------------------------------------ BM_sendVec_binderize/4 45959 ns 20288 ns 34351 BM_sendVec_binderize/8 45603 ns 20080 ns 34909 BM_sendVec_binderize/16 45528 ns 20113 ns 34863 BM_sendVec_binderize/32 45551 ns 20122 ns 34881 BM_sendVec_binderize/64 45701 ns 20183 ns 34864 BM_sendVec_binderize/128 45824 ns 20250 ns 34576 BM_sendVec_binderize/256 45695 ns 20171 ns 34759 BM_sendVec_binderize/512 45743 ns 20211 ns 34489 BM_sendVec_binderize/1024 46169 ns 20430 ns 34081 After patch: ------------------------------------------------------------------ Benchmark Time CPU Iterations ------------------------------------------------------------------ BM_sendVec_binderize/4 42939 ns 17262 ns 40653 BM_sendVec_binderize/8 42823 ns 17243 ns 40671 BM_sendVec_binderize/16 42898 ns 17243 ns 40594 BM_sendVec_binderize/32 42838 ns 17267 ns 40527 BM_sendVec_binderize/64 42854 ns 17249 ns 40379 BM_sendVec_binderize/128 42881 ns 17288 ns 40427 BM_sendVec_binderize/256 42917 ns 17297 ns 40429 BM_sendVec_binderize/512 43184 ns 17395 ns 40411 BM_sendVec_binderize/1024 43119 ns 17357 ns 40432 Signed-off-by: Martijn Coenen Change-Id: Ia70287066d62aba64e98ac44ff1214e37ca75693 --- drivers/staging/android/binder.c | 144 +++++++++++++++++++++---------- 1 file changed, 100 insertions(+), 44 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 030b6da9e8cf..8a3e02916107 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -597,6 +597,8 @@ enum { * (protected by @proc->inner_lock) * @todo: list of work to do for this thread * (protected by @proc->inner_lock) + * @process_todo: whether work in @todo should be processed + * (protected by @proc->inner_lock) * @return_error: transaction errors reported by this thread * (only accessed by this thread) * @reply_error: transaction errors reported by target thread @@ -623,6 +625,7 @@ struct binder_thread { bool looper_need_return; /* can be written by other thread */ struct binder_transaction *transaction_stack; struct list_head todo; + bool process_todo; struct binder_error return_error; struct binder_error reply_error; wait_queue_head_t wait; @@ -810,6 +813,16 @@ static bool binder_worklist_empty(struct binder_proc *proc, return ret; } +/** + * binder_enqueue_work_ilocked() - Add an item to the work list + * @work: struct binder_work to add to list + * @target_list: list to add work to + * + * Adds the work to the specified list. Asserts that work + * is not already on a list. + * + * Requires the proc->inner_lock to be held. + */ static void binder_enqueue_work_ilocked(struct binder_work *work, struct list_head *target_list) @@ -820,22 +833,56 @@ binder_enqueue_work_ilocked(struct binder_work *work, } /** - * binder_enqueue_work() - Add an item to the work list - * @proc: binder_proc associated with list + * binder_enqueue_thread_work_ilocked_nowake() - Add thread work + * @thread: thread to queue work to * @work: struct binder_work to add to list - * @target_list: list to add work to * - * Adds the work to the specified list. Asserts that work - * is not already on a list. + * Adds the work to the todo list of the thread. Doesn't set the process_todo + * flag, which means that (if it wasn't already set) the thread will go to + * sleep without handling this work when it calls read. + * + * Requires the proc->inner_lock to be held. */ static void -binder_enqueue_work(struct binder_proc *proc, - struct binder_work *work, - struct list_head *target_list) +binder_enqueue_thread_work_ilocked_nowake(struct binder_thread *thread, + struct binder_work *work) { - binder_inner_proc_lock(proc); - binder_enqueue_work_ilocked(work, target_list); - binder_inner_proc_unlock(proc); + binder_enqueue_work_ilocked(work, &thread->todo); +} + +/** + * binder_enqueue_thread_work_ilocked() - Add an item to the thread work list + * @thread: thread to queue work to + * @work: struct binder_work to add to list + * + * Adds the work to the todo list of the thread, and enables processing + * of the todo queue. + * + * Requires the proc->inner_lock to be held. + */ +static void +binder_enqueue_thread_work_ilocked(struct binder_thread *thread, + struct binder_work *work) +{ + binder_enqueue_work_ilocked(work, &thread->todo); + thread->process_todo = true; +} + +/** + * binder_enqueue_thread_work() - Add an item to the thread work list + * @thread: thread to queue work to + * @work: struct binder_work to add to list + * + * Adds the work to the todo list of the thread, and enables processing + * of the todo queue. + */ +static void +binder_enqueue_thread_work(struct binder_thread *thread, + struct binder_work *work) +{ + binder_inner_proc_lock(thread->proc); + binder_enqueue_thread_work_ilocked(thread, work); + binder_inner_proc_unlock(thread->proc); } static void @@ -950,7 +997,7 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) static bool binder_has_work_ilocked(struct binder_thread *thread, bool do_proc_work) { - return !binder_worklist_empty_ilocked(&thread->todo) || + return thread->process_todo || thread->looper_need_return || (do_proc_work && !binder_worklist_empty_ilocked(&thread->proc->todo)); @@ -1367,6 +1414,17 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong, node->local_strong_refs++; if (!node->has_strong_ref && target_list) { binder_dequeue_work_ilocked(&node->work); + /* + * Note: this function is the only place where we queue + * directly to a thread->todo without using the + * corresponding binder_enqueue_thread_work() helper + * functions; in this case it's ok to not set the + * process_todo flag, since we know this node work will + * always be followed by other work that starts queue + * processing: in case of synchronous transactions, a + * BR_REPLY or BR_ERROR; in case of oneway + * transactions, a BR_TRANSACTION_COMPLETE. + */ binder_enqueue_work_ilocked(&node->work, target_list); } } else { @@ -1378,6 +1436,9 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong, node->debug_id); return -EINVAL; } + /* + * See comment above + */ binder_enqueue_work_ilocked(&node->work, target_list); } } @@ -2067,9 +2128,9 @@ static void binder_send_failed_reply(struct binder_transaction *t, binder_pop_transaction_ilocked(target_thread, t); if (target_thread->reply_error.cmd == BR_OK) { target_thread->reply_error.cmd = error_code; - binder_enqueue_work_ilocked( - &target_thread->reply_error.work, - &target_thread->todo); + binder_enqueue_thread_work_ilocked( + target_thread, + &target_thread->reply_error.work); wake_up_interruptible(&target_thread->wait); } else { WARN(1, "Unexpected reply error: %u\n", @@ -2708,11 +2769,10 @@ static bool binder_proc_transaction(struct binder_transaction *t, struct binder_proc *proc, struct binder_thread *thread) { - struct list_head *target_list = NULL; struct binder_node *node = t->buffer->target_node; struct binder_priority node_prio; bool oneway = !!(t->flags & TF_ONE_WAY); - bool wakeup = true; + bool pending_async = false; BUG_ON(!node); binder_node_lock(node); @@ -2722,8 +2782,7 @@ static bool binder_proc_transaction(struct binder_transaction *t, if (oneway) { BUG_ON(thread); if (node->has_async_transaction) { - target_list = &node->async_todo; - wakeup = false; + pending_async = true; } else { node->has_async_transaction = 1; } @@ -2737,22 +2796,20 @@ static bool binder_proc_transaction(struct binder_transaction *t, return false; } - if (!thread && !target_list) + if (!thread && !pending_async) thread = binder_select_thread_ilocked(proc); if (thread) { - target_list = &thread->todo; binder_transaction_priority(thread->task, t, node_prio, node->inherit_rt); - } else if (!target_list) { - target_list = &proc->todo; + binder_enqueue_thread_work_ilocked(thread, &t->work); + } else if (!pending_async) { + binder_enqueue_work_ilocked(&t->work, &proc->todo); } else { - BUG_ON(target_list != &node->async_todo); + binder_enqueue_work_ilocked(&t->work, &node->async_todo); } - binder_enqueue_work_ilocked(&t->work, target_list); - - if (wakeup) + if (!pending_async) binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); binder_inner_proc_unlock(proc); @@ -3254,10 +3311,10 @@ static void binder_transaction(struct binder_proc *proc, } } tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; - binder_enqueue_work(proc, tcomplete, &thread->todo); t->work.type = BINDER_WORK_TRANSACTION; if (reply) { + binder_enqueue_thread_work(thread, tcomplete); binder_inner_proc_lock(target_proc); if (target_thread->is_dead) { binder_inner_proc_unlock(target_proc); @@ -3265,7 +3322,7 @@ static void binder_transaction(struct binder_proc *proc, } BUG_ON(t->buffer->async_transaction != 0); binder_pop_transaction_ilocked(target_thread, in_reply_to); - binder_enqueue_work_ilocked(&t->work, &target_thread->todo); + binder_enqueue_thread_work_ilocked(target_thread, &t->work); binder_inner_proc_unlock(target_proc); wake_up_interruptible_sync(&target_thread->wait); binder_restore_priority(current, in_reply_to->saved_priority); @@ -3273,6 +3330,7 @@ static void binder_transaction(struct binder_proc *proc, } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); binder_inner_proc_lock(proc); + binder_enqueue_thread_work_ilocked_nowake(thread, tcomplete); t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; @@ -3286,6 +3344,7 @@ static void binder_transaction(struct binder_proc *proc, } else { BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); + binder_enqueue_thread_work(thread, tcomplete); if (!binder_proc_transaction(t, target_proc, NULL)) goto err_dead_proc_or_thread; } @@ -3365,15 +3424,11 @@ err_invalid_target_handle: if (in_reply_to) { binder_restore_priority(current, in_reply_to->saved_priority); thread->return_error.cmd = BR_TRANSACTION_COMPLETE; - binder_enqueue_work(thread->proc, - &thread->return_error.work, - &thread->todo); + binder_enqueue_thread_work(thread, &thread->return_error.work); binder_send_failed_reply(in_reply_to, return_error); } else { thread->return_error.cmd = return_error; - binder_enqueue_work(thread->proc, - &thread->return_error.work, - &thread->todo); + binder_enqueue_thread_work(thread, &thread->return_error.work); } } @@ -3677,10 +3732,9 @@ static int binder_thread_write(struct binder_proc *proc, WARN_ON(thread->return_error.cmd != BR_OK); thread->return_error.cmd = BR_ERROR; - binder_enqueue_work( - thread->proc, - &thread->return_error.work, - &thread->todo); + binder_enqueue_thread_work( + thread, + &thread->return_error.work); binder_debug( BINDER_DEBUG_FAILED_TRANSACTION, "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", @@ -3760,9 +3814,9 @@ static int binder_thread_write(struct binder_proc *proc, if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) - binder_enqueue_work_ilocked( - &death->work, - &thread->todo); + binder_enqueue_thread_work_ilocked( + thread, + &death->work); else { binder_enqueue_work_ilocked( &death->work, @@ -3817,8 +3871,8 @@ static int binder_thread_write(struct binder_proc *proc, if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) - binder_enqueue_work_ilocked( - &death->work, &thread->todo); + binder_enqueue_thread_work_ilocked( + thread, &death->work); else { binder_enqueue_work_ilocked( &death->work, @@ -3992,6 +4046,8 @@ retry: break; } w = binder_dequeue_work_head_ilocked(list); + if (binder_worklist_empty_ilocked(&thread->todo)) + thread->process_todo = false; switch (w->type) { case BINDER_WORK_TRANSACTION: { -- GitLab From ca1f6a05202ab3d7dd9956196b359e2123a7144f Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Tue, 24 Oct 2017 16:37:39 +0200 Subject: [PATCH 1372/5498] ANDROID: binder: show high watermark of alloc->pages. Show the high watermark of the index into the alloc->pages array, to facilitate sizing the buffer on a per-process basis. Change-Id: I2b40cd16628e0ee45216c51dc9b3c5b0c862032e Signed-off-by: Martijn Coenen --- drivers/staging/android/binder_alloc.c | 4 ++++ drivers/staging/android/binder_alloc.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c index 8309da048a8c..b900b0722b8b 100644 --- a/drivers/staging/android/binder_alloc.c +++ b/drivers/staging/android/binder_alloc.c @@ -282,6 +282,9 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, goto err_vm_insert_page_failed; } + if (index + 1 > alloc->pages_high) + alloc->pages_high = index + 1; + trace_binder_alloc_page_end(alloc, index); /* vm_insert_page does not seem to increment the refcount */ } @@ -855,6 +858,7 @@ void binder_alloc_print_pages(struct seq_file *m, } mutex_unlock(&alloc->mutex); seq_printf(m, " pages: %d:%d:%d\n", active, lru, free); + seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high); } /** diff --git a/drivers/staging/android/binder_alloc.h b/drivers/staging/android/binder_alloc.h index a87913ac200b..9dc8f32a53d6 100644 --- a/drivers/staging/android/binder_alloc.h +++ b/drivers/staging/android/binder_alloc.h @@ -92,6 +92,7 @@ struct binder_lru_page { * @pages: array of binder_lru_page * @buffer_size: size of address space specified via mmap * @pid: pid for associated binder_proc (invariant after init) + * @pages_high: high watermark of offset in @pages * * Bookkeeping structure for per-proc address space management for binder * buffers. It is normally initialized during binder_init() and binder_mmap() @@ -112,6 +113,7 @@ struct binder_alloc { size_t buffer_size; uint32_t buffer_free; int pid; + size_t pages_high; }; #ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST -- GitLab From 76cb0211326655ca1fcaea4aacf3bc818d21af31 Mon Sep 17 00:00:00 2001 From: Shaikh Shadul Date: Fri, 27 Oct 2017 13:20:17 +0530 Subject: [PATCH 1373/5498] ARM: dts: msm: remove sensors nodes from msm8909w Remove sensors nodes from msm8909w BG as sensors are connected to MDSP. Change-Id: I0ef650a29f8e76e1e84cf1a3f84d32879ea51c4a Signed-off-by: Shaikh Shadul --- .../arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index 9db086835a04..803dc355cd42 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -76,20 +76,15 @@ }; &i2c_1 { - mpu6050@68 { - /delete-property/ vdd-supply; - /delete-property/ vlogic-supply; - }; - - avago@39 { - /delete-property/ vdd-supply; - /delete-property/ vio-supply; - }; + /delete-node/ mpu6050@68; + /delete-node/ avago@39; + /delete-node/ akm@c; +}; - akm@c { - /delete-property/ vdd-supply; - /delete-property/ vio-supply; - }; +&msm_gpio { + /delete-node/ mpu6050_int_pin; + /delete-node/ apds99xx_int_pin; + /delete-node/ ak8963_int_pin; }; &i2c_3 { -- GitLab From 763db147a4cbac670f57707c9c164dc11cc515ba Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Fri, 27 Oct 2017 16:44:39 +0530 Subject: [PATCH 1374/5498] Revert "ARM: dts: msm: remove modem wdog for mdmcalifornium" This reverts commit e2eba1942a261b1ad0f1a212832c1696a4e615a6. Revert this change so that HLOS could handle mpss wdog bite. Change-Id: I531b1a2e21877a310dc901c022910aadc6be1355 Signed-off-by: Jitendra Sharma --- arch/arm/boot/dts/qcom/mdm9650.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/mdm9650.dtsi b/arch/arm/boot/dts/qcom/mdm9650.dtsi index 4511838e5845..e8a0febdc64e 100644 --- a/arch/arm/boot/dts/qcom/mdm9650.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650.dtsi @@ -158,6 +158,7 @@ "gpll0_mss_clk"; vdd_cx-supply = <&pmd9650_s5_level>; + interrupts = <0 24 1>; vdd_cx-voltage = ; vdd_mx-supply = <&pmd9650_l9_level>; vdd_mx-uV = ; -- GitLab From c5f211a9ba81ed5814b173563f96482233b65fa9 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Mon, 16 Oct 2017 17:23:37 +0530 Subject: [PATCH 1375/5498] ASoC: msm: msm_bg: add support to turn on regulator Changes to vote for turn on the regulator on boot up for speaker audio. Change-Id: I4f030433a00caaaf05eccbee97ad4105909b1f5c Signed-off-by: Bala Kishore Pati --- .../bindings/sound/qcom-audio-dev.txt | 1 + sound/soc/msm/msm_bg.c | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 17fb6451a31e..36f04c2c4f28 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2106,6 +2106,7 @@ Optional properties: - asoc-codec-names: This property contains list of codec dai names. The order of the codec dai names should match to that of the phandle order given in "asoc-codec". +- vdd-spkr-supply: BG codec supply's speaker regulator device tree node. Example: sound { diff --git a/sound/soc/msm/msm_bg.c b/sound/soc/msm/msm_bg.c index b190680d7ae5..471dcf672eea 100644 --- a/sound/soc/msm/msm_bg.c +++ b/sound/soc/msm/msm_bg.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #define DRV_NAME "msm-bg-asoc-wcd" +#define SPEAK_VREG_NAME "vdd-spkr" + #define BTSCO_RATE_8KHZ 8000 #define BTSCO_RATE_16KHZ 16000 @@ -143,6 +146,7 @@ struct msm8916_asoc_mach_data { void __iomem *vaddr_gpio_mux_pcm_ctl; void __iomem *vaddr_gpio_mux_sec_pcm_ctl; struct device_node *pri_mi2s_gpio_p; + struct regulator *spkr_vreg; }; static inline int param_is_mask(int p) @@ -2157,7 +2161,35 @@ static int msm_bg_asoc_machine_probe(struct platform_device *pdev) ret); goto err; } + if (of_get_property( + pdev->dev.of_node, + SPEAK_VREG_NAME "-supply", NULL)) { + pdata->spkr_vreg = regulator_get(&pdev->dev, SPEAK_VREG_NAME); + if (IS_ERR(pdata->spkr_vreg)) { + ret = PTR_ERR(pdata->spkr_vreg); + pr_err("VDD-speaker get failed error=%d\n", ret); + goto err; + } + + ret = regulator_set_voltage( + pdata->spkr_vreg, 1800000, 1800000); + if (ret) { + pr_err("VDD-speaker set voltage failed error=%d\n", + ret); + goto err_vreg_regulator; + } else { + ret = regulator_enable(pdata->spkr_vreg); + if (ret) { + pr_err("VDD-speaker voltage enable\n" + "\nfailed error=%d\n", ret); + goto err_vreg_regulator; + } + } + } return 0; + +err_vreg_regulator: + regulator_put(pdata->spkr_vreg); err: if (pdata->vaddr_gpio_mux_spkr_ctl) iounmap(pdata->vaddr_gpio_mux_spkr_ctl); -- GitLab From 06a1d2a976eda7841b3e124bd1afdaf49bc9f954 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Tue, 24 Oct 2017 19:30:15 +0530 Subject: [PATCH 1376/5498] ARM: dts: msm: use appropriate regulators for audio on msm8909w Audio client will vote for speaker voltage when usecase starts. Add vdd-spk support for PM660. Change-Id: Ib7bbacc8776de8fa0f1303be10cce9279edd151e Signed-off-by: Bala Kishore Pati --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 1 + arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 52e3e49952a5..2ebd38cd69c0 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -185,6 +185,7 @@ &audio_codec_bg { status = "ok"; + vdd-spkr-supply = <&pm660_l11>; }; &bg_cdc { diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 36644ba7d92c..1c3358a127de 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -174,6 +174,7 @@ &audio_codec_bg { status = "ok"; + vdd-spkr-supply = <&pm660_l11>; }; &bg_cdc { -- GitLab From c4524e08fe34130f1e2d7fcb460c810da01da138 Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Thu, 8 Dec 2016 20:40:54 +0530 Subject: [PATCH 1377/5498] soc: qcom: glink: Fix queue intent after glink close Glink pkt client is calling glink_queue_rx_intent even after calling glink_close, this result in calling with invalid handle. glink_queue_rx_intent is called with mutex lock and dev handle is set to NULL right after call to glink_close. CRs-Fixed: 1098097 Change-Id: I6fc8e7d91a6d27fe6602e8aabcffcdbfb1316c97 Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/msm_glink_pkt.c | 37 +++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/msm_glink_pkt.c b/drivers/soc/qcom/msm_glink_pkt.c index 9490882b6ecb..e3bb4bc598ca 100644 --- a/drivers/soc/qcom/msm_glink_pkt.c +++ b/drivers/soc/qcom/msm_glink_pkt.c @@ -522,13 +522,21 @@ static void glink_pkt_queue_rx_intent_worker(struct work_struct *work) struct queue_rx_intent_work, work); struct glink_pkt_dev *devp = work_item->devp; - if (!devp || !devp->handle) { + if (!devp) { + GLINK_PKT_ERR("%s: Invalid device\n", __func__); + kfree(work_item); + return; + } + mutex_lock(&devp->ch_lock); + if (!devp->handle) { GLINK_PKT_ERR("%s: Invalid device Handle\n", __func__); + mutex_unlock(&devp->ch_lock); kfree(work_item); return; } ret = glink_queue_rx_intent(devp->handle, devp, work_item->intent_size); + mutex_unlock(&devp->ch_lock); GLINK_PKT_INFO("%s: Triggered with size[%zu] ret[%d]\n", __func__, work_item->intent_size, ret); if (ret) @@ -1075,6 +1083,27 @@ error: return ret; } +/** + * pop_rx_pkt() - return first pkt from rx pkt_list + * devp: pointer to G-Link packet device. + * + * This function return first item from rx pkt_list and NULL if list is empty. + */ +static struct glink_rx_pkt *pop_rx_pkt(struct glink_pkt_dev *devp) +{ + unsigned long flags; + struct glink_rx_pkt *pkt = NULL; + + spin_lock_irqsave(&devp->pkt_list_lock, flags); + if (!list_empty(&devp->pkt_list)) { + pkt = list_first_entry(&devp->pkt_list, + struct glink_rx_pkt, list); + list_del(&pkt->list); + } + spin_unlock_irqrestore(&devp->pkt_list_lock, flags); + return pkt; +} + /** * glink_pkt_release() - release operation on glink_pkt device * inode: Pointer to the inode structure. @@ -1089,6 +1118,7 @@ int glink_pkt_release(struct inode *inode, struct file *file) int ret = 0; struct glink_pkt_dev *devp = file->private_data; unsigned long flags; + struct glink_rx_pkt *pkt; GLINK_PKT_INFO("%s() on dev id:%d by [%s] ref_cnt[%d]\n", __func__, devp->i, current->comm, devp->ref_cnt); @@ -1097,9 +1127,14 @@ int glink_pkt_release(struct inode *inode, struct file *file) devp->ref_cnt--; if (devp->handle && devp->ref_cnt == 0) { + while ((pkt = pop_rx_pkt(devp))) { + glink_rx_done(devp->handle, pkt->data, false); + kfree(pkt); + } wake_up(&devp->ch_read_wait_queue); wake_up_interruptible(&devp->ch_opened_wait_queue); ret = glink_close(devp->handle); + devp->handle = NULL; if (ret) { GLINK_PKT_ERR("%s: close failed ret[%d]\n", __func__, ret); -- GitLab From 5cfc7c6bb764860465a3f94e7cba7d67b7f9d608 Mon Sep 17 00:00:00 2001 From: tharun kumar Date: Thu, 26 Oct 2017 22:27:37 +0530 Subject: [PATCH 1378/5498] msm: ADSPRPC: Enable fastrpc to communicate with mDSP Enable fastrpc to support fopen calls from mDSP Change-Id: I72c3b992bc4c91d20efa1eae747671ee4e469860 Signed-off-by: Tharun Kumar Merugu --- arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 778645c09403..7194e76b9e9e 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -206,6 +206,7 @@ CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_MSM_SMD_PKT=y +CONFIG_MSM_ADSPRPC=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MSM_V2=y CONFIG_SLIMBUS=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 7fda81c9616e..d53242d4ea21 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -208,6 +208,7 @@ CONFIG_DIAG_CHAR=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MSM_LEGACY=y CONFIG_MSM_SMD_PKT=y +CONFIG_MSM_ADSPRPC=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MSM_V2=y CONFIG_SLIMBUS=y -- GitLab From 9c8878f8742e0cd84a3c61bcd2dd43b61d69e376 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Wed, 6 Sep 2017 15:56:16 +0530 Subject: [PATCH 1379/5498] misc: qpnp-misc: Add support for TWM mode TWM (Traditional watch mode) is a low power mode supported on the BG platform. PMIC is configured differently while entering into TWM mode. Add a sysfs node to - 1. Allow the BG daemon to enable/disable TWM mode. echo 1 > /sys/class/pmic_twm/twm_enable --> Enable TWM mode echo 0 > /sys/class/pmic_twm/twm_enable --> Clear TWM mode 2. Allow the BG daemon to check the TWM_EXIT state. cat /sys/class/pmic_twm/twm_exit Value of "1" indicates exit from TWM state Also, add a notifier to notify other modules of the TWM enable configuration. Change-Id: Ide2f56a80d7b21d3def6a640db71512c20d30015 Signed-off-by: Anirudh Ghayal --- .../devicetree/bindings/misc/qpnp-misc.txt | 7 + drivers/misc/qpnp-misc.c | 147 ++++++++++++++++++ include/linux/qpnp-misc.h | 34 ++++ 3 files changed, 188 insertions(+) diff --git a/Documentation/devicetree/bindings/misc/qpnp-misc.txt b/Documentation/devicetree/bindings/misc/qpnp-misc.txt index 9de6857a2643..a80701a13fc2 100644 --- a/Documentation/devicetree/bindings/misc/qpnp-misc.txt +++ b/Documentation/devicetree/bindings/misc/qpnp-misc.txt @@ -16,6 +16,13 @@ Optional properties: if a non-zero PWM source is specified under "qcom,pwm-sel" property. +- qcom,support-twm-config Enable configuration for TWM mode. + +- qcom,twm-mode The TWM mode which PMIC enters post power-off. + Valid only if 'qcom,support-twm-config' is + defined. If not specified, the default mode + is 3. + Example: qcom,spmi@fc4c0000 { #address-cells = <1>; diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c index 56a9b8ae3f83..ff8a2f6ea0a9 100644 --- a/drivers/misc/qpnp-misc.c +++ b/drivers/misc/qpnp-misc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #define QPNP_MISC_DEV_NAME "qcom,qpnp-misc" @@ -29,8 +30,20 @@ #define PWM_SEL_MAX 0x03 #define GP_DRIVER_EN_BIT BIT(0) +enum twm { + TWM_MODE_1 = 1, + TWM_MODE_2, + TWM_MODE_3, +}; + +enum twm_attrib { + TWM_ENABLE, + TWM_EXIT, +}; + static DEFINE_MUTEX(qpnp_misc_dev_list_mutex); static LIST_HEAD(qpnp_misc_dev_list); +static RAW_NOTIFIER_HEAD(twm_notifier); struct qpnp_misc_version { u8 subtype; @@ -57,9 +70,13 @@ struct qpnp_misc_dev { struct resource *resource; struct spmi_device *spmi; struct qpnp_misc_version version; + struct class twm_class; + u8 twm_mode; u8 pwm_sel; bool enable_gp_driver; + bool support_twm_config; + bool twm_enable; }; static struct of_device_id qpnp_misc_match_table[] = { @@ -207,9 +224,99 @@ int qpnp_misc_irqs_available(struct device *consumer_dev) return __misc_irqs_available(mdev_found); } +#define MISC_SPARE_1 0x50 +#define MISC_SPARE_2 0x51 +#define ENABLE_TWM_MODE 0x80 +#define DISABLE_TWM_MODE 0x0 +#define TWM_EXIT_BIT BIT(0) +static ssize_t twm_enable_store(struct class *c, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct qpnp_misc_dev *mdev = container_of(c, + struct qpnp_misc_dev, twm_class); + u8 val = 0; + ssize_t rc = 0; + + rc = kstrtou8(buf, 10, &val); + if (rc < 0) + return rc; + + mdev->twm_enable = val ? true : false; + + /* Notify the TWM state */ + raw_notifier_call_chain(&twm_notifier, + mdev->twm_enable ? PMIC_TWM_ENABLE : PMIC_TWM_CLEAR, NULL); + + return count; +} + +static ssize_t twm_enable_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct qpnp_misc_dev *mdev = container_of(c, + struct qpnp_misc_dev, twm_class); + + return snprintf(buf, PAGE_SIZE, "%d\n", mdev->twm_enable); +} + +static ssize_t twm_exit_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct qpnp_misc_dev *mdev = container_of(c, + struct qpnp_misc_dev, twm_class); + ssize_t rc = 0; + u8 val = 0; + + rc = qpnp_read_byte(mdev->spmi, + mdev->resource->start + MISC_SPARE_1, &val); + if (rc < 0) { + pr_err("Failed to read TWM enable (misc_spare_1) rc=%d\n", rc); + return rc; + } + + pr_debug("TWM_EXIT (misc_spare_1) register = 0x%02x\n", val); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!(val & TWM_EXIT_BIT)); +} + +static struct class_attribute twm_attributes[] = { + [TWM_ENABLE] = __ATTR(twm_enable, S_IRUGO | S_IWUSR, + twm_enable_show, twm_enable_store), + [TWM_EXIT] = __ATTR(twm_exit, S_IRUGO | S_IWUSR, + twm_exit_show, NULL), + __ATTR_NULL, +}; + +int qpnp_misc_twm_notifier_register(struct notifier_block *nb) +{ + return raw_notifier_chain_register(&twm_notifier, nb); +} +EXPORT_SYMBOL(qpnp_misc_twm_notifier_register); + +int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb) +{ + return raw_notifier_chain_unregister(&twm_notifier, nb); +} +EXPORT_SYMBOL(qpnp_misc_twm_notifier_unregister); + static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev) { u32 val; + int rc; + + if (of_property_read_bool(mdev->dev->of_node, + "qcom,support-twm-config")) { + mdev->support_twm_config = true; + mdev->twm_mode = TWM_MODE_3; + rc = of_property_read_u8(mdev->dev->of_node, "qcom,twm-mode", + &mdev->twm_mode); + if (!rc && (mdev->twm_mode < TWM_MODE_1 || + mdev->twm_mode > TWM_MODE_3)) { + pr_err("Invalid TWM mode %d\n", mdev->twm_mode); + return -EINVAL; + } + } if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) { if (val > PWM_SEL_MAX) { @@ -261,6 +368,18 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev) break; } + if (mdev->support_twm_config) { + mdev->twm_class.name = "pmic_twm", + mdev->twm_class.owner = THIS_MODULE, + mdev->twm_class.class_attrs = twm_attributes; + + rc = class_register(&mdev->twm_class); + if (rc < 0) { + pr_err("Failed to register pmic_twm class rc=%d\n", rc); + return rc; + } + } + return 0; } @@ -283,6 +402,7 @@ static int qpnp_misc_probe(struct spmi_device *spmi) mdev->spmi = spmi; mdev->dev = &(spmi->dev); mdev->resource = resource; + dev_set_drvdata(&spmi->dev, mdev); rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE, &mdev->version.subtype); @@ -320,8 +440,35 @@ static int qpnp_misc_probe(struct spmi_device *spmi) return 0; } +static void qpnp_misc_shutdown(struct spmi_device *spmi) +{ + struct qpnp_misc_dev *mdev = dev_get_drvdata(&spmi->dev); + int rc; + + if (mdev->support_twm_config) { + rc = qpnp_write_byte(mdev->spmi, + mdev->resource->start + MISC_SPARE_2, + mdev->twm_enable ? mdev->twm_mode : 0x0); + if (rc < 0) + pr_err("Failed to write MISC_SPARE_2 (twm_mode) val=%d rc=%d\n", + mdev->twm_enable ? mdev->twm_mode : 0x0, rc); + + rc = qpnp_write_byte(mdev->spmi, + mdev->resource->start + MISC_SPARE_1, + mdev->twm_enable ? ENABLE_TWM_MODE : 0x0); + if (rc < 0) + pr_err("Failed to write MISC_SPARE_1 (twm_state) val=%d rc=%d\n", + mdev->twm_enable ? ENABLE_TWM_MODE : 0x0, rc); + + pr_debug("PMIC configured for TWM-%s MODE=%d\n", + mdev->twm_enable ? "enabled" : "disabled", + mdev->twm_mode); + } +} + static struct spmi_driver qpnp_misc_driver = { .probe = qpnp_misc_probe, + .shutdown = qpnp_misc_shutdown, .driver = { .name = QPNP_MISC_DEV_NAME, .owner = THIS_MODULE, diff --git a/include/linux/qpnp-misc.h b/include/linux/qpnp-misc.h index 7d95bf24a425..eeec8177862b 100644 --- a/include/linux/qpnp-misc.h +++ b/include/linux/qpnp-misc.h @@ -15,7 +15,13 @@ #include +enum twm_state { + PMIC_TWM_CLEAR, + PMIC_TWM_ENABLE, +}; + #ifdef CONFIG_QPNP_MISC + /** * qpnp_misc_irqs_available - check if IRQs are available * @@ -42,6 +48,26 @@ int qpnp_misc_irqs_available(struct device *consumer_dev); */ int qpnp_misc_read_reg(struct device_node *node, u16 addr, u8 *val); + +/** + * qpnp_misc_twm_notifier_register - register to the twm mode notifier + * + * @nb: pointer to the client's notifier handle + * + * This function returns 0 if the client is successfuly added to the + * notifer list. + */ +int qpnp_misc_twm_notifier_register(struct notifier_block *nb); + +/** + * qpnp_misc_twm_notifier_unregister - unregister to the twm mode notifier + * + * @nb: pointer to the client's notifier handle + * + * This function returns 0 if the client is successfuly removed from the + * notifer list. + */ +int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb); #else static inline int qpnp_misc_irqs_available(struct device *consumer_dev) { @@ -52,5 +78,13 @@ static inline int qpnp_misc_read_reg(struct device_node *node, u16 addr, { return 0; } +static inline int qpnp_misc_twm_notifier_register(struct notifier_block *nb) +{ + return 0; +} +static inline int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb) +{ + return 0; +} #endif #endif -- GitLab From 236905aed640dbe45502f5a9dfc5b4c21e2f3ee2 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Wed, 6 Sep 2017 16:04:51 +0530 Subject: [PATCH 1380/5498] platform: qpnp-power-on: Add support for TWM config TWM (traditional watch mode) is a low-power mode configuration on the BG platform. Add a notification callback to be notified of an entry into this mode. Add logic to skip the PS_HOLD reset configuration and initiate the PBS trigger if TWM mode is enabled. The PBS trigger configures the PMIC for TWM entry. Change-Id: I31a48c8d2506a668b18737ec3da827cff27b830d Signed-off-by: Anirudh Ghayal --- .../bindings/platform/msm/qpnp-power-on.txt | 4 ++ drivers/platform/msm/qpnp-power-on.c | 65 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt index 73349250110d..562b9f5f150b 100644 --- a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt +++ b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt @@ -77,6 +77,10 @@ Optional properties: but this applies for the system shutdown case. - qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic on the KPDPWR_N rising edge. +- qcom,support-twm-config Boolean property to allow the PON module to be + configured to support TWM modes. +- qcom,pbs-client Phandle of the PBS client node. Should be + defined if 'qcom,support-twm-config' is present. All the below properties are in the sub-node section (properties of the child diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c index ac58ba30990b..c4cd79469108 100644 --- a/drivers/platform/msm/qpnp-power-on.c +++ b/drivers/platform/msm/qpnp-power-on.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #define CREATE_MASK(NUM_BITS, POS) \ ((unsigned char) (((1 << (NUM_BITS)) - 1) << (POS))) @@ -207,6 +209,7 @@ struct qpnp_pon { struct list_head list; struct delayed_work bark_work; struct dentry *debugfs; + struct device_node *pbs_dev_node; int pon_trigger_reason; int pon_power_off_reason; int num_pon_reg; @@ -222,10 +225,13 @@ struct qpnp_pon { u8 pon_ver; u8 warm_reset_reason1; u8 warm_reset_reason2; + u8 twm_state; bool is_spon; bool store_hard_reset_reason; bool kpdpwr_dbc_enable; + bool support_twm_config; ktime_t kpdpwr_last_release_time; + struct notifier_block pon_nb; }; static struct qpnp_pon *sys_reset_dev; @@ -490,12 +496,26 @@ static ssize_t qpnp_pon_dbc_store(struct device *dev, static DEVICE_ATTR(debounce_us, 0664, qpnp_pon_dbc_show, qpnp_pon_dbc_store); +#define PON_TWM_ENTRY_PBS_BIT BIT(0) static int qpnp_pon_reset_config(struct qpnp_pon *pon, enum pon_power_off_type type) { int rc; u16 rst_en_reg; + /* Ignore the PS_HOLD reset config if TWM ENTRY is enabled */ + if (pon->support_twm_config && pon->twm_state == PMIC_TWM_ENABLE) { + rc = qpnp_pbs_trigger_event(pon->pbs_dev_node, + PON_TWM_ENTRY_PBS_BIT); + if (rc < 0) { + pr_err("Unable to trigger PBS trigger for TWM entry rc=%d\n", + rc); + return rc; + } + pr_crit("PMIC configured for TWM entry\n"); + return 0; + } + if (pon->pon_ver == QPNP_PON_GEN1_V1) rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL(pon); else @@ -1984,6 +2004,35 @@ static int read_gen2_pon_off_reason(struct qpnp_pon *pon, u16 *reason, return 0; } +static int pon_twm_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct qpnp_pon *pon = container_of(nb, struct qpnp_pon, pon_nb); + + if (action != PMIC_TWM_CLEAR && + action != PMIC_TWM_ENABLE) { + pr_debug("Unsupported option %lu\n", action); + return NOTIFY_OK; + } + + pon->twm_state = (u8)action; + pr_debug("TWM state = %d\n", pon->twm_state); + + return NOTIFY_OK; +} + +static int pon_register_twm_notifier(struct qpnp_pon *pon) +{ + int rc; + + pon->pon_nb.notifier_call = pon_twm_notifier_cb; + rc = qpnp_misc_twm_notifier_register(&pon->pon_nb); + if (rc < 0) + pr_err("Failed to register pon_twm_notifier_cb rc=%d\n", rc); + + return rc; +} + static int qpnp_pon_probe(struct spmi_device *spmi) { struct qpnp_pon *pon; @@ -2241,6 +2290,22 @@ static int qpnp_pon_probe(struct spmi_device *spmi) return rc; } + if (of_property_read_bool(pon->spmi->dev.of_node, + "qcom,support-twm-config")) { + pon->support_twm_config = true; + rc = pon_register_twm_notifier(pon); + if (rc < 0) { + pr_err("Failed to register TWM notifier rc=%d\n", rc); + return rc; + } + pon->pbs_dev_node = of_parse_phandle(pon->spmi->dev.of_node, + "qcom,pbs-client", 0); + if (!pon->pbs_dev_node) { + pr_err("Missing qcom,pbs-client property\n"); + return -EINVAL; + } + } + rc = of_property_read_u32(pon->spmi->dev.of_node, "qcom,pon-dbc-delay", &delay); if (rc) { -- GitLab From 8a02a95ba46042c3dac233f9e334472f2c496a23 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Devarakonda Date: Thu, 17 Aug 2017 16:16:42 +0530 Subject: [PATCH 1381/5498] msm: mdss: Enable clocks for WFD setup The WFD setup path has few register writes. If the MDP clocks aren't switched on, the register access might cause SMMU faults. Turn on the clocks, before calling WFD setup. Change-Id: Icc865178f43dc6068b6b4b201b8f9eada2570598 Signed-off-by: Krishna Chaitanya Devarakonda --- drivers/video/msm/mdss/mdss_mdp_layer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/video/msm/mdss/mdss_mdp_layer.c b/drivers/video/msm/mdss/mdss_mdp_layer.c index 3ff446cd1d51..0be600fd1486 100644 --- a/drivers/video/msm/mdss/mdss_mdp_layer.c +++ b/drivers/video/msm/mdss/mdss_mdp_layer.c @@ -2289,11 +2289,14 @@ int mdss_mdp_layer_atomic_validate_wfd(struct msm_fb_data_type *mfd, goto validate_failed; } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); rc = mdss_mdp_wfd_setup(wfd, output_layer); if (rc) { pr_err("fail to prepare wfd = %d\n", rc); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); goto validate_failed; } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); rc = mdss_mdp_layer_atomic_validate(mfd, file, commit); if (rc) { -- GitLab From 94dd49b5f934512a66bd119e279a67e1ee284af6 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Wed, 11 Oct 2017 17:35:25 +0530 Subject: [PATCH 1382/5498] ARM: dts: msm: Change bg-spi clock frequency for msm8909w BG Change the bg-spi clock frequency to match changed BG clock frequency. Change-Id: Id517199dfb5925d0860bcb7a4bfb911b828b8d47 Signed-off-by: Arjun Singh --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 2 +- arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 52e3e49952a5..0de420edde78 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -131,7 +131,7 @@ qcom,bg-spi { compatible = "qcom,bg-spi"; reg = <0>; - spi-max-frequency = <19200000>; + spi-max-frequency = <16000000>; interrupt-parent = <&msm_gpio>; qcom,irq-gpio = <&msm_gpio 110 1>; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 36644ba7d92c..a6caee9a120e 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -133,7 +133,7 @@ qcom,bg-spi { compatible = "qcom,bg-spi"; reg = <0>; - spi-max-frequency = <19200000>; + spi-max-frequency = <16000000>; interrupt-parent = <&msm_gpio>; qcom,irq-gpio = <&msm_gpio 110 1>; }; -- GitLab From 7499f135bf9b7908d5b93ef880360e7017d5b914 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Wed, 10 May 2017 15:43:57 +0530 Subject: [PATCH 1383/5498] drivers: soc: qcom: spm: Add support for 16bit vlevel spm regulator driver can send 16bit voltage levels to be sent to pmic arbiter for certain PMICs. SAW VCTL can have max pmic data width as 8bit. Update spm driver to send lower 8bit of voltage level first and then send upper 8bit to PVC port. Voltage change happens when upper 8bit is written to PMIC registers. Change-Id: Iff1d1976adfe75058f6c64d4212c78d88422278c Signed-off-by: Maulik Shah Signed-off-by: Srinivas Rao L --- .../devicetree/bindings/arm/msm/spm-v2.txt | 4 + drivers/soc/qcom/spm.c | 110 +++++++++++++----- drivers/soc/qcom/spm_devices.c | 4 + drivers/soc/qcom/spm_driver.h | 4 +- 4 files changed, 90 insertions(+), 32 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt index e9a2ed3feb26..89c5e0b77ccb 100644 --- a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt +++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt @@ -45,6 +45,8 @@ Optional properties for only Non-PSCI targets index to send the PMIC data to - qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing voltage +- qcom,vctl-port-ub: The PVC (PMIC Virtual Channel) port used for changing + voltage - qcom,phase-port: The PVC port used for changing the number of phases - qcom,pfm-port: The PVC port used for enabling PWM/PFM modes - qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control. @@ -107,6 +109,8 @@ Optional properties for only PSCI targets: between AVS controller requests - qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing voltage +- qcom,vctl-port-ub: The PVC (PMIC Virtual Channel) port used for changing + voltage - qcom,phase-port: The PVC port used for changing the number of phases - qcom,pfm-port: The PVC port used for enabling PWM/PFM modes - qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 705666e7ab70..23d19163e6e1 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -190,18 +190,12 @@ static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev) } static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev, - uint32_t vlevel) + uint32_t vlevel, uint32_t vctl_port) { unsigned int pmic_data = 0; - /** - * VCTL_PORT has to be 0, for PMIC_STS register to be updated. - * Ensure that vctl_port is always set to 0. - */ - WARN_ON(dev->vctl_port); - pmic_data |= vlevel; - pmic_data |= (dev->vctl_port & 0x7) << 16; + pmic_data |= (vctl_port & 0x7) << 16; dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0x700FF; dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= pmic_data; @@ -506,10 +500,45 @@ static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev, unsigned int vlevel) { } #endif +static inline int msm_spm_drv_validate_data(struct msm_spm_driver_data *dev, + unsigned int vlevel, int vctl_port) +{ + int timeout_us = dev->vctl_timeout_us; + uint32_t new_level; + + /* Confirm the voltage we set was what hardware sent and + * FSM is idle */ + do { + udelay(1); + new_level = msm_spm_drv_get_sts_curr_pmic_data(dev); + + /** + * VCTL_PORT has to be 0, for vlevel to be updated. + * If port is not 0, check for PMIC_STATE only. + */ + + if (((new_level & 0x30000) == MSM_SPM_PMIC_STATE_IDLE) && + (vctl_port || ((new_level & 0xFF) == vlevel))) + break; + } while (--timeout_us); + + if (!timeout_us) { + pr_err("Wrong level %#x\n", new_level); + return -EIO; + } + + if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) + pr_info("%s: done, remaining timeout %u us\n", + __func__, timeout_us); + + return 0; +} + int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel) { - uint32_t timeout_us, new_level; + uint32_t vlevel_set = vlevel; bool avs_enabled; + int ret = 0; if (!dev) return -EINVAL; @@ -525,45 +554,63 @@ int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel) if (avs_enabled) msm_spm_drv_disable_avs(dev); + if (dev->vctl_port_ub >= 0) { + /** + * VCTL can send 8bit voltage level at once. + * Send lower 8bit first, vlevel change happens + * when upper 8bit is sent. + */ + vlevel = vlevel_set & 0xFF; + } + /* Kick the state machine back to idle */ dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1; msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST); - msm_spm_drv_set_vctl2(dev, vlevel); + msm_spm_drv_set_vctl2(dev, vlevel, dev->vctl_port); - timeout_us = dev->vctl_timeout_us; - /* Confirm the voltage we set was what hardware sent */ - do { - udelay(1); - new_level = msm_spm_drv_get_sts_curr_pmic_data(dev); - /* FSM is idle */ - if (((new_level & 0x30000) == 0) && - ((new_level & 0xFF) == vlevel)) - break; - } while (--timeout_us); - if (!timeout_us) { - pr_info("Wrong level %#x\n", new_level); + ret = msm_spm_drv_validate_data(dev, vlevel, dev->vctl_port); + if (ret) goto set_vdd_bail; - } - if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) - pr_info("%s: done, remaining timeout %u us\n", - __func__, timeout_us); + if (dev->vctl_port_ub >= 0) { + /* Send upper 8bit of voltage level */ + vlevel = (vlevel_set >> 8) & 0xFF; + + /* Kick the state machine back to idle */ + dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1; + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST); + + /* + * Steps for sending for vctl port other than '0' + * Write VCTL register with pmic data and address index + * Perform system barrier + * Wait for 1us + * Read PMIC_STS register to make sure operation is complete + */ + msm_spm_drv_set_vctl2(dev, vlevel, dev->vctl_port_ub); + + mb(); /* To make sure data is sent before checking status */ + + ret = msm_spm_drv_validate_data(dev, vlevel, dev->vctl_port_ub); + if (ret) + goto set_vdd_bail; + } /* Set AVS min/max */ if (avs_enabled) { - msm_spm_drv_set_avs_vlevel(dev, vlevel); + msm_spm_drv_set_avs_vlevel(dev, vlevel_set); msm_spm_drv_enable_avs(dev); } - return 0; + return ret; set_vdd_bail: if (avs_enabled) msm_spm_drv_enable_avs(dev); - pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n", - __func__, vlevel, timeout_us, new_level); + pr_err("%s: failed %#x vlevel setting in timeout %uus\n", + __func__, vlevel_set, dev->vctl_timeout_us); return -EIO; } @@ -692,6 +739,7 @@ int msm_spm_drv_init(struct msm_spm_driver_data *dev, BUG_ON(!dev || !data); dev->vctl_port = data->vctl_port; + dev->vctl_port_ub = data->vctl_port_ub; dev->phase_port = data->phase_port; dev->pfm_port = data->pfm_port; dev->reg_base_addr = data->reg_base_addr; diff --git a/drivers/soc/qcom/spm_devices.c b/drivers/soc/qcom/spm_devices.c index 2ad4d09537f0..e3e9a1ba8523 100644 --- a/drivers/soc/qcom/spm_devices.c +++ b/drivers/soc/qcom/spm_devices.c @@ -798,12 +798,16 @@ static int msm_spm_dev_probe(struct platform_device *pdev) } spm_data.vctl_port = -1; + spm_data.vctl_port_ub = -1; spm_data.phase_port = -1; spm_data.pfm_port = -1; key = "qcom,vctl-port"; of_property_read_u32(node, key, &spm_data.vctl_port); + key = "qcom,vctl-port-ub"; + of_property_read_u32(node, key, &spm_data.vctl_port_ub); + key = "qcom,phase-port"; of_property_read_u32(node, key, &spm_data.phase_port); diff --git a/drivers/soc/qcom/spm_driver.h b/drivers/soc/qcom/spm_driver.h index 7e10f11a3bc9..376cd0233727 100644 --- a/drivers/soc/qcom/spm_driver.h +++ b/drivers/soc/qcom/spm_driver.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -62,6 +62,7 @@ struct msm_spm_platform_data { uint32_t ver_reg; uint32_t vctl_port; + int vctl_port_ub; uint32_t phase_port; uint32_t pfm_port; @@ -84,6 +85,7 @@ struct msm_spm_driver_data { uint32_t minor; uint32_t ver_reg; uint32_t vctl_port; + int vctl_port_ub; uint32_t phase_port; uint32_t pfm_port; void __iomem *reg_base_addr; -- GitLab From 98296258553fd46f5b90162707a964a4fc5d4e86 Mon Sep 17 00:00:00 2001 From: Srinivas Rao L Date: Tue, 24 Oct 2017 17:30:39 +0530 Subject: [PATCH 1384/5498] ARM: dts: msm: Support 16bit vlevel setting from SAW for 8909W Update pvc port configuration for sending upper byte of 16bit vlevel and remove bypass spm entry from PM660 regulator node for 8909W + PM660. Change-Id: Id176c4b96e2df1c8a0f36aab32d908c769e3c670 Signed-off-by: Srinivas Rao L --- arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi | 2 +- arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi b/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi index c1980bb99ac4..6e27fb59e770 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pm660-pm.dtsi @@ -159,7 +159,7 @@ qcom,cpu-vctl-list = <&CPU0 &CPU1 &CPU2 &CPU3>; qcom,vctl-timeout-us = <500>; qcom,vctl-port = <0x0>; - qcom,phase-port = <0x1>; + qcom,vctl-port-ub = <0x1>; qcom,pfm-port = <0x2>; qcom,mode0 { qcom,label = "qcom,saw2-spm-cmd-ret"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi index 449eda549b8d..c30f40e7e55a 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi @@ -326,9 +326,6 @@ reg = <0x1400 0x100>; regulator-min-microvolt = <1052000>; regulator-max-microvolt = <1352000>; - - /* TODO: remove after SAW support is enabled */ - qcom,bypass-spm; }; }; }; -- GitLab From 55dd9918550a99e42dc32498963160888cf41430 Mon Sep 17 00:00:00 2001 From: Prateek Sood Date: Sat, 14 Oct 2017 17:31:13 +0530 Subject: [PATCH 1385/5498] locking/osq_lock: Fix osq_lock queue corruption Fix ordering of link creation between node->prev and prev->next in osq_lock(). A case in which the status of optimistic spin queue is CPU6->CPU2 in which CPU6 has acquired the lock. tail v ,-. <- ,-. |6| |2| `-' -> `-' At this point if CPU0 comes in to acquire osq_lock, it will update the tail count. CPU2 CPU0 ---------------------------------- tail v ,-. <- ,-. ,-. |6| |2| |0| `-' -> `-' `-' After tail count update if CPU2 starts to unqueue itself from optimistic spin queue, it will find an updated tail count with CPU0 and update CPU2 node->next to NULL in osq_wait_next(). unqueue-A tail v ,-. <- ,-. ,-. |6| |2| |0| `-' `-' `-' unqueue-B ->tail != curr && !node->next If reordering of following stores happen then prev->next where prev being CPU2 would be updated to point to CPU0 node: tail v ,-. <- ,-. ,-. |6| |2| |0| `-' `-' -> `-' osq_wait_next() node->next <- 0 xchg(node->next, NULL) tail v ,-. <- ,-. ,-. |6| |2| |0| `-' `-' `-' unqueue-C At this point if next instruction WRITE_ONCE(next->prev, prev); in CPU2 path is committed before the update of CPU0 node->prev = prev then CPU0 node->prev will point to CPU6 node. tail v----------. v ,-. <- ,-. ,-. |6| |2| |0| `-' `-' `-' `----------^ At this point if CPU0 path's node->prev = prev is committed resulting in change of CPU0 prev back to CPU2 node. CPU2 node->next is NULL currently, tail v ,-. <- ,-. <- ,-. |6| |2| |0| `-' `-' `-' `----------^ so if CPU0 gets into unqueue path of osq_lock it will keep spinning in infinite loop as condition prev->next == node will never be true. Change-Id: I5f473433ae2e10308fa7b27680bf98530fe65b0d Signed-off-by: Prateek Sood [ Added pictures, rewrote comments. ] Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: sramana@codeaurora.org Link: http://lkml.kernel.org/r/1500040076-27626-1-git-send-email-prsood@codeaurora.org Signed-off-by: Ingo Molnar Git-commit: 50972fe78f24f1cd0b9d7bbf1f87d2be9e4f412e Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git --- kernel/locking/mcs_spinlock.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c index 620759f24da5..9fd645312376 100644 --- a/kernel/locking/mcs_spinlock.c +++ b/kernel/locking/mcs_spinlock.c @@ -102,6 +102,19 @@ bool osq_lock(struct optimistic_spin_queue *lock) prev = decode_cpu(old); node->prev = prev; + + /* + * osq_lock() unqueue + * + * node->prev = prev osq_wait_next() + * WMB MB + * prev->next = node next->prev = prev // unqueue-C + * + * Here 'node->prev' and 'next->prev' are the same variable and we need + * to ensure these stores happen in-order to avoid corrupting the list. + */ + smp_wmb(); + ACCESS_ONCE(prev->next) = node; /* -- GitLab From 7e9bcbf510f882e6af15758246745eda48864e43 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Tue, 24 Oct 2017 09:27:57 +0530 Subject: [PATCH 1386/5498] ARM: dts: msm: Enable TWM mode config for PM660 Add the MISC, PBS and PON module configuration for TWM mode on PM660 BG platform. Change-Id: Iefca6d55ca4297a1bb64e58874d54343a1e41ecf Signed-off-by: Anirudh Ghayal --- arch/arm/boot/dts/qcom/msm-pm660.dtsi | 6 +++--- arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi index 95c60691d687..899554edd954 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi @@ -30,7 +30,7 @@ reg = <0x900 0x100>; }; - qcom,power-on@800 { + pm660_pon: qcom,power-on@800 { compatible = "qcom,qpnp-power-on"; reg = <0x800 0x100>; interrupts = <0x0 0x8 0x0>, @@ -568,9 +568,9 @@ }; }; - pm660_pbs: qcom,pbs@7300 { + pm660_pbs: qcom,pbs@7400 { compatible = "qcom,qpnp-pbs"; - reg = <0x7300 0x100>; + reg = <0x7400 0x100>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index 9db086835a04..10744431ae61 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -231,3 +231,16 @@ qcom,out-strength = <2>; }; }; + +&pm660_misc { + qcom,support-twm-config; +}; + +&pm660_pbs { + status = "okay"; +}; + +&pm660_pon { + qcom,support-twm-config; + qcom,pbs-client = <&pm660_pbs>; +}; -- GitLab From 1dd5d61f399a7a40b2d28bee671d384a85678c72 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Mon, 23 Oct 2017 20:28:45 +0530 Subject: [PATCH 1387/5498] soc: qcom: qpnp-pbs: Fix the retry variable check logic In case of a failure the 'retries' variable becomes negative thus bypassing the failure check. Fix this. While at it, also fix the return from the error path. Change-Id: I40ad767f4faf68b1b83c8b2cd1675af83318d96b Signed-off-by: Anirudh Ghayal --- drivers/soc/qcom/qpnp-pbs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/qpnp-pbs.c b/drivers/soc/qcom/qpnp-pbs.c index 033ee6381017..cbdadb80cbcd 100644 --- a/drivers/soc/qcom/qpnp-pbs.c +++ b/drivers/soc/qcom/qpnp-pbs.c @@ -115,7 +115,7 @@ static int qpnp_pbs_wait_for_ack(struct qpnp_pbs *pbs, u8 bit_pos) u16 retries = 2000; u8 val; - while (retries--) { + while (retries) { rc = qpnp_pbs_read(pbs, pbs->base + PBS_CLIENT_SCRATCH2, &val, 1); if (rc < 0) { @@ -144,6 +144,7 @@ static int qpnp_pbs_wait_for_ack(struct qpnp_pbs *pbs, u8 bit_pos) } usleep_range(QPNP_PBS_RETRY_SLEEP, QPNP_PBS_RETRY_SLEEP + 100); + retries--; } if (!retries) { @@ -282,11 +283,8 @@ int qpnp_pbs_trigger_event(struct device_node *dev_node, u8 bitmap) error: /* Clear all the requested bitmap */ - rc = qpnp_pbs_masked_write(pbs, pbs->base + PBS_CLIENT_SCRATCH1, + qpnp_pbs_masked_write(pbs, pbs->base + PBS_CLIENT_SCRATCH1, bitmap, 0); - if (rc < 0) - pr_err("Failed to clear %x reg bit rc=%d\n", - PBS_CLIENT_SCRATCH1, rc); out: mutex_unlock(&pbs->pbs_lock); -- GitLab From b2dba212bf9a13b90048c58e37e3aa63f5ae18fb Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 4 Oct 2016 15:57:08 +0530 Subject: [PATCH 1388/5498] ASoC: msm: qdsp6v2: add support for latest version of media format command Add support for ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 command. This command adds support to playback/record 32 bit data in 32 bit word and also provides a way to inform DSP about the endianness of the data. CRs-Fixed: 2129947 Change-Id: I3b013bedde8ccfa97a02e255e237df0cf2de13b8 Signed-off-by: Ashish Jain --- include/sound/apr_audio-v2.h | 135 ++++++++++ include/sound/q6asm-v2.h | 59 +++++ sound/soc/msm/qdsp6v2/q6asm.c | 462 ++++++++++++++++++++++++++++++++-- 3 files changed, 636 insertions(+), 20 deletions(-) diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 6f88d5348f2f..aaacfcc46739 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -3391,6 +3391,8 @@ struct asm_softvolume_params { #define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 0x00010DDC +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 0x0001320C + #define ASM_MEDIA_FMT_EVRCB_FS 0x00010BEF #define ASM_MEDIA_FMT_EVRCWB_FS 0x00010BF0 @@ -3493,6 +3495,56 @@ struct asm_multi_channel_pcm_fmt_blk_v3 { */ } __packed; +struct asm_multi_channel_pcm_fmt_blk_v4 { + uint16_t num_channels; +/* + * Number of channels + * Supported values: 1 to 8 + */ + + uint16_t bits_per_sample; +/* + * Number of bits per sample per channel + * Supported values: 16, 24, 32 + */ + + uint32_t sample_rate; +/* + * Number of samples per second + * Supported values: 2000 to 48000, 96000,192000 Hz + */ + + uint16_t is_signed; +/* Flag that indicates that PCM samples are signed (1) */ + + uint16_t sample_word_size; +/* + * Size in bits of the word that holds a sample of a channel. + * Supported values: 12,24,32 + */ + + uint8_t channel_mapping[8]; +/* + * Each element, i, in the array describes channel i inside the buffer where + * 0 <= i < num_channels. Unused channels are set to 0. + */ + uint16_t endianness; +/* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; +/* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + * 31 - for 24b unpacked or 32bit + */ +} __packed; + /* * Payload of the multichannel PCM configuration parameters in * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format. @@ -3503,6 +3555,16 @@ struct asm_multi_channel_pcm_fmt_blk_param_v3 { struct asm_multi_channel_pcm_fmt_blk_v3 param; } __packed; +/* + * Payload of the multichannel PCM configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format. + */ +struct asm_multi_channel_pcm_fmt_blk_param_v4 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + struct asm_multi_channel_pcm_fmt_blk_v4 param; +} __packed; + struct asm_stream_cmd_set_encdec_param { u32 param_id; /* ID of the parameter. */ @@ -3538,6 +3600,79 @@ struct asm_dec_ddp_endp_param_v2 { int endp_param_value; } __packed; +/* + * Payload of the multichannel PCM encoder configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format. + */ + +struct asm_multi_channel_pcm_enc_cfg_v4 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; + /* + * Number of PCM channels. + * @values + * - 0 -- Native mode + * - 1 -- 8 channels + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + */ + uint16_t bits_per_sample; + /* + * Number of bits per sample per channel. + * @values 16, 24 + */ + uint32_t sample_rate; + /* + * Number of samples per second. + * @values 0, 8000 to 48000 Hz + * A value of 0 indicates the native sampling rate. Encoding is + * performed at the input sampling rate. + */ + uint16_t is_signed; + /* + * Flag that indicates the PCM samples are signed (1). Currently, only + * signed PCM samples are supported. + */ + uint16_t sample_word_size; + /* + * The size in bits of the word that holds a sample of a channel. + * @values 16, 24, 32 + * 16-bit samples are always placed in 16-bit words: + * sample_word_size = 1. + * 24-bit samples can be placed in 32-bit words or in consecutive + * 24-bit words. + * - If sample_word_size = 32, 24-bit samples are placed in the + * most significant 24 bits of a 32-bit word. + * - If sample_word_size = 24, 24-bit samples are placed in + * 24-bit words. @tablebulletend + */ + uint8_t channel_mapping[8]; + /* + * Channel mapping array expected at the encoder output. + * Channel[i] mapping describes channel i inside the buffer, where + * 0 @le i < num_channels. All valid used channels must be present at + * the beginning of the array. + * If Native mode is set for the channels, this field is ignored. + * @values See Section @xref{dox:PcmChannelDefs} + */ + uint16_t endianness; + /* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; + /* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + * 31 - for 24b unpacked or 32bit + */ +} __packed; /* * Payload of the multichannel PCM encoder configuration parameters in diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 7771f2468eee..4a68a9075dd8 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -103,6 +103,24 @@ #define ASM_SHIFT_GAPLESS_MODE_FLAG 31 #define ASM_SHIFT_LAST_BUFFER_FLAG 30 +#define ASM_LITTLE_ENDIAN 0 +#define ASM_BIG_ENDIAN 1 + +/* PCM_MEDIA_FORMAT_Version */ +enum { + PCM_MEDIA_FORMAT_V2 = 0, + PCM_MEDIA_FORMAT_V3, + PCM_MEDIA_FORMAT_V4, +}; + +/* PCM format modes in DSP */ +enum { + DEFAULT_QF = 0, + Q15 = 15, + Q23 = 23, + Q31 = 31, +}; + /* payload structure bytes */ #define READDONE_IDX_STATUS 0 #define READDONE_IDX_BUFADD_LSW 1 @@ -270,6 +288,9 @@ int q6asm_open_shared_io(struct audio_client *ac, int q6asm_open_write_v3(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample); +int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, int32_t stream_id, bool is_gapless_mode); @@ -278,6 +299,10 @@ int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, int32_t stream_id, bool is_gapless_mode); +int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format, uint32_t passthrough_flag); @@ -386,6 +411,13 @@ int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, bool use_back_flavor, u8 *channel_map, uint16_t sample_word_size); +int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode); + int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, uint32_t rate, uint32_t channels, uint16_t bits_per_sample); @@ -395,6 +427,13 @@ int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac, uint16_t bits_per_sample, uint16_t sample_word_size); +int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + int q6asm_set_encdec_chan_map(struct audio_client *ac, uint32_t num_channels); @@ -444,6 +483,17 @@ int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac, char *channel_map, uint16_t sample_word_size); +int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels, bool use_default_chmap, char *channel_map); @@ -461,6 +511,15 @@ int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, uint16_t bits_per_sample, uint16_t sample_word_size); +int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + int q6asm_media_format_block_aac(struct audio_client *ac, struct asm_aac_cfg *cfg); diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index fdf8c6fda03a..013dded0fa3a 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -129,6 +129,24 @@ static int out_cold_index; static char *out_buffer; static char *in_buffer; +static inline uint32_t q6asm_get_pcm_format_id(uint32_t media_format_block_ver) +{ + uint32_t pcm_format_id; + + switch (media_format_block_ver) { + case PCM_MEDIA_FORMAT_V4: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4; + break; + case PCM_MEDIA_FORMAT_V3: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + case PCM_MEDIA_FORMAT_V2: + default: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + } + return pcm_format_id; +} static int audio_output_latency_dbgfs_open(struct inode *inode, struct file *file) @@ -2253,7 +2271,8 @@ static void q6asm_add_mmaphdr(struct audio_client *ac, struct apr_hdr *hdr, static int __q6asm_open_read(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, - bool use_v3_format, bool ts_mode) + uint32_t pcm_format_block_ver, + bool ts_mode) { int rc = 0x00; struct asm_stream_cmd_open_read_v3 open; @@ -2293,12 +2312,9 @@ static int __q6asm_open_read(struct audio_client *ac, switch (format) { case FORMAT_LINEAR_PCM: open.mode_flags |= 0x00; + open.enc_cfg_id = q6asm_get_pcm_format_id(pcm_format_block_ver); if (ts_mode) open.mode_flags |= ABSOLUTE_TIMESTAMP_ENABLE; - if (use_v3_format) - open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; - else - open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; break; case FORMAT_MPEG4_AAC: open.mode_flags |= BUFFER_META_ENABLE; @@ -2369,14 +2385,16 @@ int q6asm_open_read(struct audio_client *ac, uint32_t format) { return __q6asm_open_read(ac, format, 16, - false /*use_v3_format*/, false/*ts_mode*/); + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/, + false/*ts_mode*/); } int q6asm_open_read_v2(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { return __q6asm_open_read(ac, format, bits_per_sample, - false /*use_v3_format*/, false/*ts_mode*/); + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/, + false/*ts_mode*/); } /* @@ -2390,7 +2408,8 @@ int q6asm_open_read_v3(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { return __q6asm_open_read(ac, format, bits_per_sample, - true /*use_v3_format*/, false/*ts_mode*/); + PCM_MEDIA_FORMAT_V3/*media fmt block ver*/, + false/*ts_mode*/); } EXPORT_SYMBOL(q6asm_open_read_v3); @@ -2405,7 +2424,8 @@ int q6asm_open_read_v4(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { return __q6asm_open_read(ac, format, bits_per_sample, - true /*use_v3_format*/, true/*ts_mode*/); + PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/, + true/*ts_mode*/); } EXPORT_SYMBOL(q6asm_open_read_v4); @@ -2496,7 +2516,8 @@ fail_cmd: static int __q6asm_open_write(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, uint32_t stream_id, - bool is_gapless_mode, bool use_v3_format) + bool is_gapless_mode, + uint32_t pcm_format_block_ver) { int rc = 0x00; struct asm_stream_cmd_open_write_v3 open; @@ -2559,11 +2580,7 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, } switch (format) { case FORMAT_LINEAR_PCM: - if (use_v3_format) - open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; - else - open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; - + open.dec_fmt_id = q6asm_get_pcm_format_id(pcm_format_block_ver); break; case FORMAT_MPEG4_AAC: open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; @@ -2642,7 +2659,7 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format) { return __q6asm_open_write(ac, format, 16, ac->stream_id, false /*gapless*/, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); } int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, @@ -2650,7 +2667,7 @@ int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, { return __q6asm_open_write(ac, format, bits_per_sample, ac->stream_id, false /*gapless*/, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); } /* @@ -2665,17 +2682,33 @@ int q6asm_open_write_v3(struct audio_client *ac, uint32_t format, { return __q6asm_open_write(ac, format, bits_per_sample, ac->stream_id, false /*gapless*/, - true /*use_v3_format*/); + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); } EXPORT_SYMBOL(q6asm_open_write_v3); +/* + * q6asm_open_write_v4 - Opens audio playback session + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write_v4); + int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, int32_t stream_id, bool is_gapless_mode) { return __q6asm_open_write(ac, format, bits_per_sample, stream_id, is_gapless_mode, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); } /* @@ -2693,10 +2726,29 @@ int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format, { return __q6asm_open_write(ac, format, bits_per_sample, stream_id, is_gapless_mode, - true /*use_v3_format*/); + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); } EXPORT_SYMBOL(q6asm_stream_open_write_v3); +/* + * q6asm_stream_open_write_v4 - Creates audio stream for playback + * + * @ac: Client session handle + * @format: asm playback format + * @bits_per_sample: bit width of requested stream + * @stream_id: stream id of stream to be associated with this session + * @is_gapless_mode: true if gapless mode needs to be enabled + */ +int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_stream_open_write_v4); + static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, uint32_t wr_format, bool is_meta_data_mode, uint32_t bits_per_sample, @@ -3619,6 +3671,108 @@ fail_cmd: return rc; } +/* + * q6asm_enc_cfg_blk_pcm_v4 - sends encoder configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @use_back_flavor: to configure back left and right channel + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_enc_cfg_v4 enc_cfg; + struct asm_enc_cfg_blk_param_v2 enc_fg_blk; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&enc_cfg, 0, sizeof(enc_cfg)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(enc_fg_blk); + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + enc_cfg.sample_word_size = sample_word_size; + enc_cfg.endianness = endianness; + enc_cfg.mode = mode; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Command open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v4); + /* * q6asm_enc_cfg_blk_pcm_v3 - sends encoder configuration parameters * @@ -3795,6 +3949,18 @@ fail_cmd: return rc; } +static int __q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, true, false, NULL, + sample_word_size, endianness, mode); +} + static int __q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, uint32_t rate, uint32_t channels, uint16_t bits_per_sample, @@ -3844,6 +4010,31 @@ int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac, } EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v3); +/* + * q6asm_enc_cfg_blk_pcm_format_support_v4 - sends encoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, sample_word_size, + endianness, mode); +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4); + int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac, uint32_t rate, uint32_t channels) { @@ -4486,6 +4677,91 @@ fail_cmd: return rc; } +static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) | + (stream_id & 0xFF); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + int q6asm_media_format_block_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels) { @@ -4553,6 +4829,47 @@ int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac, } EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v3); +/* + * q6asm_media_format_block_pcm_format_support_v4- sends pcm decoder + * configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @stream_id: stream id of stream to be associated with this session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm_v4(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map, + sample_word_size, endianness, + mode); + +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v4); + + static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels, bool use_default_chmap, char *channel_map, @@ -4686,6 +5003,78 @@ fail_cmd: return rc; } +static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels, @@ -4733,6 +5122,39 @@ int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, } EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v3); +/* + * q6asm_media_format_block_multi_ch_pcm_v4 - sends pcm decoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_media_format_block_multi_ch_pcm_v4(ac, rate, channels, + use_default_chmap, + channel_map, + bits_per_sample, + sample_word_size, + endianness, + mode); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v4); + static int __q6asm_media_format_block_multi_aac(struct audio_client *ac, struct asm_aac_cfg *cfg, int stream_id) { -- GitLab From 18ff677c5eab12e9bfc6c1891a9c5b3618be20e6 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Mon, 3 Oct 2016 22:16:02 +0530 Subject: [PATCH 1389/5498] ASoc: msm: Enable support for 32 bit format and 384Khz Add support for 32 bit data format (SNDRV_PCM_FORMAT_S32_LE) and sampling rate of 384Khz. Also update platform drivers to use latest version of pcm media format command ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4. CRs-Fixed: 2129947 Change-Id: I6b7ea860a398a7e4dd5f7e23ac3906ff0c6f2b3e Signed-off-by: Ashish Jain --- sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 14 +++++--- sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c | 41 +++++++++++++++------- sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h | 4 +-- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 838cc2a66e93..77af86ee0a5d 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -822,6 +822,10 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, } switch (prtd->codec_param.codec.format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + sample_word_size = 32; + break; case SNDRV_PCM_FORMAT_S24_LE: bit_width = 24; sample_word_size = 32; @@ -836,14 +840,16 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, sample_word_size = 16; break; } - ret = q6asm_media_format_block_pcm_format_support_v3( + ret = q6asm_media_format_block_pcm_format_support_v4( prtd->audio_client, prtd->sample_rate, prtd->num_channels, bit_width, stream_id, use_default_chmap, chmap, - sample_word_size); + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); if (ret < 0) pr_err("%s: CMD Format block failed\n", __func__); @@ -1117,7 +1123,7 @@ static int msm_compr_configure_dsp_for_playback } else { pr_debug("%s: stream_id %d bits_per_sample %d\n", __func__, ac->stream_id, bits_per_sample); - ret = q6asm_stream_open_write_v3(ac, + ret = q6asm_stream_open_write_v4(ac, prtd->codec, bits_per_sample, ac->stream_id, prtd->gapless_state.use_dsp_gapless_mode); @@ -2332,7 +2338,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: open_write stream_id %d bits_per_sample %d", __func__, stream_id, bits_per_sample); - rc = q6asm_stream_open_write_v3(prtd->audio_client, + rc = q6asm_stream_open_write_v4(prtd->audio_client, prtd->codec, bits_per_sample, stream_id, prtd->gapless_state.use_dsp_gapless_mode); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index eefae71a27bf..bb6de7a4d7bc 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -68,10 +68,11 @@ static struct snd_pcm_hardware msm_pcm_hardware_capture = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), - .rates = SNDRV_PCM_RATE_8000_48000, + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 384000, .channels_min = 1, .channels_max = 4, .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * @@ -91,10 +92,11 @@ static struct snd_pcm_hardware msm_pcm_hardware_playback = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), - .rates = SNDRV_PCM_RATE_8000_192000, + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 8, .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * @@ -109,7 +111,7 @@ static struct snd_pcm_hardware msm_pcm_hardware_playback = { /* Conventional and unconventional sample rate supported */ static unsigned int supported_sample_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, - 88200, 96000, 176400, 192000 + 88200, 96000, 176400, 192000, 384000 }; static struct snd_pcm_hw_constraint_list constraints_sample_rates = { @@ -321,6 +323,10 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) pr_debug("%s: perf: %x\n", __func__, prtd->audio_client->perf_mode); switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; case SNDRV_PCM_FORMAT_S24_LE: bits_per_sample = 24; sample_word_size = 32; @@ -336,7 +342,7 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) break; } - ret = q6asm_open_write_v3(prtd->audio_client, + ret = q6asm_open_write_v4(prtd->audio_client, FORMAT_LINEAR_PCM, bits_per_sample); if (ret < 0) { @@ -361,11 +367,12 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) return ret; } - ret = q6asm_media_format_block_multi_ch_pcm_v3( + ret = q6asm_media_format_block_multi_ch_pcm_v4( prtd->audio_client, runtime->rate, runtime->channels, !prtd->set_channel_map, prtd->channel_map, bits_per_sample, - sample_word_size); + sample_word_size, ASM_LITTLE_ENDIAN, + DEFAULT_QF); if (ret < 0) pr_info("%s: CMD Format block failed\n", __func__); @@ -410,6 +417,8 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) || (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE)) bits_per_sample = 24; + else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; /* ULL mode is not supported in capture path */ if (pdata->perf_mode_set == LEGACY_PCM_MODE) @@ -421,7 +430,7 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) __func__, params_channels(params), prtd->audio_client->perf_mode); - ret = q6asm_open_read_v3(prtd->audio_client, FORMAT_LINEAR_PCM, + ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM, bits_per_sample); if (ret < 0) { pr_err("%s: q6asm_open_read failed\n", __func__); @@ -467,6 +476,10 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) return 0; switch (runtime->format) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; case SNDRV_PCM_FORMAT_S24_LE: bits_per_sample = 24; sample_word_size = 32; @@ -485,11 +498,13 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) pr_debug("%s: Samp_rate = %d Channel = %d bit width = %d, word size = %d\n", __func__, prtd->samp_rate, prtd->channel_mode, bits_per_sample, sample_word_size); - ret = q6asm_enc_cfg_blk_pcm_format_support_v3(prtd->audio_client, + ret = q6asm_enc_cfg_blk_pcm_format_support_v4(prtd->audio_client, prtd->samp_rate, prtd->channel_mode, bits_per_sample, - sample_word_size); + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); if (ret < 0) pr_debug("%s: cmd cfg pcm was block failed", __func__); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h index 187133b1db75..fe215438a4c7 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h @@ -59,11 +59,11 @@ struct msm_audio_in_frame_info { #define PLAYBACK_MIN_NUM_PERIODS 2 #define PLAYBACK_MAX_NUM_PERIODS 8 -#define PLAYBACK_MAX_PERIOD_SIZE 61440 +#define PLAYBACK_MAX_PERIOD_SIZE 122880 #define PLAYBACK_MIN_PERIOD_SIZE 128 #define CAPTURE_MIN_NUM_PERIODS 2 #define CAPTURE_MAX_NUM_PERIODS 8 -#define CAPTURE_MAX_PERIOD_SIZE 61440 +#define CAPTURE_MAX_PERIOD_SIZE 122880 #define CAPTURE_MIN_PERIOD_SIZE 320 struct msm_audio { -- GitLab From dfe94b0f0ff85990948c912e7309f360d97501d5 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Mon, 30 Oct 2017 14:53:22 +0530 Subject: [PATCH 1390/5498] ASoC: msm: qdsp6v2: add 32bit capture support in dai driver Add support for 24/32 bit recording support in FE and BE drivers. CRs-Fixed: 2129947 Change-Id: I22c0a98b1d6b724762de70c683672ddeaf76eb1e Signed-off-by: Surendar karka --- sound/soc/msm/msm-dai-fe.c | 36 ++++++++---- sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 64 ++++++++++++++++------ sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 3 + 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index dec64067f2cf..7e385675e3e4 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -108,7 +108,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -140,7 +141,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -223,7 +225,9 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rates = (SNDRV_PCM_RATE_8000_192000| SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE), + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -255,7 +259,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 0, .channels_max = 8, .rate_min = 8000, @@ -288,7 +293,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -320,7 +326,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -372,7 +379,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2118,7 +2126,10 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .aif_name = "MM_UL9", .rates = (SNDRV_PCM_RATE_8000_48000| SNDRV_PCM_RATE_KNOT), - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2513,7 +2524,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2532,7 +2544,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2551,7 +2564,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 4b1159d3774b..c1e6b6c2c503 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -3132,6 +3132,10 @@ static int msm_dai_q6_mi2s_hw_params(struct snd_pcm_substream *substream, dai_data->port_config.i2s.bit_width = 24; dai_data->bitwidth = 24; break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.i2s.bit_width = 32; + dai_data->bitwidth = 32; + break; default: pr_err("%s: format %d\n", __func__, params_format(params)); @@ -3290,10 +3294,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "Primary MI2S Capture", .aif_name = "PRI_MI2S_TX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_dai_q6_mi2s_ops, .id = MSM_PRIM_MI2S, @@ -3315,10 +3323,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "Secondary MI2S Capture", .aif_name = "SEC_MI2S_TX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_dai_q6_mi2s_ops, .id = MSM_SEC_MI2S, @@ -3339,10 +3351,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "Tertiary MI2S Capture", .aif_name = "TERT_MI2S_TX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_dai_q6_mi2s_ops, .id = MSM_TERT_MI2S, @@ -3364,10 +3380,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "Quaternary MI2S Capture", .aif_name = "QUAT_MI2S_TX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_dai_q6_mi2s_ops, .id = MSM_QUAT_MI2S, @@ -3401,10 +3421,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "Quinary MI2S Capture", .aif_name = "QUIN_MI2S_TX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_dai_q6_mi2s_ops, .id = MSM_QUIN_MI2S, @@ -3416,10 +3440,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .stream_name = "Senary_mi2s Capture", .aif_name = "SENARY_TX", .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_dai_q6_mi2s_ops, .id = MSM_SENARY_MI2S, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 03c8a052c9c8..fa014347e29f 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -123,6 +123,9 @@ static int msm_routing_get_bit_width(unsigned int format) int bit_width; switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + break; case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_3LE: bit_width = 24; -- GitLab From 233694e24daf854d5b3cd1225b2683cef7d7ebab Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Mon, 18 Sep 2017 09:27:49 -0700 Subject: [PATCH 1391/5498] msm: ipa: Race condition fix to avoid missing GSI interrupts Changes to set the gsi poll mode before/after enabling/disabling interrupts to avoid a race condition with missing GSI interrupts. Change-Id: Idcc569e62cc91bea8ebc581eb46d1903e2021d0b CRs-Fixed: 2092580 Acked-by: Jyothi Jayanthi Signed-off-by: Michael Adisumarta --- drivers/platform/msm/gsi/gsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index 61b510daa559..684e255b2cbd 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -2579,15 +2579,16 @@ int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode) if (curr == GSI_CHAN_MODE_CALLBACK && mode == GSI_CHAN_MODE_POLL) { __gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, 0); + atomic_set(&ctx->poll_mode, mode); ctx->stats.callback_to_poll++; } if (curr == GSI_CHAN_MODE_POLL && mode == GSI_CHAN_MODE_CALLBACK) { + atomic_set(&ctx->poll_mode, mode); __gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, ~0); ctx->stats.poll_to_callback++; } - atomic_set(&ctx->poll_mode, mode); spin_unlock_irqrestore(&gsi_ctx->slock, flags); return GSI_STATUS_SUCCESS; -- GitLab From c98c0c1ea94d59b27e4979709832f9d0f6ada992 Mon Sep 17 00:00:00 2001 From: Arun Kumar Neelakantam Date: Tue, 31 Oct 2017 15:27:55 +0530 Subject: [PATCH 1392/5498] ARM: dts: msm: Add two new SMD TTY device nodes mdm9607 Two new SMD TTY devices are exposed to allow access to SMD resource from user space for DATA team CMUX feature. Change-Id: I9f241611f063509c2b40073ec36f250824324f9e Signed-off-by: Arun Kumar Neelakantam --- arch/arm/boot/dts/qcom/mdm9607.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/boot/dts/qcom/mdm9607.dtsi b/arch/arm/boot/dts/qcom/mdm9607.dtsi index 4f637e7cbf0d..25b223dcdd60 100644 --- a/arch/arm/boot/dts/qcom/mdm9607.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9607.dtsi @@ -71,6 +71,8 @@ /* smdtty devices */ smd7 = &smdtty_data1; smd8 = &smdtty_data4; + smd9 = &smdtty_data2; + smd10 = &smdtty_data3; smd11 = &smdtty_data11; smd21 = &smdtty_data21; smd36 = &smdtty_loopback; @@ -1355,6 +1357,16 @@ qcom,smdtty-port-name = "DATA1"; }; + smdtty_data2: qcom,smdtty-data2 { + qcom,smdtty-remote = "modem"; + qcom,smdtty-port-name = "DATA2"; + }; + + smdtty_data3: qcom,smdtty-data3 { + qcom,smdtty-remote = "modem"; + qcom,smdtty-port-name = "DATA3"; + }; + smdtty_data4: qcom,smdtty-data4 { qcom,smdtty-remote = "modem"; qcom,smdtty-port-name = "DATA4"; -- GitLab From 3c47ad5baa685dcc2bfd61416ea9f8f2795c5bed Mon Sep 17 00:00:00 2001 From: Skylar Chang Date: Tue, 4 Oct 2016 14:15:25 -0700 Subject: [PATCH 1393/5498] msm: ipa3: header file change for wdi-stats Add structure changes for querying WDI-stats, wlan needs register the callback functions for IPA to query WDI stats and sets the quota limit on STA mode. Change-Id: I62de66e9b0bb1eeeac3c94d1ac1037285811b631 Signed-off-by: Skylar Chang --- include/linux/ipa.h | 64 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/include/linux/ipa.h b/include/linux/ipa.h index f7c96d87c436..0ffbde7403bb 100644 --- a/include/linux/ipa.h +++ b/include/linux/ipa.h @@ -104,7 +104,7 @@ enum ipa_dp_evt_type { }; /** - * enum hdr_total_len_or_pad_type - type vof alue held by TOTAL_LEN_OR_PAD + * enum hdr_total_len_or_pad_type - type of value held by TOTAL_LEN_OR_PAD * field in header configuration register. * @IPA_HDR_PAD: field is used as padding length * @IPA_HDR_TOTAL_LEN: field is used as total length @@ -453,6 +453,55 @@ struct ipa_ep_cfg_ctrl { typedef void (*ipa_notify_cb)(void *priv, enum ipa_dp_evt_type evt, unsigned long data); +/** + * enum ipa_wdi_meter_evt_type - type of event client callback is + * for AP+STA mode metering + * @IPA_GET_WDI_SAP_STATS: get IPA_stats betwen SAP and STA - + * use ipa_get_wdi_sap_stats structure + * @IPA_SET_WIFI_QUOTA: set quota limit on STA - + * use ipa_set_wifi_quota structure + */ +enum ipa_wdi_meter_evt_type { + IPA_GET_WDI_SAP_STATS, + IPA_SET_WIFI_QUOTA, +}; + +struct ipa_get_wdi_sap_stats { + /* indicate to reset stats after query */ + uint8_t reset_stats; + /* indicate valid stats from wlan-fw */ + uint8_t stats_valid; + /* Tx: SAP->STA */ + uint64_t ipv4_tx_packets; + uint64_t ipv4_tx_bytes; + /* Rx: STA->SAP */ + uint64_t ipv4_rx_packets; + uint64_t ipv4_rx_bytes; + uint64_t ipv6_tx_packets; + uint64_t ipv6_tx_bytes; + uint64_t ipv6_rx_packets; + uint64_t ipv6_rx_bytes; +}; + +/** + * struct ipa_set_wifi_quota - structure used for + * IPA_SET_WIFI_QUOTA. + * + * @quota_bytes: Quota (in bytes) for the STA interface. + * @set_quota: Indicate whether to set the quota (use 1) or + * unset the quota. + * + */ +struct ipa_set_wifi_quota { + uint64_t quota_bytes; + uint8_t set_quota; + /* indicate valid quota set from wlan-fw */ + uint8_t set_valid; +}; + +typedef void (*ipa_wdi_meter_notifier_cb)(enum ipa_wdi_meter_evt_type evt, + void *data); + /** * struct ipa_connect_params - low-level client connect input parameters. Either * client allocates the data and desc FIFO and specifies that in data+desc OR @@ -1019,6 +1068,7 @@ struct ipa_wdi_dl_params_smmu { * @ul_smmu: WDI_RX configuration info when WLAN uses SMMU * @dl_smmu: WDI_TX configuration info when WLAN uses SMMU * @smmu_enabled: true if WLAN uses SMMU + * @ipa_wdi_meter_notifier_cb: Get WDI stats and quato info */ struct ipa_wdi_in_params { struct ipa_sys_connect_params sys; @@ -1029,6 +1079,9 @@ struct ipa_wdi_in_params { struct ipa_wdi_dl_params_smmu dl_smmu; } u; bool smmu_enabled; +#ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN + ipa_wdi_meter_notifier_cb wdi_notify; +#endif }; /** @@ -1291,6 +1344,9 @@ int ipa_resume_wdi_pipe(u32 clnt_hdl); int ipa_suspend_wdi_pipe(u32 clnt_hdl); int ipa_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats); u16 ipa_get_smem_restr_bytes(void); +int ipa_broadcast_wdi_quota_reach_ind(uint32_t fid, + uint64_t num_bytes); + /* * To retrieve doorbell physical address of * wlan pipes @@ -1878,6 +1934,12 @@ static inline int ipa_suspend_wdi_pipe(u32 clnt_hdl) return -EPERM; } +static inline int ipa_broadcast_wdi_quota_reach_ind(uint32_t fid, + uint64_t num_bytes) +{ + return -EPERM; +} + static inline int ipa_uc_wdi_get_dbpa( struct ipa_wdi_db_params *out) { -- GitLab From a9735b5ec6644d90c60f3d158f58b4e3ede9a202 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Mon, 2 Oct 2017 12:38:10 -0700 Subject: [PATCH 1394/5498] ASoC: wcd9xxx: restrict debugfs permission Remove read permission for debugfs reg dump node for group and users to not allow reading of wcd9xxx registers. CRs-fixed: 2113240 Change-Id: I73a22e140446828e694fdc95fde7ac4e051c9548 Signed-off-by: Karthikeyan Mani --- drivers/mfd/wcd9xxx-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c index d85aa32405f0..de1b1ba28f83 100644 --- a/drivers/mfd/wcd9xxx-core.c +++ b/drivers/mfd/wcd9xxx-core.c @@ -3130,19 +3130,19 @@ static int wcd9xxx_slim_probe(struct slim_device *slim) ("wcd9xxx_core", 0); if (!IS_ERR(debugfs_wcd9xxx_dent)) { debugfs_peek = debugfs_create_file("slimslave_peek", - S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent, + S_IFREG | S_IRUSR, debugfs_wcd9xxx_dent, (void *) "slimslave_peek", &codec_debug_ops); debugfs_poke = debugfs_create_file("slimslave_poke", - S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent, + S_IFREG | S_IRUSR, debugfs_wcd9xxx_dent, (void *) "slimslave_poke", &codec_debug_ops); debugfs_power_state = debugfs_create_file("power_state", - S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent, + S_IFREG | S_IRUSR, debugfs_wcd9xxx_dent, (void *) "power_state", &codec_debug_ops); debugfs_reg_dump = debugfs_create_file("slimslave_reg_dump", - S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent, + S_IFREG | S_IRUSR, debugfs_wcd9xxx_dent, (void *) "slimslave_reg_dump", &codec_debug_ops); } #endif -- GitLab From d9bfbfef40c51223ceefe69c3f31bb12e7e056ac Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Mon, 11 Sep 2017 17:48:17 +0530 Subject: [PATCH 1395/5498] ASoC: msm: qdsp6v2: Fix dangling pointer access Fix access of a dangling pointer by assigning it to NULL. CRs-Fixed: 2096407 Change-Id: I22c1d55ea611ac59cdca51924787f6831bad8c2b Signed-off-by: Aditya Bavanari --- sound/soc/msm/qdsp6v2/q6asm.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index fdf8c6fda03a..83ae40c8ca27 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -2989,6 +2989,15 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, int bytes_to_alloc, rc; size_t len; + mutex_lock(&ac->cmd_lock); + + if (ac->port[dir].buf) { + pr_err("%s: Buffer already allocated\n", __func__); + rc = -EINVAL; + mutex_unlock(&ac->cmd_lock); + goto done; + } + buf_circ = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL); if (!buf_circ) { @@ -2996,10 +3005,6 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, goto done; } - mutex_lock(&ac->cmd_lock); - - ac->port[dir].buf = buf_circ; - bytes_to_alloc = bufsz * bufcnt; bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc); @@ -3011,11 +3016,12 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, if (rc) { pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__, rc); - mutex_unlock(&ac->cmd_lock); kfree(buf_circ); + mutex_unlock(&ac->cmd_lock); goto done; } + ac->port[dir].buf = buf_circ; buf_circ->used = dir ^ 1; buf_circ->size = bytes_to_alloc; buf_circ->actual_size = bytes_to_alloc; @@ -3177,12 +3183,6 @@ int q6asm_open_shared_io(struct audio_client *ac, goto done; } - if (ac->port[dir].buf) { - pr_err("%s: Buffer already allocated\n", __func__); - rc = -EINVAL; - goto done; - } - rc = q6asm_set_shared_circ_buff(ac, open, bufsz, bufcnt, dir); if (rc) -- GitLab From 16ebb9a9e32125228e37e6737cbfd289c786adba Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Tue, 31 Oct 2017 16:38:28 +0530 Subject: [PATCH 1396/5498] ARM: dts: msm: Change GPU maximum frequency to 456 Mhz for 8909 Change GPU frequency on 8909 to support maximum frequency of 456 MHz. This is the only speed configuration supported on this target so simplify the frequency table too. Change-Id: I550cc01fe1c5201064aa70dd5983a0861f25cf0e Signed-off-by: Sunil Khatri --- arch/arm/boot/dts/qcom/msm8909-gpu.dtsi | 40 ++---------------------- arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi | 3 -- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi index acb9d88fff27..0e0e528a06ea 100644 --- a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi @@ -51,8 +51,6 @@ interrupt-names = "kgsl_3d0_irq"; qcom,id = <0>; - qcom,gpu-speed-config = <0>; - qcom,chipid = <0x03000400>; qcom,initial-pwrlevel = <1>; @@ -124,7 +122,7 @@ qcom,gpu-pwrlevel@0 { reg = <0>; - qcom,gpu-freq = <409600000>; + qcom,gpu-freq = <456000000>; qcom,bus-freq = <3>; }; @@ -147,41 +145,7 @@ }; }; - qcom,gpu-speed-config@0 { - compatible = "gpu-speed-config@0"; - /* Power levels */ - qcom,gpu-pwrlevels { - #address-cells = <1>; - #size-cells = <0>; - - compatible = "qcom,gpu-pwrlevels"; - - qcom,gpu-pwrlevel@0 { - reg = <0>; - qcom,gpu-freq = <456000000>; - qcom,bus-freq = <3>; - }; - - qcom,gpu-pwrlevel@1 { - reg = <1>; - qcom,gpu-freq = <307200000>; - qcom,bus-freq = <2>; - }; - - qcom,gpu-pwrlevel@2 { - reg = <2>; - qcom,gpu-freq = <200000000>; - qcom,bus-freq = <1>; - }; - - qcom,gpu-pwrlevel@3 { - reg = <3>; - qcom,gpu-freq = <19200000>; - qcom,bus-freq = <0>; - }; - }; - }; - }; + }; kgsl_msm_iommu: qcom,kgsl-iommu@1f00000 { compatible = "qcom,kgsl-smmu-v2"; reg = <0x1f00000 0x10000>; diff --git a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi index d1e821053ece..e926093ec4f6 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi @@ -22,8 +22,6 @@ &msm_gpu { - /delete-property/qcom,gpu-speed-config; - /* To disable GPU wake up on touch event */ qcom,disable-wake-on-touch; @@ -62,7 +60,6 @@ }; - /delete-node/qcom,gpu-speed-config@0; }; &kgsl_msm_iommu { -- GitLab From 2c8247a1bd4ea163c26700a0d397de09fdd477e3 Mon Sep 17 00:00:00 2001 From: Sreelakshmi Gownipalli Date: Mon, 9 Oct 2017 12:59:56 -0700 Subject: [PATCH 1397/5498] diag: Add mutex protection while reading dci debug statistics Unserialized access to diag_dbgfs_dci_data_index can lead to heap overflow. Add mutex protection while updating the diag_dbgfs_dci_data_index. Change-Id: Iee9d0447494e3576e6293afcd4d7611bc429aa8a Signed-off-by: Sreelakshmi Gownipalli --- drivers/char/diag/diag_debugfs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c index b66c8cb8257c..040790a22614 100644 --- a/drivers/char/diag/diag_debugfs.c +++ b/drivers/char/diag/diag_debugfs.c @@ -50,7 +50,7 @@ static int diag_dbgfs_bridgeinfo_index; static int diag_dbgfs_finished; static int diag_dbgfs_dci_data_index; static int diag_dbgfs_dci_finished; - +static struct mutex diag_dci_dbgfs_mutex; static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { @@ -151,6 +151,7 @@ static ssize_t diag_dbgfs_read_dcistats(struct file *file, buf_size = ksize(buf); bytes_remaining = buf_size; + mutex_lock(&diag_dci_dbgfs_mutex); if (diag_dbgfs_dci_data_index == 0) { bytes_written = scnprintf(buf, buf_size, @@ -206,8 +207,8 @@ static ssize_t diag_dbgfs_read_dcistats(struct file *file, } temp_data++; } - diag_dbgfs_dci_data_index = (i >= DIAG_DCI_DEBUG_CNT) ? 0 : i + 1; + mutex_unlock(&diag_dci_dbgfs_mutex); bytes_written = simple_read_from_buffer(ubuf, count, ppos, buf, bytes_in_buf); kfree(buf); @@ -1065,6 +1066,7 @@ int diag_debugfs_init(void) pr_warn("diag: could not allocate memory for dci debug info\n"); mutex_init(&dci_stat_mutex); + mutex_init(&diag_dci_dbgfs_mutex); return 0; err: kfree(dci_traffic); @@ -1081,6 +1083,7 @@ void diag_debugfs_cleanup(void) kfree(dci_traffic); mutex_destroy(&dci_stat_mutex); + mutex_destroy(&diag_dci_dbgfs_mutex); } #else int diag_debugfs_init(void) { return 0; } -- GitLab From 1d218311b7d435809a6e1a6c1d851c04aa96caa9 Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Thu, 2 Nov 2017 12:17:41 +0530 Subject: [PATCH 1398/5498] ARM: dts: msm: Remove modem wdog property for SDX20 This property is not required for SDX20, as HLOS won't register mpss wdog bite. Change-Id: Ic2417eabea25de7052407d9065646c69e9007284 Signed-off-by: Jitendra Sharma --- arch/arm/boot/dts/qcom/sdx20.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/sdx20.dtsi b/arch/arm/boot/dts/qcom/sdx20.dtsi index bf9e1eac5771..fac893dbbbea 100644 --- a/arch/arm/boot/dts/qcom/sdx20.dtsi +++ b/arch/arm/boot/dts/qcom/sdx20.dtsi @@ -393,6 +393,7 @@ &mss { /delete-property/qcom,qdsp6v61-1-1; qcom,qdsp6v62-1-4; + /delete-property/interrupts; }; &mhi_device { -- GitLab From 55d4aa12af57ea7782f0c8bbc3b01e44673b05ba Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 19 Oct 2017 08:52:58 -0400 Subject: [PATCH 1399/5498] ceph: unlock dangling spinlock in try_flush_caps() commit 6c2838fbdedb9b72a81c931d49e56b229b6cdbca upstream. sparse warns: fs/ceph/caps.c:2042:9: warning: context imbalance in 'try_flush_caps' - wrong count at exit We need to exit this function with the lock unlocked, but a couple of cases leave it locked. Signed-off-by: Jeff Layton Reviewed-by: "Yan, Zheng" Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov Signed-off-by: Greg Kroah-Hartman --- fs/ceph/caps.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index cefca661464b..45692e8a9397 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1748,6 +1748,7 @@ static int try_flush_caps(struct inode *inode, unsigned *flush_tid) retry: spin_lock(&ci->i_ceph_lock); if (ci->i_ceph_flags & CEPH_I_NOFLUSH) { + spin_unlock(&ci->i_ceph_lock); dout("try_flush_caps skipping %p I_NOFLUSH set\n", inode); goto out; } @@ -1765,8 +1766,10 @@ retry: mutex_lock(&session->s_mutex); goto retry; } - if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) + if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) { + spin_unlock(&ci->i_ceph_lock); goto out; + } flushing = __mark_caps_flushing(inode, session); -- GitLab From aaa7d1a5d9f1293b13e378a0e0606b9f1487a355 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 6 Oct 2017 17:45:30 +0300 Subject: [PATCH 1400/5498] usb: xhci: Handle error condition in xhci_stop_device() commit b3207c65dfafae27e7c492cb9188c0dc0eeaf3fd upstream. xhci_stop_device() calls xhci_queue_stop_endpoint() multiple times without checking the return value. xhci_queue_stop_endpoint() can return error if the HC is already halted or unable to queue commands. This can cause a deadlock condition as xhci_stop_device() would end up waiting indefinitely for a completion for the command that didn't get queued. Fix this by checking the return value and bailing out of xhci_stop_device() in case of error. This patch happens to fix potential memory leaks of the allocated command structures as well. Fixes: c311e391a7ef ("xhci: rework command timeout and cancellation,") Signed-off-by: Mayank Rana Signed-off-by: Jack Pham Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 08dbe69c1942..2b08111313f9 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -293,15 +293,25 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) GFP_NOWAIT); if (!command) { spin_unlock_irqrestore(&xhci->lock, flags); - xhci_free_command(xhci, cmd); - return -ENOMEM; + ret = -ENOMEM; + goto cmd_cleanup; + } + ret = xhci_queue_stop_endpoint(xhci, command, slot_id, + i, suspend); + if (ret) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, command); + goto cmd_cleanup; } - xhci_queue_stop_endpoint(xhci, command, slot_id, i, - suspend); } } - xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend); + ret = xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend); + if (ret) { + spin_unlock_irqrestore(&xhci->lock, flags); + goto cmd_cleanup; + } + xhci_ring_cmd_db(xhci); spin_unlock_irqrestore(&xhci->lock, flags); @@ -312,6 +322,8 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); ret = -ETIME; } + +cmd_cleanup: xhci_free_command(xhci, cmd); return ret; } -- GitLab From 458a78ab7eb1ca036984ab7323a92dca87e73cec Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 10 Sep 2017 20:29:45 +0300 Subject: [PATCH 1401/5498] spi: uapi: spidev: add missing ioctl header commit a2b4a79b88b24c49d98d45a06a014ffd22ada1a4 upstream. The SPI_IOC_MESSAGE() macro references _IOC_SIZEBITS. Add linux/ioctl.h to make sure this macro is defined. This fixes the following build failure of lcdproc with the musl libc: In file included from .../sysroot/usr/include/sys/ioctl.h:7:0, from hd44780-spi.c:31: hd44780-spi.c: In function 'spi_transfer': hd44780-spi.c:89:24: error: '_IOC_SIZEBITS' undeclared (first use in this function) status = ioctl(p->fd, SPI_IOC_MESSAGE(1), &xfer); ^ Signed-off-by: Baruch Siach Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/spi/spidev.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/spi/spidev.h b/include/uapi/linux/spi/spidev.h index dd5f21e75805..856de39d0b89 100644 --- a/include/uapi/linux/spi/spidev.h +++ b/include/uapi/linux/spi/spidev.h @@ -23,6 +23,7 @@ #define SPIDEV_H #include +#include /* User space versions of kernel symbols for SPI clocking modes, * matching -- GitLab From b7be20b0175f4d148a06224df0324b5810aadb99 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 25 Oct 2017 16:34:27 +0200 Subject: [PATCH 1402/5498] fuse: fix READDIRPLUS skipping an entry commit c6cdd51404b7ac12dd95173ddfc548c59ecf037f upstream. Marios Titas running a Haskell program noticed a problem with fuse's readdirplus: when it is interrupted by a signal, it skips one directory entry. The reason is that fuse erronously updates ctx->pos after a failed dir_emit(). The issue originates from the patch adding readdirplus support. Reported-by: Jakob Unterwurzacher Tested-by: Marios Titas Signed-off-by: Miklos Szeredi Fixes: 0b05b18381ee ("fuse: implement NFS-like readdirplus support") Signed-off-by: Greg Kroah-Hartman --- fs/fuse/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 015e21edd6bc..1bd3c563ec0b 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1371,7 +1371,8 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, */ over = !dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, dirent->type); - ctx->pos = dirent->off; + if (!over) + ctx->pos = dirent->off; } buf += reclen; -- GitLab From 9c73743447f244eb0e7422a285dc907283d3630e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 23 Oct 2017 16:46:00 -0700 Subject: [PATCH 1403/5498] Input: gtco - fix potential out-of-bound access commit a50829479f58416a013a4ccca791336af3c584c7 upstream. parse_hid_report_descriptor() has a while (i < length) loop, which only guarantees that there's at least 1 byte in the buffer, but the loop body can read multiple bytes which causes out-of-bounds access. Reported-by: Andrey Konovalov Reviewed-by: Andrey Konovalov Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/tablet/gtco.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c index a51de543a0b2..fe1ab5067b5d 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -231,13 +231,17 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, /* Walk this report and pull out the info we need */ while (i < length) { - prefix = report[i]; - - /* Skip over prefix */ - i++; + prefix = report[i++]; /* Determine data size and save the data in the proper variable */ - size = PREF_SIZE(prefix); + size = (1U << PREF_SIZE(prefix)) >> 1; + if (i + size > length) { + dev_err(ddev, + "Not enough data (need %d, have %d)\n", + i + size, length); + break; + } + switch (size) { case 1: data = report[i]; @@ -245,8 +249,7 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, case 2: data16 = get_unaligned_le16(&report[i]); break; - case 3: - size = 4; + case 4: data32 = get_unaligned_le32(&report[i]); break; } -- GitLab From 38dc93fa84026ef8711827fb7063744e7197b887 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 11 Oct 2017 23:32:27 +0100 Subject: [PATCH 1404/5498] assoc_array: Fix a buggy node-splitting case commit ea6789980fdaa610d7eb63602c746bf6ec70cd2b upstream. This fixes CVE-2017-12193. Fix a case in the assoc_array implementation in which a new leaf is added that needs to go into a node that happens to be full, where the existing leaves in that node cluster together at that level to the exclusion of new leaf. What needs to happen is that the existing leaves get moved out to a new node, N1, at level + 1 and the existing node needs replacing with one, N0, that has pointers to the new leaf and to N1. The code that tries to do this gets this wrong in two ways: (1) The pointer that should've pointed from N0 to N1 is set to point recursively to N0 instead. (2) The backpointer from N0 needs to be set correctly in the case N0 is either the root node or reached through a shortcut. Fix this by removing this path and using the split_node path instead, which achieves the same end, but in a more general way (thanks to Eric Biggers for spotting the redundancy). The problem manifests itself as: BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 IP: assoc_array_apply_edit+0x59/0xe5 Fixes: 3cb989501c26 ("Add a generic associative array implementation.") Reported-and-tested-by: WU Fan Signed-off-by: David Howells Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- lib/assoc_array.c | 51 ++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/lib/assoc_array.c b/lib/assoc_array.c index 03a77f4740c1..0d122543bd63 100644 --- a/lib/assoc_array.c +++ b/lib/assoc_array.c @@ -597,21 +597,31 @@ static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit, if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0) goto all_leaves_cluster_together; - /* Otherwise we can just insert a new node ahead of the old - * one. + /* Otherwise all the old leaves cluster in the same slot, but + * the new leaf wants to go into a different slot - so we + * create a new node (n0) to hold the new leaf and a pointer to + * a new node (n1) holding all the old leaves. + * + * This can be done by falling through to the node splitting + * path. */ - goto present_leaves_cluster_but_not_new_leaf; + pr_devel("present leaves cluster but not new leaf\n"); } split_node: pr_devel("split node\n"); - /* We need to split the current node; we know that the node doesn't - * simply contain a full set of leaves that cluster together (it - * contains meta pointers and/or non-clustering leaves). + /* We need to split the current node. The node must contain anything + * from a single leaf (in the one leaf case, this leaf will cluster + * with the new leaf) and the rest meta-pointers, to all leaves, some + * of which may cluster. + * + * It won't contain the case in which all the current leaves plus the + * new leaves want to cluster in the same slot. * * We need to expel at least two leaves out of a set consisting of the - * leaves in the node and the new leaf. + * leaves in the node and the new leaf. The current meta pointers can + * just be copied as they shouldn't cluster with any of the leaves. * * We need a new node (n0) to replace the current one and a new node to * take the expelled nodes (n1). @@ -716,33 +726,6 @@ found_slot_for_multiple_occupancy: pr_devel("<--%s() = ok [split node]\n", __func__); return true; -present_leaves_cluster_but_not_new_leaf: - /* All the old leaves cluster in the same slot, but the new leaf wants - * to go into a different slot, so we create a new node to hold the new - * leaf and a pointer to a new node holding all the old leaves. - */ - pr_devel("present leaves cluster but not new leaf\n"); - - new_n0->back_pointer = node->back_pointer; - new_n0->parent_slot = node->parent_slot; - new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch; - new_n1->back_pointer = assoc_array_node_to_ptr(new_n0); - new_n1->parent_slot = edit->segment_cache[0]; - new_n1->nr_leaves_on_branch = node->nr_leaves_on_branch; - edit->adjust_count_on = new_n0; - - for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) - new_n1->slots[i] = node->slots[i]; - - new_n0->slots[edit->segment_cache[0]] = assoc_array_node_to_ptr(new_n0); - edit->leaf_p = &new_n0->slots[edit->segment_cache[ASSOC_ARRAY_FAN_OUT]]; - - edit->set[0].ptr = &assoc_array_ptr_to_node(node->back_pointer)->slots[node->parent_slot]; - edit->set[0].to = assoc_array_node_to_ptr(new_n0); - edit->excised_meta[0] = assoc_array_node_to_ptr(node); - pr_devel("<--%s() = ok [insert node before]\n", __func__); - return true; - all_leaves_cluster_together: /* All the leaves, new and old, want to cluster together in this node * in the same slot, so we have to replace this node with a shortcut to -- GitLab From 16c847d66c10482a9290a886aa8556422027e916 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Fri, 13 Oct 2017 15:40:07 +0200 Subject: [PATCH 1405/5498] scsi: zfcp: fix erp_action use-before-initialize in REC action trace commit ab31fd0ce65ec93828b617123792c1bb7c6dcc42 upstream. v4.10 commit 6f2ce1c6af37 ("scsi: zfcp: fix rport unblock race with LUN recovery") extended accessing parent pointer fields of struct zfcp_erp_action for tracing. If an erp_action has never been enqueued before, these parent pointer fields are uninitialized and NULL. Examples are zfcp objects freshly added to the parent object's children list, before enqueueing their first recovery subsequently. In zfcp_erp_try_rport_unblock(), we iterate such list. Accessing erp_action fields can cause a NULL pointer dereference. Since the kernel can read from lowcore on s390, it does not immediately cause a kernel page fault. Instead it can cause hangs on trying to acquire the wrong erp_action->adapter->dbf->rec_lock in zfcp_dbf_rec_action_lvl() ^bogus^ while holding already other locks with IRQs disabled. Real life example from attaching lots of LUNs in parallel on many CPUs: crash> bt 17723 PID: 17723 TASK: ... CPU: 25 COMMAND: "zfcperp0.0.1800" LOWCORE INFO: -psw : 0x0404300180000000 0x000000000038e424 -function : _raw_spin_lock_wait_flags at 38e424 ... #0 [fdde8fc90] zfcp_dbf_rec_action_lvl at 3e0004e9862 [zfcp] #1 [fdde8fce8] zfcp_erp_try_rport_unblock at 3e0004dfddc [zfcp] #2 [fdde8fd38] zfcp_erp_strategy at 3e0004e0234 [zfcp] #3 [fdde8fda8] zfcp_erp_thread at 3e0004e0a12 [zfcp] #4 [fdde8fe60] kthread at 173550 #5 [fdde8feb8] kernel_thread_starter at 10add2 zfcp_adapter zfcp_port zfcp_unit
, 0x404040d600000000 scsi_device NULL, returning early! zfcp_scsi_dev.status = 0x40000000 0x40000000 ZFCP_STATUS_COMMON_RUNNING crash> zfcp_unit
struct zfcp_unit { erp_action = { adapter = 0x0, port = 0x0, unit = 0x0, }, } zfcp_erp_action is always fully embedded into its container object. Such container object is never moved in its object tree (only add or delete). Hence, erp_action parent pointers can never change. To fix the issue, initialize the erp_action parent pointers before adding the erp_action container to any list and thus before it becomes accessible from outside of its initializing function. In order to also close the time window between zfcp_erp_setup_act() memsetting the entire erp_action to zero and setting the parent pointers again, drop the memset and instead explicitly initialize individually all erp_action fields except for parent pointers. To be extra careful not to introduce any other unintended side effect, even keep zeroing the erp_action fields for list and timer. Also double-check with WARN_ON_ONCE that erp_action parent pointers never change, so we get to know when we would deviate from previous behavior. Signed-off-by: Steffen Maier Fixes: 6f2ce1c6af37 ("scsi: zfcp: fix rport unblock race with LUN recovery") Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/s390/scsi/zfcp_aux.c | 5 +++++ drivers/s390/scsi/zfcp_erp.c | 18 +++++++++++------- drivers/s390/scsi/zfcp_scsi.c | 5 +++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 8004b071a9f2..a7a0b3e4f5ea 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -356,6 +356,8 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update); + adapter->erp_action.adapter = adapter; + if (zfcp_qdio_setup(adapter)) goto failed; @@ -512,6 +514,9 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, port->dev.groups = zfcp_port_attr_groups; port->dev.release = zfcp_port_release; + port->erp_action.adapter = adapter; + port->erp_action.port = port; + if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) { kfree(port); goto err_out; diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index c82fe65c4128..f277ac9c5467 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -193,9 +193,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &zfcp_sdev->status); erp_action = &zfcp_sdev->erp_action; - memset(erp_action, 0, sizeof(struct zfcp_erp_action)); - erp_action->port = port; - erp_action->sdev = sdev; + WARN_ON_ONCE(erp_action->port != port); + WARN_ON_ONCE(erp_action->sdev != sdev); if (!(atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_RUNNING)) act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; @@ -208,8 +207,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, zfcp_erp_action_dismiss_port(port); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status); erp_action = &port->erp_action; - memset(erp_action, 0, sizeof(struct zfcp_erp_action)); - erp_action->port = port; + WARN_ON_ONCE(erp_action->port != port); + WARN_ON_ONCE(erp_action->sdev != NULL); if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING)) act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; break; @@ -219,7 +218,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, zfcp_erp_action_dismiss_adapter(adapter); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status); erp_action = &adapter->erp_action; - memset(erp_action, 0, sizeof(struct zfcp_erp_action)); + WARN_ON_ONCE(erp_action->port != NULL); + WARN_ON_ONCE(erp_action->sdev != NULL); if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; @@ -229,7 +229,11 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, return NULL; } - erp_action->adapter = adapter; + WARN_ON_ONCE(erp_action->adapter != adapter); + memset(&erp_action->list, 0, sizeof(erp_action->list)); + memset(&erp_action->timer, 0, sizeof(erp_action->timer)); + erp_action->step = ZFCP_ERP_STEP_UNINITIALIZED; + erp_action->fsf_req_id = 0; erp_action->action = need; erp_action->status = act_status; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 92f6e9e466b5..be9c7d2524e7 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -138,10 +138,15 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev) struct zfcp_unit *unit; int npiv = adapter->connection_features & FSF_FEATURE_NPIV_MODE; + zfcp_sdev->erp_action.adapter = adapter; + zfcp_sdev->erp_action.sdev = sdev; + port = zfcp_get_port_by_wwpn(adapter, rport->port_name); if (!port) return -ENXIO; + zfcp_sdev->erp_action.port = port; + unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev)); if (unit) put_device(&unit->dev); -- GitLab From 7242605318a014abe6488de723fdadf7a6c2d7fe Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 15 Oct 2017 18:16:33 +0100 Subject: [PATCH 1406/5498] scsi: sg: Re-fix off by one in sg_fill_request_table() commit 587c3c9f286cee5c9cac38d28c8ae1875f4ec85b upstream. Commit 109bade9c625 ("scsi: sg: use standard lists for sg_requests") introduced an off-by-one error in sg_ioctl(), which was fixed by commit bd46fc406b30 ("scsi: sg: off by one in sg_ioctl()"). Unfortunately commit 4759df905a47 ("scsi: sg: factor out sg_fill_request_table()") moved that code, and reintroduced the bug (perhaps due to a botched rebase). Fix it again. Fixes: 4759df905a47 ("scsi: sg: factor out sg_fill_request_table()") Signed-off-by: Ben Hutchings Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 591575587f47..09b670555620 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -863,7 +863,7 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) val = 0; list_for_each_entry(srp, &sfp->rq_list, entry) { - if (val > SG_MAX_QUEUE) + if (val >= SG_MAX_QUEUE) break; rinfo[val].req_state = srp->done + 1; rinfo[val].problem = -- GitLab From 56621bc7d28c8023c020f0f38b5cf808f46ee5f9 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 24 Oct 2017 12:23:28 +0200 Subject: [PATCH 1407/5498] can: kvaser_usb: Correct return value in printout commit 8f65a923e6b628e187d5e791cf49393dd5e8c2f9 upstream. If the return value from kvaser_usb_send_simple_msg() was non-zero, the return value from kvaser_usb_flush_queue() was printed in the kernel warning. Signed-off-by: Jimmy Assarsson Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 5d777956ae1f..fc8ca6c59e1e 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -1251,7 +1251,8 @@ static int kvaser_usb_close(struct net_device *netdev) if (err) netdev_warn(netdev, "Cannot flush queue, error %d\n", err); - if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel)) + err = kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel); + if (err) netdev_warn(netdev, "Cannot reset card, error %d\n", err); err = kvaser_usb_stop_chip(priv); -- GitLab From 5560a1cac11138fe3df5643c7819fd3c0d3755eb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:51:27 -0700 Subject: [PATCH 1408/5498] ecryptfs: fix dereference of NULL user_key_payload commit f66665c09ab489a11ca490d6a82df57cfc1bea3e upstream. In eCryptfs, we failed to verify that the authentication token keys are not revoked before dereferencing their payloads, which is problematic because the payload of a revoked key is NULL. request_key() *does* skip revoked keys, but there is still a window where the key can be revoked before we acquire the key semaphore. Fix it by updating ecryptfs_get_key_payload_data() to return -EKEYREVOKED if the key payload is NULL. For completeness we check this for "encrypted" keys as well as "user" keys, although encrypted keys cannot be revoked currently. Alternatively we could use key_validate(), but since we'll also need to fix ecryptfs_get_key_payload_data() to validate the payload length, it seems appropriate to just check the payload pointer. Fixes: 237fead61998 ("[PATCH] ecryptfs: fs/Makefile and fs/Kconfig") Reviewed-by: James Morris Cc: Michael Halcrow Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/ecryptfs_kernel.h | 25 +++++++++++++++++-------- fs/ecryptfs/keystore.c | 9 ++++++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 90d1882b306f..4682bef34bb6 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -84,11 +84,16 @@ struct ecryptfs_page_crypt_context { static inline struct ecryptfs_auth_tok * ecryptfs_get_encrypted_key_payload_data(struct key *key) { - if (key->type == &key_type_encrypted) - return (struct ecryptfs_auth_tok *) - (&((struct encrypted_key_payload *)key->payload.data)->payload_data); - else + struct encrypted_key_payload *payload; + + if (key->type != &key_type_encrypted) return NULL; + + payload = key->payload.data; + if (!payload) + return ERR_PTR(-EKEYREVOKED); + + return (struct ecryptfs_auth_tok *)payload->payload_data; } static inline struct key *ecryptfs_get_encrypted_key(char *sig) @@ -114,13 +119,17 @@ static inline struct ecryptfs_auth_tok * ecryptfs_get_key_payload_data(struct key *key) { struct ecryptfs_auth_tok *auth_tok; + struct user_key_payload *ukp; auth_tok = ecryptfs_get_encrypted_key_payload_data(key); - if (!auth_tok) - return (struct ecryptfs_auth_tok *) - (((struct user_key_payload *)key->payload.data)->data); - else + if (auth_tok) return auth_tok; + + ukp = key->payload.data; + if (!ukp) + return ERR_PTR(-EKEYREVOKED); + + return (struct ecryptfs_auth_tok *)ukp->data; } #define ECRYPTFS_MAX_KEYSET_SIZE 1024 diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 635e8e16a5b7..5924e279733f 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -458,7 +458,8 @@ out: * @auth_tok_key: key containing the authentication token * @auth_tok: authentication token * - * Returns zero on valid auth tok; -EINVAL otherwise + * Returns zero on valid auth tok; -EINVAL if the payload is invalid; or + * -EKEYREVOKED if the key was revoked before we acquired its semaphore. */ static int ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key, @@ -467,6 +468,12 @@ ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key, int rc = 0; (*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key); + if (IS_ERR(*auth_tok)) { + rc = PTR_ERR(*auth_tok); + *auth_tok = NULL; + goto out; + } + if (ecryptfs_verify_version((*auth_tok)->version)) { printk(KERN_ERR "Data structure version mismatch. Userspace " "tools must match eCryptfs kernel module with major " -- GitLab From 4f823316dac3de3463dfbea2be3812102a76e246 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Nov 2017 09:36:48 +0100 Subject: [PATCH 1409/5498] Linux 3.18.79 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 527627294778..fb97c5f3d3a0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 78 +SUBLEVEL = 79 EXTRAVERSION = NAME = Diseased Newt -- GitLab From cc3b5a859ecb02c49ae5bb0d44ae2f57d040b0c0 Mon Sep 17 00:00:00 2001 From: Derek Chen Date: Thu, 2 Nov 2017 15:17:19 -0400 Subject: [PATCH 1410/5498] ASoC: APR_VM: initialize struct member before being used Initialize member value of struct apr_client_data after declaration. CRs-Fixed: 2091948 Change-Id: I951acff03edd2a1dc524fc74ab59a9e225e5ca0e Signed-off-by: Derek Chen --- drivers/soc/qcom/qdsp6v2/apr_vm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/qdsp6v2/apr_vm.c b/drivers/soc/qcom/qdsp6v2/apr_vm.c index 553e44fd4b44..b994a8675838 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_vm.c +++ b/drivers/soc/qcom/qdsp6v2/apr_vm.c @@ -981,6 +981,7 @@ void dispatch_event(unsigned long code, uint16_t proc) uint16_t clnt; int i, j; + memset(&data, 0, sizeof(data)); data.opcode = RESET_EVENTS; data.reset_event = code; -- GitLab From 972d662cc777dfd6e947f08cadbe3151436db0a1 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Mon, 30 Oct 2017 11:30:11 -0700 Subject: [PATCH 1411/5498] cnss2: Remove deprecated API cnss_set_driver_status() is no longer needed by WLAN host driver. Hence remove it from platform driver. Change-Id: I31a3174bdded924a9279a40ece05a835277e4626 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 11 ----------- include/net/cnss2.h | 1 - 2 files changed, 12 deletions(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 26ee607fa277..0edf10878850 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -289,17 +289,6 @@ int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) } EXPORT_SYMBOL(cnss_get_soc_info); -void cnss_set_driver_status(enum cnss_driver_status driver_status) -{ - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); - - if (!plat_priv) - return; - - plat_priv->driver_status = driver_status; -} -EXPORT_SYMBOL(cnss_set_driver_status); - void cnss_request_pm_qos(u32 qos_val) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); diff --git a/include/net/cnss2.h b/include/net/cnss2.h index f1d321299492..80d2a6bae0dd 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -151,7 +151,6 @@ extern int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files, u32 target_type, u32 target_version); extern int cnss_get_platform_cap(struct cnss_platform_cap *cap); extern int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info); -extern void cnss_set_driver_status(enum cnss_driver_status driver_status); extern int cnss_request_bus_bandwidth(int bandwidth); extern int cnss_power_up(struct device *dev); extern int cnss_power_down(struct device *dev); -- GitLab From b5712a2199659c7c7d3927ee9701fd822a252fbb Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Mon, 30 Oct 2017 11:31:08 -0700 Subject: [PATCH 1412/5498] cnss2: Add device pointer to all external APIs Add device pointer to all external APIs so that platform driver is able to support multiple devices/instances at the same time. Change-Id: I09dfb146da30d3fd6514dc79dc31cd239a03afa8 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 37 +++++++++++++++++-------------- drivers/net/wireless/cnss2/pci.c | 12 +++++----- include/net/cnss2.h | 32 ++++++++++++++++---------- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 0edf10878850..6288a99fd8f2 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -189,19 +189,20 @@ static void cnss_pm_relax(struct cnss_plat_data *plat_priv) pm_relax(&plat_priv->plat_dev->dev); } -void cnss_lock_pm_sem(void) +void cnss_lock_pm_sem(struct device *dev) { down_read(&cnss_pm_sem); } EXPORT_SYMBOL(cnss_lock_pm_sem); -void cnss_release_pm_sem(void) +void cnss_release_pm_sem(struct device *dev) { up_read(&cnss_pm_sem); } EXPORT_SYMBOL(cnss_release_pm_sem); -int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files, +int cnss_get_fw_files_for_target(struct device *dev, + struct cnss_fw_files *pfw_files, u32 target_type, u32 target_version) { if (!pfw_files) @@ -223,10 +224,10 @@ int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files, } EXPORT_SYMBOL(cnss_get_fw_files_for_target); -int cnss_request_bus_bandwidth(int bandwidth) +int cnss_request_bus_bandwidth(struct device *dev, int bandwidth) { int ret = 0; - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct cnss_bus_bw_info *bus_bw_info; if (!plat_priv) @@ -258,9 +259,9 @@ int cnss_request_bus_bandwidth(int bandwidth) } EXPORT_SYMBOL(cnss_request_bus_bandwidth); -int cnss_get_platform_cap(struct cnss_platform_cap *cap) +int cnss_get_platform_cap(struct device *dev, struct cnss_platform_cap *cap) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); if (!plat_priv) return -ENODEV; @@ -289,9 +290,9 @@ int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) } EXPORT_SYMBOL(cnss_get_soc_info); -void cnss_request_pm_qos(u32 qos_val) +void cnss_request_pm_qos(struct device *dev, u32 qos_val) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); if (!plat_priv) return; @@ -301,9 +302,9 @@ void cnss_request_pm_qos(u32 qos_val) } EXPORT_SYMBOL(cnss_request_pm_qos); -void cnss_remove_pm_qos(void) +void cnss_remove_pm_qos(struct device *dev) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); if (!plat_priv) return; @@ -1037,7 +1038,8 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv) cnss_driver_call_remove(plat_priv); - cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); + cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, + CNSS_BUS_WIDTH_NONE); cnss_pci_set_monitor_wake_intr(pci_priv, false); cnss_pci_set_auto_suspended(pci_priv, 0); @@ -1143,7 +1145,8 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) cnss_driver_call_remove(plat_priv); - cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE); + cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, + CNSS_BUS_WIDTH_NONE); cnss_pci_set_monitor_wake_intr(pci_priv, false); cnss_pci_set_auto_suspended(pci_priv, 0); @@ -1340,9 +1343,9 @@ static int cnss_ramdump(int enable, const struct subsys_desc *subsys_desc) return ret; } -void *cnss_get_virt_ramdump_mem(unsigned long *size) +void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct cnss_ramdump_info *ramdump_info; if (!plat_priv) @@ -1355,9 +1358,9 @@ void *cnss_get_virt_ramdump_mem(unsigned long *size) } EXPORT_SYMBOL(cnss_get_virt_ramdump_mem); -void cnss_device_crashed(void) +void cnss_device_crashed(struct device *dev) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct cnss_subsys_info *subsys_info; if (!plat_priv) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index ec5a6d21ab2b..0eff4db509de 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -585,9 +585,9 @@ static int cnss_pci_runtime_idle(struct device *dev) return -EBUSY; } -int cnss_wlan_pm_control(bool vote) +int cnss_wlan_pm_control(struct device *dev, bool vote) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct cnss_pci_data *pci_priv; struct pci_dev *pci_dev; @@ -607,10 +607,10 @@ int cnss_wlan_pm_control(bool vote) } EXPORT_SYMBOL(cnss_wlan_pm_control); -int cnss_auto_suspend(void) +int cnss_auto_suspend(struct device *dev) { int ret = 0; - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct pci_dev *pci_dev; struct cnss_pci_data *pci_priv; struct cnss_bus_bw_info *bus_bw_info; @@ -665,10 +665,10 @@ out: } EXPORT_SYMBOL(cnss_auto_suspend); -int cnss_auto_resume(void) +int cnss_auto_resume(struct device *dev) { int ret = 0; - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct pci_dev *pci_dev; struct cnss_pci_data *pci_priv; struct cnss_bus_bw_info *bus_bw_info; diff --git a/include/net/cnss2.h b/include/net/cnss2.h index 80d2a6bae0dd..ca2de6013a36 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -18,6 +18,12 @@ #define CNSS_MAX_FILE_NAME 20 #define CNSS_MAX_TIMESTAMP_LEN 32 +/* + * Temporary change for compilation, will be removed + * after WLAN host driver switched to use new APIs + */ +#define CNSS_API_WITH_DEV + enum cnss_bus_width_type { CNSS_BUS_WIDTH_NONE, CNSS_BUS_WIDTH_LOW, @@ -139,28 +145,30 @@ enum cnss_recovery_reason { extern int cnss_wlan_register_driver(struct cnss_wlan_driver *driver); extern void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver); -extern void cnss_device_crashed(void); +extern void cnss_device_crashed(struct device *dev); extern int cnss_pci_link_down(struct device *dev); extern void cnss_schedule_recovery(struct device *dev, enum cnss_recovery_reason reason); extern int cnss_self_recovery(struct device *dev, enum cnss_recovery_reason reason); extern int cnss_force_fw_assert(struct device *dev); -extern void *cnss_get_virt_ramdump_mem(unsigned long *size); -extern int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files, +extern void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size); +extern int cnss_get_fw_files_for_target(struct device *dev, + struct cnss_fw_files *pfw_files, u32 target_type, u32 target_version); -extern int cnss_get_platform_cap(struct cnss_platform_cap *cap); +extern int cnss_get_platform_cap(struct device *dev, + struct cnss_platform_cap *cap); extern int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info); -extern int cnss_request_bus_bandwidth(int bandwidth); +extern int cnss_request_bus_bandwidth(struct device *dev, int bandwidth); extern int cnss_power_up(struct device *dev); extern int cnss_power_down(struct device *dev); -extern void cnss_request_pm_qos(u32 qos_val); -extern void cnss_remove_pm_qos(void); -extern void cnss_lock_pm_sem(void); -extern void cnss_release_pm_sem(void); -extern int cnss_wlan_pm_control(bool vote); -extern int cnss_auto_suspend(void); -extern int cnss_auto_resume(void); +extern void cnss_request_pm_qos(struct device *dev, u32 qos_val); +extern void cnss_remove_pm_qos(struct device *dev); +extern void cnss_lock_pm_sem(struct device *dev); +extern void cnss_release_pm_sem(struct device *dev); +extern int cnss_wlan_pm_control(struct device *dev, bool vote); +extern int cnss_auto_suspend(struct device *dev); +extern int cnss_auto_resume(struct device *dev); extern int cnss_get_user_msi_assignment(struct device *dev, char *user_name, int *num_vectors, uint32_t *user_base_data, -- GitLab From 18c43e673cc06f14fd42d4a204a6382a4cbe97bd Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:46:18 -0700 Subject: [PATCH 1413/5498] UPSTREAM: fscrypt: fix dereference of NULL user_key_payload When an fscrypt-encrypted file is opened, we request the file's master key from the keyrings service as a logon key, then access its payload. However, a revoked key has a NULL payload, and we failed to check for this. request_key() *does* skip revoked keys, but there is still a window where the key can be revoked before we acquire its semaphore. Fix it by checking for a NULL payload, treating it like a key which was already revoked at the time it was requested. Fixes: 88bd6ccdcdd6 ("ext4 crypto: add encryption key management facilities") Reviewed-by: James Morris Cc: [v4.1+] Signed-off-by: Eric Biggers Signed-off-by: David Howells (cherry-picked from commit d60b5b7854c3d135b869f74fb93eaf63cbb1991a and fixed up for android-3.18) Change-Id: I7522324dc41969b5401fbe88ff90b4cd1f049cf2 Signed-off-by: Eric Biggers --- fs/crypto/keyinfo.c | 5 +++++ fs/ext4/crypto_key.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index be24dc401ac9..80e7fdfc7804 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -122,6 +122,11 @@ static int validate_user_key(struct fscrypt_info *crypt_info, goto out; } ukp = user_key_payload(keyring_key); + if (!ukp) { + /* key was revoked before we acquired its semaphore */ + res = -EKEYREVOKED; + goto out; + } if (ukp->datalen != sizeof(struct fscrypt_key)) { res = -EINVAL; goto out; diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index a4ea942ef6f6..998f6dac7a24 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -204,6 +204,12 @@ int ext4_get_encryption_info(struct inode *inode) } down_read(&keyring_key->sem); ukp = ((struct user_key_payload *)keyring_key->payload.data); + if (!ukp) { + /* key was revoked before we acquired its semaphore */ + res = -EKEYREVOKED; + up_read(&keyring_key->sem); + goto out; + } if (ukp->datalen != sizeof(struct ext4_encryption_key)) { res = -EINVAL; up_read(&keyring_key->sem); -- GitLab From 2ce7358044a2503ea230083046f30dd94b688a53 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Fri, 11 Aug 2017 15:00:19 +0530 Subject: [PATCH 1414/5498] msm: ipa: support aggregated ipa stats query add support on wan-driver to query modem or wlan-fw to get the total data usage for all tethered clients. Change-Id: I56f40f1c0f6b2ec4279e78b3aeb81c687d08bf2e Acked-by: Pooja Kumari Signed-off-by: Mohammed Javid Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c | 5 +- .../platform/msm/ipa/ipa_v2/ipa_qmi_service.h | 3 + drivers/platform/msm/ipa/ipa_v2/ipa_utils.c | 2 +- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 70 ++++++++++++++++++ .../msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c | 29 ++++++++ drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c | 5 +- .../platform/msm/ipa/ipa_v3/ipa_qmi_service.h | 3 + drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 2 +- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 74 +++++++++++++++++++ .../msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c | 28 +++++++ include/uapi/linux/msm_ipa.h | 14 +++- include/uapi/linux/rmnet_ipa_fd_ioctl.h | 15 ++++ 12 files changed, 244 insertions(+), 6 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index f33205c3e813..4c4a97f52987 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -85,7 +85,10 @@ const char *ipa_event_name[] = { __stringify(ADD_VLAN_IFACE), __stringify(DEL_VLAN_IFACE), __stringify(ADD_L2TP_VLAN_MAPPING), - __stringify(DEL_L2TP_VLAN_MAPPING) + __stringify(DEL_L2TP_VLAN_MAPPING), + __stringify(IPA_QUOTA_REACH), + __stringify(IPA_SSR_BEFORE_SHUTDOWN), + __stringify(IPA_SSR_AFTER_POWERUP), }; const char *ipa_hdr_l2_type_name[] = { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h index 45bae21c230e..18904d9b9322 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h @@ -176,6 +176,9 @@ int rmnet_ipa_set_tether_client_pipe(struct wan_ioctl_set_tether_client_pipe int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, bool reset); +int rmnet_ipa_query_tethering_stats_all( + struct wan_ioctl_query_tether_stats_all *data); + int ipa_qmi_get_data_stats(struct ipa_get_data_stats_req_msg_v01 *req, struct ipa_get_data_stats_resp_msg_v01 *resp); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 6783d20b06cb..7f0dde425f79 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -962,7 +962,7 @@ int ipa2_get_ep_mapping(enum ipa_client_type client) void ipa2_set_client(int index, enum ipacm_client_enum client, bool uplink) { - if (client >= IPACM_CLIENT_MAX || client < IPACM_CLIENT_USB) { + if (client > IPACM_CLIENT_MAX || client < IPACM_CLIENT_USB) { IPAERR("Bad client number! client =%d\n", client); } else if (index >= IPA_MAX_NUM_PIPES || index < 0) { IPAERR("Bad pipe index! index =%d\n", index); diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 9255ccc95bee..cf30cb2283b1 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2270,6 +2270,29 @@ static struct platform_driver rmnet_ipa_driver = { .remove = ipa_wwan_remove, }; +/** +* rmnet_ipa_send_ssr_notification(bool ssr_done) - send SSR notification +* +* This function sends the SSR notification before modem shutdown and +* after_powerup from SSR framework, to user-space module +*/ +static void rmnet_ipa_send_ssr_notification(bool ssr_done) +{ + struct ipa_msg_meta msg_meta; + int rc; + + memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); + if (ssr_done) + msg_meta.msg_type = IPA_SSR_AFTER_POWERUP; + else + msg_meta.msg_type = IPA_SSR_BEFORE_SHUTDOWN; + rc = ipa_send_msg(&msg_meta, NULL, NULL); + if (rc) { + IPAWANERR("ipa_send_msg failed: %d\n", rc); + return; + } +} + static int ssr_notifier_cb(struct notifier_block *this, unsigned long code, void *data) @@ -2277,6 +2300,8 @@ static int ssr_notifier_cb(struct notifier_block *this, if (ipa_rmnet_ctx.ipa_rmnet_ssr) { if (SUBSYS_BEFORE_SHUTDOWN == code) { pr_info("IPA received MPSS BEFORE_SHUTDOWN\n"); + /* send SSR before-shutdown notification to IPACM */ + rmnet_ipa_send_ssr_notification(false); atomic_set(&is_ssr, 1); ipa_q6_pre_shutdown_cleanup(); if (ipa_netdevs[0]) @@ -2452,6 +2477,25 @@ static void rmnet_ipa_get_network_stats_and_update(void) } } +/** +* rmnet_ipa_send_quota_reach_ind() - send quota_reach notification from +* IPA Modem +* This function sends the quota_reach indication from the IPA Modem driver +* via QMI, to user-space module +*/ +static void rmnet_ipa_send_quota_reach_ind(void) +{ + struct ipa_msg_meta msg_meta; + int rc; + + memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); + msg_meta.msg_type = IPA_QUOTA_REACH; + rc = ipa_send_msg(&msg_meta, NULL, NULL); + if (rc) { + IPAWANERR("ipa_send_msg failed: %d\n", rc); + return; + } +} /** * rmnet_ipa_poll_tethering_stats() - Tethering stats polling IOCTL handler * @data - IOCTL data @@ -2748,6 +2792,28 @@ int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, return 0; } +int rmnet_ipa_query_tethering_stats_all( + struct wan_ioctl_query_tether_stats_all *data) +{ + struct wan_ioctl_query_tether_stats tether_stats; + int rc = 0; + + memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats)); + + tether_stats.ipa_client = data->ipa_client; + rc = rmnet_ipa_query_tethering_stats( + &tether_stats, data->reset_stats); + if (rc) { + IPAWANERR("modem WAN_IOC_QUERY_TETHER_STATS failed\n"); + return rc; + } + data->tx_bytes = tether_stats.ipv4_tx_bytes + + tether_stats.ipv6_tx_bytes; + data->rx_bytes = tether_stats.ipv4_rx_bytes + + tether_stats.ipv6_rx_bytes; + return rc; +} + /** * ipa_broadcast_quota_reach_ind() - Send Netlink broadcast on Quota * @mux_id - The MUX ID on which the quota has been reached @@ -2798,6 +2864,7 @@ void ipa_broadcast_quota_reach_ind(u32 mux_id) IPAWANERR("putting nlmsg: <%s> <%s> <%s>\n", alert_msg, iface_name_l, iface_name_m); kobject_uevent_env(&(ipa_netdevs[0]->dev.kobj), KOBJ_CHANGE, envp); + rmnet_ipa_send_quota_reach_ind(); } /** @@ -2822,6 +2889,9 @@ void ipa_q6_handshake_complete(bool ssr_bootup) */ ipa2_proxy_clk_unvote(); + /* send SSR power-up notification to IPACM */ + rmnet_ipa_send_ssr_notification(true); + /* * It is required to recover the network stats after * SSR recovery diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c index 62bbd0f40dc2..7161b3ab78f3 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c @@ -47,6 +47,9 @@ #define WAN_IOC_QUERY_DL_FILTER_STATS32 _IOWR(WAN_IOC_MAGIC, \ WAN_IOCTL_QUERY_DL_FILTER_STATS, \ compat_uptr_t) +#define WAN_IOC_QUERY_TETHER_STATS_ALL32 _IOWR(WAN_IOC_MAGIC, \ + WAN_IOCTL_QUERY_TETHER_STATS_ALL, \ + compat_uptr_t) #endif static unsigned int dev_num = 1; @@ -238,6 +241,32 @@ static long wan_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; + case WAN_IOC_QUERY_TETHER_STATS_ALL: + IPAWANDBG_LOW("got WAN_IOC_QUERY_TETHER_STATS_ALL :>>>\n"); + pyld_sz = sizeof(struct wan_ioctl_query_tether_stats_all); + param = kzalloc(pyld_sz, GFP_KERNEL); + if (!param) { + retval = -ENOMEM; + break; + } + if (copy_from_user(param, (const void __user *)arg, pyld_sz)) { + retval = -EFAULT; + break; + } + + if (rmnet_ipa_query_tethering_stats_all( + (struct wan_ioctl_query_tether_stats_all *)param)) { + IPAWANERR("WAN_IOC_QUERY_TETHER_STATS failed\n"); + retval = -EFAULT; + break; + } + + if (copy_to_user((void __user *)arg, param, pyld_sz)) { + retval = -EFAULT; + break; + } + break; + case WAN_IOC_RESET_TETHER_STATS: IPAWANDBG_LOW("got WAN_IOC_RESET_TETHER_STATS :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_reset_tether_stats); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index 4baec40f2b7b..25159acf6e9b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -66,7 +66,10 @@ const char *ipa3_event_name[] = { __stringify(ADD_VLAN_IFACE), __stringify(DEL_VLAN_IFACE), __stringify(ADD_L2TP_VLAN_MAPPING), - __stringify(DEL_L2TP_VLAN_MAPPING) + __stringify(DEL_L2TP_VLAN_MAPPING), + __stringify(IPA_QUOTA_REACH), + __stringify(IPA_SSR_BEFORE_SHUTDOWN), + __stringify(IPA_SSR_AFTER_POWERUP), }; const char *ipa3_hdr_l2_type_name[] = { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h index c8020cb3eca4..1433a18c7454 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h @@ -216,6 +216,9 @@ int rmnet_ipa3_enable_per_client_stats(bool *data); int rmnet_ipa3_query_per_client_stats( struct wan_ioctl_query_per_client_stats *data); +int rmnet_ipa3_query_tethering_stats_all( + struct wan_ioctl_query_tether_stats_all *data); + int ipa3_qmi_get_data_stats(struct ipa_get_data_stats_req_msg_v01 *req, struct ipa_get_data_stats_resp_msg_v01 *resp); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index e4652e3794a0..98405e447a00 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -1810,7 +1810,7 @@ u8 ipa3_get_qmb_master_sel(enum ipa_client_type client) void ipa3_set_client(int index, enum ipacm_client_enum client, bool uplink) { - if (client >= IPACM_CLIENT_MAX || client < IPACM_CLIENT_USB) { + if (client > IPACM_CLIENT_MAX || client < IPACM_CLIENT_USB) { IPAERR("Bad client number! client =%d\n", client); } else if (index >= IPA3_MAX_NUM_PIPES || index < 0) { IPAERR("Bad pipe index! index =%d\n", index); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index eb128faa1933..09bb7c404516 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -2424,6 +2424,31 @@ static struct platform_driver rmnet_ipa_driver = { .remove = ipa3_wwan_remove, }; +/** +* +* rmnet_ipa_send_ssr_notification(bool ssr_done) - send SSR notification +* +* This function sends the SSR notification before modem shutdown and +* after_powerup from SSR framework, to user-space module +*/ + +static void rmnet_ipa_send_ssr_notification(bool ssr_done) +{ + struct ipa_msg_meta msg_meta; + int rc; + + memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); + if (ssr_done) + msg_meta.msg_type = IPA_SSR_AFTER_POWERUP; + else + msg_meta.msg_type = IPA_SSR_BEFORE_SHUTDOWN; + rc = ipa_send_msg(&msg_meta, NULL, NULL); + if (rc) { + IPAWANERR("ipa_send_msg failed: %d\n", rc); + return; + } +} + static int ipa3_ssr_notifier_cb(struct notifier_block *this, unsigned long code, void *data) @@ -2434,6 +2459,8 @@ static int ipa3_ssr_notifier_cb(struct notifier_block *this, switch (code) { case SUBSYS_BEFORE_SHUTDOWN: IPAWANINFO("IPA received MPSS BEFORE_SHUTDOWN\n"); + /* send SSR before-shutdown notification to IPACM */ + rmnet_ipa_send_ssr_notification(false); atomic_set(&rmnet_ipa3_ctx->is_ssr, 1); ipa3_q6_pre_shutdown_cleanup(); if (IPA_NETDEV()) @@ -2611,6 +2638,26 @@ static void rmnet_ipa_get_network_stats_and_update(void) } } +/** +* rmnet_ipa_send_quota_reach_ind() - send quota_reach notification from +* IPA Modem +* This function sends the quota_reach indication from the IPA Modem driver +* via QMI, to user-space module +*/ +static void rmnet_ipa_send_quota_reach_ind(void) +{ + struct ipa_msg_meta msg_meta; + int rc; + + memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); + msg_meta.msg_type = IPA_QUOTA_REACH; + rc = ipa_send_msg(&msg_meta, NULL, NULL); + if (rc) { + IPAWANERR("ipa_send_msg failed: %d\n", rc); + return; + } +} + /** * rmnet_ipa3_poll_tethering_stats() - Tethering stats polling IOCTL handler * @data - IOCTL data @@ -2906,6 +2953,29 @@ int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, return 0; } + +int rmnet_ipa3_query_tethering_stats_all( + struct wan_ioctl_query_tether_stats_all *data) +{ + struct wan_ioctl_query_tether_stats tether_stats; + int rc = 0; + + memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats)); + + tether_stats.ipa_client = data->ipa_client; + rc = rmnet_ipa3_query_tethering_stats( + &tether_stats, data->reset_stats); + if (rc) { + IPAWANERR("WAN_IOC_QUERY_TETHER_STATS failed\n"); + return rc; + } + data->tx_bytes = tether_stats.ipv4_tx_bytes + + tether_stats.ipv6_tx_bytes; + data->rx_bytes = tether_stats.ipv4_rx_bytes + + tether_stats.ipv6_rx_bytes; + return rc; +} + /** * ipa3_broadcast_quota_reach_ind() - Send Netlink broadcast on Quota * @mux_id - The MUX ID on which the quota has been reached @@ -2957,6 +3027,7 @@ void ipa3_broadcast_quota_reach_ind(u32 mux_id) alert_msg, iface_name_l, iface_name_m); kobject_uevent_env(&(IPA_NETDEV()->dev.kobj), KOBJ_CHANGE, envp); + rmnet_ipa_send_quota_reach_ind(); } /** @@ -2981,6 +3052,9 @@ void ipa3_q6_handshake_complete(bool ssr_bootup) */ ipa3_proxy_clk_unvote(); + /* send SSR power-up notification to IPACM */ + rmnet_ipa_send_ssr_notification(true); + /* * It is required to recover the network stats after * SSR recovery diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c index c7656e4a08d0..c428e18ac7f8 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c @@ -56,6 +56,9 @@ #define WAN_IOCTL_SET_LAN_CLIENT_INFO32 _IOWR(WAN_IOC_MAGIC, \ WAN_IOCTL_SET_LAN_CLIENT_INFO, \ compat_uptr_t) +#define WAN_IOC_QUERY_TETHER_STATS_ALL32 _IOWR(WAN_IOC_MAGIC, \ + WAN_IOCTL_QUERY_TETHER_STATS_ALL, \ + compat_uptr_t) #endif static unsigned int dev_num = 1; @@ -301,6 +304,31 @@ static long ipa3_wan_ioctl(struct file *filp, } break; + case WAN_IOC_QUERY_TETHER_STATS_ALL: + IPAWANDBG_LOW("got WAN_IOC_QUERY_TETHER_STATS_ALL :>>>\n"); + pyld_sz = sizeof(struct wan_ioctl_query_tether_stats_all); + param = kzalloc(pyld_sz, GFP_KERNEL); + if (!param) { + retval = -ENOMEM; + break; + } + if (copy_from_user(param, (const void __user *)arg, pyld_sz)) { + retval = -EFAULT; + break; + } + if (rmnet_ipa3_query_tethering_stats_all( + (struct wan_ioctl_query_tether_stats_all *)param)) { + IPAWANERR("WAN_IOC_QUERY_TETHER_STATS failed\n"); + retval = -EFAULT; + break; + } + + if (copy_to_user((void __user *)arg, param, pyld_sz)) { + retval = -EFAULT; + break; + } + break; + case WAN_IOC_RESET_TETHER_STATS: IPAWANDBG_LOW("device %s got WAN_IOC_RESET_TETHER_STATS :>>>\n", DRIVER_NAME); diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index b8afeae79606..081bbd91bfec 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -460,8 +460,18 @@ enum ipa_vlan_l2tp_event { IPA_VLAN_L2TP_EVENT_MAX, }; -#define IPA_EVENT_MAX_NUM (IPA_VLAN_L2TP_EVENT_MAX) -#define IPA_EVENT_MAX ((int)IPA_EVENT_MAX_NUM) +enum ipa_quota_event { + IPA_QUOTA_REACH = IPA_VLAN_L2TP_EVENT_MAX, + IPA_QUOTA_EVENT_MAX, +}; + +enum ipa_ssr_event { + IPA_SSR_BEFORE_SHUTDOWN = IPA_QUOTA_EVENT_MAX, + IPA_SSR_AFTER_POWERUP, + IPA_SSR_EVENT_MAX, +}; + +#define IPA_EVENT_MAX_NUM (IPA_SSR_EVENT_MAX) /** * enum ipa_rm_resource_name - IPA RM clients identification names diff --git a/include/uapi/linux/rmnet_ipa_fd_ioctl.h b/include/uapi/linux/rmnet_ipa_fd_ioctl.h index f588c3588647..7c081706500f 100644 --- a/include/uapi/linux/rmnet_ipa_fd_ioctl.h +++ b/include/uapi/linux/rmnet_ipa_fd_ioctl.h @@ -38,6 +38,7 @@ #define WAN_IOCTL_SET_LAN_CLIENT_INFO 13 #define WAN_IOCTL_CLEAR_LAN_CLIENT_INFO 14 #define WAN_IOCTL_SEND_LAN_CLIENT_MSG 15 +#define WAN_IOCTL_QUERY_TETHER_STATS_ALL 16 /* User space may not have this defined. */ #ifndef IFNAMSIZ @@ -88,6 +89,16 @@ struct wan_ioctl_set_tether_client_pipe { uint32_t dl_dst_pipe_list[QMI_IPA_MAX_PIPES_V01]; }; +struct wan_ioctl_query_tether_stats_all { + /* Name of the upstream interface */ + char upstreamIface[IFNAMSIZ]; + /* enum of tether interface */ + enum ipacm_client_enum ipa_client; + uint8_t reset_stats; + uint64_t tx_bytes; + uint64_t rx_bytes; +}; + struct wan_ioctl_query_tether_stats { /* Name of the upstream interface */ char upstreamIface[IFNAMSIZ]; @@ -235,4 +246,8 @@ struct wan_ioctl_query_per_client_stats { #define WAN_IOC_CLEAR_LAN_CLIENT_INFO _IOWR(WAN_IOC_MAGIC, \ WAN_IOCTL_CLEAR_LAN_CLIENT_INFO, \ struct wan_ioctl_lan_client_info *) + +#define WAN_IOC_QUERY_TETHER_STATS_ALL _IOWR(WAN_IOC_MAGIC, \ + WAN_IOCTL_QUERY_TETHER_STATS_ALL, \ + struct wan_ioctl_query_tether_stats_all *) #endif /* _RMNET_IPA_FD_IOCTL_H */ -- GitLab From 4565d126585676c3d0d186d3665ef9bb317df8c2 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Wed, 1 Nov 2017 11:20:03 +0530 Subject: [PATCH 1415/5498] USB: dwc3: gadget: Fix TxFIFO resizing logic The TxFIFO RAM start address for some USB controller might be non-zero. The current FIFO resizing logic in place always considers that this start address is 0x0000 and writes the RAM start address for subsequent TxFIFOs with the last FIFO depth only, leading to the controller not functioning properly. To make the controller work, start address of GTXFIFOSIZ(#n) should be written with the start address of GTXFIFOSIZ(0) + last FIFO depth. Fix the resizing logic accordingly. Change-Id: Ia83edef7165b980828f2a43832493be2349ae0dc Signed-off-by: Ajay Agarwal --- drivers/usb/dwc3/gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8d6bfdd810f9..55b962c6c429 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -218,7 +218,8 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc, struct dwc3_ep *dep) tmp = ((max_packet + mdwidth) * mult) + mdwidth; fifo_size = DIV_ROUND_UP(tmp, mdwidth); dep->fifo_depth = fifo_size; - fifo_size |= (dwc->last_fifo_depth << 16); + fifo_size |= (dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)) & 0xffff0000) + + (dwc->last_fifo_depth << 16); dwc->last_fifo_depth += (fifo_size & 0xffff); dev_dbg(dwc->dev, "%s ep_num:%d last_fifo_depth:%04x fifo_depth:%d\n", -- GitLab From d69579316bfbf68772ea26e269eb42d81788adf5 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Wed, 1 Nov 2017 15:58:59 +0530 Subject: [PATCH 1416/5498] dwc3: Preserve TxFIFO of IN/INT EP for UDC without tx-fifo-resize We are clearing existing allocated TxFIFO during set_config even if a controller does not have tx-fifo-resize flag set in the DT. Also we do not resize the FIFOs for such controller. As a result, the FIFO depths for the IN/INT EPs (excpet 0 IN) of the controller are 0 and it cannot work in device mode. Fix this issue by not clearing the default TxFIFO if tx-fifo-resize flag is not set. Change-Id: I5cf7d2eb017b8ed55348e578c10856d62a3e282e Signed-off-by: Ajay Agarwal --- drivers/usb/dwc3/ep0.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 745ff533df1e..ce242d26f5c4 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -598,22 +598,30 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) break; case USB_STATE_ADDRESS: - /* Read ep0IN related TXFIFO size */ - dwc->last_fifo_depth = (dwc3_readl(dwc->regs, - DWC3_GTXFIFOSIZ(0)) & 0xFFFF); - /* Clear existing allocated TXFIFO for all IN eps except ep0 */ - for (num = 0; num < dwc->num_in_eps; num++) { - dep = dwc->eps[(num << 1) | 1]; - if (num) { - dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), 0); - dep->fifo_depth = 0; - } else { - dep->fifo_depth = dwc->last_fifo_depth; - } - - dev_dbg(dwc->dev, "%s(): %s dep->fifo_depth:%x\n", + /* + * If tx-fifo-resize flag is not set for the controller, then + * do not clear existing allocated TXFIFO since we do not + * allocate it again in dwc3_gadget_resize_tx_fifos + */ + if (dwc->needs_fifo_resize) { + /* Read ep0IN related TXFIFO size */ + dwc->last_fifo_depth = (dwc3_readl(dwc->regs, + DWC3_GTXFIFOSIZ(0)) & 0xFFFF); + /* Clear existing TXFIFO for all IN eps except ep0 */ + for (num = 0; num < dwc->num_in_eps; num++) { + dep = dwc->eps[(num << 1) | 1]; + if (num) { + dwc3_writel(dwc->regs, + DWC3_GTXFIFOSIZ(num), 0); + dep->fifo_depth = 0; + } else { + dep->fifo_depth = dwc->last_fifo_depth; + } + + dev_dbg(dwc->dev, "%s(): %s fifo_depth:%x\n", __func__, dep->name, dep->fifo_depth); - dbg_event(0xFF, "fifo_reset", dep->number); + dbg_event(0xFF, "fifo_reset", dep->number); + } } ret = dwc3_ep0_delegate_req(dwc, ctrl); -- GitLab From daf3b388d8776825070e0045210676079b802e2e Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 27 Oct 2017 12:08:28 +0530 Subject: [PATCH 1417/5498] defconfig: Enable sdcardfs Enable sdcardfs for MSM8937/53/17/20/40, SDM450, MSM8996 MSM8937/17/20/40 msm8937_defconfig MSM8953, SDM450 msmcortex_defconfig MSM8996 msm_defconfig Change-Id: I4c76be4183076a6ff597bf20340bd7a57618fc56 Signed-off-by: Ankit Jain --- arch/arm/configs/msm8937-perf_defconfig | 1 + arch/arm/configs/msm8937_defconfig | 1 + arch/arm/configs/msmcortex-perf_defconfig | 1 + arch/arm/configs/msmcortex_defconfig | 1 + arch/arm64/configs/msm-perf_defconfig | 1 + arch/arm64/configs/msm8937-perf_defconfig | 1 + arch/arm64/configs/msm8937_defconfig | 1 + arch/arm64/configs/msm_defconfig | 1 + arch/arm64/configs/msmcortex-perf_defconfig | 1 + arch/arm64/configs/msmcortex_defconfig | 1 + 10 files changed, 10 insertions(+) diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig index 5007cd91c653..6f5fa17edaac 100644 --- a/arch/arm/configs/msm8937-perf_defconfig +++ b/arch/arm/configs/msm8937-perf_defconfig @@ -608,6 +608,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig index 5da435cff5c8..e68e6158b610 100644 --- a/arch/arm/configs/msm8937_defconfig +++ b/arch/arm/configs/msm8937_defconfig @@ -624,6 +624,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msmcortex-perf_defconfig b/arch/arm/configs/msmcortex-perf_defconfig index e0a82c8be586..1d28f3770f0d 100644 --- a/arch/arm/configs/msmcortex-perf_defconfig +++ b/arch/arm/configs/msmcortex-perf_defconfig @@ -595,6 +595,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index bbe065ac3034..97bf81865344 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -604,6 +604,7 @@ CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 4fc259bfe75d..d51f8ff95085 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -607,6 +607,7 @@ CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_ECRYPT_FS=y CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig index 88d6f93335e8..e1ca823804fc 100644 --- a/arch/arm64/configs/msm8937-perf_defconfig +++ b/arch/arm64/configs/msm8937-perf_defconfig @@ -617,6 +617,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig index 56664acc16c9..99fe22088bd8 100644 --- a/arch/arm64/configs/msm8937_defconfig +++ b/arch/arm64/configs/msm8937_defconfig @@ -632,6 +632,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index f2ace01d5aef..fb769c1cde09 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -627,6 +627,7 @@ CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_ECRYPT_FS=y CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 0a48d66a5ac6..0bbd7446b9d2 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -597,6 +597,7 @@ CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 5d2b0cfc233e..e71da71c255e 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -636,6 +636,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y -- GitLab From 57ae1f5552c0fb3c79301c0cbef78b93de16039d Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Fri, 21 Apr 2017 21:56:19 +0530 Subject: [PATCH 1418/5498] fs/dcache: Fix indefinite wait in d_invalidate() In the path of task exit, the proc dentries corresponding to this task will be killed by moving it to a shrink list. If those dentries are already claimed by another task for shrinking, the exiting task waits in a tight loop until those dentries are killed. This can potentially result in a deadlock if those dentries are corresponding to an RT task but the task which is shrinking those dentries is a lower priority task. Fix this by not doing tight loop, if our dentries are claimed by other task. Change-Id: If6848521469db7dea2bbba0dbaf8597094716267 Signed-off-by: Sahitya Tummala --- fs/dcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/dcache.c b/fs/dcache.c index 08bb8b0c2622..f35043c1c699 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1340,7 +1340,7 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry) goto out; if (dentry->d_flags & DCACHE_SHRINK_LIST) { - data->found++; + goto out; } else { if (dentry->d_flags & DCACHE_LRU_LIST) d_lru_del(dentry); -- GitLab From ef2449ef55370a0bcc53c13e408d205e6cd61b81 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Mon, 6 Nov 2017 11:48:12 +0530 Subject: [PATCH 1419/5498] spmi: pmic-arb: Correct the peripheral mapping table size spmi pmic arbiter V2 supports 256 peripherals. But the size of the peripheral mapping table in the driver is defined as 255, which leads to the buffer overflow. Fix it by correcting the mapping table size to 256. Change-Id: I88f1a01f28ad831811d3865b748beb1cba678c7d Signed-off-by: Kiran Gunda --- drivers/platform/msm/spmi/spmi-pmic-arb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/spmi/spmi-pmic-arb.c b/drivers/platform/msm/spmi/spmi-pmic-arb.c index 88961ae2bf7d..3cb309a233b2 100644 --- a/drivers/platform/msm/spmi/spmi-pmic-arb.c +++ b/drivers/platform/msm/spmi/spmi-pmic-arb.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -73,7 +73,7 @@ u32 pmic_arb_regs_v2[] = { #define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) #define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) -#define SPMI_MAPPING_TABLE_LEN 255 +#define SPMI_MAPPING_TABLE_LEN 256 #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ /* Ownership Table */ -- GitLab From a97d0a0971419b9737a6000eb15fbaf2efd3cee7 Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Mon, 6 Nov 2017 12:50:21 -0800 Subject: [PATCH 1420/5498] mhi_dev: mhi: Fix memory leak in ep_pcie event handler Free the event data structure in one of the handler cases to avoid memory leak. Change-Id: If9c1809b0db86df0929352396a1d3815cb57d651 Signed-off-by: Gustavo Solaira --- drivers/platform/msm/mhi_dev/mhi_sm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.c b/drivers/platform/msm/mhi_dev/mhi_sm.c index e35d35fc54bf..fbc83274e45d 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.c +++ b/drivers/platform/msm/mhi_dev/mhi_sm.c @@ -1192,6 +1192,7 @@ void mhi_dev_sm_pcie_handler(struct ep_pcie_notify *notify) ep_pcie_mask_irq_event(mhi_sm_ctx->mhi_dev->phandle, EP_PCIE_INT_EVT_MHI_A7, false); mhi_dev_notify_a7_event(mhi_sm_ctx->mhi_dev); + kfree(dstate_change_evt); goto exit; default: MHI_SM_ERR("Invalid ep_pcie event, received 0x%x event\n", -- GitLab From 49dd399fd20b12d1cb82df2baece12486e3e6f35 Mon Sep 17 00:00:00 2001 From: Nirmal Abraham Date: Thu, 2 Nov 2017 09:36:19 +0530 Subject: [PATCH 1421/5498] msm: mdp3: Error handling during histogram start failure The mdp3_session->lock mutex is not unlocked in few error paths in the histogram start flow. Add changes to unlock the mutex if histogram start fails for some reason. This makes sure that post-proc userspace tasks doesn't exit with mutex lock held thereby avoiding display freeze issues caused by other display tasks waiting on the session lock mutex. Change-Id: Ie16df32b3f4737d5cb064bd0ef30ee3143e9b67b Signed-off-by: Nirmal Abraham --- drivers/video/msm/mdss/mdp3_ctrl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index 34876c51d2e1..3e5f4c66a661 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -1808,12 +1808,15 @@ static int mdp3_histogram_start(struct mdp3_session_data *session, pr_debug("mdp3_histogram_start\n"); ret = mdp3_validate_start_req(req); - if (ret) + if (ret) { + mutex_unlock(&session->lock); return ret; + } if (!session->dma->histo_op || !session->dma->config_histo) { pr_err("mdp3_histogram_start not supported\n"); + mutex_unlock(&session->lock); return -EINVAL; } @@ -1822,6 +1825,7 @@ static int mdp3_histogram_start(struct mdp3_session_data *session, if (session->histo_status) { pr_info("mdp3_histogram_start already started\n"); mutex_unlock(&session->histo_lock); + mutex_unlock(&session->lock); return 0; } -- GitLab From 9be78884af51a1db0031923da9cc047da775feab Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 6 Nov 2017 16:17:05 +0530 Subject: [PATCH 1422/5498] defconfig: Enable sdcardfs Enable sdcardfs for msm8909 and for its variants. Change-Id: Icf46116194142ef9e105418bb729d96e1bb7bd9f Signed-off-by: Ankit Jain --- arch/arm/configs/msm8909-perf_defconfig | 1 + arch/arm/configs/msm8909_defconfig | 1 + arch/arm/configs/msm8909w-1gb-perf_defconfig | 1 + arch/arm/configs/msm8909w-1gb_defconfig | 1 + arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 6 files changed, 6 insertions(+) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index 7de592f49679..b8dda972a4b8 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -505,6 +505,7 @@ CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_SDCARD_FS=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y CONFIG_NLS_CODEPAGE_437=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index 0eda04c77b5b..3944e6fe6bf7 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -521,6 +521,7 @@ CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_SDCARD_FS=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y CONFIG_NLS_CODEPAGE_437=y diff --git a/arch/arm/configs/msm8909w-1gb-perf_defconfig b/arch/arm/configs/msm8909w-1gb-perf_defconfig index eeeae22338be..cfcb221b31cc 100644 --- a/arch/arm/configs/msm8909w-1gb-perf_defconfig +++ b/arch/arm/configs/msm8909w-1gb-perf_defconfig @@ -453,6 +453,7 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msm8909w-1gb_defconfig b/arch/arm/configs/msm8909w-1gb_defconfig index ada44b8677bf..7b7f6b6394ea 100644 --- a/arch/arm/configs/msm8909w-1gb_defconfig +++ b/arch/arm/configs/msm8909w-1gb_defconfig @@ -460,6 +460,7 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 9f928860118f..8e0542ab2671 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -394,6 +394,7 @@ CONFIG_QUOTA=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 7e43e5387cde..354ac91979d3 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -401,6 +401,7 @@ CONFIG_QUOTA=y CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_SDCARD_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y -- GitLab From c8140f201fb3e291e0894af7e5135f99da5801cb Mon Sep 17 00:00:00 2001 From: Jonathan Basseri Date: Wed, 25 Oct 2017 09:52:27 -0700 Subject: [PATCH 1423/5498] BACKPORT: xfrm: Clear sk_dst_cache when applying per-socket policy. If a socket has a valid dst cache, then xfrm_lookup_route will get skipped. However, the cache is not invalidated when applying policy to a socket (i.e. IPV6_XFRM_POLICY). The result is that new policies are sometimes ignored on those sockets. (Note: This was broken for IPv4 and IPv6 at different times.) This can be demonstrated like so, 1. Create UDP socket. 2. connect() the socket. 3. Apply an outbound XFRM policy to the socket. (setsockopt) 4. send() data on the socket. Packets will continue to be sent in the clear instead of matching an xfrm or returning a no-match error (EAGAIN). This affects calls to send() and not sendto(). Invalidating the sk_dst_cache is necessary to correctly apply xfrm policies. Since we do this in xfrm_user_policy(), the sk_lock was already acquired in either do_ip_setsockopt() or do_ipv6_setsockopt(), and we may call __sk_dst_reset(). Performance impact should be negligible, since this code is only called when changing xfrm policy, and only affects the socket in question. Change-Id: I54b4ec422aa5f4e31652a8c6913696f0a5610a51 Fixes: 00bc0ef5880d ("ipv6: Skip XFRM lookup if dst_entry in socket cache is valid") Tested: https://android-review.googlesource.com/517555 Tested: https://android-review.googlesource.com/418659 Signed-off-by: Jonathan Basseri Signed-off-by: Steffen Klassert (cherry picked from commit 2b06cdf3e688b98fcc9945873b5d42792bd4eee0) --- net/xfrm/xfrm_state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index de971b6d38c5..24c9945f1659 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1869,6 +1869,7 @@ int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen if (err >= 0) { xfrm_sk_policy_insert(sk, err, pol); xfrm_pol_put(pol); + __sk_dst_reset(sk); err = 0; } -- GitLab From 298efc5057e78775c3be26975f0ef591ed2efbd5 Mon Sep 17 00:00:00 2001 From: Yan He Date: Tue, 7 Nov 2017 19:18:33 -0800 Subject: [PATCH 1424/5498] msm: ep_pcie: enable L1ss exit for sending message Enable the request to exit L1ss when send MSI or LTR message. Change-Id: I68a506bd22f531acabc0c313f4839f53d59682cf Signed-off-by: Yan He --- drivers/platform/msm/ep_pcie/ep_pcie_com.h | 1 + drivers/platform/msm/ep_pcie/ep_pcie_core.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index 185c9cd8ef83..c58b94f48828 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -43,6 +43,7 @@ #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x1A8 #define PCIE20_PARF_Q2A_FLUSH 0x1AC #define PCIE20_PARF_LTSSM 0x1B0 +#define PCIE20_PARF_CFG_BITS 0x210 #define PCIE20_PARF_LTR_MSI_EXIT_L1SS 0x214 #define PCIE20_PARF_INT_ALL_STATUS 0x224 #define PCIE20_PARF_INT_ALL_CLEAR 0x228 diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index fb187dac37b3..b1abf7e19516 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -519,6 +519,9 @@ static void ep_pcie_core_init(struct ep_pcie_dev_t *dev, bool configured) /* Set AUX power to be on */ ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, 0, BIT(4)); + /* Request to exit from L1SS for MSI and LTR MSG */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_CFG_BITS, 0, BIT(1)); + EP_PCIE_DBG(dev, "Initial: CLASS_CODE_REVISION_ID:0x%x; HDR_TYPE:0x%x\n", readl_relaxed(dev->dm_core + PCIE20_CLASS_CODE_REVISION_ID), -- GitLab From b6885d31d1c6b6f4ccd50535d24dbe5c3d8a7d7b Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 9 Aug 2015 03:41:51 -0400 Subject: [PATCH 1425/5498] blk-mq: fix race between timeout and freeing request commit 0048b4837affd153897ed1222283492070027aa9 upstream. Inside timeout handler, blk_mq_tag_to_rq() is called to retrieve the request from one tag. This way is obviously wrong because the request can be freed any time and some fiedds of the request can't be trusted, then kernel oops might be triggered[1]. Currently wrt. blk_mq_tag_to_rq(), the only special case is that the flush request can share same tag with the request cloned from, and the two requests can't be active at the same time, so this patch fixes the above issue by updating tags->rqs[tag] with the active request(either flush rq or the request cloned from) of the tag. Also blk_mq_tag_to_rq() gets much simplified with this patch. Given blk_mq_tag_to_rq() is mainly for drivers and the caller must make sure the request can't be freed, so in bt_for_each() this helper is replaced with tags->rqs[tag]. [1] kernel oops log [ 439.696220] BUG: unable to handle kernel NULL pointer dereference at 0000000000000158^M [ 439.697162] IP: [] blk_mq_tag_to_rq+0x21/0x6e^M [ 439.700653] PGD 7ef765067 PUD 7ef764067 PMD 0 ^M [ 439.700653] Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC ^M [ 439.700653] Dumping ftrace buffer:^M [ 439.700653] (ftrace buffer empty)^M [ 439.700653] Modules linked in: nbd ipv6 kvm_intel kvm serio_raw^M [ 439.700653] CPU: 6 PID: 2779 Comm: stress-ng-sigfd Not tainted 4.2.0-rc5-next-20150805+ #265^M [ 439.730500] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011^M [ 439.730500] task: ffff880605308000 ti: ffff88060530c000 task.ti: ffff88060530c000^M [ 439.730500] RIP: 0010:[] [] blk_mq_tag_to_rq+0x21/0x6e^M [ 439.730500] RSP: 0018:ffff880819203da0 EFLAGS: 00010283^M [ 439.730500] RAX: ffff880811b0e000 RBX: ffff8800bb465f00 RCX: 0000000000000002^M [ 439.730500] RDX: 0000000000000000 RSI: 0000000000000202 RDI: 0000000000000000^M [ 439.730500] RBP: ffff880819203db0 R08: 0000000000000002 R09: 0000000000000000^M [ 439.730500] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000202^M [ 439.730500] R13: ffff880814104800 R14: 0000000000000002 R15: ffff880811a2ea00^M [ 439.730500] FS: 00007f165b3f5740(0000) GS:ffff880819200000(0000) knlGS:0000000000000000^M [ 439.730500] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b^M [ 439.730500] CR2: 0000000000000158 CR3: 00000007ef766000 CR4: 00000000000006e0^M [ 439.730500] Stack:^M [ 439.730500] 0000000000000008 ffff8808114eed90 ffff880819203e00 ffffffff812dc104^M [ 439.755663] ffff880819203e40 ffffffff812d9f5e 0000020000000000 ffff8808114eed80^M [ 439.755663] Call Trace:^M [ 439.755663] ^M [ 439.755663] [] bt_for_each+0x6e/0xc8^M [ 439.755663] [] ? blk_mq_rq_timed_out+0x6a/0x6a^M [ 439.755663] [] ? blk_mq_rq_timed_out+0x6a/0x6a^M [ 439.755663] [] blk_mq_tag_busy_iter+0x55/0x5e^M [ 439.755663] [] ? blk_mq_bio_to_request+0x38/0x38^M [ 439.755663] [] blk_mq_rq_timer+0x5d/0xd4^M [ 439.755663] [] call_timer_fn+0xf7/0x284^M [ 439.755663] [] ? call_timer_fn+0x5/0x284^M [ 439.755663] [] ? blk_mq_bio_to_request+0x38/0x38^M [ 439.755663] [] run_timer_softirq+0x1ce/0x1f8^M [ 439.755663] [] __do_softirq+0x181/0x3a4^M [ 439.755663] [] irq_exit+0x40/0x94^M [ 439.755663] [] smp_apic_timer_interrupt+0x33/0x3e^M [ 439.755663] [] apic_timer_interrupt+0x84/0x90^M [ 439.755663] ^M [ 439.755663] [] ? _raw_spin_unlock_irq+0x32/0x4a^M [ 439.755663] [] finish_task_switch+0xe0/0x163^M [ 439.755663] [] ? finish_task_switch+0xa2/0x163^M [ 439.755663] [] __schedule+0x469/0x6cd^M [ 439.755663] [] schedule+0x82/0x9a^M [ 439.789267] [] signalfd_read+0x186/0x49a^M [ 439.790911] [] ? wake_up_q+0x47/0x47^M [ 439.790911] [] __vfs_read+0x28/0x9f^M [ 439.790911] [] ? __fget_light+0x4d/0x74^M [ 439.790911] [] vfs_read+0x7a/0xc6^M [ 439.790911] [] SyS_read+0x49/0x7f^M [ 439.790911] [] entry_SYSCALL_64_fastpath+0x12/0x6f^M [ 439.790911] Code: 48 89 e5 e8 a9 b8 e7 ff 5d c3 0f 1f 44 00 00 55 89 f2 48 89 e5 41 54 41 89 f4 53 48 8b 47 60 48 8b 1c d0 48 8b 7b 30 48 8b 53 38 <48> 8b 87 58 01 00 00 48 85 c0 75 09 48 8b 97 88 0c 00 00 eb 10 ^M [ 439.790911] RIP [] blk_mq_tag_to_rq+0x21/0x6e^M [ 439.790911] RSP ^M [ 439.790911] CR2: 0000000000000158^M [ 439.790911] ---[ end trace d40af58949325661 ]---^M Signed-off-by: Ming Lei Signed-off-by: Jens Axboe Signed-off-by: Dmitry Shmidt Signed-off-by: Greg Kroah-Hartman --- block/blk-flush.c | 15 ++++++++++++++- block/blk-mq-tag.c | 2 +- block/blk-mq-tag.h | 12 ++++++++++++ block/blk-mq.c | 16 +--------------- block/blk.h | 6 ++++++ 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/block/blk-flush.c b/block/blk-flush.c index 20badd7b9d1b..9c423e53324a 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -73,6 +73,7 @@ #include "blk.h" #include "blk-mq.h" +#include "blk-mq-tag.h" /* FLUSH/FUA sequences */ enum { @@ -226,7 +227,12 @@ static void flush_end_io(struct request *flush_rq, int error) struct blk_flush_queue *fq = blk_get_flush_queue(q, flush_rq->mq_ctx); if (q->mq_ops) { + struct blk_mq_hw_ctx *hctx; + + /* release the tag's ownership to the req cloned from */ spin_lock_irqsave(&fq->mq_flush_lock, flags); + hctx = q->mq_ops->map_queue(q, flush_rq->mq_ctx->cpu); + blk_mq_tag_set_rq(hctx, flush_rq->tag, fq->orig_rq); flush_rq->tag = -1; } @@ -308,11 +314,18 @@ static bool blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq) /* * Borrow tag from the first request since they can't - * be in flight at the same time. + * be in flight at the same time. And acquire the tag's + * ownership for flush req. */ if (q->mq_ops) { + struct blk_mq_hw_ctx *hctx; + flush_rq->mq_ctx = first_rq->mq_ctx; flush_rq->tag = first_rq->tag; + fq->orig_rq = first_rq; + + hctx = q->mq_ops->map_queue(q, first_rq->mq_ctx->cpu); + blk_mq_tag_set_rq(hctx, first_rq->tag, flush_rq); } flush_rq->cmd_type = REQ_TYPE_FS; diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 702ae29b8d90..4fbc8d563777 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -403,7 +403,7 @@ static void bt_for_each(struct blk_mq_hw_ctx *hctx, for (bit = find_first_bit(&bm->word, bm->depth); bit < bm->depth; bit = find_next_bit(&bm->word, bm->depth, bit + 1)) { - rq = blk_mq_tag_to_rq(hctx->tags, off + bit); + rq = hctx->tags->rqs[off + bit]; if (rq->q == hctx->queue) fn(hctx, rq, data, reserved); } diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h index 6206ed17ef76..14c6e4c92556 100644 --- a/block/blk-mq-tag.h +++ b/block/blk-mq-tag.h @@ -85,4 +85,16 @@ static inline void blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) __blk_mq_tag_idle(hctx); } +/* + * This helper should only be used for flush request to share tag + * with the request cloned from, and both the two requests can't be + * in flight at the same time. The caller has to make sure the tag + * can't be freed. + */ +static inline void blk_mq_tag_set_rq(struct blk_mq_hw_ctx *hctx, + unsigned int tag, struct request *rq) +{ + hctx->tags->rqs[tag] = rq; +} + #endif diff --git a/block/blk-mq.c b/block/blk-mq.c index 691959ecb80f..4b839c117c56 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -498,23 +498,9 @@ void blk_mq_kick_requeue_list(struct request_queue *q) } EXPORT_SYMBOL(blk_mq_kick_requeue_list); -static inline bool is_flush_request(struct request *rq, - struct blk_flush_queue *fq, unsigned int tag) -{ - return ((rq->cmd_flags & REQ_FLUSH_SEQ) && - fq->flush_rq->tag == tag); -} - struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag) { - struct request *rq = tags->rqs[tag]; - /* mq_ctx of flush rq is always cloned from the corresponding req */ - struct blk_flush_queue *fq = blk_get_flush_queue(rq->q, rq->mq_ctx); - - if (!is_flush_request(rq, fq, tag)) - return rq; - - return fq->flush_rq; + return tags->rqs[tag]; } EXPORT_SYMBOL(blk_mq_tag_to_rq); diff --git a/block/blk.h b/block/blk.h index 43b036185712..e01b5ee80fe7 100644 --- a/block/blk.h +++ b/block/blk.h @@ -22,6 +22,12 @@ struct blk_flush_queue { struct list_head flush_queue[2]; struct list_head flush_data_in_flight; struct request *flush_rq; + + /* + * flush_rq shares tag with this rq, both can't be active + * at the same time + */ + struct request *orig_rq; spinlock_t mq_flush_lock; }; -- GitLab From 856849bd48eed8e216d3a31add70067144400bad Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 29 Oct 2017 11:02:04 +0100 Subject: [PATCH 1426/5498] ALSA: timer: Add missing mutex lock for compat ioctls commit 79fb0518fec8c8b4ea7f1729f54f293724b3dbb0 upstream. The races among ioctl and other operations were protected by the commit af368027a49a ("ALSA: timer: Fix race among timer ioctls") and later fixes, but one code path was forgotten in the scenario: the 32bit compat ioctl. As syzkaller recently spotted, a very similar use-after-free may happen with the combination of compat ioctls. The fix is simply to apply the same ioctl_lock to the compat_ioctl callback, too. Fixes: af368027a49a ("ALSA: timer: Fix race among timer ioctls") Reference: http://lkml.kernel.org/r/089e082686ac9b482e055c832617@google.com Reported-by: syzbot Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/timer_compat.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c index 2e908225d754..0b4b028e8e98 100644 --- a/sound/core/timer_compat.c +++ b/sound/core/timer_compat.c @@ -106,7 +106,8 @@ enum { #endif /* CONFIG_X86_X32 */ }; -static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) +static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) { void __user *argp = compat_ptr(arg); @@ -127,7 +128,7 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns case SNDRV_TIMER_IOCTL_PAUSE: case SNDRV_TIMER_IOCTL_PAUSE_OLD: case SNDRV_TIMER_IOCTL_NEXT_DEVICE: - return snd_timer_user_ioctl(file, cmd, (unsigned long)argp); + return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp); case SNDRV_TIMER_IOCTL_INFO32: return snd_timer_user_info_compat(file, argp); case SNDRV_TIMER_IOCTL_STATUS32: @@ -139,3 +140,15 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns } return -ENOIOCTLCMD; } + +static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct snd_timer_user *tu = file->private_data; + long ret; + + mutex_lock(&tu->ioctl_lock); + ret = __snd_timer_user_ioctl_compat(file, cmd, arg); + mutex_unlock(&tu->ioctl_lock); + return ret; +} -- GitLab From e2943f47605b0e1be306f10887c1255007de54c4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 29 Oct 2017 11:10:43 +0100 Subject: [PATCH 1427/5498] ALSA: seq: Fix nested rwsem annotation for lockdep splat commit 1f20f9ff57ca23b9f5502fca85ce3977e8496cb1 upstream. syzkaller reported the lockdep splat due to the possible deadlock of grp->list_mutex of each sequencer client object. Actually this is rather a false-positive report due to the missing nested lock annotations. The sequencer client may deliver the event directly to another client which takes another own lock. For addressing this issue, this patch replaces the simple down_read() with down_read_nested(). As a lock subclass, the already existing "hop" can be re-used, which indicates the depth of the call. Reference: http://lkml.kernel.org/r/089e082686ac9b482e055c832617@google.com Reported-by: syzbot Reported-by: Dmitry Vyukov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_clientmgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index a3988a4bcfd6..0a52e377a617 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -676,7 +676,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client, if (atomic) read_lock(&grp->list_lock); else - down_read(&grp->list_mutex); + down_read_nested(&grp->list_mutex, hop); list_for_each_entry(subs, &grp->list_head, src_list) { /* both ports ready? */ if (atomic_read(&subs->ref_count) != 2) -- GitLab From e5c2a548f826c9fb1c8b0d41576fc01f81c523ad Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 30 Oct 2017 13:28:03 +1100 Subject: [PATCH 1428/5498] cifs: check MaxPathNameComponentLength != 0 before using it commit f74bc7c6679200a4a83156bb89cbf6c229fe8ec0 upstream. And fix tcon leak in error path. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French Reviewed-by: David Disseldorp Signed-off-by: Greg Kroah-Hartman --- fs/cifs/dir.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 6fe4526b5209..6dc46b6e6cb5 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -193,7 +193,8 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon) struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); int i; - if (unlikely(direntry->d_name.len > + if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength && + direntry->d_name.len > le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength))) return -ENAMETOOLONG; @@ -509,7 +510,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, rc = check_name(direntry, tcon); if (rc) - goto out_free_xid; + goto out; server = tcon->ses->server; -- GitLab From bcb91ec291c146b896b4b36c44f3f1ac4c6a258d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 2 Nov 2017 00:47:03 +0000 Subject: [PATCH 1429/5498] KEYS: return full count in keyring_read() if buffer is too small commit 3239b6f29bdfb4b0a2ba59df995fc9e6f4df7f1f upstream. Commit e645016abc80 ("KEYS: fix writing past end of user-supplied buffer in keyring_read()") made keyring_read() stop corrupting userspace memory when the user-supplied buffer is too small. However it also made the return value in that case be the short buffer size rather than the size required, yet keyctl_read() is actually documented to return the size required. Therefore, switch it over to the documented behavior. Note that for now we continue to have it fill the short buffer, since it did that before (pre-v3.13) and dump_key_tree_aux() in keyutils arguably relies on it. Fixes: e645016abc80 ("KEYS: fix writing past end of user-supplied buffer in keyring_read()") Reported-by: Ben Hutchings Signed-off-by: Eric Biggers Signed-off-by: David Howells Reviewed-by: James Morris Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/keys/keyring.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 6d913f40b6f0..ac424781d54d 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -452,34 +452,33 @@ static long keyring_read(const struct key *keyring, char __user *buffer, size_t buflen) { struct keyring_read_iterator_context ctx; - unsigned long nr_keys; - int ret; + long ret; kenter("{%d},,%zu", key_serial(keyring), buflen); if (buflen & (sizeof(key_serial_t) - 1)) return -EINVAL; - nr_keys = keyring->keys.nr_leaves_on_tree; - if (nr_keys == 0) - return 0; - - /* Calculate how much data we could return */ - if (!buffer || !buflen) - return nr_keys * sizeof(key_serial_t); - - /* Copy the IDs of the subscribed keys into the buffer */ - ctx.buffer = (key_serial_t __user *)buffer; - ctx.buflen = buflen; - ctx.count = 0; - ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx); - if (ret < 0) { - kleave(" = %d [iterate]", ret); - return ret; + /* Copy as many key IDs as fit into the buffer */ + if (buffer && buflen) { + ctx.buffer = (key_serial_t __user *)buffer; + ctx.buflen = buflen; + ctx.count = 0; + ret = assoc_array_iterate(&keyring->keys, + keyring_read_iterator, &ctx); + if (ret < 0) { + kleave(" = %ld [iterate]", ret); + return ret; + } } - kleave(" = %zu [ok]", ctx.count); - return ctx.count; + /* Return the size of the buffer needed */ + ret = keyring->keys.nr_leaves_on_tree * sizeof(key_serial_t); + if (ret <= buflen) + kleave("= %ld [ok]", ret); + else + kleave("= %ld [buffer too small]", ret); + return ret; } /* -- GitLab From ef2518bac6306b00cae25b6f9232d73ebe3804e9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 2 Nov 2017 00:47:19 +0000 Subject: [PATCH 1430/5498] KEYS: fix out-of-bounds read during ASN.1 parsing commit 2eb9eabf1e868fda15808954fb29b0f105ed65f1 upstream. syzkaller with KASAN reported an out-of-bounds read in asn1_ber_decoder(). It can be reproduced by the following command, assuming CONFIG_X509_CERTIFICATE_PARSER=y and CONFIG_KASAN=y: keyctl add asymmetric desc $'\x30\x30' @s The bug is that the length of an ASN.1 data value isn't validated in the case where it is encoded using the short form, causing the decoder to read past the end of the input buffer. Fix it by validating the length. The bug report was: BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x10cb/0x1730 lib/asn1_decoder.c:233 Read of size 1 at addr ffff88003cccfa02 by task syz-executor0/6818 CPU: 1 PID: 6818 Comm: syz-executor0 Not tainted 4.14.0-rc7-00008-g5f479447d983 #2 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:16 [inline] dump_stack+0xb3/0x10b lib/dump_stack.c:52 print_address_description+0x79/0x2a0 mm/kasan/report.c:252 kasan_report_error mm/kasan/report.c:351 [inline] kasan_report+0x236/0x340 mm/kasan/report.c:409 __asan_report_load1_noabort+0x14/0x20 mm/kasan/report.c:427 asn1_ber_decoder+0x10cb/0x1730 lib/asn1_decoder.c:233 x509_cert_parse+0x1db/0x650 crypto/asymmetric_keys/x509_cert_parser.c:89 x509_key_preparse+0x64/0x7a0 crypto/asymmetric_keys/x509_public_key.c:174 asymmetric_key_preparse+0xcb/0x1a0 crypto/asymmetric_keys/asymmetric_type.c:388 key_create_or_update+0x347/0xb20 security/keys/key.c:855 SYSC_add_key security/keys/keyctl.c:122 [inline] SyS_add_key+0x1cd/0x340 security/keys/keyctl.c:62 entry_SYSCALL_64_fastpath+0x1f/0xbe RIP: 0033:0x447c89 RSP: 002b:00007fca7a5d3bd8 EFLAGS: 00000246 ORIG_RAX: 00000000000000f8 RAX: ffffffffffffffda RBX: 00007fca7a5d46cc RCX: 0000000000447c89 RDX: 0000000020006f4a RSI: 0000000020006000 RDI: 0000000020001ff5 RBP: 0000000000000046 R08: fffffffffffffffd R09: 0000000000000000 R10: 0000000000000002 R11: 0000000000000246 R12: 0000000000000000 R13: 0000000000000000 R14: 00007fca7a5d49c0 R15: 00007fca7a5d4700 Fixes: 42d5ec27f873 ("X.509: Add an ASN.1 decoder") Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- lib/asn1_decoder.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c index 806c5b6b4b3a..828ead6b78a8 100644 --- a/lib/asn1_decoder.c +++ b/lib/asn1_decoder.c @@ -276,6 +276,9 @@ next_op: if (unlikely(len > datalen - dp)) goto data_overrun_error; } + } else { + if (unlikely(len > datalen - dp)) + goto data_overrun_error; } if (flags & FLAG_CONS) { -- GitLab From 211b1914f59a95bcecefec72b952748b8166cadd Mon Sep 17 00:00:00 2001 From: Ricard Wanderlof Date: Thu, 7 Sep 2017 15:31:38 +0200 Subject: [PATCH 1431/5498] ASoC: adau17x1: Workaround for noise bug in ADC commit 1e6f4fc06f6411adf98bbbe7fcd79442cd2b2a75 upstream. The ADC in the ADAU1361 (and possibly other Analog Devices codecs) exhibits a cyclic variation in the noise floor (in our test setup between -87 and -93 dB), a new value being attained within this range whenever a new capture stream is started. The cycle repeats after about 10 or 11 restarts. The workaround recommended by the manufacturer is to toggle the ADOSR bit in the Converter Control 0 register each time a new capture stream is started. I have verified that the patch fixes this problem on the ADAU1361, and according to the manufacturer toggling the bit in question in this manner will at least have no detrimental effect on other chips served by this driver. Signed-off-by: Ricard Wanderlof Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/adau17x1.c | 24 +++++++++++++++++++++++- sound/soc/codecs/adau17x1.h | 2 ++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 3e16c1c64115..f21980dd1c3a 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -88,6 +88,27 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w, return 0; } +static int adau17x1_adc_fixup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct adau *adau = snd_soc_codec_get_drvdata(codec); + + /* + * If we are capturing, toggle the ADOSR bit in Converter Control 0 to + * avoid losing SNR (workaround from ADI). This must be done after + * the ADC(s) have been enabled. According to the data sheet, it is + * normally illegal to set this bit when the sampling rate is 96 kHz, + * but according to ADI it is acceptable for this workaround. + */ + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_ADOSR, ADAU17X1_CONVERTER0_ADOSR); + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_ADOSR, 0); + + return 0; +} + static const char * const adau17x1_mono_stereo_text[] = { "Stereo", "Mono Left Channel (L+R)", @@ -119,7 +140,8 @@ static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = { SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0, &adau17x1_dac_mode_mux), - SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0), + SND_SOC_DAPM_ADC_E("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0, + adau17x1_adc_fixup, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0), SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0), SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0), diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index e4a557fd7155..8c066bee1bf7 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -119,5 +119,7 @@ bool adau17x1_has_dsp(struct adau *adau); #define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7 +#define ADAU17X1_CONVERTER0_ADOSR BIT(3) + #endif -- GitLab From 5814e7754a0a9608ec65f3fce274ed7c2aea031b Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 2 Nov 2017 16:12:03 +0000 Subject: [PATCH 1432/5498] arm64: ensure __dump_instr() checks addr_limit commit 7a7003b1da010d2b0d1dc8bf21c10f5c73b389f1 upstream. It's possible for a user to deliberately trigger __dump_instr with a chosen kernel address. Let's avoid problems resulting from this by using get_user() rather than __get_user(), ensuring that we don't erroneously access kernel memory. Where we use __dump_instr() on kernel text, we already switch to KERNEL_DS, so this shouldn't adversely affect those cases. Fixes: 60ffc30d5652810d ("arm64: Exception handling") Acked-by: Will Deacon Signed-off-by: Mark Rutland Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index bb8f140d24c8..27a2637543ff 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -115,7 +115,7 @@ static void dump_instr(const char *lvl, struct pt_regs *regs) for (i = -4; i < 1; i++) { unsigned int val, bad; - bad = __get_user(val, &((u32 *)addr)[i]); + bad = get_user(val, &((u32 *)addr)[i]); if (!bad) p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val); -- GitLab From e03cbe4026a460ed3d9c4b1d05ad5aaf3d367c38 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 20 Oct 2017 21:17:05 +0100 Subject: [PATCH 1433/5498] ARM: 8715/1: add a private asm/unaligned.h commit 1cce91dfc8f7990ca3aea896bfb148f240b12860 upstream. The asm-generic/unaligned.h header provides two different implementations for accessing unaligned variables: the access_ok.h version used when CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is set pretends that all pointers are in fact aligned, while the le_struct.h version convinces gcc that the alignment of a pointer is '1', to make it issue the correct load/store instructions depending on the architecture flags. On ARMv5 and older, we always use the second version, to let the compiler use byte accesses. On ARMv6 and newer, we currently use the access_ok.h version, so the compiler can use any instruction including stm/ldm and ldrd/strd that will cause an alignment trap. This trap can significantly impact performance when we have to do a lot of fixups and, worse, has led to crashes in the LZ4 decompressor code that does not have a trap handler. This adds an ARM specific version of asm/unaligned.h that uses the le_struct.h/be_struct.h implementation unconditionally. This should lead to essentially the same code on ARMv6+ as before, with the exception of using regular load/store instructions instead of the trapping instructions multi-register variants. The crash in the LZ4 decompressor code was probably introduced by the patch replacing the LZ4 implementation, commit 4e1a33b105dd ("lib: update LZ4 compressor module"), so linux-4.11 and higher would be affected most. However, we probably want to have this backported to all older stable kernels as well, to help with the performance issues. There are two follow-ups that I think we should also work on, but not backport to stable kernels, first to change the asm-generic version of the header to remove the ARM special case, and second to review all other uses of CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS to see if they might be affected by the same problem on ARM. Signed-off-by: Arnd Bergmann Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/include/asm/Kbuild | 1 - arch/arm/include/asm/unaligned.h | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 arch/arm/include/asm/unaligned.h diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild index 70cd84eb7fda..a2a14155b25d 100644 --- a/arch/arm/include/asm/Kbuild +++ b/arch/arm/include/asm/Kbuild @@ -37,4 +37,3 @@ generic-y += termbits.h generic-y += termios.h generic-y += timex.h generic-y += trace_clock.h -generic-y += unaligned.h diff --git a/arch/arm/include/asm/unaligned.h b/arch/arm/include/asm/unaligned.h new file mode 100644 index 000000000000..ab905ffcf193 --- /dev/null +++ b/arch/arm/include/asm/unaligned.h @@ -0,0 +1,27 @@ +#ifndef __ASM_ARM_UNALIGNED_H +#define __ASM_ARM_UNALIGNED_H + +/* + * We generally want to set CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS on ARMv6+, + * but we don't want to use linux/unaligned/access_ok.h since that can lead + * to traps on unaligned stm/ldm or strd/ldrd. + */ +#include + +#if defined(__LITTLE_ENDIAN) +# include +# include +# include +# define get_unaligned __get_unaligned_le +# define put_unaligned __put_unaligned_le +#elif defined(__BIG_ENDIAN) +# include +# include +# include +# define get_unaligned __get_unaligned_be +# define put_unaligned __put_unaligned_be +#else +# error need to define endianess +#endif + +#endif /* __ASM_ARM_UNALIGNED_H */ -- GitLab From 163e8355bbe9af2bb3ce7c4cc7a11ed6124b76bf Mon Sep 17 00:00:00 2001 From: Ashish Samant Date: Thu, 2 Nov 2017 15:59:37 -0700 Subject: [PATCH 1434/5498] ocfs2: fstrim: Fix start offset of first cluster group during fstrim commit 105ddc93f06ebe3e553f58563d11ed63dbcd59f0 upstream. The first cluster group descriptor is not stored at the start of the group but at an offset from the start. We need to take this into account while doing fstrim on the first cluster group. Otherwise we will wrongly start fstrim a few blocks after the desired start block and the range can cross over into the next cluster group and zero out the group descriptor there. This can cause filesytem corruption that cannot be fixed by fsck. Link: http://lkml.kernel.org/r/1507835579-7308-1-git-send-email-ashish.samant@oracle.com Signed-off-by: Ashish Samant Reviewed-by: Junxiao Bi Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/alloc.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index a93bf9892256..d9db90446c9d 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -7235,13 +7235,24 @@ out: static int ocfs2_trim_extent(struct super_block *sb, struct ocfs2_group_desc *gd, - u32 start, u32 count) + u64 group, u32 start, u32 count) { u64 discard, bcount; + struct ocfs2_super *osb = OCFS2_SB(sb); bcount = ocfs2_clusters_to_blocks(sb, count); - discard = le64_to_cpu(gd->bg_blkno) + - ocfs2_clusters_to_blocks(sb, start); + discard = ocfs2_clusters_to_blocks(sb, start); + + /* + * For the first cluster group, the gd->bg_blkno is not at the start + * of the group, but at an offset from the start. If we add it while + * calculating discard for first group, we will wrongly start fstrim a + * few blocks after the desried start block and the range can cross + * over into the next cluster group. So, add it only if this is not + * the first cluster group. + */ + if (group != osb->first_cluster_group_blkno) + discard += le64_to_cpu(gd->bg_blkno); trace_ocfs2_trim_extent(sb, (unsigned long long)discard, bcount); @@ -7249,7 +7260,7 @@ static int ocfs2_trim_extent(struct super_block *sb, } static int ocfs2_trim_group(struct super_block *sb, - struct ocfs2_group_desc *gd, + struct ocfs2_group_desc *gd, u64 group, u32 start, u32 max, u32 minbits) { int ret = 0, count = 0, next; @@ -7268,7 +7279,7 @@ static int ocfs2_trim_group(struct super_block *sb, next = ocfs2_find_next_bit(bitmap, max, start); if ((next - start) >= minbits) { - ret = ocfs2_trim_extent(sb, gd, + ret = ocfs2_trim_extent(sb, gd, group, start, next - start); if (ret < 0) { mlog_errno(ret); @@ -7366,7 +7377,8 @@ int ocfs2_trim_fs(struct super_block *sb, struct fstrim_range *range) } gd = (struct ocfs2_group_desc *)gd_bh->b_data; - cnt = ocfs2_trim_group(sb, gd, first_bit, last_bit, minlen); + cnt = ocfs2_trim_group(sb, gd, group, + first_bit, last_bit, minlen); brelse(gd_bh); gd_bh = NULL; if (cnt < 0) { -- GitLab From f449835963f47a3755d2f893a49928cf8d7ca58e Mon Sep 17 00:00:00 2001 From: Kasin Li Date: Mon, 19 Jun 2017 15:36:53 -0600 Subject: [PATCH 1435/5498] drm/msm: Fix potential buffer overflow issue commit 4a630fadbb29d9efaedb525f1a8f7449ad107641 upstream. In function submit_create, if nr_cmds or nr_bos is assigned with negative value, the allocated buffer may be small than intended. Using this buffer will lead to buffer overflow issue. Signed-off-by: Kasin Li Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/msm/msm_gem_submit.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 4a45ae01cc3e..8acdb8192d20 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -34,10 +34,13 @@ static inline void __user *to_user_ptr(u64 address) } static struct msm_gem_submit *submit_create(struct drm_device *dev, - struct msm_gpu *gpu, int nr) + struct msm_gpu *gpu, uint32_t nr) { struct msm_gem_submit *submit; - int sz = sizeof(*submit) + (nr * sizeof(submit->bos[0])); + uint64_t sz = sizeof(*submit) + (nr * sizeof(submit->bos[0])); + + if (sz > SIZE_MAX) + return NULL; submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); if (submit) { -- GitLab From 77336ca65ccf544bbca59d55b0a1bb10bf420fe3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 30 Jun 2017 10:59:15 +0300 Subject: [PATCH 1436/5498] drm/msm: fix an integer overflow test commit 65e93108891e571f177c202add9288eda9ac4100 upstream. We recently added an integer overflow check but it needs an additional tweak to work properly on 32 bit systems. The problem is that we're doing the right hand side of the assignment as type unsigned long so the max it will have an integer overflow instead of being larger than SIZE_MAX. That means the "sz > SIZE_MAX" condition is never true even on 32 bit systems. We need to first cast it to u64 and then do the math. Fixes: 4a630fadbb29 ("drm/msm: Fix potential buffer overflow issue") Signed-off-by: Dan Carpenter Acked-by: Jordan Crouse Signed-off-by: Rob Clark Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/msm/msm_gem_submit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 8acdb8192d20..258ec8644dee 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -37,7 +37,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, struct msm_gpu *gpu, uint32_t nr) { struct msm_gem_submit *submit; - uint64_t sz = sizeof(*submit) + (nr * sizeof(submit->bos[0])); + uint64_t sz = sizeof(*submit) + ((u64)nr * sizeof(submit->bos[0])); if (sz > SIZE_MAX) return NULL; -- GitLab From 6c3c4c7373de9d0891e874f710aaee833e937f40 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Oct 2017 13:12:25 +0200 Subject: [PATCH 1437/5498] x86/microcode/intel: Disable late loading on model 79 commit 723f2828a98c8ca19842042f418fb30dd8cfc0f7 upstream. Blacklist Broadwell X model 79 for late loading due to an erratum. Signed-off-by: Borislav Petkov Acked-by: Tony Luck Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20171018111225.25635-1-bp@alien8.de Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/microcode/intel.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index c6826d1e8082..0dd7bcae3f5b 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -267,6 +267,18 @@ static int get_ucode_fw(void *to, const void *from, size_t n) return 0; } +static bool is_blacklisted(unsigned int cpu) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86 == 6 && c->x86_model == 79) { + pr_err_once("late loading on model 79 is disabled.\n"); + return true; + } + + return false; +} + static enum ucode_state request_microcode_fw(int cpu, struct device *device, bool refresh_fw) { @@ -275,6 +287,9 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device, const struct firmware *firmware; enum ucode_state ret; + if (is_blacklisted(cpu)) + return UCODE_NFOUND; + sprintf(name, "intel-ucode/%02x-%02x-%02x", c->x86, c->x86_model, c->x86_mask); @@ -299,6 +314,9 @@ static int get_ucode_user(void *to, const void *from, size_t n) static enum ucode_state request_microcode_user(int cpu, const void __user *buf, size_t size) { + if (is_blacklisted(cpu)) + return UCODE_NFOUND; + return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user); } -- GitLab From 8c0ddbd6ae04154c09418fbb97e8ad06af905dc9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 7 Oct 2017 22:37:59 +0000 Subject: [PATCH 1438/5498] mmc: s3cmci: include linux/interrupt.h for tasklet_struct [ Upstream commit e1c6ec26b853e9062f0b3daaf695c546d0702953 ] I got this new build error on today's linux-next drivers/mmc/host/s3cmci.h:69:24: error: field 'pio_tasklet' has incomplete type struct tasklet_struct pio_tasklet; drivers/mmc/host/s3cmci.c: In function 's3cmci_enable_irq': drivers/mmc/host/s3cmci.c:390:4: error: implicit declaration of function 'enable_irq';did you mean 'enable_imask'? [-Werror=implicit-function-declaration] While I haven't found out why this happened now and not earlier, the solution is obvious, we should include the header that defines the structure. Signed-off-by: Arnd Bergmann Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/s3cmci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 94cddf381ba3..5186ac611564 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include -- GitLab From 2c9c8c7a44990c46ae24209896e5000a7aab2630 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 7 Oct 2017 22:37:59 +0000 Subject: [PATCH 1439/5498] staging: rtl8712u: Fix endian settings for structs describing network packets [ Upstream commit 221c46d28957bd6e2158abc2179ce4a8c9ce07d3 ] The headers describing a number of network packets do not have the correct endian settings for several types of data. Signed-off-by: Larry Finger Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8712/ieee80211.h | 84 ++++++++++++++--------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/staging/rtl8712/ieee80211.h b/drivers/staging/rtl8712/ieee80211.h index 8269be80437a..53c39ca99ee4 100644 --- a/drivers/staging/rtl8712/ieee80211.h +++ b/drivers/staging/rtl8712/ieee80211.h @@ -142,52 +142,52 @@ struct ieee_ibss_seq { }; struct ieee80211_hdr { - u16 frame_ctl; - u16 duration_id; + __le16 frame_ctl; + __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; - u16 seq_ctl; + __le16 seq_ctl; u8 addr4[ETH_ALEN]; -} __packed; +} __packed __aligned(2); struct ieee80211_hdr_3addr { - u16 frame_ctl; - u16 duration_id; + __le16 frame_ctl; + __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; - u16 seq_ctl; -} __packed; + __le16 seq_ctl; +} __packed __aligned(2); struct ieee80211_hdr_qos { - u16 frame_ctl; - u16 duration_id; + __le16 frame_ctl; + __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; - u16 seq_ctl; + __le16 seq_ctl; u8 addr4[ETH_ALEN]; - u16 qc; -} __packed; + __le16 qc; +} __packed __aligned(2); struct ieee80211_hdr_3addr_qos { - u16 frame_ctl; - u16 duration_id; + __le16 frame_ctl; + __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; - u16 seq_ctl; - u16 qc; + __le16 seq_ctl; + __le16 qc; } __packed; struct eapol { u8 snap[6]; - u16 ethertype; + __be16 ethertype; u8 version; u8 type; - u16 length; + __le16 length; } __packed; @@ -554,13 +554,13 @@ Total: 28-2340 bytes */ struct ieee80211_header_data { - u16 frame_ctl; - u16 duration_id; + __le16 frame_ctl; + __le16 duration_id; u8 addr1[6]; u8 addr2[6]; u8 addr3[6]; - u16 seq_ctrl; -}; + __le16 seq_ctrl; +} __packed __aligned(2); #define BEACON_PROBE_SSID_ID_POSITION 12 @@ -592,18 +592,18 @@ struct ieee80211_info_element { /* * These are the data types that can make up management packets * - u16 auth_algorithm; - u16 auth_sequence; - u16 beacon_interval; - u16 capability; + __le16 auth_algorithm; + __le16 auth_sequence; + __le16 beacon_interval; + __le16 capability; u8 current_ap[ETH_ALEN]; - u16 listen_interval; + __le16 listen_interval; struct { u16 association_id:14, reserved:2; } __packed; - u32 time_stamp[2]; - u16 reason; - u16 status; + __le32 time_stamp[2]; + __le16 reason; + __le16 status; */ #define IEEE80211_DEFAULT_TX_ESSID "Penguin" @@ -611,16 +611,16 @@ struct ieee80211_info_element { struct ieee80211_authentication { struct ieee80211_header_data header; - u16 algorithm; - u16 transaction; - u16 status; + __le16 algorithm; + __le16 transaction; + __le16 status; } __packed; struct ieee80211_probe_response { struct ieee80211_header_data header; - u32 time_stamp[2]; - u16 beacon_interval; - u16 capability; + __le32 time_stamp[2]; + __le16 beacon_interval; + __le16 capability; struct ieee80211_info_element info_element; } __packed; @@ -630,16 +630,16 @@ struct ieee80211_probe_request { struct ieee80211_assoc_request_frame { struct ieee80211_hdr_3addr header; - u16 capability; - u16 listen_interval; + __le16 capability; + __le16 listen_interval; struct ieee80211_info_element_hdr info_element; } __packed; struct ieee80211_assoc_response_frame { struct ieee80211_hdr_3addr header; - u16 capability; - u16 status; - u16 aid; + __le16 capability; + __le16 status; + __le16 aid; } __packed; struct ieee80211_txb { -- GitLab From f59fbdd3621693b36e4deb086f2d83031ec770a5 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Sat, 7 Oct 2017 22:37:59 +0000 Subject: [PATCH 1440/5498] ext4: fix stripe-unaligned allocations [ Upstream commit d9b22cf9f5466a057f2a4f1e642b469fa9d73117 ] When a filesystem is created using: mkfs.ext4 -b 4096 -E stride=512 and we try to allocate 64MB extent, we will end up directly in ext4_mb_complex_scan_group(). This is because the request is detected as power-of-two allocation (so we start in ext4_mb_regular_allocator() with ac_criteria == 0) however the check before ext4_mb_simple_scan_group() refuses the direct buddy scan because the allocation request is too large. Since cr == 0, the check whether we should use ext4_mb_scan_aligned() fails as well and we fall back to ext4_mb_complex_scan_group(). Fix the problem by checking for upper limit on power-of-two requests directly when detecting them. Reported-by: Ross Zwisler Signed-off-by: Jan Kara Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/ext4/mballoc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index da268894e7c7..6f51f016f80b 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2113,8 +2113,10 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac) * We search using buddy data only if the order of the request * is greater than equal to the sbi_s_mb_order2_reqs * You can tune it via /sys/fs/ext4//mb_order2_req + * We also support searching for power-of-two requests only for + * requests upto maximum buddy size we have constructed. */ - if (i >= sbi->s_mb_order2_reqs) { + if (i >= sbi->s_mb_order2_reqs && i <= sb->s_blocksize_bits + 2) { /* * This should tell if fe_len is exactly power of 2 */ @@ -2176,7 +2178,7 @@ repeat: } ac->ac_groups_scanned++; - if (cr == 0 && ac->ac_2order < sb->s_blocksize_bits+2) + if (cr == 0) ext4_mb_simple_scan_group(ac, &e4b); else if (cr == 1 && sbi->s_stripe && !(ac->ac_g_ex.fe_len % sbi->s_stripe)) -- GitLab From 52f145db2870583eeb6afcf414578f155b85179e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Sat, 7 Oct 2017 22:37:59 +0000 Subject: [PATCH 1441/5498] ext4: do not use stripe_width if it is not set [ Upstream commit 5469d7c3087ecaf760f54b447f11af6061b7c897 ] Avoid using stripe_width for sbi->s_stripe value if it is not actually set. It prevents using the stride for sbi->s_stripe. Signed-off-by: Jan Kara Signed-off-by: Theodore Ts'o Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 51454eb4e4cb..288aac46c317 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2441,9 +2441,9 @@ static unsigned long ext4_get_stripe_size(struct ext4_sb_info *sbi) if (sbi->s_stripe && sbi->s_stripe <= sbi->s_blocks_per_group) ret = sbi->s_stripe; - else if (stripe_width <= sbi->s_blocks_per_group) + else if (stripe_width && stripe_width <= sbi->s_blocks_per_group) ret = stripe_width; - else if (stride <= sbi->s_blocks_per_group) + else if (stride && stride <= sbi->s_blocks_per_group) ret = stride; else ret = 0; -- GitLab From 4d941aa300bfc3ab19db598fbe72520a47c93dcc Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Sat, 7 Oct 2017 22:38:00 +0000 Subject: [PATCH 1442/5498] i2c: riic: correctly finish transfers [ Upstream commit 71ccea095ea1d4efd004dab971be6d599e06fc3f ] This fixes the condition where the controller has not fully completed its final transfer and leaves the bus and controller in a undesirable state. At the end of the last transmitted byte, the existing driver would just signal for a STOP condition to be transmitted then immediately signal completion. However, the full STOP procedure might not have fully taken place by the time the runtime PM shuts off the peripheral clock, leaving the bus in a suspended state. Alternatively, the STOP condition on the bus may have completed, but when the next transaction is requested by the upper layer, not all the necessary register cleanup was finished from the last transfer which made the driver return BUS BUSY when it really wasn't. This patch now makes all transmit and receive transactions wait for the STOP condition to fully complete before signaling a completed transaction. With this new method, runtime PM no longer seems to be an issue. Fixes: 310c18a41450 ("i2c: riic: add driver") Signed-off-by: Chris Brandt Reviewed-by: Wolfram Sang Signed-off-by: Wolfram Sang Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-riic.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index af3b3d032a9f..7a7b71e97ba4 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -80,6 +80,7 @@ #define ICIER_TEIE 0x40 #define ICIER_RIE 0x20 #define ICIER_NAKIE 0x10 +#define ICIER_SPIE 0x08 #define ICSR2_NACKF 0x10 @@ -216,11 +217,10 @@ static irqreturn_t riic_tend_isr(int irq, void *data) return IRQ_NONE; } - if (riic->is_last || riic->err) + if (riic->is_last || riic->err) { + riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER); writeb(ICCR2_SP, riic->base + RIIC_ICCR2); - - writeb(0, riic->base + RIIC_ICIER); - complete(&riic->msg_done); + } return IRQ_HANDLED; } @@ -240,13 +240,13 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data) if (riic->bytes_left == 1) { /* STOP must come before we set ACKBT! */ - if (riic->is_last) + if (riic->is_last) { + riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER); writeb(ICCR2_SP, riic->base + RIIC_ICCR2); + } riic_clear_set_bit(riic, 0, ICMR3_ACKBT, RIIC_ICMR3); - writeb(0, riic->base + RIIC_ICIER); - complete(&riic->msg_done); } else { riic_clear_set_bit(riic, ICMR3_ACKBT, 0, RIIC_ICMR3); } @@ -259,6 +259,21 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t riic_stop_isr(int irq, void *data) +{ + struct riic_dev *riic = data; + + /* read back registers to confirm writes have fully propagated */ + writeb(0, riic->base + RIIC_ICSR2); + readb(riic->base + RIIC_ICSR2); + writeb(0, riic->base + RIIC_ICIER); + readb(riic->base + RIIC_ICIER); + + complete(&riic->msg_done); + + return IRQ_HANDLED; +} + static u32 riic_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; @@ -326,6 +341,7 @@ static struct riic_irq_desc riic_irqs[] = { { .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" }, { .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" }, { .res_num = 2, .isr = riic_tdre_isr, .name = "riic-tdre" }, + { .res_num = 3, .isr = riic_stop_isr, .name = "riic-stop" }, { .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" }, }; -- GitLab From e79412f61f0e9536d347ac8501f4104e340bc4f7 Mon Sep 17 00:00:00 2001 From: Oleh Kravchenko Date: Sat, 7 Oct 2017 22:38:00 +0000 Subject: [PATCH 1443/5498] cx231xx: Fix I2C on Internal Master 3 Bus [ Upstream commit 6c5da8031a3abfad259190d35f83d89568b72ee2 ] Internal Master 3 Bus can send and receive only 4 bytes per time. Signed-off-by: Oleh Kravchenko Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/cx231xx/cx231xx-core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 180103e48036..6ec0ac8a737e 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -365,7 +365,12 @@ int cx231xx_send_vendor_cmd(struct cx231xx *dev, */ if ((ven_req->wLength > 4) && ((ven_req->bRequest == 0x4) || (ven_req->bRequest == 0x5) || - (ven_req->bRequest == 0x6))) { + (ven_req->bRequest == 0x6) || + + /* Internal Master 3 Bus can send + * and receive only 4 bytes per time + */ + (ven_req->bRequest == 0x2))) { unsend_size = 0; pdata = ven_req->pBuff; -- GitLab From 5a2f613a97d9cb03912f24a132a01487aac64440 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Sat, 7 Oct 2017 22:38:00 +0000 Subject: [PATCH 1444/5498] xen/manage: correct return value check on xenbus_scanf() [ Upstream commit 4fed1b125eb6252bde478665fc05d4819f774fa8 ] A negative return value indicates an error; in fact the function at present won't ever return zero. Signed-off-by: Jan Beulich Reviewed-by: Juergen Gross Signed-off-by: Boris Ostrovsky Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/xen/manage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index bf1940706422..f905ba624b8d 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -272,7 +272,7 @@ static void sysrq_handler(struct xenbus_watch *watch, const char **vec, err = xenbus_transaction_start(&xbt); if (err) return; - if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { + if (xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key) < 0) { pr_err("Unable to read sysrq code in control/sysrq\n"); xenbus_transaction_end(xbt, 1); return; -- GitLab From ffe4638c9609bcf2d1bed35e9f302d1079b72678 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 7 Oct 2017 22:38:00 +0000 Subject: [PATCH 1445/5498] platform/x86: intel_mid_thermal: Fix module autoload [ Upstream commit a93151a72061e944a4915458b1b1d6d505c03bbf ] If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/platform/x86/intel_mid_thermal.ko | grep alias $ After this patch: $ modinfo drivers/platform/x86/intel_mid_thermal.ko | grep alias alias: platform:msic_thermal Signed-off-by: Javier Martinez Canillas Signed-off-by: Andy Shevchenko Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel_mid_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index ab7860a21a22..4709206e5eed 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -551,6 +551,7 @@ static const struct platform_device_id therm_id_table[] = { { "msic_thermal", 1 }, { } }; +MODULE_DEVICE_TABLE(platform, therm_id_table); static struct platform_driver mid_thermal_driver = { .driver = { -- GitLab From 4e3492219704ef7d8f7330af3ea03262f33672b5 Mon Sep 17 00:00:00 2001 From: frank zago Date: Sat, 7 Oct 2017 22:38:01 +0000 Subject: [PATCH 1446/5498] staging: lustre: hsm: stack overrun in hai_dump_data_field [ Upstream commit 22aadb91c0a0055935109c175f5446abfb130702 ] The function hai_dump_data_field will do a stack buffer overrun when cat'ing /sys/fs/lustre/.../hsm/actions if an action has some data in it. hai_dump_data_field uses snprintf. But there is no check for truncation, and the value returned by snprintf is used as-is. The coordinator code calls hai_dump_data_field with 12 bytes in the buffer. The 6th byte of data is printed incompletely to make room for the terminating NUL. However snprintf still returns 2, so when hai_dump_data_field writes the final NUL, it does it outside the reserved buffer, in the 13th byte of the buffer. This stack buffer overrun hangs my VM. Fix by checking that there is enough room for the next 2 characters plus the NUL terminator. Don't print half bytes. Change the format to 02X instead of .2X, which makes more sense. Signed-off-by: frank zago Intel-bug-id: https://jira.hpdd.intel.com/browse/LU-8171 Reviewed-on: http://review.whamcloud.com/20338 Reviewed-by: John L. Hammond Reviewed-by: Jean-Baptiste Riaux Reviewed-by: Oleg Drokin Signed-off-by: James Simmons Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../lustre/lustre/include/lustre/lustre_user.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h index 89794fdfec9d..3b93270d5146 100644 --- a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h +++ b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h @@ -1066,23 +1066,21 @@ struct hsm_action_item { * \retval buffer */ static inline char *hai_dump_data_field(struct hsm_action_item *hai, - char *buffer, int len) + char *buffer, size_t len) { - int i, sz, data_len; + int i, data_len; char *ptr; ptr = buffer; - sz = len; data_len = hai->hai_len - sizeof(*hai); - for (i = 0 ; (i < data_len) && (sz > 0) ; i++) { - int cnt; - - cnt = snprintf(ptr, sz, "%.2X", - (unsigned char)hai->hai_data[i]); - ptr += cnt; - sz -= cnt; + for (i = 0; (i < data_len) && (len > 2); i++) { + snprintf(ptr, 3, "%02X", (unsigned char)hai->hai_data[i]); + ptr += 2; + len -= 2; } + *ptr = '\0'; + return buffer; } -- GitLab From 4220879c089ec493322ef250105ac32bc1b8d9d4 Mon Sep 17 00:00:00 2001 From: Alexander Boyko Date: Sat, 7 Oct 2017 22:38:01 +0000 Subject: [PATCH 1447/5498] staging: lustre: ptlrpc: skip lock if export failed [ Upstream commit 4c43c27ddc461d8473cedd70f2549614641dfbc7 ] This patch resolves IO vs eviction race. After eviction failed export stayed at stale list, a client had IO processing and reconnected during it. A client sent brw rpc with last lock cookie and new connection. The lock with failed export was found and assert was happened. (ost_handler.c:1812:ost_prolong_lock_one()) ASSERTION( lock->l_export == opd->opd_exp ) failed: 1. Skip the lock at ldlm_handle2lock if lock export failed. 2. Validation of lock for IO was added at hpreq_check(). The lock searching is based on granted interval tree. If server doesn`t have a valid lock, it reply to client with ESTALE. Signed-off-by: Alexander Boyko Intel-bug-id: https://jira.hpdd.intel.com/browse/LU-7702 Seagate-bug-id: MRP-2787 Reviewed-on: http://review.whamcloud.com/18120 Reviewed-by: Fan Yong Reviewed-by: Vitaly Fertman Reviewed-by: Oleg Drokin Signed-off-by: James Simmons Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../staging/lustre/lustre/ldlm/ldlm_lock.c | 7 +++++++ .../staging/lustre/lustre/ptlrpc/service.c | 21 +++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c index 6140130b6056..23ce4a9d09f4 100644 --- a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c +++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c @@ -573,6 +573,13 @@ struct ldlm_lock *__ldlm_handle2lock(const struct lustre_handle *handle, if (lock == NULL) return NULL; + if (lock->l_export && lock->l_export->exp_failed) { + CDEBUG(D_INFO, "lock export failed: lock %p, exp %p\n", + lock, lock->l_export); + LDLM_LOCK_PUT(lock); + return NULL; + } + /* It's unlikely but possible that someone marked the lock as * destroyed after we did handle2object on it */ if (flags == 0 && ((lock->l_flags & LDLM_FL_DESTROYED)== 0)) { diff --git a/drivers/staging/lustre/lustre/ptlrpc/service.c b/drivers/staging/lustre/lustre/ptlrpc/service.c index a8df8a792333..7ca8d24464f6 100644 --- a/drivers/staging/lustre/lustre/ptlrpc/service.c +++ b/drivers/staging/lustre/lustre/ptlrpc/service.c @@ -1506,20 +1506,15 @@ static int ptlrpc_server_hpreq_init(struct ptlrpc_service_part *svcpt, * it may hit swab race at LU-1044. */ if (req->rq_ops->hpreq_check) { rc = req->rq_ops->hpreq_check(req); - /** - * XXX: Out of all current - * ptlrpc_hpreq_ops::hpreq_check(), only - * ldlm_cancel_hpreq_check() can return an error code; - * other functions assert in similar places, which seems - * odd. What also does not seem right is that handlers - * for those RPCs do not assert on the same checks, but - * rather handle the error cases. e.g. see - * ost_rw_hpreq_check(), and ost_brw_read(), - * ost_brw_write(). + if (rc == -ESTALE) { + req->rq_status = rc; + ptlrpc_error(req); + } + /** can only return error, + * 0 for normal request, + * or 1 for high priority request */ - if (rc < 0) - return rc; - LASSERT(rc == 0 || rc == 1); + LASSERT(rc <= 1); } spin_lock_bh(&req->rq_export->exp_rpc_lock); -- GitLab From dd795459b42d8f58fcc78f3297323bcb23b0fdf1 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Sat, 7 Oct 2017 22:38:01 +0000 Subject: [PATCH 1448/5498] s390/dasd: check for device error pointer within state change interrupts [ Upstream commit 2202134e48a3b50320aeb9e3dd1186833e9d7e66 ] Check if the device pointer is valid. Just a sanity check since we already are in the int handler of the device. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/s390/block/dasd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 35672b081254..7c73388f8c1c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1672,8 +1672,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, /* check for for attention message */ if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) { device = dasd_device_from_cdev_locked(cdev); - device->discipline->check_attention(device, irb->esw.esw1.lpum); - dasd_put_device(device); + if (!IS_ERR(device)) { + device->discipline->check_attention(device, + irb->esw.esw1.lpum); + dasd_put_device(device); + } } if (!cqr) -- GitLab From e24061ae703d4b2c7dc70a7c6d64d57a4dede405 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Sat, 7 Oct 2017 22:38:01 +0000 Subject: [PATCH 1449/5498] bt8xx: fix memory leak [ Upstream commit 6792eb0cf9310ec240b7e7c9bfa86dff4c758c68 ] If dvb_attach() fails then we were just printing an error message and exiting but the memory allocated to state was not released. Signed-off-by: Sudip Mukherjee Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/pci/bt8xx/dvb-bt8xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index d407244fd1bc..bd0f5b195188 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -680,6 +680,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) /* DST is not a frontend, attaching the ASIC */ if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) { pr_err("%s: Could not find a Twinhan DST\n", __func__); + kfree(state); break; } /* Attach other DST peripherals if any */ -- GitLab From ed7d79ee9e5ec514558f75b39c7f3809ca2e77eb Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Tue, 30 May 2017 20:52:26 +0200 Subject: [PATCH 1450/5498] xen: don't print error message in case of missing Xenstore entry [ Upstream commit 4e93b6481c87ea5afde944a32b4908357ec58992 ] When registering for the Xenstore watch of the node control/sysrq the handler will be called at once. Don't issue an error message if the Xenstore node isn't there, as it will be created only when an event is being triggered. Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Juergen Gross Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/xen/manage.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index f905ba624b8d..77909a2c6723 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -272,8 +272,16 @@ static void sysrq_handler(struct xenbus_watch *watch, const char **vec, err = xenbus_transaction_start(&xbt); if (err) return; - if (xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key) < 0) { - pr_err("Unable to read sysrq code in control/sysrq\n"); + err = xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key); + if (err < 0) { + /* + * The Xenstore watch fires directly after registering it and + * after a suspend/resume cycle. So ENOENT is no error but + * might happen in those cases. + */ + if (err != -ENOENT) + pr_err("Error %d reading sysrq code in control/sysrq\n", + err); xenbus_transaction_end(xbt, 1); return; } -- GitLab From f772276045d8f5b706b3d6cc60e86ed0371c1f62 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Fri, 10 Feb 2017 21:30:27 -0600 Subject: [PATCH 1451/5498] staging: r8712u: Fix Sparse warning in rtl871x_xmit.c [ Upstream commit 07222e535831b916221dd2a48a3047ec7e45dc72 ] Sparse reports the following: CHECK drivers/staging/rtl8712/rtl871x_xmit.c drivers/staging/rtl8712/rtl871x_xmit.c:350:44: warning: restricted __le32 degrades to integer drivers/staging/rtl8712/rtl871x_xmit.c:491:23: warning: incorrect type in initializer (different base types) drivers/staging/rtl8712/rtl871x_xmit.c:491:23: expected unsigned short [usertype] *fctrl drivers/staging/rtl8712/rtl871x_xmit.c:491:23: got restricted __le16 * drivers/staging/rtl8712/rtl871x_xmit.c:580:36: warning: incorrect type in assignment (different base types) drivers/staging/rtl8712/rtl871x_xmit.c:580:36: expected unsigned short [unsigned] [short] [usertype] drivers/staging/rtl8712/rtl871x_xmit.c:580:36: got restricted __be16 [usertype] Signed-off-by: Larry Finger Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8712/rtl871x_xmit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/rtl8712/rtl871x_xmit.c b/drivers/staging/rtl8712/rtl871x_xmit.c index f49acaf04076..565290b0991d 100644 --- a/drivers/staging/rtl8712/rtl871x_xmit.c +++ b/drivers/staging/rtl8712/rtl871x_xmit.c @@ -340,7 +340,8 @@ sint r8712_update_attrib(struct _adapter *padapter, _pkt *pkt, /* if in MP_STATE, update pkt_attrib from mp_txcmd, and overwrite * some settings above.*/ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) - pattrib->priority = (txdesc.txdw1 >> QSEL_SHT) & 0x1f; + pattrib->priority = + (le32_to_cpu(txdesc.txdw1) >> QSEL_SHT) & 0x1f; return _SUCCESS; } @@ -481,7 +482,7 @@ static sint make_wlanhdr(struct _adapter *padapter , u8 *hdr, struct ieee80211_hdr *pwlanhdr = (struct ieee80211_hdr *)hdr; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct qos_priv *pqospriv = &pmlmepriv->qospriv; - u16 *fctrl = &pwlanhdr->frame_ctl; + __le16 *fctrl = &pwlanhdr->frame_ctl; memset(hdr, 0, WLANHDR_OFFSET); SetFrameSubType(fctrl, pattrib->subtype); @@ -569,7 +570,7 @@ static sint r8712_put_snap(u8 *data, u16 h_proto) snap->oui[0] = oui[0]; snap->oui[1] = oui[1]; snap->oui[2] = oui[2]; - *(u16 *)(data + SNAP_SIZE) = htons(h_proto); + *(__be16 *)(data + SNAP_SIZE) = htons(h_proto); return SNAP_SIZE + sizeof(u16); } -- GitLab From 943dc0b3ef9f0168494d6dca305cd0cf53a0b3d4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Nov 2017 10:03:50 +0100 Subject: [PATCH 1452/5498] Linux 3.18.80 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fb97c5f3d3a0..8feea3dadc60 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 79 +SUBLEVEL = 80 EXTRAVERSION = NAME = Diseased Newt -- GitLab From f92af7866ecd170847a9efed6997274627d52967 Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Mon, 20 Jun 2016 10:11:39 +0530 Subject: [PATCH 1453/5498] usb: gadget: f_mbim: Fix extra decrement of notify_count on resume If usb_request for notification was already queued before bus_suspend then re-queuing the same on bus_resume fails with -EBUSY. Driver currently decrements notify_count on error making it negative. This causes driver to always hold one notification which is sent later when host send next command. Fix this by not decrementing notify_count if func_ep_queue failed with -EBUSY. CRs-fixed: 1028997 Change-Id: I88b298c96f01b4fca7ddd86e081a30ba2ae1d38b Signed-off-by: Manu Gautam --- drivers/usb/gadget/function/f_mbim.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_mbim.c b/drivers/usb/gadget/function/f_mbim.c index f2f95a315672..f1c6f28a8a39 100644 --- a/drivers/usb/gadget/function/f_mbim.c +++ b/drivers/usb/gadget/function/f_mbim.c @@ -707,7 +707,9 @@ static void mbim_do_notify(struct f_mbim *mbim) req, GFP_ATOMIC); spin_lock(&mbim->lock); if (status) { - atomic_dec(&mbim->not_port.notify_count); + /* ignore if request already queued before bus_resume */ + if (status != -EBUSY) + atomic_dec(&mbim->not_port.notify_count); pr_err("Queue notify request failed, err: %d\n", status); } -- GitLab From 3b935a0a047fe0282f1a20eb025cb83e2cd00776 Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Tue, 21 Oct 2014 01:59:03 +0100 Subject: [PATCH 1454/5498] UPSTREAM: arm64: optimize memcpy_{from,to}io() and memset_io() (cherry picked from commit 70ddb63a88bfd45eb6abe36e2bf4f8f351a447d7) Optimize memcpy_{from,to}io() and memset_io() by transferring in 64 bit as much as possible with minimized barrier usage. This simplest optimization brings faster throughput compare to current byte-by-byte read and write with barrier in the loop. Code's skeleton is taken from the powerpc. Link: http://lkml.kernel.org/p/20141020133304.GH23751@e104818-lin.cambridge.arm.com Reviewed-by: Catalin Marinas Reviewed-by: Trilok Soni Signed-off-by: Joonwoo Park Signed-off-by: Will Deacon Bug: 63716230 Change-Id: I27a3ecfcdb8d1e80ceac5eb5f71609283e0c901b --- arch/arm64/kernel/io.c | 66 +++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/arch/arm64/kernel/io.c b/arch/arm64/kernel/io.c index 7d37ead4d199..354be2a872ae 100644 --- a/arch/arm64/kernel/io.c +++ b/arch/arm64/kernel/io.c @@ -25,12 +25,26 @@ */ void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count) { - unsigned char *t = to; - while (count) { + while (count && (!IS_ALIGNED((unsigned long)from, 8) || + !IS_ALIGNED((unsigned long)to, 8))) { + *(u8 *)to = __raw_readb(from); + from++; + to++; count--; - *t = readb(from); - t++; + } + + while (count >= 8) { + *(u64 *)to = __raw_readq(from); + from += 8; + to += 8; + count -= 8; + } + + while (count) { + *(u8 *)to = __raw_readb(from); from++; + to++; + count--; } } EXPORT_SYMBOL(__memcpy_fromio); @@ -40,12 +54,26 @@ EXPORT_SYMBOL(__memcpy_fromio); */ void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count) { - const unsigned char *f = from; - while (count) { + while (count && (!IS_ALIGNED((unsigned long)to, 8) || + !IS_ALIGNED((unsigned long)from, 8))) { + __raw_writeb(*(volatile u8 *)from, to); + from++; + to++; count--; - writeb(*f, to); - f++; + } + + while (count >= 8) { + __raw_writeq(*(volatile u64 *)from, to); + from += 8; + to += 8; + count -= 8; + } + + while (count) { + __raw_writeb(*(volatile u8 *)from, to); + from++; to++; + count--; } } EXPORT_SYMBOL(__memcpy_toio); @@ -55,10 +83,28 @@ EXPORT_SYMBOL(__memcpy_toio); */ void __memset_io(volatile void __iomem *dst, int c, size_t count) { - while (count) { + u64 qc = (u8)c; + + qc |= qc << 8; + qc |= qc << 16; + qc |= qc << 32; + + while (count && !IS_ALIGNED((unsigned long)dst, 8)) { + __raw_writeb(c, dst); + dst++; count--; - writeb(c, dst); + } + + while (count >= 8) { + __raw_writeq(qc, dst); + dst += 8; + count -= 8; + } + + while (count) { + __raw_writeb(c, dst); dst++; + count--; } } EXPORT_SYMBOL(__memset_io); -- GitLab From 8acf9c92a896bf12e0d4b3632b8df420363ceb9b Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 24 Oct 2017 07:47:14 -0700 Subject: [PATCH 1455/5498] FROMLIST: arm64: Avoid aligning normal memory pointers in __memcpy_{to,from}io (cherry picked from arm64/for-next/core commit 9ca255bf041ddc7698b6906dbd846c0ba64b1fe1) __memcpy_{to,from}io fall back to byte-at-a-time copying if both the source and destination pointers are not 8-byte aligned. Since one of the pointers always points at normal memory, this is unnecessary and detrimental to performance, so only do byte copying until we hit an 8-byte boundary for the device pointer. This change was motivated by performance issues in the pstore driver. On a test platform, measuring probe time for pstore, console buffer size of 1/4MB and pmsg of 1/2MB, was in the 90-107ms region. Change managed to reduce it to 10-25ms, an improvement in boot time. Cc: Kees Cook Cc: Anton Vorontsov Cc: Tony Luck Cc: Catalin Marinas Cc: Will Deacon Cc: Anton Vorontsov Cc: Robin Murphy Signed-off-by: Mark Salyzyn Signed-off-by: Will Deacon Bug: 63716230 Change-Id: I245545e8243a54b44d30fbb0d0c71a9b8a77ef63 --- arch/arm64/kernel/io.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/arch/arm64/kernel/io.c b/arch/arm64/kernel/io.c index 354be2a872ae..79b17384effa 100644 --- a/arch/arm64/kernel/io.c +++ b/arch/arm64/kernel/io.c @@ -25,8 +25,7 @@ */ void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count) { - while (count && (!IS_ALIGNED((unsigned long)from, 8) || - !IS_ALIGNED((unsigned long)to, 8))) { + while (count && !IS_ALIGNED((unsigned long)from, 8)) { *(u8 *)to = __raw_readb(from); from++; to++; @@ -54,23 +53,22 @@ EXPORT_SYMBOL(__memcpy_fromio); */ void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count) { - while (count && (!IS_ALIGNED((unsigned long)to, 8) || - !IS_ALIGNED((unsigned long)from, 8))) { - __raw_writeb(*(volatile u8 *)from, to); + while (count && !IS_ALIGNED((unsigned long)to, 8)) { + __raw_writeb(*(u8 *)from, to); from++; to++; count--; } while (count >= 8) { - __raw_writeq(*(volatile u64 *)from, to); + __raw_writeq(*(u64 *)from, to); from += 8; to += 8; count -= 8; } while (count) { - __raw_writeb(*(volatile u8 *)from, to); + __raw_writeb(*(u8 *)from, to); from++; to++; count--; -- GitLab From e05f5e661fb6e1a659d8f06cd95c7989d8618097 Mon Sep 17 00:00:00 2001 From: Haibin Liu Date: Tue, 24 Oct 2017 20:32:48 +0800 Subject: [PATCH 1456/5498] msm: sensor: actuator: avoid accessing out of bound memory Issue: When total_steps is updated, after that, copy_from_user fails with an error, then, i2c_reg_tbl is not allocated. In this case, when calling msm_actuator_parse_i2c_params, it lead to out-of-bound memory write. Fix: 1) Assign total_steps to zero when error from copying. 2) Add NULL pointer check for i2c tbl. CRs-Fixed: 2111672 Change-Id: Ib9dcb182356e2df8078c131edfd0791fa95a35e0 Signed-off-by: Haibin Liu --- .../msm/camera_v2/sensor/actuator/msm_actuator.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 001faf56a5d7..1f55d389d43e 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -96,6 +96,11 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, return; } + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return; + } + size = a_ctrl->reg_tbl_size; write_arr = a_ctrl->reg_tbl; i2c_tbl = a_ctrl->i2c_reg_tbl; @@ -1278,9 +1283,11 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, if (copy_from_user(&a_ctrl->region_params, (void *)set_info->af_tuning_params.region_params, - a_ctrl->region_size * sizeof(struct region_params_t))) + a_ctrl->region_size * sizeof(struct region_params_t))) { + a_ctrl->total_steps = 0; + pr_err("Error copying region_params\n"); return -EFAULT; - + } if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) { cci_client = a_ctrl->i2c_client.cci_client; cci_client->sid = -- GitLab From c8bfbda6aadb5d8bde33c4ba873e334444575597 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Tue, 5 Sep 2017 16:44:07 +0530 Subject: [PATCH 1457/5498] msm: ais: isp: Reducing ISPIF stop waiting time. Initial ISPIF waiting was one second which causes hang for one second in case of sensor removal hence wait is reduced to 200ms. Change-Id: I531a880435983fe2e1e4f0f7616c6a6b1632dac8 Signed-off-by: Rahul Sharma --- drivers/media/platform/msm/ais/ispif/msm_ispif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/ais/ispif/msm_ispif.c b/drivers/media/platform/msm/ais/ispif/msm_ispif.c index 1698181c5687..ee60a3a2f4d4 100644 --- a/drivers/media/platform/msm/ais/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/ais/ispif/msm_ispif.c @@ -46,7 +46,7 @@ #define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY 0x02 #define ISPIF_TIMEOUT_SLEEP_US 1000 -#define ISPIF_TIMEOUT_ALL_US 1000000 +#define ISPIF_TIMEOUT_ALL_US 200000 #define ISPIF_SOF_DEBUG_COUNT 0 #undef CDBG -- GitLab From feb82a0059ab0bfcba2b170bda23d1237ff64282 Mon Sep 17 00:00:00 2001 From: Lior David Date: Mon, 16 Oct 2017 20:39:31 +0300 Subject: [PATCH 1458/5498] wil6210: fix length check in __wmi_send The current length check: sizeof(cmd) + len > r->entry_size will allow very large values of len (> U16_MAX - sizeof(cmd)) and can cause a buffer overflow. Fix the check to cover this case. In addition, ensure the mailbox entry_size is not too small, since this can also bypass the above check. Change-Id: Iecb4f53ef05da0e015bc954b57b0e40debb7c8b7 Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/interrupt.c | 22 +++++++++++++++++++- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 64046e0bd0a2..a37533cffc7c 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -356,6 +356,25 @@ static void wil_cache_mbox_regs(struct wil6210_priv *wil) wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); } +static bool wil_validate_mbox_regs(struct wil6210_priv *wil) +{ + size_t min_size = sizeof(struct wil6210_mbox_hdr) + + sizeof(struct wmi_cmd_hdr); + + if (wil->mbox_ctl.rx.entry_size < min_size) { + wil_err(wil, "rx mbox entry too small (%d)\n", + wil->mbox_ctl.rx.entry_size); + return false; + } + if (wil->mbox_ctl.tx.entry_size < min_size) { + wil_err(wil, "tx mbox entry too small (%d)\n", + wil->mbox_ctl.tx.entry_size); + return false; + } + + return true; +} + static irqreturn_t wil6210_irq_misc(int irq, void *cookie) { struct wil6210_priv *wil = cookie; @@ -391,7 +410,8 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) if (isr & ISR_MISC_FW_READY) { wil_dbg_irq(wil, "IRQ: FW ready\n"); wil_cache_mbox_regs(wil); - set_bit(wil_status_mbox_ready, wil->status); + if (wil_validate_mbox_regs(wil)) + set_bit(wil_status_mbox_ready, wil->status); /** * Actual FW ready indicated by the * WMI_FW_READY_EVENTID diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a47f293cfafc..234dc117de0d 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -210,7 +210,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) uint retry; int rc = 0; - if (sizeof(cmd) + len > r->entry_size) { + if (len > r->entry_size - sizeof(cmd)) { wil_err(wil, "WMI size too large: %d bytes, max is %d\n", (int)(sizeof(cmd) + len), r->entry_size); return -ERANGE; -- GitLab From 7abe9e590d85d5de1cd14be61da2cc42d8560f7d Mon Sep 17 00:00:00 2001 From: Lior David Date: Tue, 17 Oct 2017 11:13:36 +0300 Subject: [PATCH 1459/5498] wil6210: add block size checks during FW load When loading FW from file add block size checks to ensure a corrupted FW file will not cause the driver to write outside the device memory. Change-Id: I6d7342cd33b2c47b701bcca4ee3cd84febbc56a2 Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/fw_inc.c | 58 ++++++++++++++-------- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + drivers/net/wireless/ath/wil6210/wmi.c | 11 +++- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index 8f40eb301924..da13a81956f8 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -26,14 +26,17 @@ prefix_type, rowsize, \ groupsize, buf, len, ascii) -#define FW_ADDR_CHECK(ioaddr, val, msg) do { \ - ioaddr = wmi_buffer(wil, val); \ - if (!ioaddr) { \ - wil_err_fw(wil, "bad " msg ": 0x%08x\n", \ - le32_to_cpu(val)); \ - return -EINVAL; \ - } \ - } while (0) +static bool wil_fw_addr_check(struct wil6210_priv *wil, + void __iomem **ioaddr, __le32 val, + u32 size, const char *msg) +{ + *ioaddr = wmi_buffer_block(wil, val, size); + if (!(*ioaddr)) { + wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val)); + return false; + } + return true; +} /** * wil_fw_verify - verify firmware file validity @@ -165,7 +168,8 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data, return -EINVAL; } - FW_ADDR_CHECK(dst, d->addr, "address"); + if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address")) + return -EINVAL; wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr), s); wil_memcpy_toio_32(dst, d->data, s); @@ -197,7 +201,8 @@ static int fw_handle_fill(struct wil6210_priv *wil, const void *data, return -EINVAL; } - FW_ADDR_CHECK(dst, d->addr, "address"); + if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address")) + return -EINVAL; v = le32_to_cpu(d->value); wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n", @@ -253,7 +258,8 @@ static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data, u32 v = le32_to_cpu(block[i].value); u32 x, y; - FW_ADDR_CHECK(dst, block[i].addr, "address"); + if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address")) + return -EINVAL; x = readl(dst); y = (x & m) | (v & ~m); @@ -319,10 +325,15 @@ static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data, wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n", n, gw_cmd); - FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr"); - FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr"); - FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr"); - FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address"); + if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0, + "gateway_addr_addr") || + !wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0, + "gateway_value_addr") || + !wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0, + "gateway_cmd_addr") || + !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0, + "gateway_ctrl_address")) + return -EINVAL; wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x" " cmd 0x%08x ctl 0x%08x\n", @@ -378,12 +389,19 @@ static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data, wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n", n, gw_cmd); - FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr"); + if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0, + "gateway_addr_addr")) + return -EINVAL; for (k = 0; k < ARRAY_SIZE(block->value); k++) - FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k], - "gateway_value_addr"); - FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr"); - FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address"); + if (!wil_fw_addr_check(wil, &gwa_val[k], + d->gateway_value_addr[k], + 0, "gateway_value_addr")) + return -EINVAL; + if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0, + "gateway_cmd_addr") || + !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0, + "gateway_ctrl_address")) + return -EINVAL; wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n", le32_to_cpu(d->gateway_addr_addr), diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 49886f0f859b..d9a99aff5be3 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -785,6 +785,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); int wil_find_cid(struct wil6210_priv *wil, const u8 *mac); void wil_set_ethtoolops(struct net_device *ndev); +void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size); void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 234dc117de0d..191a5157f9ee 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -128,13 +128,15 @@ static u32 wmi_addr_remap(u32 x) /** * Check address validity for WMI buffer; remap if needed * @ptr - internal (linker) fw/ucode address + * @size - if non zero, validate the block does not + * exceed the device memory (bar) * * Valid buffer should be DWORD aligned * * return address for accessing buffer from the host; * if buffer is not valid, return NULL. */ -void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) +void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr_, u32 size) { u32 off; u32 ptr = le32_to_cpu(ptr_); @@ -149,10 +151,17 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) off = HOSTADDR(ptr); if (off > WIL6210_MEM_SIZE - 4) return NULL; + if (size && ((off + size > WIL6210_MEM_SIZE) || (off + size < off))) + return NULL; return wil->csr + off; } +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) +{ + return wmi_buffer_block(wil, ptr_, 0); +} + /** * Check address validity */ -- GitLab From 9e220200eaa03d0e9311e30a68c6facb1d41e098 Mon Sep 17 00:00:00 2001 From: Lior David Date: Tue, 17 Oct 2017 14:18:17 +0300 Subject: [PATCH 1460/5498] wil6210: missing length check in wmi_set_ie Add a length check in wmi_set_ie to detect unsigned integer overflow. Change-Id: Id1ec6a6218f3fe6e00cc3f9a8e674f8f843273f2 Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/wmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 191a5157f9ee..5c540e0d2e14 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1335,8 +1335,14 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) }; int rc; u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; - struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); + struct wmi_set_appie_cmd *cmd; + if (len < ie_len) { + rc = -EINVAL; + goto out; + } + + cmd = kzalloc(len, GFP_KERNEL); if (!cmd) { rc = -ENOMEM; goto out; -- GitLab From 78c566756320a4b6f4392597d245d4c84d0a8089 Mon Sep 17 00:00:00 2001 From: Lior David Date: Tue, 17 Oct 2017 15:44:26 +0300 Subject: [PATCH 1461/5498] wil6210: missing length check in wil_cfg80211_mgmt_tx Add a length check in wil_cfg80211_mgmt_tx to detect unsigned integer overflow. Change-Id: I37f988481433a2e1238831980715aef32aa89a85 Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/cfg80211.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 5d673e2d302b..73f044f6f45b 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -763,7 +763,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, u64 *cookie) { const u8 *buf = params->buf; - size_t len = params->len; + size_t len = params->len, total; struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; bool tx_status = false; @@ -787,7 +787,11 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, if (len < sizeof(struct ieee80211_mgmt)) return -EINVAL; - cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); + total = sizeof(*cmd) + len; + if (total < len) + return -EINVAL; + + cmd = kmalloc(total, GFP_KERNEL); if (!cmd) { rc = -ENOMEM; goto out; @@ -797,7 +801,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, cmd->len = cpu_to_le16(len); memcpy(cmd->payload, buf, len); - rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len, + rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, total, WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); if (rc == 0) tx_status = !evt.evt.status; -- GitLab From a085f9612e48d30341e4cd655868e66d842733a9 Mon Sep 17 00:00:00 2001 From: mraja Date: Wed, 8 Nov 2017 14:46:44 +0530 Subject: [PATCH 1462/5498] ARM: dts: msm: modify the cnss_sdio address in mdm9206 As per the new hardware specification, update the address of cnss_sdio device node. Also, keep this device node disabled by default. Change-Id: Iceaf36dc96c62b9a2b3d607748ebc4dcaff31b23 Signed-off-by: mraja --- arch/arm/boot/dts/qcom/mdm9206.dtsi | 6 ++++++ arch/arm/boot/dts/qcom/mdm9607.dtsi | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/mdm9206.dtsi b/arch/arm/boot/dts/qcom/mdm9206.dtsi index e7c6bc371b0d..5dc4bbb1e7aa 100644 --- a/arch/arm/boot/dts/qcom/mdm9206.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9206.dtsi @@ -29,6 +29,12 @@ &cnss_debug_mem { status = "disabled"; + reg = <0x82800000 0x200000>; +}; + +&cnss_sdio { + status = "disabled"; + reg = <0x82800000 0x200000>; }; &qcom_seecom { diff --git a/arch/arm/boot/dts/qcom/mdm9607.dtsi b/arch/arm/boot/dts/qcom/mdm9607.dtsi index 4f637e7cbf0d..9f8c509e5667 100644 --- a/arch/arm/boot/dts/qcom/mdm9607.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9607.dtsi @@ -575,7 +575,7 @@ status = "disabled"; }; - qcom,cnss-sdio { + cnss_sdio: qcom,cnss-sdio { compatible = "qcom,cnss_sdio"; reg = <0x87a00000 0x200000>; reg-names = "ramdump"; -- GitLab From 4ca8b29497a6799179b74252ffb3362cfadf61eb Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Tue, 12 Jul 2016 16:01:21 -0700 Subject: [PATCH 1463/5498] msm: mdss: move to a kthread for vsync_retire_work_handler vsync_retire_work_handler is in the critical display path and should never be delayed because of other non-FIFO work. bug 30115868 (cherry picked from commit 91fd2c322af5c111309e343e7d58f6e5e1841823) Change-Id: Ic97744a81198ff303f9c87e1cf7c2468b4356a25 Signed-off-by: Naseer Ahmed Signed-off-by: Nirmal Abraham --- drivers/video/msm/mdss/mdss_mdp.h | 5 ++++- drivers/video/msm/mdss/mdss_mdp_overlay.c | 25 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h index b9ac545128df..f33b908e0d62 100644 --- a/drivers/video/msm/mdss/mdss_mdp.h +++ b/drivers/video/msm/mdss/mdss_mdp.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "mdss.h" #include "mdss_mdp_hwio.h" @@ -1005,7 +1006,6 @@ struct mdss_overlay_private { struct sw_sync_timeline *vsync_timeline; struct mdss_mdp_vsync_handler vsync_retire_handler; - struct work_struct retire_work; int retire_cnt; bool kickoff_released; u32 cursor_ndx[2]; @@ -1019,6 +1019,9 @@ struct mdss_overlay_private { /* video frame info used by deterministic frame rate control */ struct mdss_mdp_frc_fsm *frc_fsm; u8 sd_transition_state; + struct kthread_worker worker; + struct kthread_work vsync_work; + struct task_struct *thread; }; struct mdss_mdp_set_ot_params { diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c index 6006a390a512..ccd1d94f172b 100644 --- a/drivers/video/msm/mdss/mdss_mdp_overlay.c +++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c @@ -6108,7 +6108,7 @@ static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd) * retire_signal api checks for retire_cnt with sync_mutex lock. */ - flush_work(&mdp5_data->retire_work); + flush_kthread_work(&mdp5_data->vsync_work); } ctl_stop: @@ -6313,13 +6313,13 @@ static void __vsync_retire_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t) } mdp5_data = mfd_to_mdp5_data(mfd); - schedule_work(&mdp5_data->retire_work); + queue_kthread_work(&mdp5_data->worker, &mdp5_data->vsync_work); } -static void __vsync_retire_work_handler(struct work_struct *work) +static void __vsync_retire_work_handler(struct kthread_work *work) { struct mdss_overlay_private *mdp5_data = - container_of(work, typeof(*mdp5_data), retire_work); + container_of(work, typeof(*mdp5_data), vsync_work); if (!mdp5_data->ctl || !mdp5_data->ctl->mfd) return; @@ -6410,6 +6410,7 @@ static int __vsync_retire_setup(struct msm_fb_data_type *mfd) { struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); char name[24]; + struct sched_param param = { .sched_priority = 5 }; snprintf(name, sizeof(name), "mdss_fb%d_retire", mfd->index); mdp5_data->vsync_timeline = sw_sync_timeline_create(name); @@ -6417,12 +6418,26 @@ static int __vsync_retire_setup(struct msm_fb_data_type *mfd) pr_err("cannot vsync create time line"); return -ENOMEM; } + + init_kthread_worker(&mdp5_data->worker); + init_kthread_work(&mdp5_data->vsync_work, __vsync_retire_work_handler); + + mdp5_data->thread = kthread_run(kthread_worker_fn, + &mdp5_data->worker, "vsync_retire_work"); + + if (IS_ERR(mdp5_data->thread)) { + pr_err("unable to start vsync thread\n"); + mdp5_data->thread = NULL; + return -ENOMEM; + } + + sched_setscheduler(mdp5_data->thread, SCHED_FIFO, ¶m); + mfd->mdp_sync_pt_data.get_retire_fence = __vsync_retire_get_fence; mdp5_data->vsync_retire_handler.vsync_handler = __vsync_retire_handle_vsync; mdp5_data->vsync_retire_handler.cmd_post_flush = false; - INIT_WORK(&mdp5_data->retire_work, __vsync_retire_work_handler); return 0; } -- GitLab From 7335d27404252c74ff190a6dd61a4089440d6e0c Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Thu, 26 Oct 2017 17:03:02 -0700 Subject: [PATCH 1464/5498] cnss2: Refactor callbacks for SSR framework SSR framework is specific for MSM kernels. Refactor the callbacks for SSR framework so that the same powerup/shutdown APIs can be also used for non-MSM kernels where SSR framework does not exist. Change-Id: Ic7bcafadc4f7ad3bd2fab0a4b672b2f5676fe401 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 123 ++++++++++++++++-------------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 6288a99fd8f2..a08e504d0993 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1187,26 +1187,9 @@ static void cnss_qca6290_crash_shutdown(struct cnss_plat_data *plat_priv) cnss_pci_collect_dump_info(pci_priv); } -static int cnss_powerup(const struct subsys_desc *subsys_desc) +static int cnss_powerup(struct cnss_plat_data *plat_priv) { - int ret = 0; - struct cnss_plat_data *plat_priv; - - if (!subsys_desc->dev) { - cnss_pr_err("dev from subsys_desc is NULL\n"); - return -ENODEV; - } - - plat_priv = dev_get_drvdata(subsys_desc->dev); - if (!plat_priv) { - cnss_pr_err("plat_priv is NULL!\n"); - return -ENODEV; - } - - if (!plat_priv->driver_state) { - cnss_pr_dbg("Powerup is ignored.\n"); - return 0; - } + int ret; switch (plat_priv->device_id) { case QCA6174_DEVICE_ID: @@ -1225,21 +1208,9 @@ static int cnss_powerup(const struct subsys_desc *subsys_desc) return ret; } -static int cnss_shutdown(const struct subsys_desc *subsys_desc, bool force_stop) +static int cnss_shutdown(struct cnss_plat_data *plat_priv) { - int ret = 0; - struct cnss_plat_data *plat_priv; - - if (!subsys_desc->dev) { - cnss_pr_err("dev from subsys_desc is NULL\n"); - return -ENODEV; - } - - plat_priv = dev_get_drvdata(subsys_desc->dev); - if (!plat_priv) { - cnss_pr_err("plat_priv is NULL!\n"); - return -ENODEV; - } + int ret; switch (plat_priv->device_id) { case QCA6174_DEVICE_ID: @@ -1258,6 +1229,53 @@ static int cnss_shutdown(const struct subsys_desc *subsys_desc, bool force_stop) return ret; } +static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc) +{ + struct cnss_plat_data *plat_priv; + + if (!subsys_desc->dev) { + cnss_pr_err("dev from subsys_desc is NULL\n"); + return -ENODEV; + } + + plat_priv = dev_get_drvdata(subsys_desc->dev); + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + if (!plat_priv->driver_state) { + cnss_pr_dbg("Powerup is ignored\n"); + return 0; + } + + return cnss_powerup(plat_priv); +} + +static int cnss_subsys_shutdown(const struct subsys_desc *subsys_desc, + bool force_stop) +{ + struct cnss_plat_data *plat_priv; + + if (!subsys_desc->dev) { + cnss_pr_err("dev from subsys_desc is NULL\n"); + return -ENODEV; + } + + plat_priv = dev_get_drvdata(subsys_desc->dev); + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + if (!plat_priv->driver_state) { + cnss_pr_dbg("shutdown is ignored\n"); + return 0; + } + + return cnss_shutdown(plat_priv); +} + static int cnss_qca6290_ramdump(struct cnss_plat_data *plat_priv) { struct cnss_ramdump_info_v2 *info_v2 = &plat_priv->ramdump_info_v2; @@ -1313,7 +1331,8 @@ static int cnss_qca6174_ramdump(struct cnss_plat_data *plat_priv) return ret; } -static int cnss_ramdump(int enable, const struct subsys_desc *subsys_desc) +static int cnss_subsys_ramdump(int enable, + const struct subsys_desc *subsys_desc) { int ret = 0; struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev); @@ -1375,7 +1394,7 @@ void cnss_device_crashed(struct device *dev) } EXPORT_SYMBOL(cnss_device_crashed); -static void cnss_crash_shutdown(const struct subsys_desc *subsys_desc) +static void cnss_subsys_crash_shutdown(const struct subsys_desc *subsys_desc) { struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev); @@ -1464,8 +1483,8 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv, return 0; self_recovery: - cnss_shutdown(&subsys_info->subsys_desc, false); - cnss_powerup(&subsys_info->subsys_desc); + cnss_shutdown(plat_priv); + cnss_powerup(plat_priv); return 0; } @@ -1614,12 +1633,11 @@ static int cnss_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data) { int ret = 0; - struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); plat_priv->driver_ops = data; - ret = cnss_powerup(&subsys_info->subsys_desc); + ret = cnss_powerup(plat_priv); if (ret) { clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); plat_priv->driver_ops = NULL; @@ -1630,10 +1648,8 @@ static int cnss_register_driver_hdlr(struct cnss_plat_data *plat_priv, static int cnss_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) { - struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; - set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); - cnss_shutdown(&subsys_info->subsys_desc, false); + cnss_shutdown(plat_priv); plat_priv->driver_ops = NULL; return 0; @@ -1642,10 +1658,9 @@ static int cnss_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; - struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; set_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); - ret = cnss_powerup(&subsys_info->subsys_desc); + ret = cnss_powerup(plat_priv); if (ret) clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); @@ -1654,10 +1669,8 @@ static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) { - struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; - cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); - cnss_shutdown(&subsys_info->subsys_desc, false); + cnss_shutdown(plat_priv); clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); return 0; @@ -1665,16 +1678,12 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) static int cnss_power_up_hdlr(struct cnss_plat_data *plat_priv) { - struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; - - return cnss_powerup(&subsys_info->subsys_desc); + return cnss_powerup(plat_priv); } static int cnss_power_down_hdlr(struct cnss_plat_data *plat_priv) { - struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; - - cnss_shutdown(&subsys_info->subsys_desc, false); + cnss_shutdown(plat_priv); return 0; } @@ -1799,10 +1808,10 @@ int cnss_register_subsys(struct cnss_plat_data *plat_priv) } subsys_info->subsys_desc.owner = THIS_MODULE; - subsys_info->subsys_desc.powerup = cnss_powerup; - subsys_info->subsys_desc.shutdown = cnss_shutdown; - subsys_info->subsys_desc.ramdump = cnss_ramdump; - subsys_info->subsys_desc.crash_shutdown = cnss_crash_shutdown; + subsys_info->subsys_desc.powerup = cnss_subsys_powerup; + subsys_info->subsys_desc.shutdown = cnss_subsys_shutdown; + subsys_info->subsys_desc.ramdump = cnss_subsys_ramdump; + subsys_info->subsys_desc.crash_shutdown = cnss_subsys_crash_shutdown; subsys_info->subsys_desc.dev = &plat_priv->plat_dev->dev; subsys_info->subsys_device = subsys_register(&subsys_info->subsys_desc); -- GitLab From b45ca68b4dce24ad282c8204d344d4ef2e32c363 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Mon, 30 Oct 2017 14:40:13 +0530 Subject: [PATCH 1465/5498] ASoC: msm: add machine driver support for 32 bit recording Add support for 32 bit format support and configure the clock values based on format for capture stream. CRs-Fixed: 2129947 Change-Id: Ica513f61ea9cdbedb86b8c1d5997abc41c1acd1d Signed-off-by: Surendar karka --- sound/soc/msm/msm8952.c | 87 +++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/sound/soc/msm/msm8952.c b/sound/soc/msm/msm8952.c index f504885dc2f2..8e495ac1789c 100644 --- a/sound/soc/msm/msm8952.c +++ b/sound/soc/msm/msm8952.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -68,6 +68,9 @@ static int msm_vi_feed_tx_ch = 2; static int mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int mi2s_rx_bits_per_sample = 16; static int mi2s_rx_sample_rate = SAMPLING_RATE_48KHZ; +static int mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int mi2s_tx_bits_per_sample = 16; +static int mi2s_tx_sample_rate = SAMPLING_RATE_48KHZ; static atomic_t quat_mi2s_clk_ref; static atomic_t quin_mi2s_clk_ref; @@ -161,7 +164,8 @@ static struct afe_clk_set wsa_ana_clk = { 0, }; -static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; static const char *const mi2s_ch_text[] = {"One", "Two"}; static const char *const loopback_mclk_text[] = {"DISABLE", "ENABLE"}; static const char *const btsco_rate_text[] = {"BTSCO_RATE_8KHZ", @@ -389,7 +393,7 @@ static int msm_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("%s(), channel:%d\n", __func__, msm_ter_mi2s_tx_ch); param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_FORMAT_S16_LE); + mi2s_tx_bit_format); rate->min = rate->max = 48000; channels->min = channels->max = msm_ter_mi2s_tx_ch; @@ -480,7 +484,7 @@ static int msm_mi2s_snd_hw_params(struct snd_pcm_substream *substream, mi2s_rx_bit_format); else param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_FORMAT_S16_LE); + mi2s_tx_bit_format); return 0; } @@ -549,7 +553,7 @@ static bool is_mi2s_rx_port(int port_id) return ret; } -static uint32_t get_mi2s_rx_clk_val(int port_id) +static uint32_t get_mi2s_clk_val(int port_id) { uint32_t clk_val = 0; @@ -559,8 +563,10 @@ static uint32_t get_mi2s_rx_clk_val(int port_id) */ if (is_mi2s_rx_port(port_id)) clk_val = (mi2s_rx_sample_rate * mi2s_rx_bits_per_sample * 2); + else + clk_val = (mi2s_tx_sample_rate * mi2s_tx_bits_per_sample * 2); - pr_debug("%s: MI2S Rx bit clock value: 0x%0x\n", __func__, clk_val); + pr_debug("%s: MI2S bit clock value: 0x%0x\n", __func__, clk_val); return clk_val; } @@ -581,7 +587,7 @@ static int msm_mi2s_sclk_ctl(struct snd_pcm_substream *substream, bool enable) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { mi2s_rx_clk_v1.clk_val1 = - get_mi2s_rx_clk_val(port_id); + get_mi2s_clk_val(port_id); ret = afe_set_lpass_clock(port_id, &mi2s_rx_clk_v1); } else { @@ -589,14 +595,14 @@ static int msm_mi2s_sclk_ctl(struct snd_pcm_substream *substream, bool enable) mi2s_rx_clk.clk_id = msm8952_get_clk_id(port_id); mi2s_rx_clk.clk_freq_in_hz = - get_mi2s_rx_clk_val(port_id); + get_mi2s_clk_val(port_id); ret = afe_set_lpass_clock_v2(port_id, &mi2s_rx_clk); } } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { mi2s_tx_clk_v1.clk_val1 = - Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ; + get_mi2s_clk_val(port_id); ret = afe_set_lpass_clock(port_id, &mi2s_tx_clk_v1); } else { @@ -604,7 +610,7 @@ static int msm_mi2s_sclk_ctl(struct snd_pcm_substream *substream, bool enable) mi2s_tx_clk.clk_id = msm8952_get_clk_id(port_id); mi2s_tx_clk.clk_freq_in_hz = - Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ; + get_mi2s_clk_val(port_id); ret = afe_set_lpass_clock_v2(port_id, &mi2s_tx_clk); } @@ -790,6 +796,61 @@ static int mi2s_rx_bit_format_put(struct snd_kcontrol *kcontrol, return 0; } +static int mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S32_LE; + mi2s_tx_bits_per_sample = 32; + break; + case 2: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + mi2s_tx_bits_per_sample = 32; + break; + case 1: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + mi2s_tx_bits_per_sample = 32; + break; + case 0: + default: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + mi2s_tx_bits_per_sample = 16; + break; + } + return 0; +} + +static int mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (mi2s_tx_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: mi2s_tx_bit_format = %d, ucontrol value = %ld\n", + __func__, mi2s_tx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + static int loopback_mclk_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -988,8 +1049,8 @@ static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, } static const struct soc_enum msm_snd_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_bit_format_text), - rx_bit_format_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(bit_format_text), + bit_format_text), SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mi2s_ch_text), mi2s_ch_text), SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(loopback_mclk_text), @@ -1007,6 +1068,8 @@ static const struct soc_enum msm_snd_enum[] = { static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("MI2S_RX Format", msm_snd_enum[0], mi2s_rx_bit_format_get, mi2s_rx_bit_format_put), + SOC_ENUM_EXT("MI2S_TX Format", msm_snd_enum[0], + mi2s_tx_bit_format_get, mi2s_tx_bit_format_put), SOC_ENUM_EXT("MI2S_TX Channels", msm_snd_enum[1], msm_ter_mi2s_tx_ch_get, msm_ter_mi2s_tx_ch_put), SOC_ENUM_EXT("MI2S_RX Channels", msm_snd_enum[1], -- GitLab From 63b5522ff44d03f32371c3671c837df87027267e Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Mon, 30 Oct 2017 14:39:27 +0530 Subject: [PATCH 1466/5498] ASoC: codecs: add 32bit capture support in codec Add 32 bit format support and set I2S mode based on format in Tx path. CRs-Fixed: 2129947 Change-Id: I017bc8764368cc20ce691598d793167641a48e7f Signed-off-by: Surendar karka --- sound/soc/codecs/msm8x16-wcd.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c index 034a2c4ac9f7..c6f1f10f1e70 100644 --- a/sound/soc/codecs/msm8x16-wcd.c +++ b/sound/soc/codecs/msm8x16-wcd.c @@ -49,7 +49,8 @@ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) #define MSM8X16_WCD_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S24_3LE) + SNDRV_PCM_FMTBIT_S24_3LE |\ + SNDRV_PCM_FMTBIT_S32_LE) #define NUM_INTERPOLATORS 3 #define BITS_PER_REG 8 @@ -4745,13 +4746,24 @@ static int msm8x16_wcd_hw_params(struct snd_pcm_substream *substream, } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - snd_soc_update_bits(dai->codec, + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_update_bits(dai->codec, MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x20); + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + snd_soc_update_bits(dai->codec, + MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x20); + } break; case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_3LE: - snd_soc_update_bits(dai->codec, + case SNDRV_PCM_FORMAT_S32_LE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_update_bits(dai->codec, MSM8X16_WCD_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x00); + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + snd_soc_update_bits(dai->codec, + MSM8X16_WCD_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x00); + } break; default: dev_err(dai->codec->dev, "%s: wrong format selected\n", -- GitLab From cb9b5bfb4cf7d2665763ac15d62318162297944e Mon Sep 17 00:00:00 2001 From: Ramesh V Date: Wed, 18 Oct 2017 11:17:22 +0530 Subject: [PATCH 1467/5498] msm: camerav2: isp: Reserve rdi ub based on image size Existing logic will allocate rdi ub with a fixed size, that will not get enough for 2pd kind of sensors, which lead overflow, to fix this allocate ub based image size. Change-Id: I52516ef38fec1bfc1d18d9280c57c8186ccc96e6 Signed-off-by: Ramesh V --- .../platform/msm/camera_v2/isp/msm_isp47.c | 67 +++++++------------ 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 7bb003b66def..b63e8ef97f5a 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -1706,26 +1706,21 @@ void msm_vfe47_cfg_axi_ub_equal_default( uint32_t prop_size = 0; uint32_t wm_ub_size; uint64_t delta; - uint32_t rdi_ub_offset; - - if (frame_src == VFE_PIX_0) { - for (i = 0; i < axi_data->hw_info->num_wm; i++) { - if (axi_data->free_wm[i] && - SRC_TO_INTF( - HANDLE_TO_IDX(axi_data->free_wm[i])) == - VFE_PIX_0) { - num_used_wms++; - total_image_size += - axi_data->wm_image_size[i]; - } + + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (axi_data->free_wm[i]) { + num_used_wms++; + total_image_size += + axi_data->wm_image_size[i]; } - ub_offset = (axi_data->hw_info->num_rdi * 2) * - axi_data->hw_info->min_wm_ub; - prop_size = vfe_dev->hw_info->vfe_ops.axi_ops. - get_ub_size(vfe_dev) - - axi_data->hw_info->min_wm_ub * (num_used_wms + - axi_data->hw_info->num_rdi * 2); } + if (!total_image_size) { + pr_err("%s: Error total_image_size is 0\n", __func__); + return; + } + prop_size = vfe_dev->hw_info->vfe_ops.axi_ops. + get_ub_size(vfe_dev) - + axi_data->hw_info->min_wm_ub * num_used_wms; for (i = 0; i < axi_data->hw_info->num_wm; i++) { if (!axi_data->free_wm[i]) { msm_camera_io_w(0, @@ -1737,30 +1732,20 @@ void msm_vfe47_cfg_axi_ub_equal_default( HANDLE_TO_IDX(axi_data->free_wm[i]))) continue; - if (frame_src == VFE_PIX_0) { - delta = (uint64_t)axi_data->wm_image_size[i] * - (uint64_t)prop_size; - do_div(delta, total_image_size); - wm_ub_size = axi_data->hw_info->min_wm_ub + - (uint32_t)delta; - msm_camera_io_w(ub_offset << 16 | (wm_ub_size - 1), - vfe_dev->vfe_base + - vfe_dev->hw_info->vfe_ops.axi_ops. - ub_reg_offset(vfe_dev, i)); - ub_offset += wm_ub_size; - } else { - - rdi_ub_offset = (SRC_TO_INTF( - HANDLE_TO_IDX(axi_data->free_wm[i])) - - VFE_RAW_0) * 2 * - axi_data->hw_info->min_wm_ub; - wm_ub_size = axi_data->hw_info->min_wm_ub * 2; - msm_camera_io_w((rdi_ub_offset << 16 | - (wm_ub_size - 1)), - vfe_dev->vfe_base + - vfe_dev->hw_info->vfe_ops.axi_ops. - ub_reg_offset(vfe_dev, i)); + delta = (uint64_t)axi_data->wm_image_size[i] * + (uint64_t)prop_size; + do_div(delta, total_image_size); + if (frame_src != VFE_PIX_0) { + if (delta <= axi_data->hw_info->min_wm_ub) + delta = axi_data->hw_info->min_wm_ub; } + wm_ub_size = axi_data->hw_info->min_wm_ub + + (uint32_t)delta; + msm_camera_io_w(ub_offset << 16 | (wm_ub_size - 1), + vfe_dev->vfe_base + + vfe_dev->hw_info->vfe_ops.axi_ops. + ub_reg_offset(vfe_dev, i)); + ub_offset += wm_ub_size; } } -- GitLab From 2c0a3e37eba573295a9c649085d0c5905cbf04b6 Mon Sep 17 00:00:00 2001 From: Tanya Dixit Date: Thu, 26 Oct 2017 13:50:42 +0530 Subject: [PATCH 1468/5498] drivers: qdsp6v2: Add mutex unlock to properly release lock Add mutex unlock in function audio_effects_shared_ioctl at appropriate place to prevent use after free. CRs-Fixed: 2123291 Change-Id: Ie0d321dc8cc20a295d102a44faea7e5710834932 Signed-off-by: Tanya Dixit --- drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c index e602650c4cb5..ebe9ab763a68 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c +++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c @@ -161,7 +161,6 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, pr_err("%s: Read buffer Allocation failed rc = %d\n", __func__, rc); rc = -ENOMEM; - mutex_unlock(&effects->lock); goto readbuf_fail; } atomic_set(&effects->out_count, effects->config.output.num_buf); @@ -176,7 +175,6 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, if (rc < 0) { pr_err("%s: pcm read block config failed\n", __func__); rc = -EINVAL; - mutex_unlock(&effects->lock); goto cfg_fail; } pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n", @@ -191,7 +189,6 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, pr_err("%s: pcm write format block config failed\n", __func__); rc = -EINVAL; - mutex_unlock(&effects->lock); goto cfg_fail; } @@ -325,6 +322,7 @@ ioctl_fail: readbuf_fail: q6asm_audio_client_buf_free_contiguous(IN, effects->ac); + mutex_unlock(&effects->lock); return rc; cfg_fail: q6asm_audio_client_buf_free_contiguous(IN, @@ -332,6 +330,7 @@ cfg_fail: q6asm_audio_client_buf_free_contiguous(OUT, effects->ac); effects->buf_alloc = 0; + mutex_unlock(&effects->lock); return rc; } -- GitLab From 0e5b0aa9b82d8cec5c7e86cc1902f787da5e5ae7 Mon Sep 17 00:00:00 2001 From: Nirmal Abraham Date: Fri, 3 Nov 2017 11:23:42 +0530 Subject: [PATCH 1469/5498] msm: mdss: Disable ESD thread during display shutdown During device power off, in display shutdown sequence, the FB event notifier for poweroff doesn't get called. In this case, disable the ESD thread before turning panel off to avoid a race condition between ESD and shutdown flow. Change-Id: I33a326eb1e34f24447fb193ec060c3a70d8a6954 Signed-off-by: Nirmal Abraham --- drivers/video/msm/mdss/mdss_dsi.c | 1 + drivers/video/msm/mdss/mdss_dsi.h | 1 + drivers/video/msm/mdss/mdss_dsi_status.c | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c index cf4ae206c97a..d786ee88866e 100644 --- a/drivers/video/msm/mdss/mdss_dsi.c +++ b/drivers/video/msm/mdss/mdss_dsi.c @@ -2665,6 +2665,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, break; case MDSS_EVENT_PANEL_OFF: power_state = (int) (unsigned long) arg; + disable_esd_thread(); ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE; if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE) rc = mdss_dsi_blank(pdata, power_state); diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h index d4b3cb3ba17e..6d6757ff592e 100644 --- a/drivers/video/msm/mdss/mdss_dsi.h +++ b/drivers/video/msm/mdss/mdss_dsi.h @@ -595,6 +595,7 @@ int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl); irqreturn_t mdss_dsi_isr(int irq, void *ptr); irqreturn_t hw_vsync_handler(int irq, void *data); +void disable_esd_thread(void); void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata); void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata); diff --git a/drivers/video/msm/mdss/mdss_dsi_status.c b/drivers/video/msm/mdss/mdss_dsi_status.c index 091321243a47..fce62350ab6a 100644 --- a/drivers/video/msm/mdss/mdss_dsi_status.c +++ b/drivers/video/msm/mdss/mdss_dsi_status.c @@ -103,6 +103,16 @@ irqreturn_t hw_vsync_handler(int irq, void *data) return IRQ_HANDLED; } +/* + * disable_esd_thread() - Cancels work item for the esd check. + */ +void disable_esd_thread(void) +{ + if (pstatus_data && + cancel_delayed_work_sync(&pstatus_data->check_status)) + pr_debug("esd thread killed\n"); +} + /* * fb_event_callback() - Call back function for the fb_register_client() * notifying events -- GitLab From de76d3c43fe32a16db6811d4b3487058e2ad4e1e Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Fri, 10 Nov 2017 11:51:01 +0530 Subject: [PATCH 1470/5498] power: qcom: msm-core: Add mutex lock for ioctl There can be use after free with multiple ioctl calls. Add mutex lock when updating userspace power. Change-Id: Ieae08d05478a462b19cf7f91b64267177eaebe84 Signed-off-by: Maulik Shah Signed-off-by: Mahesh Sivasubramanian --- drivers/power/qcom/msm-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c index 2c35da1b62b1..b9797adb1ded 100644 --- a/drivers/power/qcom/msm-core.c +++ b/drivers/power/qcom/msm-core.c @@ -407,9 +407,10 @@ static int update_userspace_power(struct sched_params __user *argp) if (!sp) return -ENOMEM; - + mutex_lock(&policy_update_mutex); sp->power = allocate_2d_array_uint32_t(node->sp->num_of_freqs); if (IS_ERR_OR_NULL(sp->power)) { + mutex_unlock(&policy_update_mutex); ret = PTR_ERR(sp->power); kfree(sp); return ret; @@ -453,6 +454,7 @@ static int update_userspace_power(struct sched_params __user *argp) } } spin_unlock(&update_lock); + mutex_unlock(&policy_update_mutex); for_each_possible_cpu(cpu) { if (!pdata_valid[cpu]) @@ -466,6 +468,7 @@ static int update_userspace_power(struct sched_params __user *argp) return 0; failed: + mutex_unlock(&policy_update_mutex); for (i = 0; i < TEMP_DATA_POINTS; i++) kfree(sp->power[i]); kfree(sp->power); -- GitLab From 8d4e5f70723b56a87481d2fc300f72a46c63b99d Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Mon, 13 Nov 2017 14:32:53 +0530 Subject: [PATCH 1471/5498] msm: mdss: Disable ECG feature for all chipsets Disbale ECG feature for all chipsets by default, for command mode panels when the FPS is less than 30, NOC errors are seen when some MDP registers are accessed after clocks are turned off from mdss driver. Change-Id: Ic90e995d1729411c0b784edab3893ae96b78320a Signed-off-by: Raghavendra Ambadas --- drivers/video/msm/mdss/mdss_mdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c index e92c2a310d5d..f12a8db73a4e 100644 --- a/drivers/video/msm/mdss/mdss_mdp.c +++ b/drivers/video/msm/mdss/mdss_mdp.c @@ -1763,8 +1763,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdata->hflip_buffer_reused = true; /* prevent disable of prefill calculations */ mdata->min_prefill_lines = 0xffff; - /* clock gating feature is enabled by default */ - mdata->enable_gate = true; + /* clock gating feature is disabled by default */ + mdata->enable_gate = false; mdata->pixel_ram_size = 0; mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL_FLAT; -- GitLab From c7a99e2b231d56d4b0ef28a37ec8c753b1c10b9c Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 9 Nov 2017 15:16:46 +0530 Subject: [PATCH 1472/5498] msm: ipa: Fix array out of bound and use after NULL check Couple of code cleanup - Check for upper boundary for resource_index not to dependent on ipa_rm_dep_get_index function. - Check actual argument for NULL and return. Change-Id: I0ab244e68d96f7841ab2a10e61f2546314166165 Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_rm_dependency_graph.c | 8 +++++--- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 5 ++++- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 5 ++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c index e10c75a473ce..f5c36fed5ede 100644 --- a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c +++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -88,7 +88,8 @@ int ipa_rm_dep_graph_get_resource( goto bail; } resource_index = ipa_rm_dep_get_index(resource_name); - if (resource_index == IPA_RM_INDEX_INVALID) { + if (resource_index == IPA_RM_INDEX_INVALID || + resource_index >= IPA_RM_RESOURCE_MAX) { result = -EINVAL; goto bail; } @@ -120,7 +121,8 @@ int ipa_rm_dep_graph_add(struct ipa_rm_dep_graph *graph, goto bail; } resource_index = ipa_rm_dep_get_index(resource->name); - if (resource_index == IPA_RM_INDEX_INVALID) { + if (resource_index == IPA_RM_INDEX_INVALID || + resource_index >= IPA_RM_RESOURCE_MAX) { result = -EINVAL; goto bail; } diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 9255ccc95bee..44f090e43097 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2630,7 +2630,10 @@ int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, IPAWANERR("reset the pipe stats\n"); } else { /* print tethered-client enum */ - IPAWANDBG_LOW("Tethered-client enum(%d)\n", data->ipa_client); + if (data == NULL) + return -EINVAL; + IPAWANDBG_LOW("Tethered-client enum(%d)\n", + data->ipa_client); } rc = ipa_qmi_get_data_stats(req, resp); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index eb128faa1933..0b6f96d7cd2e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -2788,7 +2788,10 @@ int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, IPAWANERR("reset the pipe stats\n"); } else { /* print tethered-client enum */ - IPAWANDBG_LOW("Tethered-client enum(%d)\n", data->ipa_client); + if (data == NULL) + return -EINVAL; + IPAWANDBG_LOW("Tethered-client enum(%d)\n", + data->ipa_client); } rc = ipa3_qmi_get_data_stats(req, resp); -- GitLab From 820fbb7be96e9c547293422adafa329f6d5f26bc Mon Sep 17 00:00:00 2001 From: depengs Date: Tue, 14 Nov 2017 15:02:21 +0800 Subject: [PATCH 1473/5498] msm: ais: sensor: Validate sensor related name length Variable "slave_info->sensor_name", "slave_info->eeprom_name", "slave_info->actuator_name" and "slave_info->ois_name" are from user input, which may be not NULL terminated. OOB will be possible when accessing these variable. Add a validation for these name length. Change-Id: I9bedad8fb6b34b789ce15443a1a5fcd96cd9e29d Signed-off-by: depengs --- .../platform/msm/ais/sensor/msm_sensor_driver.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c index 57386e096775..f4295086459d 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c @@ -771,6 +771,21 @@ int32_t msm_sensor_driver_probe(void *setting, } } + if (strlen(slave_info->sensor_name) >= MAX_SENSOR_NAME || + strlen(slave_info->eeprom_name) >= MAX_SENSOR_NAME || + strlen(slave_info->actuator_name) >= MAX_SENSOR_NAME || + strlen(slave_info->ois_name) >= MAX_SENSOR_NAME) { + pr_err("failed: name len greater than 32.\n"); + pr_err("sensor name len:%zu, eeprom name len: %zu.\n", + strlen(slave_info->sensor_name), + strlen(slave_info->eeprom_name)); + pr_err("actuator name len: %zu, ois name len:%zu.\n", + strlen(slave_info->actuator_name), + strlen(slave_info->ois_name)); + rc = -EINVAL; + goto free_slave_info; + } + /* Print slave info */ CDBG("camera id %d Slave addr 0x%X addr_type %d\n", slave_info->camera_id, slave_info->slave_addr, -- GitLab From 98fc3cf66fb6d3a8da722b7600fd5d5e3cdbe2c4 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Tue, 14 Nov 2017 18:02:20 +0530 Subject: [PATCH 1474/5498] msm: ais: sensor: actuator: avoid accessing out of bound memory Issue: When total_steps is updated, after that, copy_from_user fails with an error, then, i2c_reg_tbl is not allocated. In this case, when calling msm_actuator_parse_i2c_params, it lead to out-of-bound memory write. Fix: 1) Assign total_steps to zero when error from copying. 2) Add NULL pointer check for i2c tbl. Change-Id: I79c6409d4bc30c559a2845bb46c35fd1f4e44108 Signed-off-by: Rahul Sharma --- .../platform/msm/ais/sensor/actuator/msm_actuator.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c index 5ce55e09ccf6..933541962e07 100644 --- a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c @@ -98,6 +98,11 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, return; } + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return; + } + size = a_ctrl->reg_tbl_size; write_arr = a_ctrl->reg_tbl; i2c_tbl = a_ctrl->i2c_reg_tbl; @@ -1290,8 +1295,11 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, if (copy_from_user(&a_ctrl->region_params, (void *)set_info->af_tuning_params.region_params, - a_ctrl->region_size * sizeof(struct region_params_t))) + a_ctrl->region_size * sizeof(struct region_params_t))) { + a_ctrl->total_steps = 0; + pr_err("Error copying region_params\n"); return -EFAULT; + } if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) { cci_client = a_ctrl->i2c_client.cci_client; -- GitLab From 2c7126d2c80919fc6c06860f666ecaaeecfb55e0 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Tue, 14 Nov 2017 17:04:12 +0100 Subject: [PATCH 1475/5498] ANDROID: binder: clarify deferred thread work. Rename the function to more accurately reflect what it does, and add a comment explaining why we use it. Change-Id: I8d011c017dfc6e24b5b54fc462578f8e153e5926 Signed-off-by: Martijn Coenen --- drivers/staging/android/binder.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 8a3e02916107..c2e2beaf0713 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -833,7 +833,7 @@ binder_enqueue_work_ilocked(struct binder_work *work, } /** - * binder_enqueue_thread_work_ilocked_nowake() - Add thread work + * binder_enqueue_deferred_thread_work_ilocked() - Add deferred thread work * @thread: thread to queue work to * @work: struct binder_work to add to list * @@ -844,8 +844,8 @@ binder_enqueue_work_ilocked(struct binder_work *work, * Requires the proc->inner_lock to be held. */ static void -binder_enqueue_thread_work_ilocked_nowake(struct binder_thread *thread, - struct binder_work *work) +binder_enqueue_deferred_thread_work_ilocked(struct binder_thread *thread, + struct binder_work *work) { binder_enqueue_work_ilocked(work, &thread->todo); } @@ -3330,7 +3330,14 @@ static void binder_transaction(struct binder_proc *proc, } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); binder_inner_proc_lock(proc); - binder_enqueue_thread_work_ilocked_nowake(thread, tcomplete); + /* + * Defer the TRANSACTION_COMPLETE, so we don't return to + * userspace immediately; this allows the target process to + * immediately start processing this transaction, reducing + * latency. We will then return the TRANSACTION_COMPLETE when + * the target replies (or there is an error). + */ + binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete); t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; -- GitLab From 71ba9f28c8aaf6deb48b63a44b567dc0320ec71b Mon Sep 17 00:00:00 2001 From: Benjamin Chan Date: Tue, 14 Nov 2017 00:27:17 -0500 Subject: [PATCH 1476/5498] msm: mdss: adjust mdss_mdp_get_plane_sizes parameters init order Parameter mdss_mdp_plane_sizes must be cleared to 0 before returning under an error condition, otherwise caller function will use the uninitialized mdss_mdp_plane_sizes values and caused incorrect operation. Change-Id: I856b17ce9e917cc450040463ec34b7309d34b9b5 Signed-off-by: Benjamin Chan --- drivers/video/msm/mdss/mdss_mdp_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c index 3d2e4212d75c..d55a0f39a204 100644 --- a/drivers/video/msm/mdss/mdss_mdp_util.c +++ b/drivers/video/msm/mdss/mdss_mdp_util.c @@ -515,11 +515,12 @@ int mdss_mdp_get_plane_sizes(struct mdss_mdp_format_params *fmt, u32 w, u32 h, if (ps == NULL) return -EINVAL; + memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes)); + if ((w > MAX_IMG_WIDTH) || (h > MAX_IMG_HEIGHT)) return -ERANGE; bpp = fmt->bpp; - memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes)); if (mdss_mdp_is_ubwc_format(fmt)) { rc = mdss_mdp_get_ubwc_plane_size(fmt, w, h, ps); -- GitLab From 170783f94e802abcb027897dc4afd2ec61d8b065 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 30 Jan 2017 17:39:48 +0100 Subject: [PATCH 1477/5498] video: fbdev: pmag-ba-fb: Remove bad `__init' annotation [ Upstream commit 879e5a0df626f39cbb3c61bb90373e56d67012c4 ] Fix: WARNING: drivers/video/fbdev/pmag-ba-fb.o(.text+0x308): Section mismatch in reference from the function pmagbafb_probe() to the function .init.text:pmagbafb_erase_cursor() The function pmagbafb_probe() references the function __init pmagbafb_erase_cursor(). This is often because pmagbafb_probe lacks a __init annotation or the annotation of pmagbafb_erase_cursor is wrong. -- a fallout from a missed update from commit 9625b51350cc ("VIDEO: PMAG-BA: Fix section mismatch") and then commit 48c68c4f1b54 ("Drivers: video: remove __dev* attributes.") Signed-off-by: Maciej W. Rozycki Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/pmag-ba-fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/pmag-ba-fb.c b/drivers/video/fbdev/pmag-ba-fb.c index 914a52ba8477..77837665ce89 100644 --- a/drivers/video/fbdev/pmag-ba-fb.c +++ b/drivers/video/fbdev/pmag-ba-fb.c @@ -129,7 +129,7 @@ static struct fb_ops pmagbafb_ops = { /* * Turn the hardware cursor off. */ -static void __init pmagbafb_erase_cursor(struct fb_info *info) +static void pmagbafb_erase_cursor(struct fb_info *info) { struct pmagbafb_par *par = info->par; -- GitLab From 990f27cc79f3be7f6a3ae3554e6d617ded97c850 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Tue, 10 Jan 2017 14:32:52 +0100 Subject: [PATCH 1478/5498] xen/netback: set default upper limit of tx/rx queues to 8 [ Upstream commit 56dd5af9bc23d0d5d23bb207c477715b4c2216c5 ] The default for the maximum number of tx/rx queues of one interface is the number of cpus of the system today. As each queue pair reserves 512 grant pages this default consumes a ridiculous number of grants for large guests. Limit the queue number to 8 as default. This value can be modified via a module parameter if required. Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Boris Ostrovsky Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/xen-netback/netback.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 7a85ff54a39f..50b7731025c1 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -67,6 +67,7 @@ module_param(rx_drain_timeout_msecs, uint, 0444); unsigned int rx_stall_timeout_msecs = 60000; module_param(rx_stall_timeout_msecs, uint, 0444); +#define MAX_QUEUES_DEFAULT 8 unsigned int xenvif_max_queues; module_param_named(max_queues, xenvif_max_queues, uint, 0644); MODULE_PARM_DESC(max_queues, @@ -2189,11 +2190,12 @@ static int __init netback_init(void) if (!xen_domain()) return -ENODEV; - /* Allow as many queues as there are CPUs if user has not + /* Allow as many queues as there are CPUs but max. 8 if user has not * specified a value. */ if (xenvif_max_queues == 0) - xenvif_max_queues = num_online_cpus(); + xenvif_max_queues = min_t(unsigned int, MAX_QUEUES_DEFAULT, + num_online_cpus()); if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) { pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n", -- GitLab From 2eb85ef18c6570e8a59643cd8d5a66122461b1fc Mon Sep 17 00:00:00 2001 From: Patrick Bruenn Date: Wed, 25 Jan 2017 06:25:48 +0100 Subject: [PATCH 1479/5498] ARM: dts: imx53-qsb-common: fix FEC pinmux config [ Upstream commit 8b649e426336d7d4800ff9c82858328f4215ba01 ] The pinmux configuration in device tree was different from manual muxing in /board/freescale/mx53loco/mx53loco.c All pins were configured as NO_PAD_CTL(1 << 31), which was fine as the bootloader already did the correct pinmuxing for us. But recently u-boot is migrating to reuse device tree files from the kernel tree, so it seems to be better to have the correct pinmuxing in our files, too. Signed-off-by: Patrick Bruenn Signed-off-by: Shawn Guo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/imx53-qsb-common.dtsi | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi index 1f55187ed9ce..53e81589368a 100644 --- a/arch/arm/boot/dts/imx53-qsb-common.dtsi +++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi @@ -215,16 +215,16 @@ pinctrl_fec: fecgrp { fsl,pins = < - MX53_PAD_FEC_MDC__FEC_MDC 0x80000000 - MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000 - MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000 - MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000 - MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000 - MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000 - MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000 - MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000 - MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000 - MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000 + MX53_PAD_FEC_MDC__FEC_MDC 0x4 + MX53_PAD_FEC_MDIO__FEC_MDIO 0x1fc + MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x180 + MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x180 + MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x180 + MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x180 + MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x180 + MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x4 + MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x4 + MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x4 >; }; -- GitLab From dddb90d818a58e91b85637b860994fc97d87d2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Jan 2017 23:56:04 +0100 Subject: [PATCH 1480/5498] drm: drm_minor_register(): Clean up debugfs on failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a67834f8bfa1e2f48bb27d07b9a552ba7c3af82a ] Call drm_debugfs_cleanup() in case drm_debugfs_init() fails to cover for failure in the drm_driver.debugfs_init callback. Signed-off-by: Noralf Trønnes Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170126225621.12314-3-noralf@tronnes.org Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index bc3da32d4585..72a71fa72653 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -320,7 +320,7 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root); if (ret) { DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); - return ret; + goto err_debugfs; } ret = device_add(minor->kdev); -- GitLab From 2c2a08631aea3ad623b741798dd39b4cb17b0065 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 20 Jan 2017 12:13:19 -0800 Subject: [PATCH 1481/5498] ARM: omap2plus_defconfig: Fix probe errors on UARTs 5 and 6 [ Upstream commit 4cd6a59f5c1a9b0cca0da09fbba42b9450ffc899 ] We have more than four uarts on some SoCs and that can cause noise with errors while booting. Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/configs/omap2plus_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index a0e51bb68b2d..85128b297ec1 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -169,6 +169,7 @@ CONFIG_INPUT_TWL4030_PWRBUTTON=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=6 CONFIG_SERIAL_8250_EXTENDED=y CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_SHARE_IRQ=y -- GitLab From 2febf192ed4bb493ea808401e925b683a412521f Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Thu, 19 Jan 2017 19:47:38 -0800 Subject: [PATCH 1482/5498] iio: trigger: free trigger resource correctly [ Upstream commit 10e840dfb0b7fc345082dd9e5fff3c1c02e7690e ] These stand-alone trigger drivers were using iio_trigger_put() where they should have been using iio_trigger_free(). The iio_trigger_put() adds a module_put which is bad since they never did a module_get. In the sysfs driver, module_get/put's are used as triggers are added & removed. This extra module_put() occurs on an error path in the probe routine (probably rare). In the bfin-timer & interrupt trigger drivers, the module resources are not explicitly managed, so it's doing a put on something that was never get'd. It occurs on the probe error path and on the remove path (not so rare). Tested with the sysfs trigger driver. The bfin & interrupt drivers were build tested & inspected only. Signed-off-by: Alison Schofield Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/iio/trigger/iio-trig-interrupt.c | 8 ++++---- drivers/iio/trigger/iio-trig-sysfs.c | 2 +- drivers/staging/iio/trigger/iio-trig-bfin-timer.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 572bc6f02ca8..e18f12b74610 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -58,7 +58,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); if (!trig_info) { ret = -ENOMEM; - goto error_put_trigger; + goto error_free_trigger; } iio_trigger_set_drvdata(trig, trig_info); trig_info->irq = irq; @@ -83,8 +83,8 @@ error_release_irq: free_irq(irq, trig); error_free_trig_info: kfree(trig_info); -error_put_trigger: - iio_trigger_put(trig); +error_free_trigger: + iio_trigger_free(trig); error_ret: return ret; } @@ -99,7 +99,7 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev) iio_trigger_unregister(trig); free_irq(trig_info->irq, trig); kfree(trig_info); - iio_trigger_put(trig); + iio_trigger_free(trig); return 0; } diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 254c7e906127..61ffbec048a7 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -173,7 +173,7 @@ static int iio_sysfs_trigger_probe(int id) return 0; out2: - iio_trigger_put(t->trig); + iio_trigger_free(t->trig); free_t: kfree(t); out1: diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c index a21b7c514776..b412f567a9c1 100644 --- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c @@ -258,7 +258,7 @@ out_free_irq: out1: iio_trigger_unregister(st->trig); out: - iio_trigger_put(st->trig); + iio_trigger_free(st->trig); return ret; } @@ -271,7 +271,7 @@ static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev) peripheral_free(st->t->pin); free_irq(st->irq, st); iio_trigger_unregister(st->trig); - iio_trigger_put(st->trig); + iio_trigger_free(st->trig); return 0; } -- GitLab From 2ae575c1b42b6ef1ee4ea8a170e388fb8168e0b4 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 13 Jan 2017 13:03:40 -0600 Subject: [PATCH 1483/5498] dt-bindings: Add LEGO MINDSTORMS EV3 compatible specification [ Upstream commit 21078ab174c99885ca83a5c32db0d33b1617745e ] This adds the board level device tree specification for LEGO MINDSTORMS EV3 Acked-by: Rob Herring Signed-off-by: David Lechner Signed-off-by: Sekhar Nori Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/davinci.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt index cfaeda4274e6..e6f4b50da1f9 100644 --- a/Documentation/devicetree/bindings/arm/davinci.txt +++ b/Documentation/devicetree/bindings/arm/davinci.txt @@ -9,6 +9,10 @@ EnBW AM1808 based CMC board Required root node properties: - compatible = "enbw,cmc", "ti,da850; +LEGO MINDSTORMS EV3 (AM1808 based) +Required root node properties: + - compatible = "lego,ev3", "ti,da850"; + Generic DaVinci Boards ---------------------- -- GitLab From 815b4687d31780191c20746c5c508ca4972fa731 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 13 Jan 2017 13:03:39 -0600 Subject: [PATCH 1484/5498] dt-bindings: Add vendor prefix for LEGO [ Upstream commit 7dcc31e2e68a386a29070384b51683ece80982bf ] Add a vendor prefix for LEGO Systems A/S Acked-by: Rob Herring Signed-off-by: David Lechner Signed-off-by: Sekhar Nori Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index a344ec2713a5..356ffd64fc9e 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -83,6 +83,7 @@ karo Ka-Ro electronics GmbH keymile Keymile GmbH lacie LaCie lantiq Lantiq Semiconductor +lego LEGO Systems A/S lenovo Lenovo Group Ltd. lg LG Corporation linux Linux-specific binding -- GitLab From 34ed694c92811398737839b05021cc161ecfdb8e Mon Sep 17 00:00:00 2001 From: Volodymyr Bendiuga Date: Thu, 19 Jan 2017 17:05:04 +0100 Subject: [PATCH 1485/5498] phy: increase size of MII_BUS_ID_SIZE and bus_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4567d686f5c6d955e57a3afa1741944c1e7f4033 ] Some bus names are pretty long and do not fit into 17 chars. Increase therefore MII_BUS_ID_SIZE and phy_fixup.bus_id to larger number. Now mii_bus.id can host larger name. Signed-off-by: Volodymyr Bendiuga Signed-off-by: Magnus Öberg Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/phy.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/linux/phy.h b/include/linux/phy.h index fbdacd1278e3..bc79f855fc32 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -135,11 +135,7 @@ static inline const char *phy_modes(phy_interface_t interface) /* Used when trying to connect to a specific phy (mii bus id:phy device id) */ #define PHY_ID_FMT "%s:%02x" -/* - * Need to be a little smaller than phydev->dev.bus_id to leave room - * for the ":%02x" - */ -#define MII_BUS_ID_SIZE (20 - 3) +#define MII_BUS_ID_SIZE 61 /* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */ @@ -573,7 +569,7 @@ struct phy_driver { /* A Structure for boards to register fixups with the PHY Lib */ struct phy_fixup { struct list_head list; - char bus_id[20]; + char bus_id[MII_BUS_ID_SIZE + 3]; u32 phy_uid; u32 phy_uid_mask; int (*run)(struct phy_device *phydev); -- GitLab From 7d699b09bc9922ccbab82ae6d16553468ded1f91 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 11 Jan 2017 16:43:32 +0200 Subject: [PATCH 1486/5498] serial: sh-sci: Fix register offsets for the IRDA serial port [ Upstream commit a752ba18af8285e3eeda572f40dddaebff0c3621 ] Even though most of its registers are 8-bit wide, the IRDA has two 16-bit registers that make it a 16-bit peripheral and not a 8-bit peripheral with addresses shifted by one. Fix the registers offset in the driver and the platform data regshift value. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/cpu/sh3/setup-sh770x.c | 1 - drivers/tty/serial/sh-sci.c | 17 ++++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c index 538c10db3537..8dc315b212c2 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -165,7 +165,6 @@ static struct plat_sci_port scif2_platform_data = { .scscr = SCSCR_TE | SCSCR_RE, .type = PORT_IRDA, .ops = &sh770x_sci_port_ops, - .regshift = 1, }; static struct resource scif2_resources[] = { diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index f6c7f043c176..118d998f6ff6 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -171,18 +171,17 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { }, /* - * Common definitions for legacy IrDA ports, dependent on - * regshift value. + * Common definitions for legacy IrDA ports. */ [SCIx_IRDA_REGTYPE] = { [SCSMR] = { 0x00, 8 }, - [SCBRR] = { 0x01, 8 }, - [SCSCR] = { 0x02, 8 }, - [SCxTDR] = { 0x03, 8 }, - [SCxSR] = { 0x04, 8 }, - [SCxRDR] = { 0x05, 8 }, - [SCFCR] = { 0x06, 8 }, - [SCFDR] = { 0x07, 16 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 8 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0a, 8 }, + [SCFCR] = { 0x0c, 8 }, + [SCFDR] = { 0x0e, 16 }, [SCTFDR] = sci_reg_invalid, [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, -- GitLab From 3bd646aed6821f667deac3c08019db12d96b9a3a Mon Sep 17 00:00:00 2001 From: William wu Date: Fri, 13 Jan 2017 11:04:22 +0800 Subject: [PATCH 1487/5498] usb: hcd: initialize hcd->flags to 0 when rm hcd [ Upstream commit 76b8db0d480e8045e1a1902fc9ab143b3b9ef115 ] On some platforms(e.g. rk3399 board), we can call hcd_add/remove consecutively without calling usb_put_hcd/usb_create_hcd in between, so hcd->flags can be stale. If the HC dies due to whatever reason then without this patch we get the below error on next hcd_add. [173.296154] xhci-hcd xhci-hcd.2.auto: HC died; cleaning up [173.296209] xhci-hcd xhci-hcd.2.auto: xHCI Host Controller [173.296762] xhci-hcd xhci-hcd.2.auto: new USB bus registered, assigned bus number 6 [173.296931] usb usb6: We don't know the algorithms for LPM for this host, disabling LPM. [173.297179] usb usb6: New USB device found, idVendor=1d6b, idProduct=0003 [173.297203] usb usb6: New USB device strings: Mfr=3, Product=2, SerialNumber=1 [173.297222] usb usb6: Product: xHCI Host Controller [173.297240] usb usb6: Manufacturer: Linux 4.4.21 xhci-hcd [173.297257] usb usb6: SerialNumber: xhci-hcd.2.auto [173.298680] hub 6-0:1.0: USB hub found [173.298749] hub 6-0:1.0: 1 port detected [173.299382] rockchip-dwc3 usb@fe800000: USB HOST connected [173.395418] hub 5-0:1.0: activate --> -19 [173.603447] irq 228: nobody cared (try booting with the "irqpoll" option) [173.603493] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.21 #9 [173.603513] Hardware name: Google Kevin (DT) [173.603531] Call trace: [173.603568] [] dump_backtrace+0x0/0x160 [173.603596] [] show_stack+0x20/0x28 [173.603623] [] dump_stack+0x90/0xb0 [173.603650] [] __report_bad_irq+0x48/0xe8 [173.603674] [] note_interrupt+0x1e8/0x28c [173.603698] [] handle_irq_event_percpu+0x1d4/0x25c [173.603722] [] handle_irq_event+0x4c/0x7c [173.603748] [] handle_fasteoi_irq+0xb4/0x124 [173.603777] [] generic_handle_irq+0x30/0x44 [173.603804] [] __handle_domain_irq+0x90/0xbc [173.603827] [] gic_handle_irq+0xcc/0x188 ... [173.604500] [] el1_irq+0x80/0xf8 [173.604530] [] cpu_startup_entry+0x38/0x3cc [173.604558] [] rest_init+0x8c/0x94 [173.604585] [] start_kernel+0x3d0/0x3fc [173.604607] [<0000000000b16000>] 0xb16000 [173.604622] handlers: [173.604648] [] usb_hcd_irq [173.604673] Disabling IRQ #228 Signed-off-by: William wu Acked-by: Roger Quadros Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 826b8fb9eb59..d7203fd67f2a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2927,6 +2927,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) } usb_put_invalidate_rhdev(hcd); + hcd->flags = 0; } EXPORT_SYMBOL_GPL(usb_remove_hcd); -- GitLab From 9d913538108b380bb9b0a5fc47086220807151e4 Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Mon, 16 Jan 2017 13:17:55 +0200 Subject: [PATCH 1488/5498] IPsec: do not ignore crypto err in ah4 input [ Upstream commit ebd89a2d0675f1325c2be5b7576fd8cb7e8defd0 ] ah4 input processing uses the asynchronous hash crypto API which supplies an error code as part of the operation completion but the error code was being ignored. Treat a crypto API error indication as a verification failure. While a crypto API reported error would almost certainly result in a memcpy of the digest failing anyway and thus the security risk seems minor, performing a memory compare on what might be uninitialized memory is wrong. Signed-off-by: Gilad Ben-Yossef Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ah4.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index ac9a32ec3ee4..0157f09c0de9 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -270,6 +270,9 @@ static void ah_input_done(struct crypto_async_request *base, int err) int ihl = ip_hdrlen(skb); int ah_hlen = (ah->hdrlen + 2) << 2; + if (err) + goto out; + work_iph = AH_SKB_CB(skb)->tmp; auth_data = ah_tmp_auth(work_iph, ihl); icv = ah_tmp_icv(ahp->ahash, auth_data, ahp->icv_trunc_len); -- GitLab From a25320c7f317f6d8c72253a7278c4dc902e124ea Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 15 Jan 2017 14:44:30 -0800 Subject: [PATCH 1489/5498] Input: mpr121 - handle multiple bits change of status register [ Upstream commit 08fea55e37f58371bffc5336a59e55d1f155955a ] This driver reports input events on their interrupts which are triggered by the sensor's status register changes. But only single bit change is reported in the interrupt handler. So if there are multiple bits are changed at almost the same time, other press or release events are ignored. This fixes it by detecting all changed bits in the status register. Signed-off-by: Akinobu Mita Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/input/keyboard/mpr121_touchkey.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 009c82256e89..3dbc7e611791 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -87,7 +87,8 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) struct mpr121_touchkey *mpr121 = dev_id; struct i2c_client *client = mpr121->client; struct input_dev *input = mpr121->input_dev; - unsigned int key_num, key_val, pressed; + unsigned long bit_changed; + unsigned int key_num; int reg; reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); @@ -105,18 +106,22 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) reg &= TOUCH_STATUS_MASK; /* use old press bit to figure out which bit changed */ - key_num = ffs(reg ^ mpr121->statusbits) - 1; - pressed = reg & (1 << key_num); + bit_changed = reg ^ mpr121->statusbits; mpr121->statusbits = reg; + for_each_set_bit(key_num, &bit_changed, mpr121->keycount) { + unsigned int key_val, pressed; - key_val = mpr121->keycodes[key_num]; + pressed = reg & BIT(key_num); + key_val = mpr121->keycodes[key_num]; - input_event(input, EV_MSC, MSC_SCAN, key_num); - input_report_key(input, key_val, pressed); - input_sync(input); + input_event(input, EV_MSC, MSC_SCAN, key_num); + input_report_key(input, key_val, pressed); + + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, + pressed ? "pressed" : "released"); - dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, - pressed ? "pressed" : "released"); + } + input_sync(input); out: return IRQ_HANDLED; -- GitLab From a20ddebf8eaa6c8207250f0640715e2bbe146e0f Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 15 Jan 2017 14:44:05 -0800 Subject: [PATCH 1490/5498] Input: mpr121 - set missing event capability [ Upstream commit 9723ddc8fe0d76ce41fe0dc16afb241ec7d0a29d ] This driver reports misc scan input events on the sensor's status register changes. But the event capability for them was not set in the device initialization, so these events were ignored. This change adds the missing event capability. Signed-off-by: Akinobu Mita Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/input/keyboard/mpr121_touchkey.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 3dbc7e611791..671d202a94fa 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -235,6 +235,7 @@ static int mpr_touchkey_probe(struct i2c_client *client, input_dev->id.bustype = BUS_I2C; input_dev->dev.parent = &client->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_dev->keycode = mpr121->keycodes; input_dev->keycodesize = sizeof(mpr121->keycodes[0]); -- GitLab From 76fc35d849a1464142af7d628e29eb6dd62570b0 Mon Sep 17 00:00:00 2001 From: Feras Daoud Date: Wed, 28 Dec 2016 14:47:28 +0200 Subject: [PATCH 1491/5498] IB/ipoib: Change list_del to list_del_init in the tx object [ Upstream commit 27d41d29c7f093f6f77843624fbb080c1b4a8b9c ] Since ipoib_cm_tx_start function and ipoib_cm_tx_reap function belong to different work queues, they can run in parallel. In this case if ipoib_cm_tx_reap calls list_del and release the lock, ipoib_cm_tx_start may acquire it and call list_del_init on the already deleted object. Changing list_del to list_del_init in ipoib_cm_tx_reap fixes the problem. Fixes: 839fcaba355a ("IPoIB: Connected mode experimental support") Signed-off-by: Feras Daoud Signed-off-by: Erez Shitrit Reviewed-by: Alex Vesker Signed-off-by: Leon Romanovsky Reviewed-by: Yuval Shaia Signed-off-by: Doug Ledford Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/ipoib/ipoib_cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index cdf0a78e0c99..d88f8dbe62d9 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -1362,7 +1362,7 @@ static void ipoib_cm_tx_reap(struct work_struct *work) while (!list_empty(&priv->cm.reap_list)) { p = list_entry(priv->cm.reap_list.next, typeof(*p), list); - list_del(&p->list); + list_del_init(&p->list); spin_unlock_irqrestore(&priv->lock, flags); netif_tx_unlock_bh(dev); ipoib_cm_tx_destroy(p); -- GitLab From cdd1a3fd76d5e232e5432998fc8e72cbc29f6bf3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 8 Jun 2017 14:49:18 +0100 Subject: [PATCH 1492/5498] KEYS: trusted: sanitize all key material commit ee618b4619b72527aaed765f0f0b74072b281159 upstream. As the previous patch did for encrypted-keys, zero sensitive any potentially sensitive data related to the "trusted" key type before it is freed. Notably, we were not zeroing the tpm_buf structures in which the actual key is stored for TPM seal and unseal, nor were we zeroing the trusted_key_payload in certain error paths. Cc: Mimi Zohar Cc: David Safford Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/keys/trusted.c | 49 ++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/security/keys/trusted.c b/security/keys/trusted.c index aeb38f1a12e7..917453895cbc 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -69,7 +69,7 @@ static int TSS_sha1(const unsigned char *data, unsigned int datalen, } ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); - kfree(sdesc); + kzfree(sdesc); return ret; } @@ -113,7 +113,7 @@ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, if (!ret) ret = crypto_shash_final(&sdesc->shash, digest); out: - kfree(sdesc); + kzfree(sdesc); return ret; } @@ -164,7 +164,7 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, paramdigest, TPM_NONCE_SIZE, h1, TPM_NONCE_SIZE, h2, 1, &c, 0, 0); out: - kfree(sdesc); + kzfree(sdesc); return ret; } @@ -245,7 +245,7 @@ static int TSS_checkhmac1(unsigned char *buffer, if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE)) ret = -EINVAL; out: - kfree(sdesc); + kzfree(sdesc); return ret; } @@ -346,7 +346,7 @@ static int TSS_checkhmac2(unsigned char *buffer, if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) ret = -EINVAL; out: - kfree(sdesc); + kzfree(sdesc); return ret; } @@ -563,7 +563,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, *bloblen = storedsize; } out: - kfree(td); + kzfree(td); return ret; } @@ -677,7 +677,7 @@ static int key_seal(struct trusted_key_payload *p, if (ret < 0) pr_info("trusted_key: srkseal failed (%d)\n", ret); - kfree(tb); + kzfree(tb); return ret; } @@ -702,7 +702,7 @@ static int key_unseal(struct trusted_key_payload *p, /* pull migratable flag out of sealed key */ p->migratable = p->key[--p->key_len]; - kfree(tb); + kzfree(tb); return ret; } @@ -961,12 +961,12 @@ static int trusted_instantiate(struct key *key, if (!ret && options->pcrlock) ret = pcrlock(options->pcrlock); out: - kfree(datablob); - kfree(options); + kzfree(datablob); + kzfree(options); if (!ret) rcu_assign_keypointer(key, payload); else - kfree(payload); + kzfree(payload); return ret; } @@ -975,8 +975,7 @@ static void trusted_rcu_free(struct rcu_head *rcu) struct trusted_key_payload *p; p = container_of(rcu, struct trusted_key_payload, rcu); - memset(p->key, 0, p->key_len); - kfree(p); + kzfree(p); } /* @@ -1018,9 +1017,10 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) ret = datablob_parse(datablob, new_p, new_o); if (ret != Opt_update) { ret = -EINVAL; - kfree(new_p); + kzfree(new_p); goto out; } + /* copy old key values, and reseal with new pcrs */ new_p->migratable = p->migratable; new_p->key_len = p->key_len; @@ -1031,22 +1031,22 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) ret = key_seal(new_p, new_o); if (ret < 0) { pr_info("trusted_key: key_seal failed (%d)\n", ret); - kfree(new_p); + kzfree(new_p); goto out; } if (new_o->pcrlock) { ret = pcrlock(new_o->pcrlock); if (ret < 0) { pr_info("trusted_key: pcrlock failed (%d)\n", ret); - kfree(new_p); + kzfree(new_p); goto out; } } rcu_assign_keypointer(key, new_p); call_rcu(&p->rcu, trusted_rcu_free); out: - kfree(datablob); - kfree(new_o); + kzfree(datablob); + kzfree(new_o); return ret; } @@ -1075,24 +1075,19 @@ static long trusted_read(const struct key *key, char __user *buffer, for (i = 0; i < p->blob_len; i++) bufp = hex_byte_pack(bufp, p->blob[i]); if ((copy_to_user(buffer, ascii_buf, 2 * p->blob_len)) != 0) { - kfree(ascii_buf); + kzfree(ascii_buf); return -EFAULT; } - kfree(ascii_buf); + kzfree(ascii_buf); return 2 * p->blob_len; } /* - * trusted_destroy - before freeing the key, clear the decrypted data + * trusted_destroy - clear and free the key's payload */ static void trusted_destroy(struct key *key) { - struct trusted_key_payload *p = key->payload.data; - - if (!p) - return; - memset(p->key, 0, p->key_len); - kfree(key->payload.data); + kzfree(key->payload.data); } struct key_type key_type_trusted = { -- GitLab From c64cc4117fecbcd048f84aaa0af47a2542dc04a5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 2 Nov 2017 00:47:12 +0000 Subject: [PATCH 1493/5498] KEYS: trusted: fix writing past end of buffer in trusted_read() commit a3c812f7cfd80cf51e8f5b7034f7418f6beb56c1 upstream. When calling keyctl_read() on a key of type "trusted", if the user-supplied buffer was too small, the kernel ignored the buffer length and just wrote past the end of the buffer, potentially corrupting userspace memory. Fix it by instead returning the size required, as per the documentation for keyctl_read(). We also don't even fill the buffer at all in this case, as this is slightly easier to implement than doing a short read, and either behavior appears to be permitted. It also makes it match the behavior of the "encrypted" key type. Fixes: d00a1c72f7f4 ("keys: add new trusted key-type") Reported-by: Ben Hutchings Cc: # v2.6.38+ Signed-off-by: Eric Biggers Signed-off-by: David Howells Reviewed-by: Mimi Zohar Reviewed-by: James Morris Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/keys/trusted.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 917453895cbc..1273e22aaa28 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1065,20 +1065,21 @@ static long trusted_read(const struct key *key, char __user *buffer, p = rcu_dereference_key(key); if (!p) return -EINVAL; - if (!buffer || buflen <= 0) - return 2 * p->blob_len; - ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL); - if (!ascii_buf) - return -ENOMEM; - bufp = ascii_buf; - for (i = 0; i < p->blob_len; i++) - bufp = hex_byte_pack(bufp, p->blob[i]); - if ((copy_to_user(buffer, ascii_buf, 2 * p->blob_len)) != 0) { + if (buffer && buflen >= 2 * p->blob_len) { + ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL); + if (!ascii_buf) + return -ENOMEM; + + bufp = ascii_buf; + for (i = 0; i < p->blob_len; i++) + bufp = hex_byte_pack(bufp, p->blob[i]); + if (copy_to_user(buffer, ascii_buf, 2 * p->blob_len) != 0) { + kzfree(ascii_buf); + return -EFAULT; + } kzfree(ascii_buf); - return -EFAULT; } - kzfree(ascii_buf); return 2 * p->blob_len; } -- GitLab From cb870a365af45b69b4bcb3323066a681e37ed4aa Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Mon, 16 Oct 2017 18:51:31 +0300 Subject: [PATCH 1494/5498] crypto: x86/sha1-mb - fix panic due to unaligned access commit d041b557792c85677f17e08eee535eafbd6b9aa2 upstream. struct sha1_ctx_mgr allocated in sha1_mb_mod_init() via kzalloc() and later passed in sha1_mb_flusher_mgr_flush_avx2() function where instructions vmovdqa used to access the struct. vmovdqa requires 16-bytes aligned argument, but nothing guarantees that struct sha1_ctx_mgr will have that alignment. Unaligned vmovdqa will generate GP fault. Fix this by replacing vmovdqa with vmovdqu which doesn't have alignment requirements. Fixes: 2249cbb53ead ("crypto: sha-mb - SHA1 multibuffer submit and flush routines for AVX2") Signed-off-by: Andrey Ryabinin Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- arch/x86/crypto/sha-mb/sha1_mb_mgr_flush_avx2.S | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/crypto/sha-mb/sha1_mb_mgr_flush_avx2.S b/arch/x86/crypto/sha-mb/sha1_mb_mgr_flush_avx2.S index 85c4e1cf7172..e1693457c178 100644 --- a/arch/x86/crypto/sha-mb/sha1_mb_mgr_flush_avx2.S +++ b/arch/x86/crypto/sha-mb/sha1_mb_mgr_flush_avx2.S @@ -174,8 +174,8 @@ LABEL skip_ %I .endr # Find min length - vmovdqa _lens+0*16(state), %xmm0 - vmovdqa _lens+1*16(state), %xmm1 + vmovdqu _lens+0*16(state), %xmm0 + vmovdqu _lens+1*16(state), %xmm1 vpminud %xmm1, %xmm0, %xmm2 # xmm2 has {D,C,B,A} vpalignr $8, %xmm2, %xmm3, %xmm3 # xmm3 has {x,x,D,C} @@ -195,8 +195,8 @@ LABEL skip_ %I vpsubd %xmm2, %xmm0, %xmm0 vpsubd %xmm2, %xmm1, %xmm1 - vmovdqa %xmm0, _lens+0*16(state) - vmovdqa %xmm1, _lens+1*16(state) + vmovdqu %xmm0, _lens+0*16(state) + vmovdqu %xmm1, _lens+1*16(state) # "state" and "args" are the same address, arg1 # len is arg2 @@ -260,8 +260,8 @@ ENTRY(sha1_mb_mgr_get_comp_job_avx2) jc .return_null # Find min length - vmovdqa _lens(state), %xmm0 - vmovdqa _lens+1*16(state), %xmm1 + vmovdqu _lens(state), %xmm0 + vmovdqu _lens+1*16(state), %xmm1 vpminud %xmm1, %xmm0, %xmm2 # xmm2 has {D,C,B,A} vpalignr $8, %xmm2, %xmm3, %xmm3 # xmm3 has {x,x,D,C} -- GitLab From 1f166fb65e89f70ee9acf0565ff35b6c2ca26e67 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Nov 2017 22:29:02 +0000 Subject: [PATCH 1495/5498] KEYS: fix NULL pointer dereference during ASN.1 parsing [ver #2] commit 624f5ab8720b3371367327a822c267699c1823b8 upstream. syzkaller reported a NULL pointer dereference in asn1_ber_decoder(). It can be reproduced by the following command, assuming CONFIG_PKCS7_TEST_KEY=y: keyctl add pkcs7_test desc '' @s The bug is that if the data buffer is empty, an integer underflow occurs in the following check: if (unlikely(dp >= datalen - 1)) goto data_overrun_error; This results in the NULL data pointer being dereferenced. Fix it by checking for 'datalen - dp < 2' instead. Also fix the similar check for 'dp >= datalen - n' later in the same function. That one possibly could result in a buffer overread. The NULL pointer dereference was reproducible using the "pkcs7_test" key type but not the "asymmetric" key type because the "asymmetric" key type checks for a 0-length payload before calling into the ASN.1 decoder but the "pkcs7_test" key type does not. The bug report was: BUG: unable to handle kernel NULL pointer dereference at (null) IP: asn1_ber_decoder+0x17f/0xe60 lib/asn1_decoder.c:233 PGD 7b708067 P4D 7b708067 PUD 7b6ee067 PMD 0 Oops: 0000 [#1] SMP Modules linked in: CPU: 0 PID: 522 Comm: syz-executor1 Not tainted 4.14.0-rc8 #7 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.3-20171021_125229-anatol 04/01/2014 task: ffff9b6b3798c040 task.stack: ffff9b6b37970000 RIP: 0010:asn1_ber_decoder+0x17f/0xe60 lib/asn1_decoder.c:233 RSP: 0018:ffff9b6b37973c78 EFLAGS: 00010216 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 000000000000021c RDX: ffffffff814a04ed RSI: ffffb1524066e000 RDI: ffffffff910759e0 RBP: ffff9b6b37973d60 R08: 0000000000000001 R09: ffff9b6b3caa4180 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000002 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 FS: 00007f10ed1f2700(0000) GS:ffff9b6b3ea00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000007b6f3000 CR4: 00000000000006f0 Call Trace: pkcs7_parse_message+0xee/0x240 crypto/asymmetric_keys/pkcs7_parser.c:139 verify_pkcs7_signature+0x33/0x180 certs/system_keyring.c:216 pkcs7_preparse+0x41/0x70 crypto/asymmetric_keys/pkcs7_key_type.c:63 key_create_or_update+0x180/0x530 security/keys/key.c:855 SYSC_add_key security/keys/keyctl.c:122 [inline] SyS_add_key+0xbf/0x250 security/keys/keyctl.c:62 entry_SYSCALL_64_fastpath+0x1f/0xbe RIP: 0033:0x4585c9 RSP: 002b:00007f10ed1f1bd8 EFLAGS: 00000216 ORIG_RAX: 00000000000000f8 RAX: ffffffffffffffda RBX: 00007f10ed1f2700 RCX: 00000000004585c9 RDX: 0000000020000000 RSI: 0000000020008ffb RDI: 0000000020008000 RBP: 0000000000000000 R08: ffffffffffffffff R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000216 R12: 00007fff1b2260ae R13: 00007fff1b2260af R14: 00007f10ed1f2700 R15: 0000000000000000 Code: dd ca ff 48 8b 45 88 48 83 e8 01 4c 39 f0 0f 86 a8 07 00 00 e8 53 dd ca ff 49 8d 46 01 48 89 85 58 ff ff ff 48 8b 85 60 ff ff ff <42> 0f b6 0c 30 89 c8 88 8d 75 ff ff ff 83 e0 1f 89 8d 28 ff ff RIP: asn1_ber_decoder+0x17f/0xe60 lib/asn1_decoder.c:233 RSP: ffff9b6b37973c78 CR2: 0000000000000000 Fixes: 42d5ec27f873 ("X.509: Add an ASN.1 decoder") Reported-by: syzbot Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- lib/asn1_decoder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c index 828ead6b78a8..162b6d290622 100644 --- a/lib/asn1_decoder.c +++ b/lib/asn1_decoder.c @@ -220,7 +220,7 @@ next_op: hdr = 2; /* Extract a tag from the data */ - if (unlikely(dp >= datalen - 1)) + if (unlikely(datalen - dp < 2)) goto data_overrun_error; tag = data[dp++]; if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) @@ -266,7 +266,7 @@ next_op: int n = len - 0x80; if (unlikely(n > 2)) goto length_too_long; - if (unlikely(dp >= datalen - n)) + if (unlikely(n > datalen - dp)) goto data_overrun_error; hdr += n; for (len = 0; n > 0; n--) { -- GitLab From 1530d28a893e621c079af741a724cec5ec9e9d93 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 2 Nov 2017 18:44:28 +0100 Subject: [PATCH 1496/5498] ARM: 8720/1: ensure dump_instr() checks addr_limit commit b9dd05c7002ee0ca8b676428b2268c26399b5e31 upstream. When CONFIG_DEBUG_USER is enabled, it's possible for a user to deliberately trigger dump_instr() with a chosen kernel address. Let's avoid problems resulting from this by using get_user() rather than __get_user(), ensuring that we don't erroneously access kernel memory. So that we can use the same code to dump user instructions and kernel instructions, the common dumping code is factored out to __dump_instr(), with the fs manipulated appropriately in dump_instr() around calls to this. Signed-off-by: Mark Rutland Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/kernel/traps.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 9f5d81881eb6..8db4c231a285 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -132,30 +132,26 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom, set_fs(fs); } -static void dump_instr(const char *lvl, struct pt_regs *regs) +static void __dump_instr(const char *lvl, struct pt_regs *regs) { unsigned long addr = instruction_pointer(regs); const int thumb = thumb_mode(regs); const int width = thumb ? 4 : 8; - mm_segment_t fs; char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; int i; /* - * We need to switch to kernel mode so that we can use __get_user - * to safely read from kernel space. Note that we now dump the - * code first, just in case the backtrace kills us. + * Note that we now dump the code first, just in case the backtrace + * kills us. */ - fs = get_fs(); - set_fs(KERNEL_DS); for (i = -4; i < 1 + !!thumb; i++) { unsigned int val, bad; if (thumb) - bad = __get_user(val, &((u16 *)addr)[i]); + bad = get_user(val, &((u16 *)addr)[i]); else - bad = __get_user(val, &((u32 *)addr)[i]); + bad = get_user(val, &((u32 *)addr)[i]); if (!bad) p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", @@ -166,8 +162,20 @@ static void dump_instr(const char *lvl, struct pt_regs *regs) } } printk("%sCode: %s\n", lvl, str); +} - set_fs(fs); +static void dump_instr(const char *lvl, struct pt_regs *regs) +{ + mm_segment_t fs; + + if (!user_mode(regs)) { + fs = get_fs(); + set_fs(KERNEL_DS); + __dump_instr(lvl, regs); + set_fs(fs); + } else { + __dump_instr(lvl, regs); + } } #ifdef CONFIG_ARM_UNWIND -- GitLab From 2a5c83f5bab19d5eead42fe829e676ad367c0060 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Nov 2017 16:05:24 +0100 Subject: [PATCH 1497/5498] ALSA: seq: Fix OSS sysex delivery in OSS emulation commit 132d358b183ac6ad8b3fea32ad5e0663456d18d1 upstream. The SYSEX event delivery in OSS sequencer emulation assumed that the event is encoded in the variable-length data with the straight buffering. This was the normal behavior in the past, but during the development, the chained buffers were introduced for carrying more data, while the OSS code was left intact. As a result, when a SYSEX event with the chained buffer data is passed to OSS sequencer port, it may end up with the wrong memory access, as if it were having a too large buffer. This patch addresses the bug, by applying the buffer data expansion by the generic snd_seq_dump_var_event() helper function. Reported-by: syzbot Reported-by: Mark Salyzyn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/oss/seq_oss_midi.c | 4 +--- sound/core/seq/oss/seq_oss_readq.c | 29 +++++++++++++++++++++++++++++ sound/core/seq/oss/seq_oss_readq.h | 2 ++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index 3a4569669efa..86353dc0439c 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -615,9 +615,7 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq if (!dp->timer->running) len = snd_seq_oss_timer_start(dp->timer); if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { - if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) - snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, - ev->data.ext.ptr, ev->data.ext.len); + snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev); } else { len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev); if (len > 0) diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index 654d17a5023c..dffc27374bdf 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -119,6 +119,35 @@ snd_seq_oss_readq_puts(struct seq_oss_readq *q, int dev, unsigned char *data, in return 0; } +/* + * put MIDI sysex bytes; the event buffer may be chained, thus it has + * to be expanded via snd_seq_dump_var_event(). + */ +struct readq_sysex_ctx { + struct seq_oss_readq *readq; + int dev; +}; + +static int readq_dump_sysex(void *ptr, void *buf, int count) +{ + struct readq_sysex_ctx *ctx = ptr; + + return snd_seq_oss_readq_puts(ctx->readq, ctx->dev, buf, count); +} + +int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev, + struct snd_seq_event *ev) +{ + struct readq_sysex_ctx ctx = { + .readq = q, + .dev = dev + }; + + if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) + return 0; + return snd_seq_dump_var_event(ev, readq_dump_sysex, &ctx); +} + /* * copy an event to input queue: * return zero if enqueued diff --git a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h index f1463f1f449e..8d033ca2d23f 100644 --- a/sound/core/seq/oss/seq_oss_readq.h +++ b/sound/core/seq/oss/seq_oss_readq.h @@ -44,6 +44,8 @@ void snd_seq_oss_readq_delete(struct seq_oss_readq *q); void snd_seq_oss_readq_clear(struct seq_oss_readq *readq); unsigned int snd_seq_oss_readq_poll(struct seq_oss_readq *readq, struct file *file, poll_table *wait); int snd_seq_oss_readq_puts(struct seq_oss_readq *readq, int dev, unsigned char *data, int len); +int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev, + struct snd_seq_event *ev); int snd_seq_oss_readq_put_event(struct seq_oss_readq *readq, union evrec *ev); int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *readq, unsigned long curt, int seq_mode); int snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec); -- GitLab From 174d20a4a01f5cfdc8727113519d1ee89679a03a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Nov 2017 20:16:50 +0100 Subject: [PATCH 1498/5498] ALSA: seq: Avoid invalid lockdep class warning commit 3510c7aa069aa83a2de6dab2b41401a198317bdc upstream. The recent fix for adding rwsem nesting annotation was using the given "hop" argument as the lock subclass key. Although the idea itself works, it may trigger a kernel warning like: BUG: looking up invalid subclass: 8 .... since the lockdep has a smaller number of subclasses (8) than we currently allow for the hops there (10). The current definition is merely a sanity check for avoiding the too deep delivery paths, and the 8 hops are already enough. So, as a quick fix, just follow the max hops as same as the max lockdep subclasses. Fixes: 1f20f9ff57ca ("ALSA: seq: Fix nested rwsem annotation for lockdep splat") Reported-by: syzbot Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- include/sound/seq_kernel.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h index 2398521f0998..d06bb8109cdc 100644 --- a/include/sound/seq_kernel.h +++ b/include/sound/seq_kernel.h @@ -55,7 +55,8 @@ typedef union snd_seq_timestamp snd_seq_timestamp_t; #define SNDRV_SEQ_DEFAULT_CLIENT_EVENTS 200 /* max delivery path length */ -#define SNDRV_SEQ_MAX_HOPS 10 +/* NOTE: this shouldn't be greater than MAX_LOCKDEP_SUBCLASSES */ +#define SNDRV_SEQ_MAX_HOPS 8 /* max size of event size */ #define SNDRV_SEQ_MAX_EVENT_LEN 0x3fffffff -- GitLab From a1b7688279b9d50d5a43d1027d7477191b300581 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 31 Oct 2017 00:35:03 -0500 Subject: [PATCH 1499/5498] MIPS: microMIPS: Fix incorrect mask in insn_table_MM commit 77238e76b9156d28d86c1e31c00ed2960df0e4de upstream. It seems that this is a typo error and the proper bit masking is "RT | RS" instead of "RS | RS". This issue was detected with the help of Coccinelle. Fixes: d6b3314b49e1 ("MIPS: uasm: Add lh uam instruction") Reported-by: Julia Lawall Signed-off-by: Gustavo A. R. Silva Reviewed-by: James Hogan Patchwork: https://patchwork.linux-mips.org/patch/17551/ Signed-off-by: James Hogan [jhogan@kernel.org: Backported 3.16..4.12] Signed-off-by: Greg Kroah-Hartman --- arch/mips/mm/uasm-micromips.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/mm/uasm-micromips.c b/arch/mips/mm/uasm-micromips.c index 8399ddf03a02..cab124e0572d 100644 --- a/arch/mips/mm/uasm-micromips.c +++ b/arch/mips/mm/uasm-micromips.c @@ -83,7 +83,7 @@ static struct insn insn_table_MM[] = { { insn_jr, M(mm_pool32a_op, 0, 0, 0, mm_jalr_op, mm_pool32axf_op), RS }, { insn_lb, M(mm_lb32_op, 0, 0, 0, 0, 0), RT | RS | SIMM }, { insn_ld, 0, 0 }, - { insn_lh, M(mm_lh32_op, 0, 0, 0, 0, 0), RS | RS | SIMM }, + { insn_lh, M(mm_lh32_op, 0, 0, 0, 0, 0), RT | RS | SIMM }, { insn_ll, M(mm_pool32c_op, 0, 0, (mm_ll_func << 1), 0, 0), RS | RT | SIMM }, { insn_lld, 0, 0 }, { insn_lui, M(mm_pool32i_op, mm_lui_op, 0, 0, 0, 0), RS | SIMM }, -- GitLab From 66bf9d410964aaaae974cb3b71a48ec7b5df077b Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 31 Oct 2017 15:09:22 -0700 Subject: [PATCH 1500/5498] MIPS: Fix CM region target definitions commit 6a6cba1d945a7511cdfaf338526871195e420762 upstream. The default CM target field in the GCR_BASE register is encoded with 0 meaning memory & 1 being reserved. However the definitions we use for those bits effectively get these two values backwards - likely because they were copied from the definitions for the CM regions where the target is encoded differently. This results in use setting up GCR_BASE with the reserved target value by default, rather than targeting memory as intended. Although we currently seem to get away with this it's not a great idea to rely upon. Fix this by changing our macros to match the documentated target values. The incorrect encoding became used as of commit 9f98f3dd0c51 ("MIPS: Add generic CM probe & access code") in the Linux v3.15 cycle, and was likely carried forwards from older but unused code introduced by commit 39b8d5254246 ("[MIPS] Add support for MIPS CMP platform.") in the v2.6.26 cycle. Fixes: 9f98f3dd0c51 ("MIPS: Add generic CM probe & access code") Signed-off-by: Paul Burton Reported-by: Matt Redfearn Reviewed-by: James Hogan Cc: Matt Redfearn Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: # v3.15+ Patchwork: https://patchwork.linux-mips.org/patch/17562/ Signed-off-by: James Hogan [jhogan@kernel.org: Backported 3.15..4.13] Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/mips-cm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/include/asm/mips-cm.h b/arch/mips/include/asm/mips-cm.h index 6a9d2dd005ca..79c882be9d17 100644 --- a/arch/mips/include/asm/mips-cm.h +++ b/arch/mips/include/asm/mips-cm.h @@ -173,8 +173,8 @@ BUILD_CM_Cx_R_(tcid_8_priority, 0x80) #define CM_GCR_BASE_GCRBASE_MSK (_ULCAST_(0x1ffff) << 15) #define CM_GCR_BASE_CMDEFTGT_SHF 0 #define CM_GCR_BASE_CMDEFTGT_MSK (_ULCAST_(0x3) << 0) -#define CM_GCR_BASE_CMDEFTGT_DISABLED 0 -#define CM_GCR_BASE_CMDEFTGT_MEM 1 +#define CM_GCR_BASE_CMDEFTGT_MEM 0 +#define CM_GCR_BASE_CMDEFTGT_RESERVED 1 #define CM_GCR_BASE_CMDEFTGT_IOCU0 2 #define CM_GCR_BASE_CMDEFTGT_IOCU1 3 -- GitLab From 30651596c4b01c5724b1507406f5b0dfbb6ba786 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 29 Oct 2017 16:27:20 +0100 Subject: [PATCH 1501/5498] MIPS: AR7: Ensure that serial ports are properly set up commit b084116f8587b222a2c5ef6dcd846f40f24b9420 upstream. Without UPF_FIXED_TYPE, the data from the PORT_AR7 uart_config entry is never copied, resulting in a dead port. Fixes: 154615d55459 ("MIPS: AR7: Use correct UART port type") Signed-off-by: Oswald Buddenhagen [jonas.gorski: add Fixes tag] Signed-off-by: Jonas Gorski Reviewed-by: Florian Fainelli Cc: Ralf Baechle Cc: Greg Kroah-Hartman Cc: Yoshihiro YUNOMAE Cc: Nicolas Schichan Cc: Oswald Buddenhagen Cc: linux-mips@linux-mips.org Cc: linux-serial@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/17543/ Signed-off-by: James Hogan Signed-off-by: Greg Kroah-Hartman --- arch/mips/ar7/platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c index 7e2356fd5fd6..e4f47d3e55bb 100644 --- a/arch/mips/ar7/platform.c +++ b/arch/mips/ar7/platform.c @@ -581,6 +581,7 @@ static int __init ar7_register_uarts(void) uart_port.type = PORT_AR7; uart_port.uartclk = clk_get_rate(bus_clk) / 2; uart_port.iotype = UPIO_MEM32; + uart_port.flags = UPF_FIXED_TYPE; uart_port.regshift = 2; uart_port.line = 0; -- GitLab From e242b094d3b3f50bf868ae35501dda2bcb2dd177 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 6 Nov 2017 11:33:36 +0100 Subject: [PATCH 1502/5498] rbd: use GFP_NOIO for parent stat and data requests commit 1e37f2f84680fa7f8394fd444b6928e334495ccc upstream. rbd_img_obj_exists_submit() and rbd_img_obj_parent_read_full() are on the writeback path for cloned images -- we attempt a stat on the parent object to see if it exists and potentially read it in to call copyup. GFP_NOIO should be used instead of GFP_KERNEL here. Link: http://tracker.ceph.com/issues/22014 Signed-off-by: Ilya Dryomov Reviewed-by: David Disseldorp [idryomov@gmail.com: backport to < 4.9: context] Signed-off-by: Greg Kroah-Hartman --- drivers/block/rbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 2f7358726737..9e97f3415b36 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2700,7 +2700,7 @@ static int rbd_img_obj_parent_read_full(struct rbd_obj_request *obj_request) * from the parent. */ page_count = (u32)calc_pages_for(0, length); - pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); + pages = ceph_alloc_page_vector(page_count, GFP_NOIO); if (IS_ERR(pages)) { result = PTR_ERR(pages); pages = NULL; @@ -2827,7 +2827,7 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request) */ size = sizeof (__le64) + sizeof (__le32) + sizeof (__le32); page_count = (u32)calc_pages_for(0, size); - pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); + pages = ceph_alloc_page_vector(page_count, GFP_NOIO); if (IS_ERR(pages)) return PTR_ERR(pages); -- GitLab From a69b239eb1f3de7db63bf24b1a74a9fa7443383a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sch=C3=BCtz?= Date: Sun, 29 Oct 2017 13:03:22 +0100 Subject: [PATCH 1503/5498] can: c_can: don't indicate triple sampling support for D_CAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fb5f0b3ef69b95e665e4bbe8a3de7201f09f1071 upstream. The D_CAN controller doesn't provide a triple sampling mode, so don't set the CAN_CTRLMODE_3_SAMPLES flag in ctrlmode_supported. Currently enabling triple sampling is a no-op. Signed-off-by: Richard Schütz Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/c_can/c_can_pci.c | 1 - drivers/net/can/c_can/c_can_platform.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c index 7be393c96b1a..8d2724327f69 100644 --- a/drivers/net/can/c_can/c_can_pci.c +++ b/drivers/net/can/c_can/c_can_pci.c @@ -177,7 +177,6 @@ static int c_can_pci_probe(struct pci_dev *pdev, break; case BOSCH_D_CAN: priv->regs = reg_map_d_can; - priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; break; default: ret = -EINVAL; diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index fb279d6ae484..ef056a33604e 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -257,7 +257,6 @@ static int c_can_plat_probe(struct platform_device *pdev) break; case BOSCH_D_CAN: priv->regs = reg_map_d_can; - priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; priv->read_reg32 = d_can_plat_read_reg32; -- GitLab From 76bb85f25229bbde79ff64c2ea6d86f18ab99b26 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 7 Nov 2017 18:53:07 +0100 Subject: [PATCH 1504/5498] x86/oprofile/ppro: Do not use __this_cpu*() in preemptible context commit a743bbeef27b9176987ec0cb7f906ab0ab52d1da upstream. The warning below says it all: BUG: using __this_cpu_read() in preemptible [00000000] code: swapper/0/1 caller is __this_cpu_preempt_check CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.0-rc8 #4 Call Trace: dump_stack check_preemption_disabled ? do_early_param __this_cpu_preempt_check arch_perfmon_init op_nmi_init ? alloc_pci_root_info oprofile_arch_init oprofile_init do_one_initcall ... These accessors should not have been used in the first place: it is PPro so no mixed silicon revisions and thus it can simply use boot_cpu_data. Reported-by: Fengguang Wu Tested-by: Fengguang Wu Fix-creation-mandated-by: Linus Torvalds Signed-off-by: Borislav Petkov Signed-off-by: Thomas Gleixner Cc: Robert Richter Cc: x86@kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/x86/oprofile/op_model_ppro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/oprofile/op_model_ppro.c b/arch/x86/oprofile/op_model_ppro.c index d90528ea5412..12c051d19e4b 100644 --- a/arch/x86/oprofile/op_model_ppro.c +++ b/arch/x86/oprofile/op_model_ppro.c @@ -212,8 +212,8 @@ static void arch_perfmon_setup_counters(void) eax.full = cpuid_eax(0xa); /* Workaround for BIOS bugs in 6/15. Taken from perfmon2 */ - if (eax.split.version_id == 0 && __this_cpu_read(cpu_info.x86) == 6 && - __this_cpu_read(cpu_info.x86_model) == 15) { + if (eax.split.version_id == 0 && boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model == 15) { eax.split.version_id = 2; eax.split.num_counters = 2; eax.split.bit_width = 40; -- GitLab From 37cdf969145fd1590b1590df5b3695245e17f01b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 15 Nov 2017 10:04:14 +0100 Subject: [PATCH 1505/5498] Linux 3.18.81 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8feea3dadc60..5e66a3704705 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 80 +SUBLEVEL = 81 EXTRAVERSION = NAME = Diseased Newt -- GitLab From cf29e522b01891eeb48628fc10283ca87f3aff15 Mon Sep 17 00:00:00 2001 From: Satyajit Desai Date: Mon, 30 Oct 2017 12:16:42 -0700 Subject: [PATCH 1506/5498] soc: qcom: Update Kconfig to be inline with standard terminology Update Kconfig wording to be inline with standard terminology. Change-Id: Idca8732cc02781dcf2aae38eddd5343b3e020440 Signed-off-by: Satyajit Desai Signed-off-by: Saranya Chidura --- drivers/soc/qcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index e78a75221a93..f7d603853044 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -902,7 +902,7 @@ config QCOM_EARLY_RANDOM to get some random data to put in the pool. If unsure, say N. config QCOM_QDSS_BRIDGE - bool "Configure bridge driver for Qualcomm MDM" + bool "Configure bridge driver for Qualcomm Technologies, Inc. MDM" depends on MSM_MHI help The driver will help route diag traffic over the QDSS sub-system to -- GitLab From 5c614402826971f029363ea4b712efe3551948aa Mon Sep 17 00:00:00 2001 From: Gaurav Singhal Date: Wed, 15 Nov 2017 14:40:55 +0530 Subject: [PATCH 1507/5498] defconfig: msm8909w: Enable NFC NQ driver Enable NFC NQ driver. Change-Id: I7f6e5c91cc7d7b314ae906e0dba1c54d260d37aa Signed-off-by: Gaurav Singhal --- arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 8e0542ab2671..1fc60b2c29cd 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -151,6 +151,7 @@ CONFIG_BT_HIDP=y CONFIG_CFG80211=y CONFIG_NL80211_TESTMODE=y CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_NFC_NQ=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 354ac91979d3..a9681bd728d2 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -151,6 +151,7 @@ CONFIG_BT_HIDP=y CONFIG_CFG80211=y CONFIG_NL80211_TESTMODE=y CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_NFC_NQ=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y -- GitLab From 20e0cbdf8b301084f6a581defc1a492280f0a999 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Fri, 10 Nov 2017 15:30:27 -0800 Subject: [PATCH 1508/5498] FROMLIST: binder: fix proc->files use-after-free (from https://patchwork.kernel.org/patch/10058587/) proc->files cleanup is initiated by binder_vma_close. Therefore a reference on the binder_proc is not enough to prevent the files_struct from being released while the binder_proc still has a reference. This can lead to an attempt to dereference the stale pointer obtained from proc->files prior to proc->files cleanup. This has been seen once in task_get_unused_fd_flags() when __alloc_fd() is called with a stale "files". The fix is to always use get_files_struct() to obtain struct_files so that the refcount on the files_struct is used to prevent a premature free. proc->files is removed since we get it every time. Bug: 69164715 Change-Id: I6431027d3d569e76913935c21885201505627982 Signed-off-by: Todd Kjos --- drivers/staging/android/binder.c | 63 +++++++++++++++----------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index c2e2beaf0713..4e86db3449dc 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -462,9 +462,8 @@ struct binder_ref { }; enum binder_deferred_state { - BINDER_DEFERRED_PUT_FILES = 0x01, - BINDER_DEFERRED_FLUSH = 0x02, - BINDER_DEFERRED_RELEASE = 0x04, + BINDER_DEFERRED_FLUSH = 0x01, + BINDER_DEFERRED_RELEASE = 0x02, }; /** @@ -501,8 +500,6 @@ struct binder_priority { * (invariant after initialized) * @tsk task_struct for group_leader of process * (invariant after initialized) - * @files files_struct for process - * (invariant after initialized) * @deferred_work_node: element for binder_deferred_list * (protected by binder_deferred_lock) * @deferred_work: bitmap of deferred work to perform @@ -549,7 +546,6 @@ struct binder_proc { struct list_head waiting_threads; int pid; struct task_struct *tsk; - struct files_struct *files; struct hlist_node deferred_work_node; int deferred_work; bool is_dead; @@ -945,22 +941,34 @@ static void binder_free_thread(struct binder_thread *thread); static void binder_free_proc(struct binder_proc *proc); static void binder_inc_node_tmpref_ilocked(struct binder_node *node); +struct files_struct *binder_get_files_struct(struct binder_proc *proc) +{ + return get_files_struct(proc->tsk); +} + static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) { - struct files_struct *files = proc->files; + struct files_struct *files; unsigned long rlim_cur; unsigned long irqs; + int ret; + files = binder_get_files_struct(proc); if (files == NULL) return -ESRCH; - if (!lock_task_sighand(proc->tsk, &irqs)) - return -EMFILE; + if (!lock_task_sighand(proc->tsk, &irqs)) { + ret = -EMFILE; + goto err; + } rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); unlock_task_sighand(proc->tsk, &irqs); - return __alloc_fd(files, 0, rlim_cur, flags); + ret = __alloc_fd(files, 0, rlim_cur, flags); +err: + put_files_struct(files); + return ret; } /* @@ -969,8 +977,12 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) static void task_fd_install( struct binder_proc *proc, unsigned int fd, struct file *file) { - if (proc->files) - __fd_install(proc->files, fd, file); + struct files_struct *files = binder_get_files_struct(proc); + + if (files) { + __fd_install(files, fd, file); + put_files_struct(files); + } } /* @@ -978,18 +990,20 @@ static void task_fd_install( */ static long task_close_fd(struct binder_proc *proc, unsigned int fd) { + struct files_struct *files = binder_get_files_struct(proc); int retval; - if (proc->files == NULL) + if (files == NULL) return -ESRCH; - retval = __close_fd(proc->files, fd); + retval = __close_fd(files, fd); /* can't restart close syscall because file table entry was cleared */ if (unlikely(retval == -ERESTARTSYS || retval == -ERESTARTNOINTR || retval == -ERESTARTNOHAND || retval == -ERESTART_RESTARTBLOCK)) retval = -EINTR; + put_files_struct(files); return retval; } @@ -4814,7 +4828,6 @@ static void binder_vma_close(struct vm_area_struct *vma) (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); binder_alloc_vma_close(&proc->alloc); - binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); } static int binder_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) @@ -4856,10 +4869,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_private_data = proc; ret = binder_alloc_mmap_handler(&proc->alloc, vma); - if (ret) - return ret; - proc->files = get_files_struct(current); - return 0; + + return ret; err_bad_arg: pr_err("binder_mmap: %d %lx-%lx %s failed %d\n", @@ -5038,8 +5049,6 @@ static void binder_deferred_release(struct binder_proc *proc) struct rb_node *n; int threads, nodes, incoming_refs, outgoing_refs, active_transactions; - BUG_ON(proc->files); - mutex_lock(&binder_procs_lock); hlist_del(&proc->proc_node); mutex_unlock(&binder_procs_lock); @@ -5121,8 +5130,6 @@ static void binder_deferred_release(struct binder_proc *proc) static void binder_deferred_func(struct work_struct *work) { struct binder_proc *proc; - struct files_struct *files; - int defer; do { @@ -5139,21 +5146,11 @@ static void binder_deferred_func(struct work_struct *work) } mutex_unlock(&binder_deferred_lock); - files = NULL; - if (defer & BINDER_DEFERRED_PUT_FILES) { - files = proc->files; - if (files) - proc->files = NULL; - } - if (defer & BINDER_DEFERRED_FLUSH) binder_deferred_flush(proc); if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ - - if (files) - put_files_struct(files); } while (proc); } static DECLARE_WORK(binder_deferred_work, binder_deferred_func); -- GitLab From bc58a36645c139ff1406834e98efa34e0a430e94 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Sat, 23 Sep 2017 17:02:18 +0800 Subject: [PATCH 1509/5498] Revert "f2fs: node segment is prior to data segment selected victim" This reverts commit b9cd20619e359d199b755543474c3d853c8e3415. That patch causes much fewer node segments (which can be used for SSR) than before, and in the corner case (e.g. create and delete *.txt files in one same directory, there will be very few node segments but many data segments), if the reserved free segments are all used up during gc, then the write_checkpoint can still flush dentry pages to data ssr segments, but will probably fail to flush node pages to node ssr segments, since there are not enough node ssr segments left (the left ones are all full). So revert this patch to give a fair chance to let node segments remain for SSR, which provides more robustness for corner cases. Conflicts: fs/f2fs/gc.c Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index bfe6a8ccc3a0..f777e073e509 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -267,16 +267,6 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) return UINT_MAX - ((100 * (100 - u) * age) / (100 + u)); } -static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - unsigned int valid_blocks = - get_valid_blocks(sbi, segno, true); - - return IS_DATASEG(get_seg_entry(sbi, segno)->type) ? - valid_blocks * 2 : valid_blocks; -} - static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, unsigned int segno, struct victim_sel_policy *p) { @@ -285,7 +275,7 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, /* alloc_mode == LFS */ if (p->gc_mode == GC_GREEDY) - return get_greedy_cost(sbi, segno); + return get_valid_blocks(sbi, segno, true); else return get_cb_cost(sbi, segno); } -- GitLab From 9944032aa37328492bea231e5180912083c5da9f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 25 Sep 2017 14:17:51 +0800 Subject: [PATCH 1510/5498] Revert "f2fs: reuse nids more aggressively" Commit 268344664603 ("f2fs: reuse nids more aggressively") tries to reuse nids as many as possilbe, in order to mitigate producing obsolete node pages in page cache. But acutally, before we reuse the nids and related node page cache, we will always invalidate that node page, so there will be not any obsolete node pages in cache. Let's just revert previous commit, so that nm_i::next_scan_nid can be increased ascendingly, making __build_free_nids traverses all NAT pages more easily, finally, free nid bitmap cache can be enabled as soon as possible. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 25d2dbe1aec8..aae90c1e12c1 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -327,10 +327,6 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, if (nat_get_blkaddr(e) != NEW_ADDR && new_blkaddr == NULL_ADDR) { unsigned char version = nat_get_version(e); nat_set_version(e, inc_node_version(version)); - - /* in order to reuse the nid */ - if (nm_i->next_scan_nid > ni->nid) - nm_i->next_scan_nid = ni->nid; } /* change address */ -- GitLab From e002c8118ab95173bf83df9aba7a1498e0e11fb9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 4 Sep 2017 18:58:02 +0800 Subject: [PATCH 1511/5498] f2fs: introduce read_inline_xattr Commit ba38c27eb93e ("f2fs: enhance lookup xattr") introduces lookup_all_xattrs duplicating from read_all_xattrs, which leaves lots of similar codes in between them, so introduce new help read_inline_xattr to clean up redundant codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 59 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index ec48fe87414b..727a6c404c3c 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -290,6 +290,29 @@ static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr, return entry; } +static int read_inline_xattr(struct inode *inode, struct page *ipage, + void *txattr_addr) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + unsigned int inline_size = inline_xattr_size(inode); + struct page *page = NULL; + void *inline_addr; + + if (ipage) { + inline_addr = inline_xattr_addr(ipage); + } else { + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) + return PTR_ERR(page); + + inline_addr = inline_xattr_addr(page); + } + memcpy(txattr_addr, inline_addr, inline_size); + f2fs_put_page(page, 1); + + return 0; +} + static int lookup_all_xattrs(struct inode *inode, struct page *ipage, unsigned int index, unsigned int len, const char *name, struct f2fs_xattr_entry **xe, @@ -312,21 +335,9 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, /* read from inline xattr */ if (inline_size) { - struct page *page = NULL; - void *inline_addr; - - if (ipage) { - inline_addr = inline_xattr_addr(ipage); - } else { - page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) { - err = PTR_ERR(page); - goto out; - } - inline_addr = inline_xattr_addr(page); - } - memcpy(txattr_addr, inline_addr, inline_size); - f2fs_put_page(page, 1); + err = read_inline_xattr(inode, ipage, txattr_addr); + if (err) + goto out; *xe = __find_inline_xattr(txattr_addr, &last_addr, index, len, name); @@ -388,21 +399,9 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, /* read from inline xattr */ if (inline_size) { - struct page *page = NULL; - void *inline_addr; - - if (ipage) { - inline_addr = inline_xattr_addr(ipage); - } else { - page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) { - err = PTR_ERR(page); - goto fail; - } - inline_addr = inline_xattr_addr(page); - } - memcpy(txattr_addr, inline_addr, inline_size); - f2fs_put_page(page, 1); + err = read_inline_xattr(inode, ipage, txattr_addr); + if (err) + goto fail; } /* read from xattr node block */ -- GitLab From ca433f07cc321d4f5384876f5f8816ab7f027d4c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 4 Sep 2017 18:58:03 +0800 Subject: [PATCH 1512/5498] f2fs: introduce read_xattr_block Commit ba38c27eb93e ("f2fs: enhance lookup xattr") introduces lookup_all_xattrs duplicating from read_all_xattrs, which leaves lots of similar codes in between them, so introduce new help read_xattr_block to clean up redundant codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 727a6c404c3c..0c2727f07a45 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -313,12 +313,31 @@ static int read_inline_xattr(struct inode *inode, struct page *ipage, return 0; } +static int read_xattr_block(struct inode *inode, void *txattr_addr) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + nid_t xnid = F2FS_I(inode)->i_xattr_nid; + unsigned int inline_size = inline_xattr_size(inode); + struct page *xpage; + void *xattr_addr; + + /* The inode already has an extended attribute block. */ + xpage = get_node_page(sbi, xnid); + if (IS_ERR(xpage)) + return PTR_ERR(xpage); + + xattr_addr = page_address(xpage); + memcpy(txattr_addr + inline_size, xattr_addr, VALID_XATTR_BLOCK_SIZE); + f2fs_put_page(xpage, 1); + + return 0; +} + static int lookup_all_xattrs(struct inode *inode, struct page *ipage, unsigned int index, unsigned int len, const char *name, struct f2fs_xattr_entry **xe, void **base_addr) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); void *cur_addr, *txattr_addr, *last_addr = NULL; nid_t xnid = F2FS_I(inode)->i_xattr_nid; unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0; @@ -347,19 +366,9 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, /* read from xattr node block */ if (xnid) { - struct page *xpage; - void *xattr_addr; - - /* The inode already has an extended attribute block. */ - xpage = get_node_page(sbi, xnid); - if (IS_ERR(xpage)) { - err = PTR_ERR(xpage); + err = read_xattr_block(inode, txattr_addr); + if (err) goto out; - } - - xattr_addr = page_address(xpage); - memcpy(txattr_addr + inline_size, xattr_addr, size); - f2fs_put_page(xpage, 1); } if (last_addr) @@ -384,7 +393,6 @@ out: static int read_all_xattrs(struct inode *inode, struct page *ipage, void **base_addr) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_xattr_header *header; nid_t xnid = F2FS_I(inode)->i_xattr_nid; unsigned int size = VALID_XATTR_BLOCK_SIZE; @@ -406,19 +414,9 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, /* read from xattr node block */ if (xnid) { - struct page *xpage; - void *xattr_addr; - - /* The inode already has an extended attribute block. */ - xpage = get_node_page(sbi, xnid); - if (IS_ERR(xpage)) { - err = PTR_ERR(xpage); + err = read_xattr_block(inode, txattr_addr); + if (err) goto fail; - } - - xattr_addr = page_address(xpage); - memcpy(txattr_addr + inline_size, xattr_addr, size); - f2fs_put_page(xpage, 1); } header = XATTR_HDR(txattr_addr); -- GitLab From 5c1821a849ea81f22da08322db4978b32f848c88 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 14 Sep 2017 10:18:01 +0800 Subject: [PATCH 1513/5498] f2fs: show flush list status in sysfs This patch adds to show flush list status in sysfs. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 5 ++++- fs/f2fs/f2fs.h | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 629c57024106..67efe669a6fe 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -61,6 +61,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) atomic_read(&SM_I(sbi)->fcc_info->issued_flush); si->nr_flushing = atomic_read(&SM_I(sbi)->fcc_info->issing_flush); + si->flush_list_empty = + llist_empty(&SM_I(sbi)->fcc_info->issue_list); } if (SM_I(sbi) && SM_I(sbi)->dcc_info) { si->nr_discarded = @@ -351,10 +353,11 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d), " + seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d %4d), " "Discard: (%4d %4d)) cmd: %4d undiscard:%4u\n", si->nr_wb_cp_data, si->nr_wb_data, si->nr_flushing, si->nr_flushed, + si->flush_list_empty, si->nr_discarding, si->nr_discarded, si->nr_discard_cmd, si->undiscard_blks); seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), " diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8193223fc146..fce44cfd82ec 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2789,7 +2789,8 @@ struct f2fs_stat_info { int free_nids, avail_nids, alloc_nids; int total_count, utilization; int bg_gc, nr_wb_cp_data, nr_wb_data; - int nr_flushing, nr_flushed, nr_discarding, nr_discarded; + int nr_flushing, nr_flushed, flush_list_empty; + int nr_discarding, nr_discarded; int nr_discard_cmd; unsigned int undiscard_blks; int inline_xattr, inline_inode, inline_dir, append, update, orphans; -- GitLab From 8e68de1c1db07d87fca7c21171187ae029480795 Mon Sep 17 00:00:00 2001 From: Hsiang Kao Date: Sun, 24 Sep 2017 02:45:42 +0800 Subject: [PATCH 1514/5498] f2fs: allow readpages with NULL file pointer Keep in line with the other Linux file system implementations since page_cache_sync_readahead supports NULL file pointer, and thus we can readahead data by f2fs itself without file opening (something like the btrfs behavior). Signed-off-by: Gao Xiang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 7b72ac0bca51..cbb3adc8291d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1329,7 +1329,7 @@ static int f2fs_read_data_pages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { - struct inode *inode = file->f_mapping->host; + struct inode *inode = mapping->host; struct page *page = list_last_entry(pages, struct page, lru); trace_f2fs_readpages(inode, page, nr_pages); -- GitLab From 33646c982202c5ce1684fc2af3ce227b25f07d31 Mon Sep 17 00:00:00 2001 From: Weichao Guo Date: Fri, 29 Sep 2017 22:43:23 +0800 Subject: [PATCH 1515/5498] f2fs: convert inline data for direct I/O & FI_NO_PREALLOC In FI_NO_PREALLOC cases, direct I/O path may allocate blocks for an inode but keep its inline data flag. This inconsistency may trigger vfs clear_inode nrpages bug_on when evicting the inode. We should convert inline data first in this case. Signed-off-by: Weichao Guo Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index cbb3adc8291d..ff3a93c8c827 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -832,6 +832,13 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, struct f2fs_map_blocks map; int err = 0; + /* convert inline data for Direct I/O*/ + if (dio) { + err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } + map.m_lblk = F2FS_BLK_ALIGN(pos); map.m_len = F2FS_BYTES_TO_BLK(pos + count); if (map.m_len > map.m_lblk) @@ -841,15 +848,11 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_next_pgofs = NULL; - if (dio) { - err = f2fs_convert_inline_inode(inode); - if (err) - return err; + if (dio) return f2fs_map_blocks(inode, &map, 1, __force_buffered_io(inode, WRITE) ? F2FS_GET_BLOCK_PRE_AIO : F2FS_GET_BLOCK_PRE_DIO); - } if (pos + count > MAX_INLINE_DATA(inode)) { err = f2fs_convert_inline_inode(inode); if (err) -- GitLab From b5b73718f0b3d9bb63a8ecdf8c889def395cb182 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Sep 2017 13:59:35 +0800 Subject: [PATCH 1516/5498] f2fs: obsolete ALLOC_NID_LIST list As Fan Li reported, there is no user traversing nid_list[ALLOC_NID_LIST] which is used for tracking preallocated nids. Let's drop it, and only track preallocated nids in free_nid_root radix-tree. Reported-by: Fan Li Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 8 ++-- fs/f2fs/f2fs.h | 15 ++++--- fs/f2fs/node.c | 97 ++++++++++++++++++++++------------------------ fs/f2fs/node.h | 15 ++----- fs/f2fs/shrinker.c | 2 +- 5 files changed, 64 insertions(+), 73 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 67efe669a6fe..9ff6c5cb4b29 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -98,9 +98,9 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->dirty_nats = NM_I(sbi)->dirty_nat_cnt; si->sits = MAIN_SEGS(sbi); si->dirty_sits = SIT_I(sbi)->dirty_sentries; - si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID_LIST]; + si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID]; si->avail_nids = NM_I(sbi)->available_nids; - si->alloc_nids = NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]; + si->alloc_nids = NM_I(sbi)->nid_cnt[PREALLOC_NID]; si->bg_gc = sbi->bg_gc; si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg) * 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg) @@ -233,8 +233,8 @@ get_cache: } /* free nids */ - si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID_LIST] + - NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]) * + si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID] + + NM_I(sbi)->nid_cnt[PREALLOC_NID]) * sizeof(struct free_nid); si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry); si->cache_mem += NM_I(sbi)->dirty_nat_cnt * diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index fce44cfd82ec..1b591f841e5e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -760,10 +760,13 @@ static inline void __try_update_largest_extent(struct inode *inode, } } -enum nid_list { - FREE_NID_LIST, - ALLOC_NID_LIST, - MAX_NID_LIST, +/* + * For free nid management + */ +enum nid_state { + FREE_NID, /* newly added to free nid list */ + PREALLOC_NID, /* it is preallocated */ + MAX_NID_STATE, }; struct f2fs_nm_info { @@ -786,8 +789,8 @@ struct f2fs_nm_info { /* free node ids management */ struct radix_tree_root free_nid_root;/* root of the free_nid cache */ - struct list_head nid_list[MAX_NID_LIST];/* lists for free nids */ - unsigned int nid_cnt[MAX_NID_LIST]; /* the number of free node id */ + struct list_head free_nid_list; /* list for free nids excluding preallocated nids */ + unsigned int nid_cnt[MAX_NID_STATE]; /* the number of free node id */ spinlock_t nid_list_lock; /* protect nid lists ops */ struct mutex build_lock; /* lock for build free nids */ unsigned char (*free_nid_bitmap)[NAT_ENTRY_BITMAP_SIZE]; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index aae90c1e12c1..8477417e4073 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -46,7 +46,7 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) * give 25%, 25%, 50%, 50%, 50% memory for each components respectively */ if (type == FREE_NIDS) { - mem_size = (nm_i->nid_cnt[FREE_NID_LIST] * + mem_size = (nm_i->nid_cnt[FREE_NID] * sizeof(struct free_nid)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == NAT_ENTRIES) { @@ -1766,8 +1766,8 @@ static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, return radix_tree_lookup(&nm_i->free_nid_root, n); } -static int __insert_nid_to_list(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_list list, bool new) +static int __insert_free_nid(struct f2fs_sb_info *sbi, + struct free_nid *i, enum nid_state state, bool new) { struct f2fs_nm_info *nm_i = NM_I(sbi); @@ -1777,22 +1777,22 @@ static int __insert_nid_to_list(struct f2fs_sb_info *sbi, return err; } - f2fs_bug_on(sbi, list == FREE_NID_LIST ? i->state != NID_NEW : - i->state != NID_ALLOC); - nm_i->nid_cnt[list]++; - list_add_tail(&i->list, &nm_i->nid_list[list]); + f2fs_bug_on(sbi, state != i->state); + nm_i->nid_cnt[state]++; + if (state == FREE_NID) + list_add_tail(&i->list, &nm_i->free_nid_list); return 0; } -static void __remove_nid_from_list(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_list list, bool reuse) +static void __remove_free_nid(struct f2fs_sb_info *sbi, + struct free_nid *i, enum nid_state state, bool reuse) { struct f2fs_nm_info *nm_i = NM_I(sbi); - f2fs_bug_on(sbi, list == FREE_NID_LIST ? i->state != NID_NEW : - i->state != NID_ALLOC); - nm_i->nid_cnt[list]--; - list_del(&i->list); + f2fs_bug_on(sbi, state != i->state); + nm_i->nid_cnt[state]--; + if (state == FREE_NID) + list_del(&i->list); if (!reuse) radix_tree_delete(&nm_i->free_nid_root, i->nid); } @@ -1812,7 +1812,7 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS); i->nid = nid; - i->state = NID_NEW; + i->state = FREE_NID; if (radix_tree_preload(GFP_NOFS)) goto err; @@ -1825,7 +1825,7 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) * - f2fs_create * - f2fs_new_inode * - alloc_nid - * - __insert_nid_to_list(ALLOC_NID_LIST) + * - __insert_nid_to_list(PREALLOC_NID) * - f2fs_balance_fs_bg * - build_free_nids * - __build_free_nids @@ -1838,8 +1838,8 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) * - new_node_page * - set_node_addr * - alloc_nid_done - * - __remove_nid_from_list(ALLOC_NID_LIST) - * - __insert_nid_to_list(FREE_NID_LIST) + * - __remove_nid_from_list(PREALLOC_NID) + * - __insert_nid_to_list(FREE_NID) */ ne = __lookup_nat_cache(nm_i, nid); if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || @@ -1848,13 +1848,13 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) e = __lookup_free_nid_list(nm_i, nid); if (e) { - if (e->state == NID_NEW) + if (e->state == FREE_NID) ret = true; goto err_out; } } ret = true; - err = __insert_nid_to_list(sbi, i, FREE_NID_LIST, true); + err = __insert_free_nid(sbi, i, FREE_NID, true); err_out: spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); @@ -1872,8 +1872,8 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); - if (i && i->state == NID_NEW) { - __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); + if (i && i->state == FREE_NID) { + __remove_free_nid(sbi, i, FREE_NID, false); need_free = true; } spin_unlock(&nm_i->nid_list_lock); @@ -1958,7 +1958,7 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) nid = i * NAT_ENTRY_PER_BLOCK + idx; add_free_nid(sbi, nid, true); - if (nm_i->nid_cnt[FREE_NID_LIST] >= MAX_FREE_NIDS) + if (nm_i->nid_cnt[FREE_NID] >= MAX_FREE_NIDS) goto out; } } @@ -1991,7 +1991,7 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) nid = 0; /* Enough entries */ - if (nm_i->nid_cnt[FREE_NID_LIST] >= NAT_ENTRY_PER_BLOCK) + if (nm_i->nid_cnt[FREE_NID] >= NAT_ENTRY_PER_BLOCK) return; if (!sync && !available_free_memory(sbi, FREE_NIDS)) @@ -2001,7 +2001,7 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) /* try to find free nids in free_nid_bitmap */ scan_free_nid_bits(sbi); - if (nm_i->nid_cnt[FREE_NID_LIST]) + if (nm_i->nid_cnt[FREE_NID]) return; } @@ -2078,15 +2078,15 @@ retry: } /* We should not use stale free nids created by build_free_nids */ - if (nm_i->nid_cnt[FREE_NID_LIST] && !on_build_free_nids(nm_i)) { - f2fs_bug_on(sbi, list_empty(&nm_i->nid_list[FREE_NID_LIST])); - i = list_first_entry(&nm_i->nid_list[FREE_NID_LIST], + if (nm_i->nid_cnt[FREE_NID] && !on_build_free_nids(nm_i)) { + f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list)); + i = list_first_entry(&nm_i->free_nid_list, struct free_nid, list); *nid = i->nid; - __remove_nid_from_list(sbi, i, FREE_NID_LIST, true); - i->state = NID_ALLOC; - __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); + __remove_free_nid(sbi, i, FREE_NID, true); + i->state = PREALLOC_NID; + __insert_free_nid(sbi, i, PREALLOC_NID, false); nm_i->available_nids--; update_free_nid_bitmap(sbi, *nid, false, false); @@ -2112,7 +2112,7 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); f2fs_bug_on(sbi, !i); - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, false); + __remove_free_nid(sbi, i, PREALLOC_NID, false); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); @@ -2135,12 +2135,12 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) f2fs_bug_on(sbi, !i); if (!available_free_memory(sbi, FREE_NIDS)) { - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, false); + __remove_free_nid(sbi, i, PREALLOC_NID, false); need_free = true; } else { - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, true); - i->state = NID_NEW; - __insert_nid_to_list(sbi, i, FREE_NID_LIST, false); + __remove_free_nid(sbi, i, PREALLOC_NID, true); + i->state = FREE_NID; + __insert_free_nid(sbi, i, FREE_NID, false); } nm_i->available_nids++; @@ -2159,20 +2159,19 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) struct free_nid *i, *next; int nr = nr_shrink; - if (nm_i->nid_cnt[FREE_NID_LIST] <= MAX_FREE_NIDS) + if (nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS) return 0; if (!mutex_trylock(&nm_i->build_lock)) return 0; spin_lock(&nm_i->nid_list_lock); - list_for_each_entry_safe(i, next, &nm_i->nid_list[FREE_NID_LIST], - list) { + list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) { if (nr_shrink <= 0 || - nm_i->nid_cnt[FREE_NID_LIST] <= MAX_FREE_NIDS) + nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS) break; - __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); + __remove_free_nid(sbi, i, FREE_NID, false); kmem_cache_free(free_nid_slab, i); nr_shrink--; } @@ -2644,16 +2643,15 @@ static int init_node_manager(struct f2fs_sb_info *sbi) /* not used nids: 0, node, meta, (and root counted as valid node) */ nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count - F2FS_RESERVED_NODE_NUM; - nm_i->nid_cnt[FREE_NID_LIST] = 0; - nm_i->nid_cnt[ALLOC_NID_LIST] = 0; + nm_i->nid_cnt[FREE_NID] = 0; + nm_i->nid_cnt[PREALLOC_NID] = 0; nm_i->nat_cnt = 0; nm_i->ram_thresh = DEF_RAM_THRESHOLD; nm_i->ra_nid_pages = DEF_RA_NID_PAGES; nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD; INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); - INIT_LIST_HEAD(&nm_i->nid_list[FREE_NID_LIST]); - INIT_LIST_HEAD(&nm_i->nid_list[ALLOC_NID_LIST]); + INIT_LIST_HEAD(&nm_i->free_nid_list); INIT_RADIX_TREE(&nm_i->nat_root, GFP_NOIO); INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_NOIO); INIT_LIST_HEAD(&nm_i->nat_entries); @@ -2745,16 +2743,15 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) /* destroy free nid list */ spin_lock(&nm_i->nid_list_lock); - list_for_each_entry_safe(i, next_i, &nm_i->nid_list[FREE_NID_LIST], - list) { - __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); + list_for_each_entry_safe(i, next_i, &nm_i->free_nid_list, list) { + __remove_free_nid(sbi, i, FREE_NID, false); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); spin_lock(&nm_i->nid_list_lock); } - f2fs_bug_on(sbi, nm_i->nid_cnt[FREE_NID_LIST]); - f2fs_bug_on(sbi, nm_i->nid_cnt[ALLOC_NID_LIST]); - f2fs_bug_on(sbi, !list_empty(&nm_i->nid_list[ALLOC_NID_LIST])); + f2fs_bug_on(sbi, nm_i->nid_cnt[FREE_NID]); + f2fs_bug_on(sbi, nm_i->nid_cnt[PREALLOC_NID]); + f2fs_bug_on(sbi, !list_empty(&nm_i->free_nid_list)); spin_unlock(&nm_i->nid_list_lock); /* destroy nat cache */ diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index bb53e9955ff2..e91b08b4a51a 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -150,18 +150,10 @@ struct nat_entry_set { unsigned int entry_cnt; /* the # of nat entries in set */ }; -/* - * For free nid mangement - */ -enum nid_state { - NID_NEW, /* newly added to free nid list */ - NID_ALLOC /* it is allocated */ -}; - struct free_nid { struct list_head list; /* for free node id list */ nid_t nid; /* node id */ - int state; /* in use or not: NID_NEW or NID_ALLOC */ + int state; /* in use or not: FREE_NID or PREALLOC_NID */ }; static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) @@ -170,12 +162,11 @@ static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) struct free_nid *fnid; spin_lock(&nm_i->nid_list_lock); - if (nm_i->nid_cnt[FREE_NID_LIST] <= 0) { + if (nm_i->nid_cnt[FREE_NID] <= 0) { spin_unlock(&nm_i->nid_list_lock); return; } - fnid = list_first_entry(&nm_i->nid_list[FREE_NID_LIST], - struct free_nid, list); + fnid = list_first_entry(&nm_i->free_nid_list, struct free_nid, list); *nid = fnid->nid; spin_unlock(&nm_i->nid_list_lock); } diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index 5c60fc28ec75..0b5664a1a6cc 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -28,7 +28,7 @@ static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi) static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) { - long count = NM_I(sbi)->nid_cnt[FREE_NID_LIST] - MAX_FREE_NIDS; + long count = NM_I(sbi)->nid_cnt[FREE_NID] - MAX_FREE_NIDS; return count > 0 ? count : 0; } -- GitLab From 9e0893947a576a6ca7e9994963b4401899f02acb Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Sep 2017 13:59:36 +0800 Subject: [PATCH 1517/5498] f2fs: drop FI_UPDATE_WRITE tag after f2fs_issue_flush If we failed to issue flush in ->fsync, we need to keep FI_UPDATE_WRITE flag to make sure triggering flush in next ->fsync. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e5a47b59d1dd..8e96536a852e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -297,10 +297,12 @@ sync_nodes: remove_ino_entry(sbi, ino, APPEND_INO); clear_inode_flag(inode, FI_APPEND_WRITE); flush_out: - remove_ino_entry(sbi, ino, UPDATE_INO); - clear_inode_flag(inode, FI_UPDATE_WRITE); if (!atomic) ret = f2fs_issue_flush(sbi); + if (!ret) { + remove_ino_entry(sbi, ino, UPDATE_INO); + clear_inode_flag(inode, FI_UPDATE_WRITE); + } f2fs_update_time(sbi, REQ_TIME); out: trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); -- GitLab From 97acaf716d65b430ca9bc983cea71c18a417b26f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Sep 2017 13:59:37 +0800 Subject: [PATCH 1518/5498] f2fs: fix to show ino management cache size correctly It needs to stat size of ino management cache with all type instead of orphan ino type. Fixes: 652be55162dc ("f2fs: show # of orphan inodes") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 9ff6c5cb4b29..fdffb08cd8ab 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -240,7 +240,7 @@ get_cache: si->cache_mem += NM_I(sbi)->dirty_nat_cnt * sizeof(struct nat_entry_set); si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); - for (i = 0; i <= ORPHAN_INO; i++) + for (i = 0; i < MAX_INO_ENTRY; i++) si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); si->cache_mem += atomic_read(&sbi->total_ext_tree) * sizeof(struct extent_tree); -- GitLab From 98e85b9050dbbe02db66e68338da98e71fe664bf Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Sep 2017 13:59:38 +0800 Subject: [PATCH 1519/5498] f2fs: enhance multiple device flush When multiple device feature is enabled, during ->fsync we will issue flush in all devices to make sure node/data of the file being persisted into storage. But some flushes of device could be unneeded as file's data may be not writebacked into those devices. So this patch adds and manage bitmap per inode in global cache to indicate which device is dirty and it needs to issue flush during ->fsync, hence, we could improve performance of fsync in scenario of multiple device. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 36 +++++++++++++++++++++++++++++----- fs/f2fs/data.c | 1 + fs/f2fs/f2fs.h | 14 +++++++++++--- fs/f2fs/file.c | 3 ++- fs/f2fs/gc.c | 2 ++ fs/f2fs/inline.c | 1 + fs/f2fs/inode.c | 1 + fs/f2fs/node.c | 3 ++- fs/f2fs/segment.c | 46 +++++++++++++++++++++++++++++++++----------- 9 files changed, 86 insertions(+), 21 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 9e8e24f679bb..246871bd6d92 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -402,7 +402,8 @@ const struct address_space_operations f2fs_meta_aops = { #endif }; -static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) +static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type) { struct inode_management *im = &sbi->im[type]; struct ino_entry *e, *tmp; @@ -427,6 +428,10 @@ retry: if (type != ORPHAN_INO) im->ino_num++; } + + if (type == FLUSH_INO) + f2fs_set_bit(devidx, (char *)&e->dirty_device); + spin_unlock(&im->ino_lock); radix_tree_preload_end(); @@ -455,7 +460,7 @@ static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) { /* add new dirty ino entry into list */ - __add_ino_entry(sbi, ino, type); + __add_ino_entry(sbi, ino, 0, type); } void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) @@ -481,7 +486,7 @@ void release_ino_entry(struct f2fs_sb_info *sbi, bool all) struct ino_entry *e, *tmp; int i; - for (i = all ? ORPHAN_INO: APPEND_INO; i <= UPDATE_INO; i++) { + for (i = all ? ORPHAN_INO : APPEND_INO; i < MAX_INO_ENTRY; i++) { struct inode_management *im = &sbi->im[i]; spin_lock(&im->ino_lock); @@ -495,6 +500,27 @@ void release_ino_entry(struct f2fs_sb_info *sbi, bool all) } } +void set_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type) +{ + __add_ino_entry(sbi, ino, devidx, type); +} + +bool is_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type) +{ + struct inode_management *im = &sbi->im[type]; + struct ino_entry *e; + bool is_dirty = false; + + spin_lock(&im->ino_lock); + e = radix_tree_lookup(&im->ino_root, ino); + if (e && f2fs_test_bit(devidx, (char *)&e->dirty_device)) + is_dirty = true; + spin_unlock(&im->ino_lock); + return is_dirty; +} + int acquire_orphan_inode(struct f2fs_sb_info *sbi) { struct inode_management *im = &sbi->im[ORPHAN_INO]; @@ -531,7 +557,7 @@ void release_orphan_inode(struct f2fs_sb_info *sbi) void add_orphan_inode(struct inode *inode) { /* add new orphan ino entry into list */ - __add_ino_entry(F2FS_I_SB(inode), inode->i_ino, ORPHAN_INO); + __add_ino_entry(F2FS_I_SB(inode), inode->i_ino, 0, ORPHAN_INO); update_inode_page(inode); } @@ -555,7 +581,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) return err; } - __add_ino_entry(sbi, ino, ORPHAN_INO); + __add_ino_entry(sbi, ino, 0, ORPHAN_INO); inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) { diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ff3a93c8c827..3faca8643eed 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1493,6 +1493,7 @@ static int __write_data_page(struct page *page, bool *submitted, int err = 0; struct f2fs_io_info fio = { .sbi = sbi, + .ino = inode->i_ino, .type = DATA, .op = REQ_OP_WRITE, .op_flags = wbc_to_write_flags(wbc), diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1b591f841e5e..0ff3ce3cf260 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -276,12 +276,14 @@ enum { ORPHAN_INO, /* for orphan ino list */ APPEND_INO, /* for append ino list */ UPDATE_INO, /* for update ino list */ + FLUSH_INO, /* for multiple device flushing */ MAX_INO_ENTRY, /* max. list */ }; struct ino_entry { - struct list_head list; /* list head */ - nid_t ino; /* inode number */ + struct list_head list; /* list head */ + nid_t ino; /* inode number */ + unsigned int dirty_device; /* dirty device bitmap */ }; /* for the list of inodes to be GCed */ @@ -868,6 +870,7 @@ enum { struct flush_cmd { struct completion wait; struct llist_node llnode; + nid_t ino; int ret; }; @@ -995,6 +998,7 @@ enum iostat_type { struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ + nid_t ino; /* inode number */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ enum temp_type temp; /* contains HOT/WARM/COLD */ int op; /* contains REQ_OP_ */ @@ -2635,7 +2639,7 @@ void drop_inmem_page(struct inode *inode, struct page *page); int commit_inmem_pages(struct inode *inode); void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need); void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi); -int f2fs_issue_flush(struct f2fs_sb_info *sbi); +int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino); int create_flush_cmd_control(struct f2fs_sb_info *sbi); void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); @@ -2697,6 +2701,10 @@ void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void release_ino_entry(struct f2fs_sb_info *sbi, bool all); bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode); +void set_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type); +bool is_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type); int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi); int acquire_orphan_inode(struct f2fs_sb_info *sbi); void release_orphan_inode(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 8e96536a852e..2b3dc34fb025 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -298,10 +298,11 @@ sync_nodes: clear_inode_flag(inode, FI_APPEND_WRITE); flush_out: if (!atomic) - ret = f2fs_issue_flush(sbi); + ret = f2fs_issue_flush(sbi, inode->i_ino); if (!ret) { remove_ino_entry(sbi, ino, UPDATE_INO); clear_inode_flag(inode, FI_UPDATE_WRITE); + remove_ino_entry(sbi, ino, FLUSH_INO); } f2fs_update_time(sbi, REQ_TIME); out: diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index f777e073e509..197ebf45e4e4 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -598,6 +598,7 @@ static void move_data_block(struct inode *inode, block_t bidx, { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), + .ino = inode->i_ino, .type = DATA, .temp = COLD, .op = REQ_OP_READ, @@ -728,6 +729,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, } else { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), + .ino = inode->i_ino, .type = DATA, .temp = COLD, .op = REQ_OP_WRITE, diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 8322e4e7bb3f..90e38d8ea688 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -112,6 +112,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(dn->inode), + .ino = dn->inode->i_ino, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC | REQ_PRIO, diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 395ca5dd3d37..00ee967949a9 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -480,6 +480,7 @@ void f2fs_evict_inode(struct inode *inode) remove_ino_entry(sbi, inode->i_ino, APPEND_INO); remove_ino_entry(sbi, inode->i_ino, UPDATE_INO); + remove_ino_entry(sbi, inode->i_ino, FLUSH_INO); sb_start_intwrite(inode->i_sb); set_inode_flag(inode, FI_NO_ALLOC); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 8477417e4073..8ac051ece57b 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -63,7 +63,7 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) } else if (type == INO_ENTRIES) { int i; - for (i = 0; i <= UPDATE_INO; i++) + for (i = 0; i < MAX_INO_ENTRY; i++) mem_size += sbi->im[i].ino_num * sizeof(struct ino_entry); mem_size >>= PAGE_SHIFT; @@ -1346,6 +1346,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, struct node_info ni; struct f2fs_io_info fio = { .sbi = sbi, + .ino = ino_of_node(page), .type = NODE, .op = REQ_OP_WRITE, .op_flags = wbc_to_write_flags(wbc), diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index bb159226bf36..ba85516fc8a1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -313,6 +313,7 @@ static int __commit_inmem_pages(struct inode *inode, struct inmem_pages *cur, *tmp; struct f2fs_io_info fio = { .sbi = sbi, + .ino = inode->i_ino, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC | REQ_PRIO, @@ -485,15 +486,17 @@ static int __submit_flush_wait(struct f2fs_sb_info *sbi, return ret; } -static int submit_flush_wait(struct f2fs_sb_info *sbi) +static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino) { - int ret = __submit_flush_wait(sbi, sbi->sb->s_bdev); + int ret = 0; int i; - if (!sbi->s_ndevs || ret) - return ret; + if (!sbi->s_ndevs) + return __submit_flush_wait(sbi, sbi->sb->s_bdev); - for (i = 1; i < sbi->s_ndevs; i++) { + for (i = 0; i < sbi->s_ndevs; i++) { + if (!is_dirty_device(sbi, ino, i, FLUSH_INO)) + continue; ret = __submit_flush_wait(sbi, FDEV(i).bdev); if (ret) break; @@ -519,7 +522,9 @@ repeat: fcc->dispatch_list = llist_del_all(&fcc->issue_list); fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); - ret = submit_flush_wait(sbi); + cmd = llist_entry(fcc->dispatch_list, struct flush_cmd, llnode); + + ret = submit_flush_wait(sbi, cmd->ino); atomic_inc(&fcc->issued_flush); llist_for_each_entry_safe(cmd, next, @@ -537,7 +542,7 @@ repeat: goto repeat; } -int f2fs_issue_flush(struct f2fs_sb_info *sbi) +int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino) { struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; struct flush_cmd cmd; @@ -547,19 +552,20 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) return 0; if (!test_opt(sbi, FLUSH_MERGE)) { - ret = submit_flush_wait(sbi); + ret = submit_flush_wait(sbi, ino); atomic_inc(&fcc->issued_flush); return ret; } - if (atomic_inc_return(&fcc->issing_flush) == 1) { - ret = submit_flush_wait(sbi); + if (atomic_inc_return(&fcc->issing_flush) == 1 || sbi->s_ndevs > 1) { + ret = submit_flush_wait(sbi, ino); atomic_dec(&fcc->issing_flush); atomic_inc(&fcc->issued_flush); return ret; } + cmd.ino = ino; init_completion(&cmd.wait); llist_add(&cmd.llnode, &fcc->issue_list); @@ -583,7 +589,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) } else { struct flush_cmd *tmp, *next; - ret = submit_flush_wait(sbi); + ret = submit_flush_wait(sbi, ino); llist_for_each_entry_safe(tmp, next, list, llnode) { if (tmp == &cmd) { @@ -2464,6 +2470,20 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, mutex_unlock(&curseg->curseg_mutex); } +static void update_device_state(struct f2fs_io_info *fio) +{ + struct f2fs_sb_info *sbi = fio->sbi; + unsigned int devidx; + + if (!sbi->s_ndevs) + return; + + devidx = f2fs_target_device_index(sbi, fio->new_blkaddr); + + /* update device state for fsync */ + set_dirty_device(sbi, fio->ino, devidx, FLUSH_INO); +} + static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { int type = __get_segment_type(fio); @@ -2478,6 +2498,8 @@ reallocate: if (err == -EAGAIN) { fio->old_blkaddr = fio->new_blkaddr; goto reallocate; + } else if (!err) { + update_device_state(fio); } } @@ -2538,6 +2560,8 @@ int rewrite_data_page(struct f2fs_io_info *fio) stat_inc_inplace_blocks(fio->sbi); err = f2fs_submit_page_bio(fio); + if (!err) + update_device_state(fio); f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); -- GitLab From 9c9d6ab93053dfecc7c1ffb830a1794c56aebf31 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 29 Sep 2017 13:59:39 +0800 Subject: [PATCH 1520/5498] f2fs: fix to flush multiple device in checkpoint If f2fs manages multiple devices, in checkpoint, we need to issue flush in those devices which contain dirty data/node in their cache before we write checkpoint region, otherwise, filesystem metadata could be corrupted if hitting SPO after checkpoint. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 6 ++++++ fs/f2fs/f2fs.h | 3 +++ fs/f2fs/segment.c | 29 +++++++++++++++++++++++++++++ fs/f2fs/super.c | 3 +++ 4 files changed, 41 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 246871bd6d92..d58c9362bdac 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1173,6 +1173,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct super_block *sb = sbi->sb; struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); u64 kbytes_written; + int err; /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { @@ -1266,6 +1267,11 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (unlikely(f2fs_cp_error(sbi))) return -EIO; + /* flush all device cache */ + err = f2fs_flush_device_cache(sbi); + if (err) + return err; + /* write out checkpoint buffer at block 0 */ update_meta_page(sbi, ckpt, start_blk++); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0ff3ce3cf260..2cf43e77568e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1220,6 +1220,8 @@ struct f2fs_sb_info { struct list_head s_list; int s_ndevs; /* number of devices */ struct f2fs_dev_info *devs; /* for device list */ + unsigned int dirty_device; /* for checkpoint data flush */ + spinlock_t dev_lock; /* protect dirty_device */ struct mutex umount_mutex; unsigned int shrinker_run_no; @@ -2641,6 +2643,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need); void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi); int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino); int create_flush_cmd_control(struct f2fs_sb_info *sbi); +int f2fs_flush_device_cache(struct f2fs_sb_info *sbi); void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ba85516fc8a1..fbf513145241 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -659,6 +659,28 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) } } +int f2fs_flush_device_cache(struct f2fs_sb_info *sbi) +{ + int ret = 0, i; + + if (!sbi->s_ndevs) + return 0; + + for (i = 1; i < sbi->s_ndevs; i++) { + if (!f2fs_test_bit(i, (char *)&sbi->dirty_device)) + continue; + ret = __submit_flush_wait(sbi, FDEV(i).bdev); + if (ret) + break; + + spin_lock(&sbi->dev_lock); + f2fs_clear_bit(i, (char *)&sbi->dirty_device); + spin_unlock(&sbi->dev_lock); + } + + return ret; +} + static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, enum dirty_type dirty_type) { @@ -2482,6 +2504,13 @@ static void update_device_state(struct f2fs_io_info *fio) /* update device state for fsync */ set_dirty_device(sbi, fio->ino, devidx, FLUSH_INO); + + /* update device state for checkpoint */ + if (!f2fs_test_bit(devidx, (char *)&sbi->dirty_device)) { + spin_lock(&sbi->dev_lock); + f2fs_set_bit(devidx, (char *)&sbi->dirty_device); + spin_unlock(&sbi->dev_lock); + } } static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9472358cc9e9..539156af1054 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1976,6 +1976,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi) for (j = HOT; j < NR_TEMP_TYPE; j++) mutex_init(&sbi->wio_mutex[i][j]); spin_lock_init(&sbi->cp_lock); + + sbi->dirty_device = 0; + spin_lock_init(&sbi->dev_lock); } static int init_percpu_info(struct f2fs_sb_info *sbi) -- GitLab From 0f5a13682d2695513abaa820ba8c648934e6a4aa Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 Oct 2017 09:08:32 +0800 Subject: [PATCH 1521/5498] f2fs: support issuing/waiting discard in range Fstrim intends to trim invalid blocks of filesystem only with specified range and granularity, but actually, it will issue all previous cached discard commands which may be out-of-range and be with unmatched granularity, it's unneeded. In order to fix above issues, this patch introduces new helps to support to issue and wait discard in range and adds a new fstrim_list for tracking in-flight discard from ->fstrim. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +- fs/f2fs/segment.c | 127 ++++++++++++++++++++++++++++++++++++++-------- fs/f2fs/super.c | 2 +- 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2cf43e77568e..a889b4e73729 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -348,6 +348,7 @@ struct discard_cmd_control { struct list_head pend_list[MAX_PLIST_NUM];/* store pending entries */ unsigned char pend_list_tag[MAX_PLIST_NUM];/* tag for pending entries */ struct list_head wait_list; /* store on-flushing entries */ + struct list_head fstrim_list; /* in-flight discard from fstrim */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ unsigned int discard_wake; /* to wake up discard thread */ struct mutex cmd_lock; @@ -2649,7 +2650,7 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); void stop_discard_thread(struct f2fs_sb_info *sbi); -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount); +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void release_discard_addrs(struct f2fs_sb_info *sbi); int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fbf513145241..533d646a9439 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -954,9 +954,11 @@ void __check_sit_bitmap(struct f2fs_sb_info *sbi, /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, - struct discard_cmd *dc) + struct discard_cmd *dc, bool fstrim) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *wait_list = fstrim ? &(dcc->fstrim_list) : + &(dcc->wait_list); struct bio *bio = NULL; if (dc->state != D_PREP) @@ -977,7 +979,7 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; submit_bio(REQ_SYNC, bio); - list_move_tail(&dc->list, &dcc->wait_list); + list_move_tail(&dc->list, wait_list); __check_sit_bitmap(sbi, dc->start, dc->start + dc->len); f2fs_update_iostat(sbi, FS_DISCARD, 1); @@ -1162,6 +1164,68 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, return 0; } +static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi, + unsigned int start, unsigned int end, + unsigned int granularity) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct discard_cmd *prev_dc = NULL, *next_dc = NULL; + struct rb_node **insert_p = NULL, *insert_parent = NULL; + struct discard_cmd *dc; + struct blk_plug plug; + int issued; + +next: + issued = 0; + + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); + + dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root, + NULL, start, + (struct rb_entry **)&prev_dc, + (struct rb_entry **)&next_dc, + &insert_p, &insert_parent, true); + if (!dc) + dc = next_dc; + + blk_start_plug(&plug); + + while (dc && dc->lstart <= end) { + struct rb_node *node; + + if (dc->len < granularity) + goto skip; + + if (dc->state != D_PREP) { + list_move_tail(&dc->list, &dcc->fstrim_list); + goto skip; + } + + __submit_discard_cmd(sbi, dc, true); + + if (++issued >= DISCARD_ISSUE_RATE) { + start = dc->lstart + dc->len; + + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); + + schedule(); + + goto next; + } +skip: + node = rb_next(&dc->rb_node); + dc = rb_entry_safe(node, struct discard_cmd, rb_node); + + if (fatal_signal_pending(current)) + break; + } + + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); +} + static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; @@ -1184,22 +1248,19 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) /* Hurry up to finish fstrim */ if (dcc->pend_list_tag[i] & P_TRIM) { - __submit_discard_cmd(sbi, dc); + __submit_discard_cmd(sbi, dc, false); issued++; - - if (fatal_signal_pending(current)) - break; continue; } if (!issue_cond) { - __submit_discard_cmd(sbi, dc); + __submit_discard_cmd(sbi, dc, false); issued++; continue; } if (is_idle(sbi)) { - __submit_discard_cmd(sbi, dc); + __submit_discard_cmd(sbi, dc, false); issued++; } else { io_interrupted = true; @@ -1253,10 +1314,14 @@ static void __wait_one_discard_bio(struct f2fs_sb_info *sbi, mutex_unlock(&dcc->cmd_lock); } -static void __wait_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) +static void __wait_discard_cmd_range(struct f2fs_sb_info *sbi, bool wait_cond, + block_t start, block_t end, + unsigned int granularity, + bool fstrim) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *wait_list = &(dcc->wait_list); + struct list_head *wait_list = fstrim ? &(dcc->fstrim_list) : + &(dcc->wait_list); struct discard_cmd *dc, *tmp; bool need_wait; @@ -1265,6 +1330,10 @@ next: mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, wait_list, list) { + if (dc->lstart + dc->len <= start || end <= dc->lstart) + continue; + if (dc->len < granularity) + continue; if (!wait_cond || (dc->state == D_DONE && !dc->ref)) { wait_for_completion_io(&dc->wait); __remove_discard_cmd(sbi, dc); @@ -1282,6 +1351,11 @@ next: } } +static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) +{ + __wait_discard_cmd_range(sbi, wait_cond, 0, UINT_MAX, 1, false); +} + /* This should be covered by global mutex, &sit_i->sentry_lock */ void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { @@ -1317,12 +1391,12 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) } } -/* This comes from f2fs_put_super and f2fs_trim_fs */ -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount) +/* This comes from f2fs_put_super */ +void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { __issue_discard_cmd(sbi, false); __drop_discard_cmd(sbi); - __wait_discard_cmd(sbi, !umount); + __wait_all_discard_cmd(sbi, false); } static void mark_discard_range_all(struct f2fs_sb_info *sbi) @@ -1366,7 +1440,7 @@ static int issue_discard_thread(void *data) issued = __issue_discard_cmd(sbi, true); if (issued) { - __wait_discard_cmd(sbi, true); + __wait_all_discard_cmd(sbi, true); wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; } else { wait_ms = DEF_MAX_DISCARD_ISSUE_TIME; @@ -1677,6 +1751,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) dcc->pend_list_tag[i] |= P_ACTIVE; } INIT_LIST_HEAD(&dcc->wait_list); + INIT_LIST_HEAD(&dcc->fstrim_list); mutex_init(&dcc->cmd_lock); atomic_set(&dcc->issued_discard, 0); atomic_set(&dcc->issing_discard, 0); @@ -2304,7 +2379,8 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) { __u64 start = F2FS_BYTES_TO_BLK(range->start); __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; - unsigned int start_segno, end_segno; + unsigned int start_segno, end_segno, cur_segno; + block_t start_block, end_block; struct cp_control cpc; int err = 0; @@ -2325,12 +2401,17 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start); end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : GET_SEGNO(sbi, end); + + start_block = START_BLOCK(sbi, start_segno); + end_block = START_BLOCK(sbi, end_segno + 1); + cpc.reason = CP_DISCARD; cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); /* do checkpoint to issue discard commands safely */ - for (; start_segno <= end_segno; start_segno = cpc.trim_end + 1) { - cpc.trim_start = start_segno; + for (cur_segno = start_segno; cur_segno <= end_segno; + cur_segno = cpc.trim_end + 1) { + cpc.trim_start = cur_segno; if (sbi->discard_blks == 0) break; @@ -2338,7 +2419,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) cpc.trim_end = end_segno; else cpc.trim_end = min_t(unsigned int, - rounddown(start_segno + + rounddown(cur_segno + BATCHED_TRIM_SEGMENTS(sbi), sbi->segs_per_sec) - 1, end_segno); @@ -2350,9 +2431,13 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) schedule(); } - /* It's time to issue all the filed discards */ - mark_discard_range_all(sbi); - f2fs_wait_discard_bios(sbi, false); + + start_block = START_BLOCK(sbi, start_segno); + end_block = START_BLOCK(sbi, min(cur_segno, end_segno) + 1); + + __issue_discard_cmd_range(sbi, start_block, end_block, cpc.trim_minlen); + __wait_discard_cmd_range(sbi, true, start_block, end_block, + cpc.trim_minlen, true); out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return err; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 539156af1054..81d99ae3b0d8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -816,7 +816,7 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - f2fs_wait_discard_bios(sbi, true); + f2fs_wait_discard_bios(sbi); if (f2fs_discard_en(sbi) && !sbi->discard_blks) { struct cp_control cpc = { -- GitLab From 4b92a1f449d6d9ba778dc2ae4f271390ef2f5040 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 Oct 2017 09:08:33 +0800 Subject: [PATCH 1522/5498] f2fs: wrap discard policy This patch wraps scattered optional parameters into discard policy as below, later, with it we expect that we can adjust these parameters with proper strategy in different scenario. struct discard_policy { unsigned int min_interval; /* used for candidates exist */ unsigned int max_interval; /* used for candidates not exist */ unsigned int max_requests; /* # of discards issued per round */ unsigned int io_aware_gran; /* minimum granularity discard not be aware of I/O */ bool io_aware; /* issue discard in idle time */ bool sync; /* submit discard with REQ_SYNC flag */ }; This patch doesn't change any logic of codes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 12 +++++++++++- fs/f2fs/segment.c | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a889b4e73729..29e318075844 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -246,7 +246,7 @@ enum { #define BATCHED_TRIM_BLOCKS(sbi) \ (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) #define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi) -#define DISCARD_ISSUE_RATE 8 +#define DEF_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */ #define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000 /* 60 s, if no candidates */ #define DEF_CP_INTERVAL 60 /* 60 secs */ @@ -342,6 +342,15 @@ struct discard_cmd { int error; /* bio error */ }; +struct discard_policy { + unsigned int min_interval; /* used for candidates exist */ + unsigned int max_interval; /* used for candidates not exist */ + unsigned int max_requests; /* # of discards issued per round */ + unsigned int io_aware_gran; /* minimum granularity discard not be aware of I/O */ + bool io_aware; /* issue discard in idle time */ + bool sync; /* submit discard with REQ_SYNC flag */ +}; + struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head entry_list; /* 4KB discard entry list */ @@ -360,6 +369,7 @@ struct discard_cmd_control { atomic_t issing_discard; /* # of issing discard */ atomic_t discard_cmd_cnt; /* # of cached cmd count */ struct rb_root root; /* root of discard rb-tree */ + struct discard_policy dpolicy; /* current discard policy */ }; /* for the list of fsync inodes, used only during recovery */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 533d646a9439..3653695fe1c0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -960,6 +960,7 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, struct list_head *wait_list = fstrim ? &(dcc->fstrim_list) : &(dcc->wait_list); struct bio *bio = NULL; + int flag = dcc->dpolicy.sync ? REQ_SYNC : 0; if (dc->state != D_PREP) return; @@ -978,7 +979,7 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, if (bio) { bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; - submit_bio(REQ_SYNC, bio); + submit_bio(flag, bio); list_move_tail(&dc->list, wait_list); __check_sit_bitmap(sbi, dc->start, dc->start + dc->len); @@ -1172,6 +1173,7 @@ static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi, struct discard_cmd *prev_dc = NULL, *next_dc = NULL; struct rb_node **insert_p = NULL, *insert_parent = NULL; struct discard_cmd *dc; + struct discard_policy *dpolicy = &dcc->dpolicy; struct blk_plug plug; int issued; @@ -1204,7 +1206,7 @@ next: __submit_discard_cmd(sbi, dc, true); - if (++issued >= DISCARD_ISSUE_RATE) { + if (++issued >= dpolicy->max_requests) { start = dc->lstart + dc->len; blk_finish_plug(&plug); @@ -1232,6 +1234,7 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) struct list_head *pend_list; struct discard_cmd *dc, *tmp; struct blk_plug plug; + struct discard_policy *dpolicy = &dcc->dpolicy; int iter = 0, issued = 0; int i; bool io_interrupted = false; @@ -1259,14 +1262,16 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) continue; } - if (is_idle(sbi)) { - __submit_discard_cmd(sbi, dc, false); - issued++; - } else { + if (dpolicy->io_aware && i < dpolicy->io_aware_gran && + !is_idle(sbi)) { io_interrupted = true; + goto skip; } - if (++iter >= DISCARD_ISSUE_RATE) + __submit_discard_cmd(sbi, dc, false); + issued++; +skip: + if (++iter >= dpolicy->max_requests) goto out; } if (list_empty(pend_list) && dcc->pend_list_tag[i] & P_TRIM) @@ -1415,6 +1420,7 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; + struct discard_policy *dpolicy = &dcc->dpolicy; unsigned int wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; int issued; @@ -1441,9 +1447,9 @@ static int issue_discard_thread(void *data) issued = __issue_discard_cmd(sbi, true); if (issued) { __wait_all_discard_cmd(sbi, true); - wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; + wait_ms = dpolicy->min_interval; } else { - wait_ms = DEF_MAX_DISCARD_ISSUE_TIME; + wait_ms = dpolicy->max_interval; } sb_end_intwrite(sbi->sb); @@ -1728,6 +1734,18 @@ skip: wake_up_discard_thread(sbi, false); } +static void inline init_discard_policy(struct discard_cmd_control *dcc) +{ + struct discard_policy *dpolicy = &dcc->dpolicy; + + dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; + dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = true; + dpolicy->sync = true; +} + static int create_discard_cmd_control(struct f2fs_sb_info *sbi) { dev_t dev = sbi->sb->s_bdev->bd_dev; @@ -1761,6 +1779,8 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) dcc->undiscard_blks = 0; dcc->root = RB_ROOT; + init_discard_policy(dcc); + init_waitqueue_head(&dcc->discard_wait_queue); SM_I(sbi)->dcc_info = dcc; init_thread: -- GitLab From d156194cf9cef9f090a2192737c50e2af6cf4860 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 Oct 2017 09:08:34 +0800 Subject: [PATCH 1523/5498] f2fs: split discard policy There are many different scenarios such as fstrim, umount, urgent or background where we will issue discards, actually, they need use different policy in aspect of io aware, discard granularity, delay interval and so on. But now they just share one common discard policy, so there will be race when changing policy in between these scenarios, the interference of changing discard policy will be very serious. This patch changes to split discard policy for different scenarios. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 17 ++++-- fs/f2fs/segment.c | 149 +++++++++++++++++++++++----------------------- fs/f2fs/segment.h | 5 +- fs/f2fs/sysfs.c | 13 ---- 4 files changed, 88 insertions(+), 96 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 29e318075844..513fa9b16937 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -307,10 +307,6 @@ struct discard_entry { #define plist_idx(blk_num) ((blk_num) >= MAX_PLIST_NUM ? \ (MAX_PLIST_NUM - 1) : (blk_num - 1)) -#define P_ACTIVE 0x01 -#define P_TRIM 0x02 -#define plist_issue(tag) (((tag) & P_ACTIVE) || ((tag) & P_TRIM)) - enum { D_PREP, D_SUBMIT, @@ -342,13 +338,23 @@ struct discard_cmd { int error; /* bio error */ }; +enum { + DPOLICY_BG, + DPOLICY_FORCE, + DPOLICY_FSTRIM, + DPOLICY_UMOUNT, + MAX_DPOLICY, +}; + struct discard_policy { + int type; /* type of discard */ unsigned int min_interval; /* used for candidates exist */ unsigned int max_interval; /* used for candidates not exist */ unsigned int max_requests; /* # of discards issued per round */ unsigned int io_aware_gran; /* minimum granularity discard not be aware of I/O */ bool io_aware; /* issue discard in idle time */ bool sync; /* submit discard with REQ_SYNC flag */ + unsigned int granularity; /* discard granularity */ }; struct discard_cmd_control { @@ -369,7 +375,6 @@ struct discard_cmd_control { atomic_t issing_discard; /* # of issing discard */ atomic_t discard_cmd_cnt; /* # of cached cmd count */ struct rb_root root; /* root of discard rb-tree */ - struct discard_policy dpolicy; /* current discard policy */ }; /* for the list of fsync inodes, used only during recovery */ @@ -2658,6 +2663,8 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi); void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); +void init_discard_policy(struct discard_policy *dpolicy, int discard_type, + unsigned int granularity); void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); void stop_discard_thread(struct f2fs_sb_info *sbi); void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3653695fe1c0..43975ab0b9f4 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -954,13 +954,14 @@ void __check_sit_bitmap(struct f2fs_sb_info *sbi, /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, - struct discard_cmd *dc, bool fstrim) + struct discard_policy *dpolicy, + struct discard_cmd *dc) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *wait_list = fstrim ? &(dcc->fstrim_list) : - &(dcc->wait_list); + struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? + &(dcc->fstrim_list) : &(dcc->wait_list); struct bio *bio = NULL; - int flag = dcc->dpolicy.sync ? REQ_SYNC : 0; + int flag = dpolicy->sync ? REQ_SYNC : 0; if (dc->state != D_PREP) return; @@ -1166,14 +1167,13 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, } static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi, - unsigned int start, unsigned int end, - unsigned int granularity) + struct discard_policy *dpolicy, + unsigned int start, unsigned int end) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_cmd *prev_dc = NULL, *next_dc = NULL; struct rb_node **insert_p = NULL, *insert_parent = NULL; struct discard_cmd *dc; - struct discard_policy *dpolicy = &dcc->dpolicy; struct blk_plug plug; int issued; @@ -1196,7 +1196,7 @@ next: while (dc && dc->lstart <= end) { struct rb_node *node; - if (dc->len < granularity) + if (dc->len < dpolicy->granularity) goto skip; if (dc->state != D_PREP) { @@ -1204,7 +1204,7 @@ next: goto skip; } - __submit_discard_cmd(sbi, dc, true); + __submit_discard_cmd(sbi, dpolicy, dc); if (++issued >= dpolicy->max_requests) { start = dc->lstart + dc->len; @@ -1228,54 +1228,39 @@ skip: mutex_unlock(&dcc->cmd_lock); } -static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) +static int __issue_discard_cmd(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *pend_list; struct discard_cmd *dc, *tmp; struct blk_plug plug; - struct discard_policy *dpolicy = &dcc->dpolicy; - int iter = 0, issued = 0; - int i; + int i, iter = 0, issued = 0; bool io_interrupted = false; mutex_lock(&dcc->cmd_lock); f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); blk_start_plug(&plug); - for (i = MAX_PLIST_NUM - 1; - i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) { + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + if (i + 1 < dpolicy->granularity) + break; pend_list = &dcc->pend_list[i]; list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); - /* Hurry up to finish fstrim */ - if (dcc->pend_list_tag[i] & P_TRIM) { - __submit_discard_cmd(sbi, dc, false); - issued++; - continue; - } - - if (!issue_cond) { - __submit_discard_cmd(sbi, dc, false); - issued++; - continue; - } - if (dpolicy->io_aware && i < dpolicy->io_aware_gran && !is_idle(sbi)) { io_interrupted = true; goto skip; } - __submit_discard_cmd(sbi, dc, false); + __submit_discard_cmd(sbi, dpolicy, dc); issued++; skip: if (++iter >= dpolicy->max_requests) goto out; } - if (list_empty(pend_list) && dcc->pend_list_tag[i] & P_TRIM) - dcc->pend_list_tag[i] &= (~P_TRIM); } out: blk_finish_plug(&plug); @@ -1319,14 +1304,13 @@ static void __wait_one_discard_bio(struct f2fs_sb_info *sbi, mutex_unlock(&dcc->cmd_lock); } -static void __wait_discard_cmd_range(struct f2fs_sb_info *sbi, bool wait_cond, - block_t start, block_t end, - unsigned int granularity, - bool fstrim) +static void __wait_discard_cmd_range(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy, + block_t start, block_t end) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *wait_list = fstrim ? &(dcc->fstrim_list) : - &(dcc->wait_list); + struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? + &(dcc->fstrim_list) : &(dcc->wait_list); struct discard_cmd *dc, *tmp; bool need_wait; @@ -1337,9 +1321,9 @@ next: list_for_each_entry_safe(dc, tmp, wait_list, list) { if (dc->lstart + dc->len <= start || end <= dc->lstart) continue; - if (dc->len < granularity) + if (dc->len < dpolicy->granularity) continue; - if (!wait_cond || (dc->state == D_DONE && !dc->ref)) { + if (dc->state == D_DONE && !dc->ref) { wait_for_completion_io(&dc->wait); __remove_discard_cmd(sbi, dc); } else { @@ -1356,9 +1340,10 @@ next: } } -static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) +static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy) { - __wait_discard_cmd_range(sbi, wait_cond, 0, UINT_MAX, 1, false); + __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX); } /* This should be covered by global mutex, &sit_i->sentry_lock */ @@ -1398,21 +1383,14 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) /* This comes from f2fs_put_super */ void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) -{ - __issue_discard_cmd(sbi, false); - __drop_discard_cmd(sbi); - __wait_all_discard_cmd(sbi, false); -} - -static void mark_discard_range_all(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - int i; + struct discard_policy dpolicy; - mutex_lock(&dcc->cmd_lock); - for (i = 0; i < MAX_PLIST_NUM; i++) - dcc->pend_list_tag[i] |= P_TRIM; - mutex_unlock(&dcc->cmd_lock); + init_discard_policy(&dpolicy, DPOLICY_UMOUNT, dcc->discard_granularity); + __issue_discard_cmd(sbi, &dpolicy); + __drop_discard_cmd(sbi); + __wait_all_discard_cmd(sbi, &dpolicy); } static int issue_discard_thread(void *data) @@ -1420,13 +1398,16 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; - struct discard_policy *dpolicy = &dcc->dpolicy; + struct discard_policy dpolicy; unsigned int wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; int issued; set_freezable(); do { + init_discard_policy(&dpolicy, DPOLICY_BG, + dcc->discard_granularity); + wait_event_interruptible_timeout(*q, kthread_should_stop() || freezing(current) || dcc->discard_wake, @@ -1439,17 +1420,18 @@ static int issue_discard_thread(void *data) if (dcc->discard_wake) { dcc->discard_wake = 0; if (sbi->gc_thread && sbi->gc_thread->gc_urgent) - mark_discard_range_all(sbi); + init_discard_policy(&dpolicy, + DPOLICY_FORCE, 1); } sb_start_intwrite(sbi->sb); - issued = __issue_discard_cmd(sbi, true); + issued = __issue_discard_cmd(sbi, &dpolicy); if (issued) { - __wait_all_discard_cmd(sbi, true); - wait_ms = dpolicy->min_interval; + __wait_all_discard_cmd(sbi, &dpolicy); + wait_ms = dpolicy.min_interval; } else { - wait_ms = dpolicy->max_interval; + wait_ms = dpolicy.max_interval; } sb_end_intwrite(sbi->sb); @@ -1734,16 +1716,35 @@ skip: wake_up_discard_thread(sbi, false); } -static void inline init_discard_policy(struct discard_cmd_control *dcc) +void init_discard_policy(struct discard_policy *dpolicy, + int discard_type, unsigned int granularity) { - struct discard_policy *dpolicy = &dcc->dpolicy; - - dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; - dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; - dpolicy->io_aware = true; + /* common policy */ + dpolicy->type = discard_type; dpolicy->sync = true; + dpolicy->granularity = granularity; + + if (discard_type == DPOLICY_BG) { + dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; + dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = true; + } else if (discard_type == DPOLICY_FORCE) { + dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; + dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = true; + } else if (discard_type == DPOLICY_FSTRIM) { + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = false; + } else if (discard_type == DPOLICY_UMOUNT) { + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = false; + } } static int create_discard_cmd_control(struct f2fs_sb_info *sbi) @@ -1763,11 +1764,8 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) dcc->discard_granularity = DEFAULT_DISCARD_GRANULARITY; INIT_LIST_HEAD(&dcc->entry_list); - for (i = 0; i < MAX_PLIST_NUM; i++) { + for (i = 0; i < MAX_PLIST_NUM; i++) INIT_LIST_HEAD(&dcc->pend_list[i]); - if (i >= dcc->discard_granularity - 1) - dcc->pend_list_tag[i] |= P_ACTIVE; - } INIT_LIST_HEAD(&dcc->wait_list); INIT_LIST_HEAD(&dcc->fstrim_list); mutex_init(&dcc->cmd_lock); @@ -1779,8 +1777,6 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) dcc->undiscard_blks = 0; dcc->root = RB_ROOT; - init_discard_policy(dcc); - init_waitqueue_head(&dcc->discard_wait_queue); SM_I(sbi)->dcc_info = dcc; init_thread: @@ -2402,6 +2398,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) unsigned int start_segno, end_segno, cur_segno; block_t start_block, end_block; struct cp_control cpc; + struct discard_policy dpolicy; int err = 0; if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) @@ -2455,9 +2452,9 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) start_block = START_BLOCK(sbi, start_segno); end_block = START_BLOCK(sbi, min(cur_segno, end_segno) + 1); - __issue_discard_cmd_range(sbi, start_block, end_block, cpc.trim_minlen); - __wait_discard_cmd_range(sbi, true, start_block, end_block, - cpc.trim_minlen, true); + init_discard_policy(&dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); + __issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block); + __wait_discard_cmd_range(sbi, &dpolicy, start_block, end_block); out: range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); return err; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 593b99cee4d1..749193af4ebc 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -794,8 +794,9 @@ static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force) goto wake_up; mutex_lock(&dcc->cmd_lock); - for (i = MAX_PLIST_NUM - 1; - i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) { + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + if (i + 1 < dcc->discard_granularity) + break; if (!list_empty(&dcc->pend_list[i])) { wakeup = true; break; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 06fda7cc7123..f316b2d72081 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -154,23 +154,10 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, } 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; } -- GitLab From b791197f673aa22fd871701cf884f20041b9caca Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 Oct 2017 09:08:35 +0800 Subject: [PATCH 1524/5498] f2fs: reduce cmd_lock coverage in __issue_discard_cmd __submit_discard_cmd may lead long latency due to exhaustion of I/O request resource in block layer, so issuing all discard under cmd_lock may lead to hangtask, in order to avoid that, let's reduce it's coverage. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 43975ab0b9f4..d308378a3b7f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1238,14 +1238,14 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, int i, iter = 0, issued = 0; bool io_interrupted = false; - mutex_lock(&dcc->cmd_lock); - f2fs_bug_on(sbi, - !__check_rb_tree_consistence(sbi, &dcc->root)); - blk_start_plug(&plug); for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { if (i + 1 < dpolicy->granularity) break; pend_list = &dcc->pend_list[i]; + + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); + blk_start_plug(&plug); list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); @@ -1259,12 +1259,14 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, issued++; skip: if (++iter >= dpolicy->max_requests) - goto out; + break; } + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); + + if (iter >= dpolicy->max_requests) + break; } -out: - blk_finish_plug(&plug); - mutex_unlock(&dcc->cmd_lock); if (!issued && io_interrupted) issued = -1; -- GitLab From 87905eb8b06680d3bee3b2a8f48ead4f47d14527 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 Oct 2017 09:08:36 +0800 Subject: [PATCH 1525/5498] f2fs: trace f2fs_remove_discard This patch adds tracepoint to trace f2fs_remove_discard. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ include/trace/events/f2fs.h | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d308378a3b7f..c53b24db17d0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -822,6 +822,8 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len); + f2fs_bug_on(sbi, dc->ref); if (dc->error == -EOPNOTSUPP) diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 8fa678a2c335..51f296647ee6 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1237,6 +1237,13 @@ DEFINE_EVENT(f2fs_discard, f2fs_issue_discard, TP_ARGS(dev, blkstart, blklen) ); +DEFINE_EVENT(f2fs_discard, f2fs_remove_discard, + + TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen), + + TP_ARGS(dev, blkstart, blklen) +); + TRACE_EVENT(f2fs_issue_reset_zone, TP_PROTO(struct block_device *dev, block_t blkstart), -- GitLab From 549f9f84502f91a5547dc734580df927a09cf498 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 Oct 2017 09:08:37 +0800 Subject: [PATCH 1526/5498] f2fs: give up CP_TRIMMED_FLAG if it drops discards In ->umount, once we drop remained discard entries, we should not set CP_TRIMMED_FLAG with another checkpoint. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/segment.c | 13 ++++++++++--- fs/f2fs/super.c | 5 +++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 513fa9b16937..f34f0c728878 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2667,7 +2667,7 @@ void init_discard_policy(struct discard_policy *dpolicy, int discard_type, unsigned int granularity); void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); void stop_discard_thread(struct f2fs_sb_info *sbi); -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); +bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void release_discard_addrs(struct f2fs_sb_info *sbi); int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c53b24db17d0..5560d4d6f431 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1276,12 +1276,13 @@ skip: return issued; } -static void __drop_discard_cmd(struct f2fs_sb_info *sbi) +static bool __drop_discard_cmd(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *pend_list; struct discard_cmd *dc, *tmp; int i; + bool dropped = false; mutex_lock(&dcc->cmd_lock); for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { @@ -1289,9 +1290,12 @@ static void __drop_discard_cmd(struct f2fs_sb_info *sbi) list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); __remove_discard_cmd(sbi, dc); + dropped = true; } } mutex_unlock(&dcc->cmd_lock); + + return dropped; } static void __wait_one_discard_bio(struct f2fs_sb_info *sbi, @@ -1386,15 +1390,18 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) } /* This comes from f2fs_put_super */ -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) +bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_policy dpolicy; + bool dropped; init_discard_policy(&dpolicy, DPOLICY_UMOUNT, dcc->discard_granularity); __issue_discard_cmd(sbi, &dpolicy); - __drop_discard_cmd(sbi); + dropped = __drop_discard_cmd(sbi); __wait_all_discard_cmd(sbi, &dpolicy); + + return dropped; } static int issue_discard_thread(void *data) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 81d99ae3b0d8..9434775683cb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -796,6 +796,7 @@ static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); int i; + bool dropped; f2fs_quota_off_umount(sb); @@ -816,9 +817,9 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - f2fs_wait_discard_bios(sbi); + dropped = f2fs_wait_discard_bios(sbi); - if (f2fs_discard_en(sbi) && !sbi->discard_blks) { + if (f2fs_discard_en(sbi) && !sbi->discard_blks && !dropped) { struct cp_control cpc = { .reason = CP_UMOUNT | CP_TRIMMED, }; -- GitLab From cc71a4a9aae067acf3e08a122b9c4fa630e7bb10 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Sat, 7 Oct 2017 16:02:21 +0200 Subject: [PATCH 1527/5498] f2fs: Fix bool initialization/comparison Bool initializations should use true and false. Bool tests don't need comparisons. Signed-off-by: Thomas Meyer Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3faca8643eed..6c536ab29433 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -417,8 +417,8 @@ next: bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; - /* set submitted = 1 as a return value */ - fio->submitted = 1; + /* set submitted = true as a return value */ + fio->submitted = true; inc_page_count(sbi, WB_DATA_TYPE(bio_page)); -- GitLab From 93bb7f1d7c616c493b33fa9bba700916a39fd145 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 9 Oct 2017 17:55:19 +0800 Subject: [PATCH 1528/5498] f2fs: fix to avoid race when accessing last_disk_size last_disk_size could be wrong due to concurrently updating, so using i_sem semaphore to make last_disk_size updating exclusive to fix this issue. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 +++ fs/f2fs/f2fs.h | 10 ++++++++-- fs/f2fs/file.c | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6c536ab29433..5098b9d69a5c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1565,8 +1565,11 @@ write: err = do_write_data_page(&fio); } } + + down_write(&F2FS_I(inode)->i_sem); if (F2FS_I(inode)->last_disk_size < psize) F2FS_I(inode)->last_disk_size = psize; + up_write(&F2FS_I(inode)->i_sem); done: if (err && err != -ENOENT) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f34f0c728878..fab4fc6d0542 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2378,9 +2378,10 @@ static inline void clear_file(struct inode *inode, int type) static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) { + bool ret; + if (dsync) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - bool ret; spin_lock(&sbi->inode_lock[DIRTY_META]); ret = list_empty(&F2FS_I(inode)->gdirty_list); @@ -2391,7 +2392,12 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) file_keep_isize(inode) || i_size_read(inode) & PAGE_MASK) return false; - return F2FS_I(inode)->last_disk_size == i_size_read(inode); + + down_read(&F2FS_I(inode)->i_sem); + ret = F2FS_I(inode)->last_disk_size == i_size_read(inode); + up_read(&F2FS_I(inode)->i_sem); + + return ret; } static inline int f2fs_readonly(struct super_block *sb) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2b3dc34fb025..7cfe4ecc5586 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -756,6 +756,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) inode->i_mtime = inode->i_ctime = current_time(inode); } + down_write(&F2FS_I(inode)->i_sem); + F2FS_I(inode)->last_disk_size = i_size_read(inode); + up_write(&F2FS_I(inode)->i_sem); + size_changed = true; } -- GitLab From 68a76f162cfd6a27c506a232a6144c04b8e42f72 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 7 Oct 2017 00:08:05 -0700 Subject: [PATCH 1529/5498] f2fs/crypto: drop crypto key at evict_inode only This patch avoids dropping crypto key in f2fs_drop_inode, so we can guarantee it happens only at evict_inode. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9434775683cb..2e4d914c209e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -688,7 +688,6 @@ static int f2fs_drop_inode(struct inode *inode) sb_end_intwrite(inode->i_sb); - fscrypt_put_encryption_info(inode, NULL); spin_lock(&inode->i_lock); atomic_dec(&inode->i_count); } -- GitLab From b9da516ce0f172754e4c0c5cb77dbe181b1c91ab Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 12 Oct 2017 19:12:53 -0700 Subject: [PATCH 1530/5498] f2fs: avoid stale fi->gdirty_list pointer When doing fault injection test, f2fs_evict_inode() didn't remove gdirty_list which incurs a kernel panic due to wrong pointer access. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 00ee967949a9..0c56c1fcd3ed 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -520,8 +520,10 @@ no_delete: stat_dec_inline_dir(inode); stat_dec_inline_inode(inode); - if (!is_set_ckpt_flags(sbi, CP_ERROR_FLAG)) + if (likely(!is_set_ckpt_flags(sbi, CP_ERROR_FLAG))) f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE)); + else + f2fs_inode_synced(inode); /* ino == 0, if f2fs_new_inode() was failed t*/ if (inode->i_ino) -- GitLab From 1aead7051ab8111593e9ba17a0b30ad422c5a798 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 13 Oct 2017 10:27:45 -0700 Subject: [PATCH 1531/5498] f2fs: expose some sectors to user in inline data or dentry case If there's some data written through inline data or dentry, we need to shouw st_blocks. This fixes reporting zero blocks even though there is small written data. Cc: stable@vger.kernel.org Reviewed-by: Chao Yu [Jaegeuk Kim: avoid link file for quotacheck] Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 7cfe4ecc5586..69bbbee6934f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -668,6 +668,12 @@ int f2fs_getattr(struct vfsmount *mnt, { struct inode *inode = d_inode(dentry); generic_fillattr(inode, stat); + + /* we need to show initial sectors used for inline_data/dentries */ + if ((S_ISREG(inode->i_mode) && f2fs_has_inline_data(inode)) || + f2fs_has_inline_dentry(inode)) + stat->blocks += (stat->size + 511) >> 9; + return 0; } -- GitLab From 0c33cb268bc8254d50c2b9eddab0812fb3bc4923 Mon Sep 17 00:00:00 2001 From: Weichao Guo Date: Sat, 14 Oct 2017 08:13:32 +0800 Subject: [PATCH 1532/5498] f2fs: skip searching non-exist range in truncate_hole Let's skip entire non-exist area to speed up truncate_hole by using get_next_page_offset. Signed-off-by: Weichao Guo Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 69bbbee6934f..8edc188a1b22 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -839,7 +839,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) err = get_dnode_of_data(&dn, pg_start, LOOKUP_NODE); if (err) { if (err == -ENOENT) { - pg_start++; + pg_start = get_next_page_offset(&dn, pg_start); continue; } return err; -- GitLab From 6ff204b6b1fdb8c5df0aaac97aeec51e1106fba6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 17 Oct 2017 17:33:41 +0800 Subject: [PATCH 1533/5498] f2fs: trace f2fs_lookup This patch adds trace for f2fs_lookup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 49 +++++++++++++++++++++----------- include/trace/events/f2fs.h | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index c8b28244dd0d..86afb89c080b 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -323,12 +323,15 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode = NULL; struct f2fs_dir_entry *de; struct page *page; - nid_t ino; + struct dentry *new; + nid_t ino = -1; int err = 0; unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir)); + trace_f2fs_lookup_start(dir, dentry, flags); + if (f2fs_encrypted_inode(dir)) { - int res = fscrypt_get_encryption_info(dir); + err = fscrypt_get_encryption_info(dir); /* * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is @@ -338,18 +341,22 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (fscrypt_has_encryption_key(dir)) fscrypt_set_encrypted_dentry(dentry); fscrypt_set_d_op(dentry); - if (res && res != -ENOKEY) - return ERR_PTR(res); + if (err && err != -ENOKEY) + goto out; } - if (dentry->d_name.len > F2FS_NAME_LEN) - return ERR_PTR(-ENAMETOOLONG); + if (dentry->d_name.len > F2FS_NAME_LEN) { + err = -ENAMETOOLONG; + goto out; + } de = f2fs_find_entry(dir, &dentry->d_name, &page); if (!de) { - if (IS_ERR(page)) - return (struct dentry *)page; - return d_splice_alias(inode, dentry); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + goto out_splice; } ino = le32_to_cpu(de->ino); @@ -357,19 +364,21 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, f2fs_put_page(page, 0); inode = f2fs_iget(dir->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) { err = __recover_dot_dentries(dir, root_ino); if (err) - goto err_out; + goto out_iput; } if (f2fs_has_inline_dots(inode)) { err = __recover_dot_dentries(inode, dir->i_ino); if (err) - goto err_out; + goto out_iput; } if (f2fs_encrypted_inode(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && @@ -378,12 +387,18 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, "Inconsistent encryption contexts: %lu/%lu", dir->i_ino, inode->i_ino); err = -EPERM; - goto err_out; + goto out_iput; } - return d_splice_alias(inode, dentry); - -err_out: +out_splice: + new = d_splice_alias(inode, dentry); + if (IS_ERR(new)) + err = PTR_ERR(new); + trace_f2fs_lookup_end(dir, dentry, ino, err); + return new; +out_iput: iput(inode); +out: + trace_f2fs_lookup_end(dir, dentry, ino, err); return ERR_PTR(err); } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 51f296647ee6..5cfe15d66dca 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -679,6 +679,62 @@ TRACE_EVENT(f2fs_get_victim, __entry->free) ); +TRACE_EVENT(f2fs_lookup_start, + + TP_PROTO(struct inode *dir, struct dentry *dentry, unsigned int flags), + + TP_ARGS(dir, dentry, flags), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(const char *, name) + __field(unsigned int, flags) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->name = dentry->d_name.name; + __entry->flags = flags; + ), + + TP_printk("dev = (%d,%d), pino = %lu, name:%s, flags:%u", + show_dev_ino(__entry), + __entry->name, + __entry->flags) +); + +TRACE_EVENT(f2fs_lookup_end, + + TP_PROTO(struct inode *dir, struct dentry *dentry, nid_t ino, + int err), + + TP_ARGS(dir, dentry, ino, err), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(const char *, name) + __field(nid_t, cino) + __field(int, err) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->name = dentry->d_name.name; + __entry->cino = ino; + __entry->err = err; + ), + + TP_printk("dev = (%d,%d), pino = %lu, name:%s, ino:%u, err:%d", + show_dev_ino(__entry), + __entry->name, + __entry->cino, + __entry->err) +); + TRACE_EVENT(f2fs_fallocate, TP_PROTO(struct inode *inode, int mode, -- GitLab From 710c0b07dfb692f052784f8e2f29eb140aa9a060 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 13 Oct 2017 18:01:33 +0800 Subject: [PATCH 1534/5498] f2fs: trace f2fs_readdir This patch adds trace for f2fs_readdir. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 14 +++++++++----- include/trace/events/f2fs.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 4f2a8fedb313..c745f977869c 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -14,6 +14,7 @@ #include "node.h" #include "acl.h" #include "xattr.h" +#include static unsigned long dir_blocks(struct inode *inode) { @@ -847,6 +848,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) struct f2fs_dentry_block *dentry_blk = NULL; struct page *dentry_page = NULL; struct file_ra_state *ra = &file->f_ra; + loff_t start_pos = ctx->pos; unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); struct f2fs_dentry_ptr d; struct fscrypt_str fstr = FSTR_INIT(NULL, 0); @@ -855,16 +857,16 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) if (f2fs_encrypted_inode(inode)) { err = fscrypt_get_encryption_info(inode); if (err && err != -ENOKEY) - return err; + goto out; err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr); if (err < 0) - return err; + goto out; } if (f2fs_has_inline_dentry(inode)) { err = f2fs_read_inline_dir(file, ctx, &fstr); - goto out; + goto out_free; } /* readahead for multi pages of dir */ @@ -880,7 +882,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) err = 0; continue; } else { - goto out; + goto out_free; } } @@ -900,8 +902,10 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } -out: +out_free: fscrypt_fname_free_buffer(&fstr); +out: + trace_f2fs_readdir(inode, start_pos, ctx->pos, err); return err < 0 ? err : 0; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 5cfe15d66dca..348429f466a6 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -735,6 +735,35 @@ TRACE_EVENT(f2fs_lookup_end, __entry->err) ); +TRACE_EVENT(f2fs_readdir, + + TP_PROTO(struct inode *dir, loff_t start_pos, loff_t end_pos, int err), + + TP_ARGS(dir, start_pos, end_pos, err), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, start) + __field(loff_t, end) + __field(int, err) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->start = start_pos; + __entry->end = end_pos; + __entry->err = err; + ), + + TP_printk("dev = (%d,%d), ino = %lu, start_pos:%llu, end_pos:%llu, err:%d", + show_dev_ino(__entry), + __entry->start, + __entry->end, + __entry->err) +); + TRACE_EVENT(f2fs_fallocate, TP_PROTO(struct inode *inode, int mode, -- GitLab From d88ba788df66151a1732190902ee33b02c6370ae Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 13 Oct 2017 18:01:34 +0800 Subject: [PATCH 1535/5498] f2fs: allow readdir() to be interrupted This patch follows ext4 to allow readdir() in large empty directory to be interrupted. Referenced commit of ext4: 1f60fbe72749 ("ext4: allow readdir()'s of large empty directories to be interrupted"). Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index c745f977869c..95500eaae681 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -10,6 +10,7 @@ */ #include #include +#include #include "f2fs.h" #include "node.h" #include "acl.h" @@ -875,6 +876,14 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES)); for (; n < npages; n++) { + + /* allow readdir() to be interrupted */ + if (fatal_signal_pending(current)) { + err = -ERESTARTSYS; + goto out_free; + } + cond_resched(); + dentry_page = get_lock_data_page(inode, n, false); if (IS_ERR(dentry_page)) { err = PTR_ERR(dentry_page); -- GitLab From f6e29eded5b269fc1397b35ed48d0bc00768beda Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 13 Oct 2017 18:01:35 +0800 Subject: [PATCH 1536/5498] f2fs: relocate readahead codes in readdir() Previously, for large directory, we just do readahead only once in readdir(), readdir()'s performance may drop when traversing latter blocks. In order to avoid this, relocate readahead codes to covering all traverse flow. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 95500eaae681..65c528539b78 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -870,11 +870,6 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) goto out_free; } - /* readahead for multi pages of dir */ - if (npages - n > 1 && !ra_has_index(ra, n)) - page_cache_sync_readahead(inode->i_mapping, ra, file, n, - min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES)); - for (; n < npages; n++) { /* allow readdir() to be interrupted */ @@ -884,6 +879,11 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) } cond_resched(); + /* readahead for multi pages of dir */ + if (npages - n > 1 && !ra_has_index(ra, n)) + page_cache_sync_readahead(inode->i_mapping, ra, file, n, + min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES)); + dentry_page = get_lock_data_page(inode, n, false); if (IS_ERR(dentry_page)) { err = PTR_ERR(dentry_page); -- GitLab From e0ac8d0ab0a2bd3813e061d3f57770748787c45d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 13 Oct 2017 18:01:36 +0800 Subject: [PATCH 1537/5498] f2fs: update ctx->pos correctly when hitting hole in directory This patch fixes to update ctx->pos correctly when hitting hole in directory. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 65c528539b78..1955707b138b 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -870,7 +870,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) goto out_free; } - for (; n < npages; n++) { + for (; n < npages; n++, ctx->pos = n * NR_DENTRY_IN_BLOCK) { /* allow readdir() to be interrupted */ if (fatal_signal_pending(current)) { @@ -907,7 +907,6 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) break; } - ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } -- GitLab From 585398f7f9a609cef999eb0e9dad32a864a5fc9e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 18 Oct 2017 19:05:57 -0700 Subject: [PATCH 1538/5498] f2fs: limit # of inmemory pages If some abnormal users try lots of atomic write operations, f2fs is able to produce pinned pages in the main memory which affects system performance. This patch limits that as 20% over total memory size, and if f2fs reaches to the limit, it will drop all the inmemory pages. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 8 ++++++++ fs/f2fs/f2fs.h | 3 +++ fs/f2fs/node.c | 4 ++++ fs/f2fs/node.h | 1 + fs/f2fs/segment.c | 38 ++++++++++++++++++++++++++++++++++++++ fs/f2fs/super.c | 1 + 6 files changed, 55 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5098b9d69a5c..36617c4831a5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1938,6 +1938,12 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, trace_f2fs_write_begin(inode, pos, len, flags); + if (f2fs_is_atomic_file(inode) && + !available_free_memory(sbi, INMEM_PAGES)) { + err = -ENOMEM; + goto fail; + } + /* * We should check this at this moment to avoid deadlock on inode page * and #0 page. The locking rule for inline_data conversion should be: @@ -2014,6 +2020,8 @@ repeat: fail: f2fs_put_page(page, 1); f2fs_write_failed(mapping, pos + len); + if (f2fs_is_atomic_file(inode)) + drop_inmem_pages_all(sbi); return err; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index fab4fc6d0542..6f35e06ff7e4 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -695,6 +695,7 @@ struct f2fs_inode_info { #endif struct list_head dirty_list; /* dirty list for dirs and files */ struct list_head gdirty_list; /* linked in global dirty list */ + struct list_head inmem_ilist; /* list for inmem inodes */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct task_struct *inmem_task; /* store inmemory task */ struct mutex inmem_lock; /* lock for inmemory pages */ @@ -1059,6 +1060,7 @@ enum inode_type { DIR_INODE, /* for dirty dir inode */ FILE_INODE, /* for dirty regular/symlink inode */ DIRTY_META, /* for all dirtied inode metadata */ + ATOMIC_FILE, /* for all atomic files */ NR_INODE_TYPE, }; @@ -2658,6 +2660,7 @@ void destroy_node_manager_caches(void); */ bool need_SSR(struct f2fs_sb_info *sbi); void register_inmem_page(struct inode *inode, struct page *page); +void drop_inmem_pages_all(struct f2fs_sb_info *sbi); void drop_inmem_pages(struct inode *inode); void drop_inmem_page(struct inode *inode, struct page *page); int commit_inmem_pages(struct inode *inode); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 8ac051ece57b..6db4b36924ca 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -74,6 +74,10 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) atomic_read(&sbi->total_ext_node) * sizeof(struct extent_node)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else if (type == INMEM_PAGES) { + /* it allows 20% / total_ram for inmemory pages */ + mem_size = get_pages(sbi, F2FS_INMEM_PAGES); + res = mem_size < (val.totalram / 5); } else { if (!sbi->sb->s_bdi->dirty_exceeded) return true; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index e91b08b4a51a..0ee3e5ff49a3 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -140,6 +140,7 @@ enum mem_type { DIRTY_DENTS, /* indicates dirty dentry pages */ INO_ENTRIES, /* indicates inode entries */ EXTENT_CACHE, /* indicates extent cache */ + INMEM_PAGES, /* indicates inmemory pages */ BASE_CHECK, /* check kernel status */ }; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5560d4d6f431..fafa4e55a900 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -186,6 +186,7 @@ bool need_SSR(struct f2fs_sb_info *sbi) void register_inmem_page(struct inode *inode, struct page *page) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); struct inmem_pages *new; @@ -204,6 +205,10 @@ void register_inmem_page(struct inode *inode, struct page *page) mutex_lock(&fi->inmem_lock); get_page(page); list_add_tail(&new->list, &fi->inmem_pages); + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (list_empty(&fi->inmem_ilist)) + list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); mutex_unlock(&fi->inmem_lock); @@ -262,12 +267,41 @@ next: return err; } +void drop_inmem_pages_all(struct f2fs_sb_info *sbi) +{ + struct list_head *head = &sbi->inode_list[ATOMIC_FILE]; + struct inode *inode; + struct f2fs_inode_info *fi; +next: + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (list_empty(head)) { + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); + return; + } + fi = list_first_entry(head, struct f2fs_inode_info, inmem_ilist); + inode = igrab(&fi->vfs_inode); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); + + if (inode) { + drop_inmem_pages(inode); + iput(inode); + } + congestion_wait(BLK_RW_ASYNC, HZ/50); + cond_resched(); + goto next; +} + void drop_inmem_pages(struct inode *inode) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); mutex_lock(&fi->inmem_lock); __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (!list_empty(&fi->inmem_ilist)) + list_del_init(&fi->inmem_ilist); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); mutex_unlock(&fi->inmem_lock); clear_inode_flag(inode, FI_ATOMIC_FILE); @@ -399,6 +433,10 @@ int commit_inmem_pages(struct inode *inode) /* drop all uncommitted pages */ __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); } + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (!list_empty(&fi->inmem_ilist)) + list_del_init(&fi->inmem_ilist); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); mutex_unlock(&fi->inmem_lock); clear_inode_flag(inode, FI_ATOMIC_COMMIT); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2e4d914c209e..0f92e1ae7c0e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -641,6 +641,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_rwsem(&fi->i_sem); INIT_LIST_HEAD(&fi->dirty_list); INIT_LIST_HEAD(&fi->gdirty_list); + INIT_LIST_HEAD(&fi->inmem_ilist); INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); init_rwsem(&fi->dio_rwsem[READ]); -- GitLab From fee61cecc49452cf39865502e524acd6206a78b0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 19 Oct 2017 09:43:56 -0700 Subject: [PATCH 1539/5498] f2fs: retry ENOMEM for quota_read|write This gives another chance to read or write quota data. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0f92e1ae7c0e..2718a9385ba2 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1337,8 +1337,13 @@ static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data, tocopy = min_t(unsigned long, sb->s_blocksize - offset, toread); repeat: page = read_mapping_page(mapping, blkidx, NULL); - if (IS_ERR(page)) + if (IS_ERR(page)) { + if (PTR_ERR(page) == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto repeat; + } return PTR_ERR(page); + } lock_page(page); @@ -1381,11 +1386,16 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type, while (towrite > 0) { tocopy = min_t(unsigned long, sb->s_blocksize - offset, towrite); - +retry: err = a_ops->write_begin(NULL, mapping, off, tocopy, 0, &page, NULL); - if (unlikely(err)) + if (unlikely(err)) { + if (err == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry; + } break; + } kaddr = kmap_atomic(page); memcpy(kaddr + offset, data, tocopy); -- GitLab From c68705b831a2975ca21a8891fd7420ff49402b5a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 19 Oct 2017 11:48:57 -0700 Subject: [PATCH 1540/5498] f2fs: remove obsolete pointer for truncate_xattr_node This patch removes obosolete parameter for truncate_xattr_node. Suggested-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 10 ++++------ fs/f2fs/xattr.c | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6f35e06ff7e4..0ee48f98bd98 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2625,7 +2625,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni); pgoff_t get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs); int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode); int truncate_inode_blocks(struct inode *inode, pgoff_t from); -int truncate_xattr_node(struct inode *inode, struct page *page); +int truncate_xattr_node(struct inode *inode); int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino); int remove_inode_page(struct inode *inode); struct page *new_inode_page(struct inode *inode); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 6db4b36924ca..b1df3451d451 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -962,7 +962,8 @@ fail: return err > 0 ? 0 : err; } -int truncate_xattr_node(struct inode *inode, struct page *page) +/* caller must lock inode page */ +int truncate_xattr_node(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t nid = F2FS_I(inode)->i_xattr_nid; @@ -978,10 +979,7 @@ int truncate_xattr_node(struct inode *inode, struct page *page) f2fs_i_xnid_write(inode, 0); - set_new_dnode(&dn, inode, page, npage, nid); - - if (page) - dn.inode_page_locked = true; + set_new_dnode(&dn, inode, NULL, npage, nid); truncate_node(&dn); return 0; } @@ -1000,7 +998,7 @@ int remove_inode_page(struct inode *inode) if (err) return err; - err = truncate_xattr_node(inode, dn.inode_page); + err = truncate_xattr_node(inode); if (err) { f2fs_put_dnode(&dn); return err; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 0c2727f07a45..436038088802 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -470,7 +470,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, /* no need to use xattr node block */ if (hsize <= inline_size) { - err = truncate_xattr_node(inode, ipage); + err = truncate_xattr_node(inode); alloc_nid_failed(sbi, new_nid); return err; } -- GitLab From ec8a71acb6892faa934abda604bd5d774982a747 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 19 Oct 2017 12:07:11 -0700 Subject: [PATCH 1541/5498] Revert "f2fs: return wrong error number on f2fs_quota_write" This reverts commit 4f31d26b0c17f2aae6a6afeb823a87e20671ab4b. It turns out that we need to report error number if nothing was written. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2718a9385ba2..563be26241b1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1412,7 +1412,7 @@ retry: } if (len == towrite) - return 0; + return err; inode->i_version++; inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); -- GitLab From 8be4d9e573041d21f418079709882c886813e1d9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 18 Oct 2017 10:34:14 +0800 Subject: [PATCH 1542/5498] f2fs: fix to correct no_fggc_candidate There may be extreme case as below: For one section contains one segment, and there are total 100 segments with 10% over-privision ratio in f2fs partition, fggc_threshold will be rounded down to 460 instead of 460.8 as below caclulation: sbi->fggc_threshold = div_u64((u64)(main_count - ovp_count) * BLKS_PER_SEC(sbi), (main_count - resv_count)); If section usage is as: 60 segments which contain 460 valid blocks 40 segments which contain 462 valid blocks As valid block number in all sections is large than fggc_threshold, so none of them will be chosen as candidate due to incorrect fggc_threshold. Let's just soften the term of choosing foreground GC candidates. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 749193af4ebc..7a5cc71bbdc5 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -729,7 +729,7 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi, unsigned int secno) { - if (get_valid_blocks(sbi, GET_SEG_FROM_SEC(sbi, secno), true) >= + if (get_valid_blocks(sbi, GET_SEG_FROM_SEC(sbi, secno), true) > sbi->fggc_threshold) return true; return false; -- GitLab From 04e41a9f0d706f8c7f9311912ca402fa885b19fa Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 19 Oct 2017 11:52:47 +0200 Subject: [PATCH 1543/5498] f2fs: avoid using timespec All uses of timespec are deprecated, and this one is not particularly useful, as the documented method for converting seconds to jiffies is to multiply by 'HZ'. Signed-off-by: Arnd Bergmann Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0ee48f98bd98..090b7c14ccd2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1303,8 +1303,7 @@ static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type) static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type) { - struct timespec ts = {sbi->interval_time[type], 0}; - unsigned long interval = timespec_to_jiffies(&ts); + unsigned long interval = sbi->interval_time[type] * HZ; return time_after(jiffies, sbi->last_time[type] + interval); } -- GitLab From d2d066913df62a70cfb64af59917b04aa158414d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 19 Oct 2017 12:58:21 +0200 Subject: [PATCH 1544/5498] f2fs: remove several redundant assignments There are several assignments to variables that are redundant as the values are never read when the variables are updated later and so the redundant statements can be safely removed. Cleans up clang warnings: fs/f2fs/segment.c:923:19: warning: Value stored to 'p' during its initialization is never read fs/f2fs/segment.c:2060:2: warning: Value stored to 'hint' is never read fs/f2fs/segment.c:2353:2: warning: Value stored to 'start_block' is never read fs/f2fs/segment.c:2354:2: warning: Value stored to 'end_block' is never read Signed-off-by: Colin Ian King Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fafa4e55a900..de12b948c788 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1038,7 +1038,7 @@ static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi, struct rb_node *insert_parent) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct rb_node **p = &dcc->root.rb_node; + struct rb_node **p; struct rb_node *parent = NULL; struct discard_cmd *dc = NULL; @@ -2175,7 +2175,6 @@ find_other_zone: } secno = left_start; skip_left: - hint = secno; segno = GET_SEG_FROM_SEC(sbi, secno); zoneno = GET_ZONE_FROM_SEC(sbi, secno); @@ -2468,9 +2467,6 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : GET_SEGNO(sbi, end); - start_block = START_BLOCK(sbi, start_segno); - end_block = START_BLOCK(sbi, end_segno + 1); - cpc.reason = CP_DISCARD; cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); -- GitLab From 02fe062bf9287a39d5b3c37ae8f52214a6838825 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 23 Oct 2017 23:48:49 +0200 Subject: [PATCH 1545/5498] f2fs: stop all the operations by cp_error flag This patch replaces to use cp_error flag instead of RDONLY for quota off. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/acl.c | 3 +++ fs/f2fs/checkpoint.c | 1 - fs/f2fs/file.c | 26 ++++++++++++++++++++++++++ fs/f2fs/namei.c | 29 +++++++++++++++++++++++++++++ fs/f2fs/super.c | 3 +++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 436b3a1464d9..2bb7c9fc5144 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -250,6 +250,9 @@ static int __f2fs_set_acl(struct inode *inode, int type, int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + return __f2fs_set_acl(inode, type, acl, NULL); } diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index d58c9362bdac..7232b70508be 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -29,7 +29,6 @@ struct kmem_cache *inode_entry_slab; void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io) { set_ckpt_flags(sbi, CP_ERROR_FLAG); - sbi->sb->s_flags |= MS_RDONLY; if (!end_io) f2fs_flush_merged_writes(sbi); } diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 8edc188a1b22..749737838275 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -56,6 +56,11 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, struct dnode_of_data dn; int err; + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; + goto err; + } + sb_start_pagefault(inode->i_sb); f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); @@ -117,6 +122,7 @@ out_sem: out: sb_end_pagefault(inode->i_sb); f2fs_update_time(sbi, REQ_TIME); +err: return block_page_mkwrite_return(err); } @@ -313,6 +319,8 @@ out: int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(file))))) + return -EIO; return f2fs_do_sync_file(file, start, end, datasync, false); } @@ -449,6 +457,9 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) struct inode *inode = file_inode(file); int err; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + /* we don't need to use inline_data strictly */ err = f2fs_convert_inline_inode(inode); if (err) @@ -635,6 +646,9 @@ int f2fs_truncate(struct inode *inode) { int err; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return 0; @@ -713,6 +727,9 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) int err; bool size_changed = false; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + err = inode_change_ok(inode, attr); if (err) return err; @@ -1451,6 +1468,9 @@ static long f2fs_fallocate(struct file *file, int mode, struct inode *inode = file_inode(file); long ret = 0; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + /* f2fs only support ->fallocate for regular file */ if (!S_ISREG(inode->i_mode)) return -EINVAL; @@ -2427,6 +2447,9 @@ static int f2fs_ioc_get_features(struct file *filp, unsigned long arg) long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp))))) + return -EIO; + switch (cmd) { case F2FS_IOC_GETFLAGS: return f2fs_ioc_getflags(filp, arg); @@ -2482,6 +2505,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct blk_plug plug; ssize_t ret; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + inode_lock(inode); ret = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (!ret) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 86afb89c080b..6b7784493e3c 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -175,6 +175,9 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, nid_t ino = 0; int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + dquot_initialize(dir); inode = f2fs_new_inode(dir, mode); @@ -217,6 +220,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, struct f2fs_sb_info *sbi = F2FS_I_SB(dir); int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if (f2fs_encrypted_inode(dir) && !fscrypt_has_permitted_context(dir, inode)) return -EPERM; @@ -412,6 +418,9 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) trace_f2fs_unlink_enter(dir, dentry); + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + dquot_initialize(dir); de = f2fs_find_entry(dir, &dentry->d_name, &page); @@ -473,6 +482,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, struct fscrypt_symlink_data *sd = NULL; int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if (f2fs_encrypted_inode(dir)) { err = fscrypt_get_encryption_info(dir); if (err) @@ -577,6 +589,9 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) struct inode *inode; int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + dquot_initialize(dir); inode = f2fs_new_inode(dir, S_IFDIR | mode); @@ -627,6 +642,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, struct inode *inode; int err = 0; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; if (!new_valid_dev(rdev)) return -EINVAL; @@ -720,6 +737,9 @@ out: static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(dir)))) + return -EIO; + if (f2fs_encrypted_inode(dir)) { int err = fscrypt_get_encryption_info(dir); if (err) @@ -731,6 +751,9 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) static int f2fs_create_whiteout(struct inode *dir, struct inode **whiteout) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(dir)))) + return -EIO; + return __f2fs_tmpfile(dir, NULL, S_IFCHR | WHITEOUT_MODE, whiteout); } @@ -750,6 +773,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, bool is_old_inline = f2fs_has_inline_dentry(old_dir); int err = -ENOENT; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if ((f2fs_encrypted_inode(old_dir) && !fscrypt_has_encryption_key(old_dir)) || (f2fs_encrypted_inode(new_dir) && @@ -939,6 +965,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, int old_nlink = 0, new_nlink = 0; int err = -ENOENT; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if ((f2fs_encrypted_inode(old_dir) && !fscrypt_has_encryption_key(old_dir)) || (f2fs_encrypted_inode(new_dir) && diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 563be26241b1..56ed42354a50 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -875,6 +875,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync) struct f2fs_sb_info *sbi = F2FS_SB(sb); int err = 0; + if (unlikely(f2fs_cp_error(sbi))) + return 0; + trace_f2fs_sync_fs(sb, sync); if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) -- GitLab From c3290d767cade91b24ebacbe94437837170888fb Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 24 Oct 2017 09:46:54 +0200 Subject: [PATCH 1546/5498] f2fs: show # of dirty segments via sysfs This patch adds one sysfs entry to show # of dirty segments which can be used for gc timing by user. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index f316b2d72081..7fc31799942b 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -63,6 +63,13 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) return NULL; } +static ssize_t dirty_segments_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%llu\n", + (unsigned long long)(dirty_segments(sbi))); +} + static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { @@ -278,6 +285,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); #endif +F2FS_GENERAL_RO_ATTR(dirty_segments); F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); F2FS_GENERAL_RO_ATTR(features); @@ -320,6 +328,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(inject_rate), ATTR_LIST(inject_type), #endif + ATTR_LIST(dirty_segments), ATTR_LIST(lifetime_write_kbytes), ATTR_LIST(features), ATTR_LIST(reserved_blocks), -- GitLab From bf85becc21511ad9aa390fe1c8e359392c3b7ee1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 23 Oct 2017 23:50:15 +0200 Subject: [PATCH 1547/5498] f2fs: add missing quota_initialize This patch adds to call quota_intialize in f2fs_set_acl, f2fs_unlink, and f2fs_rename. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 4 ++++ fs/f2fs/xattr.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 6b7784493e3c..4d429578b68c 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -422,6 +422,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) return -EIO; dquot_initialize(dir); + dquot_initialize(inode); de = f2fs_find_entry(dir, &dentry->d_name, &page); if (!de) { @@ -797,6 +798,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, dquot_initialize(new_dir); + if (new_inode) + dquot_initialize(new_inode); + old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) { if (IS_ERR(old_page)) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 436038088802..8fcf19cdfc6b 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -720,6 +720,8 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); int err; + dquot_initialize(inode); + /* this case is only from init_inode_metadata */ if (ipage) return __f2fs_setxattr(inode, index, name, value, -- GitLab From eb25e774fe3114a2ba4a12c2dffcc1a565d97af8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 26 Oct 2017 10:31:22 +0200 Subject: [PATCH 1548/5498] f2fs: show current cp state This patch shows whether checkpoint met any error case. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index fdffb08cd8ab..e11861ff8031 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -266,9 +266,10 @@ static int stat_show(struct seq_file *s, void *v) update_general_status(si->sbi); - seq_printf(s, "\n=====[ partition info(%s). #%d, %s]=====\n", + seq_printf(s, "\n=====[ partition info(%s). #%d, %s, CP: %s]=====\n", bdevname(si->sbi->sb->s_bdev, devname), i++, - f2fs_readonly(si->sbi->sb) ? "RO": "RW"); + f2fs_readonly(si->sbi->sb) ? "RO": "RW", + f2fs_cp_error(si->sbi) ? "Error": "Good"); seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ", si->sit_area_segs, si->nat_area_segs); seq_printf(s, "[SSA: %d] [MAIN: %d", -- GitLab From e254d1a66b684aef37f74c92275c6a85fee08266 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 6 Sep 2017 21:59:50 +0800 Subject: [PATCH 1549/5498] f2fs: support flexible inline xattr size Now, in product, more and more features based on file encryption were introduced, their demand of xattr space is increasing, however, inline xattr has fixed-size of 200 bytes, once inline xattr space is full, new increased xattr data would occupy additional xattr block which may bring us more space usage and performance regression during persisting. In order to resolve above issue, it's better to expand inline xattr size flexibly according to user's requirement. So this patch introduces new filesystem feature 'flexible inline xattr', and new mount option 'inline_xattr_size=%u', once mkfs enables the feature, we can use the option to make f2fs supporting flexible inline xattr size. To support this feature, we add extra attribute i_inline_xattr_size in inode layout, indicating that how many space inline xattr borrows from block address mapping space in inode layout, by this, we can easily locate and store flexible-sized inline xattr data in inode. Inode disk layout: +----------------------+ | .i_mode | | ... | | .i_ext | +----------------------+ | .i_extra_isize | | .i_inline_xattr_size |-----------+ | ... | | +----------------------+ | | .i_addr | | | - block address or | | | - inline data | | +----------------------+<---+ v | inline xattr | +---inline xattr range +----------------------+<---+ | .i_nid | +----------------------+ | node_footer | | (nid, ino, offset) | +----------------------+ Note that, we have to cnosider backward compatibility which reserved inline_data space, 200 bytes, all the time, reported by Sheng Yong. Previous inline data or directory always reserved 200 bytes in inode layout, even if inline_xattr is disabled. In order to keep inline_dentry's structure for backward compatibility, we get the space back only from inline_data. Signed-off-by: Chao Yu Reported-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 39 ++++++++++++++++++++++++++------------- fs/f2fs/inode.c | 21 +++++++++++++++++++++ fs/f2fs/namei.c | 13 +++++++++++++ fs/f2fs/node.c | 10 ++++++++-- fs/f2fs/super.c | 32 +++++++++++++++++++++++++++++++- fs/f2fs/sysfs.c | 7 +++++++ fs/f2fs/xattr.c | 18 +++++++++--------- include/linux/f2fs_fs.h | 5 +++-- 8 files changed, 118 insertions(+), 27 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 090b7c14ccd2..8a3386239c7e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -95,6 +95,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_GRPQUOTA 0x00100000 #define F2FS_MOUNT_PRJQUOTA 0x00200000 #define F2FS_MOUNT_QUOTA 0x00400000 +#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -120,6 +121,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_FEATURE_INODE_CHKSUM 0x0020 +#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -493,11 +495,14 @@ struct f2fs_flush_device { /* for inline stuff */ #define DEF_INLINE_RESERVED_SIZE 1 +#define DEF_MIN_INLINE_SIZE 1 static inline int get_extra_isize(struct inode *inode); -#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ - (CUR_ADDRS_PER_INODE(inode) - \ - DEF_INLINE_RESERVED_SIZE - \ - F2FS_INLINE_XATTR_ADDRS)) +static inline int get_inline_xattr_addrs(struct inode *inode); +#define F2FS_INLINE_XATTR_ADDRS(inode) get_inline_xattr_addrs(inode) +#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ + (CUR_ADDRS_PER_INODE(inode) - \ + F2FS_INLINE_XATTR_ADDRS(inode) - \ + DEF_INLINE_RESERVED_SIZE)) /* for inline dir */ #define NR_INLINE_DENTRY(inode) (MAX_INLINE_DATA(inode) * BITS_PER_BYTE / \ @@ -706,6 +711,7 @@ struct f2fs_inode_info { int i_extra_isize; /* size of extra space located in i_addr */ kprojid_t i_projid; /* id for project quota */ + int i_inline_xattr_size; /* inline xattr size */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1167,6 +1173,7 @@ struct f2fs_sb_info { loff_t max_file_blocks; /* max block index of file */ int active_logs; /* # of active logs */ int dir_level; /* directory level */ + int inline_xattr_size; /* inline xattr size */ block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ @@ -2280,25 +2287,20 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) static inline unsigned int addrs_per_inode(struct inode *inode) { - if (f2fs_has_inline_xattr(inode)) - return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS; - return CUR_ADDRS_PER_INODE(inode); + return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS(inode); } -static inline void *inline_xattr_addr(struct page *page) +static inline void *inline_xattr_addr(struct inode *inode, struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - - F2FS_INLINE_XATTR_ADDRS]); + F2FS_INLINE_XATTR_ADDRS(inode)]); } static inline int inline_xattr_size(struct inode *inode) { - if (f2fs_has_inline_xattr(inode)) - return F2FS_INLINE_XATTR_ADDRS << 2; - else - return 0; + return get_inline_xattr_addrs(inode) * sizeof(__le32); } static inline int f2fs_has_inline_data(struct inode *inode) @@ -2468,6 +2470,12 @@ static inline int get_extra_isize(struct inode *inode) return F2FS_I(inode)->i_extra_isize / sizeof(__le32); } +static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb); +static inline int get_inline_xattr_addrs(struct inode *inode) +{ + return F2FS_I(inode)->i_inline_xattr_size; +} + #define get_inode_mode(i) \ ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) @@ -3138,6 +3146,11 @@ static inline int f2fs_sb_has_inode_chksum(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM); } +static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 0c56c1fcd3ed..1ae5b61cf2bc 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -232,6 +232,23 @@ static int do_read_inode(struct inode *inode) fi->i_extra_isize = f2fs_has_extra_attr(inode) ? le16_to_cpu(ri->i_extra_isize) : 0; + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) { + f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode)); + fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); + } else if (f2fs_has_inline_xattr(inode) || + f2fs_has_inline_dentry(inode)) { + fi->i_inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; + } else { + + /* + * Previous inline data or directory always reserved 200 bytes + * in inode layout, even if inline_xattr is disabled. In order + * to keep inline_dentry's structure for backward compatibility, + * we get the space back only from inline_data. + */ + fi->i_inline_xattr_size = 0; + } + /* check data exist */ if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) __recover_inline_status(inode, node_page); @@ -384,6 +401,10 @@ int update_inode(struct inode *inode, struct page *node_page) if (f2fs_has_extra_attr(inode)) { ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + if (f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(inode)->sb)) + ri->i_inline_xattr_size = + cpu_to_le16(F2FS_I(inode)->i_inline_xattr_size); + if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) && F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, i_projid)) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 4d429578b68c..eaf7476a0942 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -30,6 +30,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) nid_t ino; struct inode *inode; bool nid_free = false; + int xattr_size = 0; int err; inode = new_inode(dir->i_sb); @@ -84,11 +85,23 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (test_opt(sbi, INLINE_XATTR)) set_inode_flag(inode, FI_INLINE_XATTR); + if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode)) set_inode_flag(inode, FI_INLINE_DATA); if (f2fs_may_inline_dentry(inode)) set_inode_flag(inode, FI_INLINE_DENTRY); + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) { + f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode)); + if (f2fs_has_inline_xattr(inode)) + xattr_size = sbi->inline_xattr_size; + /* Otherwise, will be 0 */ + } else if (f2fs_has_inline_xattr(inode) || + f2fs_has_inline_dentry(inode)) { + xattr_size = DEFAULT_INLINE_XATTR_ADDRS; + } + F2FS_I(inode)->i_inline_xattr_size = xattr_size; + f2fs_init_extent_tree(inode, NULL); stat_inc_inline_xattr(inode); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b1df3451d451..fde6ad4ed775 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2200,8 +2200,8 @@ void recover_inline_xattr(struct inode *inode, struct page *page) goto update_inode; } - dst_addr = inline_xattr_addr(ipage); - src_addr = inline_xattr_addr(page); + dst_addr = inline_xattr_addr(inode, ipage); + src_addr = inline_xattr_addr(inode, page); inline_size = inline_xattr_size(inode); f2fs_wait_on_page_writeback(ipage, NODE, true); @@ -2290,6 +2290,12 @@ retry: dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR); if (dst->i_inline & F2FS_EXTRA_ATTR) { dst->i_extra_isize = src->i_extra_isize; + + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb) && + F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), + i_inline_xattr_size)) + dst->i_inline_xattr_size = src->i_inline_xattr_size; + if (f2fs_sb_has_project_quota(sbi->sb) && F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), i_projid)) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 56ed42354a50..145ddd5ff2b4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -95,6 +95,7 @@ enum { Opt_disable_ext_identify, Opt_inline_xattr, Opt_noinline_xattr, + Opt_inline_xattr_size, Opt_inline_data, Opt_inline_dentry, Opt_noinline_dentry, @@ -144,6 +145,7 @@ static match_table_t f2fs_tokens = { {Opt_disable_ext_identify, "disable_ext_identify"}, {Opt_inline_xattr, "inline_xattr"}, {Opt_noinline_xattr, "noinline_xattr"}, + {Opt_inline_xattr_size, "inline_xattr_size=%u"}, {Opt_inline_data, "inline_data"}, {Opt_inline_dentry, "inline_dentry"}, {Opt_noinline_dentry, "noinline_dentry"}, @@ -394,6 +396,12 @@ static int parse_options(struct super_block *sb, char *options) case Opt_noinline_xattr: clear_opt(sbi, INLINE_XATTR); break; + case Opt_inline_xattr_size: + if (args->from && match_int(args, &arg)) + return -EINVAL; + set_opt(sbi, INLINE_XATTR_SIZE); + sbi->inline_xattr_size = arg; + break; #else case Opt_user_xattr: f2fs_msg(sb, KERN_INFO, @@ -620,6 +628,24 @@ static int parse_options(struct super_block *sb, char *options) F2FS_IO_SIZE_KB(sbi)); return -EINVAL; } + + if (test_opt(sbi, INLINE_XATTR_SIZE)) { + if (!test_opt(sbi, INLINE_XATTR)) { + f2fs_msg(sb, KERN_ERR, + "inline_xattr_size option should be " + "set with inline_xattr option"); + return -EINVAL; + } + if (!sbi->inline_xattr_size || + sbi->inline_xattr_size >= DEF_ADDRS_PER_INODE - + F2FS_TOTAL_EXTRA_ATTR_SIZE - + DEF_INLINE_RESERVED_SIZE - + DEF_MIN_INLINE_SIZE) { + f2fs_msg(sb, KERN_ERR, + "inline xattr size is out of range"); + return -EINVAL; + } + } return 0; } @@ -1066,6 +1092,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",inline_xattr"); else seq_puts(seq, ",noinline_xattr"); + if (test_opt(sbi, INLINE_XATTR_SIZE)) + seq_printf(seq, ",inline_xattr_size=%u", + sbi->inline_xattr_size); #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL if (test_opt(sbi, POSIX_ACL)) @@ -1128,6 +1157,7 @@ static void default_options(struct f2fs_sb_info *sbi) { /* init some FS parameters */ sbi->active_logs = NR_CURSEG_TYPE; + sbi->inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; set_opt(sbi, BG_GC); set_opt(sbi, INLINE_XATTR); @@ -1681,7 +1711,7 @@ static loff_t max_file_blocks(void) /* * note: previously, result is equal to (DEF_ADDRS_PER_INODE - - * F2FS_INLINE_XATTR_ADDRS), but now f2fs try to reserve more + * DEFAULT_INLINE_XATTR_ADDRS), but now f2fs try to reserve more * space in inode.i_addr, it will be more safe to reassign * result as zero. */ diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 7fc31799942b..79c524ec6ec6 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -107,6 +107,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_inode_chksum(sb)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "inode_checksum"); + if (f2fs_sb_has_flexible_inline_xattr(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "flexible_inline_xattr"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } @@ -216,6 +219,7 @@ enum feat_id { FEAT_EXTRA_ATTR, FEAT_PROJECT_QUOTA, FEAT_INODE_CHECKSUM, + FEAT_FLEXIBLE_INLINE_XATTR, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -228,6 +232,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_EXTRA_ATTR: case FEAT_PROJECT_QUOTA: case FEAT_INODE_CHECKSUM: + case FEAT_FLEXIBLE_INLINE_XATTR: return snprintf(buf, PAGE_SIZE, "supported\n"); } return 0; @@ -299,6 +304,7 @@ F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE); F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); +F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -346,6 +352,7 @@ static struct attribute *f2fs_feat_attrs[] = { ATTR_LIST(extra_attr), ATTR_LIST(project_quota), ATTR_LIST(inode_checksum), + ATTR_LIST(flexible_inline_xattr), NULL, }; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 8fcf19cdfc6b..3fb8301aeaee 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -266,12 +266,12 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, return entry; } -static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr, - void **last_addr, int index, - size_t len, const char *name) +static struct f2fs_xattr_entry *__find_inline_xattr(struct inode *inode, + void *base_addr, void **last_addr, int index, + size_t len, const char *name) { struct f2fs_xattr_entry *entry; - unsigned int inline_size = F2FS_INLINE_XATTR_ADDRS << 2; + unsigned int inline_size = inline_xattr_size(inode); list_for_each_xattr(entry, base_addr) { if ((void *)entry + sizeof(__u32) > base_addr + inline_size || @@ -299,13 +299,13 @@ static int read_inline_xattr(struct inode *inode, struct page *ipage, void *inline_addr; if (ipage) { - inline_addr = inline_xattr_addr(ipage); + inline_addr = inline_xattr_addr(inode, ipage); } else { page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) return PTR_ERR(page); - inline_addr = inline_xattr_addr(page); + inline_addr = inline_xattr_addr(inode, page); } memcpy(txattr_addr, inline_addr, inline_size); f2fs_put_page(page, 1); @@ -358,7 +358,7 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, if (err) goto out; - *xe = __find_inline_xattr(txattr_addr, &last_addr, + *xe = __find_inline_xattr(inode, txattr_addr, &last_addr, index, len, name); if (*xe) goto check; @@ -453,7 +453,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, void *inline_addr; if (ipage) { - inline_addr = inline_xattr_addr(ipage); + inline_addr = inline_xattr_addr(inode, ipage); f2fs_wait_on_page_writeback(ipage, NODE, true); set_page_dirty(ipage); } else { @@ -462,7 +462,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, alloc_nid_failed(sbi, new_nid); return PTR_ERR(page); } - inline_addr = inline_xattr_addr(page); + inline_addr = inline_xattr_addr(inode, page); f2fs_wait_on_page_writeback(page, NODE, true); } memcpy(inline_addr, txattr_addr, inline_size); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 00cdb19429d5..27d4bc47cefb 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -184,7 +184,8 @@ struct f2fs_extent { } __packed; #define F2FS_NAME_LEN 255 -#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ +/* 200 bytes for inline xattrs by default */ +#define DEFAULT_INLINE_XATTR_ADDRS 50 #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ get_extra_isize(inode)) @@ -238,7 +239,7 @@ struct f2fs_inode { union { struct { __le16 i_extra_isize; /* extra inode attribute size */ - __le16 i_padding; /* padding */ + __le16 i_inline_xattr_size; /* inline xattr size, unit: 4 bytes */ __le32 i_projid; /* project id */ __le32 i_inode_checksum;/* inode meta checksum */ __le32 i_extra_end[0]; /* for attribute size calculation */ -- GitLab From 30d0b7c1450fbbbdf692ea80352b5ccba8398743 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 16 Oct 2017 15:05:16 -0700 Subject: [PATCH 1550/5498] f2fs: handle error case when adding xattr entry This patch fixes recovering incomplete xattr entries remaining in inline xattr and xattr block, caused by any kind of errors. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 3fb8301aeaee..56c80831976b 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -438,10 +438,12 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); size_t inline_size = inline_xattr_size(inode); + struct page *in_page = NULL; void *xattr_addr; + void *inline_addr = NULL; struct page *xpage; nid_t new_nid = 0; - int err; + int err = 0; if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid) if (!alloc_nid(sbi, &new_nid)) @@ -449,30 +451,30 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, /* write to inline xattr */ if (inline_size) { - struct page *page = NULL; - void *inline_addr; - if (ipage) { inline_addr = inline_xattr_addr(inode, ipage); - f2fs_wait_on_page_writeback(ipage, NODE, true); - set_page_dirty(ipage); } else { - page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) { + in_page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(in_page)) { alloc_nid_failed(sbi, new_nid); - return PTR_ERR(page); + return PTR_ERR(in_page); } - inline_addr = inline_xattr_addr(inode, page); - f2fs_wait_on_page_writeback(page, NODE, true); + inline_addr = inline_xattr_addr(inode, in_page); } - memcpy(inline_addr, txattr_addr, inline_size); - f2fs_put_page(page, 1); + f2fs_wait_on_page_writeback(ipage ? ipage : in_page, + NODE, true); /* no need to use xattr node block */ if (hsize <= inline_size) { err = truncate_xattr_node(inode); alloc_nid_failed(sbi, new_nid); - return err; + if (err) { + f2fs_put_page(in_page, 1); + return err; + } + memcpy(inline_addr, txattr_addr, inline_size); + set_page_dirty(ipage ? ipage : in_page); + goto in_page_out; } } @@ -481,7 +483,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); if (IS_ERR(xpage)) { alloc_nid_failed(sbi, new_nid); - return PTR_ERR(xpage); + goto in_page_out; } f2fs_bug_on(sbi, new_nid); f2fs_wait_on_page_writeback(xpage, NODE, true); @@ -491,17 +493,24 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, xpage = new_node_page(&dn, XATTR_NODE_OFFSET); if (IS_ERR(xpage)) { alloc_nid_failed(sbi, new_nid); - return PTR_ERR(xpage); + goto in_page_out; } alloc_nid_done(sbi, new_nid); } - xattr_addr = page_address(xpage); + + if (inline_size) + memcpy(inline_addr, txattr_addr, inline_size); memcpy(xattr_addr, txattr_addr + inline_size, VALID_XATTR_BLOCK_SIZE); + + if (inline_size) + set_page_dirty(ipage ? ipage : in_page); set_page_dirty(xpage); - f2fs_put_page(xpage, 1); - return 0; + f2fs_put_page(xpage, 1); +in_page_out: + f2fs_put_page(in_page, 1); + return err; } int f2fs_getxattr(struct inode *inode, int index, const char *name, -- GitLab From 2606cff9a1c68b24a6f2390a1dc3273ed00233d9 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Fri, 27 Oct 2017 20:45:05 +0800 Subject: [PATCH 1551/5498] f2fs: support soft block reservation It supports to extend reserved_blocks sysfs interface to be soft threshold, which allows user configure it exceeding current available user space. This patch also introduces a new sysfs interface called current_reserved_blocks, which shows the current blocks that have already been reserved. Signed-off-by: Yunlong Song Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 13 ++++++++++++- fs/f2fs/f2fs.h | 13 +++++++++++-- fs/f2fs/super.c | 3 ++- fs/f2fs/sysfs.c | 15 ++++++++++++--- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index cecb93af8967..96d70de16ccc 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -138,7 +138,18 @@ What: /sys/fs/f2fs//reserved_blocks Date: June 2017 Contact: "Chao Yu" Description: - Controls current reserved blocks in system. + Controls target reserved blocks in system, the threshold + is soft, it could exceed current available user space. + +What: /sys/fs/f2fs//current_reserved_blocks +Date: October 2017 +Contact: "Yunlong Song" +Contact: "Chao Yu" +Description: + Shows current reserved blocks in system, it may be temporarily + smaller than target_reserved_blocks, but will gradually + increase to target_reserved_blocks when more free blocks are + freed by user later. What: /sys/fs/f2fs//gc_urgent Date: August 2017 diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8a3386239c7e..ad6bea11fb55 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1180,6 +1180,7 @@ struct f2fs_sb_info { block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ block_t reserved_blocks; /* configurable reserved blocks */ + block_t current_reserved_blocks; /* current reserved blocks */ u32 s_next_generation; /* for NFS support */ @@ -1652,7 +1653,8 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); sbi->total_valid_block_count += (block_t)(*count); - avail_user_block_count = sbi->user_block_count - sbi->reserved_blocks; + avail_user_block_count = sbi->user_block_count - + sbi->current_reserved_blocks; if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { diff = sbi->total_valid_block_count - avail_user_block_count; *count -= diff; @@ -1686,6 +1688,10 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count); f2fs_bug_on(sbi, inode->i_blocks < sectors); sbi->total_valid_block_count -= (block_t)count; + if (sbi->reserved_blocks && + sbi->current_reserved_blocks < sbi->reserved_blocks) + sbi->current_reserved_blocks = min(sbi->reserved_blocks, + sbi->current_reserved_blocks + count); spin_unlock(&sbi->stat_lock); f2fs_i_blocks_write(inode, count, false, true); } @@ -1832,7 +1838,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); valid_block_count = sbi->total_valid_block_count + 1; - if (unlikely(valid_block_count + sbi->reserved_blocks > + if (unlikely(valid_block_count + sbi->current_reserved_blocks > sbi->user_block_count)) { spin_unlock(&sbi->stat_lock); goto enospc; @@ -1875,6 +1881,9 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, sbi->total_valid_node_count--; sbi->total_valid_block_count--; + if (sbi->reserved_blocks && + sbi->current_reserved_blocks < sbi->reserved_blocks) + sbi->current_reserved_blocks++; spin_unlock(&sbi->stat_lock); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 145ddd5ff2b4..e421efc66795 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1003,7 +1003,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = total_count - start_count; buf->f_bfree = user_block_count - valid_user_blocks(sbi) + ovp_count; buf->f_bavail = user_block_count - valid_user_blocks(sbi) - - sbi->reserved_blocks; + sbi->current_reserved_blocks; avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; @@ -2475,6 +2475,7 @@ try_onemore: le64_to_cpu(sbi->ckpt->valid_block_count); sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->reserved_blocks = 0; + sbi->current_reserved_blocks = 0; for (i = 0; i < NR_INODE_TYPE; i++) { INIT_LIST_HEAD(&sbi->inode_list[i]); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 79c524ec6ec6..7796e34d90a4 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -30,7 +30,7 @@ enum { FAULT_INFO_RATE, /* struct f2fs_fault_info */ FAULT_INFO_TYPE, /* struct f2fs_fault_info */ #endif - RESERVED_BLOCKS, + RESERVED_BLOCKS, /* struct f2fs_sb_info */ }; struct f2fs_attr { @@ -114,6 +114,12 @@ static ssize_t features_show(struct f2fs_attr *a, return len; } +static ssize_t current_reserved_blocks_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", sbi->current_reserved_blocks); +} + static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { @@ -153,12 +159,13 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, #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) { + if (t > (unsigned long)sbi->user_block_count) { spin_unlock(&sbi->stat_lock); return -EINVAL; } *ui = t; + sbi->current_reserved_blocks = min(sbi->reserved_blocks, + sbi->user_block_count - valid_user_blocks(sbi)); spin_unlock(&sbi->stat_lock); return count; } @@ -293,6 +300,7 @@ F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); F2FS_GENERAL_RO_ATTR(dirty_segments); F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); F2FS_GENERAL_RO_ATTR(features); +F2FS_GENERAL_RO_ATTR(current_reserved_blocks); #ifdef CONFIG_F2FS_FS_ENCRYPTION F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); @@ -338,6 +346,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(lifetime_write_kbytes), ATTR_LIST(features), ATTR_LIST(reserved_blocks), + ATTR_LIST(current_reserved_blocks), NULL, }; -- GitLab From a0daae0960b2fd3ae732ca1fdd118b25863bb92f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 28 Oct 2017 16:52:29 +0800 Subject: [PATCH 1552/5498] f2fs: add missing sysfs description There are some missing sysfs entries' description in document, add them. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 96d70de16ccc..a07134c517e0 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -51,6 +51,12 @@ Description: Controls the dirty page count condition for the in-place-update policies. +What: /sys/fs/f2fs//min_hot_blocks +Date: March 2017 +Contact: "Jaegeuk Kim" +Description: + Controls the dirty page count condition for redefining hot data. + What: /sys/fs/f2fs//max_small_discards Date: November 2013 Contact: "Jaegeuk Kim" @@ -102,6 +108,12 @@ Contact: "Jaegeuk Kim" Description: Controls the idle timing. +What: /sys/fs/f2fs//iostat_enable +Date: August 2017 +Contact: "Chao Yu" +Description: + Controls to enable/disable IO stat. + What: /sys/fs/f2fs//ra_nid_pages Date: October 2015 Contact: "Chao Yu" @@ -122,6 +134,12 @@ Contact: "Shuoran Liu" Description: Shows total written kbytes issued to disk. +What: /sys/fs/f2fs//feature +Date: July 2017 +Contact: "Jaegeuk Kim" +Description: + Shows all enabled features in current device. + What: /sys/fs/f2fs//inject_rate Date: May 2016 Contact: "Sheng Yong" -- GitLab From cf2c2387a36c3c1f2eb9ec48af2f138021a4024a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 14 Oct 2015 17:03:35 -0700 Subject: [PATCH 1553/5498] Input: add input-event-codes header file Add input-event-codes header file and move all type and axis defines there. The purpose of this new header file is to have a single canonical source for event-codes which can be used outside of C-code too. One example of such usage is the use of event-codes in devicetree source files. Change-Id: Ie66a4ce9dab38c3fc51a5bff63c0b589a82ae6d3 Signed-off-by: Hans de Goede Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov Git-commit: f902dd893427eade90f7eaf858e5ff8b150a5a12 Git-repo: https://android.googlesource.com/kernel/common/ [spathi@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Srinivasarao P --- include/uapi/linux/Kbuild | 1 + include/uapi/linux/input-event-codes.h | 806 +++++++++++++++++++++++++ include/uapi/linux/input.h | 789 +----------------------- 3 files changed, 808 insertions(+), 788 deletions(-) create mode 100644 include/uapi/linux/input-event-codes.h diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 06a5fb00642e..c94ec777da53 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -199,6 +199,7 @@ header-y += in_route.h header-y += inet_diag.h header-y += inotify.h header-y += input.h +header-y += input-event-codes.h header-y += ioctl.h header-y += ion.h header-y += ip.h diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h new file mode 100644 index 000000000000..a8e046b8b024 --- /dev/null +++ b/include/uapi/linux/input-event-codes.h @@ -0,0 +1,806 @@ +/* + * Input event codes + * + * *** IMPORTANT *** + * This file is not only included from C-code but also from devicetree source + * files. As such this file MUST only contain comments and defines. + * + * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 2015 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#ifndef _UAPI_INPUT_EVENT_CODES_H +#define _UAPI_INPUT_EVENT_CODES_H + +/* + * Device properties and quirks + */ + +#define INPUT_PROP_POINTER 0x00 /* needs a pointer */ +#define INPUT_PROP_DIRECT 0x01 /* direct input devices */ +#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ +#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ +#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ +#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ +#define INPUT_PROP_NO_DUMMY_RELEASE 0x06 /* no dummy event */ + +#define INPUT_PROP_MAX 0x1f +#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) + +/* + * Event types + */ + +#define EV_SYN 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_SW 0x05 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f +#define EV_CNT (EV_MAX+1) + +/* + * Synchronization events. + */ + +#define SYN_REPORT 0 +#define SYN_CONFIG 1 +#define SYN_MT_REPORT 2 +#define SYN_DROPPED 3 +#define SYN_TIME_SEC 4 +#define SYN_TIME_NSEC 5 +#define SYN_MAX 0xf +#define SYN_CNT (SYN_MAX+1) + +/* + * Keys and buttons + * + * Most of the keys/buttons are modeled after USB HUT 1.12 + * (see http://www.usb.org/developers/hidpage). + * Abbreviations in the comments: + * AC - Application Control + * AL - Application Launch Button + * SC - System Control + */ + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 + +#define KEY_ZENKAKUHANKAKU 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_RO 89 +#define KEY_KATAKANA 90 +#define KEY_HIRAGANA 91 +#define KEY_HENKAN 92 +#define KEY_KATAKANAHIRAGANA 93 +#define KEY_MUHENKAN 94 +#define KEY_KPJPCOMMA 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 /* SC System Power Down */ +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ + +#define KEY_KPCOMMA 121 +#define KEY_HANGEUL 122 +#define KEY_HANGUEL KEY_HANGEUL +#define KEY_HANJA 123 +#define KEY_YEN 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 /* AC Stop */ +#define KEY_AGAIN 129 +#define KEY_PROPS 130 /* AC Properties */ +#define KEY_UNDO 131 /* AC Undo */ +#define KEY_FRONT 132 +#define KEY_COPY 133 /* AC Copy */ +#define KEY_OPEN 134 /* AC Open */ +#define KEY_PASTE 135 /* AC Paste */ +#define KEY_FIND 136 /* AC Search */ +#define KEY_CUT 137 /* AC Cut */ +#define KEY_HELP 138 /* AL Integrated Help Center */ +#define KEY_MENU 139 /* Menu (show menu) */ +#define KEY_CALC 140 /* AL Calculator */ +#define KEY_SETUP 141 +#define KEY_SLEEP 142 /* SC System Sleep */ +#define KEY_WAKEUP 143 /* System Wake Up */ +#define KEY_FILE 144 /* AL Local Machine Browser */ +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 /* AL Internet Browser */ +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ +#define KEY_SCREENLOCK KEY_COFFEE +#define KEY_DIRECTION 153 +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 /* AC Bookmarks */ +#define KEY_COMPUTER 157 +#define KEY_BACK 158 /* AC Back */ +#define KEY_FORWARD 159 /* AC Forward */ +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 /* Media Select Telephone */ +#define KEY_ISO 170 +#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ +#define KEY_HOMEPAGE 172 /* AC Home */ +#define KEY_REFRESH 173 /* AC Refresh */ +#define KEY_EXIT 174 /* AC Exit */ +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 +#define KEY_NEW 181 /* AC New */ +#define KEY_REDO 182 /* AC Redo/Repeat */ + +#define KEY_F13 183 +#define KEY_F14 184 +#define KEY_F15 185 +#define KEY_F16 186 +#define KEY_F17 187 +#define KEY_F18 188 +#define KEY_F19 189 +#define KEY_F20 190 +#define KEY_F21 191 +#define KEY_F22 192 +#define KEY_F23 193 +#define KEY_F24 194 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_DASHBOARD 204 /* AL Dashboard */ +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 /* AC Close */ +#define KEY_PLAY 207 +#define KEY_FASTFORWARD 208 +#define KEY_BASSBOOST 209 +#define KEY_PRINT 210 /* AC Print */ +#define KEY_HP 211 +#define KEY_CAMERA 212 +#define KEY_SOUND 213 +#define KEY_QUESTION 214 +#define KEY_EMAIL 215 +#define KEY_CHAT 216 +#define KEY_SEARCH 217 +#define KEY_CONNECT 218 +#define KEY_FINANCE 219 /* AL Checkbook/Finance */ +#define KEY_SPORT 220 +#define KEY_SHOP 221 +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 /* AC Cancel */ +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 +#define KEY_MEDIA 226 + +#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video + outputs (Monitor/LCD/TV-out/etc) */ +#define KEY_KBDILLUMTOGGLE 228 +#define KEY_KBDILLUMDOWN 229 +#define KEY_KBDILLUMUP 230 + +#define KEY_SEND 231 /* AC Send */ +#define KEY_REPLY 232 /* AC Reply */ +#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ +#define KEY_SAVE 234 /* AC Save */ +#define KEY_DOCUMENTS 235 + +#define KEY_BATTERY 236 + +#define KEY_BLUETOOTH 237 +#define KEY_WLAN 238 +#define KEY_UWB 239 + +#define KEY_UNKNOWN 240 + +#define KEY_VIDEO_NEXT 241 /* drive next video source */ +#define KEY_VIDEO_PREV 242 /* drive previous video source */ +#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ +#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual + brightness control is off, + rely on ambient */ +#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO +#define KEY_DISPLAY_OFF 245 /* display device to off state */ + +#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ +#define KEY_WIMAX KEY_WWAN +#define KEY_RFKILL 247 /* Key that controls all radios */ + +#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ + +/* Code 255 is reserved for special needs of AT keyboard driver */ + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 +#define BTN_TASK 0x117 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_SOUTH 0x130 +#define BTN_A BTN_SOUTH +#define BTN_EAST 0x131 +#define BTN_B BTN_EAST +#define BTN_C 0x132 +#define BTN_NORTH 0x133 +#define BTN_X BTN_NORTH +#define BTN_WEST 0x134 +#define BTN_Y BTN_WEST +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c +#define BTN_TOOL_DOUBLETAP 0x14d +#define BTN_TOOL_TRIPLETAP 0x14e +#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_OK 0x160 +#define KEY_SELECT 0x161 +#define KEY_GOTO 0x162 +#define KEY_CLEAR 0x163 +#define KEY_POWER2 0x164 +#define KEY_OPTION 0x165 +#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ +#define KEY_TIME 0x167 +#define KEY_VENDOR 0x168 +#define KEY_ARCHIVE 0x169 +#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ +#define KEY_CHANNEL 0x16b +#define KEY_FAVORITES 0x16c +#define KEY_EPG 0x16d +#define KEY_PVR 0x16e /* Media Select Home */ +#define KEY_MHP 0x16f +#define KEY_LANGUAGE 0x170 +#define KEY_TITLE 0x171 +#define KEY_SUBTITLE 0x172 +#define KEY_ANGLE 0x173 +#define KEY_ZOOM 0x174 +#define KEY_MODE 0x175 +#define KEY_KEYBOARD 0x176 +#define KEY_SCREEN 0x177 +#define KEY_PC 0x178 /* Media Select Computer */ +#define KEY_TV 0x179 /* Media Select TV */ +#define KEY_TV2 0x17a /* Media Select Cable */ +#define KEY_VCR 0x17b /* Media Select VCR */ +#define KEY_VCR2 0x17c /* VCR Plus */ +#define KEY_SAT 0x17d /* Media Select Satellite */ +#define KEY_SAT2 0x17e +#define KEY_CD 0x17f /* Media Select CD */ +#define KEY_TAPE 0x180 /* Media Select Tape */ +#define KEY_RADIO 0x181 +#define KEY_TUNER 0x182 /* Media Select Tuner */ +#define KEY_PLAYER 0x183 +#define KEY_TEXT 0x184 +#define KEY_DVD 0x185 /* Media Select DVD */ +#define KEY_AUX 0x186 +#define KEY_MP3 0x187 +#define KEY_AUDIO 0x188 /* AL Audio Browser */ +#define KEY_VIDEO 0x189 /* AL Movie Browser */ +#define KEY_DIRECTORY 0x18a +#define KEY_LIST 0x18b +#define KEY_MEMO 0x18c /* Media Select Messages */ +#define KEY_CALENDAR 0x18d +#define KEY_RED 0x18e +#define KEY_GREEN 0x18f +#define KEY_YELLOW 0x190 +#define KEY_BLUE 0x191 +#define KEY_CHANNELUP 0x192 /* Channel Increment */ +#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ +#define KEY_FIRST 0x194 +#define KEY_LAST 0x195 /* Recall Last */ +#define KEY_AB 0x196 +#define KEY_NEXT 0x197 +#define KEY_RESTART 0x198 +#define KEY_SLOW 0x199 +#define KEY_SHUFFLE 0x19a +#define KEY_BREAK 0x19b +#define KEY_PREVIOUS 0x19c +#define KEY_DIGITS 0x19d +#define KEY_TEEN 0x19e +#define KEY_TWEN 0x19f +#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ +#define KEY_GAMES 0x1a1 /* Media Select Games */ +#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ +#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ +#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ +#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ +#define KEY_EDITOR 0x1a6 /* AL Text Editor */ +#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ +#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ +#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ +#define KEY_DATABASE 0x1aa /* AL Database App */ +#define KEY_NEWS 0x1ab /* AL Newsreader */ +#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ +#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ +#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ +#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ +#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE +#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ +#define KEY_LOGOFF 0x1b1 /* AL Logoff */ + +#define KEY_DOLLAR 0x1b2 +#define KEY_EURO 0x1b3 + +#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ +#define KEY_FRAMEFORWARD 0x1b5 +#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ +#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ +#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ +#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ +#define KEY_IMAGES 0x1ba /* AL Image Browser */ + +#define KEY_DEL_EOL 0x1c0 +#define KEY_DEL_EOS 0x1c1 +#define KEY_INS_LINE 0x1c2 +#define KEY_DEL_LINE 0x1c3 + +#define KEY_FN 0x1d0 +#define KEY_FN_ESC 0x1d1 +#define KEY_FN_F1 0x1d2 +#define KEY_FN_F2 0x1d3 +#define KEY_FN_F3 0x1d4 +#define KEY_FN_F4 0x1d5 +#define KEY_FN_F5 0x1d6 +#define KEY_FN_F6 0x1d7 +#define KEY_FN_F7 0x1d8 +#define KEY_FN_F8 0x1d9 +#define KEY_FN_F9 0x1da +#define KEY_FN_F10 0x1db +#define KEY_FN_F11 0x1dc +#define KEY_FN_F12 0x1dd +#define KEY_FN_1 0x1de +#define KEY_FN_2 0x1df +#define KEY_FN_D 0x1e0 +#define KEY_FN_E 0x1e1 +#define KEY_FN_F 0x1e2 +#define KEY_FN_S 0x1e3 +#define KEY_FN_B 0x1e4 + +#define KEY_BRL_DOT1 0x1f1 +#define KEY_BRL_DOT2 0x1f2 +#define KEY_BRL_DOT3 0x1f3 +#define KEY_BRL_DOT4 0x1f4 +#define KEY_BRL_DOT5 0x1f5 +#define KEY_BRL_DOT6 0x1f6 +#define KEY_BRL_DOT7 0x1f7 +#define KEY_BRL_DOT8 0x1f8 +#define KEY_BRL_DOT9 0x1f9 +#define KEY_BRL_DOT10 0x1fa + +#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ +#define KEY_NUMERIC_1 0x201 /* and other keypads */ +#define KEY_NUMERIC_2 0x202 +#define KEY_NUMERIC_3 0x203 +#define KEY_NUMERIC_4 0x204 +#define KEY_NUMERIC_5 0x205 +#define KEY_NUMERIC_6 0x206 +#define KEY_NUMERIC_7 0x207 +#define KEY_NUMERIC_8 0x208 +#define KEY_NUMERIC_9 0x209 +#define KEY_NUMERIC_STAR 0x20a +#define KEY_NUMERIC_POUND 0x20b + +#define KEY_CAMERA_FOCUS 0x210 +#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ + +#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ +#define KEY_TOUCHPAD_ON 0x213 +#define KEY_TOUCHPAD_OFF 0x214 + +#define KEY_CAMERA_ZOOMIN 0x215 +#define KEY_CAMERA_ZOOMOUT 0x216 +#define KEY_CAMERA_UP 0x217 +#define KEY_CAMERA_DOWN 0x218 +#define KEY_CAMERA_LEFT 0x219 +#define KEY_CAMERA_RIGHT 0x21a + +#define KEY_ATTENDANT_ON 0x21b +#define KEY_ATTENDANT_OFF 0x21c +#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ +#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ + +#define BTN_DPAD_UP 0x220 +#define BTN_DPAD_DOWN 0x221 +#define BTN_DPAD_LEFT 0x222 +#define BTN_DPAD_RIGHT 0x223 + +#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ + +#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ +#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ +#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ +#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ +#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ +#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ +#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ + +#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ +#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ + +#define KEY_KBDINPUTASSIST_PREV 0x260 +#define KEY_KBDINPUTASSIST_NEXT 0x261 +#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 +#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 +#define KEY_KBDINPUTASSIST_ACCEPT 0x264 +#define KEY_KBDINPUTASSIST_CANCEL 0x265 + +#define BTN_TRIGGER_HAPPY 0x2c0 +#define BTN_TRIGGER_HAPPY1 0x2c0 +#define BTN_TRIGGER_HAPPY2 0x2c1 +#define BTN_TRIGGER_HAPPY3 0x2c2 +#define BTN_TRIGGER_HAPPY4 0x2c3 +#define BTN_TRIGGER_HAPPY5 0x2c4 +#define BTN_TRIGGER_HAPPY6 0x2c5 +#define BTN_TRIGGER_HAPPY7 0x2c6 +#define BTN_TRIGGER_HAPPY8 0x2c7 +#define BTN_TRIGGER_HAPPY9 0x2c8 +#define BTN_TRIGGER_HAPPY10 0x2c9 +#define BTN_TRIGGER_HAPPY11 0x2ca +#define BTN_TRIGGER_HAPPY12 0x2cb +#define BTN_TRIGGER_HAPPY13 0x2cc +#define BTN_TRIGGER_HAPPY14 0x2cd +#define BTN_TRIGGER_HAPPY15 0x2ce +#define BTN_TRIGGER_HAPPY16 0x2cf +#define BTN_TRIGGER_HAPPY17 0x2d0 +#define BTN_TRIGGER_HAPPY18 0x2d1 +#define BTN_TRIGGER_HAPPY19 0x2d2 +#define BTN_TRIGGER_HAPPY20 0x2d3 +#define BTN_TRIGGER_HAPPY21 0x2d4 +#define BTN_TRIGGER_HAPPY22 0x2d5 +#define BTN_TRIGGER_HAPPY23 0x2d6 +#define BTN_TRIGGER_HAPPY24 0x2d7 +#define BTN_TRIGGER_HAPPY25 0x2d8 +#define BTN_TRIGGER_HAPPY26 0x2d9 +#define BTN_TRIGGER_HAPPY27 0x2da +#define BTN_TRIGGER_HAPPY28 0x2db +#define BTN_TRIGGER_HAPPY29 0x2dc +#define BTN_TRIGGER_HAPPY30 0x2dd +#define BTN_TRIGGER_HAPPY31 0x2de +#define BTN_TRIGGER_HAPPY32 0x2df +#define BTN_TRIGGER_HAPPY33 0x2e0 +#define BTN_TRIGGER_HAPPY34 0x2e1 +#define BTN_TRIGGER_HAPPY35 0x2e2 +#define BTN_TRIGGER_HAPPY36 0x2e3 +#define BTN_TRIGGER_HAPPY37 0x2e4 +#define BTN_TRIGGER_HAPPY38 0x2e5 +#define BTN_TRIGGER_HAPPY39 0x2e6 +#define BTN_TRIGGER_HAPPY40 0x2e7 + +/* We avoid low common keys in module aliases so they don't get huge. */ +#define KEY_MIN_INTERESTING KEY_MUTE +#define KEY_MAX 0x2ff +#define KEY_CNT (KEY_MAX+1) + +/* + * Relative axes + */ + +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_RX 0x03 +#define REL_RY 0x04 +#define REL_RZ 0x05 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +#define REL_MAX 0x0f +#define REL_CNT (REL_MAX+1) + +/* + * Absolute axes + */ + +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_TOOL_WIDTH 0x1c + +#define ABS_VOLUME 0x20 + +#define ABS_MISC 0x28 + +#define ABS_MT_SLOT 0x2f /* MT slot being modified */ +#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ +#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ +#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ +#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ +#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ +#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ +#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ +#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ +#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ +#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ +#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ +#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ +#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ + + +#define ABS_MAX 0x3f +#define ABS_CNT (ABS_MAX+1) + +/* + * Switch events + */ + +#define SW_LID 0x00 /* set = lid shut */ +#define SW_TABLET_MODE 0x01 /* set = tablet mode */ +#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ +#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" + set = radio enabled */ +#define SW_RADIO SW_RFKILL_ALL /* deprecated */ +#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ +#define SW_DOCK 0x05 /* set = plugged into dock */ +#define SW_LINEOUT_INSERT 0x06 /* set = inserted */ +#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */ +#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */ +#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */ +#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */ +#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ +#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ +#define SW_LINEIN_INSERT 0x0d /* set = inserted */ +#define SW_HPHL_OVERCURRENT 0x0e /* set = over current on left hph */ +#define SW_HPHR_OVERCURRENT 0x0f /* set = over current on right hph */ +#define SW_UNSUPPORT_INSERT 0x10 /* set = unsupported device inserted */ +#define SW_MICROPHONE2_INSERT 0x11 /* set = inserted */ +#define SW_MUTE_DEVICE 0x12 /* set = device disabled */ +#define SW_MAX 0x20 +#define SW_CNT (SW_MAX+1) + +/* + * Misc events + */ + +#define MSC_SERIAL 0x00 +#define MSC_PULSELED 0x01 +#define MSC_GESTURE 0x02 +#define MSC_RAW 0x03 +#define MSC_SCAN 0x04 +#define MSC_TIMESTAMP 0x05 +#define MSC_MAX 0x07 +#define MSC_CNT (MSC_MAX+1) + +/* + * LEDs + */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAIL 0x09 +#define LED_CHARGING 0x0a +#define LED_MAX 0x0f +#define LED_CNT (LED_MAX+1) + +/* + * Autorepeat values + */ + +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 +#define REP_CNT (REP_MAX+1) + +/* + * Sounds + */ + +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_TONE 0x02 +#define SND_MAX 0x07 +#define SND_CNT (SND_MAX+1) + +#endif diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index c27510de731d..1cf896a8aa60 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -16,6 +16,7 @@ #include #endif +#include "input-event-codes.h" /* * The event structure itself @@ -161,794 +162,6 @@ struct input_keymap_entry { #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ -/* - * Device properties and quirks - */ - -#define INPUT_PROP_POINTER 0x00 /* needs a pointer */ -#define INPUT_PROP_DIRECT 0x01 /* direct input devices */ -#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ -#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ -#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ -#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ -#define INPUT_PROP_NO_DUMMY_RELEASE 0x06 /* no dummy event */ - -#define INPUT_PROP_MAX 0x1f -#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) - -/* - * Event types - */ - -#define EV_SYN 0x00 -#define EV_KEY 0x01 -#define EV_REL 0x02 -#define EV_ABS 0x03 -#define EV_MSC 0x04 -#define EV_SW 0x05 -#define EV_LED 0x11 -#define EV_SND 0x12 -#define EV_REP 0x14 -#define EV_FF 0x15 -#define EV_PWR 0x16 -#define EV_FF_STATUS 0x17 -#define EV_MAX 0x1f -#define EV_CNT (EV_MAX+1) - -/* - * Synchronization events. - */ - -#define SYN_REPORT 0 -#define SYN_CONFIG 1 -#define SYN_MT_REPORT 2 -#define SYN_DROPPED 3 -#define SYN_TIME_SEC 4 -#define SYN_TIME_NSEC 5 -#define SYN_MAX 0xf -#define SYN_CNT (SYN_MAX+1) - -/* - * Keys and buttons - * - * Most of the keys/buttons are modeled after USB HUT 1.12 - * (see http://www.usb.org/developers/hidpage). - * Abbreviations in the comments: - * AC - Application Control - * AL - Application Launch Button - * SC - System Control - */ - -#define KEY_RESERVED 0 -#define KEY_ESC 1 -#define KEY_1 2 -#define KEY_2 3 -#define KEY_3 4 -#define KEY_4 5 -#define KEY_5 6 -#define KEY_6 7 -#define KEY_7 8 -#define KEY_8 9 -#define KEY_9 10 -#define KEY_0 11 -#define KEY_MINUS 12 -#define KEY_EQUAL 13 -#define KEY_BACKSPACE 14 -#define KEY_TAB 15 -#define KEY_Q 16 -#define KEY_W 17 -#define KEY_E 18 -#define KEY_R 19 -#define KEY_T 20 -#define KEY_Y 21 -#define KEY_U 22 -#define KEY_I 23 -#define KEY_O 24 -#define KEY_P 25 -#define KEY_LEFTBRACE 26 -#define KEY_RIGHTBRACE 27 -#define KEY_ENTER 28 -#define KEY_LEFTCTRL 29 -#define KEY_A 30 -#define KEY_S 31 -#define KEY_D 32 -#define KEY_F 33 -#define KEY_G 34 -#define KEY_H 35 -#define KEY_J 36 -#define KEY_K 37 -#define KEY_L 38 -#define KEY_SEMICOLON 39 -#define KEY_APOSTROPHE 40 -#define KEY_GRAVE 41 -#define KEY_LEFTSHIFT 42 -#define KEY_BACKSLASH 43 -#define KEY_Z 44 -#define KEY_X 45 -#define KEY_C 46 -#define KEY_V 47 -#define KEY_B 48 -#define KEY_N 49 -#define KEY_M 50 -#define KEY_COMMA 51 -#define KEY_DOT 52 -#define KEY_SLASH 53 -#define KEY_RIGHTSHIFT 54 -#define KEY_KPASTERISK 55 -#define KEY_LEFTALT 56 -#define KEY_SPACE 57 -#define KEY_CAPSLOCK 58 -#define KEY_F1 59 -#define KEY_F2 60 -#define KEY_F3 61 -#define KEY_F4 62 -#define KEY_F5 63 -#define KEY_F6 64 -#define KEY_F7 65 -#define KEY_F8 66 -#define KEY_F9 67 -#define KEY_F10 68 -#define KEY_NUMLOCK 69 -#define KEY_SCROLLLOCK 70 -#define KEY_KP7 71 -#define KEY_KP8 72 -#define KEY_KP9 73 -#define KEY_KPMINUS 74 -#define KEY_KP4 75 -#define KEY_KP5 76 -#define KEY_KP6 77 -#define KEY_KPPLUS 78 -#define KEY_KP1 79 -#define KEY_KP2 80 -#define KEY_KP3 81 -#define KEY_KP0 82 -#define KEY_KPDOT 83 - -#define KEY_ZENKAKUHANKAKU 85 -#define KEY_102ND 86 -#define KEY_F11 87 -#define KEY_F12 88 -#define KEY_RO 89 -#define KEY_KATAKANA 90 -#define KEY_HIRAGANA 91 -#define KEY_HENKAN 92 -#define KEY_KATAKANAHIRAGANA 93 -#define KEY_MUHENKAN 94 -#define KEY_KPJPCOMMA 95 -#define KEY_KPENTER 96 -#define KEY_RIGHTCTRL 97 -#define KEY_KPSLASH 98 -#define KEY_SYSRQ 99 -#define KEY_RIGHTALT 100 -#define KEY_LINEFEED 101 -#define KEY_HOME 102 -#define KEY_UP 103 -#define KEY_PAGEUP 104 -#define KEY_LEFT 105 -#define KEY_RIGHT 106 -#define KEY_END 107 -#define KEY_DOWN 108 -#define KEY_PAGEDOWN 109 -#define KEY_INSERT 110 -#define KEY_DELETE 111 -#define KEY_MACRO 112 -#define KEY_MUTE 113 -#define KEY_VOLUMEDOWN 114 -#define KEY_VOLUMEUP 115 -#define KEY_POWER 116 /* SC System Power Down */ -#define KEY_KPEQUAL 117 -#define KEY_KPPLUSMINUS 118 -#define KEY_PAUSE 119 -#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ - -#define KEY_KPCOMMA 121 -#define KEY_HANGEUL 122 -#define KEY_HANGUEL KEY_HANGEUL -#define KEY_HANJA 123 -#define KEY_YEN 124 -#define KEY_LEFTMETA 125 -#define KEY_RIGHTMETA 126 -#define KEY_COMPOSE 127 - -#define KEY_STOP 128 /* AC Stop */ -#define KEY_AGAIN 129 -#define KEY_PROPS 130 /* AC Properties */ -#define KEY_UNDO 131 /* AC Undo */ -#define KEY_FRONT 132 -#define KEY_COPY 133 /* AC Copy */ -#define KEY_OPEN 134 /* AC Open */ -#define KEY_PASTE 135 /* AC Paste */ -#define KEY_FIND 136 /* AC Search */ -#define KEY_CUT 137 /* AC Cut */ -#define KEY_HELP 138 /* AL Integrated Help Center */ -#define KEY_MENU 139 /* Menu (show menu) */ -#define KEY_CALC 140 /* AL Calculator */ -#define KEY_SETUP 141 -#define KEY_SLEEP 142 /* SC System Sleep */ -#define KEY_WAKEUP 143 /* System Wake Up */ -#define KEY_FILE 144 /* AL Local Machine Browser */ -#define KEY_SENDFILE 145 -#define KEY_DELETEFILE 146 -#define KEY_XFER 147 -#define KEY_PROG1 148 -#define KEY_PROG2 149 -#define KEY_WWW 150 /* AL Internet Browser */ -#define KEY_MSDOS 151 -#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ -#define KEY_SCREENLOCK KEY_COFFEE -#define KEY_DIRECTION 153 -#define KEY_CYCLEWINDOWS 154 -#define KEY_MAIL 155 -#define KEY_BOOKMARKS 156 /* AC Bookmarks */ -#define KEY_COMPUTER 157 -#define KEY_BACK 158 /* AC Back */ -#define KEY_FORWARD 159 /* AC Forward */ -#define KEY_CLOSECD 160 -#define KEY_EJECTCD 161 -#define KEY_EJECTCLOSECD 162 -#define KEY_NEXTSONG 163 -#define KEY_PLAYPAUSE 164 -#define KEY_PREVIOUSSONG 165 -#define KEY_STOPCD 166 -#define KEY_RECORD 167 -#define KEY_REWIND 168 -#define KEY_PHONE 169 /* Media Select Telephone */ -#define KEY_ISO 170 -#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ -#define KEY_HOMEPAGE 172 /* AC Home */ -#define KEY_REFRESH 173 /* AC Refresh */ -#define KEY_EXIT 174 /* AC Exit */ -#define KEY_MOVE 175 -#define KEY_EDIT 176 -#define KEY_SCROLLUP 177 -#define KEY_SCROLLDOWN 178 -#define KEY_KPLEFTPAREN 179 -#define KEY_KPRIGHTPAREN 180 -#define KEY_NEW 181 /* AC New */ -#define KEY_REDO 182 /* AC Redo/Repeat */ - -#define KEY_F13 183 -#define KEY_F14 184 -#define KEY_F15 185 -#define KEY_F16 186 -#define KEY_F17 187 -#define KEY_F18 188 -#define KEY_F19 189 -#define KEY_F20 190 -#define KEY_F21 191 -#define KEY_F22 192 -#define KEY_F23 193 -#define KEY_F24 194 - -#define KEY_PLAYCD 200 -#define KEY_PAUSECD 201 -#define KEY_PROG3 202 -#define KEY_PROG4 203 -#define KEY_DASHBOARD 204 /* AL Dashboard */ -#define KEY_SUSPEND 205 -#define KEY_CLOSE 206 /* AC Close */ -#define KEY_PLAY 207 -#define KEY_FASTFORWARD 208 -#define KEY_BASSBOOST 209 -#define KEY_PRINT 210 /* AC Print */ -#define KEY_HP 211 -#define KEY_CAMERA 212 -#define KEY_SOUND 213 -#define KEY_QUESTION 214 -#define KEY_EMAIL 215 -#define KEY_CHAT 216 -#define KEY_SEARCH 217 -#define KEY_CONNECT 218 -#define KEY_FINANCE 219 /* AL Checkbook/Finance */ -#define KEY_SPORT 220 -#define KEY_SHOP 221 -#define KEY_ALTERASE 222 -#define KEY_CANCEL 223 /* AC Cancel */ -#define KEY_BRIGHTNESSDOWN 224 -#define KEY_BRIGHTNESSUP 225 -#define KEY_MEDIA 226 - -#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video - outputs (Monitor/LCD/TV-out/etc) */ -#define KEY_KBDILLUMTOGGLE 228 -#define KEY_KBDILLUMDOWN 229 -#define KEY_KBDILLUMUP 230 - -#define KEY_SEND 231 /* AC Send */ -#define KEY_REPLY 232 /* AC Reply */ -#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ -#define KEY_SAVE 234 /* AC Save */ -#define KEY_DOCUMENTS 235 - -#define KEY_BATTERY 236 - -#define KEY_BLUETOOTH 237 -#define KEY_WLAN 238 -#define KEY_UWB 239 - -#define KEY_UNKNOWN 240 - -#define KEY_VIDEO_NEXT 241 /* drive next video source */ -#define KEY_VIDEO_PREV 242 /* drive previous video source */ -#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ -#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual - brightness control is off, - rely on ambient */ -#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO -#define KEY_DISPLAY_OFF 245 /* display device to off state */ - -#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ -#define KEY_WIMAX KEY_WWAN -#define KEY_RFKILL 247 /* Key that controls all radios */ - -#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ - -/* Code 255 is reserved for special needs of AT keyboard driver */ - -#define BTN_MISC 0x100 -#define BTN_0 0x100 -#define BTN_1 0x101 -#define BTN_2 0x102 -#define BTN_3 0x103 -#define BTN_4 0x104 -#define BTN_5 0x105 -#define BTN_6 0x106 -#define BTN_7 0x107 -#define BTN_8 0x108 -#define BTN_9 0x109 - -#define BTN_MOUSE 0x110 -#define BTN_LEFT 0x110 -#define BTN_RIGHT 0x111 -#define BTN_MIDDLE 0x112 -#define BTN_SIDE 0x113 -#define BTN_EXTRA 0x114 -#define BTN_FORWARD 0x115 -#define BTN_BACK 0x116 -#define BTN_TASK 0x117 - -#define BTN_JOYSTICK 0x120 -#define BTN_TRIGGER 0x120 -#define BTN_THUMB 0x121 -#define BTN_THUMB2 0x122 -#define BTN_TOP 0x123 -#define BTN_TOP2 0x124 -#define BTN_PINKIE 0x125 -#define BTN_BASE 0x126 -#define BTN_BASE2 0x127 -#define BTN_BASE3 0x128 -#define BTN_BASE4 0x129 -#define BTN_BASE5 0x12a -#define BTN_BASE6 0x12b -#define BTN_DEAD 0x12f - -#define BTN_GAMEPAD 0x130 -#define BTN_SOUTH 0x130 -#define BTN_A BTN_SOUTH -#define BTN_EAST 0x131 -#define BTN_B BTN_EAST -#define BTN_C 0x132 -#define BTN_NORTH 0x133 -#define BTN_X BTN_NORTH -#define BTN_WEST 0x134 -#define BTN_Y BTN_WEST -#define BTN_Z 0x135 -#define BTN_TL 0x136 -#define BTN_TR 0x137 -#define BTN_TL2 0x138 -#define BTN_TR2 0x139 -#define BTN_SELECT 0x13a -#define BTN_START 0x13b -#define BTN_MODE 0x13c -#define BTN_THUMBL 0x13d -#define BTN_THUMBR 0x13e - -#define BTN_DIGI 0x140 -#define BTN_TOOL_PEN 0x140 -#define BTN_TOOL_RUBBER 0x141 -#define BTN_TOOL_BRUSH 0x142 -#define BTN_TOOL_PENCIL 0x143 -#define BTN_TOOL_AIRBRUSH 0x144 -#define BTN_TOOL_FINGER 0x145 -#define BTN_TOOL_MOUSE 0x146 -#define BTN_TOOL_LENS 0x147 -#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ -#define BTN_TOUCH 0x14a -#define BTN_STYLUS 0x14b -#define BTN_STYLUS2 0x14c -#define BTN_TOOL_DOUBLETAP 0x14d -#define BTN_TOOL_TRIPLETAP 0x14e -#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ - -#define BTN_WHEEL 0x150 -#define BTN_GEAR_DOWN 0x150 -#define BTN_GEAR_UP 0x151 - -#define KEY_OK 0x160 -#define KEY_SELECT 0x161 -#define KEY_GOTO 0x162 -#define KEY_CLEAR 0x163 -#define KEY_POWER2 0x164 -#define KEY_OPTION 0x165 -#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ -#define KEY_TIME 0x167 -#define KEY_VENDOR 0x168 -#define KEY_ARCHIVE 0x169 -#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ -#define KEY_CHANNEL 0x16b -#define KEY_FAVORITES 0x16c -#define KEY_EPG 0x16d -#define KEY_PVR 0x16e /* Media Select Home */ -#define KEY_MHP 0x16f -#define KEY_LANGUAGE 0x170 -#define KEY_TITLE 0x171 -#define KEY_SUBTITLE 0x172 -#define KEY_ANGLE 0x173 -#define KEY_ZOOM 0x174 -#define KEY_MODE 0x175 -#define KEY_KEYBOARD 0x176 -#define KEY_SCREEN 0x177 -#define KEY_PC 0x178 /* Media Select Computer */ -#define KEY_TV 0x179 /* Media Select TV */ -#define KEY_TV2 0x17a /* Media Select Cable */ -#define KEY_VCR 0x17b /* Media Select VCR */ -#define KEY_VCR2 0x17c /* VCR Plus */ -#define KEY_SAT 0x17d /* Media Select Satellite */ -#define KEY_SAT2 0x17e -#define KEY_CD 0x17f /* Media Select CD */ -#define KEY_TAPE 0x180 /* Media Select Tape */ -#define KEY_RADIO 0x181 -#define KEY_TUNER 0x182 /* Media Select Tuner */ -#define KEY_PLAYER 0x183 -#define KEY_TEXT 0x184 -#define KEY_DVD 0x185 /* Media Select DVD */ -#define KEY_AUX 0x186 -#define KEY_MP3 0x187 -#define KEY_AUDIO 0x188 /* AL Audio Browser */ -#define KEY_VIDEO 0x189 /* AL Movie Browser */ -#define KEY_DIRECTORY 0x18a -#define KEY_LIST 0x18b -#define KEY_MEMO 0x18c /* Media Select Messages */ -#define KEY_CALENDAR 0x18d -#define KEY_RED 0x18e -#define KEY_GREEN 0x18f -#define KEY_YELLOW 0x190 -#define KEY_BLUE 0x191 -#define KEY_CHANNELUP 0x192 /* Channel Increment */ -#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ -#define KEY_FIRST 0x194 -#define KEY_LAST 0x195 /* Recall Last */ -#define KEY_AB 0x196 -#define KEY_NEXT 0x197 -#define KEY_RESTART 0x198 -#define KEY_SLOW 0x199 -#define KEY_SHUFFLE 0x19a -#define KEY_BREAK 0x19b -#define KEY_PREVIOUS 0x19c -#define KEY_DIGITS 0x19d -#define KEY_TEEN 0x19e -#define KEY_TWEN 0x19f -#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ -#define KEY_GAMES 0x1a1 /* Media Select Games */ -#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ -#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ -#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ -#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ -#define KEY_EDITOR 0x1a6 /* AL Text Editor */ -#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ -#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ -#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ -#define KEY_DATABASE 0x1aa /* AL Database App */ -#define KEY_NEWS 0x1ab /* AL Newsreader */ -#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ -#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ -#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ -#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ -#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE -#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ -#define KEY_LOGOFF 0x1b1 /* AL Logoff */ - -#define KEY_DOLLAR 0x1b2 -#define KEY_EURO 0x1b3 - -#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ -#define KEY_FRAMEFORWARD 0x1b5 -#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ -#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ -#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ -#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ -#define KEY_IMAGES 0x1ba /* AL Image Browser */ - -#define KEY_DEL_EOL 0x1c0 -#define KEY_DEL_EOS 0x1c1 -#define KEY_INS_LINE 0x1c2 -#define KEY_DEL_LINE 0x1c3 - -#define KEY_FN 0x1d0 -#define KEY_FN_ESC 0x1d1 -#define KEY_FN_F1 0x1d2 -#define KEY_FN_F2 0x1d3 -#define KEY_FN_F3 0x1d4 -#define KEY_FN_F4 0x1d5 -#define KEY_FN_F5 0x1d6 -#define KEY_FN_F6 0x1d7 -#define KEY_FN_F7 0x1d8 -#define KEY_FN_F8 0x1d9 -#define KEY_FN_F9 0x1da -#define KEY_FN_F10 0x1db -#define KEY_FN_F11 0x1dc -#define KEY_FN_F12 0x1dd -#define KEY_FN_1 0x1de -#define KEY_FN_2 0x1df -#define KEY_FN_D 0x1e0 -#define KEY_FN_E 0x1e1 -#define KEY_FN_F 0x1e2 -#define KEY_FN_S 0x1e3 -#define KEY_FN_B 0x1e4 - -#define KEY_BRL_DOT1 0x1f1 -#define KEY_BRL_DOT2 0x1f2 -#define KEY_BRL_DOT3 0x1f3 -#define KEY_BRL_DOT4 0x1f4 -#define KEY_BRL_DOT5 0x1f5 -#define KEY_BRL_DOT6 0x1f6 -#define KEY_BRL_DOT7 0x1f7 -#define KEY_BRL_DOT8 0x1f8 -#define KEY_BRL_DOT9 0x1f9 -#define KEY_BRL_DOT10 0x1fa - -#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ -#define KEY_NUMERIC_1 0x201 /* and other keypads */ -#define KEY_NUMERIC_2 0x202 -#define KEY_NUMERIC_3 0x203 -#define KEY_NUMERIC_4 0x204 -#define KEY_NUMERIC_5 0x205 -#define KEY_NUMERIC_6 0x206 -#define KEY_NUMERIC_7 0x207 -#define KEY_NUMERIC_8 0x208 -#define KEY_NUMERIC_9 0x209 -#define KEY_NUMERIC_STAR 0x20a -#define KEY_NUMERIC_POUND 0x20b - -#define KEY_CAMERA_FOCUS 0x210 -#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ - -#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ -#define KEY_TOUCHPAD_ON 0x213 -#define KEY_TOUCHPAD_OFF 0x214 - -#define KEY_CAMERA_ZOOMIN 0x215 -#define KEY_CAMERA_ZOOMOUT 0x216 -#define KEY_CAMERA_UP 0x217 -#define KEY_CAMERA_DOWN 0x218 -#define KEY_CAMERA_LEFT 0x219 -#define KEY_CAMERA_RIGHT 0x21a - -#define KEY_ATTENDANT_ON 0x21b -#define KEY_ATTENDANT_OFF 0x21c -#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ -#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ - -#define BTN_DPAD_UP 0x220 -#define BTN_DPAD_DOWN 0x221 -#define BTN_DPAD_LEFT 0x222 -#define BTN_DPAD_RIGHT 0x223 - -#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ - -#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ -#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ -#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ -#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ -#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ -#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ -#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ - -#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ -#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ - -#define KEY_KBDINPUTASSIST_PREV 0x260 -#define KEY_KBDINPUTASSIST_NEXT 0x261 -#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 -#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 -#define KEY_KBDINPUTASSIST_ACCEPT 0x264 -#define KEY_KBDINPUTASSIST_CANCEL 0x265 - -#define BTN_TRIGGER_HAPPY 0x2c0 -#define BTN_TRIGGER_HAPPY1 0x2c0 -#define BTN_TRIGGER_HAPPY2 0x2c1 -#define BTN_TRIGGER_HAPPY3 0x2c2 -#define BTN_TRIGGER_HAPPY4 0x2c3 -#define BTN_TRIGGER_HAPPY5 0x2c4 -#define BTN_TRIGGER_HAPPY6 0x2c5 -#define BTN_TRIGGER_HAPPY7 0x2c6 -#define BTN_TRIGGER_HAPPY8 0x2c7 -#define BTN_TRIGGER_HAPPY9 0x2c8 -#define BTN_TRIGGER_HAPPY10 0x2c9 -#define BTN_TRIGGER_HAPPY11 0x2ca -#define BTN_TRIGGER_HAPPY12 0x2cb -#define BTN_TRIGGER_HAPPY13 0x2cc -#define BTN_TRIGGER_HAPPY14 0x2cd -#define BTN_TRIGGER_HAPPY15 0x2ce -#define BTN_TRIGGER_HAPPY16 0x2cf -#define BTN_TRIGGER_HAPPY17 0x2d0 -#define BTN_TRIGGER_HAPPY18 0x2d1 -#define BTN_TRIGGER_HAPPY19 0x2d2 -#define BTN_TRIGGER_HAPPY20 0x2d3 -#define BTN_TRIGGER_HAPPY21 0x2d4 -#define BTN_TRIGGER_HAPPY22 0x2d5 -#define BTN_TRIGGER_HAPPY23 0x2d6 -#define BTN_TRIGGER_HAPPY24 0x2d7 -#define BTN_TRIGGER_HAPPY25 0x2d8 -#define BTN_TRIGGER_HAPPY26 0x2d9 -#define BTN_TRIGGER_HAPPY27 0x2da -#define BTN_TRIGGER_HAPPY28 0x2db -#define BTN_TRIGGER_HAPPY29 0x2dc -#define BTN_TRIGGER_HAPPY30 0x2dd -#define BTN_TRIGGER_HAPPY31 0x2de -#define BTN_TRIGGER_HAPPY32 0x2df -#define BTN_TRIGGER_HAPPY33 0x2e0 -#define BTN_TRIGGER_HAPPY34 0x2e1 -#define BTN_TRIGGER_HAPPY35 0x2e2 -#define BTN_TRIGGER_HAPPY36 0x2e3 -#define BTN_TRIGGER_HAPPY37 0x2e4 -#define BTN_TRIGGER_HAPPY38 0x2e5 -#define BTN_TRIGGER_HAPPY39 0x2e6 -#define BTN_TRIGGER_HAPPY40 0x2e7 - -/* We avoid low common keys in module aliases so they don't get huge. */ -#define KEY_MIN_INTERESTING KEY_MUTE -#define KEY_MAX 0x2ff -#define KEY_CNT (KEY_MAX+1) - -/* - * Relative axes - */ - -#define REL_X 0x00 -#define REL_Y 0x01 -#define REL_Z 0x02 -#define REL_RX 0x03 -#define REL_RY 0x04 -#define REL_RZ 0x05 -#define REL_HWHEEL 0x06 -#define REL_DIAL 0x07 -#define REL_WHEEL 0x08 -#define REL_MISC 0x09 -#define REL_MAX 0x0f -#define REL_CNT (REL_MAX+1) - -/* - * Absolute axes - */ - -#define ABS_X 0x00 -#define ABS_Y 0x01 -#define ABS_Z 0x02 -#define ABS_RX 0x03 -#define ABS_RY 0x04 -#define ABS_RZ 0x05 -#define ABS_THROTTLE 0x06 -#define ABS_RUDDER 0x07 -#define ABS_WHEEL 0x08 -#define ABS_GAS 0x09 -#define ABS_BRAKE 0x0a -#define ABS_HAT0X 0x10 -#define ABS_HAT0Y 0x11 -#define ABS_HAT1X 0x12 -#define ABS_HAT1Y 0x13 -#define ABS_HAT2X 0x14 -#define ABS_HAT2Y 0x15 -#define ABS_HAT3X 0x16 -#define ABS_HAT3Y 0x17 -#define ABS_PRESSURE 0x18 -#define ABS_DISTANCE 0x19 -#define ABS_TILT_X 0x1a -#define ABS_TILT_Y 0x1b -#define ABS_TOOL_WIDTH 0x1c - -#define ABS_VOLUME 0x20 - -#define ABS_MISC 0x28 - -#define ABS_MT_SLOT 0x2f /* MT slot being modified */ -#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ -#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ -#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ -#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ -#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ -#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ -#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ -#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ -#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ -#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ -#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ -#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ - - -#define ABS_MAX 0x3f -#define ABS_CNT (ABS_MAX+1) - -/* - * Switch events - */ - -#define SW_LID 0x00 /* set = lid shut */ -#define SW_TABLET_MODE 0x01 /* set = tablet mode */ -#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ -#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" - set = radio enabled */ -#define SW_RADIO SW_RFKILL_ALL /* deprecated */ -#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ -#define SW_DOCK 0x05 /* set = plugged into dock */ -#define SW_LINEOUT_INSERT 0x06 /* set = inserted */ -#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */ -#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */ -#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */ -#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */ -#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ -#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ -#define SW_LINEIN_INSERT 0x0d /* set = inserted */ -#define SW_HPHL_OVERCURRENT 0x0e /* set = over current on left hph */ -#define SW_HPHR_OVERCURRENT 0x0f /* set = over current on right hph */ -#define SW_UNSUPPORT_INSERT 0x10 /* set = unsupported device inserted */ -#define SW_MICROPHONE2_INSERT 0x11 /* set = inserted */ -#define SW_MUTE_DEVICE 0x12 /* set = device disabled */ -#define SW_MAX 0x20 -#define SW_CNT (SW_MAX+1) - -/* - * Misc events - */ - -#define MSC_SERIAL 0x00 -#define MSC_PULSELED 0x01 -#define MSC_GESTURE 0x02 -#define MSC_RAW 0x03 -#define MSC_SCAN 0x04 -#define MSC_TIMESTAMP 0x05 -#define MSC_MAX 0x07 -#define MSC_CNT (MSC_MAX+1) - -/* - * LEDs - */ - -#define LED_NUML 0x00 -#define LED_CAPSL 0x01 -#define LED_SCROLLL 0x02 -#define LED_COMPOSE 0x03 -#define LED_KANA 0x04 -#define LED_SLEEP 0x05 -#define LED_SUSPEND 0x06 -#define LED_MUTE 0x07 -#define LED_MISC 0x08 -#define LED_MAIL 0x09 -#define LED_CHARGING 0x0a -#define LED_MAX 0x0f -#define LED_CNT (LED_MAX+1) - -/* - * Autorepeat values - */ - -#define REP_DELAY 0x00 -#define REP_PERIOD 0x01 -#define REP_MAX 0x01 -#define REP_CNT (REP_MAX+1) - -/* - * Sounds - */ - -#define SND_CLICK 0x00 -#define SND_BELL 0x01 -#define SND_TONE 0x02 -#define SND_MAX 0x07 -#define SND_CNT (SND_MAX+1) - /* * IDs. */ -- GitLab From ac53c2e8b8c13fc9c920e897e321d14e06affc8c Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 2 Nov 2017 19:10:22 +0530 Subject: [PATCH 1554/5498] msm: ipa: Fix to use GFP_DMA flag IPAv2 hardware works with 32 bit addressing, so allocate a kernel memory using GFP_DMA flag which is processed by IPA hardware. Change-Id: I6f35e7f2179dc48f718221f6a3d228aca67c4154 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa.c | 5 +++-- drivers/platform/msm/ipa/ipa_v2/ipa_utils.c | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index 22bb8e0b2fc1..2dea5e5818b3 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -1980,6 +1980,7 @@ static int ipa_q6_set_ex_path_dis_agg(void) int index; struct ipa_register_write *reg_write; int retval; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); desc = kcalloc(ipa_ctx->ipa_num_pipes, sizeof(struct ipa_desc), GFP_KERNEL); @@ -1997,7 +1998,7 @@ static int ipa_q6_set_ex_path_dis_agg(void) if (ipa_ctx->ep[ep_idx].valid && ipa_ctx->ep[ep_idx].skip_ep_cfg) { BUG_ON(num_descs >= ipa_ctx->ipa_num_pipes); - reg_write = kzalloc(sizeof(*reg_write), GFP_KERNEL); + reg_write = kzalloc(sizeof(*reg_write), flag); if (!reg_write) { IPAERR("failed to allocate memory\n"); @@ -2030,7 +2031,7 @@ static int ipa_q6_set_ex_path_dis_agg(void) continue; if (IPA_CLIENT_IS_Q6_NON_ZIP_CONS(client_idx) || IPA_CLIENT_IS_Q6_ZIP_CONS(client_idx)) { - reg_write = kzalloc(sizeof(*reg_write), GFP_KERNEL); + reg_write = kzalloc(sizeof(*reg_write), flag); if (!reg_write) { IPAERR("failed to allocate memory\n"); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 7f0dde425f79..f354ced5c509 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -4591,7 +4591,7 @@ int ipa_tag_process(struct ipa_desc desc[], } /* IP_PACKET_INIT IC for tag status to be sent to apps */ - pkt_init = kzalloc(sizeof(*pkt_init), GFP_KERNEL); + pkt_init = kzalloc(sizeof(*pkt_init), flag); if (!pkt_init) { IPAERR("failed to allocate memory\n"); res = -ENOMEM; @@ -4610,7 +4610,7 @@ int ipa_tag_process(struct ipa_desc desc[], desc_idx++; /* NO-OP IC for ensuring that IPA pipeline is empty */ - reg_write_nop = kzalloc(sizeof(*reg_write_nop), GFP_KERNEL); + reg_write_nop = kzalloc(sizeof(*reg_write_nop), flag); if (!reg_write_nop) { IPAERR("no mem\n"); res = -ENOMEM; @@ -4629,7 +4629,7 @@ int ipa_tag_process(struct ipa_desc desc[], desc_idx++; /* status IC */ - status = kzalloc(sizeof(*status), GFP_KERNEL); + status = kzalloc(sizeof(*status), flag); if (!status) { IPAERR("no mem\n"); res = -ENOMEM; @@ -4665,7 +4665,7 @@ int ipa_tag_process(struct ipa_desc desc[], atomic_set(&comp->cnt, 2); /* dummy packet to send to IPA. packet payload is a completion object */ - dummy_skb = alloc_skb(sizeof(comp), GFP_KERNEL); + dummy_skb = alloc_skb(sizeof(comp), flag); if (!dummy_skb) { IPAERR("failed to allocate memory\n"); res = -ENOMEM; -- GitLab From f085b3595fcfe7ceb31fc5e8b24865c937195ff7 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Thu, 16 Nov 2017 17:00:09 +0530 Subject: [PATCH 1555/5498] usb: dwc3: Fix incorrect ep0 state on reset According to the databook ep0 should be in setup phase during reset. If host issues reset between control transfers, ep0 will be in an invalid state. Fix this my issuing stall and restart on ep0 if it is not in setup phase. CRs-Fixed: 2136658 Change-Id: I6dc20c2735a6ce772533ccb5b63ba5d1b01f89d7 Signed-off-by: Sriharsha Allenki --- drivers/usb/dwc3/ep0.c | 2 +- drivers/usb/dwc3/gadget.c | 9 +++++++++ drivers/usb/dwc3/gadget.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index ce242d26f5c4..68db80966a1d 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -267,7 +267,7 @@ out: return ret; } -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { struct dwc3_ep *dep; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 55b962c6c429..37815aae8cb6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3001,6 +3001,15 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->test_mode = false; + /* + * From SNPS databook section 8.1.2 + * the EP0 should be in setup phase. So ensure + * that EP0 is in setup phase by issuing a stall + * and restart if EP0 is not in setup phase. + */ + if (dwc->ep0state != EP0_SETUP_PHASE) + dwc3_ep0_stall_and_restart(dwc); + dwc3_stop_active_transfers(dwc); dwc3_clear_stall_all_ep(dwc); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index a21962c8f513..ca943d18a9ad 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -98,6 +98,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc); int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, -- GitLab From 82b4a626e5d44c8c1e92e4ef401af0e0d461542d Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Thu, 16 Nov 2017 18:53:37 +0530 Subject: [PATCH 1556/5498] usb: gadget: Fix mismatched function wake notification When the device wants to wake up a function while the bus is in U3, it requires that it wakes up the bus into U0 before sending the function wake notification. Currently, our logic is as below...consider a 2 interface composition as an example: 1. Interface#1 needs to send something to host after the function was suspended (FUNC_SUSPEND(3)) 2. Calls usb_func_wakeup() to attempt to notify function wake 3. Since bus is in U3, issue wakeup to transistion bus to U0 4. Once receiving link state change event into U0, we will call composite_resume() 5. Composite_resume() will queue the function wake for the FIRST interface as well that has function remote wakeup enabled and function suspended(ie if both interfaces#0 and #1 can wake up the function, then it will ALWAYS send it for interface#0). So even though interface#1 generated the function wakeup sequence, the SW currently sends it for interface#0. Fix this by using the func_wakeup_pending flag and initiate the function wakeup only if function wakeup is pending for that specific function. CRs-Fixed: 2122935 Change-Id: I4776912fb9f8569d4d39007d68e1afafcb0dce43 Signed-off-by: Sriharsha Allenki --- drivers/usb/dwc3/gadget.c | 2 +- drivers/usb/gadget/composite.c | 43 ++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 55b962c6c429..4a3fee71d16a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1883,7 +1883,7 @@ static int dwc_gadget_func_wakeup(struct usb_gadget *g, int interface_id) if (dwc3_gadget_is_suspended(dwc)) { pr_debug("USB bus is suspended. Scheduling wakeup and returning -EAGAIN.\n"); dwc3_gadget_wakeup(&dwc->gadget); - return -EAGAIN; + return -EACCES; } /* diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 22b0b9a42f91..66d4449f1602 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -413,11 +413,17 @@ int usb_func_wakeup(struct usb_function *func) spin_lock_irqsave(&func->config->cdev->lock, flags); ret = usb_func_wakeup_int(func); - if (ret == -EAGAIN) { + if (ret == -EACCES) { DBG(func->config->cdev, "Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n", func->name ? func->name : ""); ret = 0; + func->func_wakeup_pending = 1; + } else if (ret == -EAGAIN) { + DBG(func->config->cdev, + "Function wakeup for %s sent.\n", + func->name ? func->name : ""); + ret = 0; } else if (ret < 0 && ret != -ENOTSUPP) { ERROR(func->config->cdev, "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n", @@ -448,7 +454,12 @@ int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep, if (func->func_is_suspended && func->func_wakeup_allowed) { ret = usb_gadget_func_wakeup(gadget, func->intf_id); - if (ret == -EAGAIN) { + if (ret == -EACCES) { + pr_debug("bus suspended func wakeup for %s delayed until bus resume.\n", + func->name ? func->name : ""); + func->func_wakeup_pending = 1; + ret = -EAGAIN; + } else if (ret == -EAGAIN) { pr_debug("bus suspended func wakeup for %s delayed until bus resume.\n", func->name ? func->name : ""); } else if (ret < 0 && ret != -ENOTSUPP) { @@ -2332,22 +2343,24 @@ composite_resume(struct usb_gadget *gadget) spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) { list_for_each_entry(f, &cdev->config->functions, list) { - ret = usb_func_wakeup_int(f); - if (ret) { - if (ret == -EAGAIN) { - ERROR(f->config->cdev, - "Function wakeup for %s could not complete due to suspend state.\n", - f->name ? f->name : ""); - break; - } else if (ret != -ENOTSUPP) { - ERROR(f->config->cdev, - "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n", - f->name ? f->name : "", - ret); + if (f->func_wakeup_pending) { + ret = usb_func_wakeup_int(f); + if (ret) { + if (ret == -EAGAIN) { + ERROR(f->config->cdev, + "Function wakeup for %s could not complete due to suspend state.\n", + f->name ? f->name : ""); + } else if (ret != -ENOTSUPP) { + ERROR(f->config->cdev, + "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n", + f->name ? f->name : "", + ret); + } } + f->func_wakeup_pending = 0; } - if (f->resume) + if (gadget->speed != USB_SPEED_SUPER && f->resume) f->resume(f); } -- GitLab From 30cd7c3c8c54ee523497e37c05e4f53fd8802634 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 19 Mar 2015 19:03:25 +0100 Subject: [PATCH 1557/5498] UPSTREAM: ARM: 8318/1: treat CPU feature register fields as signed quantities The various CPU feature registers consist of 4-bit blocks that represent signed quantities, whose positive values represent incremental features, and whose negative values are reserved. To improve forward compatibility, update the feature detection code to take possible future higher values into account, but ignore negative values. Signed-off-by: Ard Biesheuvel Signed-off-by: Russell King (cherry picked from commit b8c9592b4a6c93211c8163888a97880d608503b5) Change-Id: Ib43b6b4a24015704ed57bbe536af8ad07530e246 Signed-off-by: Eric Biggers --- arch/arm/include/asm/cputype.h | 16 ++++++++++++++++ arch/arm/kernel/setup.c | 22 +++++++++------------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index 819777d0e91f..85e374f873ac 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -253,4 +253,20 @@ static inline int cpu_is_pj4(void) #else #define cpu_is_pj4() 0 #endif + +static inline int __attribute_const__ cpuid_feature_extract_field(u32 features, + int field) +{ + int feature = (features >> field) & 15; + + /* feature registers are signed values */ + if (feature > 8) + feature -= 16; + + return feature; +} + +#define cpuid_feature_extract(reg, field) \ + cpuid_feature_extract_field(read_cpuid_ext(reg), field) + #endif diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 1ca80a3d30e4..6d6c5871ef2f 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -374,30 +374,26 @@ void __init early_print(const char *str, ...) static void __init cpuid_init_hwcaps(void) { - unsigned int divide_instrs, vmsa; + int block; if (cpu_architecture() < CPU_ARCH_ARMv7) return; - divide_instrs = (read_cpuid_ext(CPUID_EXT_ISAR0) & 0x0f000000) >> 24; - - switch (divide_instrs) { - case 2: + block = cpuid_feature_extract(CPUID_EXT_ISAR0, 24); + if (block >= 2) elf_hwcap |= HWCAP_IDIVA; - case 1: + if (block >= 1) elf_hwcap |= HWCAP_IDIVT; - } /* LPAE implies atomic ldrd/strd instructions */ - vmsa = (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xf) >> 0; - if (vmsa >= 5) + block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0); + if (block >= 5) elf_hwcap |= HWCAP_LPAE; } static void __init elf_hwcap_fixup(void) { unsigned id = read_cpuid_id(); - unsigned sync_prim; /* * HWCAP_TLS is available only on 1136 r1p0 and later, @@ -418,9 +414,9 @@ static void __init elf_hwcap_fixup(void) * avoid advertising SWP; it may not be atomic with * multiprocessing cores. */ - sync_prim = ((read_cpuid_ext(CPUID_EXT_ISAR3) >> 8) & 0xf0) | - ((read_cpuid_ext(CPUID_EXT_ISAR4) >> 20) & 0x0f); - if (sync_prim >= 0x13) + if (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) > 1 || + (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) == 1 && + cpuid_feature_extract(CPUID_EXT_ISAR3, 20) >= 3)) elf_hwcap &= ~HWCAP_SWP; } -- GitLab From 145ab1f4d09a7af1220241c5452c5254cbdd9780 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 19 Mar 2015 19:04:05 +0100 Subject: [PATCH 1558/5498] UPSTREAM: ARM: 8319/1: advertise availability of v8 Crypto instructions When running the 32-bit ARM kernel on ARMv8 capable bare metal (e.g., 32-bit Android userland and kernel on a Cortex-A53), or as a KVM guest on a 64-bit host, we should advertise the availability of the Crypto instructions, so that userland libraries such as OpenSSL may use them. (Support for the v8 Crypto instructions in the 32-bit build was added to OpenSSL more than six months ago) This adds the ID feature bit detection, and sets elf_hwcap2 accordingly. Signed-off-by: Ard Biesheuvel Signed-off-by: Russell King (cherry picked from commit a092aedb8115c16cb49bc64dd09cb20471ff942b) Change-Id: I14b20ebdaad29812fbbdb7bfc3661531950a54aa Signed-off-by: Eric Biggers --- arch/arm/kernel/setup.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 6d6c5871ef2f..c29ae862f2b7 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -375,6 +375,7 @@ void __init early_print(const char *str, ...) static void __init cpuid_init_hwcaps(void) { int block; + u32 isar5; if (cpu_architecture() < CPU_ARCH_ARMv7) return; @@ -389,6 +390,27 @@ static void __init cpuid_init_hwcaps(void) block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0); if (block >= 5) elf_hwcap |= HWCAP_LPAE; + + /* check for supported v8 Crypto instructions */ + isar5 = read_cpuid_ext(CPUID_EXT_ISAR5); + + block = cpuid_feature_extract_field(isar5, 4); + if (block >= 2) + elf_hwcap2 |= HWCAP2_PMULL; + if (block >= 1) + elf_hwcap2 |= HWCAP2_AES; + + block = cpuid_feature_extract_field(isar5, 8); + if (block >= 1) + elf_hwcap2 |= HWCAP2_SHA1; + + block = cpuid_feature_extract_field(isar5, 12); + if (block >= 1) + elf_hwcap2 |= HWCAP2_SHA2; + + block = cpuid_feature_extract_field(isar5, 16); + if (block >= 1) + elf_hwcap2 |= HWCAP2_CRC32; } static void __init elf_hwcap_fixup(void) -- GitLab From 2aa408b18c3b2c25408c645e0970bf31f5dac859 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Tue, 19 Apr 2016 12:35:20 +0100 Subject: [PATCH 1559/5498] UPSTREAM: ARM: 8563/1: fix demoting HWCAP_SWP Commit b8c9592 "ARM: 8318/1: treat CPU feature register fields as signed quantities" accidentally altered cpuid register used to demote HWCAP_SWP. ARM ARM says that SyncPrim_instrs bits in ID_ISAR3 should be used with SynchPrim_instrs_frac from ID_ISAR4. So, follow this rule. Signed-off-by: Vladimir Murzin Acked-by: Ard Biesheuvel Signed-off-by: Russell King (cherry picked from commit 03f1217e5fafac8eb9e28aa8d04a67b6db1e435b) Change-Id: Iaec80be5c2ec4f46421a78dae4521f7bca22a2ab Signed-off-by: Eric Biggers --- arch/arm/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index c29ae862f2b7..4146a5555139 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -438,7 +438,7 @@ static void __init elf_hwcap_fixup(void) */ if (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) > 1 || (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) == 1 && - cpuid_feature_extract(CPUID_EXT_ISAR3, 20) >= 3)) + cpuid_feature_extract(CPUID_EXT_ISAR4, 20) >= 3)) elf_hwcap &= ~HWCAP_SWP; } -- GitLab From 57545cc3e0ad6001738c46fbd624a0b150de27fd Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Tue, 19 Apr 2016 12:35:55 +0100 Subject: [PATCH 1560/5498] UPSTREAM: ARM: 8564/1: fix cpu feature extracting helper Commit b8c9592 "ARM: 8318/1: treat CPU feature register fields as signed quantities" introduced helper to extract signed quantities of 4-bit blocks. However, with a current code feature with value 0b1000 isn't rejected as negative. So fix the "if" condition. Reported-by: Jonathan Brawn Signed-off-by: Vladimir Murzin Acked-by: Ard Biesheuvel Signed-off-by: Russell King (cherry picked from commit ac36a881b72a1e9831f1c59abf935868c90685cf) Change-Id: I7745736681331f8f18740675044612ad538c2ce3 Signed-off-by: Eric Biggers --- arch/arm/include/asm/cputype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index 85e374f873ac..0f47054d3ad0 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -260,7 +260,7 @@ static inline int __attribute_const__ cpuid_feature_extract_field(u32 features, int feature = (features >> field) & 15; /* feature registers are signed values */ - if (feature > 8) + if (feature > 7) feature -= 16; return feature; -- GitLab From 2a2ea6ca1f7521f563db0469184096bdd76cd347 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 10 Mar 2015 09:47:47 +0100 Subject: [PATCH 1561/5498] BACKPORT: crypto: arm - AES in ECB/CBC/CTR/XTS modes using ARMv8 Crypto Extensions This implements the ECB, CBC, CTR and XTS asynchronous block ciphers using the AArch32 versions of the ARMv8 Crypto Extensions for AES. Signed-off-by: Ard Biesheuvel Signed-off-by: Herbert Xu (cherry-picked from commit 86464859cc77ecfd989ad5c912bef167b1128b0b) Change-Id: I5d666b976345633243af9bfb690118895a319249 Signed-off-by: Eric Biggers --- arch/arm/crypto/Makefile | 2 + arch/arm/crypto/aes-ce-core.S | 518 +++++++++++++++++++++++++++++++++ arch/arm/crypto/aes-ce-glue.c | 520 ++++++++++++++++++++++++++++++++++ crypto/Kconfig | 9 + 4 files changed, 1049 insertions(+) create mode 100644 arch/arm/crypto/aes-ce-core.S create mode 100644 arch/arm/crypto/aes-ce-glue.c diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile index 2cee53b27238..2086c386a70e 100644 --- a/arch/arm/crypto/Makefile +++ b/arch/arm/crypto/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CRYPTO_AES_ARM) += aes-arm.o obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o +obj-$(CONFIG_CRYPTO_AES_ARM_CE) += aes-arm-ce.o obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o @@ -16,6 +17,7 @@ sha1-arm-neon-y := sha1-armv7-neon.o sha1_neon_glue.o sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o sha256-arm-y := sha256-core.o sha256_glue.o $(sha256-arm-neon-y) sha512-arm-neon-y := sha512-armv7-neon.o sha512_neon_glue.o +aes-arm-ce-y := aes-ce-core.o aes-ce-glue.o quiet_cmd_perl = PERL $@ cmd_perl = $(PERL) $(<) > $(@) diff --git a/arch/arm/crypto/aes-ce-core.S b/arch/arm/crypto/aes-ce-core.S new file mode 100644 index 000000000000..8cfa468ee570 --- /dev/null +++ b/arch/arm/crypto/aes-ce-core.S @@ -0,0 +1,518 @@ +/* + * aes-ce-core.S - AES in CBC/CTR/XTS mode using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + + .text + .fpu crypto-neon-fp-armv8 + .align 3 + + .macro enc_round, state, key + aese.8 \state, \key + aesmc.8 \state, \state + .endm + + .macro dec_round, state, key + aesd.8 \state, \key + aesimc.8 \state, \state + .endm + + .macro enc_dround, key1, key2 + enc_round q0, \key1 + enc_round q0, \key2 + .endm + + .macro dec_dround, key1, key2 + dec_round q0, \key1 + dec_round q0, \key2 + .endm + + .macro enc_fround, key1, key2, key3 + enc_round q0, \key1 + aese.8 q0, \key2 + veor q0, q0, \key3 + .endm + + .macro dec_fround, key1, key2, key3 + dec_round q0, \key1 + aesd.8 q0, \key2 + veor q0, q0, \key3 + .endm + + .macro enc_dround_3x, key1, key2 + enc_round q0, \key1 + enc_round q1, \key1 + enc_round q2, \key1 + enc_round q0, \key2 + enc_round q1, \key2 + enc_round q2, \key2 + .endm + + .macro dec_dround_3x, key1, key2 + dec_round q0, \key1 + dec_round q1, \key1 + dec_round q2, \key1 + dec_round q0, \key2 + dec_round q1, \key2 + dec_round q2, \key2 + .endm + + .macro enc_fround_3x, key1, key2, key3 + enc_round q0, \key1 + enc_round q1, \key1 + enc_round q2, \key1 + aese.8 q0, \key2 + aese.8 q1, \key2 + aese.8 q2, \key2 + veor q0, q0, \key3 + veor q1, q1, \key3 + veor q2, q2, \key3 + .endm + + .macro dec_fround_3x, key1, key2, key3 + dec_round q0, \key1 + dec_round q1, \key1 + dec_round q2, \key1 + aesd.8 q0, \key2 + aesd.8 q1, \key2 + aesd.8 q2, \key2 + veor q0, q0, \key3 + veor q1, q1, \key3 + veor q2, q2, \key3 + .endm + + .macro do_block, dround, fround + cmp r3, #12 @ which key size? + vld1.8 {q10-q11}, [ip]! + \dround q8, q9 + vld1.8 {q12-q13}, [ip]! + \dround q10, q11 + vld1.8 {q10-q11}, [ip]! + \dround q12, q13 + vld1.8 {q12-q13}, [ip]! + \dround q10, q11 + blo 0f @ AES-128: 10 rounds + vld1.8 {q10-q11}, [ip]! + beq 1f @ AES-192: 12 rounds + \dround q12, q13 + vld1.8 {q12-q13}, [ip] + \dround q10, q11 +0: \fround q12, q13, q14 + bx lr + +1: \dround q12, q13 + \fround q10, q11, q14 + bx lr + .endm + + /* + * Internal, non-AAPCS compliant functions that implement the core AES + * transforms. These should preserve all registers except q0 - q2 and ip + * Arguments: + * q0 : first in/output block + * q1 : second in/output block (_3x version only) + * q2 : third in/output block (_3x version only) + * q8 : first round key + * q9 : secound round key + * ip : address of 3rd round key + * q14 : final round key + * r3 : number of rounds + */ + .align 6 +aes_encrypt: + add ip, r2, #32 @ 3rd round key +.Laes_encrypt_tweak: + do_block enc_dround, enc_fround +ENDPROC(aes_encrypt) + + .align 6 +aes_decrypt: + add ip, r2, #32 @ 3rd round key + do_block dec_dround, dec_fround +ENDPROC(aes_decrypt) + + .align 6 +aes_encrypt_3x: + add ip, r2, #32 @ 3rd round key + do_block enc_dround_3x, enc_fround_3x +ENDPROC(aes_encrypt_3x) + + .align 6 +aes_decrypt_3x: + add ip, r2, #32 @ 3rd round key + do_block dec_dround_3x, dec_fround_3x +ENDPROC(aes_decrypt_3x) + + .macro prepare_key, rk, rounds + add ip, \rk, \rounds, lsl #4 + vld1.8 {q8-q9}, [\rk] @ load first 2 round keys + vld1.8 {q14}, [ip] @ load last round key + .endm + + /* + * aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks) + * aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks) + */ +ENTRY(ce_aes_ecb_encrypt) + push {r4, lr} + ldr r4, [sp, #8] + prepare_key r2, r3 +.Lecbencloop3x: + subs r4, r4, #3 + bmi .Lecbenc1x + vld1.8 {q0-q1}, [r1, :64]! + vld1.8 {q2}, [r1, :64]! + bl aes_encrypt_3x + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + b .Lecbencloop3x +.Lecbenc1x: + adds r4, r4, #3 + beq .Lecbencout +.Lecbencloop: + vld1.8 {q0}, [r1, :64]! + bl aes_encrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lecbencloop +.Lecbencout: + pop {r4, pc} +ENDPROC(ce_aes_ecb_encrypt) + +ENTRY(ce_aes_ecb_decrypt) + push {r4, lr} + ldr r4, [sp, #8] + prepare_key r2, r3 +.Lecbdecloop3x: + subs r4, r4, #3 + bmi .Lecbdec1x + vld1.8 {q0-q1}, [r1, :64]! + vld1.8 {q2}, [r1, :64]! + bl aes_decrypt_3x + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + b .Lecbdecloop3x +.Lecbdec1x: + adds r4, r4, #3 + beq .Lecbdecout +.Lecbdecloop: + vld1.8 {q0}, [r1, :64]! + bl aes_decrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lecbdecloop +.Lecbdecout: + pop {r4, pc} +ENDPROC(ce_aes_ecb_decrypt) + + /* + * aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks, u8 iv[]) + * aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks, u8 iv[]) + */ +ENTRY(ce_aes_cbc_encrypt) + push {r4-r6, lr} + ldrd r4, r5, [sp, #16] + vld1.8 {q0}, [r5] + prepare_key r2, r3 +.Lcbcencloop: + vld1.8 {q1}, [r1, :64]! @ get next pt block + veor q0, q0, q1 @ ..and xor with iv + bl aes_encrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lcbcencloop + vst1.8 {q0}, [r5] + pop {r4-r6, pc} +ENDPROC(ce_aes_cbc_encrypt) + +ENTRY(ce_aes_cbc_decrypt) + push {r4-r6, lr} + ldrd r4, r5, [sp, #16] + vld1.8 {q6}, [r5] @ keep iv in q6 + prepare_key r2, r3 +.Lcbcdecloop3x: + subs r4, r4, #3 + bmi .Lcbcdec1x + vld1.8 {q0-q1}, [r1, :64]! + vld1.8 {q2}, [r1, :64]! + vmov q3, q0 + vmov q4, q1 + vmov q5, q2 + bl aes_decrypt_3x + veor q0, q0, q6 + veor q1, q1, q3 + veor q2, q2, q4 + vmov q6, q5 + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + b .Lcbcdecloop3x +.Lcbcdec1x: + adds r4, r4, #3 + beq .Lcbcdecout + vmov q15, q14 @ preserve last round key +.Lcbcdecloop: + vld1.8 {q0}, [r1, :64]! @ get next ct block + veor q14, q15, q6 @ combine prev ct with last key + vmov q6, q0 + bl aes_decrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lcbcdecloop +.Lcbcdecout: + vst1.8 {q6}, [r5] @ keep iv in q6 + pop {r4-r6, pc} +ENDPROC(ce_aes_cbc_decrypt) + + /* + * aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks, u8 ctr[]) + */ +ENTRY(ce_aes_ctr_encrypt) + push {r4-r6, lr} + ldrd r4, r5, [sp, #16] + vld1.8 {q6}, [r5] @ load ctr + prepare_key r2, r3 + vmov r6, s27 @ keep swabbed ctr in r6 + rev r6, r6 + cmn r6, r4 @ 32 bit overflow? + bcs .Lctrloop +.Lctrloop3x: + subs r4, r4, #3 + bmi .Lctr1x + add r6, r6, #1 + vmov q0, q6 + vmov q1, q6 + rev ip, r6 + add r6, r6, #1 + vmov q2, q6 + vmov s7, ip + rev ip, r6 + add r6, r6, #1 + vmov s11, ip + vld1.8 {q3-q4}, [r1, :64]! + vld1.8 {q5}, [r1, :64]! + bl aes_encrypt_3x + veor q0, q0, q3 + veor q1, q1, q4 + veor q2, q2, q5 + rev ip, r6 + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + vmov s27, ip + b .Lctrloop3x +.Lctr1x: + adds r4, r4, #3 + beq .Lctrout +.Lctrloop: + vmov q0, q6 + bl aes_encrypt + subs r4, r4, #1 + bmi .Lctrhalfblock @ blocks < 0 means 1/2 block + vld1.8 {q3}, [r1, :64]! + veor q3, q0, q3 + vst1.8 {q3}, [r0, :64]! + + adds r6, r6, #1 @ increment BE ctr + rev ip, r6 + vmov s27, ip + bcs .Lctrcarry + teq r4, #0 + bne .Lctrloop +.Lctrout: + vst1.8 {q6}, [r5] + pop {r4-r6, pc} + +.Lctrhalfblock: + vld1.8 {d1}, [r1, :64] + veor d0, d0, d1 + vst1.8 {d0}, [r0, :64] + pop {r4-r6, pc} + +.Lctrcarry: + .irp sreg, s26, s25, s24 + vmov ip, \sreg @ load next word of ctr + rev ip, ip @ ... to handle the carry + adds ip, ip, #1 + rev ip, ip + vmov \sreg, ip + bcc 0f + .endr +0: teq r4, #0 + beq .Lctrout + b .Lctrloop +ENDPROC(ce_aes_ctr_encrypt) + + /* + * aes_xts_encrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, + * int blocks, u8 iv[], u8 const rk2[], int first) + * aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, + * int blocks, u8 iv[], u8 const rk2[], int first) + */ + + .macro next_tweak, out, in, const, tmp + vshr.s64 \tmp, \in, #63 + vand \tmp, \tmp, \const + vadd.u64 \out, \in, \in + vext.8 \tmp, \tmp, \tmp, #8 + veor \out, \out, \tmp + .endm + + .align 3 +.Lxts_mul_x: + .quad 1, 0x87 + +ce_aes_xts_init: + vldr d14, .Lxts_mul_x + vldr d15, .Lxts_mul_x + 8 + + ldrd r4, r5, [sp, #16] @ load args + ldr r6, [sp, #28] + vld1.8 {q0}, [r5] @ load iv + teq r6, #1 @ start of a block? + bxne lr + + @ Encrypt the IV in q0 with the second AES key. This should only + @ be done at the start of a block. + ldr r6, [sp, #24] @ load AES key 2 + prepare_key r6, r3 + add ip, r6, #32 @ 3rd round key of key 2 + b .Laes_encrypt_tweak @ tail call +ENDPROC(ce_aes_xts_init) + +ENTRY(ce_aes_xts_encrypt) + push {r4-r6, lr} + + bl ce_aes_xts_init @ run shared prologue + prepare_key r2, r3 + vmov q3, q0 + + teq r6, #0 @ start of a block? + bne .Lxtsenc3x + +.Lxtsencloop3x: + next_tweak q3, q3, q7, q6 +.Lxtsenc3x: + subs r4, r4, #3 + bmi .Lxtsenc1x + vld1.8 {q0-q1}, [r1, :64]! @ get 3 pt blocks + vld1.8 {q2}, [r1, :64]! + next_tweak q4, q3, q7, q6 + veor q0, q0, q3 + next_tweak q5, q4, q7, q6 + veor q1, q1, q4 + veor q2, q2, q5 + bl aes_encrypt_3x + veor q0, q0, q3 + veor q1, q1, q4 + veor q2, q2, q5 + vst1.8 {q0-q1}, [r0, :64]! @ write 3 ct blocks + vst1.8 {q2}, [r0, :64]! + vmov q3, q5 + teq r4, #0 + beq .Lxtsencout + b .Lxtsencloop3x +.Lxtsenc1x: + adds r4, r4, #3 + beq .Lxtsencout +.Lxtsencloop: + vld1.8 {q0}, [r1, :64]! + veor q0, q0, q3 + bl aes_encrypt + veor q0, q0, q3 + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + beq .Lxtsencout + next_tweak q3, q3, q7, q6 + b .Lxtsencloop +.Lxtsencout: + vst1.8 {q3}, [r5] + pop {r4-r6, pc} +ENDPROC(ce_aes_xts_encrypt) + + +ENTRY(ce_aes_xts_decrypt) + push {r4-r6, lr} + + bl ce_aes_xts_init @ run shared prologue + prepare_key r2, r3 + vmov q3, q0 + + teq r6, #0 @ start of a block? + bne .Lxtsdec3x + +.Lxtsdecloop3x: + next_tweak q3, q3, q7, q6 +.Lxtsdec3x: + subs r4, r4, #3 + bmi .Lxtsdec1x + vld1.8 {q0-q1}, [r1, :64]! @ get 3 ct blocks + vld1.8 {q2}, [r1, :64]! + next_tweak q4, q3, q7, q6 + veor q0, q0, q3 + next_tweak q5, q4, q7, q6 + veor q1, q1, q4 + veor q2, q2, q5 + bl aes_decrypt_3x + veor q0, q0, q3 + veor q1, q1, q4 + veor q2, q2, q5 + vst1.8 {q0-q1}, [r0, :64]! @ write 3 pt blocks + vst1.8 {q2}, [r0, :64]! + vmov q3, q5 + teq r4, #0 + beq .Lxtsdecout + b .Lxtsdecloop3x +.Lxtsdec1x: + adds r4, r4, #3 + beq .Lxtsdecout +.Lxtsdecloop: + vld1.8 {q0}, [r1, :64]! + veor q0, q0, q3 + add ip, r2, #32 @ 3rd round key + bl aes_decrypt + veor q0, q0, q3 + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + beq .Lxtsdecout + next_tweak q3, q3, q7, q6 + b .Lxtsdecloop +.Lxtsdecout: + vst1.8 {q3}, [r5] + pop {r4-r6, pc} +ENDPROC(ce_aes_xts_decrypt) + + /* + * u32 ce_aes_sub(u32 input) - use the aese instruction to perform the + * AES sbox substitution on each byte in + * 'input' + */ +ENTRY(ce_aes_sub) + vdup.32 q1, r0 + veor q0, q0, q0 + aese.8 q0, q1 + vmov r0, s0 + bx lr +ENDPROC(ce_aes_sub) + + /* + * void ce_aes_invert(u8 *dst, u8 *src) - perform the Inverse MixColumns + * operation on round key *src + */ +ENTRY(ce_aes_invert) + vld1.8 {q0}, [r1] + aesimc.8 q0, q0 + vst1.8 {q0}, [r0] + bx lr +ENDPROC(ce_aes_invert) diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c new file mode 100644 index 000000000000..d2ee59157ec7 --- /dev/null +++ b/arch/arm/crypto/aes-ce-glue.c @@ -0,0 +1,520 @@ +/* + * aes-ce-glue.c - wrapper code for ARMv8 AES + * + * Copyright (C) 2015 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel "); +MODULE_LICENSE("GPL v2"); + +/* defined in aes-ce-core.S */ +asmlinkage u32 ce_aes_sub(u32 input); +asmlinkage void ce_aes_invert(void *dst, void *src); + +asmlinkage void ce_aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks); +asmlinkage void ce_aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks); + +asmlinkage void ce_aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks, u8 iv[]); +asmlinkage void ce_aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks, u8 iv[]); + +asmlinkage void ce_aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks, u8 ctr[]); + +asmlinkage void ce_aes_xts_encrypt(u8 out[], u8 const in[], u8 const rk1[], + int rounds, int blocks, u8 iv[], + u8 const rk2[], int first); +asmlinkage void ce_aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], + int rounds, int blocks, u8 iv[], + u8 const rk2[], int first); + +struct aes_block { + u8 b[AES_BLOCK_SIZE]; +}; + +static int num_rounds(struct crypto_aes_ctx *ctx) +{ + /* + * # of rounds specified by AES: + * 128 bit key 10 rounds + * 192 bit key 12 rounds + * 256 bit key 14 rounds + * => n byte key => 6 + (n/4) rounds + */ + return 6 + ctx->key_length / 4; +} + +static int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, + unsigned int key_len) +{ + /* + * The AES key schedule round constants + */ + static u8 const rcon[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, + }; + + u32 kwords = key_len / sizeof(u32); + struct aes_block *key_enc, *key_dec; + int i, j; + + if (key_len != AES_KEYSIZE_128 && + key_len != AES_KEYSIZE_192 && + key_len != AES_KEYSIZE_256) + return -EINVAL; + + memcpy(ctx->key_enc, in_key, key_len); + ctx->key_length = key_len; + + kernel_neon_begin(); + for (i = 0; i < sizeof(rcon); i++) { + u32 *rki = ctx->key_enc + (i * kwords); + u32 *rko = rki + kwords; + + rko[0] = ror32(ce_aes_sub(rki[kwords - 1]), 8); + rko[0] = rko[0] ^ rki[0] ^ rcon[i]; + rko[1] = rko[0] ^ rki[1]; + rko[2] = rko[1] ^ rki[2]; + rko[3] = rko[2] ^ rki[3]; + + if (key_len == AES_KEYSIZE_192) { + if (i >= 7) + break; + rko[4] = rko[3] ^ rki[4]; + rko[5] = rko[4] ^ rki[5]; + } else if (key_len == AES_KEYSIZE_256) { + if (i >= 6) + break; + rko[4] = ce_aes_sub(rko[3]) ^ rki[4]; + rko[5] = rko[4] ^ rki[5]; + rko[6] = rko[5] ^ rki[6]; + rko[7] = rko[6] ^ rki[7]; + } + } + + /* + * Generate the decryption keys for the Equivalent Inverse Cipher. + * This involves reversing the order of the round keys, and applying + * the Inverse Mix Columns transformation on all but the first and + * the last one. + */ + key_enc = (struct aes_block *)ctx->key_enc; + key_dec = (struct aes_block *)ctx->key_dec; + j = num_rounds(ctx); + + key_dec[0] = key_enc[j]; + for (i = 1, j--; j > 0; i++, j--) + ce_aes_invert(key_dec + i, key_enc + j); + key_dec[i] = key_enc[0]; + + kernel_neon_end(); + return 0; +} + +static int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key, + unsigned int key_len) +{ + struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); + int ret; + + ret = ce_aes_expandkey(ctx, in_key, key_len); + if (!ret) + return 0; + + tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; +} + +struct crypto_aes_xts_ctx { + struct crypto_aes_ctx key1; + struct crypto_aes_ctx __aligned(8) key2; +}; + +static int xts_set_key(struct crypto_tfm *tfm, const u8 *in_key, + unsigned int key_len) +{ + struct crypto_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm); + int ret; + + ret = ce_aes_expandkey(&ctx->key1, in_key, key_len / 2); + if (!ret) + ret = ce_aes_expandkey(&ctx->key2, &in_key[key_len / 2], + key_len / 2); + if (!ret) + return 0; + + tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; +} + +static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_ecb_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_enc, num_rounds(ctx), blocks); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + return err; +} + +static int ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_ecb_decrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_dec, num_rounds(ctx), blocks); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + return err; +} + +static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_cbc_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_enc, num_rounds(ctx), blocks, + walk.iv); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + return err; +} + +static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_cbc_decrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_dec, num_rounds(ctx), blocks, + walk.iv); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + return err; +} + +static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + int err, blocks; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_ctr_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_enc, num_rounds(ctx), blocks, + walk.iv); + nbytes -= blocks * AES_BLOCK_SIZE; + if (nbytes && nbytes == walk.nbytes % AES_BLOCK_SIZE) + break; + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + if (nbytes) { + u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE; + u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE; + u8 __aligned(8) tail[AES_BLOCK_SIZE]; + + /* + * Minimum alignment is 8 bytes, so if nbytes is <= 8, we need + * to tell aes_ctr_encrypt() to only read half a block. + */ + blocks = (nbytes <= 8) ? -1 : 1; + + ce_aes_ctr_encrypt(tail, tsrc, (u8 *)ctx->key_enc, + num_rounds(ctx), blocks, walk.iv); + memcpy(tdst, tail, nbytes); + err = blkcipher_walk_done(desc, &walk, 0); + } + kernel_neon_end(); + + return err; +} + +static int xts_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + int err, first, rounds = num_rounds(&ctx->key1); + struct blkcipher_walk walk; + unsigned int blocks; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { + ce_aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key1.key_enc, rounds, blocks, + walk.iv, (u8 *)ctx->key2.key_enc, first); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + + return err; +} + +static int xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + int err, first, rounds = num_rounds(&ctx->key1); + struct blkcipher_walk walk; + unsigned int blocks; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { + ce_aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key1.key_dec, rounds, blocks, + walk.iv, (u8 *)ctx->key2.key_enc, first); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + + return err; +} + +static struct crypto_alg aes_algs[] = { { + .cra_name = "__ecb-aes-ce", + .cra_driver_name = "__driver-ecb-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct crypto_aes_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ce_aes_setkey, + .encrypt = ecb_encrypt, + .decrypt = ecb_decrypt, + }, +}, { + .cra_name = "__cbc-aes-ce", + .cra_driver_name = "__driver-cbc-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct crypto_aes_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ce_aes_setkey, + .encrypt = cbc_encrypt, + .decrypt = cbc_decrypt, + }, +}, { + .cra_name = "__ctr-aes-ce", + .cra_driver_name = "__driver-ctr-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct crypto_aes_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ce_aes_setkey, + .encrypt = ctr_encrypt, + .decrypt = ctr_encrypt, + }, +}, { + .cra_name = "__xts-aes-ce", + .cra_driver_name = "__driver-xts-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct crypto_aes_xts_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = 2 * AES_MIN_KEY_SIZE, + .max_keysize = 2 * AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = xts_set_key, + .encrypt = xts_encrypt, + .decrypt = xts_decrypt, + }, +}, { + .cra_name = "ecb(aes)", + .cra_driver_name = "ecb-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ablk_set_key, + .encrypt = ablk_encrypt, + .decrypt = ablk_decrypt, + } +}, { + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ablk_set_key, + .encrypt = ablk_encrypt, + .decrypt = ablk_decrypt, + } +}, { + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ablk_set_key, + .encrypt = ablk_encrypt, + .decrypt = ablk_decrypt, + } +}, { + .cra_name = "xts(aes)", + .cra_driver_name = "xts-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_ablkcipher = { + .min_keysize = 2 * AES_MIN_KEY_SIZE, + .max_keysize = 2 * AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ablk_set_key, + .encrypt = ablk_encrypt, + .decrypt = ablk_decrypt, + } +} }; + +static int __init aes_init(void) +{ + if (!(elf_hwcap2 & HWCAP2_AES)) + return -ENODEV; + return crypto_register_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +static void __exit aes_exit(void) +{ + crypto_unregister_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +module_init(aes_init); +module_exit(aes_exit); diff --git a/crypto/Kconfig b/crypto/Kconfig index a27272396dac..7b89a98e8244 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -857,6 +857,15 @@ config CRYPTO_AES_ARM_BS This implementation does not rely on any lookup tables so it is believed to be invulnerable to cache timing attacks. +config CRYPTO_AES_ARM_CE + tristate "Accelerated AES using ARMv8 Crypto Extensions" + depends on ARM && KERNEL_MODE_NEON + select CRYPTO_ALGAPI + select CRYPTO_ABLK_HELPER + help + Use an implementation of AES in CBC, CTR and XTS modes that uses + ARMv8 Crypto Extensions + config CRYPTO_ANUBIS tristate "Anubis cipher algorithm" select CRYPTO_ALGAPI -- GitLab From 203a37f418509e0117fbd95583daeecdc078dcda Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 8 May 2015 10:46:22 +0200 Subject: [PATCH 1562/5498] UPSTREAM: crypto: arm/aes - streamline AES-192 code path This trims off a couple of instructions of the total size of the core AES transform by reordering the final branch in the AES-192 code path with the rounds that are performed regardless of whether the branch is taken or not. Other than the slight size reduction, this has no performance benefit. Fix up a comment regarding the prototype of this function while we're at it. Signed-off-by: Ard Biesheuvel Signed-off-by: Herbert Xu (cherry picked from commit 6499e8cfaa8f5d041b20af24d8409dec9f3ac3d0) Change-Id: If77f938fba6b3cbfb3af577d40f668f18aef8290 Signed-off-by: Eric Biggers --- arch/arm/crypto/aes-ce-core.S | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm/crypto/aes-ce-core.S b/arch/arm/crypto/aes-ce-core.S index 8cfa468ee570..987aa632c9f0 100644 --- a/arch/arm/crypto/aes-ce-core.S +++ b/arch/arm/crypto/aes-ce-core.S @@ -101,15 +101,14 @@ \dround q10, q11 blo 0f @ AES-128: 10 rounds vld1.8 {q10-q11}, [ip]! - beq 1f @ AES-192: 12 rounds \dround q12, q13 + beq 1f @ AES-192: 12 rounds vld1.8 {q12-q13}, [ip] \dround q10, q11 0: \fround q12, q13, q14 bx lr -1: \dround q12, q13 - \fround q10, q11, q14 +1: \fround q10, q11, q14 bx lr .endm @@ -122,8 +121,8 @@ * q2 : third in/output block (_3x version only) * q8 : first round key * q9 : secound round key - * ip : address of 3rd round key * q14 : final round key + * r2 : address of round key array * r3 : number of rounds */ .align 6 -- GitLab From c071afe2b97eece9faffa8525f29de7c41e6419c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 13 Sep 2016 09:48:52 +0100 Subject: [PATCH 1563/5498] UPSTREAM: crypto: arm/aes-ctr - fix NULL dereference in tail processing The AES-CTR glue code avoids calling into the blkcipher API for the tail portion of the walk, by comparing the remainder of walk.nbytes modulo AES_BLOCK_SIZE with the residual nbytes, and jumping straight into the tail processing block if they are equal. This tail processing block checks whether nbytes != 0, and does nothing otherwise. However, in case of an allocation failure in the blkcipher layer, we may enter this code with walk.nbytes == 0, while nbytes > 0. In this case, we should not dereference the source and destination pointers, since they may be NULL. So instead of checking for nbytes != 0, check for (walk.nbytes % AES_BLOCK_SIZE) != 0, which implies the former in non-error conditions. Fixes: 86464859cc77 ("crypto: arm - AES in ECB/CBC/CTR/XTS modes using ARMv8 Crypto Extensions") Cc: stable@vger.kernel.org Reported-by: xiakaixu Signed-off-by: Ard Biesheuvel Signed-off-by: Herbert Xu (cherry picked from commit f82e90b28654804ab72881d577d87c3d5c65e2bc) Change-Id: Ie3b6479e96bb5c39cd89d2e575b983cc90021615 Signed-off-by: Eric Biggers --- arch/arm/crypto/aes-ce-glue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c index d2ee59157ec7..ae966d4e6573 100644 --- a/arch/arm/crypto/aes-ce-glue.c +++ b/arch/arm/crypto/aes-ce-glue.c @@ -279,7 +279,7 @@ static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, err = blkcipher_walk_done(desc, &walk, walk.nbytes % AES_BLOCK_SIZE); } - if (nbytes) { + if (walk.nbytes % AES_BLOCK_SIZE) { u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE; u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE; u8 __aligned(8) tail[AES_BLOCK_SIZE]; -- GitLab From 073c2ede2b59fe1242de5210bbc3bd9636d67260 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2016 19:15:20 +0100 Subject: [PATCH 1564/5498] UPSTREAM: crypto: arm/aes-ce - fix for big endian The AES key schedule generation is mostly endian agnostic, with the exception of the rotation and the incorporation of the round constant at the start of each round. So implement a big endian specific version of that part to make the whole routine big endian compatible. Fixes: 86464859cc77 ("crypto: arm - AES in ECB/CBC/CTR/XTS modes using ARMv8 Crypto Extensions") Signed-off-by: Ard Biesheuvel Signed-off-by: Herbert Xu (cherry picked from commit 58010fa6f71c9577922b22e46014b95a4ec80fa0) Change-Id: I7eb0dc2f91c6ceba66f67a81c0d87aafdfd484a1 Signed-off-by: Eric Biggers --- arch/arm/crypto/aes-ce-glue.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c index ae966d4e6573..297e375521c2 100644 --- a/arch/arm/crypto/aes-ce-glue.c +++ b/arch/arm/crypto/aes-ce-glue.c @@ -87,8 +87,13 @@ static int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, u32 *rki = ctx->key_enc + (i * kwords); u32 *rko = rki + kwords; +#ifndef CONFIG_CPU_BIG_ENDIAN rko[0] = ror32(ce_aes_sub(rki[kwords - 1]), 8); rko[0] = rko[0] ^ rki[0] ^ rcon[i]; +#else + rko[0] = rol32(ce_aes_sub(rki[kwords - 1]), 8); + rko[0] = rko[0] ^ rki[0] ^ (rcon[i] << 24); +#endif rko[1] = rko[0] ^ rki[1]; rko[2] = rko[1] ^ rki[2]; rko[3] = rko[2] ^ rki[3]; -- GitLab From 009697541b5fce2a1df26b19216d7cb2af554a72 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 16 Nov 2015 13:12:48 +0100 Subject: [PATCH 1565/5498] UPSTREAM: arm64: crypto: reduce priority of core AES cipher The asynchronous, merged implementations of AES in CBC, CTR and XTS modes are preferred when available (i.e., when instantiating ablkciphers explicitly). However, the synchronous core AES cipher combined with the generic CBC mode implementation will produce a 'cbc(aes)' blkcipher that is callable asynchronously as well. To prevent this implementation from being used when the accelerated asynchronous implemenation is also available, lower its priority to 250 (i.e., below the asynchronous module's priority of 300). Signed-off-by: Ard Biesheuvel Signed-off-by: Catalin Marinas (cherry-picked from commit 08c6781cfaa196d4b257ec043c17e8746d9284e3) Change-Id: I50cd239615906dd70bf0a1bf945d40c9d95a3c00 Signed-off-by: Eric Biggers --- arch/arm64/crypto/aes-ce-cipher.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher.c index 2075e1acae6b..28c05484cf24 100644 --- a/arch/arm64/crypto/aes-ce-cipher.c +++ b/arch/arm64/crypto/aes-ce-cipher.c @@ -127,7 +127,7 @@ static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) static struct crypto_alg aes_alg = { .cra_name = "aes", .cra_driver_name = "aes-ce", - .cra_priority = 300, + .cra_priority = 250, .cra_flags = CRYPTO_ALG_TYPE_CIPHER, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto_aes_ctx), -- GitLab From 175a1da8548f93f0d3f9152885b11a807428d38d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 9 Nov 2017 13:56:55 -0800 Subject: [PATCH 1566/5498] ANDROID: arm: crypto: reduce priority of bit-sliced AES cipher All the aes-bs (bit-sliced) and aes-ce (cryptographic extensions) algorithms had a priority of 300. This is undesirable because it means an aes-bs algorithm may be used when an aes-ce algorithm is available. The aes-ce algorithms have much better performance. Fix it by decreasing the priority of the aes-bs algorithms to 250. This was fixed upstream by commit cc477bf64573 ("crypto: arm/aes - replace bit-sliced OpenSSL NEON code"), but it was just a small part of a complete rewrite. This patch just fixes the priority bug. Change-Id: I478d67bfd77cb3807853b00976aae8496bb7f5ae Signed-off-by: Eric Biggers --- arch/arm/crypto/aesbs-glue.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/crypto/aesbs-glue.c b/arch/arm/crypto/aesbs-glue.c index 15468fbbdea3..68679cafe0bb 100644 --- a/arch/arm/crypto/aesbs-glue.c +++ b/arch/arm/crypto/aesbs-glue.c @@ -354,7 +354,7 @@ static struct crypto_alg aesbs_algs[] = { { }, { .cra_name = "cbc(aes)", .cra_driver_name = "cbc-aes-neonbs", - .cra_priority = 300, + .cra_priority = 250, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct async_helper_ctx), @@ -374,7 +374,7 @@ static struct crypto_alg aesbs_algs[] = { { }, { .cra_name = "ctr(aes)", .cra_driver_name = "ctr-aes-neonbs", - .cra_priority = 300, + .cra_priority = 250, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct async_helper_ctx), @@ -394,7 +394,7 @@ static struct crypto_alg aesbs_algs[] = { { }, { .cra_name = "xts(aes)", .cra_driver_name = "xts-aes-neonbs", - .cra_priority = 300, + .cra_priority = 250, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct async_helper_ctx), -- GitLab From 458359bf7762a1bc4cde9671054680ff3c62f4d8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 10 Mar 2015 09:47:46 +0100 Subject: [PATCH 1567/5498] BACKPORT: crypto: arm - add support for SHA-224/256 using ARMv8 Crypto Extensions This implements the SHA-224/256 secure hash algorithm using the AArch32 versions of the ARMv8 Crypto Extensions for SHA2. Signed-off-by: Ard Biesheuvel Signed-off-by: Herbert Xu (cherry-picked from commit 006d0624fa0d71787448cacee0195bf20f2d47c8) (increased .cra_priority to 300 because this kernel already had commit f2f770d74a8d ("crypto: arm/sha256 - Add optimized SHA-256/224") backported to it too) Change-Id: I2ba82e37426abe77c43f69e8bbd414e75d2dff14 Signed-off-by: Eric Biggers --- arch/arm/crypto/Makefile | 2 + arch/arm/crypto/sha2-ce-core.S | 134 ++++++++++++++++++++++ arch/arm/crypto/sha2-ce-glue.c | 203 +++++++++++++++++++++++++++++++++ crypto/Kconfig | 9 ++ 4 files changed, 348 insertions(+) create mode 100644 arch/arm/crypto/sha2-ce-core.S create mode 100644 arch/arm/crypto/sha2-ce-glue.c diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile index 2086c386a70e..742f215306fe 100644 --- a/arch/arm/crypto/Makefile +++ b/arch/arm/crypto/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o obj-$(CONFIG_CRYPTO_SHA512_ARM_NEON) += sha512-arm-neon.o +obj-$(CONFIG_CRYPTO_SHA2_ARM_CE) += sha2-arm-ce.o aes-arm-y := aes-armv4.o aes_glue.o aes-arm-bs-y := aesbs-core.o aesbs-glue.o @@ -18,6 +19,7 @@ sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o sha256-arm-y := sha256-core.o sha256_glue.o $(sha256-arm-neon-y) sha512-arm-neon-y := sha512-armv7-neon.o sha512_neon_glue.o aes-arm-ce-y := aes-ce-core.o aes-ce-glue.o +sha2-arm-ce-y := sha2-ce-core.o sha2-ce-glue.o quiet_cmd_perl = PERL $@ cmd_perl = $(PERL) $(<) > $(@) diff --git a/arch/arm/crypto/sha2-ce-core.S b/arch/arm/crypto/sha2-ce-core.S new file mode 100644 index 000000000000..96af09fe957b --- /dev/null +++ b/arch/arm/crypto/sha2-ce-core.S @@ -0,0 +1,134 @@ +/* + * sha2-ce-core.S - SHA-224/256 secure hash using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd. + * Author: Ard Biesheuvel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + + .text + .fpu crypto-neon-fp-armv8 + + k0 .req q7 + k1 .req q8 + rk .req r3 + + ta0 .req q9 + ta1 .req q10 + tb0 .req q10 + tb1 .req q9 + + dga .req q11 + dgb .req q12 + + dg0 .req q13 + dg1 .req q14 + dg2 .req q15 + + .macro add_only, ev, s0 + vmov dg2, dg0 + .ifnb \s0 + vld1.32 {k\ev}, [rk, :128]! + .endif + sha256h.32 dg0, dg1, tb\ev + sha256h2.32 dg1, dg2, tb\ev + .ifnb \s0 + vadd.u32 ta\ev, q\s0, k\ev + .endif + .endm + + .macro add_update, ev, s0, s1, s2, s3 + sha256su0.32 q\s0, q\s1 + add_only \ev, \s1 + sha256su1.32 q\s0, q\s2, q\s3 + .endm + + .align 6 +.Lsha256_rcon: + .word 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5 + .word 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5 + .word 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3 + .word 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174 + .word 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc + .word 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da + .word 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7 + .word 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967 + .word 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13 + .word 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85 + .word 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3 + .word 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070 + .word 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5 + .word 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3 + .word 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208 + .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + + /* + * void sha2_ce_transform(int blocks, u8 const *src, u32 *state, + * u8 *head); + */ +ENTRY(sha2_ce_transform) + /* load state */ + vld1.32 {dga-dgb}, [r2] + + /* load partial input (if supplied) */ + teq r3, #0 + beq 0f + vld1.32 {q0-q1}, [r3]! + vld1.32 {q2-q3}, [r3] + teq r0, #0 + b 1f + + /* load input */ +0: vld1.32 {q0-q1}, [r1]! + vld1.32 {q2-q3}, [r1]! + subs r0, r0, #1 + +1: +#ifndef CONFIG_CPU_BIG_ENDIAN + vrev32.8 q0, q0 + vrev32.8 q1, q1 + vrev32.8 q2, q2 + vrev32.8 q3, q3 +#endif + + /* load first round constant */ + adr rk, .Lsha256_rcon + vld1.32 {k0}, [rk, :128]! + + vadd.u32 ta0, q0, k0 + vmov dg0, dga + vmov dg1, dgb + + add_update 1, 0, 1, 2, 3 + add_update 0, 1, 2, 3, 0 + add_update 1, 2, 3, 0, 1 + add_update 0, 3, 0, 1, 2 + add_update 1, 0, 1, 2, 3 + add_update 0, 1, 2, 3, 0 + add_update 1, 2, 3, 0, 1 + add_update 0, 3, 0, 1, 2 + add_update 1, 0, 1, 2, 3 + add_update 0, 1, 2, 3, 0 + add_update 1, 2, 3, 0, 1 + add_update 0, 3, 0, 1, 2 + + add_only 1, 1 + add_only 0, 2 + add_only 1, 3 + add_only 0 + + /* update state */ + vadd.u32 dga, dga, dg0 + vadd.u32 dgb, dgb, dg1 + bne 0b + + /* store new state */ + vst1.32 {dga-dgb}, [r2] + bx lr +ENDPROC(sha2_ce_transform) diff --git a/arch/arm/crypto/sha2-ce-glue.c b/arch/arm/crypto/sha2-ce-glue.c new file mode 100644 index 000000000000..0449eca3aab3 --- /dev/null +++ b/arch/arm/crypto/sha2-ce-glue.c @@ -0,0 +1,203 @@ +/* + * sha2-ce-glue.c - SHA-224/SHA-256 using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_DESCRIPTION("SHA-224/SHA-256 secure hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel "); +MODULE_LICENSE("GPL v2"); + +asmlinkage void sha2_ce_transform(int blocks, u8 const *src, u32 *state, + u8 *head); + +static int sha224_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + *sctx = (struct sha256_state){ + .state = { + SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3, + SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7, + } + }; + return 0; +} + +static int sha256_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + *sctx = (struct sha256_state){ + .state = { + SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, + SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7, + } + }; + return 0; +} + +static int sha2_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + unsigned int partial; + + if (!may_use_simd()) + return crypto_sha256_update(desc, data, len); + + partial = sctx->count % SHA256_BLOCK_SIZE; + sctx->count += len; + + if ((partial + len) >= SHA256_BLOCK_SIZE) { + int blocks; + + if (partial) { + int p = SHA256_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data, p); + data += p; + len -= p; + } + + blocks = len / SHA256_BLOCK_SIZE; + len %= SHA256_BLOCK_SIZE; + + kernel_neon_begin(); + sha2_ce_transform(blocks, data, sctx->state, + partial ? sctx->buf : NULL); + kernel_neon_end(); + + data += blocks * SHA256_BLOCK_SIZE; + partial = 0; + } + if (len) + memcpy(sctx->buf + partial, data, len); + return 0; +} + +static void sha2_final(struct shash_desc *desc) +{ + static const u8 padding[SHA256_BLOCK_SIZE] = { 0x80, }; + + struct sha256_state *sctx = shash_desc_ctx(desc); + __be64 bits = cpu_to_be64(sctx->count << 3); + u32 padlen = SHA256_BLOCK_SIZE + - ((sctx->count + sizeof(bits)) % SHA256_BLOCK_SIZE); + + sha2_update(desc, padding, padlen); + sha2_update(desc, (const u8 *)&bits, sizeof(bits)); +} + +static int sha224_final(struct shash_desc *desc, u8 *out) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + __be32 *dst = (__be32 *)out; + int i; + + sha2_final(desc); + + for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(__be32); i++) + put_unaligned_be32(sctx->state[i], dst++); + + *sctx = (struct sha256_state){}; + return 0; +} + +static int sha256_final(struct shash_desc *desc, u8 *out) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + __be32 *dst = (__be32 *)out; + int i; + + sha2_final(desc); + + for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(__be32); i++) + put_unaligned_be32(sctx->state[i], dst++); + + *sctx = (struct sha256_state){}; + return 0; +} + +static int sha2_export(struct shash_desc *desc, void *out) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + struct sha256_state *dst = out; + + *dst = *sctx; + return 0; +} + +static int sha2_import(struct shash_desc *desc, const void *in) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + struct sha256_state const *src = in; + + *sctx = *src; + return 0; +} + +static struct shash_alg algs[] = { { + .init = sha224_init, + .update = sha2_update, + .final = sha224_final, + .export = sha2_export, + .import = sha2_import, + .descsize = sizeof(struct sha256_state), + .digestsize = SHA224_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name = "sha224-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}, { + .init = sha256_init, + .update = sha2_update, + .final = sha256_final, + .export = sha2_export, + .import = sha2_import, + .descsize = sizeof(struct sha256_state), + .digestsize = SHA256_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; + +static int __init sha2_ce_mod_init(void) +{ + if (!(elf_hwcap2 & HWCAP2_SHA2)) + return -ENODEV; + return crypto_register_shashes(algs, ARRAY_SIZE(algs)); +} + +static void __exit sha2_ce_mod_fini(void) +{ + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); +} + +module_init(sha2_ce_mod_init); +module_exit(sha2_ce_mod_fini); diff --git a/crypto/Kconfig b/crypto/Kconfig index 7b89a98e8244..bc96a82240f1 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -611,6 +611,15 @@ config CRYPTO_SHA256_SPARC64 SHA-256 secure hash standard (DFIPS 180-2) implemented using sparc64 crypto instructions, when available. +config CRYPTO_SHA2_ARM_CE + tristate "SHA-224/256 digest algorithm (ARM v8 Crypto Extensions)" + depends on ARM && KERNEL_MODE_NEON + select CRYPTO_SHA256 + select CRYPTO_HASH + help + SHA-256 secure hash standard (DFIPS 180-2) implemented + using special ARMv8 Crypto Extensions. + config CRYPTO_SHA512 tristate "SHA384 and SHA512 digest algorithms" select CRYPTO_HASH -- GitLab From 710adf1ce8a5321e0e76660bb846e2a641e4b550 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 28 Oct 2017 16:52:30 +0800 Subject: [PATCH 1568/5498] f2fs: support get_page error injection This patch adds to support get_page error injection to simulate out-of-memory test scenario. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- fs/f2fs/f2fs.h | 14 ++++++++++++++ fs/f2fs/gc.c | 4 ++-- fs/f2fs/node.c | 10 +++------- fs/f2fs/super.c | 1 + 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 36617c4831a5..a0312e8e2bf6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1959,7 +1959,8 @@ repeat: * Do not use grab_cache_page_write_begin() to avoid deadlock due to * wait_for_stable_page. Will wait that below with our IO control. */ - page = grab_cache_page(mapping, index); + page = f2fs_pagecache_get_page(mapping, index, + FGP_LOCK | FGP_WRITE | FGP_CREAT, GFP_NOFS); if (!page) { err = -ENOMEM; goto fail; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ad6bea11fb55..1978a43b2497 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -48,6 +48,7 @@ enum { FAULT_KMALLOC, FAULT_PAGE_ALLOC, + FAULT_PAGE_GET, FAULT_ALLOC_NID, FAULT_ORPHAN, FAULT_BLOCK, @@ -1930,6 +1931,19 @@ static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, return grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS); } +static inline struct page *f2fs_pagecache_get_page( + struct address_space *mapping, pgoff_t index, + int fgp_flags, gfp_t gfp_mask) +{ +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(F2FS_M_SB(mapping), FAULT_PAGE_GET)) { + f2fs_show_injection_info(FAULT_PAGE_GET); + return NULL; + } +#endif + return pagecache_get_page(mapping, index, fgp_flags, gfp_mask); +} + static inline void f2fs_copy_page(struct page *src, struct page *dst) { char *src_kaddr = kmap(src); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 197ebf45e4e4..c7b1d704846a 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -650,8 +650,8 @@ static void move_data_block(struct inode *inode, block_t bidx, allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr, &sum, CURSEG_COLD_DATA, NULL, false); - fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr, - FGP_LOCK | FGP_CREAT, GFP_NOFS); + fio.encrypted_page = f2fs_pagecache_get_page(META_MAPPING(fio.sbi), + newaddr, FGP_LOCK | FGP_CREAT, GFP_NOFS); if (!fio.encrypted_page) { err = -ENOMEM; goto recover_block; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index fde6ad4ed775..9300938934ca 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1219,13 +1219,11 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) if (!inode) return; - page = find_get_page(inode->i_mapping, 0); + page = f2fs_pagecache_get_page(inode->i_mapping, 0, + FGP_LOCK|FGP_NOWAIT, 0); if (!page) goto iput_out; - if (!trylock_page(page)) - goto release_out; - if (!PageUptodate(page)) goto page_out; @@ -1241,9 +1239,7 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) if (ret) set_page_dirty(page); page_out: - unlock_page(page); -release_out: - f2fs_put_page(page, 0); + f2fs_put_page(page, 1); iput_out: iput(inode); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index e421efc66795..c64b2b1350bc 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -47,6 +47,7 @@ static struct kmem_cache *f2fs_inode_cachep; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", [FAULT_PAGE_ALLOC] = "page alloc", + [FAULT_PAGE_GET] = "page get", [FAULT_ALLOC_NID] = "alloc nid", [FAULT_ORPHAN] = "orphan", [FAULT_BLOCK] = "no more block", -- GitLab From be5d050a42f59a8fc1902b623baae1db51b7ee74 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 28 Oct 2017 16:52:31 +0800 Subject: [PATCH 1569/5498] f2fs: support bio allocation error injection This patch adds to support bio allocation error injection to simulate out-of-memory test scenario. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 23 +++++++++++++++++------ fs/f2fs/segment.c | 5 +++-- fs/f2fs/super.c | 1 + 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a0312e8e2bf6..388d2e4045f7 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -172,7 +172,7 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, { struct bio *bio; - bio = f2fs_bio_alloc(npages); + bio = f2fs_bio_alloc(sbi, npages, true); f2fs_target_device(sbi, blk_addr, bio); bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; @@ -472,7 +472,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, f2fs_wait_on_block_writeback(sbi, blkaddr); } - bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); + bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false); if (!bio) { if (ctx) fscrypt_release_ctx(ctx); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1978a43b2497..2bef52b12c04 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -49,6 +49,7 @@ enum { FAULT_KMALLOC, FAULT_PAGE_ALLOC, FAULT_PAGE_GET, + FAULT_ALLOC_BIO, FAULT_ALLOC_NID, FAULT_ORPHAN, FAULT_BLOCK, @@ -1993,15 +1994,25 @@ static inline void *f2fs_kmem_cache_alloc(struct kmem_cache *cachep, return entry; } -static inline struct bio *f2fs_bio_alloc(int npages) +static inline struct bio *f2fs_bio_alloc(struct f2fs_sb_info *sbi, + int npages, bool no_fail) { struct bio *bio; - /* No failure on bio allocation */ - bio = bio_alloc(GFP_NOIO, npages); - if (!bio) - bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, npages); - return bio; + if (no_fail) { + /* No failure on bio allocation */ + bio = bio_alloc(GFP_NOIO, npages); + if (!bio) + bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, npages); + return bio; + } +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_ALLOC_BIO)) { + f2fs_show_injection_info(FAULT_ALLOC_BIO); + return NULL; + } +#endif + return bio_alloc(GFP_KERNEL, npages); } static inline void f2fs_radix_tree_insert(struct radix_tree_root *root, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index de12b948c788..7920d964a24f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -511,7 +511,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) static int __submit_flush_wait(struct f2fs_sb_info *sbi, struct block_device *bdev) { - struct bio *bio = f2fs_bio_alloc(0); + struct bio *bio = f2fs_bio_alloc(sbi, 0, true); int ret; bio->bi_rw = REQ_OP_WRITE; @@ -943,7 +943,8 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, if (ret) return ret; } - bio = f2fs_bio_alloc(1); + + bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, 1); bio->bi_iter.bi_sector = sector; bio->bi_bdev = bdev; bio_set_op_attrs(bio, op, 0); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c64b2b1350bc..efb3c8d6cc74 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -48,6 +48,7 @@ char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", [FAULT_PAGE_ALLOC] = "page alloc", [FAULT_PAGE_GET] = "page get", + [FAULT_ALLOC_BIO] = "alloc bio", [FAULT_ALLOC_NID] = "alloc nid", [FAULT_ORPHAN] = "orphan", [FAULT_BLOCK] = "no more block", -- GitLab From 1ead0d06fe725f7e240bff749aaa2a5a72450ea3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 28 Oct 2017 16:52:32 +0800 Subject: [PATCH 1570/5498] f2fs: give correct trimmed blocks in fstrim We have supported to issue discard in specified range during fstrim, it needs to return caller with successfully trimmed bytes in that range instead of bytes of invalid blocks which are scanned in checkpoint. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - fs/f2fs/segment.c | 27 +++++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2bef52b12c04..9f1d38e9bd07 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -261,7 +261,6 @@ struct cp_control { __u64 trim_start; __u64 trim_end; __u64 trim_minlen; - __u64 trimmed; }; /* diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7920d964a24f..97582aa0e7bd 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1337,21 +1337,27 @@ static bool __drop_discard_cmd(struct f2fs_sb_info *sbi) return dropped; } -static void __wait_one_discard_bio(struct f2fs_sb_info *sbi, +static unsigned int __wait_one_discard_bio(struct f2fs_sb_info *sbi, struct discard_cmd *dc) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + unsigned int len = 0; wait_for_completion_io(&dc->wait); mutex_lock(&dcc->cmd_lock); f2fs_bug_on(sbi, dc->state != D_DONE); dc->ref--; - if (!dc->ref) + if (!dc->ref) { + if (!dc->error) + len = dc->len; __remove_discard_cmd(sbi, dc); + } mutex_unlock(&dcc->cmd_lock); + + return len; } -static void __wait_discard_cmd_range(struct f2fs_sb_info *sbi, +static unsigned int __wait_discard_cmd_range(struct f2fs_sb_info *sbi, struct discard_policy *dpolicy, block_t start, block_t end) { @@ -1360,6 +1366,7 @@ static void __wait_discard_cmd_range(struct f2fs_sb_info *sbi, &(dcc->fstrim_list) : &(dcc->wait_list); struct discard_cmd *dc, *tmp; bool need_wait; + unsigned int trimmed = 0; next: need_wait = false; @@ -1372,6 +1379,8 @@ next: continue; if (dc->state == D_DONE && !dc->ref) { wait_for_completion_io(&dc->wait); + if (!dc->error) + trimmed += dc->len; __remove_discard_cmd(sbi, dc); } else { dc->ref++; @@ -1382,9 +1391,11 @@ next: mutex_unlock(&dcc->cmd_lock); if (need_wait) { - __wait_one_discard_bio(sbi, dc); + trimmed += __wait_one_discard_bio(sbi, dc); goto next; } + + return trimmed; } static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, @@ -1745,7 +1756,6 @@ find_next: f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, len); - cpc->trimmed += len; total_len += len; } else { next_pos = find_next_bit_le(entry->discard_map, @@ -2448,12 +2458,12 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) block_t start_block, end_block; struct cp_control cpc; struct discard_policy dpolicy; + unsigned long long trimmed = 0; int err = 0; if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) return -EINVAL; - cpc.trimmed = 0; if (end <= MAIN_BLKADDR(sbi)) goto out; @@ -2500,9 +2510,10 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) init_discard_policy(&dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); __issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block); - __wait_discard_cmd_range(sbi, &dpolicy, start_block, end_block); + trimmed = __wait_discard_cmd_range(sbi, &dpolicy, + start_block, end_block); out: - range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); + range->len = F2FS_BLK_TO_BYTES(trimmed); return err; } -- GitLab From 8f5dcea4021d5eda39af4ceb9e1fdb51ea26208e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 28 Oct 2017 16:52:33 +0800 Subject: [PATCH 1571/5498] f2fs: export SSR allocation threshold This patch exports min_ssr_segments threshold in sysfs to let user control triggering SSR allocation flexibly. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 6 ++++++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/segment.c | 3 ++- fs/f2fs/sysfs.c | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index a07134c517e0..2baed1151eac 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -57,6 +57,12 @@ Contact: "Jaegeuk Kim" Description: Controls the dirty page count condition for redefining hot data. +What: /sys/fs/f2fs//min_ssr_sections +Date: October 2017 +Contact: "Chao Yu" +Description: + Controls the fee section threshold to trigger SSR allocation. + What: /sys/fs/f2fs//max_small_discards Date: November 2013 Contact: "Jaegeuk Kim" diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9f1d38e9bd07..e37b294b134b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -934,6 +934,7 @@ struct f2fs_sm_info { unsigned int min_ipu_util; /* in-place-update threshold */ unsigned int min_fsync_blocks; /* threshold for fsync */ unsigned int min_hot_blocks; /* threshold for hot block allocation */ + unsigned int min_ssr_sections; /* threshold to trigger SSR allocation */ /* for flush command control */ struct flush_cmd_control *fcc_info; @@ -1175,6 +1176,7 @@ struct f2fs_sb_info { int active_logs; /* # of active logs */ int dir_level; /* directory level */ int inline_xattr_size; /* inline xattr size */ + unsigned int trigger_ssr_threshold; /* threshold to trigger ssr */ block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 97582aa0e7bd..0f90d3a610e8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -181,7 +181,7 @@ bool need_SSR(struct f2fs_sb_info *sbi) return true; return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + - 2 * reserved_sections(sbi)); + SM_I(sbi)->min_ssr_sections + reserved_sections(sbi)); } void register_inmem_page(struct inode *inode, struct page *page) @@ -3752,6 +3752,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS; + sm_info->min_ssr_sections = reserved_sections(sbi); sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 7796e34d90a4..f5778984ead2 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -285,6 +285,7 @@ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_blocks); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ssr_sections, min_ssr_sections); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio); @@ -330,6 +331,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(min_ipu_util), ATTR_LIST(min_fsync_blocks), ATTR_LIST(min_hot_blocks), + ATTR_LIST(min_ssr_sections), ATTR_LIST(max_victim_search), ATTR_LIST(dir_level), ATTR_LIST(ram_thresh), -- GitLab From 71b4052790967c5c1187a101fb1cab3ab0a988c1 Mon Sep 17 00:00:00 2001 From: Fan Li Date: Sat, 28 Oct 2017 19:03:37 +0800 Subject: [PATCH 1572/5498] f2fs: add a function to move nid This patch add a new function to move nid from one state to another. Move operation is heavily used, by adding a new function for it we can cut down some branches from several flow. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 57 +++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9300938934ca..53a44dd78a5a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1766,15 +1766,13 @@ static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, } static int __insert_free_nid(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_state state, bool new) + struct free_nid *i, enum nid_state state) { struct f2fs_nm_info *nm_i = NM_I(sbi); - if (new) { - int err = radix_tree_insert(&nm_i->free_nid_root, i->nid, i); - if (err) - return err; - } + int err = radix_tree_insert(&nm_i->free_nid_root, i->nid, i); + if (err) + return err; f2fs_bug_on(sbi, state != i->state); nm_i->nid_cnt[state]++; @@ -1784,7 +1782,7 @@ static int __insert_free_nid(struct f2fs_sb_info *sbi, } static void __remove_free_nid(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_state state, bool reuse) + struct free_nid *i, enum nid_state state) { struct f2fs_nm_info *nm_i = NM_I(sbi); @@ -1792,8 +1790,29 @@ static void __remove_free_nid(struct f2fs_sb_info *sbi, nm_i->nid_cnt[state]--; if (state == FREE_NID) list_del(&i->list); - if (!reuse) - radix_tree_delete(&nm_i->free_nid_root, i->nid); + radix_tree_delete(&nm_i->free_nid_root, i->nid); +} + +static void __move_free_nid(struct f2fs_sb_info *sbi, struct free_nid *i, + enum nid_state org_state, enum nid_state dst_state) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + + f2fs_bug_on(sbi, org_state != i->state); + i->state = dst_state; + nm_i->nid_cnt[org_state]--; + nm_i->nid_cnt[dst_state]++; + + switch (dst_state) { + case PREALLOC_NID: + list_del(&i->list); + break; + case FREE_NID: + list_add_tail(&i->list, &nm_i->free_nid_list); + break; + default: + BUG_ON(1); + } } /* return if the nid is recognized as free */ @@ -1853,7 +1872,7 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) } } ret = true; - err = __insert_free_nid(sbi, i, FREE_NID, true); + err = __insert_free_nid(sbi, i, FREE_NID); err_out: spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); @@ -1872,7 +1891,7 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); if (i && i->state == FREE_NID) { - __remove_free_nid(sbi, i, FREE_NID, false); + __remove_free_nid(sbi, i, FREE_NID); need_free = true; } spin_unlock(&nm_i->nid_list_lock); @@ -2083,9 +2102,7 @@ retry: struct free_nid, list); *nid = i->nid; - __remove_free_nid(sbi, i, FREE_NID, true); - i->state = PREALLOC_NID; - __insert_free_nid(sbi, i, PREALLOC_NID, false); + __move_free_nid(sbi, i, FREE_NID, PREALLOC_NID); nm_i->available_nids--; update_free_nid_bitmap(sbi, *nid, false, false); @@ -2111,7 +2128,7 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); f2fs_bug_on(sbi, !i); - __remove_free_nid(sbi, i, PREALLOC_NID, false); + __remove_free_nid(sbi, i, PREALLOC_NID); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); @@ -2134,12 +2151,10 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) f2fs_bug_on(sbi, !i); if (!available_free_memory(sbi, FREE_NIDS)) { - __remove_free_nid(sbi, i, PREALLOC_NID, false); + __remove_free_nid(sbi, i, PREALLOC_NID); need_free = true; } else { - __remove_free_nid(sbi, i, PREALLOC_NID, true); - i->state = FREE_NID; - __insert_free_nid(sbi, i, FREE_NID, false); + __move_free_nid(sbi, i, PREALLOC_NID, FREE_NID); } nm_i->available_nids++; @@ -2170,7 +2185,7 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS) break; - __remove_free_nid(sbi, i, FREE_NID, false); + __remove_free_nid(sbi, i, FREE_NID); kmem_cache_free(free_nid_slab, i); nr_shrink--; } @@ -2749,7 +2764,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) /* destroy free nid list */ spin_lock(&nm_i->nid_list_lock); list_for_each_entry_safe(i, next_i, &nm_i->free_nid_list, list) { - __remove_free_nid(sbi, i, FREE_NID, false); + __remove_free_nid(sbi, i, FREE_NID); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); spin_lock(&nm_i->nid_list_lock); -- GitLab From 0e42a91594b5eff2fa28f2c1cfb0b91262e11c66 Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Mon, 30 Oct 2017 09:33:41 +0800 Subject: [PATCH 1573/5498] Revert "f2fs: handle dirty segments inside refresh_sit_entry" This reverts commit 5e443818fa0b2a2845561ee25bec181424fb2889 The commit should be reverted because call sequence of below two parts of code must be kept: a. update sit information, it needs to be updated before segment allocation since latter allocation may trigger SSR, and SSR allocation needs latest valid block information of all segments. b. update segment status, it needs to be updated after segment allocation since we can skip updating current opened segment status. Fixes: 5e443818fa0b ("f2fs: handle dirty segments inside refresh_sit_entry") Suggested-by: Chao Yu Signed-off-by: Yunlong Song Reviewed-by: Chao Yu [Jaegeuk Kim: remove refresh_sit_entry function] Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - fs/f2fs/segment.c | 27 ++++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e37b294b134b..7d08dbab4a54 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2716,7 +2716,6 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void init_discard_policy(struct discard_policy *dpolicy, int discard_type, unsigned int granularity); -void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); void stop_discard_thread(struct f2fs_sb_info *sbi); bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0f90d3a610e8..0464fa18c665 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1975,16 +1975,6 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) get_sec_entry(sbi, segno)->valid_blocks += del; } -void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new) -{ - update_sit_entry(sbi, new, 1); - if (GET_SEGNO(sbi, old) != NULL_SEGNO) - update_sit_entry(sbi, old, -1); - - locate_dirty_segment(sbi, GET_SEGNO(sbi, old)); - locate_dirty_segment(sbi, GET_SEGNO(sbi, new)); -} - void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) { unsigned int segno = GET_SEGNO(sbi, addr); @@ -2621,13 +2611,24 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, stat_inc_block_count(sbi, curseg); + /* + * SIT information should be updated before segment allocation, + * since SSR needs latest valid block information. + */ + update_sit_entry(sbi, *new_blkaddr, 1); + if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) + update_sit_entry(sbi, old_blkaddr, -1); + if (!__has_curseg_space(sbi, type)) sit_i->s_ops->allocate_segment(sbi, type, false); + /* - * SIT information should be updated after segment allocation, - * since we need to keep dirty segments precisely under SSR. + * segment dirty status should be updated after segment allocation, + * so we just need to update status only one time after previous + * segment being closed. */ - refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr); + locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); + locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr)); mutex_unlock(&sit_i->sentry_lock); -- GitLab From 51349d888f43993eac28a13ee02784149c1812c6 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Mon, 30 Oct 2017 14:18:55 +0800 Subject: [PATCH 1574/5498] f2fs: modify for accurate fggc node io stat modify for accurate fggc node io stat Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 62 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 53a44dd78a5a..ff1aeeae526a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1244,37 +1244,6 @@ iput_out: iput(inode); } -void move_node_page(struct page *node_page, int gc_type) -{ - if (gc_type == FG_GC) { - struct f2fs_sb_info *sbi = F2FS_P_SB(node_page); - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 1, - .for_reclaim = 0, - }; - - set_page_dirty(node_page); - f2fs_wait_on_page_writeback(node_page, NODE, true); - - f2fs_bug_on(sbi, PageWriteback(node_page)); - if (!clear_page_dirty_for_io(node_page)) - goto out_page; - - if (NODE_MAPPING(sbi)->a_ops->writepage(node_page, &wbc)) - unlock_page(node_page); - goto release_page; - } else { - /* set page dirty and write it */ - if (!PageWriteback(node_page)) - set_page_dirty(node_page); - } -out_page: - unlock_page(node_page); -release_page: - f2fs_put_page(node_page, 0); -} - static struct page *last_fsync_dnode(struct f2fs_sb_info *sbi, nid_t ino) { pgoff_t index, end; @@ -1417,6 +1386,37 @@ redirty_out: return AOP_WRITEPAGE_ACTIVATE; } +void move_node_page(struct page *node_page, int gc_type) +{ + if (gc_type == FG_GC) { + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 1, + .for_reclaim = 0, + }; + + set_page_dirty(node_page); + f2fs_wait_on_page_writeback(node_page, NODE, true); + + f2fs_bug_on(F2FS_P_SB(node_page), PageWriteback(node_page)); + if (!clear_page_dirty_for_io(node_page)) + goto out_page; + + if (__write_node_page(node_page, false, NULL, + &wbc, false, FS_GC_NODE_IO)) + unlock_page(node_page); + goto release_page; + } else { + /* set page dirty and write it */ + if (!PageWriteback(node_page)) + set_page_dirty(node_page); + } +out_page: + unlock_page(node_page); +release_page: + f2fs_put_page(node_page, 0); +} + static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { -- GitLab From 468c609578d718ed8778f4cfd55121da95e3ccc4 Mon Sep 17 00:00:00 2001 From: Fan Li Date: Mon, 30 Oct 2017 15:19:48 +0800 Subject: [PATCH 1575/5498] f2fs: optimize __update_nat_bits Make three modification for __update_nat_bits: 1. Take the codes of dealing the nat with nid 0 out of the loop Such nat only needs to be dealt with once at beginning. 2. Use " nat_index == 0" instead of " start_nid == 0" to decide if it's the first nat block It's better that we don't assume @start_nid is the first nid of the nat block it's in. 3. Use " if (nat_blk->entries[i].block_addr != NULL_ADDR)" to explicitly comfirm the value of block_addr use constant to make sure the codes is right, even if the value of NULL_ADDR changes. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index ff1aeeae526a..db06577be0f1 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2425,15 +2425,17 @@ static void __update_nat_bits(struct f2fs_sb_info *sbi, nid_t start_nid, unsigned int nat_index = start_nid / NAT_ENTRY_PER_BLOCK; struct f2fs_nat_block *nat_blk = page_address(page); int valid = 0; - int i; + int i = 0; if (!enabled_nat_bits(sbi, NULL)) return; - for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) { - if (start_nid == 0 && i == 0) - valid++; - if (nat_blk->entries[i].block_addr) + if (nat_index == 0) { + valid = 1; + i = 1; + } + for (; i < NAT_ENTRY_PER_BLOCK; i++) { + if (nat_blk->entries[i].block_addr != NULL_ADDR) valid++; } if (valid == 0) { -- GitLab From e5617e7a943a9811279eea57d962be3e5bfcfe70 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 5 Oct 2017 21:03:06 -0700 Subject: [PATCH 1576/5498] f2fs: add quota_ino feature infra This patch adds quota_ino feature infra to be used for quota files. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 6 ++++++ fs/f2fs/sysfs.c | 7 +++++++ include/linux/f2fs_fs.h | 6 +++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7d08dbab4a54..eaf433a6bdd0 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -124,6 +124,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_FEATURE_INODE_CHKSUM 0x0020 #define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 +#define F2FS_FEATURE_QUOTA_INO 0x0080 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -3185,6 +3186,11 @@ static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); } +static inline int f2fs_sb_has_quota_ino(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_QUOTA_INO); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index f5778984ead2..5c14bcaa19a4 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -110,6 +110,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_flexible_inline_xattr(sb)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "flexible_inline_xattr"); + if (f2fs_sb_has_quota_ino(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "quota_ino"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } @@ -227,6 +230,7 @@ enum feat_id { FEAT_PROJECT_QUOTA, FEAT_INODE_CHECKSUM, FEAT_FLEXIBLE_INLINE_XATTR, + FEAT_QUOTA_INO, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -240,6 +244,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_PROJECT_QUOTA: case FEAT_INODE_CHECKSUM: case FEAT_FLEXIBLE_INLINE_XATTR: + case FEAT_QUOTA_INO: return snprintf(buf, PAGE_SIZE, "supported\n"); } return 0; @@ -314,6 +319,7 @@ F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); +F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -364,6 +370,7 @@ static struct attribute *f2fs_feat_attrs[] = { ATTR_LIST(project_quota), ATTR_LIST(inode_checksum), ATTR_LIST(flexible_inline_xattr), + ATTR_LIST(quota_ino), NULL, }; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 27d4bc47cefb..c178ac599bb8 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -36,6 +36,9 @@ #define F2FS_NODE_INO(sbi) (sbi->node_ino_num) #define F2FS_META_INO(sbi) (sbi->meta_ino_num) +#define F2FS_QUOTA_INO 3 +#define F2FS_MAX_QUOTAS 3 + #define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */ #define F2FS_IO_SIZE_KB(sbi) (1 << ((sbi)->write_io_size_bits + 2)) /* KB */ #define F2FS_IO_SIZE_BYTES(sbi) (1 << ((sbi)->write_io_size_bits + 12)) /* B */ @@ -108,7 +111,8 @@ struct f2fs_super_block { __u8 encryption_level; /* versioning level for encryption */ __u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */ struct f2fs_device devs[MAX_DEVICES]; /* device list */ - __u8 reserved[327]; /* valid reserved region */ + __le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */ + __u8 reserved[315]; /* valid reserved region */ } __packed; /* -- GitLab From 722458dbae0614a5b5fb555eebb523d8510e9c81 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 6 Oct 2017 09:14:28 -0700 Subject: [PATCH 1577/5498] f2fs: support quota sys files This patch supports hidden quota files in the system, which will be used for Android. It requires up-to-date f2fs-tools later than v1.9.0. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 9 +- fs/f2fs/f2fs.h | 10 ++- fs/f2fs/recovery.c | 8 +- fs/f2fs/super.c | 188 +++++++++++++++++++++++++++++++++++++--- include/linux/f2fs_fs.h | 1 - 5 files changed, 197 insertions(+), 19 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 7232b70508be..a202ec0acba2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -616,6 +616,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) block_t start_blk, orphan_blocks, i, j; unsigned int s_flags = sbi->sb->s_flags; int err = 0; +#ifdef CONFIG_QUOTA + int quota_enabled; +#endif if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) return 0; @@ -628,8 +631,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) #ifdef CONFIG_QUOTA /* Needed for iput() to work correctly and not trash data */ sbi->sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ - f2fs_enable_quota_files(sbi); + quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY); #endif start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); @@ -657,7 +661,8 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) out: #ifdef CONFIG_QUOTA /* Turn quotas off */ - f2fs_quota_off_umount(sbi->sb); + if (quota_enabled) + f2fs_quota_off_umount(sbi->sb); #endif sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index eaf433a6bdd0..834930ce976f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1479,6 +1479,13 @@ static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) return le64_to_cpu(cp->checkpoint_ver); } +static inline unsigned long f2fs_qf_ino(struct super_block *sb, int type) +{ + if (type < F2FS_MAX_QUOTAS) + return le32_to_cpu(F2FS_SB(sb)->raw_super->qf_ino[type]); + return 0; +} + static inline __u64 cur_cp_crc(struct f2fs_checkpoint *cp) { size_t crc_offset = le32_to_cpu(cp->checksum_offset); @@ -2439,6 +2446,7 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) return ret; } +#define sb_rdonly f2fs_readonly static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; @@ -2640,7 +2648,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) */ int f2fs_inode_dirtied(struct inode *inode, bool sync); void f2fs_inode_synced(struct inode *inode); -void f2fs_enable_quota_files(struct f2fs_sb_info *sbi); +int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly); void f2fs_quota_off_umount(struct super_block *sb); int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); int f2fs_sync_fs(struct super_block *sb, int sync); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index bc7503ed742e..559904e9868f 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -582,6 +582,9 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) int ret = 0; unsigned long s_flags = sbi->sb->s_flags; bool need_writecp = false; +#ifdef CONFIG_QUOTA + int quota_enabled; +#endif if (s_flags & MS_RDONLY) { f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); @@ -592,7 +595,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) /* Needed for iput() to work correctly and not trash data */ sbi->sb->s_flags |= MS_ACTIVE; /* Turn on quotas so that they are updated correctly */ - f2fs_enable_quota_files(sbi); + quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY); #endif fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", @@ -653,7 +656,8 @@ skip: out: #ifdef CONFIG_QUOTA /* Turn quotas off */ - f2fs_quota_off_umount(sbi->sb); + if (quota_enabled) + f2fs_quota_off_umount(sbi->sb); #endif sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index efb3c8d6cc74..edf8f75b962b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -219,6 +219,12 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, "quota options when quota turned on"); return -EINVAL; } + if (f2fs_sb_has_quota_ino(sb)) { + f2fs_msg(sb, KERN_INFO, + "QUOTA feature is enabled, so ignore qf_name"); + return 0; + } + qname = match_strdup(args); if (!qname) { f2fs_msg(sb, KERN_ERR, @@ -302,6 +308,18 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) return -1; } } + + if (f2fs_sb_has_quota_ino(sbi->sb) && sbi->s_jquota_fmt) { + f2fs_msg(sbi->sb, KERN_INFO, + "QUOTA feature is enabled, so ignore jquota_fmt"); + sbi->s_jquota_fmt = 0; + } + if (f2fs_sb_has_quota_ino(sbi->sb) && sb_rdonly(sbi->sb)) { + f2fs_msg(sbi->sb, KERN_INFO, + "Filesystem with quota feature cannot be mounted RDWR " + "without CONFIG_QUOTA"); + return -1; + } return 0; } #endif @@ -1188,6 +1206,9 @@ static void default_options(struct f2fs_sb_info *sbi) #endif } +#ifdef CONFIG_QUOTA +static int f2fs_enable_quotas(struct super_block *sb); +#endif static int f2fs_remount(struct super_block *sb, int *flags, char *data) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -1254,6 +1275,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) if (f2fs_readonly(sb) && (*flags & MS_RDONLY)) goto skip; +#ifdef CONFIG_QUOTA if (!f2fs_readonly(sb) && (*flags & MS_RDONLY)) { err = dquot_suspend(sb, -1); if (err < 0) @@ -1261,9 +1283,15 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) } else { /* dquot_resume needs RW */ sb->s_flags &= ~MS_RDONLY; - dquot_resume(sb, -1); + if (sb_any_quota_suspended(sb)) { + dquot_resume(sb, -1); + } else if (f2fs_sb_has_quota_ino(sb)) { + err = f2fs_enable_quotas(sb); + if (err) + goto restore_opts; + } } - +#endif /* disallow enable/disable extent_cache dynamically */ if (no_extent_cache == !!test_opt(sbi, EXTENT_CACHE)) { err = -EINVAL; @@ -1465,19 +1493,93 @@ static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type) sbi->s_jquota_fmt, type); } -void f2fs_enable_quota_files(struct f2fs_sb_info *sbi) +int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly) { - int i, ret; + int enabled = 0; + int i, err; + + if (f2fs_sb_has_quota_ino(sbi->sb) && rdonly) { + err = f2fs_enable_quotas(sbi->sb); + if (err) { + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on quota_ino: %d", err); + return 0; + } + return 1; + } for (i = 0; i < F2FS_MAXQUOTAS; i++) { if (sbi->s_qf_names[i]) { - ret = f2fs_quota_on_mount(sbi, i); - if (ret < 0) - f2fs_msg(sbi->sb, KERN_ERR, - "Cannot turn on journaled " - "quota: error %d", ret); + err = f2fs_quota_on_mount(sbi, i); + if (!err) { + enabled = 1; + continue; + } + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on quotas: %d on %d", err, i); } } + return enabled; +} + +static int f2fs_quota_enable(struct super_block *sb, int type, int format_id, + unsigned int flags) +{ + struct inode *qf_inode; + unsigned long qf_inum; + int err; + + BUG_ON(!f2fs_sb_has_quota_ino(sb)); + + qf_inum = f2fs_qf_ino(sb, type); + if (!qf_inum) + return -EPERM; + + qf_inode = f2fs_iget(sb, qf_inum); + if (IS_ERR(qf_inode)) { + f2fs_msg(sb, KERN_ERR, + "Bad quota inode %u:%lu", type, qf_inum); + return PTR_ERR(qf_inode); + } + + /* Don't account quota for quota files to avoid recursion */ + qf_inode->i_flags |= S_NOQUOTA; + err = dquot_enable(qf_inode, type, format_id, flags); + iput(qf_inode); + return err; +} + +static int f2fs_enable_quotas(struct super_block *sb) +{ + int type, err = 0; + unsigned long qf_inum; + bool quota_mopt[MAXQUOTAS] = { + test_opt(F2FS_SB(sb), USRQUOTA), + test_opt(F2FS_SB(sb), GRPQUOTA), +#if 0 /* not support */ + test_opt(F2FS_SB(sb), PRJQUOTA), +#endif + }; + + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE; + for (type = 0; type < MAXQUOTAS; type++) { + qf_inum = f2fs_qf_ino(sb, type); + if (qf_inum) { + err = f2fs_quota_enable(sb, type, QFMT_VFS_V1, + DQUOT_USAGE_ENABLED | + (quota_mopt[type] ? DQUOT_LIMITS_ENABLED : 0)); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to enable quota tracking " + "(type=%d, err=%d). Please run " + "fsck to fix.", type, err); + for (type--; type >= 0; type--) + dquot_quota_off(sb, type); + return err; + } + } + } + return 0; } static int f2fs_quota_sync(struct super_block *sb, int type) @@ -1537,6 +1639,21 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id, return 0; } +/* + * quota_on function that is used when QUOTA feature is set. + */ +static int f2fs_quota_on_sysfile(struct super_block *sb, int type, + int format_id) +{ + if (!f2fs_sb_has_quota_ino(sb)) + return -EINVAL; + + /* + * USAGE was enabled at mount time. Only need to enable LIMITS now. + */ + return f2fs_quota_enable(sb, type, format_id, DQUOT_LIMITS_ENABLED); +} + static int f2fs_quota_off(struct super_block *sb, int type) { struct inode *inode = sb_dqopt(sb)->files[type]; @@ -1548,7 +1665,7 @@ static int f2fs_quota_off(struct super_block *sb, int type) f2fs_quota_sync(sb, type); err = dquot_quota_off(sb, type); - if (err) + if (err || f2fs_sb_has_quota_ino(sb)) goto out_put; inode_lock(inode); @@ -1561,6 +1678,18 @@ out_put: return err; } +/* + * quota_off function that is used when QUOTA feature is set. + */ +static int f2fs_quota_off_sysfile(struct super_block *sb, int type) +{ + if (!f2fs_sb_has_quota_ino(sb)) + return -EINVAL; + + /* Disable only the limits. */ + return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED); +} + void f2fs_quota_off_umount(struct super_block *sb) { int type; @@ -1599,6 +1728,16 @@ static const struct quotactl_ops f2fs_quotactl_ops = { .get_dqblk = dquot_get_dqblk, .set_dqblk = dquot_set_dqblk, }; + +static const struct quotactl_ops f2fs_quotactl_sysfile_ops = { + .quota_on_meta = f2fs_quota_on_sysfile, + .quota_off = f2fs_quota_off_sysfile, + .quota_sync = dquot_quota_sync, + .get_info = dquot_get_dqinfo, + .set_info = dquot_set_dqinfo, + .get_dqblk = dquot_get_dqblk, + .set_dqblk = dquot_set_dqblk +}; #else void f2fs_quota_off_umount(struct super_block *sb) { @@ -2379,7 +2518,10 @@ try_onemore: #ifdef CONFIG_QUOTA sb->dq_op = &f2fs_quota_operations; - sb->s_qcop = &f2fs_quotactl_ops; + if (f2fs_sb_has_quota_ino(sb)) + sb->s_qcop = &f2fs_quotactl_sysfile_ops; + else + sb->s_qcop = &f2fs_quotactl_ops; #if 0 /* not support */ sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; #endif @@ -2552,10 +2694,24 @@ try_onemore: if (err) goto free_root_inode; +#ifdef CONFIG_QUOTA + /* + * Turn on quotas which were not enabled for read-only mounts if + * filesystem has quota feature, so that they are updated correctly. + */ + if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb)) { + err = f2fs_enable_quotas(sb); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Cannot turn on quotas: error %d", err); + goto free_sysfs; + } + } +#endif /* if there are nt orphan nodes free them */ err = recover_orphan_inodes(sbi); if (err) - goto free_sysfs; + goto free_meta; /* recover fsynced data */ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { @@ -2589,7 +2745,7 @@ try_onemore: err = -EINVAL; f2fs_msg(sb, KERN_ERR, "Need to recover fsync data"); - goto free_sysfs; + goto free_meta; } } skip_recovery: @@ -2623,6 +2779,10 @@ skip_recovery: return 0; free_meta: +#ifdef CONFIG_QUOTA + if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb)) + f2fs_quota_off_umount(sbi->sb); +#endif f2fs_sync_inode_meta(sbi); /* * Some dirty meta pages can be produced by recover_orphan_inodes() @@ -2631,7 +2791,9 @@ free_meta: * falls into an infinite loop in sync_meta_pages(). */ truncate_inode_pages_final(META_MAPPING(sbi)); +#ifdef CONFIG_QUOTA free_sysfs: +#endif f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index c178ac599bb8..30dc9d69d60e 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -36,7 +36,6 @@ #define F2FS_NODE_INO(sbi) (sbi->node_ino_num) #define F2FS_META_INO(sbi) (sbi->meta_ino_num) -#define F2FS_QUOTA_INO 3 #define F2FS_MAX_QUOTAS 3 #define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */ -- GitLab From 4fd29af3ad097e04761a1303e5ff757b54e5cf7e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 30 Oct 2017 17:49:53 +0800 Subject: [PATCH 1578/5498] f2fs: use rw_semaphore to protect SIT cache There are some cases user didn't update SIT cache under this lock, so let's use rw_semaphore instead of mutex to enhance concurrently accessing. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 12 ++++++------ fs/f2fs/segment.c | 34 +++++++++++++++++++--------------- fs/f2fs/segment.h | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c7b1d704846a..ff8f0012888d 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -456,10 +456,10 @@ static int check_valid_map(struct f2fs_sb_info *sbi, struct seg_entry *sentry; int ret; - mutex_lock(&sit_i->sentry_lock); + down_read(&sit_i->sentry_lock); sentry = get_seg_entry(sbi, segno); ret = f2fs_test_bit(offset, sentry->cur_valid_map); - mutex_unlock(&sit_i->sentry_lock); + up_read(&sit_i->sentry_lock); return ret; } @@ -893,10 +893,10 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, struct sit_info *sit_i = SIT_I(sbi); int ret; - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, NO_CHECK_TYPE, LFS); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); return ret; } @@ -944,8 +944,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, /* * this is to avoid deadlock: * - lock_page(sum_page) - f2fs_replace_block - * - check_valid_map() - mutex_lock(sentry_lock) - * - mutex_lock(sentry_lock) - change_curseg() + * - check_valid_map() - down_write(sentry_lock) + * - down_read(sentry_lock) - change_curseg() * - lock_page(sum_page) */ if (type == SUM_TYPE_NODE) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0464fa18c665..f393025d76d2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1985,14 +1985,14 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) return; /* add it into sit main buffer */ - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); update_sit_entry(sbi, addr, -1); /* add it into dirty seglist */ locate_dirty_segment(sbi, segno); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); } bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) @@ -2005,7 +2005,7 @@ bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) return true; - mutex_lock(&sit_i->sentry_lock); + down_read(&sit_i->sentry_lock); segno = GET_SEGNO(sbi, blkaddr); se = get_seg_entry(sbi, segno); @@ -2014,7 +2014,7 @@ bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) if (f2fs_test_bit(offset, se->ckpt_valid_map)) is_cp = true; - mutex_unlock(&sit_i->sentry_lock); + up_read(&sit_i->sentry_lock); return is_cp; } @@ -2410,12 +2410,16 @@ void allocate_new_segments(struct f2fs_sb_info *sbi) unsigned int old_segno; int i; + down_write(&SIT_I(sbi)->sentry_lock); + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { curseg = CURSEG_I(sbi, i); old_segno = curseg->segno; SIT_I(sbi)->s_ops->allocate_segment(sbi, i, true); locate_dirty_segment(sbi, old_segno); } + + up_write(&SIT_I(sbi)->sentry_lock); } static const struct segment_allocation default_salloc_ops = { @@ -2427,14 +2431,14 @@ bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc) __u64 trim_start = cpc->trim_start; bool has_candidate = false; - mutex_lock(&SIT_I(sbi)->sentry_lock); + down_write(&SIT_I(sbi)->sentry_lock); for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { if (add_discard_addrs(sbi, cpc, true)) { has_candidate = true; break; } } - mutex_unlock(&SIT_I(sbi)->sentry_lock); + up_write(&SIT_I(sbi)->sentry_lock); cpc->trim_start = trim_start; return has_candidate; @@ -2594,7 +2598,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, struct curseg_info *curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); @@ -2630,7 +2634,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr)); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); if (page && IS_NODESEG(type)) { fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); @@ -2788,7 +2792,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); old_cursegno = curseg->segno; old_blkoff = curseg->next_blkoff; @@ -2820,7 +2824,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, curseg->next_blkoff = old_blkoff; } - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); mutex_unlock(&curseg->curseg_mutex); } @@ -3275,7 +3279,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) bool to_journal = true; struct seg_entry *se; - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); if (!sit_i->dirty_sentries) goto out; @@ -3369,7 +3373,7 @@ out: cpc->trim_start = trim_start; } - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); set_prefree_as_free_segments(sbi); } @@ -3462,7 +3466,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time); sit_i->mounted_time = CURRENT_TIME_SEC.tv_sec; - mutex_init(&sit_i->sentry_lock); + init_rwsem(&sit_i->sentry_lock); return 0; } @@ -3703,7 +3707,7 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi) struct sit_info *sit_i = SIT_I(sbi); unsigned int segno; - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); sit_i->min_mtime = LLONG_MAX; @@ -3720,7 +3724,7 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi) sit_i->min_mtime = mtime; } sit_i->max_mtime = get_mtime(sbi); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); } int build_segment_manager(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 7a5cc71bbdc5..300e5f02c39c 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -230,7 +230,7 @@ struct sit_info { unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */ unsigned int dirty_sentries; /* # of dirty sentries */ unsigned int sents_per_block; /* # of SIT entries per block */ - struct mutex sentry_lock; /* to protect SIT cache */ + struct rw_semaphore sentry_lock; /* to protect SIT cache */ struct seg_entry *sentries; /* SIT segment-level cache */ struct sec_entry *sec_entries; /* SIT section-level cache */ -- GitLab From c86b0e230e5e4d17fb73175e6968ad9ec2a60451 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 30 Oct 2017 17:49:54 +0800 Subject: [PATCH 1579/5498] f2fs: check curseg space before foreground GC When we are closing to trigger foreground GC, if there are only a few of dirty metas, we can log these dirty metas in left space of opened segments instead of triggering foreground GC. With this patch, total count of foreground GC triggered by test/generic/* of fstest suit reduce from 254 to 184. So let's do the check before foreground GC anyway. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 300e5f02c39c..806e7b7866df 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -496,6 +496,33 @@ static inline int reserved_sections(struct f2fs_sb_info *sbi) return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi)); } +static inline bool has_curseg_enough_space(struct f2fs_sb_info *sbi) +{ + unsigned int node_blocks = get_pages(sbi, F2FS_DIRTY_NODES) + + get_pages(sbi, F2FS_DIRTY_DENTS); + unsigned int dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS); + unsigned int segno, left_blocks; + int i; + + /* check current node segment */ + for (i = CURSEG_HOT_NODE; i <= CURSEG_COLD_NODE; i++) { + segno = CURSEG_I(sbi, i)->segno; + left_blocks = sbi->blocks_per_seg - + get_seg_entry(sbi, segno)->ckpt_valid_blocks; + + if (node_blocks > left_blocks) + return false; + } + + /* check current data segment */ + segno = CURSEG_I(sbi, CURSEG_HOT_DATA)->segno; + left_blocks = sbi->blocks_per_seg - + get_seg_entry(sbi, segno)->ckpt_valid_blocks; + if (dent_blocks > left_blocks) + return false; + return true; +} + static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed, int needed) { @@ -506,6 +533,9 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return false; + if (free_sections(sbi) + freed == reserved_sections(sbi) + needed && + has_curseg_enough_space(sbi)) + return false; return (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + imeta_secs + reserved_sections(sbi) + needed); -- GitLab From 24f6fa056dafa53f6bc93f40b8b6a076c0cf83c3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 30 Oct 2017 11:11:56 -0400 Subject: [PATCH 1580/5498] f2fs: don't bother with inode->i_version f2fs does not set the SB_I_VERSION flag, so the i_version will never be incremented on write. It was recently changed to increment the i_version on a quota write, which isn't necessary here. Signed-off-by: Jeff Layton Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index edf8f75b962b..b06b3ead8cb1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -680,7 +680,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_once((void *) fi); /* Initialize f2fs-specific inode info */ - fi->vfs_inode.i_version = 1; atomic_set(&fi->dirty_pages, 0); fi->i_current_depth = 1; fi->i_advise = 0; @@ -1476,7 +1475,6 @@ retry: if (len == towrite) return err; - inode->i_version++; inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); return len - towrite; -- GitLab From d86513f6f0bbcbc95a213211571b7130bf287fbc Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 2 Nov 2017 20:41:01 +0800 Subject: [PATCH 1581/5498] f2fs: remove unneeded semicolon Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a202ec0acba2..a811f0e3a90f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1017,7 +1017,7 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi) update_inode_page(inode); iput(inode); } - }; + } return 0; } -- GitLab From a1cc64f1bc22020c5383ad2d66588798d92c6d57 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 2 Nov 2017 20:41:02 +0800 Subject: [PATCH 1582/5498] f2fs: remove dead code in update_meta_page After commit a468f0ef516f ("f2fs: use crc and cp version to determine roll-forward recovery"), last caller of update_meta_page passing @src with NULL is gone, so remove related dead code there. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f393025d76d2..d5ed0c5d9a86 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2072,12 +2072,8 @@ struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr) { struct page *page = grab_meta_page(sbi, blk_addr); - void *dst = page_address(page); - if (src) - memcpy(dst, src, PAGE_SIZE); - else - memset(dst, 0, PAGE_SIZE); + memcpy(page_address(page), src, PAGE_SIZE); set_page_dirty(page); f2fs_put_page(page, 1); } -- GitLab From 5bef5b29e71bd44509f933ac13b8dbbfb51f7efe Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 2 Nov 2017 20:41:03 +0800 Subject: [PATCH 1583/5498] f2fs: fix summary info corruption Sometimes, after running generic/270 of fstest, fsck reports summary info and actual position of block address in direct node becoming inconsistent. The root cause is race in between __f2fs_replace_block and change_curseg as below: Thread A Thread B - __clone_blkaddrs - f2fs_replace_block - __f2fs_replace_block - segnoA = GET_SEGNO(sbi, blkaddrA); - type = se->type:=CURSEG_HOT_DATA - if (!IS_CURSEG(sbi, segnoA)) type = CURSEG_WARM_DATA - allocate_data_block - allocate_segment - get_ssr_segment - change_curseg(segnoA, CURSEG_HOT_DATA) - change_curseg(segnoA, CURSEG_WARM_DATA) - reset_curseg - __set_sit_entry_type - change se->type from CURSEG_HOT_DATA to CURSEG_WARM_DATA So finally, hot curseg locates in segnoA, but type of segnoA becomes CURSEG_WARM_DATA. Then if we invoke __f2fs_replace_block(blkaddrB, blkaddrA, true, false), as blkaddrA locates in segnoA, so we will move warm type curseg to segnoA, then change its summary cache and writeback it to summary block. But segnoA is used by hot type curseg too, once it moves or persist, it will cover summary block content with inner old summary cache, result in inconsistent status. This patch tries to fix this issue by introduce global curseg lock to avoid race in between __f2fs_replace_block and change_curseg. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/segment.c | 28 +++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 834930ce976f..ede51e144861 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -914,6 +914,8 @@ struct f2fs_sm_info { struct dirty_seglist_info *dirty_info; /* dirty segment information */ struct curseg_info *curseg_array; /* active segment information */ + struct rw_semaphore curseg_lock; /* for preventing curseg change */ + block_t seg0_blkaddr; /* block address of 0'th segment */ block_t main_blkaddr; /* start block address of main area */ block_t ssa_blkaddr; /* start block address of SSA area */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d5ed0c5d9a86..8b78bcc12594 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2593,6 +2593,8 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, type); + down_read(&SM_I(sbi)->curseg_lock); + mutex_lock(&curseg->curseg_mutex); down_write(&sit_i->sentry_lock); @@ -2650,6 +2652,8 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, } mutex_unlock(&curseg->curseg_mutex); + + up_read(&SM_I(sbi)->curseg_lock); } static void update_device_state(struct f2fs_io_info *fio) @@ -2757,6 +2761,18 @@ int rewrite_data_page(struct f2fs_io_info *fio) return err; } +static inline int __f2fs_get_curseg(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + int i; + + for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) { + if (CURSEG_I(sbi, i)->segno == segno) + break; + } + return i; +} + void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, block_t old_blkaddr, block_t new_blkaddr, bool recover_curseg, bool recover_newaddr) @@ -2772,6 +2788,8 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, se = get_seg_entry(sbi, segno); type = se->type; + down_write(&SM_I(sbi)->curseg_lock); + if (!recover_curseg) { /* for recovery flow */ if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { @@ -2781,8 +2799,13 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, type = CURSEG_WARM_DATA; } } else { - if (!IS_CURSEG(sbi, segno)) + if (IS_CURSEG(sbi, segno)) { + /* se->type is volatile as SSR allocation */ + type = __f2fs_get_curseg(sbi, segno); + f2fs_bug_on(sbi, type == NO_CHECK_TYPE); + } else { type = CURSEG_WARM_DATA; + } } curseg = CURSEG_I(sbi, type); @@ -2822,6 +2845,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, up_write(&sit_i->sentry_lock); mutex_unlock(&curseg->curseg_mutex); + up_write(&SM_I(sbi)->curseg_lock); } void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, @@ -3759,6 +3783,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sm_info->sit_entry_set); + init_rwsem(&sm_info->curseg_lock); + if (!f2fs_readonly(sbi->sb)) { err = create_flush_cmd_control(sbi); if (err) -- GitLab From 1dd480acf660416f287f298c192f989be2b6736a Mon Sep 17 00:00:00 2001 From: Fan Li Date: Thu, 2 Nov 2017 11:02:52 +0800 Subject: [PATCH 1584/5498] f2fs: save a multiplication for last_nid calculation Use a slightly easier way to calculate last_nid. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index db06577be0f1..2bf2d52ee21a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2630,7 +2630,7 @@ static inline void load_free_nid_bitmap(struct f2fs_sb_info *sbi) __set_bit_le(i, nm_i->nat_block_bitmap); nid = i * NAT_ENTRY_PER_BLOCK; - last_nid = (i + 1) * NAT_ENTRY_PER_BLOCK; + last_nid = nid + NAT_ENTRY_PER_BLOCK; spin_lock(&NM_I(sbi)->nid_list_lock); for (; nid < last_nid; nid++) -- GitLab From 9d154a40d815a8e176e6c91ac0b09baa36ac5d53 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 3 Nov 2017 10:21:05 +0800 Subject: [PATCH 1585/5498] f2fs: avoid race in between GC and block exchange During block exchange in {insert,collapse,move}_range, page-block mapping is unstable due to mapping moving or recovery, so there should be no concurrent cache read operation rely on such mapping, nor cache write operation to mess up block exchange. So this patch let background GC be aware of that. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 26 +++++++++++++++++++++----- fs/f2fs/gc.c | 7 +++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 749737838275..645e93597a4f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1171,11 +1171,14 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) if (ret) goto out; + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + truncate_pagecache(inode, offset); ret = f2fs_do_collapse(inode, pg_start, pg_end); if (ret) - goto out; + goto out_unlock; /* write out all moved pages, if possible */ filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); @@ -1187,7 +1190,8 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) ret = truncate_blocks(inode, new_size, true); if (!ret) f2fs_i_size_write(inode, new_size); - +out_unlock: + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); out: up_write(&F2FS_I(inode)->i_mmap_sem); return ret; @@ -1370,6 +1374,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (ret) goto out; + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + truncate_pagecache(inode, offset); pg_start = offset >> PAGE_SHIFT; @@ -1397,6 +1404,8 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); + + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); out: up_write(&F2FS_I(inode)->i_mmap_sem); return ret; @@ -2261,9 +2270,13 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, } inode_lock(src); + down_write(&F2FS_I(src)->dio_rwsem[WRITE]); if (src != dst) { - if (!inode_trylock(dst)) { - ret = -EBUSY; + ret = -EBUSY; + if (!inode_trylock(dst)) + goto out; + if (!down_write_trylock(&F2FS_I(dst)->dio_rwsem[WRITE])) { + inode_unlock(dst); goto out; } } @@ -2323,9 +2336,12 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, } f2fs_unlock_op(sbi); out_unlock: - if (src != dst) + if (src != dst) { + up_write(&F2FS_I(dst)->dio_rwsem[WRITE]); inode_unlock(dst); + } out: + up_write(&F2FS_I(src)->dio_rwsem[WRITE]); inode_unlock(src); return ret; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ff8f0012888d..5d5bba462f26 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -832,10 +832,17 @@ next_step: continue; } + if (!down_write_trylock( + &F2FS_I(inode)->dio_rwsem[WRITE])) { + iput(inode); + continue; + } + start_bidx = start_bidx_of_node(nofs, inode); data_page = get_read_data_page(inode, start_bidx + ofs_in_node, REQ_RAHEAD, true); + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); if (IS_ERR(data_page)) { iput(inode); continue; -- GitLab From e1c850fe940f23e49245a160b35b33b675cf1133 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 5 Nov 2017 21:53:30 +0800 Subject: [PATCH 1586/5498] f2fs: keep isize once block is reserved cross EOF Without FADVISE_KEEP_SIZE_BIT, we will try to recover file size according to last non-hole block, so in fallocate(), we must set FADVISE_KEEP_SIZE_BIT flag once we have preallocated block cross EOF, instead of when all preallocation is success. Otherwise, file size will be incorrect due to lack of this flag. Simple testcase to reproduce this: 1. echo 2 > /sys/fs/f2fs//inject_type 2. echo 10 > /sys/fs/f2fs//inject_rate 3. run tests/generic/392 4. disable fault injection 5. do remount Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 645e93597a4f..aa265976d90b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1455,8 +1455,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset, new_size = ((loff_t)pg_end << PAGE_SHIFT) + off_end; } - if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) - f2fs_i_size_write(inode, new_size); + if (new_size > i_size_read(inode)) { + if (mode & FALLOC_FL_KEEP_SIZE) + file_set_keep_isize(inode); + else + f2fs_i_size_write(inode, new_size); + } return err; } @@ -1513,8 +1517,6 @@ static long f2fs_fallocate(struct file *file, int mode, if (!ret) { inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); - if (mode & FALLOC_FL_KEEP_SIZE) - file_set_keep_isize(inode); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } -- GitLab From 44131bb6ad9ccd2d0bc197137ae13ed045211b50 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 6 Nov 2017 22:51:45 +0800 Subject: [PATCH 1587/5498] f2fs: trace checkpoint reason in fsync() This patch slightly changes need_do_checkpoint to return the detail info that indicates why we need do checkpoint, then caller could print it with trace message. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 12 ++++++++++++ fs/f2fs/file.c | 34 ++++++++++++++++++---------------- include/trace/events/f2fs.h | 24 ++++++++++++++++++------ 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ede51e144861..5cdf9d6741aa 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1007,6 +1007,18 @@ enum need_lock_type { LOCK_RETRY, }; +enum cp_reason_type { + CP_NO_NEEDED, + CP_NON_REGULAR, + CP_HARDLINK, + CP_SB_NEED_CP, + CP_WRONG_PINO, + CP_NO_SPC_ROLL, + CP_NODE_NEED_CP, + CP_FASTBOOT_MODE, + CP_SPEC_LOG_NUM, +}; + enum iostat_type { APP_DIRECT_IO, /* app direct IOs */ APP_BUFFERED_IO, /* app buffered IOs */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index aa265976d90b..02c6450e6e58 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -147,27 +147,29 @@ static int get_parent_ino(struct inode *inode, nid_t *pino) return 1; } -static inline bool need_do_checkpoint(struct inode *inode) +static inline enum cp_reason_type need_do_checkpoint(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - bool need_cp = false; + enum cp_reason_type cp_reason = CP_NO_NEEDED; - if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) - need_cp = true; + if (!S_ISREG(inode->i_mode)) + cp_reason = CP_NON_REGULAR; + else if (inode->i_nlink != 1) + cp_reason = CP_HARDLINK; else if (is_sbi_flag_set(sbi, SBI_NEED_CP)) - need_cp = true; + cp_reason = CP_SB_NEED_CP; else if (file_wrong_pino(inode)) - need_cp = true; + cp_reason = CP_WRONG_PINO; else if (!space_for_roll_forward(sbi)) - need_cp = true; + cp_reason = CP_NO_SPC_ROLL; else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino)) - need_cp = true; + cp_reason = CP_NODE_NEED_CP; else if (test_opt(sbi, FASTBOOT)) - need_cp = true; + cp_reason = CP_FASTBOOT_MODE; else if (sbi->active_logs == 2) - need_cp = true; + cp_reason = CP_SPEC_LOG_NUM; - return need_cp; + return cp_reason; } static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino) @@ -202,7 +204,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t ino = inode->i_ino; int ret = 0; - bool need_cp = false; + enum cp_reason_type cp_reason = 0; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, @@ -221,7 +223,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, clear_inode_flag(inode, FI_NEED_IPU); if (ret) { - trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret); return ret; } @@ -252,10 +254,10 @@ go_write: * sudden-power-off. */ down_read(&F2FS_I(inode)->i_sem); - need_cp = need_do_checkpoint(inode); + cp_reason = need_do_checkpoint(inode); up_read(&F2FS_I(inode)->i_sem); - if (need_cp) { + if (cp_reason) { /* all the dirty node pages should be flushed for POR */ ret = f2fs_sync_fs(inode->i_sb, 1); @@ -312,7 +314,7 @@ flush_out: } f2fs_update_time(sbi, REQ_TIME); out: - trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret); f2fs_trace_ios(NULL, 1); return ret; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 348429f466a6..7e117885dde5 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -91,6 +91,18 @@ { CP_DISCARD, "Discard" }, \ { CP_UMOUNT | CP_TRIMMED, "Umount,Trimmed" }) +#define show_fsync_cpreason(type) \ + __print_symbolic(type, \ + { CP_NO_NEEDED, "no needed" }, \ + { CP_NON_REGULAR, "non regular" }, \ + { CP_HARDLINK, "hardlink" }, \ + { CP_SB_NEED_CP, "sb needs cp" }, \ + { CP_WRONG_PINO, "wrong pino" }, \ + { CP_NO_SPC_ROLL, "no space roll forward" }, \ + { CP_NODE_NEED_CP, "node needs cp" }, \ + { CP_FASTBOOT_MODE, "fastboot mode" }, \ + { CP_SPEC_LOG_NUM, "log type is 2" }) + struct victim_sel_policy; struct f2fs_map_blocks; @@ -165,14 +177,14 @@ DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter, TRACE_EVENT(f2fs_sync_file_exit, - TP_PROTO(struct inode *inode, int need_cp, int datasync, int ret), + TP_PROTO(struct inode *inode, int cp_reason, int datasync, int ret), - TP_ARGS(inode, need_cp, datasync, ret), + TP_ARGS(inode, cp_reason, datasync, ret), TP_STRUCT__entry( __field(dev_t, dev) __field(ino_t, ino) - __field(int, need_cp) + __field(int, cp_reason) __field(int, datasync) __field(int, ret) ), @@ -180,15 +192,15 @@ TRACE_EVENT(f2fs_sync_file_exit, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->need_cp = need_cp; + __entry->cp_reason = cp_reason; __entry->datasync = datasync; __entry->ret = ret; ), - TP_printk("dev = (%d,%d), ino = %lu, checkpoint is %s, " + TP_printk("dev = (%d,%d), ino = %lu, cp_reason: %s, " "datasync = %d, ret = %d", show_dev_ino(__entry), - __entry->need_cp ? "needed" : "not needed", + show_fsync_cpreason(__entry->cp_reason), __entry->datasync, __entry->ret) ); -- GitLab From d3754d20ae32b7f6f7d69f12f6a1ddb59d93433d Mon Sep 17 00:00:00 2001 From: Fan Li Date: Tue, 7 Nov 2017 11:04:33 +0800 Subject: [PATCH 1588/5498] f2fs: keep scanning until enough free nids are acquired In current version, after scan_free_nid_bits, the scan is over if nid_cnt[FREE_NID] != 0. In most cases, there are still free nids in the free list during the scan, and scan_free_nid_bits usually can't increase nid_cnt[FREE_NID]. It causes that __build_free_nids is called many times without solving the shortage of the free nids. This patch fixes that. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2bf2d52ee21a..9fab7414ce3e 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2019,7 +2019,7 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) /* try to find free nids in free_nid_bitmap */ scan_free_nid_bits(sbi); - if (nm_i->nid_cnt[FREE_NID]) + if (nm_i->nid_cnt[FREE_NID] >= NAT_ENTRY_PER_BLOCK) return; } -- GitLab From 59b905175501fce30fd4550e41a20ca7aa2f032f Mon Sep 17 00:00:00 2001 From: Fan Li Date: Tue, 7 Nov 2017 19:14:24 +0800 Subject: [PATCH 1589/5498] f2fs: optimize the way of traversing free_nid_bitmap We call scan_free_nid_bits only when there isn't many free nids left, it means that marked bits in free_nid_bitmap are supposed to be few, use find_next_bit_le is more efficient in such case. According to my tests, use find_next_bit_le instead of test_bit_le will cut down the traversal time to one third of its original. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9fab7414ce3e..d3e08f9729a9 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1959,6 +1959,7 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = curseg->journal; unsigned int i, idx; + nid_t nid; down_read(&nm_i->nat_tree_lock); @@ -1968,10 +1969,10 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) if (!nm_i->free_nid_count[i]) continue; for (idx = 0; idx < NAT_ENTRY_PER_BLOCK; idx++) { - nid_t nid; - - if (!test_bit_le(idx, nm_i->free_nid_bitmap[i])) - continue; + idx = find_next_bit_le(nm_i->free_nid_bitmap[i], + NAT_ENTRY_PER_BLOCK, idx); + if (idx >= NAT_ENTRY_PER_BLOCK) + break; nid = i * NAT_ENTRY_PER_BLOCK + idx; add_free_nid(sbi, nid, true); @@ -1984,7 +1985,6 @@ out: down_read(&curseg->journal_rwsem); for (i = 0; i < nats_in_cursum(journal); i++) { block_t addr; - nid_t nid; addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); nid = le32_to_cpu(nid_in_journal(journal, i)); -- GitLab From d62ae05c399bcb999c5188427262572090188371 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 8 Nov 2017 17:47:36 +0800 Subject: [PATCH 1590/5498] f2fs: introduce scan_curseg_cache for cleanup Commit 4ac912427c42 ("f2fs: introduce free nid bitmap") copied codes from __build_free_nids() into scan_free_nid_bits(), they are redundant, introduce one common function scan_curseg_cache for cleanup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d3e08f9729a9..e860ca08e9fd 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1953,11 +1953,30 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, } } -static void scan_free_nid_bits(struct f2fs_sb_info *sbi) +static void scan_curseg_cache(struct f2fs_sb_info *sbi) { - struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = curseg->journal; + int i; + + down_read(&curseg->journal_rwsem); + for (i = 0; i < nats_in_cursum(journal); i++) { + block_t addr; + nid_t nid; + + addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); + nid = le32_to_cpu(nid_in_journal(journal, i)); + if (addr == NULL_ADDR) + add_free_nid(sbi, nid, true); + else + remove_free_nid(sbi, nid); + } + up_read(&curseg->journal_rwsem); +} + +static void scan_free_nid_bits(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int i, idx; nid_t nid; @@ -1982,26 +2001,14 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) } } out: - down_read(&curseg->journal_rwsem); - for (i = 0; i < nats_in_cursum(journal); i++) { - block_t addr; + scan_curseg_cache(sbi); - addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); - nid = le32_to_cpu(nid_in_journal(journal, i)); - if (addr == NULL_ADDR) - add_free_nid(sbi, nid, true); - else - remove_free_nid(sbi, nid); - } - up_read(&curseg->journal_rwsem); up_read(&nm_i->nat_tree_lock); } static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) { struct f2fs_nm_info *nm_i = NM_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = curseg->journal; int i = 0; nid_t nid = nm_i->next_scan_nid; @@ -2047,18 +2054,8 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) nm_i->next_scan_nid = nid; /* find free nids from current sum_pages */ - down_read(&curseg->journal_rwsem); - for (i = 0; i < nats_in_cursum(journal); i++) { - block_t addr; + scan_curseg_cache(sbi); - addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); - nid = le32_to_cpu(nid_in_journal(journal, i)); - if (addr == NULL_ADDR) - add_free_nid(sbi, nid, true); - else - remove_free_nid(sbi, nid); - } - up_read(&curseg->journal_rwsem); up_read(&nm_i->nat_tree_lock); ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid), -- GitLab From 5799a6dd496dc92b4b35a94037eb066159352104 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 9 Nov 2017 14:51:27 +0900 Subject: [PATCH 1591/5498] f2fs: apply write hints to select the type of segments for buffered write Write hints helps F2FS to determine which type of segments would be selected for buffered write. This patch implements the mapping from write hints to segment types as shown below. hints segment type ----- ------------ WRITE_LIFE_SHORT CURSEG_HOT_DATA WRITE_LIFE_EXTREME CURSEG_COLD_DATA others CURSEG_WARM_DATA the F2FS poliy for hot/cold seperation has precedence over this hints. And hints are not applied in in-place update. Signed-off-by: Hyunchul Lee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 8b78bcc12594..c126195a993a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2515,6 +2515,20 @@ static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) return false; } +#if 0 +int rw_hint_to_seg_type(enum rw_hint hint) +{ + switch (hint) { + case WRITE_LIFE_SHORT: + return CURSEG_HOT_DATA; + case WRITE_LIFE_EXTREME: + return CURSEG_COLD_DATA; + default: + return CURSEG_WARM_DATA; + } +} +#endif + static int __get_segment_type_2(struct f2fs_io_info *fio) { if (fio->type == DATA) @@ -2549,6 +2563,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) return CURSEG_COLD_DATA; if (is_inode_flag_set(inode, FI_HOT_DATA)) return CURSEG_HOT_DATA; + + /* rw_hint_to_seg_type(inode->i_write_hint); */ return CURSEG_WARM_DATA; } else { if (IS_DNODE(fio->page)) -- GitLab From 2007e708bd3b26c38c85aba5fcc7288571f47455 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 10 Nov 2017 09:30:42 +0800 Subject: [PATCH 1592/5498] f2fs: avoid opened loop codes in __add_ino_entry We will keep __add_ino_entry success all the time, for ENOMEM failure case, we have already handled it by using __GFP_NOFAIL flag, so we don't have to use additional opened loop codes here, remove them. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a811f0e3a90f..288966b4c89d 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -408,18 +408,16 @@ static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, struct ino_entry *e, *tmp; tmp = f2fs_kmem_cache_alloc(ino_entry_slab, GFP_NOFS); -retry: + radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); spin_lock(&im->ino_lock); e = radix_tree_lookup(&im->ino_root, ino); if (!e) { e = tmp; - if (radix_tree_insert(&im->ino_root, ino, e)) { - spin_unlock(&im->ino_lock); - radix_tree_preload_end(); - goto retry; - } + if (unlikely(radix_tree_insert(&im->ino_root, ino, e))) + f2fs_bug_on(sbi, 1); + memset(e, 0, sizeof(struct ino_entry)); e->ino = ino; -- GitLab From 52c4320423a42a139dd18577370d64965ad475ef Mon Sep 17 00:00:00 2001 From: LiFan Date: Fri, 10 Nov 2017 15:41:42 +0800 Subject: [PATCH 1593/5498] f2fs: validate before set/clear free nat bitmap In flush_nat_entries, all dirty nats will be flushed and if their new address isn't NULL_ADDR, their bitmaps will be updated, the free_nid_count of the bitmaps will be increaced regardless of whether the nats have already been occupied before. This could lead to wrong free_nid_count. So this patch checks the status of the bits beforeactually set/clear them. Fixes: 586d1492f301 ("f2fs: skip scanning free nid bitmap of full NAT blocks") Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index e860ca08e9fd..ac1fe303ab2d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1910,15 +1910,18 @@ static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) return; - if (set) + if (set) { + if (test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - else - __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - - if (set) nm_i->free_nid_count[nat_ofs]++; - else if (!build) - nm_i->free_nid_count[nat_ofs]--; + } else { + if (!test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; + __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + if (!build) + nm_i->free_nid_count[nat_ofs]--; + } } static void scan_nat_page(struct f2fs_sb_info *sbi, -- GitLab From c269d14336de38698f838431ef18f989137ceeaf Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Fri, 10 Nov 2017 13:36:51 -0800 Subject: [PATCH 1594/5498] f2fs: separate nat entry mem alloc from nat_tree_lock This patch splits memory allocation part in nat_entry to avoid lock contention. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 98 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index ac1fe303ab2d..57e86a43cbf2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -138,6 +138,44 @@ static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid) return dst_page; } +static struct nat_entry *__alloc_nat_entry(nid_t nid, bool no_fail) +{ + struct nat_entry *new; + + if (no_fail) + new = f2fs_kmem_cache_alloc(nat_entry_slab, + GFP_NOFS | __GFP_ZERO); + else + new = kmem_cache_alloc(nat_entry_slab, + GFP_NOFS | __GFP_ZERO); + if (new) { + nat_set_nid(new, nid); + nat_reset_flag(new); + } + return new; +} + +static void __free_nat_entry(struct nat_entry *e) +{ + kmem_cache_free(nat_entry_slab, e); +} + +/* must be locked by nat_tree_lock */ +static struct nat_entry *__init_nat_entry(struct f2fs_nm_info *nm_i, + struct nat_entry *ne, struct f2fs_nat_entry *raw_ne, bool no_fail) +{ + if (no_fail) + f2fs_radix_tree_insert(&nm_i->nat_root, nat_get_nid(ne), ne); + else if (radix_tree_insert(&nm_i->nat_root, nat_get_nid(ne), ne)) + return NULL; + + if (raw_ne) + node_info_from_raw_nat(&ne->ni, raw_ne); + list_add_tail(&ne->list, &nm_i->nat_entries); + nm_i->nat_cnt++; + return ne; +} + static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n) { return radix_tree_lookup(&nm_i->nat_root, n); @@ -154,7 +192,7 @@ static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e) list_del(&e->list); radix_tree_delete(&nm_i->nat_root, nat_get_nid(e)); nm_i->nat_cnt--; - kmem_cache_free(nat_entry_slab, e); + __free_nat_entry(e); } static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, @@ -250,49 +288,29 @@ bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) return need_update; } -static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, - bool no_fail) -{ - struct nat_entry *new; - - if (no_fail) { - new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS); - f2fs_radix_tree_insert(&nm_i->nat_root, nid, new); - } else { - new = kmem_cache_alloc(nat_entry_slab, GFP_NOFS); - if (!new) - return NULL; - if (radix_tree_insert(&nm_i->nat_root, nid, new)) { - kmem_cache_free(nat_entry_slab, new); - return NULL; - } - } - - memset(new, 0, sizeof(struct nat_entry)); - nat_set_nid(new, nid); - nat_reset_flag(new); - list_add_tail(&new->list, &nm_i->nat_entries); - nm_i->nat_cnt++; - return new; -} - +/* must be locked by nat_tree_lock */ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry *ne) { struct f2fs_nm_info *nm_i = NM_I(sbi); - struct nat_entry *e; + struct nat_entry *new, *e; + new = __alloc_nat_entry(nid, false); + if (!new) + return; + + down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); - if (!e) { - e = grab_nat_entry(nm_i, nid, false); - if (e) - node_info_from_raw_nat(&e->ni, ne); - } else { + if (!e) + e = __init_nat_entry(nm_i, new, ne, false); + else f2fs_bug_on(sbi, nat_get_ino(e) != le32_to_cpu(ne->ino) || nat_get_blkaddr(e) != le32_to_cpu(ne->block_addr) || nat_get_version(e) != ne->version); - } + up_write(&nm_i->nat_tree_lock); + if (e != new) + __free_nat_entry(new); } static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, @@ -300,11 +318,12 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; + struct nat_entry *new = __alloc_nat_entry(ni->nid, true); down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, ni->nid); if (!e) { - e = grab_nat_entry(nm_i, ni->nid, true); + e = __init_nat_entry(nm_i, new, NULL, true); copy_node_info(&e->ni, ni); f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR); } else if (new_blkaddr == NEW_ADDR) { @@ -316,6 +335,9 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, copy_node_info(&e->ni, ni); f2fs_bug_on(sbi, ni->blk_addr != NULL_ADDR); } + /* let's free early to reduce memory consumption */ + if (e != new) + __free_nat_entry(new); /* sanity check */ f2fs_bug_on(sbi, nat_get_blkaddr(e) != ni->blk_addr); @@ -424,9 +446,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) f2fs_put_page(page, 1); cache: /* cache nat entry */ - down_write(&nm_i->nat_tree_lock); cache_nat_entry(sbi, nid, &ne); - up_write(&nm_i->nat_tree_lock); } /* @@ -2378,8 +2398,8 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) ne = __lookup_nat_cache(nm_i, nid); if (!ne) { - ne = grab_nat_entry(nm_i, nid, true); - node_info_from_raw_nat(&ne->ni, &raw_ne); + ne = __alloc_nat_entry(nid, true); + __init_nat_entry(nm_i, ne, &raw_ne, true); } /* -- GitLab From bc7ed7f3a112bd5d304fc326a97d34c5ecfe0ff0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Nov 2017 17:46:38 -0800 Subject: [PATCH 1595/5498] f2fs: expose quota information in debugfs This patch shows # of dirty pages and # of hidden quota files. Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 11 +++++++++++ fs/f2fs/f2fs.h | 10 ++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index e11861ff8031..36d6a7277924 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -45,9 +45,18 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA); + si->ndirty_qdata = get_pages(sbi, F2FS_DIRTY_QDATA); si->ndirty_imeta = get_pages(sbi, F2FS_DIRTY_IMETA); si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; + + si->nquota_files = 0; + if (f2fs_sb_has_quota_ino(sbi->sb)) { + for (i = 0; i < MAXQUOTAS; i++) { + if (f2fs_qf_ino(sbi->sb, i)) + si->nquota_files++; + } + } si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->aw_cnt = atomic_read(&sbi->aw_cnt); @@ -371,6 +380,8 @@ static int stat_show(struct seq_file *s, void *v) si->ndirty_dent, si->ndirty_dirs, si->ndirty_all); seq_printf(s, " - datas: %4d in files:%4d\n", si->ndirty_data, si->ndirty_files); + seq_printf(s, " - quota datas: %4d in quota files:%4d\n", + si->ndirty_qdata, si->nquota_files); seq_printf(s, " - meta: %4d in %4d\n", si->ndirty_meta, si->meta_pages); seq_printf(s, " - imeta: %4d\n", diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5cdf9d6741aa..82bab3313b38 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -959,6 +959,7 @@ struct f2fs_sm_info { enum count_type { F2FS_DIRTY_DENTS, F2FS_DIRTY_DATA, + F2FS_DIRTY_QDATA, F2FS_DIRTY_NODES, F2FS_DIRTY_META, F2FS_INMEM_PAGES, @@ -1737,6 +1738,8 @@ static inline void inode_inc_dirty_pages(struct inode *inode) atomic_inc(&F2FS_I(inode)->dirty_pages); inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); + if (IS_NOQUOTA(inode)) + inc_page_count(F2FS_I_SB(inode), F2FS_DIRTY_QDATA); } static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) @@ -1753,6 +1756,8 @@ static inline void inode_dec_dirty_pages(struct inode *inode) atomic_dec(&F2FS_I(inode)->dirty_pages); dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); + if (IS_NOQUOTA(inode)) + dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_QDATA); } static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type) @@ -2887,9 +2892,10 @@ struct f2fs_stat_info { unsigned long long hit_largest, hit_cached, hit_rbtree; unsigned long long hit_total, total_ext; int ext_tree, zombie_tree, ext_node; - int ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta; + int ndirty_node, ndirty_dent, ndirty_meta, ndirty_imeta; + int ndirty_data, ndirty_qdata; int inmem_pages; - unsigned int ndirty_dirs, ndirty_files, ndirty_all; + unsigned int ndirty_dirs, ndirty_files, nquota_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits; int free_nids, avail_nids, alloc_nids; int total_count, utilization; -- GitLab From f3c3ffad64ab28eb3faf5aa749f346371213a1dc Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 13 Nov 2017 17:32:40 +0800 Subject: [PATCH 1596/5498] f2fs: inject fault in inc_valid_node_count This patch adds missing fault injection in inc_valid_node_count. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 82bab3313b38..98c8319908fb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1865,6 +1865,13 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, return ret; } +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_BLOCK)) { + f2fs_show_injection_info(FAULT_BLOCK); + goto enospc; + } +#endif + spin_lock(&sbi->stat_lock); valid_block_count = sbi->total_valid_block_count + 1; -- GitLab From c6f66cb7665837672dd9402e4941d2316a59854c Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 14 Nov 2017 19:28:42 +0800 Subject: [PATCH 1597/5498] f2fs: deny accessing encryption policy if encryption is off This patch adds missing feature check in encryption ioctl interface. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 02c6450e6e58..002a54ba8001 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1912,6 +1912,9 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + if (!f2fs_sb_has_crypto(inode->i_sb)) + return -EOPNOTSUPP; + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return fscrypt_ioctl_set_policy(filp, (const void __user *)arg); @@ -1919,6 +1922,8 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) { + if (!f2fs_sb_has_crypto(file_inode(filp)->i_sb)) + return -EOPNOTSUPP; return fscrypt_ioctl_get_policy(filp, (void __user *)arg); } -- GitLab From 0b02baa2e9945ce807e734fd8422fc978fe53fbe Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Wed, 15 Nov 2017 14:42:32 +0530 Subject: [PATCH 1598/5498] ARM: dts: msm: correct gpio for volume plus key for MSM8909W BG V2 Correct gpio for volume plus key for suspend state for MSM8909W BG V2 target. Change-Id: I1b60bc17a4f62aa2baade2e7c4f53bbd6b158482 Signed-off-by: Shantanu Jain --- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index 72a3a313b8ef..7a7f0120ab12 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -970,7 +970,7 @@ gpio_key_suspend: gpio_key_suspend { mux { - pins = "gpio91", "gpio91", "gpio92"; + pins = "gpio90", "gpio91", "gpio92"; function = "gpio"; }; -- GitLab From 8978589b24080f011f44b77632ccb0002b3b521a Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Wed, 15 Nov 2017 17:35:10 -0800 Subject: [PATCH 1599/5498] ARM: dts: msm: Remove ethernet supply GPIO on mdm9650 CCARD V2 Remove the Neutrino power supply GPIO on CCARD V2 boards to avoid a crash that happens on module remove and system reboot. IPA still tries to access Neutrino registers even after rmmod. Change-Id: Ied34dc022736ab4900a44b519a87b8fe0df3255b Signed-off-by: Gustavo Solaira --- arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-ccard-v2.dts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-ccard-v2.dts b/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-ccard-v2.dts index 19de98bc96cb..17e0db9a3f8c 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-ccard-v2.dts +++ b/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-ccard-v2.dts @@ -25,12 +25,6 @@ status = "ok"; }; -&soc { - ethernet { - ntn-supply-enable-gpio = <&pmd9650_gpios 3 0>; - }; -}; - &usb3 { /* Enable USB detection on v2 */ interrupt-map = <0x0 0 &intc 0 202 0 -- GitLab From 45dd045d4ae32f513f1018be9755589cd8b4561c Mon Sep 17 00:00:00 2001 From: Ashwanth Goli Date: Thu, 16 Nov 2017 15:25:53 +0530 Subject: [PATCH 1600/5498] Revert "defconfig: cleaning up NET related configs for LW" This reverts commit 7c4059a482627e47e3c8ca363caf4b6812189622 since it is buggy and removes polciy based routing related configs causing issues with basic data call and wifi bring-up. Change-Id: I13b8062145026bee8fee46c1a778fa82a33ce0b5 Signed-off-by: Ashwanth Goli --- arch/arm/configs/msm8909w-perf_defconfig | 70 ++++++++++++++++++++++-- arch/arm/configs/msm8909w_defconfig | 64 ++++++++++++++++++++++ 2 files changed, 130 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 8e0542ab2671..77002d404c59 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -63,9 +63,13 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_INET_AH=y @@ -74,24 +78,54 @@ CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set CONFIG_IPV6=y -# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET6_XFRM_MODE_TUNNEL is not set -# CONFIG_INET6_XFRM_MODE_BEET is not set -# CONFIG_IPV6_SIT is not set +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NETMAP=y CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_REDIRECT=y CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -104,11 +138,14 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y +CONFIG_NF_NAT_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -122,10 +159,20 @@ CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_LOG_IPV6=y +CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -133,6 +180,7 @@ CONFIG_NET_CLS_FW=y CONFIG_NET_CLS_U32=y CONFIG_NET_CLS_FLOW=y CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_NBYTE=y CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y @@ -170,9 +218,23 @@ CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_VERITY=y CONFIG_NETDEVICES=y +CONFIG_DUMMY=y CONFIG_IFB=y +CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 354ac91979d3..cb9bd64592bb 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -67,9 +67,13 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_INET_AH=y @@ -78,8 +82,36 @@ CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set CONFIG_IPV6=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y @@ -90,8 +122,12 @@ CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -104,11 +140,14 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y +CONFIG_NF_NAT_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -122,10 +161,20 @@ CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_LOG_IPV6=y +CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -133,6 +182,7 @@ CONFIG_NET_CLS_FW=y CONFIG_NET_CLS_U32=y CONFIG_NET_CLS_FLOW=y CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_NBYTE=y CONFIG_NET_EMATCH_U32=y CONFIG_NET_EMATCH_META=y CONFIG_NET_EMATCH_TEXT=y @@ -170,9 +220,23 @@ CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_VERITY=y CONFIG_NETDEVICES=y +CONFIG_DUMMY=y CONFIG_IFB=y +CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y -- GitLab From 964aa31cf68b978553bf031cd3d0d4fece38deb6 Mon Sep 17 00:00:00 2001 From: Ashwanth Goli Date: Thu, 16 Nov 2017 16:05:31 +0530 Subject: [PATCH 1601/5498] defconfig: cleaning up NET related configs for LW Removing IMS, VPN, NAT related configs since these features are not required on wearable targets. Change-Id: I5d3d8238f702045d3f9b3b91ac920afe141dc817 Signed-off-by: Ashwanth Goli --- arch/arm/configs/msm8909w-perf_defconfig | 53 +----------------------- arch/arm/configs/msm8909w_defconfig | 51 +---------------------- 2 files changed, 2 insertions(+), 102 deletions(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 77002d404c59..aaace32f0aa7 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -63,7 +63,6 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y -CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -72,9 +71,8 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y -CONFIG_INET_AH=y -CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set CONFIG_IPV6=y @@ -88,44 +86,19 @@ CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SIP=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NETMAP=y CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_REDIRECT=y CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -138,18 +111,14 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y -CONFIG_NF_NAT_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_RPFILTER=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y @@ -159,20 +128,12 @@ CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_NF_CONNTRACK_IPV6=y CONFIG_NF_LOG_IPV6=y -CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y -CONFIG_L2TP=y -CONFIG_L2TP_DEBUGFS=y -CONFIG_L2TP_V3=y -CONFIG_L2TP_IP=y -CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -223,18 +184,6 @@ CONFIG_IFB=y CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y -CONFIG_PPP=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_FILTER=y -CONFIG_PPP_MPPE=y -CONFIG_PPP_MULTILINK=y -CONFIG_PPPOE=y -CONFIG_PPPOL2TP=y -CONFIG_PPPOLAC=y -CONFIG_PPPOPNS=y -CONFIG_PPP_ASYNC=y -CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index cb9bd64592bb..8540fd70aeec 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -67,7 +67,6 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y -CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -76,9 +75,8 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y -CONFIG_INET_AH=y -CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set CONFIG_IPV6=y @@ -92,26 +90,7 @@ CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SIP=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y @@ -122,12 +101,8 @@ CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -140,18 +115,14 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y -CONFIG_NF_NAT_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_RPFILTER=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y @@ -161,20 +132,12 @@ CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_NF_CONNTRACK_IPV6=y CONFIG_NF_LOG_IPV6=y -CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y -CONFIG_L2TP=y -CONFIG_L2TP_DEBUGFS=y -CONFIG_L2TP_V3=y -CONFIG_L2TP_IP=y -CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -225,18 +188,6 @@ CONFIG_IFB=y CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y -CONFIG_PPP=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_FILTER=y -CONFIG_PPP_MPPE=y -CONFIG_PPP_MULTILINK=y -CONFIG_PPPOE=y -CONFIG_PPPOL2TP=y -CONFIG_PPPOLAC=y -CONFIG_PPPOPNS=y -CONFIG_PPP_ASYNC=y -CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y -- GitLab From 314fdea217f3ff946fddf7d18db0e65414f54887 Mon Sep 17 00:00:00 2001 From: Rashi Bindra Date: Mon, 21 Aug 2017 19:16:09 +0530 Subject: [PATCH 1602/5498] ARM: dts: msm: Add support for HD+ video mode panel on msm8917 Add changes to add panel init sequence, on/off commands and other panel properties for MirrorLake HD+ (720x1440p) video mode panel for msm8917. Change-Id: I87eb32496ea21c6f94e25b8dab0e19698a0abf78 Signed-off-by: Rashi Bindra --- .../dsi-panel-icn9706-720-1440p-video.dtsi | 98 +++++++++++++++++++ arch/arm/boot/dts/qcom/msm8917-cdp.dtsi | 6 ++ arch/arm/boot/dts/qcom/msm8917-mtp.dtsi | 8 +- .../boot/dts/qcom/msm8937-mdss-panels.dtsi | 3 +- 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/dsi-panel-icn9706-720-1440p-video.dtsi diff --git a/arch/arm/boot/dts/qcom/dsi-panel-icn9706-720-1440p-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-icn9706-720-1440p-video.dtsi new file mode 100644 index 000000000000..50c98a0ad1f4 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-icn9706-720-1440p-video.dtsi @@ -0,0 +1,98 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_icn9706_720_1440_vid: qcom,mdss_dsi_icn9706_720_1440p_video { + qcom,mdss-dsi-panel-name = + "icn9706 720 1440p video mode dsi panel"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1440>; + qcom,mdss-dsi-h-front-porch = <84>; + qcom,mdss-dsi-h-back-porch = <84>; + qcom,mdss-dsi-h-pulse-width = <24>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <20>; + qcom,mdss-dsi-v-front-porch = <24>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [8b 1e 14 00 44 48 18 22 19 + 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x04>; + qcom,mdss-dsi-t-clk-pre = <0x1c>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-on-command = [39 01 00 00 64 00 02 01 00 + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 03 f1 5a 5a + 39 01 00 00 00 00 03 f0 b4 4b + 39 01 00 00 00 00 03 b6 10 10 + 39 01 00 00 00 00 15 b4 0a 08 12 10 0e 0c 00 00 + 00 03 00 03 03 03 03 03 03 03 04 06 + 39 01 00 00 00 00 15 b3 0b 09 13 11 0f 0d 00 00 + 00 03 00 03 03 03 03 03 03 03 05 07 + 39 01 00 00 00 00 0d b0 54 32 23 45 44 44 44 44 + 60 01 60 01 + 39 01 00 00 00 00 09 b1 32 84 02 83 15 01 57 01 + 39 01 00 00 00 00 02 b2 33 + 39 01 00 00 00 00 07 bd 54 14 6a 6a 20 19 + 39 01 00 00 00 00 12 b7 01 01 09 11 0d 15 19 0d + 21 1d 00 00 20 00 02 ff 3c + 39 01 00 00 00 00 06 b8 23 01 30 34 53 + 39 01 00 00 00 00 05 b9 a1 2c ff c4 + 39 01 00 00 00 00 03 ba 88 23 + 39 01 00 00 00 00 07 c1 16 16 04 0c 10 04 + 39 01 00 00 00 00 03 c2 12 68 + 39 01 00 00 00 00 04 c3 22 31 04 + 39 01 00 00 00 00 06 c7 05 23 6b 41 00 + 39 01 00 00 00 00 27 c8 7c 54 3d 2d 26 16 1b 08 + 25 28 2d 4f 3e 48 3d 3d 35 25 06 7c 54 3d 2d + 26 16 1b 08 25 28 2d 4f 3e 48 3d 3d 35 25 06 + 39 01 00 00 00 00 09 c6 00 00 68 00 00 60 36 00 + 05 01 00 00 64 00 02 11 00 + 05 01 00 00 32 00 02 29 00]; + + qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00 + 05 01 00 00 32 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_lp_mode"; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-reset-sequence = <1 2>, <0 20>, <1 50>; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-pan-physical-width-dimension = <63>; + qcom,mdss-pan-physical-height-dimension = <112>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi index 9ddbf75c4042..45416452988e 100644 --- a/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8917-cdp.dtsi @@ -165,6 +165,12 @@ qcom,panel-roi-alignment = <2 2 2 2 2 2>; }; +&dsi_icn9706_720_1440_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; +}; + &tlmm { tlmm_gpio_key { gpio_key_active: gpio_key_active { diff --git a/arch/arm/boot/dts/qcom/msm8917-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8917-mtp.dtsi index 8c28e1e3bc5b..861dd1abb9f3 100644 --- a/arch/arm/boot/dts/qcom/msm8917-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8917-mtp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -145,6 +145,12 @@ qcom,panel-roi-alignment = <2 2 2 2 2 2>; }; +&dsi_icn9706_720_1440_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; +}; + &sdhc_1 { /* device core power supply */ vdd-supply = <&pm8917_l8>; diff --git a/arch/arm/boot/dts/qcom/msm8937-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8937-mdss-panels.dtsi index 34d0e90467fd..7fdcb2d6d599 100644 --- a/arch/arm/boot/dts/qcom/msm8937-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8937-mdss-panels.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,6 +23,7 @@ #include "dsi-panel-truly-wuxga-video.dtsi" #include "dsi-panel-truly-720p-cmd.dtsi" #include "dsi-panel-lead-fl10802-fwvga-video.dtsi" +#include "dsi-panel-icn9706-720-1440p-video.dtsi" &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { -- GitLab From ddf67667002bf53ad39657e37bf0192c25af54c3 Mon Sep 17 00:00:00 2001 From: c_mtharu Date: Wed, 15 Nov 2017 13:39:05 +0530 Subject: [PATCH 1603/5498] msm: ADSPRPC: validate context pointer with magic number Validate context pointer using magic number instead of searching through context list. It removes the usage of spin lock in interrupt handler for avoiding deadlock and reducing latency. Change-Id: I2492a7984a8d6545618a9cfb7a2d239d03ddd5a2 Acked-by: Viswanatham Paduchuri Signed-off-by: Tharun Kumar Merugu --- drivers/char/adsprpc.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index cb98517c5ee6..48b7c0fd2b5a 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -65,6 +65,7 @@ #define BALIGN 128 #define NUM_CHANNELS 3 /*1 compute 1 cpz 1 mdsp*/ #define NUM_SESSIONS 8 /*8 compute*/ +#define FASTRPC_CTX_MAGIC (0xbeeddeed) #define IS_CACHE_ALIGNED(x) (((x) & ((L1_CACHE_BYTES)-1)) == 0) @@ -153,6 +154,7 @@ struct smq_invoke_ctx { struct overlap *overs; struct overlap **overps; struct smq_msg msg; + unsigned int magic; }; struct fastrpc_ctx_lst { @@ -829,6 +831,7 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, ctx->pid = current->pid; ctx->tgid = current->tgid; init_completion(&ctx->work); + ctx->magic = FASTRPC_CTX_MAGIC; spin_lock(&fl->hlock); hlist_add_head(&ctx->hn, &clst->pending); @@ -863,6 +866,7 @@ static void context_free(struct smq_invoke_ctx *ctx) for (i = 0; i < nbufs; ++i) fastrpc_mmap_free(ctx->maps[i]); fastrpc_buf_free(ctx->buf, 1); + ctx->magic = 0; kfree(ctx); } @@ -1285,15 +1289,23 @@ static void fastrpc_smd_read_handler(int cid) { struct fastrpc_apps *me = &gfa; struct smq_invoke_rsp rsp = {0}; - int ret = 0; + struct smq_invoke_ctx *ctx; + int ret = 0, err = 0; do { ret = smd_read_from_cb(me->channel[cid].chan, &rsp, sizeof(rsp)); if (ret != sizeof(rsp)) break; + ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp.ctx)); + VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC)); + if (err) + goto bail; context_notify_user(uint64_to_ptr(rsp.ctx), rsp.retval); } while (ret == sizeof(rsp)); +bail: + if (err) + pr_err("adsprpc: invalid response or context\n"); } static void smd_event_handler(void *priv, unsigned event) @@ -1845,13 +1857,22 @@ void fastrpc_glink_notify_rx(void *handle, const void *priv, const void *pkt_priv, const void *ptr, size_t size) { struct smq_invoke_rsp *rsp = (struct smq_invoke_rsp *)ptr; - int len = size; + struct smq_invoke_ctx *ctx; + int err = 0; - while (len >= sizeof(*rsp) && rsp) { - context_notify_user(uint64_to_ptr(rsp->ctx), rsp->retval); - rsp++; - len = len - sizeof(*rsp); - } + VERIFY(err, (rsp && size >= sizeof(*rsp))); + if (err) + goto bail; + + ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp->ctx)); + VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC)); + if (err) + goto bail; + + context_notify_user(ctx, rsp->retval); +bail: + if (err) + pr_err("adsprpc: invalid response or context\n"); glink_rx_done(handle, ptr, true); } -- GitLab From 204a4c2579591b8150fd36ed9b98fc2f89e662c4 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 16 Nov 2017 20:49:04 +0530 Subject: [PATCH 1604/5498] ASoc: msm: msm_bg: add support to set optimum regulator mode Add support to set optimum regulator mode. Avoid reset of mux configuration during tx and rx concurrent sessions. Change-Id: I33329c37ee622eba6204b9eff56010a8b76e0759 Signed-off-by: Ashish Jain --- sound/soc/msm/msm_bg.c | 90 +++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/sound/soc/msm/msm_bg.c b/sound/soc/msm/msm_bg.c index 471dcf672eea..f7bd6f1018ca 100644 --- a/sound/soc/msm/msm_bg.c +++ b/sound/soc/msm/msm_bg.c @@ -140,6 +140,7 @@ static const char *const vi_feed_ch_text[] = {"One", "Two"}; struct msm8916_asoc_mach_data { int ext_pa; int afe_clk_ver; + atomic_t primary_tdm_ref_count; void __iomem *vaddr_gpio_mux_spkr_ctl; void __iomem *vaddr_gpio_mux_mic_ctl; void __iomem *vaddr_gpio_mux_quin_ctl; @@ -963,29 +964,41 @@ static int msm_tdm_startup(struct snd_pcm_substream *substream) case AFE_PORT_ID_PRIMARY_TDM_TX_5: case AFE_PORT_ID_PRIMARY_TDM_TX_6: case AFE_PORT_ID_PRIMARY_TDM_TX_7: - /* Configure mux for Primary TDM */ - if (pdata->vaddr_gpio_mux_pcm_ctl) { - val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); - val = val | 0x00000001; - iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); - } else { - return -EINVAL; - } - if (pdata->vaddr_gpio_mux_mic_ctl) { - val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); - /*0x02020002 Use this value for master mode*/ - val = val | 0x1808000; /*this is for slave mode*/ - iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); - } else { - return -EINVAL; + atomic_inc(&pdata->primary_tdm_ref_count); + if (atomic_read(&pdata->primary_tdm_ref_count) == 1) { + /* Set regulator to normal mode */ + ret = regulator_set_optimum_mode(pdata->spkr_vreg, + 100000); + if (ret < 0) { + pr_err("Failed to set spkr_vreg mode.\n"); + goto err; + } + /* Configure mux for Primary TDM */ + if (pdata->vaddr_gpio_mux_pcm_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); + val = val | 0x00000001; + iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); + } else { + goto err; + } + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + /*0x02020002 Use this value for master mode*/ + val = val | 0x1808000; /*for slave mode*/ + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } else { + goto err; + } } break; default: pr_err("dai id 0x%x not supported", cpu_dai->id); break; - return -EINVAL; + goto err; } return ret; +err: + return -EINVAL; } static void msm_tdm_shutdown(struct snd_pcm_substream *substream) @@ -994,7 +1007,7 @@ static void msm_tdm_shutdown(struct snd_pcm_substream *substream) struct snd_soc_card *card = rtd->card; struct msm8916_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); - int val = 0; + int ret = 0, val = 0; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -1014,26 +1027,38 @@ static void msm_tdm_shutdown(struct snd_pcm_substream *substream) case AFE_PORT_ID_PRIMARY_TDM_TX_4: case AFE_PORT_ID_PRIMARY_TDM_TX_5: case AFE_PORT_ID_PRIMARY_TDM_TX_6: - /* Reset Configuration of mux for Primary TDM */ - if (pdata->vaddr_gpio_mux_pcm_ctl) { - val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); - val = val & (~0x00000001); - iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); - } else { - return; - } - if (pdata->vaddr_gpio_mux_mic_ctl) { - val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); - /*0x02020002 Use this value for master mode*/ - val = val & (~0x1808000); /*this is for slave mode*/ - iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); - } else { - return; + if (atomic_read(&pdata->primary_tdm_ref_count) > 0) + atomic_dec(&pdata->primary_tdm_ref_count); + if (atomic_read(&pdata->primary_tdm_ref_count) == 0) { + /* Set regulator to standby mode */ + ret = regulator_set_optimum_mode(pdata->spkr_vreg, 0); + if (ret < 0) { + pr_err("Failed to set spkr_vreg mode.\n"); + goto err; + } + /* Reset Configuration of mux for Primary TDM */ + if (pdata->vaddr_gpio_mux_pcm_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); + val = val & (~0x00000001); + iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); + } else { + goto err; + } + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + /*0x02020002 Use this value for master mode*/ + val = val & (~0x1808000); /*for slave mode*/ + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } else { + goto err; + } } break; default: break; } +err: + return; } static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) @@ -2186,6 +2211,7 @@ static int msm_bg_asoc_machine_probe(struct platform_device *pdev) } } } + atomic_set(&pdata->primary_tdm_ref_count, 0); return 0; err_vreg_regulator: -- GitLab From 41f612ae707e301e334872416582460674d03ef6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 15 Nov 2017 18:26:01 +0100 Subject: [PATCH 1605/5498] Revert "ceph: unlock dangling spinlock in try_flush_caps()" This reverts commit 55d4aa12af57ea7782f0c8bbc3b01e44673b05ba which is commit 6c2838fbdedb9b72a81c931d49e56b229b6cdbca upstream. The locking issue was not a problem in 3.18, and now sparse rightly complains about this being an issue, so go back to the "correct" code. Cc: Jeff Layton Cc: "Yan, Zheng" Cc: Ilya Dryomov Reported-by: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- fs/ceph/caps.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 45692e8a9397..cefca661464b 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1748,7 +1748,6 @@ static int try_flush_caps(struct inode *inode, unsigned *flush_tid) retry: spin_lock(&ci->i_ceph_lock); if (ci->i_ceph_flags & CEPH_I_NOFLUSH) { - spin_unlock(&ci->i_ceph_lock); dout("try_flush_caps skipping %p I_NOFLUSH set\n", inode); goto out; } @@ -1766,10 +1765,8 @@ retry: mutex_lock(&session->s_mutex); goto retry; } - if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) { - spin_unlock(&ci->i_ceph_lock); + if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) goto out; - } flushing = __mark_caps_flushing(inode, session); -- GitLab From 6891c6fd2a500d1f39d1426765f610bdc2c2a39d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Sep 2017 14:54:54 +0200 Subject: [PATCH 1606/5498] mac80211: accept key reinstall without changing anything commit fdf7cb4185b60c68e1a75e61691c4afdc15dea0e upstream. When a key is reinstalled we can reset the replay counters etc. which can lead to nonce reuse and/or replay detection being impossible, breaking security properties, as described in the "KRACK attacks". In particular, CVE-2017-13080 applies to GTK rekeying that happened in firmware while the host is in D3, with the second part of the attack being done after the host wakes up. In this case, the wpa_supplicant mitigation isn't sufficient since wpa_supplicant doesn't know the GTK material. In case this happens, simply silently accept the new key coming from userspace but don't take any action on it since it's the same key; this keeps the PN replay counters intact. Signed-off-by: Johannes Berg Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- net/mac80211/key.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 94368404744b..8f794436b028 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -4,6 +4,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007-2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright 2017 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -485,9 +486,6 @@ int ieee80211_key_link(struct ieee80211_key *key, pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; idx = key->conf.keyidx; - key->local = sdata->local; - key->sdata = sdata; - key->sta = sta; mutex_lock(&sdata->local->key_mtx); @@ -498,6 +496,21 @@ int ieee80211_key_link(struct ieee80211_key *key, else old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); + /* + * Silently accept key re-installation without really installing the + * new version of the key to avoid nonce reuse or replay issues. + */ + if (old_key && key->conf.keylen == old_key->conf.keylen && + !memcmp(key->conf.key, old_key->conf.key, key->conf.keylen)) { + ieee80211_key_free_unused(key); + ret = 0; + goto out; + } + + key->local = sdata->local; + key->sdata = sdata; + key->sta = sta; + increment_tailroom_need_count(sdata); ieee80211_key_replace(sdata, sta, pairwise, old_key, key); @@ -513,6 +526,7 @@ int ieee80211_key_link(struct ieee80211_key *key, ret = 0; } + out: mutex_unlock(&sdata->local->key_mtx); return ret; -- GitLab From 32025b5c58605cdc0552d7976d010f10fa13f28a Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 17 Oct 2017 20:32:07 +0200 Subject: [PATCH 1607/5498] mac80211: use constant time comparison with keys commit 2bdd713b92a9cade239d3c7d15205a09f556624d upstream. Otherwise we risk leaking information via timing side channel. Fixes: fdf7cb4185b6 ("mac80211: accept key reinstall without changing anything") Signed-off-by: Jason A. Donenfeld Signed-off-by: Johannes Berg Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- net/mac80211/key.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 8f794436b028..54dfbaac8be6 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -501,7 +502,7 @@ int ieee80211_key_link(struct ieee80211_key *key, * new version of the key to avoid nonce reuse or replay issues. */ if (old_key && key->conf.keylen == old_key->conf.keylen && - !memcmp(key->conf.key, old_key->conf.key, key->conf.keylen)) { + !crypto_memneq(key->conf.key, old_key->conf.key, key->conf.keylen)) { ieee80211_key_free_unused(key); ret = 0; goto out; -- GitLab From 241d7ab103703ef4060ff205aed0643378d013a1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Oct 2017 21:12:13 +0200 Subject: [PATCH 1608/5498] mac80211: don't compare TKIP TX MIC key in reinstall prevention commit cfbb0d90a7abb289edc91833d0905931f8805f12 upstream. For the reinstall prevention, the code I had added compares the whole key. It turns out though that iwlwifi firmware doesn't provide the TKIP TX MIC key as it's not needed in client mode, and thus the comparison will always return false. For client mode, thus always zero out the TX MIC key part before doing the comparison in order to avoid accepting the reinstall of the key with identical encryption and RX MIC key, but not the same TX MIC key (since the supplicant provides the real one.) Fixes: fdf7cb4185b6 ("mac80211: accept key reinstall without changing anything") Signed-off-by: Johannes Berg Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- net/mac80211/key.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 54dfbaac8be6..375ff902a142 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -476,6 +476,39 @@ void ieee80211_key_free_unused(struct ieee80211_key *key) ieee80211_key_free_common(key); } +static bool ieee80211_key_identical(struct ieee80211_sub_if_data *sdata, + struct ieee80211_key *old, + struct ieee80211_key *new) +{ + u8 tkip_old[WLAN_KEY_LEN_TKIP], tkip_new[WLAN_KEY_LEN_TKIP]; + u8 *tk_old, *tk_new; + + if (!old || new->conf.keylen != old->conf.keylen) + return false; + + tk_old = old->conf.key; + tk_new = new->conf.key; + + /* + * In station mode, don't compare the TX MIC key, as it's never used + * and offloaded rekeying may not care to send it to the host. This + * is the case in iwlwifi, for example. + */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + new->conf.cipher == WLAN_CIPHER_SUITE_TKIP && + new->conf.keylen == WLAN_KEY_LEN_TKIP && + !(new->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + memcpy(tkip_old, tk_old, WLAN_KEY_LEN_TKIP); + memcpy(tkip_new, tk_new, WLAN_KEY_LEN_TKIP); + memset(tkip_old + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, 0, 8); + memset(tkip_new + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, 0, 8); + tk_old = tkip_old; + tk_new = tkip_new; + } + + return !crypto_memneq(tk_old, tk_new, new->conf.keylen); +} + int ieee80211_key_link(struct ieee80211_key *key, struct ieee80211_sub_if_data *sdata, struct sta_info *sta) @@ -501,8 +534,7 @@ int ieee80211_key_link(struct ieee80211_key *key, * Silently accept key re-installation without really installing the * new version of the key to avoid nonce reuse or replay issues. */ - if (old_key && key->conf.keylen == old_key->conf.keylen && - !crypto_memneq(key->conf.key, old_key->conf.key, key->conf.keylen)) { + if (ieee80211_key_identical(sdata, old_key, key)) { ieee80211_key_free_unused(key); ret = 0; goto out; -- GitLab From 32530efaed51e4df01e2bc151822143d23ae403a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 29 Sep 2017 10:54:24 -0400 Subject: [PATCH 1609/5498] usb: usbtest: fix NULL pointer dereference commit 7c80f9e4a588f1925b07134bb2e3689335f6c6d8 upstream. If the usbtest driver encounters a device with an IN bulk endpoint but no OUT bulk endpoint, it will try to dereference a NULL pointer (out->desc.bEndpointAddress). The problem can be solved by adding a missing test. Signed-off-by: Alan Stern Reported-by: Andrey Konovalov Tested-by: Andrey Konovalov Signed-off-by: Felipe Balbi Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 58b4c2828ee9..9176837442e9 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -183,12 +183,13 @@ found: return tmp; } - if (in) { + if (in) dev->in_pipe = usb_rcvbulkpipe(udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + if (out) dev->out_pipe = usb_sndbulkpipe(udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - } + if (iso_in) { dev->iso_in = &iso_in->desc; dev->in_iso_pipe = usb_rcvisocpipe(udev, -- GitLab From c0f26c8f0562869e43e5001bed22817e6019d456 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 7 Oct 2017 11:07:47 -0700 Subject: [PATCH 1610/5498] Input: ims-psu - check if CDC union descriptor is sane commit ea04efee7635c9120d015dcdeeeb6988130cb67a upstream. Before trying to use CDC union descriptor, try to validate whether that it is sane by checking that intf->altsetting->extra is big enough and that descriptor bLength is not too big and not too small. Reported-by: Andrey Konovalov Signed-off-by: Dmitry Torokhov Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 25bd4d701722..063898d429c7 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1635,13 +1635,25 @@ ims_pcu_get_cdc_union_desc(struct usb_interface *intf) return NULL; } - while (buflen > 0) { + while (buflen >= sizeof(*union_desc)) { union_desc = (struct usb_cdc_union_desc *)buf; + if (union_desc->bLength > buflen) { + dev_err(&intf->dev, "Too large descriptor\n"); + return NULL; + } + if (union_desc->bDescriptorType == USB_DT_CS_INTERFACE && union_desc->bDescriptorSubType == USB_CDC_UNION_TYPE) { dev_dbg(&intf->dev, "Found union header\n"); - return union_desc; + + if (union_desc->bLength >= sizeof(*union_desc)) + return union_desc; + + dev_err(&intf->dev, + "Union descriptor to short (%d vs %zd\n)", + union_desc->bLength, sizeof(*union_desc)); + return NULL; } buflen -= union_desc->bLength; -- GitLab From 84ea7fc42294c7bbc4f6dc9ee7aa57366c9cf0c7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 16 Nov 2017 14:29:13 +0100 Subject: [PATCH 1611/5498] Revert "ARM: dts: imx53-qsb-common: fix FEC pinmux config" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2eb85ef18c6570e8a59643cd8d5a66122461b1fc which is commit 8b649e426336d7d4800ff9c82858328f4215ba01 upstream. Turns out not to be a good idea in the stable kernels for now as Patrick writes: As discussed for 4.4 stable queue this patch might break existing machines, if they use a different pinmux configuration with their own bootloader. Reported-by: Patrick Brünn Cc: Shawn Guo Cc: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/imx53-qsb-common.dtsi | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi index 53e81589368a..1f55187ed9ce 100644 --- a/arch/arm/boot/dts/imx53-qsb-common.dtsi +++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi @@ -215,16 +215,16 @@ pinctrl_fec: fecgrp { fsl,pins = < - MX53_PAD_FEC_MDC__FEC_MDC 0x4 - MX53_PAD_FEC_MDIO__FEC_MDIO 0x1fc - MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x180 - MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x180 - MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x180 - MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x180 - MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x180 - MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x4 - MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x4 - MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x4 + MX53_PAD_FEC_MDC__FEC_MDC 0x80000000 + MX53_PAD_FEC_MDIO__FEC_MDIO 0x80000000 + MX53_PAD_FEC_REF_CLK__FEC_TX_CLK 0x80000000 + MX53_PAD_FEC_RX_ER__FEC_RX_ER 0x80000000 + MX53_PAD_FEC_CRS_DV__FEC_RX_DV 0x80000000 + MX53_PAD_FEC_RXD1__FEC_RDATA_1 0x80000000 + MX53_PAD_FEC_RXD0__FEC_RDATA_0 0x80000000 + MX53_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000 + MX53_PAD_FEC_TXD1__FEC_TDATA_1 0x80000000 + MX53_PAD_FEC_TXD0__FEC_TDATA_0 0x80000000 >; }; -- GitLab From 5b0a3512492f4e5b47cadaa609d62e0363de667c Mon Sep 17 00:00:00 2001 From: Craig Gallek Date: Mon, 30 Oct 2017 18:50:11 -0400 Subject: [PATCH 1612/5498] tun/tap: sanitize TUNSETSNDBUF input [ Upstream commit 93161922c658c714715686cd0cf69b090cb9bf1d ] Syzkaller found several variants of the lockup below by setting negative values with the TUNSETSNDBUF ioctl. This patch adds a sanity check to both the tun and tap versions of this ioctl. watchdog: BUG: soft lockup - CPU#0 stuck for 22s! [repro:2389] Modules linked in: irq event stamp: 329692056 hardirqs last enabled at (329692055): [] _raw_spin_unlock_irqrestore+0x31/0x75 hardirqs last disabled at (329692056): [] apic_timer_interrupt+0x98/0xb0 softirqs last enabled at (35659740): [] __do_softirq+0x328/0x48c softirqs last disabled at (35659731): [] irq_exit+0xbc/0xd0 CPU: 0 PID: 2389 Comm: repro Not tainted 4.14.0-rc7 #23 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 task: ffff880009452140 task.stack: ffff880006a20000 RIP: 0010:_raw_spin_lock_irqsave+0x11/0x80 RSP: 0018:ffff880006a27c50 EFLAGS: 00000282 ORIG_RAX: ffffffffffffff10 RAX: ffff880009ac68d0 RBX: ffff880006a27ce0 RCX: 0000000000000000 RDX: 0000000000000001 RSI: ffff880006a27ce0 RDI: ffff880009ac6900 RBP: ffff880006a27c60 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000001 R11: 000000000063ff00 R12: ffff880009ac6900 R13: ffff880006a27cf8 R14: 0000000000000001 R15: ffff880006a27cf8 FS: 00007f4be4838700(0000) GS:ffff88000cc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020101000 CR3: 0000000009616000 CR4: 00000000000006f0 Call Trace: prepare_to_wait+0x26/0xc0 sock_alloc_send_pskb+0x14e/0x270 ? remove_wait_queue+0x60/0x60 tun_get_user+0x2cc/0x19d0 ? __tun_get+0x60/0x1b0 tun_chr_write_iter+0x57/0x86 __vfs_write+0x156/0x1e0 vfs_write+0xf7/0x230 SyS_write+0x57/0xd0 entry_SYSCALL_64_fastpath+0x1f/0xbe RIP: 0033:0x7f4be4356df9 RSP: 002b:00007ffc18101c08 EFLAGS: 00000293 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f4be4356df9 RDX: 0000000000000046 RSI: 0000000020101000 RDI: 0000000000000005 RBP: 00007ffc18101c40 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000000001 R11: 0000000000000293 R12: 0000559c75f64780 R13: 00007ffc18101d30 R14: 0000000000000000 R15: 0000000000000000 Fixes: 33dccbb050bb ("tun: Limit amount of queued packets per device") Fixes: 20d29d7a916a ("net: macvtap driver") Signed-off-by: Craig Gallek Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/macvtap.c | 2 ++ drivers/net/tun.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 91120f0ed98c..d058c35b4985 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -1047,6 +1047,8 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, case TUNSETSNDBUF: if (get_user(u, up)) return -EFAULT; + if (u <= 0) + return -EINVAL; q->sk.sk_sndbuf = u; return 0; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a4685a22f665..cd0fbf9b0a22 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2054,6 +2054,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, ret = -EFAULT; break; } + if (sndbuf <= 0) { + ret = -EINVAL; + break; + } tun->sndbuf = sndbuf; tun_set_sndbuf(tun); -- GitLab From 54bcfd5d715280ae21adabf724df68f4f3bf9051 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 30 Oct 2017 23:08:20 -0700 Subject: [PATCH 1613/5498] tcp: fix tcp_mtu_probe() vs highest_sack [ Upstream commit 2b7cda9c35d3b940eb9ce74b30bbd5eb30db493d ] Based on SNMP values provided by Roman, Yuchung made the observation that some crashes in tcp_sacktag_walk() might be caused by MTU probing. Looking at tcp_mtu_probe(), I found that when a new skb was placed in front of the write queue, we were not updating tcp highest sack. If one skb is freed because all its content was copied to the new skb (for MTU probing), then tp->highest_sack could point to a now freed skb. Bad things would then happen, including infinite loops. This patch renames tcp_highest_sack_combine() and uses it from tcp_mtu_probe() to fix the bug. Note that I also removed one test against tp->sacked_out, since we want to replace tp->highest_sack regardless of whatever condition, since keeping a stale pointer to freed skb is a recipe for disaster. Fixes: a47e5a988a57 ("[TCP]: Convert highest_sack to sk_buff to allow direct access") Signed-off-by: Eric Dumazet Reported-by: Alexei Starovoitov Reported-by: Roman Gushchin Reported-by: Oleksandr Natalenko Acked-by: Alexei Starovoitov Acked-by: Neal Cardwell Acked-by: Yuchung Cheng Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/tcp.h | 6 +++--- net/ipv4/tcp_output.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 7219b8f38cef..2cd7003a8a19 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1532,12 +1532,12 @@ static inline void tcp_highest_sack_reset(struct sock *sk) tcp_sk(sk)->highest_sack = tcp_write_queue_head(sk); } -/* Called when old skb is about to be deleted (to be combined with new skb) */ -static inline void tcp_highest_sack_combine(struct sock *sk, +/* Called when old skb is about to be deleted and replaced by new skb */ +static inline void tcp_highest_sack_replace(struct sock *sk, struct sk_buff *old, struct sk_buff *new) { - if (tcp_sk(sk)->sacked_out && (old == tcp_sk(sk)->highest_sack)) + if (old == tcp_highest_sack(sk)) tcp_sk(sk)->highest_sack = new; } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 2adfa23285be..ff47e881e205 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1870,6 +1870,7 @@ static int tcp_mtu_probe(struct sock *sk) nskb->ip_summed = skb->ip_summed; tcp_insert_write_queue_before(nskb, skb, sk); + tcp_highest_sack_replace(sk, skb, nskb); len = 0; tcp_for_write_queue_from_safe(skb, next, sk) { @@ -2374,7 +2375,7 @@ static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) BUG_ON(tcp_skb_pcount(skb) != 1 || tcp_skb_pcount(next_skb) != 1); - tcp_highest_sack_combine(sk, next_skb, skb); + tcp_highest_sack_replace(sk, next_skb, skb); tcp_unlink_write_queue(next_skb, sk); -- GitLab From a5bec3f163eec0d94516abc0d4106994a6610ff7 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Fri, 13 Oct 2017 19:22:35 +0200 Subject: [PATCH 1614/5498] l2tp: check ps->sock before running pppol2tp_session_ioctl() [ Upstream commit 5903f594935a3841137c86b9d5b75143a5b7121c ] When pppol2tp_session_ioctl() is called by pppol2tp_tunnel_ioctl(), the session may be unconnected. That is, it was created by pppol2tp_session_create() and hasn't been connected with pppol2tp_connect(). In this case, ps->sock is NULL, so we need to check for this case in order to avoid dereferencing a NULL pointer. Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP") Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/l2tp/l2tp_ppp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 5a234fbe2fec..447843b24d7d 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1017,6 +1017,9 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session, session->name, cmd, arg); sk = ps->sock; + if (!sk) + return -EBADR; + sock_hold(sk); switch (cmd) { -- GitLab From 638c8339e05eb7eee584dfe4b3102376cf35664b Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 13 Oct 2017 11:58:53 -0700 Subject: [PATCH 1615/5498] tun: call dev_get_valid_name() before register_netdevice() [ Upstream commit 0ad646c81b2182f7fa67ec0c8c825e0ee165696d ] register_netdevice() could fail early when we have an invalid dev name, in which case ->ndo_uninit() is not called. For tun device, this is a problem because a timer etc. are already initialized and it expects ->ndo_uninit() to clean them up. We could move these initializations into a ->ndo_init() so that register_netdevice() knows better, however this is still complicated due to the logic in tun_detach(). Therefore, I choose to just call dev_get_valid_name() before register_netdevice(), which is quicker and much easier to audit. And for this specific case, it is already enough. Fixes: 96442e42429e ("tuntap: choose the txq based on rxq") Reported-by: Dmitry Alexeev Cc: Jason Wang Cc: "Michael S. Tsirkin" Signed-off-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/tun.c | 3 +++ include/linux/netdevice.h | 3 +++ net/core/dev.c | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index cd0fbf9b0a22..cd6b6d3fd4e2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1656,6 +1656,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (!dev) return -ENOMEM; + err = dev_get_valid_name(net, dev, name); + if (err) + goto err_free_dev; dev_net_set(dev, net); dev->rtnl_link_ops = &tun_link_ops; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 49ac10f99da0..de98fd0fc5b0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3245,6 +3245,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, unsigned char name_assign_type, void (*setup)(struct net_device *), unsigned int txqs, unsigned int rxqs); +int dev_get_valid_name(struct net *net, struct net_device *dev, + const char *name); + #define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \ alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1) diff --git a/net/core/dev.c b/net/core/dev.c index 21b0bd976699..93e36e9102e9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1059,9 +1059,8 @@ static int dev_alloc_name_ns(struct net *net, return ret; } -static int dev_get_valid_name(struct net *net, - struct net_device *dev, - const char *name) +int dev_get_valid_name(struct net *net, struct net_device *dev, + const char *name) { BUG_ON(!net); @@ -1077,6 +1076,7 @@ static int dev_get_valid_name(struct net *net, return 0; } +EXPORT_SYMBOL(dev_get_valid_name); /** * dev_change_name - change name of a device -- GitLab From 698b423e409fc4c169afd43d50ba2f16a0b9fd73 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 18 Oct 2017 21:37:49 +0800 Subject: [PATCH 1616/5498] sctp: add the missing sock_owned_by_user check in sctp_icmp_redirect [ Upstream commit 1cc276cec9ec574d41cf47dfc0f51406b6f26ab4 ] Now sctp processes icmp redirect packet in sctp_icmp_redirect where it calls sctp_transport_dst_check in which tp->dst can be released. The problem is before calling sctp_transport_dst_check, it doesn't check sock_owned_by_user, which means tp->dst could be freed while a process is accessing it with owning the socket. An use-after-free issue could be triggered by this. This patch is to fix it by checking sock_owned_by_user before calling sctp_transport_dst_check in sctp_icmp_redirect, so that it would not release tp->dst if users still hold sock lock. Besides, the same issue fixed in commit 45caeaa5ac0b ("dccp/tcp: fix routing redirect race") on sctp also needs this check. Fixes: 55be7a9c6074 ("ipv4: Add redirect support to all protocol icmp error handlers") Reported-by: Eric Dumazet Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Acked-by: Neil Horman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/input.c b/net/sctp/input.c index 2d7859c03fd2..71c2ef84c5b0 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -420,7 +420,7 @@ void sctp_icmp_redirect(struct sock *sk, struct sctp_transport *t, { struct dst_entry *dst; - if (!t) + if (sock_owned_by_user(sk) || !t) return; dst = sctp_transport_dst_check(t); if (dst) -- GitLab From 5c39875a93d64fc2a3581b93e1db77dc25ca67b0 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 25 Oct 2017 10:16:42 -0700 Subject: [PATCH 1617/5498] net/unix: don't show information about sockets from other namespaces [ Upstream commit 0f5da659d8f1810f44de14acf2c80cd6499623a0 ] socket_diag shows information only about sockets from a namespace where a diag socket lives. But if we request information about one unix socket, the kernel don't check that its netns is matched with a diag socket namespace, so any user can get information about any unix socket in a system. This looks like a bug. v2: add a Fixes tag Fixes: 51d7cccf0723 ("net: make sock diag per-namespace") Signed-off-by: Andrei Vagin Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/unix/diag.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/unix/diag.c b/net/unix/diag.c index 9d4218fc0a61..c4e4e2f664e8 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -256,6 +256,8 @@ static int unix_diag_get_exact(struct sk_buff *in_skb, err = -ENOENT; if (sk == NULL) goto out_nosk; + if (!net_eq(sock_net(sk), net)) + goto out; err = sock_diag_check_cookie(sk, req->udiag_cookie); if (err) -- GitLab From 0e14c46808100ab64d3c935de776afb0ed7f0550 Mon Sep 17 00:00:00 2001 From: Julien Gomes Date: Wed, 25 Oct 2017 11:50:50 -0700 Subject: [PATCH 1618/5498] tun: allow positive return values on dev_get_valid_name() call [ Upstream commit 5c25f65fd1e42685f7ccd80e0621829c105785d9 ] If the name argument of dev_get_valid_name() contains "%d", it will try to assign it a unit number in __dev__alloc_name() and return either the unit number (>= 0) or an error code (< 0). Considering positive values as error values prevent tun device creations relying this mechanism, therefor we should only consider negative values as errors here. Signed-off-by: Julien Gomes Acked-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/tun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index cd6b6d3fd4e2..3067f840210e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1657,7 +1657,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (!dev) return -ENOMEM; err = dev_get_valid_name(net, dev, name); - if (err) + if (err < 0) goto err_free_dev; dev_net_set(dev, net); -- GitLab From e9a09040718d4c7dcc932c2e9fbea33887df54e8 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 28 Oct 2017 02:13:29 +0800 Subject: [PATCH 1619/5498] sctp: reset owner sk for data chunks on out queues when migrating a sock [ Upstream commit d04adf1b355181e737b6b1e23d801b07f0b7c4c0 ] Now when migrating sock to another one in sctp_sock_migrate(), it only resets owner sk for the data in receive queues, not the chunks on out queues. It would cause that data chunks length on the sock is not consistent with sk sk_wmem_alloc. When closing the sock or freeing these chunks, the old sk would never be freed, and the new sock may crash due to the overflow sk_wmem_alloc. syzbot found this issue with this series: r0 = socket$inet_sctp() sendto$inet(r0) listen(r0) accept4(r0) close(r0) Although listen() should have returned error when one TCP-style socket is in connecting (I may fix this one in another patch), it could also be reproduced by peeling off an assoc. This issue is there since very beginning. This patch is to reset owner sk for the chunks on out queues so that sk sk_wmem_alloc has correct value after accept one sock or peeloff an assoc to one sock. Note that when resetting owner sk for chunks on outqueue, it has to sctp_clear_owner_w/skb_orphan chunks before changing assoc->base.sk first and then sctp_set_owner_w them after changing assoc->base.sk, due to that sctp_wfree and it's callees are using assoc->base.sk. Reported-by: Dmitry Vyukov Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/socket.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 92c6eac72ea6..07324ca1df1d 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -173,6 +173,36 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk) sk_mem_charge(sk, chunk->skb->truesize); } +static void sctp_clear_owner_w(struct sctp_chunk *chunk) +{ + skb_orphan(chunk->skb); +} + +static void sctp_for_each_tx_datachunk(struct sctp_association *asoc, + void (*cb)(struct sctp_chunk *)) + +{ + struct sctp_outq *q = &asoc->outqueue; + struct sctp_transport *t; + struct sctp_chunk *chunk; + + list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) + list_for_each_entry(chunk, &t->transmitted, transmitted_list) + cb(chunk); + + list_for_each_entry(chunk, &q->retransmit, list) + cb(chunk); + + list_for_each_entry(chunk, &q->sacked, list) + cb(chunk); + + list_for_each_entry(chunk, &q->abandoned, list) + cb(chunk); + + list_for_each_entry(chunk, &q->out_chunk_list, list) + cb(chunk); +} + /* Verify that this is a valid address. */ static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len) @@ -7367,7 +7397,9 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, * paths won't try to lock it and then oldsk. */ lock_sock_nested(newsk, SINGLE_DEPTH_NESTING); + sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w); sctp_assoc_migrate(assoc, newsk); + sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w); /* If the association on the newsk is already closed before accept() * is called, set RCV_SHUTDOWN flag. -- GitLab From 6e69760c8c44937b0664b0fbc71a190cafd9c1e3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 21 Oct 2017 12:26:23 -0700 Subject: [PATCH 1620/5498] ipv6: flowlabel: do not leave opt->tot_len with garbage [ Upstream commit 864e2a1f8aac05effac6063ce316b480facb46ff ] When syzkaller team brought us a C repro for the crash [1] that had been reported many times in the past, I finally could find the root cause. If FlowLabel info is merged by fl6_merge_options(), we leave part of the opt_space storage provided by udp/raw/l2tp with random value in opt_space.tot_len, unless a control message was provided at sendmsg() time. Then ip6_setup_cork() would use this random value to perform a kzalloc() call. Undefined behavior and crashes. Fix is to properly set tot_len in fl6_merge_options() At the same time, we can also avoid consuming memory and cpu cycles to clear it, if every option is copied via a kmemdup(). This is the change in ip6_setup_cork(). [1] kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 0 PID: 6613 Comm: syz-executor0 Not tainted 4.14.0-rc4+ #127 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 task: ffff8801cb64a100 task.stack: ffff8801cc350000 RIP: 0010:ip6_setup_cork+0x274/0x15c0 net/ipv6/ip6_output.c:1168 RSP: 0018:ffff8801cc357550 EFLAGS: 00010203 RAX: dffffc0000000000 RBX: ffff8801cc357748 RCX: 0000000000000010 RDX: 0000000000000002 RSI: ffffffff842bd1d9 RDI: 0000000000000014 RBP: ffff8801cc357620 R08: ffff8801cb17f380 R09: ffff8801cc357b10 R10: ffff8801cb64a100 R11: 0000000000000000 R12: ffff8801cc357ab0 R13: ffff8801cc357b10 R14: 0000000000000000 R15: ffff8801c3bbf0c0 FS: 00007f9c5c459700(0000) GS:ffff8801db200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020324000 CR3: 00000001d1cf2000 CR4: 00000000001406f0 DR0: 0000000020001010 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000600 Call Trace: ip6_make_skb+0x282/0x530 net/ipv6/ip6_output.c:1729 udpv6_sendmsg+0x2769/0x3380 net/ipv6/udp.c:1340 inet_sendmsg+0x11f/0x5e0 net/ipv4/af_inet.c:762 sock_sendmsg_nosec net/socket.c:633 [inline] sock_sendmsg+0xca/0x110 net/socket.c:643 SYSC_sendto+0x358/0x5a0 net/socket.c:1750 SyS_sendto+0x40/0x50 net/socket.c:1718 entry_SYSCALL_64_fastpath+0x1f/0xbe RIP: 0033:0x4520a9 RSP: 002b:00007f9c5c458c08 EFLAGS: 00000216 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 0000000000718000 RCX: 00000000004520a9 RDX: 0000000000000001 RSI: 0000000020fd1000 RDI: 0000000000000016 RBP: 0000000000000086 R08: 0000000020e0afe4 R09: 000000000000001c R10: 0000000000000000 R11: 0000000000000216 R12: 00000000004bb1ee R13: 00000000ffffffff R14: 0000000000000016 R15: 0000000000000029 Code: e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85 ea 0f 00 00 48 8d 79 04 48 b8 00 00 00 00 00 fc ff df 45 8b 74 24 04 48 89 fa 48 c1 ea 03 <0f> b6 14 02 48 89 f8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85 RIP: ip6_setup_cork+0x274/0x15c0 net/ipv6/ip6_output.c:1168 RSP: ffff8801cc357550 Signed-off-by: Eric Dumazet Reported-by: Dmitry Vyukov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_flowlabel.c | 1 + net/ipv6/ip6_output.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index a837f2065d2a..1a83031bcddd 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -316,6 +316,7 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space, } opt_space->dst1opt = fopt->dst1opt; opt_space->opt_flen = fopt->opt_flen; + opt_space->tot_len = fopt->tot_len; return opt_space; } EXPORT_SYMBOL_GPL(fl6_merge_options); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index f71de459139f..6db1f8ad8ac3 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1177,11 +1177,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, if (WARN_ON(np->cork.opt)) return -EINVAL; - np->cork.opt = kzalloc(opt->tot_len, sk->sk_allocation); + np->cork.opt = kzalloc(sizeof(*opt), sk->sk_allocation); if (unlikely(np->cork.opt == NULL)) return -ENOBUFS; - np->cork.opt->tot_len = opt->tot_len; + np->cork.opt->tot_len = sizeof(*opt); np->cork.opt->opt_flen = opt->opt_flen; np->cork.opt->opt_nflen = opt->opt_nflen; -- GitLab From 53380d17dfa8a31c015c5b1b7d45fb5ad0bb06a8 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 26 Oct 2017 19:19:56 +0800 Subject: [PATCH 1621/5498] ipip: only increase err_count for some certain type icmp in ipip_err [ Upstream commit f3594f0a7ea36661d7fd942facd7f31a64245f1a ] t->err_count is used to count the link failure on tunnel and an err will be reported to user socket in tx path if t->err_count is not 0. udp socket could even return EHOSTUNREACH to users. Since commit fd58156e456d ("IPIP: Use ip-tunneling code.") removed the 'switch check' for icmp type in ipip_err(), err_count would be increased by the icmp packet with ICMP_EXC_FRAGTIME code. an link failure would be reported out due to this. In Jianlin's case, when receiving ICMP_EXC_FRAGTIME a icmp packet, udp netperf failed with the err: send_data: data send error: No route to host (errno 113) We expect this error reported from tunnel to socket when receiving some certain type icmp, but not ICMP_EXC_FRAGTIME, ICMP_SR_FAILED or ICMP_PARAMETERPROB ones. This patch is to bring 'switch check' for icmp type back to ipip_err so that it only reports link failure for the right type icmp, just as in ipgre_err() and ipip6_err(). Fixes: fd58156e456d ("IPIP: Use ip-tunneling code.") Reported-by: Jianlin Shi Signed-off-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ipip.c | 59 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 37096d64730e..1dac2436b4d5 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -128,43 +128,68 @@ static struct rtnl_link_ops ipip_link_ops __read_mostly; static int ipip_err(struct sk_buff *skb, u32 info) { - -/* All the routers (except for Linux) return only - 8 bytes of packet payload. It means, that precise relaying of - ICMP in the real Internet is absolutely infeasible. - */ + /* All the routers (except for Linux) return only + 8 bytes of packet payload. It means, that precise relaying of + ICMP in the real Internet is absolutely infeasible. + */ struct net *net = dev_net(skb->dev); struct ip_tunnel_net *itn = net_generic(net, ipip_net_id); const struct iphdr *iph = (const struct iphdr *)skb->data; - struct ip_tunnel *t; - int err; const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; + struct ip_tunnel *t; + int err = 0; + + switch (type) { + case ICMP_DEST_UNREACH: + switch (code) { + case ICMP_SR_FAILED: + /* Impossible event. */ + goto out; + default: + /* All others are translated to HOST_UNREACH. + * rfc2003 contains "deep thoughts" about NET_UNREACH, + * I believe they are just ether pollution. --ANK + */ + break; + } + break; + + case ICMP_TIME_EXCEEDED: + if (code != ICMP_EXC_TTL) + goto out; + break; + + case ICMP_REDIRECT: + break; + + default: + goto out; + } - err = -ENOENT; t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY, iph->daddr, iph->saddr, 0); - if (t == NULL) + if (!t) { + err = -ENOENT; goto out; + } if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { - ipv4_update_pmtu(skb, dev_net(skb->dev), info, - t->parms.link, 0, IPPROTO_IPIP, 0); - err = 0; + ipv4_update_pmtu(skb, net, info, t->parms.link, 0, + iph->protocol, 0); goto out; } if (type == ICMP_REDIRECT) { - ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0, - IPPROTO_IPIP, 0); - err = 0; + ipv4_redirect(skb, net, t->parms.link, 0, iph->protocol, 0); goto out; } - if (t->parms.iph.daddr == 0) + if (t->parms.iph.daddr == 0) { + err = -ENOENT; goto out; + } - err = 0; if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) goto out; -- GitLab From eb36c7de3dc1b232094e0a9d2c30caa30c99bb9d Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 26 Oct 2017 19:23:27 +0800 Subject: [PATCH 1622/5498] ip6_gre: only increase err_count for some certain type icmpv6 in ip6gre_err [ Upstream commit f8d20b46ce55cf40afb30dcef6d9288f7ef46d9b ] The similar fix in patch 'ipip: only increase err_count for some certain type icmp in ipip_err' is needed for ip6gre_err. In Jianlin's case, udp netperf broke even when receiving a TooBig icmpv6 packet. Fixes: c12b395a4664 ("gre: Support GRE over IPv6") Reported-by: Jianlin Shi Signed-off-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_gre.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 77adf255d238..a97e74023e34 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -409,13 +409,16 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, case ICMPV6_DEST_UNREACH: net_warn_ratelimited("%s: Path to destination invalid or inactive!\n", t->parms.name); - break; + if (code != ICMPV6_PORT_UNREACH) + break; + return; case ICMPV6_TIME_EXCEED: if (code == ICMPV6_EXC_HOPLIMIT) { net_warn_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n", t->parms.name); + break; } - break; + return; case ICMPV6_PARAMPROB: teli = 0; if (code == ICMPV6_HDR_FIELD) @@ -431,13 +434,13 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, net_warn_ratelimited("%s: Recipient unable to parse tunneled packet!\n", t->parms.name); } - break; + return; case ICMPV6_PKT_TOOBIG: mtu = be32_to_cpu(info) - offset; if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; t->dev->mtu = mtu; - break; + return; } if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO)) -- GitLab From 86fa24082f80e1f3b2641f164bbe5d5834abc581 Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Thu, 8 Jun 2017 14:47:26 +0100 Subject: [PATCH 1623/5498] security/keys: add CONFIG_KEYS_COMPAT to Kconfig commit 47b2c3fff4932e6fc17ce13d51a43c6969714e20 upstream. CONFIG_KEYS_COMPAT is defined in arch-specific Kconfigs and is missing for several 64-bit architectures : mips, parisc, tile. At the moment and for those architectures, calling in 32-bit userspace the keyctl syscall would return an ENOSYS error. This patch moves the CONFIG_KEYS_COMPAT option to security/keys/Kconfig, to make sure the compatibility wrapper is registered by default for any 64-bit architecture as long as it is configured with CONFIG_COMPAT. [DH: Modified to remove arm64 compat enablement also as requested by Eric Biggers] Signed-off-by: Bilal Amarni Signed-off-by: David Howells Reviewed-by: Arnd Bergmann cc: Eric Biggers Signed-off-by: James Morris Cc: James Cowgill Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/Kconfig | 5 ----- arch/s390/Kconfig | 3 --- arch/sparc/Kconfig | 3 --- arch/x86/Kconfig | 4 ---- security/keys/Kconfig | 4 ++++ 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 88eace4e28c3..22e64fd346ad 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -1070,11 +1070,6 @@ source "arch/powerpc/Kconfig.debug" source "security/Kconfig" -config KEYS_COMPAT - bool - depends on COMPAT && KEYS - default y - source "crypto/Kconfig" config PPC_LIB_RHEAP diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index f2cf1f90295b..1d814b527924 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -332,9 +332,6 @@ config COMPAT config SYSVIPC_COMPAT def_bool y if COMPAT && SYSVIPC -config KEYS_COMPAT - def_bool y if COMPAT && KEYS - config SMP def_bool y prompt "Symmetric multi-processing support" diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 96ac69c5eba0..e51b50af6373 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -542,9 +542,6 @@ config SYSVIPC_COMPAT depends on COMPAT && SYSVIPC default y -config KEYS_COMPAT - def_bool y if COMPAT && KEYS - endmenu source "net/Kconfig" diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index c9148e268512..a5d100cf8ed6 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2471,10 +2471,6 @@ config COMPAT_FOR_U64_ALIGNMENT config SYSVIPC_COMPAT def_bool y depends on SYSVIPC - -config KEYS_COMPAT - def_bool y - depends on KEYS endif endmenu diff --git a/security/keys/Kconfig b/security/keys/Kconfig index a4f3f8c48d6e..d7734b53b3a8 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -20,6 +20,10 @@ config KEYS If you are unsure as to whether this is required, answer N. +config KEYS_COMPAT + def_bool y + depends on COMPAT && KEYS + config PERSISTENT_KEYRINGS bool "Enable register of persistent per-UID keyrings" depends on KEYS -- GitLab From bc88a575f203a4efb64e05a027063ea706969bef Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 5 Jan 2017 12:39:57 +0100 Subject: [PATCH 1624/5498] target/iscsi: Fix iSCSI task reassignment handling commit 59b6986dbfcdab96a971f9663221849de79a7556 upstream. Allocate a task management request structure for all task management requests, including task reassignment. This change avoids that the se_tmr->response assignment dereferences an uninitialized se_tmr pointer. Reported-by: Moshe David Signed-off-by: Bart Van Assche Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Cc: Moshe David Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target.c | 19 +++++++------------ include/target/target_core_base.h | 1 + 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index ce844e980990..801369f882b3 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1749,7 +1749,7 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct iscsi_tm *hdr; int out_of_order_cmdsn = 0, ret; bool sess_ref = false; - u8 function; + u8 function, tcm_function = TMR_UNKNOWN; hdr = (struct iscsi_tm *) buf; hdr->flags &= ~ISCSI_FLAG_CMD_FINAL; @@ -1795,10 +1795,6 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * LIO-Target $FABRIC_MOD */ if (function != ISCSI_TM_FUNC_TASK_REASSIGN) { - - u8 tcm_function; - int ret; - transport_init_se_cmd(&cmd->se_cmd, &lio_target_fabric_configfs->tf_ops, conn->sess->se_sess, 0, DMA_NONE, @@ -1835,15 +1831,14 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } - - ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req, - tcm_function, GFP_KERNEL); - if (ret < 0) - return iscsit_add_reject_cmd(cmd, + } + ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req, tcm_function, + GFP_KERNEL); + if (ret < 0) + return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); - cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req; - } + cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req; cmd->iscsi_opcode = ISCSI_OP_SCSI_TMFUNC; cmd->i_state = ISTATE_SEND_TASKMGTRSP; diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index d1e7cb952168..ab4bff088898 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -231,6 +231,7 @@ enum tcm_tmreq_table { TMR_LUN_RESET = 5, TMR_TARGET_WARM_RESET = 6, TMR_TARGET_COLD_RESET = 7, + TMR_UNKNOWN = 0xff, }; /* fabric independent task management response values */ -- GitLab From 2f95dcc30a114dae9c0e54cdb451050e9261fdc6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 18 Nov 2017 11:06:31 +0100 Subject: [PATCH 1625/5498] Linux 3.18.82 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5e66a3704705..786deb6599f1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 81 +SUBLEVEL = 82 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 3eb48b407adf742b093543b3ef3c06e5ca45ff01 Mon Sep 17 00:00:00 2001 From: Adrian Salido Date: Thu, 14 Jul 2016 13:43:54 -0700 Subject: [PATCH 1626/5498] msm: mdss: avoid scheduling pp work during continuous updates Ping pong work is only relevant when last frame update happens to go into idle screen cases. If a commit is in progress there is no need to schedule this work. bug 30115868 Signed-off-by: Adrian Salido (cherry picked from commit e49a573e0ece576ece60c087595ea12ae4191ae8) Git-commit: e49a573e0ece576ece60c087595ea12ae4191ae8 Git-repo: https://android.googlesource.com/kernel/msm.git Change-Id: I61956a4038f80f55d9507be87d7f7ee278ccf45e Signed-off-by: Nirmal Abraham --- drivers/video/msm/mdss/mdss_mdp.h | 1 + drivers/video/msm/mdss/mdss_mdp_ctl.c | 15 +++++++++++---- drivers/video/msm/mdss/mdss_mdp_intf_cmd.c | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h index f33b908e0d62..90d0f7575b55 100644 --- a/drivers/video/msm/mdss/mdss_mdp.h +++ b/drivers/video/msm/mdss/mdss_mdp.h @@ -643,6 +643,7 @@ struct mdss_mdp_ctl { /* vsync handler for FRC */ struct mdss_mdp_vsync_handler frc_vsync_handler; + bool commit_in_progress; }; struct mdss_mdp_mixer { diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c index 71a3e14bbbfd..a6a211dc4a68 100644 --- a/drivers/video/msm/mdss/mdss_mdp_ctl.c +++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c @@ -5517,7 +5517,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, } else { sctl_flush_bits = sctl->flush_bits; } + sctl->commit_in_progress = true; } + ctl->commit_in_progress = true; ctl_flush_bits = ctl->flush_bits; ATRACE_END("postproc_programming"); @@ -5657,11 +5659,16 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, ATRACE_BEGIN("flush_kickoff"); mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl_flush_bits); - if (sctl && sctl_flush_bits) { - mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_FLUSH, - sctl_flush_bits); - sctl->flush_bits = 0; + if (sctl) { + if (sctl_flush_bits) { + mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_FLUSH, + sctl_flush_bits); + sctl->flush_bits = 0; + } + sctl->commit_in_progress = false; } + ctl->commit_in_progress = false; + MDSS_XLOG(ctl->intf_num, ctl_flush_bits, sctl_flush_bits, split_lm_valid); wmb(); diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c index 1603b01bb8a8..3188f6a2be9f 100644 --- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c +++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c @@ -1206,7 +1206,8 @@ static void mdss_mdp_cmd_pingpong_done(void *arg) atomic_read(&ctx->koff_cnt)); if (sync_ppdone) { atomic_inc(&ctx->pp_done_cnt); - schedule_work(&ctx->pp_done_work); + if (!ctl->commit_in_progress) + schedule_work(&ctx->pp_done_work); mdss_mdp_resource_control(ctl, MDP_RSRC_CTL_EVENT_PP_DONE); -- GitLab From c4b785116b0177b0c1d9729ea29be79b9c372948 Mon Sep 17 00:00:00 2001 From: Naseer Ahmed Date: Mon, 11 Jul 2016 15:48:03 -0400 Subject: [PATCH 1627/5498] msm: mdss: Do not wait for kickoff in display commit Just wait for the display to idle instead and immediately start the next commit. This reduces the depth of the commit pending queue. Change-Id: Ic4ee6f738f5fa77890e806b55d9a0304915761ea Signed-off-by: Naseer Ahmed --- drivers/video/msm/mdss/mdss_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index 44d4882031a0..ad80c8646fb1 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -3174,7 +3174,7 @@ static int mdss_fb_pan_display_ex(struct fb_info *info, if (var->yoffset > (info->var.yres_virtual - info->var.yres)) return -EINVAL; - ret = mdss_fb_wait_for_kickoff(mfd); + ret = mdss_fb_pan_idle(mfd); if (ret) { pr_err("wait_for_kick failed. rc=%d\n", ret); return ret; -- GitLab From d2b62bc6e11c31766eed581adbce2b4c8f404f01 Mon Sep 17 00:00:00 2001 From: Adrian Salido Date: Tue, 12 Jul 2016 13:31:17 -0700 Subject: [PATCH 1628/5498] msm: mdss: change retire fence signaling Signal retire fence on first vsync after frame has been programmed to hardware. This indicates when the frame starts being fetched by MDSS hardware. Signed-off-by: Adrian Salido (cherry picked from commit 80d5d7558ac98c56527cb1040dab3fc5f438ab51) Git-commit: 80d5d7558ac98c56527cb1040dab3fc5f438ab51 Git-repo: https://android.googlesource.com/kernel/msm.git Change-Id: Ib69f64ff17a1609b67ff34ef277f658bfa5cfb5a Signed-off-by: Nirmal Abraham --- drivers/video/msm/mdss/mdss_mdp_overlay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c index ccd1d94f172b..7cf206b00051 100644 --- a/drivers/video/msm/mdss/mdss_mdp_overlay.c +++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c @@ -2900,7 +2900,6 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); mdss_mdp_check_ctl_reset_status(ctl); - __vsync_set_vsync_handler(mfd); __validate_and_set_roi(mfd, data); if (ctl->ops.wait_pingpong && mdp5_data->mdata->serialize_wait4pp) @@ -2954,6 +2953,7 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, &commit_cb); ATRACE_END("display_commit"); } + __vsync_set_vsync_handler(mfd); /* * release the commit pending flag; we are releasing this flag -- GitLab From f6e2b0609db4f1a5eb98814454ba379c8c5d35c3 Mon Sep 17 00:00:00 2001 From: Srinivasarao P Date: Thu, 16 Nov 2017 17:30:29 +0530 Subject: [PATCH 1629/5498] defconfig: msm8909: enable uid stats Enable CONFIG_UID_STAT for UID based statistics. Change-Id: I4a0d64cf6a5c153a61d08ceef6fe318daabd77bc Signed-off-by: Srinivasarao P --- arch/arm/configs/msm8909-perf_defconfig | 1 + arch/arm/configs/msm8909_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index b8dda972a4b8..0bc996cf9c84 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -233,6 +233,7 @@ CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_APDS9930=y +CONFIG_UID_STAT=y CONFIG_QSEECOM=y CONFIG_UID_SYS_STATS=y CONFIG_MSM_MCU_TIME_SYNC=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index 3944e6fe6bf7..46573429be51 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -239,6 +239,7 @@ CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_APDS9930=y +CONFIG_UID_STAT=y CONFIG_QSEECOM=y CONFIG_UID_SYS_STATS=y CONFIG_MSM_MCU_TIME_SYNC=y -- GitLab From 0d4768ee25efe949d45faf6d8a0a0b21c65eb2d0 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 10 Jan 2017 13:35:49 -0800 Subject: [PATCH 1630/5498] BACKPORT: arm64: Use __pa_symbol for kernel symbols (cherry-pick from commit 2077be6783b5936c3daa838d8addbb635667927f) __pa_symbol is technically the marcro that should be used for kernel symbols. Switch to this as a pre-requisite for DEBUG_VIRTUAL which will do bounds checking. Reviewed-by: Mark Rutland Tested-by: Mark Rutland Signed-off-by: Laura Abbott Signed-off-by: Will Deacon Bug: 20045882 Bug: 63737556 Change-Id: Ibef89e5935c9562fa69e946778c705636c1ca61e --- arch/arm64/include/asm/kvm_mmu.h | 2 +- arch/arm64/include/asm/memory.h | 1 + arch/arm64/include/asm/mmu_context.h | 2 +- arch/arm64/include/asm/pgtable.h | 2 +- arch/arm64/kernel/cpufeature.c | 1 + arch/arm64/kernel/insn.c | 2 +- arch/arm64/kernel/psci.c | 5 +++- arch/arm64/kernel/setup.c | 9 ++++--- arch/arm64/kernel/smp_spin_table.c | 3 ++- arch/arm64/kernel/vdso.c | 7 ++++-- arch/arm64/mm/init.c | 3 ++- arch/arm64/mm/mmu.c | 37 +++++++++++++++++++--------- 12 files changed, 49 insertions(+), 25 deletions(-) diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 390bf1230c69..77292dd6c73e 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -243,7 +243,7 @@ static inline void __kvm_flush_dcache_pud(pud_t pud) kvm_flush_dcache_to_poc(page_address(page), PUD_SIZE); } -#define kvm_virt_to_phys(x) __virt_to_phys((unsigned long)(x)) +#define kvm_virt_to_phys(x) __pa_symbol(x) void kvm_set_way_flush(struct kvm_vcpu *vcpu); void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled); diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 1b8624e61c28..f520918473f5 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -131,6 +131,7 @@ static inline void *phys_to_virt(phys_addr_t x) #define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #define virt_to_pfn(x) __phys_to_pfn(__virt_to_phys(x)) +#define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x)) /* * virt_to_page(k) convert a _valid_ virtual address to struct page * diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index b61e47aea3a1..be402897426a 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -49,7 +49,7 @@ static inline void contextidr_thread_switch(struct task_struct *next) */ static inline void cpu_set_reserved_ttbr0(void) { - unsigned long ttbr = virt_to_phys(empty_zero_page); + unsigned long ttbr = __pa_symbol(empty_zero_page); asm( " msr ttbr0_el1, %0 // set TTBR0\n" diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 7fbc40f7a4d0..4b10ae9548c8 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -109,7 +109,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); * for zero-mapped memory areas etc.. */ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; -#define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page) +#define ZERO_PAGE(vaddr) phys_to_page(__pa_symbol(empty_zero_page)) #define pte_ERROR(pte) __pte_error(__FILE__, __LINE__, pte_val(pte)) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 58347534d765..ed631a6778f3 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index dd9671cd0bb2..ab4111eed363 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c @@ -96,7 +96,7 @@ static void __kprobes *patch_map(void *addr, int fixmap) if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX)) page = vmalloc_to_page(addr); else if (!module && IS_ENABLED(CONFIG_DEBUG_RODATA)) - page = virt_to_page(addr); + page = phys_to_page(__pa_symbol(addr)); else return addr; diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 7a57cf5c3441..0ba727862af6 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -22,6 +22,8 @@ #include #include #include +#include + #include #include @@ -413,7 +415,8 @@ static int __init cpu_psci_cpu_prepare(unsigned int cpu) static int cpu_psci_cpu_boot(unsigned int cpu) { - int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry)); + int err = psci_ops.cpu_on(cpu_logical_map(cpu), + __pa_symbol(secondary_entry)); if (err) pr_err("failed to boot CPU%d (%d)\n", cpu, err); diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 8c82db7d5bbf..39194cc7c665 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -222,10 +223,10 @@ static void __init request_standard_resources(void) struct memblock_region *region; struct resource *res; - kernel_code.start = virt_to_phys(_text); - kernel_code.end = virt_to_phys(__init_begin - 1); - kernel_data.start = virt_to_phys(_sdata); - kernel_data.end = virt_to_phys(_end - 1); + kernel_code.start = __pa_symbol(_text); + kernel_code.end = __pa_symbol(__init_begin - 1); + kernel_data.start = __pa_symbol(_sdata); + kernel_data.end = __pa_symbol(_end - 1); for_each_memblock(memory, region) { res = alloc_bootmem_low(sizeof(*res)); diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c index 14944e5b28da..9026187a5b87 100644 --- a/arch/arm64/kernel/smp_spin_table.c +++ b/arch/arm64/kernel/smp_spin_table.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -90,7 +91,7 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu) * boot-loader's endianess before jumping. This is mandated by * the boot protocol. */ - writeq_relaxed(__pa(secondary_holding_pen), release_addr); + writeq_relaxed(__pa_symbol(secondary_holding_pen), release_addr); __flush_dcache_area((__force void *)release_addr, sizeof(*release_addr)); diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 71766af43b70..85e4c04dfd17 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -114,6 +114,7 @@ static struct vm_special_mapping vdso_spec[2]; static int __init vdso_init(void) { int i; + unsigned long pfn; if (memcmp(&vdso_start, "\177ELF", 4)) { pr_err("vDSO is not a valid ELF object!\n"); @@ -131,11 +132,13 @@ static int __init vdso_init(void) return -ENOMEM; /* Grab the vDSO data page. */ - vdso_pagelist[0] = virt_to_page(vdso_data); + vdso_pagelist[0] = phys_to_page(__pa_symbol(vdso_data)); /* Grab the vDSO code pages. */ + pfn = sym_to_pfn(&vdso_start); + for (i = 0; i < vdso_pages; i++) - vdso_pagelist[i + 1] = virt_to_page(&vdso_start + i * PAGE_SIZE); + vdso_pagelist[i + 1] = pfn_to_page(pfn + i); /* Populate the special mapping structures */ vdso_spec[0] = (struct vm_special_mapping) { diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index a5b0839d08ed..7badb61bcb2c 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -146,7 +147,7 @@ void __init arm64_memblock_init(void) * Register the kernel text, kernel data, initrd, and initial * pagetables with memblock. */ - memblock_reserve(__pa(_text), _end - _text); + memblock_reserve(__pa_symbol(_text), _end - _text); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start) memblock_reserve(__virt_to_phys(initrd_start), initrd_end - initrd_start); diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 6147b780f805..5e320d4bd076 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -305,8 +306,10 @@ static void __init __map_memblock(phys_addr_t start, phys_addr_t end) * for now. This will get more fine grained later once all memory * is mapped */ - unsigned long kernel_x_start = round_down(__pa(_stext), SECTION_SIZE); - unsigned long kernel_x_end = round_up(__pa(__init_end), SECTION_SIZE); + unsigned long kernel_x_start = round_down(__pa_symbol(_stext), + SECTION_SIZE); + unsigned long kernel_x_end = round_up(__pa_symbol(__init_end), + SECTION_SIZE); if (end < kernel_x_start) { create_mapping(start, __phys_to_virt(start), @@ -394,19 +397,20 @@ void __init fixup_executable(void) #ifdef CONFIG_DEBUG_RODATA /* now that we are actually fully mapped, make the start/end more fine grained */ if (!IS_ALIGNED((unsigned long)_stext, SECTION_SIZE)) { - unsigned long aligned_start = round_down(__pa(_stext), + unsigned long aligned_start = round_down(__pa_symbol(_stext), SECTION_SIZE); create_mapping(aligned_start, __phys_to_virt(aligned_start), - __pa(_stext) - aligned_start, + __pa_symbol(_stext) - aligned_start, PAGE_KERNEL); } if (!IS_ALIGNED((unsigned long)__init_end, SECTION_SIZE)) { - unsigned long aligned_end = round_up(__pa(__init_end), + unsigned long aligned_end = round_up(__pa_symbol(__init_end), SECTION_SIZE); - create_mapping(__pa(__init_end), (unsigned long)__init_end, - aligned_end - __pa(__init_end), + create_mapping(__pa_symbol(__init_end), + (unsigned long)__init_end, + aligned_end - __pa_symbol(__init_end), PAGE_KERNEL); } #endif @@ -415,7 +419,7 @@ void __init fixup_executable(void) #ifdef CONFIG_DEBUG_RODATA void mark_rodata_ro(void) { - create_mapping_late(__pa(_stext), (unsigned long)_stext, + create_mapping_late(__pa_symbol(_stext), (unsigned long)_stext, (unsigned long)__init_begin - (unsigned long)_stext, PAGE_KERNEL_EXEC | PTE_RDONLY); } @@ -423,7 +427,8 @@ void mark_rodata_ro(void) void fixup_init(void) { - create_mapping_late(__pa(__init_begin), (unsigned long)__init_begin, + create_mapping_late(__pa_symbol(__init_begin), + (unsigned long)__init_begin, (unsigned long)__init_end - (unsigned long)__init_begin, PAGE_KERNEL); } @@ -567,6 +572,12 @@ static inline pte_t * fixmap_pte(unsigned long addr) return pte_offset_kernel(pmd, addr); } +/* + * The p*d_populate functions call virt_to_phys implicitly so they can't be used + * directly on kernel symbols (bm_p*d). This function is called too early to use + * lm_alias so __p*d_populate functions must be used to populate with the + * physical address from __pa_symbol. + */ void __init early_fixmap_init(void) { pgd_t *pgd; @@ -575,11 +586,13 @@ void __init early_fixmap_init(void) unsigned long addr = FIXADDR_START; pgd = pgd_offset_k(addr); - pgd_populate(&init_mm, pgd, bm_pud); + if (pgd_none(*pgd)) + __pgd_populate(pgd, __pa_symbol(bm_pud), PUD_TABLE_TYPE); pud = pud_offset(pgd, addr); - pud_populate(&init_mm, pud, bm_pmd); + if (pud_none(*pud)) + __pud_populate(pud, __pa_symbol(bm_pmd), PMD_TYPE_TABLE); pmd = pmd_offset(pud, addr); - pmd_populate_kernel(&init_mm, pmd, bm_pte); + __pmd_populate(pmg, __pa_symbol(bm_pte), PMD_TYPE_TABLE); /* * The boot-ioremap range spans multiple pmds, for which -- GitLab From cfe9be918b6bc977f75db3f81a1ec319668da7f0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Jan 2017 12:43:40 +0100 Subject: [PATCH 1631/5498] BACKPORT: arm64: Use __pa_symbol for empty_zero_page (cherry-pick commit from cbb999dd0b452991f4f698142aa7ffe566c0b415) If CONFIG_DEBUG_VIRTUAL=y and CONFIG_ARM64_SW_TTBR0_PAN=y: virt_to_phys used for non-linear address: ffffff8008cc0000 (empty_zero_page+0x0/0x1000) WARNING: CPU: 0 PID: 0 at arch/arm64/mm/physaddr.c:14 __virt_to_phys+0x28/0x60 ... [] __virt_to_phys+0x28/0x60 [] setup_arch+0x46c/0x4d4 Fixes: 2077be6783b5936c ("arm64: Use __pa_symbol for kernel symbols") Acked-by: Mark Rutland Acked-by: Laura Abbott Signed-off-by: Geert Uytterhoeven Signed-off-by: Will Deacon Bug: 20045882 Bug: 63737556 Change-Id: Ida933e532d0423e074b3621207a1e2a5f8609742 --- arch/arm64/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 39194cc7c665..b4e3a9bc646b 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -296,7 +296,7 @@ void __init setup_arch(char **cmdline_p) * faults in case uaccess_enable() is inadvertently called by the init * thread. */ - init_thread_info.ttbr0 = virt_to_phys(empty_zero_page); + init_thread_info.ttbr0 = __pa_symbol(empty_zero_page); #endif #ifdef CONFIG_VT -- GitLab From 356791cb20b892420bc8106c3e0430cd09b78af0 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Mon, 20 Nov 2017 09:39:09 -0800 Subject: [PATCH 1632/5498] ANDROID: arm64: Use __pa_symbol for kernel symbols 'BACKPORT: arm64: Use __pa_symbol for kernel symbols' missed a spot. Upstream has removed the fragment associated with idmap_pg_dir, so this is a 3.18-ony adjustment. Signed-off-by: Mark Salyzyn Bug: 63737556 Bug: 20045882 Change-Id: If4a65eed30113993e7ac178f69625d5305892184 --- arch/arm64/kernel/suspend.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c index ab14fee1f255..16215385cc25 100644 --- a/arch/arm64/kernel/suspend.c +++ b/arch/arm64/kernel/suspend.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -163,7 +164,7 @@ static int __init cpu_suspend_init(void) sleep_save_sp.save_ptr_stash = ctx_ptr; sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr); - sleep_idmap_phys = virt_to_phys(idmap_pg_dir); + sleep_idmap_phys = __pa_symbol(idmap_pg_dir); __flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp)); __flush_dcache_area(&sleep_idmap_phys, sizeof(sleep_idmap_phys)); -- GitLab From 376563a575bb9e6ed835f3ed229398ca29ec6122 Mon Sep 17 00:00:00 2001 From: Udaya Bhaskara Reddy Mallavarapu Date: Thu, 5 Jan 2017 10:49:50 +0530 Subject: [PATCH 1633/5498] media: dvb-core: Fix for dvb_ringbuffer merge conflict resolve the dvb_ringbuffer merge conflicts due to v4.4-16.09-android-tmp branch merging. Change-Id: Ic5ec87d48e86fd9504fed7232286cf81b46c4035 Signed-off-by: Udaya Bhaskara Reddy Mallavarapu --- drivers/media/dvb-core/dvb_ringbuffer.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c index e0aaea1016e4..6627235fa272 100644 --- a/drivers/media/dvb-core/dvb_ringbuffer.c +++ b/drivers/media/dvb-core/dvb_ringbuffer.c @@ -5,7 +5,7 @@ * Copyright (C) 2003 Oliver Endriss * Copyright (C) 2004 Andrew de Quincey * - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014,2017 The Linux Foundation. All rights reserved. * * based on code originally found in av7110.c & dvb_ci.c: * Copyright (C) 1999-2003 Ralph Metzler @@ -235,9 +235,11 @@ ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, */ smp_store_release(&rbuf->pwrite, 0); } - status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo); - if (status) - return len - todo; + + if (copy_from_user(rbuf->data + rbuf->pwrite, buf, todo)) { + smp_store_release(&rbuf->pwrite, oldpwrite); + return -EFAULT; + } /* smp_store_release() for write pointer update, see above */ smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); -- GitLab From d0829423b58e9900ff86b85a338ff1dad123557d Mon Sep 17 00:00:00 2001 From: Balvinder Singh Date: Mon, 6 Nov 2017 11:50:46 +0530 Subject: [PATCH 1634/5498] [Bluetooth] - Fix for checking proper user-supplied buffers During patch download of devices, size validations & zero alloc the buffers to ensure values passed are in range CRs-fixed: 2084692 Change-Id: Ie1cd76fe68766d6d12d7262202e48c18ebe42274 Signed-off-by: Balvinder Singh --- drivers/bluetooth/ath3k.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 19044d75e875..074c816adf74 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -262,13 +262,27 @@ static int ath3k_load_firmware(struct usb_device *udev, { u8 *send_buf; int err, pipe, len, size, sent = 0; - int count = firmware->size; + int count; BT_DBG("udev %p", udev); + if (!firmware || !firmware->data || firmware->size <= 0) { + err = -EINVAL; + BT_ERR("Not a valid FW file"); + return err; + } + + count = firmware->size; + + if (count < FW_HDR_SIZE) { + err = -EINVAL; + BT_ERR("ath3k loading invalid size of file"); + return err; + } + pipe = usb_sndctrlpipe(udev, 0); - send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); + send_buf = kzalloc(BULK_SIZE, GFP_KERNEL); if (!send_buf) { BT_ERR("Can't allocate memory chunk for firmware"); return -ENOMEM; -- GitLab From 8fb6cbbd125f9f76f050e8157199f403a20c6e46 Mon Sep 17 00:00:00 2001 From: smanag Date: Wed, 8 Nov 2017 12:02:28 +0530 Subject: [PATCH 1635/5498] ARM: dts: msm: Add sound node for 8909 cdp Add audio codec node to enable audio on 8909 cdp. CRs-Fixed: 2140674 Change-Id: Ic048a0e2cce678fcacf7753e1b6ef0641f73b435 Signed-off-by: Soumya Managoli --- arch/arm/boot/dts/qcom/msm8909-cdp.dtsi | 85 +++++++++++++++++++ .../arm/boot/dts/qcom/msm8909-pm8916-cdp.dtsi | 9 +- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8909-cdp.dtsi index aacd72a37ee2..75def6ab9e56 100644 --- a/arch/arm/boot/dts/qcom/msm8909-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-cdp.dtsi @@ -214,6 +214,91 @@ }; }; + audio_codec: sound { + compatible = "qcom,msm8952-audio-codec"; + qcom,model = "msm8909-snd-card"; + reg = <0x7702000 0x4>, + <0x7702004 0x4>, + <0x7702008 0x4>; + reg-names = "csr_gp_io_mux_mic_ctl", + "csr_gp_io_mux_spkr_ctl", + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel"; + + qcom,msm-snd-card-id = <0>; + qcom,msm-codec-type = "internal"; + qcom,msm-ext-pa = "primary"; + qcom,msm-mclk-freq = <9600000>; + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,msm-hs-micbias-type = "internal"; + qcom,msm-micbias1-ext-cap; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "SPK_RX_BIAS", "MCLK", + "INT_LDO_H", "MCLK", + "MIC BIAS Internal1", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS Internal1", "Secondary Mic", + "AMIC1", "MIC BIAS Internal1", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS Internal1"; + qcom,msm-gpios = + "pri_i2s", + "us_eu_gpio"; + qcom,pinctrl-names = + "all_off", + "pri_i2s_act", + "us_eu_gpio_act", + "pri_i2s_us_eu_gpio_act"; + pinctrl-names = + "all_off", + "pri_i2s_act", + "us_eu_gpio_act", + "pri_i2s_us_eu_gpio_act"; + pinctrl-0 = <&cdc_pdm_lines_sus &cross_conn_det_sus + &vdd_spkdrv_sus>; + pinctrl-1 = <&cdc_pdm_lines_act &cross_conn_det_sus + &vdd_spkdrv_act>; + pinctrl-2 = <&cdc_pdm_lines_sus &cross_conn_det_act + &vdd_spkdrv_sus>; + pinctrl-3 = <&cdc_pdm_lines_act &cross_conn_det_act + &vdd_spkdrv_act>; + qcom,cdc-us-euro-gpios = <&msm_gpio 97 0>; + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>, + <&voice_svc>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-pcm-lpa", + "msm-voice-svc"; + asoc-cpu = <&dai_pri_auxpcm>, + <&dai_mi2s0>, <&dai_mi2s1>, + <&dai_mi2s2>, <&dai_mi2s3>, + <&dai_mi2s5>, <&dai_mi2s6>, + <&bt_sco_rx>, <&bt_sco_tx>, <&bt_a2dp_rx>, + <&int_fm_rx>, <&int_fm_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, + <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, + <&incall_music_rx>, <&incall_music_2_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-mi2s.5", "msm-dai-q6-mi2s.6", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12290", "msm-dai-q6-dev.12292", + "msm-dai-q6-dev.12293", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770"; + asoc-codec = <&stub_codec>, <&pm8909_conga_dig>; + asoc-codec-names = "msm-stub-codec.1", "cajon_codec"; + }; }; &blsp1_uart1 { diff --git a/arch/arm/boot/dts/qcom/msm8909-pm8916-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8909-pm8916-cdp.dtsi index b211ef6ce08b..6e84bf50f32a 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pm8916-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pm8916-cdp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2016, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,13 @@ }; +&audio_codec { + status = "ok"; + qcom,model = "msm8909-pm8916-snd-card"; + asoc-codec = <&stub_codec>, <&pm8916_tombak_dig>; + asoc-codec-names = "msm-stub-codec.1", "tombak_codec"; +}; + &dsi_auo_qvga_cmd { qcom,mdss-dsi-pwm-gpio = <&pm8916_mpps 4 0>; }; -- GitLab From b02dac504efc353e1e8507c10f835f09757b0068 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 9 Oct 2017 20:14:48 +0200 Subject: [PATCH 1636/5498] media: imon: Fix null-ptr-deref in imon_probe commit 58fd55e838276a0c13d1dc7c387f90f25063cbf3 upstream. It seems that the return value of usb_ifnum_to_if() can be NULL and needs to be checked. Signed-off-by: Arvind Yadav Tested-by: Andrey Konovalov Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/media/rc/imon.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index eb9e7feb9b13..7a16e9ea041c 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -2419,6 +2419,11 @@ static int imon_probe(struct usb_interface *interface, mutex_lock(&driver_lock); first_if = usb_ifnum_to_if(usbdev, 0); + if (!first_if) { + ret = -ENODEV; + goto fail; + } + first_if_ctx = usb_get_intfdata(first_if); if (ifnum == 0) { -- GitLab From 49fc34138b882bb1c6f2ba20df5339bca8a9167e Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 2 Nov 2017 10:38:21 -0400 Subject: [PATCH 1637/5498] media: dib0700: fix invalid dvb_detach argument commit eb0c19942288569e0ae492476534d5a485fb8ab4 upstream. dvb_detach(arg) calls symbol_put_addr(arg), where arg should be a pointer to a function. Right now a pointer to state->dib7000p_ops is passed to dvb_detach(), which causes a BUG() in symbol_put_addr() as discovered by syzkaller. Pass state->dib7000p_ops.set_wbd_ref instead. ------------[ cut here ]------------ kernel BUG at kernel/module.c:1081! invalid opcode: 0000 [#1] PREEMPT SMP KASAN Modules linked in: CPU: 1 PID: 1151 Comm: kworker/1:1 Tainted: G W 4.14.0-rc1-42251-gebb2c2437d80 #224 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Workqueue: usb_hub_wq hub_event task: ffff88006a336300 task.stack: ffff88006a7c8000 RIP: 0010:symbol_put_addr+0x54/0x60 kernel/module.c:1083 RSP: 0018:ffff88006a7ce210 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff880062a8d190 RCX: 0000000000000000 RDX: dffffc0000000020 RSI: ffffffff85876d60 RDI: ffff880062a8d190 RBP: ffff88006a7ce218 R08: 1ffff1000d4f9c12 R09: 1ffff1000d4f9ae4 R10: 1ffff1000d4f9bed R11: 0000000000000000 R12: ffff880062a8d180 R13: 00000000ffffffed R14: ffff880062a8d190 R15: ffff88006947c000 FS: 0000000000000000(0000) GS:ffff88006c900000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f6416532000 CR3: 00000000632f5000 CR4: 00000000000006e0 Call Trace: stk7070p_frontend_attach+0x515/0x610 drivers/media/usb/dvb-usb/dib0700_devices.c:1013 dvb_usb_adapter_frontend_init+0x32b/0x660 drivers/media/usb/dvb-usb/dvb-usb-dvb.c:286 dvb_usb_adapter_init drivers/media/usb/dvb-usb/dvb-usb-init.c:86 dvb_usb_init drivers/media/usb/dvb-usb/dvb-usb-init.c:162 dvb_usb_device_init+0xf70/0x17f0 drivers/media/usb/dvb-usb/dvb-usb-init.c:277 dib0700_probe+0x171/0x5a0 drivers/media/usb/dvb-usb/dib0700_core.c:886 usb_probe_interface+0x35d/0x8e0 drivers/usb/core/driver.c:361 really_probe drivers/base/dd.c:413 driver_probe_device+0x610/0xa00 drivers/base/dd.c:557 __device_attach_driver+0x230/0x290 drivers/base/dd.c:653 bus_for_each_drv+0x161/0x210 drivers/base/bus.c:463 __device_attach+0x26e/0x3d0 drivers/base/dd.c:710 device_initial_probe+0x1f/0x30 drivers/base/dd.c:757 bus_probe_device+0x1eb/0x290 drivers/base/bus.c:523 device_add+0xd0b/0x1660 drivers/base/core.c:1835 usb_set_configuration+0x104e/0x1870 drivers/usb/core/message.c:1932 generic_probe+0x73/0xe0 drivers/usb/core/generic.c:174 usb_probe_device+0xaf/0xe0 drivers/usb/core/driver.c:266 really_probe drivers/base/dd.c:413 driver_probe_device+0x610/0xa00 drivers/base/dd.c:557 __device_attach_driver+0x230/0x290 drivers/base/dd.c:653 bus_for_each_drv+0x161/0x210 drivers/base/bus.c:463 __device_attach+0x26e/0x3d0 drivers/base/dd.c:710 device_initial_probe+0x1f/0x30 drivers/base/dd.c:757 bus_probe_device+0x1eb/0x290 drivers/base/bus.c:523 device_add+0xd0b/0x1660 drivers/base/core.c:1835 usb_new_device+0x7b8/0x1020 drivers/usb/core/hub.c:2457 hub_port_connect drivers/usb/core/hub.c:4903 hub_port_connect_change drivers/usb/core/hub.c:5009 port_event drivers/usb/core/hub.c:5115 hub_event+0x194d/0x3740 drivers/usb/core/hub.c:5195 process_one_work+0xc7f/0x1db0 kernel/workqueue.c:2119 worker_thread+0x221/0x1850 kernel/workqueue.c:2253 kthread+0x3a1/0x470 kernel/kthread.c:231 ret_from_fork+0x2a/0x40 arch/x86/entry/entry_64.S:431 Code: ff ff 48 85 c0 74 24 48 89 c7 e8 48 ea ff ff bf 01 00 00 00 e8 de 20 e3 ff 65 8b 05 b7 2f c2 7e 85 c0 75 c9 e8 f9 0b c1 ff eb c2 <0f> 0b 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 b8 00 00 RIP: symbol_put_addr+0x54/0x60 RSP: ffff88006a7ce210 ---[ end trace b75b357739e7e116 ]--- Signed-off-by: Andrey Konovalov Cc: Ben Hutchings Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/dvb-usb/dib0700_devices.c | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 6aa4e9f4e441..2e50c37c94e0 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -292,7 +292,7 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) stk7700d_dib7000p_mt2266_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } } @@ -326,7 +326,7 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) stk7700d_dib7000p_mt2266_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } } @@ -479,7 +479,7 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) &stk7700ph_dib7700_xc3028_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } @@ -1013,7 +1013,7 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) &dib7070p_dib7000p_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } @@ -1071,7 +1071,7 @@ static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap) &dib7770p_dib7000p_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } @@ -3039,7 +3039,7 @@ static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); @@ -3092,7 +3092,7 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) /* initialize IC 0 */ if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, &tfe7090pvr_dib7000p_config[0]) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } @@ -3122,7 +3122,7 @@ static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) i2c = state->dib7000p_ops.get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); if (state->dib7000p_ops.i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } @@ -3197,7 +3197,7 @@ static int tfe7790p_frontend_attach(struct dvb_usb_adapter *adap) 1, 0x10, &tfe7790p_dib7000p_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, @@ -3292,7 +3292,7 @@ static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) stk7070pd_dib7000p_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } @@ -3367,7 +3367,7 @@ static int novatd_frontend_attach(struct dvb_usb_adapter *adap) stk7070pd_dib7000p_config) != 0) { err("%s: state->dib7000p_ops.i2c_enumeration failed. Cannot continue\n", __func__); - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } } @@ -3603,7 +3603,7 @@ static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap) if (state->dib7000p_ops.dib7000pc_detection(&adap->dev->i2c_adap) == 0) { /* Demodulator not found for some reason? */ - dvb_detach(&state->dib7000p_ops); + dvb_detach(state->dib7000p_ops.set_wbd_ref); return -ENODEV; } -- GitLab From 4273d07525354daf17258f4b8d7d11576e67375d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 4 Aug 2017 23:59:31 -0700 Subject: [PATCH 1638/5498] iscsi-target: Fix iscsi_np reset hung task during parallel delete commit 978d13d60c34818a41fc35962602bdfa5c03f214 upstream. This patch fixes a bug associated with iscsit_reset_np_thread() that can occur during parallel configfs rmdir of a single iscsi_np used across multiple iscsi-target instances, that would result in hung task(s) similar to below where configfs rmdir process context was blocked indefinately waiting for iscsi_np->np_restart_comp to finish: [ 6726.112076] INFO: task dcp_proxy_node_:15550 blocked for more than 120 seconds. [ 6726.119440] Tainted: G W O 4.1.26-3321 #2 [ 6726.125045] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 6726.132927] dcp_proxy_node_ D ffff8803f202bc88 0 15550 1 0x00000000 [ 6726.140058] ffff8803f202bc88 ffff88085c64d960 ffff88083b3b1ad0 ffff88087fffeb08 [ 6726.147593] ffff8803f202c000 7fffffffffffffff ffff88083f459c28 ffff88083b3b1ad0 [ 6726.155132] ffff88035373c100 ffff8803f202bca8 ffffffff8168ced2 ffff8803f202bcb8 [ 6726.162667] Call Trace: [ 6726.165150] [] schedule+0x32/0x80 [ 6726.170156] [] schedule_timeout+0x214/0x290 [ 6726.176030] [] ? __send_signal+0x52/0x4a0 [ 6726.181728] [] wait_for_completion+0x96/0x100 [ 6726.187774] [] ? wake_up_state+0x10/0x10 [ 6726.193395] [] iscsit_reset_np_thread+0x62/0xe0 [iscsi_target_mod] [ 6726.201278] [] iscsit_tpg_disable_portal_group+0x96/0x190 [iscsi_target_mod] [ 6726.210033] [] lio_target_tpg_store_enable+0x4f/0xc0 [iscsi_target_mod] [ 6726.218351] [] configfs_write_file+0xaa/0x110 [ 6726.224392] [] vfs_write+0xa4/0x1b0 [ 6726.229576] [] SyS_write+0x41/0xb0 [ 6726.234659] [] system_call_fastpath+0x12/0x71 It would happen because each iscsit_reset_np_thread() sets state to ISCSI_NP_THREAD_RESET, sends SIGINT, and then blocks waiting for completion on iscsi_np->np_restart_comp. However, if iscsi_np was active processing a login request and more than a single iscsit_reset_np_thread() caller to the same iscsi_np was blocked on iscsi_np->np_restart_comp, iscsi_np kthread process context in __iscsi_target_login_thread() would flush pending signals and only perform a single completion of np->np_restart_comp before going back to sleep within transport specific iscsit_transport->iscsi_accept_np code. To address this bug, add a iscsi_np->np_reset_count and update __iscsi_target_login_thread() to keep completing np->np_restart_comp until ->np_reset_count has reached zero. Reported-by: Gary Guo Tested-by: Gary Guo Cc: Mike Christie Cc: Hannes Reinecke Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target.c | 1 + drivers/target/iscsi/iscsi_target_core.h | 1 + drivers/target/iscsi/iscsi_target_login.c | 7 +++++-- include/target/iscsi/iscsi_target_core.h | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 801369f882b3..2fc6b495df05 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -428,6 +428,7 @@ int iscsit_reset_np_thread( return 0; } np->np_thread_state = ISCSI_NP_THREAD_RESET; + atomic_inc(&np->np_reset_count); if (np->np_thread) { spin_unlock_bh(&np->np_thread_lock); diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 1863de28ce46..bf3da8e461f7 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -783,6 +783,7 @@ struct iscsi_np { int np_sock_type; enum np_thread_state_table np_thread_state; bool enabled; + atomic_t np_reset_count; enum iscsi_timer_flags_table np_login_timer_flags; u32 np_exports; enum np_flags_table np_flags; diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 4608d8dac04c..540af1be2f92 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -1275,9 +1275,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) flush_signals(current); spin_lock_bh(&np->np_thread_lock); - if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { + if (atomic_dec_if_positive(&np->np_reset_count) >= 0) { np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; + spin_unlock_bh(&np->np_thread_lock); complete(&np->np_restart_comp); + return 1; } else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) { spin_unlock_bh(&np->np_thread_lock); goto exit; @@ -1310,7 +1312,8 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) goto exit; } else if (rc < 0) { spin_lock_bh(&np->np_thread_lock); - if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { + if (atomic_dec_if_positive(&np->np_reset_count) >= 0) { + np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; spin_unlock_bh(&np->np_thread_lock); complete(&np->np_restart_comp); iscsit_put_transport(conn->conn_transport); diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index 7bd03f867fca..e365ed912edc 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -784,6 +784,7 @@ struct iscsi_np { int np_sock_type; enum np_thread_state_table np_thread_state; bool enabled; + atomic_t np_reset_count; enum iscsi_timer_flags_table np_login_timer_flags; u32 np_exports; enum np_flags_table np_flags; -- GitLab From d487af849accc63ae2157d2edd95c0bcd688b55c Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Dec 2016 10:45:31 +0200 Subject: [PATCH 1639/5498] extcon: palmas: Check the parent instance to prevent the NULL [ Upstream commit 9fe172b9be532acc23e35ba693700383ab775e66 ] extcon-palmas must be child of palmas and expects parent's drvdata to be valid. Check for non NULL parent drvdata and fail if it is NULL. Not doing so will result in a NULL pointer dereference later in the probe() parent drvdata is NULL (e.g. misplaced extcon-palmas node in device tree). Signed-off-by: Roger Quadros Signed-off-by: Chanwoo Choi Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon-palmas.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 230e1220ce48..1cee9f4909db 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -150,6 +150,11 @@ static int palmas_usb_probe(struct platform_device *pdev) struct palmas_usb *palmas_usb; int status; + if (!palmas) { + dev_err(&pdev->dev, "failed to get valid parent\n"); + return -EINVAL; + } + palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); if (!palmas_usb) return -ENOMEM; -- GitLab From daa36dd42767665901d126d1586a517c003ba528 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 5 Jan 2017 11:08:20 -0800 Subject: [PATCH 1640/5498] ARM: OMAP2+: Fix init for multiple quirks for the same SoC [ Upstream commit 6e613ebf4405fc09e2a8c16ed193b47f80a3cbed ] It's possible that there are multiple quirks that need to be initialized for the same SoC. Fix the issue by not returning on the first match. Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-omap2/pdata-quirks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index cec9d6c6442c..5de8f2aaf2da 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -417,7 +417,6 @@ static void pdata_quirks_check(struct pdata_init *quirks) if (of_machine_is_compatible(quirks->compatible)) { if (quirks->fn) quirks->fn(); - break; } quirks++; } -- GitLab From 1fde1741984553fd9099aa9c936ed483652a88d0 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 5 Jan 2017 11:07:18 -0800 Subject: [PATCH 1641/5498] ARM: dts: Fix omap3 off mode pull defines [ Upstream commit d97556c8012015901a3ce77f46960078139cd79d ] We need to also have OFFPULLUDENABLE bit set to use the off mode pull values. Otherwise the line is pulled down internally if no external pull exists. This is has some documentation at: http://processors.wiki.ti.com/index.php/Optimizing_OMAP35x_and_AM/DM37x_OFF_mode_PAD_configuration Note that the value is still glitchy during off mode transitions as documented in spz319f.pdf "Advisory 1.45". It's best to use external pulls instead of relying on the internal ones for off mode and even then anything pulled up will get driven down momentarily on off mode restore for GPIO banks other than bank1. Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/dt-bindings/pinctrl/omap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dt-bindings/pinctrl/omap.h b/include/dt-bindings/pinctrl/omap.h index 1c75b8ca5228..3835162ce371 100644 --- a/include/dt-bindings/pinctrl/omap.h +++ b/include/dt-bindings/pinctrl/omap.h @@ -45,8 +45,8 @@ #define PIN_OFF_NONE 0 #define PIN_OFF_OUTPUT_HIGH (OFF_EN | OFFOUT_EN | OFFOUT_VAL) #define PIN_OFF_OUTPUT_LOW (OFF_EN | OFFOUT_EN) -#define PIN_OFF_INPUT_PULLUP (OFF_EN | OFF_PULL_EN | OFF_PULL_UP) -#define PIN_OFF_INPUT_PULLDOWN (OFF_EN | OFF_PULL_EN) +#define PIN_OFF_INPUT_PULLUP (OFF_EN | OFFOUT_EN | OFF_PULL_EN | OFF_PULL_UP) +#define PIN_OFF_INPUT_PULLDOWN (OFF_EN | OFFOUT_EN | OFF_PULL_EN) #define PIN_OFF_WAKEUPENABLE WAKEUP_EN /* -- GitLab From 674c5db9b8fc392942d7d3fbdaaaf0af55576022 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 3 Jan 2017 19:09:46 +0100 Subject: [PATCH 1642/5498] ata: ATA_BMDMA should depend on HAS_DMA [ Upstream commit 7bc7ab1e63dfe004931502f90ce7020e375623da ] If NO_DMA=y: ERROR: "dmam_alloc_coherent" [drivers/ata/libata.ko] undefined! Add a dependency on HAS_DMA to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/ata/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index cd4cccbfd2ab..0cfce7a81544 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -245,6 +245,7 @@ config SATA_SX4 config ATA_BMDMA bool "ATA BMDMA support" + depends on HAS_DMA default y help This option adds support for SFF ATA controllers with BMDMA -- GitLab From 88b9e58302cffc88e91f77551538ef97f62b4cd0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 3 Jan 2017 19:09:45 +0100 Subject: [PATCH 1643/5498] ata: SATA_HIGHBANK should depend on HAS_DMA [ Upstream commit 2a736e0585e585c2566b5119af8381910a170e44 ] If NO_DMA=y: ERROR: "bad_dma_ops" [drivers/ata/sata_highbank.ko] undefined! Add a dependency on HAS_DMA to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/ata/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 0cfce7a81544..98076d5d417b 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -291,6 +291,7 @@ config SATA_DWC_VDEBUG config SATA_HIGHBANK tristate "Calxeda Highbank SATA support" + depends on HAS_DMA depends on ARCH_HIGHBANK || COMPILE_TEST help This option enables support for the Calxeda Highbank SoC's -- GitLab From 45dce99ab8167986a98bb15ae8fdce73ff41b612 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 3 Jan 2017 19:09:44 +0100 Subject: [PATCH 1644/5498] ata: SATA_MV should depend on HAS_DMA [ Upstream commit 62989cebd367a1aae1e009e1a5b1ec046a4c8fdc ] If NO_DMA=y: ERROR: "dma_pool_alloc" [drivers/ata/sata_mv.ko] undefined! ERROR: "dmam_pool_create" [drivers/ata/sata_mv.ko] undefined! ERROR: "dma_pool_free" [drivers/ata/sata_mv.ko] undefined! Add a dependency on HAS_DMA to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/ata/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 98076d5d417b..aaf30674d223 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -301,6 +301,7 @@ config SATA_HIGHBANK config SATA_MV tristate "Marvell SATA support" + depends on HAS_DMA depends on PCI || ARCH_DOVE || ARCH_MV78XX0 || \ ARCH_MVEBU || ARCH_ORION5X || COMPILE_TEST select GENERIC_PHY -- GitLab From 7d881e89d38cdd4de8d9d5e55082a11650cd9a10 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 21 Dec 2016 11:00:12 +0530 Subject: [PATCH 1645/5498] drm/sti: sti_vtg: Handle return NULL error from devm_ioremap_nocache [ Upstream commit 1ae0d5af347df224a6e76334683f13a96d915a44 ] Here, If devm_ioremap_nocache will fail. It will return NULL. Kernel can run into a NULL-pointer dereference. This error check will avoid NULL pointer dereference. Signed-off-by: Arvind Yadav Acked-by: Vincent Abriou Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/sti/sti_vtg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c index 740d6e347a62..b0fc11ec6ab7 100644 --- a/drivers/gpu/drm/sti/sti_vtg.c +++ b/drivers/gpu/drm/sti/sti_vtg.c @@ -303,6 +303,10 @@ static int vtg_probe(struct platform_device *pdev) return -ENOMEM; } vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!vtg->regs) { + DRM_ERROR("failed to remap I/O memory\n"); + return -ENOMEM; + } np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0); if (np) { -- GitLab From c5536a15a696a354f6839d1ab250a0b8fab7cb3b Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Tue, 29 Nov 2016 10:03:56 -0600 Subject: [PATCH 1646/5498] igb: reset the PHY before reading the PHY ID [ Upstream commit 182785335447957409282ca745aa5bc3968facee ] Several people have reported firmware leaving the I210/I211 PHY's page select register set to something other than the default of zero. This causes the first accesses, PHY_IDx register reads, to access something else, resulting in device probe failure: igb: Intel(R) Gigabit Ethernet Network Driver - version 5.4.0-k igb: Copyright (c) 2007-2014 Intel Corporation. igb: probe of 0000:01:00.0 failed with error -2 This problem began for them after a previous patch I submitted was applied: commit 2a3cdead8b408351fa1e3079b220fa331480ffbc Author: Aaron Sierra Date: Tue Nov 3 12:37:09 2015 -0600 igb: Remove GS40G specific defines/functions I personally experienced this problem after attempting to PXE boot from I210 devices using this firmware: Intel(R) Boot Agent GE v1.5.78 Copyright (C) 1997-2014, Intel Corporation Resetting the PHY before reading from it, ensures the page select register is in its default state and doesn't make assumptions about the PHY's register set before the PHY has been probed. Cc: Matwey V. Kornilov Cc: Chris Arges Cc: Jochen Henneberg Signed-off-by: Aaron Sierra Tested-by: Matwey V. Kornilov Tested-by: Chris J Arges Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igb/e1000_82575.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 051ea94bdcd3..38d68623ea33 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -215,6 +215,17 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) hw->bus.func = (rd32(E1000_STATUS) & E1000_STATUS_FUNC_MASK) >> E1000_STATUS_FUNC_SHIFT; + /* Make sure the PHY is in a good state. Several people have reported + * firmware leaving the PHY's page select register set to something + * other than the default of zero, which causes the PHY ID read to + * access something other than the intended register. + */ + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + hw_dbg("Error resetting the PHY.\n"); + goto out; + } + /* Set phy->phy_addr and phy->id. */ ret_val = igb_get_phy_id_82575(hw); if (ret_val) -- GitLab From 3d208b5adcf3199bb84b13c2b83d39886c0a4fb2 Mon Sep 17 00:00:00 2001 From: Todd Fujinaka Date: Tue, 15 Nov 2016 08:54:26 -0800 Subject: [PATCH 1647/5498] igb: close/suspend race in netif_device_detach [ Upstream commit 9474933caf21a4cb5147223dca1551f527aaac36 ] Similar to ixgbe, when an interface is part of a namespace it is possible that igb_close() may be called while __igb_shutdown() is running which ends up in a double free WARN and/or a BUG in free_msi_irqs(). Extend the rtnl_lock() to protect the call to netif_device_detach() and igb_clear_interrupt_scheme() in __igb_shutdown() and check for netif_device_present() to avoid calling igb_clear_interrupt_scheme() a second time in igb_close(). Also extend the rtnl lock in igb_resume() to netif_device_attach(). Signed-off-by: Todd Fujinaka Acked-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igb/igb_main.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index e0f36647d3dd..21d868e287df 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3170,7 +3170,9 @@ static int __igb_close(struct net_device *netdev, bool suspending) static int igb_close(struct net_device *netdev) { - return __igb_close(netdev, false); + if (netif_device_present(netdev)) + return __igb_close(netdev, false); + return 0; } /** @@ -7328,12 +7330,14 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake, int retval = 0; #endif + rtnl_lock(); netif_device_detach(netdev); if (netif_running(netdev)) __igb_close(netdev, true); igb_clear_interrupt_scheme(adapter); + rtnl_unlock(); #ifdef CONFIG_PM retval = pci_save_state(pdev); @@ -7452,16 +7456,15 @@ static int igb_resume(struct device *dev) wr32(E1000_WUS, ~0); - if (netdev->flags & IFF_UP) { - rtnl_lock(); + rtnl_lock(); + if (!err && netif_running(netdev)) err = __igb_open(netdev, true); - rtnl_unlock(); - if (err) - return err; - } - netif_device_attach(netdev); - return 0; + if (!err) + netif_device_attach(netdev); + rtnl_unlock(); + + return err; } #ifdef CONFIG_PM_RUNTIME -- GitLab From 170c8c366ad4b23e285f37311ff106c3a6f737b1 Mon Sep 17 00:00:00 2001 From: Hannu Lounento Date: Mon, 2 Jan 2017 18:26:06 +0100 Subject: [PATCH 1648/5498] igb: Fix hw_dbg logging in igb_update_flash_i210 [ Upstream commit 76ed5a8f47476e4984cc8c0c1bc4cee62650f7fd ] Fix an if statement with hw_dbg lines where the logic was inverted with regards to the corresponding return value used in the if statement. Signed-off-by: Hannu Lounento Signed-off-by: Peter Senna Tschudin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igb/e1000_i210.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c index 65d931669f81..89402fce7d79 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.c +++ b/drivers/net/ethernet/intel/igb/e1000_i210.c @@ -699,9 +699,9 @@ static s32 igb_update_flash_i210(struct e1000_hw *hw) ret_val = igb_pool_flash_update_done_i210(hw); if (ret_val) - hw_dbg("Flash update complete\n"); - else hw_dbg("Flash update time out\n"); + else + hw_dbg("Flash update complete\n"); out: return ret_val; -- GitLab From d3750dd19128e35e01678f7cc94f3298e63e5dbe Mon Sep 17 00:00:00 2001 From: Galo Navarro Date: Tue, 3 Jan 2017 23:12:09 +0100 Subject: [PATCH 1649/5498] staging: rtl8188eu: fix incorrect ERROR tags from logs [ Upstream commit 401579c22ccbcb54244494069973e64b1fe980d2 ] Several lifecycle events in the rtl8188eu driver are logged using the DBG_88E_LEVEL macro from rtw_debug.h, which is tagged as ERROR regardless of the actual level. Below are dmesg excerpts after loading and unloading the module, the messages are misleading as there was no error. [517434.916239] usbcore: registered new interface driver r8188eu [517435.680653] R8188EU: ERROR indicate disassoc [517437.122606] R8188EU: ERROR assoc success [517797.735611] usbcore: deregistering interface driver r8188eu [517797.736069] R8188EU: ERROR indicate disassoc Remove the ERROR prefix from the logs. After the patch, logs are: [517949.873976] usbcore: registered new interface driver r8188eu [517950.592845] R8188EU: indicate disassoc [517951.993973] R8188EU: assoc success [521778.784448] usbcore: deregistering interface driver r8188eu [521778.784838] R8188EU: indicate disassoc Signed-off-by: Galo Navarro Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8188eu/include/rtw_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8188eu/include/rtw_debug.h b/drivers/staging/rtl8188eu/include/rtw_debug.h index a38616e3cad2..acef31275b0e 100644 --- a/drivers/staging/rtl8188eu/include/rtw_debug.h +++ b/drivers/staging/rtl8188eu/include/rtw_debug.h @@ -75,7 +75,7 @@ extern u32 GlobalDebugLevel; #define DBG_88E_LEVEL(_level, fmt, arg...) \ do { \ if (_level <= GlobalDebugLevel) \ - pr_info(DRIVER_PREFIX"ERROR " fmt, ##arg); \ + pr_info(DRIVER_PREFIX fmt, ##arg); \ } while (0) #define DBG_88E(...) \ -- GitLab From 70e2afb4c9d98955bff8b9ed266872e7accde459 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 19 Dec 2016 15:07:30 -0800 Subject: [PATCH 1650/5498] scsi: lpfc: Add missing memory barrier [ Upstream commit 6b3b3bdb83b4ad51252d21bb13596db879e51850 ] On loosely ordered memory systems (PPC for example), the WQE elements were being updated in memory, but not necessarily flushed before the separate doorbell was written to hw which would cause hw to dma the WQE element. Thus, the hardware occasionally received partially updated WQE data. Add the memory barrier after updating the WQE memory. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_sli.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 207a43d952fa..42236ef89ab0 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -118,6 +118,8 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe) if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED) bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id); lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size); + /* ensure WQE bcopy flushed before doorbell write */ + wmb(); /* Update the host index before invoking device */ host_index = q->host_index; -- GitLab From ee4961b9093fcb8061461224e0bf70e020a6a4e2 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 19 Dec 2016 15:07:25 -0800 Subject: [PATCH 1651/5498] scsi: lpfc: FCoE VPort enable-disable does not bring up the VPort [ Upstream commit 104450eb08ca662e6b1d02da11aca9598e978f3e ] FCoE VPort enable-disable does not bring up the VPort. VPI structure needed to be initialized before being re-registered. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_vport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index a87ee33f4f2a..1ea4702da5bb 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -528,6 +528,12 @@ enable_vport(struct fc_vport *fc_vport) spin_lock_irq(shost->host_lock); vport->load_flag |= FC_LOADING; + if (vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI) { + spin_unlock_irq(shost->host_lock); + lpfc_issue_init_vpi(vport); + goto out; + } + vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; spin_unlock_irq(shost->host_lock); @@ -548,6 +554,8 @@ enable_vport(struct fc_vport *fc_vport) } else { lpfc_vport_set_state(vport, FC_VPORT_FAILED); } + +out: lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT, "1827 Vport Enabled.\n"); return VPORT_OK; -- GitLab From c0e8131e652c48f44090e0d3df592b2cee04b4e4 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 19 Dec 2016 15:07:24 -0800 Subject: [PATCH 1652/5498] scsi: lpfc: Correct host name in symbolic_name field [ Upstream commit 6c9231f604c2575be24c96d38deb70f145172f92 ] Correct host name in symbolic_name field of nameserver registrations Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_attr.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 2f9b96826ac0..a53dc1c71fd2 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -5136,6 +5136,19 @@ lpfc_free_sysfs_attr(struct lpfc_vport *vport) * Dynamic FC Host Attributes Support */ +/** + * lpfc_get_host_symbolic_name - Copy symbolic name into the scsi host + * @shost: kernel scsi host pointer. + **/ +static void +lpfc_get_host_symbolic_name(struct Scsi_Host *shost) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; + + lpfc_vport_symbolic_node_name(vport, fc_host_symbolic_name(shost), + sizeof fc_host_symbolic_name(shost)); +} + /** * lpfc_get_host_port_id - Copy the vport DID into the scsi host port id * @shost: kernel scsi host pointer. @@ -5670,6 +5683,8 @@ struct fc_function_template lpfc_transport_functions = { .show_host_supported_fc4s = 1, .show_host_supported_speeds = 1, .show_host_maxframe_size = 1, + + .get_host_symbolic_name = lpfc_get_host_symbolic_name, .show_host_symbolic_name = 1, /* dynamic attributes the driver supports */ @@ -5737,6 +5752,8 @@ struct fc_function_template lpfc_vport_transport_functions = { .show_host_supported_fc4s = 1, .show_host_supported_speeds = 1, .show_host_maxframe_size = 1, + + .get_host_symbolic_name = lpfc_get_host_symbolic_name, .show_host_symbolic_name = 1, /* dynamic attributes the driver supports */ -- GitLab From e7fc5d22fae5904b7ce7915456360e824fafefab Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 19 Dec 2016 15:07:23 -0800 Subject: [PATCH 1653/5498] scsi: lpfc: Correct issue leading to oops during link reset [ Upstream commit e6c6acc0e0223ddaf867628d420ee196349c6fae ] Correct issue leading to oops during link reset. Missing vport pointer. [mkp: fixed typo] Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_sli.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 42236ef89ab0..fb7dc27a2e1c 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -9774,6 +9774,7 @@ lpfc_sli_abort_iotag_issue(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, iabt->ulpCommand = CMD_CLOSE_XRI_CN; abtsiocbp->iocb_cmpl = lpfc_sli_abort_els_cmpl; + abtsiocbp->vport = vport; lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI, "0339 Abort xri x%x, original iotag x%x, " -- GitLab From ad550021c29227e60d8433fde31493bba100b3b1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 4 Jan 2017 12:34:14 +0100 Subject: [PATCH 1654/5498] ALSA: vx: Don't try to update capture stream before running [ Upstream commit ed3c177d960bb5881b945ca6f784868126bb90db ] The update of stream costs significantly, and we should avoid it unless the stream really has started. Check pipe->running flag instead of pipe->prepared. Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/drivers/vx/vx_pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index 11467272089e..69f252585780 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -1015,7 +1015,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream int size, space, count; struct snd_pcm_runtime *runtime = subs->runtime; - if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) + if (!pipe->running || (chip->chip_status & VX_STAT_IS_STALE)) return; size = runtime->buffer_size - snd_pcm_capture_avail(runtime); -- GitLab From 637661fadb01d7f33c549fa1506848a250a4b1d6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 4 Jan 2017 12:19:15 +0100 Subject: [PATCH 1655/5498] ALSA: vx: Fix possible transfer overflow [ Upstream commit 874e1f6fad9a5184b67f4cee37c1335cd2cc5677 ] The pseudo DMA transfer codes in VX222 and VX-pocket driver have a slight bug where they check the buffer boundary wrongly, and may overflow. Also, the zero sample count might be handled badly for the playback (although it shouldn't happen in theory). This patch addresses these issues. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=141541 Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/drivers/vx/vx_pcm.c | 6 ++++-- sound/pci/vx222/vx222_ops.c | 12 ++++++------ sound/pcmcia/vx/vxp_ops.c | 12 ++++++------ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index 69f252585780..ea7b377f0378 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -1048,8 +1048,10 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream /* ok, let's accelerate! */ int align = pipe->align * 3; space = (count / align) * align; - vx_pseudo_dma_read(chip, runtime, pipe, space); - count -= space; + if (space > 0) { + vx_pseudo_dma_read(chip, runtime, pipe, space); + count -= space; + } } /* read the rest of bytes */ while (count > 0) { diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 2d1570273e99..5c541ed723dd 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -264,12 +264,12 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, /* Transfer using pseudo-dma. */ - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) { + for (; length > 0; length--) { outl(cpu_to_le32(*addr), port); addr++; } @@ -279,7 +279,7 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) { + for (; count > 0; count--) { outl(cpu_to_le32(*addr), port); addr++; } @@ -302,12 +302,12 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, vx2_setup_pseudo_dma(chip, 0); /* Transfer using pseudo-dma. */ - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) + for (; length > 0; length--) *addr++ = le32_to_cpu(inl(port)); addr = (u32 *)runtime->dma_area; pipe->hw_ptr = 0; @@ -315,7 +315,7 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) + for (; count > 0; count--) *addr++ = le32_to_cpu(inl(port)); vx2_release_pseudo_dma(chip); diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 281972913c32..56aa1ba73ccc 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -369,12 +369,12 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); vx_setup_pseudo_dma(chip, 1); - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) { + for (; length > 0; length--) { outw(cpu_to_le16(*addr), port); addr++; } @@ -384,7 +384,7 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) { + for (; count > 0; count--) { outw(cpu_to_le16(*addr), port); addr++; } @@ -411,12 +411,12 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, if (snd_BUG_ON(count % 2)) return; vx_setup_pseudo_dma(chip, 0); - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) + for (; length > 0; length--) *addr++ = le16_to_cpu(inw(port)); addr = (unsigned short *)runtime->dma_area; pipe->hw_ptr = 0; @@ -424,7 +424,7 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 1) + for (; count > 1; count--) *addr++ = le16_to_cpu(inw(port)); /* Disable DMA */ pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; -- GitLab From 7f213c377aaf7f36f0d523446a985462291cc474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 6 Jul 2016 19:33:05 +0200 Subject: [PATCH 1656/5498] backlight: lcd: Fix race condition during register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit cc21942bce652d1a92dae85b785378256e1df1f7 ] Once device_register is called for a device its attributes might be accessed. As the callbacks of a lcd device's attributes make use of the lcd_ops, the respective member must be setup before calling device_register. Signed-off-by: Uwe Kleine-König Signed-off-by: Lee Jones Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/backlight/lcd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 7de847df224f..4b40c6a4d441 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -226,6 +226,8 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, dev_set_name(&new_ld->dev, "%s", name); dev_set_drvdata(&new_ld->dev, devdata); + new_ld->ops = ops; + rc = device_register(&new_ld->dev); if (rc) { put_device(&new_ld->dev); @@ -238,8 +240,6 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, return ERR_PTR(rc); } - new_ld->ops = ops; - return new_ld; } EXPORT_SYMBOL(lcd_device_register); -- GitLab From c141816f72880d3bf2b0b2f86935986348da39d2 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 9 Jul 2016 01:19:51 +0300 Subject: [PATCH 1657/5498] backlight: adp5520: Fix error handling in adp5520_bl_probe() [ Upstream commit 0eb3fba8c68275f0122f65f7316efaaf86448016 ] If adp5520_bl_setup() fails, sysfs group left unremoved. By the way, fix overcomplicated assignement of error code. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Acked-by: Michael Hennerich Signed-off-by: Lee Jones Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/backlight/adp5520_bl.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c index dd88ba1d71ce..35373e2065b2 100644 --- a/drivers/video/backlight/adp5520_bl.c +++ b/drivers/video/backlight/adp5520_bl.c @@ -332,10 +332,18 @@ static int adp5520_bl_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, bl); - ret |= adp5520_bl_setup(bl); + ret = adp5520_bl_setup(bl); + if (ret) { + dev_err(&pdev->dev, "failed to setup\n"); + if (data->pdata->en_ambl_sens) + sysfs_remove_group(&bl->dev.kobj, + &adp5520_bl_attr_group); + return ret; + } + backlight_update_status(bl); - return ret; + return 0; } static int adp5520_bl_remove(struct platform_device *pdev) -- GitLab From 31308442a25c9980259f0b7c795116e8353c1a3b Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 3 Jan 2017 17:00:27 +0530 Subject: [PATCH 1658/5498] gpu: drm: mgag200: mgag200_main:- Handle error from pci_iomap [ Upstream commit 4b0ea93f250afc6c1128e201b0a8a115ae613e47 ] Here, pci_iomap can fail, handle this case and return -ENOMEM. Signed-off-by: Arvind Yadav Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1483443027-13444-1-git-send-email-arvind.yadav.cs@gmail.com Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/mgag200/mgag200_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index f6b283b8375e..d8352e47774d 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -138,6 +138,8 @@ static int mga_vram_init(struct mga_device *mdev) } mem = pci_iomap(mdev->dev->pdev, 0, 0); + if (!mem) + return -ENOMEM; mdev->mc.vram_size = mga_probe_vram(mdev, mem); -- GitLab From e0c60f54183970ee0f79e4fa595f9ea25fb2841d Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 16 Nov 2016 09:48:02 -0800 Subject: [PATCH 1659/5498] ixgbe: fix AER error handling [ Upstream commit 126db13fa0e6d05c9f94e0125f61e773bd5ab079 ] Make sure that we free the IRQs in ixgbe_io_error_detected() when responding to an PCIe AER error and also restore them when the interface recovers from it. Previously it was possible to trigger BUG_ON() check in free_msix_irqs() in the case where we call ixgbe_remove() after a failed recovery from AER error because the interrupts were not freed. Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index cc51554c9e99..f2b91915210e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8575,7 +8575,7 @@ skip_bad_vf_detection: } if (netif_running(netdev)) - ixgbe_down(adapter); + ixgbe_close_suspend(adapter); if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state)) pci_disable_device(pdev); @@ -8645,10 +8645,12 @@ static void ixgbe_io_resume(struct pci_dev *pdev) } #endif + rtnl_lock(); if (netif_running(netdev)) - ixgbe_up(adapter); + ixgbe_open(netdev); netif_device_attach(netdev); + rtnl_unlock(); } static const struct pci_error_handlers ixgbe_err_handler = { -- GitLab From 96f99bb96d9d3690dd099e4a9ee723377eea9f87 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Fri, 11 Nov 2016 10:07:47 -0800 Subject: [PATCH 1660/5498] ixgbe: handle close/suspend race with netif_device_detach/present [ Upstream commit f7f37e7ff2b9b7eff7fbd035569cab35896869a3 ] When an interface is part of a namespace it is possible that ixgbe_close() may be called while __ixgbe_shutdown() is running which ends up in a double free WARN and/or a BUG in free_msi_irqs(). To handle this situation we extend the rtnl_lock() to protect the call to netif_device_detach() and ixgbe_clear_interrupt_scheme() in __ixgbe_shutdown() and check for netif_device_present() to avoid clearing the interrupts second time in ixgbe_close(); Also extend the rtnl lock in ixgbe_resume() to netif_device_attach(). Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index f2b91915210e..de3a17088578 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5560,7 +5560,8 @@ static int ixgbe_close(struct net_device *netdev) ixgbe_ptp_stop(adapter); - ixgbe_close_suspend(adapter); + if (netif_device_present(netdev)) + ixgbe_close_suspend(adapter); ixgbe_fdir_filter_exit(adapter); @@ -5605,14 +5606,12 @@ static int ixgbe_resume(struct pci_dev *pdev) if (!err && netif_running(netdev)) err = ixgbe_open(netdev); - rtnl_unlock(); - if (err) - return err; - - netif_device_attach(netdev); + if (!err) + netif_device_attach(netdev); + rtnl_unlock(); - return 0; + return err; } #endif /* CONFIG_PM */ @@ -5627,14 +5626,14 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake) int retval = 0; #endif + rtnl_lock(); netif_device_detach(netdev); - rtnl_lock(); if (netif_running(netdev)) ixgbe_close_suspend(adapter); - rtnl_unlock(); ixgbe_clear_interrupt_scheme(adapter); + rtnl_unlock(); #ifdef CONFIG_PM retval = pci_save_state(pdev); -- GitLab From 623c6108c80d9919df98158eb52ee55b795beb4d Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Mon, 7 Nov 2016 11:14:09 +0000 Subject: [PATCH 1661/5498] MIPS: End asm function prologue macros with .insn [ Upstream commit 08889582b8aa0bbc01a1e5a0033b9f98d2e11caa ] When building a kernel targeting a microMIPS ISA, recent GNU linkers will fail the link if they cannot determine that the target of a branch or jump is microMIPS code, with errors such as the following: mips-img-linux-gnu-ld: arch/mips/built-in.o: .text+0x542c: Unsupported jump between ISA modes; consider recompiling with interlinking enabled. mips-img-linux-gnu-ld: final link failed: Bad value or: ./arch/mips/include/asm/uaccess.h:1017: warning: JALX to a non-word-aligned address Placing anything other than an instruction at the start of a function written in assembly appears to trigger such errors. In order to prepare for allowing us to follow function prologue macros with an EXPORT_SYMBOL invocation, end the prologue macros (LEAD, NESTED & FEXPORT) with a .insn directive. This ensures that the start of the function is marked as code, which always makes sense for functions & safely prevents us from hitting the link errors described above. Signed-off-by: Paul Burton Reviewed-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/14508/ Signed-off-by: Ralf Baechle Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/asm.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/mips/include/asm/asm.h b/arch/mips/include/asm/asm.h index 7c26b28bf252..859cf7048347 100644 --- a/arch/mips/include/asm/asm.h +++ b/arch/mips/include/asm/asm.h @@ -54,7 +54,8 @@ .align 2; \ .type symbol, @function; \ .ent symbol, 0; \ -symbol: .frame sp, 0, ra +symbol: .frame sp, 0, ra; \ + .insn /* * NESTED - declare nested routine entry point @@ -63,8 +64,9 @@ symbol: .frame sp, 0, ra .globl symbol; \ .align 2; \ .type symbol, @function; \ - .ent symbol, 0; \ -symbol: .frame sp, framesize, rpc + .ent symbol, 0; \ +symbol: .frame sp, framesize, rpc; \ + .insn /* * END - mark end of function @@ -86,7 +88,7 @@ symbol: #define FEXPORT(symbol) \ .globl symbol; \ .type symbol, @function; \ -symbol: +symbol: .insn /* * ABS - export absolute symbol -- GitLab From d7cad58e010f2bd758638cc0d293e4342ed80921 Mon Sep 17 00:00:00 2001 From: Marcin Nowakowski Date: Wed, 23 Nov 2016 14:43:44 +0100 Subject: [PATCH 1662/5498] MIPS: init: Ensure reserved memory regions are not added to bootmem [ Upstream commit e89ef66d7682f031f026eee6bba03c8c2248d2a9 ] Memories managed through boot_mem_map are generally expected to define non-crossing areas. However, if part of a larger memory block is marked as reserved, it would still be added to bootmem allocator as an available block and could end up being overwritten by the allocator. Prevent this by explicitly marking the memory as reserved it if exists in the range used by bootmem allocator. Signed-off-by: Marcin Nowakowski Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/14608/ Signed-off-by: Ralf Baechle Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/setup.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index f3b635f86c39..e9f330a864af 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -427,6 +427,10 @@ static void __init bootmem_init(void) continue; default: /* Not usable memory */ + if (start > min_low_pfn && end < max_low_pfn) + reserve_bootmem(boot_mem_map.map[i].addr, + boot_mem_map.map[i].size, + BOOTMEM_DEFAULT); continue; } -- GitLab From 8a8eca870ee7f9900596116b7b3aff2f48f3ce42 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Mon, 7 Nov 2016 11:30:41 +0000 Subject: [PATCH 1663/5498] MIPS: Netlogic: Exclude netlogic,xlp-pic code from XLR builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9799270affc53414da96e77e454a5616b39cdab0 ] Code in arch/mips/netlogic/common/irq.c which handles the XLP PIC fails to build in XLR configurations due to cpu_is_xlp9xx not being defined, leading to the following build failure: arch/mips/netlogic/common/irq.c: In function ‘xlp_of_pic_init’: arch/mips/netlogic/common/irq.c:298:2: error: implicit declaration of function ‘cpu_is_xlp9xx’ [-Werror=implicit-function-declaration] if (cpu_is_xlp9xx()) { ^ Although the code was conditional upon CONFIG_OF which is indirectly selected by CONFIG_NLM_XLP_BOARD but not CONFIG_NLM_XLR_BOARD, the failing XLR with CONFIG_OF configuration can be configured manually or by randconfig. Fix the build failure by making the affected XLP PIC code conditional upon CONFIG_CPU_XLP which is used to guard the inclusion of asm/netlogic/xlp-hal/xlp.h that provides the required cpu_is_xlp9xx function. [ralf@linux-mips.org: Fixed up as per Jayachandran's suggestion.] Signed-off-by: Paul Burton Cc: Jayachandran C Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/14524/ Signed-off-by: Ralf Baechle Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/mips/netlogic/common/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/netlogic/common/irq.c b/arch/mips/netlogic/common/irq.c index c100b9afa0ab..48b43ee0d199 100644 --- a/arch/mips/netlogic/common/irq.c +++ b/arch/mips/netlogic/common/irq.c @@ -275,7 +275,7 @@ asmlinkage void plat_irq_dispatch(void) do_IRQ(nlm_irq_to_xirq(node, i)); } -#ifdef CONFIG_OF +#ifdef CONFIG_CPU_XLP static const struct irq_domain_ops xlp_pic_irq_domain_ops = { .xlate = irq_domain_xlate_onetwocell, }; @@ -348,7 +348,7 @@ void __init arch_init_irq(void) #if defined(CONFIG_CPU_XLR) nlm_setup_fmn_irq(); #endif -#if defined(CONFIG_OF) +#ifdef CONFIG_CPU_XLP of_irq_init(xlp_pic_irq_ids); #endif } -- GitLab From 83c47200d062b1bc679d4c82d439d9db8c8089f3 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 13 Nov 2017 17:55:20 -0500 Subject: [PATCH 1664/5498] Revert "crypto: xts - Add ECB dependency" This reverts commit 6145171a6bc0abdc3eca7a4b795ede467d2ba569. The commit fixes a bug that was only introduced in 4.10, thus is irrelevant for <=4.9. Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- crypto/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index 7bd153fd68fe..87bbc9c1e681 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -290,7 +290,6 @@ config CRYPTO_XTS select CRYPTO_BLKCIPHER select CRYPTO_MANAGER select CRYPTO_GF128MUL - select CRYPTO_ECB help XTS: IEEE1619/D16 narrow block cipher use with aes-xts-plain, key size 256, 384 or 512 bits. This implementation currently -- GitLab From fb75ce8dc5811d5c19919f94079118997838d8d3 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 13 Nov 2017 18:03:32 -0500 Subject: [PATCH 1665/5498] Revert "uapi: fix linux/rds.h userspace compilation errors" This reverts commit ad50561ba7a664bc581826c9d57d137fcf17bfa5. There was a mixup with the commit message for two upstream commit that have the same subject line. This revert will be followed by the two commits with proper commit messages. Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/rds.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h index 25e865320288..91950950aa59 100644 --- a/include/uapi/linux/rds.h +++ b/include/uapi/linux/rds.h @@ -35,7 +35,6 @@ #define _LINUX_RDS_H #include -#include /* For __kernel_sockaddr_storage. */ #define RDS_IB_ABI_VERSION 0x301 @@ -214,7 +213,7 @@ struct rds_get_mr_args { }; struct rds_get_mr_for_dest_args { - struct __kernel_sockaddr_storage dest_addr; + struct sockaddr_storage dest_addr; struct rds_iovec vec; uint64_t cookie_addr; uint64_t flags; -- GitLab From 12c50a1f5746e803d5e661ab8bfe0f870e8ce2e2 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 16 Feb 2017 18:05:45 +0300 Subject: [PATCH 1666/5498] uapi: fix linux/rds.h userspace compilation error [ Upstream commit 1786dbf3702e33ce3afd2d3dbe630bd04b1d2e58 ] On the kernel side, sockaddr_storage is #define'd to __kernel_sockaddr_storage. Replacing struct sockaddr_storage with struct __kernel_sockaddr_storage defined by fixes the following linux/rds.h userspace compilation error: /usr/include/linux/rds.h:226:26: error: field 'dest_addr' has incomplete type struct sockaddr_storage dest_addr; Signed-off-by: Dmitry V. Levin Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/rds.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h index 91950950aa59..25e865320288 100644 --- a/include/uapi/linux/rds.h +++ b/include/uapi/linux/rds.h @@ -35,6 +35,7 @@ #define _LINUX_RDS_H #include +#include /* For __kernel_sockaddr_storage. */ #define RDS_IB_ABI_VERSION 0x301 @@ -213,7 +214,7 @@ struct rds_get_mr_args { }; struct rds_get_mr_for_dest_args { - struct sockaddr_storage dest_addr; + struct __kernel_sockaddr_storage dest_addr; struct rds_iovec vec; uint64_t cookie_addr; uint64_t flags; -- GitLab From 8cf8ebdce459c57c9ca75448e29feced04dcfd79 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 16 Feb 2017 18:05:13 +0300 Subject: [PATCH 1667/5498] uapi: fix linux/rds.h userspace compilation errors [ Upstream commit feb0869d90e51ce8b6fd8a46588465b1b5a26d09 ] Consistently use types from linux/types.h to fix the following linux/rds.h userspace compilation errors: /usr/include/linux/rds.h:106:2: error: unknown type name 'uint8_t' uint8_t name[32]; /usr/include/linux/rds.h:107:2: error: unknown type name 'uint64_t' uint64_t value; /usr/include/linux/rds.h:117:2: error: unknown type name 'uint64_t' uint64_t next_tx_seq; /usr/include/linux/rds.h:118:2: error: unknown type name 'uint64_t' uint64_t next_rx_seq; /usr/include/linux/rds.h:121:2: error: unknown type name 'uint8_t' uint8_t transport[TRANSNAMSIZ]; /* null term ascii */ /usr/include/linux/rds.h:122:2: error: unknown type name 'uint8_t' uint8_t flags; /usr/include/linux/rds.h:129:2: error: unknown type name 'uint64_t' uint64_t seq; /usr/include/linux/rds.h:130:2: error: unknown type name 'uint32_t' uint32_t len; /usr/include/linux/rds.h:135:2: error: unknown type name 'uint8_t' uint8_t flags; /usr/include/linux/rds.h:139:2: error: unknown type name 'uint32_t' uint32_t sndbuf; /usr/include/linux/rds.h:144:2: error: unknown type name 'uint32_t' uint32_t rcvbuf; /usr/include/linux/rds.h:145:2: error: unknown type name 'uint64_t' uint64_t inum; /usr/include/linux/rds.h:153:2: error: unknown type name 'uint64_t' uint64_t hdr_rem; /usr/include/linux/rds.h:154:2: error: unknown type name 'uint64_t' uint64_t data_rem; /usr/include/linux/rds.h:155:2: error: unknown type name 'uint32_t' uint32_t last_sent_nxt; /usr/include/linux/rds.h:156:2: error: unknown type name 'uint32_t' uint32_t last_expected_una; /usr/include/linux/rds.h:157:2: error: unknown type name 'uint32_t' uint32_t last_seen_una; /usr/include/linux/rds.h:164:2: error: unknown type name 'uint8_t' uint8_t src_gid[RDS_IB_GID_LEN]; /usr/include/linux/rds.h:165:2: error: unknown type name 'uint8_t' uint8_t dst_gid[RDS_IB_GID_LEN]; /usr/include/linux/rds.h:167:2: error: unknown type name 'uint32_t' uint32_t max_send_wr; /usr/include/linux/rds.h:168:2: error: unknown type name 'uint32_t' uint32_t max_recv_wr; /usr/include/linux/rds.h:169:2: error: unknown type name 'uint32_t' uint32_t max_send_sge; /usr/include/linux/rds.h:170:2: error: unknown type name 'uint32_t' uint32_t rdma_mr_max; /usr/include/linux/rds.h:171:2: error: unknown type name 'uint32_t' uint32_t rdma_mr_size; /usr/include/linux/rds.h:212:9: error: unknown type name 'uint64_t' typedef uint64_t rds_rdma_cookie_t; /usr/include/linux/rds.h:215:2: error: unknown type name 'uint64_t' uint64_t addr; /usr/include/linux/rds.h:216:2: error: unknown type name 'uint64_t' uint64_t bytes; /usr/include/linux/rds.h:221:2: error: unknown type name 'uint64_t' uint64_t cookie_addr; /usr/include/linux/rds.h:222:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:228:2: error: unknown type name 'uint64_t' uint64_t cookie_addr; /usr/include/linux/rds.h:229:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:234:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:240:2: error: unknown type name 'uint64_t' uint64_t local_vec_addr; /usr/include/linux/rds.h:241:2: error: unknown type name 'uint64_t' uint64_t nr_local; /usr/include/linux/rds.h:242:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:243:2: error: unknown type name 'uint64_t' uint64_t user_token; /usr/include/linux/rds.h:248:2: error: unknown type name 'uint64_t' uint64_t local_addr; /usr/include/linux/rds.h:249:2: error: unknown type name 'uint64_t' uint64_t remote_addr; /usr/include/linux/rds.h:252:4: error: unknown type name 'uint64_t' uint64_t compare; /usr/include/linux/rds.h:253:4: error: unknown type name 'uint64_t' uint64_t swap; /usr/include/linux/rds.h:256:4: error: unknown type name 'uint64_t' uint64_t add; /usr/include/linux/rds.h:259:4: error: unknown type name 'uint64_t' uint64_t compare; /usr/include/linux/rds.h:260:4: error: unknown type name 'uint64_t' uint64_t swap; /usr/include/linux/rds.h:261:4: error: unknown type name 'uint64_t' uint64_t compare_mask; /usr/include/linux/rds.h:262:4: error: unknown type name 'uint64_t' uint64_t swap_mask; /usr/include/linux/rds.h:265:4: error: unknown type name 'uint64_t' uint64_t add; /usr/include/linux/rds.h:266:4: error: unknown type name 'uint64_t' uint64_t nocarry_mask; /usr/include/linux/rds.h:269:2: error: unknown type name 'uint64_t' uint64_t flags; /usr/include/linux/rds.h:270:2: error: unknown type name 'uint64_t' uint64_t user_token; /usr/include/linux/rds.h:274:2: error: unknown type name 'uint64_t' uint64_t user_token; /usr/include/linux/rds.h:275:2: error: unknown type name 'int32_t' int32_t status; Signed-off-by: Dmitry V. Levin Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/rds.h | 102 +++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h index 25e865320288..a9cb4e33b527 100644 --- a/include/uapi/linux/rds.h +++ b/include/uapi/linux/rds.h @@ -94,8 +94,8 @@ #define RDS_INFO_LAST 10010 struct rds_info_counter { - uint8_t name[32]; - uint64_t value; + __u8 name[32]; + __u64 value; } __attribute__((packed)); #define RDS_INFO_CONNECTION_FLAG_SENDING 0x01 @@ -105,35 +105,35 @@ struct rds_info_counter { #define TRANSNAMSIZ 16 struct rds_info_connection { - uint64_t next_tx_seq; - uint64_t next_rx_seq; + __u64 next_tx_seq; + __u64 next_rx_seq; __be32 laddr; __be32 faddr; - uint8_t transport[TRANSNAMSIZ]; /* null term ascii */ - uint8_t flags; + __u8 transport[TRANSNAMSIZ]; /* null term ascii */ + __u8 flags; } __attribute__((packed)); #define RDS_INFO_MESSAGE_FLAG_ACK 0x01 #define RDS_INFO_MESSAGE_FLAG_FAST_ACK 0x02 struct rds_info_message { - uint64_t seq; - uint32_t len; + __u64 seq; + __u32 len; __be32 laddr; __be32 faddr; __be16 lport; __be16 fport; - uint8_t flags; + __u8 flags; } __attribute__((packed)); struct rds_info_socket { - uint32_t sndbuf; + __u32 sndbuf; __be32 bound_addr; __be32 connected_addr; __be16 bound_port; __be16 connected_port; - uint32_t rcvbuf; - uint64_t inum; + __u32 rcvbuf; + __u64 inum; } __attribute__((packed)); struct rds_info_tcp_socket { @@ -141,25 +141,25 @@ struct rds_info_tcp_socket { __be16 local_port; __be32 peer_addr; __be16 peer_port; - uint64_t hdr_rem; - uint64_t data_rem; - uint32_t last_sent_nxt; - uint32_t last_expected_una; - uint32_t last_seen_una; + __u64 hdr_rem; + __u64 data_rem; + __u32 last_sent_nxt; + __u32 last_expected_una; + __u32 last_seen_una; } __attribute__((packed)); #define RDS_IB_GID_LEN 16 struct rds_info_rdma_connection { __be32 src_addr; __be32 dst_addr; - uint8_t src_gid[RDS_IB_GID_LEN]; - uint8_t dst_gid[RDS_IB_GID_LEN]; + __u8 src_gid[RDS_IB_GID_LEN]; + __u8 dst_gid[RDS_IB_GID_LEN]; - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t rdma_mr_max; - uint32_t rdma_mr_size; + __u32 max_send_wr; + __u32 max_recv_wr; + __u32 max_send_sge; + __u32 rdma_mr_max; + __u32 rdma_mr_size; }; /* @@ -200,70 +200,70 @@ struct rds_info_rdma_connection { * (so that the application does not have to worry about * alignment). */ -typedef uint64_t rds_rdma_cookie_t; +typedef __u64 rds_rdma_cookie_t; struct rds_iovec { - uint64_t addr; - uint64_t bytes; + __u64 addr; + __u64 bytes; }; struct rds_get_mr_args { struct rds_iovec vec; - uint64_t cookie_addr; - uint64_t flags; + __u64 cookie_addr; + __u64 flags; }; struct rds_get_mr_for_dest_args { struct __kernel_sockaddr_storage dest_addr; struct rds_iovec vec; - uint64_t cookie_addr; - uint64_t flags; + __u64 cookie_addr; + __u64 flags; }; struct rds_free_mr_args { rds_rdma_cookie_t cookie; - uint64_t flags; + __u64 flags; }; struct rds_rdma_args { rds_rdma_cookie_t cookie; struct rds_iovec remote_vec; - uint64_t local_vec_addr; - uint64_t nr_local; - uint64_t flags; - uint64_t user_token; + __u64 local_vec_addr; + __u64 nr_local; + __u64 flags; + __u64 user_token; }; struct rds_atomic_args { rds_rdma_cookie_t cookie; - uint64_t local_addr; - uint64_t remote_addr; + __u64 local_addr; + __u64 remote_addr; union { struct { - uint64_t compare; - uint64_t swap; + __u64 compare; + __u64 swap; } cswp; struct { - uint64_t add; + __u64 add; } fadd; struct { - uint64_t compare; - uint64_t swap; - uint64_t compare_mask; - uint64_t swap_mask; + __u64 compare; + __u64 swap; + __u64 compare_mask; + __u64 swap_mask; } m_cswp; struct { - uint64_t add; - uint64_t nocarry_mask; + __u64 add; + __u64 nocarry_mask; } m_fadd; }; - uint64_t flags; - uint64_t user_token; + __u64 flags; + __u64 user_token; }; struct rds_rdma_notify { - uint64_t user_token; - int32_t status; + __u64 user_token; + __s32 status; }; #define RDS_RDMA_SUCCESS 0 -- GitLab From 6a5d144279755576826148b94b28f652bdf0cca3 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 8 Nov 2017 12:23:17 -0500 Subject: [PATCH 1668/5498] USB: usbfs: compute urb->actual_length for isochronous commit 2ef47001b3ee3ded579b7532ebdcf8680e4d8c54 upstream. The USB kerneldoc says that the actual_length field "is read in non-iso completion functions", but the usbfs driver uses it for all URB types in processcompl(). Since not all of the host controller drivers set actual_length for isochronous URBs, programs using usbfs with some host controllers don't work properly. For example, Minas reports that a USB camera controlled by libusb doesn't work properly with a dwc2 controller. It doesn't seem worthwhile to change the HCDs and the documentation, since the in-kernel USB class drivers evidently don't rely on actual_length for isochronous transfers. The easiest solution is for usbfs to calculate the actual_length value for itself, by adding up the lengths of the individual packets in an isochronous transfer. Signed-off-by: Alan Stern CC: Minas Harutyunyan Reported-and-tested-by: wlf Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index d7edec160a6d..f322111db601 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1649,6 +1649,18 @@ static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg) return 0; } +static void compute_isochronous_actual_length(struct urb *urb) +{ + unsigned int i; + + if (urb->number_of_packets > 0) { + urb->actual_length = 0; + for (i = 0; i < urb->number_of_packets; i++) + urb->actual_length += + urb->iso_frame_desc[i].actual_length; + } +} + static int processcompl(struct async *as, void __user * __user *arg) { struct urb *urb = as->urb; @@ -1656,6 +1668,7 @@ static int processcompl(struct async *as, void __user * __user *arg) void __user *addr = as->userurb; unsigned int i; + compute_isochronous_actual_length(urb); if (as->userbuffer && urb->actual_length) { if (copy_urb_data_to_user(as->userbuffer, urb)) goto err_out; @@ -1825,6 +1838,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) void __user *addr = as->userurb; unsigned int i; + compute_isochronous_actual_length(urb); if (as->userbuffer && urb->actual_length) { if (copy_urb_data_to_user(as->userbuffer, urb)) return -EFAULT; -- GitLab From cbe3cdcf641b056bd31dda9a9618a08824d23635 Mon Sep 17 00:00:00 2001 From: Bernhard Rosenkraenzer Date: Fri, 3 Nov 2017 16:46:02 +0100 Subject: [PATCH 1669/5498] USB: Add delay-init quirk for Corsair K70 LUX keyboards commit a0fea6027f19c62727315aba1a7fae75a9caa842 upstream. Without this patch, K70 LUX keyboards don't work, saying usb 3-3: unable to read config index 0 descriptor/all usb 3-3: can't read configurations, error -110 usb usb3-port3: unable to enumerate USB device Signed-off-by: Bernhard Rosenkraenzer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 76d92d84e0d6..d2746553b5f5 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -214,6 +214,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Corsair Strafe RGB */ { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Corsair K70 LUX */ + { USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT }, + /* MIDI keyboard WORLDE MINI */ { USB_DEVICE(0x1c75, 0x0204), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, -- GitLab From a23a605121c28917fdb85abd6f863f6ab50dc885 Mon Sep 17 00:00:00 2001 From: Douglas Fischer Date: Sun, 29 Oct 2017 23:29:55 +0000 Subject: [PATCH 1670/5498] USB: serial: qcserial: add pid/vid for Sierra Wireless EM7355 fw update commit 771394a54148f18926ca86414e51c69eda27d0cd upstream. Add USB PID/VID for Sierra Wireless EM7355 LTE modem QDL firmware update mode. Signed-off-by: Douglas Fischer Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 2a2ff0ba9f7f..adc372411019 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -143,6 +143,7 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */ {DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */ {DEVICE_SWI(0x1199, 0x901c)}, /* Sierra Wireless EM7700 */ + {DEVICE_SWI(0x1199, 0x901e)}, /* Sierra Wireless EM7355 QDL */ {DEVICE_SWI(0x1199, 0x901f)}, /* Sierra Wireless EM7355 */ {DEVICE_SWI(0x1199, 0x9040)}, /* Sierra Wireless Modem */ {DEVICE_SWI(0x1199, 0x9051)}, /* Netgear AirCard 340U */ -- GitLab From c0d6a6f83281a73ef8d60ab0e3ebdd134217c295 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 3 Jan 2017 16:39:41 +0100 Subject: [PATCH 1671/5498] USB: serial: garmin_gps: fix memory leak on failed URB submit commit c4ac4496e835b78a45dfbf74f6173932217e4116 upstream. Make sure to free the URB transfer buffer in case submission fails (e.g. due to a disconnect). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/garmin_gps.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index db591d19d416..37d0e8cc7af6 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1044,6 +1044,7 @@ static int garmin_write_bulk(struct usb_serial_port *port, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __func__, status); count = status; + kfree(buffer); } /* we are done with this urb, so let the host driver -- GitLab From 50cc5314001862bd08e025f54c71ecd5f8b211ce Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Oct 2017 14:02:57 +0200 Subject: [PATCH 1672/5498] USB: serial: garmin_gps: fix I/O after failed probe and remove commit 19a565d9af6e0d828bd0d521d3bafd5017f4ce52 upstream. Make sure to stop any submitted interrupt and bulk-out URBs before returning after failed probe and when the port is being unbound to avoid later NULL-pointer dereferences in the completion callbacks. Also fix up the related and broken I/O cancellation on failed open and on close. (Note that port->write_urb was never submitted.) Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/garmin_gps.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 37d0e8cc7af6..8ac161cbfba8 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -138,6 +138,7 @@ struct garmin_data { __u8 privpkt[4*6]; spinlock_t lock; struct list_head pktlist; + struct usb_anchor write_urbs; }; @@ -906,13 +907,19 @@ static int garmin_init_session(struct usb_serial_port *port) sizeof(GARMIN_START_SESSION_REQ), 0); if (status < 0) - break; + goto err_kill_urbs; } if (status > 0) status = 0; } + return status; + +err_kill_urbs: + usb_kill_anchored_urbs(&garmin_data_p->write_urbs); + usb_kill_urb(port->interrupt_in_urb); + return status; } @@ -931,7 +938,6 @@ static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port) spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* shutdown any bulk reads that might be going on */ - usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); if (garmin_data_p->state == STATE_RESET) @@ -954,7 +960,7 @@ static void garmin_close(struct usb_serial_port *port) /* shutdown our urbs */ usb_kill_urb(port->read_urb); - usb_kill_urb(port->write_urb); + usb_kill_anchored_urbs(&garmin_data_p->write_urbs); /* keep reset state so we know that we must start a new session */ if (garmin_data_p->state != STATE_RESET) @@ -1038,12 +1044,14 @@ static int garmin_write_bulk(struct usb_serial_port *port, } /* send it down the pipe */ + usb_anchor_urb(urb, &garmin_data_p->write_urbs); status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __func__, status); count = status; + usb_unanchor_urb(urb); kfree(buffer); } @@ -1402,6 +1410,7 @@ static int garmin_port_probe(struct usb_serial_port *port) garmin_data_p->state = 0; garmin_data_p->flags = 0; garmin_data_p->count = 0; + init_usb_anchor(&garmin_data_p->write_urbs); usb_set_serial_port_data(port, garmin_data_p); status = garmin_init_session(port); @@ -1414,6 +1423,7 @@ static int garmin_port_remove(struct usb_serial_port *port) { struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); + usb_kill_anchored_urbs(&garmin_data_p->write_urbs); usb_kill_urb(port->interrupt_in_urb); del_timer_sync(&garmin_data_p->timer); kfree(garmin_data_p); -- GitLab From 6d7a134fe3c25f806a413cd38a11ab1f5bec0c28 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 11 Oct 2017 14:02:58 +0200 Subject: [PATCH 1673/5498] USB: serial: garmin_gps: fix memory leak on probe errors commit 74d471b598444b7f2d964930f7234779c80960a0 upstream. Make sure to free the port private data before returning after a failed probe attempt. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/garmin_gps.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 8ac161cbfba8..2220c1b9df10 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1414,6 +1414,12 @@ static int garmin_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, garmin_data_p); status = garmin_init_session(port); + if (status) + goto err_free; + + return 0; +err_free: + kfree(garmin_data_p); return status; } -- GitLab From c35c375efa4e2c832946a04e83155f928135e8f6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 21 Nov 2017 09:01:08 +0100 Subject: [PATCH 1674/5498] Linux 3.18.83 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 786deb6599f1..8a1e51e5b0cf 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 82 +SUBLEVEL = 83 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 71bc67af5e6b33c906e9c4038648d0cef656a5d6 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Tue, 21 Nov 2017 12:15:33 +0530 Subject: [PATCH 1675/5498] usb: gadget: Send DSR linestate on DTR high from host When the host PC sends a DTR high during DUN call, it expects to get a DSR signal from the modem in reply. Add necessary support for that. Change-Id: If46c4b75fa6210c2b15cd2880bc93461fa3a4e89 Signed-off-by: Ajay Agarwal --- drivers/usb/gadget/function/u_data_bridge.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/usb/gadget/function/u_data_bridge.c b/drivers/usb/gadget/function/u_data_bridge.c index a5bd9fda32e9..cc4ae14db721 100644 --- a/drivers/usb/gadget/function/u_data_bridge.c +++ b/drivers/usb/gadget/function/u_data_bridge.c @@ -633,6 +633,9 @@ static int gbridge_port_tiocmget(struct gbridge_port *port) if (gser->serial_state & TIOCM_RI) result |= TIOCM_RI; + + if (gser->serial_state & TIOCM_DSR) + result |= TIOCM_DSR; fail: spin_unlock_irqrestore(&port->port_lock, flags); return result; @@ -682,6 +685,10 @@ static int gbridge_port_tiocmset(struct gbridge_port *port, status = gser->send_carrier_detect(gser, 0); } } + if (set & TIOCM_DSR) + gser->serial_state |= TIOCM_DSR; + if (clear & TIOCM_DSR) + gser->serial_state &= ~TIOCM_DSR; fail: spin_unlock_irqrestore(&port->port_lock, flags); return status; @@ -755,6 +762,18 @@ static void gbridge_notify_modem(void *gptr, u8 portno, int ctrl_bits) port->cbits_to_modem = temp; port->cbits_updated = true; spin_unlock_irqrestore(&port->port_lock, flags); + /* if DTR is high, update latest modem info to laptop */ + if (port->cbits_to_modem & TIOCM_DTR) { + unsigned int result; + unsigned cbits_to_laptop; + + result = gbridge_port_tiocmget(port); + cbits_to_laptop = convert_uart_sigs_to_acm(result); + if (gser->send_modem_ctrl_bits) + gser->send_modem_ctrl_bits( + port->port_usb, cbits_to_laptop); + } + wake_up(&port->read_wq); } -- GitLab From 7a85b5fa976d0aa7ebfb9e4492e5397b276b7e88 Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Tue, 21 Nov 2017 18:32:08 +0530 Subject: [PATCH 1676/5498] msm: ep_pcie: Unregister to bus driver in error path Add support to unregister the client from the msm bus driver in error and exit path. Change-Id: I7ec7a17dd2d53f6725a70f4a42758be817576e50 Signed-off-by: Rama Krishna Phani A --- drivers/platform/msm/ep_pcie/ep_pcie_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index 1bda256aad73..743f009f1875 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -993,6 +993,11 @@ static void ep_pcie_release_resources(struct ep_pcie_dev_t *dev) dev->phy = NULL; dev->mmio = NULL; dev->msi = NULL; + + if (dev->bus_client) { + msm_bus_scale_unregister_client(dev->bus_client); + dev->bus_client = 0; + } } static void ep_pcie_enumeration_complete(struct ep_pcie_dev_t *dev) -- GitLab From e5f582703197f71698786c5670901d73f8fe7b5e Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Fri, 17 Nov 2017 14:10:36 +0530 Subject: [PATCH 1677/5498] soc: qcom: bgcom: Adds support for BG-RSB events Adds support for multiple events geenerated by BG. Parses the event data and sends the event to respective clients. Change-Id: If344d8b805475595142afc2be13f584f0107ed78 Signed-off-by: Arjun Singh --- drivers/soc/qcom/bgcom_spi.c | 48 ++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c index 745438419cb5..a338a277d4a0 100644 --- a/drivers/soc/qcom/bgcom_spi.c +++ b/drivers/soc/qcom/bgcom_spi.c @@ -41,6 +41,9 @@ #define BG_SPI_MAX_WORDS (0x3FFFFFFD) #define BG_SPI_MAX_REGS (0x0A) #define SLEEP_IN_STATE_CHNG 2000 +#define HED_EVENT_ID_LEN (0x02) +#define HED_EVENT_SIZE_LEN (0x02) +#define HED_EVENT_DATA_STRT_LEN (0x05) enum bgcom_state { /*BGCOM Staus ready*/ @@ -86,9 +89,13 @@ static uint32_t g_slav_status_reg; /* BGCOM client callbacks set-up */ static struct list_head cb_head = LIST_HEAD_INIT(cb_head); - static enum bgcom_spi_state spi_state; +static void augmnt_fifo(uint8_t *data, int pos) +{ + data[pos] = '\0'; +} + int bgcom_set_spi_state(enum bgcom_spi_state state) { struct bg_spi_priv *bg_spi = container_of(bg_com_drv, @@ -214,6 +221,34 @@ void send_event(enum bgcom_event_type event, } } +static void parse_fifo(uint8_t *data, union bgcom_event_data_type *event_data) +{ + uint16_t p_len; + uint16_t event_id; + void *evnt_data; + + while (*data != '\0') { + + event_id = *((uint16_t *) data); + data = data + HED_EVENT_ID_LEN; + p_len = *((uint16_t *) data); + data = data + HED_EVENT_SIZE_LEN; + + if (event_id == 0x0001) { + evnt_data = kmalloc(p_len, GFP_KERNEL); + if (evnt_data != NULL) { + memcpy(evnt_data, data, p_len); + event_data->fifo_data.to_master_fifo_used = + p_len/BG_SPI_WORD_SIZE; + event_data->fifo_data.data = evnt_data; + send_event(BGCOM_EVENT_TO_MASTER_FIFO_USED, + event_data); + } + } + data = data + p_len; + } +} + static void send_back_notification(uint32_t slav_status_reg, uint32_t slav_status_auto_clear_reg, uint32_t fifo_fill_reg, uint32_t fifo_size_reg) @@ -288,18 +323,17 @@ static void send_back_notification(uint32_t slav_status_reg, } if (master_fifo_used > 0) { - ptr = kzalloc(master_fifo_used*BG_SPI_WORD_SIZE, + ptr = kzalloc(master_fifo_used*BG_SPI_WORD_SIZE + 1, GFP_KERNEL | GFP_ATOMIC); if (ptr != NULL) { ret = read_bg_locl(BGCOM_READ_FIFO, master_fifo_used, ptr); if (!ret) { - event_data.fifo_data.to_master_fifo_used = - master_fifo_used; - event_data.fifo_data.data = ptr; - send_event(BGCOM_EVENT_TO_MASTER_FIFO_USED, - &event_data); + augmnt_fifo((uint8_t *)ptr, + master_fifo_used*BG_SPI_WORD_SIZE); + parse_fifo((uint8_t *)ptr, &event_data); } + kfree(ptr); } } -- GitLab From 6ab844ecc33ce6165f59527dcb8e4dfb73a0a48e Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 27 Oct 2017 10:34:47 +0530 Subject: [PATCH 1678/5498] ARM: dts: msm: Disable 'no-map-fixup' for modem carveout CONFIG_ENABLE_VMALLOC_SAVING has been enabled by default for targets supporting 32bit, to extend vmalloc space to carveouts. CONFIG_ENABLE_VMALLOC_SAVING and 'no-map-fixup' are not effective together as 'no-map-fixup' performs a delayed carveout while vmalloc saving feature coming up early assumes it to be logical mapped region, thus not being able to reuse corresponding address space for vmalloc. Disable 'no-map-fixup' for all relevant targets. Change-Id: Ib30b9b79d43207176a597a6c65f882da171eebb5 Signed-off-by: Shiraz Hashim --- arch/arm/boot/dts/qcom/msm8917.dtsi | 2 +- arch/arm/boot/dts/qcom/msm8937.dtsi | 2 +- arch/arm/boot/dts/qcom/msm8953.dtsi | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8917.dtsi b/arch/arm/boot/dts/qcom/msm8917.dtsi index c529836d6d66..aad25c36d7f5 100644 --- a/arch/arm/boot/dts/qcom/msm8917.dtsi +++ b/arch/arm/boot/dts/qcom/msm8917.dtsi @@ -88,7 +88,7 @@ modem_mem: modem_region@0 { compatible = "removed-dma-pool"; - no-map-fixup; + no-map; reg = <0x0 0x86800000 0x0 0x5000000>; }; diff --git a/arch/arm/boot/dts/qcom/msm8937.dtsi b/arch/arm/boot/dts/qcom/msm8937.dtsi index ddf40d1b3bb3..7865dd07775a 100644 --- a/arch/arm/boot/dts/qcom/msm8937.dtsi +++ b/arch/arm/boot/dts/qcom/msm8937.dtsi @@ -64,7 +64,7 @@ modem_mem: modem_region@0 { compatible = "removed-dma-pool"; - no-map-fixup; + no-map; reg = <0x0 0x86800000 0x0 0x5000000>; }; diff --git a/arch/arm/boot/dts/qcom/msm8953.dtsi b/arch/arm/boot/dts/qcom/msm8953.dtsi index b857f1895562..2c523daaaadf 100644 --- a/arch/arm/boot/dts/qcom/msm8953.dtsi +++ b/arch/arm/boot/dts/qcom/msm8953.dtsi @@ -65,7 +65,7 @@ modem_mem: modem_region@0 { compatible = "removed-dma-pool"; - no-map-fixup; + no-map; reg = <0x0 0x86c00000 0x0 0x6a00000>; }; -- GitLab From 22804ee7bbc6122bb3cefca307a192385e226bce Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 21 Nov 2017 07:25:24 -0800 Subject: [PATCH 1679/5498] Revert "ANDROID: arm64: Use __pa_symbol for kernel symbols" This reverts commit 356791cb20b892420bc8106c3e0430cd09b78af0. Signed-off-by: Mark Salyzyn Bug: 20045882 Bug: 63737556 Change-Id: I0f3d7f16609b54c8610938210c66c1d6fe067785 --- arch/arm64/kernel/suspend.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c index 16215385cc25..ab14fee1f255 100644 --- a/arch/arm64/kernel/suspend.c +++ b/arch/arm64/kernel/suspend.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -164,7 +163,7 @@ static int __init cpu_suspend_init(void) sleep_save_sp.save_ptr_stash = ctx_ptr; sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr); - sleep_idmap_phys = __pa_symbol(idmap_pg_dir); + sleep_idmap_phys = virt_to_phys(idmap_pg_dir); __flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp)); __flush_dcache_area(&sleep_idmap_phys, sizeof(sleep_idmap_phys)); -- GitLab From aaeacdf4c6ac44b6d705aa00d65bee7b55eaeb77 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 21 Nov 2017 07:25:38 -0800 Subject: [PATCH 1680/5498] Revert "BACKPORT: arm64: Use __pa_symbol for empty_zero_page" This reverts commit cfe9be918b6bc977f75db3f81a1ec319668da7f0. Signed-off-by: Mark Salyzyn Bug: 20045882 Bug: 63737556 Change-Id: I54b109f27b13aaee14436681aa4d544c7d4f8021 --- arch/arm64/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index b4e3a9bc646b..39194cc7c665 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -296,7 +296,7 @@ void __init setup_arch(char **cmdline_p) * faults in case uaccess_enable() is inadvertently called by the init * thread. */ - init_thread_info.ttbr0 = __pa_symbol(empty_zero_page); + init_thread_info.ttbr0 = virt_to_phys(empty_zero_page); #endif #ifdef CONFIG_VT -- GitLab From 3cbb683dfb1e20e101a5df4d329b321b39bfc4f4 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Tue, 21 Nov 2017 07:26:41 -0800 Subject: [PATCH 1681/5498] Revert "BACKPORT: arm64: Use __pa_symbol for kernel symbols" This reverts commit 0d4768ee25efe949d45faf6d8a0a0b21c65eb2d0. Signed-off-by: Mark Salyzyn Bug: 20045882 Bug: 63737556 Change-Id: I506c7917add8322394038e2264c77d31c7979090 --- arch/arm64/include/asm/kvm_mmu.h | 2 +- arch/arm64/include/asm/memory.h | 1 - arch/arm64/include/asm/mmu_context.h | 2 +- arch/arm64/include/asm/pgtable.h | 2 +- arch/arm64/kernel/cpufeature.c | 1 - arch/arm64/kernel/insn.c | 2 +- arch/arm64/kernel/psci.c | 5 +--- arch/arm64/kernel/setup.c | 9 +++---- arch/arm64/kernel/smp_spin_table.c | 3 +-- arch/arm64/kernel/vdso.c | 7 ++---- arch/arm64/mm/init.c | 3 +-- arch/arm64/mm/mmu.c | 37 +++++++++------------------- 12 files changed, 25 insertions(+), 49 deletions(-) diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 77292dd6c73e..390bf1230c69 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -243,7 +243,7 @@ static inline void __kvm_flush_dcache_pud(pud_t pud) kvm_flush_dcache_to_poc(page_address(page), PUD_SIZE); } -#define kvm_virt_to_phys(x) __pa_symbol(x) +#define kvm_virt_to_phys(x) __virt_to_phys((unsigned long)(x)) void kvm_set_way_flush(struct kvm_vcpu *vcpu); void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled); diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index f520918473f5..1b8624e61c28 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -131,7 +131,6 @@ static inline void *phys_to_virt(phys_addr_t x) #define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #define virt_to_pfn(x) __phys_to_pfn(__virt_to_phys(x)) -#define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x)) /* * virt_to_page(k) convert a _valid_ virtual address to struct page * diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index be402897426a..b61e47aea3a1 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -49,7 +49,7 @@ static inline void contextidr_thread_switch(struct task_struct *next) */ static inline void cpu_set_reserved_ttbr0(void) { - unsigned long ttbr = __pa_symbol(empty_zero_page); + unsigned long ttbr = virt_to_phys(empty_zero_page); asm( " msr ttbr0_el1, %0 // set TTBR0\n" diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 4b10ae9548c8..7fbc40f7a4d0 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -109,7 +109,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); * for zero-mapped memory areas etc.. */ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; -#define ZERO_PAGE(vaddr) phys_to_page(__pa_symbol(empty_zero_page)) +#define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page) #define pte_ERROR(pte) __pte_error(__FILE__, __LINE__, pte_val(pte)) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index ed631a6778f3..58347534d765 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index ab4111eed363..dd9671cd0bb2 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c @@ -96,7 +96,7 @@ static void __kprobes *patch_map(void *addr, int fixmap) if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX)) page = vmalloc_to_page(addr); else if (!module && IS_ENABLED(CONFIG_DEBUG_RODATA)) - page = phys_to_page(__pa_symbol(addr)); + page = virt_to_page(addr); else return addr; diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 0ba727862af6..7a57cf5c3441 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -22,8 +22,6 @@ #include #include #include -#include - #include #include @@ -415,8 +413,7 @@ static int __init cpu_psci_cpu_prepare(unsigned int cpu) static int cpu_psci_cpu_boot(unsigned int cpu) { - int err = psci_ops.cpu_on(cpu_logical_map(cpu), - __pa_symbol(secondary_entry)); + int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry)); if (err) pr_err("failed to boot CPU%d (%d)\n", cpu, err); diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 39194cc7c665..8c82db7d5bbf 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include @@ -223,10 +222,10 @@ static void __init request_standard_resources(void) struct memblock_region *region; struct resource *res; - kernel_code.start = __pa_symbol(_text); - kernel_code.end = __pa_symbol(__init_begin - 1); - kernel_data.start = __pa_symbol(_sdata); - kernel_data.end = __pa_symbol(_end - 1); + kernel_code.start = virt_to_phys(_text); + kernel_code.end = virt_to_phys(__init_begin - 1); + kernel_data.start = virt_to_phys(_sdata); + kernel_data.end = virt_to_phys(_end - 1); for_each_memblock(memory, region) { res = alloc_bootmem_low(sizeof(*res)); diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c index 9026187a5b87..14944e5b28da 100644 --- a/arch/arm64/kernel/smp_spin_table.c +++ b/arch/arm64/kernel/smp_spin_table.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -91,7 +90,7 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu) * boot-loader's endianess before jumping. This is mandated by * the boot protocol. */ - writeq_relaxed(__pa_symbol(secondary_holding_pen), release_addr); + writeq_relaxed(__pa(secondary_holding_pen), release_addr); __flush_dcache_area((__force void *)release_addr, sizeof(*release_addr)); diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 85e4c04dfd17..71766af43b70 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -114,7 +114,6 @@ static struct vm_special_mapping vdso_spec[2]; static int __init vdso_init(void) { int i; - unsigned long pfn; if (memcmp(&vdso_start, "\177ELF", 4)) { pr_err("vDSO is not a valid ELF object!\n"); @@ -132,13 +131,11 @@ static int __init vdso_init(void) return -ENOMEM; /* Grab the vDSO data page. */ - vdso_pagelist[0] = phys_to_page(__pa_symbol(vdso_data)); + vdso_pagelist[0] = virt_to_page(vdso_data); /* Grab the vDSO code pages. */ - pfn = sym_to_pfn(&vdso_start); - for (i = 0; i < vdso_pages; i++) - vdso_pagelist[i + 1] = pfn_to_page(pfn + i); + vdso_pagelist[i + 1] = virt_to_page(&vdso_start + i * PAGE_SIZE); /* Populate the special mapping structures */ vdso_spec[0] = (struct vm_special_mapping) { diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 7badb61bcb2c..a5b0839d08ed 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include @@ -147,7 +146,7 @@ void __init arm64_memblock_init(void) * Register the kernel text, kernel data, initrd, and initial * pagetables with memblock. */ - memblock_reserve(__pa_symbol(_text), _end - _text); + memblock_reserve(__pa(_text), _end - _text); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start) memblock_reserve(__virt_to_phys(initrd_start), initrd_end - initrd_start); diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 5e320d4bd076..6147b780f805 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -306,10 +305,8 @@ static void __init __map_memblock(phys_addr_t start, phys_addr_t end) * for now. This will get more fine grained later once all memory * is mapped */ - unsigned long kernel_x_start = round_down(__pa_symbol(_stext), - SECTION_SIZE); - unsigned long kernel_x_end = round_up(__pa_symbol(__init_end), - SECTION_SIZE); + unsigned long kernel_x_start = round_down(__pa(_stext), SECTION_SIZE); + unsigned long kernel_x_end = round_up(__pa(__init_end), SECTION_SIZE); if (end < kernel_x_start) { create_mapping(start, __phys_to_virt(start), @@ -397,20 +394,19 @@ void __init fixup_executable(void) #ifdef CONFIG_DEBUG_RODATA /* now that we are actually fully mapped, make the start/end more fine grained */ if (!IS_ALIGNED((unsigned long)_stext, SECTION_SIZE)) { - unsigned long aligned_start = round_down(__pa_symbol(_stext), + unsigned long aligned_start = round_down(__pa(_stext), SECTION_SIZE); create_mapping(aligned_start, __phys_to_virt(aligned_start), - __pa_symbol(_stext) - aligned_start, + __pa(_stext) - aligned_start, PAGE_KERNEL); } if (!IS_ALIGNED((unsigned long)__init_end, SECTION_SIZE)) { - unsigned long aligned_end = round_up(__pa_symbol(__init_end), + unsigned long aligned_end = round_up(__pa(__init_end), SECTION_SIZE); - create_mapping(__pa_symbol(__init_end), - (unsigned long)__init_end, - aligned_end - __pa_symbol(__init_end), + create_mapping(__pa(__init_end), (unsigned long)__init_end, + aligned_end - __pa(__init_end), PAGE_KERNEL); } #endif @@ -419,7 +415,7 @@ void __init fixup_executable(void) #ifdef CONFIG_DEBUG_RODATA void mark_rodata_ro(void) { - create_mapping_late(__pa_symbol(_stext), (unsigned long)_stext, + create_mapping_late(__pa(_stext), (unsigned long)_stext, (unsigned long)__init_begin - (unsigned long)_stext, PAGE_KERNEL_EXEC | PTE_RDONLY); } @@ -427,8 +423,7 @@ void mark_rodata_ro(void) void fixup_init(void) { - create_mapping_late(__pa_symbol(__init_begin), - (unsigned long)__init_begin, + create_mapping_late(__pa(__init_begin), (unsigned long)__init_begin, (unsigned long)__init_end - (unsigned long)__init_begin, PAGE_KERNEL); } @@ -572,12 +567,6 @@ static inline pte_t * fixmap_pte(unsigned long addr) return pte_offset_kernel(pmd, addr); } -/* - * The p*d_populate functions call virt_to_phys implicitly so they can't be used - * directly on kernel symbols (bm_p*d). This function is called too early to use - * lm_alias so __p*d_populate functions must be used to populate with the - * physical address from __pa_symbol. - */ void __init early_fixmap_init(void) { pgd_t *pgd; @@ -586,13 +575,11 @@ void __init early_fixmap_init(void) unsigned long addr = FIXADDR_START; pgd = pgd_offset_k(addr); - if (pgd_none(*pgd)) - __pgd_populate(pgd, __pa_symbol(bm_pud), PUD_TABLE_TYPE); + pgd_populate(&init_mm, pgd, bm_pud); pud = pud_offset(pgd, addr); - if (pud_none(*pud)) - __pud_populate(pud, __pa_symbol(bm_pmd), PMD_TYPE_TABLE); + pud_populate(&init_mm, pud, bm_pmd); pmd = pmd_offset(pud, addr); - __pmd_populate(pmg, __pa_symbol(bm_pte), PMD_TYPE_TABLE); + pmd_populate_kernel(&init_mm, pmd, bm_pte); /* * The boot-ioremap range spans multiple pmds, for which -- GitLab From f94f68d8d7cea82529186ce27e67b887298df403 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 21 Nov 2017 16:36:12 +0530 Subject: [PATCH 1682/5498] ASoC: bg: pass source info to BG BG expects source to be passed as a part of start and stop command, this is to infom BG if the captured data is to be consumed by BG or passed to MSM. Change-Id: Iea3166b714e5eb660d1c56905fb59aaff5a3cff8 Signed-off-by: Ashish Jain --- sound/soc/codecs/bg_codec.c | 127 +++++++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index 5d2a4278820e..34f36570ab2e 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -34,8 +34,6 @@ #include "pktzr.h" -#define TASHA_RX_PORT_START_NUMBER 16 - #define BG_RATES_MAX (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) @@ -81,6 +79,14 @@ struct bg_cdc_priv { struct delayed_work bg_cdc_pktzr_init_work; unsigned long status_mask; struct bg_hw_params hw_params; + int src[NUM_CODEC_DAIS]; +}; + +struct codec_ssn_rt_setup_t { + /* active session_id */ + uint32_t active_session; + /* To indicate if playback/record happens from/to BG or MSM */ + uint32_t route_to_bg; }; struct graphite_basic_rsp_result { @@ -88,6 +94,69 @@ struct graphite_basic_rsp_result { uint32_t status; }; +static int bg_get_src(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(codec); + int dai_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + + if ((bg_cdc == NULL) || (dai_id >= NUM_CODEC_DAIS) || + (dai_id < 0)) { + pr_err("%s invalid input\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = bg_cdc->src[dai_id]; + dev_dbg(codec->dev, "%s: dai_id: %d src: %d\n", __func__, + dai_id, bg_cdc->src[dai_id]); + + return 0; +} + +static int bg_put_src(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(codec); + int dai_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + + if ((bg_cdc == NULL) || (dai_id >= NUM_CODEC_DAIS) || + (dai_id < 0)) { + pr_err("%s invalid input\n", __func__); + return -EINVAL; + } + + bg_cdc->src[dai_id] = ucontrol->value.integer.value[0]; + dev_dbg(codec->dev, "%s: dai_id: %d src: %d\n", __func__, + dai_id, bg_cdc->src[dai_id]); + + return 0; +} + + +static const struct snd_kcontrol_new bg_snd_controls[] = { + SOC_SINGLE_EXT("RX_0 SRC", BG_AIF1_PB, 0, 1, 0, + bg_get_src, bg_put_src), + SOC_SINGLE_EXT("RX_1 SRC", BG_AIF2_PB, 0, 1, 0, + bg_get_src, bg_put_src), + SOC_SINGLE_EXT("RX_2 SRC", BG_AIF3_PB, 0, 1, 0, + bg_get_src, bg_put_src), + SOC_SINGLE_EXT("RX_3 SRC", BG_AIF4_PB, 0, 1, 0, + bg_get_src, bg_put_src), + SOC_SINGLE_EXT("TX_0 DST", BG_AIF1_CAP, 0, 1, 0, + bg_get_src, bg_put_src), + SOC_SINGLE_EXT("TX_1 DST", BG_AIF2_CAP, 0, 1, 0, + bg_get_src, bg_put_src), + SOC_SINGLE_EXT("TX_2 DST", BG_AIF3_CAP, 0, 1, 0, + bg_get_src, bg_put_src), + SOC_SINGLE_EXT("TX_3 DST", BG_AIF4_CAP, 0, 1, 0, + bg_get_src, bg_put_src), +}; + + static int bg_cdc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -147,11 +216,13 @@ static int bg_cdc_hw_params(struct snd_pcm_substream *substream, bg_cdc->hw_params.active_session = 0x00020000; break; case BG_AIF3_PB: + bg_cdc->hw_params.active_session = 0x0002; break; case BG_AIF3_CAP: bg_cdc->hw_params.active_session = 0x00010000; break; case BG_AIF4_PB: + bg_cdc->hw_params.active_session = 0x0004; break; case BG_AIF4_CAP: bg_cdc->hw_params.active_session = 0x00020000; @@ -184,7 +255,6 @@ static int bg_cdc_hw_params(struct snd_pcm_substream *substream, ret = pktzr_cmd_set_params(&hw_params, sizeof(hw_params), &rsp); if (ret < 0) { pr_err("pktzr cmd set params failed\n"); - ret = 0; goto exit; } exit: @@ -198,7 +268,8 @@ static int bg_cdc_hw_free(struct snd_pcm_substream *substream, { struct pktzr_cmd_rsp rsp; int ret = 0; - uint32_t active_session = 0; + struct codec_ssn_rt_setup_t codec_start; + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); pr_debug("%s: dai_name = %s DAI-ID %x\n", __func__, dai->name, dai->id); @@ -206,26 +277,28 @@ static int bg_cdc_hw_free(struct snd_pcm_substream *substream, switch (dai->id) { case BG_AIF1_PB: - active_session = 0x0001; + codec_start.active_session = 0x0001; break; case BG_AIF1_CAP: - active_session = 0x00010000; + codec_start.active_session = 0x00010000; break; case BG_AIF2_PB: - active_session = 0x0001; + codec_start.active_session = 0x0001; break; case BG_AIF2_CAP: - active_session = 0x00020000; + codec_start.active_session = 0x00020000; break; case BG_AIF3_PB: + codec_start.active_session = 0x0002; break; case BG_AIF3_CAP: - active_session = 0x00010000; + codec_start.active_session = 0x00010000; break; case BG_AIF4_PB: + codec_start.active_session = 0x0004; break; case BG_AIF4_CAP: - active_session = 0x00020000; + codec_start.active_session = 0x00020000; break; default: pr_err("%s:Invalid dai id %d", __func__, dai->id); @@ -233,16 +306,18 @@ static int bg_cdc_hw_free(struct snd_pcm_substream *substream, goto exit; } + codec_start.route_to_bg = bg_cdc->src[dai->id]; + pr_debug("%s active_session %x route_to_bg %d\n", + __func__, codec_start.active_session, codec_start.route_to_bg); rsp.buf_size = sizeof(struct graphite_basic_rsp_result); rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); if (!rsp.buf) return -ENOMEM; - /* Send command to BG to start session */ - ret = pktzr_cmd_stop(&active_session, - sizeof(active_session), &rsp); + /* Send command to BG to stop session */ + ret = pktzr_cmd_stop(&codec_start, + sizeof(codec_start), &rsp); if (ret < 0) { pr_err("pktzr cmd close failed\n"); - ret = 0; goto exit; } exit: @@ -256,7 +331,7 @@ static int bg_cdc_prepare(struct snd_pcm_substream *substream, { struct pktzr_cmd_rsp rsp; int ret = 0; - uint32_t active_session = 0; + struct codec_ssn_rt_setup_t codec_start; struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); rsp.buf = NULL; @@ -279,26 +354,28 @@ static int bg_cdc_prepare(struct snd_pcm_substream *substream, switch (dai->id) { case BG_AIF1_PB: - active_session = 0x0001; + codec_start.active_session = 0x0001; break; case BG_AIF1_CAP: - active_session = 0x00010000; + codec_start.active_session = 0x00010000; break; case BG_AIF2_PB: - active_session = 0x0001; + codec_start.active_session = 0x0001; break; case BG_AIF2_CAP: - active_session = 0x00020000; + codec_start.active_session = 0x00020000; break; case BG_AIF3_PB: + codec_start.active_session = 0x0002; break; case BG_AIF3_CAP: - active_session = 0x00010000; + codec_start.active_session = 0x00010000; break; case BG_AIF4_PB: + codec_start.active_session = 0x0004; break; case BG_AIF4_CAP: - active_session = 0x00020000; + codec_start.active_session = 0x00020000; break; default: pr_err("%s:Invalid dai id %d", __func__, dai->id); @@ -306,15 +383,17 @@ static int bg_cdc_prepare(struct snd_pcm_substream *substream, goto exit; } + codec_start.route_to_bg = bg_cdc->src[dai->id]; + pr_debug("%s active_session %x route_to_bg %d\n", + __func__, codec_start.active_session, codec_start.route_to_bg); rsp.buf_size = sizeof(struct graphite_basic_rsp_result); rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); if (!rsp.buf) return -ENOMEM; /* Send command to BG to start session */ - ret = pktzr_cmd_start(&active_session, sizeof(active_session), &rsp); + ret = pktzr_cmd_start(&codec_start, sizeof(codec_start), &rsp); if (ret < 0) { pr_err("pktzr cmd start failed\n"); - ret = 0; goto exit; } exit: @@ -538,6 +617,8 @@ static int bg_cdc_codec_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_bg_cdc = { .probe = bg_cdc_codec_probe, .remove = bg_cdc_codec_remove, + .controls = bg_snd_controls, + .num_controls = ARRAY_SIZE(bg_snd_controls), }; static void bg_cdc_add_child_devices(struct work_struct *work) -- GitLab From 0fae5372132fcf4d8bc8d7a4e5cad1b5279b5223 Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Fri, 10 Nov 2017 10:18:59 -0800 Subject: [PATCH 1683/5498] mhi_dev: mhi: Process PCIe events from a workqueue Currently MHI initializes MMIO after receiving a callback from PCIe. Queue the events instead of running from the callback context as PCIe callbacks maybe called in atomic context. Change-Id: I441494d9804ba43ab8982cb40884e494c65cbf89 Signed-off-by: Siddartha Mohanadoss --- drivers/platform/msm/mhi_dev/mhi.c | 32 +++++++++++++++++++++--------- drivers/platform/msm/mhi_dev/mhi.h | 2 ++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index 075f9331a73a..6d7ea7d9d8b0 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -72,6 +72,7 @@ static void mhi_ring_init_cb(void *user_data); static void mhi_update_state_info(uint32_t info); static int mhi_deinit(struct mhi_dev *mhi); static void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify); +static int mhi_dev_pcie_notify_event; void mhi_dev_read_from_host(struct mhi_dev *mhi, struct mhi_addr *transfer) { @@ -2492,26 +2493,31 @@ static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx) void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify) { - int rc = 0; - - if (!notify) { + if (!notify || !notify->user) { pr_err("Null argument for notify\n"); return; } mhi_ctx = notify->user; - if (!mhi_ctx) { - pr_err("Invalid mhi_ctx\n"); - return; - } + mhi_dev_pcie_notify_event = notify->options; + mhi_log(MHI_MSG_INFO, + "PCIe event=0x%x\n", notify->options); + queue_work(mhi_ctx->pcie_event_wq, &mhi_ctx->pcie_event); +} + +static void mhi_dev_pcie_handle_event(struct work_struct *work) +{ + struct mhi_dev *mhi_ctx = container_of(work, struct mhi_dev, + pcie_event); + int rc = 0; - if (notify->options == MHI_INIT) { + if (mhi_dev_pcie_notify_event == MHI_INIT) { rc = mhi_dev_resume_mmio_mhi_init(mhi_ctx); if (rc) { pr_err("Error during MHI device initialization\n"); return; } - } else if (notify->options == MHI_REINIT) { + } else if (mhi_dev_pcie_notify_event == MHI_REINIT) { rc = mhi_dev_resume_mmio_mhi_reinit(mhi_ctx); if (rc) { pr_err("Error during MHI device re-initialization\n"); @@ -2540,6 +2546,14 @@ static int mhi_dev_probe(struct platform_device *pdev) mhi_update_state_info(MHI_STATE_CONFIGURED); } + INIT_WORK(&mhi_ctx->pcie_event, mhi_dev_pcie_handle_event); + mhi_ctx->pcie_event_wq = alloc_workqueue("mhi_dev_pcie_event_wq", + WQ_HIGHPRI, 0); + if (!mhi_ctx->pcie_event_wq) { + rc = -ENOMEM; + return rc; + } + mhi_ctx->phandle = ep_pcie_get_phandle(mhi_ctx->ifc_id); if (mhi_ctx->phandle) { /* PCIe link is already up */ diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index 39b7a37bd233..577ecb36a2c2 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -523,9 +523,11 @@ struct mhi_dev { struct work_struct re_init; /* EP PCIe registration */ + struct workqueue_struct *pcie_event_wq; struct ep_pcie_register_event event_reg; u32 ifc_id; struct ep_pcie_hw *phandle; + struct work_struct pcie_event; atomic_t write_active; atomic_t is_suspended; -- GitLab From e480f2d1c0c0235c449f2cd209cb742b491eda1a Mon Sep 17 00:00:00 2001 From: Alan Kwong Date: Thu, 12 Oct 2017 11:49:55 -0400 Subject: [PATCH 1684/5498] msm: sde: disable support for v4l2 event subscription V4l2 event subscription is not used by rotator, and default implementation on 4.9 is causing failure during file release. Disable v4l2 event subscription for rotator to avoid invalid file release. Change-Id: I486f2eba7112852a1c818efeeb86033f17139b33 Signed-off-by: Alan Kwong --- .../platform/msm/sde/rotator/sde_rotator_dev.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c index f3a5d010c0fb..be8655fb3c9b 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -2018,6 +2018,18 @@ static long sde_rotator_compat_ioctl32(struct file *file, } #endif +static int sde_rotator_ctrl_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return -EINVAL; +} + +static int sde_rotator_event_unsubscribe(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return -EINVAL; +} + /* V4l2 ioctl handlers */ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = { .vidioc_querycap = sde_rotator_querycap, @@ -2044,8 +2056,8 @@ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = { .vidioc_s_priority = sde_rotator_s_priority, .vidioc_default = sde_rotator_private_ioctl, .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_subscribe_event = sde_rotator_ctrl_subscribe_event, + .vidioc_unsubscribe_event = sde_rotator_event_unsubscribe, }; /* -- GitLab From e56d3e7a410378f001a4226a56965a90940c6d27 Mon Sep 17 00:00:00 2001 From: Yan He Date: Fri, 27 Oct 2017 17:40:24 -0700 Subject: [PATCH 1685/5498] msm: ep_pcie: add the support of D3 cold before BME is set Host side could turn off PCIe link before BME is set or before PCIe client is notified that link is enabled. Add the support here to handle the D3 cold in those situations. Change-Id: I2cbc30fca6a40375094c116662d421505cc8a140 Signed-off-by: Yan He --- drivers/platform/msm/ep_pcie/ep_pcie_com.h | 3 + drivers/platform/msm/ep_pcie/ep_pcie_core.c | 69 ++++++++++++++++++--- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index 185c9cd8ef83..dd35d4d6cf5b 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -331,10 +331,13 @@ struct ep_pcie_dev_t { bool l23_ready; bool l1ss_enabled; struct ep_pcie_msi_config msi_cfg; + bool no_notify; + bool client_ready; struct ep_pcie_register_event *event_reg; struct work_struct handle_perst_work; struct work_struct handle_bme_work; + struct work_struct handle_d3cold_work; }; extern struct ep_pcie_dev_t ep_pcie_dev; diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index d820f0f88ef6..905f2b7714f1 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -994,6 +994,10 @@ static void ep_pcie_release_resources(struct ep_pcie_dev_t *dev) static void ep_pcie_enumeration_complete(struct ep_pcie_dev_t *dev) { + unsigned long irqsave_flags; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + dev->enumerated = true; dev->link_status = EP_PCIE_LINK_ENABLED; @@ -1020,7 +1024,14 @@ static void ep_pcie_enumeration_complete(struct ep_pcie_dev_t *dev) "PCIe V%d: register driver for device 0x%x.\n", ep_pcie_dev.rev, hw_drv.device_id); ep_pcie_register_drv(&hw_drv); - ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP); + if (!dev->no_notify) + ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP); + else + EP_PCIE_DBG(dev, + "PCIe V%d: do not notify client about linkup.\n", + dev->rev); + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); return; } @@ -1520,7 +1531,13 @@ static irqreturn_t ep_pcie_handle_dstate_change_irq(int irq, void *data) "PCIe V%d: No. %ld change to D3 state.\n", dev->rev, dev->d3_counter); ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(1)); - ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_HOT); + + if (dev->enumerated) + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_HOT); + else + EP_PCIE_DBG(dev, + "PCIe V%d: do not notify client about this D3 hot event since enumeration by HLOS is not done yet.\n", + dev->rev); } else if (dstate == 0) { dev->l23_ready = false; dev->d0_counter++; @@ -1584,9 +1601,25 @@ static void handle_perst_func(struct work_struct *work) struct ep_pcie_dev_t *dev = container_of(work, struct ep_pcie_dev_t, handle_perst_work); + EP_PCIE_DBG(dev, + "PCIe V%d: Start enumeration due to PERST deassertion.\n", + dev->rev); + ep_pcie_enumeration(dev); } +static void handle_d3cold_func(struct work_struct *work) +{ + struct ep_pcie_dev_t *dev = container_of(work, struct ep_pcie_dev_t, + handle_d3cold_work); + + EP_PCIE_DBG(dev, + "PCIe V%d: shutdown PCIe link due to PERST assertion before BME is set.\n", + dev->rev); + ep_pcie_core_disable_endpoint(); + dev->no_notify = false; +} + static void handle_bme_func(struct work_struct *work) { struct ep_pcie_dev_t *dev = container_of(work, @@ -1609,10 +1642,14 @@ static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data) EP_PCIE_DBG(dev, "PCIe V%d: PCIe is not enumerated yet; PERST is %sasserted.\n", dev->rev, perst ? "de" : ""); - if ((!dev->perst_enum) || !perst) - goto out; - /* start work for link enumeration with the host side */ - schedule_work(&dev->handle_perst_work); + if (perst) { + /* start work for link enumeration with the host side */ + schedule_work(&dev->handle_perst_work); + } else { + dev->no_notify = true; + /* shutdown the link if the link is already on */ + schedule_work(&dev->handle_d3cold_work); + } goto out; } @@ -1630,7 +1667,16 @@ static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data) EP_PCIE_DBG(dev, "PCIe V%d: No. %ld PERST assertion.\n", dev->rev, dev->perst_ast_counter); - ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_COLD); + + if (dev->client_ready) { + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_COLD); + } else { + dev->no_notify = true; + EP_PCIE_DBG(dev, + "PCIe V%d: Client driver is not ready when this PERST assertion happens; shutdown link now.\n", + dev->rev); + schedule_work(&dev->handle_d3cold_work); + } } out: @@ -1715,6 +1761,7 @@ int32_t ep_pcie_irq_init(struct ep_pcie_dev_t *dev) /* Initialize all works to be performed before registering for IRQs*/ INIT_WORK(&dev->handle_perst_work, handle_perst_func); INIT_WORK(&dev->handle_bme_work, handle_bme_func); + INIT_WORK(&dev->handle_d3cold_work, handle_d3cold_func); if (dev->aggregated_irq) { ret = devm_request_irq(pdev, @@ -1868,6 +1915,8 @@ int ep_pcie_core_register_event(struct ep_pcie_register_event *reg) "PCIe V%d: Event 0x%x is registered\n", ep_pcie_dev.rev, reg->events); + ep_pcie_dev.client_ready = true; + return 0; } @@ -1904,6 +1953,12 @@ enum ep_pcie_link_status ep_pcie_core_get_linkstatus(void) "PCIe V%d: PCIe link is up and BME is enabled; current SW link status:%d.\n", dev->rev, dev->link_status); dev->link_status = EP_PCIE_LINK_ENABLED; + if (dev->no_notify) { + EP_PCIE_DBG(dev, + "PCIe V%d: BME is set now, but do not tell client about BME enable.\n", + dev->rev); + return EP_PCIE_LINK_UP; + } } else { EP_PCIE_DBG(dev, "PCIe V%d: PCIe link is up but BME is disabled; current SW link status:%d.\n", -- GitLab From 3b4bedd543e9c5195599987e1278649adba1e2c3 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Mon, 6 Nov 2017 17:49:56 +0530 Subject: [PATCH 1686/5498] msm: ipa3: Fix IPA aggregation force close call In IPA driver uninitialized structure causing some garbage values result into IPA data stall. Added code changes to reset zero uninitialized structure. Change-Id: I3b98041f8a96ea1d571eeaa60fc84457f6309d45 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c index ca1c128c3bb0..7ee4adf4c70a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c @@ -1559,6 +1559,8 @@ void ipahal_get_aggr_force_close_valmask(int ep_idx, return; } + memset(valmask, 0, sizeof(struct ipahal_reg_valmask)); + if (ipahal_ctx->hw_type <= IPA_HW_v3_1) { shft = IPA_AGGR_FORCE_CLOSE_AGGR_FORCE_CLOSE_PIPE_BITMAP_SHFT; bmsk = IPA_AGGR_FORCE_CLOSE_AGGR_FORCE_CLOSE_PIPE_BITMAP_BMSK; -- GitLab From 8cb107b638ce0b3280320a8a60cbfd78eb59ec72 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Mon, 7 Aug 2017 23:15:48 +0530 Subject: [PATCH 1687/5498] msm: ipa: UAPI security code changes Added code changes for UAPI security code userspace inputs validation Change-Id: I5f8b7c9cd27a9d8ca721a98f1cbf05c14588dd85 Acked-by: Ashok Vuyyuru Acked-by: Abhishek Choubey Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa_flt.c | 8 +- .../platform/msm/ipa/ipa_v2/ipa_qmi_service.c | 55 ++++++++++- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 7 +- drivers/platform/msm/ipa/ipa_v3/ipa.c | 3 + .../platform/msm/ipa/ipa_v3/ipa_qmi_service.c | 93 ++++++++++++++++++- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 7 +- .../platform/msm/ipa/test/ipa_ut_framework.c | 10 +- 7 files changed, 171 insertions(+), 12 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c index 72542bf6dd5d..c0af295c7362 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c @@ -23,10 +23,10 @@ static int ipa_generate_hw_rule_from_eq( const struct ipa_ipfltri_rule_eq *attrib, u8 **buf) { - int num_offset_meq_32 = attrib->num_offset_meq_32; - int num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16; - int num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32; - int num_offset_meq_128 = attrib->num_offset_meq_128; + uint8_t num_offset_meq_32 = attrib->num_offset_meq_32; + uint8_t num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16; + uint8_t num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32; + uint8_t num_offset_meq_128 = attrib->num_offset_meq_128; int i; if (attrib->tos_eq_present) { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c index 027191bc73bb..fe206a8bd6e8 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c @@ -508,6 +508,7 @@ int qmi_filter_request_send(struct ipa_install_fltr_rule_req_msg_v01 *req) struct ipa_install_fltr_rule_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; int rc; + int i; /* check if the filter rules from IPACM is valid */ if (req->filter_spec_list_len == 0) { @@ -517,6 +518,38 @@ int qmi_filter_request_send(struct ipa_install_fltr_rule_req_msg_v01 *req) req->filter_spec_list_len); } + if (req->filter_spec_list_len >= QMI_IPA_MAX_FILTERS_V01) { + IPAWANDBG( + "IPACM passes the number of filtering rules exceed limit\n"); + return -EINVAL; + } else if (req->source_pipe_index_valid != 0) { + IPAWANDBG( + "IPACM passes source_pipe_index_valid not zero 0 != %d\n", + req->source_pipe_index_valid); + return -EINVAL; + } else if (req->source_pipe_index >= ipa_ctx->ipa_num_pipes) { + IPAWANDBG( + "IPACM passes source pipe index not valid ID = %d\n", + req->source_pipe_index); + return -EINVAL; + } + for (i = 0; i < req->filter_spec_list_len; i++) { + if ((req->filter_spec_list[i].ip_type != + QMI_IPA_IP_TYPE_V4_V01) && + (req->filter_spec_list[i].ip_type != + QMI_IPA_IP_TYPE_V6_V01)) + return -EINVAL; + if (req->filter_spec_list[i].is_mux_id_valid == false) + return -EINVAL; + if (req->filter_spec_list[i].is_routing_table_index_valid + == false) + return -EINVAL; + if ((req->filter_spec_list[i].filter_action <= + QMI_IPA_FILTER_ACTION_INVALID_V01) && + (req->filter_spec_list[i].filter_action > + QMI_IPA_FILTER_ACTION_EXCEPTION_V01)) + return -EINVAL; + } mutex_lock(&ipa_qmi_lock); if (ipa_qmi_ctx != NULL) { /* cache the qmi_filter_request */ @@ -648,7 +681,6 @@ int qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01 *req) if (req->filter_index_list_len == 0) { IPAWANERR(" delete UL filter rule for pipe %d\n", req->source_pipe_index); - return -EINVAL; } else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) { IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n", req->source_pipe_index, @@ -667,6 +699,27 @@ int qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01 *req) return -EINVAL; } + if (req->install_status != IPA_QMI_RESULT_SUCCESS_V01) { + IPAWANERR(" UL filter rule for pipe %d install_status = %d\n", + req->source_pipe_index, req->install_status); + return -EINVAL; + } else if (req->source_pipe_index >= ipa_ctx->ipa_num_pipes) { + IPAWANERR("IPACM passes source pipe index not valid ID = %d\n", + req->source_pipe_index); + return -EINVAL; + } else if (((req->embedded_pipe_index_valid != true) || + (req->embedded_call_mux_id_valid != true)) && + ((req->embedded_pipe_index_valid != false) || + (req->embedded_call_mux_id_valid != false))) { + IPAWANERR( + "IPACM passes embedded pipe and mux valid not valid\n"); + return -EINVAL; + } else if (req->embedded_pipe_index >= ipa_ctx->ipa_num_pipes) { + IPAWANERR("IPACM passes source pipe index not valid ID = %d\n", + req->source_pipe_index); + return -EINVAL; + } + mutex_lock(&ipa_qmi_lock); if (ipa_qmi_ctx != NULL) { /* cache the qmi_filter_request */ diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index e47f32ca8acd..a9f0bd5f4267 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -636,6 +636,8 @@ static int wwan_add_ul_flt_rule_to_ipa(void) return -ENOMEM; } + memset(req, 0, sizeof(struct ipa_fltr_installed_notif_req_msg_v01)); + param->commit = 1; param->ep = IPA_CLIENT_APPS_LAN_WAN_PROD; param->global = false; @@ -1402,8 +1404,8 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Get driver name */ case RMNET_IOCTL_GET_DRIVER_NAME: memcpy(&extend_ioctl_data.u.if_name, - ipa_netdevs[0]->name, - sizeof(IFNAMSIZ)); + ipa_netdevs[0]->name, IFNAMSIZ); + extend_ioctl_data.u.if_name[IFNAMSIZ - 1] = '\0'; if (copy_to_user((u8 *)ifr->ifr_ifru.ifru_data, &extend_ioctl_data, sizeof(struct rmnet_ioctl_extended_s))) @@ -1617,6 +1619,7 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) sizeof(wan_msg->upstream_ifname); strlcpy(wan_msg->upstream_ifname, extend_ioctl_data.u.if_name, len); + wan_msg->upstream_ifname[len - 1] = '\0'; memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); msg_meta.msg_type = WAN_XLAT_CONNECT; msg_meta.msg_len = sizeof(struct ipa_wan_msg); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 43b32fef0e56..87a24c75f7a0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4583,6 +4583,9 @@ static ssize_t ipa3_write(struct file *file, const char __user *buf, return -EFAULT; } + if (count > 0) + dbg_buff[count - 1] = '\0'; + /* Prevent consequent calls from trying to load the FW again. */ if (ipa3_is_ready()) return count; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index d9c690ecb2fd..5d04990076a2 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -600,13 +600,47 @@ int ipa3_qmi_filter_request_send(struct ipa_install_fltr_rule_req_msg_v01 *req) struct ipa_install_fltr_rule_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; int rc; + int i; /* check if the filter rules from IPACM is valid */ - if (req->filter_spec_ex_list_len == 0) { + if (req->filter_spec_list_len == 0) { IPAWANDBG("IPACM pass zero rules to Q6\n"); } else { IPAWANDBG("IPACM pass %u rules to Q6\n", - req->filter_spec_ex_list_len); + req->filter_spec_list_len); + } + + if (req->filter_spec_list_len >= QMI_IPA_MAX_FILTERS_V01) { + IPAWANDBG( + "IPACM passes the number of filtering rules exceed limit\n"); + return -EINVAL; + } else if (req->source_pipe_index_valid != 0) { + IPAWANDBG( + "IPACM passes source_pipe_index_valid not zero 0 != %d\n", + req->source_pipe_index_valid); + return -EINVAL; + } else if (req->source_pipe_index >= ipa3_ctx->ipa_num_pipes) { + IPAWANDBG( + "IPACM passes source pipe index not valid ID = %d\n", + req->source_pipe_index); + return -EINVAL; + } + for (i = 0; i < req->filter_spec_list_len; i++) { + if ((req->filter_spec_list[i].ip_type != + QMI_IPA_IP_TYPE_V4_V01) && + (req->filter_spec_list[i].ip_type != + QMI_IPA_IP_TYPE_V6_V01)) + return -EINVAL; + if (req->filter_spec_list[i].is_mux_id_valid == false) + return -EINVAL; + if (req->filter_spec_list[i].is_routing_table_index_valid + == false) + return -EINVAL; + if ((req->filter_spec_list[i].filter_action <= + QMI_IPA_FILTER_ACTION_INVALID_V01) && + (req->filter_spec_list[i].filter_action > + QMI_IPA_FILTER_ACTION_EXCEPTION_V01)) + return -EINVAL; } mutex_lock(&ipa3_qmi_lock); @@ -648,6 +682,7 @@ int ipa3_qmi_filter_request_ex_send( struct ipa_install_fltr_rule_resp_ex_msg_v01 resp; struct msg_desc req_desc, resp_desc; int rc; + int i; /* check if the filter rules from IPACM is valid */ if (req->filter_spec_ex_list_len == 0) { @@ -657,6 +692,34 @@ int ipa3_qmi_filter_request_ex_send( req->filter_spec_ex_list_len); } + if (req->filter_spec_ex_list_len >= QMI_IPA_MAX_FILTERS_EX_V01) { + IPAWANDBG( + "IPACM pass the number of filtering rules exceed limit\n"); + return -EINVAL; + } else if (req->source_pipe_index_valid != 0) { + IPAWANDBG( + "IPACM passes source_pipe_index_valid not zero 0 != %d\n", + req->source_pipe_index_valid); + return -EINVAL; + } + + for (i = 0; i < req->filter_spec_ex_list_len-1; i++) { + if ((req->filter_spec_ex_list[i].ip_type != + QMI_IPA_IP_TYPE_V4_V01) && + (req->filter_spec_ex_list[i].ip_type != + QMI_IPA_IP_TYPE_V6_V01)) + return -EINVAL; + if (req->filter_spec_ex_list[i].is_mux_id_valid == false) + return -EINVAL; + if (req->filter_spec_ex_list[i].is_routing_table_index_valid + == false) + return -EINVAL; + if ((req->filter_spec_ex_list[i].filter_action <= + QMI_IPA_FILTER_ACTION_INVALID_V01) && + (req->filter_spec_ex_list[i].filter_action > + QMI_IPA_FILTER_ACTION_EXCEPTION_V01)) + return -EINVAL; + } mutex_lock(&ipa3_qmi_lock); if (ipa3_qmi_ctx != NULL) { /* cache the qmi_filter_request */ @@ -855,6 +918,32 @@ int ipa3_qmi_filter_notify_send( return -EINVAL; } + if (req->install_status != IPA_QMI_RESULT_SUCCESS_V01) { + IPAWANERR(" UL filter rule for pipe %d install_status = %d\n", + req->source_pipe_index, req->install_status); + return -EINVAL; + } else if (req->rule_id_valid != 1) { + IPAWANERR(" UL filter rule for pipe %d rule_id_valid = %d\n", + req->source_pipe_index, req->rule_id_valid); + return -EINVAL; + } else if (req->source_pipe_index >= ipa3_ctx->ipa_num_pipes) { + IPAWANDBG( + "IPACM passes source pipe index not valid ID = %d\n", + req->source_pipe_index); + return -EINVAL; + } else if (((req->embedded_pipe_index_valid != true) || + (req->embedded_call_mux_id_valid != true)) && + ((req->embedded_pipe_index_valid != false) || + (req->embedded_call_mux_id_valid != false))) { + IPAWANERR( + "IPACM passes embedded pipe and mux valid not valid\n"); + return -EINVAL; + } else if (req->embedded_pipe_index >= ipa3_ctx->ipa_num_pipes) { + IPAWANERR("IPACM passes source pipe index not valid ID = %d\n", + req->source_pipe_index); + return -EINVAL; + } + if (req->source_pipe_index == -1) { IPAWANERR("Source pipe index invalid\n"); return -EINVAL; diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 09bb7c404516..3d149deaa47e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -667,6 +667,8 @@ static int ipa3_wwan_add_ul_flt_rule_to_ipa(void) param->global = false; param->num_rules = (uint8_t)1; + memset(req, 0, sizeof(struct ipa_fltr_installed_notif_req_msg_v01)); + for (i = 0; i < rmnet_ipa3_ctx->num_q6_rules; i++) { param->ip = ipa3_qmi_ctx->q6_ul_filter_rule[i].ip; memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add)); @@ -1536,8 +1538,8 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Get driver name */ case RMNET_IOCTL_GET_DRIVER_NAME: memcpy(&extend_ioctl_data.u.if_name, - IPA_NETDEV()->name, - sizeof(IFNAMSIZ)); + IPA_NETDEV()->name, IFNAMSIZ); + extend_ioctl_data.u.if_name[IFNAMSIZ - 1] = '\0'; if (copy_to_user((u8 *)ifr->ifr_ifru.ifru_data, &extend_ioctl_data, sizeof(struct rmnet_ioctl_extended_s))) @@ -1705,6 +1707,7 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) sizeof(wan_msg->upstream_ifname); strlcpy(wan_msg->upstream_ifname, extend_ioctl_data.u.if_name, len); + wan_msg->upstream_ifname[len-1] = '\0'; memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); msg_meta.msg_type = WAN_XLAT_CONNECT; msg_meta.msg_len = sizeof(struct ipa_wan_msg); diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c index 3bf9ac11f2d1..dfc8442e0862 100644 --- a/drivers/platform/msm/ipa/test/ipa_ut_framework.c +++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -215,6 +215,10 @@ static ssize_t ipa_ut_dbgfs_meta_test_write(struct file *file, IPA_UT_DBG("Entry\n"); mutex_lock(&ipa_ut_ctx->lock); + if (file == NULL) { + rc = -EFAULT; + goto unlock_mutex; + } suite = file->f_inode->i_private; ipa_assert_on(!suite); meta_type = (long)(file->private_data); @@ -470,6 +474,10 @@ static ssize_t ipa_ut_dbgfs_test_write(struct file *file, IPA_UT_DBG("Entry\n"); mutex_lock(&ipa_ut_ctx->lock); + if (file == NULL) { + rc = -EFAULT; + goto unlock_mutex; + } test = file->f_inode->i_private; ipa_assert_on(!test); -- GitLab From 962ffbd935b6b486bb6f9f1ed6ba7b1584590e37 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Thu, 23 Nov 2017 08:32:12 +0530 Subject: [PATCH 1688/5498] power: qpnp-fg-gen3: Handle status change only after SOC is ready Currently, SW can start handling status_change from other power supplies viz. battery/parallel/usb as soon as the driver is probed. But in some cases like device is booting up with the charger connected, this can cause some of the SW algorithms not to start correctly as the charging status got updated before they are ready. Later when the algorithms are supposed to run, they cannot start as the signal based on charging status is missed. Fix this by skipping doing anything in status_change_work until the profile_load_work is complete and schedule status_change_work after profile_load_work is complete. While at it, start showing 50% if SOC is not ready. CRs-Fixed: 2147343 Change-Id: Ib0a9f95d5a9a71f03d7a6c5f7ab36a8118a904ca Signed-off-by: Subbaraman Narayanamurthy Signed-off-by: Anirudh Ghayal --- drivers/power/qpnp-fg-gen3.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/power/qpnp-fg-gen3.c b/drivers/power/qpnp-fg-gen3.c index 5c00796b1759..5afd1e799289 100644 --- a/drivers/power/qpnp-fg-gen3.c +++ b/drivers/power/qpnp-fg-gen3.c @@ -816,7 +816,7 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) return 0; } - if (chip->battery_missing) { + if (chip->battery_missing || !chip->soc_reporting_ready) { *val = BATT_MISS_SOC; return 0; } @@ -2161,6 +2161,11 @@ static void status_change_work(struct work_struct *work) goto out; } + if (!chip->soc_reporting_ready) { + fg_dbg(chip, FG_STATUS, "Profile load is not complete yet\n"); + goto out; + } + rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, &prop); if (rc < 0) { @@ -2227,7 +2232,7 @@ static void status_change_work(struct work_struct *work) fg_batt_avg_update(chip); out: - fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n", + fg_dbg(chip, FG_STATUS, "charge_status:%d charge_type:%d charge_done:%d\n", chip->charge_status, chip->charge_type, chip->charge_done); pm_relax(chip->dev); } @@ -2623,6 +2628,10 @@ done: out: chip->soc_reporting_ready = true; vote(chip->awake_votable, PROFILE_LOAD, false, 0); + if (!work_pending(&chip->status_change_work)) { + pm_stay_awake(chip->dev); + schedule_work(&chip->status_change_work); + } } static void sram_dump_work(struct work_struct *work) -- GitLab From 1f489e665375eaeea9ce5e058c365984346f8938 Mon Sep 17 00:00:00 2001 From: Shadab Naseem Date: Thu, 23 Nov 2017 12:16:09 +0530 Subject: [PATCH 1689/5498] defconfig: msm: Update treble compatibilty configs Update configs which are mandatory for treble compatibity. Change-Id: Ib5d8a34823609e9d2617ed97ff373c4759032b9f Signed-off-by: Shadab Naseem --- arch/arm/configs/msm8909w-perf_defconfig | 3 +++ arch/arm/configs/msm8909w_defconfig | 3 +++ 2 files changed, 6 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 39be53b46ba0..e28176d8012c 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -1,4 +1,5 @@ CONFIG_EARLY_IOREMAP=y +# CONFIG_USELIB is not set CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y @@ -421,9 +422,11 @@ CONFIG_IPC_LOGGING=y CONFIG_DEBUG_USER=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_KEYS=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_CRYPTO_GF128MUL=y CONFIG_CRYPTO_NULL=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 629723f0eb53..f43f58016006 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -1,4 +1,5 @@ CONFIG_EARLY_IOREMAP=y +# CONFIG_USELIB is not set CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y @@ -454,9 +455,11 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_DEBUG_USER=y CONFIG_FREE_PAGES_RDONLY=y CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 +CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_CTR=y -- GitLab From fa156811a90fb4f3fc2e52827b07b365e092fc9e Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Fri, 27 Oct 2017 18:53:28 +0530 Subject: [PATCH 1690/5498] ASoC: msm: enable group config for both tx and rx Add support to enable group config for tdm tx and rx ports.On platform where ebit is unsupported it is expected to start tx and rx ports at a time. Ensure to start both tdm tx and rx group as group devices. Change-Id: Idb995c8215ccad147612b85bf38b289fb24391f2 Signed-off-by: Bala Kishore Pati --- sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 176 +++++++++++++++++++++----- 1 file changed, 146 insertions(+), 30 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 4b1159d3774b..1820a2131a97 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -191,6 +191,7 @@ struct msm_dai_q6_auxpcm_dai_data { }; static union afe_port_group_config group_cfg_tx; +static union afe_port_group_config group_cfg_rx; struct msm_dai_q6_tdm_dai_data { DECLARE_BITMAP(status_mask, STATUS_MAX); @@ -5522,24 +5523,34 @@ static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, custom_tdm_header->header[6], custom_tdm_header->header[7]); } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + group_cfg_tx.tdm_cfg.num_channels = + dai_data->group_cfg.tdm_cfg.num_channels; + group_cfg_tx.tdm_cfg.sample_rate = + dai_data->group_cfg.tdm_cfg.sample_rate; + group_cfg_tx.tdm_cfg.bit_width = + dai_data->group_cfg.tdm_cfg.bit_width; + group_cfg_tx.tdm_cfg.nslots_per_frame = + dai_data->group_cfg.tdm_cfg.nslots_per_frame; + group_cfg_tx.tdm_cfg.slot_width = + dai_data->group_cfg.tdm_cfg.slot_width; + group_cfg_tx.tdm_cfg.slot_mask = + dai_data->group_cfg.tdm_cfg.slot_mask; + } else { + group_cfg_rx.tdm_cfg.num_channels = + dai_data->group_cfg.tdm_cfg.num_channels; + group_cfg_rx.tdm_cfg.sample_rate = + dai_data->group_cfg.tdm_cfg.sample_rate; + group_cfg_rx.tdm_cfg.bit_width = + dai_data->group_cfg.tdm_cfg.bit_width; + group_cfg_rx.tdm_cfg.nslots_per_frame = + dai_data->group_cfg.tdm_cfg.nslots_per_frame; + group_cfg_rx.tdm_cfg.slot_width = + dai_data->group_cfg.tdm_cfg.slot_width; + group_cfg_rx.tdm_cfg.slot_mask = + dai_data->group_cfg.tdm_cfg.slot_mask; - memcpy(&group_cfg_tx.group_cfg, &dai_data->group_cfg.group_cfg, - sizeof(dai_data->group_cfg.group_cfg)); - memcpy(&group_cfg_tx.group_enable, &dai_data->group_cfg.group_enable, - sizeof(dai_data->group_cfg.group_enable)); - memcpy(&group_cfg_tx.tdm_cfg, &dai_data->group_cfg.tdm_cfg, - sizeof(dai_data->group_cfg.tdm_cfg)); - pr_debug("%s: TDM GROUP:\n" - "num_channels=%d sample_rate=%d bit_width=%d\n" - "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n", - __func__, - group_cfg_tx.tdm_cfg.num_channels, - group_cfg_tx.tdm_cfg.sample_rate, - group_cfg_tx.tdm_cfg.bit_width, - group_cfg_tx.tdm_cfg.nslots_per_frame, - group_cfg_tx.tdm_cfg.slot_width, - group_cfg_tx.tdm_cfg.slot_mask); - + } return 0; } @@ -5550,10 +5561,13 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, struct msm_dai_q6_tdm_dai_data *dai_data = dev_get_drvdata(dai->dev); u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; + u16 sec_group_id = 0; int group_idx = 0; + int sec_group_idx = 0; u16 prim_port_id = 0; u16 sec_port_id = 0; atomic_t *group_ref = NULL; + atomic_t *sec_group_ref = NULL; group_idx = msm_dai_q6_get_group_idx(dai->id); if (group_idx < 0) { @@ -5578,13 +5592,14 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, rc = msm_dai_q6_tdm_set_clk(dai_data, dai->id, true); if (IS_ERR_VALUE(rc)) { - dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n", + dev_err(dai->dev, + "%s:AFE CLK enable fail 0x%x\n", __func__, dai->id); goto rtn; } } if (dai_data->num_group_ports > 1) { - dev_dbg(dai->dev, "%s:enable RX afe group\n", + dev_dbg(dai->dev, "%s:enable afe group\n", __func__); rc = afe_port_group_enable(group_id, &dai_data->group_cfg, true); @@ -5605,12 +5620,64 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) + if (dai_data->afe_ebit_unsupported) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; + sec_group_id = + AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX; + sec_group_idx = + msm_dai_q6_get_group_idx(sec_group_id); + if (sec_group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x\n" + "not supported\n", __func__, + sec_group_id); + goto rtn; + } + sec_group_ref = &tdm_group_ref[sec_group_idx]; + if (atomic_read(sec_group_ref) == 0) { + rc = afe_port_group_enable( + sec_group_id, + &group_cfg_tx, + true); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, + "%s:failed to\n" + "enable grp\n" + "%x\n", __func__, + group_id); + goto rtn; + } + } + } } else { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) + if (dai_data->afe_ebit_unsupported) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; + sec_group_id = + AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX; + sec_group_idx = + msm_dai_q6_get_group_idx(sec_group_id); + if (sec_group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x\n" + " not supported\n", __func__, + sec_group_id); + goto rtn; + } + sec_group_ref = &tdm_group_ref[sec_group_idx]; + if (atomic_read(sec_group_ref) == 0) { + rc = afe_port_group_enable( + sec_group_id, + &group_cfg_rx, + true); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, + "%s:failed to\n" + "enable grp\n" + "%x\n", __func__, + group_id); + goto rtn; + } + } + } } dev_dbg(dai->dev, "\n%s:open prim port id %d TDM rate: %d\n" "dai_data->port_cfg.tdm.slot_mask %x\n" @@ -5667,7 +5734,7 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, } else { set_bit(STATUS_PORT_STARTED, dai_data->status_mask); - atomic_inc(group_ref); + atomic_inc(sec_group_ref); } } @@ -5688,10 +5755,13 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, struct msm_dai_q6_tdm_dai_data *dai_data = dev_get_drvdata(dai->dev); u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; + u16 sec_group_id = 0; int group_idx = 0; + int sec_group_idx = 0; u16 prim_port_id = 0; u16 sec_port_id = 0; atomic_t *group_ref = NULL; + atomic_t *sec_group_ref = NULL; group_idx = msm_dai_q6_get_group_idx(dai->id); if (group_idx < 0) { @@ -5707,14 +5777,37 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) + if (dai_data->afe_ebit_unsupported) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; + sec_group_id = + AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX; + sec_group_idx = + msm_dai_q6_get_group_idx(sec_group_id); + if (sec_group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x\n" + "not supported\n", + __func__, sec_group_id); + return; + } + sec_group_ref = &tdm_group_ref[sec_group_idx]; + } } else { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) + if (dai_data->afe_ebit_unsupported) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; + sec_group_id = + AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX; + sec_group_idx = + msm_dai_q6_get_group_idx(sec_group_id); + if (sec_group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x\n" + "not supported\n", + __func__, sec_group_id); + return; + } + sec_group_ref = &tdm_group_ref[sec_group_idx]; + } } - rc = afe_close(prim_port_id); if (IS_ERR_VALUE(rc)) { dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n", @@ -5727,6 +5820,7 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, dev_err(dai->dev, "%s: fail AFE port 0x%x\n", __func__, sec_port_id); } + atomic_dec(sec_group_ref); } atomic_dec(group_ref); @@ -5749,7 +5843,16 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, __func__, dai->id); } } - + if (dai_data->afe_ebit_unsupported) { + if (atomic_read(sec_group_ref) == 0) { + rc = afe_port_group_enable(sec_group_id, + NULL, false); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s:failed to\n" + "enable grp %x\n", __func__, group_id); + } + } + } /* TODO: need to monitor PCM/MI2S/TDM HW status */ /* NOTE: AFE should error out if HW resource contention */ @@ -7125,9 +7228,6 @@ static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX_1 || tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX_2 || tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX_3) { - dev_dbg(&pdev->dev, "Copy TX group config id %d\n", tdm_dev_id); - /*memcpy (&group_cfg_tx,&dai_data->group_cfg , - sizeof(dai_data->group_cfg));*/ memcpy(&group_cfg_tx.group_cfg, &dai_data->group_cfg.group_cfg , sizeof(dai_data->group_cfg.group_cfg)); @@ -7141,7 +7241,23 @@ static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) "Copy TX group configuration Successfully tdm_id %d\n", tdm_dev_id); } - + if (tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_RX || + tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_RX_1 || + tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_RX_2 || + tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_RX_3) { + memcpy(&group_cfg_rx.group_cfg, + &dai_data->group_cfg.group_cfg , + sizeof(dai_data->group_cfg.group_cfg)); + memcpy(&group_cfg_rx.group_enable, + &dai_data->group_cfg.group_enable, + sizeof(dai_data->group_cfg.group_enable)); + memcpy(&group_cfg_rx.tdm_cfg, + &dai_data->group_cfg.tdm_cfg, + sizeof(dai_data->group_cfg.tdm_cfg)); + dev_dbg(&pdev->dev, + "Copy RX group configuration Successfully tdm_id %d\n", + tdm_dev_id); + } dev_set_drvdata(&pdev->dev, dai_data); -- GitLab From 76f0dd36003d32fc7b1c4f877eb947ed5e7c4c95 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Thu, 23 Nov 2017 12:55:12 +0530 Subject: [PATCH 1691/5498] input: touchscreen: correct regulator calls in suspend/resume Correct regulator calls in suspend/resume path of Synaptics DSX V2.6 touch driver. Don't execute regulator_get or regulaotr_put calls in suspend/resume, if touch-to-wake is disabled. Change-Id: Ifce1ca9a6e8a08acc0bf22050727f976f959bb75 Signed-off-by: Shantanu Jain --- .../synaptics_dsx_2.6/synaptics_dsx_core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c index 0195dd80fd3f..c12cd4cdf280 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c @@ -1088,7 +1088,6 @@ static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, input_sync(rmi4_data->input_dev); input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0); input_sync(rmi4_data->input_dev); - rmi4_data->suspend = false; } return 0; @@ -1249,7 +1248,6 @@ static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, input_sync(rmi4_data->input_dev); input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0); input_sync(rmi4_data->input_dev); - rmi4_data->suspend = false; } return 0; @@ -4594,10 +4592,9 @@ exit: } mutex_unlock(&exp_data.mutex); - if (!rmi4_data->suspend && !rmi4_data->enable_wakeup_gesture) { + if (!rmi4_data->suspend && !rmi4_data->enable_wakeup_gesture) synaptics_rmi4_enable_reg(rmi4_data, false); - synaptics_rmi4_get_reg(rmi4_data, false); - } + rmi4_data->suspend = true; return 0; @@ -4632,10 +4629,8 @@ static int synaptics_rmi4_resume(struct device *dev) rmi4_data->current_page = MASK_8BIT; - if(rmi4_data->suspend) { - synaptics_rmi4_get_reg(rmi4_data, true); + if (rmi4_data->suspend) synaptics_rmi4_enable_reg(rmi4_data, true); - } synaptics_rmi4_sleep_enable(rmi4_data, false); synaptics_rmi4_irq_enable(rmi4_data, true, false); -- GitLab From 7fadf855dea4e9117ac7aef65a771eb4ec67831f Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Mon, 3 Jul 2017 10:52:29 +0530 Subject: [PATCH 1692/5498] power: smb-lib: Improve the OTG enable workaround for PM660 There are scenarios where inductive load with higher inrush current may fail to turn on the OTG. Improve the OTG turn-on sequence by gradually increasing the current-limit if it fails to turn-on with a lower current. Change-Id: I41c1e25257a4ee87f00287e07773331b1445b5f2 Signed-off-by: Anirudh Ghayal --- drivers/power/smb-lib.c | 102 ++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 30 deletions(-) diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index c1e7c2b87c92..5608a42de65c 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -1293,68 +1293,110 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev) #define MAX_RETRY 15 #define MIN_DELAY_US 2000 #define MAX_DELAY_US 9000 -static int _smblib_vbus_regulator_enable(struct regulator_dev *rdev) +static int otg_current[] = {250000, 500000, 1000000, 1500000}; +static int smblib_enable_otg_wa(struct smb_charger *chg) { - struct smb_charger *chg = rdev_get_drvdata(rdev); - int rc, retry_count = 0, min_delay = MIN_DELAY_US; u8 stat; + int rc, i, retry_count = 0, min_delay = MIN_DELAY_US; - smblib_dbg(chg, PR_OTG, "halt 1 in 8 mode\n"); - rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG, - ENG_BUCKBOOST_HALT1_8_MODE_BIT, - ENG_BUCKBOOST_HALT1_8_MODE_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", - rc); - return rc; - } + for (i = 0; i < ARRAY_SIZE(otg_current); i++) { + smblib_dbg(chg, PR_OTG, "enabling OTG with %duA\n", + otg_current[i]); + rc = smblib_set_charge_param(chg, &chg->param.otg_cl, + otg_current[i]); + if (rc < 0) { + smblib_err(chg, "Couldn't set otg limit rc=%d\n", rc); + return rc; + } - smblib_dbg(chg, PR_OTG, "enabling OTG\n"); - rc = smblib_write(chg, CMD_OTG_REG, OTG_EN_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't enable OTG regulator rc=%d\n", rc); - return rc; - } + rc = smblib_write(chg, CMD_OTG_REG, OTG_EN_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't enable OTG rc=%d\n", rc); + return rc; + } - if (chg->wa_flags & OTG_WA) { - /* check for softstart */ + retry_count = 0; + min_delay = MIN_DELAY_US; do { usleep_range(min_delay, min_delay + 100); rc = smblib_read(chg, OTG_STATUS_REG, &stat); if (rc < 0) { - smblib_err(chg, - "Couldn't read OTG status rc=%d\n", - rc); + smblib_err(chg, "Couldn't read OTG status rc=%d\n", + rc); goto out; } if (stat & BOOST_SOFTSTART_DONE_BIT) { rc = smblib_set_charge_param(chg, &chg->param.otg_cl, chg->otg_cl_ua); - if (rc < 0) - smblib_err(chg, - "Couldn't set otg limit\n"); + if (rc < 0) { + smblib_err(chg, "Couldn't set otg limit rc=%d\n", + rc); + goto out; + } break; } - /* increase the delay for following iterations */ if (retry_count > 5) min_delay = MAX_DELAY_US; + } while (retry_count++ < MAX_RETRY); if (retry_count >= MAX_RETRY) { - smblib_dbg(chg, PR_OTG, "Boost Softstart not done\n"); - goto out; + smblib_dbg(chg, PR_OTG, "OTG enable failed with %duA\n", + otg_current[i]); + rc = smblib_write(chg, CMD_OTG_REG, 0); + if (rc < 0) { + smblib_err(chg, "disable OTG rc=%d\n", rc); + goto out; + } + } else { + smblib_dbg(chg, PR_OTG, "OTG enabled\n"); + return 0; } } + if (i == ARRAY_SIZE(otg_current)) { + rc = -EINVAL; + goto out; + } + return 0; out: - /* disable OTG if softstart failed */ smblib_write(chg, CMD_OTG_REG, 0); return rc; } +static int _smblib_vbus_regulator_enable(struct regulator_dev *rdev) +{ + struct smb_charger *chg = rdev_get_drvdata(rdev); + int rc; + + smblib_dbg(chg, PR_OTG, "halt 1 in 8 mode\n"); + rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG, + ENG_BUCKBOOST_HALT1_8_MODE_BIT, + ENG_BUCKBOOST_HALT1_8_MODE_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", + rc); + return rc; + } + + smblib_dbg(chg, PR_OTG, "enabling OTG\n"); + + if (chg->wa_flags & OTG_WA) { + rc = smblib_enable_otg_wa(chg); + if (rc < 0) + smblib_err(chg, "Couldn't enable OTG rc=%d\n", rc); + } else { + rc = smblib_write(chg, CMD_OTG_REG, OTG_EN_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't enable OTG rc=%d\n", rc); + } + + return rc; +} + int smblib_vbus_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); -- GitLab From ecfd12b6b371e1e0880b45327dfca6ed374b5b9f Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Thu, 25 May 2017 11:58:02 -0700 Subject: [PATCH 1693/5498] power: qcom: smb-lib: optimize parallel current limiting with PD In MID-MID parallel charging configuration the parallel charger needs to be limited such that it does not draw more power than the input can provide. Currently the limiting algorithm assumes that the input voltage when using PD is always 5V, but if the input voltage changes then the limiting algorithm would unnecessarily limit the parallel charge current. Fix this by using the PD input voltage as an input to the limiting algorithm. Change-Id: I5a59ce11f0e802c982e944598fe61bad43e36779 Signed-off-by: Nicholas Troast --- drivers/power/smb-lib.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index 5608a42de65c..ab2a20b35eee 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -2341,16 +2341,9 @@ int smblib_get_prop_input_current_settled(struct smb_charger *chg, int smblib_get_prop_input_voltage_settled(struct smb_charger *chg, union power_supply_propval *val) { - const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); int rc, pulses; - val->intval = MICRO_5V; - if (apsd_result == NULL) { - smblib_err(chg, "APSD result is NULL\n"); - return 0; - } - - switch (apsd_result->pst) { + switch (chg->real_charger_type) { case POWER_SUPPLY_TYPE_USB_HVDCP_3: rc = smblib_get_pulse_cnt(chg, &pulses); if (rc < 0) { @@ -2360,6 +2353,9 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg, } val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses; break; + case POWER_SUPPLY_TYPE_USB_PD: + val->intval = chg->voltage_min_uv; + break; default: val->intval = MICRO_5V; break; @@ -2606,6 +2602,7 @@ int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, } chg->voltage_min_uv = min_uv; + power_supply_changed(&chg->usb_main_psy); return rc; } -- GitLab From 3f45934ed0bd864f878a78c3dfbd1ad437ba427f Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 9 May 2017 16:59:54 -0700 Subject: [PATCH 1694/5498] ipv6/dccp: do not inherit ipv6_mc_list from parent commit 83eaddab4378db256d00d295bda6ca997cd13a52 upstream. Like commit 657831ffc38e ("dccp/tcp: do not inherit mc_list from parent") we should clear ipv6_mc_list etc. for IPv6 sockets too. Cc: Eric Dumazet Signed-off-by: Cong Wang Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Connor O'Brien [AmitP: cherry-picked this backported commit from android-3.18] Signed-off-by: Amit Pundir Signed-off-by: Greg Kroah-Hartman --- net/dccp/ipv6.c | 7 +++++++ net/ipv6/tcp_ipv6.c | 2 ++ 2 files changed, 9 insertions(+) diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index ff186dac3e07..4d925dbe4bb7 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -487,6 +487,9 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, newsk->sk_backlog_rcv = dccp_v4_do_rcv; newnp->pktoptions = NULL; newnp->opt = NULL; + newnp->ipv6_mc_list = NULL; + newnp->ipv6_ac_list = NULL; + newnp->ipv6_fl_list = NULL; newnp->mcast_oif = inet6_iif(skb); newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; @@ -562,6 +565,10 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, /* Clone RX bits */ newnp->rxopt.all = np->rxopt.all; + newnp->ipv6_mc_list = NULL; + newnp->ipv6_ac_list = NULL; + newnp->ipv6_fl_list = NULL; + /* Clone pktoptions received with SYN */ newnp->pktoptions = NULL; if (ireq->pktopts != NULL) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 19fe7b789a72..b8e05f16659c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1113,6 +1113,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp->af_specific = &tcp_sock_ipv6_mapped_specific; #endif + newnp->ipv6_mc_list = NULL; newnp->ipv6_ac_list = NULL; newnp->ipv6_fl_list = NULL; newnp->pktoptions = NULL; @@ -1184,6 +1185,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, First: no IPv4 options. */ newinet->inet_opt = NULL; + newnp->ipv6_mc_list = NULL; newnp->ipv6_ac_list = NULL; newnp->ipv6_fl_list = NULL; -- GitLab From b4b4a3b633cbaaba16b1a21a2e3d3c0fead187da Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 15 Nov 2017 22:17:48 -0600 Subject: [PATCH 1695/5498] net/sctp: Always set scope_id in sctp_inet6_skb_msgname [ Upstream commit 7c8a61d9ee1df0fb4747879fa67a99614eb62fec ] Alexandar Potapenko while testing the kernel with KMSAN and syzkaller discovered that in some configurations sctp would leak 4 bytes of kernel stack. Working with his reproducer I discovered that those 4 bytes that are leaked is the scope id of an ipv6 address returned by recvmsg. With a little code inspection and a shrewd guess I discovered that sctp_inet6_skb_msgname only initializes the scope_id field for link local ipv6 addresses to the interface index the link local address pertains to instead of initializing the scope_id field for all ipv6 addresses. That is almost reasonable as scope_id's are meaniningful only for link local addresses. Set the scope_id in all other cases to 0 which is not a valid interface index to make it clear there is nothing useful in the scope_id field. There should be no danger of breaking userspace as the stack leak guaranteed that previously meaningless random data was being returned. Fixes: 372f525b495c ("SCTP: Resync with LKSCTP tree.") History-tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git Reported-by: Alexander Potapenko Tested-by: Alexander Potapenko Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/ipv6.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 70966fee8835..5b5ddc0f2dbc 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -805,6 +805,8 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct sctp_ulpevent *ev = sctp_skb2event(skb); addr->v6.sin6_scope_id = ev->iif; + } else { + addr->v6.sin6_scope_id = 0; } } -- GitLab From 475b9905b0c09ee64d45aa4bbcfd89f3546fc9cc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 2 Nov 2017 12:30:25 -0700 Subject: [PATCH 1696/5498] tcp: do not mangle skb->cb[] in tcp_make_synack() [ Upstream commit 3b11775033dc87c3d161996c54507b15ba26414a ] Christoph Paasch sent a patch to address the following issue : tcp_make_synack() is leaving some TCP private info in skb->cb[], then send the packet by other means than tcp_transmit_skb() tcp_transmit_skb() makes sure to clear skb->cb[] to not confuse IPv4/IPV6 stacks, but we have no such cleanup for SYNACK. tcp_make_synack() should not use tcp_init_nondata_skb() : tcp_init_nondata_skb() really should be limited to skbs put in write/rtx queues (the ones that are only sent via tcp_transmit_skb()) This patch fixes the issue and should even save few cpu cycles ;) Fixes: 971f10eca186 ("tcp: better TCP_SKB_CB layout to reduce cache line misses") Signed-off-by: Eric Dumazet Reported-by: Christoph Paasch Reviewed-by: Christoph Paasch Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_output.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index ff47e881e205..55da3338bfb2 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2911,13 +2911,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, tcp_ecn_make_synack(req, th, sk); th->source = htons(ireq->ir_num); th->dest = ireq->ir_rmt_port; - /* Setting of flags are superfluous here for callers (and ECE is - * not even correctly set) - */ - tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn, - TCPHDR_SYN | TCPHDR_ACK); - - th->seq = htonl(TCP_SKB_CB(skb)->seq); + skb->ip_summed = CHECKSUM_PARTIAL; + th->seq = htonl(tcp_rsk(req)->snt_isn); /* XXX data is queued and acked as is. No buffer/window check */ th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt); -- GitLab From 5b2af5fff8faeb56e654108a7d6465d52996d3a5 Mon Sep 17 00:00:00 2001 From: Ye Yin Date: Thu, 26 Oct 2017 16:57:05 +0800 Subject: [PATCH 1697/5498] netfilter/ipvs: clear ipvs_property flag when SKB net namespace changed [ Upstream commit 2b5ec1a5f9738ee7bf8f5ec0526e75e00362c48f ] When run ipvs in two different network namespace at the same host, and one ipvs transport network traffic to the other network namespace ipvs. 'ipvs_property' flag will make the second ipvs take no effect. So we should clear 'ipvs_property' when SKB network namespace changed. Fixes: 621e84d6f373 ("dev: introduce skb_scrub_packet()") Signed-off-by: Ye Yin Signed-off-by: Wei Zhou Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/linux/skbuff.h | 7 +++++++ net/core/skbuff.c | 1 + 2 files changed, 8 insertions(+) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2ff757f2d3a3..e5ba0236047e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3117,6 +3117,13 @@ static inline void nf_reset_trace(struct sk_buff *skb) #endif } +static inline void ipvs_reset(struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_IP_VS) + skb->ipvs_property = 0; +#endif +} + /* Note: This doesn't put any conntrack and bridge info in dst. */ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src, bool copy) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b2e2a53c2284..9c830242a1f9 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4069,6 +4069,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet) if (!xnet) return; + ipvs_reset(skb); skb_orphan(skb); skb->mark = 0; } -- GitLab From 39c3fff9ef51ba9f2748f37ad7d9cfef365e87fe Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 17 Oct 2017 23:26:10 +0800 Subject: [PATCH 1698/5498] sctp: do not peel off an assoc from one netns to another one [ Upstream commit df80cd9b28b9ebaa284a41df611dbf3a2d05ca74 ] Now when peeling off an association to the sock in another netns, all transports in this assoc are not to be rehashed and keep use the old key in hashtable. As a transport uses sk->net as the hash key to insert into hashtable, it would miss removing these transports from hashtable due to the new netns when closing the sock and all transports are being freeed, then later an use-after-free issue could be caused when looking up an asoc and dereferencing those transports. This is a very old issue since very beginning, ChunYu found it with syzkaller fuzz testing with this series: socket$inet6_sctp() bind$inet6() sendto$inet6() unshare(0x40000000) getsockopt$inet_sctp6_SCTP_GET_ASSOC_ID_LIST() getsockopt$inet_sctp6_SCTP_SOCKOPT_PEELOFF() This patch is to block this call when peeling one assoc off from one netns to another one, so that the netns of all transport would not go out-sync with the key in hashtable. Note that this patch didn't fix it by rehashing transports, as it's difficult to handle the situation when the tuple is already in use in the new netns. Besides, no one would like to peel off one assoc to another netns, considering ipaddrs, ifaces, etc. are usually different. Reported-by: ChunYu Wang Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Acked-by: Neil Horman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/socket.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 07324ca1df1d..3f89cd063246 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4466,6 +4466,10 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) struct socket *sock; int err = 0; + /* Do not peel off from one netns to another one. */ + if (!net_eq(current->nsproxy->net_ns, sock_net(sk))) + return -EINVAL; + if (!asoc) return -EINVAL; -- GitLab From e823c90371874557d4393060568cb7629436849f Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 16 Nov 2017 11:07:15 +0800 Subject: [PATCH 1699/5498] fealnx: Fix building error on MIPS [ Upstream commit cc54c1d32e6a4bb3f116721abf900513173e4d02 ] This patch try to fix the building error on MIPS. The reason is MIPS has already defined the LONG macro, which conflicts with the LONG enum in drivers/net/ethernet/fealnx.c. Signed-off-by: Huacai Chen Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/fealnx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c index b1b9ebafb354..a3b2e23921bf 100644 --- a/drivers/net/ethernet/fealnx.c +++ b/drivers/net/ethernet/fealnx.c @@ -257,8 +257,8 @@ enum rx_desc_status_bits { RXFSD = 0x00000800, /* first descriptor */ RXLSD = 0x00000400, /* last descriptor */ ErrorSummary = 0x80, /* error summary */ - RUNT = 0x40, /* runt packet received */ - LONG = 0x20, /* long packet received */ + RUNTPKT = 0x40, /* runt packet received */ + LONGPKT = 0x20, /* long packet received */ FAE = 0x10, /* frame align error */ CRC = 0x08, /* crc error */ RXER = 0x04, /* receive error */ @@ -1633,7 +1633,7 @@ static int netdev_rx(struct net_device *dev) dev->name, rx_status); dev->stats.rx_errors++; /* end of a packet. */ - if (rx_status & (LONG | RUNT)) + if (rx_status & (LONGPKT | RUNTPKT)) dev->stats.rx_length_errors++; if (rx_status & RXER) dev->stats.rx_frame_errors++; -- GitLab From 5c44a51d67e915fd5d3f2251ce285ffb50e74277 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 9 Nov 2017 13:04:44 +0900 Subject: [PATCH 1700/5498] af_netlink: ensure that NLMSG_DONE never fails in dumps [ Upstream commit 0642840b8bb008528dbdf929cec9f65ac4231ad0 ] The way people generally use netlink_dump is that they fill in the skb as much as possible, breaking when nla_put returns an error. Then, they get called again and start filling out the next skb, and again, and so forth. The mechanism at work here is the ability for the iterative dumping function to detect when the skb is filled up and not fill it past the brim, waiting for a fresh skb for the rest of the data. However, if the attributes are small and nicely packed, it is possible that a dump callback function successfully fills in attributes until the skb is of size 4080 (libmnl's default page-sized receive buffer size). The dump function completes, satisfied, and then, if it happens to be that this is actually the last skb, and no further ones are to be sent, then netlink_dump will add on the NLMSG_DONE part: nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); It is very important that netlink_dump does this, of course. However, in this example, that call to nlmsg_put_answer will fail, because the previous filling by the dump function did not leave it enough room. And how could it possibly have done so? All of the nla_put variety of functions simply check to see if the skb has enough tailroom, independent of the context it is in. In order to keep the important assumptions of all netlink dump users, it is therefore important to give them an skb that has this end part of the tail already reserved, so that the call to nlmsg_put_answer does not fail. Otherwise, library authors are forced to find some bizarre sized receive buffer that has a large modulo relative to the common sizes of messages received, which is ugly and buggy. This patch thus saves the NLMSG_DONE for an additional message, for the case that things are dangerously close to the brim. This requires keeping track of the errno from ->dump() across calls. Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/netlink/af_netlink.c | 17 +++++++++++------ net/netlink/af_netlink.h | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 4792e76b7d4a..d22e8d210fce 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1927,7 +1927,7 @@ static int netlink_dump(struct sock *sk) struct sk_buff *skb = NULL; struct nlmsghdr *nlh; struct module *module; - int len, err = -ENOBUFS; + int err = -ENOBUFS; int alloc_size; mutex_lock(nlk->cb_mutex); @@ -1965,9 +1965,11 @@ static int netlink_dump(struct sock *sk) goto errout_skb; netlink_skb_set_owner_r(skb, sk); - len = cb->dump(skb, cb); + if (nlk->dump_done_errno > 0) + nlk->dump_done_errno = cb->dump(skb, cb); - if (len > 0) { + if (nlk->dump_done_errno > 0 || + skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) { mutex_unlock(nlk->cb_mutex); if (sk_filter(sk, skb)) @@ -1977,13 +1979,15 @@ static int netlink_dump(struct sock *sk) return 0; } - nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); - if (!nlh) + nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, + sizeof(nlk->dump_done_errno), NLM_F_MULTI); + if (WARN_ON(!nlh)) goto errout_skb; nl_dump_check_consistent(cb, nlh); - memcpy(nlmsg_data(nlh), &len, sizeof(len)); + memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, + sizeof(nlk->dump_done_errno)); if (sk_filter(sk, skb)) kfree_skb(skb); @@ -2048,6 +2052,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, cb->skb = skb; nlk->cb_running = true; + nlk->dump_done_errno = INT_MAX; mutex_unlock(nlk->cb_mutex); diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index c6bd3dd35cb3..81a2d371bcf6 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -35,6 +35,7 @@ struct netlink_sock { size_t max_recvmsg_len; wait_queue_head_t wait; bool cb_running; + int dump_done_errno; struct netlink_callback cb; struct mutex *cb_mutex; struct mutex cb_def_mutex; -- GitLab From 28343f8b19c48a9c431930e75890d31dbf80a094 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 9 Nov 2017 16:43:13 -0800 Subject: [PATCH 1701/5498] vlan: fix a use-after-free in vlan_device_event() [ Upstream commit 052d41c01b3a2e3371d66de569717353af489d63 ] After refcnt reaches zero, vlan_vid_del() could free dev->vlan_info via RCU: RCU_INIT_POINTER(dev->vlan_info, NULL); call_rcu(&vlan_info->rcu, vlan_info_rcu_free); However, the pointer 'grp' still points to that memory since it is set before vlan_vid_del(): vlan_info = rtnl_dereference(dev->vlan_info); if (!vlan_info) goto out; grp = &vlan_info->grp; Depends on when that RCU callback is scheduled, we could trigger a use-after-free in vlan_group_for_each_dev() right following this vlan_vid_del(). Fix it by moving vlan_vid_del() before setting grp. This is also symmetric to the vlan_vid_add() we call in vlan_device_event(). Reported-by: Fengguang Wu Fixes: efc73f4bbc23 ("net: Fix memory leak - vlan_info struct") Cc: Alexander Duyck Cc: Linus Torvalds Cc: Girish Moodalbail Signed-off-by: Cong Wang Reviewed-by: Girish Moodalbail Tested-by: Fengguang Wu Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/8021q/vlan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 4ccf021b85d5..6b9c7eaca478 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -376,6 +376,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, dev->name); vlan_vid_add(dev, htons(ETH_P_8021Q), 0); } + if (event == NETDEV_DOWN && + (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) + vlan_vid_del(dev, htons(ETH_P_8021Q), 0); vlan_info = rtnl_dereference(dev->vlan_info); if (!vlan_info) @@ -420,9 +423,6 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, break; case NETDEV_DOWN: - if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) - vlan_vid_del(dev, htons(ETH_P_8021Q), 0); - /* Put all VLANs for this dev in the down state too. */ vlan_group_for_each_dev(grp, i, vlandev) { flgs = vlandev->flags; -- GitLab From 72df596704b8ce5448e92dea14851e767a9ae592 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Tue, 7 Nov 2017 11:37:07 +0100 Subject: [PATCH 1702/5498] ima: do not update security.ima if appraisal status is not INTEGRITY_PASS commit 020aae3ee58c1af0e7ffc4e2cc9fe4dc630338cb upstream. Commit b65a9cfc2c38 ("Untangling ima mess, part 2: deal with counters") moved the call of ima_file_check() from may_open() to do_filp_open() at a point where the file descriptor is already opened. This breaks the assumption made by IMA that file descriptors being closed belong to files whose access was granted by ima_file_check(). The consequence is that security.ima and security.evm are updated with good values, regardless of the current appraisal status. For example, if a file does not have security.ima, IMA will create it after opening the file for writing, even if access is denied. Access to the file will be allowed afterwards. Avoid this issue by checking the appraisal status before updating security.ima. Signed-off-by: Roberto Sassu Signed-off-by: Mimi Zohar Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/integrity/ima/ima_appraise.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index dd88b6e1e8a1..ee7618115756 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -297,6 +297,9 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) if (iint->flags & IMA_DIGSIG) return; + if (iint->ima_file_status != INTEGRITY_PASS) + return; + rc = ima_collect_measurement(iint, file, NULL, NULL); if (rc < 0) return; -- GitLab From f72e2ba19765ec94ab1b704bce53c3d1ca13202e Mon Sep 17 00:00:00 2001 From: alex chen Date: Wed, 15 Nov 2017 17:31:40 -0800 Subject: [PATCH 1703/5498] ocfs2: should wait dio before inode lock in ocfs2_setattr() commit 28f5a8a7c033cbf3e32277f4cc9c6afd74f05300 upstream. we should wait dio requests to finish before inode lock in ocfs2_setattr(), otherwise the following deadlock will happen: process 1 process 2 process 3 truncate file 'A' end_io of writing file 'A' receiving the bast messages ocfs2_setattr ocfs2_inode_lock_tracker ocfs2_inode_lock_full inode_dio_wait __inode_dio_wait -->waiting for all dio requests finish dlm_proxy_ast_handler dlm_do_local_bast ocfs2_blocking_ast ocfs2_generic_handle_bast set OCFS2_LOCK_BLOCKED flag dio_end_io dio_bio_end_aio dio_complete ocfs2_dio_end_io ocfs2_dio_end_io_write ocfs2_inode_lock __ocfs2_cluster_lock ocfs2_wait_for_mask -->waiting for OCFS2_LOCK_BLOCKED flag to be cleared, that is waiting for 'process 1' unlocking the inode lock inode_dio_end -->here dec the i_dio_count, but will never be called, so a deadlock happened. Link: http://lkml.kernel.org/r/59F81636.70508@huawei.com Signed-off-by: Alex Chen Reviewed-by: Jun Piao Reviewed-by: Joseph Qi Acked-by: Changwei Ge Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/file.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 2adcb9876e91..6c6fa10a82ca 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1151,6 +1151,13 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) dquot_initialize(inode); size_change = S_ISREG(inode->i_mode) && attr->ia_valid & ATTR_SIZE; if (size_change) { + /* + * Here we should wait dio to finish before inode lock + * to avoid a deadlock between ocfs2_setattr() and + * ocfs2_dio_end_io_write() + */ + inode_dio_wait(inode); + status = ocfs2_rw_lock(inode, 1); if (status < 0) { mlog_errno(status); @@ -1170,8 +1177,6 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) if (status) goto bail_unlock; - inode_dio_wait(inode); - if (i_size_read(inode) >= attr->ia_size) { if (ocfs2_should_order_data(inode)) { status = ocfs2_begin_ordered_truncate(inode, -- GitLab From 2722def0f5274f4ccd8a768736158bb297c5feee Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Sat, 29 Jul 2017 21:14:55 -0500 Subject: [PATCH 1704/5498] ipmi: fix unsigned long underflow commit 392a17b10ec4320d3c0e96e2a23ebaad1123b989 upstream. When I set the timeout to a specific value such as 500ms, the timeout event will not happen in time due to the overflow in function check_msg_timeout: ... ent->timeout -= timeout_period; if (ent->timeout > 0) return; ... The type of timeout_period is long, but ent->timeout is unsigned long. This patch makes the type consistent. Reported-by: Weilong Chen Signed-off-by: Corey Minyard Tested-by: Weilong Chen Signed-off-by: Greg Kroah-Hartman --- drivers/char/ipmi/ipmi_msghandler.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index f816211f062f..63164ff66bb4 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4010,7 +4010,8 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, } static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, - struct list_head *timeouts, long timeout_period, + struct list_head *timeouts, + unsigned long timeout_period, int slot, unsigned long *flags, unsigned int *waiting_msgs) { @@ -4023,8 +4024,8 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, if (!ent->inuse) return; - ent->timeout -= timeout_period; - if (ent->timeout > 0) { + if (timeout_period < ent->timeout) { + ent->timeout -= timeout_period; (*waiting_msgs)++; return; } @@ -4091,7 +4092,8 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, } } -static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period) +static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, + unsigned long timeout_period) { struct list_head timeouts; struct ipmi_recv_msg *msg, *msg2; -- GitLab From d73b3941f58ef10d3dbd9de148c5f534ce6bb0fd Mon Sep 17 00:00:00 2001 From: Jan Harkes Date: Wed, 27 Sep 2017 15:52:12 -0400 Subject: [PATCH 1705/5498] coda: fix 'kernel memory exposure attempt' in fsync commit d337b66a4c52c7b04eec661d86c2ef6e168965a2 upstream. When an application called fsync on a file in Coda a small request with just the file identifier was allocated, but the declared length was set to the size of union of all possible upcall requests. This bug has been around for a very long time and is now caught by the extra checking in usercopy that was introduced in Linux-4.8. The exposure happens when the Coda cache manager process reads the fsync upcall request at which point it is killed. As a result there is nobody servicing any further upcalls, trapping any processes that try to access the mounted Coda filesystem. Signed-off-by: Jan Harkes Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/coda/upcall.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 5bb6e27298a4..21dbff85829a 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -446,8 +446,7 @@ int venus_fsync(struct super_block *sb, struct CodaFid *fid) UPARG(CODA_FSYNC); inp->coda_fsync.VFid = *fid; - error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs), - &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; -- GitLab From 7166ceea0a4eba3f8c86925ad60e6f0543db6234 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Nov 2017 08:30:05 +0100 Subject: [PATCH 1706/5498] Linux 3.18.84 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8a1e51e5b0cf..107b5778b864 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 83 +SUBLEVEL = 84 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 7c9bfd6b12613e31d16b385f1678701e466f1b0b Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 6 Oct 2014 13:48:51 +0100 Subject: [PATCH 1707/5498] UPSTREAM: dm bufio: switch from a huge hash table to an rbtree Converting over to using an rbtree eliminates a fixed 8MB allocation from vmalloc space for the hash table. Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer (cherry picked from commit 4e420c452b11edf9d510c8180ac66f529e5b6206) (this commit from v3.19 is needed to avoid wasting a huge amount of memory when using dm-verity, especially on both the system and vendor partitions with forward error correction enabled) Change-Id: Ia48049bb4ed1b766e108bb73619f3939b6f0da90 Signed-off-by: Eric Biggers --- drivers/md/dm-bufio.c | 97 ++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 9c5a0b4d5446..9d4c27b5c3e5 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -14,6 +14,7 @@ #include #include #include +#include #define DM_MSG_PREFIX "bufio" @@ -47,14 +48,6 @@ */ #define DM_BUFIO_INLINE_VECS 16 -/* - * Buffer hash - */ -#define DM_BUFIO_HASH_BITS 20 -#define DM_BUFIO_HASH(block) \ - ((((block) >> DM_BUFIO_HASH_BITS) ^ (block)) & \ - ((1 << DM_BUFIO_HASH_BITS) - 1)) - /* * Don't try to use kmem_cache_alloc for blocks larger than this. * For explanation, see alloc_buffer_data below. @@ -106,7 +99,7 @@ struct dm_bufio_client { unsigned minimum_buffers; - struct hlist_head *cache_hash; + struct rb_root buffer_tree; wait_queue_head_t free_buffer_wait; int async_write_error; @@ -135,7 +128,7 @@ enum data_mode { }; struct dm_buffer { - struct hlist_node hash_list; + struct rb_node node; struct list_head lru_list; sector_t block; void *data; @@ -253,6 +246,53 @@ static LIST_HEAD(dm_bufio_all_clients); */ static DEFINE_MUTEX(dm_bufio_clients_lock); +/*---------------------------------------------------------------- + * A red/black tree acts as an index for all the buffers. + *--------------------------------------------------------------*/ +static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block) +{ + struct rb_node *n = c->buffer_tree.rb_node; + struct dm_buffer *b; + + while (n) { + b = container_of(n, struct dm_buffer, node); + + if (b->block == block) + return b; + + n = (b->block < block) ? n->rb_left : n->rb_right; + } + + return NULL; +} + +static void __insert(struct dm_bufio_client *c, struct dm_buffer *b) +{ + struct rb_node **new = &c->buffer_tree.rb_node, *parent = NULL; + struct dm_buffer *found; + + while (*new) { + found = container_of(*new, struct dm_buffer, node); + + if (found->block == b->block) { + BUG_ON(found != b); + return; + } + + parent = *new; + new = (found->block < b->block) ? + &((*new)->rb_left) : &((*new)->rb_right); + } + + rb_link_node(&b->node, parent, new); + rb_insert_color(&b->node, &c->buffer_tree); +} + +static void __remove(struct dm_bufio_client *c, struct dm_buffer *b) +{ + rb_erase(&b->node, &c->buffer_tree); +} + /*----------------------------------------------------------------*/ static void adjust_total_allocated(enum data_mode data_mode, long diff) @@ -435,7 +475,7 @@ static void __link_buffer(struct dm_buffer *b, sector_t block, int dirty) b->block = block; b->list_mode = dirty; list_add(&b->lru_list, &c->lru[dirty]); - hlist_add_head(&b->hash_list, &c->cache_hash[DM_BUFIO_HASH(block)]); + __insert(b->c, b); b->last_accessed = jiffies; } @@ -449,7 +489,7 @@ static void __unlink_buffer(struct dm_buffer *b) BUG_ON(!c->n_buffers[b->list_mode]); c->n_buffers[b->list_mode]--; - hlist_del(&b->hash_list); + __remove(b->c, b); list_del(&b->lru_list); } @@ -907,23 +947,6 @@ static void __check_watermark(struct dm_bufio_client *c, __write_dirty_buffers_async(c, 1, write_list); } -/* - * Find a buffer in the hash. - */ -static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block) -{ - struct dm_buffer *b; - - hlist_for_each_entry(b, &c->cache_hash[DM_BUFIO_HASH(block)], - hash_list) { - dm_bufio_cond_resched(); - if (b->block == block) - return b; - } - - return NULL; -} - /*---------------------------------------------------------------- * Getting a buffer *--------------------------------------------------------------*/ @@ -1553,11 +1576,7 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign r = -ENOMEM; goto bad_client; } - c->cache_hash = vmalloc(sizeof(struct hlist_head) << DM_BUFIO_HASH_BITS); - if (!c->cache_hash) { - r = -ENOMEM; - goto bad_hash; - } + c->buffer_tree = RB_ROOT; c->bdev = bdev; c->block_size = block_size; @@ -1576,9 +1595,6 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign c->n_buffers[i] = 0; } - for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++) - INIT_HLIST_HEAD(&c->cache_hash[i]); - mutex_init(&c->lock); INIT_LIST_HEAD(&c->reserved_buffers); c->need_reserved_buffers = reserved_buffers; @@ -1652,8 +1668,6 @@ bad_cache: } dm_io_client_destroy(c->dm_io); bad_dm_io: - vfree(c->cache_hash); -bad_hash: kfree(c); bad_client: return ERR_PTR(r); @@ -1680,9 +1694,7 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c) mutex_unlock(&dm_bufio_clients_lock); - for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++) - BUG_ON(!hlist_empty(&c->cache_hash[i])); - + BUG_ON(!RB_EMPTY_ROOT(&c->buffer_tree)); BUG_ON(c->need_reserved_buffers); while (!list_empty(&c->reserved_buffers)) { @@ -1700,7 +1712,6 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c) BUG_ON(c->n_buffers[i]); dm_io_client_destroy(c->dm_io); - vfree(c->cache_hash); kfree(c); } EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); -- GitLab From 821abd95b340ca4159082b0c362a7b3e38c5555c Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Tue, 23 Jun 2015 18:38:54 +0800 Subject: [PATCH 1708/5498] time: Always make sure wall_to_monotonic isn't positive Two issues were found on an IMX6 development board without an enabled RTC device(resulting in the boot time and monotonic time being initialized to 0). Issue 1:exportfs -a generate: "exportfs: /opt/nfs/arm does not support NFS export" Issue 2:cat /proc/stat: "btime 4294967236" The same issues can be reproduced on x86 after running the following code: int main(void) { struct timeval val; int ret; val.tv_sec = 0; val.tv_usec = 0; ret = settimeofday(&val, NULL); return 0; } Two issues are different symptoms of same problem: The reason is a positive wall_to_monotonic pushes boot time back to the time before Epoch, and getboottime will return negative value. In symptom 1: negative boot time cause get_expiry() to overflow time_t when input expire time is 2147483647, then cache_flush() always clears entries just added in ip_map_parse. In symptom 2: show_stat() uses "unsigned long" to print negative btime value returned by getboottime. This patch fix the problem by prohibiting time from being set to a value which would cause a negative boot time. As a result one can't set the CLOCK_REALTIME time prior to (1970 + system uptime). Change-Id: I31c2093baf48f9cdef49a8ec515d6fb193de5ebc Cc: Prarit Bhargava Cc: Richard Cochran Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Wang YanQing [jstultz: reworded commit message] Signed-off-by: John Stultz Git-commit: e1d7ba8735551ed79c7a0463a042353574b96da3 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Neeraj Upadhyay --- kernel/time/timekeeping.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 5ba37f44aff4..3b6d2ccca6bc 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -700,6 +700,7 @@ int do_settimeofday(const struct timespec *tv) struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts_delta, xt, tmp; unsigned long flags; + int ret = 0; if (!timespec_valid_strict(tv)) return -EINVAL; @@ -713,11 +714,16 @@ int do_settimeofday(const struct timespec *tv) ts_delta.tv_sec = tv->tv_sec - xt.tv_sec; ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec; + if (timespec64_compare(&tk->wall_to_monotonic, &ts_delta) > 0) { + ret = -EINVAL; + goto out; + } + tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts_delta)); tmp = timespec_to_timespec64(*tv); tk_set_xtime(tk, &tmp); - +out: timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&tk_core.seq); @@ -726,7 +732,7 @@ int do_settimeofday(const struct timespec *tv) /* signal hrtimers about time change */ clock_was_set(); - return 0; + return ret; } EXPORT_SYMBOL(do_settimeofday); @@ -755,7 +761,8 @@ int timekeeping_inject_offset(struct timespec *ts) /* Make sure the proposed value is valid */ tmp = timespec64_add(tk_xtime(tk), ts64); - if (!timespec64_valid_strict(&tmp)) { + if (timespec64_compare(&tk->wall_to_monotonic, &ts64) > 0 || + !timespec64_valid_strict(&tmp)) { ret = -EINVAL; goto error; } -- GitLab From 089ebed215a43111328dc23e75b7f841ad9191d4 Mon Sep 17 00:00:00 2001 From: smanag Date: Thu, 16 Nov 2017 13:42:12 +0530 Subject: [PATCH 1709/5498] ASoC: msm8x16-wcd: Read subsystem name from dtsi msm8x16-wcd codec registers callback with adsp subsystem to receive subsystem restart notifications. This will not work for msm8909 and msm8916 targets where audio dsp firmware is run on modem dsp. Read subsystem name from codec node of dtsi and register callback with the respective subsystem. msm8916, msm8909 are expected to have this subsys-name property defined in the codec node. CRs-Fixed: 2127307 Change-Id: I7a2d98513b9a32ed9f0af895820113d627e65813 Signed-off-by: Soumya Managoli --- .../bindings/sound/qcom-audio-dev.txt | 13 ++++++++++++- sound/soc/codecs/msm8x16-wcd.c | 17 ++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 36f04c2c4f28..c9c55f8d6da8 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -756,7 +756,12 @@ normally open. Optional Properties: - qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming. - +- qcom,subsys-name: This value provides the subsystem name where codec + is present. This property enables the codec driver to + register and receive subsytem restart notification from subsystem + and follow appropriate steps to ensure codec is in proper state + after subsytem restart. By default codec driver register + with ADSP subsystem. Example: sound { @@ -1649,6 +1654,12 @@ capacitor mode. - qcom,msm-micbias2-ext-cap : Boolean. Enable micbias2 external capacitor mode. - qcom,msm-spk-ext-pa : GPIO which enables external speaker pa. +- qcom,subsys-name: This value provides the subsystem name where codec + is present. This property enables the codec driver to + register and receive subsytem restart notification from subsystem + and follow appropriate steps to ensure codec is in proper state + after subsytem restart. By default codec driver register + with ADSP subsystem. To Configure External Audio Switch - qcom,msm-ext-audio-switch : GPIO which controls external switch that switches diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c index c6f1f10f1e70..ba000656105d 100644 --- a/sound/soc/codecs/msm8x16-wcd.c +++ b/sound/soc/codecs/msm8x16-wcd.c @@ -5759,6 +5759,7 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) struct msm8x16_wcd *msm8x16_wcd; struct msm8x16_wcd_pdata *pdata; int i, ret; + const char *subsys_name = NULL; dev_dbg(codec->dev, "%s()\n", __func__); @@ -5888,9 +5889,19 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec) /* Set initial cap mode */ msm8x16_wcd_configure_cap(codec, false, false); registered_codec = codec; - adsp_state_notifier = - subsys_notif_register_notifier("adsp", - &adsp_state_notifier_block); + ret = of_property_read_string(codec->dev->of_node, + "qcom,subsys-name", + &subsys_name); + if (ret) { + dev_dbg(codec->dev, "missing subsys-name entry in dt node\n"); + adsp_state_notifier = + subsys_notif_register_notifier("adsp", + &adsp_state_notifier_block); + } else { + adsp_state_notifier = + subsys_notif_register_notifier(subsys_name, + &adsp_state_notifier_block); + } if (!adsp_state_notifier) { dev_err(codec->dev, "Failed to register adsp state notifier\n"); iounmap(msm8x16_wcd->dig_base); -- GitLab From 701ebd1f9d53e5699bf7da5cc9c7232878e5f81d Mon Sep 17 00:00:00 2001 From: smanag Date: Thu, 23 Nov 2017 16:35:50 +0530 Subject: [PATCH 1710/5498] ARM: dts: msm: Update subsys-name in codec node for MSM8916, MSM8909 Targets which use 8x16 internal codec have to register with subsytem restart notifier module for modem subsytem to get notifier callbacks during subsystem restart. CRs-Fixed: 2127307 Change-Id: I8b4e3c2082c95dd17abe45940c6580cf7d5530dd Signed-off-by: Soumya Managoli --- arch/arm/boot/dts/qcom/msm-pm8916.dtsi | 4 +++- arch/arm/boot/dts/qcom/msm8909-pm8909-mtp.dtsi | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm-pm8916.dtsi b/arch/arm/boot/dts/qcom/msm-pm8916.dtsi index b71055a16437..54318c617fca 100644 --- a/arch/arm/boot/dts/qcom/msm-pm8916.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm8916.dtsi @@ -1,4 +1,5 @@ -/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -600,6 +601,7 @@ "cdc-vdda-cp"; qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias"; + qcom,subsys-name = "modem"; }; pm8916_tombak_analog: msm8x16_wcd_codec@f100{ diff --git a/arch/arm/boot/dts/qcom/msm8909-pm8909-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909-pm8909-mtp.dtsi index a3c925503367..4f210806a64f 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pm8909-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pm8909-mtp.dtsi @@ -116,6 +116,7 @@ qcom,cdc-vdd-spkdrv-current = <20000>; qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias", "cdc-vdd-spkdrv"; + qcom,subsys-name = "modem"; }; &wcnss { -- GitLab From 36977929e45c8303047570f72c2d5133e12c023d Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Mon, 20 Nov 2017 17:08:15 +0530 Subject: [PATCH 1711/5498] power: smb-lib: Restart charging from soft JEITA In the scenario where the charging is terminated in the JEITA soft condition, the HW is expected to restart charging when battery temperature returns back to normal. However, this does not work as expected and the charging stays terminated. Fix this by disabling and re-enabling charging CMD bit to restart charging. CRs-Fixed: 2071261 Change-Id: I81d2a89c72ede840cc561b736ce1366c65da8c42 Signed-off-by: Anirudh Ghayal --- drivers/power/qpnp-smb2.c | 2 ++ drivers/power/smb-lib.c | 53 +++++++++++++++++++++++++++++++++++++++ drivers/power/smb-lib.h | 1 + 3 files changed, 56 insertions(+) diff --git a/drivers/power/qpnp-smb2.c b/drivers/power/qpnp-smb2.c index d7e31a7a1ff7..a9a3ec339061 100644 --- a/drivers/power/qpnp-smb2.c +++ b/drivers/power/qpnp-smb2.c @@ -1423,6 +1423,7 @@ static int smb2_determine_initial_status(struct smb2 *chip) smblib_handle_usb_source_change(0, &irq_data); smblib_handle_chg_state_change(0, &irq_data); smblib_handle_icl_change(0, &irq_data); + smblib_handle_batt_temp_changed(0, &irq_data); return 0; } @@ -1485,6 +1486,7 @@ static struct smb_irq_info smb2_irqs[] = { .name = "bat-temp", .handler = smblib_handle_batt_temp_changed, .flags = IRQ_TYPE_EDGE_RISING, + .wake = true, }, [BATT_OCP_IRQ] = { .name = "bat-ocp", diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index ab2a20b35eee..8ef000d7c35c 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -2887,6 +2887,51 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, return rc; } +static int smblib_recover_from_soft_jeita(struct smb_charger *chg) +{ + u8 stat_1 = 0, stat_2 = 0; + int rc; + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat_1); + if (rc < 0) { + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + rc); + return rc; + } + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat_2); + if (rc < 0) { + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", + rc); + return rc; + } + + if ((chg->jeita_status && !(stat_2 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK) && + ((stat_1 & BATTERY_CHARGER_STATUS_MASK) == TERMINATE_CHARGE))) { + /* + * We are moving from JEITA soft -> Normal and charging + * is terminated + */ + rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, 0); + if (rc < 0) { + smblib_err(chg, "Couldn't disable charging rc=%d\n", + rc); + return rc; + } + rc = smblib_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't enable charging rc=%d\n", + rc); + return rc; + } + } + + chg->jeita_status = stat_2 & BAT_TEMP_STATUS_SOFT_LIMIT_MASK; + + return 0; +} + /*********************** * USB MAIN PSY GETTERS * *************************/ @@ -3079,6 +3124,14 @@ irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; + int rc; + + rc = smblib_recover_from_soft_jeita(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't recover chg from soft jeita rc=%d\n", + rc); + return IRQ_HANDLED; + } rerun_election(chg->fcc_votable); power_supply_changed(&chg->batt_psy); diff --git a/drivers/power/smb-lib.h b/drivers/power/smb-lib.h index cc2b722c4367..61ca3e9e6959 100644 --- a/drivers/power/smb-lib.h +++ b/drivers/power/smb-lib.h @@ -317,6 +317,7 @@ struct smb_charger { int typec_mode; int usb_icl_change_irq_enabled; bool skip_usb_notification; + u32 jeita_status; /* workaround flag */ u32 wa_flags; -- GitLab From d1b4aa235fa3a327d494fdb1830fa8c7ce555ff6 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Mon, 20 Nov 2017 17:31:26 +0530 Subject: [PATCH 1712/5498] power: smb-lib: Update the FLOAT detection logic An SDP may get detected as a FLOAT charger by PMIC APSD. To handle this case do the following steps when a FLOAT charger is detected- 1. Limit the ICL to 100mA and start USB enumeration 2. If enumeration succeeds, USB notifies a valid ICL and the charger updates ICL and charger-type to SDP. 3. If enumeration fails, USB notifies -ETIMEDOUT and charger applies ICL based on the Rp value. CRs-Fixed: 2081457 Change-Id: I2747a42ed9f9531e83c53d781a8ae9baa9aa74d0 Signed-off-by: Anirudh Ghayal --- drivers/power/qpnp-smb2.c | 7 +++++ drivers/power/smb-lib.c | 60 +++++++++++++++++++++++++++++++++++++-- drivers/power/smb-lib.h | 1 + 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/drivers/power/qpnp-smb2.c b/drivers/power/qpnp-smb2.c index a9a3ec339061..6b0954f4af11 100644 --- a/drivers/power/qpnp-smb2.c +++ b/drivers/power/qpnp-smb2.c @@ -1262,6 +1262,13 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } + rc = smblib_read(chg, USBIN_OPTIONS_2_CFG_REG, &chg->float_cfg); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read float charger options rc=%d\n", + rc); + return rc; + } + switch (chip->dt.chg_inhibit_thr_mv) { case 50: rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG, diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index 8ef000d7c35c..296ea4cc182d 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -537,10 +537,17 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); /* if PD is active, APSD is disabled so won't have a valid result */ - if (chg->pd_active) + if (chg->pd_active) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; - else + } else { + /* + * Update real charger type only if its not FLOAT + * detected as as SDP + */ + if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT && + chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) chg->real_charger_type = apsd_result->pst; + } if (!chg->skip_usb_notification) power_supply_set_supply_type(chg->usb_psy, @@ -796,6 +803,7 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) { int rc; u8 icl_options; + const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); /* power source is SDP */ switch (icl_ua) { @@ -820,6 +828,21 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return -EINVAL; } + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB && + apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT) { + /* + * change the float charger configuration to SDP, if this + * is the case of SDP being detected as FLOAT + */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + FORCE_FLOAT_SDP_CFG_BIT, FORCE_FLOAT_SDP_CFG_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't set float ICL options rc=%d\n", + rc); + return rc; + } + } + rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options); if (rc < 0) { @@ -3510,11 +3533,17 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); break; case POWER_SUPPLY_TYPE_USB_DCP: - case POWER_SUPPLY_TYPE_USB_FLOAT: typec_mode = smblib_get_prop_typec_mode(chg); rp_ua = get_rp_based_dcp_current(chg, typec_mode); vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); break; + case POWER_SUPPLY_TYPE_USB_FLOAT: + /* + * limit ICL to 100mA, the USB driver will enumerate to check + * if this is a SDP and appropriately set the current + */ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); + break; case POWER_SUPPLY_TYPE_USB_HVDCP: case POWER_SUPPLY_TYPE_USB_HVDCP_3: vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000); @@ -3720,6 +3749,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->pd_hard_reset = 0; chg->typec_legacy_valid = false; + /* write back the default FLOAT charger configuration */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + (u8)FLOAT_OPTIONS_MASK, chg->float_cfg); + if (rc < 0) + smblib_err(chg, "Couldn't write float charger options rc=%d\n", + rc); + /* reset back to 120mS tCC debounce */ rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0); if (rc < 0) @@ -3820,6 +3856,24 @@ static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT)) return; + /* + * if APSD indicates FLOAT and the USB stack had detected SDP, + * do not respond to Rp changes as we do not confirm that its + * a legacy cable + */ + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) + return; + /* + * We want the ICL vote @ 100mA for a FLOAT charger + * until the detection by the USB stack is complete. + * Ignore the Rp changes unless there is a + * pre-existing valid vote. + */ + if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT && + get_client_vote(chg->usb_icl_votable, + LEGACY_UNKNOWN_VOTER) <= 100000) + return; + /* * handle Rp change for DCP/FLOAT/OCP. * Update the current only if the Rp is different from diff --git a/drivers/power/smb-lib.h b/drivers/power/smb-lib.h index 61ca3e9e6959..96aa5c27c1d2 100644 --- a/drivers/power/smb-lib.h +++ b/drivers/power/smb-lib.h @@ -318,6 +318,7 @@ struct smb_charger { int usb_icl_change_irq_enabled; bool skip_usb_notification; u32 jeita_status; + u8 float_cfg; /* workaround flag */ u32 wa_flags; -- GitLab From 18a5c301d6b304bd5957c636857ac74663f6c5b2 Mon Sep 17 00:00:00 2001 From: Ashay Jaiswal Date: Mon, 20 Nov 2017 17:46:38 +0530 Subject: [PATCH 1713/5498] power: smb-lib: enable DPDM regulator at CC attach In case of quick back-to-back insertion/removal of USB there is a possibility that VBUS does not fall below the 1V usb-plugout threshold and hence the subsequent insertion does not generate a plug-in event. This keeps the DPDM regulator disabled at insertion thus impacting the APSD result. Fix this by voting to enable the DPDM regulator in the cc-attach handler. CRs-Fixed: 2042071 Change-Id: I37a32081f0847965e34eb1c4114602ec61e9a005 Signed-off-by: Ashay Jaiswal --- drivers/power/smb-lib.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index 296ea4cc182d..96ca4ea719bb 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -3841,10 +3841,14 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg) smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", rc); - if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) + if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) { typec_sink_insertion(chg); - else + } else { + /* vote to the USB stack to float DP_DM before APSD */ + power_supply_set_dp_dm(chg->usb_psy, + POWER_SUPPLY_DP_DM_DPF_DMF); typec_sink_removal(chg); + } } static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) -- GitLab From e60e95954cb5df0e0f2fc232a86911162f5894d5 Mon Sep 17 00:00:00 2001 From: Nicholas Troast Date: Wed, 26 Jul 2017 14:17:34 -0700 Subject: [PATCH 1714/5498] power: qpnp-smb2: expose CHARGE_COUNTER prop via batt psy CHARGE_COUNTER is a required battery power supply property. Add it. Change-Id: Ieb9c64447f88213479ce14b738f71fa703ffc194 Signed-off-by: Nicholas Troast --- drivers/power/qpnp-smb2.c | 4 ++++ drivers/power/smb-lib.c | 13 +++++++++++++ drivers/power/smb-lib.h | 2 ++ 3 files changed, 19 insertions(+) diff --git a/drivers/power/qpnp-smb2.c b/drivers/power/qpnp-smb2.c index 6b0954f4af11..a0c223cb9891 100644 --- a/drivers/power/qpnp-smb2.c +++ b/drivers/power/qpnp-smb2.c @@ -561,6 +561,7 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_DIE_HEALTH, POWER_SUPPLY_PROP_RERUN_AICL, POWER_SUPPLY_PROP_DP_DM, + POWER_SUPPLY_PROP_CHARGE_COUNTER, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -661,6 +662,9 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_RERUN_AICL: val->intval = 0; break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + rc = smblib_get_prop_batt_charge_counter(chg, val); + break; default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index 96ca4ea719bb..c041fc70234f 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -1823,6 +1823,19 @@ int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg, return 0; } +int smblib_get_prop_batt_charge_counter(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CHARGE_COUNTER, val); + return rc; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ diff --git a/drivers/power/smb-lib.h b/drivers/power/smb-lib.h index 96aa5c27c1d2..2cd3ff183279 100644 --- a/drivers/power/smb-lib.h +++ b/drivers/power/smb-lib.h @@ -408,6 +408,8 @@ int smblib_get_prop_batt_current_now(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_temp(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_batt_charge_counter(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_batt_capacity(struct smb_charger *chg, -- GitLab From 67cc5e6bfe71826faf1010c20c89b2840957c4b4 Mon Sep 17 00:00:00 2001 From: Harry Yang Date: Tue, 12 Sep 2017 11:15:04 -0700 Subject: [PATCH 1715/5498] power: smblib: correct PD voting when inactive Currently, when becoming inactive, PD leaves the HDC and input limited interrupt enabled. Correct it by disabling it. Change-Id: I62a1d837f6126951cb428a982fe1d8876f1d5bdc Signed-off-by: Harry Yang --- drivers/power/smb-lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index c041fc70234f..bb956c514566 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -2731,7 +2731,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, hvdcp = stat & QC_CHARGER_BIT; vote(chg->apsd_disable_votable, PD_VOTER, false, 0); vote(chg->pd_allowed_votable, PD_VOTER, true, 0); - vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, false, 0); -- GitLab From 1bd51b786627aebb75e11e33ad946fa43bf2a467 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Tue, 19 Sep 2017 18:04:15 -0700 Subject: [PATCH 1716/5498] smb-lib: allow reading vbus without presence Currently the driver skips invoking adc api's if usb is not present i.e. vbus has fallen below 3.3V. This presence check is not required for the vbus. Skip that check. Change-Id: I4a2dd91109a308b553b7407618ee8f3221127f61 Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/smb-lib.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index bb956c514566..34994bf5226a 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -2169,12 +2169,6 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, union power_supply_propval *val) { - int rc = 0; - - rc = smblib_get_prop_usb_present(chg, val); - if (rc < 0 || !val->intval) - return rc; - if (!chg->iio.usbin_v_chan || PTR_ERR(chg->iio.usbin_v_chan) == -EPROBE_DEFER) chg->iio.usbin_v_chan = iio_channel_get(chg->dev, "usbin_v"); -- GitLab From 15f77b0176e5faba0e6ed97ca759a587131b7146 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 24 Aug 2017 14:28:54 +0800 Subject: [PATCH 1717/5498] power: smb-lib: Disable USB IRQs during high duty cycle Disable USB IRQs after high duty cycle IRQ triggered and re-enable them in the delay worker. This helps to avoid the high duty cycle IRQ storming. CRs-Fixed: 2094725 Change-Id: Iba22a7402692b5ee5f738cc17ed9c214f6014ac0 Signed-off-by: Fenglin Wu --- drivers/power/smb-lib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index 34994bf5226a..236bef366812 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -3988,6 +3988,14 @@ irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; chg->is_hdc = true; + /* + * Disable usb IRQs after the flag set and re-enable IRQs after + * the flag cleared in the delayed work queue, to avoid any IRQ + * storming during the delays + */ + if (chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq) + disable_irq_nosync(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); + schedule_delayed_work(&chg->clear_hdc_work, msecs_to_jiffies(60)); return IRQ_HANDLED; @@ -4162,6 +4170,8 @@ static void clear_hdc_work(struct work_struct *work) clear_hdc_work.work); chg->is_hdc = 0; + if (chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq) + enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); } static void rdstd_cc2_detach_work(struct work_struct *work) -- GitLab From 2bf1521df1c7f9339248508f62b8ac55629e9c74 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Fri, 24 Nov 2017 10:29:59 +0530 Subject: [PATCH 1718/5498] power: qpnp-smb2: Add a DT property to disable USB power-delivery (PD) Platforms where the USB PHY is not enabled to support USB PD, the PD support from PMIC can be disabled. Change-Id: Ibc69700642c48d17c0dc54361fb1e0c011d572ae Signed-off-by: Anirudh Ghayal --- Documentation/devicetree/bindings/power/qpnp-smb2.txt | 5 +++++ drivers/power/qpnp-smb2.c | 6 ++++++ drivers/power/smb-lib.c | 2 +- drivers/power/smb-lib.h | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/power/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qpnp-smb2.txt index 468db388b0a6..1f98d0d69ce7 100644 --- a/Documentation/devicetree/bindings/power/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/qpnp-smb2.txt @@ -182,6 +182,11 @@ Charger specific properties: Definition: Specifies the deglitch interval for OTG detection. If the value is not present, 50 msec is used as default. +- qcom,pd-not-supported + Usage: optional + Value type: bool + Definition: Option to indicate if the platform supports USB PD (power delivery). + ============================================= Second Level Nodes - SMB2 Charger Peripherals ============================================= diff --git a/drivers/power/qpnp-smb2.c b/drivers/power/qpnp-smb2.c index a0c223cb9891..a75343d262a6 100644 --- a/drivers/power/qpnp-smb2.c +++ b/drivers/power/qpnp-smb2.c @@ -167,6 +167,7 @@ struct smb_dt_props { bool no_battery; bool hvdcp_disable; bool auto_recharge_soc; + bool no_pd; }; struct smb2 { @@ -202,6 +203,9 @@ static int smb2_parse_dt(struct smb2 *chip) chip->dt.no_battery = of_property_read_bool(node, "qcom,batteryless-platform"); + chip->dt.no_pd = of_property_read_bool(node, + "qcom,pd-not-supported"); + chg->skip_usb_notification = of_property_read_bool(node, "qcom,skip-usb-notification"); @@ -1154,6 +1158,8 @@ static int smb2_init_hw(struct smb2 *chip) chg->micro_usb_mode, 0); vote(chg->hvdcp_enable_votable, MICRO_USB_VOTER, chg->micro_usb_mode, 0); + vote(chg->pd_disallowed_votable_indirect, PD_NOT_SUPPORTED_VOTER, + chip->dt.no_pd, 0); /* * AICL configuration: diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index 24bb284000c8..f10533d881bd 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -2720,7 +2720,7 @@ static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active) hvdcp = stat & QC_CHARGER_BIT; vote(chg->apsd_disable_votable, PD_VOTER, false, 0); - vote(chg->pd_allowed_votable, PD_VOTER, true, 0); + vote(chg->pd_allowed_votable, PD_VOTER, false, 0); vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, false, 0); diff --git a/drivers/power/smb-lib.h b/drivers/power/smb-lib.h index 2cd3ff183279..9a6d01162a36 100644 --- a/drivers/power/smb-lib.h +++ b/drivers/power/smb-lib.h @@ -64,6 +64,7 @@ enum print_reason { #define OTG_DELAY_VOTER "OTG_DELAY_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER" +#define PD_NOT_SUPPORTED_VOTER "PD_NOT_SUPPORTED_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 -- GitLab From 81404d4dc2b8bff9a740980c90d2c212e8c0bf40 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Mon, 20 Nov 2017 22:53:53 +0530 Subject: [PATCH 1719/5498] smb-lib: allow hvdcp if pd is disabled Currently when pd is disabled, we do not allow hvdcp. To fix that once hvdcp timeout happens and if pd is disabled, force a pd_active = 0. This will cause us to run legacy workaround and rerun apsd if the charger was detected as hvdcp. Importantly, once legacy bit workaround is run, wait 400mS for typeC to debounce. Currently we wait for 100mS only and that causes a removal detection since the CC lines are not settled within 100mS. Also it may take up to 650mS for the vbus to drop once CC lines are floated during the workaround. The current wait is just 500mS, bump it up to a 1 second. Change-Id: I17a7341e96d6efccde6270bc69d79c3f215b83e4 Signed-off-by: Abhijeet Dharmapurikar --- drivers/power/smb-lib.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/power/smb-lib.c b/drivers/power/smb-lib.c index 236bef366812..24bb284000c8 100644 --- a/drivers/power/smb-lib.c +++ b/drivers/power/smb-lib.c @@ -2654,17 +2654,13 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg, return rc; } -int smblib_set_prop_pd_active(struct smb_charger *chg, - const union power_supply_propval *val) +static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active) { int rc; bool orientation, sink_attached, hvdcp; u8 stat; - if (!get_effective_result(chg->pd_allowed_votable)) - return -EINVAL; - - chg->pd_active = val->intval; + chg->pd_active = pd_active; if (chg->pd_active) { vote(chg->apsd_disable_votable, PD_VOTER, true, 0); vote(chg->pd_allowed_votable, PD_VOTER, true, 0); @@ -2752,6 +2748,15 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, return rc; } +int smblib_set_prop_pd_active(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (!get_effective_result(chg->pd_allowed_votable)) + return -EINVAL; + + return __smblib_set_prop_pd_active(chg, val->intval); +} + int smblib_set_prop_ship_mode(struct smb_charger *chg, const union power_supply_propval *val) { @@ -3496,6 +3501,13 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, /* enforce DCP ICL if specified */ vote(chg->usb_icl_votable, DCP_VOTER, chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua); + + /* + * if pd is not allowed, then set pd_active = false right here, + * so that it starts the hvdcp engine + */ + if (!get_effective_result(chg->pd_allowed_votable)) + __smblib_set_prop_pd_active(chg, 0); } smblib_dbg(chg, PR_INTERRUPT, "IRQ: smblib_handle_hvdcp_check_timeout %s\n", @@ -4469,7 +4481,9 @@ static void smblib_legacy_detection_work(struct work_struct *work) smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc); /* wait for the adapter to turn off VBUS */ - msleep(500); + msleep(1000); + + smblib_dbg(chg, PR_MISC, "legacy workaround enabling typec\n"); rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, @@ -4478,7 +4492,7 @@ static void smblib_legacy_detection_work(struct work_struct *work) smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc); /* wait for type-c detection to complete */ - msleep(100); + msleep(400); rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat); if (rc < 0) { @@ -4490,6 +4504,8 @@ static void smblib_legacy_detection_work(struct work_struct *work) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT; rp_high = chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH; + smblib_dbg(chg, PR_MISC, "legacy workaround done legacy = %d rp_high = %d\n", + legacy, rp_high); if (!legacy || !rp_high) vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, false, 0); -- GitLab From 91f71a354fb9696cc42cf3a5613b329bca8c2a58 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Fri, 24 Nov 2017 10:41:24 +0530 Subject: [PATCH 1720/5498] ARM: dts: msm: Add specific FG and Charger DT config for 8909w+660 Add the following- 1. Battery profile for itech-3000mah and ascent-3450mAh 2. Enable these battery profiles for 8909w+PM660 3. Enable 'pd-no-supported' as the USB PHY does not support it Change-Id: I527fda5787372c743f396e92a44cb8380da9bcd3 Signed-off-by: Anirudh Ghayal --- .../fg-gen3-batterydata-ascent-3450mah.dtsi | 81 +++++++++++++++++++ .../fg-gen3-batterydata-itech-3000mah.dtsi | 81 +++++++++++++++++++ .../arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 16 ++++ 3 files changed, 178 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi create mode 100644 arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi new file mode 100644 index 000000000000..c7cecbca3929 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi @@ -0,0 +1,81 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +qcom,ascent_3450mah { + /* Ascent_with_connector_3450mAh_averaged_MasterSlave_Jan6th2017 */ + qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; + qcom,fastchg-current-ma = <3450>; + qcom,batt-id-kohm = <60>; + qcom,battery-beta = <3435>; + qcom,battery-type = "ascent_3450mah_averaged_masterslave_jan6th2017"; + qcom,checksum = <0x96AC>; + qcom,gui-version = "PMI8998GUI - 2.0.0.54"; + qcom,fg-profile-data = [ + 9C 1F 85 05 + 82 0A 73 FC + 2B 1D 72 EA + EE 03 66 0C + C8 17 F4 22 + E0 45 1F 52 + 5C 00 00 00 + 10 00 00 00 + 00 00 4A C4 + C7 BC 48 C2 + 0F 00 08 00 + E1 DA 5D ED + 8D FD B2 F3 + 96 E2 A7 12 + 7E F4 0E 3B + 24 06 09 20 + 27 00 14 00 + 83 1F EE 05 + 1F 0A 45 FD + 6B 1D 53 E5 + EC 0B 31 14 + 44 18 49 23 + 18 45 A6 53 + 55 00 00 00 + 0E 00 00 00 + 00 00 61 CC + B7 C3 0F BC + 0F 00 00 00 + 92 00 5D ED + E3 06 E0 00 + 75 FD 9C 03 + 47 DB B3 22 + CB 33 CC FF + 07 10 00 00 + 99 0D 99 45 + 0F 00 40 00 + AB 01 0A FA + FF 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi new file mode 100644 index 000000000000..03801ee90589 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi @@ -0,0 +1,81 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +qcom,itech_3000mah { + /* #Itech_B00826LF_3000mAh_ver1660_averaged_MasterSlave_Jan10th2017*/ + qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; + qcom,fastchg-current-ma = <3000>; + qcom,batt-id-kohm = <100>; + qcom,battery-beta = <3435>; + qcom,battery-type = "itech_b00826lf_3000mah_ver1660_jan10th2017"; + qcom,checksum = <0xFB8F>; + qcom,gui-version = "PMI8998GUI - 2.0.0.54"; + qcom,fg-profile-data = [ + A4 1F 6E 05 + 9C 0A 2B FC + 32 1D 23 E5 + 60 0B 1B 15 + AD 17 8C 22 + EA 3C 89 4A + 5B 00 00 00 + 12 00 00 00 + 00 00 62 C2 + 0C CD D8 C2 + 19 00 08 00 + 85 EA C7 EC + E2 05 2F 01 + 9B F5 12 12 + 5E 05 88 3B + 22 06 09 20 + 27 00 14 00 + 7D 1F DD 05 + 3F 0A E5 FC + 72 1D E3 F5 + 6F 12 C0 1D + 88 18 FB 22 + 8D 45 C6 52 + 54 00 00 00 + 0F 00 00 00 + 00 00 BD CD + 55 C2 5D C5 + 14 00 00 00 + 7E 00 C7 EC + 60 06 BB 00 + 59 06 61 03 + D9 FC 75 1B + B3 33 CC FF + 07 10 00 00 + 3E 0B 99 45 + 14 00 40 00 + AE 01 0A FA + FF 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index b5948356d9bf..46c553c577f7 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -239,3 +239,19 @@ qcom,support-twm-config; qcom,pbs-client = <&pm660_pbs>; }; + +/ { + mtp_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <15>; + #include "fg-gen3-batterydata-itech-3000mah.dtsi" + #include "fg-gen3-batterydata-ascent-3450mah.dtsi" + }; +}; + +&pm660_charger { + qcom,pd-not-supported; +}; + +&pm660_fg { + qcom,battery-data = <&mtp_batterydata>; +}; -- GitLab From c3a6cc0f31e482d788e3131d9056d3f859214541 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Tue, 29 Aug 2017 01:05:46 +0530 Subject: [PATCH 1721/5498] msm: ipa: Cache CNE event Provide support to cache CNE event for debugging purpose. Change-Id: I00d101d3b9a627d7aba3e4524ff301797ea6085e Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa.c | 29 +++++++++++++++++++++---- drivers/platform/msm/ipa/ipa_v2/ipa_i.h | 10 +++++++++ drivers/platform/msm/ipa/ipa_v3/ipa.c | 29 +++++++++++++++++++++---- drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 9 ++++++++ 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index 22bb8e0b2fc1..01d95e8da12f 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -532,7 +532,8 @@ static void ipa_wan_msg_free_cb(void *buff, u32 len, u32 type) kfree(buff); } -static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type) +static int ipa_send_wan_msg(unsigned long usr_param, + uint8_t msg_type, bool is_cache) { int retval; struct ipa_wan_msg *wan_msg; @@ -560,6 +561,25 @@ static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type) return retval; } + if (is_cache) { + mutex_lock(&ipa_ctx->ipa_cne_evt_lock); + + /* cache the cne event */ + memcpy(&ipa_ctx->ipa_cne_evt_req_cache[ + ipa_ctx->num_ipa_cne_evt_req].wan_msg, + wan_msg, + sizeof(struct ipa_wan_msg)); + + memcpy(&ipa_ctx->ipa_cne_evt_req_cache[ + ipa_ctx->num_ipa_cne_evt_req].msg_meta, + &msg_meta, + sizeof(struct ipa_msg_meta)); + + ipa_ctx->num_ipa_cne_evt_req++; + ipa_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE; + mutex_unlock(&ipa_ctx->ipa_cne_evt_lock); + } + return 0; } @@ -1329,21 +1349,21 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD: - retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD); + retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true); if (retval) { IPAERR("ipa_send_wan_msg failed: %d\n", retval); break; } break; case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL: - retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL); + retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true); if (retval) { IPAERR("ipa_send_wan_msg failed: %d\n", retval); break; } break; case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED: - retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT); + retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT, false); if (retval) { IPAERR("ipa_send_wan_msg failed: %d\n", retval); break; @@ -4180,6 +4200,7 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p, mutex_init(&ipa_ctx->lock); mutex_init(&ipa_ctx->nat_mem.lock); + mutex_init(&ipa_ctx->ipa_cne_evt_lock); idr_init(&ipa_ctx->ipa_idr); spin_lock_init(&ipa_ctx->idr_lock); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index fe2695aee20c..ceb0f7dbdf99 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -65,6 +65,8 @@ #define IPA_IPC_LOG_PAGES 50 +#define IPA_MAX_NUM_REQ_CACHE 10 + #define IPADBG(fmt, args...) \ do { \ pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\ @@ -988,6 +990,11 @@ struct ipacm_client_info { bool uplink; }; +struct ipa_cne_evt { + struct ipa_wan_msg wan_msg; + struct ipa_msg_meta msg_meta; +}; + /** * struct ipa_context - IPA context * @class: pointer to the struct class @@ -1188,6 +1195,9 @@ struct ipa_context { u32 ipa_rx_min_timeout_usec; u32 ipa_rx_max_timeout_usec; u32 ipa_polling_iteration; + struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE]; + int num_ipa_cne_evt_req; + struct mutex ipa_cne_evt_lock; }; /** diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 43b32fef0e56..d89c42d19675 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -598,7 +598,8 @@ static void ipa3_wan_msg_free_cb(void *buff, u32 len, u32 type) kfree(buff); } -static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type) +static int ipa3_send_wan_msg(unsigned long usr_param, + uint8_t msg_type, bool is_cache) { int retval; struct ipa_wan_msg *wan_msg; @@ -626,6 +627,25 @@ static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type) return retval; } + if (is_cache) { + mutex_lock(&ipa3_ctx->ipa_cne_evt_lock); + + /* cache the cne event */ + memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[ + ipa3_ctx->num_ipa_cne_evt_req].wan_msg, + wan_msg, + sizeof(struct ipa_wan_msg)); + + memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[ + ipa3_ctx->num_ipa_cne_evt_req].msg_meta, + &msg_meta, + sizeof(struct ipa_msg_meta)); + + ipa3_ctx->num_ipa_cne_evt_req++; + ipa3_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE; + mutex_unlock(&ipa3_ctx->ipa_cne_evt_lock); + } + return 0; } @@ -1646,21 +1666,21 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD: - retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD); + retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true); if (retval) { IPAERR("ipa3_send_wan_msg failed: %d\n", retval); break; } break; case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL: - retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL); + retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true); if (retval) { IPAERR("ipa3_send_wan_msg failed: %d\n", retval); break; } break; case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED: - retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT); + retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT, false); if (retval) { IPAERR("ipa3_send_wan_msg failed: %d\n", retval); break; @@ -5111,6 +5131,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, mutex_init(&ipa3_ctx->lock); mutex_init(&ipa3_ctx->nat_mem.lock); mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex); + mutex_init(&ipa3_ctx->ipa_cne_evt_lock); idr_init(&ipa3_ctx->ipa_idr); spin_lock_init(&ipa3_ctx->idr_lock); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index be8d5702618b..a67fe34153bc 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -66,6 +66,7 @@ #define IPA_IPC_LOG_PAGES 50 #define IPA_PM_THRESHOLD_MAX 2 +#define IPA_MAX_NUM_REQ_CACHE 10 #define IPADBG(fmt, args...) \ do { \ @@ -1106,6 +1107,11 @@ struct ipa_dma_task_info { struct ipahal_imm_cmd_pyld *cmd_pyld; }; +struct ipa_cne_evt { + struct ipa_wan_msg wan_msg; + struct ipa_msg_meta msg_meta; +}; + /** * struct ipa3_context - IPA context * @class: pointer to the struct class @@ -1333,6 +1339,9 @@ struct ipa3_context { u32 ipa_tz_unlock_reg_num; struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg; struct ipa_dma_task_info dma_task_info; + struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE]; + int num_ipa_cne_evt_req; + struct mutex ipa_cne_evt_lock; }; /** -- GitLab From 006a5f61d69ce8a071b2ddbd23867b5ee9a832c5 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Tue, 3 Oct 2017 13:10:05 +0530 Subject: [PATCH 1722/5498] msm: ipa: Fix use after free issue Added code changes to avoid use-after-free by having local copy and cache it upon successful return. Change-Id: Iffac9ba89658b986bd8b630d22af619300e0ff5d Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa.c | 7 +++++-- drivers/platform/msm/ipa/ipa_v3/ipa.c | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index 01d95e8da12f..3e20edf520e0 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -538,6 +538,7 @@ static int ipa_send_wan_msg(unsigned long usr_param, int retval; struct ipa_wan_msg *wan_msg; struct ipa_msg_meta msg_meta; + struct ipa_wan_msg cache_wan_msg; wan_msg = kzalloc(sizeof(struct ipa_wan_msg), GFP_KERNEL); if (!wan_msg) { @@ -551,6 +552,8 @@ static int ipa_send_wan_msg(unsigned long usr_param, return -EFAULT; } + memcpy(&cache_wan_msg, wan_msg, sizeof(cache_wan_msg)); + memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); msg_meta.msg_type = msg_type; msg_meta.msg_len = sizeof(struct ipa_wan_msg); @@ -567,8 +570,8 @@ static int ipa_send_wan_msg(unsigned long usr_param, /* cache the cne event */ memcpy(&ipa_ctx->ipa_cne_evt_req_cache[ ipa_ctx->num_ipa_cne_evt_req].wan_msg, - wan_msg, - sizeof(struct ipa_wan_msg)); + &cache_wan_msg, + sizeof(cache_wan_msg)); memcpy(&ipa_ctx->ipa_cne_evt_req_cache[ ipa_ctx->num_ipa_cne_evt_req].msg_meta, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index d89c42d19675..8505f73bf7bd 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -604,6 +604,7 @@ static int ipa3_send_wan_msg(unsigned long usr_param, int retval; struct ipa_wan_msg *wan_msg; struct ipa_msg_meta msg_meta; + struct ipa_wan_msg cache_wan_msg; wan_msg = kzalloc(sizeof(struct ipa_wan_msg), GFP_KERNEL); if (!wan_msg) { @@ -617,6 +618,8 @@ static int ipa3_send_wan_msg(unsigned long usr_param, return -EFAULT; } + memcpy(&cache_wan_msg, wan_msg, sizeof(cache_wan_msg)); + memset(&msg_meta, 0, sizeof(struct ipa_msg_meta)); msg_meta.msg_type = msg_type; msg_meta.msg_len = sizeof(struct ipa_wan_msg); @@ -633,8 +636,8 @@ static int ipa3_send_wan_msg(unsigned long usr_param, /* cache the cne event */ memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[ ipa3_ctx->num_ipa_cne_evt_req].wan_msg, - wan_msg, - sizeof(struct ipa_wan_msg)); + &cache_wan_msg, + sizeof(cache_wan_msg)); memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[ ipa3_ctx->num_ipa_cne_evt_req].msg_meta, -- GitLab From 3a6b562f6bf546074279fc7739a278c42420182b Mon Sep 17 00:00:00 2001 From: Sayali Lokhande Date: Fri, 17 Nov 2017 10:27:18 +0530 Subject: [PATCH 1723/5498] scsi: ufs: Fix off-by-one bug in ufs debugfs driver When getting string from userspace by simple_write_to_buffer in ufs_qcom_dbg_testbus_cfg_write() function, null byte may be written out of bounds of configuration buffer if return value is same as size of buffer, causing off-by-one bug. This change passes correct available size of configuration buffer to simple_write_to_buffer function. Change-Id: If6085e806aa34622a9a57b02f563869c51011d2f Signed-off-by: Sayali Lokhande --- drivers/scsi/ufs/ufs-qcom-debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c index 2d605668682a..6ad9eca0e11d 100644 --- a/drivers/scsi/ufs/ufs-qcom-debugfs.c +++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c @@ -114,7 +114,8 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file, struct ufs_hba *hba = host->hba; - ret = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, + ret = simple_write_to_buffer(configuration, + TESTBUS_CFG_BUFF_LINE_SIZE - 1, &buff_pos, ubuf, cnt); if (ret < 0) { dev_err(host->hba->dev, "%s: failed to read user data\n", -- GitLab From 1f5e19efb8c872f581292cb493ba452c78c586fb Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Sirasanagandla Date: Thu, 23 Nov 2017 14:20:01 +0530 Subject: [PATCH 1724/5498] msm: wlan: Update regulatory database Remove 160 MHz band-width support for VN as per master sheet. CRs-Fixed: 2145374 Change-Id: Id0cda10bf3343bccda5eea08ae5369754bb40edb Signed-off-by: Rajeev Kumar Sirasanagandla --- net/wireless/db.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/wireless/db.txt b/net/wireless/db.txt index e423dd9e931c..9675fbd3a17d 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -1438,9 +1438,9 @@ country VI: DFS-FCC country VN: DFS-FCC (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (5170 - 5250 @ 80), (24) + (5250 - 5330 @ 80), (24), DFS + (5490 - 5730 @ 80), (24), DFS (5735 - 5835 @ 80), (30) country VU: DFS-FCC -- GitLab From 4a969a5ae7d92536ffb16ebc1e7282944a38ff2f Mon Sep 17 00:00:00 2001 From: Jishnu Prakash Date: Tue, 14 Nov 2017 18:59:34 +0530 Subject: [PATCH 1725/5498] msm: sps: Update debug message format specifier Restrict printing of kernel virtual addresses in SPS driver. In debug code, %p is used to print virtual addresses of kernel objects, which can be exploited by attackers. It is replaced with %pK, which hides these values if kptr_restrict is set (default on Android). Change-Id: I57585fa655abc01b2e8d694c8f31b7617bbf4ec7 Signed-off-by: Jishnu Prakash --- drivers/platform/msm/sps/bam.c | 110 ++++++++++++++--------------- drivers/platform/msm/sps/sps.c | 4 +- drivers/platform/msm/sps/sps_bam.c | 8 +-- drivers/platform/msm/sps/sps_mem.c | 5 +- drivers/platform/msm/sps/sps_rm.c | 7 +- 5 files changed, 67 insertions(+), 67 deletions(-) diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c index c94536398dac..8eb29a3fc66d 100644 --- a/drivers/platform/msm/sps/bam.c +++ b/drivers/platform/msm/sps/bam.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -707,7 +707,7 @@ static inline u32 bam_get_register_offset(void *base, enum bam_regs reg, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } @@ -756,7 +756,7 @@ static inline u32 bam_read_reg(void *base, enum bam_regs reg, u32 param) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } @@ -767,7 +767,7 @@ static inline u32 bam_read_reg(void *base, enum bam_regs reg, u32 param) return offset; } val = ioread32(dev->base + offset); - SPS_DBG(dev, "sps:bam 0x%p(va) offset 0x%x reg 0x%x r_val 0x%x.\n", + SPS_DBG(dev, "sps:bam 0x%pK(va) offset 0x%x reg 0x%x r_val 0x%x.\n", dev->base, offset, reg, val); return val; } @@ -788,7 +788,7 @@ static inline u32 bam_read_reg_field(void *base, enum bam_regs reg, u32 param, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } @@ -802,7 +802,7 @@ static inline u32 bam_read_reg_field(void *base, enum bam_regs reg, u32 param, val = ioread32(dev->base + offset); val &= mask; /* clear other bits */ val >>= shift; - SPS_DBG(dev, "sps:bam 0x%p(va) read reg 0x%x mask 0x%x r_val 0x%x.\n", + SPS_DBG(dev, "sps:bam 0x%pK(va) read reg 0x%x mask 0x%x r_val 0x%x.\n", dev->base, offset, mask, val); return val; } @@ -823,7 +823,7 @@ static inline void bam_write_reg(void *base, enum bam_regs reg, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } @@ -834,7 +834,7 @@ static inline void bam_write_reg(void *base, enum bam_regs reg, return; } iowrite32(val, dev->base + offset); - SPS_DBG(dev, "sps:bam 0x%p(va) write reg 0x%x w_val 0x%x.\n", + SPS_DBG(dev, "sps:bam 0x%pK(va) write reg 0x%x w_val 0x%x.\n", dev->base, offset, val); } @@ -854,7 +854,7 @@ static inline void bam_write_reg_field(void *base, enum bam_regs reg, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } @@ -870,7 +870,7 @@ static inline void bam_write_reg_field(void *base, enum bam_regs reg, tmp &= ~mask; /* clear written bits */ val = tmp | (val << shift); iowrite32(val, dev->base + offset); - SPS_DBG(dev, "sps:bam 0x%p(va) write reg 0x%x w_val 0x%x.\n", + SPS_DBG(dev, "sps:bam 0x%pK(va) write reg 0x%x w_val 0x%x.\n", dev->base, offset, val); } @@ -888,28 +888,28 @@ int bam_init(void *base, u32 ee, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } - SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).ee=%d.", __func__, + SPS_DBG3(dev, "sps:%s:bam=%pa 0x%pK(va).ee=%d.", __func__, BAM_ID(dev), dev->base, ee); ver = bam_read_reg_field(base, REVISION, 0, BAM_REVISION); if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) { - SPS_ERR(dev, "sps:bam 0x%p(va) Invalid BAM REVISION 0x%x.\n", + SPS_ERR(dev, "sps:bam 0x%pK(va) Invalid BAM REVISION 0x%x.\n", dev->base, ver); return -ENODEV; } else - SPS_DBG(dev, "sps:REVISION of BAM 0x%p is 0x%x.\n", + SPS_DBG(dev, "sps:REVISION of BAM 0x%pK is 0x%x.\n", dev->base, ver); if (summing_threshold == 0) { summing_threshold = 4; SPS_ERR(dev, - "sps:bam 0x%p(va) summing_threshold is zero,use default 4.\n", + "sps:bam 0x%pK(va) summing_threshold is zero,use default 4.\n", dev->base); } @@ -1009,12 +1009,12 @@ int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } - SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).", __func__, + SPS_DBG3(dev, "sps:%s:bam=%pa 0x%pK(va).", __func__, BAM_ID(dev), dev->base); /* @@ -1025,14 +1025,14 @@ int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask) num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); if (version < 3 || version > 0x1F) { SPS_ERR(dev, - "sps:bam 0x%p(va) security is not supported for this BAM version 0x%x.\n", + "sps:bam 0x%pK(va) security is not supported for this BAM version 0x%x.\n", dev->base, version); return -ENODEV; } if (num_pipes > BAM_MAX_PIPES) { SPS_ERR(dev, - "sps:bam 0x%p(va) the number of pipes is more than the maximum number allowed.\n", + "sps:bam 0x%pK(va) the number of pipes is more than the maximum number allowed.\n", dev->base); return -ENODEV; } @@ -1080,12 +1080,12 @@ int bam_check(void *base, u32 *version, u32 ee, u32 *num_pipes) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } - SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).", + SPS_DBG3(dev, "sps:%s:bam=%pa 0x%pK(va).", __func__, BAM_ID(dev), dev->base); if (!enhd_pipe) @@ -1094,7 +1094,7 @@ int bam_check(void *base, u32 *version, u32 ee, u32 *num_pipes) enabled = bam_get_pipe_attr(base, ee, true); if (!enabled) { - SPS_ERR(dev, "sps:%s:bam 0x%p(va) is not enabled.\n", + SPS_ERR(dev, "sps:%s:bam 0x%pK(va) is not enabled.\n", __func__, dev->base); return -ENODEV; } @@ -1110,7 +1110,7 @@ int bam_check(void *base, u32 *version, u32 ee, u32 *num_pipes) /* Check BAM version */ if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) { - SPS_ERR(dev, "sps:%s:bam 0x%p(va) Invalid BAM version 0x%x.\n", + SPS_ERR(dev, "sps:%s:bam 0x%pK(va) Invalid BAM version 0x%x.\n", __func__, dev->base, ver); return -ENODEV; } @@ -1127,11 +1127,11 @@ void bam_exit(void *base, u32 ee) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } - SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).ee=%d.", + SPS_DBG3(dev, "sps:%s:bam=%pa 0x%pK(va).ee=%d.", __func__, BAM_ID(dev), dev->base, ee); bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, BAM_IRQ, 0); @@ -1155,7 +1155,7 @@ void bam_output_register_content(void *base, u32 ee) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } @@ -1166,7 +1166,7 @@ void bam_output_register_content(void *base, u32 ee) num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); - SPS_INFO(dev, "sps:bam %pa 0x%p(va) has %d pipes.", + SPS_INFO(dev, "sps:bam %pa 0x%pK(va) has %d pipes.", BAM_ID(dev), dev->base, num_pipes); pipe_attr = enhd_pipe ? @@ -1193,7 +1193,7 @@ u32 bam_check_irq_source(void *base, u32 ee, u32 mask, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } @@ -1205,13 +1205,13 @@ u32 bam_check_irq_source(void *base, u32 ee, u32 mask, status = bam_read_reg(base, IRQ_STTS, 0); if (status & IRQ_STTS_BAM_ERROR_IRQ) { - SPS_ERR(dev, "sps:bam %pa 0x%p(va);bam irq status=" + SPS_ERR(dev, "sps:bam %pa 0x%pK(va);bam irq status=" "0x%x.\nsps: BAM_ERROR_IRQ\n", BAM_ID(dev), dev->base, status); bam_output_register_content(base, ee); *cb_case = SPS_CALLBACK_BAM_ERROR_IRQ; } else if (status & IRQ_STTS_BAM_HRESP_ERR_IRQ) { - SPS_ERR(dev, "sps:bam %pa 0x%p(va);bam irq status=" + SPS_ERR(dev, "sps:bam %pa 0x%pK(va);bam irq status=" "0x%x.\nsps: BAM_HRESP_ERR_IRQ\n", BAM_ID(dev), dev->base, status); bam_output_register_content(base, ee); @@ -1219,13 +1219,13 @@ u32 bam_check_irq_source(void *base, u32 ee, u32 mask, #ifdef CONFIG_SPS_SUPPORT_NDP_BAM } else if (status & IRQ_STTS_BAM_TIMER_IRQ) { SPS_DBG1(dev, - "sps:bam 0x%p(va);receive BAM_TIMER_IRQ\n", + "sps:bam 0x%pK(va);receive BAM_TIMER_IRQ\n", dev->base); *cb_case = SPS_CALLBACK_BAM_TIMER_IRQ; #endif } else SPS_INFO(dev, - "sps:bam %pa 0x%p(va);bam irq status=0x%x.\n", + "sps:bam %pa 0x%pK(va);bam irq status=0x%x.\n", BAM_ID(dev), dev->base, status); bam_write_reg(base, IRQ_CLR, 0, status); @@ -1243,11 +1243,11 @@ void bam_pipe_reset(void *base, u32 pipe) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } - SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.", + SPS_DBG2(dev, "sps:%s:bam=%pa 0x%pK(va).pipe=%d.", __func__, BAM_ID(dev), dev->base, pipe); bam_write_reg(base, P_RST, pipe, 1); @@ -1264,11 +1264,11 @@ void bam_disable_pipe(void *base, u32 pipe) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } - SPS_DBG2(dev, "sps:%s:bam=0x%p(va).pipe=%d.", __func__, base, pipe); + SPS_DBG2(dev, "sps:%s:bam=0x%pK(va).pipe=%d.", __func__, base, pipe); bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0); wmb(); /* ensure pipe is disabled */ } @@ -1281,20 +1281,20 @@ bool bam_pipe_check_zlt(void *base, u32 pipe) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return false; } if (bam_read_reg_field(base, P_HALT, pipe, P_HALT_P_LAST_DESC_ZLT)) { SPS_DBG(dev, - "sps:%s:bam=0x%p(va).pipe=%d: the last desc is ZLT.", + "sps:%s:bam=0x%pK(va).pipe=%d: the last desc is ZLT.", __func__, base, pipe); return true; } SPS_DBG(dev, - "sps:%s:bam=0x%p(va).pipe=%d: the last desc is not ZLT.", + "sps:%s:bam=0x%pK(va).pipe=%d: the last desc is not ZLT.", __func__, base, pipe); return false; } @@ -1307,20 +1307,20 @@ bool bam_pipe_check_pipe_empty(void *base, u32 pipe) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return false; } if (bam_read_reg_field(base, P_HALT, pipe, P_HALT_P_PIPE_EMPTY)) { SPS_DBG(dev, - "sps:%s:bam=0x%p(va).pipe=%d: desc FIFO is empty.", + "sps:%s:bam=0x%pK(va).pipe=%d: desc FIFO is empty.", __func__, base, pipe); return true; } SPS_DBG(dev, - "sps:%s:bam=0x%p(va).pipe=%d: desc FIFO is not empty.", + "sps:%s:bam=0x%pK(va).pipe=%d: desc FIFO is not empty.", __func__, base, pipe); return false; } @@ -1334,11 +1334,11 @@ int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return SPS_ERROR; } - SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.", + SPS_DBG2(dev, "sps:%s:bam=%pa 0x%pK(va).pipe=%d.", __func__, BAM_ID(dev), dev->base, pipe); /* Reset the BAM pipe */ @@ -1372,7 +1372,7 @@ int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param, bam_write_reg_field(base, P_CTRL, pipe, P_LOCK_GROUP, param->lock_group); - SPS_DBG(dev, "sps:bam=0x%p(va).pipe=%d.lock_group=%d.\n", + SPS_DBG(dev, "sps:bam=0x%pK(va).pipe=%d.lock_group=%d.\n", dev->base, pipe, param->lock_group); #endif @@ -1388,7 +1388,7 @@ int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param, bam_write_reg(base, P_EVNT_DEST_ADDR, pipe, peer_dest_addr); - SPS_DBG2(dev, "sps:bam=0x%p(va).pipe=%d.peer_bam=0x%x." + SPS_DBG2(dev, "sps:bam=0x%pK(va).pipe=%d.peer_bam=0x%x." "peer_pipe=%d.\n", dev->base, pipe, (u32) param->peer_phys_addr, @@ -1424,11 +1424,11 @@ void bam_pipe_exit(void *base, u32 pipe, u32 ee) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } - SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.", + SPS_DBG2(dev, "sps:%s:bam=%pa 0x%pK(va).pipe=%d.", __func__, BAM_ID(dev), dev->base, pipe); bam_write_reg(base, P_IRQ_EN, pipe, 0); @@ -1449,15 +1449,15 @@ void bam_pipe_enable(void *base, u32 pipe) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } - SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.", + SPS_DBG2(dev, "sps:%s:bam=%pa 0x%pK(va).pipe=%d.", __func__, BAM_ID(dev), dev->base, pipe); if (bam_read_reg_field(base, P_CTRL, pipe, P_EN)) - SPS_DBG2(dev, "sps:bam=0x%p(va).pipe=%d is already enabled.\n", + SPS_DBG2(dev, "sps:bam=0x%pK(va).pipe=%d is already enabled.\n", dev->base, pipe); else bam_write_reg_field(base, P_CTRL, pipe, P_EN, 1); @@ -1472,11 +1472,11 @@ void bam_pipe_disable(void *base, u32 pipe) struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } - SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.", + SPS_DBG2(dev, "sps:%s:bam=%pa 0x%pK(va).pipe=%d.", __func__, BAM_ID(dev), dev->base, pipe); bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0); @@ -1501,12 +1501,12 @@ void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en, struct sps_bam *dev = to_sps_bam_dev(base); if ((dev == NULL) || (&dev->base != base)) { - SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n", + SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%pK\n", __func__, base); return; } SPS_DBG2(dev, - "sps:%s:bam=%pa 0x%p(va).pipe=%d; irq_en:%d; src_mask:0x%x; ee:%d.\n", + "sps:%s:bam=%pa 0x%pK(va).pipe=%d; irq_en:%d; src_mask:0x%x; ee:%d.\n", __func__, BAM_ID(dev), dev->base, pipe, irq_en, src_mask, ee); if (src_mask & BAM_PIPE_IRQ_RST_ERROR) { diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c index c90bee671178..03cc14aa7e29 100644 --- a/drivers/platform/msm/sps/sps.c +++ b/drivers/platform/msm/sps/sps.c @@ -931,7 +931,7 @@ static int sps_device_init(void) goto exit_err; } - SPS_DBG3(sps, "sps:bamdma_bam.phys=%pa.virt=0x%p.", + SPS_DBG3(sps, "sps:bamdma_bam.phys=%pa.virt=0x%pK.", &bamdma_props.phys_addr, bamdma_props.virt_addr); @@ -946,7 +946,7 @@ static int sps_device_init(void) goto exit_err; } - SPS_DBG3(sps, "sps:bamdma_dma.phys=%pa.virt=0x%p.", + SPS_DBG3(sps, "sps:bamdma_dma.phys=%pa.virt=0x%pK.", &bamdma_props.periph_phys_addr, bamdma_props.periph_virt_addr); diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c index b337d47c90aa..908203b3fb64 100644 --- a/drivers/platform/msm/sps/sps_bam.c +++ b/drivers/platform/msm/sps/sps_bam.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -508,12 +508,12 @@ int sps_bam_enable(struct sps_bam *dev) if (dev->props.logging_number > 0) dev->props.logging_number--; SPS_INFO(dev, - "sps:BAM %pa (va:0x%p) enabled: ver:0x%x, number of pipes:%d\n", + "sps:BAM %pa (va:0x%pK) enabled: ver:0x%x, number of pipes:%d\n", BAM_ID(dev), dev->base, dev->version, dev->props.num_pipes); } else SPS_DBG3(dev, - "sps:BAM %pa (va:0x%p) enabled: ver:0x%x, number of pipes:%d\n", + "sps:BAM %pa (va:0x%pK) enabled: ver:0x%x, number of pipes:%d\n", BAM_ID(dev), dev->base, dev->version, dev->props.num_pipes); @@ -2134,7 +2134,7 @@ int sps_bam_pipe_get_event(struct sps_bam *dev, if (pipe->sys.no_queue) { SPS_ERR(dev, - "sps:Invalid connection for event: BAM %pa pipe %d context 0x%p\n", + "sps:Invalid connection for event: BAM %pa pipe %d context 0x%pK\n", BAM_ID(dev), pipe_index, pipe); notify->event_id = SPS_EVENT_INVALID; return SPS_ERROR; diff --git a/drivers/platform/msm/sps/sps_mem.c b/drivers/platform/msm/sps/sps_mem.c index becb9a15882a..db36e64f96d8 100644 --- a/drivers/platform/msm/sps/sps_mem.c +++ b/drivers/platform/msm/sps/sps_mem.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2011-2013, 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2013, 2015, 2017, The Linux Foundation. + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -129,7 +130,7 @@ int sps_mem_init(phys_addr_t pipemem_phys_base, u32 pipemem_size) iomem_offset = 0; SPS_DBG(sps, - "sps:sps_mem_init.iomem_phys=%pa,iomem_virt=0x%p.", + "sps:sps_mem_init.iomem_phys=%pa,iomem_virt=0x%pK.", &iomem_phys, iomem_virt); } diff --git a/drivers/platform/msm/sps/sps_rm.c b/drivers/platform/msm/sps/sps_rm.c index ec64e6c25465..db5db28d5f9e 100644 --- a/drivers/platform/msm/sps/sps_rm.c +++ b/drivers/platform/msm/sps/sps_rm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -723,8 +723,7 @@ int sps_rm_state_change(struct sps_pipe *pipe, u32 state) state == SPS_STATE_ALLOCATE) { if (sps_rm_alloc(pipe)) { SPS_ERR(pipe->bam, - "sps:Fail to allocate resource for" - " BAM 0x%p pipe %d.\n", + "sps:Fail to allocate resource for BAM 0x%pK pipe %d.\n", pipe->bam, pipe->pipe_index); return SPS_ERROR; } @@ -745,7 +744,7 @@ int sps_rm_state_change(struct sps_pipe *pipe, u32 state) result = sps_bam_pipe_connect(pipe, ¶ms); if (result) { SPS_ERR(pipe->bam, - "sps:Failed to connect BAM 0x%p pipe %d", + "sps:Failed to connect BAM 0x%pK pipe %d", pipe->bam, pipe->pipe_index); return SPS_ERROR; } -- GitLab From 5e1c5c13b981afaedda55441253ce3e0d2776d2b Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Wed, 22 Nov 2017 19:40:38 +0530 Subject: [PATCH 1726/5498] msm: ipa3: Remove ep_delay on disconnect sequence When device in Low Power Mode, usb driver invokes suspend and then disconnect upon cable disconnection. Remove the ep_delay if it is set. Change-Id: I203d57d70e8c189a2f3bc7f7830392fac98cfbb1 Signed-off-by: Mohammed Javid --- .../platform/msm/ipa/ipa_clients/ipa_usb.c | 1 + drivers/platform/msm/ipa/ipa_v3/ipa_client.c | 42 +++++++++++++++++++ drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 2 + 3 files changed, 45 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index 6916e0375d71..0dc204472214 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -2220,6 +2220,7 @@ static int ipa_usb_xdci_dismiss_channels(u32 ul_clnt_hdl, u32 dl_clnt_hdl, } if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + ipa3_xdci_ep_delay_rm(ul_clnt_hdl); /* Remove ep_delay if set */ /* Reset UL channel */ result = ipa3_reset_gsi_channel(ul_clnt_hdl); if (result) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index d8ed2aef802b..01b020013c71 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1768,6 +1768,48 @@ exit: return result; } +void ipa3_xdci_ep_delay_rm(u32 clnt_hdl) +{ + struct ipa3_ep_context *ep; + struct ipa_ep_cfg_ctrl ep_cfg_ctrl; + int result; + + if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || + ipa3_ctx->ep[clnt_hdl].valid == 0) { + IPAERR("bad parm.\n"); + return; + } + + ep = &ipa3_ctx->ep[clnt_hdl]; + + if (ep->ep_delay_set == true) { + + memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); + ep_cfg_ctrl.ipa_ep_delay = false; + + if (!ep->keep_ipa_awake) + IPA_ACTIVE_CLIENTS_INC_EP + (ipa3_get_client_mapping(clnt_hdl)); + + result = ipa3_cfg_ep_ctrl(clnt_hdl, + &ep_cfg_ctrl); + + if (!ep->keep_ipa_awake) + IPA_ACTIVE_CLIENTS_DEC_EP + (ipa3_get_client_mapping(clnt_hdl)); + + if (result) { + IPAERR + ("client (ep: %d) failed to remove delay result=%d\n", + clnt_hdl, result); + } else { + IPADBG("client (ep: %d) delay removed\n", + clnt_hdl); + ep->ep_delay_set = false; + } + } +} + int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id) { struct ipa3_ep_context *ep; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index be8d5702618b..d4cb30c30f8d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1513,6 +1513,8 @@ int ipa3_xdci_connect(u32 clnt_hdl); int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id); +void ipa3_xdci_ep_delay_rm(u32 clnt_hdl); + int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, bool should_force_clear, u32 qmi_req_id, bool is_dpl); -- GitLab From 9312910d87a20816d8a5ecc4bce3d11a6d48fa0a Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Wed, 22 Nov 2017 16:14:10 +0530 Subject: [PATCH 1727/5498] msm: ipa3: Fix to set QMB on USB ep Fix to config USB end point with QMB. Change-Id: I2d4691024aa4fc9856c9c4213491a76a1033ae04 Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v3/ipa_client.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index d8ed2aef802b..9d9816a05a2c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1209,6 +1209,22 @@ int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params, ep->priv = params->priv; ep->keep_ipa_awake = params->keep_ipa_awake; + + /* Config QMB for USB_CONS ep */ + if (!IPA_CLIENT_IS_PROD(ep->client)) { + IPADBG("Configuring QMB on USB CONS pipe\n"); + if (ipa_ep_idx >= ipa3_ctx->ipa_num_pipes || + ipa3_ctx->ep[ipa_ep_idx].valid == 0) { + IPAERR("bad parm.\n"); + return -EINVAL; + } + result = ipa3_cfg_ep_cfg(ipa_ep_idx, ¶ms->ipa_ep_cfg.cfg); + if (result) { + IPAERR("fail to configure QMB.\n"); + return result; + } + } + if (!ep->skip_ep_cfg) { if (ipa3_cfg_ep(ipa_ep_idx, ¶ms->ipa_ep_cfg)) { IPAERR("fail to configure EP.\n"); -- GitLab From 849650d838d22d8bdd6b388186beb2bd68de9e13 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Mon, 27 Nov 2017 09:24:33 -0800 Subject: [PATCH 1728/5498] UPSTREAM: android: binder: fix type mismatch warning Allowing binder to expose the 64-bit API on 32-bit kernels caused a build warning: drivers/android/binder.c: In function 'binder_transaction_buffer_release': drivers/android/binder.c:2220:15: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] fd_array = (u32 *)(parent_buffer + fda->parent_offset); ^ drivers/android/binder.c: In function 'binder_translate_fd_array': drivers/android/binder.c:2445:13: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] fd_array = (u32 *)(parent_buffer + fda->parent_offset); ^ drivers/android/binder.c: In function 'binder_fixup_parent': drivers/android/binder.c:2511:18: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] This adds extra type casts to avoid the warning. However, there is another problem with the Kconfig option: turning it on or off creates two incompatible ABI versions, a kernel that has this enabled cannot run user space that was built without it or vice versa. A better solution might be to leave the option hidden until the binder code is fixed to deal with both ABI versions. Fixes: e8d2ed7db7c3 ("Revert "staging: Fix build issues with new binder API"") Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 1c363eaece2752c5f8b1b874cb4ae435de06aa66) Change-Id: Id09185a6f86905926699e92a2b30201b8a5e83e5 --- drivers/staging/android/binder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 4e86db3449dc..0ed2fb01dc9e 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2464,7 +2464,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, debug_id, (u64)fda->num_fds); continue; } - fd_array = (u32 *)(parent_buffer + fda->parent_offset); + fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset); for (fd_index = 0; fd_index < fda->num_fds; fd_index++) task_close_fd(proc, fd_array[fd_index]); } break; @@ -2688,7 +2688,7 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, */ parent_buffer = parent->buffer - binder_alloc_get_user_buffer_offset(&target_proc->alloc); - fd_array = (u32 *)(parent_buffer + fda->parent_offset); + fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset); if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) { binder_user_error("%d:%d parent offset not aligned correctly.\n", proc->pid, thread->pid); @@ -2754,7 +2754,7 @@ static int binder_fixup_parent(struct binder_transaction *t, proc->pid, thread->pid); return -EINVAL; } - parent_buffer = (u8 *)(parent->buffer - + parent_buffer = (u8 *)((uintptr_t)parent->buffer - binder_alloc_get_user_buffer_offset( &target_proc->alloc)); *(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer; -- GitLab From ecf38009636131c04931972ec0348960bf315448 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 23 Sep 2017 17:02:18 +0800 Subject: [PATCH 1729/5498] f2fs: updates on 4.15-rc1 Pull f2fs updates from Jaegeuk Kim: "In this round, we introduce sysfile-based quota support which is required for Android by default. In addition, we allow that users are able to reserve some blocks in runtime to mitigate performance drops in low free space. Enhancements: - assign proper data segments according to write_hints given by user - issue cache_flush on dirty devices only among multiple devices - exploit cp_error flag and add more faults to enhance fault injection test - conduct more readaheads during f2fs_readdir - add a range for discard commands Bug fixes: - fix zero stat->st_blocks when inline_data is set - drop crypto key and free stale memory pointer while evict_inode is failing - fix some corner cases in free space and segment management - fix wrong last_disk_size This series includes lots of clean-ups and code enhancement in terms of xattr operations, discard/flush command control. In addition, it adds versatile debugfs entries to monitor f2fs status" Cherry-picked from origin/upstream-f2fs-stable-linux-3.18.y: c6f66cb76658 f2fs: deny accessing encryption policy if encryption is off f3c3ffad64ab f2fs: inject fault in inc_valid_node_count bc7ed7f3a112 f2fs: expose quota information in debugfs c269d14336de f2fs: separate nat entry mem alloc from nat_tree_lock 52c4320423a4 f2fs: validate before set/clear free nat bitmap 2007e708bd3b f2fs: avoid opened loop codes in __add_ino_entry 5799a6dd496d f2fs: apply write hints to select the type of segments for buffered write d62ae05c399b f2fs: introduce scan_curseg_cache for cleanup 59b905175501 f2fs: optimize the way of traversing free_nid_bitmap d3754d20ae32 f2fs: keep scanning until enough free nids are acquired 44131bb6ad9c f2fs: trace checkpoint reason in fsync() e1c850fe940f f2fs: keep isize once block is reserved cross EOF 9d154a40d815 f2fs: avoid race in between GC and block exchange 1dd480acf660 f2fs: save a multiplication for last_nid calculation 5bef5b29e71b f2fs: fix summary info corruption a1cc64f1bc22 f2fs: remove dead code in update_meta_page d86513f6f0bb f2fs: remove unneeded semicolon 24f6fa056daf f2fs: don't bother with inode->i_version c86b0e230e5e f2fs: check curseg space before foreground GC 4fd29af3ad09 f2fs: use rw_semaphore to protect SIT cache 722458dbae06 f2fs: support quota sys files e5617e7a943a f2fs: add quota_ino feature infra 468c609578d7 f2fs: optimize __update_nat_bits 51349d888f43 f2fs: modify for accurate fggc node io stat 0e42a91594b5 Revert "f2fs: handle dirty segments inside refresh_sit_entry" 71b405279096 f2fs: add a function to move nid 8f5dcea4021d f2fs: export SSR allocation threshold 1ead0d06fe72 f2fs: give correct trimmed blocks in fstrim be5d050a42f5 f2fs: support bio allocation error injection 710adf1ce8a5 f2fs: support get_page error injection a0daae0960b2 f2fs: add missing sysfs description 2606cff9a1c6 f2fs: support soft block reservation 30d0b7c1450f f2fs: handle error case when adding xattr entry e254d1a66b68 f2fs: support flexible inline xattr size eb25e774fe31 f2fs: show current cp state bf85becc2151 f2fs: add missing quota_initialize c3290d767cad f2fs: show # of dirty segments via sysfs 02fe062bf928 f2fs: stop all the operations by cp_error flag d2d066913df6 f2fs: remove several redundant assignments 04e41a9f0d70 f2fs: avoid using timespec 8be4d9e57304 f2fs: fix to correct no_fggc_candidate ec8a71acb689 Revert "f2fs: return wrong error number on f2fs_quota_write" c68705b831a2 f2fs: remove obsolete pointer for truncate_xattr_node fee61cecc494 f2fs: retry ENOMEM for quota_read|write 585398f7f9a6 f2fs: limit # of inmemory pages e0ac8d0ab0a2 f2fs: update ctx->pos correctly when hitting hole in directory f6e29eded5b2 f2fs: relocate readahead codes in readdir() d88ba788df66 f2fs: allow readdir() to be interrupted 710c0b07dfb6 f2fs: trace f2fs_readdir 6ff204b6b1fd f2fs: trace f2fs_lookup 0c33cb268bc8 f2fs: skip searching non-exist range in truncate_hole 1aead7051ab8 f2fs: expose some sectors to user in inline data or dentry case b9da516ce0f1 f2fs: avoid stale fi->gdirty_list pointer 68a76f162cfd f2fs/crypto: drop crypto key at evict_inode only 93bb7f1d7c61 f2fs: fix to avoid race when accessing last_disk_size cc71a4a9aae0 f2fs: Fix bool initialization/comparison 549f9f84502f f2fs: give up CP_TRIMMED_FLAG if it drops discards 87905eb8b066 f2fs: trace f2fs_remove_discard b791197f673a f2fs: reduce cmd_lock coverage in __issue_discard_cmd d156194cf9ce f2fs: split discard policy 4b92a1f449d6 f2fs: wrap discard policy 0f5a13682d26 f2fs: support issuing/waiting discard in range 9c9d6ab93053 f2fs: fix to flush multiple device in checkpoint 98e85b9050db f2fs: enhance multiple device flush 97acaf716d65 f2fs: fix to show ino management cache size correctly 9e0893947a57 f2fs: drop FI_UPDATE_WRITE tag after f2fs_issue_flush b5b73718f0b3 f2fs: obsolete ALLOC_NID_LIST list 33646c982202 f2fs: convert inline data for direct I/O & FI_NO_PREALLOC 8e68de1c1db0 f2fs: allow readpages with NULL file pointer 5c1821a849ea f2fs: show flush list status in sysfs ca433f07cc32 f2fs: introduce read_xattr_block e002c8118ab9 f2fs: introduce read_inline_xattr 9944032aa373 Revert "f2fs: reuse nids more aggressively" bc58a36645c1 Revert "f2fs: node segment is prior to data segment selected victim" Change-Id: I67cdc08f7bfca2da8b434fec66a779705c0ed5b2 Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 37 +- fs/f2fs/acl.c | 3 + fs/f2fs/checkpoint.c | 64 ++- fs/f2fs/data.c | 38 +- fs/f2fs/debug.c | 31 +- fs/f2fs/dir.c | 32 +- fs/f2fs/f2fs.h | 223 +++++++--- fs/f2fs/file.c | 119 ++++-- fs/f2fs/gc.c | 37 +- fs/f2fs/inline.c | 1 + fs/f2fs/inode.c | 26 +- fs/f2fs/namei.c | 95 ++++- fs/f2fs/node.c | 417 ++++++++++--------- fs/f2fs/node.h | 16 +- fs/f2fs/recovery.c | 8 +- fs/f2fs/segment.c | 514 ++++++++++++++++++------ fs/f2fs/segment.h | 39 +- fs/f2fs/shrinker.c | 2 +- fs/f2fs/super.c | 258 ++++++++++-- fs/f2fs/sysfs.c | 53 ++- fs/f2fs/xattr.c | 172 ++++---- include/linux/f2fs_fs.h | 10 +- include/trace/events/f2fs.h | 116 +++++- 23 files changed, 1682 insertions(+), 629 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index cecb93af8967..2baed1151eac 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -51,6 +51,18 @@ Description: Controls the dirty page count condition for the in-place-update policies. +What: /sys/fs/f2fs//min_hot_blocks +Date: March 2017 +Contact: "Jaegeuk Kim" +Description: + Controls the dirty page count condition for redefining hot data. + +What: /sys/fs/f2fs//min_ssr_sections +Date: October 2017 +Contact: "Chao Yu" +Description: + Controls the fee section threshold to trigger SSR allocation. + What: /sys/fs/f2fs//max_small_discards Date: November 2013 Contact: "Jaegeuk Kim" @@ -102,6 +114,12 @@ Contact: "Jaegeuk Kim" Description: Controls the idle timing. +What: /sys/fs/f2fs//iostat_enable +Date: August 2017 +Contact: "Chao Yu" +Description: + Controls to enable/disable IO stat. + What: /sys/fs/f2fs//ra_nid_pages Date: October 2015 Contact: "Chao Yu" @@ -122,6 +140,12 @@ Contact: "Shuoran Liu" Description: Shows total written kbytes issued to disk. +What: /sys/fs/f2fs//feature +Date: July 2017 +Contact: "Jaegeuk Kim" +Description: + Shows all enabled features in current device. + What: /sys/fs/f2fs//inject_rate Date: May 2016 Contact: "Sheng Yong" @@ -138,7 +162,18 @@ What: /sys/fs/f2fs//reserved_blocks Date: June 2017 Contact: "Chao Yu" Description: - Controls current reserved blocks in system. + Controls target reserved blocks in system, the threshold + is soft, it could exceed current available user space. + +What: /sys/fs/f2fs//current_reserved_blocks +Date: October 2017 +Contact: "Yunlong Song" +Contact: "Chao Yu" +Description: + Shows current reserved blocks in system, it may be temporarily + smaller than target_reserved_blocks, but will gradually + increase to target_reserved_blocks when more free blocks are + freed by user later. What: /sys/fs/f2fs//gc_urgent Date: August 2017 diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 436b3a1464d9..2bb7c9fc5144 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -250,6 +250,9 @@ static int __f2fs_set_acl(struct inode *inode, int type, int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + return __f2fs_set_acl(inode, type, acl, NULL); } diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 9e8e24f679bb..288966b4c89d 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -29,7 +29,6 @@ struct kmem_cache *inode_entry_slab; void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io) { set_ckpt_flags(sbi, CP_ERROR_FLAG); - sbi->sb->s_flags |= MS_RDONLY; if (!end_io) f2fs_flush_merged_writes(sbi); } @@ -402,24 +401,23 @@ const struct address_space_operations f2fs_meta_aops = { #endif }; -static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) +static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type) { struct inode_management *im = &sbi->im[type]; struct ino_entry *e, *tmp; tmp = f2fs_kmem_cache_alloc(ino_entry_slab, GFP_NOFS); -retry: + radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); spin_lock(&im->ino_lock); e = radix_tree_lookup(&im->ino_root, ino); if (!e) { e = tmp; - if (radix_tree_insert(&im->ino_root, ino, e)) { - spin_unlock(&im->ino_lock); - radix_tree_preload_end(); - goto retry; - } + if (unlikely(radix_tree_insert(&im->ino_root, ino, e))) + f2fs_bug_on(sbi, 1); + memset(e, 0, sizeof(struct ino_entry)); e->ino = ino; @@ -427,6 +425,10 @@ retry: if (type != ORPHAN_INO) im->ino_num++; } + + if (type == FLUSH_INO) + f2fs_set_bit(devidx, (char *)&e->dirty_device); + spin_unlock(&im->ino_lock); radix_tree_preload_end(); @@ -455,7 +457,7 @@ static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) { /* add new dirty ino entry into list */ - __add_ino_entry(sbi, ino, type); + __add_ino_entry(sbi, ino, 0, type); } void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) @@ -481,7 +483,7 @@ void release_ino_entry(struct f2fs_sb_info *sbi, bool all) struct ino_entry *e, *tmp; int i; - for (i = all ? ORPHAN_INO: APPEND_INO; i <= UPDATE_INO; i++) { + for (i = all ? ORPHAN_INO : APPEND_INO; i < MAX_INO_ENTRY; i++) { struct inode_management *im = &sbi->im[i]; spin_lock(&im->ino_lock); @@ -495,6 +497,27 @@ void release_ino_entry(struct f2fs_sb_info *sbi, bool all) } } +void set_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type) +{ + __add_ino_entry(sbi, ino, devidx, type); +} + +bool is_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type) +{ + struct inode_management *im = &sbi->im[type]; + struct ino_entry *e; + bool is_dirty = false; + + spin_lock(&im->ino_lock); + e = radix_tree_lookup(&im->ino_root, ino); + if (e && f2fs_test_bit(devidx, (char *)&e->dirty_device)) + is_dirty = true; + spin_unlock(&im->ino_lock); + return is_dirty; +} + int acquire_orphan_inode(struct f2fs_sb_info *sbi) { struct inode_management *im = &sbi->im[ORPHAN_INO]; @@ -531,7 +554,7 @@ void release_orphan_inode(struct f2fs_sb_info *sbi) void add_orphan_inode(struct inode *inode) { /* add new orphan ino entry into list */ - __add_ino_entry(F2FS_I_SB(inode), inode->i_ino, ORPHAN_INO); + __add_ino_entry(F2FS_I_SB(inode), inode->i_ino, 0, ORPHAN_INO); update_inode_page(inode); } @@ -555,7 +578,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) return err; } - __add_ino_entry(sbi, ino, ORPHAN_INO); + __add_ino_entry(sbi, ino, 0, ORPHAN_INO); inode = f2fs_iget_retry(sbi->sb, ino); if (IS_ERR(inode)) { @@ -591,6 +614,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) block_t start_blk, orphan_blocks, i, j; unsigned int s_flags = sbi->sb->s_flags; int err = 0; +#ifdef CONFIG_QUOTA + int quota_enabled; +#endif if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) return 0; @@ -603,8 +629,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) #ifdef CONFIG_QUOTA /* Needed for iput() to work correctly and not trash data */ sbi->sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ - f2fs_enable_quota_files(sbi); + quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY); #endif start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); @@ -632,7 +659,8 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) out: #ifdef CONFIG_QUOTA /* Turn quotas off */ - f2fs_quota_off_umount(sbi->sb); + if (quota_enabled) + f2fs_quota_off_umount(sbi->sb); #endif sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ @@ -987,7 +1015,7 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi) update_inode_page(inode); iput(inode); } - }; + } return 0; } @@ -1147,6 +1175,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct super_block *sb = sbi->sb; struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE); u64 kbytes_written; + int err; /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) { @@ -1240,6 +1269,11 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (unlikely(f2fs_cp_error(sbi))) return -EIO; + /* flush all device cache */ + err = f2fs_flush_device_cache(sbi); + if (err) + return err; + /* write out checkpoint buffer at block 0 */ update_meta_page(sbi, ckpt, start_blk++); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 2da4cd318d63..8a26a069d3c9 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -173,7 +173,7 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, { struct bio *bio; - bio = f2fs_bio_alloc(npages); + bio = f2fs_bio_alloc(sbi, npages, true); f2fs_target_device(sbi, blk_addr, bio); bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; @@ -418,8 +418,8 @@ next: bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; - /* set submitted = 1 as a return value */ - fio->submitted = 1; + /* set submitted = true as a return value */ + fio->submitted = true; inc_page_count(sbi, WB_DATA_TYPE(bio_page)); @@ -473,7 +473,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, f2fs_wait_on_block_writeback(sbi, blkaddr); } - bio = bio_alloc(GFP_KERNEL, min_t(int, nr_pages, BIO_MAX_PAGES)); + bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false); if (!bio) { if (ctx) fscrypt_release_ctx(ctx); @@ -833,6 +833,13 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, struct f2fs_map_blocks map; int err = 0; + /* convert inline data for Direct I/O*/ + if (dio) { + err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } + map.m_lblk = F2FS_BLK_ALIGN(pos); map.m_len = F2FS_BYTES_TO_BLK(pos + count); if (map.m_len > map.m_lblk) @@ -842,15 +849,11 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_next_pgofs = NULL; - if (dio) { - err = f2fs_convert_inline_inode(inode); - if (err) - return err; + if (dio) return f2fs_map_blocks(inode, &map, 1, __force_buffered_io(inode, WRITE) ? F2FS_GET_BLOCK_PRE_AIO : F2FS_GET_BLOCK_PRE_DIO); - } if (pos + count > MAX_INLINE_DATA(inode)) { err = f2fs_convert_inline_inode(inode); if (err) @@ -1330,7 +1333,7 @@ static int f2fs_read_data_pages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { - struct inode *inode = file->f_mapping->host; + struct inode *inode = mapping->host; struct page *page = list_last_entry(pages, struct page, lru); trace_f2fs_readpages(inode, page, nr_pages); @@ -1491,6 +1494,7 @@ static int __write_data_page(struct page *page, bool *submitted, int err = 0; struct f2fs_io_info fio = { .sbi = sbi, + .ino = inode->i_ino, .type = DATA, .op = REQ_OP_WRITE, .op_flags = wbc_to_write_flags(wbc), @@ -1562,8 +1566,11 @@ write: err = do_write_data_page(&fio); } } + + down_write(&F2FS_I(inode)->i_sem); if (F2FS_I(inode)->last_disk_size < psize) F2FS_I(inode)->last_disk_size = psize; + up_write(&F2FS_I(inode)->i_sem); done: if (err && err != -ENOENT) @@ -1942,6 +1949,12 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, } trace_f2fs_write_begin(inode, pos, len, flags); + if (f2fs_is_atomic_file(inode) && + !available_free_memory(sbi, INMEM_PAGES)) { + err = -ENOMEM; + goto fail; + } + /* * We should check this at this moment to avoid deadlock on inode page * and #0 page. The locking rule for inline_data conversion should be: @@ -1957,7 +1970,8 @@ repeat: * Do not use grab_cache_page_write_begin() to avoid deadlock due to * wait_for_stable_page. Will wait that below with our IO control. */ - page = grab_cache_page(mapping, index); + page = f2fs_pagecache_get_page(mapping, index, + FGP_LOCK | FGP_WRITE | FGP_CREAT, GFP_NOFS); if (!page) { err = -ENOMEM; goto fail; @@ -2018,6 +2032,8 @@ repeat: fail: f2fs_put_page(page, 1); f2fs_write_failed(mapping, pos + len); + if (f2fs_is_atomic_file(inode)) + drop_inmem_pages_all(sbi); return err; } diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 629c57024106..36d6a7277924 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -45,9 +45,18 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA); + si->ndirty_qdata = get_pages(sbi, F2FS_DIRTY_QDATA); si->ndirty_imeta = get_pages(sbi, F2FS_DIRTY_IMETA); si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; + + si->nquota_files = 0; + if (f2fs_sb_has_quota_ino(sbi->sb)) { + for (i = 0; i < MAXQUOTAS; i++) { + if (f2fs_qf_ino(sbi->sb, i)) + si->nquota_files++; + } + } si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->aw_cnt = atomic_read(&sbi->aw_cnt); @@ -61,6 +70,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) atomic_read(&SM_I(sbi)->fcc_info->issued_flush); si->nr_flushing = atomic_read(&SM_I(sbi)->fcc_info->issing_flush); + si->flush_list_empty = + llist_empty(&SM_I(sbi)->fcc_info->issue_list); } if (SM_I(sbi) && SM_I(sbi)->dcc_info) { si->nr_discarded = @@ -96,9 +107,9 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->dirty_nats = NM_I(sbi)->dirty_nat_cnt; si->sits = MAIN_SEGS(sbi); si->dirty_sits = SIT_I(sbi)->dirty_sentries; - si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID_LIST]; + si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID]; si->avail_nids = NM_I(sbi)->available_nids; - si->alloc_nids = NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]; + si->alloc_nids = NM_I(sbi)->nid_cnt[PREALLOC_NID]; si->bg_gc = sbi->bg_gc; si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg) * 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg) @@ -231,14 +242,14 @@ get_cache: } /* free nids */ - si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID_LIST] + - NM_I(sbi)->nid_cnt[ALLOC_NID_LIST]) * + si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID] + + NM_I(sbi)->nid_cnt[PREALLOC_NID]) * sizeof(struct free_nid); si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry); si->cache_mem += NM_I(sbi)->dirty_nat_cnt * sizeof(struct nat_entry_set); si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); - for (i = 0; i <= ORPHAN_INO; i++) + for (i = 0; i < MAX_INO_ENTRY; i++) si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); si->cache_mem += atomic_read(&sbi->total_ext_tree) * sizeof(struct extent_tree); @@ -264,9 +275,10 @@ static int stat_show(struct seq_file *s, void *v) update_general_status(si->sbi); - seq_printf(s, "\n=====[ partition info(%s). #%d, %s]=====\n", + seq_printf(s, "\n=====[ partition info(%s). #%d, %s, CP: %s]=====\n", bdevname(si->sbi->sb->s_bdev, devname), i++, - f2fs_readonly(si->sbi->sb) ? "RO": "RW"); + f2fs_readonly(si->sbi->sb) ? "RO": "RW", + f2fs_cp_error(si->sbi) ? "Error": "Good"); seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ", si->sit_area_segs, si->nat_area_segs); seq_printf(s, "[SSA: %d] [MAIN: %d", @@ -351,10 +363,11 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n", si->ext_tree, si->zombie_tree, si->ext_node); seq_puts(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d), " + seq_printf(s, " - IO (CP: %4d, Data: %4d, Flush: (%4d %4d %4d), " "Discard: (%4d %4d)) cmd: %4d undiscard:%4u\n", si->nr_wb_cp_data, si->nr_wb_data, si->nr_flushing, si->nr_flushed, + si->flush_list_empty, si->nr_discarding, si->nr_discarded, si->nr_discard_cmd, si->undiscard_blks); seq_printf(s, " - inmem: %4d, atomic IO: %4d (Max. %4d), " @@ -367,6 +380,8 @@ static int stat_show(struct seq_file *s, void *v) si->ndirty_dent, si->ndirty_dirs, si->ndirty_all); seq_printf(s, " - datas: %4d in files:%4d\n", si->ndirty_data, si->ndirty_files); + seq_printf(s, " - quota datas: %4d in quota files:%4d\n", + si->ndirty_qdata, si->nquota_files); seq_printf(s, " - meta: %4d in %4d\n", si->ndirty_meta, si->meta_pages); seq_printf(s, " - imeta: %4d\n", diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 4f2a8fedb313..1955707b138b 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -10,10 +10,12 @@ */ #include #include +#include #include "f2fs.h" #include "node.h" #include "acl.h" #include "xattr.h" +#include static unsigned long dir_blocks(struct inode *inode) { @@ -847,6 +849,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) struct f2fs_dentry_block *dentry_blk = NULL; struct page *dentry_page = NULL; struct file_ra_state *ra = &file->f_ra; + loff_t start_pos = ctx->pos; unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); struct f2fs_dentry_ptr d; struct fscrypt_str fstr = FSTR_INIT(NULL, 0); @@ -855,24 +858,32 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) if (f2fs_encrypted_inode(inode)) { err = fscrypt_get_encryption_info(inode); if (err && err != -ENOKEY) - return err; + goto out; err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr); if (err < 0) - return err; + goto out; } if (f2fs_has_inline_dentry(inode)) { err = f2fs_read_inline_dir(file, ctx, &fstr); - goto out; + goto out_free; } - /* readahead for multi pages of dir */ - if (npages - n > 1 && !ra_has_index(ra, n)) - page_cache_sync_readahead(inode->i_mapping, ra, file, n, + for (; n < npages; n++, ctx->pos = n * NR_DENTRY_IN_BLOCK) { + + /* allow readdir() to be interrupted */ + if (fatal_signal_pending(current)) { + err = -ERESTARTSYS; + goto out_free; + } + cond_resched(); + + /* readahead for multi pages of dir */ + if (npages - n > 1 && !ra_has_index(ra, n)) + page_cache_sync_readahead(inode->i_mapping, ra, file, n, min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES)); - for (; n < npages; n++) { dentry_page = get_lock_data_page(inode, n, false); if (IS_ERR(dentry_page)) { err = PTR_ERR(dentry_page); @@ -880,7 +891,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) err = 0; continue; } else { - goto out; + goto out_free; } } @@ -896,12 +907,13 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) break; } - ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } -out: +out_free: fscrypt_fname_free_buffer(&fstr); +out: + trace_f2fs_readdir(inode, start_pos, ctx->pos, err); return err < 0 ? err : 0; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 226934652d08..e40eb01797e8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -48,6 +48,8 @@ enum { FAULT_KMALLOC, FAULT_PAGE_ALLOC, + FAULT_PAGE_GET, + FAULT_ALLOC_BIO, FAULT_ALLOC_NID, FAULT_ORPHAN, FAULT_BLOCK, @@ -95,6 +97,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_GRPQUOTA 0x00100000 #define F2FS_MOUNT_PRJQUOTA 0x00200000 #define F2FS_MOUNT_QUOTA 0x00400000 +#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -120,6 +123,8 @@ struct f2fs_mount_info { #define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_FEATURE_INODE_CHKSUM 0x0020 +#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 +#define F2FS_FEATURE_QUOTA_INO 0x0080 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -246,7 +251,7 @@ enum { #define BATCHED_TRIM_BLOCKS(sbi) \ (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) #define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi) -#define DISCARD_ISSUE_RATE 8 +#define DEF_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */ #define DEF_MIN_DISCARD_ISSUE_TIME 50 /* 50 ms, if exists */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000 /* 60 s, if no candidates */ #define DEF_CP_INTERVAL 60 /* 60 secs */ @@ -257,7 +262,6 @@ struct cp_control { __u64 trim_start; __u64 trim_end; __u64 trim_minlen; - __u64 trimmed; }; /* @@ -276,12 +280,14 @@ enum { ORPHAN_INO, /* for orphan ino list */ APPEND_INO, /* for append ino list */ UPDATE_INO, /* for update ino list */ + FLUSH_INO, /* for multiple device flushing */ MAX_INO_ENTRY, /* max. list */ }; struct ino_entry { - struct list_head list; /* list head */ - nid_t ino; /* inode number */ + struct list_head list; /* list head */ + nid_t ino; /* inode number */ + unsigned int dirty_device; /* dirty device bitmap */ }; /* for the list of inodes to be GCed */ @@ -305,10 +311,6 @@ struct discard_entry { #define plist_idx(blk_num) ((blk_num) >= MAX_PLIST_NUM ? \ (MAX_PLIST_NUM - 1) : (blk_num - 1)) -#define P_ACTIVE 0x01 -#define P_TRIM 0x02 -#define plist_issue(tag) (((tag) & P_ACTIVE) || ((tag) & P_TRIM)) - enum { D_PREP, D_SUBMIT, @@ -340,12 +342,32 @@ struct discard_cmd { int error; /* bio error */ }; +enum { + DPOLICY_BG, + DPOLICY_FORCE, + DPOLICY_FSTRIM, + DPOLICY_UMOUNT, + MAX_DPOLICY, +}; + +struct discard_policy { + int type; /* type of discard */ + unsigned int min_interval; /* used for candidates exist */ + unsigned int max_interval; /* used for candidates not exist */ + unsigned int max_requests; /* # of discards issued per round */ + unsigned int io_aware_gran; /* minimum granularity discard not be aware of I/O */ + bool io_aware; /* issue discard in idle time */ + bool sync; /* submit discard with REQ_SYNC flag */ + unsigned int granularity; /* discard granularity */ +}; + struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head entry_list; /* 4KB discard entry list */ struct list_head pend_list[MAX_PLIST_NUM];/* store pending entries */ unsigned char pend_list_tag[MAX_PLIST_NUM];/* tag for pending entries */ struct list_head wait_list; /* store on-flushing entries */ + struct list_head fstrim_list; /* in-flight discard from fstrim */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ unsigned int discard_wake; /* to wake up discard thread */ struct mutex cmd_lock; @@ -475,11 +497,14 @@ struct f2fs_flush_device { /* for inline stuff */ #define DEF_INLINE_RESERVED_SIZE 1 +#define DEF_MIN_INLINE_SIZE 1 static inline int get_extra_isize(struct inode *inode); -#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ - (CUR_ADDRS_PER_INODE(inode) - \ - DEF_INLINE_RESERVED_SIZE - \ - F2FS_INLINE_XATTR_ADDRS)) +static inline int get_inline_xattr_addrs(struct inode *inode); +#define F2FS_INLINE_XATTR_ADDRS(inode) get_inline_xattr_addrs(inode) +#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ + (CUR_ADDRS_PER_INODE(inode) - \ + F2FS_INLINE_XATTR_ADDRS(inode) - \ + DEF_INLINE_RESERVED_SIZE)) /* for inline dir */ #define NR_INLINE_DENTRY(inode) (MAX_INLINE_DATA(inode) * BITS_PER_BYTE / \ @@ -677,6 +702,7 @@ struct f2fs_inode_info { #endif struct list_head dirty_list; /* dirty list for dirs and files */ struct list_head gdirty_list; /* linked in global dirty list */ + struct list_head inmem_ilist; /* list for inmem inodes */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct task_struct *inmem_task; /* store inmemory task */ struct mutex inmem_lock; /* lock for inmemory pages */ @@ -687,6 +713,7 @@ struct f2fs_inode_info { int i_extra_isize; /* size of extra space located in i_addr */ kprojid_t i_projid; /* id for project quota */ + int i_inline_xattr_size; /* inline xattr size */ }; static inline void get_extent_info(struct extent_info *ext, @@ -760,10 +787,13 @@ static inline void __try_update_largest_extent(struct inode *inode, } } -enum nid_list { - FREE_NID_LIST, - ALLOC_NID_LIST, - MAX_NID_LIST, +/* + * For free nid management + */ +enum nid_state { + FREE_NID, /* newly added to free nid list */ + PREALLOC_NID, /* it is preallocated */ + MAX_NID_STATE, }; struct f2fs_nm_info { @@ -786,8 +816,8 @@ struct f2fs_nm_info { /* free node ids management */ struct radix_tree_root free_nid_root;/* root of the free_nid cache */ - struct list_head nid_list[MAX_NID_LIST];/* lists for free nids */ - unsigned int nid_cnt[MAX_NID_LIST]; /* the number of free node id */ + struct list_head free_nid_list; /* list for free nids excluding preallocated nids */ + unsigned int nid_cnt[MAX_NID_STATE]; /* the number of free node id */ spinlock_t nid_list_lock; /* protect nid lists ops */ struct mutex build_lock; /* lock for build free nids */ unsigned char (*free_nid_bitmap)[NAT_ENTRY_BITMAP_SIZE]; @@ -865,6 +895,7 @@ enum { struct flush_cmd { struct completion wait; struct llist_node llnode; + nid_t ino; int ret; }; @@ -883,6 +914,8 @@ struct f2fs_sm_info { struct dirty_seglist_info *dirty_info; /* dirty segment information */ struct curseg_info *curseg_array; /* active segment information */ + struct rw_semaphore curseg_lock; /* for preventing curseg change */ + block_t seg0_blkaddr; /* block address of 0'th segment */ block_t main_blkaddr; /* start block address of main area */ block_t ssa_blkaddr; /* start block address of SSA area */ @@ -904,6 +937,7 @@ struct f2fs_sm_info { unsigned int min_ipu_util; /* in-place-update threshold */ unsigned int min_fsync_blocks; /* threshold for fsync */ unsigned int min_hot_blocks; /* threshold for hot block allocation */ + unsigned int min_ssr_sections; /* threshold to trigger SSR allocation */ /* for flush command control */ struct flush_cmd_control *fcc_info; @@ -925,6 +959,7 @@ struct f2fs_sm_info { enum count_type { F2FS_DIRTY_DENTS, F2FS_DIRTY_DATA, + F2FS_DIRTY_QDATA, F2FS_DIRTY_NODES, F2FS_DIRTY_META, F2FS_INMEM_PAGES, @@ -973,6 +1008,18 @@ enum need_lock_type { LOCK_RETRY, }; +enum cp_reason_type { + CP_NO_NEEDED, + CP_NON_REGULAR, + CP_HARDLINK, + CP_SB_NEED_CP, + CP_WRONG_PINO, + CP_NO_SPC_ROLL, + CP_NODE_NEED_CP, + CP_FASTBOOT_MODE, + CP_SPEC_LOG_NUM, +}; + enum iostat_type { APP_DIRECT_IO, /* app direct IOs */ APP_BUFFERED_IO, /* app buffered IOs */ @@ -992,6 +1039,7 @@ enum iostat_type { struct f2fs_io_info { struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ + nid_t ino; /* inode number */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ enum temp_type temp; /* contains HOT/WARM/COLD */ int op; /* contains REQ_OP_ */ @@ -1036,6 +1084,7 @@ enum inode_type { DIR_INODE, /* for dirty dir inode */ FILE_INODE, /* for dirty regular/symlink inode */ DIRTY_META, /* for all dirtied inode metadata */ + ATOMIC_FILE, /* for all atomic files */ NR_INODE_TYPE, }; @@ -1142,12 +1191,15 @@ struct f2fs_sb_info { loff_t max_file_blocks; /* max block index of file */ int active_logs; /* # of active logs */ int dir_level; /* directory level */ + int inline_xattr_size; /* inline xattr size */ + unsigned int trigger_ssr_threshold; /* threshold to trigger ssr */ block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ block_t reserved_blocks; /* configurable reserved blocks */ + block_t current_reserved_blocks; /* current reserved blocks */ u32 s_next_generation; /* for NFS support */ @@ -1213,6 +1265,8 @@ struct f2fs_sb_info { struct list_head s_list; int s_ndevs; /* number of devices */ struct f2fs_dev_info *devs; /* for device list */ + unsigned int dirty_device; /* for checkpoint data flush */ + spinlock_t dev_lock; /* protect dirty_device */ struct mutex umount_mutex; unsigned int shrinker_run_no; @@ -1276,8 +1330,7 @@ static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type) static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type) { - struct timespec ts = {sbi->interval_time[type], 0}; - unsigned long interval = timespec_to_jiffies(&ts); + unsigned long interval = sbi->interval_time[type] * HZ; return time_after(jiffies, sbi->last_time[type] + interval); } @@ -1441,6 +1494,13 @@ static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) return le64_to_cpu(cp->checkpoint_ver); } +static inline unsigned long f2fs_qf_ino(struct super_block *sb, int type) +{ + if (type < F2FS_MAX_QUOTAS) + return le32_to_cpu(F2FS_SB(sb)->raw_super->qf_ino[type]); + return 0; +} + static inline __u64 cur_cp_crc(struct f2fs_checkpoint *cp) { size_t crc_offset = le32_to_cpu(cp->checksum_offset); @@ -1619,7 +1679,8 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); sbi->total_valid_block_count += (block_t)(*count); - avail_user_block_count = sbi->user_block_count - sbi->reserved_blocks; + avail_user_block_count = sbi->user_block_count - + sbi->current_reserved_blocks; if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { diff = sbi->total_valid_block_count - avail_user_block_count; *count -= diff; @@ -1653,6 +1714,10 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count); f2fs_bug_on(sbi, inode->i_blocks < sectors); sbi->total_valid_block_count -= (block_t)count; + if (sbi->reserved_blocks && + sbi->current_reserved_blocks < sbi->reserved_blocks) + sbi->current_reserved_blocks = min(sbi->reserved_blocks, + sbi->current_reserved_blocks + count); spin_unlock(&sbi->stat_lock); f2fs_i_blocks_write(inode, count, false, true); } @@ -1673,6 +1738,8 @@ static inline void inode_inc_dirty_pages(struct inode *inode) atomic_inc(&F2FS_I(inode)->dirty_pages); inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); + if (IS_NOQUOTA(inode)) + inc_page_count(F2FS_I_SB(inode), F2FS_DIRTY_QDATA); } static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) @@ -1689,6 +1756,8 @@ static inline void inode_dec_dirty_pages(struct inode *inode) atomic_dec(&F2FS_I(inode)->dirty_pages); dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ? F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA); + if (IS_NOQUOTA(inode)) + dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_QDATA); } static inline s64 get_pages(struct f2fs_sb_info *sbi, int count_type) @@ -1796,10 +1865,17 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, return ret; } +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_BLOCK)) { + f2fs_show_injection_info(FAULT_BLOCK); + goto enospc; + } +#endif + spin_lock(&sbi->stat_lock); valid_block_count = sbi->total_valid_block_count + 1; - if (unlikely(valid_block_count + sbi->reserved_blocks > + if (unlikely(valid_block_count + sbi->current_reserved_blocks > sbi->user_block_count)) { spin_unlock(&sbi->stat_lock); goto enospc; @@ -1842,6 +1918,9 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, sbi->total_valid_node_count--; sbi->total_valid_block_count--; + if (sbi->reserved_blocks && + sbi->current_reserved_blocks < sbi->reserved_blocks) + sbi->current_reserved_blocks++; spin_unlock(&sbi->stat_lock); @@ -1888,6 +1967,19 @@ static inline struct page *f2fs_grab_cache_page(struct address_space *mapping, return grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS); } +static inline struct page *f2fs_pagecache_get_page( + struct address_space *mapping, pgoff_t index, + int fgp_flags, gfp_t gfp_mask) +{ +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(F2FS_M_SB(mapping), FAULT_PAGE_GET)) { + f2fs_show_injection_info(FAULT_PAGE_GET); + return NULL; + } +#endif + return pagecache_get_page(mapping, index, fgp_flags, gfp_mask); +} + static inline void f2fs_copy_page(struct page *src, struct page *dst) { char *src_kaddr = kmap(src); @@ -1937,15 +2029,25 @@ static inline void *f2fs_kmem_cache_alloc(struct kmem_cache *cachep, return entry; } -static inline struct bio *f2fs_bio_alloc(int npages) +static inline struct bio *f2fs_bio_alloc(struct f2fs_sb_info *sbi, + int npages, bool no_fail) { struct bio *bio; - /* No failure on bio allocation */ - bio = bio_alloc(GFP_NOIO, npages); - if (!bio) - bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, npages); - return bio; + if (no_fail) { + /* No failure on bio allocation */ + bio = bio_alloc(GFP_NOIO, npages); + if (!bio) + bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, npages); + return bio; + } +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_ALLOC_BIO)) { + f2fs_show_injection_info(FAULT_ALLOC_BIO); + return NULL; + } +#endif + return bio_alloc(GFP_KERNEL, npages); } static inline void f2fs_radix_tree_insert(struct radix_tree_root *root, @@ -2254,25 +2356,20 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) static inline unsigned int addrs_per_inode(struct inode *inode) { - if (f2fs_has_inline_xattr(inode)) - return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS; - return CUR_ADDRS_PER_INODE(inode); + return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS(inode); } -static inline void *inline_xattr_addr(struct page *page) +static inline void *inline_xattr_addr(struct inode *inode, struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - - F2FS_INLINE_XATTR_ADDRS]); + F2FS_INLINE_XATTR_ADDRS(inode)]); } static inline int inline_xattr_size(struct inode *inode) { - if (f2fs_has_inline_xattr(inode)) - return F2FS_INLINE_XATTR_ADDRS << 2; - else - return 0; + return get_inline_xattr_addrs(inode) * sizeof(__le32); } static inline int f2fs_has_inline_data(struct inode *inode) @@ -2353,9 +2450,10 @@ static inline void clear_file(struct inode *inode, int type) static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) { + bool ret; + if (dsync) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - bool ret; spin_lock(&sbi->inode_lock[DIRTY_META]); ret = list_empty(&F2FS_I(inode)->gdirty_list); @@ -2366,9 +2464,15 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) file_keep_isize(inode) || i_size_read(inode) & PAGE_MASK) return false; - return F2FS_I(inode)->last_disk_size == i_size_read(inode); + + down_read(&F2FS_I(inode)->i_sem); + ret = F2FS_I(inode)->last_disk_size == i_size_read(inode); + up_read(&F2FS_I(inode)->i_sem); + + return ret; } +#define sb_rdonly f2fs_readonly static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; @@ -2436,6 +2540,12 @@ static inline int get_extra_isize(struct inode *inode) return F2FS_I(inode)->i_extra_isize / sizeof(__le32); } +static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb); +static inline int get_inline_xattr_addrs(struct inode *inode) +{ + return F2FS_I(inode)->i_inline_xattr_size; +} + #define get_inode_mode(i) \ ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) @@ -2564,7 +2674,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) */ int f2fs_inode_dirtied(struct inode *inode, bool sync); void f2fs_inode_synced(struct inode *inode); -void f2fs_enable_quota_files(struct f2fs_sb_info *sbi); +int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly); void f2fs_quota_off_umount(struct super_block *sb); int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); int f2fs_sync_fs(struct super_block *sb, int sync); @@ -2592,7 +2702,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni); pgoff_t get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs); int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode); int truncate_inode_blocks(struct inode *inode, pgoff_t from); -int truncate_xattr_node(struct inode *inode, struct page *page); +int truncate_xattr_node(struct inode *inode); int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino); int remove_inode_page(struct inode *inode); struct page *new_inode_page(struct inode *inode); @@ -2627,19 +2737,22 @@ void destroy_node_manager_caches(void); */ bool need_SSR(struct f2fs_sb_info *sbi); void register_inmem_page(struct inode *inode, struct page *page); +void drop_inmem_pages_all(struct f2fs_sb_info *sbi); void drop_inmem_pages(struct inode *inode); void drop_inmem_page(struct inode *inode, struct page *page); int commit_inmem_pages(struct inode *inode); void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need); void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi); -int f2fs_issue_flush(struct f2fs_sb_info *sbi); +int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino); int create_flush_cmd_control(struct f2fs_sb_info *sbi); +int f2fs_flush_device_cache(struct f2fs_sb_info *sbi); void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free); void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); -void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new); +void init_discard_policy(struct discard_policy *dpolicy, int discard_type, + unsigned int granularity); void stop_discard_thread(struct f2fs_sb_info *sbi); -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount); +bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void release_discard_addrs(struct f2fs_sb_info *sbi); int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); @@ -2694,6 +2807,10 @@ void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type); void release_ino_entry(struct f2fs_sb_info *sbi, bool all); bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode); +void set_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type); +bool is_dirty_device(struct f2fs_sb_info *sbi, nid_t ino, + unsigned int devidx, int type); int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi); int acquire_orphan_inode(struct f2fs_sb_info *sbi); void release_orphan_inode(struct f2fs_sb_info *sbi); @@ -2782,14 +2899,16 @@ struct f2fs_stat_info { unsigned long long hit_largest, hit_cached, hit_rbtree; unsigned long long hit_total, total_ext; int ext_tree, zombie_tree, ext_node; - int ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, ndirty_imeta; + int ndirty_node, ndirty_dent, ndirty_meta, ndirty_imeta; + int ndirty_data, ndirty_qdata; int inmem_pages; - unsigned int ndirty_dirs, ndirty_files, ndirty_all; + unsigned int ndirty_dirs, ndirty_files, nquota_files, ndirty_all; int nats, dirty_nats, sits, dirty_sits; int free_nids, avail_nids, alloc_nids; int total_count, utilization; int bg_gc, nr_wb_cp_data, nr_wb_data; - int nr_flushing, nr_flushed, nr_discarding, nr_discarded; + int nr_flushing, nr_flushed, flush_list_empty; + int nr_discarding, nr_discarded; int nr_discard_cmd; unsigned int undiscard_blks; int inline_xattr, inline_inode, inline_dir, append, update, orphans; @@ -3097,6 +3216,16 @@ static inline int f2fs_sb_has_inode_chksum(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM); } +static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); +} + +static inline int f2fs_sb_has_quota_ino(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_QUOTA_INO); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 0bc56822ffc3..002a54ba8001 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -56,6 +56,11 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, struct dnode_of_data dn; int err; + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; + goto err; + } + sb_start_pagefault(inode->i_sb); f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); @@ -117,6 +122,7 @@ out_sem: out: sb_end_pagefault(inode->i_sb); f2fs_update_time(sbi, REQ_TIME); +err: return block_page_mkwrite_return(err); } @@ -141,27 +147,29 @@ static int get_parent_ino(struct inode *inode, nid_t *pino) return 1; } -static inline bool need_do_checkpoint(struct inode *inode) +static inline enum cp_reason_type need_do_checkpoint(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - bool need_cp = false; + enum cp_reason_type cp_reason = CP_NO_NEEDED; - if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) - need_cp = true; + if (!S_ISREG(inode->i_mode)) + cp_reason = CP_NON_REGULAR; + else if (inode->i_nlink != 1) + cp_reason = CP_HARDLINK; else if (is_sbi_flag_set(sbi, SBI_NEED_CP)) - need_cp = true; + cp_reason = CP_SB_NEED_CP; else if (file_wrong_pino(inode)) - need_cp = true; + cp_reason = CP_WRONG_PINO; else if (!space_for_roll_forward(sbi)) - need_cp = true; + cp_reason = CP_NO_SPC_ROLL; else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino)) - need_cp = true; + cp_reason = CP_NODE_NEED_CP; else if (test_opt(sbi, FASTBOOT)) - need_cp = true; + cp_reason = CP_FASTBOOT_MODE; else if (sbi->active_logs == 2) - need_cp = true; + cp_reason = CP_SPEC_LOG_NUM; - return need_cp; + return cp_reason; } static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino) @@ -196,7 +204,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t ino = inode->i_ino; int ret = 0; - bool need_cp = false; + enum cp_reason_type cp_reason = 0; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, @@ -215,7 +223,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, clear_inode_flag(inode, FI_NEED_IPU); if (ret) { - trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret); return ret; } @@ -246,10 +254,10 @@ go_write: * sudden-power-off. */ down_read(&F2FS_I(inode)->i_sem); - need_cp = need_do_checkpoint(inode); + cp_reason = need_do_checkpoint(inode); up_read(&F2FS_I(inode)->i_sem); - if (need_cp) { + if (cp_reason) { /* all the dirty node pages should be flushed for POR */ ret = f2fs_sync_fs(inode->i_sb, 1); @@ -297,19 +305,24 @@ sync_nodes: remove_ino_entry(sbi, ino, APPEND_INO); clear_inode_flag(inode, FI_APPEND_WRITE); flush_out: - remove_ino_entry(sbi, ino, UPDATE_INO); - clear_inode_flag(inode, FI_UPDATE_WRITE); if (!atomic) - ret = f2fs_issue_flush(sbi); + ret = f2fs_issue_flush(sbi, inode->i_ino); + if (!ret) { + remove_ino_entry(sbi, ino, UPDATE_INO); + clear_inode_flag(inode, FI_UPDATE_WRITE); + remove_ino_entry(sbi, ino, FLUSH_INO); + } f2fs_update_time(sbi, REQ_TIME); out: - trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret); f2fs_trace_ios(NULL, 1); return ret; } int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(file))))) + return -EIO; return f2fs_do_sync_file(file, start, end, datasync, false); } @@ -446,6 +459,9 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) struct inode *inode = file_inode(file); int err; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + /* we don't need to use inline_data strictly */ err = f2fs_convert_inline_inode(inode); if (err) @@ -632,6 +648,9 @@ int f2fs_truncate(struct inode *inode) { int err; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return 0; @@ -667,7 +686,8 @@ int f2fs_getattr(struct vfsmount *mnt, generic_fillattr(inode, stat); /* we need to show initial sectors used for inline_data/dentries */ - if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) + if ((S_ISREG(inode->i_mode) && f2fs_has_inline_data(inode)) || + f2fs_has_inline_dentry(inode)) stat->blocks += (stat->size + 511) >> 9; return 0; @@ -709,6 +729,9 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) int err; bool size_changed = false; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + err = inode_change_ok(inode, attr); if (err) return err; @@ -758,6 +781,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) inode->i_mtime = inode->i_ctime = current_time(inode); } + down_write(&F2FS_I(inode)->i_sem); + F2FS_I(inode)->last_disk_size = i_size_read(inode); + up_write(&F2FS_I(inode)->i_sem); + size_changed = true; } @@ -831,7 +858,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) err = get_dnode_of_data(&dn, pg_start, LOOKUP_NODE); if (err) { if (err == -ENOENT) { - pg_start++; + pg_start = get_next_page_offset(&dn, pg_start); continue; } return err; @@ -1146,11 +1173,14 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) if (ret) goto out; + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + truncate_pagecache(inode, offset); ret = f2fs_do_collapse(inode, pg_start, pg_end); if (ret) - goto out; + goto out_unlock; /* write out all moved pages, if possible */ filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); @@ -1162,7 +1192,8 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) ret = truncate_blocks(inode, new_size, true); if (!ret) f2fs_i_size_write(inode, new_size); - +out_unlock: + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); out: up_write(&F2FS_I(inode)->i_mmap_sem); return ret; @@ -1345,6 +1376,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (ret) goto out; + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + truncate_pagecache(inode, offset); pg_start = offset >> PAGE_SHIFT; @@ -1372,6 +1406,8 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); + + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); out: up_write(&F2FS_I(inode)->i_mmap_sem); return ret; @@ -1421,8 +1457,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset, new_size = ((loff_t)pg_end << PAGE_SHIFT) + off_end; } - if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) - f2fs_i_size_write(inode, new_size); + if (new_size > i_size_read(inode)) { + if (mode & FALLOC_FL_KEEP_SIZE) + file_set_keep_isize(inode); + else + f2fs_i_size_write(inode, new_size); + } return err; } @@ -1443,6 +1483,9 @@ static long f2fs_fallocate(struct file *file, int mode, struct inode *inode = file_inode(file); long ret = 0; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + /* f2fs only support ->fallocate for regular file */ if (!S_ISREG(inode->i_mode)) return -EINVAL; @@ -1476,8 +1519,6 @@ static long f2fs_fallocate(struct file *file, int mode, if (!ret) { inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); - if (mode & FALLOC_FL_KEEP_SIZE) - file_set_keep_isize(inode); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); } @@ -1871,6 +1912,9 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + if (!f2fs_sb_has_crypto(inode->i_sb)) + return -EOPNOTSUPP; + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); return fscrypt_ioctl_set_policy(filp, (const void __user *)arg); @@ -1878,6 +1922,8 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) { + if (!f2fs_sb_has_crypto(file_inode(filp)->i_sb)) + return -EOPNOTSUPP; return fscrypt_ioctl_get_policy(filp, (void __user *)arg); } @@ -2233,9 +2279,13 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, } inode_lock(src); + down_write(&F2FS_I(src)->dio_rwsem[WRITE]); if (src != dst) { - if (!inode_trylock(dst)) { - ret = -EBUSY; + ret = -EBUSY; + if (!inode_trylock(dst)) + goto out; + if (!down_write_trylock(&F2FS_I(dst)->dio_rwsem[WRITE])) { + inode_unlock(dst); goto out; } } @@ -2295,9 +2345,12 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, } f2fs_unlock_op(sbi); out_unlock: - if (src != dst) + if (src != dst) { + up_write(&F2FS_I(dst)->dio_rwsem[WRITE]); inode_unlock(dst); + } out: + up_write(&F2FS_I(src)->dio_rwsem[WRITE]); inode_unlock(src); return ret; } @@ -2419,6 +2472,9 @@ static int f2fs_ioc_get_features(struct file *filp, unsigned long arg) long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp))))) + return -EIO; + switch (cmd) { case F2FS_IOC_GETFLAGS: return f2fs_ioc_getflags(filp, arg); @@ -2474,6 +2530,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct blk_plug plug; ssize_t ret; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + return -EIO; + inode_lock(inode); ret = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (!ret) { diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index bfe6a8ccc3a0..5d5bba462f26 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -267,16 +267,6 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) return UINT_MAX - ((100 * (100 - u) * age) / (100 + u)); } -static unsigned int get_greedy_cost(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - unsigned int valid_blocks = - get_valid_blocks(sbi, segno, true); - - return IS_DATASEG(get_seg_entry(sbi, segno)->type) ? - valid_blocks * 2 : valid_blocks; -} - static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, unsigned int segno, struct victim_sel_policy *p) { @@ -285,7 +275,7 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, /* alloc_mode == LFS */ if (p->gc_mode == GC_GREEDY) - return get_greedy_cost(sbi, segno); + return get_valid_blocks(sbi, segno, true); else return get_cb_cost(sbi, segno); } @@ -466,10 +456,10 @@ static int check_valid_map(struct f2fs_sb_info *sbi, struct seg_entry *sentry; int ret; - mutex_lock(&sit_i->sentry_lock); + down_read(&sit_i->sentry_lock); sentry = get_seg_entry(sbi, segno); ret = f2fs_test_bit(offset, sentry->cur_valid_map); - mutex_unlock(&sit_i->sentry_lock); + up_read(&sit_i->sentry_lock); return ret; } @@ -608,6 +598,7 @@ static void move_data_block(struct inode *inode, block_t bidx, { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), + .ino = inode->i_ino, .type = DATA, .temp = COLD, .op = REQ_OP_READ, @@ -659,8 +650,8 @@ static void move_data_block(struct inode *inode, block_t bidx, allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr, &sum, CURSEG_COLD_DATA, NULL, false); - fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr, - FGP_LOCK | FGP_CREAT, GFP_NOFS); + fio.encrypted_page = f2fs_pagecache_get_page(META_MAPPING(fio.sbi), + newaddr, FGP_LOCK | FGP_CREAT, GFP_NOFS); if (!fio.encrypted_page) { err = -ENOMEM; goto recover_block; @@ -738,6 +729,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, } else { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), + .ino = inode->i_ino, .type = DATA, .temp = COLD, .op = REQ_OP_WRITE, @@ -840,10 +832,17 @@ next_step: continue; } + if (!down_write_trylock( + &F2FS_I(inode)->dio_rwsem[WRITE])) { + iput(inode); + continue; + } + start_bidx = start_bidx_of_node(nofs, inode); data_page = get_read_data_page(inode, start_bidx + ofs_in_node, REQ_RAHEAD, true); + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); if (IS_ERR(data_page)) { iput(inode); continue; @@ -901,10 +900,10 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, struct sit_info *sit_i = SIT_I(sbi); int ret; - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, NO_CHECK_TYPE, LFS); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); return ret; } @@ -952,8 +951,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, /* * this is to avoid deadlock: * - lock_page(sum_page) - f2fs_replace_block - * - check_valid_map() - mutex_lock(sentry_lock) - * - mutex_lock(sentry_lock) - change_curseg() + * - check_valid_map() - down_write(sentry_lock) + * - down_read(sentry_lock) - change_curseg() * - lock_page(sum_page) */ if (type == SUM_TYPE_NODE) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index bbd092269454..8e2924bb5ca4 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -130,6 +130,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) { struct f2fs_io_info fio = { .sbi = F2FS_I_SB(dn->inode), + .ino = dn->inode->i_ino, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC | REQ_PRIO, diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 395ca5dd3d37..1ae5b61cf2bc 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -232,6 +232,23 @@ static int do_read_inode(struct inode *inode) fi->i_extra_isize = f2fs_has_extra_attr(inode) ? le16_to_cpu(ri->i_extra_isize) : 0; + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) { + f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode)); + fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); + } else if (f2fs_has_inline_xattr(inode) || + f2fs_has_inline_dentry(inode)) { + fi->i_inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; + } else { + + /* + * Previous inline data or directory always reserved 200 bytes + * in inode layout, even if inline_xattr is disabled. In order + * to keep inline_dentry's structure for backward compatibility, + * we get the space back only from inline_data. + */ + fi->i_inline_xattr_size = 0; + } + /* check data exist */ if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) __recover_inline_status(inode, node_page); @@ -384,6 +401,10 @@ int update_inode(struct inode *inode, struct page *node_page) if (f2fs_has_extra_attr(inode)) { ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + if (f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(inode)->sb)) + ri->i_inline_xattr_size = + cpu_to_le16(F2FS_I(inode)->i_inline_xattr_size); + if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) && F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, i_projid)) { @@ -480,6 +501,7 @@ void f2fs_evict_inode(struct inode *inode) remove_ino_entry(sbi, inode->i_ino, APPEND_INO); remove_ino_entry(sbi, inode->i_ino, UPDATE_INO); + remove_ino_entry(sbi, inode->i_ino, FLUSH_INO); sb_start_intwrite(inode->i_sb); set_inode_flag(inode, FI_NO_ALLOC); @@ -519,8 +541,10 @@ no_delete: stat_dec_inline_dir(inode); stat_dec_inline_inode(inode); - if (!is_set_ckpt_flags(sbi, CP_ERROR_FLAG)) + if (likely(!is_set_ckpt_flags(sbi, CP_ERROR_FLAG))) f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE)); + else + f2fs_inode_synced(inode); /* ino == 0, if f2fs_new_inode() was failed t*/ if (inode->i_ino) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index c8b28244dd0d..eaf7476a0942 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -30,6 +30,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) nid_t ino; struct inode *inode; bool nid_free = false; + int xattr_size = 0; int err; inode = new_inode(dir->i_sb); @@ -84,11 +85,23 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (test_opt(sbi, INLINE_XATTR)) set_inode_flag(inode, FI_INLINE_XATTR); + if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode)) set_inode_flag(inode, FI_INLINE_DATA); if (f2fs_may_inline_dentry(inode)) set_inode_flag(inode, FI_INLINE_DENTRY); + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) { + f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode)); + if (f2fs_has_inline_xattr(inode)) + xattr_size = sbi->inline_xattr_size; + /* Otherwise, will be 0 */ + } else if (f2fs_has_inline_xattr(inode) || + f2fs_has_inline_dentry(inode)) { + xattr_size = DEFAULT_INLINE_XATTR_ADDRS; + } + F2FS_I(inode)->i_inline_xattr_size = xattr_size; + f2fs_init_extent_tree(inode, NULL); stat_inc_inline_xattr(inode); @@ -175,6 +188,9 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, nid_t ino = 0; int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + dquot_initialize(dir); inode = f2fs_new_inode(dir, mode); @@ -217,6 +233,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, struct f2fs_sb_info *sbi = F2FS_I_SB(dir); int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if (f2fs_encrypted_inode(dir) && !fscrypt_has_permitted_context(dir, inode)) return -EPERM; @@ -323,12 +342,15 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode = NULL; struct f2fs_dir_entry *de; struct page *page; - nid_t ino; + struct dentry *new; + nid_t ino = -1; int err = 0; unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir)); + trace_f2fs_lookup_start(dir, dentry, flags); + if (f2fs_encrypted_inode(dir)) { - int res = fscrypt_get_encryption_info(dir); + err = fscrypt_get_encryption_info(dir); /* * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is @@ -338,18 +360,22 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (fscrypt_has_encryption_key(dir)) fscrypt_set_encrypted_dentry(dentry); fscrypt_set_d_op(dentry); - if (res && res != -ENOKEY) - return ERR_PTR(res); + if (err && err != -ENOKEY) + goto out; } - if (dentry->d_name.len > F2FS_NAME_LEN) - return ERR_PTR(-ENAMETOOLONG); + if (dentry->d_name.len > F2FS_NAME_LEN) { + err = -ENAMETOOLONG; + goto out; + } de = f2fs_find_entry(dir, &dentry->d_name, &page); if (!de) { - if (IS_ERR(page)) - return (struct dentry *)page; - return d_splice_alias(inode, dentry); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + goto out_splice; } ino = le32_to_cpu(de->ino); @@ -357,19 +383,21 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, f2fs_put_page(page, 0); inode = f2fs_iget(dir->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) { err = __recover_dot_dentries(dir, root_ino); if (err) - goto err_out; + goto out_iput; } if (f2fs_has_inline_dots(inode)) { err = __recover_dot_dentries(inode, dir->i_ino); if (err) - goto err_out; + goto out_iput; } if (f2fs_encrypted_inode(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && @@ -378,12 +406,18 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, "Inconsistent encryption contexts: %lu/%lu", dir->i_ino, inode->i_ino); err = -EPERM; - goto err_out; + goto out_iput; } - return d_splice_alias(inode, dentry); - -err_out: +out_splice: + new = d_splice_alias(inode, dentry); + if (IS_ERR(new)) + err = PTR_ERR(new); + trace_f2fs_lookup_end(dir, dentry, ino, err); + return new; +out_iput: iput(inode); +out: + trace_f2fs_lookup_end(dir, dentry, ino, err); return ERR_PTR(err); } @@ -397,7 +431,11 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) trace_f2fs_unlink_enter(dir, dentry); + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + dquot_initialize(dir); + dquot_initialize(inode); de = f2fs_find_entry(dir, &dentry->d_name, &page); if (!de) { @@ -458,6 +496,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, struct fscrypt_symlink_data *sd = NULL; int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if (f2fs_encrypted_inode(dir)) { err = fscrypt_get_encryption_info(dir); if (err) @@ -562,6 +603,9 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) struct inode *inode; int err; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + dquot_initialize(dir); inode = f2fs_new_inode(dir, S_IFDIR | mode); @@ -612,6 +656,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, struct inode *inode; int err = 0; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; if (!new_valid_dev(rdev)) return -EINVAL; @@ -705,6 +751,9 @@ out: static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(dir)))) + return -EIO; + if (f2fs_encrypted_inode(dir)) { int err = fscrypt_get_encryption_info(dir); if (err) @@ -716,6 +765,9 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) static int f2fs_create_whiteout(struct inode *dir, struct inode **whiteout) { + if (unlikely(f2fs_cp_error(F2FS_I_SB(dir)))) + return -EIO; + return __f2fs_tmpfile(dir, NULL, S_IFCHR | WHITEOUT_MODE, whiteout); } @@ -735,6 +787,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, bool is_old_inline = f2fs_has_inline_dentry(old_dir); int err = -ENOENT; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if ((f2fs_encrypted_inode(old_dir) && !fscrypt_has_encryption_key(old_dir)) || (f2fs_encrypted_inode(new_dir) && @@ -756,6 +811,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, dquot_initialize(new_dir); + if (new_inode) + dquot_initialize(new_inode); + old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); if (!old_entry) { if (IS_ERR(old_page)) @@ -924,6 +982,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, int old_nlink = 0, new_nlink = 0; int err = -ENOENT; + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + if ((f2fs_encrypted_inode(old_dir) && !fscrypt_has_encryption_key(old_dir)) || (f2fs_encrypted_inode(new_dir) && diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 25d2dbe1aec8..57e86a43cbf2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -46,7 +46,7 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) * give 25%, 25%, 50%, 50%, 50% memory for each components respectively */ if (type == FREE_NIDS) { - mem_size = (nm_i->nid_cnt[FREE_NID_LIST] * + mem_size = (nm_i->nid_cnt[FREE_NID] * sizeof(struct free_nid)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == NAT_ENTRIES) { @@ -63,7 +63,7 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) } else if (type == INO_ENTRIES) { int i; - for (i = 0; i <= UPDATE_INO; i++) + for (i = 0; i < MAX_INO_ENTRY; i++) mem_size += sbi->im[i].ino_num * sizeof(struct ino_entry); mem_size >>= PAGE_SHIFT; @@ -74,6 +74,10 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) atomic_read(&sbi->total_ext_node) * sizeof(struct extent_node)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else if (type == INMEM_PAGES) { + /* it allows 20% / total_ram for inmemory pages */ + mem_size = get_pages(sbi, F2FS_INMEM_PAGES); + res = mem_size < (val.totalram / 5); } else { if (!sbi->sb->s_bdi->dirty_exceeded) return true; @@ -134,6 +138,44 @@ static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid) return dst_page; } +static struct nat_entry *__alloc_nat_entry(nid_t nid, bool no_fail) +{ + struct nat_entry *new; + + if (no_fail) + new = f2fs_kmem_cache_alloc(nat_entry_slab, + GFP_NOFS | __GFP_ZERO); + else + new = kmem_cache_alloc(nat_entry_slab, + GFP_NOFS | __GFP_ZERO); + if (new) { + nat_set_nid(new, nid); + nat_reset_flag(new); + } + return new; +} + +static void __free_nat_entry(struct nat_entry *e) +{ + kmem_cache_free(nat_entry_slab, e); +} + +/* must be locked by nat_tree_lock */ +static struct nat_entry *__init_nat_entry(struct f2fs_nm_info *nm_i, + struct nat_entry *ne, struct f2fs_nat_entry *raw_ne, bool no_fail) +{ + if (no_fail) + f2fs_radix_tree_insert(&nm_i->nat_root, nat_get_nid(ne), ne); + else if (radix_tree_insert(&nm_i->nat_root, nat_get_nid(ne), ne)) + return NULL; + + if (raw_ne) + node_info_from_raw_nat(&ne->ni, raw_ne); + list_add_tail(&ne->list, &nm_i->nat_entries); + nm_i->nat_cnt++; + return ne; +} + static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n) { return radix_tree_lookup(&nm_i->nat_root, n); @@ -150,7 +192,7 @@ static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e) list_del(&e->list); radix_tree_delete(&nm_i->nat_root, nat_get_nid(e)); nm_i->nat_cnt--; - kmem_cache_free(nat_entry_slab, e); + __free_nat_entry(e); } static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, @@ -246,49 +288,29 @@ bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) return need_update; } -static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, - bool no_fail) -{ - struct nat_entry *new; - - if (no_fail) { - new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS); - f2fs_radix_tree_insert(&nm_i->nat_root, nid, new); - } else { - new = kmem_cache_alloc(nat_entry_slab, GFP_NOFS); - if (!new) - return NULL; - if (radix_tree_insert(&nm_i->nat_root, nid, new)) { - kmem_cache_free(nat_entry_slab, new); - return NULL; - } - } - - memset(new, 0, sizeof(struct nat_entry)); - nat_set_nid(new, nid); - nat_reset_flag(new); - list_add_tail(&new->list, &nm_i->nat_entries); - nm_i->nat_cnt++; - return new; -} - +/* must be locked by nat_tree_lock */ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry *ne) { struct f2fs_nm_info *nm_i = NM_I(sbi); - struct nat_entry *e; + struct nat_entry *new, *e; + + new = __alloc_nat_entry(nid, false); + if (!new) + return; + down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); - if (!e) { - e = grab_nat_entry(nm_i, nid, false); - if (e) - node_info_from_raw_nat(&e->ni, ne); - } else { + if (!e) + e = __init_nat_entry(nm_i, new, ne, false); + else f2fs_bug_on(sbi, nat_get_ino(e) != le32_to_cpu(ne->ino) || nat_get_blkaddr(e) != le32_to_cpu(ne->block_addr) || nat_get_version(e) != ne->version); - } + up_write(&nm_i->nat_tree_lock); + if (e != new) + __free_nat_entry(new); } static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, @@ -296,11 +318,12 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; + struct nat_entry *new = __alloc_nat_entry(ni->nid, true); down_write(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, ni->nid); if (!e) { - e = grab_nat_entry(nm_i, ni->nid, true); + e = __init_nat_entry(nm_i, new, NULL, true); copy_node_info(&e->ni, ni); f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR); } else if (new_blkaddr == NEW_ADDR) { @@ -312,6 +335,9 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, copy_node_info(&e->ni, ni); f2fs_bug_on(sbi, ni->blk_addr != NULL_ADDR); } + /* let's free early to reduce memory consumption */ + if (e != new) + __free_nat_entry(new); /* sanity check */ f2fs_bug_on(sbi, nat_get_blkaddr(e) != ni->blk_addr); @@ -327,10 +353,6 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, if (nat_get_blkaddr(e) != NEW_ADDR && new_blkaddr == NULL_ADDR) { unsigned char version = nat_get_version(e); nat_set_version(e, inc_node_version(version)); - - /* in order to reuse the nid */ - if (nm_i->next_scan_nid > ni->nid) - nm_i->next_scan_nid = ni->nid; } /* change address */ @@ -424,9 +446,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) f2fs_put_page(page, 1); cache: /* cache nat entry */ - down_write(&nm_i->nat_tree_lock); cache_nat_entry(sbi, nid, &ne); - up_write(&nm_i->nat_tree_lock); } /* @@ -962,7 +982,8 @@ fail: return err > 0 ? 0 : err; } -int truncate_xattr_node(struct inode *inode, struct page *page) +/* caller must lock inode page */ +int truncate_xattr_node(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t nid = F2FS_I(inode)->i_xattr_nid; @@ -978,10 +999,7 @@ int truncate_xattr_node(struct inode *inode, struct page *page) f2fs_i_xnid_write(inode, 0); - set_new_dnode(&dn, inode, page, npage, nid); - - if (page) - dn.inode_page_locked = true; + set_new_dnode(&dn, inode, NULL, npage, nid); truncate_node(&dn); return 0; } @@ -1000,7 +1018,7 @@ int remove_inode_page(struct inode *inode) if (err) return err; - err = truncate_xattr_node(inode, dn.inode_page); + err = truncate_xattr_node(inode); if (err) { f2fs_put_dnode(&dn); return err; @@ -1221,13 +1239,11 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) if (!inode) return; - page = find_get_page(inode->i_mapping, 0); + page = f2fs_pagecache_get_page(inode->i_mapping, 0, + FGP_LOCK|FGP_NOWAIT, 0); if (!page) goto iput_out; - if (!trylock_page(page)) - goto release_out; - if (!PageUptodate(page)) goto page_out; @@ -1243,44 +1259,11 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino) if (ret) set_page_dirty(page); page_out: - unlock_page(page); -release_out: - f2fs_put_page(page, 0); + f2fs_put_page(page, 1); iput_out: iput(inode); } -void move_node_page(struct page *node_page, int gc_type) -{ - if (gc_type == FG_GC) { - struct f2fs_sb_info *sbi = F2FS_P_SB(node_page); - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 1, - .for_reclaim = 0, - }; - - set_page_dirty(node_page); - f2fs_wait_on_page_writeback(node_page, NODE, true); - - f2fs_bug_on(sbi, PageWriteback(node_page)); - if (!clear_page_dirty_for_io(node_page)) - goto out_page; - - if (NODE_MAPPING(sbi)->a_ops->writepage(node_page, &wbc)) - unlock_page(node_page); - goto release_page; - } else { - /* set page dirty and write it */ - if (!PageWriteback(node_page)) - set_page_dirty(node_page); - } -out_page: - unlock_page(node_page); -release_page: - f2fs_put_page(node_page, 0); -} - static struct page *last_fsync_dnode(struct f2fs_sb_info *sbi, nid_t ino) { pgoff_t index, end; @@ -1350,6 +1333,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, struct node_info ni; struct f2fs_io_info fio = { .sbi = sbi, + .ino = ino_of_node(page), .type = NODE, .op = REQ_OP_WRITE, .op_flags = wbc_to_write_flags(wbc), @@ -1422,6 +1406,37 @@ redirty_out: return AOP_WRITEPAGE_ACTIVATE; } +void move_node_page(struct page *node_page, int gc_type) +{ + if (gc_type == FG_GC) { + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 1, + .for_reclaim = 0, + }; + + set_page_dirty(node_page); + f2fs_wait_on_page_writeback(node_page, NODE, true); + + f2fs_bug_on(F2FS_P_SB(node_page), PageWriteback(node_page)); + if (!clear_page_dirty_for_io(node_page)) + goto out_page; + + if (__write_node_page(node_page, false, NULL, + &wbc, false, FS_GC_NODE_IO)) + unlock_page(node_page); + goto release_page; + } else { + /* set page dirty and write it */ + if (!PageWriteback(node_page)) + set_page_dirty(node_page); + } +out_page: + unlock_page(node_page); +release_page: + f2fs_put_page(node_page, 0); +} + static int f2fs_write_node_page(struct page *page, struct writeback_control *wbc) { @@ -1770,35 +1785,54 @@ static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, return radix_tree_lookup(&nm_i->free_nid_root, n); } -static int __insert_nid_to_list(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_list list, bool new) +static int __insert_free_nid(struct f2fs_sb_info *sbi, + struct free_nid *i, enum nid_state state) { struct f2fs_nm_info *nm_i = NM_I(sbi); - if (new) { - int err = radix_tree_insert(&nm_i->free_nid_root, i->nid, i); - if (err) - return err; - } + int err = radix_tree_insert(&nm_i->free_nid_root, i->nid, i); + if (err) + return err; - f2fs_bug_on(sbi, list == FREE_NID_LIST ? i->state != NID_NEW : - i->state != NID_ALLOC); - nm_i->nid_cnt[list]++; - list_add_tail(&i->list, &nm_i->nid_list[list]); + f2fs_bug_on(sbi, state != i->state); + nm_i->nid_cnt[state]++; + if (state == FREE_NID) + list_add_tail(&i->list, &nm_i->free_nid_list); return 0; } -static void __remove_nid_from_list(struct f2fs_sb_info *sbi, - struct free_nid *i, enum nid_list list, bool reuse) +static void __remove_free_nid(struct f2fs_sb_info *sbi, + struct free_nid *i, enum nid_state state) { struct f2fs_nm_info *nm_i = NM_I(sbi); - f2fs_bug_on(sbi, list == FREE_NID_LIST ? i->state != NID_NEW : - i->state != NID_ALLOC); - nm_i->nid_cnt[list]--; - list_del(&i->list); - if (!reuse) - radix_tree_delete(&nm_i->free_nid_root, i->nid); + f2fs_bug_on(sbi, state != i->state); + nm_i->nid_cnt[state]--; + if (state == FREE_NID) + list_del(&i->list); + radix_tree_delete(&nm_i->free_nid_root, i->nid); +} + +static void __move_free_nid(struct f2fs_sb_info *sbi, struct free_nid *i, + enum nid_state org_state, enum nid_state dst_state) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + + f2fs_bug_on(sbi, org_state != i->state); + i->state = dst_state; + nm_i->nid_cnt[org_state]--; + nm_i->nid_cnt[dst_state]++; + + switch (dst_state) { + case PREALLOC_NID: + list_del(&i->list); + break; + case FREE_NID: + list_add_tail(&i->list, &nm_i->free_nid_list); + break; + default: + BUG_ON(1); + } } /* return if the nid is recognized as free */ @@ -1816,7 +1850,7 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS); i->nid = nid; - i->state = NID_NEW; + i->state = FREE_NID; if (radix_tree_preload(GFP_NOFS)) goto err; @@ -1829,7 +1863,7 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) * - f2fs_create * - f2fs_new_inode * - alloc_nid - * - __insert_nid_to_list(ALLOC_NID_LIST) + * - __insert_nid_to_list(PREALLOC_NID) * - f2fs_balance_fs_bg * - build_free_nids * - __build_free_nids @@ -1842,8 +1876,8 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) * - new_node_page * - set_node_addr * - alloc_nid_done - * - __remove_nid_from_list(ALLOC_NID_LIST) - * - __insert_nid_to_list(FREE_NID_LIST) + * - __remove_nid_from_list(PREALLOC_NID) + * - __insert_nid_to_list(FREE_NID) */ ne = __lookup_nat_cache(nm_i, nid); if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) || @@ -1852,13 +1886,13 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) e = __lookup_free_nid_list(nm_i, nid); if (e) { - if (e->state == NID_NEW) + if (e->state == FREE_NID) ret = true; goto err_out; } } ret = true; - err = __insert_nid_to_list(sbi, i, FREE_NID_LIST, true); + err = __insert_free_nid(sbi, i, FREE_NID); err_out: spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); @@ -1876,8 +1910,8 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); - if (i && i->state == NID_NEW) { - __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); + if (i && i->state == FREE_NID) { + __remove_free_nid(sbi, i, FREE_NID); need_free = true; } spin_unlock(&nm_i->nid_list_lock); @@ -1896,15 +1930,18 @@ static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) return; - if (set) + if (set) { + if (test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - else - __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - - if (set) nm_i->free_nid_count[nat_ofs]++; - else if (!build) - nm_i->free_nid_count[nat_ofs]--; + } else { + if (!test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; + __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + if (!build) + nm_i->free_nid_count[nat_ofs]--; + } } static void scan_nat_page(struct f2fs_sb_info *sbi, @@ -1939,12 +1976,32 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, } } -static void scan_free_nid_bits(struct f2fs_sb_info *sbi) +static void scan_curseg_cache(struct f2fs_sb_info *sbi) { - struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = curseg->journal; + int i; + + down_read(&curseg->journal_rwsem); + for (i = 0; i < nats_in_cursum(journal); i++) { + block_t addr; + nid_t nid; + + addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); + nid = le32_to_cpu(nid_in_journal(journal, i)); + if (addr == NULL_ADDR) + add_free_nid(sbi, nid, true); + else + remove_free_nid(sbi, nid); + } + up_read(&curseg->journal_rwsem); +} + +static void scan_free_nid_bits(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int i, idx; + nid_t nid; down_read(&nm_i->nat_tree_lock); @@ -1954,40 +2011,27 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) if (!nm_i->free_nid_count[i]) continue; for (idx = 0; idx < NAT_ENTRY_PER_BLOCK; idx++) { - nid_t nid; - - if (!test_bit_le(idx, nm_i->free_nid_bitmap[i])) - continue; + idx = find_next_bit_le(nm_i->free_nid_bitmap[i], + NAT_ENTRY_PER_BLOCK, idx); + if (idx >= NAT_ENTRY_PER_BLOCK) + break; nid = i * NAT_ENTRY_PER_BLOCK + idx; add_free_nid(sbi, nid, true); - if (nm_i->nid_cnt[FREE_NID_LIST] >= MAX_FREE_NIDS) + if (nm_i->nid_cnt[FREE_NID] >= MAX_FREE_NIDS) goto out; } } out: - down_read(&curseg->journal_rwsem); - for (i = 0; i < nats_in_cursum(journal); i++) { - block_t addr; - nid_t nid; + scan_curseg_cache(sbi); - addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); - nid = le32_to_cpu(nid_in_journal(journal, i)); - if (addr == NULL_ADDR) - add_free_nid(sbi, nid, true); - else - remove_free_nid(sbi, nid); - } - up_read(&curseg->journal_rwsem); up_read(&nm_i->nat_tree_lock); } static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) { struct f2fs_nm_info *nm_i = NM_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_journal *journal = curseg->journal; int i = 0; nid_t nid = nm_i->next_scan_nid; @@ -1995,7 +2039,7 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) nid = 0; /* Enough entries */ - if (nm_i->nid_cnt[FREE_NID_LIST] >= NAT_ENTRY_PER_BLOCK) + if (nm_i->nid_cnt[FREE_NID] >= NAT_ENTRY_PER_BLOCK) return; if (!sync && !available_free_memory(sbi, FREE_NIDS)) @@ -2005,7 +2049,7 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) /* try to find free nids in free_nid_bitmap */ scan_free_nid_bits(sbi); - if (nm_i->nid_cnt[FREE_NID_LIST]) + if (nm_i->nid_cnt[FREE_NID] >= NAT_ENTRY_PER_BLOCK) return; } @@ -2033,18 +2077,8 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) nm_i->next_scan_nid = nid; /* find free nids from current sum_pages */ - down_read(&curseg->journal_rwsem); - for (i = 0; i < nats_in_cursum(journal); i++) { - block_t addr; + scan_curseg_cache(sbi); - addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); - nid = le32_to_cpu(nid_in_journal(journal, i)); - if (addr == NULL_ADDR) - add_free_nid(sbi, nid, true); - else - remove_free_nid(sbi, nid); - } - up_read(&curseg->journal_rwsem); up_read(&nm_i->nat_tree_lock); ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid), @@ -2082,15 +2116,13 @@ retry: } /* We should not use stale free nids created by build_free_nids */ - if (nm_i->nid_cnt[FREE_NID_LIST] && !on_build_free_nids(nm_i)) { - f2fs_bug_on(sbi, list_empty(&nm_i->nid_list[FREE_NID_LIST])); - i = list_first_entry(&nm_i->nid_list[FREE_NID_LIST], + if (nm_i->nid_cnt[FREE_NID] && !on_build_free_nids(nm_i)) { + f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list)); + i = list_first_entry(&nm_i->free_nid_list, struct free_nid, list); *nid = i->nid; - __remove_nid_from_list(sbi, i, FREE_NID_LIST, true); - i->state = NID_ALLOC; - __insert_nid_to_list(sbi, i, ALLOC_NID_LIST, false); + __move_free_nid(sbi, i, FREE_NID, PREALLOC_NID); nm_i->available_nids--; update_free_nid_bitmap(sbi, *nid, false, false); @@ -2116,7 +2148,7 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid) spin_lock(&nm_i->nid_list_lock); i = __lookup_free_nid_list(nm_i, nid); f2fs_bug_on(sbi, !i); - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, false); + __remove_free_nid(sbi, i, PREALLOC_NID); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); @@ -2139,12 +2171,10 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) f2fs_bug_on(sbi, !i); if (!available_free_memory(sbi, FREE_NIDS)) { - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, false); + __remove_free_nid(sbi, i, PREALLOC_NID); need_free = true; } else { - __remove_nid_from_list(sbi, i, ALLOC_NID_LIST, true); - i->state = NID_NEW; - __insert_nid_to_list(sbi, i, FREE_NID_LIST, false); + __move_free_nid(sbi, i, PREALLOC_NID, FREE_NID); } nm_i->available_nids++; @@ -2163,20 +2193,19 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) struct free_nid *i, *next; int nr = nr_shrink; - if (nm_i->nid_cnt[FREE_NID_LIST] <= MAX_FREE_NIDS) + if (nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS) return 0; if (!mutex_trylock(&nm_i->build_lock)) return 0; spin_lock(&nm_i->nid_list_lock); - list_for_each_entry_safe(i, next, &nm_i->nid_list[FREE_NID_LIST], - list) { + list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) { if (nr_shrink <= 0 || - nm_i->nid_cnt[FREE_NID_LIST] <= MAX_FREE_NIDS) + nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS) break; - __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); + __remove_free_nid(sbi, i, FREE_NID); kmem_cache_free(free_nid_slab, i); nr_shrink--; } @@ -2202,8 +2231,8 @@ void recover_inline_xattr(struct inode *inode, struct page *page) goto update_inode; } - dst_addr = inline_xattr_addr(ipage); - src_addr = inline_xattr_addr(page); + dst_addr = inline_xattr_addr(inode, ipage); + src_addr = inline_xattr_addr(inode, page); inline_size = inline_xattr_size(inode); f2fs_wait_on_page_writeback(ipage, NODE, true); @@ -2292,6 +2321,12 @@ retry: dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR); if (dst->i_inline & F2FS_EXTRA_ATTR) { dst->i_extra_isize = src->i_extra_isize; + + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb) && + F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), + i_inline_xattr_size)) + dst->i_inline_xattr_size = src->i_inline_xattr_size; + if (f2fs_sb_has_project_quota(sbi->sb) && F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), i_projid)) @@ -2363,8 +2398,8 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi) ne = __lookup_nat_cache(nm_i, nid); if (!ne) { - ne = grab_nat_entry(nm_i, nid, true); - node_info_from_raw_nat(&ne->ni, &raw_ne); + ne = __alloc_nat_entry(nid, true); + __init_nat_entry(nm_i, ne, &raw_ne, true); } /* @@ -2410,15 +2445,17 @@ static void __update_nat_bits(struct f2fs_sb_info *sbi, nid_t start_nid, unsigned int nat_index = start_nid / NAT_ENTRY_PER_BLOCK; struct f2fs_nat_block *nat_blk = page_address(page); int valid = 0; - int i; + int i = 0; if (!enabled_nat_bits(sbi, NULL)) return; - for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) { - if (start_nid == 0 && i == 0) - valid++; - if (nat_blk->entries[i].block_addr) + if (nat_index == 0) { + valid = 1; + i = 1; + } + for (; i < NAT_ENTRY_PER_BLOCK; i++) { + if (nat_blk->entries[i].block_addr != NULL_ADDR) valid++; } if (valid == 0) { @@ -2613,7 +2650,7 @@ static inline void load_free_nid_bitmap(struct f2fs_sb_info *sbi) __set_bit_le(i, nm_i->nat_block_bitmap); nid = i * NAT_ENTRY_PER_BLOCK; - last_nid = (i + 1) * NAT_ENTRY_PER_BLOCK; + last_nid = nid + NAT_ENTRY_PER_BLOCK; spin_lock(&NM_I(sbi)->nid_list_lock); for (; nid < last_nid; nid++) @@ -2648,16 +2685,15 @@ static int init_node_manager(struct f2fs_sb_info *sbi) /* not used nids: 0, node, meta, (and root counted as valid node) */ nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count - F2FS_RESERVED_NODE_NUM; - nm_i->nid_cnt[FREE_NID_LIST] = 0; - nm_i->nid_cnt[ALLOC_NID_LIST] = 0; + nm_i->nid_cnt[FREE_NID] = 0; + nm_i->nid_cnt[PREALLOC_NID] = 0; nm_i->nat_cnt = 0; nm_i->ram_thresh = DEF_RAM_THRESHOLD; nm_i->ra_nid_pages = DEF_RA_NID_PAGES; nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD; INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); - INIT_LIST_HEAD(&nm_i->nid_list[FREE_NID_LIST]); - INIT_LIST_HEAD(&nm_i->nid_list[ALLOC_NID_LIST]); + INIT_LIST_HEAD(&nm_i->free_nid_list); INIT_RADIX_TREE(&nm_i->nat_root, GFP_NOIO); INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_NOIO); INIT_LIST_HEAD(&nm_i->nat_entries); @@ -2749,16 +2785,15 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) /* destroy free nid list */ spin_lock(&nm_i->nid_list_lock); - list_for_each_entry_safe(i, next_i, &nm_i->nid_list[FREE_NID_LIST], - list) { - __remove_nid_from_list(sbi, i, FREE_NID_LIST, false); + list_for_each_entry_safe(i, next_i, &nm_i->free_nid_list, list) { + __remove_free_nid(sbi, i, FREE_NID); spin_unlock(&nm_i->nid_list_lock); kmem_cache_free(free_nid_slab, i); spin_lock(&nm_i->nid_list_lock); } - f2fs_bug_on(sbi, nm_i->nid_cnt[FREE_NID_LIST]); - f2fs_bug_on(sbi, nm_i->nid_cnt[ALLOC_NID_LIST]); - f2fs_bug_on(sbi, !list_empty(&nm_i->nid_list[ALLOC_NID_LIST])); + f2fs_bug_on(sbi, nm_i->nid_cnt[FREE_NID]); + f2fs_bug_on(sbi, nm_i->nid_cnt[PREALLOC_NID]); + f2fs_bug_on(sbi, !list_empty(&nm_i->free_nid_list)); spin_unlock(&nm_i->nid_list_lock); /* destroy nat cache */ diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index bb53e9955ff2..0ee3e5ff49a3 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -140,6 +140,7 @@ enum mem_type { DIRTY_DENTS, /* indicates dirty dentry pages */ INO_ENTRIES, /* indicates inode entries */ EXTENT_CACHE, /* indicates extent cache */ + INMEM_PAGES, /* indicates inmemory pages */ BASE_CHECK, /* check kernel status */ }; @@ -150,18 +151,10 @@ struct nat_entry_set { unsigned int entry_cnt; /* the # of nat entries in set */ }; -/* - * For free nid mangement - */ -enum nid_state { - NID_NEW, /* newly added to free nid list */ - NID_ALLOC /* it is allocated */ -}; - struct free_nid { struct list_head list; /* for free node id list */ nid_t nid; /* node id */ - int state; /* in use or not: NID_NEW or NID_ALLOC */ + int state; /* in use or not: FREE_NID or PREALLOC_NID */ }; static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) @@ -170,12 +163,11 @@ static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) struct free_nid *fnid; spin_lock(&nm_i->nid_list_lock); - if (nm_i->nid_cnt[FREE_NID_LIST] <= 0) { + if (nm_i->nid_cnt[FREE_NID] <= 0) { spin_unlock(&nm_i->nid_list_lock); return; } - fnid = list_first_entry(&nm_i->nid_list[FREE_NID_LIST], - struct free_nid, list); + fnid = list_first_entry(&nm_i->free_nid_list, struct free_nid, list); *nid = fnid->nid; spin_unlock(&nm_i->nid_list_lock); } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index bc7503ed742e..559904e9868f 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -582,6 +582,9 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) int ret = 0; unsigned long s_flags = sbi->sb->s_flags; bool need_writecp = false; +#ifdef CONFIG_QUOTA + int quota_enabled; +#endif if (s_flags & MS_RDONLY) { f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); @@ -592,7 +595,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) /* Needed for iput() to work correctly and not trash data */ sbi->sb->s_flags |= MS_ACTIVE; /* Turn on quotas so that they are updated correctly */ - f2fs_enable_quota_files(sbi); + quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY); #endif fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", @@ -653,7 +656,8 @@ skip: out: #ifdef CONFIG_QUOTA /* Turn quotas off */ - f2fs_quota_off_umount(sbi->sb); + if (quota_enabled) + f2fs_quota_off_umount(sbi->sb); #endif sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index bb159226bf36..c126195a993a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -181,11 +181,12 @@ bool need_SSR(struct f2fs_sb_info *sbi) return true; return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + - 2 * reserved_sections(sbi)); + SM_I(sbi)->min_ssr_sections + reserved_sections(sbi)); } void register_inmem_page(struct inode *inode, struct page *page) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); struct inmem_pages *new; @@ -204,6 +205,10 @@ void register_inmem_page(struct inode *inode, struct page *page) mutex_lock(&fi->inmem_lock); get_page(page); list_add_tail(&new->list, &fi->inmem_pages); + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (list_empty(&fi->inmem_ilist)) + list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); mutex_unlock(&fi->inmem_lock); @@ -262,12 +267,41 @@ next: return err; } +void drop_inmem_pages_all(struct f2fs_sb_info *sbi) +{ + struct list_head *head = &sbi->inode_list[ATOMIC_FILE]; + struct inode *inode; + struct f2fs_inode_info *fi; +next: + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (list_empty(head)) { + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); + return; + } + fi = list_first_entry(head, struct f2fs_inode_info, inmem_ilist); + inode = igrab(&fi->vfs_inode); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); + + if (inode) { + drop_inmem_pages(inode); + iput(inode); + } + congestion_wait(BLK_RW_ASYNC, HZ/50); + cond_resched(); + goto next; +} + void drop_inmem_pages(struct inode *inode) { + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); mutex_lock(&fi->inmem_lock); __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (!list_empty(&fi->inmem_ilist)) + list_del_init(&fi->inmem_ilist); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); mutex_unlock(&fi->inmem_lock); clear_inode_flag(inode, FI_ATOMIC_FILE); @@ -313,6 +347,7 @@ static int __commit_inmem_pages(struct inode *inode, struct inmem_pages *cur, *tmp; struct f2fs_io_info fio = { .sbi = sbi, + .ino = inode->i_ino, .type = DATA, .op = REQ_OP_WRITE, .op_flags = REQ_SYNC | REQ_PRIO, @@ -398,6 +433,10 @@ int commit_inmem_pages(struct inode *inode) /* drop all uncommitted pages */ __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); } + spin_lock(&sbi->inode_lock[ATOMIC_FILE]); + if (!list_empty(&fi->inmem_ilist)) + list_del_init(&fi->inmem_ilist); + spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); mutex_unlock(&fi->inmem_lock); clear_inode_flag(inode, FI_ATOMIC_COMMIT); @@ -472,7 +511,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) static int __submit_flush_wait(struct f2fs_sb_info *sbi, struct block_device *bdev) { - struct bio *bio = f2fs_bio_alloc(0); + struct bio *bio = f2fs_bio_alloc(sbi, 0, true); int ret; bio->bi_rw = REQ_OP_WRITE; @@ -485,15 +524,17 @@ static int __submit_flush_wait(struct f2fs_sb_info *sbi, return ret; } -static int submit_flush_wait(struct f2fs_sb_info *sbi) +static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino) { - int ret = __submit_flush_wait(sbi, sbi->sb->s_bdev); + int ret = 0; int i; - if (!sbi->s_ndevs || ret) - return ret; + if (!sbi->s_ndevs) + return __submit_flush_wait(sbi, sbi->sb->s_bdev); - for (i = 1; i < sbi->s_ndevs; i++) { + for (i = 0; i < sbi->s_ndevs; i++) { + if (!is_dirty_device(sbi, ino, i, FLUSH_INO)) + continue; ret = __submit_flush_wait(sbi, FDEV(i).bdev); if (ret) break; @@ -519,7 +560,9 @@ repeat: fcc->dispatch_list = llist_del_all(&fcc->issue_list); fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); - ret = submit_flush_wait(sbi); + cmd = llist_entry(fcc->dispatch_list, struct flush_cmd, llnode); + + ret = submit_flush_wait(sbi, cmd->ino); atomic_inc(&fcc->issued_flush); llist_for_each_entry_safe(cmd, next, @@ -537,7 +580,7 @@ repeat: goto repeat; } -int f2fs_issue_flush(struct f2fs_sb_info *sbi) +int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino) { struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; struct flush_cmd cmd; @@ -547,19 +590,20 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) return 0; if (!test_opt(sbi, FLUSH_MERGE)) { - ret = submit_flush_wait(sbi); + ret = submit_flush_wait(sbi, ino); atomic_inc(&fcc->issued_flush); return ret; } - if (atomic_inc_return(&fcc->issing_flush) == 1) { - ret = submit_flush_wait(sbi); + if (atomic_inc_return(&fcc->issing_flush) == 1 || sbi->s_ndevs > 1) { + ret = submit_flush_wait(sbi, ino); atomic_dec(&fcc->issing_flush); atomic_inc(&fcc->issued_flush); return ret; } + cmd.ino = ino; init_completion(&cmd.wait); llist_add(&cmd.llnode, &fcc->issue_list); @@ -583,7 +627,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) } else { struct flush_cmd *tmp, *next; - ret = submit_flush_wait(sbi); + ret = submit_flush_wait(sbi, ino); llist_for_each_entry_safe(tmp, next, list, llnode) { if (tmp == &cmd) { @@ -653,6 +697,28 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) } } +int f2fs_flush_device_cache(struct f2fs_sb_info *sbi) +{ + int ret = 0, i; + + if (!sbi->s_ndevs) + return 0; + + for (i = 1; i < sbi->s_ndevs; i++) { + if (!f2fs_test_bit(i, (char *)&sbi->dirty_device)) + continue; + ret = __submit_flush_wait(sbi, FDEV(i).bdev); + if (ret) + break; + + spin_lock(&sbi->dev_lock); + f2fs_clear_bit(i, (char *)&sbi->dirty_device); + spin_unlock(&sbi->dev_lock); + } + + return ret; +} + static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, enum dirty_type dirty_type) { @@ -794,6 +860,8 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len); + f2fs_bug_on(sbi, dc->ref); if (dc->error == -EOPNOTSUPP) @@ -875,7 +943,8 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, if (ret) return ret; } - bio = f2fs_bio_alloc(1); + + bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, 1); bio->bi_iter.bi_sector = sector; bio->bi_bdev = bdev; bio_set_op_attrs(bio, op, 0); @@ -926,10 +995,14 @@ void __check_sit_bitmap(struct f2fs_sb_info *sbi, /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, - struct discard_cmd *dc) + struct discard_policy *dpolicy, + struct discard_cmd *dc) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? + &(dcc->fstrim_list) : &(dcc->wait_list); struct bio *bio = NULL; + int flag = dpolicy->sync ? REQ_SYNC : 0; if (dc->state != D_PREP) return; @@ -948,8 +1021,8 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi, if (bio) { bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; - submit_bio(REQ_SYNC, bio); - list_move_tail(&dc->list, &dcc->wait_list); + submit_bio(flag, bio); + list_move_tail(&dc->list, wait_list); __check_sit_bitmap(sbi, dc->start, dc->start + dc->len); f2fs_update_iostat(sbi, FS_DISCARD, 1); @@ -966,7 +1039,7 @@ static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi, struct rb_node *insert_parent) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct rb_node **p = &dcc->root.rb_node; + struct rb_node **p; struct rb_node *parent = NULL; struct discard_cmd *dc = NULL; @@ -1134,58 +1207,107 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, return 0; } -static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond) +static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy, + unsigned int start, unsigned int end) +{ + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct discard_cmd *prev_dc = NULL, *next_dc = NULL; + struct rb_node **insert_p = NULL, *insert_parent = NULL; + struct discard_cmd *dc; + struct blk_plug plug; + int issued; + +next: + issued = 0; + + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); + + dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root, + NULL, start, + (struct rb_entry **)&prev_dc, + (struct rb_entry **)&next_dc, + &insert_p, &insert_parent, true); + if (!dc) + dc = next_dc; + + blk_start_plug(&plug); + + while (dc && dc->lstart <= end) { + struct rb_node *node; + + if (dc->len < dpolicy->granularity) + goto skip; + + if (dc->state != D_PREP) { + list_move_tail(&dc->list, &dcc->fstrim_list); + goto skip; + } + + __submit_discard_cmd(sbi, dpolicy, dc); + + if (++issued >= dpolicy->max_requests) { + start = dc->lstart + dc->len; + + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); + + schedule(); + + goto next; + } +skip: + node = rb_next(&dc->rb_node); + dc = rb_entry_safe(node, struct discard_cmd, rb_node); + + if (fatal_signal_pending(current)) + break; + } + + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); +} + +static int __issue_discard_cmd(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *pend_list; struct discard_cmd *dc, *tmp; struct blk_plug plug; - int iter = 0, issued = 0; - int i; + int i, iter = 0, issued = 0; bool io_interrupted = false; - mutex_lock(&dcc->cmd_lock); - f2fs_bug_on(sbi, - !__check_rb_tree_consistence(sbi, &dcc->root)); - blk_start_plug(&plug); - for (i = MAX_PLIST_NUM - 1; - i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) { + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + if (i + 1 < dpolicy->granularity) + break; pend_list = &dcc->pend_list[i]; + + mutex_lock(&dcc->cmd_lock); + f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); + blk_start_plug(&plug); list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); - /* Hurry up to finish fstrim */ - if (dcc->pend_list_tag[i] & P_TRIM) { - __submit_discard_cmd(sbi, dc); - issued++; - - if (fatal_signal_pending(current)) - break; - continue; - } - - if (!issue_cond) { - __submit_discard_cmd(sbi, dc); - issued++; - continue; - } - - if (is_idle(sbi)) { - __submit_discard_cmd(sbi, dc); - issued++; - } else { + if (dpolicy->io_aware && i < dpolicy->io_aware_gran && + !is_idle(sbi)) { io_interrupted = true; + goto skip; } - if (++iter >= DISCARD_ISSUE_RATE) - goto out; + __submit_discard_cmd(sbi, dpolicy, dc); + issued++; +skip: + if (++iter >= dpolicy->max_requests) + break; } - if (list_empty(pend_list) && dcc->pend_list_tag[i] & P_TRIM) - dcc->pend_list_tag[i] &= (~P_TRIM); + blk_finish_plug(&plug); + mutex_unlock(&dcc->cmd_lock); + + if (iter >= dpolicy->max_requests) + break; } -out: - blk_finish_plug(&plug); - mutex_unlock(&dcc->cmd_lock); if (!issued && io_interrupted) issued = -1; @@ -1193,12 +1315,13 @@ out: return issued; } -static void __drop_discard_cmd(struct f2fs_sb_info *sbi) +static bool __drop_discard_cmd(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct list_head *pend_list; struct discard_cmd *dc, *tmp; int i; + bool dropped = false; mutex_lock(&dcc->cmd_lock); for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { @@ -1206,39 +1329,58 @@ static void __drop_discard_cmd(struct f2fs_sb_info *sbi) list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); __remove_discard_cmd(sbi, dc); + dropped = true; } } mutex_unlock(&dcc->cmd_lock); + + return dropped; } -static void __wait_one_discard_bio(struct f2fs_sb_info *sbi, +static unsigned int __wait_one_discard_bio(struct f2fs_sb_info *sbi, struct discard_cmd *dc) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + unsigned int len = 0; wait_for_completion_io(&dc->wait); mutex_lock(&dcc->cmd_lock); f2fs_bug_on(sbi, dc->state != D_DONE); dc->ref--; - if (!dc->ref) + if (!dc->ref) { + if (!dc->error) + len = dc->len; __remove_discard_cmd(sbi, dc); + } mutex_unlock(&dcc->cmd_lock); + + return len; } -static void __wait_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond) +static unsigned int __wait_discard_cmd_range(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy, + block_t start, block_t end) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - struct list_head *wait_list = &(dcc->wait_list); + struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? + &(dcc->fstrim_list) : &(dcc->wait_list); struct discard_cmd *dc, *tmp; bool need_wait; + unsigned int trimmed = 0; next: need_wait = false; mutex_lock(&dcc->cmd_lock); list_for_each_entry_safe(dc, tmp, wait_list, list) { - if (!wait_cond || (dc->state == D_DONE && !dc->ref)) { + if (dc->lstart + dc->len <= start || end <= dc->lstart) + continue; + if (dc->len < dpolicy->granularity) + continue; + if (dc->state == D_DONE && !dc->ref) { wait_for_completion_io(&dc->wait); + if (!dc->error) + trimmed += dc->len; __remove_discard_cmd(sbi, dc); } else { dc->ref++; @@ -1249,9 +1391,17 @@ next: mutex_unlock(&dcc->cmd_lock); if (need_wait) { - __wait_one_discard_bio(sbi, dc); + trimmed += __wait_one_discard_bio(sbi, dc); goto next; } + + return trimmed; +} + +static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, + struct discard_policy *dpolicy) +{ + __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX); } /* This should be covered by global mutex, &sit_i->sentry_lock */ @@ -1289,23 +1439,19 @@ void stop_discard_thread(struct f2fs_sb_info *sbi) } } -/* This comes from f2fs_put_super and f2fs_trim_fs */ -void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi, bool umount) -{ - __issue_discard_cmd(sbi, false); - __drop_discard_cmd(sbi); - __wait_discard_cmd(sbi, !umount); -} - -static void mark_discard_range_all(struct f2fs_sb_info *sbi) +/* This comes from f2fs_put_super */ +bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; - int i; + struct discard_policy dpolicy; + bool dropped; - mutex_lock(&dcc->cmd_lock); - for (i = 0; i < MAX_PLIST_NUM; i++) - dcc->pend_list_tag[i] |= P_TRIM; - mutex_unlock(&dcc->cmd_lock); + init_discard_policy(&dpolicy, DPOLICY_UMOUNT, dcc->discard_granularity); + __issue_discard_cmd(sbi, &dpolicy); + dropped = __drop_discard_cmd(sbi); + __wait_all_discard_cmd(sbi, &dpolicy); + + return dropped; } static int issue_discard_thread(void *data) @@ -1313,12 +1459,16 @@ static int issue_discard_thread(void *data) struct f2fs_sb_info *sbi = data; struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; wait_queue_head_t *q = &dcc->discard_wait_queue; + struct discard_policy dpolicy; unsigned int wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; int issued; set_freezable(); do { + init_discard_policy(&dpolicy, DPOLICY_BG, + dcc->discard_granularity); + wait_event_interruptible_timeout(*q, kthread_should_stop() || freezing(current) || dcc->discard_wake, @@ -1331,17 +1481,18 @@ static int issue_discard_thread(void *data) if (dcc->discard_wake) { dcc->discard_wake = 0; if (sbi->gc_thread && sbi->gc_thread->gc_urgent) - mark_discard_range_all(sbi); + init_discard_policy(&dpolicy, + DPOLICY_FORCE, 1); } sb_start_intwrite(sbi->sb); - issued = __issue_discard_cmd(sbi, true); + issued = __issue_discard_cmd(sbi, &dpolicy); if (issued) { - __wait_discard_cmd(sbi, true); - wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; + __wait_all_discard_cmd(sbi, &dpolicy); + wait_ms = dpolicy.min_interval; } else { - wait_ms = DEF_MAX_DISCARD_ISSUE_TIME; + wait_ms = dpolicy.max_interval; } sb_end_intwrite(sbi->sb); @@ -1605,7 +1756,6 @@ find_next: f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, len); - cpc->trimmed += len; total_len += len; } else { next_pos = find_next_bit_le(entry->discard_map, @@ -1626,6 +1776,37 @@ skip: wake_up_discard_thread(sbi, false); } +void init_discard_policy(struct discard_policy *dpolicy, + int discard_type, unsigned int granularity) +{ + /* common policy */ + dpolicy->type = discard_type; + dpolicy->sync = true; + dpolicy->granularity = granularity; + + if (discard_type == DPOLICY_BG) { + dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; + dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = true; + } else if (discard_type == DPOLICY_FORCE) { + dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; + dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = true; + } else if (discard_type == DPOLICY_FSTRIM) { + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = false; + } else if (discard_type == DPOLICY_UMOUNT) { + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + dpolicy->io_aware = false; + } +} + static int create_discard_cmd_control(struct f2fs_sb_info *sbi) { dev_t dev = sbi->sb->s_bdev->bd_dev; @@ -1643,12 +1824,10 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) dcc->discard_granularity = DEFAULT_DISCARD_GRANULARITY; INIT_LIST_HEAD(&dcc->entry_list); - for (i = 0; i < MAX_PLIST_NUM; i++) { + for (i = 0; i < MAX_PLIST_NUM; i++) INIT_LIST_HEAD(&dcc->pend_list[i]); - if (i >= dcc->discard_granularity - 1) - dcc->pend_list_tag[i] |= P_ACTIVE; - } INIT_LIST_HEAD(&dcc->wait_list); + INIT_LIST_HEAD(&dcc->fstrim_list); mutex_init(&dcc->cmd_lock); atomic_set(&dcc->issued_discard, 0); atomic_set(&dcc->issing_discard, 0); @@ -1796,16 +1975,6 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) get_sec_entry(sbi, segno)->valid_blocks += del; } -void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new) -{ - update_sit_entry(sbi, new, 1); - if (GET_SEGNO(sbi, old) != NULL_SEGNO) - update_sit_entry(sbi, old, -1); - - locate_dirty_segment(sbi, GET_SEGNO(sbi, old)); - locate_dirty_segment(sbi, GET_SEGNO(sbi, new)); -} - void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) { unsigned int segno = GET_SEGNO(sbi, addr); @@ -1816,14 +1985,14 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) return; /* add it into sit main buffer */ - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); update_sit_entry(sbi, addr, -1); /* add it into dirty seglist */ locate_dirty_segment(sbi, segno); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); } bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) @@ -1836,7 +2005,7 @@ bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) return true; - mutex_lock(&sit_i->sentry_lock); + down_read(&sit_i->sentry_lock); segno = GET_SEGNO(sbi, blkaddr); se = get_seg_entry(sbi, segno); @@ -1845,7 +2014,7 @@ bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) if (f2fs_test_bit(offset, se->ckpt_valid_map)) is_cp = true; - mutex_unlock(&sit_i->sentry_lock); + up_read(&sit_i->sentry_lock); return is_cp; } @@ -1903,12 +2072,8 @@ struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr) { struct page *page = grab_meta_page(sbi, blk_addr); - void *dst = page_address(page); - if (src) - memcpy(dst, src, PAGE_SIZE); - else - memset(dst, 0, PAGE_SIZE); + memcpy(page_address(page), src, PAGE_SIZE); set_page_dirty(page); f2fs_put_page(page, 1); } @@ -2007,7 +2172,6 @@ find_other_zone: } secno = left_start; skip_left: - hint = secno; segno = GET_SEG_FROM_SEC(sbi, secno); zoneno = GET_ZONE_FROM_SEC(sbi, secno); @@ -2242,12 +2406,16 @@ void allocate_new_segments(struct f2fs_sb_info *sbi) unsigned int old_segno; int i; + down_write(&SIT_I(sbi)->sentry_lock); + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { curseg = CURSEG_I(sbi, i); old_segno = curseg->segno; SIT_I(sbi)->s_ops->allocate_segment(sbi, i, true); locate_dirty_segment(sbi, old_segno); } + + up_write(&SIT_I(sbi)->sentry_lock); } static const struct segment_allocation default_salloc_ops = { @@ -2259,14 +2427,14 @@ bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc) __u64 trim_start = cpc->trim_start; bool has_candidate = false; - mutex_lock(&SIT_I(sbi)->sentry_lock); + down_write(&SIT_I(sbi)->sentry_lock); for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { if (add_discard_addrs(sbi, cpc, true)) { has_candidate = true; break; } } - mutex_unlock(&SIT_I(sbi)->sentry_lock); + up_write(&SIT_I(sbi)->sentry_lock); cpc->trim_start = trim_start; return has_candidate; @@ -2276,14 +2444,16 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) { __u64 start = F2FS_BYTES_TO_BLK(range->start); __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; - unsigned int start_segno, end_segno; + unsigned int start_segno, end_segno, cur_segno; + block_t start_block, end_block; struct cp_control cpc; + struct discard_policy dpolicy; + unsigned long long trimmed = 0; int err = 0; if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) return -EINVAL; - cpc.trimmed = 0; if (end <= MAIN_BLKADDR(sbi)) goto out; @@ -2297,12 +2467,14 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start); end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : GET_SEGNO(sbi, end); + cpc.reason = CP_DISCARD; cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); /* do checkpoint to issue discard commands safely */ - for (; start_segno <= end_segno; start_segno = cpc.trim_end + 1) { - cpc.trim_start = start_segno; + for (cur_segno = start_segno; cur_segno <= end_segno; + cur_segno = cpc.trim_end + 1) { + cpc.trim_start = cur_segno; if (sbi->discard_blks == 0) break; @@ -2310,7 +2482,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) cpc.trim_end = end_segno; else cpc.trim_end = min_t(unsigned int, - rounddown(start_segno + + rounddown(cur_segno + BATCHED_TRIM_SEGMENTS(sbi), sbi->segs_per_sec) - 1, end_segno); @@ -2322,11 +2494,16 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) schedule(); } - /* It's time to issue all the filed discards */ - mark_discard_range_all(sbi); - f2fs_wait_discard_bios(sbi, false); + + start_block = START_BLOCK(sbi, start_segno); + end_block = START_BLOCK(sbi, min(cur_segno, end_segno) + 1); + + init_discard_policy(&dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); + __issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block); + trimmed = __wait_discard_cmd_range(sbi, &dpolicy, + start_block, end_block); out: - range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); + range->len = F2FS_BLK_TO_BYTES(trimmed); return err; } @@ -2338,6 +2515,20 @@ static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) return false; } +#if 0 +int rw_hint_to_seg_type(enum rw_hint hint) +{ + switch (hint) { + case WRITE_LIFE_SHORT: + return CURSEG_HOT_DATA; + case WRITE_LIFE_EXTREME: + return CURSEG_COLD_DATA; + default: + return CURSEG_WARM_DATA; + } +} +#endif + static int __get_segment_type_2(struct f2fs_io_info *fio) { if (fio->type == DATA) @@ -2372,6 +2563,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) return CURSEG_COLD_DATA; if (is_inode_flag_set(inode, FI_HOT_DATA)) return CURSEG_HOT_DATA; + + /* rw_hint_to_seg_type(inode->i_write_hint); */ return CURSEG_WARM_DATA; } else { if (IS_DNODE(fio->page)) @@ -2416,8 +2609,10 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, type); + down_read(&SM_I(sbi)->curseg_lock); + mutex_lock(&curseg->curseg_mutex); - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); @@ -2434,15 +2629,26 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, stat_inc_block_count(sbi, curseg); + /* + * SIT information should be updated before segment allocation, + * since SSR needs latest valid block information. + */ + update_sit_entry(sbi, *new_blkaddr, 1); + if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) + update_sit_entry(sbi, old_blkaddr, -1); + if (!__has_curseg_space(sbi, type)) sit_i->s_ops->allocate_segment(sbi, type, false); + /* - * SIT information should be updated after segment allocation, - * since we need to keep dirty segments precisely under SSR. + * segment dirty status should be updated after segment allocation, + * so we just need to update status only one time after previous + * segment being closed. */ - refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr); + locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); + locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr)); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); if (page && IS_NODESEG(type)) { fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); @@ -2462,6 +2668,29 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, } mutex_unlock(&curseg->curseg_mutex); + + up_read(&SM_I(sbi)->curseg_lock); +} + +static void update_device_state(struct f2fs_io_info *fio) +{ + struct f2fs_sb_info *sbi = fio->sbi; + unsigned int devidx; + + if (!sbi->s_ndevs) + return; + + devidx = f2fs_target_device_index(sbi, fio->new_blkaddr); + + /* update device state for fsync */ + set_dirty_device(sbi, fio->ino, devidx, FLUSH_INO); + + /* update device state for checkpoint */ + if (!f2fs_test_bit(devidx, (char *)&sbi->dirty_device)) { + spin_lock(&sbi->dev_lock); + f2fs_set_bit(devidx, (char *)&sbi->dirty_device); + spin_unlock(&sbi->dev_lock); + } } static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) @@ -2478,6 +2707,8 @@ reallocate: if (err == -EAGAIN) { fio->old_blkaddr = fio->new_blkaddr; goto reallocate; + } else if (!err) { + update_device_state(fio); } } @@ -2538,12 +2769,26 @@ int rewrite_data_page(struct f2fs_io_info *fio) stat_inc_inplace_blocks(fio->sbi); err = f2fs_submit_page_bio(fio); + if (!err) + update_device_state(fio); f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); return err; } +static inline int __f2fs_get_curseg(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + int i; + + for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) { + if (CURSEG_I(sbi, i)->segno == segno) + break; + } + return i; +} + void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, block_t old_blkaddr, block_t new_blkaddr, bool recover_curseg, bool recover_newaddr) @@ -2559,6 +2804,8 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, se = get_seg_entry(sbi, segno); type = se->type; + down_write(&SM_I(sbi)->curseg_lock); + if (!recover_curseg) { /* for recovery flow */ if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { @@ -2568,14 +2815,19 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, type = CURSEG_WARM_DATA; } } else { - if (!IS_CURSEG(sbi, segno)) + if (IS_CURSEG(sbi, segno)) { + /* se->type is volatile as SSR allocation */ + type = __f2fs_get_curseg(sbi, segno); + f2fs_bug_on(sbi, type == NO_CHECK_TYPE); + } else { type = CURSEG_WARM_DATA; + } } curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); old_cursegno = curseg->segno; old_blkoff = curseg->next_blkoff; @@ -2607,8 +2859,9 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, curseg->next_blkoff = old_blkoff; } - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); mutex_unlock(&curseg->curseg_mutex); + up_write(&SM_I(sbi)->curseg_lock); } void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, @@ -3062,7 +3315,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) bool to_journal = true; struct seg_entry *se; - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); if (!sit_i->dirty_sentries) goto out; @@ -3156,7 +3409,7 @@ out: cpc->trim_start = trim_start; } - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); set_prefree_as_free_segments(sbi); } @@ -3249,7 +3502,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time); sit_i->mounted_time = CURRENT_TIME_SEC.tv_sec; - mutex_init(&sit_i->sentry_lock); + init_rwsem(&sit_i->sentry_lock); return 0; } @@ -3490,7 +3743,7 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi) struct sit_info *sit_i = SIT_I(sbi); unsigned int segno; - mutex_lock(&sit_i->sentry_lock); + down_write(&sit_i->sentry_lock); sit_i->min_mtime = LLONG_MAX; @@ -3507,7 +3760,7 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi) sit_i->min_mtime = mtime; } sit_i->max_mtime = get_mtime(sbi); - mutex_unlock(&sit_i->sentry_lock); + up_write(&sit_i->sentry_lock); } int build_segment_manager(struct f2fs_sb_info *sbi) @@ -3540,11 +3793,14 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS; + sm_info->min_ssr_sections = reserved_sections(sbi); sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS; INIT_LIST_HEAD(&sm_info->sit_entry_set); + init_rwsem(&sm_info->curseg_lock); + if (!f2fs_readonly(sbi->sb)) { err = create_flush_cmd_control(sbi); if (err) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 593b99cee4d1..806e7b7866df 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -230,7 +230,7 @@ struct sit_info { unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */ unsigned int dirty_sentries; /* # of dirty sentries */ unsigned int sents_per_block; /* # of SIT entries per block */ - struct mutex sentry_lock; /* to protect SIT cache */ + struct rw_semaphore sentry_lock; /* to protect SIT cache */ struct seg_entry *sentries; /* SIT segment-level cache */ struct sec_entry *sec_entries; /* SIT section-level cache */ @@ -496,6 +496,33 @@ static inline int reserved_sections(struct f2fs_sb_info *sbi) return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi)); } +static inline bool has_curseg_enough_space(struct f2fs_sb_info *sbi) +{ + unsigned int node_blocks = get_pages(sbi, F2FS_DIRTY_NODES) + + get_pages(sbi, F2FS_DIRTY_DENTS); + unsigned int dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS); + unsigned int segno, left_blocks; + int i; + + /* check current node segment */ + for (i = CURSEG_HOT_NODE; i <= CURSEG_COLD_NODE; i++) { + segno = CURSEG_I(sbi, i)->segno; + left_blocks = sbi->blocks_per_seg - + get_seg_entry(sbi, segno)->ckpt_valid_blocks; + + if (node_blocks > left_blocks) + return false; + } + + /* check current data segment */ + segno = CURSEG_I(sbi, CURSEG_HOT_DATA)->segno; + left_blocks = sbi->blocks_per_seg - + get_seg_entry(sbi, segno)->ckpt_valid_blocks; + if (dent_blocks > left_blocks) + return false; + return true; +} + static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed, int needed) { @@ -506,6 +533,9 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return false; + if (free_sections(sbi) + freed == reserved_sections(sbi) + needed && + has_curseg_enough_space(sbi)) + return false; return (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + imeta_secs + reserved_sections(sbi) + needed); @@ -729,7 +759,7 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi, unsigned int secno) { - if (get_valid_blocks(sbi, GET_SEG_FROM_SEC(sbi, secno), true) >= + if (get_valid_blocks(sbi, GET_SEG_FROM_SEC(sbi, secno), true) > sbi->fggc_threshold) return true; return false; @@ -794,8 +824,9 @@ static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force) goto wake_up; mutex_lock(&dcc->cmd_lock); - for (i = MAX_PLIST_NUM - 1; - i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) { + for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { + if (i + 1 < dcc->discard_granularity) + break; if (!list_empty(&dcc->pend_list[i])) { wakeup = true; break; diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index 5c60fc28ec75..0b5664a1a6cc 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -28,7 +28,7 @@ static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi) static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) { - long count = NM_I(sbi)->nid_cnt[FREE_NID_LIST] - MAX_FREE_NIDS; + long count = NM_I(sbi)->nid_cnt[FREE_NID] - MAX_FREE_NIDS; return count > 0 ? count : 0; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9472358cc9e9..b06b3ead8cb1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -47,6 +47,8 @@ static struct kmem_cache *f2fs_inode_cachep; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", [FAULT_PAGE_ALLOC] = "page alloc", + [FAULT_PAGE_GET] = "page get", + [FAULT_ALLOC_BIO] = "alloc bio", [FAULT_ALLOC_NID] = "alloc nid", [FAULT_ORPHAN] = "orphan", [FAULT_BLOCK] = "no more block", @@ -95,6 +97,7 @@ enum { Opt_disable_ext_identify, Opt_inline_xattr, Opt_noinline_xattr, + Opt_inline_xattr_size, Opt_inline_data, Opt_inline_dentry, Opt_noinline_dentry, @@ -144,6 +147,7 @@ static match_table_t f2fs_tokens = { {Opt_disable_ext_identify, "disable_ext_identify"}, {Opt_inline_xattr, "inline_xattr"}, {Opt_noinline_xattr, "noinline_xattr"}, + {Opt_inline_xattr_size, "inline_xattr_size=%u"}, {Opt_inline_data, "inline_data"}, {Opt_inline_dentry, "inline_dentry"}, {Opt_noinline_dentry, "noinline_dentry"}, @@ -215,6 +219,12 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, "quota options when quota turned on"); return -EINVAL; } + if (f2fs_sb_has_quota_ino(sb)) { + f2fs_msg(sb, KERN_INFO, + "QUOTA feature is enabled, so ignore qf_name"); + return 0; + } + qname = match_strdup(args); if (!qname) { f2fs_msg(sb, KERN_ERR, @@ -298,6 +308,18 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) return -1; } } + + if (f2fs_sb_has_quota_ino(sbi->sb) && sbi->s_jquota_fmt) { + f2fs_msg(sbi->sb, KERN_INFO, + "QUOTA feature is enabled, so ignore jquota_fmt"); + sbi->s_jquota_fmt = 0; + } + if (f2fs_sb_has_quota_ino(sbi->sb) && sb_rdonly(sbi->sb)) { + f2fs_msg(sbi->sb, KERN_INFO, + "Filesystem with quota feature cannot be mounted RDWR " + "without CONFIG_QUOTA"); + return -1; + } return 0; } #endif @@ -394,6 +416,12 @@ static int parse_options(struct super_block *sb, char *options) case Opt_noinline_xattr: clear_opt(sbi, INLINE_XATTR); break; + case Opt_inline_xattr_size: + if (args->from && match_int(args, &arg)) + return -EINVAL; + set_opt(sbi, INLINE_XATTR_SIZE); + sbi->inline_xattr_size = arg; + break; #else case Opt_user_xattr: f2fs_msg(sb, KERN_INFO, @@ -620,6 +648,24 @@ static int parse_options(struct super_block *sb, char *options) F2FS_IO_SIZE_KB(sbi)); return -EINVAL; } + + if (test_opt(sbi, INLINE_XATTR_SIZE)) { + if (!test_opt(sbi, INLINE_XATTR)) { + f2fs_msg(sb, KERN_ERR, + "inline_xattr_size option should be " + "set with inline_xattr option"); + return -EINVAL; + } + if (!sbi->inline_xattr_size || + sbi->inline_xattr_size >= DEF_ADDRS_PER_INODE - + F2FS_TOTAL_EXTRA_ATTR_SIZE - + DEF_INLINE_RESERVED_SIZE - + DEF_MIN_INLINE_SIZE) { + f2fs_msg(sb, KERN_ERR, + "inline xattr size is out of range"); + return -EINVAL; + } + } return 0; } @@ -634,13 +680,13 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) init_once((void *) fi); /* Initialize f2fs-specific inode info */ - fi->vfs_inode.i_version = 1; atomic_set(&fi->dirty_pages, 0); fi->i_current_depth = 1; fi->i_advise = 0; init_rwsem(&fi->i_sem); INIT_LIST_HEAD(&fi->dirty_list); INIT_LIST_HEAD(&fi->gdirty_list); + INIT_LIST_HEAD(&fi->inmem_ilist); INIT_LIST_HEAD(&fi->inmem_pages); mutex_init(&fi->inmem_lock); init_rwsem(&fi->dio_rwsem[READ]); @@ -688,7 +734,6 @@ static int f2fs_drop_inode(struct inode *inode) sb_end_intwrite(inode->i_sb); - fscrypt_put_encryption_info(inode, NULL); spin_lock(&inode->i_lock); atomic_dec(&inode->i_count); } @@ -796,6 +841,7 @@ static void f2fs_put_super(struct super_block *sb) { struct f2fs_sb_info *sbi = F2FS_SB(sb); int i; + bool dropped; f2fs_quota_off_umount(sb); @@ -816,9 +862,9 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - f2fs_wait_discard_bios(sbi, true); + dropped = f2fs_wait_discard_bios(sbi); - if (f2fs_discard_en(sbi) && !sbi->discard_blks) { + if (f2fs_discard_en(sbi) && !sbi->discard_blks && !dropped) { struct cp_control cpc = { .reason = CP_UMOUNT | CP_TRIMMED, }; @@ -874,6 +920,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync) struct f2fs_sb_info *sbi = F2FS_SB(sb); int err = 0; + if (unlikely(f2fs_cp_error(sbi))) + return 0; + trace_f2fs_sync_fs(sb, sync); if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) @@ -973,7 +1022,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = total_count - start_count; buf->f_bfree = user_block_count - valid_user_blocks(sbi) + ovp_count; buf->f_bavail = user_block_count - valid_user_blocks(sbi) - - sbi->reserved_blocks; + sbi->current_reserved_blocks; avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; @@ -1062,6 +1111,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",inline_xattr"); else seq_puts(seq, ",noinline_xattr"); + if (test_opt(sbi, INLINE_XATTR_SIZE)) + seq_printf(seq, ",inline_xattr_size=%u", + sbi->inline_xattr_size); #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL if (test_opt(sbi, POSIX_ACL)) @@ -1124,6 +1176,7 @@ static void default_options(struct f2fs_sb_info *sbi) { /* init some FS parameters */ sbi->active_logs = NR_CURSEG_TYPE; + sbi->inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; set_opt(sbi, BG_GC); set_opt(sbi, INLINE_XATTR); @@ -1152,6 +1205,9 @@ static void default_options(struct f2fs_sb_info *sbi) #endif } +#ifdef CONFIG_QUOTA +static int f2fs_enable_quotas(struct super_block *sb); +#endif static int f2fs_remount(struct super_block *sb, int *flags, char *data) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -1218,6 +1274,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) if (f2fs_readonly(sb) && (*flags & MS_RDONLY)) goto skip; +#ifdef CONFIG_QUOTA if (!f2fs_readonly(sb) && (*flags & MS_RDONLY)) { err = dquot_suspend(sb, -1); if (err < 0) @@ -1225,9 +1282,15 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) } else { /* dquot_resume needs RW */ sb->s_flags &= ~MS_RDONLY; - dquot_resume(sb, -1); + if (sb_any_quota_suspended(sb)) { + dquot_resume(sb, -1); + } else if (f2fs_sb_has_quota_ino(sb)) { + err = f2fs_enable_quotas(sb); + if (err) + goto restore_opts; + } } - +#endif /* disallow enable/disable extent_cache dynamically */ if (no_extent_cache == !!test_opt(sbi, EXTENT_CACHE)) { err = -EINVAL; @@ -1336,8 +1399,13 @@ static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data, tocopy = min_t(unsigned long, sb->s_blocksize - offset, toread); repeat: page = read_mapping_page(mapping, blkidx, NULL); - if (IS_ERR(page)) + if (IS_ERR(page)) { + if (PTR_ERR(page) == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto repeat; + } return PTR_ERR(page); + } lock_page(page); @@ -1380,11 +1448,16 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type, while (towrite > 0) { tocopy = min_t(unsigned long, sb->s_blocksize - offset, towrite); - +retry: err = a_ops->write_begin(NULL, mapping, off, tocopy, 0, &page, NULL); - if (unlikely(err)) + if (unlikely(err)) { + if (err == -ENOMEM) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry; + } break; + } kaddr = kmap_atomic(page); memcpy(kaddr + offset, data, tocopy); @@ -1401,8 +1474,7 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type, } if (len == towrite) - return 0; - inode->i_version++; + return err; inode->i_mtime = inode->i_ctime = current_time(inode); f2fs_mark_inode_dirty_sync(inode, false); return len - towrite; @@ -1419,19 +1491,93 @@ static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type) sbi->s_jquota_fmt, type); } -void f2fs_enable_quota_files(struct f2fs_sb_info *sbi) +int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly) { - int i, ret; + int enabled = 0; + int i, err; + + if (f2fs_sb_has_quota_ino(sbi->sb) && rdonly) { + err = f2fs_enable_quotas(sbi->sb); + if (err) { + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on quota_ino: %d", err); + return 0; + } + return 1; + } for (i = 0; i < F2FS_MAXQUOTAS; i++) { if (sbi->s_qf_names[i]) { - ret = f2fs_quota_on_mount(sbi, i); - if (ret < 0) - f2fs_msg(sbi->sb, KERN_ERR, - "Cannot turn on journaled " - "quota: error %d", ret); + err = f2fs_quota_on_mount(sbi, i); + if (!err) { + enabled = 1; + continue; + } + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on quotas: %d on %d", err, i); + } + } + return enabled; +} + +static int f2fs_quota_enable(struct super_block *sb, int type, int format_id, + unsigned int flags) +{ + struct inode *qf_inode; + unsigned long qf_inum; + int err; + + BUG_ON(!f2fs_sb_has_quota_ino(sb)); + + qf_inum = f2fs_qf_ino(sb, type); + if (!qf_inum) + return -EPERM; + + qf_inode = f2fs_iget(sb, qf_inum); + if (IS_ERR(qf_inode)) { + f2fs_msg(sb, KERN_ERR, + "Bad quota inode %u:%lu", type, qf_inum); + return PTR_ERR(qf_inode); + } + + /* Don't account quota for quota files to avoid recursion */ + qf_inode->i_flags |= S_NOQUOTA; + err = dquot_enable(qf_inode, type, format_id, flags); + iput(qf_inode); + return err; +} + +static int f2fs_enable_quotas(struct super_block *sb) +{ + int type, err = 0; + unsigned long qf_inum; + bool quota_mopt[MAXQUOTAS] = { + test_opt(F2FS_SB(sb), USRQUOTA), + test_opt(F2FS_SB(sb), GRPQUOTA), +#if 0 /* not support */ + test_opt(F2FS_SB(sb), PRJQUOTA), +#endif + }; + + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE; + for (type = 0; type < MAXQUOTAS; type++) { + qf_inum = f2fs_qf_ino(sb, type); + if (qf_inum) { + err = f2fs_quota_enable(sb, type, QFMT_VFS_V1, + DQUOT_USAGE_ENABLED | + (quota_mopt[type] ? DQUOT_LIMITS_ENABLED : 0)); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to enable quota tracking " + "(type=%d, err=%d). Please run " + "fsck to fix.", type, err); + for (type--; type >= 0; type--) + dquot_quota_off(sb, type); + return err; + } } } + return 0; } static int f2fs_quota_sync(struct super_block *sb, int type) @@ -1491,6 +1637,21 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id, return 0; } +/* + * quota_on function that is used when QUOTA feature is set. + */ +static int f2fs_quota_on_sysfile(struct super_block *sb, int type, + int format_id) +{ + if (!f2fs_sb_has_quota_ino(sb)) + return -EINVAL; + + /* + * USAGE was enabled at mount time. Only need to enable LIMITS now. + */ + return f2fs_quota_enable(sb, type, format_id, DQUOT_LIMITS_ENABLED); +} + static int f2fs_quota_off(struct super_block *sb, int type) { struct inode *inode = sb_dqopt(sb)->files[type]; @@ -1502,7 +1663,7 @@ static int f2fs_quota_off(struct super_block *sb, int type) f2fs_quota_sync(sb, type); err = dquot_quota_off(sb, type); - if (err) + if (err || f2fs_sb_has_quota_ino(sb)) goto out_put; inode_lock(inode); @@ -1515,6 +1676,18 @@ out_put: return err; } +/* + * quota_off function that is used when QUOTA feature is set. + */ +static int f2fs_quota_off_sysfile(struct super_block *sb, int type) +{ + if (!f2fs_sb_has_quota_ino(sb)) + return -EINVAL; + + /* Disable only the limits. */ + return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED); +} + void f2fs_quota_off_umount(struct super_block *sb) { int type; @@ -1553,6 +1726,16 @@ static const struct quotactl_ops f2fs_quotactl_ops = { .get_dqblk = dquot_get_dqblk, .set_dqblk = dquot_set_dqblk, }; + +static const struct quotactl_ops f2fs_quotactl_sysfile_ops = { + .quota_on_meta = f2fs_quota_on_sysfile, + .quota_off = f2fs_quota_off_sysfile, + .quota_sync = dquot_quota_sync, + .get_info = dquot_get_dqinfo, + .set_info = dquot_set_dqinfo, + .get_dqblk = dquot_get_dqblk, + .set_dqblk = dquot_set_dqblk +}; #else void f2fs_quota_off_umount(struct super_block *sb) { @@ -1667,7 +1850,7 @@ static loff_t max_file_blocks(void) /* * note: previously, result is equal to (DEF_ADDRS_PER_INODE - - * F2FS_INLINE_XATTR_ADDRS), but now f2fs try to reserve more + * DEFAULT_INLINE_XATTR_ADDRS), but now f2fs try to reserve more * space in inode.i_addr, it will be more safe to reassign * result as zero. */ @@ -1976,6 +2159,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi) for (j = HOT; j < NR_TEMP_TYPE; j++) mutex_init(&sbi->wio_mutex[i][j]); spin_lock_init(&sbi->cp_lock); + + sbi->dirty_device = 0; + spin_lock_init(&sbi->dev_lock); } static int init_percpu_info(struct f2fs_sb_info *sbi) @@ -2330,7 +2516,10 @@ try_onemore: #ifdef CONFIG_QUOTA sb->dq_op = &f2fs_quota_operations; - sb->s_qcop = &f2fs_quotactl_ops; + if (f2fs_sb_has_quota_ino(sb)) + sb->s_qcop = &f2fs_quotactl_sysfile_ops; + else + sb->s_qcop = &f2fs_quotactl_ops; #if 0 /* not support */ sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; #endif @@ -2428,6 +2617,7 @@ try_onemore: le64_to_cpu(sbi->ckpt->valid_block_count); sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->reserved_blocks = 0; + sbi->current_reserved_blocks = 0; for (i = 0; i < NR_INODE_TYPE; i++) { INIT_LIST_HEAD(&sbi->inode_list[i]); @@ -2502,10 +2692,24 @@ try_onemore: if (err) goto free_root_inode; +#ifdef CONFIG_QUOTA + /* + * Turn on quotas which were not enabled for read-only mounts if + * filesystem has quota feature, so that they are updated correctly. + */ + if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb)) { + err = f2fs_enable_quotas(sb); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Cannot turn on quotas: error %d", err); + goto free_sysfs; + } + } +#endif /* if there are nt orphan nodes free them */ err = recover_orphan_inodes(sbi); if (err) - goto free_sysfs; + goto free_meta; /* recover fsynced data */ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { @@ -2539,7 +2743,7 @@ try_onemore: err = -EINVAL; f2fs_msg(sb, KERN_ERR, "Need to recover fsync data"); - goto free_sysfs; + goto free_meta; } } skip_recovery: @@ -2573,6 +2777,10 @@ skip_recovery: return 0; free_meta: +#ifdef CONFIG_QUOTA + if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb)) + f2fs_quota_off_umount(sbi->sb); +#endif f2fs_sync_inode_meta(sbi); /* * Some dirty meta pages can be produced by recover_orphan_inodes() @@ -2581,7 +2789,9 @@ free_meta: * falls into an infinite loop in sync_meta_pages(). */ truncate_inode_pages_final(META_MAPPING(sbi)); +#ifdef CONFIG_QUOTA free_sysfs: +#endif f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 06fda7cc7123..5c14bcaa19a4 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -30,7 +30,7 @@ enum { FAULT_INFO_RATE, /* struct f2fs_fault_info */ FAULT_INFO_TYPE, /* struct f2fs_fault_info */ #endif - RESERVED_BLOCKS, + RESERVED_BLOCKS, /* struct f2fs_sb_info */ }; struct f2fs_attr { @@ -63,6 +63,13 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) return NULL; } +static ssize_t dirty_segments_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%llu\n", + (unsigned long long)(dirty_segments(sbi))); +} + static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { @@ -100,10 +107,22 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_inode_chksum(sb)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "inode_checksum"); + if (f2fs_sb_has_flexible_inline_xattr(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "flexible_inline_xattr"); + if (f2fs_sb_has_quota_ino(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "quota_ino"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } +static ssize_t current_reserved_blocks_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", sbi->current_reserved_blocks); +} + static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { @@ -143,34 +162,22 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, #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) { + if (t > (unsigned long)sbi->user_block_count) { spin_unlock(&sbi->stat_lock); return -EINVAL; } *ui = t; + sbi->current_reserved_blocks = min(sbi->reserved_blocks, + sbi->user_block_count - valid_user_blocks(sbi)); 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; } @@ -222,6 +229,8 @@ enum feat_id { FEAT_EXTRA_ATTR, FEAT_PROJECT_QUOTA, FEAT_INODE_CHECKSUM, + FEAT_FLEXIBLE_INLINE_XATTR, + FEAT_QUOTA_INO, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -234,6 +243,8 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_EXTRA_ATTR: case FEAT_PROJECT_QUOTA: case FEAT_INODE_CHECKSUM: + case FEAT_FLEXIBLE_INLINE_XATTR: + case FEAT_QUOTA_INO: return snprintf(buf, PAGE_SIZE, "supported\n"); } return 0; @@ -279,6 +290,7 @@ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_blocks); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ssr_sections, min_ssr_sections); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages); F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio); @@ -291,8 +303,10 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); #endif +F2FS_GENERAL_RO_ATTR(dirty_segments); F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); F2FS_GENERAL_RO_ATTR(features); +F2FS_GENERAL_RO_ATTR(current_reserved_blocks); #ifdef CONFIG_F2FS_FS_ENCRYPTION F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); @@ -304,6 +318,8 @@ F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE); F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); +F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); +F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -321,6 +337,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(min_ipu_util), ATTR_LIST(min_fsync_blocks), ATTR_LIST(min_hot_blocks), + ATTR_LIST(min_ssr_sections), ATTR_LIST(max_victim_search), ATTR_LIST(dir_level), ATTR_LIST(ram_thresh), @@ -333,9 +350,11 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(inject_rate), ATTR_LIST(inject_type), #endif + ATTR_LIST(dirty_segments), ATTR_LIST(lifetime_write_kbytes), ATTR_LIST(features), ATTR_LIST(reserved_blocks), + ATTR_LIST(current_reserved_blocks), NULL, }; @@ -350,6 +369,8 @@ static struct attribute *f2fs_feat_attrs[] = { ATTR_LIST(extra_attr), ATTR_LIST(project_quota), ATTR_LIST(inode_checksum), + ATTR_LIST(flexible_inline_xattr), + ATTR_LIST(quota_ino), NULL, }; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index ec48fe87414b..56c80831976b 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -266,12 +266,12 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, return entry; } -static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr, - void **last_addr, int index, - size_t len, const char *name) +static struct f2fs_xattr_entry *__find_inline_xattr(struct inode *inode, + void *base_addr, void **last_addr, int index, + size_t len, const char *name) { struct f2fs_xattr_entry *entry; - unsigned int inline_size = F2FS_INLINE_XATTR_ADDRS << 2; + unsigned int inline_size = inline_xattr_size(inode); list_for_each_xattr(entry, base_addr) { if ((void *)entry + sizeof(__u32) > base_addr + inline_size || @@ -290,12 +290,54 @@ static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr, return entry; } +static int read_inline_xattr(struct inode *inode, struct page *ipage, + void *txattr_addr) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + unsigned int inline_size = inline_xattr_size(inode); + struct page *page = NULL; + void *inline_addr; + + if (ipage) { + inline_addr = inline_xattr_addr(inode, ipage); + } else { + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) + return PTR_ERR(page); + + inline_addr = inline_xattr_addr(inode, page); + } + memcpy(txattr_addr, inline_addr, inline_size); + f2fs_put_page(page, 1); + + return 0; +} + +static int read_xattr_block(struct inode *inode, void *txattr_addr) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + nid_t xnid = F2FS_I(inode)->i_xattr_nid; + unsigned int inline_size = inline_xattr_size(inode); + struct page *xpage; + void *xattr_addr; + + /* The inode already has an extended attribute block. */ + xpage = get_node_page(sbi, xnid); + if (IS_ERR(xpage)) + return PTR_ERR(xpage); + + xattr_addr = page_address(xpage); + memcpy(txattr_addr + inline_size, xattr_addr, VALID_XATTR_BLOCK_SIZE); + f2fs_put_page(xpage, 1); + + return 0; +} + static int lookup_all_xattrs(struct inode *inode, struct page *ipage, unsigned int index, unsigned int len, const char *name, struct f2fs_xattr_entry **xe, void **base_addr) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); void *cur_addr, *txattr_addr, *last_addr = NULL; nid_t xnid = F2FS_I(inode)->i_xattr_nid; unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0; @@ -312,23 +354,11 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, /* read from inline xattr */ if (inline_size) { - struct page *page = NULL; - void *inline_addr; - - if (ipage) { - inline_addr = inline_xattr_addr(ipage); - } else { - page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) { - err = PTR_ERR(page); - goto out; - } - inline_addr = inline_xattr_addr(page); - } - memcpy(txattr_addr, inline_addr, inline_size); - f2fs_put_page(page, 1); + err = read_inline_xattr(inode, ipage, txattr_addr); + if (err) + goto out; - *xe = __find_inline_xattr(txattr_addr, &last_addr, + *xe = __find_inline_xattr(inode, txattr_addr, &last_addr, index, len, name); if (*xe) goto check; @@ -336,19 +366,9 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, /* read from xattr node block */ if (xnid) { - struct page *xpage; - void *xattr_addr; - - /* The inode already has an extended attribute block. */ - xpage = get_node_page(sbi, xnid); - if (IS_ERR(xpage)) { - err = PTR_ERR(xpage); + err = read_xattr_block(inode, txattr_addr); + if (err) goto out; - } - - xattr_addr = page_address(xpage); - memcpy(txattr_addr + inline_size, xattr_addr, size); - f2fs_put_page(xpage, 1); } if (last_addr) @@ -373,7 +393,6 @@ out: static int read_all_xattrs(struct inode *inode, struct page *ipage, void **base_addr) { - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_xattr_header *header; nid_t xnid = F2FS_I(inode)->i_xattr_nid; unsigned int size = VALID_XATTR_BLOCK_SIZE; @@ -388,38 +407,16 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, /* read from inline xattr */ if (inline_size) { - struct page *page = NULL; - void *inline_addr; - - if (ipage) { - inline_addr = inline_xattr_addr(ipage); - } else { - page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) { - err = PTR_ERR(page); - goto fail; - } - inline_addr = inline_xattr_addr(page); - } - memcpy(txattr_addr, inline_addr, inline_size); - f2fs_put_page(page, 1); + err = read_inline_xattr(inode, ipage, txattr_addr); + if (err) + goto fail; } /* read from xattr node block */ if (xnid) { - struct page *xpage; - void *xattr_addr; - - /* The inode already has an extended attribute block. */ - xpage = get_node_page(sbi, xnid); - if (IS_ERR(xpage)) { - err = PTR_ERR(xpage); + err = read_xattr_block(inode, txattr_addr); + if (err) goto fail; - } - - xattr_addr = page_address(xpage); - memcpy(txattr_addr + inline_size, xattr_addr, size); - f2fs_put_page(xpage, 1); } header = XATTR_HDR(txattr_addr); @@ -441,10 +438,12 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); size_t inline_size = inline_xattr_size(inode); + struct page *in_page = NULL; void *xattr_addr; + void *inline_addr = NULL; struct page *xpage; nid_t new_nid = 0; - int err; + int err = 0; if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid) if (!alloc_nid(sbi, &new_nid)) @@ -452,30 +451,30 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, /* write to inline xattr */ if (inline_size) { - struct page *page = NULL; - void *inline_addr; - if (ipage) { - inline_addr = inline_xattr_addr(ipage); - f2fs_wait_on_page_writeback(ipage, NODE, true); - set_page_dirty(ipage); + inline_addr = inline_xattr_addr(inode, ipage); } else { - page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) { + in_page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(in_page)) { alloc_nid_failed(sbi, new_nid); - return PTR_ERR(page); + return PTR_ERR(in_page); } - inline_addr = inline_xattr_addr(page); - f2fs_wait_on_page_writeback(page, NODE, true); + inline_addr = inline_xattr_addr(inode, in_page); } - memcpy(inline_addr, txattr_addr, inline_size); - f2fs_put_page(page, 1); + f2fs_wait_on_page_writeback(ipage ? ipage : in_page, + NODE, true); /* no need to use xattr node block */ if (hsize <= inline_size) { - err = truncate_xattr_node(inode, ipage); + err = truncate_xattr_node(inode); alloc_nid_failed(sbi, new_nid); - return err; + if (err) { + f2fs_put_page(in_page, 1); + return err; + } + memcpy(inline_addr, txattr_addr, inline_size); + set_page_dirty(ipage ? ipage : in_page); + goto in_page_out; } } @@ -484,7 +483,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); if (IS_ERR(xpage)) { alloc_nid_failed(sbi, new_nid); - return PTR_ERR(xpage); + goto in_page_out; } f2fs_bug_on(sbi, new_nid); f2fs_wait_on_page_writeback(xpage, NODE, true); @@ -494,17 +493,24 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, xpage = new_node_page(&dn, XATTR_NODE_OFFSET); if (IS_ERR(xpage)) { alloc_nid_failed(sbi, new_nid); - return PTR_ERR(xpage); + goto in_page_out; } alloc_nid_done(sbi, new_nid); } - xattr_addr = page_address(xpage); + + if (inline_size) + memcpy(inline_addr, txattr_addr, inline_size); memcpy(xattr_addr, txattr_addr + inline_size, VALID_XATTR_BLOCK_SIZE); + + if (inline_size) + set_page_dirty(ipage ? ipage : in_page); set_page_dirty(xpage); - f2fs_put_page(xpage, 1); - return 0; + f2fs_put_page(xpage, 1); +in_page_out: + f2fs_put_page(in_page, 1); + return err; } int f2fs_getxattr(struct inode *inode, int index, const char *name, @@ -723,6 +729,8 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); int err; + dquot_initialize(inode); + /* this case is only from init_inode_metadata */ if (ipage) return __f2fs_setxattr(inode, index, name, value, diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 00cdb19429d5..30dc9d69d60e 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -36,6 +36,8 @@ #define F2FS_NODE_INO(sbi) (sbi->node_ino_num) #define F2FS_META_INO(sbi) (sbi->meta_ino_num) +#define F2FS_MAX_QUOTAS 3 + #define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */ #define F2FS_IO_SIZE_KB(sbi) (1 << ((sbi)->write_io_size_bits + 2)) /* KB */ #define F2FS_IO_SIZE_BYTES(sbi) (1 << ((sbi)->write_io_size_bits + 12)) /* B */ @@ -108,7 +110,8 @@ struct f2fs_super_block { __u8 encryption_level; /* versioning level for encryption */ __u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */ struct f2fs_device devs[MAX_DEVICES]; /* device list */ - __u8 reserved[327]; /* valid reserved region */ + __le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */ + __u8 reserved[315]; /* valid reserved region */ } __packed; /* @@ -184,7 +187,8 @@ struct f2fs_extent { } __packed; #define F2FS_NAME_LEN 255 -#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ +/* 200 bytes for inline xattrs by default */ +#define DEFAULT_INLINE_XATTR_ADDRS 50 #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ get_extra_isize(inode)) @@ -238,7 +242,7 @@ struct f2fs_inode { union { struct { __le16 i_extra_isize; /* extra inode attribute size */ - __le16 i_padding; /* padding */ + __le16 i_inline_xattr_size; /* inline xattr size, unit: 4 bytes */ __le32 i_projid; /* project id */ __le32 i_inode_checksum;/* inode meta checksum */ __le32 i_extra_end[0]; /* for attribute size calculation */ diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 8fa678a2c335..7e117885dde5 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -91,6 +91,18 @@ { CP_DISCARD, "Discard" }, \ { CP_UMOUNT | CP_TRIMMED, "Umount,Trimmed" }) +#define show_fsync_cpreason(type) \ + __print_symbolic(type, \ + { CP_NO_NEEDED, "no needed" }, \ + { CP_NON_REGULAR, "non regular" }, \ + { CP_HARDLINK, "hardlink" }, \ + { CP_SB_NEED_CP, "sb needs cp" }, \ + { CP_WRONG_PINO, "wrong pino" }, \ + { CP_NO_SPC_ROLL, "no space roll forward" }, \ + { CP_NODE_NEED_CP, "node needs cp" }, \ + { CP_FASTBOOT_MODE, "fastboot mode" }, \ + { CP_SPEC_LOG_NUM, "log type is 2" }) + struct victim_sel_policy; struct f2fs_map_blocks; @@ -165,14 +177,14 @@ DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter, TRACE_EVENT(f2fs_sync_file_exit, - TP_PROTO(struct inode *inode, int need_cp, int datasync, int ret), + TP_PROTO(struct inode *inode, int cp_reason, int datasync, int ret), - TP_ARGS(inode, need_cp, datasync, ret), + TP_ARGS(inode, cp_reason, datasync, ret), TP_STRUCT__entry( __field(dev_t, dev) __field(ino_t, ino) - __field(int, need_cp) + __field(int, cp_reason) __field(int, datasync) __field(int, ret) ), @@ -180,15 +192,15 @@ TRACE_EVENT(f2fs_sync_file_exit, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->need_cp = need_cp; + __entry->cp_reason = cp_reason; __entry->datasync = datasync; __entry->ret = ret; ), - TP_printk("dev = (%d,%d), ino = %lu, checkpoint is %s, " + TP_printk("dev = (%d,%d), ino = %lu, cp_reason: %s, " "datasync = %d, ret = %d", show_dev_ino(__entry), - __entry->need_cp ? "needed" : "not needed", + show_fsync_cpreason(__entry->cp_reason), __entry->datasync, __entry->ret) ); @@ -679,6 +691,91 @@ TRACE_EVENT(f2fs_get_victim, __entry->free) ); +TRACE_EVENT(f2fs_lookup_start, + + TP_PROTO(struct inode *dir, struct dentry *dentry, unsigned int flags), + + TP_ARGS(dir, dentry, flags), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(const char *, name) + __field(unsigned int, flags) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->name = dentry->d_name.name; + __entry->flags = flags; + ), + + TP_printk("dev = (%d,%d), pino = %lu, name:%s, flags:%u", + show_dev_ino(__entry), + __entry->name, + __entry->flags) +); + +TRACE_EVENT(f2fs_lookup_end, + + TP_PROTO(struct inode *dir, struct dentry *dentry, nid_t ino, + int err), + + TP_ARGS(dir, dentry, ino, err), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(const char *, name) + __field(nid_t, cino) + __field(int, err) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->name = dentry->d_name.name; + __entry->cino = ino; + __entry->err = err; + ), + + TP_printk("dev = (%d,%d), pino = %lu, name:%s, ino:%u, err:%d", + show_dev_ino(__entry), + __entry->name, + __entry->cino, + __entry->err) +); + +TRACE_EVENT(f2fs_readdir, + + TP_PROTO(struct inode *dir, loff_t start_pos, loff_t end_pos, int err), + + TP_ARGS(dir, start_pos, end_pos, err), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, start) + __field(loff_t, end) + __field(int, err) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->start = start_pos; + __entry->end = end_pos; + __entry->err = err; + ), + + TP_printk("dev = (%d,%d), ino = %lu, start_pos:%llu, end_pos:%llu, err:%d", + show_dev_ino(__entry), + __entry->start, + __entry->end, + __entry->err) +); + TRACE_EVENT(f2fs_fallocate, TP_PROTO(struct inode *inode, int mode, @@ -1237,6 +1334,13 @@ DEFINE_EVENT(f2fs_discard, f2fs_issue_discard, TP_ARGS(dev, blkstart, blklen) ); +DEFINE_EVENT(f2fs_discard, f2fs_remove_discard, + + TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen), + + TP_ARGS(dev, blkstart, blklen) +); + TRACE_EVENT(f2fs_issue_reset_zone, TP_PROTO(struct block_device *dev, block_t blkstart), -- GitLab From e2b3ed0ff72d15aeb76977ea10dc9fef4f7cd6d8 Mon Sep 17 00:00:00 2001 From: Swetha Chikkaboraiah Date: Fri, 3 Nov 2017 15:29:55 +0530 Subject: [PATCH 1730/5498] ARM: dts: msm: add support for mirror lake touch panel on msm8917 Add new DT files to support for MirrorLake HD+ (720x1440p) touch panel for msm8917 CDP. CRs-Fixed: 2107916 Change-Id: Ib9fc8ebb7300c55d1fd8a5178fa24c70045816b4 Signed-off-by: Swetha Chikkaboraiah --- arch/arm/boot/dts/qcom/Makefile | 1 + .../qcom/msm8917-cdp-mirror-lake-touch.dtsi | 256 ++++++++++++++++++ .../msm8917-pmi8950-cdp-mirror-lake-touch.dts | 24 ++ ...msm8917-pmi8950-cdp-mirror-lake-touch.dtsi | 100 +++++++ 4 files changed, 381 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi create mode 100644 arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dts create mode 100644 arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dtsi diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 25902b673a6c..7bd2873a5e74 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -218,6 +218,7 @@ dtb-$(CONFIG_ARCH_MSM8917) += msm8917-rumi.dtb \ msm8917-pmi8940-cdp.dtb \ msm8917-pmi8940-mtp.dtb \ msm8917-pmi8950-cdp.dtb \ + msm8917-pmi8950-cdp-mirror-lake-touch.dtb \ msm8917-pmi8950-ext-codec-cdp.dtb \ msm8917-pmi8950-mtp.dtb \ msm8917-pmi8937-rcm.dtb \ diff --git a/arch/arm/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi b/arch/arm/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi new file mode 100644 index 000000000000..00fb8ef7042c --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm8917-pinctrl.dtsi" +#include "msm8917-camera-sensor-cdp.dtsi" + +&soc { + gpio_keys { + compatible = "gpio-keys"; + input-name = "gpio-keys"; + pinctrl-names = "tlmm_gpio_key_active", "tlmm_gpio_key_suspend"; + pinctrl-0 = <&gpio_key_active>; + pinctrl-1 = <&gpio_key_suspend>; + + camera_focus { + label = "camera_focus"; + gpios = <&tlmm 128 0x1>; + linux,input-type = <1>; + linux,code = <0x210>; + debounce-interval = <15>; + }; + + camera_snapshot { + label = "camera_snapshot"; + gpios = <&tlmm 127 0x1>; + linux,input-type = <1>; + linux,code = <0x2fe>; + debounce-interval = <15>; + }; + + vol_up { + label = "volume_up"; + gpios = <&tlmm 91 0x1>; + linux,input-type = <1>; + linux,code = <115>; + debounce-interval = <15>; + }; + + home { + label = "home"; + gpios = <&tlmm 86 0x1>; + linux,input-type = <1>; + linux,code = <102>; + debounce-interval = <15>; + }; + }; + + hbtp { + compatible = "qcom,hbtp-input"; + vcc_ana-supply = <&pm8917_l10>; + vcc_dig-supply = <&pm8917_l5>; + qcom,afe-load = <50000>; + qcom,afe-vtg-min = <2850000>; + qcom,afe-vtg-max = <2850000>; + qcom,dig-load = <15000>; + qcom,dig-vtg-min = <1800000>; + qcom,dig-vtg-max = <1800000>; + }; + + usb_detect { + compatible = "qcom,gpio-usbdetect"; + interrupt-names = "vbus_det_irq"; + interrupt-parent = <&tlmm>; + interrupts = <130 0>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_mode_select>; + qcom,gpio-mode-sel = <&tlmm 130 0>; + qcom,notify-host-mode; + status = "disabled"; + }; +}; + +&flash_led { + compatible = "qcom,qpnp-flash-led"; + reg = <0xd300 0x100>; + pinctrl-names = "flash_led_enable","flash_led_disable"; + pinctrl-0 = <&rear_flash_led_enable>; + pinctrl-1 = <&rear_flash_led_disable>; + qcom,follow-otst2-rb-disabled; +}; + +&wled { + qcom,cons-sync-write-delay-us = <1000>; +}; + +&pmi_haptic{ + qcom,actuator-type = "lra"; + qcom,wave-play-rate-us = <4165>; + qcom,lra-auto-res-mode = "qwd"; + qcom,lra-high-z = "opt1"; + qcom,lra-res-cal-period = <0>; +}; + +&blsp1_uart2 { + status = "ok"; + pinctrl-names = "default"; + pinctrl-0 = <&uart_console_active>; +}; + +#include "msm8937-mdss-panels.dtsi" + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_truly_720_vid>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + + qcom,platform-te-gpio = <&tlmm 24 0>; + qcom,platform-reset-gpio = <&tlmm 60 0>; + qcom,platform-bklight-en-gpio = <&tlmm 98 0>; +}; + +&dsi_truly_720_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; +}; + +&dsi_truly_720_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,ulps-enabled; + qcom,partial-update-enabled; + qcom,panel-roi-alignment = <2 2 2 2 2 2>; +}; + +&dsi_icn9706_720_1440_vid { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; +}; + +&tlmm { + tlmm_gpio_key { + gpio_key_active: gpio_key_active { + mux { + pins = "gpio86", "gpio91", "gpio127", "gpio128"; + function = "gpio"; + }; + + config { + pins = "gpio86", "gpio91", "gpio127", "gpio128"; + }; + }; + + gpio_key_suspend: gpio_key_suspend { + mux { + pins = "gpio86", "gpio91", "gpio127", "gpio128"; + function = "gpio"; + }; + + config { + pins = "gpio86", "gpio91", "gpio127", "gpio128"; + }; + }; + }; +}; + +&sdhc_1 { + /* device core power supply */ + vdd-supply = <&pm8917_l8>; + qcom,vdd-voltage-level = <2900000 2900000>; + qcom,vdd-current-level = <200 570000>; + + /* device communication power supply */ + vdd-io-supply = <&pm8917_l5>; + qcom,vdd-io-always-on; + qcom,vdd-io-lpm-sup; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 325000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>; + pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>; + + qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 192000000 + 384000000>; + qcom,nonremovable; + qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v"; + + status = "ok"; +}; + +&sdhc_2 { + /* device core power supply */ + vdd-supply = <&pm8917_l11>; + qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-current-level = <15000 800000>; + + /* device communication power supply */ + vdd-io-supply = <&pm8917_l12>; + qcom,vdd-io-voltage-level = <1800000 2950000>; + qcom,vdd-io-current-level = <200 22000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>; + + #address-cells = <0>; + interrupt-parent = <&sdhc_2>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 125 0 + 1 &intc 0 221 0 + 2 &tlmm 67 0>; + interrupt-names = "hc_irq", "pwr_irq", "status_irq"; + cd-gpios = <&tlmm 67 0x1>; + + qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 + 200000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; + + status = "ok"; +}; + +&i2c_3 { + status = "okay"; + synaptics@22 { + compatible = "synaptics,dsx"; + reg = <0x22>; + interrupt-parent = <&tlmm>; + interrupts = <65 0x2008>; + avdd-supply = <&pm8917_l10>; + vdd-supply = <&pm8917_l5>; + synaptics,vdd-voltage = <1880000 1880000>; + synaptics,avdd-voltage = <3008000 3008000>; + synaptics,vdd-current = <40000>; + synaptics,avdd-current = <20000>; + pinctrl-names = "pmx_ts_active","pmx_ts_suspend"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + synaptics,display-coords = <0 0 719 1439>; + synaptics,panel-coords = <0 0 719 1439>; + synaptics,reset-gpio = <&tlmm 64 0x00>; + synaptics,irq-gpio = <&tlmm 65 0x2008>; + synaptics,disable-gpios; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dts b/arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dts new file mode 100644 index 000000000000..0a5b628ac4de --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "msm8917.dtsi" +#include "msm8917-pmi8950-cdp-mirror-lake-touch.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM8917-PMI8950 CDP ML Touch"; + compatible = "qcom,msm8917-cdp", "qcom,msm8917", "qcom,cdp"; + qcom,board-id = <1 4>; + qcom,pmic-id = <0x10019 0x010011 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dtsi b/arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dtsi new file mode 100644 index 000000000000..62b7c489eef0 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8917-pmi8950-cdp-mirror-lake-touch.dtsi @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm-pmi8950.dtsi" +#include "msm8917-cdp-mirror-lake-touch.dtsi" +#include "msm8917-audio-cdp.dtsi" + +&soc { + led_flash0: qcom,camera-flash { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-type = <1>; + qcom,flash-source = <&pmi8950_flash0 &pmi8950_flash1>; + qcom,torch-source = <&pmi8950_torch0 &pmi8950_torch1>; + qcom,switch-source = <&pmi8950_switch>; + }; + + bluetooth: bt_qca6174 { + compatible = "qca,qca6174"; + qca,bt-reset-gpio = <&tlmm 129 0>; /* BT_EN */ + }; +}; + +&pm8937_gpios { + gpio@c400 { + qcom,mode = <0>; + qcom,output-type = <0>; + qcom,pull = <0>; + qcom,vin-sel = <2>; + qcom,out-strength = <3>; + qcom,src-sel = <0>; + qcom,master-en = <1>; + status = "okay"; + }; +}; + +&i2c_5 { /* BLSP2 QUP1 */ + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&tlmm 17 0x00>; + qcom,nq-ven = <&tlmm 16 0x00>; + qcom,nq-firm = <&tlmm 130 0x00>; + qcom,nq-clkreq = <&pm8937_gpios 5 0x00>; + interrupt-parent = <&tlmm>; + qcom,clk-src = "BBCLK2"; + interrupts = <17 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active", "nfc_suspend"; + pinctrl-0 = <&nfc_int_active &nfc_disable_active>; + pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>; + clocks = <&clock_gcc clk_bb_clk2_pin>; + clock-names = "ref_clk"; + }; +}; + +&mdss_dsi0 { + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; + +&labibb { + status = "ok"; + qpnp,qpnp-labibb-mode = "lcd"; +}; + +&ibb_regulator { + qcom,qpnp-ibb-discharge-resistor = <32>; +}; + +&dsi_panel_pwr_supply { + qcom,panel-supply-entry@2 { + reg = <2>; + qcom,supply-name = "lab"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@3 { + reg = <3>; + qcom,supply-name = "ibb"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-post-on-sleep = <20>; + }; +}; -- GitLab From 0491ff5a6358aa5593d7eae934bd51b5bc4fd86c Mon Sep 17 00:00:00 2001 From: Kishor PK Date: Mon, 6 Nov 2017 18:09:58 +0530 Subject: [PATCH 1731/5498] ARM: dts: msm: Add support for 8909 cdp,rcm Add device tree supoort for 8909 cdp and rcm targets. Change-Id: Ie25bc2aa895b177987e22a77e0a817bd4f0acb74 Signed-off-by: Kishor PK --- arch/arm/boot/dts/qcom/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 25902b673a6c..c44cb547350f 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -302,6 +302,7 @@ dtb-$(CONFIG_ARCH_MSM8916) += msm8952-qrd-skum.dtb \ msm8952-mtp.dtb dtb-$(CONFIG_ARCH_MSM8909) += msm8909-pm8916-mtp.dtb \ + msm8909-cdp.dtb \ msm8909-1gb-qrd-skuc.dtb \ msm8909-1gb-qrd-skue.dtb \ msm8909-qrd-skue.dtb \ @@ -348,8 +349,10 @@ dtb-$(CONFIG_ARCH_MSM8909) += msm8909-pm8916-mtp.dtb \ apq8009-robot-rome.dtb \ apq8009-mtp-drone.dtb \ msm8909-mtp.dtb \ - msm8909-1gb-mtp.dtb - + msm8909-1gb-mtp.dtb \ + msm8909-1gb-rcm.dtb \ + msm8909-pm8916-1gb-rcm.dtb \ + msm8909-1gb-cdp.dtb ifeq ($(CONFIG_ARM64),y) always := $(dtb-y) -- GitLab From 1a08fca7636b3564b074582591459430f340af5f Mon Sep 17 00:00:00 2001 From: Ashwanth Goli Date: Fri, 24 Nov 2017 12:34:31 +0530 Subject: [PATCH 1732/5498] defconfig: Making changes for treble compatibility This reverts commit 964aa31cf68b978553bf031cd3d0d4fece38deb6 since it removes configs that are needed for treble compatibility. Apart from this a few more configs are enabled to meet this requirement. Change-Id: I4c7675d19e2fdcbce4cfb03ea1be0bea1ee4a12f Signed-off-by: Ashwanth Goli --- arch/arm/configs/msm8909w-perf_defconfig | 55 +++++++++++++++++++++++- arch/arm/configs/msm8909w_defconfig | 55 +++++++++++++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 39be53b46ba0..b51a220b2371 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -63,6 +63,7 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -71,10 +72,12 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y CONFIG_IPV6=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y @@ -86,7 +89,26 @@ CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y @@ -97,8 +119,12 @@ CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -111,29 +137,44 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_MANGLE=y CONFIG_IP_NF_RAW=y CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y CONFIG_NF_LOG_IPV6=y +CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -185,6 +226,18 @@ CONFIG_IFB=y CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 629723f0eb53..344b63463149 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -67,6 +67,7 @@ CONFIG_PM_RUNTIME=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -75,10 +76,12 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set # CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y CONFIG_IPV6=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y @@ -90,7 +93,26 @@ CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_CT=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y @@ -101,8 +123,12 @@ CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DSCP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y @@ -115,29 +141,44 @@ CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y CONFIG_NF_LOG_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y CONFIG_IP_NF_MATCH_TTL=y CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_MANGLE=y CONFIG_IP_NF_RAW=y CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y CONFIG_NF_LOG_IPV6=y +CONFIG_NF_NAT_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -189,6 +230,18 @@ CONFIG_IFB=y CONFIG_TUN=y CONFIG_KS8851=y CONFIG_MSM_RMNET_BAM=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y CONFIG_WCNSS_CORE=y CONFIG_WCNSS_CORE_PRONTO=y CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y -- GitLab From 00192d146c2d87447d24de537c12e0879d387356 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 8 Jun 2017 16:44:21 -0700 Subject: [PATCH 1733/5498] BACKPORT: time: Fix CLOCK_MONOTONIC_RAW sub-nanosecond accounting (cherry pick from commit 3d88d56c5873f6eebe23e05c3da701960146b801) Due to how the MONOTONIC_RAW accumulation logic was handled, there is the potential for a 1ns discontinuity when we do accumulations. This small discontinuity has for the most part gone un-noticed, but since ARM64 enabled CLOCK_MONOTONIC_RAW in their vDSO clock_gettime implementation, we've seen failures with the inconsistency-check test in kselftest. This patch addresses the issue by using the same sub-ns accumulation handling that CLOCK_MONOTONIC uses, which avoids the issue for in-kernel users. Since the ARM64 vDSO implementation has its own clock_gettime calculation logic, this patch reduces the frequency of errors, but failures are still seen. The ARM64 vDSO will need to be updated to include the sub-nanosecond xtime_nsec values in its calculation for this issue to be completely fixed. Signed-off-by: John Stultz Tested-by: Daniel Mentz Cc: Prarit Bhargava Cc: Kevin Brodsky Cc: Richard Cochran Cc: Stephen Boyd Cc: Will Deacon Cc: "stable #4 . 8+" Cc: Miroslav Lichvar Link: http://lkml.kernel.org/r/1496965462-20003-3-git-send-email-john.stultz@linaro.org Signed-off-by: Thomas Gleixner Bug: 20045882 Bug: 63737556 Change-Id: I6c55dd7685f6bd212c6af9d09c527528e1dd5fa1 --- include/linux/timekeeper_internal.h | 4 ++-- kernel/time/timekeeping.c | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index fdb9136ac711..7a6499ecaa4e 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -54,7 +54,7 @@ struct tk_read_base { * interval. * @xtime_remainder: Shifted nano seconds left over when rounding * @cycle_interval - * @raw_interval: Raw nano seconds accumulated per NTP interval. + * @raw_interval: Shifted raw nano seconds accumulated per NTP interval. * @ntp_error: Difference between accumulated time and NTP time in ntp * shifted nano seconds. * @ntp_error_shift: Shift conversion between clock shifted nano seconds and @@ -89,7 +89,7 @@ struct timekeeper { cycle_t cycle_interval; u64 xtime_interval; s64 xtime_remainder; - u32 raw_interval; + u64 raw_interval; /* The ntp_tick_length() value currently being used. * This cached copy ensures we consistently apply the tick * length for an entire tick, as ntp_tick_length may change diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 9ba8641e835c..9e3fe70c0580 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -161,8 +161,7 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) /* Go back from cycles -> shifted ns */ tk->xtime_interval = (u64) interval * clock->mult; tk->xtime_remainder = ntpinterval - tk->xtime_interval; - tk->raw_interval = - ((u64) interval * clock->mult) >> clock->shift; + tk->raw_interval = interval * clock->mult; /* if changing clocks, convert xtime_nsec shift units */ if (old_clock) { @@ -1470,7 +1469,7 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, unsigned int *clock_set) { cycle_t interval = tk->cycle_interval << shift; - u64 raw_nsecs; + u64 snsec_per_sec; /* If the offset is smaller then a shifted interval, do nothing */ if (offset < interval) @@ -1485,14 +1484,15 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, *clock_set |= accumulate_nsecs_to_secs(tk); /* Accumulate raw time */ - raw_nsecs = (u64)tk->raw_interval << shift; - raw_nsecs += tk->raw_time.tv_nsec; - if (raw_nsecs >= NSEC_PER_SEC) { - u64 raw_secs = raw_nsecs; - raw_nsecs = do_div(raw_secs, NSEC_PER_SEC); - tk->raw_time.tv_sec += raw_secs; + tk->tkr_raw.xtime_nsec += (u64)tk->raw_time.tv_nsec << tk->tkr_raw.shift; + tk->tkr_raw.xtime_nsec += tk->raw_interval << shift; + snsec_per_sec = (u64)NSEC_PER_SEC << tk->tkr_raw.shift; + while (tk->tkr_raw.xtime_nsec >= snsec_per_sec) { + tk->tkr_raw.xtime_nsec -= snsec_per_sec; + tk->raw_time.tv_sec++; } - tk->raw_time.tv_nsec = raw_nsecs; + tk->raw_time.tv_nsec = tk->tkr_raw.xtime_nsec >> tk->tkr_raw.shift; + tk->tkr_raw.xtime_nsec -= (u64)tk->raw_time.tv_nsec << tk->tkr_raw.shift; /* Accumulate error between NTP and clock interval */ tk->ntp_error += tk->ntp_tick << shift; -- GitLab From 8e3a02c312ccd6348cf838aa8f6f53b80fb2ffe7 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Fri, 3 Nov 2017 14:16:55 +0800 Subject: [PATCH 1734/5498] mmc: core: add power on to SD card detect In commit aaf56cab2137 ("mmc: core: power off host if SD card resume fails"), the mmc host is powered off if SD card init failed during resume. However, the power is not turned on before next time SD card detect. It would not cause crash or malfunction but lead to too many warnings. This change adds power on to function mmc_sd_detect to make sure the power is on before send commands to SD card. Change-Id: I7c85ec4d6b561d167f9ff77a71f8e00f5860e8d2 Signed-off-by: Can Guo --- drivers/mmc/core/sd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b58bf6d1f505..40180fb7164d 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1156,6 +1156,8 @@ static void mmc_sd_detect(struct mmc_host *host) return; } + mmc_power_up(host, host->ocr_avail); + /* * Just check if our card has been removed. */ -- GitLab From 875b55e2176ff80069dfcda5f2e89ece3d0cc9a9 Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Thu, 23 Nov 2017 17:36:47 +0530 Subject: [PATCH 1735/5498] soc: qcom: xport_bgcom: Modify string check in bgcom_suspend Glink_bgcom_suspend function compares the edge string with "bgcom". Which is wrong value, as it should be "bg". Modified the string to be compared to as "bg". Change-Id: I5a6efe1e7cce261e9a42178b3a51cfc36d17e9de Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/glink_bgcom_xprt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/glink_bgcom_xprt.c b/drivers/soc/qcom/glink_bgcom_xprt.c index c38c3de5f5c3..9dbc1ec0fae6 100644 --- a/drivers/soc/qcom/glink_bgcom_xprt.c +++ b/drivers/soc/qcom/glink_bgcom_xprt.c @@ -1690,7 +1690,7 @@ static int glink_bgcom_suspend(struct platform_device *pdev, int rc = -EBUSY; einfo = (struct edge_info *)dev_get_drvdata(&pdev->dev); - if (strcmp(einfo->xprt_cfg.edge, "bgcom")) + if (strcmp(einfo->xprt_cfg.edge, "bg")) return 0; spin_lock_irqsave(&einfo->activity_lock, flags); -- GitLab From 5bc65481ca1666cad8abc19acce7ac386200e26e Mon Sep 17 00:00:00 2001 From: Trishansh Bhardwaj Date: Wed, 22 Nov 2017 14:55:32 +0530 Subject: [PATCH 1736/5498] msm: camera: Synchronize v4l2 subscribe and unsubscribe event in camera.c If same event is unsubscribed before v4l2_event_subscribe returned, Then function v4l2_event_subscribe have possibility use-after-free. Serialize msm_subscribe_event and msm_unsubscribe_event to prevent parallel invocation of v4l2_event_subscribe and v4l2_event_unsubscribe. Change-Id: Ia28cfb9d46550d58221f157337a1468f524753e3 Signed-off-by: Trishansh Bhardwaj --- drivers/media/platform/msm/camera_v2/camera/camera.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index 12f07de2b7a3..6160c0afafa9 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -458,7 +458,9 @@ static int camera_v4l2_subscribe_event(struct v4l2_fh *fh, int rc = 0; struct camera_v4l2_private *sp = fh_to_private(fh); + mutex_lock(&sp->lock); rc = v4l2_event_subscribe(&sp->fh, sub, 5, NULL); + mutex_unlock(&sp->lock); return rc; } @@ -469,7 +471,9 @@ static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh, int rc = 0; struct camera_v4l2_private *sp = fh_to_private(fh); + mutex_lock(&sp->lock); rc = v4l2_event_unsubscribe(&sp->fh, sub); + mutex_unlock(&sp->lock); return rc; } -- GitLab From a14365d923e6a87a77a80decb7b67f1103b8ec24 Mon Sep 17 00:00:00 2001 From: Gopikrishna Mogasati Date: Mon, 22 May 2017 12:05:11 +0530 Subject: [PATCH 1737/5498] diag: dci: Add validity check for dci client's process descriptor This fix checks the validity of dci client's process descriptor before issuing a signal to it when subsystem restart is performed. This fix avoids accessing cleaned-up process descriptor's fields. CRs-Fixed: 2047235 Change-Id: Ic26977dc22c68f0a7007dd963c9273bba2a5dbfe Signed-off-by: Gopikrishna Mogasati --- drivers/char/diag/diag_dci.c | 40 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index c8d4039b39b6..37cc6c777421 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -1362,10 +1362,12 @@ void diag_dci_channel_open_work(struct work_struct *work) void diag_dci_notify_client(int peripheral_mask, int data, int proc) { - int stat; + int stat = 0; struct siginfo info; struct list_head *start, *temp; struct diag_dci_client_tbl *entry = NULL; + struct pid *pid_struct = NULL; + struct task_struct *dci_task = NULL; memset(&info, 0, sizeof(struct siginfo)); info.si_code = SI_QUEUE; @@ -1383,20 +1385,32 @@ void diag_dci_notify_client(int peripheral_mask, int data, int proc) continue; if (entry->client_info.notification_list & peripheral_mask) { info.si_signo = entry->client_info.signal_type; - if (entry->client && - entry->tgid == entry->client->tgid) { - DIAG_LOG(DIAG_DEBUG_DCI, - "entry tgid = %d, dci client tgid = %d\n", - entry->tgid, entry->client->tgid); - stat = send_sig_info( - entry->client_info.signal_type, - &info, entry->client); - if (stat) - pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n", + pid_struct = find_get_pid(entry->tgid); + if (pid_struct) { + dci_task = get_pid_task(pid_struct, + PIDTYPE_PID); + if (!dci_task) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: dci client with pid = %d Exited..\n", + entry->tgid); + mutex_unlock(&driver->dci_mutex); + return; + } + if (entry->client && + entry->tgid == dci_task->tgid) { + DIAG_LOG(DIAG_DEBUG_DCI, + "entry tgid = %d, dci client tgid = %d\n", + entry->tgid, dci_task->tgid); + stat = send_sig_info( + entry->client_info.signal_type, + &info, dci_task); + if (stat) + pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n", info.si_int, stat); - } else - pr_err("diag: client data is corrupted, signal data: 0x%x, stat: %d\n", + } else + pr_err("diag: client data is corrupted, signal data: 0x%x, stat: %d\n", info.si_int, stat); + } } } mutex_unlock(&driver->dci_mutex); -- GitLab From b3bb6e0c1a21db3dc9ff0b9ea613cd81100e7bb6 Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Wed, 29 Nov 2017 14:18:21 +0530 Subject: [PATCH 1738/5498] msm: mdp3: Map splash addr before smmu attach During bootup while continuous splash is enabled for video mode panel, iommmu page fault seen for first iommu attach. As DMA transfer is active during continuous splash, need to map the splash addr before iommu is attached. Change-Id: I7596a5bfb0fe36d037c63f9b7fff2dce7ab8998f Signed-off-by: Raghavendra Ambadas --- drivers/video/msm/mdss/mdp3.c | 6 ++++++ drivers/video/msm/mdss/mdp3_ctrl.c | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index 4d4f859be3ff..8c17ba25453f 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -2184,6 +2184,12 @@ void mdp3_release_splash_memory(struct msm_fb_data_type *mfd) { /* Give back the reserved memory to the system */ if (mdp3_res->splash_mem_addr) { + if ((mfd->panel.type == MIPI_VIDEO_PANEL) && + (mdp3_res->cont_splash_en)) { + mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE, + mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_size); + } mdp3_free(mfd); pr_debug("mdp3_release_splash_memory\n"); memblock_free(mdp3_res->splash_mem_addr, diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index 3e5f4c66a661..3c066d07e6bd 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "mdp3_ctrl.h" #include "mdp3.h" @@ -1173,6 +1174,16 @@ int mdp3_ctrl_reset(struct msm_fb_data_type *mfd) mdp3_qos_remapper_setup(panel); } + /*Map the splash addr for VIDEO mode panel before smmu attach*/ + if ((mfd->panel.type == MIPI_VIDEO_PANEL) && + (mdp3_session->in_splash_screen)) { + rc = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE, + mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_addr, + mdp3_res->splash_mem_size, + IOMMU_READ | IOMMU_NOEXEC); + } + rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); if (rc) { pr_err("fail to attach dma iommu\n"); -- GitLab From fcee98852d96f2895bd49c7c87e815a6ee552caf Mon Sep 17 00:00:00 2001 From: Lokesh Kumar Aakulu Date: Tue, 14 Nov 2017 17:49:55 +0530 Subject: [PATCH 1739/5498] msm: camera: isp: Allocate kernel page and map to userspace Share memory with kernel and user and update latest kernel sof frame id. Change-Id: Ie35132ffb9bd0298483b451b3333b3b2e3bb32ac Signed-off-by: Lokesh Kumar Aakulu --- .../platform/msm/camera_v2/isp/msm_isp.c | 70 ++++++++++++++++++- .../platform/msm/camera_v2/isp/msm_isp.h | 6 ++ .../msm/camera_v2/isp/msm_isp_axi_util.c | 5 +- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c index 608805569f28..411226242b27 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c @@ -437,11 +437,69 @@ static long msm_isp_v4l2_fops_ioctl(struct file *file, unsigned int cmd, return video_usercopy(file, cmd, arg, msm_isp_subdev_do_ioctl); } +static void isp_vma_open(struct vm_area_struct *vma) +{ + pr_debug("%s: open called\n", __func__); +} + +static void isp_vma_close(struct vm_area_struct *vma) +{ + pr_debug("%s: close called\n", __func__); +} + +static int isp_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct page *page; + struct vfe_device *vfe_dev = vma->vm_private_data; + struct isp_proc *isp_page = NULL; + + isp_page = vfe_dev->isp_page; + + pr_debug("%s: vfeid:%d u_virt_addr:0x%lx k_virt_addr:%pK\n", + __func__, vfe_dev->pdev->id, vma->vm_start, + (void *)isp_page); + if (isp_page != NULL) { + page = virt_to_page(isp_page); + get_page(page); + vmf->page = page; + isp_page->kernel_sofid = + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; + isp_page->vfeid = vfe_dev->pdev->id; + } + return 0; +} + +static const struct vm_operations_struct isp_vm_ops = { + .open = isp_vma_open, + .close = isp_vma_close, + .fault = isp_vma_fault, +}; + +static int msm_isp_v4l2_fops_mmap(struct file *filep, + struct vm_area_struct *vma) +{ + int ret = -EINVAL; + struct video_device *vdev = video_devdata(filep); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + + vma->vm_ops = &isp_vm_ops; + vma->vm_flags |= + (unsigned long)(VM_DONTEXPAND | VM_DONTDUMP); + vma->vm_private_data = vfe_dev; + isp_vma_open(vma); + ret = 0; + pr_debug("%s: isp mmap is called vm_start: 0x%lx\n", + __func__, vma->vm_start); + return ret; +} + static struct v4l2_file_operations msm_isp_v4l2_fops = { #ifdef CONFIG_COMPAT .compat_ioctl32 = msm_isp_v4l2_fops_ioctl, #endif - .unlocked_ioctl = msm_isp_v4l2_fops_ioctl + .unlocked_ioctl = msm_isp_v4l2_fops_ioctl, + .mmap = msm_isp_v4l2_fops_mmap }; static int vfe_set_common_data(struct platform_device *pdev) @@ -637,6 +695,8 @@ int vfe_hw_probe(struct platform_device *pdev) msm_isp_v4l2_fops.compat_ioctl32 = msm_isp_v4l2_fops_ioctl; #endif + msm_isp_v4l2_fops.mmap = msm_isp_v4l2_fops_mmap; + vfe_dev->subdev.sd.devnode->fops = &msm_isp_v4l2_fops; vfe_dev->buf_mgr = &vfe_buf_mgr; @@ -655,6 +715,14 @@ int vfe_hw_probe(struct platform_device *pdev) vfe_dev->hw_info->num_iommu_secure_ctx; vfe_dev->buf_mgr->init_done = 1; vfe_dev->vfe_open_cnt = 0; + /*Allocate a page in kernel and map it to camera user process*/ + vfe_dev->isp_page = (struct isp_proc *)get_zeroed_page(GFP_KERNEL); + if (vfe_dev->isp_page == NULL) { + pr_err("%s: no enough memory\n", __func__); + rc = -ENOMEM; + goto probe_fail3; + } + vfe_dev->isp_page->vfeid = vfe_dev->pdev->id; return rc; probe_fail3: diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index d64877ca1ed5..d223401d8688 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -703,6 +703,11 @@ struct msm_vfe_common_subdev { struct msm_vfe_common_dev_data *common_data; }; +struct isp_proc { + uint32_t kernel_sofid; + uint32_t vfeid; +}; + struct vfe_device { /* Driver private data */ struct platform_device *pdev; @@ -788,6 +793,7 @@ struct vfe_device { /* Store the buf_idx for pd stats RDI stream */ uint8_t pd_buf_idx; uint32_t ms_frame_id; + struct isp_proc *isp_page; }; struct vfe_parent_device { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 364b74cca822..f4292ad406ad 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -939,10 +939,13 @@ void msm_isp_increment_frame_id(struct vfe_device *vfe_dev, msm_isp_halt_send_error(vfe_dev, ISP_EVENT_REG_UPDATE_MISSING); } - } else vfe_dev->axi_data.src_info[frame_src].frame_id++; } + if (frame_src == VFE_PIX_0) { + vfe_dev->isp_page->kernel_sofid = + vfe_dev->axi_data.src_info[frame_src].frame_id; + } sof_info = vfe_dev->axi_data.src_info[frame_src]. dual_hw_ms_info.sof_info; if (dual_hw_type == DUAL_HW_MASTER_SLAVE && -- GitLab From b5564fdebdd27821e7d08f38bd7724dd597034c6 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Tue, 24 Feb 2015 17:21:07 -0600 Subject: [PATCH 1740/5498] UPSTREAM: arm64: vdso: minor ABI fix for clock_getres (cherry picked from commit e1b6b6ce55a0a25c8aa8af019095253b2133a41a) The vdso implementation of clock_getres currently returns 0 (success) whenever a null timespec is provided by the caller, regardless of the clock id supplied. This behavior is incorrect. It should fall back to syscall when an unrecognized clock id is passed, even when the timespec argument is null. This ensures that clock_getres always returns an error for invalid clock ids. Signed-off-by: Nathan Lynch Acked-by: Will Deacon Signed-off-by: Catalin Marinas Bug: 20045882 Bug: 63737556 Bug: 69626243 Change-Id: I93caac645d807403cab85245a33faf6439c6f3c1 --- arch/arm64/kernel/vdso/gettimeofday.S | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S index 1f8bba27e2f3..e00b4671bd7c 100644 --- a/arch/arm64/kernel/vdso/gettimeofday.S +++ b/arch/arm64/kernel/vdso/gettimeofday.S @@ -297,8 +297,6 @@ ENDPROC(__kernel_clock_gettime) /* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */ ENTRY(__kernel_clock_getres) .cfi_startproc - cbz w1, 3f - cmp w0, #CLOCK_REALTIME ccmp w0, #CLOCK_MONOTONIC, #0x4, ne ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne @@ -312,6 +310,7 @@ ENTRY(__kernel_clock_getres) b.ne 4f ldr x2, 6f 2: + cbz w1, 3f stp xzr, x2, [x1] 3: /* res == NULL. */ -- GitLab From a2ec34c80807ffdd70e791d716cde5c8b4d50e47 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 30 Oct 2017 21:23:19 +0000 Subject: [PATCH 1741/5498] UPSTREAM: arm64: vdso: fix clock_getres for 4GiB-aligned res (cherry pick from commit c80ed088a519da53f27b798a69748eaabc66aadf) The vdso tries to check for a NULL res pointer in __kernel_clock_getres, but only checks the lower 32 bits as is uses CBZ on the W register the res pointer is held in. Thus, if the res pointer happened to be aligned to a 4GiB boundary, we'd spuriously skip storing the timespec to it, while returning a zero error code to the caller. Prevent this by checking the whole pointer, using CBZ on the X register the res pointer is held in. Fixes: 9031fefde6f2ac1d ("arm64: VDSO support") Signed-off-by: Mark Rutland Reported-by: Andrew Pinski Reported-by: Mark Salyzyn Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Will Deacon Bug: 20045882 Bug: 63737556 Change-Id: Iab5449d8515f9d655e792e3d7ce43a8f016fa2a0 --- arch/arm64/kernel/vdso/gettimeofday.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S index e00b4671bd7c..c97ce91cf023 100644 --- a/arch/arm64/kernel/vdso/gettimeofday.S +++ b/arch/arm64/kernel/vdso/gettimeofday.S @@ -310,7 +310,7 @@ ENTRY(__kernel_clock_getres) b.ne 4f ldr x2, 6f 2: - cbz w1, 3f + cbz x1, 3f stp xzr, x2, [x1] 3: /* res == NULL. */ -- GitLab From c8c539ce62864375e117c2942d600933dd361e12 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 25 Jan 2016 11:45:06 +0000 Subject: [PATCH 1742/5498] UPSTREAM: arm64: mm: add __{pud,pgd}_populate (cherry picked from commit 1e531cce68c92b46c7d29f36a72f9a3e5886678f) We currently have __pmd_populate for creating a pmd table entry given the physical address of a pte, but don't have equivalents for the pud or pgd levels of table. To enable us to manipulate tables which are mapped outside of the linear mapping (where we have a PA, but not a linear map VA), it is useful to have these functions. This patch adds __{pud,pgd}_populate. As these should not be called when the kernel uses folded {pmd,pud}s, in these cases they expand to BUILD_BUG(). So long as the appropriate checks are made on the {pud,pgd} entry prior to attempting population, these should be optimized out at compile time. Signed-off-by: Mark Rutland Reviewed-by: Catalin Marinas Tested-by: Ard Biesheuvel Reviewed-by: Ard Biesheuvel Tested-by: Jeremy Linton Cc: Laura Abbott Cc: Will Deacon Signed-off-by: Catalin Marinas Bug: 20045882 Bug: 63737556 Change-Id: I014e24adec99f29fa1019e541fce3ac2cf1973b4 --- arch/arm64/include/asm/pgalloc.h | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h index 87b73a6cc7e3..6e6fa1ce1d15 100644 --- a/arch/arm64/include/asm/pgalloc.h +++ b/arch/arm64/include/asm/pgalloc.h @@ -39,11 +39,20 @@ static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) free_page((unsigned long)pmd); } -static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) +static inline void __pud_populate(pud_t *pud, phys_addr_t pmd, pudval_t prot) { - set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE)); + set_pud(pud, __pud(pmd | prot)); } +static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) +{ + __pud_populate(pud, __pa(pmd), PMD_TYPE_TABLE); +} +#else +static inline void __pud_populate(pud_t *pud, phys_addr_t pmd, pudval_t prot) +{ + BUILD_BUG(); +} #endif /* CONFIG_PGTABLE_LEVELS > 2 */ #if CONFIG_PGTABLE_LEVELS > 3 @@ -59,11 +68,20 @@ static inline void pud_free(struct mm_struct *mm, pud_t *pud) free_page((unsigned long)pud); } -static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud) +static inline void __pgd_populate(pgd_t *pgdp, phys_addr_t pud, pgdval_t prot) { - set_pgd(pgd, __pgd(__pa(pud) | PUD_TYPE_TABLE)); + set_pgd(pgdp, __pgd(pud | prot)); } +static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud) +{ + __pgd_populate(pgd, __pa(pud), PUD_TYPE_TABLE); +} +#else +static inline void __pgd_populate(pgd_t *pgdp, phys_addr_t pud, pgdval_t prot) +{ + BUILD_BUG(); +} #endif /* CONFIG_PGTABLE_LEVELS > 3 */ extern pgd_t *pgd_alloc(struct mm_struct *mm); -- GitLab From ab932c9746b8bf6912f30d707d159614284e2c40 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 22 May 2017 17:20:20 -0700 Subject: [PATCH 1743/5498] BACKPORT: time: Clean up CLOCK_MONOTONIC_RAW time handling (cherry pick from commit fc6eead7c1e2e5376c25d2795d4539fdacbc0648) Now that we fixed the sub-ns handling for CLOCK_MONOTONIC_RAW, remove the duplicitive tk->raw_time.tv_nsec, which can be stored in tk->tkr_raw.xtime_nsec (similarly to how its handled for monotonic time). Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Miroslav Lichvar Cc: Richard Cochran Cc: Prarit Bhargava Cc: Stephen Boyd Cc: Kevin Brodsky Cc: Will Deacon Cc: Daniel Mentz Tested-by: Daniel Mentz Signed-off-by: John Stultz Bug: 20045882 Bug: 63737556 Change-Id: I243827d21b08703a09d2d2fe738a9258be224582 --- arch/arm64/kernel/vdso.c | 4 +-- include/linux/timekeeper_internal.h | 4 +-- kernel/time/timekeeping.c | 38 +++++++++++++++++------------ 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 71766af43b70..b0d064c3ed35 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -215,8 +215,8 @@ void update_vsyscall(struct timekeeper *tk) if (!use_syscall) { /* tkr_mono.cycle_last == tkr_raw.cycle_last */ vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last; - vdso_data->raw_time_sec = tk->raw_time.tv_sec; - vdso_data->raw_time_nsec = tk->raw_time.tv_nsec; + vdso_data->raw_time_sec = tk->raw_sec; + vdso_data->raw_time_nsec = tk->tkr_raw.xtime_nsec; vdso_data->xtime_clock_sec = tk->xtime_sec; vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec; /* tkr_raw.xtime_nsec == 0 */ diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 7a6499ecaa4e..97cd2f965e41 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -48,7 +48,7 @@ struct tk_read_base { * @offs_boot: Offset clock monotonic -> clock boottime * @offs_tai: Offset clock monotonic -> clock tai * @tai_offset: The current UTC to TAI offset in seconds - * @raw_time: Monotonic raw base time in timespec64 format + * @raw_sec: CLOCK_MONOTONIC_RAW time in seconds * @cycle_interval: Number of clock cycles in one NTP interval * @xtime_interval: Number of clock shifted nano seconds in one NTP * interval. @@ -83,7 +83,7 @@ struct timekeeper { ktime_t offs_boot; ktime_t offs_tai; s32 tai_offset; - struct timespec64 raw_time; + u64 raw_sec; /* The following members are for timekeeping internal use */ cycle_t cycle_interval; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 9e3fe70c0580..43f0f6f183da 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -72,6 +72,10 @@ static inline void tk_normalize_xtime(struct timekeeper *tk) tk->tkr_mono.xtime_nsec -= (u64)NSEC_PER_SEC << tk->tkr_mono.shift; tk->xtime_sec++; } + while (tk->tkr_raw.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_raw.shift)) { + tk->tkr_raw.xtime_nsec -= (u64)NSEC_PER_SEC << tk->tkr_raw.shift; + tk->raw_sec++; + } } static inline struct timespec64 tk_xtime(struct timekeeper *tk) @@ -166,12 +170,14 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) /* if changing clocks, convert xtime_nsec shift units */ if (old_clock) { int shift_change = clock->shift - old_clock->shift; - if (shift_change < 0) + if (shift_change < 0) { tk->tkr_mono.xtime_nsec >>= -shift_change; - else + tk->tkr_raw.xtime_nsec >>= -shift_change; + } else { tk->tkr_mono.xtime_nsec <<= shift_change; + tk->tkr_raw.xtime_nsec <<= shift_change; + } } - tk->tkr_raw.xtime_nsec = 0; tk->tkr_mono.shift = clock->shift; tk->tkr_raw.shift = clock->shift; @@ -405,6 +411,7 @@ EXPORT_SYMBOL_GPL(pvclock_gtod_unregister_notifier); */ static inline void tk_update_ktime_data(struct timekeeper *tk) { + u64 seconds; s64 nsec; /* @@ -420,7 +427,9 @@ static inline void tk_update_ktime_data(struct timekeeper *tk) tk->tkr_mono.base = ns_to_ktime(nsec); /* Update the monotonic raw base */ - tk->tkr_raw.base = timespec64_to_ktime(tk->raw_time); + seconds = tk->raw_sec; + nsec = (u32)(tk->tkr_raw.xtime_nsec >> tk->tkr_raw.shift); + tk->tkr_raw.base = ns_to_ktime(seconds * NSEC_PER_SEC + nsec); } /* must hold timekeeper_lock */ @@ -454,7 +463,6 @@ static void timekeeping_forward_now(struct timekeeper *tk) { struct clocksource *clock = tk->tkr_mono.clock; cycle_t cycle_now, delta; - s64 nsec; cycle_now = tk->tkr_mono.read(clock); delta = clocksource_delta(cycle_now, tk->tkr_mono.cycle_last, tk->tkr_mono.mask); @@ -466,10 +474,13 @@ static void timekeeping_forward_now(struct timekeeper *tk) /* If arch requires, add in get_arch_timeoffset() */ tk->tkr_mono.xtime_nsec += (u64)arch_gettimeoffset() << tk->tkr_mono.shift; - tk_normalize_xtime(tk); - nsec = clocksource_cyc2ns(delta, tk->tkr_raw.mult, tk->tkr_raw.shift); - timespec64_add_ns(&tk->raw_time, nsec); + tk->tkr_raw.xtime_nsec += delta * tk->tkr_raw.mult; + + /* If arch requires, add in get_arch_timeoffset() */ + tk->tkr_raw.xtime_nsec += (u64)arch_gettimeoffset() << tk->tkr_raw.shift; + + tk_normalize_xtime(tk); } /** @@ -898,11 +909,12 @@ void getrawmonotonic(struct timespec *ts) do { seq = read_seqcount_begin(&tk_core.seq); + ts64.tv_sec = tk->raw_sec; nsecs = timekeeping_get_ns(&tk->tkr_raw); - ts64 = tk->raw_time; } while (read_seqcount_retry(&tk_core.seq, seq)); + ts64.tv_nsec = 0; timespec64_add_ns(&ts64, nsecs); *ts = timespec64_to_timespec(ts64); } @@ -1016,8 +1028,7 @@ void __init timekeeping_init(void) tk_setup_internals(tk, clock); tk_set_xtime(tk, &now); - tk->raw_time.tv_sec = 0; - tk->raw_time.tv_nsec = 0; + tk->raw_sec = 0; if (boot.tv_sec == 0 && boot.tv_nsec == 0) boot = tk_xtime(tk); @@ -1484,15 +1495,12 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, *clock_set |= accumulate_nsecs_to_secs(tk); /* Accumulate raw time */ - tk->tkr_raw.xtime_nsec += (u64)tk->raw_time.tv_nsec << tk->tkr_raw.shift; tk->tkr_raw.xtime_nsec += tk->raw_interval << shift; snsec_per_sec = (u64)NSEC_PER_SEC << tk->tkr_raw.shift; while (tk->tkr_raw.xtime_nsec >= snsec_per_sec) { tk->tkr_raw.xtime_nsec -= snsec_per_sec; - tk->raw_time.tv_sec++; + tk->raw_sec++; } - tk->raw_time.tv_nsec = tk->tkr_raw.xtime_nsec >> tk->tkr_raw.shift; - tk->tkr_raw.xtime_nsec -= (u64)tk->raw_time.tv_nsec << tk->tkr_raw.shift; /* Accumulate error between NTP and clock interval */ tk->ntp_error += tk->ntp_tick << shift; -- GitLab From b9d3af916a44a04453971604d50c413bf6d44482 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Wed, 29 Nov 2017 20:44:14 +0530 Subject: [PATCH 1744/5498] ASoC: msm: msm_bg: add new dai links for hfp Add new dai links to support hfp usecase. CRs-Fixed: 2123073 Change-Id: Ib457eb8d3b5d2e00caa32c6b65b51c2e0d7e4a81 Signed-off-by: Bala Kishore Pati --- sound/soc/msm/msm_bg.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/sound/soc/msm/msm_bg.c b/sound/soc/msm/msm_bg.c index 471dcf672eea..521863b9d2e9 100644 --- a/sound/soc/msm/msm_bg.c +++ b/sound/soc/msm/msm_bg.c @@ -213,6 +213,19 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + pr_debug("%s()\n", __func__); + rate->min = rate->max = 8000; + channels->min = channels->max = 1; + return 0; +} + static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1893,6 +1906,34 @@ static struct snd_soc_dai_link msm_bg_tdm_be_dai[] = { .ops = &msm_tdm_be_ops, .ignore_suspend = 1, }, + { + .name = LPASS_BE_INT_BT_SCO_RX, + .stream_name = "Internal BT-SCO Playback", + .cpu_dai_name = "msm-dai-q6-dev.12288", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX, + .be_hw_params_fixup = msm_btsco_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT_BT_SCO_TX, + .stream_name = "Internal BT-SCO Capture", + .cpu_dai_name = "msm-dai-q6-dev.12289", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX, + .be_hw_params_fixup = msm_btsco_be_hw_params_fixup, + .ignore_suspend = 1, + }, }; static struct snd_soc_dai_link msm_bg_split_a2dp_dai_link[] = { -- GitLab From e52bf3aee1e04b3766682a2708edbaa296068b4c Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 30 Nov 2017 13:50:33 +0530 Subject: [PATCH 1745/5498] power: qcom: msm-pm: Correct check for not null Correctly check if pdev is not null. Change-Id: I0b2991af9b03f548330e84fbf99594dbf05e26f1 Signed-off-by: Maulik Shah --- drivers/power/qcom/msm-pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/qcom/msm-pm.c b/drivers/power/qcom/msm-pm.c index 7ff56b93af02..26da722c3632 100644 --- a/drivers/power/qcom/msm-pm.c +++ b/drivers/power/qcom/msm-pm.c @@ -550,7 +550,7 @@ static int msm_cpu_status_probe(struct platform_device *pdev) u32 cpu; int rc; - if (!pdev | !pdev->dev.of_node) + if (!pdev || !pdev->dev.of_node) return -EFAULT; msm_pm_slp_sts = devm_kzalloc(&pdev->dev, -- GitLab From 45cc2b6efc84f2de7357339c842c8aa81343080a Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Wed, 15 Nov 2017 14:15:36 +0100 Subject: [PATCH 1746/5498] s390/disassembler: increase show_code buffer size commit b192571d1ae375e0bbe0aa3ccfa1a3c3704454b9 upstream. Current buffer size of 64 is too small. objdump shows that there are instructions which would require up to 75 bytes buffer (with current formating). 128 bytes "ought to be enough for anybody". Also replaces 8 spaces with a single tab to reduce the memory footprint. Fixes the following KASAN finding: BUG: KASAN: stack-out-of-bounds in number+0x3fe/0x538 Write of size 1 at addr 000000005a4a75a0 by task bash/1282 CPU: 1 PID: 1282 Comm: bash Not tainted 4.14.0+ #215 Hardware name: IBM 2964 N96 702 (z/VM 6.4.0) Call Trace: ([<000000000011eeb6>] show_stack+0x56/0x88) [<0000000000e1ce1a>] dump_stack+0x15a/0x1b0 [<00000000004e2994>] print_address_description+0xf4/0x288 [<00000000004e2cf2>] kasan_report+0x13a/0x230 [<0000000000e38ae6>] number+0x3fe/0x538 [<0000000000e3dfe4>] vsnprintf+0x194/0x948 [<0000000000e3ea42>] sprintf+0xa2/0xb8 [<00000000001198dc>] print_insn+0x374/0x500 [<0000000000119346>] show_code+0x4ee/0x538 [<000000000011f234>] show_registers+0x34c/0x388 [<000000000011f2ae>] show_regs+0x3e/0xa8 [<000000000011f502>] die+0x1ea/0x2e8 [<0000000000138f0e>] do_no_context+0x106/0x168 [<0000000000139a1a>] do_protection_exception+0x4da/0x7d0 [<0000000000e55914>] pgm_check_handler+0x16c/0x1c0 [<000000000090639e>] sysrq_handle_crash+0x46/0x58 ([<0000000000000007>] 0x7) [<00000000009073fa>] __handle_sysrq+0x102/0x218 [<0000000000907c06>] write_sysrq_trigger+0xd6/0x100 [<000000000061d67a>] proc_reg_write+0xb2/0x128 [<0000000000520be6>] __vfs_write+0xee/0x368 [<0000000000521222>] vfs_write+0x21a/0x278 [<000000000052156a>] SyS_write+0xda/0x178 [<0000000000e555cc>] system_call+0xc4/0x270 The buggy address belongs to the page: page:000003d1016929c0 count:0 mapcount:0 mapping: (null) index:0x0 flags: 0x0() raw: 0000000000000000 0000000000000000 0000000000000000 ffffffff00000000 raw: 0000000000000100 0000000000000200 0000000000000000 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: 000000005a4a7480: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 000000005a4a7500: 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00 00 00 00 >000000005a4a7580: 00 00 00 00 f3 f3 f3 f3 00 00 00 00 00 00 00 00 ^ 000000005a4a7600: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 f8 f8 000000005a4a7680: f2 f2 f2 f2 f2 f2 f8 f8 f2 f2 f3 f3 f3 f3 00 00 ================================================================== Signed-off-by: Vasily Gorbik Signed-off-by: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/dis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index f3762937dd82..18ebf7c0b70c 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -1997,7 +1997,7 @@ void show_code(struct pt_regs *regs) { char *mode = user_mode(regs) ? "User" : "Krnl"; unsigned char code[64]; - char buffer[64], *ptr; + char buffer[128], *ptr; mm_segment_t old_fs; unsigned long addr; int start, end, opsize, hops, i; @@ -2060,7 +2060,7 @@ void show_code(struct pt_regs *regs) start += opsize; printk(buffer); ptr = buffer; - ptr += sprintf(ptr, "\n "); + ptr += sprintf(ptr, "\n\t "); hops++; } printk("\n"); -- GitLab From 7982b5eddf5c9c2594498e9dd6354d292579ecbf Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 20 Jun 2017 11:42:27 -0700 Subject: [PATCH 1747/5498] ipv6: only call ip6_route_dev_notify() once for NETDEV_UNREGISTER commit 76da0704507bbc51875013f6557877ab308cfd0a upstream. In commit 242d3a49a2a1 ("ipv6: reorder ip6_route_dev_notifier after ipv6_dev_notf") I assumed NETDEV_REGISTER and NETDEV_UNREGISTER are paired, unfortunately, as reported by jeffy, netdev_wait_allrefs() could rebroadcast NETDEV_UNREGISTER event until all refs are gone. We have to add an additional check to avoid this corner case. For netdev_wait_allrefs() dev->reg_state is NETREG_UNREGISTERED, for dev_change_net_namespace(), dev->reg_state is NETREG_REGISTERED. So check for dev->reg_state != NETREG_UNREGISTERED. Fixes: 242d3a49a2a1 ("ipv6: reorder ip6_route_dev_notifier after ipv6_dev_notf") Reported-by: jeffy Cc: David Ahern Signed-off-by: Cong Wang Acked-by: David Ahern Signed-off-by: David S. Miller Cc: Konstantin Khlebnikov Signed-off-by: Greg Kroah-Hartman --- net/ipv6/route.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5ded6638a5e4..31ecdb35fd7f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2827,7 +2827,11 @@ static int ip6_route_dev_notify(struct notifier_block *this, net->ipv6.ip6_blk_hole_entry->dst.dev = dev; net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); #endif - } else if (event == NETDEV_UNREGISTER) { + } else if (event == NETDEV_UNREGISTER && + dev->reg_state != NETREG_UNREGISTERED) { + /* NETDEV_UNREGISTER could be fired for multiple times by + * netdev_wait_allrefs(). Make sure we only call this once. + */ in6_dev_put(net->ipv6.ip6_null_entry->rt6i_idev); #ifdef CONFIG_IPV6_MULTIPLE_TABLES in6_dev_put(net->ipv6.ip6_prohibit_entry->rt6i_idev); -- GitLab From 1c3a4e61b11d1c0ec1834ea9b343706b06cab774 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 18 Sep 2017 08:54:40 -0700 Subject: [PATCH 1748/5498] sched: Make resched_cpu() unconditional commit 7c2102e56a3f7d85b5d8f33efbd7aecc1f36fdd8 upstream. The current implementation of synchronize_sched_expedited() incorrectly assumes that resched_cpu() is unconditional, which it is not. This means that synchronize_sched_expedited() can hang when resched_cpu()'s trylock fails as follows (analysis by Neeraj Upadhyay): o CPU1 is waiting for expedited wait to complete: sync_rcu_exp_select_cpus rdp->exp_dynticks_snap & 0x1 // returns 1 for CPU5 IPI sent to CPU5 synchronize_sched_expedited_wait ret = swait_event_timeout(rsp->expedited_wq, sync_rcu_preempt_exp_done(rnp_root), jiffies_stall); expmask = 0x20, CPU 5 in idle path (in cpuidle_enter()) o CPU5 handles IPI and fails to acquire rq lock. Handles IPI sync_sched_exp_handler resched_cpu returns while failing to try lock acquire rq->lock need_resched is not set o CPU5 calls rcu_idle_enter() and as need_resched is not set, goes to idle (schedule() is not called). o CPU 1 reports RCU stall. Given that resched_cpu() is now used only by RCU, this commit fixes the assumption by making resched_cpu() unconditional. Reported-by: Neeraj Upadhyay Suggested-by: Neeraj Upadhyay Signed-off-by: Paul E. McKenney Acked-by: Steven Rostedt (VMware) Acked-by: Peter Zijlstra (Intel) Signed-off-by: Greg Kroah-Hartman --- kernel/sched/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d0eb16373bf0..5c64c714ad08 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -632,8 +632,7 @@ void resched_cpu(int cpu) struct rq *rq = cpu_rq(cpu); unsigned long flags; - if (!raw_spin_trylock_irqsave(&rq->lock, flags)) - return; + raw_spin_lock_irqsave(&rq->lock, flags); resched_curr(rq); raw_spin_unlock_irqrestore(&rq->lock, flags); } -- GitLab From 03a2209fe340efa020eb76bcaa74b93356fc952a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Nov 2017 14:15:27 -0800 Subject: [PATCH 1749/5498] lib/mpi: call cond_resched() from mpi_powm() loop commit 1d9ddde12e3c9bab7f3d3484eb9446315e3571ca upstream. On a non-preemptible kernel, if KEYCTL_DH_COMPUTE is called with the largest permitted inputs (16384 bits), the kernel spends 10+ seconds doing modular exponentiation in mpi_powm() without rescheduling. If all threads do it, it locks up the system. Moreover, it can cause rcu_sched-stall warnings. Notwithstanding the insanity of doing this calculation in kernel mode rather than in userspace, fix it by calling cond_resched() as each bit from the exponent is processed. It's still noninterruptible, but at least it's preemptible now. Do the cond_resched() once per bit rather than once per MPI limb because each limb might still easily take 100+ milliseconds on slow CPUs. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- lib/mpi/mpi-pow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c index e24388a863a7..468fb7cd1221 100644 --- a/lib/mpi/mpi-pow.c +++ b/lib/mpi/mpi-pow.c @@ -26,6 +26,7 @@ * however I decided to publish this code under the plain GPL. */ +#include #include #include "mpi-internal.h" #include "longlong.h" @@ -256,6 +257,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) } e <<= 1; c--; + cond_resched(); } i--; -- GitLab From a63a5859bc4aba1bb1ebda952aaf26d51f673012 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 24 Nov 2017 13:56:30 +0900 Subject: [PATCH 1750/5498] x86/decoder: Add new TEST instruction pattern commit 12a78d43de767eaf8fb272facb7a7b6f2dc6a9df upstream. The kbuild test robot reported this build warning: Warning: arch/x86/tools/test_get_len found difference at :ffffffff8103dd2c Warning: ffffffff8103dd82: f6 09 d8 testb $0xd8,(%rcx) Warning: objdump says 3 bytes, but insn_get_length() says 2 Warning: decoded and checked 1569014 instructions with 1 warnings This sequence seems to be a new instruction not in the opcode map in the Intel SDM. The instruction sequence is "F6 09 d8", means Group3(F6), MOD(00)REG(001)RM(001), and 0xd8. Intel SDM vol2 A.4 Table A-6 said the table index in the group is "Encoding of Bits 5,4,3 of the ModR/M Byte (bits 2,1,0 in parenthesis)" In that table, opcodes listed by the index REG bits as: 000 001 010 011 100 101 110 111 TEST Ib/Iz,(undefined),NOT,NEG,MUL AL/rAX,IMUL AL/rAX,DIV AL/rAX,IDIV AL/rAX So, it seems TEST Ib is assigned to 001. Add the new pattern. Reported-by: kbuild test robot Signed-off-by: Masami Hiramatsu Cc: Greg Kroah-Hartman Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/lib/x86-opcode-map.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/lib/x86-opcode-map.txt b/arch/x86/lib/x86-opcode-map.txt index 1a2be7c6895d..3ce0dcc56ce5 100644 --- a/arch/x86/lib/x86-opcode-map.txt +++ b/arch/x86/lib/x86-opcode-map.txt @@ -814,7 +814,7 @@ EndTable GrpTable: Grp3_1 0: TEST Eb,Ib -1: +1: TEST Eb,Ib 2: NOT Eb 3: NEG Eb 4: MUL AL,Eb -- GitLab From f4f6d52542aafca8aec688490ff9aadc8ad53d39 Mon Sep 17 00:00:00 2001 From: Philip Derrin Date: Tue, 14 Nov 2017 00:55:26 +0100 Subject: [PATCH 1751/5498] ARM: 8721/1: mm: dump: check hardware RO bit for LPAE commit 3b0c0c922ff4be275a8beb87ce5657d16f355b54 upstream. When CONFIG_ARM_LPAE is set, the PMD dump relies on the software read-only bit to determine whether a page is writable. This concealed a bug which left the kernel text section writable (AP2=0) while marked read-only in the software bit. In a kernel with the AP2 bug, the dump looks like this: ---[ Kernel Mapping ]--- 0xc0000000-0xc0200000 2M RW NX SHD 0xc0200000-0xc0600000 4M ro x SHD 0xc0600000-0xc0800000 2M ro NX SHD 0xc0800000-0xc4800000 64M RW NX SHD The fix is to check that the software and hardware bits are both set before displaying "ro". The dump then shows the true perms: ---[ Kernel Mapping ]--- 0xc0000000-0xc0200000 2M RW NX SHD 0xc0200000-0xc0600000 4M RW x SHD 0xc0600000-0xc0800000 2M RW NX SHD 0xc0800000-0xc4800000 64M RW NX SHD Fixes: ded947798469 ("ARM: 8109/1: mm: Modify pte_write and pmd_write logic for LPAE") Signed-off-by: Philip Derrin Tested-by: Neil Dick Reviewed-by: Kees Cook Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/mm/dump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mm/dump.c b/arch/arm/mm/dump.c index 59424937e52b..e223322fc71b 100644 --- a/arch/arm/mm/dump.c +++ b/arch/arm/mm/dump.c @@ -126,8 +126,8 @@ static const struct prot_bits section_bits[] = { .val = PMD_SECT_USER, .set = "USR", }, { - .mask = L_PMD_SECT_RDONLY, - .val = L_PMD_SECT_RDONLY, + .mask = L_PMD_SECT_RDONLY | PMD_SECT_AP2, + .val = L_PMD_SECT_RDONLY | PMD_SECT_AP2, .set = "ro", .clear = "RW", #elif __LINUX_ARM_ARCH__ >= 6 -- GitLab From b0464fa2a31c13109870f9ae2c7cb149c807f308 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 23 Nov 2017 20:07:00 +0530 Subject: [PATCH 1752/5498] ALSA: hda: Add Raven PCI ID commit 9ceace3c9c18c67676e75141032a65a8e01f9a7a upstream. This commit adds PCI ID for Raven platform Signed-off-by: Vijendar Mukunda Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c1115b3b90c8..5b89e110f6d4 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2092,6 +2092,9 @@ static const struct pci_device_id azx_ids[] = { /* AMD Hudson */ { PCI_DEVICE(0x1022, 0x780d), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, + /* AMD Raven */ + { PCI_DEVICE(0x1022, 0x15e3), + .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, /* ATI HDMI */ { PCI_DEVICE(0x1002, 0x0002), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, -- GitLab From 853d2670b8b6a53663e54adcd757566039da59f2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 15 Nov 2017 16:38:09 -0800 Subject: [PATCH 1753/5498] dm bufio: fix integer overflow when limiting maximum cache size commit 74d4108d9e681dbbe4a2940ed8fdff1f6868184c upstream. The default max_cache_size_bytes for dm-bufio is meant to be the lesser of 25% of the size of the vmalloc area and 2% of the size of lowmem. However, on 32-bit systems the intermediate result in the expression (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100 overflows, causing the wrong result to be computed. For example, on a 32-bit system where the vmalloc area is 520093696 bytes, the result is 1174405 rather than the expected 130023424, which makes the maximum cache size much too small (far less than 2% of lowmem). This causes severe performance problems for dm-verity users on affected systems. Fix this by using mult_frac() to correctly multiply by a percentage. Do this for all places in dm-bufio that multiply by a percentage. Also replace (VMALLOC_END - VMALLOC_START) with VMALLOC_TOTAL, which contrary to the comment is now defined in include/linux/vmalloc.h. Depends-on: 9993bc635 ("sched/x86: Fix overflow in cyc2ns_offset") Fixes: 95d402f057f2 ("dm: add bufio") Signed-off-by: Eric Biggers Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-bufio.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 9c5a0b4d5446..a673f0e7a9a7 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -876,7 +876,8 @@ static void __get_memory_limit(struct dm_bufio_client *c, buffers = c->minimum_buffers; *limit_buffers = buffers; - *threshold_buffers = buffers * DM_BUFIO_WRITEBACK_PERCENT / 100; + *threshold_buffers = mult_frac(buffers, + DM_BUFIO_WRITEBACK_PERCENT, 100); } /* @@ -1764,19 +1765,15 @@ static int __init dm_bufio_init(void) memset(&dm_bufio_caches, 0, sizeof dm_bufio_caches); memset(&dm_bufio_cache_names, 0, sizeof dm_bufio_cache_names); - mem = (__u64)((totalram_pages - totalhigh_pages) * - DM_BUFIO_MEMORY_PERCENT / 100) << PAGE_SHIFT; + mem = (__u64)mult_frac(totalram_pages - totalhigh_pages, + DM_BUFIO_MEMORY_PERCENT, 100) << PAGE_SHIFT; if (mem > ULONG_MAX) mem = ULONG_MAX; #ifdef CONFIG_MMU - /* - * Get the size of vmalloc space the same way as VMALLOC_TOTAL - * in fs/proc/internal.h - */ - if (mem > (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100) - mem = (VMALLOC_END - VMALLOC_START) * DM_BUFIO_VMALLOC_PERCENT / 100; + if (mem > mult_frac(VMALLOC_TOTAL, DM_BUFIO_VMALLOC_PERCENT, 100)) + mem = mult_frac(VMALLOC_TOTAL, DM_BUFIO_VMALLOC_PERCENT, 100); #endif dm_bufio_default_cache_size = mem; -- GitLab From 84cc7b5d26c17384f29b25a1ba4d42e2e820043a Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Wed, 1 Nov 2017 15:42:36 +0800 Subject: [PATCH 1754/5498] dm: fix race between dm_get_from_kobject() and __dm_destroy() commit b9a41d21dceadf8104812626ef85dc56ee8a60ed upstream. The following BUG_ON was hit when testing repeat creation and removal of DM devices: kernel BUG at drivers/md/dm.c:2919! CPU: 7 PID: 750 Comm: systemd-udevd Not tainted 4.1.44 Call Trace: [] dm_get_from_kobject+0x34/0x3a [] dm_attr_show+0x2b/0x5e [] ? mutex_lock+0x26/0x44 [] sysfs_kf_seq_show+0x83/0xcf [] kernfs_seq_show+0x23/0x25 [] seq_read+0x16f/0x325 [] kernfs_fop_read+0x3a/0x13f [] __vfs_read+0x26/0x9d [] ? security_file_permission+0x3c/0x44 [] ? rw_verify_area+0x83/0xd9 [] vfs_read+0x8f/0xcf [] ? __fdget_pos+0x12/0x41 [] SyS_read+0x4b/0x76 [] system_call_fastpath+0x12/0x71 The bug can be easily triggered, if an extra delay (e.g. 10ms) is added between the test of DMF_FREEING & DMF_DELETING and dm_get() in dm_get_from_kobject(). To fix it, we need to ensure the test of DMF_FREEING & DMF_DELETING and dm_get() are done in an atomic way, so _minor_lock is used. The other callers of dm_get() have also been checked to be OK: some callers invoke dm_get() under _minor_lock, some callers invoke it under _hash_lock, and dm_start_request() invoke it after increasing md->open_count. Signed-off-by: Hou Tao Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index fb07be386287..1d08dc6fa0b5 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -3033,11 +3033,15 @@ struct mapped_device *dm_get_from_kobject(struct kobject *kobj) md = container_of(kobj, struct mapped_device, kobj_holder.kobj); - if (test_bit(DMF_FREEING, &md->flags) || - dm_deleting_md(md)) - return NULL; - + spin_lock(&_minor_lock); + if (test_bit(DMF_FREEING, &md->flags) || dm_deleting_md(md)) { + md = NULL; + goto out; + } dm_get(md); +out: + spin_unlock(&_minor_lock); + return md; } -- GitLab From 778934f5837b647893dacf8ec5a224417f3c40c7 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Tue, 7 Nov 2017 19:09:20 +0000 Subject: [PATCH 1755/5498] MIPS: Fix an n32 core file generation regset support regression commit 547da673173de51f73887377eb275304775064ad upstream. Fix a commit 7aeb753b5353 ("MIPS: Implement task_user_regset_view.") regression, then activated by commit 6a9c001b7ec3 ("MIPS: Switch ELF core dumper to use regsets.)", that caused n32 processes to dump o32 core files by failing to set the EF_MIPS_ABI2 flag in the ELF core file header's `e_flags' member: $ file tls-core tls-core: ELF 32-bit MSB executable, MIPS, N32 MIPS64 rel2 version 1 (SYSV), [...] $ ./tls-core Aborted (core dumped) $ file core core: ELF 32-bit MSB core file MIPS, MIPS-I version 1 (SYSV), SVR4-style $ Previously the flag was set as the result of a: statement placed in arch/mips/kernel/binfmt_elfn32.c, however in the regset case, i.e. when CORE_DUMP_USE_REGSET is set, ELF_CORE_EFLAGS is no longer used by `fill_note_info' in fs/binfmt_elf.c, and instead the `->e_flags' member of the regset view chosen is. We have the views defined in arch/mips/kernel/ptrace.c, however only an o32 and an n64 one, and the latter is used for n32 as well. Consequently an o32 core file is incorrectly dumped from n32 processes (the ELF32 vs ELF64 class is chosen elsewhere, and the 32-bit one is correctly selected for n32). Correct the issue then by defining an n32 regset view and using it as appropriate. Issue discovered in GDB testing. Fixes: 7aeb753b5353 ("MIPS: Implement task_user_regset_view.") Signed-off-by: Maciej W. Rozycki Cc: Ralf Baechle Cc: Djordje Todorovic Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/17617/ Signed-off-by: James Hogan Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 29a56858f0e0..d23073284ad9 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -522,6 +522,19 @@ static const struct user_regset_view user_mips64_view = { .n = ARRAY_SIZE(mips64_regsets), }; +#ifdef CONFIG_MIPS32_N32 + +static const struct user_regset_view user_mipsn32_view = { + .name = "mipsn32", + .e_flags = EF_MIPS_ABI2, + .e_machine = ELF_ARCH, + .ei_osabi = ELF_OSABI, + .regsets = mips64_regsets, + .n = ARRAY_SIZE(mips64_regsets), +}; + +#endif /* CONFIG_MIPS32_N32 */ + #endif /* CONFIG_64BIT */ const struct user_regset_view *task_user_regset_view(struct task_struct *task) @@ -532,6 +545,10 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) #ifdef CONFIG_MIPS32_O32 if (test_tsk_thread_flag(task, TIF_32BIT_REGS)) return &user_mips_view; +#endif +#ifdef CONFIG_MIPS32_N32 + if (test_tsk_thread_flag(task, TIF_32BIT_ADDR)) + return &user_mipsn32_view; #endif return &user_mips64_view; #endif -- GitLab From a170fea94397340912a3586ca4cd7a5ad045e14c Mon Sep 17 00:00:00 2001 From: Mirko Parthey Date: Thu, 18 May 2017 21:30:03 +0200 Subject: [PATCH 1756/5498] MIPS: BCM47XX: Fix LED inversion for WRT54GSv1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 56a46acf62af5ba44fca2f3f1c7c25a2d5385b19 upstream. The WLAN LED on the Linksys WRT54GSv1 is active low, but the software treats it as active high. Fix the inverted logic. Fixes: 7bb26b169116 ("MIPS: BCM47xx: Fix LEDs on WRT54GS V1.0") Signed-off-by: Mirko Parthey Looks-ok-by: RafaÅ‚ MiÅ‚ecki Cc: Hauke Mehrtens Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/16071/ Signed-off-by: James Hogan Signed-off-by: Greg Kroah-Hartman --- arch/mips/bcm47xx/leds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/bcm47xx/leds.c b/arch/mips/bcm47xx/leds.c index 903a656d4119..2e2a7cf14ac1 100644 --- a/arch/mips/bcm47xx/leds.c +++ b/arch/mips/bcm47xx/leds.c @@ -323,7 +323,7 @@ bcm47xx_leds_linksys_wrt54g3gv2[] __initconst = { /* Verified on: WRT54GS V1.0 */ static const struct gpio_led bcm47xx_leds_linksys_wrt54g_type_0101[] __initconst = { - BCM47XX_GPIO_LED(0, "green", "wlan", 0, LEDS_GPIO_DEFSTATE_OFF), + BCM47XX_GPIO_LED(0, "green", "wlan", 1, LEDS_GPIO_DEFSTATE_OFF), BCM47XX_GPIO_LED(1, "green", "power", 0, LEDS_GPIO_DEFSTATE_ON), BCM47XX_GPIO_LED(7, "green", "dmz", 1, LEDS_GPIO_DEFSTATE_OFF), }; -- GitLab From 76b2a7f24267253dfa7061bd8799496a8f3984b6 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 17 Nov 2017 15:29:13 -0800 Subject: [PATCH 1757/5498] autofs: don't fail mount for transient error commit ecc0c469f27765ed1e2b967be0aa17cee1a60b76 upstream. Currently if the autofs kernel module gets an error when writing to the pipe which links to the daemon, then it marks the whole moutpoint as catatonic, and it will stop working. It is possible that the error is transient. This can happen if the daemon is slow and more than 16 requests queue up. If a subsequent process tries to queue a request, and is then signalled, the write to the pipe will return -ERESTARTSYS and autofs will take that as total failure. So change the code to assess -ERESTARTSYS and -ENOMEM as transient failures which only abort the current request, not the whole mountpoint. It isn't a crash or a data corruption, but having autofs mountpoints suddenly stop working is rather inconvenient. Ian said: : And given the problems with a half dozen (or so) user space applications : consuming large amounts of CPU under heavy mount and umount activity this : could happen more easily than we expect. Link: http://lkml.kernel.org/r/87y3norvgp.fsf@notabene.neil.brown.name Signed-off-by: NeilBrown Acked-by: Ian Kent Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/autofs4/waitq.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 116fd38ee472..8a2dcfc08c1c 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -87,7 +87,8 @@ static int autofs4_write(struct autofs_sb_info *sbi, spin_unlock_irqrestore(¤t->sighand->siglock, flags); } - return (bytes > 0); + /* if 'wr' returned 0 (impossible) we assume -EIO (safe) */ + return bytes == 0 ? 0 : wr < 0 ? wr : -EIO; } static void autofs4_notify_daemon(struct autofs_sb_info *sbi, @@ -101,6 +102,7 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, } pkt; struct file *pipe = NULL; size_t pktsz; + int ret; DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", (unsigned long) wq->wait_queue_token, wq->name.len, wq->name.name, type); @@ -173,7 +175,18 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, mutex_unlock(&sbi->wq_mutex); if (autofs4_write(sbi, pipe, &pkt, pktsz)) + switch (ret = autofs4_write(sbi, pipe, &pkt, pktsz)) { + case 0: + break; + case -ENOMEM: + case -ERESTARTSYS: + /* Just fail this one */ + autofs4_wait_release(sbi, wq->wait_queue_token, ret); + break; + default: autofs4_catatonic_mode(sbi); + break; + } fput(pipe); } -- GitLab From e69699d4c6fc4848f64455a617bb8a8943f526f8 Mon Sep 17 00:00:00 2001 From: Andreas Rohner Date: Fri, 17 Nov 2017 15:29:35 -0800 Subject: [PATCH 1758/5498] nilfs2: fix race condition that causes file system corruption commit 31ccb1f7ba3cfe29631587d451cf5bb8ab593550 upstream. There is a race condition between nilfs_dirty_inode() and nilfs_set_file_dirty(). When a file is opened, nilfs_dirty_inode() is called to update the access timestamp in the inode. It calls __nilfs_mark_inode_dirty() in a separate transaction. __nilfs_mark_inode_dirty() caches the ifile buffer_head in the i_bh field of the inode info structure and marks it as dirty. After some data was written to the file in another transaction, the function nilfs_set_file_dirty() is called, which adds the inode to the ns_dirty_files list. Then the segment construction calls nilfs_segctor_collect_dirty_files(), which goes through the ns_dirty_files list and checks the i_bh field. If there is a cached buffer_head in i_bh it is not marked as dirty again. Since nilfs_dirty_inode() and nilfs_set_file_dirty() use separate transactions, it is possible that a segment construction that writes out the ifile occurs in-between the two. If this happens the inode is not on the ns_dirty_files list, but its ifile block is still marked as dirty and written out. In the next segment construction, the data for the file is written out and nilfs_bmap_propagate() updates the b-tree. Eventually the bmap root is written into the i_bh block, which is not dirty, because it was written out in another segment construction. As a result the bmap update can be lost, which leads to file system corruption. Either the virtual block address points to an unallocated DAT block, or the DAT entry will be reused for something different. The error can remain undetected for a long time. A typical error message would be one of the "bad btree" errors or a warning that a DAT entry could not be found. This bug can be reproduced reliably by a simple benchmark that creates and overwrites millions of 4k files. Link: http://lkml.kernel.org/r/1509367935-3086-2-git-send-email-konishi.ryusuke@lab.ntt.co.jp Signed-off-by: Andreas Rohner Signed-off-by: Ryusuke Konishi Tested-by: Andreas Rohner Tested-by: Ryusuke Konishi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/segment.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 0c3f303baf32..3d38dc01991e 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1884,8 +1884,6 @@ static int nilfs_segctor_collect_dirty_files(struct nilfs_sc_info *sci, "failed to get inode block.\n"); return err; } - mark_buffer_dirty(ibh); - nilfs_mdt_mark_dirty(ifile); spin_lock(&nilfs->ns_inode_lock); if (likely(!ii->i_bh)) ii->i_bh = ibh; @@ -1894,6 +1892,10 @@ static int nilfs_segctor_collect_dirty_files(struct nilfs_sc_info *sci, goto retry; } + // Always redirty the buffer to avoid race condition + mark_buffer_dirty(ii->i_bh); + nilfs_mdt_mark_dirty(ifile); + clear_bit(NILFS_I_QUEUED, &ii->i_state); set_bit(NILFS_I_BUSY, &ii->i_state); list_move_tail(&ii->i_dirty, &sci->sc_dirty_files); -- GitLab From 1f0e79c6f523a90d1b4e998944413dba70d96ea9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 22 Aug 2017 23:41:28 +0300 Subject: [PATCH 1759/5498] eCryptfs: use after free in ecryptfs_release_messaging() commit db86be3a12d0b6e5c5b51c2ab2a48f06329cb590 upstream. We're freeing the list iterator so we should be using the _safe() version of hlist_for_each_entry(). Fixes: 88b4a07e6610 ("[PATCH] eCryptfs: Public key transport mechanism") Signed-off-by: Dan Carpenter Signed-off-by: Tyler Hicks Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/messaging.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 286f10b0363b..4f457d5c4933 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -442,15 +442,16 @@ void ecryptfs_release_messaging(void) } if (ecryptfs_daemon_hash) { struct ecryptfs_daemon *daemon; + struct hlist_node *n; int i; mutex_lock(&ecryptfs_daemon_hash_mux); for (i = 0; i < (1 << ecryptfs_hash_bits); i++) { int rc; - hlist_for_each_entry(daemon, - &ecryptfs_daemon_hash[i], - euid_chain) { + hlist_for_each_entry_safe(daemon, n, + &ecryptfs_daemon_hash[i], + euid_chain) { rc = ecryptfs_exorcise_daemon(daemon); if (rc) printk(KERN_ERR "%s: Error whilst " -- GitLab From 001cefee48affd8751808d38de16e6a32eb4b730 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Fri, 13 Oct 2017 16:35:29 -0700 Subject: [PATCH 1760/5498] bcache: check ca->alloc_thread initialized before wake up it commit 91af8300d9c1d7c6b6a2fd754109e08d4798b8d8 upstream. In bcache code, sysfs entries are created before all resources get allocated, e.g. allocation thread of a cache set. There is posibility for NULL pointer deference if a resource is accessed but which is not initialized yet. Indeed Jorg Bornschein catches one on cache set allocation thread and gets a kernel oops. The reason for this bug is, when bch_bucket_alloc() is called during cache set registration and attaching, ca->alloc_thread is not properly allocated and initialized yet, call wake_up_process() on ca->alloc_thread triggers NULL pointer deference failure. A simple and fast fix is, before waking up ca->alloc_thread, checking whether it is allocated, and only wake up ca->alloc_thread when it is not NULL. Signed-off-by: Coly Li Reported-by: Jorg Bornschein Cc: Kent Overstreet Reviewed-by: Michael Lyle Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/alloc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c index 8eeab72b93e2..ea47980949ef 100644 --- a/drivers/md/bcache/alloc.c +++ b/drivers/md/bcache/alloc.c @@ -406,7 +406,8 @@ long bch_bucket_alloc(struct cache *ca, unsigned reserve, bool wait) finish_wait(&ca->set->bucket_wait, &w); out: - wake_up_process(ca->alloc_thread); + if (ca->alloc_thread) + wake_up_process(ca->alloc_thread); trace_bcache_alloc(ca, reserve); -- GitLab From 491e4e3b12701cf54458f4ffe7dd76c64c37f076 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 19 Oct 2017 16:47:48 +0200 Subject: [PATCH 1761/5498] isofs: fix timestamps beyond 2027 commit 34be4dbf87fc3e474a842305394534216d428f5d upstream. isofs uses a 'char' variable to load the number of years since 1900 for an inode timestamp. On architectures that use a signed char type by default, this results in an invalid date for anything beyond 2027. This changes the function argument to a 'u8' array, which is defined the same way on all architectures, and unambiguously lets us use years until 2155. This should be backported to all kernels that might still be in use by that date. Signed-off-by: Arnd Bergmann Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/isofs/isofs.h | 2 +- fs/isofs/rock.h | 2 +- fs/isofs/util.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 0ac4c1f73fbd..25177e6bd603 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -103,7 +103,7 @@ static inline unsigned int isonum_733(char *p) /* Ignore bigendian datum due to broken mastering programs */ return get_unaligned_le32(p); } -extern int iso_date(char *, int); +extern int iso_date(u8 *, int); struct inode; /* To make gcc happy */ diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h index ed09e2b08637..f835976ce033 100644 --- a/fs/isofs/rock.h +++ b/fs/isofs/rock.h @@ -65,7 +65,7 @@ struct RR_PL_s { }; struct stamp { - char time[7]; + __u8 time[7]; /* actually 6 unsigned, 1 signed */ } __attribute__ ((packed)); struct RR_TF_s { diff --git a/fs/isofs/util.c b/fs/isofs/util.c index 01e1ee7a998b..5cdf5359b0e9 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -14,7 +14,7 @@ * to GMT. Thus we should always be correct. */ -int iso_date(char * p, int flag) +int iso_date(u8 *p, int flag) { int year, month, day, hour, minute, second, tz; int crtime, days, i; -- GitLab From 982a47b3ba76c04b2c56ec6f236623b5e2698d82 Mon Sep 17 00:00:00 2001 From: Joshua Watt Date: Tue, 7 Nov 2017 16:25:47 -0600 Subject: [PATCH 1762/5498] NFS: Fix typo in nomigration mount option commit f02fee227e5f21981152850744a6084ff3fa94ee upstream. The option was incorrectly masking off all other options. Signed-off-by: Joshua Watt Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 368d9395d2e7..dbdc2d2f91cf 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1321,7 +1321,7 @@ static int nfs_parse_mount_options(char *raw, mnt->options |= NFS_OPTION_MIGRATION; break; case Opt_nomigration: - mnt->options &= NFS_OPTION_MIGRATION; + mnt->options &= ~NFS_OPTION_MIGRATION; break; /* -- GitLab From c3014cd85e7ac963592b9efb1961f28e5bcadb4d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 5 Nov 2017 15:45:22 -0500 Subject: [PATCH 1763/5498] nfs: Fix ugly referral attributes commit c05cefcc72416a37eba5a2b35f0704ed758a9145 upstream. Before traversing a referral and performing a mount, the mounted-on directory looks strange: dr-xr-xr-x. 2 4294967294 4294967294 0 Dec 31 1969 dir.0 nfs4_get_referral is wiping out any cached attributes with what was returned via GETATTR(fs_locations), but the bit mask for that operation does not request any file attributes. Retrieve owner and timestamp information so that the memcpy in nfs4_get_referral fills in more attributes. Changes since v1: - Don't request attributes that the client unconditionally replaces - Request only MOUNTED_ON_FILEID or FILEID attribute, not both - encode_fs_locations() doesn't use the third bitmask word Fixes: 6b97fd3da1ea ("NFSv4: Follow a referral") Suggested-by: Pradeep Thomas Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4proc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index dbd010051b33..f7957690ff20 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -243,15 +243,12 @@ const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE }; const u32 nfs4_fs_locations_bitmap[3] = { - FATTR4_WORD0_TYPE - | FATTR4_WORD0_CHANGE + FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE | FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID | FATTR4_WORD0_FS_LOCATIONS, - FATTR4_WORD1_MODE - | FATTR4_WORD1_NUMLINKS - | FATTR4_WORD1_OWNER + FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV | FATTR4_WORD1_SPACE_USED @@ -6143,9 +6140,7 @@ static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, struct page *page) { struct nfs_server *server = NFS_SERVER(dir); - u32 bitmask[3] = { - [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, - }; + u32 bitmask[3]; struct nfs4_fs_locations_arg args = { .dir_fh = NFS_FH(dir), .name = name, @@ -6164,12 +6159,15 @@ static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, dprintk("%s: start\n", __func__); + bitmask[0] = nfs4_fattr_bitmap[0] | FATTR4_WORD0_FS_LOCATIONS; + bitmask[1] = nfs4_fattr_bitmap[1]; + /* Ask for the fileid of the absent filesystem if mounted_on_fileid * is not supported */ if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) - bitmask[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID; + bitmask[0] &= ~FATTR4_WORD0_FILEID; else - bitmask[0] |= FATTR4_WORD0_FILEID; + bitmask[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; nfs_fattr_init(&fs_locations->fattr); fs_locations->server = server; -- GitLab From 6b9f616effa8401bdd393099cb17adb5f7ecd3a4 Mon Sep 17 00:00:00 2001 From: Andrew Elble Date: Fri, 3 Nov 2017 14:06:31 -0400 Subject: [PATCH 1764/5498] nfsd: deal with revoked delegations appropriately commit 95da1b3a5aded124dd1bda1e3cdb876184813140 upstream. If a delegation has been revoked by the server, operations using that delegation should error out with NFS4ERR_DELEG_REVOKED in the >4.1 case, and NFS4ERR_BAD_STATEID otherwise. The server needs NFSv4.1 clients to explicitly free revoked delegations. If the server returns NFS4ERR_DELEG_REVOKED, the client will do that; otherwise it may just forget about the delegation and be unable to recover when it later sees SEQ4_STATUS_RECALLABLE_STATE_REVOKED set on a SEQUENCE reply. That can cause the Linux 4.1 client to loop in its stage manager. Signed-off-by: Andrew Elble Reviewed-by: Trond Myklebust Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0b9476aea67e..d59ce5a665d9 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3602,7 +3602,8 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei { struct nfs4_stid *ret; - ret = find_stateid_by_type(cl, s, NFS4_DELEG_STID); + ret = find_stateid_by_type(cl, s, + NFS4_DELEG_STID|NFS4_REVOKED_DELEG_STID); if (!ret) return NULL; return delegstateid(ret); @@ -3625,6 +3626,12 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open, deleg = find_deleg_stateid(cl, &open->op_delegate_stateid); if (deleg == NULL) goto out; + if (deleg->dl_stid.sc_type == NFS4_REVOKED_DELEG_STID) { + nfs4_put_stid(&deleg->dl_stid); + if (cl->cl_minorversion) + status = nfserr_deleg_revoked; + goto out; + } flags = share_access_to_flags(open->op_share_access); status = nfs4_check_delegmode(deleg, flags); if (status) { @@ -4451,6 +4458,16 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, struct nfs4_stid **s, struct nfsd_net *nn) { __be32 status; + bool return_revoked = false; + + /* + * only return revoked delegations if explicitly asked. + * otherwise we report revoked or bad_stateid status. + */ + if (typemask & NFS4_REVOKED_DELEG_STID) + return_revoked = true; + else if (typemask & NFS4_DELEG_STID) + typemask |= NFS4_REVOKED_DELEG_STID; if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) return nfserr_bad_stateid; @@ -4465,6 +4482,12 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, *s = find_stateid_by_type(cstate->clp, stateid, typemask); if (!*s) return nfserr_bad_stateid; + if (((*s)->sc_type == NFS4_REVOKED_DELEG_STID) && !return_revoked) { + nfs4_put_stid(*s); + if (cstate->minorversion) + return nfserr_deleg_revoked; + return nfserr_bad_stateid; + } return nfs_ok; } -- GitLab From 9b3f0217e0b7272204c9efc07bec1a5e4e1d6502 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 6 Oct 2017 23:09:55 -0400 Subject: [PATCH 1765/5498] ext4: fix interaction between i_size, fallocate, and delalloc after a crash commit 51e3ae81ec58e95f10a98ef3dd6d7bce5d8e35a2 upstream. If there are pending writes subject to delayed allocation, then i_size will show size after the writes have completed, while i_disksize contains the value of i_size on the disk (since the writes have not been persisted to disk). If fallocate(2) is called with the FALLOC_FL_KEEP_SIZE flag, either with or without the FALLOC_FL_ZERO_RANGE flag set, and the new size after the fallocate(2) is between i_size and i_disksize, then after a crash, if a journal commit has resulted in the changes made by the fallocate() call to be persisted after a crash, but the delayed allocation write has not resolved itself, i_size would not be updated, and this would cause the following e2fsck complaint: Inode 12, end of extent exceeds allowed value (logical block 33, physical block 33441, len 7) This can only take place on a sparse file, where the fallocate(2) call is allocating blocks in a range which is before a pending delayed allocation write which is extending i_size. Since this situation is quite rare, and the window in which the crash must take place is typically < 30 seconds, in practice this condition will rarely happen. Nevertheless, it can be triggered in testing, and in particular by xfstests generic/456. Signed-off-by: Theodore Ts'o Reported-by: Amir Goldstein Signed-off-by: Greg Kroah-Hartman --- fs/ext4/extents.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index e5b44dd2724e..f2f2b86936a5 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4807,7 +4807,8 @@ static long ext4_zero_range(struct file *file, loff_t offset, } if (!(mode & FALLOC_FL_KEEP_SIZE) && - offset + len > i_size_read(inode)) { + (offset + len > i_size_read(inode) || + offset + len > EXT4_I(inode)->i_disksize)) { new_size = offset + len; ret = inode_newsize_ok(inode, new_size); if (ret) @@ -4951,7 +4952,8 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) } if (!(mode & FALLOC_FL_KEEP_SIZE) && - offset + len > i_size_read(inode)) { + (offset + len > i_size_read(inode) || + offset + len > EXT4_I(inode)->i_disksize)) { new_size = offset + len; ret = inode_newsize_ok(inode, new_size); if (ret) -- GitLab From 7940c950ca9460311cd1804f86655b1791ab128e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Nov 2017 16:55:51 +0100 Subject: [PATCH 1766/5498] ALSA: usb-audio: Add sanity checks to FE parser commit d937cd6790a2bef2d07b500487646bd794c039bb upstream. When the usb-audio descriptor contains the malformed feature unit description with a too short length, the driver may access out-of-bounds. Add a sanity check of the header size at the beginning of parse_audio_feature_unit(). Fixes: 23caaf19b11e ("ALSA: usb-mixer: Add support for Audio Class v2.0") Reported-by: Andrey Konovalov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index d0bbc7b32190..127c6395d764 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1373,6 +1373,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, __u8 *bmaControls; if (state->mixer->protocol == UAC_VERSION_1) { + if (hdr->bLength < 7) { + usb_audio_err(state->chip, + "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } csize = hdr->bControlSize; if (!csize) { usb_audio_dbg(state->chip, @@ -1390,6 +1396,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, } } else { struct uac2_feature_unit_descriptor *ftr = _ftr; + if (hdr->bLength < 6) { + usb_audio_err(state->chip, + "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", + unitid); + return -EINVAL; + } csize = 4; channels = (hdr->bLength - 6) / 4 - 1; bmaControls = ftr->bmaControls; -- GitLab From d1db6f6a2ccbc1f6910cdd13fd18cc7291b89656 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Nov 2017 17:00:32 +0100 Subject: [PATCH 1767/5498] ALSA: usb-audio: Fix potential out-of-bound access at parsing SU commit f658f17b5e0e339935dca23e77e0f3cad591926b upstream. The usb-audio driver may trigger an out-of-bound access at parsing a malformed selector unit, as it checks the header length only after evaluating bNrInPins field, which can be already above the given length. Fix it by adding the length check beforehand. Fixes: 99fc86450c43 ("ALSA: usb-mixer: parse descriptors with structs") Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 127c6395d764..efa045c6ad63 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2018,7 +2018,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, const struct usbmix_name_map *map; char **namelist; - if (!desc->bNrInPins || desc->bLength < 5 + desc->bNrInPins) { + if (desc->bLength < 5 || !desc->bNrInPins || + desc->bLength < 5 + desc->bNrInPins) { usb_audio_err(state->chip, "invalid SELECTOR UNIT descriptor %d\n", unitid); return -EINVAL; -- GitLab From 2033ec9cc47915bfc11aa1719e80089b0f994c87 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Nov 2017 17:28:06 +0100 Subject: [PATCH 1768/5498] ALSA: usb-audio: Add sanity checks in v2 clock parsers commit 0a62d6c966956d77397c32836a5bbfe3af786fc1 upstream. The helper functions to parse and look for the clock source, selector and multiplier unit may return the descriptor with a too short length than required, while there is no sanity check in the caller side. Add some sanity checks in the parsers, at least, to guarantee the given descriptor size, for avoiding the potential crashes. Fixes: 79f920fbff56 ("ALSA: usb-audio: parse clock topology of UAC2 devices") Reported-by: Andrey Konovalov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/clock.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 2ed260b10f6d..f041196b36c2 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -43,7 +43,7 @@ static struct uac_clock_source_descriptor * while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_SOURCE))) { - if (cs->bClockID == clock_id) + if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id) return cs; } @@ -59,8 +59,11 @@ static struct uac_clock_selector_descriptor * while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_SELECTOR))) { - if (cs->bClockID == clock_id) + if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id) { + if (cs->bLength < 5 + cs->bNrInPins) + return NULL; return cs; + } } return NULL; @@ -75,7 +78,7 @@ static struct uac_clock_multiplier_descriptor * while ((cs = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, cs, UAC2_CLOCK_MULTIPLIER))) { - if (cs->bClockID == clock_id) + if (cs->bLength >= sizeof(*cs) && cs->bClockID == clock_id) return cs; } -- GitLab From 9f8eafa18a285d5d10e9df8e6622f592df78c037 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Nov 2017 16:36:11 +0100 Subject: [PATCH 1769/5498] ALSA: timer: Remove kernel warning at compat ioctl error paths commit 3d4e8303f2c747c8540a0a0126d0151514f6468b upstream. Some timer compat ioctls have NULL checks of timer instance with snd_BUG_ON() that bring up WARN_ON() when the debug option is set. Actually the condition can be met in the normal situation and it's confusing and bad to spew kernel warnings with stack trace there. Let's remove snd_BUG_ON() invocation and replace with the simple checks. Also, correct the error code to EBADFD to follow the native ioctl error handling. Reported-by: syzbot Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/timer_compat.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c index 0b4b028e8e98..de9155eed727 100644 --- a/sound/core/timer_compat.c +++ b/sound/core/timer_compat.c @@ -40,11 +40,11 @@ static int snd_timer_user_info_compat(struct file *file, struct snd_timer *t; tu = file->private_data; - if (snd_BUG_ON(!tu->timeri)) - return -ENXIO; + if (!tu->timeri) + return -EBADFD; t = tu->timeri->timer; - if (snd_BUG_ON(!t)) - return -ENXIO; + if (!t) + return -EBADFD; memset(&info, 0, sizeof(info)); info.card = t->card ? t->card->number : -1; if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) @@ -73,8 +73,8 @@ static int snd_timer_user_status_compat(struct file *file, struct snd_timer_status32 status; tu = file->private_data; - if (snd_BUG_ON(!tu->timeri)) - return -ENXIO; + if (!tu->timeri) + return -EBADFD; memset(&status, 0, sizeof(status)); status.tstamp.tv_sec = tu->tstamp.tv_sec; status.tstamp.tv_nsec = tu->tstamp.tv_nsec; -- GitLab From e4820a86b26ee8da52ef5cf620c5f24865f89628 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Wed, 6 Sep 2017 17:59:07 +0300 Subject: [PATCH 1770/5498] fs/9p: Compare qid.path in v9fs_test_inode commit 8ee031631546cf2f7859cc69593bd60bbdd70b46 upstream. Commit fd2421f54423 ("fs/9p: When doing inode lookup compare qid details and inode mode bits.") transformed v9fs_qid_iget() to use iget5_locked() instead of iget_locked(). However, the test() callback is not checking fid.path at all, which means that a lookup in the inode cache can now accidentally locate a completely wrong inode from the same inode hash bucket if the other fields (qid.type and qid.version) match. Fixes: fd2421f54423 ("fs/9p: When doing inode lookup compare qid details and inode mode bits.") Reviewed-by: Latchesar Ionkov Signed-off-by: Tuomas Tynkkynen Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/9p/vfs_inode.c | 3 +++ fs/9p/vfs_inode_dotl.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index a2f6e9ae1a98..82f1ea6d21a7 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -483,6 +483,9 @@ static int v9fs_test_inode(struct inode *inode, void *data) if (v9inode->qid.type != st->qid.type) return 0; + + if (v9inode->qid.path != st->qid.path) + return 0; return 1; } diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 092d20c643e5..2a03f78dea86 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -87,6 +87,9 @@ static int v9fs_test_inode_dotl(struct inode *inode, void *data) if (v9inode->qid.type != st->qid.type) return 0; + + if (v9inode->qid.path != st->qid.path) + return 0; return 1; } -- GitLab From eeb6715ffb90cf22a379d1627dcdaa733f9f5b1e Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 27 Oct 2017 20:52:56 -0700 Subject: [PATCH 1771/5498] iscsi-target: Fix non-immediate TMR reference leak commit 3fc9fb13a4b2576aeab86c62fd64eb29ab68659c upstream. This patch fixes a se_cmd->cmd_kref reference leak that can occur when a non immediate TMR is proceeded our of command sequence number order, and CMDSN_LOWER_THAN_EXP is returned by iscsit_sequence_cmd(). To address this bug, call target_put_sess_cmd() during this special case following what iscsit_process_scsi_cmd() does upon CMDSN_LOWER_THAN_EXP. Cc: Mike Christie Cc: Hannes Reinecke Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 2fc6b495df05..f0ace220753e 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1915,12 +1915,14 @@ attach: if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) { int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn); - if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) + if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) { out_of_order_cmdsn = 1; - else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) + } else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { + target_put_sess_cmd(&cmd->se_cmd); return 0; - else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) + } else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) { return -1; + } } iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn)); -- GitLab From 27cd63fee6964d5af2004298f69cadaae71708e4 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Wed, 11 Oct 2017 16:54:42 +0200 Subject: [PATCH 1772/5498] KVM: nVMX: set IDTR and GDTR limits when loading L1 host state commit 21f2d551183847bc7fbe8d866151d00cdad18752 upstream. Intel SDM 27.5.2 Loading Host Segment and Descriptor-Table Registers: "The GDTR and IDTR limits are each set to FFFFH." Signed-off-by: Ladi Prosek Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index a9ca10baf936..3f51f81233c5 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -8929,6 +8929,8 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, vmcs_writel(GUEST_SYSENTER_EIP, vmcs12->host_ia32_sysenter_eip); vmcs_writel(GUEST_IDTR_BASE, vmcs12->host_idtr_base); vmcs_writel(GUEST_GDTR_BASE, vmcs12->host_gdtr_base); + vmcs_write32(GUEST_IDTR_LIMIT, 0xFFFF); + vmcs_write32(GUEST_GDTR_LIMIT, 0xFFFF); /* If not VM_EXIT_CLEAR_BNDCFGS, the L2 value propagates to L1. */ if (vmcs12->vm_exit_controls & VM_EXIT_CLEAR_BNDCFGS) -- GitLab From 5f8a3df04772b367a1b18c943b7e1671ad9fd9e9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 26 Oct 2017 09:13:27 +0200 Subject: [PATCH 1773/5498] KVM: SVM: obey guest PAT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 15038e14724799b8c205beb5f20f9e54896013c3 upstream. For many years some users of assigned devices have reported worse performance on AMD processors with NPT than on AMD without NPT, Intel or bare metal. The reason turned out to be that SVM is discarding the guest PAT setting and uses the default (PA0=PA4=WB, PA1=PA5=WT, PA2=PA6=UC-, PA3=UC). The guest might be using a different setting, and especially might want write combining but isn't getting it (instead getting slow UC or UC- accesses). Thanks a lot to geoff@hostfission.com for noticing the relation to the g_pat setting. The patch has been tested also by a bunch of people on VFIO users forums. Fixes: 709ddebf81cb40e3c36c6109a7892e8b93a09464 Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=196409 Signed-off-by: Paolo Bonzini Reviewed-by: David Hildenbrand Tested-by: Nick Sarnie Signed-off-by: Radim KrÄmář Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 9dc0aa0dae96..073c107faeb4 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -3187,6 +3187,13 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) u32 ecx = msr->index; u64 data = msr->data; switch (ecx) { + case MSR_IA32_CR_PAT: + if (!kvm_mtrr_valid(vcpu, MSR_IA32_CR_PAT, data)) + return 1; + vcpu->arch.pat = data; + svm->vmcb->save.g_pat = data; + mark_dirty(svm->vmcb, VMCB_NPT); + break; case MSR_IA32_TSC: kvm_write_tsc(vcpu, msr); break; -- GitLab From 26b3a4046639b949979fa071c761ef139f48c33d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Mar 2016 16:13:32 +0200 Subject: [PATCH 1774/5498] clk: ti: dra7-atl-clock: Fix of_node reference counting commit 660e1551939931657808d47838a3f443c0e83fd0 upstream. of_find_node_by_name() will call of_node_put() on the node so we need to get it first to avoid warnings. The cfg_node needs to be put after we have finished processing the properties. Signed-off-by: Peter Ujfalusi Tested-by: Nishanth Menon Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/clk/ti/clk-dra7-atl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index 59bb4b39d12e..dd980c84dab7 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -259,6 +259,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) /* Get configuration for the ATL instances */ snprintf(prop, sizeof(prop), "atl%u", i); + of_node_get(node); cfg_node = of_find_node_by_name(node, prop); if (cfg_node) { ret = of_property_read_u32(cfg_node, "bws", @@ -272,6 +273,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) atl_write(cinfo, DRA7_ATL_AWSMUX_REG(i), cdesc->aws); } + of_node_put(cfg_node); } cdesc->probed = true; -- GitLab From 9b4080c28dabbf055e6eb7362a90de0a5e29f5c1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 11 Nov 2017 17:29:29 +0100 Subject: [PATCH 1775/5498] clk: ti: dra7-atl-clock: fix child-node lookups commit 33ec6dbc5a02677509d97fe36cd2105753f0f0ea upstream. Fix child node-lookup during probe, which ended up searching the whole device tree depth-first starting at parent rather than just matching on its children. Note that the original premature free of the parent node has already been fixed separately, but that fix was apparently never backported to stable. Fixes: 9ac33b0ce81f ("CLK: TI: Driver for DRA7 ATL (Audio Tracking Logic)") Fixes: 660e15519399 ("clk: ti: dra7-atl-clock: Fix of_node reference counting") Cc: Peter Ujfalusi Signed-off-by: Johan Hovold Acked-by: Peter Ujfalusi Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/clk/ti/clk-dra7-atl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index dd980c84dab7..d41b83239a3c 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -259,8 +259,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) /* Get configuration for the ATL instances */ snprintf(prop, sizeof(prop), "atl%u", i); - of_node_get(node); - cfg_node = of_find_node_by_name(node, prop); + cfg_node = of_get_child_by_name(node, prop); if (cfg_node) { ret = of_property_read_u32(cfg_node, "bws", &cdesc->bws); -- GitLab From d7c44dae8d8ca328a67db884194b1fa592f723db Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 11 Oct 2017 10:27:22 -0700 Subject: [PATCH 1776/5498] IB/srpt: Do not accept invalid initiator port names commit c70ca38960399a63d5c048b7b700612ea321d17e upstream. Make srpt_parse_i_port_id() return a negative value if hex2bin() fails. Fixes: commit a42d985bd5b2 ("ib_srpt: Initial SRP Target merge for v3.3-rc1") Signed-off-by: Bart Van Assche Signed-off-by: Doug Ledford Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/srpt/ib_srpt.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 9fc0326c1da7..9d72100a0394 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -3522,7 +3522,7 @@ static int srpt_parse_i_port_id(u8 i_port_id[16], const char *name) { const char *p; unsigned len, count, leading_zero_bytes; - int ret, rc; + int ret; p = name; if (strncasecmp(p, "0x", 2) == 0) @@ -3534,10 +3534,9 @@ static int srpt_parse_i_port_id(u8 i_port_id[16], const char *name) count = min(len / 2, 16U); leading_zero_bytes = 16 - count; memset(i_port_id, 0, leading_zero_bytes); - rc = hex2bin(i_port_id + leading_zero_bytes, p, count); - if (rc < 0) - pr_debug("hex2bin failed for srpt_parse_i_port_id: %d\n", rc); - ret = 0; + ret = hex2bin(i_port_id + leading_zero_bytes, p, count); + if (ret < 0) + pr_debug("hex2bin failed for srpt_parse_i_port_id: %d\n", ret); out: return ret; } -- GitLab From b880bebed0d520385676be5e028a067b1914c062 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 9 Jul 2017 13:08:58 +0200 Subject: [PATCH 1777/5498] NFC: fix device-allocation error return commit c45e3e4c5b134b081e8af362109905427967eb19 upstream. A recent change fixing NFC device allocation itself introduced an error-handling bug by returning an error pointer in case device-id allocation failed. This is clearly broken as the callers still expected NULL to be returned on errors as detected by Dan's static checker. Fix this up by returning NULL in the event that we've run out of memory when allocating a new device id. Note that the offending commit is marked for stable (3.8) so this fix needs to be backported along with it. Fixes: 20777bc57c34 ("NFC: fix broken device allocation") Reported-by: Dan Carpenter Signed-off-by: Johan Hovold Signed-off-by: Samuel Ortiz Signed-off-by: Greg Kroah-Hartman --- net/nfc/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/nfc/core.c b/net/nfc/core.c index 2ff3c924b64f..a8e8756878de 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -1074,7 +1074,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, err_free_dev: kfree(dev); - return ERR_PTR(rc); + return NULL; } EXPORT_SYMBOL(nfc_allocate_device); -- GitLab From 7a615c8566d78a8a0d07de99ef5c6da7f61c77b7 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Tue, 23 Jun 2015 18:38:54 +0800 Subject: [PATCH 1778/5498] time: Always make sure wall_to_monotonic isn't positive commit e1d7ba8735551ed79c7a0463a042353574b96da3 upstream. Two issues were found on an IMX6 development board without an enabled RTC device(resulting in the boot time and monotonic time being initialized to 0). Issue 1:exportfs -a generate: "exportfs: /opt/nfs/arm does not support NFS export" Issue 2:cat /proc/stat: "btime 4294967236" The same issues can be reproduced on x86 after running the following code: int main(void) { struct timeval val; int ret; val.tv_sec = 0; val.tv_usec = 0; ret = settimeofday(&val, NULL); return 0; } Two issues are different symptoms of same problem: The reason is a positive wall_to_monotonic pushes boot time back to the time before Epoch, and getboottime will return negative value. In symptom 1: negative boot time cause get_expiry() to overflow time_t when input expire time is 2147483647, then cache_flush() always clears entries just added in ip_map_parse. In symptom 2: show_stat() uses "unsigned long" to print negative btime value returned by getboottime. This patch fix the problem by prohibiting time from being set to a value which would cause a negative boot time. As a result one can't set the CLOCK_REALTIME time prior to (1970 + system uptime). Cc: Prarit Bhargava Cc: Richard Cochran Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Wang YanQing [jstultz: reworded commit message] [msfjarvis: Backport to 3.18 as we are missing the do_settimeofday64 function the upstream commit patches, so we apply the changes to do_settimeofday] Signed-off-by: John Stultz Signed-off-by: Harsh Shandilya Signed-off-by: Greg Kroah-Hartman --- kernel/time/timekeeping.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index c596236607ae..70e3e6ae06d2 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -712,6 +712,7 @@ int do_settimeofday(const struct timespec *tv) struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts_delta, xt, tmp; unsigned long flags; + int ret = 0; if (!timespec_valid_strict(tv)) return -EINVAL; @@ -725,11 +726,16 @@ int do_settimeofday(const struct timespec *tv) ts_delta.tv_sec = tv->tv_sec - xt.tv_sec; ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec; + if (timespec64_compare(&tk->wall_to_monotonic, &ts_delta) > 0) { + ret = -EINVAL; + goto out; + } + tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts_delta)); tmp = timespec_to_timespec64(*tv); tk_set_xtime(tk, &tmp); - +out: timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&tk_core.seq); @@ -738,7 +744,7 @@ int do_settimeofday(const struct timespec *tv) /* signal hrtimers about time change */ clock_was_set(); - return 0; + return ret; } EXPORT_SYMBOL(do_settimeofday); @@ -767,7 +773,8 @@ int timekeeping_inject_offset(struct timespec *ts) /* Make sure the proposed value is valid */ tmp = timespec64_add(tk_xtime(tk), ts64); - if (!timespec64_valid_strict(&tmp)) { + if (timespec64_compare(&tk->wall_to_monotonic, &ts64) > 0 || + !timespec64_valid_strict(&tmp)) { ret = -EINVAL; goto error; } -- GitLab From f4e3b96ba8ba8404e504632aa780f225363596dd Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 17 Nov 2017 11:05:44 -0600 Subject: [PATCH 1779/5498] i40e: Use smp_rmb rather than read_barrier_depends commit 52c6912fde0133981ee50ba08808f257829c4c93 upstream. The original issue being fixed in this patch was seen with the ixgbe driver, but the same issue exists with i40e as well, as the code is very similar. read_barrier_depends is not sufficient to ensure loads following it are not speculatively loaded out of order by the CPU, which can result in stale data being loaded, causing potential system crashes. Signed-off-by: Brian King Acked-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c3a7f4a4b775..81cd60dd8a2f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3214,7 +3214,7 @@ static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if the descriptor isn't done, no work yet to do */ if (!(eop_desc->cmd_type_offset_bsz & diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 3195d82e4942..2d8d49e8bd99 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -688,7 +688,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* we have caught up to head, no work left to do */ if (tx_head == tx_desc) -- GitLab From 5f068a6634761bf1d732a1173305faf92fecbe1f Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 17 Nov 2017 11:05:47 -0600 Subject: [PATCH 1780/5498] igb: Use smp_rmb rather than read_barrier_depends commit c4cb99185b4cc96c0a1c70104dc21ae14d7e7f28 upstream. The original issue being fixed in this patch was seen with the ixgbe driver, but the same issue exists with igb as well, as the code is very similar. read_barrier_depends is not sufficient to ensure loads following it are not speculatively loaded out of order by the CPU, which can result in stale data being loaded, causing potential system crashes. Signed-off-by: Brian King Acked-by: Jesse Brandeburg Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 21d868e287df..2ba73fc6d328 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6383,7 +6383,7 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(E1000_TXD_STAT_DD))) -- GitLab From 4fc9c3c798478cc3e741e583d2936a705f2ee9e4 Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 17 Nov 2017 11:05:46 -0600 Subject: [PATCH 1781/5498] igbvf: Use smp_rmb rather than read_barrier_depends commit 1e1f9ca546556e508d021545861f6b5fc75a95fe upstream. The original issue being fixed in this patch was seen with the ixgbe driver, but the same issue exists with igbvf as well, as the code is very similar. read_barrier_depends is not sufficient to ensure loads following it are not speculatively loaded out of order by the CPU, which can result in stale data being loaded, causing potential system crashes. Signed-off-by: Brian King Acked-by: Jesse Brandeburg Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igbvf/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 63c807c9b21c..7cf3fbb8bc35 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -808,7 +808,7 @@ static bool igbvf_clean_tx_irq(struct igbvf_ring *tx_ring) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(E1000_TXD_STAT_DD))) -- GitLab From bc0a7c84883c8b0eabac4adaa65228a911694f0e Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 17 Nov 2017 11:05:45 -0600 Subject: [PATCH 1782/5498] ixgbevf: Use smp_rmb rather than read_barrier_depends commit ae0c585d93dfaf923d2c7eb44b2c3ab92854ea9b upstream. The original issue being fixed in this patch was seen with the ixgbe driver, but the same issue exists with ixgbevf as well, as the code is very similar. read_barrier_depends is not sufficient to ensure loads following it are not speculatively loaded out of order by the CPU, which can result in stale data being loaded, causing potential system crashes. Signed-off-by: Brian King Acked-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 030a219c85e3..85bf4453b905 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -249,7 +249,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD))) -- GitLab From 777ee59c69604acbee8be25c3e9403b7dfa15147 Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 17 Nov 2017 11:05:49 -0600 Subject: [PATCH 1783/5498] i40evf: Use smp_rmb rather than read_barrier_depends commit f72271e2a0ae4277d53c4053f5eed8bb346ba38a upstream. The original issue being fixed in this patch was seen with the ixgbe driver, but the same issue exists with i40evf as well, as the code is very similar. read_barrier_depends is not sufficient to ensure loads following it are not speculatively loaded out of order by the CPU, which can result in stale data being loaded, causing potential system crashes. Signed-off-by: Brian King Acked-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 04c7c1557a0c..139c8103d83a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -222,7 +222,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* we have caught up to head, no work left to do */ if (tx_head == tx_desc) -- GitLab From 049cd7e4029bbc4990d0d687bdb0226c9c7be1b8 Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 17 Nov 2017 11:05:48 -0600 Subject: [PATCH 1784/5498] fm10k: Use smp_rmb rather than read_barrier_depends commit 7b8edcc685b5e2c3c37aa13dc50a88e84a5bfef8 upstream. The original issue being fixed in this patch was seen with the ixgbe driver, but the same issue exists with fm10k as well, as the code is very similar. read_barrier_depends is not sufficient to ensure loads following it are not speculatively loaded out of order by the CPU, which can result in stale data being loaded, causing potential system crashes. Signed-off-by: Brian King Acked-by: Jesse Brandeburg Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index e645af412e76..1f9b9c8da5a4 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1218,7 +1218,7 @@ static bool fm10k_clean_tx_irq(struct fm10k_q_vector *q_vector, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->flags & FM10K_TXD_FLAG_DONE)) -- GitLab From ebfaa7529584779fd940e16b9204aa5c35c058ce Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 17 Nov 2017 11:05:43 -0600 Subject: [PATCH 1785/5498] ixgbe: Fix skb list corruption on Power systems commit 0a9a17e3bb4564caf4bfe2a6783ae1287667d188 upstream. This patch fixes an issue seen on Power systems with ixgbe which results in skb list corruption and an eventual kernel oops. The following is what was observed: CPU 1 CPU2 ============================ ============================ 1: ixgbe_xmit_frame_ring ixgbe_clean_tx_irq 2: first->skb = skb eop_desc = tx_buffer->next_to_watch 3: ixgbe_tx_map read_barrier_depends() 4: wmb check adapter written status bit 5: first->next_to_watch = tx_desc napi_consume_skb(tx_buffer->skb ..); 6: writel(i, tx_ring->tail); The read_barrier_depends is insufficient to ensure that tx_buffer->skb does not get loaded prior to tx_buffer->next_to_watch, which then results in loading a stale skb pointer. This patch replaces the read_barrier_depends with smp_rmb to ensure loads are ordered with respect to the load of tx_buffer->next_to_watch. Signed-off-by: Brian King Acked-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index de3a17088578..b250aa806d07 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1080,7 +1080,7 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, break; /* prevent any other reads prior to eop_desc */ - read_barrier_depends(); + smp_rmb(); /* if DD is not set pending work has not been completed */ if (!(eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD))) -- GitLab From c7ac1d7390e934e1aec27170a512edfaac25809b Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Sat, 11 Nov 2017 17:11:16 -0500 Subject: [PATCH 1786/5498] parisc: Fix validity check of pointer size argument in new CAS implementation commit 05f016d2ca7a4fab99d5d5472168506ddf95e74f upstream. As noted by Christoph Biedl, passing a pointer size of 4 in the new CAS implementation causes a kernel crash. The attached patch corrects the off by one error in the argument validity check. In reviewing the code, I noticed that we only perform word operations with the pointer size argument. The subi instruction intentionally uses a word condition on 64-bit kernels. Nullification was used instead of a cmpib instruction as the branch should never be taken. The shlw pseudo-operation generates a depw,z instruction and it clears the target before doing a shift left word deposit. Thus, we don't need to clip the upper 32 bits of this argument on 64-bit kernels. Tested with a gcc testsuite run with a 64-bit kernel. The gcc atomic code in libgcc is the only direct user of the new CAS implementation that I am aware of. Signed-off-by: John David Anglin Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/syscall.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index 95f090fe385a..196395a0ac91 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -688,15 +688,15 @@ cas_action: /* ELF32 Process entry path */ lws_compare_and_swap_2: #ifdef CONFIG_64BIT - /* Clip the input registers */ + /* Clip the input registers. We don't need to clip %r23 as we + only use it for word operations */ depdi 0, 31, 32, %r26 depdi 0, 31, 32, %r25 depdi 0, 31, 32, %r24 - depdi 0, 31, 32, %r23 #endif /* Check the validity of the size pointer */ - subi,>>= 4, %r23, %r0 + subi,>>= 3, %r23, %r0 b,n lws_exit_nosys /* Jump to the functions which will load the old and new values into -- GitLab From b91c8602241112b7ed61087d5b69d8fe737ea393 Mon Sep 17 00:00:00 2001 From: "Naveen N. Rao" Date: Thu, 31 Aug 2017 21:55:57 +0530 Subject: [PATCH 1787/5498] powerpc/signal: Properly handle return value from uprobe_deny_signal() commit 46725b17f1c6c815a41429259b3f070c01e71bc1 upstream. When a uprobe is installed on an instruction that we currently do not emulate, we copy the instruction into a xol buffer and single step that instruction. If that instruction generates a fault, we abort the single stepping before invoking the signal handler. Once the signal handler is done, the uprobe trap is hit again since the instruction is retried and the process repeats. We use uprobe_deny_signal() to detect if the xol instruction triggered a signal. If so, we clear TIF_SIGPENDING and set TIF_UPROBE so that the signal is not handled until after the single stepping is aborted. In this case, uprobe_deny_signal() returns true and get_signal() ends up returning 0. However, in do_signal(), we are not looking at the return value, but depending on ksig.sig for further action, all with an uninitialized ksig that is not touched in this scenario. Fix the same by initializing ksig.sig to 0. Fixes: 129b69df9c90 ("powerpc: Use get_signal() signal_setup_done()") Reported-by: Anton Blanchard Signed-off-by: Naveen N. Rao Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index cf8c7e4e0b21..984a54c85952 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -102,7 +102,7 @@ static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, static void do_signal(struct pt_regs *regs) { sigset_t *oldset = sigmask_to_save(); - struct ksignal ksig; + struct ksignal ksig = { .sig = 0 }; int ret; int is32 = is_32bit_task(); -- GitLab From d4674227f40f47734d1c1575445a4f7cee2494f8 Mon Sep 17 00:00:00 2001 From: Michele Baldessari Date: Mon, 6 Nov 2017 08:50:22 -0500 Subject: [PATCH 1788/5498] media: Don't do DMA on stack for firmware upload in the AS102 driver commit b3120d2cc447ee77b9d69bf4ad7b452c9adb4d39 upstream. Firmware load on AS102 is using the stack which is not allowed any longer. We currently fail with: kernel: transfer buffer not dma capable kernel: ------------[ cut here ]------------ kernel: WARNING: CPU: 0 PID: 598 at drivers/usb/core/hcd.c:1595 usb_hcd_map_urb_for_dma+0x41d/0x620 kernel: Modules linked in: amd64_edac_mod(-) edac_mce_amd as102_fe dvb_as102(+) kvm_amd kvm snd_hda_codec_realtek dvb_core snd_hda_codec_generic snd_hda_codec_hdmi snd_hda_intel snd_hda_codec irqbypass crct10dif_pclmul crc32_pclmul snd_hda_core snd_hwdep snd_seq ghash_clmulni_intel sp5100_tco fam15h_power wmi k10temp i2c_piix4 snd_seq_device snd_pcm snd_timer parport_pc parport tpm_infineon snd tpm_tis soundcore tpm_tis_core tpm shpchp acpi_cpufreq xfs libcrc32c amdgpu amdkfd amd_iommu_v2 radeon hid_logitech_hidpp i2c_algo_bit drm_kms_helper crc32c_intel ttm drm r8169 mii hid_logitech_dj kernel: CPU: 0 PID: 598 Comm: systemd-udevd Not tainted 4.13.10-200.fc26.x86_64 #1 kernel: Hardware name: ASUS All Series/AM1I-A, BIOS 0505 03/13/2014 kernel: task: ffff979933b24c80 task.stack: ffffaf83413a4000 kernel: RIP: 0010:usb_hcd_map_urb_for_dma+0x41d/0x620 systemd-fsck[659]: /dev/sda2: clean, 49/128016 files, 268609/512000 blocks kernel: RSP: 0018:ffffaf83413a7728 EFLAGS: 00010282 systemd-udevd[604]: link_config: autonegotiation is unset or enabled, the speed and duplex are not writable. kernel: RAX: 000000000000001f RBX: ffff979930bce780 RCX: 0000000000000000 kernel: RDX: 0000000000000000 RSI: ffff97993ec0e118 RDI: ffff97993ec0e118 kernel: RBP: ffffaf83413a7768 R08: 000000000000039a R09: 0000000000000000 kernel: R10: 0000000000000001 R11: 00000000ffffffff R12: 00000000fffffff5 kernel: R13: 0000000001400000 R14: 0000000000000001 R15: ffff979930806800 kernel: FS: 00007effaca5c8c0(0000) GS:ffff97993ec00000(0000) knlGS:0000000000000000 kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 kernel: CR2: 00007effa9fca962 CR3: 0000000233089000 CR4: 00000000000406f0 kernel: Call Trace: kernel: usb_hcd_submit_urb+0x493/0xb40 kernel: ? page_cache_tree_insert+0x100/0x100 kernel: ? xfs_iunlock+0xd5/0x100 [xfs] kernel: ? xfs_file_buffered_aio_read+0x57/0xc0 [xfs] kernel: usb_submit_urb+0x22d/0x560 kernel: usb_start_wait_urb+0x6e/0x180 kernel: usb_bulk_msg+0xb8/0x160 kernel: as102_send_ep1+0x49/0xe0 [dvb_as102] kernel: ? devres_add+0x3f/0x50 kernel: as102_firmware_upload.isra.0+0x1dc/0x210 [dvb_as102] kernel: as102_fw_upload+0xb6/0x1f0 [dvb_as102] kernel: as102_dvb_register+0x2af/0x2d0 [dvb_as102] kernel: as102_usb_probe+0x1f3/0x260 [dvb_as102] kernel: usb_probe_interface+0x124/0x300 kernel: driver_probe_device+0x2ff/0x450 kernel: __driver_attach+0xa4/0xe0 kernel: ? driver_probe_device+0x450/0x450 kernel: bus_for_each_dev+0x6e/0xb0 kernel: driver_attach+0x1e/0x20 kernel: bus_add_driver+0x1c7/0x270 kernel: driver_register+0x60/0xe0 kernel: usb_register_driver+0x81/0x150 kernel: ? 0xffffffffc0807000 kernel: as102_usb_driver_init+0x1e/0x1000 [dvb_as102] kernel: do_one_initcall+0x50/0x190 kernel: ? __vunmap+0x81/0xb0 kernel: ? kfree+0x154/0x170 kernel: ? kmem_cache_alloc_trace+0x15f/0x1c0 kernel: ? do_init_module+0x27/0x1e9 kernel: do_init_module+0x5f/0x1e9 kernel: load_module+0x2602/0x2c30 kernel: SYSC_init_module+0x170/0x1a0 kernel: ? SYSC_init_module+0x170/0x1a0 kernel: SyS_init_module+0xe/0x10 kernel: do_syscall_64+0x67/0x140 kernel: entry_SYSCALL64_slow_path+0x25/0x25 kernel: RIP: 0033:0x7effab6cf3ea kernel: RSP: 002b:00007fff5cfcbbc8 EFLAGS: 00000246 ORIG_RAX: 00000000000000af kernel: RAX: ffffffffffffffda RBX: 00005569e0b83760 RCX: 00007effab6cf3ea kernel: RDX: 00007effac2099c5 RSI: 0000000000009a13 RDI: 00005569e0b98c50 kernel: RBP: 00007effac2099c5 R08: 00005569e0b83ed0 R09: 0000000000001d80 kernel: R10: 00007effab98db00 R11: 0000000000000246 R12: 00005569e0b98c50 kernel: R13: 00005569e0b81c60 R14: 0000000000020000 R15: 00005569dfadfdf7 kernel: Code: 48 39 c8 73 30 80 3d 59 60 9d 00 00 41 bc f5 ff ff ff 0f 85 26 ff ff ff 48 c7 c7 b8 6b d0 92 c6 05 3f 60 9d 00 01 e8 24 3d ad ff <0f> ff 8b 53 64 e9 09 ff ff ff 65 48 8b 0c 25 00 d3 00 00 48 8b kernel: ---[ end trace c4cae366180e70ec ]--- kernel: as10x_usb: error during firmware upload part1 Let's allocate the the structure dynamically so we can get the firmware loaded correctly: [ 14.243057] as10x_usb: firmware: as102_data1_st.hex loaded with success [ 14.500777] as10x_usb: firmware: as102_data2_st.hex loaded with success Signed-off-by: Michele Baldessari Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/as102/as102_fw.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/as102/as102_fw.c b/drivers/media/usb/as102/as102_fw.c index 07d08c49f4d4..b2e16bb67572 100644 --- a/drivers/media/usb/as102/as102_fw.c +++ b/drivers/media/usb/as102/as102_fw.c @@ -101,18 +101,23 @@ static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, unsigned char *cmd, const struct firmware *firmware) { - struct as10x_fw_pkt_t fw_pkt; + struct as10x_fw_pkt_t *fw_pkt; int total_read_bytes = 0, errno = 0; unsigned char addr_has_changed = 0; + fw_pkt = kmalloc(sizeof(*fw_pkt), GFP_KERNEL); + if (!fw_pkt) + return -ENOMEM; + + for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { int read_bytes = 0, data_len = 0; /* parse intel hex line */ read_bytes = parse_hex_line( (u8 *) (firmware->data + total_read_bytes), - fw_pkt.raw.address, - fw_pkt.raw.data, + fw_pkt->raw.address, + fw_pkt->raw.data, &data_len, &addr_has_changed); @@ -122,28 +127,28 @@ static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, /* detect the end of file */ total_read_bytes += read_bytes; if (total_read_bytes == firmware->size) { - fw_pkt.u.request[0] = 0x00; - fw_pkt.u.request[1] = 0x03; + fw_pkt->u.request[0] = 0x00; + fw_pkt->u.request[1] = 0x03; /* send EOF command */ errno = bus_adap->ops->upload_fw_pkt(bus_adap, (uint8_t *) - &fw_pkt, 2, 0); + fw_pkt, 2, 0); if (errno < 0) goto error; } else { if (!addr_has_changed) { /* prepare command to send */ - fw_pkt.u.request[0] = 0x00; - fw_pkt.u.request[1] = 0x01; + fw_pkt->u.request[0] = 0x00; + fw_pkt->u.request[1] = 0x01; - data_len += sizeof(fw_pkt.u.request); - data_len += sizeof(fw_pkt.raw.address); + data_len += sizeof(fw_pkt->u.request); + data_len += sizeof(fw_pkt->raw.address); /* send cmd to device */ errno = bus_adap->ops->upload_fw_pkt(bus_adap, (uint8_t *) - &fw_pkt, + fw_pkt, data_len, 0); if (errno < 0) @@ -152,6 +157,7 @@ static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, } } error: + kfree(fw_pkt); return (errno == 0) ? total_read_bytes : errno; } -- GitLab From 0cbab0776bd74966db8a6829b423dd42295e3580 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 8 Oct 2017 14:18:52 -0400 Subject: [PATCH 1789/5498] media: rc: check for integer overflow commit 3e45067f94bbd61dec0619b1c32744eb0de480c8 upstream. The ioctl LIRC_SET_REC_TIMEOUT would set a timeout of 704ns if called with a timeout of 4294968us. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/rc/ir-lirc-codec.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 98893a8332c7..080130582303 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -289,11 +289,14 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (!dev->max_timeout) return -ENOSYS; + /* Check for multiply overflow */ + if (val > U32_MAX / 1000) + return -EINVAL; + tmp = val * 1000; - if (tmp < dev->min_timeout || - tmp > dev->max_timeout) - return -EINVAL; + if (tmp < dev->min_timeout || tmp > dev->max_timeout) + return -EINVAL; dev->timeout = tmp; break; -- GitLab From 49fd514f53514a6f296b65bd59bd03aa703013fa Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 17 Oct 2017 11:48:50 -0400 Subject: [PATCH 1790/5498] media: v4l2-ctrl: Fix flags field on Control events commit 9cac9d2fb2fe0e0cadacdb94415b3fe49e3f724f upstream. VIDIOC_DQEVENT and VIDIOC_QUERY_EXT_CTRL should give the same output for the control flags field. This patch creates a new function user_flags(), that calculates the user exported flags value (which is different than the kernel internal flags structure). This function is then used by all the code that exports the internal flags to userspace. Reported-by: Dimitrios Katsaros Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/v4l2-core/v4l2-ctrls.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 86012140923f..7905ad9ffa35 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1196,6 +1196,16 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, } EXPORT_SYMBOL(v4l2_ctrl_fill); +static u32 user_flags(const struct v4l2_ctrl *ctrl) +{ + u32 flags = ctrl->flags; + + if (ctrl->is_ptr) + flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; + + return flags; +} + static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { memset(ev->reserved, 0, sizeof(ev->reserved)); @@ -1203,7 +1213,7 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->id = ctrl->id; ev->u.ctrl.changes = changes; ev->u.ctrl.type = ctrl->type; - ev->u.ctrl.flags = ctrl->flags; + ev->u.ctrl.flags = user_flags(ctrl); if (ctrl->is_ptr) ev->u.ctrl.value64 = 0; else @@ -2540,10 +2550,8 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr else qc->id = ctrl->id; strlcpy(qc->name, ctrl->name, sizeof(qc->name)); - qc->flags = ctrl->flags; + qc->flags = user_flags(ctrl); qc->type = ctrl->type; - if (ctrl->is_ptr) - qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; qc->elem_size = ctrl->elem_size; qc->elems = ctrl->elems; qc->nr_of_dims = ctrl->nr_of_dims; -- GitLab From 2bbd69104fe10523b53960974647ae0ec683f73f Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Wed, 6 Sep 2017 17:59:08 +0300 Subject: [PATCH 1791/5498] net/9p: Switch to wait_event_killable() commit 9523feac272ccad2ad8186ba4fcc89103754de52 upstream. Because userspace gets Very Unhappy when calls like stat() and execve() return -EINTR on 9p filesystem mounts. For instance, when bash is looking in PATH for things to execute and some SIGCHLD interrupts stat(), bash can throw a spurious 'command not found' since it doesn't retry the stat(). In practice, hitting the problem is rare and needs a really slow/bogged down 9p server. Signed-off-by: Tuomas Tynkkynen Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- net/9p/client.c | 3 +-- net/9p/trans_virtio.c | 13 ++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/net/9p/client.c b/net/9p/client.c index d43baabfb02d..f24b774d2215 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -753,8 +753,7 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) } again: /* Wait for the response */ - err = wait_event_interruptible(*req->wq, - req->status >= REQ_STATUS_RCVD); + err = wait_event_killable(*req->wq, req->status >= REQ_STATUS_RCVD); /* * Make sure our req is coherent with regard to updates in other diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index daa749c8b3fb..071ac5c495fc 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -292,8 +292,8 @@ req_retry: if (err == -ENOSPC) { chan->ring_bufs_avail = 0; spin_unlock_irqrestore(&chan->lock, flags); - err = wait_event_interruptible(*chan->vc_wq, - chan->ring_bufs_avail); + err = wait_event_killable(*chan->vc_wq, + chan->ring_bufs_avail); if (err == -ERESTARTSYS) return err; @@ -324,7 +324,7 @@ static int p9_get_mapped_pages(struct virtio_chan *chan, * Other zc request to finish here */ if (atomic_read(&vp_pinned) >= chan->p9_max_pages) { - err = wait_event_interruptible(vp_wq, + err = wait_event_killable(vp_wq, (atomic_read(&vp_pinned) < chan->p9_max_pages)); if (err == -ERESTARTSYS) return err; @@ -454,8 +454,8 @@ req_retry_pinned: if (err == -ENOSPC) { chan->ring_bufs_avail = 0; spin_unlock_irqrestore(&chan->lock, flags); - err = wait_event_interruptible(*chan->vc_wq, - chan->ring_bufs_avail); + err = wait_event_killable(*chan->vc_wq, + chan->ring_bufs_avail); if (err == -ERESTARTSYS) goto err_out; @@ -472,8 +472,7 @@ req_retry_pinned: virtqueue_kick(chan->vq); spin_unlock_irqrestore(&chan->lock, flags); p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); - err = wait_event_interruptible(*req->wq, - req->status >= REQ_STATUS_RCVD); + err = wait_event_killable(*req->wq, req->status >= REQ_STATUS_RCVD); /* * Non kernel buffers are pinned, unpin them */ -- GitLab From e0d73be54e8cebd4859fbf17fa5c5cd280279f6f Mon Sep 17 00:00:00 2001 From: Brent Taylor Date: Mon, 30 Oct 2017 22:32:45 -0500 Subject: [PATCH 1792/5498] mtd: nand: Fix writing mtdoops to nand flash. commit 30863e38ebeb500a31cecee8096fb5002677dd9b upstream. When mtdoops calls mtd_panic_write(), it eventually calls panic_nand_write() in nand_base.c. In order to properly wait for the nand chip to be ready in panic_nand_wait(), the chip must first be selected. When using the atmel nand flash controller, a panic would occur due to a NULL pointer exception. Fixes: 2af7c6539931 ("mtd: Add panic_write for NAND flashes") Signed-off-by: Brent Taylor Signed-off-by: Boris Brezillon Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/nand_base.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2e2da0a4be1a..d5c0a6c7e0e7 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2501,15 +2501,18 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; + int chipnr = (int)(to >> chip->chip_shift); struct mtd_oob_ops ops; int ret; - /* Wait for the device to get ready */ - panic_nand_wait(mtd, chip, 400); - /* Grab the device */ panic_nand_get_device(chip, mtd, FL_WRITING); + chip->select_chip(mtd, chipnr); + + /* Wait for the device to get ready */ + panic_nand_wait(mtd, chip, 400); + ops.len = len; ops.datbuf = (uint8_t *)buf; ops.oobbuf = NULL; -- GitLab From 88c17527a118c883d6290a6a3e8b93d6669475d9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 23 Nov 2017 16:20:05 +0100 Subject: [PATCH 1793/5498] USB: fix buffer overflows with parsing CDC headers Parsing CDC headers a buffer overflow cannot just be prevented by checking that the remainder of the buffer is longer than minimum length. The size of the fields to be parsed must be figured in, too. In newer kernels this issue has been fixed at a central location with commit 2e1c42391ff2556387b3cb6308b24f6f65619feb Author: Greg Kroah-Hartman Date: Thu Sep 21 16:58:48 2017 +0200 USB: core: harden cdc_parse_cdc_header on anything older the parsing had not been centralised, so a separate fix for each driver is necessary. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/cdc_ether.c | 9 ++++++++- drivers/usb/class/cdc-acm.c | 2 +- drivers/usb/class/cdc-wdm.c | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index d3920b54a92c..579500d69757 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -171,6 +171,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) dev_dbg(&intf->dev, "extra CDC header\n"); goto bad_desc; } + if (len < sizeof(struct usb_cdc_header_desc)) + break; info->header = (void *) buf; if (info->header->bLength != sizeof(*info->header)) { dev_dbg(&intf->dev, "CDC header len %u\n", @@ -184,6 +186,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) */ if (rndis) { struct usb_cdc_acm_descriptor *acm; + if (len < sizeof(struct usb_cdc_acm_descriptor)) + break; acm = (void *) buf; if (acm->bmCapabilities) { @@ -200,6 +204,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) dev_dbg(&intf->dev, "extra CDC union\n"); goto bad_desc; } + if (len < sizeof(struct usb_cdc_union_desc)) + break; info->u = (void *) buf; if (info->u->bLength != sizeof(*info->u)) { dev_dbg(&intf->dev, "CDC union len %u\n", @@ -258,6 +264,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) dev_dbg(&intf->dev, "extra CDC ether\n"); goto bad_desc; } + if (len < sizeof(struct usb_cdc_ether_desc)) + break; info->ether = (void *) buf; if (info->ether->bLength != sizeof(*info->ether)) { dev_dbg(&intf->dev, "CDC ether len %u\n", @@ -275,7 +283,6 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) dev_dbg(&intf->dev, "extra MDLM descriptor\n"); goto bad_desc; } - desc = (void *)buf; if (desc->bLength != sizeof(*desc)) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d67484b3dcfa..a5e94b8fa2dc 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1139,7 +1139,7 @@ static int acm_probe(struct usb_interface *intf, } } - while (buflen > 0) { + while (buflen >= 3) { /* minimum length making sense */ elength = buffer[0]; if (!elength) { dev_err(&intf->dev, "skipping garbage byte\n"); diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index a81f9dd7ee97..df0878c4810c 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -891,6 +891,8 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) case USB_CDC_HEADER_TYPE: break; case USB_CDC_DMM_TYPE: + if (buflen < sizeof(struct usb_cdc_dmm_desc)) + break; dmhd = (struct usb_cdc_dmm_desc *)buffer; maxcom = le16_to_cpu(dmhd->wMaxCommand); dev_dbg(&intf->dev, -- GitLab From 6812311cfbebb068e70e0a41a3aefbdc0f647f72 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 22 Nov 2017 17:12:41 +0000 Subject: [PATCH 1794/5498] iio: iio-trig-periodic-rtc: Free trigger resource correctly This is based on upstream commit 10e840dfb0b7, which did not touch the iio-trig-periodic-rtc driver because it has been removed upstream. The following explanation comes from that commit: These stand-alone trigger drivers were using iio_trigger_put() where they should have been using iio_trigger_free(). The iio_trigger_put() adds a module_put which is bad since they never did a module_get. In the sysfs driver, module_get/put's are used as triggers are added & removed. This extra module_put() occurs on an error path in the probe routine (probably rare). In the bfin-timer & interrupt trigger drivers, the module resources are not explicitly managed, so it's doing a put on something that was never get'd. It occurs on the probe error path and on the remove path (not so rare). Tested with the sysfs trigger driver. The bfin & interrupt drivers were build tested & inspected only. This was build tested only. Cc: Alison Schofield Cc: Jonathan Cameron Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/trigger/iio-trig-periodic-rtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c index 8f0a2ffa7150..0ba55312990f 100644 --- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -137,7 +137,7 @@ static int iio_trig_periodic_rtc_probe(struct platform_device *dev) trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); if (!trig_info) { ret = -ENOMEM; - goto error_put_trigger_and_remove_from_list; + goto error_free_trigger_and_remove_from_list; } iio_trigger_set_drvdata(trig, trig_info); trig->ops = &iio_prtc_trigger_ops; @@ -164,9 +164,9 @@ error_close_rtc: rtc_class_close(trig_info->rtc); error_free_trig_info: kfree(trig_info); -error_put_trigger_and_remove_from_list: +error_free_trigger_and_remove_from_list: list_del(&trig->alloc_list); - iio_trigger_put(trig); + iio_trigger_free(trig); error_free_completed_registrations: list_for_each_entry_safe(trig, trig2, -- GitLab From 239f6bb7dfd6018848fc23be178c1b745deab774 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Fri, 21 Jul 2017 11:36:23 -0700 Subject: [PATCH 1795/5498] e1000e: Fix error path in link detection commit c4c40e51f9c32c6dd8adf606624c930a1c4d9bbb upstream. In case of error from e1e_rphy(), the loop will exit early and "success" will be set to true erroneously. Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Amit Pundir Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/e1000e/phy.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c index b2005e13fb01..0963aa2d5e45 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.c +++ b/drivers/net/ethernet/intel/e1000e/phy.c @@ -1744,6 +1744,7 @@ s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, s32 ret_val = 0; u16 i, phy_status; + *success = false; for (i = 0; i < iterations; i++) { /* Some PHYs require the MII_BMSR register to be read * twice due to the link bit being sticky. No harm doing @@ -1763,16 +1764,16 @@ s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, ret_val = e1e_rphy(hw, MII_BMSR, &phy_status); if (ret_val) break; - if (phy_status & BMSR_LSTATUS) + if (phy_status & BMSR_LSTATUS) { + *success = true; break; + } if (usec_interval >= 1000) msleep(usec_interval / 1000); else udelay(usec_interval); } - *success = (i < iterations); - return ret_val; } -- GitLab From c493ba2db0f2b8e1ec73757e8e23690c5c4bff36 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Fri, 21 Jul 2017 11:36:25 -0700 Subject: [PATCH 1796/5498] e1000e: Fix return value test commit d3509f8bc7b0560044c15f0e3ecfde1d9af757a6 upstream. All the helpers return -E1000_ERR_PHY. Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Amit Pundir Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/e1000e/netdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 247335d2c7ec..48d7786e3041 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4862,7 +4862,7 @@ static bool e1000e_has_link(struct e1000_adapter *adapter) break; } - if ((ret_val == E1000_ERR_PHY) && (hw->phy.type == e1000_phy_igp_3) && + if ((ret_val == -E1000_ERR_PHY) && (hw->phy.type == e1000_phy_igp_3) && (er32(CTRL) & E1000_PHY_CTRL_GBE_DISABLE)) { /* See e1000_kmrn_lock_loss_workaround_ich8lan() */ e_info("Gigabit has been disabled, downgrading speed\n"); -- GitLab From 53801d8f9ff2c8dc18b762cc96c20c62bf43dbe7 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Fri, 21 Jul 2017 11:36:26 -0700 Subject: [PATCH 1797/5498] e1000e: Separate signaling for link check/link up commit 19110cfbb34d4af0cdfe14cd243f3b09dc95b013 upstream. Lennart reported the following race condition: \ e1000_watchdog_task \ e1000e_has_link \ hw->mac.ops.check_for_link() === e1000e_check_for_copper_link /* link is up */ mac->get_link_status = false; /* interrupt */ \ e1000_msix_other hw->mac.get_link_status = true; link_active = !hw->mac.get_link_status /* link_active is false, wrongly */ This problem arises because the single flag get_link_status is used to signal two different states: link status needs checking and link status is down. Avoid the problem by using the return value of .check_for_link to signal the link status to e1000e_has_link(). Reported-by: Lennart Sorensen Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Amit Pundir Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/e1000e/mac.c | 11 ++++++++--- drivers/net/ethernet/intel/e1000e/netdev.c | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c index 30b74d590bee..1c9cb53c25a3 100644 --- a/drivers/net/ethernet/intel/e1000e/mac.c +++ b/drivers/net/ethernet/intel/e1000e/mac.c @@ -410,6 +410,9 @@ void e1000e_clear_hw_cntrs_base(struct e1000_hw *hw) * Checks to see of the link status of the hardware has changed. If a * change in link status has been detected, then we read the PHY registers * to get the current speed/duplex if link exists. + * + * Returns a negative error code (-E1000_ERR_*) or 0 (link down) or 1 (link + * up). **/ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) { @@ -423,7 +426,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) * Change or Rx Sequence Error interrupt. */ if (!mac->get_link_status) - return 0; + return 1; /* First we want to see if the MII Status Register reports * link. If so, then we want to get the current speed/duplex @@ -461,10 +464,12 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) * different link partner. */ ret_val = e1000e_config_fc_after_link_up(hw); - if (ret_val) + if (ret_val) { e_dbg("Error configuring flow control\n"); + return ret_val; + } - return ret_val; + return 1; } /** diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 48d7786e3041..081dad4e0178 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4844,7 +4844,7 @@ static bool e1000e_has_link(struct e1000_adapter *adapter) case e1000_media_type_copper: if (hw->mac.get_link_status) { ret_val = hw->mac.ops.check_for_link(hw); - link_active = !hw->mac.get_link_status; + link_active = ret_val > 0; } else { link_active = true; } -- GitLab From 952835ccd917682ebb705f89ff1e56fbf068a1d8 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Mon, 4 Jul 2016 17:04:37 -0700 Subject: [PATCH 1798/5498] RDS: RDMA: return appropriate error on rdma map failures [ Upstream commit 584a8279a44a800dea5a5c1e9d53a002e03016b4 ] The first message to a remote node should prompt a new connection even if it is RDMA operation. For RDMA operation the MR mapping can fail because connections is not yet up. Since the connection establishment is asynchronous, we make sure the map failure because of unavailable connection reach to the user by appropriate error code. Before returning to the user, lets trigger the connection so that its ready for the next retry. Signed-off-by: Santosh Shilimkar Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/rds/send.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/rds/send.c b/net/rds/send.c index 45b800c3cc83..e827b41c9da2 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -903,6 +903,11 @@ static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm, ret = rds_cmsg_rdma_map(rs, rm, cmsg); if (!ret) *allocated_mr = 1; + else if (ret == -ENODEV) + /* Accommodate the get_mr() case which can fail + * if connection isn't established yet. + */ + ret = -EAGAIN; break; case RDS_CMSG_ATOMIC_CSWP: case RDS_CMSG_ATOMIC_FADD: @@ -1011,8 +1016,12 @@ int rds_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, /* Parse any control messages the user may have included. */ ret = rds_cmsg_send(rs, rm, msg, &allocated_mr); - if (ret) + if (ret) { + /* Trigger connection so that its ready for the next retry */ + if (ret == -EAGAIN) + rds_conn_connect_if_down(conn); goto out; + } if (rm->rdma.op_active && !conn->c_trans->xmit_rdma) { printk_ratelimited(KERN_NOTICE "rdma_op %p conn xmit_rdma %p\n", -- GitLab From 09101462f7b7263b745bebdf9bfbeaaefbf71c22 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 2 Jan 2017 14:04:24 -0600 Subject: [PATCH 1799/5498] PCI: Apply _HPX settings only to relevant devices [ Upstream commit 977509f7c5c6fb992ffcdf4291051af343b91645 ] Previously we didn't check the type of device before trying to apply Type 1 (PCI-X) or Type 2 (PCIe) Setting Records from _HPX. We don't support PCI-X Setting Records, so this was harmless, but the warning was useless. We do support PCIe Setting Records, and we didn't check whether a device was PCIe before applying settings. I don't think anything bad happened on non-PCIe devices because pcie_capability_clear_and_set_word(), pcie_cap_has_lnkctl(), etc., would fail before doing any harm. But it's ugly to depend on those internals. Check the device type before attempting to apply Type 1 and Type 2 Setting Records (Type 0 records are applicable to PCI, PCI-X, and PCIe devices). A side benefit is that this prevents useless "not supported" warnings when a BIOS supplies a Type 1 (PCI-X) Setting Record and we try to apply it to every single device: pci 0000:00:00.0: PCI-X settings not supported After this patch, we'll get the warning only when a BIOS supplies a Type 1 record and we have a PCI-X device to which it should be applied. Link: https://bugzilla.kernel.org/show_bug.cgi?id=187731 Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/probe.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7de026897f1d..c43300ced57d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1329,8 +1329,16 @@ static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp) static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp) { - if (hpp) - dev_warn(&dev->dev, "PCI-X settings not supported\n"); + int pos; + + if (!hpp) + return; + + pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); + if (!pos) + return; + + dev_warn(&dev->dev, "PCI-X settings not supported\n"); } static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) @@ -1341,6 +1349,9 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) if (!hpp) return; + if (!pci_is_pcie(dev)) + return; + if (hpp->revision > 1) { dev_warn(&dev->dev, "PCIe settings rev %d not supported\n", hpp->revision); -- GitLab From cbbe150dd94043f94461be5b5f1aa08e1f618c58 Mon Sep 17 00:00:00 2001 From: Thomas Preisner Date: Fri, 30 Dec 2016 03:37:54 +0100 Subject: [PATCH 1800/5498] net: 3com: typhoon: typhoon_init_one: make return values more specific [ Upstream commit 6b6bbb5922a4b1d4b58125a572da91010295fba3 ] In some cases the return value of a failing function is not being used and the function typhoon_init_one() returns another negative error code instead. Signed-off-by: Thomas Preisner Signed-off-by: Milan Stephan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/3com/typhoon.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 48775b88bac7..0f55088d18c4 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -2366,9 +2366,9 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) * 4) Get the hardware address. * 5) Put the card to sleep. */ - if (typhoon_reset(ioaddr, WaitSleep) < 0) { + err = typhoon_reset(ioaddr, WaitSleep); + if (err < 0) { err_msg = "could not reset 3XP"; - err = -EIO; goto error_out_dma; } @@ -2382,16 +2382,16 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) typhoon_init_interface(tp); typhoon_init_rings(tp); - if(typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) { + err = typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST); + if (err < 0) { err_msg = "cannot boot 3XP sleep image"; - err = -EIO; goto error_out_reset; } INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_MAC_ADDRESS); - if(typhoon_issue_command(tp, 1, &xp_cmd, 1, xp_resp) < 0) { + err = typhoon_issue_command(tp, 1, &xp_cmd, 1, xp_resp); + if (err < 0) { err_msg = "cannot read MAC address"; - err = -EIO; goto error_out_reset; } @@ -2424,9 +2424,9 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if(xp_resp[0].numDesc != 0) tp->capabilities |= TYPHOON_WAKEUP_NEEDS_RESET; - if(typhoon_sleep(tp, PCI_D3hot, 0) < 0) { + err = typhoon_sleep(tp, PCI_D3hot, 0); + if (err < 0) { err_msg = "cannot put adapter to sleep"; - err = -EIO; goto error_out_reset; } -- GitLab From aecff719d3cac6ab3ed3bad28294628489a4f700 Mon Sep 17 00:00:00 2001 From: Thomas Preisner Date: Fri, 30 Dec 2016 03:37:53 +0100 Subject: [PATCH 1801/5498] net: 3com: typhoon: typhoon_init_one: fix incorrect return values [ Upstream commit 107fded7bf616ad6f46823d98b8ed6405d7adf2d ] In a few cases the err-variable is not set to a negative error code if a function call in typhoon_init_one() fails and thus 0 is returned instead. It may be better to set err to the appropriate negative error code before returning. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=188841 Reported-by: Pan Bian Signed-off-by: Thomas Preisner Signed-off-by: Milan Stephan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/3com/typhoon.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 0f55088d18c4..247879a68f29 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -2398,8 +2398,9 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) *(__be16 *)&dev->dev_addr[0] = htons(le16_to_cpu(xp_resp[0].parm1)); *(__be32 *)&dev->dev_addr[2] = htonl(le32_to_cpu(xp_resp[0].parm2)); - if(!is_valid_ether_addr(dev->dev_addr)) { + if (!is_valid_ether_addr(dev->dev_addr)) { err_msg = "Could not obtain valid ethernet address, aborting"; + err = -EIO; goto error_out_reset; } @@ -2407,7 +2408,8 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) * later when we print out the version reported. */ INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS); - if(typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) { + err = typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp); + if (err < 0) { err_msg = "Could not get Sleep Image version"; goto error_out_reset; } @@ -2449,7 +2451,8 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->features = dev->hw_features | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM; - if(register_netdev(dev) < 0) { + err = register_netdev(dev); + if (err < 0) { err_msg = "unable to register netdev"; goto error_out_reset; } -- GitLab From d67ddde8a8dc029c3ec251f0d05ed61f91779457 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 30 Dec 2016 17:38:52 +0100 Subject: [PATCH 1802/5498] drm/armada: Fix compile fail [ Upstream commit 7357f89954b6d005df6ab8929759e78d7d9a80f9 ] I reported the include issue for tracepoints a while ago, but nothing seems to have happened. Now it bit us, since the drm_mm_print conversion was broken for armada. Fix it, so I can re-enable armada in the drm-misc build configs. v2: Rebase just the compile fix on top of Chris' build fix. Cc: Russell King Cc: Chris Wilson Acked: Chris Wilson Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1483115932-19584-1-git-send-email-daniel.vetter@ffwll.ch Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/armada/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile index d6f43e06150a..c9c619727806 100644 --- a/drivers/gpu/drm/armada/Makefile +++ b/drivers/gpu/drm/armada/Makefile @@ -5,3 +5,5 @@ armada-y += armada_510.o armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o obj-$(CONFIG_DRM_ARMADA) := armada.o + +CFLAGS_armada_trace.o := -I$(src) -- GitLab From ed3203658694f8cecbfeed7d82b35d9b85219883 Mon Sep 17 00:00:00 2001 From: Gabriele Mazzotta Date: Sat, 24 Dec 2016 19:50:00 +0100 Subject: [PATCH 1803/5498] ALSA: hda - Apply ALC269_FIXUP_NO_SHUTUP on HDA_FIXUP_ACT_PROBE [ Upstream commit 972aa2c708703c21f14eb958b37e82aae2530e44 ] Setting shutup when the action is HDA_FIXUP_ACT_PRE_PROBE might not have the desired effect since it could be overridden by another more generic shutup function. Prevent this by setting the more specific shutup function on HDA_FIXUP_ACT_PROBE. Signed-off-by: Gabriele Mazzotta Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 09686203052c..fefc502f5bf5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4289,7 +4289,7 @@ static void alc_no_shutup(struct hda_codec *codec) static void alc_fixup_no_shutup(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { struct alc_spec *spec = codec->spec; spec->shutup = alc_no_shutup; } -- GitLab From 9d825502c39338214c535fd688e80ec850167114 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Thu, 8 Dec 2016 10:15:50 +0900 Subject: [PATCH 1804/5498] mac80211: Remove invalid flag operations in mesh TSF synchronization [ Upstream commit 76f43b4c0a9337af22827d78de4f2b8fd5328489 ] mesh_sync_offset_adjust_tbtt() implements Extensible synchronization framework ([1] 13.13.2 Extensible synchronization framework). It shall not operate the flag "TBTT Adjusting subfield" ([1] 8.4.2.100.8 Mesh Capability), since it is used only for MBCA ([1] 13.13.4 Mesh beacon collision avoidance, see 13.13.4.4.3 TBTT scanning and adjustment procedures for detail). So this patch remove the flag operations. [1] IEEE Std 802.11 2012 Signed-off-by: Masashi Honma [remove adjusting_tbtt entirely, since it's now unused] Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mac80211/ieee80211_i.h | 1 - net/mac80211/mesh.c | 3 --- net/mac80211/mesh_sync.c | 11 ----------- 3 files changed, 15 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index aed656d18b23..9460d4e3248e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -640,7 +640,6 @@ struct ieee80211_if_mesh { const struct ieee80211_mesh_sync_ops *sync_ops; s64 sync_offset_clockdrift_max; spinlock_t sync_offset_lock; - bool adjusting_tbtt; /* mesh power save */ enum nl80211_mesh_power_mode nonpeer_pm; int ps_peers_light_sleep; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index ac04e3874af1..30bea9dcafed 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -289,8 +289,6 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ *pos |= ifmsh->ps_peers_deep_sleep ? IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; - *pos++ |= ifmsh->adjusting_tbtt ? - IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; *pos++ = 0x00; return 0; @@ -790,7 +788,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ifmsh->mesh_cc_id = 0; /* Disabled */ /* register sync ops from extensible synchronization framework */ ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id); - ifmsh->adjusting_tbtt = false; ifmsh->sync_offset_clockdrift_max = 0; set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); ieee80211_mesh_root_setup(ifmsh); diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 09625d6205c3..6e8ece73bfa6 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -119,7 +119,6 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, */ if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { - clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", sta->sta.addr); goto no_sync; @@ -168,11 +167,9 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata, struct beacon_data *beacon) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - u8 cap; WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); WARN_ON(!rcu_read_lock_held()); - cap = beacon->meshconf->meshconf_cap; spin_lock_bh(&ifmsh->sync_offset_lock); @@ -186,21 +183,13 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata, "TBTT : kicking off TBTT adjustment with clockdrift_max=%lld\n", ifmsh->sync_offset_clockdrift_max); set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); - - ifmsh->adjusting_tbtt = true; } else { msync_dbg(sdata, "TBTT : max clockdrift=%lld; too small to adjust\n", (long long)ifmsh->sync_offset_clockdrift_max); ifmsh->sync_offset_clockdrift_max = 0; - - ifmsh->adjusting_tbtt = false; } spin_unlock_bh(&ifmsh->sync_offset_lock); - - beacon->meshconf->meshconf_cap = ifmsh->adjusting_tbtt ? - IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING | cap : - ~IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING & cap; } static const struct sync_method sync_methods[] = { -- GitLab From 1171ebb0693cd181fb7095b89185f71b9579d0fc Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Wed, 30 Nov 2016 09:06:04 +0900 Subject: [PATCH 1805/5498] mac80211: Suppress NEW_PEER_CANDIDATE event if no room [ Upstream commit 11197d006bcfabf0173a7820a163fcaac420d10e ] Previously, kernel sends NEW_PEER_CANDIDATE event to user land even if the found peer does not have any room to accept other peer. This causes continuous connection trials. Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mac80211/mesh_plink.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index b488e1859b18..7d3e925430ae 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -448,12 +448,14 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, /* Userspace handles station allocation */ if (sdata->u.mesh.user_mpm || - sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) - cfg80211_notify_new_peer_candidate(sdata->dev, addr, - elems->ie_start, - elems->total_len, - GFP_KERNEL); - else + sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { + if (mesh_peer_accepts_plinks(elems) && + mesh_plink_availables(sdata)) + cfg80211_notify_new_peer_candidate(sdata->dev, addr, + elems->ie_start, + elems->total_len, + GFP_KERNEL); + } else sta = __mesh_sta_info_alloc(sdata, addr); return sta; -- GitLab From efaa1a4a7250791bde907b73155909026b9e4479 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sat, 3 Dec 2016 21:44:30 +0800 Subject: [PATCH 1806/5498] staging: iio: cdc: fix improper return value [ Upstream commit 91ca1a8c584f55857b1f6ab20a1d3a1ce7a559bb ] At the end of function ad7150_write_event_config(), directly returns 0. As a result, the errors will be ignored by the callers. It may be better to return variable "ret". Signed-off-by: Pan Bian Signed-off-by: Jonathan Cameron Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/cdc/ad7150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/iio/cdc/ad7150.c b/drivers/staging/iio/cdc/ad7150.c index a2b7ae3329c0..9fe1d5793cee 100644 --- a/drivers/staging/iio/cdc/ad7150.c +++ b/drivers/staging/iio/cdc/ad7150.c @@ -275,7 +275,7 @@ static int ad7150_write_event_config(struct iio_dev *indio_dev, error_ret: mutex_unlock(&chip->state_lock); - return 0; + return ret; } static int ad7150_read_event_value(struct iio_dev *indio_dev, -- GitLab From f243d9f3f4a0286a8c3071adbc6e42f93a6450a3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 11 Dec 2016 20:46:51 +0100 Subject: [PATCH 1807/5498] netfilter: nft_queue: use raw_smp_processor_id() [ Upstream commit c2e756ff9e699865d294cdc112acfc36419cf5cc ] Using smp_processor_id() causes splats with PREEMPT_RCU: [19379.552780] BUG: using smp_processor_id() in preemptible [00000000] code: ping/32389 [19379.552793] caller is debug_smp_processor_id+0x17/0x19 [...] [19379.552823] Call Trace: [19379.552832] [] dump_stack+0x67/0x90 [19379.552837] [] check_preemption_disabled+0xe5/0xf5 [19379.552842] [] debug_smp_processor_id+0x17/0x19 [19379.552849] [] nft_queue_eval+0x35/0x20c [nft_queue] No need to disable preemption since we only fetch the numeric value, so let's use raw_smp_processor_id() instead. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index e8ae2f6bf232..88cd49d3548e 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -37,7 +37,7 @@ static void nft_queue_eval(const struct nft_expr *expr, if (priv->queues_total > 1) { if (priv->flags & NFT_QUEUE_FLAG_CPU_FANOUT) { - int cpu = smp_processor_id(); + int cpu = raw_smp_processor_id(); queue = priv->queuenum + cpu % priv->queues_total; } else { -- GitLab From e72ebbefe1b222336bd11de0671f0d7ce76e1c6e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 13 Dec 2016 13:59:33 +0100 Subject: [PATCH 1808/5498] netfilter: nf_tables: fix oob access [ Upstream commit 3e38df136e453aa69eb4472108ebce2fb00b1ba6 ] BUG: KASAN: slab-out-of-bounds in nf_tables_rule_destroy+0xf1/0x130 at addr ffff88006a4c35c8 Read of size 8 by task nft/1607 When we've destroyed last valid expr, nft_expr_next() returns an invalid expr. We must not dereference it unless it passes != nft_expr_last() check. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_tables_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9fe2baa01fbe..74724560bc48 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1869,7 +1869,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, * is called on error from nf_tables_newrule(). */ expr = nft_expr_first(rule); - while (expr->ops && expr != nft_expr_last(rule)) { + while (expr != nft_expr_last(rule) && expr->ops) { nf_tables_expr_destroy(ctx, expr); expr = nft_expr_next(expr); } -- GitLab From a387098fe030a94e7cb454c687e4cc5a05857763 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 4 Dec 2016 12:51:53 +0800 Subject: [PATCH 1809/5498] btrfs: return the actual error value from from btrfs_uuid_tree_iterate [ Upstream commit 73ba39ab9307340dc98ec3622891314bbc09cc2e ] In function btrfs_uuid_tree_iterate(), errno is assigned to variable ret on errors. However, it directly returns 0. It may be better to return ret. This patch also removes the warning, because the caller already prints a warning. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=188731 Signed-off-by: Pan Bian Reviewed-by: Omar Sandoval [ edited subject ] Signed-off-by: David Sterba Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/uuid-tree.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c index 778282944530..837a9a8d579e 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -348,7 +348,5 @@ skip: out: btrfs_free_path(path); - if (ret) - btrfs_warn(fs_info, "btrfs_uuid_tree_iterate failed %d", ret); - return 0; + return ret; } -- GitLab From d3e2563be9273a5fa159ed94a6dff2df09ef4a2e Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 20 Dec 2016 10:29:12 +0000 Subject: [PATCH 1810/5498] ASoC: wm_adsp: Don't overrun firmware file buffer when reading region data [ Upstream commit 1cab2a84f470e15ecc8e5143bfe9398c6e888032 ] Protect against corrupt firmware files by ensuring that the length we get for the data in a region actually lies within the available firmware file data buffer. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm_adsp.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 7f2f661c6453..d53bfd4d0ada 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -532,7 +532,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) const struct wmfw_region *region; const struct wm_adsp_region *mem; const char *region_name; - char *file, *text; + char *file, *text = NULL; struct wm_adsp_buf *buf; unsigned int reg; int regions = 0; @@ -677,10 +677,21 @@ static int wm_adsp_load(struct wm_adsp *dsp) regions, le32_to_cpu(region->len), offset, region_name); + if ((pos + le32_to_cpu(region->len) + sizeof(*region)) > + firmware->size) { + adsp_err(dsp, + "%s.%d: %s region len %d bytes exceeds file length %zu\n", + file, regions, region_name, + le32_to_cpu(region->len), firmware->size); + ret = -EINVAL; + goto out_fw; + } + if (text) { memcpy(text, region->data, le32_to_cpu(region->len)); adsp_info(dsp, "%s: %s\n", file, text); kfree(text); + text = NULL; } if (reg) { @@ -737,6 +748,7 @@ out_fw: regmap_async_complete(regmap); wm_adsp_buf_free(&buf_list); release_firmware(firmware); + kfree(text); out: kfree(file); @@ -1316,6 +1328,17 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } if (reg) { + if ((pos + le32_to_cpu(blk->len) + sizeof(*blk)) > + firmware->size) { + adsp_err(dsp, + "%s.%d: %s region len %d bytes exceeds file length %zu\n", + file, blocks, region_name, + le32_to_cpu(blk->len), + firmware->size); + ret = -EINVAL; + goto out_fw; + } + buf = wm_adsp_buf_alloc(blk->data, le32_to_cpu(blk->len), &buf_list); -- GitLab From 3241a972aeecad279bac9d1a120b454b96fe63ba Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 20 Dec 2016 12:58:10 +0100 Subject: [PATCH 1811/5498] s390/kbuild: enable modversions for symbols exported from asm [ Upstream commit cabab3f9f5ca077535080b3252e6168935b914af ] s390 version of commit 334bb7738764 ("x86/kbuild: enable modversions for symbols exported from asm") so we get also rid of all these warnings: WARNING: EXPORT symbol "_mcount" [vmlinux] version generation failed, symbol will not be versioned. WARNING: EXPORT symbol "memcpy" [vmlinux] version generation failed, symbol will not be versioned. WARNING: EXPORT symbol "memmove" [vmlinux] version generation failed, symbol will not be versioned. WARNING: EXPORT symbol "memset" [vmlinux] version generation failed, symbol will not be versioned. WARNING: EXPORT symbol "save_fpu_regs" [vmlinux] version generation failed, symbol will not be versioned. WARNING: EXPORT symbol "sie64a" [vmlinux] version generation failed, symbol will not be versioned. WARNING: EXPORT symbol "sie_exit" [vmlinux] version generation failed, symbol will not be versioned. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/s390/include/asm/asm-prototypes.h | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 arch/s390/include/asm/asm-prototypes.h diff --git a/arch/s390/include/asm/asm-prototypes.h b/arch/s390/include/asm/asm-prototypes.h new file mode 100644 index 000000000000..2c3413b0ca52 --- /dev/null +++ b/arch/s390/include/asm/asm-prototypes.h @@ -0,0 +1,8 @@ +#ifndef _ASM_S390_PROTOTYPES_H + +#include +#include +#include +#include + +#endif /* _ASM_S390_PROTOTYPES_H */ -- GitLab From d412e6b098fb2a100d431898b89e2cd59a42ad73 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 22 Dec 2016 08:19:46 +0100 Subject: [PATCH 1812/5498] xen: xenbus driver must not accept invalid transaction ids [ Upstream commit 639b08810d6ad74ded2c5f6e233c4fcb9d147168 ] When accessing Xenstore in a transaction the user is specifying a transaction id which he normally obtained from Xenstore when starting the transaction. Xenstore is validating a transaction id against all known transaction ids of the connection the request came in. As all requests of a domain not being the one where Xenstore lives share one connection, validation of transaction ids of different users of Xenstore in that domain should be done by the kernel of that domain being the multiplexer between the Xenstore users in that domain and Xenstore. In order to prohibit one Xenstore user "hijacking" a transaction from another user the xenbus driver has to verify a given transaction id against all known transaction ids of the user before forwarding it to Xenstore. Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Juergen Gross Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/xen/xenbus/xenbus_dev_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index 0e0eb10f82a0..816a0e08ef10 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -316,7 +316,7 @@ static int xenbus_write_transaction(unsigned msg_type, rc = -ENOMEM; goto out; } - } else if (msg_type == XS_TRANSACTION_END) { + } else if (u->u.msg.tx_id != 0) { list_for_each_entry(trans, &u->transactions, list) if (trans->handle.id == u->u.msg.tx_id) break; -- GitLab From b42518053ffd221d79cff2df8c0257db88a71334 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Nov 2017 08:35:56 +0000 Subject: [PATCH 1813/5498] Linux 3.18.85 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 107b5778b864..9630ca7223c9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 84 +SUBLEVEL = 85 EXTRAVERSION = NAME = Diseased Newt -- GitLab From bb026499262575ace310604ccbdb96d2a417961f Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Sun, 19 Nov 2017 22:11:22 +0530 Subject: [PATCH 1814/5498] soc: qcom: bgrsb: Add rsb support for BG Provide glink communication between BG and MSM over RSB channel. enable/disable RSB on power state change.Sends input events to input framework. Change-Id: Ie8e3276d5ad98ff16b19126f3e864adb800fe128 Signed-off-by: Arjun Singh --- .../devicetree/bindings/soc/qcom/bg_rsb.txt | 19 + drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/bg_rsb.c | 719 ++++++++++++++++++ drivers/soc/qcom/bgrsb.h | 36 + 5 files changed, 784 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/bg_rsb.txt create mode 100644 drivers/soc/qcom/bg_rsb.c create mode 100644 drivers/soc/qcom/bgrsb.h diff --git a/Documentation/devicetree/bindings/soc/qcom/bg_rsb.txt b/Documentation/devicetree/bindings/soc/qcom/bg_rsb.txt new file mode 100644 index 000000000000..26a13cd4aa37 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/bg_rsb.txt @@ -0,0 +1,19 @@ +Qualcomm technologies Inc bg-rsb + +BG-RSB : bg-rsb is used to communicate with BG over Glink to +configure the RSB events. bg-rsb enable/disable LDO11 and LDO15 +before making any communication to BG regarding RSB. +It also provides an input device, which is used to send the RSB/Button +events to input framework. + +Required properties: +- compatible : should be "qcom,bg-rsb" +- vdd-ldo1-supply : pm660_l11 regulator +- vdd-ldo2-supply : for pm660_l15 regulator + +Example: + qcom,bg-rsb { + compatible = "qcom,bg-rsb"; + vdd-ldo1-supply = <&pm660_l11>; + vdd-ldo2-supply = <&pm660_l15>; + }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index f7d603853044..67827f911426 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -680,6 +680,15 @@ config MSM_GLINK_PKT This enable the usersapce clients to read and write to some glink packets channel. +config MSM_BGRSB + bool "Provide support for rsb events on Blackghost chipset" + depends on MSM_GLINK + help + BGRSB communicates to BG over Glink for RSB configuration and + enable/disable on device power state change. It enables/disables + the regulator specific to RSB. Sends the side band events generated + by BG to input framework. + config MSM_TZ_SMMU bool "Helper functions for SMMU configuration through TZ" depends on ARCH_MSMTHULIUM || ARCH_MSM8953 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index fe78445ee8af..d17a1e87e689 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -93,6 +93,7 @@ ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += ramdump.o endif +obj-$(CONFIG_MSM_BGRSB) += bg_rsb.o obj-$(CONFIG_MSM_BGCOM_INTERFACE) += bgcom_interface.o obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o sysmon-qmi.o diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c new file mode 100644 index 000000000000..7402ba475a54 --- /dev/null +++ b/drivers/soc/qcom/bg_rsb.c @@ -0,0 +1,719 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(msg) "bgrsb: %s: " msg, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bgrsb.h" + +#define BGRSB_GLINK_INTENT_SIZE 0x04 +#define BGRSB_MSG_SIZE 0x08 +#define TIMEOUT_MS 500 + +#define BGRSB_LDO15_VTG_MIN_UV 3300000 +#define BGRSB_LDO15_VTG_MAX_UV 3300000 + +#define BGRSB_LDO11_VTG_MIN_UV 1800000 +#define BGRSB_LDO11_VTG_MAX_UV 1800000 + +#define BGRSB_BGWEAR_SUBSYS "bg-wear" + +#define BGRSB_POWER_ENABLE 1 +#define BGRSB_POWER_DISABLE 0 + + +struct bgrsb_regulator { + struct regulator *regldo11; + struct regulator *regldo15; +}; + +enum ldo_task { + BGRSB_ENABLE_LDO11, + BGRSB_ENABLE_LDO15, + BGRSB_DISABLE_LDO11, + BGRSB_DISABLE_LDO15, + BGRSB_NO_ACTION +}; + +enum bgrsb_state { + BGRSB_STATE_UNKNOWN, + BGRSB_STATE_INIT, + BGRSB_STATE_LDO11_ENABLED, + BGRSB_STATE_RSB_CONFIGURED, + BGRSB_STATE_LDO15_ENABLED, + BGRSB_STATE_RSB_EBNABLED +}; + +struct bgrsb_msg { + uint32_t cmd_id; + uint32_t data; +}; + +struct bgrsb_priv { + void *handle; + struct input_dev *input; + struct mutex glink_mutex; + + enum bgrsb_state bgrsb_current_state; + enum glink_link_state link_state; + + bool chnl_state; + void *lhndl; + + struct work_struct bg_work; + struct work_struct glink_work; + + struct workqueue_struct *bgrsb_event_wq; + struct workqueue_struct *bgrsb_wq; + + struct bg_glink_chnl chnl; + char rx_buf[BGRSB_GLINK_INTENT_SIZE]; + + struct bgrsb_regulator rgltr; + + enum ldo_task ldo_action; + + void *bgwear_subsys_handle; + + struct completion bg_resp_cmplt; + struct completion wrk_cmplt; + struct completion bg_lnikup_cmplt; + struct completion tx_done; + + struct device *ldev; + + wait_queue_head_t link_state_wait; +}; + +static void *bgrsb_drv; + +int bgrsb_send_input(struct event *evnt) +{ + struct bgrsb_priv *dev = + container_of(bgrsb_drv, struct bgrsb_priv, lhndl); + + if (!evnt) + return -EINVAL; + + if (evnt->sub_id == 1) { + input_report_rel(dev->input, REL_WHEEL, evnt->evnt_data); + input_sync(dev->input); + } else + pr_debug("event: type[%d] , data: %d\n", + evnt->sub_id, evnt->evnt_data); + + return 0; +} +EXPORT_SYMBOL(bgrsb_send_input); + +static void bgrsb_glink_notify_rx(void *handle, const void *priv, + const void *pkt_priv, const void *ptr, size_t size) +{ + struct bgrsb_priv *dev = (struct bgrsb_priv *)priv; + + memcpy(dev->rx_buf, ptr, size); + glink_rx_done(dev->handle, ptr, false); + complete(&dev->bg_resp_cmplt); +} + +static void bgrsb_glink_notify_state(void *handle, const void *priv, + unsigned event) +{ + struct bgrsb_priv *dev = (struct bgrsb_priv *)priv; + + switch (event) { + case GLINK_CONNECTED: + complete(&dev->bg_lnikup_cmplt); + break; + case GLINK_REMOTE_DISCONNECTED: + case GLINK_LOCAL_DISCONNECTED: + dev->chnl_state = false; + break; + } +} + +static int bgrsb_configr_rsb(struct bgrsb_priv *dev) +{ + int rc = 0; + struct bgrsb_msg req = {0}; + uint32_t resp = 0; + + mutex_lock(&dev->glink_mutex); + init_completion(&dev->bg_resp_cmplt); + init_completion(&dev->tx_done); + + rc = glink_queue_rx_intent(dev->handle, + (void *)dev, BGRSB_GLINK_INTENT_SIZE); + + if (rc) { + pr_err("Failed to queue intent\n"); + goto err_ret; + } + + req.cmd_id = 0x01; + req.data = 0x01; + + rc = glink_tx(dev->handle, (void *)dev, &req, + BGRSB_MSG_SIZE, GLINK_TX_REQ_INTENT); + if (rc) { + pr_err("Failed to send command\n"); + goto err_ret; + } + + rc = wait_for_completion_timeout(&dev->tx_done, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("Timed out waiting sending command\n"); + rc = -ETIMEDOUT; + goto err_ret; + } + + + rc = wait_for_completion_timeout(&dev->bg_resp_cmplt, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("Timed out waiting for response\n"); + rc = -ETIMEDOUT; + goto err_ret; + } + + resp = *(uint32_t *)dev->rx_buf; + if (!(resp == 0x01)) { + pr_err("Bad RSB Configure response\n"); + rc = -EINVAL; + goto err_ret; + } + rc = 0; + +err_ret: + mutex_unlock(&dev->glink_mutex); + return rc; +} + +static void bgrsb_glink_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + struct bgrsb_priv *dev = (struct bgrsb_priv *)priv; + + complete(&dev->tx_done); +} + +static void bgrsb_glink_close_work(struct work_struct *work) +{ + struct bgrsb_priv *dev = + container_of(work, struct bgrsb_priv, glink_work); + + if (dev->handle) + glink_close(dev->handle); + dev->handle = NULL; +} + +static void bgrsb_glink_open_work(struct work_struct *work) +{ + struct glink_open_config open_cfg; + void *hndl = NULL; + int rc = 0; + struct bgrsb_priv *dev = + container_of(work, struct bgrsb_priv, glink_work); + + if (dev->handle) + return; + + memset(&open_cfg, 0, sizeof(struct glink_open_config)); + open_cfg.priv = (void *)dev; + open_cfg.edge = dev->chnl.chnl_edge; + open_cfg.transport = dev->chnl.chnl_trnsprt; + open_cfg.name = dev->chnl.chnl_name; + open_cfg.notify_tx_done = bgrsb_glink_notify_tx_done; + open_cfg.notify_state = bgrsb_glink_notify_state; + open_cfg.notify_rx = bgrsb_glink_notify_rx; + + init_completion(&dev->bg_lnikup_cmplt); + hndl = glink_open(&open_cfg); + + if (IS_ERR_OR_NULL(hndl)) { + pr_err("Glink open failed[%s]\n", + dev->chnl.chnl_name); + dev->handle = NULL; + return; + } + + rc = wait_for_completion_timeout(&dev->bg_lnikup_cmplt, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("Channel open failed. Time out\n"); + return; + } + dev->chnl_state = true; + dev->handle = hndl; +} + +static void bgrsb_glink_state_cb(struct glink_link_state_cb_info *cb_info, + void *data) +{ + struct bgrsb_priv *dev = (struct bgrsb_priv *)data; + + dev->link_state = cb_info->link_state; + switch (dev->link_state) { + case GLINK_LINK_STATE_UP: + INIT_WORK(&dev->glink_work, bgrsb_glink_open_work); + queue_work(dev->bgrsb_event_wq, &dev->glink_work); + break; + case GLINK_LINK_STATE_DOWN: + INIT_WORK(&dev->glink_work, bgrsb_glink_close_work); + queue_work(dev->bgrsb_event_wq, &dev->glink_work); + break; + } +} + +static int bgrsb_init_link_inf(struct bgrsb_priv *dev) +{ + struct glink_link_info link_info; + void *hndl; + + link_info.glink_link_state_notif_cb = bgrsb_glink_state_cb; + link_info.transport = dev->chnl.chnl_trnsprt; + link_info.edge = dev->chnl.chnl_edge; + + hndl = glink_register_link_state_cb(&link_info, (void *)dev); + if (IS_ERR_OR_NULL(hndl)) { + pr_err("Unable to register link[%s]\n", + dev->chnl.chnl_name); + return -EFAULT; + } + return 0; +} + +static int bgrsb_init_regulators(struct device *pdev) +{ + struct regulator *reg11; + struct regulator *reg15; + struct bgrsb_priv *dev = dev_get_drvdata(pdev); + + reg11 = regulator_get(pdev, "vdd-ldo1"); + if (IS_ERR_OR_NULL(reg11)) { + pr_err("Unable to get regulator for LDO-11\n"); + return PTR_ERR(reg11); + } + + reg15 = regulator_get(pdev, "vdd-ldo2"); + if (IS_ERR_OR_NULL(reg15)) { + pr_err("Unable to get regulator for LDO-15\n"); + return PTR_ERR(reg15); + } + + dev->rgltr.regldo11 = reg11; + dev->rgltr.regldo15 = reg15; + + return 0; +} + +static int bgrsb_init(struct bgrsb_priv *dev) +{ + bgrsb_drv = &dev->lhndl; + dev->chnl.chnl_name = "RSB_CTRL"; + dev->chnl.chnl_edge = "bg"; + dev->chnl.chnl_trnsprt = "bgcom"; + mutex_init(&dev->glink_mutex); + dev->link_state = GLINK_LINK_STATE_DOWN; + + dev->ldo_action = BGRSB_NO_ACTION; + + dev->bgrsb_event_wq = + create_singlethread_workqueue(dev->chnl.chnl_name); + if (!dev->bgrsb_event_wq) { + pr_err("Failed to init Glink work-queue\n"); + return -EFAULT; + } + + dev->bgrsb_wq = + create_singlethread_workqueue("bg-work-queue"); + if (!dev->bgrsb_wq) { + pr_err("Failed to init BG-RSB work-queue\n"); + return -EFAULT; + } + + init_waitqueue_head(&dev->link_state_wait); + + /* set default bgrsb state */ + dev->bgrsb_current_state = BGRSB_STATE_INIT; + return 0; +} + +static int bgrsb_ldo_work(struct bgrsb_priv *dev, enum ldo_task ldo_action) +{ + int ret = 0; + + switch (ldo_action) { + case BGRSB_ENABLE_LDO11: + ret = regulator_set_voltage(dev->rgltr.regldo11, + BGRSB_LDO11_VTG_MIN_UV, BGRSB_LDO11_VTG_MAX_UV); + if (ret) { + pr_err("Failed to request LDO-11 voltage.\n"); + goto err_ret; + } + ret = regulator_enable(dev->rgltr.regldo11); + if (ret) { + pr_err("Failed to enable LDO-11 %d\n", ret); + goto err_ret; + } + break; + + case BGRSB_ENABLE_LDO15: + ret = regulator_set_voltage(dev->rgltr.regldo15, + BGRSB_LDO15_VTG_MIN_UV, BGRSB_LDO15_VTG_MAX_UV); + if (ret) { + pr_err("Failed to request LDO-15 voltage.\n"); + goto err_ret; + } + ret = regulator_enable(dev->rgltr.regldo15); + if (ret) { + pr_err("Failed to enable LDO-15 %d\n", ret); + goto err_ret; + } + break; + case BGRSB_DISABLE_LDO11: + ret = regulator_disable(dev->rgltr.regldo11); + if (ret) { + pr_err("Failed to disable LDO-11 %d\n", ret); + goto err_ret; + } + break; + + case BGRSB_DISABLE_LDO15: + ret = regulator_disable(dev->rgltr.regldo15); + if (ret) { + pr_err("Failed to disable LDO-15 %d\n", ret); + goto err_ret; + } + regulator_set_optimum_mode(dev->rgltr.regldo15, 0); + break; + default: + ret = -EINVAL; + } + +err_ret: + return ret; +} + +static void bgrsb_bgdown_work(struct work_struct *work) +{ + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + + bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15); + bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11); + dev->bgrsb_current_state = BGRSB_STATE_INIT; +} + +static void bgrsb_bgup_work(struct work_struct *work) +{ + int rc = 0; + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + + if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) { + + rc = wait_event_timeout(dev->link_state_wait, + (dev->chnl_state == true), + msecs_to_jiffies(TIMEOUT_MS*4)); + if (rc == 0) { + pr_err("Glink channel connection time out\n"); + return; + } + rc = bgrsb_configr_rsb(dev); + if (rc != 0) { + pr_err("BG failed to configure RSB %d\n", rc); + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) + dev->bgrsb_current_state = BGRSB_STATE_INIT; + return; + } + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + pr_debug("BGRSB_STATE_RSB_CONFIGURED\n"); + } +} + +/** + *ssr_bg_cb(): callback function is called + *by ssr framework when BG goes down, up and during ramdump + *collection. It handles BG shutdown and power up events. + */ +static int ssr_bgrsb_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + struct bgrsb_priv *dev = container_of(bgrsb_drv, + struct bgrsb_priv, lhndl); + + switch (opcode) { + case SUBSYS_BEFORE_SHUTDOWN: + INIT_WORK(&dev->bg_work, bgrsb_bgdown_work); + queue_work(dev->bgrsb_wq, &dev->bg_work); + break; + case SUBSYS_AFTER_POWERUP: + if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { + INIT_WORK(&dev->bg_work, bgrsb_bgup_work); + queue_work(dev->bgrsb_wq, &dev->bg_work); + } + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block ssr_bg_nb = { + .notifier_call = ssr_bgrsb_cb, + .priority = 0, +}; + +/** + * ssr_register checks that domain id should be in range and register + * SSR framework for value at domain id. + */ +static int bgrsb_ssr_register(struct bgrsb_priv *dev) +{ + struct notifier_block *nb; + + if (!dev) + return -ENODEV; + + nb = &ssr_bg_nb; + dev->bgwear_subsys_handle = + subsys_notif_register_notifier(BGRSB_BGWEAR_SUBSYS, nb); + + if (!dev->bgwear_subsys_handle) { + dev->bgwear_subsys_handle = NULL; + return -EFAULT; + } + return 0; +} + +static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) +{ + int rc = 0; + + if (!dev->chnl_state) + return -ENODEV; + + mutex_lock(&dev->glink_mutex); + init_completion(&dev->tx_done); + + rc = glink_tx(dev->handle, (void *)dev, msg, + len, GLINK_TX_REQ_INTENT); + if (rc) { + pr_err("Failed to send command\n"); + goto err_ret; + } + + rc = wait_for_completion_timeout(&dev->tx_done, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("Timed out waiting for Command to send\n"); + rc = -ETIMEDOUT; + goto err_ret; + } + rc = 0; + +err_ret: + mutex_unlock(&dev->glink_mutex); + return rc; +} + + +static void bgrsb_enable_rsb(struct work_struct *work) +{ + int rc = 0; + struct bgrsb_msg req = {0}; + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + + if (dev->bgrsb_current_state != BGRSB_STATE_RSB_CONFIGURED) { + pr_err("BG is not yet configured for RSB\n"); + return; + } + + if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO15) == 0) { + + req.cmd_id = 0x02; + req.data = 0x01; + + rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); + if (rc != 0) { + pr_err("Failed to send enable command to BG\n"); + bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15); + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + return; + } + } + dev->bgrsb_current_state = BGRSB_STATE_RSB_EBNABLED; + pr_debug("BGRSB_STATE_RSB_EBNABLED\n"); +} + +static void bgrsb_disable_rsb(struct work_struct *work) +{ + int rc = 0; + struct bgrsb_msg req = {0}; + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_EBNABLED) { + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) + return; + + req.cmd_id = 0x02; + req.data = 0x00; + + rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); + if (rc != 0) { + pr_err("Failed to send disable command to BG\n"); + return; + } + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + pr_debug("BGRSB_STATE_RSB_CONFIGURED\n"); + } +} + +static int store_enable(struct device *pdev, struct device_attribute *attr, + const char *buff, size_t count) +{ + long pwr_st; + int ret; + struct bgrsb_priv *dev = dev_get_drvdata(pdev); + + ret = kstrtol(buff, 10, &pwr_st); + if (ret < 0) + return ret; + + if (pwr_st == BGRSB_POWER_ENABLE) { + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_EBNABLED) + return 0; + INIT_WORK(&dev->bg_work, bgrsb_enable_rsb); + queue_work(dev->bgrsb_wq, &dev->bg_work); + } else if (pwr_st == BGRSB_POWER_DISABLE) { + INIT_WORK(&dev->bg_work, bgrsb_disable_rsb); + queue_work(dev->bgrsb_wq, &dev->bg_work); + } + return 0; +} + +static int show_enable(struct device *dev, struct device_attribute *attr, + char *buff) +{ + return 0; +} + +static struct device_attribute dev_attr_rsb = { + .attr = { + .name = "enable", + .mode = 00660, + }, + .show = show_enable, + .store = store_enable, +}; + +static int bg_rsb_probe(struct platform_device *pdev) +{ + struct bgrsb_priv *dev; + struct input_dev *input; + int rc; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->bgrsb_current_state = BGRSB_STATE_UNKNOWN; + rc = bgrsb_init(dev); + if (rc) + goto err_ret_dev; + + rc = bgrsb_init_link_inf(dev); + if (rc) + goto err_ret_dev; + + /* Set up input device */ + input = input_allocate_device(); + if (!input) + goto err_ret_dev; + + input_set_capability(input, EV_REL, REL_WHEEL); + input->name = "bg-spi"; + + rc = input_register_device(input); + if (rc) { + pr_err("Input device registration failed\n"); + goto err_ret_inp; + } + dev->input = input; + + /* register device for bg-wear ssr */ + rc = bgrsb_ssr_register(dev); + if (rc) { + pr_err("Failed to register for bg ssr\n"); + goto err_ret_inp; + } + rc = device_create_file(&pdev->dev, &dev_attr_rsb); + if (rc) { + pr_err("Not able to create the file bg-rsb/enable\n"); + goto err_ret_inp; + } + dev_set_drvdata(&pdev->dev, dev); + rc = bgrsb_init_regulators(&pdev->dev); + if (rc) { + pr_err("Failed to set regulators\n"); + goto err_ret_inp; + } + return 0; + +err_ret_inp: + input_free_device(input); + +err_ret_dev: + devm_kfree(&pdev->dev, dev); + return -ENODEV; +} + +static int bg_rsb_remove(struct platform_device *pdev) +{ + struct bgrsb_priv *dev = platform_get_drvdata(pdev); + + input_free_device(dev->input); + return 0; +} + +static const struct of_device_id bg_rsb_of_match[] = { + { .compatible = "qcom,bg-rsb", }, + { } +}; + +static struct platform_driver bg_rsb_driver = { + .driver = { + .name = "bg-rsb", + .of_match_table = bg_rsb_of_match, + }, + .probe = bg_rsb_probe, + .remove = bg_rsb_remove, + +}; + +module_platform_driver(bg_rsb_driver); +MODULE_DESCRIPTION("SoC BG RSB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/bgrsb.h b/drivers/soc/qcom/bgrsb.h new file mode 100644 index 000000000000..1ac75d9821f8 --- /dev/null +++ b/drivers/soc/qcom/bgrsb.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef BGRSB_H +#define BGRSB_H + +struct event { + uint8_t sub_id; + int16_t evnt_data; + uint32_t evnt_tm; +}; + + +struct bg_glink_chnl { + char *chnl_name; + char *chnl_edge; + char *chnl_trnsprt; +}; + +/** + * bgrsb_send_input() - send the recived input to input framework + * @evnt: pointer to the event structure + */ +int bgrsb_send_input(struct event *evnt); + +#endif /* BGCOM_H */ -- GitLab From f27c9680440693c69189c37fa073f08980394020 Mon Sep 17 00:00:00 2001 From: Manu Gautam Date: Thu, 30 Nov 2017 16:26:27 +0530 Subject: [PATCH 1815/5498] usb: ehci-msm-hsic: Disable HSIC on driver unbind Keeping HSIC enabled in PHY register increases power leakage as long as PHY is powered. Disable HSIC as part of driver_remove to minimize leakage from HSIC PHY if not in use. Change-Id: Ic246b972424c1e1be9dc8292b804e5be0578db64 Signed-off-by: Manu Gautam --- drivers/usb/host/ehci-msm-hsic.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c index 5948a90c2985..5520de7f8d31 100644 --- a/drivers/usb/host/ehci-msm-hsic.c +++ b/drivers/usb/host/ehci-msm-hsic.c @@ -2318,6 +2318,9 @@ static int ehci_hsic_msm_remove(struct platform_device *pdev) /* Remove the HCD prior to releasing our resources. */ usb_remove_hcd(hcd); + /* Disable HSIC mode in HSIC_CFG register */ + ulpi_write(mehci, 0x0, 0x30); + if (pdata && pdata->standalone_latency) pm_qos_remove_request(&mehci->pm_qos_req_dma); -- GitLab From 475b88edaa993c949dce87fb224b899828a63a2d Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Mon, 20 Nov 2017 16:36:27 +0530 Subject: [PATCH 1816/5498] soc: qcom: glink_pkt: Modify conversion of CTS/DTR signals SMD_DTR_SIG and SMD_CTS_SIG signals are converted to TIOCM_DTR and TIOCM_RTS respectively, for client notification. This is incorrect conversion. SMD_DTR_SIG and SMD_CTS_SIG signals conversion is modified to TIOCM_DSR and TIOCM_CTS respectively. CRs-Fixed: 2148119 Change-Id: I7cd61afe4639fc7c5fd993f4047fad3ff04c53a7 Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/msm_glink_pkt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/msm_glink_pkt.c b/drivers/soc/qcom/msm_glink_pkt.c index e3bb4bc598ca..b02ed1cd0f78 100644 --- a/drivers/soc/qcom/msm_glink_pkt.c +++ b/drivers/soc/qcom/msm_glink_pkt.c @@ -66,9 +66,9 @@ #define map_from_smd_trans_signal(sigs) \ do { \ if (sigs & SMD_DTR_SIG) \ - sigs |= TIOCM_DTR; \ + sigs |= TIOCM_DSR; \ if (sigs & SMD_CTS_SIG) \ - sigs |= TIOCM_RTS; \ + sigs |= TIOCM_CTS; \ if (sigs & SMD_CD_SIG) \ sigs |= TIOCM_CD; \ if (sigs & SMD_RI_SIG) \ -- GitLab From 0fbffa4446a2907750b8ee02ae4b8d0a7a7fff5d Mon Sep 17 00:00:00 2001 From: Shadab Naseem Date: Mon, 13 Nov 2017 17:10:32 +0530 Subject: [PATCH 1817/5498] ARM: dts: msm: Add DT support for APQ8053 Lite dragonboard Add Device Tree support for apq8053 lite dragonboard. Change-Id: I3bf74bf860e08aa7a8a13790f33bebba16dd2149 Signed-off-by: Shadab Naseem --- .../devicetree/bindings/arm/msm/msm.txt | 1 + arch/arm/boot/dts/qcom/Makefile | 1 + .../qcom/apq8053-camera-sensor-dragon.dtsi | 164 ++++++ .../dts/qcom/apq8053-lite-dragon-v1.0.dts | 28 + .../boot/dts/qcom/apq8053-lite-dragon.dtsi | 539 ++++++++++++++++++ arch/arm/boot/dts/qcom/apq8053-lite.dtsi | 120 ++++ 6 files changed, 853 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi create mode 100644 arch/arm/boot/dts/qcom/apq8053-lite-dragon-v1.0.dts create mode 100644 arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi create mode 100644 arch/arm/boot/dts/qcom/apq8053-lite.dtsi diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt index 31dae6d25193..9cd54af2aa36 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm.txt @@ -197,6 +197,7 @@ compatible = "qcom,apq8017-mtp" compatible = "qcom,apq8053-cdp" compatible = "qcom,apq8053-mtp" compatible = "qcom,apq8053-ipc" +compatible = "qcom,apq8053-lite-dragonboard" compatible = "qcom,mdm9630-cdp" compatible = "qcom,mdm9630-mtp" compatible = "qcom,mdm9630-sim" diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 92aad57dc494..bafba5170504 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -267,6 +267,7 @@ dtb-$(CONFIG_ARCH_MSM8953) += msm8953-sim.dtb \ apq8053-mtp.dtb \ apq8053-ext-audio-mtp.dtb \ apq8053-ext-codec-rcm.dtb \ + apq8053-lite-dragon-v1.0.dtb \ msm8953-cdp-1200p.dtb \ msm8953-iot-mtp.dtb \ apq8053-iot-mtp.dtb \ diff --git a/arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi b/arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi new file mode 100644 index 000000000000..3b5225f50907 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; + cam_vaf-supply = <&pm8953_l22>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <2850000>; + qcom,cam-vreg-max-voltage = <2850000>; + qcom,cam-vreg-op-mode = <80000>; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; + qcam_vaf-supply = <&eldo_cam1_vcm_vreg>; + qcom,cam-vreg-name = "cam_vaf"; + qcom,cam-vreg-min-voltage = <2850000>; + qcom,cam-vreg-max-voltage = <2850000>; + qcom,cam-vreg-op-mode = <80000>; + }; + + camera0: qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <0>; + /*qcom,led-flash-src = <&led_flash0>;*/ + qcom,actuator-src = <&actuator0>; + cam_vio-supply = <&pm8953_l6>; + cam_vdig-supply = <&eldo_cam0_vreg>; + cam_vana-supply = <&pm8953_l17>; + qcom,cam-vreg-name = "cam_vio", "cam_vdig", + "cam_vana"; + qcom,cam-vreg-min-voltage = <1800000 1100000 2850000>; + qcom,cam-vreg-max-voltage = <1800000 1100000 2850000>; + qcom,cam-vreg-op-mode = <105000 0 100000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk0_default + &cam_sensor_rear_default + &cam_sensor_rear_vana>; + pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep + &cam_sensor_rear_vana_sleep>; + gpios = <&tlmm 26 0>, + <&tlmm 40 0>, + <&tlmm 39 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK0", + "CAM_RESET0", + "CAM_STANDBY0"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_gcc clk_mclk0_clk_src>, + <&clock_gcc clk_gcc_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + camera1: qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <0>; + qcom,actuator-src = <&actuator1>; + cam_vio-supply = <&pm8953_l6>; + cam_vdig-supply = <&eldo_cam1_vreg>; + cam_vana-supply = <&pm8953_l17>; + qcom,cam-vreg-name = "cam_vio", "cam_vdig", + "cam_vana"; + qcom,cam-vreg-min-voltage = <1800000 1100000 2850000>; + qcom,cam-vreg-max-voltage = <1800000 1100000 2850000>; + qcom,cam-vreg-op-mode = <105000 0 100000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk1_default + &cam_sensor_front1_default>; + pinctrl-1 = <&cam_sensor_mclk1_sleep + &cam_sensor_front1_sleep>; + gpios = <&tlmm 27 0>, + <&tlmm 129 0>, + <&tlmm 130 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK1", + "CAM_RESET1", + "CAM_STANDBY1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_gcc clk_mclk1_clk_src>, + <&clock_gcc clk_gcc_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + camera2: qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <0>; + qcom,actuator-src = <&actuator0>; + cam_vio-supply = <&pm8953_l6>; + cam_vdig-supply = <&eldo_cam2_vreg>; + cam_vana-supply = <&pm8953_l17>; + qcom,cam-vreg-name = "cam_vio", "cam_vdig", + "cam_vana"; + qcom,cam-vreg-min-voltage = <1800000 1100000 2850000>; + qcom,cam-vreg-max-voltage = <1800000 1100000 2850000>; + qcom,cam-vreg-op-mode = <105000 0 100000>; + /*qcom,gpio-no-mux = <0>;*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk2_default + &cam_sensor_front_default>; + pinctrl-1 = <&cam_sensor_mclk2_sleep + &cam_sensor_front_sleep>; + gpios = <&tlmm 28 0>, + <&tlmm 131 0>, + <&tlmm 132 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", + "CAM_RESET2", + "CAM_STANDBY2"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_gcc clk_mclk2_clk_src>, + <&clock_gcc clk_gcc_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/apq8053-lite-dragon-v1.0.dts b/arch/arm/boot/dts/qcom/apq8053-lite-dragon-v1.0.dts new file mode 100644 index 000000000000..18cdc4345590 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8053-lite-dragon-v1.0.dts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "apq8053-lite.dtsi" +#include "apq8053-lite-dragon.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard"; + compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053", + "qcom,dragonboard"; + qcom,board-id = <0x0102000a 0>; +}; + +&blsp2_uart0 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi new file mode 100644 index 000000000000..6286015ae2c2 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include "msm8953-mdss-panels.dtsi" +#include "msm-pmi8950.dtsi" +#include "msm8953-pmi8950.dtsi" +#include "msm8953-pinctrl.dtsi" +#include "apq8053-camera-sensor-dragon.dtsi" + +&soc { + vreg_5p0: vreg_5p0 { + compatible = "regulator-fixed"; + regulator-name = "vreg_5p0"; + status = "ok"; + enable-active-high; + }; + + eldo_cam0_vreg: eldo_cam0_vreg { + compatible = "regulator-fixed"; + regulator-name = "eldo_cam0_vreg"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + status = "ok"; + enable-active-high; + vin-supply = <&pm8953_l5>; + }; + + eldo_cam1_vreg: eldo_cam1_vreg { + compatible = "regulator-fixed"; + regulator-name = "eldo_cam1_vreg"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + status = "ok"; + enable-active-high; + vin-supply = <&pm8953_l5>; + }; + + eldo_cam2_vreg: eldo_cam2_vreg { + compatible = "regulator-fixed"; + regulator-name = "eldo_cam2_vreg"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + status = "ok"; + enable-active-high; + vin-supply = <&pm8953_l5>; + }; + + eldo_cam1_vcm_vreg: eldo_cam1_vcm_vreg { + compatible = "regulator-fixed"; + regulator-name = "eldo_cam1_vcm_vreg"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + status = "ok"; + enable-active-high; + }; + + gpio_keys { + compatible = "gpio-keys"; + input-name = "gpio-keys"; + pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend"; + pinctrl-0 = <&gpio_key_active>; + pinctrl-1 = <&gpio_key_suspend>; + + camera_focus { + label = "camera_focus"; + gpios = <&tlmm 87 0x1>; + linux,input-type = <1>; + linux,code = <0x210>; + debounce-interval = <15>; + }; + + camera_snapshot { + label = "camera_snapshot"; + gpios = <&tlmm 86 0x1>; + linux,input-type = <1>; + linux,code = <0x2fe>; + debounce-interval = <15>; + }; + + vol_up { + label = "volume_up"; + gpios = <&tlmm 85 0x1>; + linux,input-type = <1>; + linux,code = <115>; + debounce-interval = <15>; + }; + }; + + cnss_sdio: qcom,cnss_sdio { + compatible = "qcom,cnss_sdio"; + subsys-name = "AR6320"; + + /** + * There is no vdd-wlan on board and this is not for DSRC. + * IO and XTAL share the same vreg. + */ + vdd-wlan-io-supply = <&pm8953_l6>; + qcom,wlan-ramdump-dynamic = <0x200000>; + qcom,msm-bus,name = "msm-cnss"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <79 512 0 0>, /* No vote */ + <79 512 6250 200000>, /* 50 Mbps */ + <79 512 25000 200000>, /* 200 Mbps */ + <79 512 2048000 4096000>; /* MAX */ + }; + + bluetooth: bt_qca9379 { + compatible = "qca,qca6174"; + qca,bt-reset-gpio = <&tlmm 76 0>; /* BT_EN */ + }; +}; + +&rpm_bus { + rpm-regulator-ldoa4 { + compatible = "qcom,rpm-smd-regulator-resource"; + qcom,resource-name = "ldoa"; + qcom,resource-id = <4>; + qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; + status = "okay"; + + pm8953_l4: regulator-l4 { + compatible = "qcom,rpm-smd-regulator"; + regulator-name = "pm8953_l4"; + qcom,set = <3>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + status = "okay"; + }; + }; +}; + +&int_codec { + status = "ok"; + qcom,model = "msm8953-snd-card-mtp"; + + /delete-property/ qcom,cdc-us-euro-gpios; + qcom,msm-hs-micbias-type = "internal"; + + qcom,audio-routing = + "RX_BIAS", "MCLK", + "SPK_RX_BIAS", "MCLK", + "INT_LDO_H", "MCLK", + "MIC BIAS Internal1", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS Internal1", "Secondary Mic", + "AMIC1", "MIC BIAS Internal1", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS Internal1", + "DMIC1", "MIC BIAS Internal1", + "DMIC1", "Digital Mic1", + "DMIC2", "MIC BIAS Internal1", + "DMIC2", "Digital Mic2"; + + /delete-property/ asoc-wsa-codec-names; + /delete-property/ asoc-wsa-codec-prefixes; + /delete-property/ msm-vdd-wsa-switch-supply; + /delete-property/ qcom,msm-vdd-wsa-switch-voltage; + /delete-property/ qcom,msm-vdd-wsa-switch-current; +}; + +&pm8953_diangu_dig { + status = "ok"; +}; +&pm8953_diangu_analog { + status = "ok"; +}; + +&wsa881x_211 { + /delete-property/ qcom,spkr-sd-n-gpio; +}; +&wsa881x_212 { + /delete-property/ qcom,spkr-sd-n-gpio; +}; +&wsa881x_213 { + /delete-property/ qcom,spkr-sd-n-gpio; +}; +&wsa881x_214 { + /delete-property/ qcom,spkr-sd-n-gpio; +}; + +&spi_3 { + status = "disabled"; +}; + +&i2c_2 { + status = "disabled"; +}; + +&i2c_3 { + status = "ok"; +}; + +&i2c_5 { + status = "disabled"; +}; + + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "dual_dsi"; +}; + +&mdss_dsi_active { + mux { + pins = "gpio61", "gpio100"; + function = "gpio"; + }; + + config { + pins = "gpio61", "gpio100"; + drive-strength = <8>; + bias-disable = <0>; + output-high; + }; +}; + +&mdss_dsi_suspend { + mux { + pins = "gpio61", "gpio100"; + function = "gpio"; + }; + + config { + pins = "gpio61", "gpio100"; + drive-strength = <2>; + bias-pull-down; + }; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_truly_1080_vid>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + + vdd-supply = <&pm8953_l10>; + vddio-supply = <&pm8953_l6>; + /delete-property/ lab-supply; + /delete-property/ ibb-supply; + + qcom,platform-te-gpio = <&tlmm 24 0>; + qcom,platform-reset-gpio = <&tlmm 61 0>; + qcom,platform-bklight-en-gpio = <&tlmm 100 0>; +}; + +&mdss_dsi1 { + qcom,dsi-pref-prim-pan = <&dsi_adv7533_1080p>; + qcom,bridge-index = <0>; + qcom,pluggable; + + vdd-supply = <&pm8953_l4>; + vddio-supply = <&pm8953_l5>; + + /delete-property/ lab-supply; + /delete-property/ ibb-supply; +}; + +&soc { + dsi_panel_pwr_supply_no_labibb: dsi_panel_pwr_supply_no_labibb { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdd"; + qcom,supply-min-voltage = <2850000>; + qcom,supply-max-voltage = <2850000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-post-on-sleep = <20>; + }; + }; +}; + +&blsp1_uart0 { + status = "ok"; + pinctrl-names = "default"; + pinctrl-0 = <&uart_console_active>; +}; + +&mem_client_3_size { + qcom,peripheral-size = <0x500000>; +}; + +&sdhc_1 { + /* device core power supply */ + vdd-supply = <&pm8953_l8>; + qcom,vdd-voltage-level = <2900000 2900000>; + qcom,vdd-current-level = <200 570000>; + + /* device communication power supply */ + vdd-io-supply = <&pm8953_l5>; + qcom,vdd-io-always-on; + qcom,vdd-io-lpm-sup; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 325000>; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>; + pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>; + + qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 192000000 + 384000000>; + qcom,nonremovable; + qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v"; + + status = "ok"; +}; + +&tlmm{ + sdc2_wlan_gpio_on: sdc2_wlan_gpio_on { + mux { + pins = "gpio75"; + function = "gpio"; + }; + config { + pins = "gpio75"; + drive-strength = <10>; + bias-pull-up; + output-high; + }; + }; + sdc2_wlan_gpio_off: sdc2_wlan_gpio_off { + mux { + pins = "gpio75"; + function = "gpio"; + }; + config { + pins = "gpio75"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; +}; + +&sdhc_2 { + /* device core power supply */ + vdd-supply = <&pm8953_l11>; + qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-current-level = <15000 800000>; + + /* device communication power supply */ + vdd-io-supply = <&pm8953_l12>; + qcom,vdd-io-always-on; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 22000>; + qcom,core_3_0v_support; + qcom,nonremovable; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on \ + &sdc2_cd_on &sdc2_wlan_gpio_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off \ + &sdc2_wlan_gpio_off>; + + #address-cells = <0>; + interrupt-parent = <&sdhc_2>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 125 0 + 1 &intc 0 221 0 + 2 &tlmm 133 0>; + interrupt-names = "hc_irq", "pwr_irq", "status_irq"; + + qcom,clk-rates = <400000 20000000 25000000 50000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; + + status = "ok"; +}; + +&spmi_bus { + qcom,pmi8950@2 { + qcom,leds@a100 { + compatible = "qcom,leds-qpnp"; + reg = <0xa100 0x100>; + label = "mpp"; + + qcom,led_mpp_2 { + label = "mpp"; + linux,name = "green"; + linux,default-trigger = "none"; + qcom,default-state = "off"; + qcom,max-current = <40>; + qcom,current-setting = <5>; + qcom,id = <6>; + qcom,mode = "manual"; + qcom,source-sel = <1>; + qcom,mode-ctrl = <0x60>; + }; + }; + + qcom,leds@a300 { + compatible = "qcom,leds-qpnp"; + reg = <0xa300 0x100>; + label = "mpp"; + + qcom,led_mpp_4 { + label = "mpp"; + linux,name = "blue"; + linux,default-trigger = "none"; + qcom,default-state = "off"; + qcom,max-current = <40>; + qcom,current-setting = <5>; + qcom,id = <6>; + qcom,mode = "manual"; + qcom,source-sel = <1>; + qcom,mode-ctrl = <0x60>; + }; + }; + }; +}; + +&pm8953_typec { + ss-mux-supply = <&pm8953_l13>; + qcom,ssmux-gpio = <&tlmm 139 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&typec_ssmux_config>; +}; + +/{ + mtp_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <15>; + #include "batterydata-itech-3000mah.dtsi" + #include "batterydata-ascent-3450mAh.dtsi" + }; +}; + +&pmi8950_charger { + qcom,battery-data = <&mtp_batterydata>; + qcom,chg-led-sw-controls; + qcom,chg-led-support; + qcom,external-typec; + qcom,typec-psy-name = "typec"; + status = "ok"; +}; + +&pmi8950_fg { + qcom,battery-data = <&mtp_batterydata>; +}; + +&pmi_haptic{ + status = "disabled"; + qcom,actuator-type = "lra"; + qcom,lra-auto-res-mode="qwd"; + qcom,lra-high-z="opt1"; + qcom,lra-res-cal-period = <0>; + qcom,wave-play-rate-us = <4165>; +}; + +&wled { + status = "disabled"; +}; + +&flash_led { + status = "disabled"; +}; + +&pm8953_pwm { + status = "ok"; +}; + +&pm8953_vadc { + /delete-node/ chan@13; +}; + +&pmi8950_gpios { + gpio@c000 { /* GPIO_1 */ + status = "ok"; + }; +}; + +&pmi8950_mpps { + mpp@a200 { /* MPP_3 */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + qcom,invert = <0>; + status = "ok"; + }; +}; + +&pm8953_gpios { + gpio@c000 { /* GPIO_1 */ + status = "ok"; + }; +}; + +&pm8953_mpps { + mpp@a000 { /* MPP_1 */ /* VDD_PX */ + status = "disabled"; + }; + mpp@a100 { /* MPP_2 */ + status = "disabled"; + }; + mpp@a200 { /* MPP_3 */ + status = "disabled"; + }; + + mpp@a300 { /* MPP_4 */ /* WLED_PWM_CTRL */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <0>; /* VPH_PWR */ + qcom,src-sel = <4>; /* DTEST1 */ + qcom,master-en = <1>; /* Enable GPIO */ + qcom,invert = <0>; + status = "ok"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/apq8053-lite.dtsi b/arch/arm/boot/dts/qcom/apq8053-lite.dtsi new file mode 100644 index 000000000000..d9685d06d837 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8053-lite.dtsi @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "skeleton64.dtsi" +#include "msm8953.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8953 Lite"; + compatible = "qcom,apq8053"; + qcom,msm-id = <304 0x0>; + interrupt-parent = <&intc>; + + soc: soc { }; +}; + +&clock_gcc_gfx { + compatible = "qcom,gcc-gfx-sdm450"; + qcom,gfxfreq-corner = + < 0 0 >, + < 133330000 1 >, /* Min SVS */ + < 216000000 2 >, /* Low SVS */ + < 320000000 3 >, /* SVS */ + < 400000000 4 >, /* SVS Plus */ + < 510000000 5 >, /* NOM */ + < 560000000 6 >, /* Nom Plus */ + < 600000000 7 >; /* Turbo */ +}; + +/* GPU Overrides*/ +&msm_gpu { + + /delete-node/qcom,gpu-pwrlevels; + + qcom,gpu-pwrlevels { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qcom,gpu-pwrlevels"; + + /* TURBO */ + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <600000000>; + qcom,bus-freq = <10>; + qcom,bus-min = <10>; + qcom,bus-max = <10>; + }; + + /* NOM+ */ + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <560000000>; + qcom,bus-freq = <10>; + qcom,bus-min = <8>; + qcom,bus-max = <10>; + }; + + /* NOM */ + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <510000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <6>; + qcom,bus-max = <10>; + }; + + /* SVS+ */ + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <400000000>; + qcom,bus-freq = <7>; + qcom,bus-min = <5>; + qcom,bus-max = <8>; + }; + + /* SVS */ + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <320000000>; + qcom,bus-freq = <4>; + qcom,bus-min = <2>; + qcom,bus-max = <6>; + }; + + /* Low SVS */ + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <216000000>; + qcom,bus-freq = <1>; + qcom,bus-min = <1>; + qcom,bus-max = <4>; + }; + + /* Min SVS */ + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <133300000>; + qcom,bus-freq = <1>; + qcom,bus-min = <1>; + qcom,bus-max = <4>; + }; + /* XO */ + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <19200000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + }; + }; +}; -- GitLab From af8de1f610de4508273f1df883b32eb3f124b607 Mon Sep 17 00:00:00 2001 From: Lynus Vaz Date: Wed, 15 Nov 2017 19:55:01 +0530 Subject: [PATCH 1818/5498] msm: kgsl: Fix the process sysfs refcounting The kobject used for the process's sysfs node did not provide a release function, so it did not correctly implement the refcounting. Add a release callback so that we keep the process structure valid as long as the kobject is alive. Change-Id: I6db54092ed29ecd4d2f157188a4f1a5fc70f1edf Signed-off-by: Lynus Vaz --- drivers/gpu/msm/kgsl_sharedmem.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index d3ba8ca0dc00..7e96127a092a 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -127,12 +127,10 @@ static ssize_t mem_entry_sysfs_show(struct kobject *kobj, ssize_t ret; /* - * 1. sysfs_remove_file waits for reads to complete before the node - * is deleted. - * 2. kgsl_process_init_sysfs takes a refcount to the process_private, - * which is put at the end of kgsl_process_uninit_sysfs. - * These two conditions imply that priv will not be freed until this - * function completes, and no further locking is needed. + * kgsl_process_init_sysfs takes a refcount to the process_private, + * which is put when the kobj is released. This implies that priv will + * not be freed until this function completes, and no further locking + * is needed. */ priv = kobj ? container_of(kobj, struct kgsl_process_private, kobj) : NULL; @@ -145,12 +143,22 @@ static ssize_t mem_entry_sysfs_show(struct kobject *kobj, return ret; } +static void mem_entry_release(struct kobject *kobj) +{ + struct kgsl_process_private *priv; + + priv = container_of(kobj, struct kgsl_process_private, kobj); + /* Put the refcount we got in kgsl_process_init_sysfs */ + kgsl_process_private_put(priv); +} + static const struct sysfs_ops mem_entry_sysfs_ops = { .show = mem_entry_sysfs_show, }; static struct kobj_type ktype_mem_entry = { .sysfs_ops = &mem_entry_sysfs_ops, + .release = &mem_entry_release, }; static struct mem_entry_stats mem_stats[] = { @@ -173,8 +181,6 @@ kgsl_process_uninit_sysfs(struct kgsl_process_private *private) } kobject_put(&private->kobj); - /* Put the refcount we got in kgsl_process_init_sysfs */ - kgsl_process_private_put(private); } /** -- GitLab From 27c62e6f686b42a0b464818fce99c0b323023825 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Wed, 29 Nov 2017 19:53:52 +0530 Subject: [PATCH 1819/5498] defconfig: msm: set 8909w variants configs for bgrsb driver Add configs and dependency for bgrsb driver. Change-Id: Ia886f06584d93a36c510dcbac9bef8c9755fee24 Signed-off-by: Arjun Singh --- arch/arm/configs/msm8909w-1gb-perf_defconfig | 1 + arch/arm/configs/msm8909w-1gb_defconfig | 1 + arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/configs/msm8909w-1gb-perf_defconfig b/arch/arm/configs/msm8909w-1gb-perf_defconfig index cfcb221b31cc..7515b6d9a508 100644 --- a/arch/arm/configs/msm8909w-1gb-perf_defconfig +++ b/arch/arm/configs/msm8909w-1gb-perf_defconfig @@ -416,6 +416,7 @@ CONFIG_MSM_L2_SPM=y CONFIG_MSM_QMI_INTERFACE=y CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_BGRSB=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y diff --git a/arch/arm/configs/msm8909w-1gb_defconfig b/arch/arm/configs/msm8909w-1gb_defconfig index 7b7f6b6394ea..45adc7da6f05 100644 --- a/arch/arm/configs/msm8909w-1gb_defconfig +++ b/arch/arm/configs/msm8909w-1gb_defconfig @@ -420,6 +420,7 @@ CONFIG_MSM_QMI_INTERFACE=y CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_BGRSB=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index e28176d8012c..71e301d08582 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -368,6 +368,7 @@ CONFIG_MSM_L2_SPM=y CONFIG_MSM_QMI_INTERFACE=y CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_BGRSB=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index f43f58016006..5b12b28bff4a 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -376,6 +376,7 @@ CONFIG_MSM_QMI_INTERFACE=y CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_BGRSB=y CONFIG_MSM_SUBSYSTEM_RESTART=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y -- GitLab From c8424506ecbbc3ef795bb83ff10e7740c2d48e76 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 20 Nov 2017 21:51:23 +0530 Subject: [PATCH 1820/5498] ARM: dts: msm: Add bg-rsb node for msm8909w BG v2 Add Glink channel and bg-rsb for RSB configuration for BG Disable the bias and keep the CS line high for BG. Chip select is active-low. CS must be high once de-asserted. Change-Id: I466cb6dbf26821a88dfebc453d73a039639289b3 Signed-off-by: Arjun Singh --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 26 +++++++++++++++++++ arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 26 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 8fabb3db8dab..4cee9ddec353 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -124,6 +124,13 @@ qcom,glinkpkt-ch-name = "display-data"; qcom,glinkpkt-dev-name = "glink_pkt_bg_display_data"; }; + + qcom,glinkpkt-bg-rsb-ctrl { + qcom,glinkpkt-transport = "bgcom"; + qcom,glinkpkt-edge = "bg"; + qcom,glinkpkt-ch-name = "RSB_CTRL"; + qcom,glinkpkt-dev-name = "glink_pkt_bg_rsb_ctrl"; + }; }; spi@78B8000 { /* BLSP1 QUP4 */ @@ -136,6 +143,12 @@ qcom,irq-gpio = <&msm_gpio 110 1>; }; }; + + qcom,bg-rsb { + compatible = "qcom,bg-rsb"; + vdd-ldo1-supply = <&pm660_l11>; + vdd-ldo2-supply = <&pm660_l15>; + }; }; &i2c_1 { @@ -248,3 +261,16 @@ pins = "gpio98", "gpio16"; }; }; + +&spi4_cs0_active { + mux { + pins = "gpio14"; + function = "blsp_spi4"; + }; + config { + pins = "gpio14"; + drive-strength = <2>; + bias-disable; /* No PULL */ + output-high; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index d7cd1522475b..53fdc56877b4 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -126,6 +126,13 @@ qcom,glinkpkt-ch-name = "display-data"; qcom,glinkpkt-dev-name = "glink_pkt_bg_display_data"; }; + + qcom,glinkpkt-bg-rsb-ctrl { + qcom,glinkpkt-transport = "bgcom"; + qcom,glinkpkt-edge = "bg"; + qcom,glinkpkt-ch-name = "RSB_CTRL"; + qcom,glinkpkt-dev-name = "glink_pkt_bg_rsb_ctrl"; + }; }; spi@78B8000 { /* BLSP1 QUP4 */ @@ -149,6 +156,12 @@ }; }; + qcom,bg-rsb { + compatible = "qcom,bg-rsb"; + vdd-ldo1-supply = <&pm660_l11>; + vdd-ldo2-supply = <&pm660_l15>; + }; + qcom,bcl { compatible = "qcom,bcl"; qcom,bcl-enable; @@ -278,3 +291,16 @@ pins = "gpio98", "gpio16"; }; }; + +&spi4_cs0_active { + mux { + pins = "gpio14"; + function = "blsp_spi4"; + }; + config { + pins = "gpio14"; + drive-strength = <2>; + bias-disable; /* No PULL */ + output-high; + }; +}; -- GitLab From b99d7a7630a83fc7f359f3517cd81e3caa9f3620 Mon Sep 17 00:00:00 2001 From: Baochu Xu Date: Fri, 1 Dec 2017 09:51:23 +0800 Subject: [PATCH 1821/5498] ARM: dts: msm: Add device tree for sdw2500 board Add device tree for msm8909w WTP with PM660 512MB variant. Change-Id: I933d406789e68eac9367558875ccd4763e4f8e47 Signed-off-by: Baochu Xu --- arch/arm/boot/dts/qcom/Makefile | 1 + arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 183 +++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 25902b673a6c..dd2355452759 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -328,6 +328,7 @@ dtb-$(CONFIG_ARCH_MSM8909) += msm8909-pm8916-mtp.dtb \ apq8009w-1gb-nowgr-swoctp-circpanel.dtb \ msm8909w-bg-wtp-v1.dtb \ msm8909w-bg-wtp-v2.dtb \ + msm8909w-wtp-v1.dtb \ apq8009w-bg-wtp-v1.dtb \ apq8009w-bg-wtp-v2.dtb \ apq8009-mtp-wcd9326.dtb \ diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts new file mode 100644 index 000000000000..71a4832c01b6 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "msm8909-mtp.dtsi" +#include "msm8909w-gpu.dtsi" +#include "msm8909w.dtsi" +#include "msm8909w-memory.dtsi" +#include "msm8909w-pm660-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM8909W-PM660 V1 WTP"; + compatible = "qcom,msm8909-mtp", "qcom,msm8909", "qcom,mtp"; + qcom,msm-id = <245 0>, + <258 0>, + <275 0>, + <300 0>; + qcom,board-id = <8 0x112>; + qcom,pmic-id = <0x0001001b 0x0 0x0 0x0>, + <0x0001011b 0x0 0x0 0x0>; +}; + +&soc { + i2c@78b9000 { /* BLSP1 QUP5 */ + synaptics@20 { + compatible = "synaptics,dsx-i2c"; + reg = <0x20>; + interrupt-parent = <&msm_gpio>; + interrupts = <98 0x2008>; + vdd_ana-supply = <&pm660_l18>; + vcc_i2c-supply = <&pm660_l13>; + synaptics,pwr-reg-name = "vdd_ana"; + synaptics,bus-reg-name = "vcc_i2c"; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend", + "pmx_ts_release"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + pinctrl-2 = <&ts_release>; + synaptics,irq-gpio = <&msm_gpio 98 0x2008>; + synaptics,irq-on-state = <0>; + synaptics,irq-flags = <0x2008>; + synaptics,power-delay-ms = <200>; + synaptics,reset-delay-ms = <200>; + synaptics,max-y-for-2d = <389>; + synaptics,resume-in-workqueue; + synaptics,x-flip; + synaptics,y-flip; + /delete-property/ synaptics,reset-gpio; + /delete-property/ synaptics,display-coords; + /delete-property/ synaptics,panel-coords; + /delete-property/ synaptics,power-down; + /delete-property/ synaptics,disable-gpios; + /delete-property/ synaptics,is_wake; + }; + + /delete-node/ it7260@46; + }; + + + qcom,msm-ssc-sensors { + compatible = "qcom,msm-ssc-sensors"; + }; + + qcom,msm-thermal { + vdd-dig-supply = <&pm660_s2_floor_corner>; + + msm_thermal_freq: qcom,vdd-apps-rstr { + qcom,vdd-rstr-reg = "vdd-apps"; + qcom,levels = <1094400>; + qcom,freq-req; + }; + }; + + qcom,bcl { + compatible = "qcom,bcl"; + qcom,bcl-enable; + qcom,bcl-framework-interface; + qcom,bcl-freq-control-list = <&CPU0 &CPU1 &CPU2 &CPU3>; + qcom,bcl-hotplug-list = <&CPU2 &CPU3>; + qcom,bcl-soc-hotplug-list = <&CPU2 &CPU3>; + qcom,ibat-monitor { + qcom,low-threshold-uamp = <1000000>; + qcom,high-threshold-uamp = <2000000>; + qcom,mitigation-freq-khz = <1094400>; + qcom,vph-high-threshold-uv = <3500000>; + qcom,vph-low-threshold-uv = <3200000>; + qcom,soc-low-threshold = <10>; + qcom,thermal-handle = <&msm_thermal_freq>; + }; + }; +}; + +&i2c_1 { + status = "disabled"; +}; + +&spi_0 { + status = "disabled"; +}; + +&i2c_3 { + status = "disabled"; +}; + +&i2c_4 { + status = "disabled"; +}; + +&i2c_2 { + status = "disabled"; +}; + +&sdhc_2 { + status = "disabled"; +}; + +&blsp1_uart1 { + status = "ok"; + pinctrl-names = "default"; + pinctrl-0 = <&uart_console_sleep>; +}; + +/* Pinctrl dt nodes for interrupt & reset gpio for Synaptics touch controller */ +&ts_int_active { + mux { + pins = "gpio98"; + }; + + config { + pins = "gpio98"; + }; +}; + +&ts_int_suspend { + mux { + pins = "gpio98"; + }; + + config { + pins = "gpio98"; + }; +}; + +&ts_reset_active { + mux { + pins = "gpio16"; + }; + + config { + pins = "gpio16"; + }; +}; + +&ts_reset_suspend { + mux { + pins = "gpio16"; + }; + + config { + pins = "gpio16"; + }; +}; + +&ts_release { + mux { + pins = "gpio98", "gpio16"; + }; + + config { + pins = "gpio98", "gpio16"; + }; +}; -- GitLab From ce0d91a7a325da78a8776ca7b0ad8090f2712ec7 Mon Sep 17 00:00:00 2001 From: Baochu Xu Date: Fri, 1 Dec 2017 10:01:42 +0800 Subject: [PATCH 1822/5498] ARM: dts: msm: modify touch panel for SDW2500 Modify Synatipcs S1222 touch panel to change reset pin for MSM8909W SDW2500 devices. Change-Id: I351578c7370b394367f0f5795af77a690e9d9714 Signed-off-by: Baochu Xu --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 71a4832c01b6..9e17d9d1908c 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -32,7 +32,7 @@ }; &soc { - i2c@78b9000 { /* BLSP1 QUP5 */ + i2c@78b7000 { /* BLSP1 QUP3 */ synaptics@20 { compatible = "synaptics,dsx-i2c"; reg = <0x20>; @@ -109,15 +109,15 @@ status = "disabled"; }; -&i2c_3 { +&i2c_4 { status = "disabled"; }; -&i2c_4 { +&i2c_2 { status = "disabled"; }; -&i2c_2 { +&i2c_5 { status = "disabled"; }; @@ -154,11 +154,11 @@ &ts_reset_active { mux { - pins = "gpio16"; + pins = "gpio31"; }; config { - pins = "gpio16"; + pins = "gpio31"; }; }; -- GitLab From d304585891fe0464ca1d7684e22c725b309a04db Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Wed, 29 Nov 2017 20:16:39 +0530 Subject: [PATCH 1823/5498] soc: qcom: bgcom: Adds RSB events support for Blackghost Provides support for RSB events from Blackghost. Sends the events to Glink/input framework using bgrsb. Change-Id: I3ee607e9214fc51334abff626650833d25ed4530 Signed-off-by: Arjun Singh --- drivers/soc/qcom/bgcom_spi.c | 56 +++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c index a338a277d4a0..010e01944c2d 100644 --- a/drivers/soc/qcom/bgcom_spi.c +++ b/drivers/soc/qcom/bgcom_spi.c @@ -26,6 +26,7 @@ #include #include #include "bgcom.h" +#include "bgrsb.h" #define BG_SPI_WORD_SIZE (0x04) #define BG_SPI_READ_LEN (0x04) @@ -84,18 +85,48 @@ struct bg_context { struct cb_data *cb; }; +struct event_list { + struct event *evnt; + struct list_head list; +}; static void *bg_com_drv; static uint32_t g_slav_status_reg; /* BGCOM client callbacks set-up */ +static void send_input_events(struct work_struct *work); static struct list_head cb_head = LIST_HEAD_INIT(cb_head); +static struct list_head pr_lst_hd = LIST_HEAD_INIT(pr_lst_hd); static enum bgcom_spi_state spi_state; + +static struct workqueue_struct *wq; +static DECLARE_WORK(input_work , send_input_events); + static void augmnt_fifo(uint8_t *data, int pos) { data[pos] = '\0'; } +static void send_input_events(struct work_struct *work) +{ + struct list_head *temp; + struct list_head *pos; + struct event_list *node; + struct event *evnt; + + if (list_empty(&pr_lst_hd)) + return; + + list_for_each_safe(pos, temp, &pr_lst_hd) { + node = list_entry(pos, struct event_list, list); + evnt = node->evnt; + bgrsb_send_input(evnt); + kfree(evnt); + list_del(&node->list); + kfree(node); + } +} + int bgcom_set_spi_state(enum bgcom_spi_state state) { struct bg_spi_priv *bg_spi = container_of(bg_com_drv, @@ -224,8 +255,12 @@ void send_event(enum bgcom_event_type event, static void parse_fifo(uint8_t *data, union bgcom_event_data_type *event_data) { uint16_t p_len; + uint8_t sub_id; + uint32_t evnt_tm; uint16_t event_id; void *evnt_data; + struct event *evnt; + struct event_list *data_list; while (*data != '\0') { @@ -234,7 +269,22 @@ static void parse_fifo(uint8_t *data, union bgcom_event_data_type *event_data) p_len = *((uint16_t *) data); data = data + HED_EVENT_SIZE_LEN; - if (event_id == 0x0001) { + if (event_id == 0xFFFE) { + + sub_id = *data; + evnt_tm = *((uint32_t *)(data+1)); + + evnt = kmalloc(sizeof(*evnt), GFP_KERNEL); + evnt->sub_id = sub_id; + evnt->evnt_tm = evnt_tm; + evnt->evnt_data = + *(int16_t *)(data + HED_EVENT_DATA_STRT_LEN); + + data_list = kmalloc(sizeof(*data_list), GFP_KERNEL); + data_list->evnt = evnt; + list_add_tail(&data_list->list, &pr_lst_hd); + + } else if (event_id == 0x0001) { evnt_data = kmalloc(p_len, GFP_KERNEL); if (evnt_data != NULL) { memcpy(evnt_data, data, p_len); @@ -247,6 +297,8 @@ static void parse_fifo(uint8_t *data, union bgcom_event_data_type *event_data) } data = data + p_len; } + if (!list_empty(&pr_lst_hd)) + queue_work(wq, &input_work); } static void send_back_notification(uint32_t slav_status_reg, @@ -764,6 +816,8 @@ static void bg_spi_init(struct bg_spi_priv *bg_spi) bg_spi->irq_lock = 0; spi_state = BGCOM_SPI_FREE; + + wq = create_singlethread_workqueue("input_wq"); } static int bg_spi_probe(struct spi_device *spi) -- GitLab From 91992a1f9d5dad109203fb2fcda59b712c1fde06 Mon Sep 17 00:00:00 2001 From: Sreelakshmi Gownipalli Date: Mon, 18 Sep 2017 12:41:47 -0700 Subject: [PATCH 1824/5498] diag: Free the memory in diagfwd_peripheral_exit() In diagfwd_peripheral_exit() free the memory associated with early_init_info. Change-Id: I862b6d806d67dad38316f1608827a4bf6f5a691f Signed-off-by: Sreelakshmi Gownipalli --- drivers/char/diag/diagfwd_peripheral.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index f353ad18f111..36f2857545e9 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -484,6 +484,7 @@ void diagfwd_peripheral_exit(void) uint8_t peripheral; uint8_t type; struct diagfwd_info *fwd_info = NULL; + int transport = 0; diag_smd_exit(); diag_socket_exit(); @@ -506,7 +507,10 @@ void diagfwd_peripheral_exit(void) driver->diagfwd_dci_cmd[peripheral] = NULL; } - kfree(early_init_info); + for (transport = 0; transport < NUM_TRANSPORT; transport++) { + kfree(early_init_info[transport]); + early_init_info[transport] = NULL; + } } int diagfwd_cntl_register(uint8_t transport, uint8_t peripheral, void *ctxt, -- GitLab From 731a2636c51f8df43d45af369dd32b197e792652 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 20 Nov 2017 19:26:02 +0900 Subject: [PATCH 1825/5498] UPSTREAM: net: xfrm: allow clearing socket xfrm policies. Currently it is possible to add or update socket policies, but not clear them. Therefore, once a socket policy has been applied, the socket cannot be used for unencrypted traffic. This patch allows (privileged) users to clear socket policies by passing in a NULL pointer and zero length argument to the {IP,IPV6}_{IPSEC,XFRM}_POLICY setsockopts. This results in both the incoming and outgoing policies being cleared. The simple approach taken in this patch cannot clear socket policies in only one direction. If desired this could be added in the future, for example by continuing to pass in a length of zero (which currently is guaranteed to return EMSGSIZE) and making the policy be a pointer to an integer that contains one of the XFRM_POLICY_{IN,OUT} enum values. An alternative would have been to interpret the length as a signed integer and use XFRM_POLICY_IN (i.e., 0) to clear the input policy and -XFRM_POLICY_OUT (i.e., -1) to clear the output policy. Bug: 65857891 Tested: https://android-review.googlesource.com/539816 Signed-off-by: Lorenzo Colitti Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 2 +- net/xfrm/xfrm_state.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index f4f17fb057ad..2e453b977284 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1292,7 +1292,7 @@ EXPORT_SYMBOL(xfrm_policy_delete); int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) { - struct net *net = xp_net(pol); + struct net *net = sock_net(sk); struct xfrm_policy *old_pol; #ifdef CONFIG_XFRM_SUB_POLICY diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 24c9945f1659..ad363cf4cc01 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1845,6 +1845,13 @@ int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen struct xfrm_mgr *km; struct xfrm_policy *pol = NULL; + if (!optval && !optlen) { + xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL); + xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL); + __sk_dst_reset(sk); + return 0; + } + if (optlen <= 0 || optlen > PAGE_SIZE) return -EMSGSIZE; -- GitLab From 893aa74b5c807e1e4073796c5fbaaaad76290a79 Mon Sep 17 00:00:00 2001 From: Lynus Vaz Date: Wed, 22 Nov 2017 11:25:04 +0530 Subject: [PATCH 1826/5498] msm: kgsl: Capture all the shader data in the snapshot The shader block size is in dwords. Make sure we copy out all the bytes that we want. Change-Id: I42364350ee6314b4830ed11cc06e61d854122543 Signed-off-by: Lynus Vaz Signed-off-by: Archana Obannagari --- drivers/gpu/msm/adreno_a5xx_snapshot.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c index f3ebdea89132..c5b8e5b30822 100644 --- a/drivers/gpu/msm/adreno_a5xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -621,7 +621,8 @@ static size_t a5xx_snapshot_shader_memory(struct kgsl_device *device, header->index = info->bank; header->size = block->sz; - memcpy(data, registers.hostptr + info->offset, block->sz); + memcpy(data, registers.hostptr + info->offset, + block->sz * sizeof(unsigned int)); return SHADER_SECTION_SZ(block->sz); } -- GitLab From f6fa478bd38ae01eda4b4e667762b88fe9cf8f19 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Mon, 2 Oct 2017 16:56:55 -0700 Subject: [PATCH 1827/5498] ALSA: pcm: remove SNDRV_PCM_IOCTL1_INFO internal command Drivers can implement 'struct snd_pcm_ops.ioctl' to handle some requests from ALSA PCM core. These requests are internal purpose in kernel land. Usually common set of operations are used for it. SNDRV_PCM_IOCTL1_INFO is one of the requests. According to code comment, it has been obsoleted in the old days. We can see old releases in ftp.alsa-project.org. The command was firstly introduced in v0.5.0 release as SND_PCM_IOCTL1_INFO, to allow drivers to fill data of 'struct snd_pcm_channel_info' type. In v0.9.0 release, this was obsoleted by the other commands for ioctl(2) such as SNDRV_PCM_IOCTL_CHANNEL_INFO. This commit removes the long-abandoned command, bye. CRs-fixed: 2112663 Change-Id: I17d9c0a759fce81d3dc9b9375f5d5f35dac583b8 Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Git-commit: e11f0f90a626f93899687b1cc909ee37dd6c5809 Git-repo: git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git Signed-off-by: Karthikeyan Mani --- include/sound/pcm.h | 2 +- sound/core/pcm_lib.c | 2 -- sound/core/pcm_native.c | 6 +----- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 272bd9edb887..ed6f5681a504 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -102,7 +102,7 @@ struct snd_pcm_ops { #define SNDRV_PCM_IOCTL1_TRUE ((void *)1) #define SNDRV_PCM_IOCTL1_RESET 0 -#define SNDRV_PCM_IOCTL1_INFO 1 +/* 1 is absent slot. */ #define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 #define SNDRV_PCM_IOCTL1_GSTATE 3 #define SNDRV_PCM_IOCTL1_FIFO_SIZE 4 diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index cace4de9ca11..d23485b81f6b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1832,8 +1832,6 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { switch (cmd) { - case SNDRV_PCM_IOCTL1_INFO: - return 0; case SNDRV_PCM_IOCTL1_RESET: return snd_pcm_lib_ioctl_reset(substream, arg); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 61b7126db40d..be2a0d5f2b45 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -217,11 +217,7 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) info->subdevices_avail = pstr->substream_count - pstr->substream_opened; strlcpy(info->subname, substream->name, sizeof(info->subname)); runtime = substream->runtime; - /* AB: FIXME!!! This is definitely nonsense */ - if (runtime) { - info->sync = runtime->sync; - substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); - } + return 0; } -- GitLab From 9ef9f25f1df19bda4fbaf5ceddb0ee2305dd227d Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Mon, 2 Oct 2017 15:28:10 -0700 Subject: [PATCH 1828/5498] ALSA: pcm: remove unused variable from snd_pcm_info() Remove unused 'runtime' variable from function snd_pcm_info(). The last usage of this variable was removed with the 'commit e11f0f90a626f9 ("ALSA: pcm: remove SNDRV_PCM_IOCTL1_INFO internal command")'. Change-Id: I3964d84f7cced811d15ff45a1c31cc28d20dc721 Signed-off-by: Banajit Goswami --- sound/core/pcm_native.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index be2a0d5f2b45..d4216dba8919 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -200,7 +200,6 @@ static inline void snd_leave_user(mm_segment_t fs) int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) { - struct snd_pcm_runtime *runtime; struct snd_pcm *pcm = substream->pcm; struct snd_pcm_str *pstr = substream->pstr; @@ -216,7 +215,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) info->subdevices_count = pstr->substream_count; info->subdevices_avail = pstr->substream_count - pstr->substream_opened; strlcpy(info->subname, substream->name, sizeof(info->subname)); - runtime = substream->runtime; return 0; } -- GitLab From 4e6a5f00cdd4a5a8d36ee1a7b75b2299ce2b6093 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Thu, 28 Sep 2017 10:54:21 -0700 Subject: [PATCH 1829/5498] ALSA: pcm: add locks for accessing runtime resource Add spin lock to resolve race conditions while accessing substream runtime resource. CRs-fixed: 2112713 Change-Id: I8db743303ceb50205d62adfc02caf6ecab635d47 Signed-off-by: Karthikeyan Mani --- include/sound/pcm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 272bd9edb887..79a671be797a 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -407,6 +407,7 @@ struct snd_pcm_substream { const struct snd_pcm_ops *ops; /* -- runtime information -- */ struct snd_pcm_runtime *runtime; + spinlock_t runtime_lock; /* -- timer section -- */ struct snd_timer *timer; /* timer */ unsigned timer_running: 1; /* time is running */ -- GitLab From 9d645101bdbb98fa32d6454d1e475a9bdfd88e56 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Thu, 28 Sep 2017 11:06:55 -0700 Subject: [PATCH 1830/5498] ALSA: pcm: use lock to protect substream runtime resource Use a spinlock to protect runtime resource in substream against race conditions which may lead to use-after-free. CRs-fixed: 2112713 Change-Id: I37dee68cad5eae05b21cfade3dabc0c2b79be6b8 Signed-off-by: Karthikeyan Mani --- sound/core/pcm.c | 4 ++++ sound/core/pcm_timer.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 2a456f2d8bd5..c4b32342ca4a 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -705,6 +705,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) } substream->group = &substream->self_group; spin_lock_init(&substream->self_group.lock); + spin_lock_init(&substream->runtime_lock); mutex_init(&substream->self_group.mutex); INIT_LIST_HEAD(&substream->self_group.substreams); list_add_tail(&substream->link_list, &substream->self_group.substreams); @@ -1001,9 +1002,11 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, void snd_pcm_detach_substream(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; + unsigned long flags = 0; if (PCM_RUNTIME_CHECK(substream)) return; + spin_lock_irqsave(&substream->runtime_lock, flags); runtime = substream->runtime; if (runtime->private_free != NULL) runtime->private_free(runtime); @@ -1020,6 +1023,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) put_pid(substream->pid); substream->pid = NULL; substream->pstr->substream_opened--; + spin_unlock_irqrestore(&substream->runtime_lock, flags); } static ssize_t show_pcm_class(struct device *dev, diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index 20ecd8f18080..5258ecca326a 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -65,9 +65,16 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) { struct snd_pcm_substream *substream; + unsigned long ret = 0, flags = 0; substream = timer->private_data; - return substream->runtime ? substream->runtime->timer_resolution : 0; + spin_lock_irqsave(&substream->runtime_lock, flags); + if (substream->runtime) + ret = substream->runtime->timer_resolution; + else + ret = 0; + spin_unlock_irqrestore(&substream->runtime_lock, flags); + return ret; } static int snd_pcm_timer_start(struct snd_timer * timer) -- GitLab From 6f36e50142adb4c63fe5c4be5a7730ae5717045d Mon Sep 17 00:00:00 2001 From: Shrey Vijay Date: Thu, 16 Nov 2017 16:21:50 +0530 Subject: [PATCH 1831/5498] ARM: dts: msm: Configure pinctrl for BLSP1 UART2 for MSM8909 Configure pinctrl settings for BLSP1 UART2 console and use correct BLSP1 UART2 HSUART pinctrl function for MSM8909. Change-Id: I6d4f17ec6ed6c491d99864190e41489e52c0a949 Signed-off-by: Shrey Vijay --- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 29 ++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index 7a7f0120ab12..635b6aed0465 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -185,10 +185,33 @@ }; }; + uart_console2_active: uart_console2_active { + mux { + pins = "gpio20", "gpio21"; + function = "blsp_uart2"; + }; + config { + pins = "gpio20", "gpio21"; + drive-strength = <2>; + bias-disable; + }; + }; + uart_console2_sleep: uart_console2_sleep { + mux { + pins = "gpio20", "gpio21"; + function = "blsp_uart2"; + }; + config { + pins = "gpio20", "gpio21"; + drive-strength = <2>; + bias-pull-down; + }; + }; + blsp1_uart2_tx_active: blsp1_uart2_tx_active { mux { pins = "gpio20"; - function = "blsp_uart2_a"; + function = "blsp_uart2"; }; config { @@ -214,7 +237,7 @@ blsp1_uart2_rxcts_active: blsp1_uart2_rxcts_active { mux { pins = "gpio21", "gpio111"; - function = "blsp_uart2_a"; + function = "blsp_uart2"; }; config { @@ -240,7 +263,7 @@ blsp1_uart2_rfr_active: blsp1_uart2_rfr_active { mux { pins = "gpio112"; - function = "blsp_uart2_a"; + function = "blsp_uart2"; }; config { -- GitLab From df38e625ef0ae9a47e19db814401b26c6881adb8 Mon Sep 17 00:00:00 2001 From: Krishna Manikandan Date: Fri, 1 Dec 2017 17:03:56 +0530 Subject: [PATCH 1832/5498] msm: mdss: Add support for disabling display driver Add support for disabling display driver, based on the module param passed from lk. Change-Id: I2c478e91a63c45a8a4c8c84324371cb6e769ffb4 Signed-off-by: Krishna Manikandan --- drivers/video/msm/mdss/mdp3.c | 7 +++++++ drivers/video/msm/mdss/mdss.h | 2 ++ drivers/video/msm/mdss/mdss_dsi.c | 6 ++++++ drivers/video/msm/mdss/mdss_dsi_status.c | 11 +++++++++++ drivers/video/msm/mdss/mdss_fb.c | 3 +++ drivers/video/msm/mdss/mdss_mdp.c | 6 ++++++ drivers/video/msm/mdss/mdss_util.c | 24 ++++++++++++++++++++++-- 7 files changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index 4d4f859be3ff..16b710c26446 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -2882,6 +2882,13 @@ static int mdp3_probe(struct platform_device *pdev) mdp3_dynamic_clock_gating_ctrl; mdp3_res->mdss_util->panel_intf_type = mdp3_panel_intf_type; mdp3_res->mdss_util->panel_intf_status = mdp3_panel_get_intf_status; + + if (mdp3_res->mdss_util->param_check(mdss_mdp3_panel)) { + mdp3_res->mdss_util->display_disabled = true; + mdp3_res->mdss_util->mdp_probe_done = true; + return 0; + } + rc = mdp3_parse_dt(pdev); if (rc) goto probe_done; diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h index e44ca678cd22..40cccb3f7b19 100644 --- a/drivers/video/msm/mdss/mdss.h +++ b/drivers/video/msm/mdss/mdss.h @@ -561,6 +561,8 @@ struct mdss_util_intf { int (*panel_intf_status)(u32 disp_num, u32 intf_type); struct mdss_panel_cfg* (*panel_intf_type)(int intf_val); int (*dyn_clk_gating_ctrl)(int enable); + bool (*param_check)(char *param_string); + bool display_disabled; }; struct mdss_util_intf *mdss_get_util_intf(void); diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c index d786ee88866e..613ffd3fb41d 100644 --- a/drivers/video/msm/mdss/mdss_dsi.c +++ b/drivers/video/msm/mdss/mdss_dsi.c @@ -3759,6 +3759,12 @@ static int mdss_dsi_probe(struct platform_device *pdev) return -EPROBE_DEFER; } + if (util->display_disabled) { + pr_info("%s: Display is disabled, not progressing with dsi probe\n", + __func__); + return -ENOTSUPP; + } + if (!pdev || !pdev->dev.of_node) { pr_err("%s: DSI driver only supports device tree probe\n", __func__); diff --git a/drivers/video/msm/mdss/mdss_dsi_status.c b/drivers/video/msm/mdss/mdss_dsi_status.c index fce62350ab6a..fcae274c83ff 100644 --- a/drivers/video/msm/mdss/mdss_dsi_status.c +++ b/drivers/video/msm/mdss/mdss_dsi_status.c @@ -227,6 +227,17 @@ static int param_set_interval(const char *val, struct kernel_param *kp) int __init mdss_dsi_status_init(void) { int rc = 0; + struct mdss_util_intf *util = mdss_get_util_intf(); + + if (!util) { + pr_err("%s: Failed to get utility functions\n", __func__); + return -ENODEV; + } + + if (util->display_disabled) { + pr_info("Display is disabled, not progressing with dsi_init\n"); + return -ENOTSUPP; + } pstatus_data = kzalloc(sizeof(struct dsi_status_data), GFP_KERNEL); if (!pstatus_data) { diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index ad80c8646fb1..2db552e280cf 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -942,6 +942,9 @@ static void mdss_fb_shutdown(struct platform_device *pdev) { struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); + if (!mfd) + return; + mfd->shutdown_pending = true; /* wake up threads waiting on idle or kickoff queues */ diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c index f12a8db73a4e..97b784ff2457 100644 --- a/drivers/video/msm/mdss/mdss_mdp.c +++ b/drivers/video/msm/mdss/mdss_mdp.c @@ -2586,6 +2586,12 @@ static int mdss_mdp_probe(struct platform_device *pdev) mdss_res->mdss_util->panel_intf_type = mdss_panel_intf_type; mdss_res->mdss_util->panel_intf_status = mdss_panel_get_intf_status; + if (mdss_res->mdss_util->param_check(mdss_mdp_panel)) { + mdss_res->mdss_util->display_disabled = true; + mdss_res->mdss_util->mdp_probe_done = true; + return 0; + } + rc = msm_dss_ioremap_byname(pdev, &mdata->mdss_io, "mdp_phys"); if (rc) { pr_err("unable to map MDP base\n"); diff --git a/drivers/video/msm/mdss/mdss_util.c b/drivers/video/msm/mdss/mdss_util.c index d2610ff80878..3ea3e2997fb8 100644 --- a/drivers/video/msm/mdss/mdss_util.c +++ b/drivers/video/msm/mdss/mdss_util.c @@ -1,5 +1,5 @@ -/* Copyright (c) 2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2007-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -199,6 +199,24 @@ void mdss_disable_irq_wake(struct mdss_hw *hw) spin_unlock_irqrestore(&mdss_lock, irq_flags); } +static bool check_display(char *param_string) +{ + char *str = NULL; + bool display_disable = false; + + str = strnstr(param_string, ";", MDSS_MAX_PANEL_LEN); + if (!str) + return display_disable; + + str = strnstr(str, ":", MDSS_MAX_PANEL_LEN); + if (!str) + return display_disable; + else if (str[1] == '1') + display_disable = 1; + + return display_disable; +} + struct mdss_util_intf mdss_util = { .register_irq = mdss_register_irq, .enable_irq = mdss_enable_irq, @@ -214,7 +232,9 @@ struct mdss_util_intf mdss_util = { .bus_scale_set_quota = NULL, .panel_intf_type = NULL, .panel_intf_status = NULL, - .mdp_probe_done = false + .mdp_probe_done = false, + .param_check = check_display, + .display_disabled = false }; struct mdss_util_intf *mdss_get_util_intf() -- GitLab From 7924bef75ec713f32ee313900a6f3f97dc32638c Mon Sep 17 00:00:00 2001 From: Swetha Chikkaboraiah Date: Wed, 20 Sep 2017 12:42:45 +0530 Subject: [PATCH 1833/5498] soc: qcom: pil: NULL check before pil_memset_io In existing implementation if vmap fails device panics for NULL pointer access while doing pil_memset_io. Instead check for the NULL pointer. Change-Id: I1b9fd084a8c168b70f6e01134e4122445e4873b4 Signed-off-by: Swetha Chikkaboraiah --- drivers/soc/qcom/peripheral-loader.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index d0fdce05adc5..ed9d11857760 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -628,6 +628,12 @@ static void pil_clear_segment(struct pil_desc *desc) /* Clear memory so that unauthorized ELF code is not left behind */ buf = desc->map_fw_mem(priv->region_start, (priv->region_end - priv->region_start), map_data); + + if (!buf) { + pil_err(desc, "Failed to map memory\n"); + return; + } + pil_memset_io(buf, 0, (priv->region_end - priv->region_start)); desc->unmap_fw_mem(buf, (priv->region_end - priv->region_start), map_data); -- GitLab From 142afbc6b2f33832f332ce5b561aa817edfff0b4 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 15 Dec 2015 15:41:37 -0800 Subject: [PATCH 1834/5498] netlink: add a start callback for starting a netlink dump commit fc9e50f5a5a4e1fa9ba2756f745a13e693cf6a06 upstream. The start callback allows the caller to set up a context for the dump callbacks. Presumably, the context can then be destroyed in the done callback. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller Cc: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- include/linux/netlink.h | 2 ++ include/net/genetlink.h | 2 ++ net/netlink/af_netlink.c | 4 ++++ net/netlink/genetlink.c | 16 ++++++++++++++++ 4 files changed, 24 insertions(+) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 9e572daa15d5..de422dd8e3a6 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -120,6 +120,7 @@ netlink_skb_clone(struct sk_buff *skb, gfp_t gfp_mask) struct netlink_callback { struct sk_buff *skb; const struct nlmsghdr *nlh; + int (*start)(struct netlink_callback *); int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); @@ -142,6 +143,7 @@ struct nlmsghdr * __nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int flags); struct netlink_dump_control { + int (*start)(struct netlink_callback *); int (*dump)(struct sk_buff *skb, struct netlink_callback *); int (*done)(struct netlink_callback *); void *data; diff --git a/include/net/genetlink.h b/include/net/genetlink.h index af10c2cf8a1d..048a24bc7719 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -106,6 +106,7 @@ static inline void genl_info_net_set(struct genl_info *info, struct net *net) * @flags: flags * @policy: attribute validation policy * @doit: standard command callback + * @start: start callback for dumps * @dumpit: callback for dumpers * @done: completion callback for dumps * @ops_list: operations list @@ -114,6 +115,7 @@ struct genl_ops { const struct nla_policy *policy; int (*doit)(struct sk_buff *skb, struct genl_info *info); + int (*start)(struct netlink_callback *cb); int (*dumpit)(struct sk_buff *skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d22e8d210fce..1dfd8d17a574 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2043,6 +2043,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, cb = &nlk->cb; memset(cb, 0, sizeof(*cb)); + cb->start = control->start; cb->dump = control->dump; cb->done = control->done; cb->nlh = nlh; @@ -2056,6 +2057,9 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, mutex_unlock(nlk->cb_mutex); + if (cb->start) + cb->start(cb); + ret = netlink_dump(sk); sock_put(sk); diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 76393f2f4b22..2983147f8ef2 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -507,6 +507,20 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, } EXPORT_SYMBOL(genlmsg_put); +static int genl_lock_start(struct netlink_callback *cb) +{ + /* our ops are always const - netlink API doesn't propagate that */ + const struct genl_ops *ops = cb->data; + int rc = 0; + + if (ops->start) { + genl_lock(); + rc = ops->start(cb); + genl_unlock(); + } + return rc; +} + static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { /* our ops are always const - netlink API doesn't propagate that */ @@ -571,6 +585,7 @@ static int genl_family_rcv_msg(struct genl_family *family, .module = family->module, /* we have const, but the netlink API doesn't */ .data = (void *)ops, + .start = genl_lock_start, .dump = genl_lock_dumpit, .done = genl_lock_done, }; @@ -582,6 +597,7 @@ static int genl_family_rcv_msg(struct genl_family *family, } else { struct netlink_dump_control c = { .module = family->module, + .start = ops->start, .dump = ops->dumpit, .done = ops->done, }; -- GitLab From 8586e18413441d265f0ff536378d6ef358d18853 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 19 Oct 2017 20:51:10 +0800 Subject: [PATCH 1835/5498] ipsec: Fix aborted xfrm policy dump crash commit 1137b5e2529a8f5ca8ee709288ecba3e68044df2 upstream. An independent security researcher, Mohamed Ghannam, has reported this vulnerability to Beyond Security's SecuriTeam Secure Disclosure program. The xfrm_dump_policy_done function expects xfrm_dump_policy to have been called at least once or it will crash. This can be triggered if a dump fails because the target socket's receive buffer is full. This patch fixes it by using the cb->start mechanism to ensure that the initialisation is always done regardless of the buffer situation. Fixes: 12a169e7d8f4 ("ipsec: Put dumpers on the dump list") Signed-off-by: Herbert Xu Signed-off-by: Steffen Klassert Cc: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_user.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index b8170ae1461e..72d65b9978ca 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1625,32 +1625,34 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr static int xfrm_dump_policy_done(struct netlink_callback *cb) { - struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; + struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args; struct net *net = sock_net(cb->skb->sk); xfrm_policy_walk_done(walk, net); return 0; } +static int xfrm_dump_policy_start(struct netlink_callback *cb) +{ + struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args; + + BUILD_BUG_ON(sizeof(*walk) > sizeof(cb->args)); + + xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); + return 0; +} + static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); - struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; + struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args; struct xfrm_dump_info info; - BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) > - sizeof(cb->args) - sizeof(cb->args[0])); - info.in_skb = cb->skb; info.out_skb = skb; info.nlmsg_seq = cb->nlh->nlmsg_seq; info.nlmsg_flags = NLM_F_MULTI; - if (!cb->args[0]) { - cb->args[0] = 1; - xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY); - } - (void) xfrm_policy_walk(net, walk, dump_one_policy, &info); return skb->len; @@ -2384,6 +2386,7 @@ static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { static const struct xfrm_link { int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); + int (*start)(struct netlink_callback *); int (*dump)(struct sk_buff *, struct netlink_callback *); int (*done)(struct netlink_callback *); const struct nla_policy *nla_pol; @@ -2397,6 +2400,7 @@ static const struct xfrm_link { [XFRM_MSG_NEWPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_add_policy }, [XFRM_MSG_DELPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy }, [XFRM_MSG_GETPOLICY - XFRM_MSG_BASE] = { .doit = xfrm_get_policy, + .start = xfrm_dump_policy_start, .dump = xfrm_dump_policy, .done = xfrm_dump_policy_done }, [XFRM_MSG_ALLOCSPI - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi }, @@ -2443,6 +2447,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct netlink_dump_control c = { + .start = link->start, .dump = link->dump, .done = link->done, }; -- GitLab From 8b37803c5fc0e2c3cbf9f03ce7bd5f376beebe2f Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Mon, 27 Nov 2017 06:21:25 +0300 Subject: [PATCH 1836/5498] mm, thp: Do not make page table dirty unconditionally in touch_p[mu]d() commit a8f97366452ed491d13cf1e44241bc0b5740b1f0 upstream. Currently, we unconditionally make page table dirty in touch_pmd(). It may result in false-positive can_follow_write_pmd(). We may avoid the situation, if we would only make the page table entry dirty if caller asks for write access -- FOLL_WRITE. The patch also changes touch_pud() in the same way. Signed-off-by: Kirill A. Shutemov Cc: Michal Hocko Cc: Hugh Dickins Signed-off-by: Linus Torvalds [Salvatore Bonaccorso: backport for 3.16: - Adjust context - Drop specific part for PUD-sized transparent hugepages. Support for PUD-sized transparent hugepages was added in v4.11-rc1 ] Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- mm/huge_memory.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 023a62bf837f..690d172436c4 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1240,17 +1240,11 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma, VM_BUG_ON_PAGE(!PageHead(page), page); if (flags & FOLL_TOUCH) { pmd_t _pmd; - /* - * We should set the dirty bit only for FOLL_WRITE but - * for now the dirty bit in the pmd is meaningless. - * And if the dirty bit will become meaningful and - * we'll only set it with FOLL_WRITE, an atomic - * set_bit will be required on the pmd to set the - * young bit, instead of the current set_pmd_at. - */ - _pmd = pmd_mkyoung(pmd_mkdirty(*pmd)); + _pmd = pmd_mkyoung(*pmd); + if (flags & FOLL_WRITE) + _pmd = pmd_mkdirty(_pmd); if (pmdp_set_access_flags(vma, addr & HPAGE_PMD_MASK, - pmd, _pmd, 1)) + pmd, _pmd, flags & FOLL_WRITE)) update_mmu_cache_pmd(vma, addr, pmd); } if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) { -- GitLab From d5ec57c35ac4eeee9b18fb31a953281e63672c0f Mon Sep 17 00:00:00 2001 From: chenjie Date: Wed, 29 Nov 2017 16:10:54 -0800 Subject: [PATCH 1837/5498] mm/madvise.c: fix madvise() infinite loop under special circumstances commit 6ea8d958a2c95a1d514015d4e29ba21a8c0a1a91 upstream. MADVISE_WILLNEED has always been a noop for DAX (formerly XIP) mappings. Unfortunately madvise_willneed() doesn't communicate this information properly to the generic madvise syscall implementation. The calling convention is quite subtle there. madvise_vma() is supposed to either return an error or update &prev otherwise the main loop will never advance to the next vma and it will keep looping for ever without a way to get out of the kernel. It seems this has been broken since introduction. Nobody has noticed because nobody seems to be using MADVISE_WILLNEED on these DAX mappings. [mhocko@suse.com: rewrite changelog] Link: http://lkml.kernel.org/r/20171127115318.911-1-guoxuenan@huawei.com Fixes: fe77ba6f4f97 ("[PATCH] xip: madvice/fadvice: execute in place") Signed-off-by: chenjie Signed-off-by: guoxuenan Acked-by: Michal Hocko Cc: Minchan Kim Cc: zhangyi (F) Cc: Miao Xie Cc: Mike Rapoport Cc: Shaohua Li Cc: Andrea Arcangeli Cc: Mel Gorman Cc: Kirill A. Shutemov Cc: David Rientjes Cc: Anshuman Khandual Cc: Rik van Riel Cc: Carsten Otte Cc: Dan Williams Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/madvise.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mm/madvise.c b/mm/madvise.c index 0938b30da4ab..3440d5da408e 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -221,9 +221,9 @@ static long madvise_willneed(struct vm_area_struct *vma, { struct file *file = vma->vm_file; + *prev = vma; #ifdef CONFIG_SWAP if (!file || mapping_cap_swap_backed(file->f_mapping)) { - *prev = vma; if (!file) force_swapin_readahead(vma, start, end); else @@ -241,7 +241,6 @@ static long madvise_willneed(struct vm_area_struct *vma, return 0; } - *prev = vma; start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; if (end > vma->vm_end) end = vma->vm_end; -- GitLab From ab1f0096ab28231bb0822b3f603cf5d2c9e43bcf Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 17 Nov 2017 14:50:46 -0500 Subject: [PATCH 1838/5498] btrfs: clear space cache inode generation always commit 8e138e0d92c6c9d3d481674fb14e3439b495be37 upstream. We discovered a box that had double allocations, and suspected the space cache may be to blame. While auditing the write out path I noticed that if we've already setup the space cache we will just carry on. This means that any error we hit after cache_save_setup before we go to actually write the cache out we won't reset the inode generation, so whatever was already written will be considered correct, except it'll be stale. Fix this by _always_ resetting the generation on the block group inode, this way we only ever have valid or invalid cache. With this patch I was no longer able to reproduce cache corruption with dm-log-writes and my bpf error injection tool. Signed-off-by: Josef Bacik Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/extent-tree.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 04627051c9f4..131fe194f9e9 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3219,13 +3219,6 @@ again: goto again; } - /* We've already setup this transaction, go ahead and exit */ - if (block_group->cache_generation == trans->transid && - i_size_read(inode)) { - dcs = BTRFS_DC_SETUP; - goto out_put; - } - /* * We want to set the generation to 0, that way if anything goes wrong * from here on out we know not to trust this cache when we load up next @@ -3235,6 +3228,13 @@ again: ret = btrfs_update_inode(trans, root, inode); WARN_ON(ret); + /* We've already setup this transaction, go ahead and exit */ + if (block_group->cache_generation == trans->transid && + i_size_read(inode)) { + dcs = BTRFS_DC_SETUP; + goto out_put; + } + if (i_size_read(inode) > 0) { ret = btrfs_check_trunc_cache_free_space(root, &root->fs_info->global_block_rsv); -- GitLab From ccabc053d5b8f88573cd6ea7e1d7e4d701b15971 Mon Sep 17 00:00:00 2001 From: Liran Alon Date: Sun, 5 Nov 2017 16:56:32 +0200 Subject: [PATCH 1839/5498] KVM: x86: Exit to user-mode on #UD intercept when emulator requires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 61cb57c9ed631c95b54f8e9090c89d18b3695b3c upstream. Instruction emulation after trapping a #UD exception can result in an MMIO access, for example when emulating a MOVBE on a processor that doesn't support the instruction. In this case, the #UD vmexit handler must exit to user mode, but there wasn't any code to do so. Add it for both VMX and SVM. Signed-off-by: Liran Alon Reviewed-by: Nikita Leshenko Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Wanpeng Li Reviewed-by: Paolo Bonzini Signed-off-by: Radim KrÄmář Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm.c | 2 ++ arch/x86/kvm/vmx.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 073c107faeb4..c59e8f5c2e2f 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -1785,6 +1785,8 @@ static int ud_interception(struct vcpu_svm *svm) int er; er = emulate_instruction(&svm->vcpu, EMULTYPE_TRAP_UD); + if (er == EMULATE_USER_EXIT) + return 0; if (er != EMULATE_DONE) kvm_queue_exception(&svm->vcpu, UD_VECTOR); return 1; diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 3f51f81233c5..66a6f57634be 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4899,6 +4899,8 @@ static int handle_exception(struct kvm_vcpu *vcpu) if (is_invalid_opcode(intr_info)) { er = emulate_instruction(vcpu, EMULTYPE_TRAP_UD); + if (er == EMULATE_USER_EXIT) + return 0; if (er != EMULATE_DONE) kvm_queue_exception(vcpu, UD_VECTOR); return 1; -- GitLab From 092b0115002b27fdbea3026bd18a6481fe67a50d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 10 Nov 2017 10:49:38 +0100 Subject: [PATCH 1840/5498] KVM: x86: inject exceptions produced by x86_decode_insn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6ea6e84309ca7e0e850b3083e6b09344ee15c290 upstream. Sometimes, a processor might execute an instruction while another processor is updating the page tables for that instruction's code page, but before the TLB shootdown completes. The interesting case happens if the page is in the TLB. In general, the processor will succeed in executing the instruction and nothing bad happens. However, what if the instruction is an MMIO access? If *that* happens, KVM invokes the emulator, and the emulator gets the updated page tables. If the update side had marked the code page as non present, the page table walk then will fail and so will x86_decode_insn. Unfortunately, even though kvm_fetch_guest_virt is correctly returning X86EMUL_PROPAGATE_FAULT, x86_decode_insn's caller treats the failure as a fatal error if the instruction cannot simply be reexecuted (as is the case for MMIO). And this in fact happened sometimes when rebooting Windows 2012r2 guests. Just checking ctxt->have_exception and injecting the exception if true is enough to fix the case. Thanks to Eduardo Habkost for helping in the debugging of this issue. Reported-by: Yanan Fu Cc: Eduardo Habkost Signed-off-by: Paolo Bonzini Signed-off-by: Radim KrÄmář Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/x86.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 1e839d801055..26329e8c9855 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5372,6 +5372,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, if (reexecute_instruction(vcpu, cr2, write_fault_to_spt, emulation_type)) return EMULATE_DONE; + if (ctxt->have_exception && inject_emulated_exception(vcpu)) + return EMULATE_DONE; if (emulation_type & EMULTYPE_SKIP) return EMULATE_FAIL; return handle_emulation_failure(vcpu); -- GitLab From 165a3c7d786e16dc4a84403b73d18dac9ea97f7d Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 24 Nov 2017 07:47:50 +0100 Subject: [PATCH 1841/5498] eeprom: at24: check at24_read/write arguments commit d9bcd462daf34aebb8de9ad7f76de0198bb5a0f0 upstream. So far we completely rely on the caller to provide valid arguments. To be on the safe side perform an own sanity check. Signed-off-by: Heiner Kallweit Signed-off-by: Bartosz Golaszewski Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at24.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index d87f77f790d6..c1cceeaf9aeb 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -274,6 +274,9 @@ static ssize_t at24_read(struct at24_data *at24, if (unlikely(!count)) return count; + if (off + count > at24->chip.byte_len) + return -EINVAL; + /* * Read data from chip, protecting against concurrent updates * from this host, but not from other I2C masters. @@ -328,6 +331,9 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf, unsigned long timeout, write_time; unsigned next_page; + if (offset + count > at24->chip.byte_len) + return -EINVAL; + /* Get corresponding I2C address and adjust offset */ client = at24_translate_offset(at24, &offset); -- GitLab From 72427ea588e3eaf0170f87c0ad4b8a568979771d Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Mon, 7 Aug 2017 21:55:45 +1000 Subject: [PATCH 1842/5498] drm/panel: simple: Add missing panel_simple_unprepare() calls commit f3621a8eb59a913612c8e6e37d81f16b649f8b6c upstream. During panel removal or system shutdown panel_simple_disable() is called which disables the panel backlight but the panel is still powered due to missing calls to panel_simple_unprepare(). Fixes: d02fd93e2cd8 ("drm/panel: simple - Disable panel on shutdown") Signed-off-by: Jonathan Liu Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20170807115545.27747-1-net147@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/panel/panel-simple.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 23de22f8c820..7a4faf911ee8 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -312,6 +312,7 @@ static int panel_simple_remove(struct device *dev) drm_panel_remove(&panel->base); panel_simple_disable(&panel->base); + panel_simple_unprepare(&panel->base); if (panel->ddc) put_device(&panel->ddc->dev); @@ -327,6 +328,7 @@ static void panel_simple_shutdown(struct device *dev) struct panel_simple *panel = dev_get_drvdata(dev); panel_simple_disable(&panel->base); + panel_simple_unprepare(&panel->base); } static const struct drm_display_mode auo_b101aw03_mode = { -- GitLab From 9ea40d143494705b074688a4330b7085f06d3942 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 25 Aug 2017 17:34:41 +1000 Subject: [PATCH 1843/5498] NFS: revalidate "." etc correctly on "open". commit b688741cb06695312f18b730653d6611e1bad28d upstream. For correct close-to-open semantics, NFS must validate the change attribute of a directory (or file) on open. Since commit ecf3d1f1aa74 ("vfs: kill FS_REVAL_DOT by adding a d_weak_revalidate dentry op"), open() of "." or a path ending ".." is not revalidated reliably (except when that direct is a mount point). Prior to that commit, "." was revalidated using nfs_lookup_revalidate() which checks the LOOKUP_OPEN flag and forces revalidation if the flag is set. Since that commit, nfs_weak_revalidate() is used for NFSv3 (which ignores the flags) and nothing is used for NFSv4. This is fixed by using nfs_lookup_verify_inode() in nfs_weak_revalidate(). This does the revalidation exactly when needed. Also, add a definition of .d_weak_revalidate for NFSv4. The incorrect behavior is easily demonstrated by running "echo *" in some non-mountpoint NFS directory while watching network traffic. Without this patch, "echo *" sometimes doesn't produce any traffic. With the patch it always does. Fixes: ecf3d1f1aa74 ("vfs: kill FS_REVAL_DOT by adding a d_weak_revalidate dentry op") cc: stable@vger.kernel.org (3.9+) Signed-off-by: NeilBrown Signed-off-by: Anna Schumaker Signed-off-by: Greg Kroah-Hartman --- fs/nfs/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 43d63a4d9a92..90526b2148f0 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1266,7 +1266,7 @@ static int nfs_weak_revalidate(struct dentry *dentry, unsigned int flags) return 0; } - error = nfs_revalidate_inode(NFS_SERVER(inode), inode); + error = nfs_lookup_verify_inode(inode, flags); dfprintk(LOOKUPCACHE, "NFS: %s: inode %lu is %s\n", __func__, inode->i_ino, error ? "invalid" : "valid"); return !error; @@ -1426,6 +1426,7 @@ static int nfs4_lookup_revalidate(struct dentry *, unsigned int); const struct dentry_operations nfs4_dentry_operations = { .d_revalidate = nfs4_lookup_revalidate, + .d_weak_revalidate = nfs_weak_revalidate, .d_delete = nfs_dentry_delete, .d_iput = nfs_dentry_iput, .d_automount = nfs_d_automount, -- GitLab From b858baba6773bfdfbd3fd419a3dfa4eacdc6f107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 23 Nov 2017 21:41:56 +0200 Subject: [PATCH 1844/5498] drm/i915: Don't try indexed reads to alternate slave addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ae5c631e605a452a5a0e73205a92810c01ed954b upstream. We can only specify the one slave address to indexed reads/writes. Make sure the messages we check are destined to the same slave address before deciding to do an indexed transfer. Cc: Daniel Kurtz Cc: Chris Wilson Cc: Daniel Vetter Cc: Sean Paul Fixes: 56f9eac05489 ("drm/i915/intel_i2c: use INDEX cycles for i2c read transactions") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20171123194157.25367-2-ville.syrjala@linux.intel.com Reviewed-by: Chris Wilson (cherry picked from commit c4deb62d7821672265b87952bcd1c808f3bf3e8f) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index ae628001fd97..6f3b747eeda5 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -394,6 +394,7 @@ static bool gmbus_is_index_read(struct i2c_msg *msgs, int i, int num) { return (i + 1 < num && + msgs[i].addr == msgs[i + 1].addr && !(msgs[i].flags & I2C_M_RD) && msgs[i].len <= 2 && (msgs[i + 1].flags & I2C_M_RD)); } -- GitLab From 9742589ef2dd482eb218bf6068eec3bc2fae75da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 23 Nov 2017 21:41:57 +0200 Subject: [PATCH 1845/5498] drm/i915: Prevent zero length "index" write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 56350fb8978bbf4aafe08f21234e161dd128b417 upstream. The hardware always writes one or two bytes in the index portion of an indexed transfer. Make sure the message we send as the index doesn't have a zero length. Cc: Daniel Kurtz Cc: Chris Wilson Cc: Daniel Vetter Cc: Sean Paul Fixes: 56f9eac05489 ("drm/i915/intel_i2c: use INDEX cycles for i2c read transactions") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20171123194157.25367-3-ville.syrjala@linux.intel.com Reviewed-by: Chris Wilson (cherry picked from commit bb9e0d4bca50f429152e74a459160b41f3d60fb2) Signed-off-by: Joonas Lahtinen Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_i2c.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 6f3b747eeda5..b4891066a369 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -395,7 +395,8 @@ gmbus_is_index_read(struct i2c_msg *msgs, int i, int num) { return (i + 1 < num && msgs[i].addr == msgs[i + 1].addr && - !(msgs[i].flags & I2C_M_RD) && msgs[i].len <= 2 && + !(msgs[i].flags & I2C_M_RD) && + (msgs[i].len == 1 || msgs[i].len == 2) && (msgs[i + 1].flags & I2C_M_RD)); } -- GitLab From 1d2acf22c2539c568e0a4bd63bf464e10acd8070 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 5 Dec 2017 11:20:47 +0100 Subject: [PATCH 1846/5498] Linux 3.18.86 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9630ca7223c9..ac3ce9725894 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 85 +SUBLEVEL = 86 EXTRAVERSION = NAME = Diseased Newt -- GitLab From a82d1e8c3bef11c4cac17f46ad1c3a6796517d40 Mon Sep 17 00:00:00 2001 From: gaolez Date: Mon, 20 Nov 2017 16:42:32 +0800 Subject: [PATCH 1847/5498] ARM: dts: msm: Add sleep pinctrl for APQ8009.LE.1.0.2 rome sdio Add sleep pinctrl for sdhc_2 in apq8009-robot-rome.dts since sometimes the wlan module do NOT come up reliably after soft reboot. Change-Id: Id54cd0257aee3157d7f3f4d5b92d6310b904b93e Signed-off-by: Gaole Zhang --- arch/arm/boot/dts/qcom/apq8009-robot-rome.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-robot-rome.dts b/arch/arm/boot/dts/qcom/apq8009-robot-rome.dts index e3f0d0761612..fb030cefaff6 100644 --- a/arch/arm/boot/dts/qcom/apq8009-robot-rome.dts +++ b/arch/arm/boot/dts/qcom/apq8009-robot-rome.dts @@ -243,7 +243,8 @@ pinctrl-names = "active", "sleep"; pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_wlan_gpio_on>; - pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_wlan_gpio_off>; qcom,nonremovable; status = "ok"; }; -- GitLab From 5fd72dd9f524c32fa559dc1395098a692fb67664 Mon Sep 17 00:00:00 2001 From: Abinaya P Date: Tue, 7 Nov 2017 16:39:22 +0530 Subject: [PATCH 1848/5498] drivers: net: can: Add suspend/resume routine for CAN driver Make the CAN interrupt as a wakeup capable interrupt and also add suspend/resume routine to handle the suspend/resume states of the device Change-Id: I0e972f3c4b005b52fb8d2e03f83995ecd3c47538 Signed-off-by: Abinaya P --- drivers/net/can/spi/k61.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/net/can/spi/k61.c b/drivers/net/can/spi/k61.c index f3c2c6e594da..d38d3cd58e7b 100644 --- a/drivers/net/can/spi/k61.c +++ b/drivers/net/can/spi/k61.c @@ -23,6 +23,7 @@ #include #include #include +#include #define DEBUG_K61 0 #if DEBUG_K61 == 1 @@ -920,11 +921,39 @@ static const struct of_device_id k61_match_table[] = { { } }; +#ifdef CONFIG_PM +static int k61_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + enable_irq_wake(spi->irq); + return 0; +} + +static int k61_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct k61_can *priv_data = spi_get_drvdata(spi); + + disable_irq_wake(spi->irq); + k61_rx_message(priv_data); + return 0; +} + +static const struct dev_pm_ops k61_dev_pm_ops = { + .suspend = k61_suspend, + .resume = k61_resume, +}; +#endif + static struct spi_driver k61_driver = { .driver = { .name = "k61", .of_match_table = k61_match_table, .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &k61_dev_pm_ops, +#endif }, .probe = k61_probe, .remove = k61_remove, -- GitLab From 76e030dfb1d53bdc1a6ba68a6d4c7be694b9a0c0 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Mon, 13 Nov 2017 12:18:46 +0530 Subject: [PATCH 1849/5498] ASoC: msm: qdspqv2: add property to start secondary port MSM8909 chipset holds the limitation where we need to start both RX and TX AFE ports for TDM. Add new property to start corresponding TX/RX port. CRs-Fixed: 2147847 Change-Id: I234918807338fc4324ae731885d8e0415d29c743 Signed-off-by: Surendar karka --- .../bindings/sound/qcom-audio-dev.txt | 3 +++ sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 22 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index c9c55f8d6da8..4d0b90f0e5ca 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -1396,6 +1396,9 @@ Required properties: clock rate as zero then afe is not configured for clock. + - qcom,msm-cpudai-tdm-sec-port-start: For chipsets with the limitation where we need + to start both RX and TX AFE ports, this flag is + used to start TX/RX port for RX/TX streams. [Second Level Nodes] diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 577b129da2b6..ed0c9752ea14 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -200,12 +200,14 @@ struct msm_dai_q6_tdm_dai_data { u32 bitwidth; u32 num_group_ports; bool afe_ebit_unsupported; + bool sec_port_enable; struct afe_clk_set clk_set; /* hold LPASS clock config. */ union afe_port_group_config group_cfg; /* hold tdm group config */ struct afe_tdm_port_config port_cfg; /* hold tdm config */ }; static bool afe_ebit_unsupported; +static bool tdm_sec_port_enable; /* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command * 0: linear PCM @@ -4170,6 +4172,11 @@ static int msm_dai_tdm_q6_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "afe_ebit_unsupported %d\n", afe_ebit_unsupported); + tdm_sec_port_enable = of_property_read_bool(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-sec-port-enable"); + + dev_dbg(&pdev->dev, "tdm_sec_port_enable %d\n", tdm_sec_port_enable); + /* other initializations within device group */ atomic_set(&tdm_group_ref[group_idx], 0); @@ -5648,7 +5655,7 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) { + if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX; @@ -5678,7 +5685,7 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, } } else { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) { + if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX; @@ -5737,7 +5744,6 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, dai_data->port_cfg.tdm.num_channels = 1; dai_data->port_cfg.tdm.slot_mask = 1; - dai_data->port_cfg.tdm.nslots_per_frame = 4; dev_dbg(dai->dev, "\n%s:open sec port id %d TDM rate: %d\n" "dai_data->port_cfg.tdm.slot_mask %x\n" @@ -5749,7 +5755,8 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, if (sec_port_id != 0) { rc = afe_tdm_port_start(sec_port_id, &dai_data->port_cfg, - dai_data->rate, 4); + dai_data->rate, + dai_data->num_group_ports); if (IS_ERR_VALUE(rc)) { if (atomic_read(group_ref) == 0) { afe_port_group_enable(group_id, @@ -5805,7 +5812,7 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) { + if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX; @@ -5821,7 +5828,7 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, } } else { prim_port_id = dai->id; - if (dai_data->afe_ebit_unsupported) { + if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX; @@ -5871,7 +5878,7 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, __func__, dai->id); } } - if (dai_data->afe_ebit_unsupported) { + if (dai_data->sec_port_enable) { if (atomic_read(sec_group_ref) == 0) { rc = afe_port_group_enable(sec_group_id, NULL, false); @@ -7251,6 +7258,7 @@ static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) tdm_dev_id); dai_data->afe_ebit_unsupported = afe_ebit_unsupported; + dai_data->sec_port_enable = tdm_sec_port_enable; if (tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX || tdm_dev_id == AFE_PORT_ID_PRIMARY_TDM_TX_1 || -- GitLab From 41920b3ca37f6e2118ab4a23d81505a74bbf20a5 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Wed, 22 Nov 2017 17:40:28 +0530 Subject: [PATCH 1850/5498] ASoC: msm: add afe loopback support for apq8009 Add machine driver changes for AFE Rx to Tx loopback. CRs-Fixed: 2147850 Change-Id: I725c7de21172516598edc30ac7aa9b5b43ee6f56 Signed-off-by: Surendar karka --- sound/soc/msm/apq8009-i2s-ext-codec.c | 36 +++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/sound/soc/msm/apq8009-i2s-ext-codec.c b/sound/soc/msm/apq8009-i2s-ext-codec.c index 22abdac808d8..896c7fd8448f 100644 --- a/sound/soc/msm/apq8009-i2s-ext-codec.c +++ b/sound/soc/msm/apq8009-i2s-ext-codec.c @@ -1070,6 +1070,22 @@ static struct snd_soc_ops msm_pri_auxpcm_be_ops = { .startup = msm_prim_auxpcm_startup, }; +static struct snd_soc_dai_link msm_afe_rxtx_lb_be_dai_link[] = { + { + .name = LPASS_BE_AFE_LOOPBACK_TX, + .stream_name = "AFE Loopback Capture", + .cpu_dai_name = "msm-dai-q6-dev.24577", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AFE_LOOPBACK_TX, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + static struct snd_soc_dai_link apq8009_9326_dai[] = { /* Backend DAI Links */ { @@ -1740,7 +1756,8 @@ static struct snd_soc_dai_link apq8009_dai[] = { static struct snd_soc_dai_link apq8009_9326_dai_links[ ARRAY_SIZE(apq8009_dai) + - ARRAY_SIZE(apq8009_9326_dai)]; + ARRAY_SIZE(apq8009_9326_dai) + + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link)]; static struct snd_soc_card snd_soc_card_9326_apq8009; @@ -1760,7 +1777,7 @@ static int populate_ext_snd_card_dt_data(struct platform_device *pdev) struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) { struct snd_soc_card *card = &snd_soc_card_9326_apq8009; - int num_links, ret; + int ret, len1, len2; card->dev = dev; ret = snd_soc_of_parse_card_name(card, "qcom,model"); @@ -1771,16 +1788,25 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) } pr_debug("%s: CARD is %s\n", __func__, card->name); - num_links = ARRAY_SIZE(apq8009_9326_dai_links); + len1 = ARRAY_SIZE(apq8009_dai); + len2 = len1 + ARRAY_SIZE(apq8009_9326_dai); memcpy(apq8009_9326_dai_links, apq8009_dai, sizeof(apq8009_dai)); - memcpy(apq8009_9326_dai_links + ARRAY_SIZE(apq8009_dai), + memcpy(apq8009_9326_dai_links + len1, apq8009_9326_dai, sizeof(apq8009_9326_dai)); + if (of_property_read_bool(dev->of_node, "qcom,afe-rxtx-lb")) { + dev_dbg(dev, "%s(): AFE RX to TX loopback supported\n", + __func__); + memcpy(apq8009_9326_dai_links + len2, + msm_afe_rxtx_lb_be_dai_link, + sizeof(msm_afe_rxtx_lb_be_dai_link)); + len2 += ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link); + } card->dai_link = apq8009_9326_dai_links; - card->num_links = num_links; + card->num_links = len2; card->dev = dev; return card; -- GitLab From b8c6afd016345b60f440304fed937332bc100088 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Wed, 22 Nov 2017 17:38:27 +0530 Subject: [PATCH 1851/5498] ARM: dts: msm: add afe_loopback_tx back-end dai for apq8009 Add afe_loopback_tx back-end dai for supporting afe loopback to get EC reference data. CRs-Fixed: 2147850 Change-Id: I60686088682d18db04e13b2c1878620ea059b6c5 Signed-off-by: Surendar karka --- arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi | 7 +++++-- arch/arm/boot/dts/qcom/msm8909.dtsi | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi b/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi index 57c3db93bb01..84589e813761 100644 --- a/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi +++ b/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi @@ -25,6 +25,7 @@ qcom,model = "apq8009-tashalite-snd-card"; qcom,msm-mbhc-hphl-swh = <0>; qcom,msm-mbhc-gnd-swh = <0>; + qcom,afe-rxtx-lb; qcom,msm-mclk-freq = <9600000>; qcom,msm-hs-micbias-type = "internal"; qcom,audio-routing = @@ -87,7 +88,8 @@ <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, <&incall_music_2_rx>, <&bt_sco_rx>, - <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>; + <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, + <&afe_loopback_tx>; asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -101,7 +103,8 @@ "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", - "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293"; + "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293", + "msm-dai-q6-dev.24577"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; qcom,wsa-max-devs = <2>; diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index a6f0e9243aa4..92e75273c030 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -1726,6 +1726,11 @@ qcom,msm-dai-q6-dev-id = <240>; }; + afe_loopback_tx: qcom,msm-dai-q6-afe-loopback-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <24577>; + }; + incall_record_rx: qcom,msm-dai-q6-incall-record-rx { compatible = "qcom,msm-dai-q6-dev"; qcom,msm-dai-q6-dev-id = <32771>; -- GitLab From 90a0786bd16fd982695bd0bb8343f69c10f1874e Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Wed, 22 Nov 2017 18:09:54 +0530 Subject: [PATCH 1852/5498] ASoC: msm: add tdm support in machine driver for apq8009 Add dai_links and soc_ops to support TDM in apq8009. CRs-Fixed: 2147847 Change-Id: Ic47c3f36665e1d61a1ee0140634262f78bcd2025 Signed-off-by: Surendar karka --- sound/soc/msm/apq8009-i2s-ext-codec.c | 680 +++++++++++++++++++++++++- 1 file changed, 672 insertions(+), 8 deletions(-) diff --git a/sound/soc/msm/apq8009-i2s-ext-codec.c b/sound/soc/msm/apq8009-i2s-ext-codec.c index 896c7fd8448f..b6790e2682a9 100644 --- a/sound/soc/msm/apq8009-i2s-ext-codec.c +++ b/sound/soc/msm/apq8009-i2s-ext-codec.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -69,11 +70,48 @@ #define WSA8810_NAME_1 "wsa881x.20170211" #define WSA8810_NAME_2 "wsa881x.20170212" +#define TDM_SLOT_OFFSET_MAX 8 + enum btsco_rates { RATE_8KHZ_ID, RATE_16KHZ_ID, }; +enum { + PRIMARY_TDM_RX_0, + PRIMARY_TDM_TX_0, + SECONDARY_TDM_RX_0, + SECONDARY_TDM_TX_0, + TDM_MAX, +}; + + +/* TDM default channels */ +static int msm_pri_tdm_rx_0_ch = 8; +static int msm_pri_tdm_tx_0_ch = 8; + +/* TDM default bit format */ +static int msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + +/* TDM default sampling rate */ +static int msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ; +static int msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ; + +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_16", "KHZ_48"}; + +/* TDM default offset */ +static unsigned int tdm_slot_offset[TDM_MAX][TDM_SLOT_OFFSET_MAX] = { + /* PRI_TDM_RX */ + {0, 4, 8, 12, 16, 20, 24, 28}, + /* PRI_TDM_TX */ + {0, 4, 8, 12, 16, 20, 24, 28}, +}; + struct apq8009_asoc_mach_data { int mclk_freq; struct afe_digital_clk_cfg digital_cdc_clk; @@ -292,6 +330,203 @@ static char const *pri_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", "KHZ_192", "KHZ_8", "KHZ_16", "KHZ_32"}; + +static int msm_pri_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_pri_tdm_rx_0_ch = %d", + msm_pri_tdm_rx_0_ch); + ucontrol->value.integer.value[0] = msm_pri_tdm_rx_0_ch - 1; + return 0; +} + +static int msm_pri_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_pri_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("msm_pri_tdm_rx_0_ch = %d", + msm_pri_tdm_rx_0_ch); + return 0; +} + +static int msm_pri_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("msm_pri_tdm_tx_0_ch = %d", + msm_pri_tdm_tx_0_ch); + ucontrol->value.integer.value[0] = msm_pri_tdm_tx_0_ch - 1; + return 0; +} + +static int msm_pri_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_pri_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("msm_pri_tdm_tx_0_ch = %d", + msm_pri_tdm_tx_0_ch); + return 0; +} + +static int msm_pri_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_rx_0_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("msm_pri_tdm_rx_0_bit_format = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_pri_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("msm_pri_tdm_rx_0_bit_format = %d", + msm_pri_tdm_rx_0_bit_format); + return 0; +} + +static int msm_pri_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_tx_0_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("msm_pri_tdm_tx_0_bit_format = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_pri_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("msm_pri_tdm_tx_0_bit_format = %d", + msm_pri_tdm_tx_0_bit_format); + return 0; +} + +static int msm_pri_tdm_rx_0_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_rx_0_sample_rate) { + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 0; + break; + case SAMPLING_RATE_48KHZ: + default: + ucontrol->value.integer.value[0] = 1; + break; + } + pr_debug("msm_pri_tdm_rx_0_sample_rate = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_rx_0_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + default: + msm_pri_tdm_rx_0_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("msm_pri_tdm_rx_0_sample_rate = %d", + msm_pri_tdm_rx_0_sample_rate); + return 0; +} + +static int msm_pri_tdm_tx_0_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_pri_tdm_tx_0_sample_rate) { + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 0; + break; + case SAMPLING_RATE_48KHZ: + default: + ucontrol->value.integer.value[0] = 1; + break; + } + pr_debug("msm_pri_tdm_tx_0_sample_rate = %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_pri_tdm_tx_0_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + default: + msm_pri_tdm_tx_0_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("msm_pri_tdm_tx_0_sample_rate = %d", + msm_pri_tdm_tx_0_sample_rate); + return 0; +} + static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -502,6 +737,203 @@ static int msm_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + channels->min = channels->max = msm_pri_tdm_rx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_pri_tdm_rx_0_bit_format); + rate->min = rate->max = msm_pri_tdm_rx_0_sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + channels->min = channels->max = msm_pri_tdm_tx_0_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_pri_tdm_tx_0_bit_format); + rate->min = rate->max = msm_pri_tdm_tx_0_sample_rate; + break; + default: + pr_err("%s: dai id 0x%x not supported", __func__, + cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d", __func__, + cpu_dai->id, channels->max, rate->max); + + return 0; +} + +static unsigned int tdm_param_set_slot_mask(u16 port_id, + int slot_width, int channels) +{ + unsigned int slot_mask = 0; + int upper, lower, i, j; + unsigned int *slot_offset; + + switch (port_id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + lower = PRIMARY_TDM_RX_0; + upper = PRIMARY_TDM_RX_0; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + lower = PRIMARY_TDM_TX_0; + upper = PRIMARY_TDM_TX_0; + break; + default: + return slot_mask; + } + + for (i = lower; i <= upper; i++) { + slot_offset = tdm_slot_offset[i]; + for (j = 0; j < channels; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + /* + * set the mask of active slot according to + * the offset table for the group of devices + */ + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channel HW configuration should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, channels); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slot_offset = tdm_slot_offset[PRIMARY_TDM_RX_0]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slot_offset = tdm_slot_offset[PRIMARY_TDM_TX_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < channels; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } + +end: + return ret; +} + static int msm_pri_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -779,6 +1211,12 @@ static const struct soc_enum msm_snd_enum[] = { SOC_ENUM_SINGLE_EXT(3, rx_bit_format_text), SOC_ENUM_SINGLE_EXT(4, mi2s_tx_ch_text), SOC_ENUM_SINGLE_EXT(6, pri_rx_sample_rate_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_ch_text), + tdm_ch_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_bit_format_text), + tdm_bit_format_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_sample_rate_text), + tdm_sample_rate_text), }; static const char *const btsco_rate_text[] = {"BTSCO_RATE_8KHZ", @@ -800,6 +1238,22 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_btsco_rate_get, msm_btsco_rate_put), SOC_ENUM_EXT("MI2S_RX SampleRate", msm_snd_enum[2], pri_rx_sample_rate_get, pri_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", msm_snd_enum[3], + msm_pri_tdm_rx_0_ch_get, msm_pri_tdm_rx_0_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", msm_snd_enum[3], + msm_pri_tdm_tx_0_ch_get, msm_pri_tdm_tx_0_ch_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Bit Format", msm_snd_enum[4], + msm_pri_tdm_rx_0_bit_format_get, + msm_pri_tdm_rx_0_bit_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Bit Format", msm_snd_enum[4], + msm_pri_tdm_tx_0_bit_format_get, + msm_pri_tdm_tx_0_bit_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", msm_snd_enum[5], + msm_pri_tdm_rx_0_sample_rate_get, + msm_pri_tdm_rx_0_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", msm_snd_enum[5], + msm_pri_tdm_tx_0_sample_rate_get, + msm_pri_tdm_tx_0_sample_rate_put), }; static int apq8009_mclk_event(struct snd_soc_dapm_widget *w, @@ -974,6 +1428,101 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) __func__, "pri_i2s"); } + +static int msm_tdm_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct apq8009_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0, val = 0; + + pr_debug("substream = %s stream = %d", + substream->name, substream->stream); + pr_debug("dai id = 0x%x", cpu_dai->id); + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + /* Configure mux for Primary TDM */ + if (pdata->vaddr_gpio_mux_pcm_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); + val = val | 0x00000001; + iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); + } else { + return -EINVAL; + } + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + /*0x1808000 Use this value for slave mode*/ + val = val | 0x02020002; /*this is for master mode*/ + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } else { + return -EINVAL; + } + + ret = msm_gpioset_activate(CLIENT_WCD_EXT, "quat_i2s"); + if (ret < 0) + pr_err("%s: failed to activate primary TDM gpio set\n", + __func__); + break; + default: + pr_err("dai id 0x%x not supported", cpu_dai->id); + break; + return -EINVAL; + } + return ret; +} + +static void msm_tdm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + ret = msm_gpioset_suspend(CLIENT_WCD_EXT, "quat_i2s"); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %s\n", + __func__, "pri_tdm"); + return; + } + break; + default: + break; + } +} + static int msm_audrx_init_wcd(struct snd_soc_pcm_runtime *rtd) { @@ -1086,6 +1635,12 @@ static struct snd_soc_dai_link msm_afe_rxtx_lb_be_dai_link[] = { }, }; +static struct snd_soc_ops msm_tdm_be_ops = { + .startup = msm_tdm_startup, + .hw_params = msm_tdm_snd_hw_params, + .shutdown = msm_tdm_shutdown, +}; + static struct snd_soc_dai_link apq8009_9326_dai[] = { /* Backend DAI Links */ { @@ -1754,10 +2309,108 @@ static struct snd_soc_dai_link apq8009_dai[] = { }, }; +static struct snd_soc_dai_link msm_tdm_fe_dai[] = { + /* FE TDM DAI links */ + { + .name = "Primary TDM RX 0 Hostless", + .stream_name = "Primary TDM RX 0 Hostless", + .cpu_dai_name = "PRI_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Primary TDM TX 0 Hostless", + .stream_name = "Primary TDM TX 0 Hostless", + .cpu_dai_name = "PRI_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary TDM RX 0 Hostless", + .stream_name = "Secondary TDM RX 0 Hostless", + .cpu_dai_name = "SEC_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary TDM TX 0 Hostless", + .stream_name = "Secondary TDM TX 0 Hostless", + .cpu_dai_name = "SEC_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_tdm_be_dai[] = { + /* TDM be dai links */ + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + static struct snd_soc_dai_link apq8009_9326_dai_links[ ARRAY_SIZE(apq8009_dai) + + ARRAY_SIZE(msm_tdm_fe_dai) + ARRAY_SIZE(apq8009_9326_dai) + - ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link)]; + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link) + + ARRAY_SIZE(msm_tdm_be_dai)]; static struct snd_soc_card snd_soc_card_9326_apq8009; @@ -1777,7 +2430,7 @@ static int populate_ext_snd_card_dt_data(struct platform_device *pdev) struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) { struct snd_soc_card *card = &snd_soc_card_9326_apq8009; - int ret, len1, len2; + int ret, len1, len2, len3; card->dev = dev; ret = snd_soc_of_parse_card_name(card, "qcom,model"); @@ -1789,24 +2442,35 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) pr_debug("%s: CARD is %s\n", __func__, card->name); len1 = ARRAY_SIZE(apq8009_dai); - len2 = len1 + ARRAY_SIZE(apq8009_9326_dai); + len2 = len1 + ARRAY_SIZE(msm_tdm_fe_dai); + len3 = len2 + ARRAY_SIZE(apq8009_9326_dai); memcpy(apq8009_9326_dai_links, apq8009_dai, sizeof(apq8009_dai)); - memcpy(apq8009_9326_dai_links + len1, - apq8009_9326_dai, sizeof(apq8009_9326_dai)); + memcpy(apq8009_9326_dai_links + len1, msm_tdm_fe_dai, + sizeof(msm_tdm_fe_dai)); + memcpy(apq8009_9326_dai_links + len2, apq8009_9326_dai, + sizeof(apq8009_9326_dai)); if (of_property_read_bool(dev->of_node, "qcom,afe-rxtx-lb")) { dev_dbg(dev, "%s(): AFE RX to TX loopback supported\n", __func__); - memcpy(apq8009_9326_dai_links + len2, + memcpy(apq8009_9326_dai_links + len3, msm_afe_rxtx_lb_be_dai_link, sizeof(msm_afe_rxtx_lb_be_dai_link)); - len2 += ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link); + len3 += ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link); + } + + if (of_property_read_bool(dev->of_node, "qcom,tdm-audio-intf")) { + dev_dbg(dev, "%s(): TDM support present\n", + __func__); + memcpy(apq8009_9326_dai_links + len3, msm_tdm_be_dai, + sizeof(msm_tdm_be_dai)); + len3 += ARRAY_SIZE(msm_tdm_be_dai); } card->dai_link = apq8009_9326_dai_links; - card->num_links = len2; + card->num_links = len3; card->dev = dev; return card; -- GitLab From 3c9d4c6b0ce039367a75ba21dc47092fddf0b825 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Wed, 22 Nov 2017 17:59:02 +0530 Subject: [PATCH 1853/5498] ARM: dts: msm: add TDM support for apq8009 Add primary Tx/Rx dai property and cpu names to support TDM in apq8009. CRs-Fixed: 2147847 Change-Id: I2d9f8351fe3f2fee46ae7a7b75c94073b001b16b Signed-off-by: Surendar karka --- .../qcom/apq8009-audio-external_codec.dtsi | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi b/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi index 84589e813761..c70e5ec90cec 100644 --- a/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi +++ b/arch/arm/boot/dts/qcom/apq8009-audio-external_codec.dtsi @@ -27,6 +27,7 @@ qcom,msm-mbhc-gnd-swh = <0>; qcom,afe-rxtx-lb; qcom,msm-mclk-freq = <9600000>; + qcom,tdm-audio-intf; qcom,msm-hs-micbias-type = "internal"; qcom,audio-routing = "AIF4 VI", "MCLK", @@ -89,7 +90,8 @@ <&incall_music_rx>, <&incall_music_2_rx>, <&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, - <&afe_loopback_tx>; + <&afe_loopback_tx>, <&dai_pri_tdm_rx_0>, + <&dai_pri_tdm_tx_0>; asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -104,7 +106,8 @@ "msm-dai-q6-dev.32770", "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293", - "msm-dai-q6-dev.24577"; + "msm-dai-q6-dev.24577", "msm-dai-q6-tdm.36864", + "msm-dai-q6-tdm.36865"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; qcom,wsa-max-devs = <2>; @@ -224,4 +227,44 @@ }; }; }; + + pri_tdm_rx: qcom,msm-dai-tdm-pri-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37120>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36864>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-sec-port-enable; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36864>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + pri_tdm_tx: qcom,msm-dai-tdm-pri-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37121>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36865>; + qcom,msm-cpudai-tdm-clk-rate = <12288000>; + qcom,msm-cpudai-tdm-sec-port-enable; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36865>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; }; -- GitLab From c2b30b43e0182209f197d8b597f08551a2d6db4d Mon Sep 17 00:00:00 2001 From: Nick Bray Date: Thu, 30 Nov 2017 15:49:54 -0800 Subject: [PATCH 1854/5498] ANDROID: initramfs: call free_initrd() when skipping init Memory allocated for initrd would not be reclaimed if initializing ramfs was skipped. Bug: 69901741 Test: "grep MemTotal /proc/meminfo" increases by a few MB on an Android device with a/b boot. Change-Id: Ifbe094d303ed12cfd6de6aa004a8a19137a2f58a Signed-off-by: Nick Bray --- init/initramfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/init/initramfs.c b/init/initramfs.c index d39079e690fa..dbc9b7adb26f 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -621,8 +621,11 @@ static int __init populate_rootfs(void) { char *err; - if (do_skip_initramfs) + if (do_skip_initramfs) { + if (initrd_start) + free_initrd(); return default_rootfs(); + } err = unpack_to_rootfs(__initramfs_start, __initramfs_size); if (err) -- GitLab From 3f55eee4017c6c56b15bf528618fe6f2ea4dd370 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Wed, 6 Dec 2017 11:12:16 +0530 Subject: [PATCH 1855/5498] usb: gadget: f_uvc: Fix incorrect wBytesPerInterval Host fetches no more than wBytesPerInterval data for every interval in super speed mode. Since this is being incorrectly calculated function driver ends up queueing more data than the host fetches in one interval resulting in MISSED ISOCs and data loss. Fix this by using the correct maxburst value to calcualte the wBytesPerInterval. Change-Id: I8e4640e6c0f8bc5285e97a0184b7491f6a369146 Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/function/f_uvc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 80c4f51ae175..f425334af22e 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -646,7 +646,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = cpu_to_le16(max_packet_size * max_packet_mult * - opts->streaming_maxburst); + (opts->streaming_maxburst + 1)); /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); -- GitLab From ae2f4d603e5caf439c1be524da53eec3029bd6ea Mon Sep 17 00:00:00 2001 From: Srinu Gorle Date: Tue, 5 Dec 2017 19:46:36 +0530 Subject: [PATCH 1856/5498] msm: vidc: fix clock voting for back to back video encoding When V4L2 client calls stream_on-> stream_off ->stream_on sequence for same session, incorrect instance state (i.e RELEASE_RESOURCES_DONE) observed after submitting new set of buffers for second stream_on. Due to no intermediate state exist between open_done and start_done, call scale clocks after instance state moves to start_done to ensure proper clocks are voted. Change-Id: Iaa2d7083ab221794456ca4e8b02af24799a155e7 Signed-off-by: Srinu Gorle --- drivers/media/platform/msm/vidc/msm_venc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index d059ca5dddb2..0343e3f087fa 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -1872,6 +1872,9 @@ static inline int start_streaming(struct msm_vidc_inst *inst) "Failed to move inst: %pK to start done state\n", inst); goto fail_start; } + + msm_comm_scale_clocks_and_bus(inst); + msm_dcvs_init_load(inst); fail_start: -- GitLab From f1e1eed4118fce7a0dd5f985023682bc803258e6 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Wed, 6 Dec 2017 08:40:57 +0530 Subject: [PATCH 1857/5498] defconfig: msm8909: disable unnecessary configs Disable unnecessary config options to reduce memory footprint. Change-Id: Ie5b8c4d44282fd3050e39a05acea535013f3ae99 Signed-off-by: Sahitya Tummala --- arch/arm/configs/msm8909-perf_defconfig | 4 ---- arch/arm/configs/msm8909_defconfig | 4 ---- arch/arm/configs/msm8909w-1gb-perf_defconfig | 2 -- arch/arm/configs/msm8909w-1gb_defconfig | 2 -- arch/arm/configs/msm8909w-perf_defconfig | 2 -- arch/arm/configs/msm8909w_defconfig | 2 -- 6 files changed, 16 deletions(-) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index 0bc996cf9c84..25bd2e4b2b62 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -493,10 +493,6 @@ CONFIG_CORESIGHT_HWEVENT=y CONFIG_SENSORS=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index 46573429be51..3d539000ea8b 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -509,10 +509,6 @@ CONFIG_CORESIGHT_QPDI=y CONFIG_SENSORS=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y diff --git a/arch/arm/configs/msm8909w-1gb-perf_defconfig b/arch/arm/configs/msm8909w-1gb-perf_defconfig index 7515b6d9a508..00c296de9e5f 100644 --- a/arch/arm/configs/msm8909w-1gb-perf_defconfig +++ b/arch/arm/configs/msm8909w-1gb-perf_defconfig @@ -447,8 +447,6 @@ CONFIG_CORESIGHT_STM=y CONFIG_CORESIGHT_HWEVENT=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_FUSE_FS=y diff --git a/arch/arm/configs/msm8909w-1gb_defconfig b/arch/arm/configs/msm8909w-1gb_defconfig index 45adc7da6f05..c2059cafc779 100644 --- a/arch/arm/configs/msm8909w-1gb_defconfig +++ b/arch/arm/configs/msm8909w-1gb_defconfig @@ -454,8 +454,6 @@ CONFIG_CORESIGHT_REMOTE_ETM=y CONFIG_CORESIGHT_QPDI=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_FUSE_FS=y diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 1f1cae0c65ef..d7f3d0cfdd4d 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -452,8 +452,6 @@ CONFIG_CORESIGHT_STM=y CONFIG_CORESIGHT_HWEVENT=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index fa3a72c3022e..ef18722a7b95 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -463,8 +463,6 @@ CONFIG_CORESIGHT_REMOTE_ETM=y CONFIG_CORESIGHT_QPDI=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y -- GitLab From ef29ea27dd89f24909e4777d62a7b6672aee22a7 Mon Sep 17 00:00:00 2001 From: Sarada Prasanna Garnayak Date: Fri, 1 Dec 2017 15:29:54 +0530 Subject: [PATCH 1858/5498] wcnss: fix the potential buffer overflow in wlan ctrl data process Validate the userspace wlan control cmd data, info and length before copy into wcnss driver buffer. Avoid unnecessary string manipulation and use kernel defined format specifier to print wlan MAC address. CRs-Fixed: 2149331 Change-Id: Ib59fdcc0e6b84cdd73972dcb62b2c05e4741f5f7 Signed-off-by: Yuanyuan Liu Signed-off-by: Sarada Prasanna Garnayak --- drivers/net/wireless/wcnss/wcnss_wlan.c | 83 +++++++++++-------------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index d62ce26ff137..674c2151fc3a 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -187,6 +187,7 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_MAX_BUILD_VER_LEN 256 #define WCNSS_MAX_CMD_LEN (128) #define WCNSS_MIN_CMD_LEN (3) +#define WCNSS_CMD_INFO_LEN 2 /* control messages from userspace */ #define WCNSS_USR_CTRL_MSG_START 0x00000000 @@ -194,7 +195,6 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3) #define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x" -#define SHOW_MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x\n" #define WCNSS_USER_MAC_ADDR_LENGTH 18 /* message types */ @@ -457,11 +457,7 @@ static ssize_t wcnss_wlan_macaddr_store(struct device *dev, (char *)&macAddr[index], sizeof(char)); } - pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__, - penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], - penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], - penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); - + pr_info("%s: Write MAC Addr: %pM\n", __func__, penv->wlan_nv_macAddr); return count; } @@ -471,10 +467,7 @@ static ssize_t wcnss_wlan_macaddr_show(struct device *dev, if (!penv) return -ENODEV; - return scnprintf(buf, PAGE_SIZE, SHOW_MAC_ADDRESS_STR, - penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], - penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], - penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); + return scnprintf(buf, PAGE_SIZE, "%pM\n", penv->wlan_nv_macAddr); } static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR, @@ -1663,10 +1656,8 @@ int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE]) return -ENODEV; memcpy(mac_addr, penv->wlan_nv_macAddr, WLAN_MAC_ADDR_SIZE); - pr_debug("%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__, - penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], - penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], - penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); + pr_debug("%s: Get MAC Addr: %pM\n", __func__, penv->wlan_nv_macAddr); + return 0; } EXPORT_SYMBOL(wcnss_get_wlan_mac_address); @@ -2649,57 +2640,57 @@ static int wcnss_ctrl_open(struct inode *inode, struct file *file) return rc; } - -void process_usr_ctrl_cmd(u8 *buf, size_t len) +static ssize_t wcnss_ctrl_write(struct file *fp, const char __user + *user_buffer, size_t count, loff_t *position) { - u16 cmd = buf[0] << 8 | buf[1]; + int rc = 0; + u16 cmd; + u8 buf[WCNSS_MAX_CMD_LEN]; - switch (cmd) { + if (!penv || !penv->ctrl_device_opened || + WCNSS_MAX_CMD_LEN < count || WCNSS_MIN_CMD_LEN > count) + return -EFAULT; + mutex_lock(&penv->ctrl_lock); + rc = copy_from_user(buf, user_buffer, count); + if (rc) { + pr_err("%s: Failed to copy ctrl data\n", __func__); + goto exit; + } + + cmd = buf[0] << 8 | buf[1]; + switch (cmd) { case WCNSS_USR_HAS_CAL_DATA: - if (1 < buf[2]) - pr_err("%s: Invalid data for cal %d\n", __func__, - buf[2]); + if (buf[2] > 1) { + pr_err("%s: Invalid cal data %d\n", __func__, buf[2]); + rc = -EINVAL; + goto exit; + } has_calibrated_data = buf[2]; break; case WCNSS_USR_WLAN_MAC_ADDR: - memcpy(&penv->wlan_nv_macAddr, &buf[2], - sizeof(penv->wlan_nv_macAddr)); + if ((count - WCNSS_CMD_INFO_LEN) != WLAN_MAC_ADDR_SIZE) { + pr_err("%s: Invalid Mac addr %d\n", __func__, buf[2]); + rc = -EINVAL; + goto exit; + } - pr_debug("%s: MAC Addr:" MAC_ADDRESS_STR "\n", __func__, - penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], - penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], - penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); + memcpy(&penv->wlan_nv_macAddr, &buf[2], + sizeof(penv->wlan_nv_macAddr)); + pr_debug("%s:MAC Addr: %pM\n", __func__, penv->wlan_nv_macAddr); break; - default: pr_err("%s: Invalid command %d\n", __func__, cmd); + rc = -EINVAL; break; } -} - -static ssize_t wcnss_ctrl_write(struct file *fp, const char __user - *user_buffer, size_t count, loff_t *position) -{ - int rc = 0; - u8 buf[WCNSS_MAX_CMD_LEN]; - - if (!penv || !penv->ctrl_device_opened || WCNSS_MAX_CMD_LEN < count - || WCNSS_MIN_CMD_LEN > count) - return -EFAULT; - - mutex_lock(&penv->ctrl_lock); - rc = copy_from_user(buf, user_buffer, count); - if (0 == rc) - process_usr_ctrl_cmd(buf, count); +exit: mutex_unlock(&penv->ctrl_lock); - return rc; } - static const struct file_operations wcnss_ctrl_fops = { .owner = THIS_MODULE, .open = wcnss_ctrl_open, -- GitLab From 2e55d7cab155777f51942df8edeebf8c95b4066c Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 4 Dec 2017 19:08:18 +0530 Subject: [PATCH 1859/5498] soc: qcom: bgrsb: Declares the bg and rsb works at probe Avoids the crash if client tries to de-reference the existing work. Change-Id: Ifbaeeded77464a7437b3395be10e10d15eaa647d Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 129 ++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index 7402ba475a54..4105f372ca2f 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -66,7 +66,7 @@ enum bgrsb_state { BGRSB_STATE_LDO11_ENABLED, BGRSB_STATE_RSB_CONFIGURED, BGRSB_STATE_LDO15_ENABLED, - BGRSB_STATE_RSB_EBNABLED + BGRSB_STATE_RSB_ENABLED }; struct bgrsb_msg { @@ -85,7 +85,12 @@ struct bgrsb_priv { bool chnl_state; void *lhndl; - struct work_struct bg_work; + struct work_struct bg_up_work; + struct work_struct bg_down_work; + + struct work_struct rsb_up_work; + struct work_struct rsb_down_work; + struct work_struct glink_work; struct workqueue_struct *bgrsb_event_wq; @@ -333,38 +338,6 @@ static int bgrsb_init_regulators(struct device *pdev) return 0; } -static int bgrsb_init(struct bgrsb_priv *dev) -{ - bgrsb_drv = &dev->lhndl; - dev->chnl.chnl_name = "RSB_CTRL"; - dev->chnl.chnl_edge = "bg"; - dev->chnl.chnl_trnsprt = "bgcom"; - mutex_init(&dev->glink_mutex); - dev->link_state = GLINK_LINK_STATE_DOWN; - - dev->ldo_action = BGRSB_NO_ACTION; - - dev->bgrsb_event_wq = - create_singlethread_workqueue(dev->chnl.chnl_name); - if (!dev->bgrsb_event_wq) { - pr_err("Failed to init Glink work-queue\n"); - return -EFAULT; - } - - dev->bgrsb_wq = - create_singlethread_workqueue("bg-work-queue"); - if (!dev->bgrsb_wq) { - pr_err("Failed to init BG-RSB work-queue\n"); - return -EFAULT; - } - - init_waitqueue_head(&dev->link_state_wait); - - /* set default bgrsb state */ - dev->bgrsb_current_state = BGRSB_STATE_INIT; - return 0; -} - static int bgrsb_ldo_work(struct bgrsb_priv *dev, enum ldo_task ldo_action) { int ret = 0; @@ -423,7 +396,8 @@ err_ret: static void bgrsb_bgdown_work(struct work_struct *work) { - struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, + bg_down_work); bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15); bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11); @@ -433,7 +407,8 @@ static void bgrsb_bgdown_work(struct work_struct *work) static void bgrsb_bgup_work(struct work_struct *work) { int rc = 0; - struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, + bg_up_work); if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) { @@ -452,7 +427,7 @@ static void bgrsb_bgup_work(struct work_struct *work) return; } dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; - pr_debug("BGRSB_STATE_RSB_CONFIGURED\n"); + pr_debug("RSB Cofigured\n"); } } @@ -469,14 +444,11 @@ static int ssr_bgrsb_cb(struct notifier_block *this, switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: - INIT_WORK(&dev->bg_work, bgrsb_bgdown_work); - queue_work(dev->bgrsb_wq, &dev->bg_work); + queue_work(dev->bgrsb_wq, &dev->bg_down_work); break; case SUBSYS_AFTER_POWERUP: - if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { - INIT_WORK(&dev->bg_work, bgrsb_bgup_work); - queue_work(dev->bgrsb_wq, &dev->bg_work); - } + if (dev->bgrsb_current_state == BGRSB_STATE_INIT) + queue_work(dev->bgrsb_wq, &dev->bg_up_work); break; } return NOTIFY_DONE; @@ -545,7 +517,8 @@ static void bgrsb_enable_rsb(struct work_struct *work) { int rc = 0; struct bgrsb_msg req = {0}; - struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, + rsb_up_work); if (dev->bgrsb_current_state != BGRSB_STATE_RSB_CONFIGURED) { pr_err("BG is not yet configured for RSB\n"); @@ -565,17 +538,18 @@ static void bgrsb_enable_rsb(struct work_struct *work) return; } } - dev->bgrsb_current_state = BGRSB_STATE_RSB_EBNABLED; - pr_debug("BGRSB_STATE_RSB_EBNABLED\n"); + dev->bgrsb_current_state = BGRSB_STATE_RSB_ENABLED; + pr_debug("RSB Enabled\n"); } static void bgrsb_disable_rsb(struct work_struct *work) { int rc = 0; struct bgrsb_msg req = {0}; - struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_work); + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, + rsb_down_work); - if (dev->bgrsb_current_state == BGRSB_STATE_RSB_EBNABLED) { + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) return; @@ -588,7 +562,7 @@ static void bgrsb_disable_rsb(struct work_struct *work) return; } dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; - pr_debug("BGRSB_STATE_RSB_CONFIGURED\n"); + pr_debug("RSB Disabled\n"); } } @@ -604,13 +578,13 @@ static int store_enable(struct device *pdev, struct device_attribute *attr, return ret; if (pwr_st == BGRSB_POWER_ENABLE) { - if (dev->bgrsb_current_state == BGRSB_STATE_RSB_EBNABLED) + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) return 0; - INIT_WORK(&dev->bg_work, bgrsb_enable_rsb); - queue_work(dev->bgrsb_wq, &dev->bg_work); + queue_work(dev->bgrsb_wq, &dev->rsb_up_work); } else if (pwr_st == BGRSB_POWER_DISABLE) { - INIT_WORK(&dev->bg_work, bgrsb_disable_rsb); - queue_work(dev->bgrsb_wq, &dev->bg_work); + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) + return 0; + queue_work(dev->bgrsb_wq, &dev->rsb_down_work); } return 0; } @@ -630,6 +604,50 @@ static struct device_attribute dev_attr_rsb = { .store = store_enable, }; +static int bgrsb_init(struct bgrsb_priv *dev) +{ + bgrsb_drv = &dev->lhndl; + dev->chnl.chnl_name = "RSB_CTRL"; + dev->chnl.chnl_edge = "bg"; + dev->chnl.chnl_trnsprt = "bgcom"; + mutex_init(&dev->glink_mutex); + dev->link_state = GLINK_LINK_STATE_DOWN; + + dev->ldo_action = BGRSB_NO_ACTION; + + dev->bgrsb_event_wq = + create_singlethread_workqueue(dev->chnl.chnl_name); + if (!dev->bgrsb_event_wq) { + pr_err("Failed to init Glink work-queue\n"); + goto err_ret; + } + + dev->bgrsb_wq = + create_singlethread_workqueue("bg-work-queue"); + if (!dev->bgrsb_wq) { + pr_err("Failed to init BG-RSB work-queue\n"); + goto free_rsb_wq; + } + + init_waitqueue_head(&dev->link_state_wait); + + /* set default bgrsb state */ + dev->bgrsb_current_state = BGRSB_STATE_INIT; + + /* Init all works */ + INIT_WORK(&dev->bg_up_work, bgrsb_bgup_work); + INIT_WORK(&dev->bg_down_work, bgrsb_bgdown_work); + INIT_WORK(&dev->rsb_up_work, bgrsb_enable_rsb); + INIT_WORK(&dev->rsb_down_work, bgrsb_disable_rsb); + + return 0; + +free_rsb_wq: + destroy_workqueue(dev->bgrsb_event_wq); +err_ret: + return -EFAULT; +} + static int bg_rsb_probe(struct platform_device *pdev) { struct bgrsb_priv *dev; @@ -695,7 +713,10 @@ static int bg_rsb_remove(struct platform_device *pdev) { struct bgrsb_priv *dev = platform_get_drvdata(pdev); + destroy_workqueue(dev->bgrsb_event_wq); + destroy_workqueue(dev->bgrsb_wq); input_free_device(dev->input); + return 0; } -- GitLab From 9bd664cc370b8d0b6654a531cb91d106734e4444 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Mon, 13 Nov 2017 23:43:27 +0530 Subject: [PATCH 1860/5498] msm: ipa: Fix to race condition proxy clock voting Added code changes to fix the race condition during proxy clock voating in IPA. Change-Id: I1fee17836bf58d25f461643ea1e60bcd06182372 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v3/ipa.c | 2 ++ drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 1 + drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 8 ++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index dd7cb81428d2..aae19811f662 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4502,6 +4502,7 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, ipa3_register_panic_hdlr(); ipa3_ctx->q6_proxy_clk_vote_valid = true; + ipa3_ctx->q6_proxy_clk_vote_cnt++; mutex_lock(&ipa3_ctx->lock); ipa3_ctx->ipa_initialization_complete = true; @@ -5138,6 +5139,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, mutex_init(&ipa3_ctx->nat_mem.lock); mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex); mutex_init(&ipa3_ctx->ipa_cne_evt_lock); + ipa3_ctx->q6_proxy_clk_vote_cnt = 0; idr_init(&ipa3_ctx->ipa_idr); spin_lock_init(&ipa3_ctx->idr_lock); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 7425442fe2b9..cd1fd6d5651a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1299,6 +1299,7 @@ struct ipa3_context { u32 curr_ipa_clk_rate; bool q6_proxy_clk_vote_valid; struct mutex q6_proxy_clk_vote_mutex; + u32 q6_proxy_clk_vote_cnt; u32 ipa_num_pipes; dma_addr_t pkt_init_imm[IPA3_MAX_NUM_PIPES]; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 98405e447a00..b471e4da984e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -5757,7 +5757,9 @@ void ipa3_proxy_clk_unvote(void) mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex); if (ipa3_ctx->q6_proxy_clk_vote_valid) { IPA_ACTIVE_CLIENTS_DEC_SPECIAL("PROXY_CLK_VOTE"); - ipa3_ctx->q6_proxy_clk_vote_valid = false; + ipa3_ctx->q6_proxy_clk_vote_cnt--; + if (ipa3_ctx->q6_proxy_clk_vote_cnt == 0) + ipa3_ctx->q6_proxy_clk_vote_valid = false; } mutex_unlock(&ipa3_ctx->q6_proxy_clk_vote_mutex); } @@ -5773,8 +5775,10 @@ void ipa3_proxy_clk_vote(void) return; mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex); - if (!ipa3_ctx->q6_proxy_clk_vote_valid) { + if (!ipa3_ctx->q6_proxy_clk_vote_valid || + (ipa3_ctx->q6_proxy_clk_vote_cnt > 0)) { IPA_ACTIVE_CLIENTS_INC_SPECIAL("PROXY_CLK_VOTE"); + ipa3_ctx->q6_proxy_clk_vote_cnt++; ipa3_ctx->q6_proxy_clk_vote_valid = true; } mutex_unlock(&ipa3_ctx->q6_proxy_clk_vote_mutex); -- GitLab From 6527e423d4297a007ba5c23fcc566b0a56d90108 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Tue, 21 Nov 2017 13:44:45 +0530 Subject: [PATCH 1861/5498] fs: f2fs: fix compilation issue Fix compilation issue observed on 3.18 due to redefinition of function get_data_block_bmap(). Change-Id: I892814566d6c279e5e96cc2eea0c32ae4e4cab9f Signed-off-by: Sahitya Tummala --- fs/f2fs/data.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a9ac9b39b793..6d467fdaf900 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1301,15 +1301,6 @@ next_page: return 0; } -static int get_data_block_bmap(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) -{ - /* Block number less than F2FS MAX BLOCKS */ - if (unlikely(iblock >= max_file_size(0))) - return -EFBIG; - return get_data_block_ro(inode, iblock, bh_result, create); -} - static int f2fs_read_data_page(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; -- GitLab From fa1da3db9b412c258c0bcb8fc24c247609d72ae8 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Tue, 21 Nov 2017 14:02:25 +0530 Subject: [PATCH 1862/5498] defconfig: Enable F2FS for 8909 Enable F2FS related config options for MSM8909. Change-Id: I54f2fcc46d067f7f1608dd8c717b9c45b353a4cb Signed-off-by: Sahitya Tummala --- arch/arm/configs/msm8909-perf_defconfig | 2 ++ arch/arm/configs/msm8909_defconfig | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index 0bc996cf9c84..2145ae804be0 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -509,6 +509,8 @@ CONFIG_TMPFS=y CONFIG_SDCARD_FS=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index 46573429be51..6e2724ac222b 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -525,6 +525,8 @@ CONFIG_TMPFS=y CONFIG_SDCARD_FS=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y -- GitLab From acf3765a1ddf79989c7c4b1ff5d362331cb49c5b Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Wed, 6 Dec 2017 08:52:36 +0530 Subject: [PATCH 1863/5498] defconfig: msm8937: Disable unnecessary configs Disable unnecessary configs to reduce memory foot print. Change-Id: Iafd3af46a1a42c82274d014a7e6bce8e562fabda Signed-off-by: Sahitya Tummala --- arch/arm/configs/msm8937-perf_defconfig | 4 ---- arch/arm/configs/msm8937_defconfig | 4 ---- 2 files changed, 8 deletions(-) diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig index 6f5fa17edaac..8efa7aeeb2d8 100644 --- a/arch/arm/configs/msm8937-perf_defconfig +++ b/arch/arm/configs/msm8937-perf_defconfig @@ -593,10 +593,6 @@ CONFIG_CORESIGHT_STM=y CONFIG_CORESIGHT_HWEVENT=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig index e68e6158b610..fcbacbffb912 100644 --- a/arch/arm/configs/msm8937_defconfig +++ b/arch/arm/configs/msm8937_defconfig @@ -609,10 +609,6 @@ CONFIG_CORESIGHT_REMOTE_ETM=y CONFIG_CORESIGHT_QPDI=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_QUOTA=y -- GitLab From c9af548d754bc12c9b0c5f0fd4f821a22a7863e2 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Wed, 6 Dec 2017 20:18:24 +0530 Subject: [PATCH 1864/5498] soc: qcom: bgcom: Add suspend/resume routines Send OK_TO_SLEEP command to BG to let BG SPI go to deep sleep and read the status register to wakes up BG. Change-Id: I1941873b5161b629c55c309efe527bfd9b5a40b7 Signed-off-by: Arjun Singh --- drivers/soc/qcom/bgcom.h | 4 +-- drivers/soc/qcom/bgcom_spi.c | 64 ++++++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/drivers/soc/qcom/bgcom.h b/drivers/soc/qcom/bgcom.h index 8e1dcceb4594..1c2c3b9074fc 100644 --- a/drivers/soc/qcom/bgcom.h +++ b/drivers/soc/qcom/bgcom.h @@ -197,14 +197,14 @@ int bgcom_ahb_write(void *handle, uint32_t ahb_start_addr, * @handle: BGCOM handle associated with the channel * Return 0 on success or -Ve on error */ -int bgcom_suspend(void *handle); +int bgcom_suspend(void **handle); /** * bgcom_resume() - Resumes the channel. * @handle: BGCOM handle associated with the channel * Return 0 on success or -Ve on error */ -int bgcom_resume(void *handle); +int bgcom_resume(void **handle); int bgcom_set_spi_state(enum bgcom_spi_state state); diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c index 010e01944c2d..bc9b845bf7c5 100644 --- a/drivers/soc/qcom/bgcom_spi.c +++ b/drivers/soc/qcom/bgcom_spi.c @@ -38,6 +38,7 @@ #define BG_SPI_AHB_CMD_LEN (0x05) #define BG_SPI_AHB_READ_CMD_LEN (0x08) #define BG_STATUS_REG (0x05) +#define BG_CMND_REG (0x14) #define BG_SPI_MAX_WORDS (0x3FFFFFFD) #define BG_SPI_MAX_REGS (0x0A) @@ -46,10 +47,14 @@ #define HED_EVENT_SIZE_LEN (0x02) #define HED_EVENT_DATA_STRT_LEN (0x05) +#define MAX_RETRY 3 + enum bgcom_state { /*BGCOM Staus ready*/ BGCOM_PROB_SUCCESS = 0, BGCOM_PROB_WAIT = 1, + BGCOM_STATE_SUSPEND = 2, + BGCOM_STATE_ACTIVE = 3 }; enum bgcom_req_type { @@ -68,6 +73,8 @@ struct bg_spi_priv { struct spi_message msg1; struct spi_transfer xfer1; int irq_lock; + + enum bgcom_state bg_state; }; struct cb_data { @@ -154,7 +161,7 @@ void add_to_irq_list(struct cb_data *data) static bool is_bgcom_ready(void) { - return bg_com_drv ? true : false; + return (bg_com_drv != NULL ? true : false); } static void bg_spi_reinit_xfer(struct spi_transfer *xfer) @@ -208,6 +215,8 @@ static int bgcom_transfer(void *handle, uint8_t *tx_buf, cntx = (struct bg_context *)handle; if (cntx->state == BGCOM_PROB_WAIT) { + if (!is_bgcom_ready()) + return -ENODEV; cntx->bg_spi = container_of(bg_com_drv, struct bg_spi_priv, lhandle); cntx->state = BGCOM_PROB_SUCCESS; @@ -708,16 +717,56 @@ int bgcom_reg_read(void *handle, uint8_t reg_start_addr, } EXPORT_SYMBOL(bgcom_reg_read); -int bgcom_resume(void *handle) +int bgcom_resume(void **handle) { - return handle ? 0 : -EINVAL; + struct bg_spi_priv *bg_spi; + struct bg_context *cntx; + uint32_t cmnd_reg = 0; + int retry = 0; + + if (*handle == NULL) + return -EINVAL; + + cntx = *handle; + bg_spi = cntx->bg_spi; + + if (bg_spi->bg_state == BGCOM_STATE_ACTIVE) + return 0; + + do { + bgcom_reg_read(*handle, BG_STATUS_REG, 1, &cmnd_reg); + if (cmnd_reg & BIT(31)) { + bg_spi->bg_state = BGCOM_STATE_ACTIVE; + break; + } + udelay(10); + ++retry; + } while (retry < MAX_RETRY); + + return (retry == MAX_RETRY ? -ETIMEDOUT : 0); } EXPORT_SYMBOL(bgcom_resume); -int bgcom_suspend(void *handle) +int bgcom_suspend(void **handle) { - return handle ? 0 : -EINVAL; + int ret; + struct bg_spi_priv *bg_spi; + struct bg_context *cntx; + uint32_t cmnd_reg = 0; + + if (*handle == NULL) + return -EINVAL; + + cntx = *handle; + bg_spi = cntx->bg_spi; + if (bg_spi->bg_state == BGCOM_STATE_SUSPEND) + return 0; + cmnd_reg |= BIT(31); + ret = bgcom_reg_write(*handle, BG_CMND_REG, 1, &cmnd_reg); + if (ret == 0) + bg_spi->bg_state = BGCOM_STATE_SUSPEND; + return ret; } EXPORT_SYMBOL(bgcom_suspend); @@ -812,12 +861,15 @@ static void bg_spi_init(struct bg_spi_priv *bg_spi) spi_message_add_tail(&bg_spi->xfer1, &bg_spi->msg1); /* BGCOM IRQ set-up */ - bg_com_drv = &bg_spi->lhandle; bg_spi->irq_lock = 0; spi_state = BGCOM_SPI_FREE; wq = create_singlethread_workqueue("input_wq"); + + bg_spi->bg_state = BGCOM_STATE_ACTIVE; + + bg_com_drv = &bg_spi->lhandle; } static int bg_spi_probe(struct spi_device *spi) -- GitLab From db03b1e0bb00551364e58b27732e8c9e83834e0a Mon Sep 17 00:00:00 2001 From: Rajesha Kini Date: Mon, 30 Oct 2017 20:54:25 +0530 Subject: [PATCH 1865/5498] ARM: dts: msm: Update Neutrino device with timestamp window for mdm9607 Updated to support timestamp valid window feature in Neutrino for mdm9607 to synchronize with gptp time. Disable this feature by default. CRs-Fixed: 2055589 Change-Id: I33693140072620d84fa918a67225d366c584c1cf Signed-off-by: Rajesha Kini --- Documentation/devicetree/bindings/net/neutrino_hsic.txt | 8 ++++++++ arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi | 2 ++ 2 files changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/net/neutrino_hsic.txt b/Documentation/devicetree/bindings/net/neutrino_hsic.txt index f9f775bb83b1..ea0978e7ce20 100644 --- a/Documentation/devicetree/bindings/net/neutrino_hsic.txt +++ b/Documentation/devicetree/bindings/net/neutrino_hsic.txt @@ -7,6 +7,12 @@ Required properties: - vdd-ntn-hsic-supply: Neutrino HSIC power supply - ntn-rst-gpio: Neutrino reset GPIO - ntn-phy-id: Neutrino PHY ID +Optional properties: + - ntn-timestamp-valid-window: Timestamp valid window in ms. + Each unit represents 16.75ms. + - ntn-timestamp-valid-window-disable: Disable Timestamp valid window feature. + if this property not set, then feature disabled + in driver. Example: qcom,ntn_hsic { @@ -16,4 +22,6 @@ Example: pinctrl-0 = <&ntn_rst_gpio_default>; ntn-rst-gpio = <&tlmm_pinmux 30 1>; ntn-phy-id = <7>; + ntn-timestamp-valid-window = <4>; + ntn-timestamp-valid-window-disable = <0>; }; diff --git a/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi b/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi index 89753a29d6d6..9fd54f7252dc 100644 --- a/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9607-mtp.dtsi @@ -36,6 +36,8 @@ pinctrl-0 = <&ntn_rst_gpio_default>; ntn-rst-gpio = <&tlmm_pinmux 30 1>; ntn-phy-id = <7>; + ntn-timestamp-valid-window = <4>; /* Each unit is 16.75ms */ + ntn-timestamp-valid-window-disable = <1>; /* 1: To Disable */ }; }; -- GitLab From f766cc34b6dd69f6cf3e8186b7dfafdaa234ae58 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Fri, 13 Oct 2017 13:51:21 +0530 Subject: [PATCH 1866/5498] msm: ipa3: using rate limit api for ioctl calls ipav3 ioctl calls input arguments validation failed cases, replacing IPAERR()/IPAHAL_ERR() with IPAERR_RL()/IPAHAL_ERR_RL() marco for controlling logs printing on the console. Change-Id: I0b48b4a2f8b37c71aebe82c7b747b1d8f6c1e143 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_common_i.h | 37 ++++++++++++++++++ drivers/platform/msm/ipa/ipa_v3/ipa_flt.c | 30 +++++++-------- drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c | 18 ++++----- drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 2 +- drivers/platform/msm/ipa/ipa_v3/ipa_intf.c | 2 +- drivers/platform/msm/ipa/ipa_v3/ipa_rt.c | 44 +++++++++++----------- 6 files changed, 85 insertions(+), 48 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h index f4163bcfeab1..609c452279a1 100644 --- a/drivers/platform/msm/ipa/ipa_common_i.h +++ b/drivers/platform/msm/ipa/ipa_common_i.h @@ -19,6 +19,10 @@ #include #include #include +#include + +#define WARNON_RATELIMIT_BURST 1 +#define IPA_RATELIMIT_BURST 1 #define __FILENAME__ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) @@ -104,6 +108,39 @@ ipa_dec_client_disable_clks(&log_info); \ } while (0) +/* + * Printing one warning message in 5 seconds if multiple warning messages + * are coming back to back. + */ + +#define WARN_ON_RATELIMIT_IPA(condition) \ +({ \ + static DEFINE_RATELIMIT_STATE(_rs, \ + DEFAULT_RATELIMIT_INTERVAL, \ + WARNON_RATELIMIT_BURST); \ + int rtn = !!(condition); \ + \ + if (unlikely(rtn && __ratelimit(&_rs))) \ + WARN_ON(rtn); \ +}) + +/* + * Printing one error message in 5 seconds if multiple error messages + * are coming back to back. + */ + +#define pr_err_ratelimited_ipa(fmt, ...) \ + printk_ratelimited_ipa(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) +#define printk_ratelimited_ipa(fmt, ...) \ +({ \ + static DEFINE_RATELIMIT_STATE(_rs, \ + DEFAULT_RATELIMIT_INTERVAL, \ + IPA_RATELIMIT_BURST); \ + \ + if (__ratelimit(&_rs)) \ + printk(fmt, ##__VA_ARGS__); \ +}) + #define ipa_assert_on(condition)\ do {\ if (unlikely(condition))\ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 3ba84463682d..0577c8713760 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -261,7 +261,7 @@ static int ipa3_generate_flt_hw_rule(enum ipa_ip_type ip, if (entry->hw_len == 0) { entry->hw_len = buf - start; } else if (entry->hw_len != (buf - start)) { - IPAERR("hw_len differs b/w passes passed=0x%x calc=0x%td\n", + IPAERR_RL("hw_len differs b/w passes passed=0x%x calc=0x%td\n", entry->hw_len, (buf - start)); return -EPERM; } @@ -634,7 +634,7 @@ static int ipa_generate_flt_hw_tbl_img(enum ipa_ip_type ip, } if (ipa_alloc_init_flt_tbl_hdr(ip, hash_hdr, nhash_hdr)) { - IPAERR("fail to alloc and init flt tbl hdr\n"); + IPAERR_RL("fail to alloc and init flt tbl hdr\n"); rc = -ENOMEM; goto no_flt_tbls; } @@ -684,13 +684,13 @@ static int ipa_generate_flt_hw_tbl_img(enum ipa_ip_type ip, if (ipa_translate_flt_tbl_to_hw_fmt(ip, IPA_RULE_HASHABLE, hash_bdy->base, hash_hdr->base, hash_bdy_start_ofst)) { - IPAERR("fail to translate hashable flt tbls to hw format\n"); + IPAERR_RL("fail to translate hashable flt tbls to hw format\n"); rc = -EPERM; goto translate_fail; } if (ipa_translate_flt_tbl_to_hw_fmt(ip, IPA_RULE_NON_HASHABLE, nhash_bdy->base, nhash_hdr->base, nhash_bdy_start_ofst)) { - IPAERR("fail to translate non-hash flt tbls to hw format\n"); + IPAERR_RL("fail to translate non-hash flt tbls to hw format\n"); rc = -EPERM; goto translate_fail; } @@ -872,7 +872,7 @@ int __ipa_commit_flt_v3(enum ipa_ip_type ip) if (ipa_generate_flt_hw_tbl_img(ip, &hash_hdr, &nhash_hdr, &hash_bdy, &nhash_bdy)) { - IPAERR("fail to generate FLT HW TBL image. IP %d\n", ip); + IPAERR_RL("fail to generate FLT HW TBL image. IP %d\n", ip); rc = -EFAULT; goto fail_gen; } @@ -1074,25 +1074,25 @@ static int __ipa_validate_flt_rule(const struct ipa_flt_rule *rule, if (rule->action != IPA_PASS_TO_EXCEPTION) { if (!rule->eq_attrib_type) { if (!rule->rt_tbl_hdl) { - IPAERR("invalid RT tbl\n"); + IPAERR_RL("invalid RT tbl\n"); goto error; } *rt_tbl = ipa3_id_find(rule->rt_tbl_hdl); if (*rt_tbl == NULL) { - IPAERR("RT tbl not found\n"); + IPAERR_RL("RT tbl not found\n"); goto error; } if ((*rt_tbl)->cookie != IPA_RT_TBL_COOKIE) { - IPAERR("RT table cookie is invalid\n"); + IPAERR_RL("RT table cookie is invalid\n"); goto error; } } else { if (rule->rt_tbl_idx > ((ip == IPA_IP_v4) ? IPA_MEM_PART(v4_modem_rt_index_hi) : IPA_MEM_PART(v6_modem_rt_index_hi))) { - IPAERR("invalid RT tbl\n"); + IPAERR_RL("invalid RT tbl\n"); goto error; } } @@ -1101,7 +1101,7 @@ static int __ipa_validate_flt_rule(const struct ipa_flt_rule *rule, if (rule->rule_id) { if (rule->rule_id >= IPA_RULE_ID_MIN_VAL && rule->rule_id <= IPA_RULE_ID_MAX_VAL) { - IPAERR("invalid rule_id provided 0x%x\n" + IPAERR_RL("invalid rule_id provided 0x%x\n" "rule_id 0x%x - 0x%x are auto generated\n", rule->rule_id, IPA_RULE_ID_MIN_VAL, @@ -1137,8 +1137,8 @@ static int __ipa_create_flt_entry(struct ipa3_flt_entry **entry, } else { id = ipa3_alloc_rule_id(&tbl->rule_ids); if (id < 0) { - IPAERR("failed to allocate rule id\n"); - WARN_ON(1); + IPAERR_RL("failed to allocate rule id\n"); + WARN_ON_RATELIMIT_IPA(1); goto rule_id_fail; } } @@ -1162,8 +1162,8 @@ static int __ipa_finish_flt_rule_add(struct ipa3_flt_tbl *tbl, entry->rt_tbl->ref_cnt++; id = ipa3_id_alloc(entry); if (id < 0) { - IPAERR("failed to add to tree\n"); - WARN_ON(1); + IPAERR_RL("failed to add to tree\n"); + WARN_ON_RATELIMIT_IPA(1); goto ipa_insert_failed; } *rule_hdl = id; @@ -1702,7 +1702,7 @@ int ipa3_reset_flt(enum ipa_ip_type ip) list_for_each_entry_safe(entry, next, &tbl->head_flt_rule_list, link) { if (ipa3_id_find(entry->id) == NULL) { - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); mutex_unlock(&ipa3_ctx->lock); return -EFAULT; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index f20f75dbda60..95a377759dfe 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -354,7 +354,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, } if (hdr_entry->cookie != IPA_HDR_COOKIE) { IPAERR_RL("Invalid header cookie %u\n", hdr_entry->cookie); - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); return -EINVAL; } IPADBG("Associated header is name=%s is_hdr_proc_ctx=%d\n", @@ -384,7 +384,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, bin = IPA_HDR_PROC_CTX_BIN1; } else { IPAERR_RL("unexpected needed len %d\n", needed_len); - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); goto bad_len; } @@ -429,8 +429,8 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, id = ipa3_id_alloc(entry); if (id < 0) { - IPAERR("failed to alloc id\n"); - WARN_ON(1); + IPAERR_RL("failed to alloc id\n"); + WARN_ON_RATELIMIT_IPA(1); goto ipa_insert_failed; } entry->id = id; @@ -566,8 +566,8 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) id = ipa3_id_alloc(entry); if (id < 0) { - IPAERR("failed to alloc id\n"); - WARN_ON(1); + IPAERR_RL("failed to alloc id\n"); + WARN_ON_RATELIMIT_IPA(1); goto ipa_insert_failed; } entry->id = id; @@ -995,7 +995,7 @@ int ipa3_reset_hdr(void) if (entry->is_hdr_proc_ctx) { IPAERR("default header is proc ctx\n"); mutex_unlock(&ipa3_ctx->lock); - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); return -EFAULT; } continue; @@ -1003,7 +1003,7 @@ int ipa3_reset_hdr(void) if (ipa3_id_find(entry->id) == NULL) { mutex_unlock(&ipa3_ctx->lock); - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); return -EFAULT; } if (entry->is_hdr_proc_ctx) { @@ -1057,7 +1057,7 @@ int ipa3_reset_hdr(void) if (ipa3_id_find(ctx_entry->id) == NULL) { mutex_unlock(&ipa3_ctx->lock); - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); return -EFAULT; } list_del(&ctx_entry->link); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 7425442fe2b9..b2cccaa58cf1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -100,7 +100,7 @@ #define IPAERR_RL(fmt, args...) \ do { \ - pr_err_ratelimited(DRV_NAME " %s:%d " fmt, __func__,\ + pr_err_ratelimited_ipa(DRV_NAME " %s:%d " fmt, __func__,\ __LINE__, ## args);\ if (ipa3_ctx) { \ IPA_IPC_LOGGING(ipa3_ctx->logbuf, \ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c index 7d3a7966ef2e..1b2648c4a52b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c @@ -221,7 +221,7 @@ int ipa3_query_intf(struct ipa_ioc_query_intf *lookup) int result = -EINVAL; if (lookup == NULL) { - IPAERR("invalid param lookup=%p\n", lookup); + IPAERR_RL("invalid param lookup=%p\n", lookup); return result; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 0072a98bbd6c..8a62defb81e2 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -68,13 +68,13 @@ int __ipa_generate_rt_hw_rule_v3_0(enum ipa_ip_type ip, rule_hdr = (struct ipa3_rt_rule_hw_hdr *)buf; pipe_idx = ipa3_get_ep_mapping(entry->rule.dst); if (pipe_idx == -1) { - IPAERR("Wrong destination pipe specified in RT rule\n"); + IPAERR_RL("Wrong destination pipe specified in RT rule\n"); WARN_ON(1); return -EPERM; } if (!IPA_CLIENT_IS_CONS(entry->rule.dst)) { - IPAERR("No RT rule on IPA_client_producer pipe.\n"); - IPAERR("pipe_idx: %d dst_pipe: %d\n", + IPAERR_RL("No RT rule on IPA_client_producer pipe.\n"); + IPAERR_RL("pipe_idx: %d dst_pipe: %d\n", pipe_idx, entry->rule.dst); WARN_ON(1); return -EPERM; @@ -177,7 +177,7 @@ static int ipa_translate_rt_tbl_to_hw_fmt(enum ipa_ip_type ip, dma_alloc_coherent(ipa3_ctx->pdev, tbl_mem.size, &tbl_mem.phys_base, GFP_KERNEL); if (!tbl_mem.base) { - IPAERR("fail to alloc DMA buf of size %d\n", + IPAERR_RL("fail to alloc DMA buf of size %d\n", tbl_mem.size); goto err; } @@ -204,7 +204,7 @@ static int ipa_translate_rt_tbl_to_hw_fmt(enum ipa_ip_type ip, entry, tbl_mem_buf); if (res) { - IPAERR("failed to gen HW RT rule\n"); + IPAERR_RL("failed to gen HW RT rule\n"); goto align_err; } tbl_mem_buf += entry->hw_len; @@ -241,7 +241,7 @@ static int ipa_translate_rt_tbl_to_hw_fmt(enum ipa_ip_type ip, entry, body_i); if (res) { - IPAERR("failed to gen HW RT rule\n"); + IPAERR_RL("failed to gen HW RT rule\n"); goto err; } body_i += entry->hw_len; @@ -404,7 +404,7 @@ static int ipa_prep_rt_tbl_for_cmt(enum ipa_ip_type ip, entry, NULL); if (res) { - IPAERR("failed to calculate HW RT rule size\n"); + IPAERR_RL("failed to calculate HW RT rule size\n"); return -EPERM; } @@ -419,8 +419,8 @@ static int ipa_prep_rt_tbl_for_cmt(enum ipa_ip_type ip, if ((tbl->sz[IPA_RULE_HASHABLE] + tbl->sz[IPA_RULE_NON_HASHABLE]) == 0) { - WARN_ON(1); - IPAERR("rt tbl %s is with zero total size\n", tbl->name); + WARN_ON_RATELIMIT_IPA(1); + IPAERR_RL("rt tbl %s is with zero total size\n", tbl->name); } if (tbl->sz[IPA_RULE_HASHABLE]) @@ -994,8 +994,8 @@ static struct ipa3_rt_tbl *__ipa_add_rt_tbl(enum ipa_ip_type ip, id = ipa3_id_alloc(entry); if (id < 0) { - IPAERR("failed to add to tree\n"); - WARN_ON(1); + IPAERR_RL("failed to add to tree\n"); + WARN_ON_RATELIMIT_IPA(1); goto ipa_insert_failed; } entry->id = id; @@ -1034,7 +1034,7 @@ static int __ipa_del_rt_tbl(struct ipa3_rt_tbl *entry) else if (entry->set == &ipa3_ctx->rt_tbl_set[IPA_IP_v6]) ip = IPA_IP_v6; else { - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); return -EPERM; } @@ -1067,14 +1067,14 @@ static int __ipa_rt_validate_hndls(const struct ipa_rt_rule *rule, struct ipa3_hdr_proc_ctx_entry **proc_ctx) { if (rule->hdr_hdl && rule->hdr_proc_ctx_hdl) { - IPAERR("rule contains both hdr_hdl and hdr_proc_ctx_hdl\n"); + IPAERR_RL("rule contains both hdr_hdl and hdr_proc_ctx_hdl\n"); return -EPERM; } if (rule->hdr_hdl) { *hdr = ipa3_id_find(rule->hdr_hdl); if ((*hdr == NULL) || ((*hdr)->cookie != IPA_HDR_COOKIE)) { - IPAERR("rt rule does not point to valid hdr\n"); + IPAERR_RL("rt rule does not point to valid hdr\n"); return -EPERM; } } else if (rule->hdr_proc_ctx_hdl) { @@ -1082,7 +1082,7 @@ static int __ipa_rt_validate_hndls(const struct ipa_rt_rule *rule, if ((*proc_ctx == NULL) || ((*proc_ctx)->cookie != IPA_PROC_HDR_COOKIE)) { - IPAERR("rt rule does not point to valid proc ctx\n"); + IPAERR_RL("rt rule does not point to valid proc ctx\n"); return -EPERM; } } @@ -1115,8 +1115,8 @@ static int __ipa_create_rt_entry(struct ipa3_rt_entry **entry, } else { id = ipa3_alloc_rule_id(&tbl->rule_ids); if (id < 0) { - IPAERR("failed to allocate rule id\n"); - WARN_ON(1); + IPAERR_RL("failed to allocate rule id\n"); + WARN_ON_RATELIMIT_IPA(1); goto alloc_rule_id_fail; } } @@ -1142,8 +1142,8 @@ static int __ipa_finish_rt_rule_add(struct ipa3_rt_entry *entry, u32 *rule_hdl, entry->proc_ctx->ref_cnt++; id = ipa3_id_alloc(entry); if (id < 0) { - IPAERR("failed to add to tree\n"); - WARN_ON(1); + IPAERR_RL("failed to add to tree\n"); + WARN_ON_RATELIMIT_IPA(1); goto ipa_insert_failed; } IPADBG("add rt rule tbl_idx=%d rule_cnt=%d rule_id=%d\n", @@ -1607,7 +1607,7 @@ int ipa3_reset_rt(enum ipa_ip_type ip) list_for_each_entry_safe(rule, rule_next, &tbl->head_rt_rule_list, link) { if (ipa3_id_find(rule->id) == NULL) { - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); mutex_unlock(&ipa3_ctx->lock); return -EFAULT; } @@ -1635,7 +1635,7 @@ int ipa3_reset_rt(enum ipa_ip_type ip) } if (ipa3_id_find(tbl->id) == NULL) { - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); mutex_unlock(&ipa3_ctx->lock); return -EFAULT; } @@ -1745,7 +1745,7 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl) else if (entry->set == &ipa3_ctx->rt_tbl_set[IPA_IP_v6]) ip = IPA_IP_v6; else { - WARN_ON(1); + WARN_ON_RATELIMIT_IPA(1); result = -EINVAL; goto ret; } -- GitLab From 9bb97b02f57f6d22336fe6dfed55c894a4a4ebb4 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 7 Dec 2017 15:28:19 +0530 Subject: [PATCH 1867/5498] usb: gadget: Mark notify_modem as NULL on gbridge_disconnect On some targets, f_serial switches transport path from char_bridge to TTY in DUN+SoftAP scenario. As a part of that, we call gbridge_disconnect but notify_modem callback is still marked as gbridge_notify_modem. Then on getting a SETUP req from host PC, gbridge_notify_modem is called which can lead to NULL pointer dereference because port_usb is NULL. Fix this by marking notify_modem callback as NULL on gbridge_disconnect. Change-Id: I4470030cde6f38b905f7a88e16ed9f261a8954d6 Signed-off-by: Ajay Agarwal --- drivers/usb/gadget/function/u_data_bridge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/u_data_bridge.c b/drivers/usb/gadget/function/u_data_bridge.c index cc4ae14db721..39eae3f0f0c7 100644 --- a/drivers/usb/gadget/function/u_data_bridge.c +++ b/drivers/usb/gadget/function/u_data_bridge.c @@ -993,6 +993,7 @@ void gbridge_disconnect(void *gptr, u8 portno) spin_lock_irqsave(&port->port_lock, flags); port->is_connected = false; + gser->notify_modem = NULL; port->port_usb = NULL; port->nbytes_from_host = port->nbytes_to_host = 0; port->nbytes_to_port_bridge = 0; -- GitLab From e77940589227b1564b684d30add194a50f4fa5da Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Fri, 10 Nov 2017 18:18:44 +0530 Subject: [PATCH 1868/5498] ARM: dts: msm: move TDM/BG properties to new file for MSM8909 Move TDM and BG codec poperties to new file as the node configuration is specific to BG on msm8909. CRs-Fixed: 2147847 Change-Id: If5ef9a067e52f9a36b591ae55c2be5d4f4959d90 Signed-off-by: Surendar karka --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 1 + .../boot/dts/qcom/msm8909-audio-bg_codec.dtsi | 180 ++++++++++++++++++ arch/arm/boot/dts/qcom/msm8909-mtp.dtsi | 59 ------ arch/arm/boot/dts/qcom/msm8909.dtsi | 106 ----------- arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts | 1 + arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 1 + arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 4 + 7 files changed, 187 insertions(+), 165 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 4cee9ddec353..4c5225fee3d8 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -18,6 +18,7 @@ #include "msm8909w.dtsi" #include "msm8909w-pm660-mtp.dtsi" #include "apq8009w-memory.dtsi" +#include "msm8909-audio-bg_codec.dtsi" / { model = "Qualcomm Technologies, Inc. APQ8009W-PM660 BLACKGHOST WTP"; diff --git a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi new file mode 100644 index 000000000000..84c3ad721726 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi @@ -0,0 +1,180 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + audio_codec_bg: sound { + status = "disabled"; + compatible = "qcom,msm-bg-audio-codec"; + qcom,model = "msm-bg-snd-card"; + reg = <0x7702000 0x4>, + <0x7702004 0x4>, + <0x7702008 0x4>, + <0x770200c 0x4>; + reg-names = "csr_gp_io_mux_mic_ctl", + "csr_gp_io_mux_spkr_ctl", + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel", + "csr_gp_io_lpaif_sec_pcm_sec_mode_muxsel"; + + qcom,msm-snd-card-id = <0>; + qcom,msm-ext-pa = "primary"; + qcom,tdm-audio-intf; + qcom,msm-afe-clk-ver = <1>; + asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>, + <&voice_svc>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-pcm-dsp.2", "msm-voip-dsp", + "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", + "msm-pcm-afe", "msm-lsm-client", + "msm-pcm-routing", "msm-pcm-lpa", + "msm-voice-svc"; + asoc-cpu = <&dai_pri_auxpcm>, + <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, + <&dai_mi2s3>, <&dai_mi2s5>, <&dai_mi2s6>, + <&bt_sco_rx>, <&bt_sco_tx>, <&bt_a2dp_rx>, + <&int_fm_rx>, <&int_fm_tx>, <&afe_pcm_rx>, + <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, + <&incall_music_rx>, <&incall_music_2_rx>, + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>, + <&dai_pri_tdm_rx_1>, <&dai_pri_tdm_tx_1>, + <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_tx_2>, + <&dai_pri_tdm_rx_3>, <&dai_pri_tdm_tx_3>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-mi2s.5", "msm-dai-q6-mi2s.6", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12290", "msm-dai-q6-dev.12292", + "msm-dai-q6-dev.12293", "msm-dai-q6-dev.224", + "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", + "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", + "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", + "msm-dai-q6-dev.32770", "msm-dai-q6-tdm.36864", + "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36866", + "msm-dai-q6-tdm.36867", "msm-dai-q6-tdm.36868", + "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36870", + "msm-dai-q6-tdm.36871"; + asoc-codec = <&stub_codec>; + asoc-codec-names = "msm-stub-codec.1"; + }; + + pri_tdm_rx: qcom,msm-dai-tdm-pri-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37120>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>; + qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-afe-ebit-unsupported; + qcom,msm-cpudai-tdm-sec-port-enable; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>; + pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>; + dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36864>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36866>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36868>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36870>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + pri_tdm_tx: qcom,msm-dai-tdm-pri-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37121>; + qcom,msm-cpudai-tdm-group-num-ports = <4>; + qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>; + qcom,msm-cpudai-tdm-clk-rate = <0>; + qcom,msm-cpudai-tdm-afe-ebit-unsupported; + qcom,msm-cpudai-tdm-sec-port-enable; + qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>; + pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>; + dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36865>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36867>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36869>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36871>; + qcom,msm-cpudai-tdm-sync-mode = <0>; + qcom,msm-cpudai-tdm-sync-src = <0>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <0>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi index 32aff172e74d..3e8f6153d484 100644 --- a/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi @@ -294,65 +294,6 @@ qcom,msm-glink-channels = <4>; }; }; - - audio_codec_bg: sound { - status = "disabled"; - compatible = "qcom,msm-bg-audio-codec"; - qcom,model = "msm-bg-snd-card"; - reg = <0x7702000 0x4>, - <0x7702004 0x4>, - <0x7702008 0x4>, - <0x770200c 0x4>; - reg-names = "csr_gp_io_mux_mic_ctl", - "csr_gp_io_mux_spkr_ctl", - "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel", - "csr_gp_io_lpaif_sec_pcm_sec_mode_muxsel"; - - qcom,msm-snd-card-id = <0>; - qcom,msm-ext-pa = "primary"; - qcom,tdm-audio-intf; - qcom,msm-afe-clk-ver = <1>; - asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, - <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&lpa>, - <&voice_svc>; - asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", - "msm-pcm-dsp.2", "msm-voip-dsp", - "msm-pcm-voice", "msm-pcm-loopback", - "msm-compress-dsp", "msm-pcm-hostless", - "msm-pcm-afe", "msm-lsm-client", - "msm-pcm-routing", "msm-pcm-lpa", - "msm-voice-svc"; - asoc-cpu = <&dai_pri_auxpcm>, - <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, - <&dai_mi2s3>, <&dai_mi2s5>, <&dai_mi2s6>, - <&bt_sco_rx>, <&bt_sco_tx>, <&bt_a2dp_rx>, - <&int_fm_rx>, <&int_fm_tx>, <&afe_pcm_rx>, - <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, - <&incall_record_rx>, <&incall_record_tx>, - <&incall_music_rx>, <&incall_music_2_rx>, - <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>, - <&dai_pri_tdm_rx_1>, <&dai_pri_tdm_tx_1>, - <&dai_pri_tdm_rx_2>, <&dai_pri_tdm_tx_2>, - <&dai_pri_tdm_rx_3>, <&dai_pri_tdm_tx_3>; - asoc-cpu-names = "msm-dai-q6-auxpcm.1", - "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", - "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", - "msm-dai-q6-mi2s.5", "msm-dai-q6-mi2s.6", - "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", - "msm-dai-q6-dev.12290", "msm-dai-q6-dev.12292", - "msm-dai-q6-dev.12293", "msm-dai-q6-dev.224", - "msm-dai-q6-dev.225", "msm-dai-q6-dev.241", - "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", - "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", - "msm-dai-q6-dev.32770", "msm-dai-q6-tdm.36864", - "msm-dai-q6-tdm.36865", "msm-dai-q6-tdm.36866", - "msm-dai-q6-tdm.36867", "msm-dai-q6-tdm.36868", - "msm-dai-q6-tdm.36869", "msm-dai-q6-tdm.36870", - "msm-dai-q6-tdm.36871"; - asoc-codec = <&stub_codec>; - asoc-codec-names = "msm-stub-codec.1"; - }; }; &blsp1_uart1 { diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index a6f0e9243aa4..60dce8f7f7c7 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -1787,112 +1787,6 @@ qcom,clk-div = <27>; }; - pri_tdm_rx: qcom,msm-dai-tdm-pri-rx { - compatible = "qcom,msm-dai-tdm"; - qcom,msm-cpudai-tdm-group-id = <37120>; - qcom,msm-cpudai-tdm-group-num-ports = <4>; - qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>; - qcom,msm-cpudai-tdm-clk-rate = <0>; - qcom,msm-cpudai-tdm-afe-ebit-unsupported; - qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>; - pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>; - dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36864>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36866>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36868>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36870>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - }; - - pri_tdm_tx: qcom,msm-dai-tdm-pri-tx { - compatible = "qcom,msm-dai-tdm"; - qcom,msm-cpudai-tdm-group-id = <37121>; - qcom,msm-cpudai-tdm-group-num-ports = <4>; - qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>; - qcom,msm-cpudai-tdm-clk-rate = <0>; - qcom,msm-cpudai-tdm-afe-ebit-unsupported; - qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&quat_mi2s_active &quat_mi2s_din_active>; - pinctrl-1 = <&quat_mi2s_sleep &quat_mi2s_din_sleep>; - dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36865>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36867>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36869>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 { - compatible = "qcom,msm-dai-q6-tdm"; - qcom,msm-cpudai-tdm-dev-id = <36871>; - qcom,msm-cpudai-tdm-sync-mode = <0>; - qcom,msm-cpudai-tdm-sync-src = <0>; - qcom,msm-cpudai-tdm-data-out = <0>; - qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; - qcom,msm-cpudai-tdm-data-align = <0>; - }; - }; - qcom,memshare { compatible = "qcom,memshare"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts index 2b4f45023862..303120c72155 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts @@ -19,6 +19,7 @@ #include "msm8909-pm8916-mtp.dtsi" #include "msm8909w.dtsi" #include "msm8909w-memory.dtsi" +#include "msm8909-audio-bg_codec.dtsi" / { model = "Qualcomm Technologies, Inc. MSM8909W-PM8916 BLACKGHOST WTP"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 53fdc56877b4..9126a0316158 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -18,6 +18,7 @@ #include "msm8909w.dtsi" #include "msm8909w-memory.dtsi" #include "msm8909w-pm660-mtp.dtsi" +#include "msm8909-audio-bg_codec.dtsi" / { model = "Qualcomm Technologies, Inc. MSM8909W-PM660 BLACKGHOST WTP"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 9e17d9d1908c..25dcb67c7910 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -101,6 +101,10 @@ }; }; +&audio_codec_mtp { + /delete-property/ asoc-codec; +}; + &i2c_1 { status = "disabled"; }; -- GitLab From d05c088d11f73140c0040d66b99e27bad8767845 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Fri, 24 Nov 2017 19:34:34 +0530 Subject: [PATCH 1869/5498] ARM: dts: msm: Update bit clock delay for TDM interface for MSM8909 BG is configured with 0 bit clock delay, configure msm8909 with the same configuration as BG. Change-Id: Ibdd4a8ec18d0ff3da3042454c3bf58e3ae1a7e4e Signed-off-by: Bala Kishore Pati --- .../boot/dts/qcom/msm8909-audio-bg_codec.dtsi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi index 84c3ad721726..d7e1f0f785eb 100644 --- a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi @@ -89,7 +89,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 { @@ -99,7 +99,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 { @@ -109,7 +109,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 { @@ -119,7 +119,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; }; @@ -143,7 +143,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 { @@ -153,7 +153,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 { @@ -163,7 +163,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 { @@ -173,7 +173,7 @@ qcom,msm-cpudai-tdm-sync-src = <0>; qcom,msm-cpudai-tdm-data-out = <0>; qcom,msm-cpudai-tdm-invert-sync = <0>; - qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-delay = <0>; qcom,msm-cpudai-tdm-data-align = <0>; }; }; -- GitLab From df6fb694400d177576ece60125d61e20f8344501 Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Wed, 25 Oct 2017 16:16:36 +0530 Subject: [PATCH 1870/5498] soc: qcom: pil: Fix error handling during PIL driver probe During probe function of the Linux PIL kernel driver Initialization of various resources are done. This fix is for acquired resource cleanup, in case of error. Change-Id: I0b3511cff7e2917fe83bddfc15086e939f5c2abc Signed-off-by: Jitendra Sharma --- drivers/soc/qcom/subsys-pil-tz.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index 9c82f7a010de..25a099bd4599 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -1042,20 +1042,46 @@ static int pil_tz_driver_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sp2soc_irq_status"); d->irq_status = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(d->irq_status)) { + dev_err(&pdev->dev, "Invalid resource for sp2soc_irq_status\n"); + rc = PTR_ERR(d->irq_status); + goto err_ramdump; + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sp2soc_irq_clr"); d->irq_clear = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(d->irq_clear)) { + dev_err(&pdev->dev, "Invalid resource for sp2soc_irq_clr\n"); + rc = PTR_ERR(d->irq_clear); + goto err_ramdump; + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sp2soc_irq_mask"); d->irq_mask = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(d->irq_mask)) { + dev_err(&pdev->dev, "Invalid resource for sp2soc_irq_mask\n"); + rc = PTR_ERR(d->irq_mask); + goto err_ramdump; + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_err"); d->err_status = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(d->err_status)) { + dev_err(&pdev->dev, "Invalid resource for rmb_err\n"); + rc = PTR_ERR(d->err_status); + goto err_ramdump; + } + rc = of_property_read_u32_array(pdev->dev.of_node, "qcom,spss-scsr-bits", d->bits_arr, sizeof(d->bits_arr)/ sizeof(d->bits_arr[0])); - if (rc) + if (rc) { dev_err(&pdev->dev, "Failed to read qcom,spss-scsr-bits"); + goto err_ramdump; + } } else { d->subsys_desc.err_fatal_handler = subsys_err_fatal_intr_handler; @@ -1080,6 +1106,7 @@ err_subsys: destroy_ramdump_device(d->ramdump_dev); err_ramdump: pil_desc_release(&d->desc); + platform_set_drvdata(pdev, NULL); return rc; } -- GitLab From 32ccb257c497b1c7f0e1f2103390b983db709c1e Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Thu, 7 Dec 2017 15:31:25 +0530 Subject: [PATCH 1871/5498] usb: gadget: f_uvc: Correct SS Companion descriptors The current calculation of maxpacket size and mult causes more data to be send that intended in a frame interval. Fix this by setting the maxpacket size to 1024 and control the data need to send in an interval by using maxburst. Change-Id: I1905e3955822a838272e48bf72f6907e9cbd477f Signed-off-by: Sriharsha Allenki --- drivers/usb/gadget/function/f_uvc.c | 8 +++++--- drivers/usb/gadget/function/u_uvc.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index f425334af22e..9affc4dd9149 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -64,6 +64,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = { #define UVC_INTF_VIDEO_STREAMING 1 #define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */ +#define UVC_STREAMING_SS_MAX_PACKET_SIZE 1024 static struct usb_interface_assoc_descriptor uvc_iad = { .bLength = sizeof(uvc_iad), @@ -640,12 +641,13 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); uvc_hs_streaming_ep.bInterval = opts->streaming_interval; - uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); + uvc_ss_streaming_ep.wMaxPacketSize = + UVC_STREAMING_SS_MAX_PACKET_SIZE; uvc_ss_streaming_ep.bInterval = opts->streaming_interval; - uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; + uvc_ss_streaming_comp.bmAttributes = 0; uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = - cpu_to_le16(max_packet_size * max_packet_mult * + cpu_to_le16(UVC_STREAMING_SS_MAX_PACKET_SIZE * 1 * (opts->streaming_maxburst + 1)); /* Allocate endpoints. */ diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 96d3f39441c9..3d615e4f0e89 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -28,7 +28,7 @@ module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); \ MODULE_PARM_DESC(streaming_maxpacket, "1-1023 (FS), 1-3072 (hs/ss)"); \ \ - static unsigned int streaming_maxburst; \ + static unsigned int streaming_maxburst = 2; \ module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); \ MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); \ \ -- GitLab From 4af2c26158dacfb388f2f715968d2e3bb79f1f23 Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Tue, 18 Apr 2017 07:32:20 +0530 Subject: [PATCH 1872/5498] lowmemorykiller: fix scan_mutex contention A livelock can be created in the system by lowmemorykiller trying to kill the same task again and again. Below is the livelock condition. P0 -> binder_main_lock(W) P1 -> binder_main_lock(T)-> mmap_sem(W) P2 -> mmap_sem(T) -> lowmem_scan::scan_mutex(W) P3 -> lowmem_scan::scan_mutex(T) -> send SIGKILL P0 Here multiple tasks are contending on scan_mutex, and binder_main_lock. Change lowmem_scan mutex_lock with mutex_trylock, so in case of lowmem_scan lock contention, task will be allowed to reclaim from other shrinkers. This will also maintain the serialization of lowmemorykiller trigger. If a task is pending MEMDIE'ing, remove sleep before falling back on other shrinkers. If the task selected to be killed is MEMDIE'ing and in un-interruptible sleep state, do not repeat kill but fallback on other shrinkers without any delay. Change-Id: I12131622f7fa7b422c6d5d09f782af848300e412 Signed-off-by: Prakash Gupta --- drivers/staging/android/lowmemorykiller.c | 31 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 3907ef52508d..f7e981f845ce 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -211,6 +211,22 @@ static int test_task_flag(struct task_struct *p, int flag) return 0; } +static int test_task_state(struct task_struct *p, int state) +{ + struct task_struct *t; + + for_each_thread(p, t) { + task_lock(t); + if (t->state & state) { + task_unlock(t); + return 1; + } + task_unlock(t); + } + + return 0; +} + static DEFINE_MUTEX(scan_mutex); int can_use_cma_pages(gfp_t gfp_mask) @@ -398,7 +414,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) int other_free; int other_file; - if (mutex_lock_interruptible(&scan_mutex) < 0) + if (!mutex_trylock(&scan_mutex)) return 0; other_free = global_page_state(NR_FREE_PAGES); @@ -457,8 +473,6 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (time_before_eq(jiffies, lowmem_deathpending_timeout)) { if (test_task_flag(tsk, TIF_MEMDIE)) { rcu_read_unlock(); - /* give the system time to free up the memory */ - msleep_interruptible(20); mutex_unlock(&scan_mutex); return 0; } @@ -495,6 +509,17 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) long cache_limit = minfree * (long)(PAGE_SIZE / 1024); long free = other_free * (long)(PAGE_SIZE / 1024); trace_lowmemory_kill(selected, cache_size, cache_limit, free); + + if (test_task_flag(selected, TIF_MEMDIE) && + (test_task_state(selected, TASK_UNINTERRUPTIBLE))) { + lowmem_print(2, "'%s' (%d) is already killed\n", + selected->comm, + selected->pid); + rcu_read_unlock(); + mutex_unlock(&scan_mutex); + return 0; + } + lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \ " to free %ldkB on behalf of '%s' (%d) because\n" \ " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \ -- GitLab From 46540f0c255a04980c93b5b7530f22c54d675733 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Fri, 8 Dec 2017 11:43:07 +0530 Subject: [PATCH 1873/5498] ASoC: msm: qdsp6v2: Set freed pointers to NULL Set freed pointers to NULL to avoid double free in msm_compr_playback_open and msm_compr_playback_free functions of the compress driver. CRs-Fixed: 2142216 Change-Id: Ifd011dd85dd9f610c7b69dd460f73d26e006cd66 Signed-off-by: Aditya Bavanari --- sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 34 +++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 77af86ee0a5d..fa1cc600653f 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -107,6 +107,7 @@ struct msm_compr_pdata { bool use_legacy_api; /* indicates use older asm apis*/ struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX]; struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX]; + bool is_in_use[MSM_FRONTEND_DAI_MAX]; }; struct msm_compr_audio { @@ -1335,11 +1336,16 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime = cstream->runtime; struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct msm_compr_audio *prtd; + struct msm_compr_audio *prtd = NULL; struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(rtd->platform); pr_debug("%s\n", __func__); + if (pdata->is_in_use[rtd->dai_link->be_id] == true) { + pr_err("%s: %s is already in use,err: %d ", + __func__, rtd->dai_link->cpu_dai_name, -EBUSY); + return -EBUSY; + } prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL); if (prtd == NULL) { pr_err("Failed to allocate memory for msm_compr_audio\n"); @@ -1351,7 +1357,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) pdata->cstream[rtd->dai_link->be_id] = cstream; pdata->audio_effects[rtd->dai_link->be_id] = kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL); - if (!pdata->audio_effects[rtd->dai_link->be_id]) { + if (pdata->audio_effects[rtd->dai_link->be_id] == NULL) { pr_err("%s: Could not allocate memory for effects\n", __func__); pdata->cstream[rtd->dai_link->be_id] = NULL; kfree(prtd); @@ -1359,10 +1365,11 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) } pdata->dec_params[rtd->dai_link->be_id] = kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL); - if (!pdata->dec_params[rtd->dai_link->be_id]) { + if (pdata->dec_params[rtd->dai_link->be_id] == NULL) { pr_err("%s: Could not allocate memory for dec params\n", __func__); kfree(pdata->audio_effects[rtd->dai_link->be_id]); + pdata->audio_effects[rtd->dai_link->be_id] = NULL; pdata->cstream[rtd->dai_link->be_id] = NULL; kfree(prtd); return -ENOMEM; @@ -1407,18 +1414,20 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) populate_codec_list(prtd); prtd->audio_client = q6asm_audio_client_alloc( (app_cb)compr_event_handler, prtd); - if (!prtd->audio_client) { + if (prtd->audio_client == NULL) { pr_err("%s: Could not allocate memory for client\n", __func__); kfree(pdata->audio_effects[rtd->dai_link->be_id]); + pdata->audio_effects[rtd->dai_link->be_id] = NULL; kfree(pdata->dec_params[rtd->dai_link->be_id]); pdata->cstream[rtd->dai_link->be_id] = NULL; - runtime->private_data = NULL; kfree(prtd); + runtime->private_data = NULL; return -ENOMEM; } pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); prtd->audio_client->perf_mode = false; prtd->session_id = prtd->audio_client->session; + pdata->is_in_use[rtd->dai_link->be_id] = true; return 0; } @@ -1574,11 +1583,15 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream) q6asm_audio_client_buf_free_contiguous(dir, ac); q6asm_audio_client_free(ac); - - kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]); - pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL; - kfree(pdata->dec_params[soc_prtd->dai_link->be_id]); - pdata->dec_params[soc_prtd->dai_link->be_id] = NULL; + if (pdata->audio_effects[soc_prtd->dai_link->be_id] != NULL) { + kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]); + pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL; + } + if (pdata->dec_params[soc_prtd->dai_link->be_id] != NULL) { + kfree(pdata->dec_params[soc_prtd->dai_link->be_id]); + pdata->dec_params[soc_prtd->dai_link->be_id] = NULL; + } + pdata->is_in_use[soc_prtd->dai_link->be_id] = false; kfree(prtd); runtime->private_data = NULL; @@ -3407,6 +3420,7 @@ static int msm_compr_probe(struct snd_soc_platform *platform) pdata->dec_params[i] = NULL; pdata->cstream[i] = NULL; pdata->ch_map[i] = NULL; + pdata->is_in_use[i] = false; } snd_soc_add_platform_controls(platform, msm_compr_gapless_controls, -- GitLab From f0f65fc9f560d83ba923bfa0339cd648d45bf9b5 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Fri, 8 Dec 2017 12:34:25 +0530 Subject: [PATCH 1874/5498] drivers: cpuidle: lpm-levels: Reset suspend wake time During suspend alarm timer sends next wakeup time to be programmed. Currently next wakeup time is not reset upon wakeup. This causes unintended wakeups if there is no alarm set during next suspend. Reset suspend wake time during exit from suspend. Change-Id: Ia0e5d08a4e5cdc71f6c2d884363b830de5ef88d5 Signed-off-by: Maulik Shah --- drivers/cpuidle/lpm-levels.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 3c8a6d924b50..c9709fb93ec3 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -815,6 +815,9 @@ static void cluster_unprepare(struct lpm_cluster *cluster, if (cluster->no_saw_devices && !use_psci) msm_spm_set_rpm_hs(false); + + if (!from_idle) + suspend_wake_time = 0; } update_debug_pc_event(CLUSTER_EXIT, cluster->last_level, -- GitLab From c7222043f45d6ef29a0bc7207a2761b4c8a3ffc5 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Savaliya Date: Thu, 7 Sep 2017 23:15:24 +0530 Subject: [PATCH 1875/5498] ARM: dts: msm: specify UART5 configuration for MDM9607 platform MDM9607 has CMUX use case on UART5 and hence enable the dtsi configuration with status disabled so that client can enable based on need. Change-Id: I250f332ca31559b7804d60a9afe1ccfd1db353cb Signed-off-by: Mukesh Kumar Savaliya --- arch/arm/boot/dts/qcom/mdm9607-pinctrl.dtsi | 26 +++++++++++++++ arch/arm/boot/dts/qcom/mdm9607.dtsi | 37 +++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/arch/arm/boot/dts/qcom/mdm9607-pinctrl.dtsi b/arch/arm/boot/dts/qcom/mdm9607-pinctrl.dtsi index 86ab18904410..ea331a282880 100644 --- a/arch/arm/boot/dts/qcom/mdm9607-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9607-pinctrl.dtsi @@ -61,6 +61,32 @@ }; }; + blsp1_uart5_active: blsp1_uart5_active { + mux { + pins = "gpio8", "gpio9", "gpio10", "gpio11"; + function = "blsp_uart5"; + }; + + config { + pins = "gpio8", "gpio9", "gpio10", "gpio11"; + drive-strength = <2>; + bias-disable; + }; + }; + + blsp1_uart5_sleep: blsp1_uart5_sleep { + mux { + pins = "gpio8", "gpio9", "gpio10", "gpio11"; + function = "gpio"; + }; + + config { + pins = "gpio8", "gpio9", "gpio10", "gpio11"; + drive-strength = <2>; + bias-disable; + }; + }; + spi1 { spi1_default: spi1_default { diff --git a/arch/arm/boot/dts/qcom/mdm9607.dtsi b/arch/arm/boot/dts/qcom/mdm9607.dtsi index caad3b0bad26..69c3cb066232 100644 --- a/arch/arm/boot/dts/qcom/mdm9607.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9607.dtsi @@ -577,6 +577,43 @@ status = "disabled"; }; + blsp1_uart5_hs: uart@78b3000 { /* BLSP1 UART5 */ + compatible = "qcom,msm-hsuart-v14"; + reg = <0x78b3000 0x200>, + <0x7884000 0x23000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp1_uart5_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 121 0 + 1 &intc 0 238 0 + 2 &tlmm_pinmux 9 0>; + + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xFD>; + + qcom,bam-tx-ep-pipe-index = <8>; + qcom,bam-rx-ep-pipe-index = <9>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc clk_gcc_blsp1_uart5_apps_clk>, + <&clock_gcc clk_gcc_blsp1_ahb_clk>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&blsp1_uart5_sleep>; + pinctrl-1 = <&blsp1_uart5_active>; + + qcom,msm-bus,name = "buart5"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + status = "disabled"; + }; + cnss_sdio: qcom,cnss-sdio { compatible = "qcom,cnss_sdio"; reg = <0x87a00000 0x200000>; -- GitLab From 4a06b0168aebf3957931f1cb87b5e7595350e9de Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Tue, 5 Dec 2017 12:37:43 +0530 Subject: [PATCH 1876/5498] ANDROID: sdcardfs: Make WARN_RATELIMIT to pr_debug With WARN_RATELIMIT lot of warning messages are sometimes causing watchdog bite or spinlock lockup (due to continuous logging). Thus change it to pr_debug. Change-Id: I2f0670bd20a39d289c105f9896e6fc509fb87de6 Signed-off-by: Ritesh Harjani --- fs/sdcardfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 36d54d6fa16d..9016aa714a0b 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -598,7 +598,7 @@ out: static int sdcardfs_permission_wrn(struct inode *inode, int mask) { - WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n"); + pr_debug("sdcardfs does not support permission. Use permission2.\n"); return -EINVAL; } -- GitLab From aa129cb1754de24f56ed60faec18fdae2e17e262 Mon Sep 17 00:00:00 2001 From: Coly Li Date: Mon, 30 Oct 2017 14:46:31 -0700 Subject: [PATCH 1877/5498] bcache: only permit to recovery read error when cache device is clean commit d59b23795933678c9638fd20c942d2b4f3cd6185 upstream. When bcache does read I/Os, for example in writeback or writethrough mode, if a read request on cache device is failed, bcache will try to recovery the request by reading from cached device. If the data on cached device is not synced with cache device, then requester will get a stale data. For critical storage system like database, providing stale data from recovery may result an application level data corruption, which is unacceptible. With this patch, for a failed read request in writeback or writethrough mode, recovery a recoverable read request only happens when cache device is clean. That is to say, all data on cached device is up to update. For other cache modes in bcache, read request will never hit cached_dev_read_error(), they don't need this patch. Please note, because cache mode can be switched arbitrarily in run time, a writethrough mode might be switched from a writeback mode. Therefore checking dc->has_data in writethrough mode still makes sense. Changelog: V4: Fix parens error pointed by Michael Lyle. v3: By response from Kent Oversteet, he thinks recovering stale data is a bug to fix, and option to permit it is unnecessary. So this version the sysfs file is removed. v2: rename sysfs entry from allow_stale_data_on_failure to allow_stale_data_on_failure, and fix the confusing commit log. v1: initial patch posted. [small change to patch comment spelling by mlyle] Signed-off-by: Coly Li Signed-off-by: Michael Lyle Reported-by: Arne Wolf Reviewed-by: Michael Lyle Cc: Kent Overstreet Cc: Nix Cc: Kai Krakow Cc: Eric Wheeler Cc: Junhui Tang Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/request.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index 62e6e98186b5..fe696137ed87 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -705,8 +705,16 @@ static void cached_dev_read_error(struct closure *cl) { struct search *s = container_of(cl, struct search, cl); struct bio *bio = &s->bio.bio; + struct cached_dev *dc = container_of(s->d, struct cached_dev, disk); - if (s->recoverable) { + /* + * If cache device is dirty (dc->has_dirty is non-zero), then + * recovery a failed read request from cached device may get a + * stale data back. So read failure recovery is only permitted + * when cache device is clean. + */ + if (s->recoverable && + (dc && !atomic_read(&dc->has_dirty))) { /* Retry from the backing device: */ trace_bcache_read_retry(s->orig_bio); -- GitLab From fa541bf05f1750c04579e70f4737967da65a611b Mon Sep 17 00:00:00 2001 From: Rui Hua Date: Fri, 24 Nov 2017 15:14:26 -0800 Subject: [PATCH 1878/5498] bcache: recover data from backing when data is clean commit e393aa2446150536929140739f09c6ecbcbea7f0 upstream. When we send a read request and hit the clean data in cache device, there is a situation called cache read race in bcache(see the commit in the tail of cache_look_up(), the following explaination just copy from there): The bucket we're reading from might be reused while our bio is in flight, and we could then end up reading the wrong data. We guard against this by checking (in bch_cache_read_endio()) if the pointer is stale again; if so, we treat it as an error (s->iop.error = -EINTR) and reread from the backing device (but we don't pass that error up anywhere) It should be noted that cache read race happened under normal circumstances, not the circumstance when SSD failed, it was counted and shown in /sys/fs/bcache/XXX/internal/cache_read_races. Without this patch, when we use writeback mode, we will never reread from the backing device when cache read race happened, until the whole cache device is clean, because the condition (s->recoverable && (dc && !atomic_read(&dc->has_dirty))) is false in cached_dev_read_error(). In this situation, the s->iop.error(= -EINTR) will be passed up, at last, user will receive -EINTR when it's bio end, this is not suitable, and wield to up-application. In this patch, we use s->read_dirty_data to judge whether the read request hit dirty data in cache device, it is safe to reread data from the backing device when the read request hit clean data. This can not only handle cache read race, but also recover data when failed read request from cache device. [edited by mlyle to fix up whitespace, commit log title, comment spelling] Fixes: d59b23795933 ("bcache: only permit to recovery read error when cache device is clean") Signed-off-by: Hua Rui Reviewed-by: Michael Lyle Reviewed-by: Coly Li Signed-off-by: Michael Lyle Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/request.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index fe696137ed87..ead0e6072af8 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -705,16 +705,15 @@ static void cached_dev_read_error(struct closure *cl) { struct search *s = container_of(cl, struct search, cl); struct bio *bio = &s->bio.bio; - struct cached_dev *dc = container_of(s->d, struct cached_dev, disk); /* - * If cache device is dirty (dc->has_dirty is non-zero), then - * recovery a failed read request from cached device may get a - * stale data back. So read failure recovery is only permitted - * when cache device is clean. + * If read request hit dirty data (s->read_dirty_data is true), + * then recovery a failed read request from cached device may + * get a stale data back. So read failure recovery is only + * permitted when read request hit clean data in cache device, + * or when cache read race happened. */ - if (s->recoverable && - (dc && !atomic_read(&dc->has_dirty))) { + if (s->recoverable && !s->read_dirty_data) { /* Retry from the backing device: */ trace_bcache_read_retry(s->orig_bio); -- GitLab From 826b2a0b10bf91e84fdc220e26e9c7daeda60161 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 28 Oct 2017 11:35:49 +0200 Subject: [PATCH 1879/5498] serial: 8250_fintek: Fix rs485 disablement on invalid ioctl() [ Upstream commit 3236a965486ba0c6043cf2c7b51943d8b382ae29 ] This driver's ->rs485_config callback checks if SER_RS485_RTS_ON_SEND and SER_RS485_RTS_AFTER_SEND have the same value. If they do, it means the user has passed in invalid data with the TIOCSRS485 ioctl() since RTS must have a different polarity when sending and when not sending. In this case, rs485 mode is not enabled (the RS485_URA bit is not set in the RS485 Enable Register) and this is supposed to be signaled back to the user by clearing the SER_RS485_ENABLED bit in struct serial_rs485 ... except a missing tilde character is preventing that from happening. Fixes: 28e3fb6c4dce ("serial: Add support for Fintek F81216A LPC to 4 UART") Cc: Ricardo Ribalda Delgado Cc: "Ji-Ze Hong (Peter Hong)" Signed-off-by: Lukas Wunner Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 1bb28cb69493..c72283f2be6d 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -118,7 +118,7 @@ static int fintek_8250_rs4850_config(struct uart_8250_port *uart, if ((!!(rs485->flags & SER_RS485_RTS_ON_SEND)) == (!!(rs485->flags & SER_RS485_RTS_AFTER_SEND))) - rs485->flags &= SER_RS485_ENABLED; + rs485->flags &= ~SER_RS485_ENABLED; else config |= RS485_URA; -- GitLab From e95cdfbe08b7d3ee15b514dc9d01df8005524979 Mon Sep 17 00:00:00 2001 From: Hiromitsu Yamasaki Date: Thu, 2 Nov 2017 10:32:36 +0100 Subject: [PATCH 1880/5498] spi: sh-msiof: Fix DMA transfer size check [ Upstream commit 36735783fdb599c94b9c86824583df367c65900b ] DMA supports 32-bit words only, even if BITLEN1 of SITMDR2 register is 16bit. Fixes: b0d0ce8b6b91 ("spi: sh-msiof: Add DMA support") Signed-off-by: Hiromitsu Yamasaki Signed-off-by: Simon Horman Acked-by: Geert Uytterhoeven Acked-by: Dirk Behme Signed-off-by: Mark Brown Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-sh-msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 14052936b1c5..d9044569b4fe 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -818,7 +818,7 @@ static int sh_msiof_transfer_one(struct spi_master *master, break; copy32 = copy_bswap32; } else if (bits <= 16) { - if (l & 1) + if (l & 3) break; copy32 = copy_wswap32; } else { -- GitLab From 4b72e3bb6bb1214da311a031f864c70ef51188a6 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 16 Oct 2017 12:40:29 -0500 Subject: [PATCH 1881/5498] EDAC, sb_edac: Fix missing break in switch [ Upstream commit a8e9b186f153a44690ad0363a56716e7077ad28c ] Add missing break statement in order to prevent the code from falling through. Signed-off-by: Gustavo A. R. Silva Cc: Qiuxu Zhuo Cc: linux-edac Link: http://lkml.kernel.org/r/20171016174029.GA19757@embeddedor.com Signed-off-by: Borislav Petkov Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/edac/sb_edac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 6aa25a3aa2c7..42f667485594 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -1645,6 +1645,7 @@ static int ibridge_mci_bind_devs(struct mem_ctl_info *mci, break; case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA: pvt->pci_ta = pdev; + break; case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_RAS: pvt->pci_ras = pdev; break; -- GitLab From 079e07453a78efcf3dbee3a40a89a6c31516c224 Mon Sep 17 00:00:00 2001 From: Jibin Xu Date: Sun, 10 Sep 2017 20:11:42 -0700 Subject: [PATCH 1882/5498] sysrq : fix Show Regs call trace on ARM [ Upstream commit b00bebbc301c8e1f74f230dc82282e56b7e7a6db ] When kernel configuration SMP,PREEMPT and DEBUG_PREEMPT are enabled, echo 1 >/proc/sys/kernel/sysrq echo p >/proc/sysrq-trigger kernel will print call trace as below: sysrq: SysRq : Show Regs BUG: using __this_cpu_read() in preemptible [00000000] code: sh/435 caller is __this_cpu_preempt_check+0x18/0x20 Call trace: [] dump_backtrace+0x0/0x1d0 [] show_stack+0x24/0x30 [] dump_stack+0x90/0xb0 [] check_preemption_disabled+0x100/0x108 [] __this_cpu_preempt_check+0x18/0x20 [] sysrq_handle_showregs+0x1c/0x40 [] __handle_sysrq+0x12c/0x1a0 [] write_sysrq_trigger+0x60/0x70 [] proc_reg_write+0x90/0xd0 [] __vfs_write+0x48/0x90 [] vfs_write+0xa4/0x190 [] SyS_write+0x54/0xb0 [] el0_svc_naked+0x24/0x28 This can be seen on a common board like an r-pi3. This happens because when echo p >/proc/sysrq-trigger, get_irq_regs() is called outside of IRQ context, if preemption is enabled in this situation,kernel will print the call trace. Since many prior discussions on the mailing lists have made it clear that get_irq_regs either just returns NULL or stale data when used outside of IRQ context,we simply avoid calling it outside of IRQ context. Signed-off-by: Jibin Xu Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 647d3df9d66a..f97e7dac3a98 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -237,8 +237,10 @@ static void sysrq_handle_showallcpus(int key) * architecture has no support for it: */ if (!trigger_all_cpu_backtrace()) { - struct pt_regs *regs = get_irq_regs(); + struct pt_regs *regs = NULL; + if (in_irq()) + regs = get_irq_regs(); if (regs) { printk(KERN_INFO "CPU%d:\n", smp_processor_id()); show_regs(regs); @@ -257,7 +259,10 @@ static struct sysrq_key_op sysrq_showallcpus_op = { static void sysrq_handle_showregs(int key) { - struct pt_regs *regs = get_irq_regs(); + struct pt_regs *regs = NULL; + + if (in_irq()) + regs = get_irq_regs(); if (regs) show_regs(regs); perf_event_print_debug(); -- GitLab From 462b3549034fc4f1b3119c3364cd636484fb1f52 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Wed, 13 Sep 2017 10:12:09 +0200 Subject: [PATCH 1883/5498] perf test attr: Fix ignored test case result [ Upstream commit 22905582f6dd4bbd0c370fe5732c607452010c04 ] Command perf test -v 16 (Setup struct perf_event_attr test) always reports success even if the test case fails. It works correctly if you also specify -F (for don't fork). root@s35lp76 perf]# ./perf test -v 16 15: Setup struct perf_event_attr : --- start --- running './tests/attr/test-record-no-delay' [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.002 MB /tmp/tmp4E1h7R/perf.data (1 samples) ] expected task=0, got 1 expected precise_ip=0, got 3 expected wakeup_events=1, got 0 FAILED './tests/attr/test-record-no-delay' - match failure test child finished with 0 ---- end ---- Setup struct perf_event_attr: Ok The reason for the wrong error reporting is the return value of the system() library call. It is called in run_dir() file tests/attr.c and returns the exit status, in above case 0xff00. This value is given as parameter to the exit() function which can only handle values 0-0xff. The child process terminates with exit value of 0 and the parent does not detect any error. This patch corrects the error reporting and prints the correct test result. Signed-off-by: Thomas-Mich Richter Acked-by: Jiri Olsa Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Martin Schwidefsky Cc: Thomas-Mich Richter LPU-Reference: 20170913081209.39570-2-tmricht@linux.vnet.ibm.com Link: http://lkml.kernel.org/n/tip-rdube6rfcjsr1nzue72c7lqn@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- tools/perf/tests/attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 2dfc9ad0e6f2..8e92b56c610a 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -150,7 +150,7 @@ static int run_dir(const char *d, const char *perf) snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s", d, d, perf, vcnt, v); - return system(cmd); + return system(cmd) ? TEST_FAIL : TEST_OK; } int test__attr(void) -- GitLab From 2be09ca3fe9c0ffcd82139b4efe06b7f91d6cae5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 3 Jan 2017 13:22:34 +0200 Subject: [PATCH 1884/5498] ARM: OMAP1: DMA: Correct the number of logical channels [ Upstream commit 657279778af54f35e54b07b6687918f254a2992c ] OMAP1510, OMAP5910 and OMAP310 have only 9 logical channels. OMAP1610, OMAP5912, OMAP1710, OMAP730, and OMAP850 have 16 logical channels available. The wired 17 for the lch_count must have been used to cover the 16 + 1 dedicated LCD channel, in reality we can only use 9 or 16 channels. The d->chan_count is not used by the omap-dma stack, so we can skip the setup. chan_count was configured to the number of logical channels and not the actual number of physical channels anyways. Signed-off-by: Peter Ujfalusi Acked-by: Aaro Koskinen Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-omap1/dma.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-omap1/dma.c b/arch/arm/mach-omap1/dma.c index 4be601b638d7..8129e5f9c94d 100644 --- a/arch/arm/mach-omap1/dma.c +++ b/arch/arm/mach-omap1/dma.c @@ -31,7 +31,6 @@ #include #define OMAP1_DMA_BASE (0xfffed800) -#define OMAP1_LOGICAL_DMA_CH_COUNT 17 static u32 enable_1510_mode; @@ -311,8 +310,6 @@ static int __init omap1_system_dma_init(void) goto exit_iounmap; } - d->lch_count = OMAP1_LOGICAL_DMA_CH_COUNT; - /* Valid attributes for omap1 plus processors */ if (cpu_is_omap15xx()) d->dev_caps = ENABLE_1510_MODE; @@ -329,13 +326,14 @@ static int __init omap1_system_dma_init(void) d->dev_caps |= CLEAR_CSR_ON_READ; d->dev_caps |= IS_WORD_16; - if (cpu_is_omap15xx()) - d->chan_count = 9; - else if (cpu_is_omap16xx() || cpu_is_omap7xx()) { - if (!(d->dev_caps & ENABLE_1510_MODE)) - d->chan_count = 16; + /* available logical channels */ + if (cpu_is_omap15xx()) { + d->lch_count = 9; + } else { + if (d->dev_caps & ENABLE_1510_MODE) + d->lch_count = 9; else - d->chan_count = 9; + d->lch_count = 16; } p = dma_plat_info; -- GitLab From ae8e8f2b0f5fc7d11d9a06af37e50f9e6dc44b6a Mon Sep 17 00:00:00 2001 From: David Forster Date: Fri, 6 Jan 2017 10:27:59 +0000 Subject: [PATCH 1885/5498] vti6: fix device register to report IFLA_INFO_KIND [ Upstream commit 93e246f783e6bd1bc64fdfbfe68b18161f69b28e ] vti6 interface is registered before the rtnl_link_ops block is attached. As a result the resulting RTM_NEWLINK is missing IFLA_INFO_KIND. Re-order attachment of rtnl_link_ops block to fix. Signed-off-by: Dave Forster Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_vti.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index a11083d37789..91fdb612279f 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -172,12 +172,12 @@ static int vti6_tnl_create2(struct net_device *dev) struct vti6_net *ip6n = net_generic(net, vti6_net_id); int err; + dev->rtnl_link_ops = &vti6_link_ops; err = register_netdevice(dev); if (err < 0) goto out; strcpy(t->parms.name, dev->name); - dev->rtnl_link_ops = &vti6_link_ops; dev_hold(dev); vti6_tnl_link(ip6n, t); -- GitLab From 351f504358fc4c7b44f6540949d6b71d89af2d0b Mon Sep 17 00:00:00 2001 From: Vlad Tsyrklevich Date: Mon, 9 Jan 2017 20:57:48 +0700 Subject: [PATCH 1886/5498] net/appletalk: Fix kernel memory disclosure [ Upstream commit ce7e40c432ba84da104438f6799d460a4cad41bc ] ipddp_route structs contain alignment padding so kernel heap memory is leaked when they are copied to user space in ipddp_ioctl(SIOCFINDIPDDPRT). Change kmalloc() to kzalloc() to clear that memory. Signed-off-by: Vlad Tsyrklevich Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/appletalk/ipddp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index e90c6a7333d7..2e4649655181 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -191,7 +191,7 @@ static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev) */ static int ipddp_create(struct ipddp_route *new_rt) { - struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL); + struct ipddp_route *rt = kzalloc(sizeof(*rt), GFP_KERNEL); if (rt == NULL) return -ENOMEM; -- GitLab From 741a5969398f43203bf09768d9b7983eeeee0d29 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 13 Jan 2017 13:31:32 -0500 Subject: [PATCH 1887/5498] NFSv4: Fix client recovery when server reboots multiple times [ Upstream commit c6180a6237174f481dc856ed6e890d8196b6f0fb ] If the server reboots multiple times, the client should rely on the server to tell it that it cannot reclaim state as per section 9.6.3.4 in RFC7530 and section 8.4.2.1 in RFC5661. Currently, the client is being to conservative, and is assuming that if the server reboots while state recovery is in progress, then it must ignore state that was not recovered before the reboot. Signed-off-by: Trond Myklebust Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4state.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 1f9d57ab8df4..f471662c0a1f 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1650,7 +1650,6 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) break; case -NFS4ERR_STALE_CLIENTID: set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); - nfs4_state_clear_reclaim_reboot(clp); nfs4_state_start_reclaim_reboot(clp); break; case -NFS4ERR_EXPIRED: -- GitLab From 7c3f696de8d19e56b8685ab70eba5e139e61696f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 20 Jan 2017 13:01:57 +0000 Subject: [PATCH 1888/5498] net: sctp: fix array overrun read on sctp_timer_tbl [ Upstream commit 0e73fc9a56f22f2eec4d2b2910c649f7af67b74d ] The comparison on the timeout can lead to an array overrun read on sctp_timer_tbl because of an off-by-one error. Fix this by using < instead of <= and also compare to the array size rather than SCTP_EVENT_TIMEOUT_MAX. Fixes CoverityScan CID#1397639 ("Out-of-bounds read") Signed-off-by: Colin Ian King Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/sctp/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/debug.c b/net/sctp/debug.c index 95d7b15dad21..e371a0d90068 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -166,7 +166,7 @@ static const char *const sctp_timer_tbl[] = { /* Lookup timer debug name. */ const char *sctp_tname(const sctp_subtype_t id) { - if (id.timeout <= SCTP_EVENT_TIMEOUT_MAX) + if (id.timeout < ARRAY_SIZE(sctp_timer_tbl)) return sctp_timer_tbl[id.timeout]; return "unknown_timer"; } -- GitLab From 42c53dc2ca8ca6c9d7f8f2092bfdbed0e129806a Mon Sep 17 00:00:00 2001 From: Parthasarathy Bhuvaragan Date: Tue, 24 Jan 2017 13:00:48 +0100 Subject: [PATCH 1889/5498] tipc: fix cleanup at module unload [ Upstream commit 35e22e49a5d6a741ebe7f2dd280b2052c3003ef7 ] In tipc_server_stop(), we iterate over the connections with limiting factor as server's idr_in_use. We ignore the fact that this variable is decremented in tipc_close_conn(), leading to premature exit. In this commit, we iterate until the we have no connections left. Acked-by: Ying Xue Acked-by: Jon Maloy Tested-by: John Thompson Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/tipc/server.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/tipc/server.c b/net/tipc/server.c index a538a02f869b..0411fac14226 100644 --- a/net/tipc/server.c +++ b/net/tipc/server.c @@ -579,14 +579,12 @@ int tipc_server_start(struct tipc_server *s) void tipc_server_stop(struct tipc_server *s) { struct tipc_conn *con; - int total = 0; int id; spin_lock_bh(&s->idr_lock); - for (id = 0; total < s->idr_in_use; id++) { + for (id = 0; s->idr_in_use; id++) { con = idr_find(&s->conn_idr, id); if (con) { - total++; spin_unlock_bh(&s->idr_lock); tipc_close_conn(con); spin_lock_bh(&s->idr_lock); -- GitLab From ccf653b185f9cb39c403e7b58a9d5ed08d924768 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 8 Feb 2017 14:30:53 -0800 Subject: [PATCH 1890/5498] mm: avoid returning VM_FAULT_RETRY from ->page_mkwrite handlers [ Upstream commit 0911d0041c22922228ca52a977d7b0b0159fee4b ] Some ->page_mkwrite handlers may return VM_FAULT_RETRY as its return code (GFS2 or Lustre can definitely do this). However VM_FAULT_RETRY from ->page_mkwrite is completely unhandled by the mm code and results in locking and writeably mapping the page which definitely is not what the caller wanted. Fix Lustre and block_page_mkwrite_ret() used by other filesystems (notably GFS2) to return VM_FAULT_NOPAGE instead which results in bailing out from the fault code, the CPU then retries the access, and we fault again effectively doing what the handler wanted. Link: http://lkml.kernel.org/r/20170203150729.15863-1-jack@suse.cz Signed-off-by: Jan Kara Reported-by: Al Viro Reviewed-by: Jinshan Xiong Cc: Matthew Wilcox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/lustre/lustre/llite/llite_mmap.c | 4 +--- include/linux/buffer_head.h | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/staging/lustre/lustre/llite/llite_mmap.c b/drivers/staging/lustre/lustre/llite/llite_mmap.c index ae605a6d9dc2..dde9fd9a39b9 100644 --- a/drivers/staging/lustre/lustre/llite/llite_mmap.c +++ b/drivers/staging/lustre/lustre/llite/llite_mmap.c @@ -407,15 +407,13 @@ static int ll_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) result = VM_FAULT_LOCKED; break; case -ENODATA: + case -EAGAIN: case -EFAULT: result = VM_FAULT_NOPAGE; break; case -ENOMEM: result = VM_FAULT_OOM; break; - case -EAGAIN: - result = VM_FAULT_RETRY; - break; default: result = VM_FAULT_SIGBUS; break; diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 73b45225a7ca..f6675ffe41ed 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -236,12 +236,10 @@ static inline int block_page_mkwrite_return(int err) { if (err == 0) return VM_FAULT_LOCKED; - if (err == -EFAULT) + if (err == -EFAULT || err == -EAGAIN) return VM_FAULT_NOPAGE; if (err == -ENOMEM) return VM_FAULT_OOM; - if (err == -EAGAIN) - return VM_FAULT_RETRY; /* -ENOSPC, -EDQUOT, -EIO ... */ return VM_FAULT_SIGBUS; } -- GitLab From ce48aa52f90fd7b8dfa62f1c81452974d0a982c7 Mon Sep 17 00:00:00 2001 From: Rui Sousa Date: Mon, 13 Feb 2017 10:01:25 +0800 Subject: [PATCH 1891/5498] net: fec: fix multicast filtering hardware setup [ Upstream commit 01f8902bcf3ff124d0aeb88a774180ebcec20ace ] Fix hardware setup of multicast address hash: - Never clear the hardware hash (to avoid packet loss) - Construct the hash register values in software and then write once to hardware Signed-off-by: Rui Sousa Signed-off-by: Fugang Duan Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/freescale/fec_main.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 51f65299094b..065a7616e961 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2793,6 +2793,7 @@ static void set_multicast_list(struct net_device *ndev) struct netdev_hw_addr *ha; unsigned int i, bit, data, crc, tmp; unsigned char hash; + unsigned int hash_high = 0, hash_low = 0; if (ndev->flags & IFF_PROMISC) { tmp = readl(fep->hwp + FEC_R_CNTRL); @@ -2815,11 +2816,7 @@ static void set_multicast_list(struct net_device *ndev) return; } - /* Clear filter and add the addresses in hash register - */ - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); - + /* Add the addresses in hash register */ netdev_for_each_mc_addr(ha, ndev) { /* calculate crc32 value of mac address */ crc = 0xffffffff; @@ -2837,16 +2834,14 @@ static void set_multicast_list(struct net_device *ndev) */ hash = (crc >> (32 - HASH_BITS)) & 0x3f; - if (hash > 31) { - tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH); - tmp |= 1 << (hash - 32); - writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); - } else { - tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW); - tmp |= 1 << hash; - writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW); - } + if (hash > 31) + hash_high |= 1 << (hash - 32); + else + hash_low |= 1 << hash; } + + writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); + writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); } /* Set a MAC change in hardware. */ -- GitLab From d51a980a967748e889815c304e7bdcb4d6c37fd3 Mon Sep 17 00:00:00 2001 From: Boshi Wang Date: Fri, 20 Oct 2017 16:01:03 +0800 Subject: [PATCH 1892/5498] ima: fix hash algorithm initialization [ Upstream commit ebe7c0a7be92bbd34c6ff5b55810546a0ee05bee ] The hash_setup function always sets the hash_setup_done flag, even when the hash algorithm is invalid. This prevents the default hash algorithm defined as CONFIG_IMA_DEFAULT_HASH from being used. This patch sets hash_setup_done flag only for valid hash algorithms. Fixes: e7a2ad7eb6f4 "ima: enable support for larger default filedata hash algorithms" Signed-off-by: Boshi Wang Signed-off-by: Mimi Zohar Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- security/integrity/ima/ima_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 62f59eca32d3..01590949eddd 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -52,6 +52,8 @@ static int __init hash_setup(char *str) ima_hash_algo = HASH_ALGO_SHA1; else if (strncmp(str, "md5", 3) == 0) ima_hash_algo = HASH_ALGO_MD5; + else + return 1; goto out; } @@ -61,6 +63,8 @@ static int __init hash_setup(char *str) break; } } + if (i == HASH_ALGO__LAST) + return 1; out: hash_setup_done = 1; return 1; -- GitLab From 94eeab3e1d012fce178f45fd32e44304b96cc0a1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 14 Nov 2017 19:27:22 +0100 Subject: [PATCH 1893/5498] uas: Always apply US_FL_NO_ATA_1X quirk to Seagate devices commit 7fee72d5e8f1e7b8d8212e28291b1a0243ecf2f1 upstream. We've been adding this as a quirk on a per device basis hoping that newer disk enclosures would do better, but that has not happened, so simply apply this quirk to all Seagate devices. Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas-detect.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index a0b92096f6f1..cafcd5243163 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -88,6 +88,10 @@ static int uas_use_uas_driver(struct usb_interface *intf, } } + /* All Seagate disk enclosures have broken ATA pass-through support */ + if (le16_to_cpu(udev->descriptor.idVendor) == 0x0bc2) + flags |= US_FL_NO_ATA_1X; + usb_stor_adjust_quirks(udev, &flags); if (flags & US_FL_IGNORE_UAS) { -- GitLab From e1ed329f60f6054aa7f045adab5d7a50c1daecd5 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 14 Nov 2017 01:31:15 -0500 Subject: [PATCH 1894/5498] usb: quirks: Add no-lpm quirk for KY-688 USB 3.1 Type-C Hub commit e43a12f1793ae1fe006e26fe9327a8840a92233c upstream. KY-688 USB 3.1 Type-C Hub internally uses a Genesys Logic hub to connect to Realtek r8153. Similar to commit ("7496cfe5431f2 usb: quirks: Add no-lpm quirk for Moshi USB to Ethernet Adapter"), no-lpm can make r8153 ethernet work. Signed-off-by: Kai-Heng Feng Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index d2746553b5f5..1ef86b4e7927 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -144,6 +144,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* appletouch */ { USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Genesys Logic hub, internally used by KY-688 USB 3.1 Type-C Hub */ + { USB_DEVICE(0x05e3, 0x0612), .driver_info = USB_QUIRK_NO_LPM }, + /* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */ { USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM }, -- GitLab From 01d9d14f89e4cec6a4002974f6e574ec271cc4ea Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 13 Nov 2017 11:31:31 -0800 Subject: [PATCH 1895/5498] serial: 8250_pci: Add Amazon PCI serial device ID commit 3bfd1300abfe3adb18e84a89d97a0e82a22124bb upstream. This device will be used in future Amazon EC2 instances as the primary serial port (i.e., data sent to this port will be available via the GetConsoleOuput [1] EC2 API). [1] http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_GetConsoleOutput.html Signed-off-by: Matt Wilson Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 54921ac390ed..5fd44ac7ecf8 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -5483,6 +5483,9 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 }, { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 }, + /* Amazon PCI serial device */ + { PCI_DEVICE(0x1d0f, 0x8250), .driver_data = pbn_b0_1_115200 }, + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL -- GitLab From 9906ad70c51c3037a29c1b1fb0fed17fa395ff88 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Thu, 9 Nov 2017 13:16:46 +0100 Subject: [PATCH 1896/5498] usb: hub: Cycle HUB power when initialization fails commit 973593a960ddac0f14f0d8877d2d0abe0afda795 upstream. Sometimes the USB device gets confused about the state of the initialization and the connection fails. In particular, the device thinks that it's already set up and running while the host thinks the device still needs to be configured. To work around this issue, power-cycle the hub's output to issue a sort of "reset" to the device. This makes the device restart its state machine and then the initialization succeeds. This fixes problems where the kernel reports a list of errors like this: usb 1-1.3: device not accepting address 19, error -71 The end result is a non-functioning device. After this patch, the sequence becomes like this: usb 1-1.3: new high-speed USB device number 18 using ci_hdrc usb 1-1.3: device not accepting address 18, error -71 usb 1-1.3: new high-speed USB device number 19 using ci_hdrc usb 1-1.3: device not accepting address 19, error -71 usb 1-1-port3: attempt power cycle usb 1-1.3: new high-speed USB device number 21 using ci_hdrc usb-storage 1-1.3:1.2: USB Mass Storage device detected Signed-off-by: Mike Looijmans Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 955d2ea37dc6..24a3d0f8e38e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4816,6 +4816,15 @@ loop: usb_put_dev(udev); if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; + + /* When halfway through our retry count, power-cycle the port */ + if (i == (SET_CONFIG_TRIES / 2) - 1) { + dev_info(&port_dev->dev, "attempt power cycle\n"); + usb_hub_set_port_power(hdev, hub, port1, false); + msleep(2 * hub_power_on_good_delay(hub)); + usb_hub_set_port_power(hdev, hub, port1, true); + msleep(hub_power_on_good_delay(hub)); + } } if (hub->hdev->parent || !hcd->driver->port_handed_over || -- GitLab From b8a9e8d2c8085ec5f909115df7e3fb7ce8901847 Mon Sep 17 00:00:00 2001 From: Mateusz Berezecki Date: Wed, 21 Dec 2016 09:19:14 -0800 Subject: [PATCH 1897/5498] USB: Increase usbfs transfer limit commit 1129d270cbfbb7e2b1ec3dede4a13930bdd10e41 upstream. Promote a variable keeping track of USB transfer memory usage to a wider data type and allow for higher bandwidth transfers from a large number of USB devices connected to a single host. Signed-off-by: Mateusz Berezecki Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 43 +++++++++++++++------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index f322111db601..afb1d8fac41f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -113,42 +113,35 @@ enum snoop_when { #define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) /* Limit on the total amount of memory we can allocate for transfers */ -static unsigned usbfs_memory_mb = 16; +static u32 usbfs_memory_mb = 16; module_param(usbfs_memory_mb, uint, 0644); MODULE_PARM_DESC(usbfs_memory_mb, "maximum MB allowed for usbfs buffers (0 = no limit)"); -/* Hard limit, necessary to avoid arithmetic overflow */ -#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000) - -static atomic_t usbfs_memory_usage; /* Total memory currently allocated */ +static atomic64_t usbfs_memory_usage; /* Total memory currently allocated */ /* Check whether it's okay to allocate more memory for a transfer */ -static int usbfs_increase_memory_usage(unsigned amount) +static int usbfs_increase_memory_usage(u64 amount) { - unsigned lim; + u64 lim; - /* - * Convert usbfs_memory_mb to bytes, avoiding overflows. - * 0 means use the hard limit (effectively unlimited). - */ lim = ACCESS_ONCE(usbfs_memory_mb); - if (lim == 0 || lim > (USBFS_XFER_MAX >> 20)) - lim = USBFS_XFER_MAX; - else - lim <<= 20; + lim <<= 20; - atomic_add(amount, &usbfs_memory_usage); - if (atomic_read(&usbfs_memory_usage) <= lim) - return 0; - atomic_sub(amount, &usbfs_memory_usage); - return -ENOMEM; + atomic64_add(amount, &usbfs_memory_usage); + + if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) { + atomic64_sub(amount, &usbfs_memory_usage); + return -ENOMEM; + } + + return 0; } /* Memory for a transfer is being deallocated */ -static void usbfs_decrease_memory_usage(unsigned amount) +static void usbfs_decrease_memory_usage(u64 amount) { - atomic_sub(amount, &usbfs_memory_usage); + atomic64_sub(amount, &usbfs_memory_usage); } static int connected(struct usb_dev_state *ps) @@ -1077,7 +1070,7 @@ static int proc_bulk(struct usb_dev_state *ps, void __user *arg) if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) return -EINVAL; len1 = bulk.len; - if (len1 >= USBFS_XFER_MAX) + if (len1 >= (INT_MAX - sizeof(struct urb))) return -EINVAL; ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb)); if (ret) @@ -1420,10 +1413,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb return -EINVAL; } - if (uurb->buffer_length >= USBFS_XFER_MAX) { - ret = -EINVAL; - goto error; - } if (uurb->buffer_length > 0 && !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) { -- GitLab From a98161fdd24462acae1a8f135126dbb2ba833539 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 22 Sep 2017 23:43:25 +0300 Subject: [PATCH 1898/5498] USB: devio: Prevent integer overflow in proc_do_submiturb() commit 57999d1107c1e60c2ca7088f2ac0f819e2f554b3 upstream. There used to be an integer overflow check in proc_do_submiturb() but we removed it. It turns out that it's still required. The uurb->buffer_length variable is a signed integer and it's controlled by the user. It can lead to an integer overflow when we do: num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); If we strip away the macro then that line looks like this: num_sgs = (uurb->buffer_length + USB_SG_SIZE - 1) / USB_SG_SIZE; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It's the first addition which can overflow. Fixes: 1129d270cbfb ("USB: Increase usbfs transfer limit") Signed-off-by: Dan Carpenter Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index afb1d8fac41f..33da87ac31b1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -118,6 +118,9 @@ module_param(usbfs_memory_mb, uint, 0644); MODULE_PARM_DESC(usbfs_memory_mb, "maximum MB allowed for usbfs buffers (0 = no limit)"); +/* Hard limit, necessary to avoid arithmetic overflow */ +#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000) + static atomic64_t usbfs_memory_usage; /* Total memory currently allocated */ /* Check whether it's okay to allocate more memory for a transfer */ @@ -1295,6 +1298,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb USBDEVFS_URB_ZERO_PACKET | USBDEVFS_URB_NO_INTERRUPT)) return -EINVAL; + if ((unsigned int)uurb->buffer_length >= USBFS_XFER_MAX) + return -EINVAL; if (uurb->buffer_length > 0 && !uurb->buffer) return -EINVAL; if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && -- GitLab From c051e2d3d8e86d04a21072a962a6457116d13393 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 23 Nov 2017 16:39:52 +0100 Subject: [PATCH 1899/5498] USB: usbfs: Filter flags passed in from user space commit 446f666da9f019ce2ffd03800995487e79a91462 upstream. USBDEVFS_URB_ISO_ASAP must be accepted only for ISO endpoints. Improve sanity checking. Reported-by: Andrey Konovalov Signed-off-by: Oliver Neukum Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 33da87ac31b1..ba121afd28fc 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1290,14 +1290,18 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb int number_of_packets = 0; unsigned int stream_id = 0; void *buf; - - if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | - USBDEVFS_URB_SHORT_NOT_OK | + unsigned long mask = USBDEVFS_URB_SHORT_NOT_OK | USBDEVFS_URB_BULK_CONTINUATION | USBDEVFS_URB_NO_FSBR | USBDEVFS_URB_ZERO_PACKET | - USBDEVFS_URB_NO_INTERRUPT)) - return -EINVAL; + USBDEVFS_URB_NO_INTERRUPT; + /* USBDEVFS_URB_ISO_ASAP is a special case */ + if (uurb->type == USBDEVFS_URB_TYPE_ISO) + mask |= USBDEVFS_URB_ISO_ASAP; + + if (uurb->flags & ~mask) + return -EINVAL; + if ((unsigned int)uurb->buffer_length >= USBFS_XFER_MAX) return -EINVAL; if (uurb->buffer_length > 0 && !uurb->buffer) -- GitLab From 5b45ab61a85666735663dbdbff8f214f25beb9e2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 7 Nov 2017 16:45:04 +0000 Subject: [PATCH 1900/5498] usb: host: fix incorrect updating of offset commit 1d5a31582ef046d3b233f0da1a68ae26519b2f0a upstream. The variable temp is incorrectly being updated, instead it should be offset otherwise the loop just reads the same capability value and loops forever. Thanks to Alan Stern for pointing out the correct fix to my original fix. Fix also cleans up clang warning: drivers/usb/host/ehci-dbg.c:840:4: warning: Value stored to 'temp' is never read Fixes: d49d43174400 ("USB: misc ehci updates") Signed-off-by: Colin Ian King Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 524cbf26d992..e37395ef5d49 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -850,7 +850,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) default: /* unknown */ break; } - temp = (cap >> 8) & 0xff; + offset = (cap >> 8) & 0xff; } } #endif -- GitLab From 2179863ede7c9ea56d80fbfe9bfc710c269c7e69 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 9 Dec 2017 18:29:48 +0100 Subject: [PATCH 1901/5498] Linux 3.18.87 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ac3ce9725894..bc657f28341b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 86 +SUBLEVEL = 87 EXTRAVERSION = NAME = Diseased Newt -- GitLab From c50c96aa813a2b323dd1bebad289f240291da319 Mon Sep 17 00:00:00 2001 From: Shadab Naseem Date: Tue, 7 Nov 2017 11:56:02 +0530 Subject: [PATCH 1902/5498] ARM: dts: msm: Add DT support for apq8009w BG Alpha Add device tree support for apq8009w BlackGhost Alpha for 512MB variant. Change-Id: I8b2f9a29f5b7e8cc763b85d97606b54a43005a3c Signed-off-by: Shadab Naseem --- arch/arm/boot/dts/qcom/Makefile | 1 + arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts | 251 +++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index bf27682cb062..e2ed50e51cff 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -334,6 +334,7 @@ dtb-$(CONFIG_ARCH_MSM8909) += msm8909-pm8916-mtp.dtb \ msm8909w-wtp-v1.dtb \ apq8009w-bg-wtp-v1.dtb \ apq8009w-bg-wtp-v2.dtb \ + apq8009w-bg-alpha.dtb \ apq8009-mtp-wcd9326.dtb \ msm8909-mtp-wcd9326.dtb \ msm8909-mtp-wcd9326-refboard.dtb \ diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts new file mode 100644 index 000000000000..44215e7beaf3 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "msm8909-mtp.dtsi" +#include "msm8909w-gpu.dtsi" +#include "msm8909w.dtsi" +#include "msm8909w-pm660-mtp.dtsi" +#include "apq8009w-memory.dtsi" +#include "msm8909-audio-bg_codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8009W-PM660 BG Alpha"; + compatible = "qcom,apq8009-mtp", "qcom,apq8009", "qcom,mtp"; + qcom,msm-id = <265 0>, + <301 0>; + qcom,board-id = <8 0x113>; + qcom,pmic-id = <0x0001001b 0x0 0x0 0x0>, + <0x0001011b 0x0 0x0 0x0>; +}; + +&soc { + i2c@78b9000 { /* BLSP1 QUP5 */ + synaptics@20 { + compatible = "synaptics,dsx-i2c"; + reg = <0x20>; + interrupt-parent = <&msm_gpio>; + interrupts = <98 0x2008>; + vdd_ana-supply = <&pm660_l18>; + vcc_i2c-supply = <&pm660_l13>; + synaptics,pwr-reg-name = "vdd_ana"; + synaptics,bus-reg-name = "vcc_i2c"; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend", + "pmx_ts_release"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + pinctrl-2 = <&ts_release>; + synaptics,irq-gpio = <&msm_gpio 98 0x2008>; + synaptics,irq-on-state = <0>; + synaptics,irq-flags = <0x2008>; + synaptics,power-delay-ms = <200>; + synaptics,reset-delay-ms = <200>; + synaptics,max-y-for-2d = <389>; + synaptics,resume-in-workqueue; + synaptics,x-flip; + synaptics,y-flip; + /delete-property/ synaptics,reset-gpio; + /delete-property/ synaptics,display-coords; + /delete-property/ synaptics,panel-coords; + /delete-property/ synaptics,power-down; + /delete-property/ synaptics,disable-gpios; + /delete-property/ synaptics,is_wake; + }; + + /delete-node/ it7260@46; + }; + + qcom,blackghost { + compatible = "qcom,pil-blackghost"; + + qcom,firmware-name = "bg-wear"; + /* GPIO inputs from blackghost */ + qcom,bg2ap-status-gpio = <&msm_gpio 97 0>; + qcom,bg2ap-errfatal-gpio = <&msm_gpio 95 0>; + /* GPIO output to blackghost */ + qcom,ap2bg-status-gpio = <&msm_gpio 17 0>; + qcom,ap2bg-errfatal-gpio = <&msm_gpio 23 0>; + }; + + qcom,msm-ssc-sensors { + compatible = "qcom,msm-ssc-sensors"; + }; + + qcom,glink-bgcom-xprt-bg { + compatible = "qcom,glink-bgcom-xprt"; + label = "bg"; + qcom,qos-config = <&glink_qos_bg>; + qcom,ramp-time = <0x10>, + <0x20>, + <0x30>, + <0x40>; + }; + + glink_qos_bg: qcom,glink-qos-config-bg { + compatible = "qcom,glink-qos-config"; + qcom,flow-info = <0x80 0x0>, + <0x70 0x1>, + <0x60 0x2>, + <0x50 0x3>; + qcom,mtu-size = <0x800>; + qcom,tput-stats-cycle = <0xa>; + }; + + qcom,glink_pkt { + compatible = "qcom,glinkpkt"; + + qcom,glinkpkt-bg-daemon { + qcom,glinkpkt-transport = "bgcom"; + qcom,glinkpkt-edge = "bg"; + qcom,glinkpkt-ch-name = "bg-daemon"; + qcom,glinkpkt-dev-name = "glink_pkt_bg_daemon"; + }; + + qcom,glinkpkt-bg-display-ctrl { + qcom,glinkpkt-transport = "bgcom"; + qcom,glinkpkt-edge = "bg"; + qcom,glinkpkt-ch-name = "display-ctrl"; + qcom,glinkpkt-dev-name = "glink_pkt_bg_display_ctrl"; + }; + + qcom,glinkpkt-bg-display-data { + qcom,glinkpkt-transport = "bgcom"; + qcom,glinkpkt-edge = "bg"; + qcom,glinkpkt-ch-name = "display-data"; + qcom,glinkpkt-dev-name = "glink_pkt_bg_display_data"; + }; + }; + + spi@78B8000 { /* BLSP1 QUP4 */ + status = "ok"; + qcom,bg-spi { + compatible = "qcom,bg-spi"; + reg = <0>; + spi-max-frequency = <16000000>; + interrupt-parent = <&msm_gpio>; + qcom,irq-gpio = <&msm_gpio 110 1>; + }; + }; +}; + +&i2c_1 { + status = "okay"; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&msm_gpio 50 0x00>; + qcom,nq-ven = <&msm_gpio 36 0x00>; + qcom,nq-firm = <&msm_gpio 38 0x00>; + qcom,nq-esepwr = <&msm_gpio 49 0x00>; + qcom,nq-clkreq = <&pm660_gpios 4 0x00>; + qcom,clk-src = "BBCLK3"; + interrupt-parent = <&msm_gpio>; + interrupts = <50 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active","nfc_suspend"; + pinctrl-0 = <&nfcw_int_active &nfcw_disable_active>; + pinctrl-1 = <&nfcw_int_suspend &nfcw_disable_suspend>; + clock-names = "ref_clk"; + }; +}; + +&spi_0 { + status = "disabled"; +}; + +&i2c_3 { + status = "disabled"; +}; + +&i2c_4 { + status = "disabled"; +}; + +&i2c_2 { + status = "disabled"; +}; + +&sdhc_2 { + status = "disabled"; +}; + +&audio_codec_mtp { + status = "disabled"; +}; + +&audio_codec_bg { + status = "ok"; + vdd-spkr-supply = <&pm660_l11>; +}; + +&bg_cdc { + status = "ok"; +}; + +&blsp1_uart1 { + status = "ok"; + pinctrl-names = "default"; + pinctrl-0 = <&uart_console_sleep>; +}; + +/* Pinctrl dt nodes for interrupt and reset gpio for Synaptics controller */ +&ts_int_active { + mux { + pins = "gpio98"; + }; + + config { + pins = "gpio98"; + }; +}; + +&ts_int_suspend { + mux { + pins = "gpio98"; + }; + + config { + pins = "gpio98"; + }; +}; + +&ts_reset_active { + mux { + pins = "gpio16"; + }; + + config { + pins = "gpio16"; + }; +}; + +&ts_reset_suspend { + mux { + pins = "gpio16"; + }; + + config { + pins = "gpio16"; + }; +}; + +&ts_release { + mux { + pins = "gpio98", "gpio16"; + }; + + config { + pins = "gpio98", "gpio16"; + }; +}; -- GitLab From 9b6cd61ca7f55915e84225f47c3337615289a028 Mon Sep 17 00:00:00 2001 From: Ashay Jaiswal Date: Mon, 4 Dec 2017 19:00:33 +0530 Subject: [PATCH 1903/5498] ARM: dts: msm: enable micro USB mode for apq8009w BG Alpha Configure charger in micro USB mode to support micro USB connector. Change-Id: Ia96b7c2c0495b96333a0fc13e79864135a2b051c Signed-off-by: Ashay Jaiswal --- arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts index 44215e7beaf3..f6f5b3b7ccff 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts @@ -249,3 +249,7 @@ pins = "gpio98", "gpio16"; }; }; + +&pm660_charger { + qcom,micro-usb; +}; -- GitLab From 8566b0b12e994f9af842b06f5e1ca7a7aa93cc99 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 11 Dec 2017 12:56:20 +0530 Subject: [PATCH 1904/5498] ARM: dts: msm: Add RSB support for apq8009w BG Alpha Adds rsb Glink channel and make the CS line high on deassertion to support RSB on apq8009w BG Alpha variant. Change-Id: I51aecf50cf9a2779dfdcce99d61c48e61f29b33b Signed-off-by: Arjun Singh --- arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts index f6f5b3b7ccff..bc2aba2a79da 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts @@ -125,6 +125,13 @@ qcom,glinkpkt-ch-name = "display-data"; qcom,glinkpkt-dev-name = "glink_pkt_bg_display_data"; }; + + qcom,glinkpkt-bg-rsb-ctrl { + qcom,glinkpkt-transport = "bgcom"; + qcom,glinkpkt-edge = "bg"; + qcom,glinkpkt-ch-name = "RSB_CTRL"; + qcom,glinkpkt-dev-name = "glink_pkt_bg_rsb_ctrl"; + }; }; spi@78B8000 { /* BLSP1 QUP4 */ @@ -137,6 +144,12 @@ qcom,irq-gpio = <&msm_gpio 110 1>; }; }; + + qcom,bg-rsb { + compatible = "qcom,bg-rsb"; + vdd-ldo1-supply = <&pm660_l11>; + vdd-ldo2-supply = <&pm660_l15>; + }; }; &i2c_1 { @@ -253,3 +266,16 @@ &pm660_charger { qcom,micro-usb; }; + +&spi4_cs0_active { + mux { + pins = "gpio14"; + function = "blsp_spi4"; + }; + config { + pins = "gpio14"; + drive-strength = <2>; + bias-disable; /* No PULL */ + output-high; + }; +}; -- GitLab From dbca13011c474ae3a974d7ef3a32ce2481bdf4d0 Mon Sep 17 00:00:00 2001 From: Surendar Karka Date: Fri, 8 Dec 2017 16:00:37 +0530 Subject: [PATCH 1905/5498] ASoC: msm: qdspqv2: enable secondary group based on group count Enable the secondary port tdm group only if number of groups is more than one. CRs-Fixed: 2147847 Change-Id: I989846f611fab78cbb50a3120c94fc44dc50fb46 Signed-off-by: Surendar karka --- sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index ed0c9752ea14..429559ae39c1 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -5657,6 +5657,10 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, prim_port_id = dai->id; if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; + sec_group_ref = &tdm_group_ref[sec_group_idx]; + } + if ((dai_data->num_group_ports > 1) && + (dai_data->sec_port_enable)) { sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX; sec_group_idx = @@ -5667,7 +5671,6 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, sec_group_id); goto rtn; } - sec_group_ref = &tdm_group_ref[sec_group_idx]; if (atomic_read(sec_group_ref) == 0) { rc = afe_port_group_enable( sec_group_id, @@ -5687,6 +5690,10 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, prim_port_id = dai->id; if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; + sec_group_ref = &tdm_group_ref[sec_group_idx]; + } + if ((dai_data->num_group_ports > 1) && + (dai_data->sec_port_enable)) { sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX; sec_group_idx = @@ -5697,7 +5704,6 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, sec_group_id); goto rtn; } - sec_group_ref = &tdm_group_ref[sec_group_idx]; if (atomic_read(sec_group_ref) == 0) { rc = afe_port_group_enable( sec_group_id, @@ -5814,6 +5820,10 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, prim_port_id = dai->id; if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_TX; + sec_group_ref = &tdm_group_ref[sec_group_idx]; + } + if ((dai_data->num_group_ports > 1) && + (dai_data->sec_port_enable)) { sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX; sec_group_idx = @@ -5824,12 +5834,15 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, __func__, sec_group_id); return; } - sec_group_ref = &tdm_group_ref[sec_group_idx]; } } else { prim_port_id = dai->id; if (dai_data->sec_port_enable) { sec_port_id = AFE_PORT_ID_PRIMARY_TDM_RX; + sec_group_ref = &tdm_group_ref[sec_group_idx]; + } + if ((dai_data->num_group_ports > 1) && + (dai_data->sec_port_enable)) { sec_group_id = AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX; sec_group_idx = @@ -5840,7 +5853,6 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, __func__, sec_group_id); return; } - sec_group_ref = &tdm_group_ref[sec_group_idx]; } } rc = afe_close(prim_port_id); @@ -5878,7 +5890,8 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, __func__, dai->id); } } - if (dai_data->sec_port_enable) { + if ((dai_data->num_group_ports > 1) && + (dai_data->sec_port_enable)) { if (atomic_read(sec_group_ref) == 0) { rc = afe_port_group_enable(sec_group_id, NULL, false); -- GitLab From c0bc15df9c1ea008082df23a09816938decbe4cc Mon Sep 17 00:00:00 2001 From: Shadab Naseem Date: Mon, 11 Dec 2017 15:51:56 +0530 Subject: [PATCH 1906/5498] ARM: dts: msm: Correct copyright year for apq8053 derived files Correct copyright year for apq8053 dragon derived files. Change-Id: I3a0327c9e700d18a377a7f10bfdd1d01ebeefd4e Signed-off-by: Shadab Naseem --- arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi | 2 +- arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi b/arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi index 3b5225f50907..d7db7fb9b0e1 100644 --- a/arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi +++ b/arch/arm/boot/dts/qcom/apq8053-camera-sensor-dragon.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi index 6286015ae2c2..61a7f7328216 100644 --- a/arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi +++ b/arch/arm/boot/dts/qcom/apq8053-lite-dragon.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and -- GitLab From 7fe551c35a3e7a38f71446ae0a55a3080650cba9 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Mon, 11 Dec 2017 15:36:11 +0530 Subject: [PATCH 1907/5498] ASoC: msm: qdspqv2: add route from MM_DL9 to PRI_MI2S_RX Flunece FFV uses MM_DL9 to record data from PRI_MI2S_RX port. Add route between MM_DL9 to PRI_MI2S_RX. CRs-Fixed: 2156759 Change-Id: I1c7a16c15893ef5cb0db00f00a1a8d78325eb328 Signed-off-by: Surendar karka --- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index dfa74d5d6e63..94041de3ffcb 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -11329,6 +11329,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, {"PRI_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, {"PRI_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, {"PRI_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, {"PRI_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, {"PRI_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, -- GitLab From b166cfddc2f94893f2f425e76183c39477392951 Mon Sep 17 00:00:00 2001 From: Baochu Xu Date: Fri, 1 Dec 2017 10:23:49 +0800 Subject: [PATCH 1908/5498] ARM: dts: msm: Modify debug uart for SDW2500 Modify debug uart for SDW2500, SDW2500 change debug uart from BLSP1_UART1 to BLSP1_UART2 Change-Id: I42613ce9a63eca9fd4edb6e656935ebb78a43d70 Signed-off-by: Baochu Xu --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 25dcb67c7910..3c71c6e0e1cd 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -130,9 +130,13 @@ }; &blsp1_uart1 { + status = "disabled"; +}; + +&blsp1_uart2 { status = "ok"; pinctrl-names = "default"; - pinctrl-0 = <&uart_console_sleep>; + pinctrl-0 = <&uart_console2_sleep>; }; /* Pinctrl dt nodes for interrupt & reset gpio for Synaptics touch controller */ -- GitLab From c8878f034af4ac85ee8fe68aefe1732e61eb6ce1 Mon Sep 17 00:00:00 2001 From: Archana Obannagari Date: Tue, 12 Dec 2017 10:59:21 +0530 Subject: [PATCH 1909/5498] ARM: dts: msm: Remove GPU mempool for msm8909 For low memory devices reserving memory can lead to low-memory conditions. So remove GPU mempool to have memory with the system, which will help overall system performance in concurrent use cases. CRs-Fixed: 2146074 Change-Id: I613e1d817b135940729d552a4d8d5a89d72c291c Signed-off-by: Archana Obannagari --- arch/arm/boot/dts/qcom/msm8909-gpu.dtsi | 20 -------------------- arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi | 1 - 2 files changed, 21 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi index 0e0e528a06ea..9f4a397188de 100644 --- a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi @@ -93,26 +93,6 @@ qcom,pm-qos-active-latency = <701>; qcom,pm-qos-wakeup-latency = <701>; - /* GPU Mempools */ - qcom,gpu-mempools { - #address-cells= <1>; - #size-cells = <0>; - compatible = "qcom,gpu-mempools"; - - qcom,mempool-max-pages = <32768>; - - /* 4K Page Pool configuration */ - qcom,gpu-mempool@0 { - reg = <0>; - qcom,mempool-page-size = <4096>; - }; - /* 64K Page Pool configuration */ - qcom,gpu-mempool@1 { - reg = <1>; - qcom,mempool-page-size = <65536>; - }; - }; - /* Power levels */ qcom,gpu-pwrlevels { #address-cells = <1>; diff --git a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi index e926093ec4f6..ff2806d9bcd6 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-gpu.dtsi @@ -32,7 +32,6 @@ <26 512 0 1536000>, <26 512 0 3070000>; - /delete-node/qcom,gpu-mempools; /delete-node/qcom,gpu-pwrlevels; /* Power levels */ -- GitLab From 604e5586733ce9043c91fdfd768d37b002f21bad Mon Sep 17 00:00:00 2001 From: Tharun Kumar Merugu Date: Mon, 11 Dec 2017 15:07:42 +0530 Subject: [PATCH 1910/5498] msm: adsprpc: Use unsigned integer for length values As the length datatype is signed, an attacker can both overflow the calculation or supply a negative number to trick the check into returning an chosen chunk. This can have undesired consequences. Always use unsigned integer types for length values. Change-Id: Ifde2f0d35129014b976507f7723a319c53fabddf Acked-by: Thyagarajan Venkatanarayanan Signed-off-by: Tharun Kumar Merugu --- drivers/char/adsprpc.c | 194 ++++++++++++++++++---------------- drivers/char/adsprpc_compat.c | 14 +-- drivers/char/adsprpc_shared.h | 25 ++--- 3 files changed, 123 insertions(+), 110 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index cb98517c5ee6..58f7455952d2 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -84,11 +84,11 @@ static inline uint64_t buf_page_offset(uint64_t buf) return offset; } -static inline int buf_num_pages(uint64_t buf, ssize_t len) +static inline uint64_t buf_num_pages(uint64_t buf, size_t len) { uint64_t start = buf_page_start(buf) >> PAGE_SHIFT; uint64_t end = (((uint64_t) buf + len - 1) & PAGE_MASK) >> PAGE_SHIFT; - int nPages = end - start + 1; + uint64_t nPages = end - start + 1; return nPages; } @@ -122,7 +122,7 @@ struct fastrpc_buf { struct fastrpc_file *fl; void *virt; uint64_t phys; - ssize_t size; + size_t size; }; struct fastrpc_ctx_lst; @@ -147,7 +147,7 @@ struct smq_invoke_ctx { int *fds; struct fastrpc_mmap **maps; struct fastrpc_buf *buf; - ssize_t used; + size_t used; struct fastrpc_file *fl; uint32_t sc; struct overlap *overs; @@ -229,9 +229,9 @@ struct fastrpc_mmap { struct dma_buf_attachment *attach; struct ion_handle *handle; uint64_t phys; - ssize_t size; + size_t size; uintptr_t va; - ssize_t len; + size_t len; int refs; uintptr_t raddr; int uncached; @@ -276,7 +276,7 @@ static struct fastrpc_channel_ctx gcinfo[NUM_CHANNELS] = { static void fastrpc_buf_free(struct fastrpc_buf *buf, int cache) { - struct fastrpc_file *fl = buf == 0 ? 0 : buf->fl; + struct fastrpc_file *fl = buf == NULL ? NULL : buf->fl; int vmid; if (!fl) @@ -311,7 +311,8 @@ static void fastrpc_buf_list_free(struct fastrpc_file *fl) struct fastrpc_buf *buf, *free; do { struct hlist_node *n; - free = 0; + + free = NULL; spin_lock(&fl->hlock); hlist_for_each_entry_safe(buf, n, &fl->bufs, hn) { hlist_del_init(&buf->hn); @@ -342,11 +343,13 @@ static void fastrpc_mmap_add(struct fastrpc_mmap *map) } static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd, uintptr_t va, - ssize_t len, int mflags, struct fastrpc_mmap **ppmap) + size_t len, int mflags, struct fastrpc_mmap **ppmap) { struct fastrpc_apps *me = &gfa; - struct fastrpc_mmap *match = 0, *map; + struct fastrpc_mmap *match = NULL, *map = NULL; struct hlist_node *n; + if ((va + len) < va) + return -EOVERFLOW; if (mflags == ADSP_MMAP_HEAP_ADDR) { spin_lock(&me->hlock); hlist_for_each_entry_safe(map, n, &me->maps, hn) { @@ -379,10 +382,10 @@ static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd, uintptr_t va, return -ENOTTY; } -static int dma_alloc_memory(phys_addr_t *region_start, ssize_t size) +static int dma_alloc_memory(phys_addr_t *region_start, size_t size) { struct fastrpc_apps *me = &gfa; - void *vaddr = 0; + void *vaddr = NULL; DEFINE_DMA_ATTRS(attrs); if (me->dev == NULL) { @@ -402,9 +405,9 @@ static int dma_alloc_memory(phys_addr_t *region_start, ssize_t size) } static int fastrpc_mmap_remove(struct fastrpc_file *fl, uintptr_t va, - ssize_t len, struct fastrpc_mmap **ppmap) + size_t len, struct fastrpc_mmap **ppmap) { - struct fastrpc_mmap *match = 0, *map; + struct fastrpc_mmap *match = NULL, *map = NULL; struct hlist_node *n; struct fastrpc_apps *me = &gfa; @@ -511,11 +514,11 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map) } static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, uintptr_t va, - ssize_t len, int mflags, struct fastrpc_mmap **ppmap) + size_t len, int mflags, struct fastrpc_mmap **ppmap) { struct fastrpc_apps *me = &gfa; struct fastrpc_session_ctx *sess = fl->sctx; - struct fastrpc_mmap *map = 0; + struct fastrpc_mmap *map = NULL; struct dma_attrs attrs; phys_addr_t region_start = 0; unsigned long flags; @@ -534,7 +537,7 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, uintptr_t va, map->fd = fd; if (mflags == ADSP_MMAP_HEAP_ADDR) { map->apps = me; - map->fl = 0; + map->fl = NULL; VERIFY(err, !dma_alloc_memory(®ion_start, len)); if (err) goto bail; @@ -609,11 +612,11 @@ bail: return err; } -static int fastrpc_buf_alloc(struct fastrpc_file *fl, ssize_t size, +static int fastrpc_buf_alloc(struct fastrpc_file *fl, size_t size, struct fastrpc_buf **obuf) { int err = 0, vmid; - struct fastrpc_buf *buf = 0, *fr = 0; + struct fastrpc_buf *buf = NULL, *fr = NULL; struct hlist_node *n; VERIFY(err, size > 0); @@ -633,13 +636,13 @@ static int fastrpc_buf_alloc(struct fastrpc_file *fl, ssize_t size, *obuf = fr; return 0; } - buf = 0; - VERIFY(err, buf = kzalloc(sizeof(*buf), GFP_KERNEL)); + buf = NULL; + VERIFY(err, NULL != (buf = kzalloc(sizeof(*buf), GFP_KERNEL))); if (err) goto bail; INIT_HLIST_NODE(&buf->hn); buf->fl = fl; - buf->virt = 0; + buf->virt = NULL; buf->phys = 0; buf->size = size; buf->virt = dma_alloc_coherent(fl->sctx->dev, buf->size, @@ -680,7 +683,7 @@ static int context_restore_interrupted(struct fastrpc_file *fl, struct smq_invoke_ctx **po) { int err = 0; - struct smq_invoke_ctx *ctx = 0, *ictx = 0; + struct smq_invoke_ctx *ctx = NULL, *ictx = NULL; struct hlist_node *n; struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; spin_lock(&fl->hlock); @@ -733,7 +736,7 @@ static int context_build_overlap(struct smq_invoke_ctx *ctx) ctx->overs[i].raix = i; ctx->overps[i] = &ctx->overs[i]; } - sort(ctx->overps, nbufs, sizeof(*ctx->overps), overlap_ptr_cmp, 0); + sort(ctx->overps, nbufs, sizeof(*ctx->overps), overlap_ptr_cmp, NULL); max.start = 0; max.end = 0; for (i = 0; i < nbufs; ++i) { @@ -762,7 +765,8 @@ bail: #define K_COPY_FROM_USER(err, kernel, dst, src, size) \ do {\ if (!(kernel))\ - VERIFY(err, 0 == copy_from_user((dst), (src),\ + VERIFY(err, 0 == copy_from_user((dst),\ + (void const __user *)(src),\ (size)));\ else\ memmove((dst), (src), (size));\ @@ -771,8 +775,8 @@ bail: #define K_COPY_TO_USER(err, kernel, dst, src, size) \ do {\ if (!(kernel))\ - VERIFY(err, 0 == copy_to_user((dst), (src),\ - (size)));\ + VERIFY(err, 0 == copy_to_user((void __user *)(dst), \ + (src), (size)));\ else\ memmove((dst), (src), (size));\ } while (0) @@ -785,7 +789,7 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, struct smq_invoke_ctx **po) { int err = 0, bufs, size = 0; - struct smq_invoke_ctx *ctx = 0; + struct smq_invoke_ctx *ctx = NULL; struct fastrpc_ctx_lst *clst = &fl->clst; struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; @@ -795,7 +799,7 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, sizeof(*ctx->overs) * (bufs) + sizeof(*ctx->overps) * (bufs); - VERIFY(err, ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL)); + VERIFY(err, NULL != (ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL))); if (err) goto bail; @@ -808,7 +812,7 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, ctx->overs = (struct overlap *)(&ctx->fds[bufs]); ctx->overps = (struct overlap **)(&ctx->overs[bufs]); - K_COPY_FROM_USER(err, kernel, ctx->lpra, invoke->pra, + K_COPY_FROM_USER(err, kernel, (void *)ctx->lpra, invoke->pra, bufs * sizeof(*ctx->lpra)); if (err) goto bail; @@ -909,10 +913,10 @@ static void context_list_ctor(struct fastrpc_ctx_lst *me) static void fastrpc_context_list_dtor(struct fastrpc_file *fl) { struct fastrpc_ctx_lst *clst = &fl->clst; - struct smq_invoke_ctx *ictx = 0, *ctxfree; + struct smq_invoke_ctx *ictx = NULL, *ctxfree; struct hlist_node *n; do { - ctxfree = 0; + ctxfree = NULL; spin_lock(&fl->hlock); hlist_for_each_entry_safe(ictx, n, &clst->interrupted, hn) { hlist_del_init(&ictx->hn); @@ -924,7 +928,7 @@ static void fastrpc_context_list_dtor(struct fastrpc_file *fl) context_free(ctxfree); } while (ctxfree); do { - ctxfree = 0; + ctxfree = NULL; spin_lock(&fl->hlock); hlist_for_each_entry_safe(ictx, n, &clst->pending, hn) { hlist_del_init(&ictx->hn); @@ -943,7 +947,7 @@ static void fastrpc_file_list_dtor(struct fastrpc_apps *me) struct fastrpc_file *fl, *free; struct hlist_node *n; do { - free = 0; + free = NULL; spin_lock(&me->hlock); hlist_for_each_entry_safe(fl, n, &me->drivers, hn) { hlist_del_init(&fl->hn); @@ -967,31 +971,32 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) int outbufs = REMOTE_SCALARS_OUTBUFS(sc); int bufs = inbufs + outbufs; uintptr_t args; - ssize_t rlen = 0, copylen = 0, metalen = 0; + size_t rlen = 0, copylen = 0, metalen = 0; int i, inh, oix; int err = 0; int mflags = 0; /* calculate size of the metadata */ - rpra = 0; + rpra = NULL; list = smq_invoke_buf_start(rpra, sc); pages = smq_phy_page_start(sc, list); ipage = pages; for (i = 0; i < bufs; ++i) { uintptr_t buf = (uintptr_t)lpra[i].buf.pv; - ssize_t len = lpra[i].buf.len; + size_t len = lpra[i].buf.len; if (ctx->fds[i]) fastrpc_mmap_create(ctx->fl, ctx->fds[i], buf, len, mflags, &ctx->maps[i]); ipage += 1; } - metalen = copylen = (ssize_t)&ipage[0]; + metalen = copylen = (size_t)&ipage[0]; /* calculate len requreed for copying */ for (oix = 0; oix < inbufs + outbufs; ++oix) { int i = ctx->overps[oix]->raix; uintptr_t mstart, mend; - ssize_t len = lpra[i].buf.len; + size_t len = lpra[i].buf.len; + if (!len) continue; if (ctx->maps[i]) @@ -1024,7 +1029,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) ipage = pages; args = (uintptr_t)ctx->buf->virt + metalen; for (i = 0; i < bufs; ++i) { - ssize_t len = lpra[i].buf.len; + size_t len = lpra[i].buf.len; list[i].num = 0; list[i].pgidx = 0; if (!len) @@ -1037,7 +1042,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) for (i = 0; i < inbufs + outbufs; ++i) { struct fastrpc_mmap *map = ctx->maps[i]; uint64_t buf = ptr_to_uint64(lpra[i].buf.pv); - ssize_t len = lpra[i].buf.len; + size_t len = lpra[i].buf.len; rpra[i].buf.pv = 0; rpra[i].buf.len = len; if (!len) @@ -1045,7 +1050,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) if (map) { struct vm_area_struct *vma; uintptr_t offset; - int num = buf_num_pages(buf, len); + uint64_t num = buf_num_pages(buf, len); int idx = list[i].pgidx; down_read(¤t->mm->mmap_sem); @@ -1071,9 +1076,10 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) for (oix = 0; oix < inbufs + outbufs; ++oix) { int i = ctx->overps[oix]->raix; struct fastrpc_mmap *map = ctx->maps[i]; - ssize_t mlen; + size_t mlen; uint64_t buf; - ssize_t len = lpra[i].buf.len; + size_t len = lpra[i].buf.len; + if (!len) continue; if (map) @@ -1153,7 +1159,7 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx, goto bail; } else { fastrpc_mmap_free(ctx->maps[i]); - ctx->maps[i] = 0; + ctx->maps[i] = NULL; } } size = sizeof(*rpra) * REMOTE_SCALARS_OUTHANDLES(sc); @@ -1252,7 +1258,7 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx, struct fastrpc_file *fl = ctx->fl; int err = 0, len; - VERIFY(err, 0 != fl->apps->channel[fl->cid].chan); + VERIFY(err, NULL != fl->apps->channel[fl->cid].chan); if (err) goto bail; msg->pid = current->tgid; @@ -1335,13 +1341,13 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode, uint32_t kernel, struct fastrpc_ioctl_invoke_fd *invokefd) { - struct smq_invoke_ctx *ctx = 0; + struct smq_invoke_ctx *ctx = NULL; struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; int cid = fl->cid; int interrupted = 0; int err = 0; - VERIFY(err, fl->sctx); + VERIFY(err, fl->sctx != NULL); if (err) goto bail; VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS); @@ -1410,7 +1416,7 @@ static int fastrpc_init_process(struct fastrpc_file *fl, int err = 0; struct fastrpc_ioctl_invoke_fd ioctl; struct smq_phy_page pages[1]; - struct fastrpc_mmap *file = 0, *mem = 0; + struct fastrpc_mmap *file = NULL, *mem = NULL; if (init->flags == FASTRPC_INIT_ATTACH) { remote_arg_t ra[1]; int tgid = current->tgid; @@ -1419,7 +1425,7 @@ static int fastrpc_init_process(struct fastrpc_file *fl, ioctl.inv.handle = 1; ioctl.inv.sc = REMOTE_SCALARS_MAKE(0, 1, 0); ioctl.inv.pra = ra; - ioctl.fds = 0; + ioctl.fds = NULL; VERIFY(err, !(err = fastrpc_internal_invoke(fl, FASTRPC_MODE_PARALLEL, 1, &ioctl))); if (err) @@ -1493,7 +1499,7 @@ static int fastrpc_release_current_dsp_process(struct fastrpc_file *fl) remote_arg_t ra[1]; int tgid = 0; - VERIFY(err, fl->apps->channel[fl->cid].chan != 0); + VERIFY(err, fl->apps->channel[fl->cid].chan != NULL); if (err) goto bail; tgid = fl->tgid; @@ -1502,7 +1508,7 @@ static int fastrpc_release_current_dsp_process(struct fastrpc_file *fl) ioctl.inv.handle = 1; ioctl.inv.sc = REMOTE_SCALARS_MAKE(1, 1, 0); ioctl.inv.pra = ra; - ioctl.fds = 0; + ioctl.fds = NULL; VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, FASTRPC_MODE_PARALLEL, 1, &ioctl))); bail: @@ -1547,7 +1553,7 @@ static int fastrpc_mmap_on_dsp(struct fastrpc_file *fl, uint32_t flags, else ioctl.inv.sc = REMOTE_SCALARS_MAKE(2, 2, 1); ioctl.inv.pra = ra; - ioctl.fds = 0; + ioctl.fds = NULL; VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, FASTRPC_MODE_PARALLEL, 1, &ioctl))); map->raddr = (uintptr_t)routargs.vaddrout; @@ -1585,7 +1591,7 @@ static int fastrpc_munmap_on_dsp_rh(struct fastrpc_file *fl, ioctl.inv.handle = 1; ioctl.inv.sc = REMOTE_SCALARS_MAKE(7, 0, 1); ioctl.inv.pra = ra; - ioctl.fds = 0; + ioctl.fds = NULL; VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, FASTRPC_MODE_PARALLEL, 1, &ioctl))); @@ -1612,7 +1618,7 @@ static int fastrpc_munmap_on_dsp(struct fastrpc_file *fl, struct { int pid; uintptr_t vaddrout; - ssize_t size; + size_t size; } inargs; if (map->flags == ADSP_MMAP_HEAP_ADDR) { VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, map)); @@ -1632,7 +1638,7 @@ static int fastrpc_munmap_on_dsp(struct fastrpc_file *fl, else ioctl.inv.sc = REMOTE_SCALARS_MAKE(3, 1, 0); ioctl.inv.pra = ra; - ioctl.fds = 0; + ioctl.fds = NULL; VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, FASTRPC_MODE_PARALLEL, 1, &ioctl))); bail: @@ -1641,7 +1647,7 @@ bail: static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl) { - struct fastrpc_mmap *match = 0, *map = NULL; + struct fastrpc_mmap *match = NULL, *map = NULL; struct hlist_node *n = NULL; int err = 0, ret = 0; struct fastrpc_apps *me = &gfa; @@ -1682,7 +1688,7 @@ bail: } static int fastrpc_mmap_remove(struct fastrpc_file *fl, uintptr_t va, - ssize_t len, struct fastrpc_mmap **ppmap); + size_t len, struct fastrpc_mmap **ppmap); static void fastrpc_mmap_add(struct fastrpc_mmap *map); @@ -1690,7 +1696,7 @@ static int fastrpc_internal_munmap(struct fastrpc_file *fl, struct fastrpc_ioctl_munmap *ud) { int err = 0; - struct fastrpc_mmap *map = 0; + struct fastrpc_mmap *map = NULL; if (!fastrpc_mmap_remove(fl, ud->vaddrout, ud->size, &map)) { VERIFY(err, !fastrpc_munmap_on_dsp(fl, map)); @@ -1708,7 +1714,7 @@ static int fastrpc_internal_mmap(struct fastrpc_file *fl, struct fastrpc_ioctl_mmap *ud) { - struct fastrpc_mmap *map = 0; + struct fastrpc_mmap *map = NULL; int err = 0; if (!fastrpc_mmap_find(fl, ud->fd, (uintptr_t)ud->vaddrin, ud->size, ud->flags, &map)) @@ -1741,7 +1747,7 @@ static void fastrpc_channel_close(struct kref *kref) glink_unregister_link_state_cb(ctx->link_notify_handle); glink_close(ctx->chan); } - ctx->chan = 0; + ctx->chan = NULL; mutex_unlock(&me->smd_mutex); cid = ctx - &gcinfo[0]; pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name, @@ -1753,7 +1759,7 @@ static void fastrpc_context_list_dtor(struct fastrpc_file *fl); static int fastrpc_file_free(struct fastrpc_file *fl) { struct hlist_node *n; - struct fastrpc_mmap *map = 0; + struct fastrpc_mmap *map = NULL; int cid; if (!fl) @@ -1829,19 +1835,20 @@ static int fastrpc_session_free(struct fastrpc_channel_ctx *chan, int session) return err; } -bool fastrpc_glink_notify_rx_intent_req(void *h, const void *priv, size_t size) +static bool fastrpc_glink_notify_rx_intent_req(void *h, const void *priv, + size_t size) { if (0 != glink_queue_rx_intent(h, NULL, size)) return false; return true; } -void fastrpc_glink_notify_tx_done(void *handle, const void *priv, +static void fastrpc_glink_notify_tx_done(void *handle, const void *priv, const void *pkt_priv, const void *ptr) { } -void fastrpc_glink_notify_rx(void *handle, const void *priv, +static void fastrpc_glink_notify_rx(void *handle, const void *priv, const void *pkt_priv, const void *ptr, size_t size) { struct smq_invoke_rsp *rsp = (struct smq_invoke_rsp *)ptr; @@ -1855,7 +1862,8 @@ void fastrpc_glink_notify_rx(void *handle, const void *priv, glink_rx_done(handle, ptr, true); } -void fastrpc_glink_notify_state(void *handle, const void *priv, unsigned event) +static void fastrpc_glink_notify_state(void *handle, const void *priv, + unsigned int event) { struct fastrpc_apps *me = &gfa; int cid = (int)(uintptr_t)priv; @@ -1876,7 +1884,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) { struct fastrpc_apps *me = &gfa; struct fastrpc_file *fl = (struct fastrpc_file *)file->private_data; - struct fastrpc_fl *pfl = 0; + struct fastrpc_fl *pfl = NULL; int session, cid; int err = 0; @@ -1910,7 +1918,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) } me->pending_free++; mutex_unlock(&me->flfree_mutex); - file->private_data = 0; + file->private_data = NULL; } bail: if (err) { @@ -1924,8 +1932,8 @@ bail: static void file_free_work_handler(struct work_struct *w) { struct fastrpc_apps *me = &gfa; - struct fastrpc_fl *fl = 0, *freefl = 0; - struct hlist_node *n = 0; + struct fastrpc_fl *fl = NULL, *freefl = NULL; + struct hlist_node *n = NULL; while (1) { mutex_lock(&me->flfree_mutex); @@ -2007,7 +2015,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) int err = 0, session; int event; struct fastrpc_apps *me = &gfa; - struct fastrpc_file *fl = 0; + struct fastrpc_file *fl = NULL; if (me->pending_free) { event = wait_event_interruptible_timeout(wait_queue, @@ -2039,7 +2047,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) fl->ssrcount = me->channel[cid].ssrcount; if ((kref_get_unless_zero(&me->channel[cid].kref) == 0) || - (me->channel[cid].chan == 0)) { + (me->channel[cid].chan == NULL)) { if (me->glink) { VERIFY(err, 0 == fastrpc_glink_open(cid, me)); } else { @@ -2055,7 +2063,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) VERIFY(err, wait_for_completion_timeout(&me->channel[cid].work, RPC_TIMEOUT)); if (err) { - me->channel[cid].chan = 0; + me->channel[cid].chan = NULL; goto bail; } kref_init(&me->channel[cid].kref); @@ -2114,7 +2122,7 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num, p.invokefd.fds = 0; size = (ioctl_num == FASTRPC_IOCTL_INVOKE) ? sizeof(p.invokefd.inv) : sizeof(p.invokefd); - VERIFY(err, 0 == copy_from_user(&p.invokefd, param, size)); + K_COPY_FROM_USER(err, 0, &p.invokefd, param, size); if (err) goto bail; VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, fl->mode, @@ -2123,20 +2131,20 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num, goto bail; break; case FASTRPC_IOCTL_MMAP: - VERIFY(err, 0 == copy_from_user(&p.mmap, param, - sizeof(p.mmap))); + K_COPY_FROM_USER(err, 0, &p.mmap, param, + sizeof(p.mmap)); if (err) goto bail; VERIFY(err, 0 == (err = fastrpc_internal_mmap(fl, &p.mmap))); if (err) goto bail; - VERIFY(err, 0 == copy_to_user(param, &p.mmap, sizeof(p.mmap))); + K_COPY_TO_USER(err, 0, param, &p.mmap, sizeof(p.mmap)); if (err) goto bail; break; case FASTRPC_IOCTL_MUNMAP: - VERIFY(err, 0 == copy_from_user(&p.munmap, param, - sizeof(p.munmap))); + K_COPY_FROM_USER(err, 0, &p.munmap, param, + sizeof(p.munmap)); if (err) goto bail; VERIFY(err, 0 == (err = fastrpc_internal_munmap(fl, @@ -2156,10 +2164,13 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num, } break; case FASTRPC_IOCTL_GETINFO: + K_COPY_FROM_USER(err, 0, &info, param, sizeof(info)); + if (err) + goto bail; VERIFY(err, 0 == (err = fastrpc_get_info(fl, &info))); if (err) goto bail; - VERIFY(err, 0 == copy_to_user(param, &info, sizeof(info))); + K_COPY_TO_USER(err, 0, param, &info, sizeof(info)); if (err) goto bail; break; @@ -2203,7 +2214,7 @@ static int fastrpc_restart_notifier_cb(struct notifier_block *nb, } else { smd_close(ctx->chan); } - ctx->chan = 0; + ctx->chan = NULL; pr_info("'restart notifier: closed /dev/%s c %d %d'\n", gcinfo[cid].name, MAJOR(me->dev_no), cid); } @@ -2259,7 +2270,8 @@ static int fastrpc_cb_probe(struct device *dev) int err = 0, i; int disable_htw = 1; - VERIFY(err, 0 != (name = of_get_property(dev->of_node, "label", NULL))); + VERIFY(err, NULL != (name = of_get_property(dev->of_node, + "label", NULL))); if (err) goto bail; for (i = 0; i < NUM_CHANNELS; i++) { @@ -2306,11 +2318,11 @@ static int fastrpc_cb_legacy_probe(struct device *dev) { struct device_node *domains_child_node = NULL; struct device_node *ctx_node = NULL; - struct fastrpc_channel_ctx *chan; - struct fastrpc_session_ctx *first_sess, *sess; - const char *name; - unsigned int *range = 0, range_size = 0; - unsigned int *sids = 0, sids_size = 0; + struct fastrpc_channel_ctx *chan = NULL; + struct fastrpc_session_ctx *first_sess = NULL, *sess = NULL; + const char *name = NULL; + unsigned int *range = NULL, range_size = 0; + unsigned int *sids = NULL, sids_size = 0; int err = 0, ret = 0, i; int disable_htw = 1; @@ -2485,17 +2497,17 @@ static void fastrpc_deinit(void) if (chan->chan) { kref_put_mutex(&chan->kref, fastrpc_channel_close, &me->smd_mutex); - chan->chan = 0; + chan->chan = NULL; } for (j = 0; j < NUM_SESSIONS; j++) { struct fastrpc_session_ctx *sess = &chan->session[j]; if (sess->smmu.enabled) { arm_iommu_detach_device(sess->dev); - sess->dev = 0; + sess->dev = NULL; } if (sess->smmu.mapping) { arm_iommu_release_mapping(sess->smmu.mapping); - sess->smmu.mapping = 0; + sess->smmu.mapping = NULL; } } } @@ -2553,7 +2565,7 @@ static int __init fastrpc_device_init(void) me->channel[i].ssrcount = 0; me->channel[i].prevssrcount = 0; me->channel[i].ramdumpenabled = 0; - me->channel[i].remoteheap_ramdump_dev = 0; + me->channel[i].remoteheap_ramdump_dev = NULL; me->channel[i].nb.notifier_call = fastrpc_restart_notifier_cb; me->channel[i].handle = subsys_notif_register_notifier( gcinfo[i].subsys, diff --git a/drivers/char/adsprpc_compat.c b/drivers/char/adsprpc_compat.c index b0c3381d14e7..d73e508f2d29 100644 --- a/drivers/char/adsprpc_compat.c +++ b/drivers/char/adsprpc_compat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -33,7 +33,7 @@ struct compat_remote_buf { compat_uptr_t pv; /* buffer pointer */ - compat_ssize_t len; /* length of buffer */ + compat_size_t len; /* length of buffer */ }; union compat_remote_arg { @@ -56,13 +56,13 @@ struct compat_fastrpc_ioctl_mmap { compat_int_t fd; /* ion fd */ compat_uint_t flags; /* flags for dsp to map with */ compat_uptr_t vaddrin; /* optional virtual address */ - compat_ssize_t size; /* size */ + compat_size_t size; /* size */ compat_uptr_t vaddrout; /* dsps virtual address */ }; struct compat_fastrpc_ioctl_munmap { compat_uptr_t vaddrout; /* address to unmap */ - compat_ssize_t size; /* size */ + compat_size_t size; /* size */ }; struct compat_fastrpc_ioctl_init { @@ -81,7 +81,7 @@ static int compat_get_fastrpc_ioctl_invoke( unsigned int cmd) { compat_uint_t u, sc; - compat_ssize_t s; + compat_size_t s; compat_uptr_t p; struct fastrpc_ioctl_invoke_fd *inv; union compat_remote_arg *pra32; @@ -164,7 +164,7 @@ static int compat_get_fastrpc_ioctl_mmap( { compat_uint_t u; compat_int_t i; - compat_ssize_t s; + compat_size_t s; compat_uptr_t p; int err; @@ -198,7 +198,7 @@ static int compat_get_fastrpc_ioctl_munmap( struct fastrpc_ioctl_munmap __user *unmap) { compat_uptr_t p; - compat_ssize_t s; + compat_size_t s; int err; err = get_user(p, &unmap32->vaddrout); diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h index f5a4ce282199..6fe8b232546d 100644 --- a/drivers/char/adsprpc_shared.h +++ b/drivers/char/adsprpc_shared.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -95,7 +95,7 @@ do {\ struct remote_buf64 { uint64_t pv; - int64_t len; + uint64_t len; }; union remote_arg64 { @@ -107,7 +107,7 @@ union remote_arg64 { struct remote_buf { void *pv; /* buffer pointer */ - ssize_t len; /* length of buffer */ + size_t len; /* length of buffer */ }; union remote_arg { @@ -128,25 +128,25 @@ struct fastrpc_ioctl_invoke_fd { struct fastrpc_ioctl_init { uint32_t flags; /* one of FASTRPC_INIT_* macros */ - uintptr_t __user file; /* pointer to elf file */ - int32_t filelen; /* elf file length */ + uintptr_t file; /* pointer to elf file */ + uint32_t filelen; /* elf file length */ int32_t filefd; /* ION fd for the file */ - uintptr_t __user mem; /* mem for the PD */ - int32_t memlen; /* mem length */ + uintptr_t mem; /* mem for the PD */ + uint32_t memlen; /* mem length */ int32_t memfd; /* ION fd for the mem */ }; struct fastrpc_ioctl_munmap { uintptr_t vaddrout; /* address to unmap */ - ssize_t size; /* size */ + size_t size; /* size */ }; struct fastrpc_ioctl_mmap { int fd; /* ion fd */ uint32_t flags; /* flags for dsp to map with */ - uintptr_t __user *vaddrin; /* optional virtual address */ - ssize_t size; /* size */ + uintptr_t vaddrin; /* optional virtual address */ + size_t size; /* size */ uintptr_t vaddrout; /* dsps virtual address */ }; @@ -185,14 +185,15 @@ struct smq_invoke_rsp { static inline struct smq_invoke_buf *smq_invoke_buf_start(remote_arg64_t *pra, uint32_t sc) { - int len = REMOTE_SCALARS_LENGTH(sc); + uint32_t len = REMOTE_SCALARS_LENGTH(sc); + return (struct smq_invoke_buf *)(&pra[len]); } static inline struct smq_phy_page *smq_phy_page_start(uint32_t sc, struct smq_invoke_buf *buf) { - int nTotal = REMOTE_SCALARS_INBUFS(sc) + REMOTE_SCALARS_OUTBUFS(sc); + uint32_t nTotal = REMOTE_SCALARS_INBUFS(sc)+REMOTE_SCALARS_OUTBUFS(sc); return (struct smq_phy_page *)(&buf[nTotal]); } -- GitLab From cf234890cfe7751ac4a4682a45c5e474705b6e84 Mon Sep 17 00:00:00 2001 From: Chandana Kishori Chiluveru Date: Fri, 27 Oct 2017 17:36:27 +0530 Subject: [PATCH 1911/5498] usb: f_gsi: Fix BUG_ON for calling sleeping function in invalid context In Current code driver calling spin_lock_irqsave two time with different lock. This will lead an issue where irqs remains disabled on processor. After that calling queu_work function reporting bug with sleeping function called from invalid context. Fix this by using spin_lcck instead of spin_lock_irq_save. Change-Id: I68f05283fe541eb5af123a4ce836ee0fc1dd7e48 Signed-off-by: Chandana Kishori Chiluveru --- drivers/usb/gadget/function/f_gsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 64f010a7e26f..429535e1e4e6 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -632,12 +632,12 @@ int ipa_usb_notify_cb(enum ipa_usb_notify_event event, return -ENOMEM; } cpkt_notify_speed->type = GSI_CTRL_NOTIFY_SPEED; - spin_lock_irqsave(&gsi->c_port.lock, flags); + spin_lock(&gsi->c_port.lock); list_add_tail(&cpkt_notify_connect->list, &gsi->c_port.cpkt_resp_q); list_add_tail(&cpkt_notify_speed->list, &gsi->c_port.cpkt_resp_q); - spin_unlock_irqrestore(&gsi->c_port.lock, flags); + spin_unlock(&gsi->c_port.lock); gsi_ctrl_send_notification(gsi); } -- GitLab From 7cd509e2c8f0972d4f75c29de7206f0d88e7f540 Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Wed, 13 Dec 2017 09:44:06 +0530 Subject: [PATCH 1912/5498] msm: mdss: enable pll regulator during phy config Make sure to enable the pll digital block during phy on sequence. This fixes some corruption observed as part of resume for cmd mode dual dsi panel, where panel does not support phy power off during suspend. Change-Id: I246917fc06e9fe79a952fe28296de3813a2e2081 Signed-off-by: Raghavendra Ambadas --- drivers/video/msm/mdss/msm_mdss_io_8974.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c index fcc8545a12ed..5520a7516dd5 100644 --- a/drivers/video/msm/mdss/msm_mdss_io_8974.c +++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c @@ -1035,6 +1035,8 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl) int j, off, ln, cnt, ln_off; char *ip; void __iomem *base; + u32 data; + struct mdss_panel_info *pinfo; pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); @@ -1118,7 +1120,13 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl) mdss_dsi_8996_pll_source_standalone(ctrl); } - MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0x7f); + pinfo = &ctrl->panel_data.panel_info; + if (!(pinfo->allow_phy_power_off) && (pinfo->type == MIPI_CMD_PANEL)) { + data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0); + MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data | 0x7f); + } else { + MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0x7f); + } wmb(); /* make sure registers committed */ } -- GitLab From b6b377f38a60bb8cca42a72602263ef57ae2c66b Mon Sep 17 00:00:00 2001 From: Meera Gande Date: Wed, 13 Dec 2017 15:49:41 +0530 Subject: [PATCH 1913/5498] msm: camerav2: isp: Allocate UB for all source types While configuring UB for write masters, we are trying to allocate UB for a particular source (pix/rdi). This is causing the ub_offset to reset for next source allocation and causing both the source types to have ub allocated in same depth. Change-Id: Ic0b85b27516837ce320d8d87afb2701910fcb532 Signed-off-by: Meera Gande --- drivers/media/platform/msm/camera_v2/isp/msm_isp40.c | 1 + drivers/media/platform/msm/camera_v2/isp/msm_isp47.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index 299c576fb5bc..c67e8f0b0ff3 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -1512,6 +1512,7 @@ static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev, msm_camera_io_w_mb((update_state == DISABLE_CAMIF ? 0x0 : 0x6), vfe_dev->vfe_base + 0x2F4); vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0; + vfe_dev->axi_data.src_info[VFE_PIX_0].flag = 0; /* testgen OFF*/ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) msm_camera_io_w(1 << 1, vfe_dev->vfe_base + 0x93C); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index b63e8ef97f5a..8cf02ec0e34b 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -1728,8 +1728,7 @@ void msm_vfe47_cfg_axi_ub_equal_default( vfe_dev->hw_info->vfe_ops.axi_ops. ub_reg_offset(vfe_dev, i)); } - if (!axi_data->free_wm[i] || frame_src != SRC_TO_INTF( - HANDLE_TO_IDX(axi_data->free_wm[i]))) + if (!axi_data->free_wm[i]) continue; delta = (uint64_t)axi_data->wm_image_size[i] * -- GitLab From 5a2f8cf1efdf3d3afb3b981d375aaf00c9952505 Mon Sep 17 00:00:00 2001 From: Sachin Bhayare Date: Wed, 13 Dec 2017 10:17:08 +0530 Subject: [PATCH 1914/5498] msm: mdss: release retire fence during commit failure In existing implementation on commit failure release fence are signaled. But retire fence is not signaled. Which block the framework from submiting next commit. And result in UI freez. Add logic to signal retire fence along with release fence commit fails. Change-Id: I84774280a110339450a1c5ef50eab68168e9bd20 Signed-off-by: Sachin Bhayare Signed-off-by: Nirmal Abraham --- drivers/video/msm/mdss/mdp3_ctrl.c | 1 + drivers/video/msm/mdss/mdss_fb.c | 3 +++ drivers/video/msm/mdss/mdss_fb.h | 2 ++ drivers/video/msm/mdss/mdss_mdp_overlay.c | 13 ++++++++++++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index 3c066d07e6bd..7177c694c3f9 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -2852,6 +2852,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) mdp3_interface->lut_update = NULL; mdp3_interface->configure_panel = mdp3_update_panel_info; mdp3_interface->input_event_handler = NULL; + mdp3_interface->signal_retire_fence = NULL; mdp3_session = kzalloc(sizeof(struct mdp3_session_data), GFP_KERNEL); if (!mdp3_session) { diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index 2db552e280cf..84e776f8e00b 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -3646,6 +3646,9 @@ skip_commit: if (IS_ERR_VALUE(ret) || !sync_pt_data->flushed) { mdss_fb_release_kickoff(mfd); mdss_fb_signal_timeline(sync_pt_data); + if ((mfd->panel.type == MIPI_CMD_PANEL) && + (mfd->mdp.signal_retire_fence)) + mfd->mdp.signal_retire_fence(mfd, 1); } if (dynamic_dsi_switch) { diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h index b03b3b571f27..0b5528a195bd 100644 --- a/drivers/video/msm/mdss/mdss_fb.h +++ b/drivers/video/msm/mdss/mdss_fb.h @@ -233,6 +233,8 @@ struct msm_mdp_interface { int dest_ctrl); int (*input_event_handler)(struct msm_fb_data_type *mfd); int (*pp_release_fnc)(struct msm_fb_data_type *mfd); + void (*signal_retire_fence)(struct msm_fb_data_type *mfd, + int retire_cnt); void *private1; }; diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c index 7cf206b00051..0f3e4385a788 100644 --- a/drivers/video/msm/mdss/mdss_mdp_overlay.c +++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c @@ -6337,8 +6337,11 @@ static void __vsync_retire_signal(struct msm_fb_data_type *mfd, int val) mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex); if (mdp5_data->retire_cnt > 0) { sw_sync_timeline_inc(mdp5_data->vsync_timeline, val); - mdp5_data->retire_cnt -= min(val, mdp5_data->retire_cnt); + pr_debug("Retire signaled! timeline val=%d remaining=%d\n", + mdp5_data->vsync_timeline->value, + mdp5_data->retire_cnt); + if (mdp5_data->retire_cnt == 0) { mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); mdp5_data->ctl->ops.remove_vsync_handler(mdp5_data->ctl, @@ -6530,6 +6533,13 @@ int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd) return rc; } +static void mdss_mdp_signal_retire_fence(struct msm_fb_data_type *mfd, + int retire_cnt) +{ + __vsync_retire_signal(mfd, retire_cnt); + pr_debug("Signaled (%d) pending retire fence\n", retire_cnt); +} + int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) { struct device *dev = mfd->fbi->dev; @@ -6578,6 +6588,7 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) mdp5_interface->splash_init_fnc = mdss_mdp_splash_init; mdp5_interface->configure_panel = mdss_mdp_update_panel_info; mdp5_interface->input_event_handler = mdss_mdp_input_event_handler; + mdp5_interface->signal_retire_fence = mdss_mdp_signal_retire_fence; if (mfd->panel_info->type == WRITEBACK_PANEL) { mdp5_interface->atomic_validate = -- GitLab From 0f18ad4f5cda5172806eef39996535575b521bf3 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Devarakonda Date: Wed, 13 Dec 2017 10:47:52 +0530 Subject: [PATCH 1915/5498] msm: mdss: Signal outstanding fences when panel is dead When the panel is in dead state, we early return from the commit. So, the fences wouldn't be signalled till the next Blank call. Handling this case by signalling the outstanding fences when panel is in dead state. Change-Id: I48a5e04707e32de0d1cdccf0592c981e30b618fc Signed-off-by: Krishna Chaitanya Devarakonda Signed-off-by: Nirmal Abraham --- drivers/video/msm/mdss/mdss_fb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index 84e776f8e00b..7651af03a38f 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -4476,6 +4476,7 @@ static int mdss_fb_atomic_commit_ioctl(struct fb_info *info, struct mdp_frc_info *frc_info = NULL; struct mdp_frc_info __user *frc_info_user; struct msm_fb_data_type *mfd; + struct mdss_overlay_private *mdp5_data = NULL; ret = copy_from_user(&commit, argp, sizeof(struct mdp_layer_commit)); if (ret) { @@ -4487,9 +4488,20 @@ static int mdss_fb_atomic_commit_ioctl(struct fb_info *info, if (!mfd) return -EINVAL; + mdp5_data = mfd_to_mdp5_data(mfd); + if (mfd->panel_info->panel_dead) { pr_debug("early commit return\n"); MDSS_XLOG(mfd->panel_info->panel_dead); + /* + * In case of an ESD attack, since we early return from the + * commits, we need to signal the outstanding fences. + */ + mdss_fb_release_fences(mfd); + if ((mfd->panel.type == MIPI_CMD_PANEL) && + mfd->mdp.signal_retire_fence && mdp5_data) + mfd->mdp.signal_retire_fence(mfd, + mdp5_data->retire_cnt); return 0; } -- GitLab From 086d2d2c7047f6941cbc45bbe7909eef3ee90bc6 Mon Sep 17 00:00:00 2001 From: Shihuan Liu Date: Mon, 16 Oct 2017 17:16:06 -0700 Subject: [PATCH 1916/5498] msm: ipa: add ucp in skb->cb Add ucp status in skb->cb for NTN driver. Change-Id: Ifde630d47323885e3120d64c5556ba10e70e0208 Acked-by: Shihuan Liu Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v2/ipa_dp.c | 6 +++++- drivers/platform/msm/ipa/ipa_v3/ipa_dp.c | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index f70826d53b6d..2c1b117e8dbd 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -2819,10 +2819,12 @@ void ipa_lan_rx_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data) struct ipa_ep_context *ep; unsigned int src_pipe; u32 metadata; + u8 ucp; status = (struct ipa_hw_pkt_status *)rx_skb->data; src_pipe = status->endp_src_idx; metadata = status->metadata; + ucp = status->ucp; ep = &ipa_ctx->ep[src_pipe]; if (unlikely(src_pipe >= ipa_ctx->ipa_num_pipes || !ep->valid || @@ -2845,8 +2847,10 @@ void ipa_lan_rx_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data) ------------------------------------------ */ *(u16 *)rx_skb->cb = ((metadata >> 16) & 0xFFFF); + *(u8 *)(rx_skb->cb + 4) = ucp; IPADBG_LOW("meta_data: 0x%x cb: 0x%x\n", - metadata, *(u32 *)rx_skb->cb); + metadata, *(u32 *)rx_skb->cb); + IPADBG_LOW("ucp: %d\n", *(u8 *)(rx_skb->cb + 4)); ep->client_notify(ep->priv, IPA_RECEIVE, (unsigned long)(rx_skb)); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index c362b69d8784..d9871468d300 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -2915,10 +2915,12 @@ void ipa3_lan_rx_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data) struct ipa3_ep_context *ep; unsigned int src_pipe; u32 metadata; + u8 ucp; ipahal_pkt_status_parse(rx_skb->data, &status); src_pipe = status.endp_src_idx; metadata = status.metadata; + ucp = status.ucp; ep = &ipa3_ctx->ep[src_pipe]; if (unlikely(src_pipe >= ipa3_ctx->ipa_num_pipes || !ep->valid || @@ -2941,8 +2943,10 @@ void ipa3_lan_rx_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data) ------------------------------------------ */ *(u16 *)rx_skb->cb = ((metadata >> 16) & 0xFFFF); + *(u8 *)(rx_skb->cb + 4) = ucp; IPADBG_LOW("meta_data: 0x%x cb: 0x%x\n", metadata, *(u32 *)rx_skb->cb); + IPADBG_LOW("ucp: %d\n", *(u8 *)(rx_skb->cb + 4)); ep->client_notify(ep->priv, IPA_RECEIVE, (unsigned long)(rx_skb)); } -- GitLab From 36df0d6d113962fc49e29dfcb85c16d6bee4ff67 Mon Sep 17 00:00:00 2001 From: Liam Mark Date: Mon, 4 Dec 2017 10:58:55 -0800 Subject: [PATCH 1917/5498] ion: ensure CMO target is valid Cleanup ION cache maintenance code to properly validate the target of userspace cache maintenance requests. Change-Id: I55b8e3584c59634f95250bc7c0bce5d8d70e6a13 Signed-off-by: Liam Mark --- drivers/staging/android/ion/ion.c | 5 +++++ drivers/staging/android/ion/msm/msm_ion.c | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 7860ded7c7a9..a7998777b8cc 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -1517,6 +1517,11 @@ static int ion_sync_for_device(struct ion_client *client, int fd) } buffer = dmabuf->priv; + if (get_secure_vmid(buffer->flags) > 0) { + pr_err("%s: cannot sync a secure dmabuf\n", __func__); + dma_buf_put(dmabuf); + return -EINVAL; + } dma_sync_sg_for_device(NULL, buffer->sg_table->sgl, buffer->sg_table->nents, DMA_BIDIRECTIONAL); dma_buf_put(dmabuf); diff --git a/drivers/staging/android/ion/msm/msm_ion.c b/drivers/staging/android/ion/msm/msm_ion.c index 882e33e4c01c..e112a4fe6682 100644 --- a/drivers/staging/android/ion/msm/msm_ion.c +++ b/drivers/staging/android/ion/msm/msm_ion.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -329,7 +329,7 @@ int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, if (!ION_IS_CACHED(flags)) return 0; - if (flags & ION_FLAG_SECURE) + if (get_secure_vmid(flags) > 0) return 0; table = ion_sg_table(client, handle); @@ -713,11 +713,11 @@ long msm_ion_custom_ioctl(struct ion_client *client, down_read(&mm->mmap_sem); - start = (unsigned long) data.flush_data.vaddr; - end = (unsigned long) data.flush_data.vaddr - + data.flush_data.length; + start = (unsigned long)data.flush_data.vaddr + + data.flush_data.offset; + end = start + data.flush_data.length; - if (start && check_vaddr_bounds(start, end)) { + if (check_vaddr_bounds(start, end)) { pr_err("%s: virtual address %pK is out of bounds\n", __func__, data.flush_data.vaddr); ret = -EINVAL; -- GitLab From dc27af0874678f00a749c84f3fa7c211c7f15f86 Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Tue, 16 May 2017 12:13:02 +0530 Subject: [PATCH 1918/5498] iommu/arm-smmu: Add support for regulator deferred disable In case of unmap call, regulator is enabled/disabled. This may introduce additional delay. For clients who do not detach, it's not possible to keep regulator vote while smmu is attached. Add support for regulator deferred disable. The time delay for deferred disable can be set using dt property qcom,deferred-regulator-disable-delay in ms. Change-Id: I462e1999bd81f6332169b24749632d7b247c75c5 Signed-off-by: Prakash Gupta --- .../devicetree/bindings/iommu/arm,smmu.txt | 6 ++++++ drivers/iommu/arm-smmu.c | 21 ++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt index c52360467571..a0ca20657f1e 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt @@ -115,6 +115,12 @@ conditions. supported as we are directly comparing client SID with ID bits of SMR registers. +- qcom,deferred-regulator-disable-delay : The time delay for deferred regulator + disable in ms. In case of unmap call, regulator is + enabled/disabled. This may introduce additional delay. For + clients who do not detach, it's not possible to keep regulator + vote while smmu is attached. Type is . + - clocks : List of clocks to be used during SMMU register access. See Documentation/devicetree/bindings/clock/clock-bindings.txt for information about the format. For each clock specified diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 5e529fea7a6a..59b3ff06f4be 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -407,6 +407,7 @@ struct arm_smmu_device { char *bus_client_name; enum tz_smmu_device_id sec_id; + int regulator_defer; }; struct arm_smmu_cfg { @@ -801,11 +802,19 @@ static int arm_smmu_unrequest_bus(struct arm_smmu_device *smmu) static int arm_smmu_disable_regulators(struct arm_smmu_device *smmu) { + int ret = 0; + arm_smmu_unprepare_clocks(smmu); arm_smmu_unrequest_bus(smmu); - if (!smmu->gdsc) - return 0; - return regulator_disable(smmu->gdsc); + + if (smmu->gdsc) { + ret = regulator_disable_deferred(smmu->gdsc, + smmu->regulator_defer); + WARN(ret, "%s: Regulator disable failed\n", + dev_name(smmu->dev)); + } + + return ret; } static int arm_smmu_enable_regulators(struct arm_smmu_device *smmu) @@ -3320,6 +3329,12 @@ static int arm_smmu_init_regulators(struct arm_smmu_device *smmu) if (!of_get_property(dev->of_node, "vdd-supply", NULL)) return 0; + if (!of_property_read_u32(dev->of_node, + "qcom,deferred-regulator-disable-delay", + &(smmu->regulator_defer))) + dev_info(dev, "regulator defer delay %d\n", + smmu->regulator_defer); + smmu->gdsc = devm_regulator_get(dev, "vdd"); if (IS_ERR(smmu->gdsc)) return PTR_ERR(smmu->gdsc); -- GitLab From 85fcea90cbda4d54eb3b6933dc7d921e48b6bf3f Mon Sep 17 00:00:00 2001 From: Baochu Xu Date: Thu, 14 Dec 2017 10:12:49 +0800 Subject: [PATCH 1919/5498] ARM: dts: msm: Modify qseecom for SDW2500 Modify qseecom memory reg for SDW2500 platform external_image_mem is defined from 0x87b00000. The oringe address of qseecom is started at 0x87a00000. This address is not reserved for TZ apps. This cause qseecom to access invalid address from 0x87a0000. Change-Id: I24d80de3c84dc7812793cbc750ce6ec7bb8808cf Signed-off-by: Baochu Xu --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 3c71c6e0e1cd..d5250ecf8d92 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -105,6 +105,12 @@ /delete-property/ asoc-codec; }; +&qcom_seecom { + reg = <0x87b00000 0x100000>; + reg-names = "secapp-region"; + status = "okay"; +}; + &i2c_1 { status = "disabled"; }; -- GitLab From 0eb0a69395c5174be0962ba589dc13dd14921761 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 6 Dec 2017 10:42:10 +0000 Subject: [PATCH 1920/5498] arm64: SW PAN: Point saved ttbr0 at the zero page when switching to init_mm update_saved_ttbr0 mandates that mm->pgd is not swapper, since swapper contains kernel mappings and should never be installed into ttbr0. However, this means that callers must avoid passing the init_mm to update_saved_ttbr0 which in turn can cause the saved ttbr0 value to be out-of-date in the context of the idle thread. For example, EFI runtime services may leave the saved ttbr0 pointing at the EFI page table, and kernel threads may end up with stale references to freed page tables. This patch changes update_saved_ttbr0 so that the init_mm points the saved ttbr0 value to the empty zero page, which always exists and never contains valid translations. EFI and switch can then call into update_saved_ttbr0 unconditionally. Change-Id: I664095969fbd4d0b546f30aa4c312769e708d6de Cc: Mark Rutland Cc: Ard Biesheuvel Cc: Vinayak Menon Cc: Fixes: 39bc88e5e38e9b21 ("arm64: Disable TTBR0_EL1 during normal kernel execution") Reviewed-by: Catalin Marinas Reviewed-by: Mark Rutland Reported-by: Vinayak Menon Signed-off-by: Will Deacon Git-commit: 0adbdfde8cfc9415aeed2a4955d2d17b3bd9bf13 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [vinmenon: changes in arch/arm64/include/asm/efi.h moved to arch/arm64/kernel/efi.c as the affected function is moved] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/mmu_context.h | 22 +++++++++++++--------- arch/arm64/kernel/efi.c | 4 +--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 0802f08919aa..4cb8e290b317 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -127,11 +127,17 @@ enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) static inline void update_saved_ttbr0(struct task_struct *tsk, struct mm_struct *mm) { - if (system_uses_ttbr0_pan()) { - BUG_ON(mm->pgd == swapper_pg_dir); - task_thread_info(tsk)->ttbr0 = - virt_to_phys(mm->pgd) | ASID(mm) << 48; - } + u64 ttbr; + + if (!system_uses_ttbr0_pan()) + return; + + if (mm == &init_mm) + ttbr = __pa_symbol(empty_zero_page); + else + ttbr = virt_to_phys(mm->pgd) | ASID(mm) << 48; + + task_thread_info(tsk)->ttbr0 = ttbr; } #else static inline void update_saved_ttbr0(struct task_struct *tsk, @@ -167,11 +173,9 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, * Update the saved TTBR0_EL1 of the scheduled-in task as the previous * value may have not been initialised yet (activate_mm caller) or the * ASID has changed since the last run (following the context switch - * of another thread of the same process). Avoid setting the reserved - * TTBR0_EL1 to swapper_pg_dir (init_mm; e.g. via idle_task_exit). + * of another thread of the same process). */ - if (next != &init_mm) - update_saved_ttbr0(tsk, next); + update_saved_ttbr0(tsk, next); } #define deactivate_mm(tsk,mm) do { } while (0) diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 7d13642092d1..0e4f402578ee 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -356,11 +356,9 @@ static void efi_set_pgd(struct mm_struct *mm) * Defer the switch to the current thread's TTBR0_EL1 * until uaccess_enable(). Restore the current * thread's saved ttbr0 corresponding to its active_mm - * (if different from init_mm). */ cpu_set_reserved_ttbr0(); - if (current->active_mm != &init_mm) - update_saved_ttbr0(current, current->active_mm); + update_saved_ttbr0(current, current->active_mm); } } } -- GitLab From 4f55079adaff7c66cbaea020fdf50fb5a5e68539 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 6 Dec 2017 10:51:12 +0000 Subject: [PATCH 1921/5498] arm64: SW PAN: Update saved ttbr0 value on enter_lazy_tlb enter_lazy_tlb is called when a kernel thread rides on the back of another mm, due to a context switch or an explicit call to unuse_mm where a call to switch_mm is elided. In these cases, it's important to keep the saved ttbr value up to date with the active mm, otherwise we can end up with a stale value which points to a potentially freed page table. This patch implements enter_lazy_tlb for arm64, so that the saved ttbr0 is kept up-to-date with the active mm for kernel threads. Change-Id: I6e5c50542ff2645c46e8801685f7a43e6773c3d2 Cc: Mark Rutland Cc: Ard Biesheuvel Cc: Vinayak Menon Cc: Fixes: 39bc88e5e38e9b21 ("arm64: Disable TTBR0_EL1 during normal kernel execution") Reviewed-by: Catalin Marinas Reviewed-by: Mark Rutland Reported-by: Vinayak Menon Signed-off-by: Will Deacon Git-commit: d96cc49bff5a7735576cc6f6f111f875d101cec8 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/mmu_context.h | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 4cb8e290b317..1f7dcb26bdfa 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -109,20 +109,6 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu); #define init_new_context(tsk,mm) ({ atomic64_set(&(mm)->context.id, 0); 0; }) -/* - * This is called when "tsk" is about to enter lazy TLB mode. - * - * mm: describes the currently active mm context - * tsk: task which is entering lazy tlb - * cpu: cpu number which is entering lazy tlb - * - * tsk->mm will be NULL - */ -static inline void -enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) -{ -} - #ifdef CONFIG_ARM64_SW_TTBR0_PAN static inline void update_saved_ttbr0(struct task_struct *tsk, struct mm_struct *mm) @@ -146,6 +132,16 @@ static inline void update_saved_ttbr0(struct task_struct *tsk, } #endif +static inline void +enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) +{ + /* + * We don't actually care about the ttbr0 mapping, so point it at the + * zero page. + */ + update_saved_ttbr0(tsk, &init_mm); +} + static inline void __switch_mm(struct mm_struct *next) { unsigned int cpu = smp_processor_id(); -- GitLab From 70d9dccf50152b0d7bfb2697d8c51e9fab9f782c Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 21 Nov 2017 08:22:26 +0100 Subject: [PATCH 1922/5498] can: kvaser_usb: free buf in error paths commit 435019b48033138581a6171093b181fc6b4d3d30 upstream. The allocated buffer was not freed if usb_submit_urb() failed. Signed-off-by: Jimmy Assarsson Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index fc8ca6c59e1e..44aa379fa496 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -602,6 +602,7 @@ static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv, if (err) { netdev_err(netdev, "Error transmitting URB\n"); usb_unanchor_urb(urb); + kfree(buf); usb_free_urb(urb); kfree(buf); return err; @@ -1385,6 +1386,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, atomic_dec(&priv->active_tx_urbs); usb_unanchor_urb(urb); + kfree(buf); stats->tx_dropped++; -- GitLab From dc16a4dee968e69c418b9d5d3232829553d32177 Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 21 Nov 2017 08:22:27 +0100 Subject: [PATCH 1923/5498] can: kvaser_usb: Fix comparison bug in kvaser_usb_read_bulk_callback() commit e84f44eb5523401faeb9cc1c97895b68e3cfb78d upstream. The conditon in the while-loop becomes true when actual_length is less than 2 (MSG_HEADER_LEN). In best case we end up with a former, already dispatched msg, that got msg->len greater than actual_length. This will result in a "Format error" error printout. Problem seen when unplugging a Kvaser USB device connected to a vbox guest. warning: comparison between signed and unsigned integer expressions [-Wsign-compare] Signed-off-by: Jimmy Assarsson Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 44aa379fa496..ed9b0fbbca22 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -989,7 +989,7 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) goto resubmit_urb; } - while (pos <= urb->actual_length - MSG_HEADER_LEN) { + while (pos <= (int)(urb->actual_length - MSG_HEADER_LEN)) { msg = urb->transfer_buffer + pos; /* The Kvaser firmware can only read and write messages that -- GitLab From 29017a8bc720adcd1d0ec5df4495751511a7317c Mon Sep 17 00:00:00 2001 From: Jimmy Assarsson Date: Tue, 21 Nov 2017 08:22:28 +0100 Subject: [PATCH 1924/5498] can: kvaser_usb: ratelimit errors if incomplete messages are received commit 8bd13bd522ff7dfa0eb371921aeb417155f7a3be upstream. Avoid flooding the kernel log with "Formate error", if incomplete message are received. Signed-off-by: Jimmy Assarsson Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index ed9b0fbbca22..23d5f4ed6acd 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -415,8 +415,8 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id, } if (pos + tmp->len > actual_len) { - dev_err(dev->udev->dev.parent, - "Format error\n"); + dev_err_ratelimited(dev->udev->dev.parent, + "Format error\n"); break; } @@ -1007,7 +1007,8 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) } if (pos + msg->len > urb->actual_length) { - dev_err(dev->udev->dev.parent, "Format error\n"); + dev_err_ratelimited(dev->udev->dev.parent, + "Format error\n"); break; } -- GitLab From bead22ffc679dd16c96062c9286d24a7d23b2021 Mon Sep 17 00:00:00 2001 From: Martin Kelly Date: Tue, 5 Dec 2017 11:15:49 -0800 Subject: [PATCH 1925/5498] can: kvaser_usb: cancel urb on -EPIPE and -EPROTO commit 6aa8d5945502baf4687d80de59b7ac865e9e666b upstream. In mcba_usb, we have observed that when you unplug the device, the driver will endlessly resubmit failing URBs, which can cause CPU stalls. This issue is fixed in mcba_usb by catching the codes seen on device disconnect (-EPIPE and -EPROTO). This driver also resubmits in the case of -EPIPE and -EPROTO, so fix it in the same way. Signed-off-by: Martin Kelly Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 23d5f4ed6acd..4745164e4325 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -981,6 +981,8 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) case 0: break; case -ENOENT: + case -EPIPE: + case -EPROTO: case -ESHUTDOWN: return; default: -- GitLab From 2d2ac259c9a05662c595becad992b82489ca5aa8 Mon Sep 17 00:00:00 2001 From: Martin Kelly Date: Tue, 5 Dec 2017 11:15:47 -0800 Subject: [PATCH 1926/5498] can: ems_usb: cancel urb on -EPIPE and -EPROTO commit bd352e1adfe0d02d3ea7c8e3fb19183dc317e679 upstream. In mcba_usb, we have observed that when you unplug the device, the driver will endlessly resubmit failing URBs, which can cause CPU stalls. This issue is fixed in mcba_usb by catching the codes seen on device disconnect (-EPIPE and -EPROTO). This driver also resubmits in the case of -EPIPE and -EPROTO, so fix it in the same way. Signed-off-by: Martin Kelly Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/ems_usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 5c9f06cc35cc..b1ca40552a84 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -290,6 +290,8 @@ static void ems_usb_read_interrupt_callback(struct urb *urb) case -ECONNRESET: /* unlink */ case -ENOENT: + case -EPIPE: + case -EPROTO: case -ESHUTDOWN: return; -- GitLab From c0c6369d0f9161a7e7fe13b511b467c7dbaa26df Mon Sep 17 00:00:00 2001 From: Martin Kelly Date: Tue, 5 Dec 2017 11:15:48 -0800 Subject: [PATCH 1927/5498] can: esd_usb2: cancel urb on -EPIPE and -EPROTO commit 7a31ced3de06e9878e4f9c3abe8f87d9344d8144 upstream. In mcba_usb, we have observed that when you unplug the device, the driver will endlessly resubmit failing URBs, which can cause CPU stalls. This issue is fixed in mcba_usb by catching the codes seen on device disconnect (-EPIPE and -EPROTO). This driver also resubmits in the case of -EPIPE and -EPROTO, so fix it in the same way. Signed-off-by: Martin Kelly Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/esd_usb2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index 30a082842620..195bb59c61ab 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -395,6 +395,8 @@ static void esd_usb2_read_bulk_callback(struct urb *urb) break; case -ENOENT: + case -EPIPE: + case -EPROTO: case -ESHUTDOWN: return; -- GitLab From 7983427599c456b1ff68d5571d98c8b3d7982757 Mon Sep 17 00:00:00 2001 From: Martin Kelly Date: Tue, 5 Dec 2017 11:15:50 -0800 Subject: [PATCH 1928/5498] can: usb_8dev: cancel urb on -EPIPE and -EPROTO commit 12147edc434c9e4c7c2f5fee2e5519b2e5ac34ce upstream. In mcba_usb, we have observed that when you unplug the device, the driver will endlessly resubmit failing URBs, which can cause CPU stalls. This issue is fixed in mcba_usb by catching the codes seen on device disconnect (-EPIPE and -EPROTO). This driver also resubmits in the case of -EPIPE and -EPROTO, so fix it in the same way. Signed-off-by: Martin Kelly Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/usb_8dev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c index ef674ecb82f8..271b42134c50 100644 --- a/drivers/net/can/usb/usb_8dev.c +++ b/drivers/net/can/usb/usb_8dev.c @@ -527,6 +527,8 @@ static void usb_8dev_read_bulk_callback(struct urb *urb) break; case -ENOENT: + case -EPIPE: + case -EPROTO: case -ESHUTDOWN: return; -- GitLab From 08fa6f2e23c519cefbf03d27731e04babf076cb4 Mon Sep 17 00:00:00 2001 From: weiping zhang Date: Wed, 29 Nov 2017 09:23:01 +0800 Subject: [PATCH 1929/5498] virtio: release virtio index when fail to device_register commit e60ea67bb60459b95a50a156296041a13e0e380e upstream. index can be reused by other virtio device. Signed-off-by: weiping zhang Reviewed-by: Cornelia Huck Signed-off-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index df598dd8c5c8..8097e30fb49b 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -268,6 +268,8 @@ int register_virtio_device(struct virtio_device *dev) /* device_register() causes the bus infrastructure to look for a * matching driver. */ err = device_register(&dev->dev); + if (err) + ida_simple_remove(&virtio_index_ida, dev->index); out: if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); -- GitLab From 843bf4aa1aad6f968152390142016c0566899a4f Mon Sep 17 00:00:00 2001 From: Paul Meyer Date: Tue, 14 Nov 2017 13:06:47 -0700 Subject: [PATCH 1930/5498] hv: kvp: Avoid reading past allocated blocks from KVP file commit 297d6b6e56c2977fc504c61bbeeaa21296923f89 upstream. While reading in more than one block (50) of KVP records, the allocation goes per block, but the reads used the total number of allocated records (without resetting the pointer/stream). This causes the records buffer to overrun when the refresh reads more than one block over the previous capacity (e.g. reading more than 100 KVP records whereas the in-memory database was empty before). Fix this by reading the correct number of KVP records from file each time. Signed-off-by: Paul Meyer Signed-off-by: Long Li Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 70 ++++++++-------------------------------- 1 file changed, 14 insertions(+), 56 deletions(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 4088b816a3ee..f660d3f69ce7 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -196,11 +196,14 @@ static void kvp_update_mem_state(int pool) for (;;) { readp = &record[records_read]; records_read += fread(readp, sizeof(struct kvp_record), - ENTRIES_PER_BLOCK * num_blocks, - filep); + ENTRIES_PER_BLOCK * num_blocks - records_read, + filep); if (ferror(filep)) { - syslog(LOG_ERR, "Failed to read file, pool: %d", pool); + syslog(LOG_ERR, + "Failed to read file, pool: %d; error: %d %s", + pool, errno, strerror(errno)); + kvp_release_lock(pool); exit(EXIT_FAILURE); } @@ -213,6 +216,7 @@ static void kvp_update_mem_state(int pool) if (record == NULL) { syslog(LOG_ERR, "malloc failed"); + kvp_release_lock(pool); exit(EXIT_FAILURE); } continue; @@ -227,15 +231,11 @@ static void kvp_update_mem_state(int pool) fclose(filep); kvp_release_lock(pool); } + static int kvp_file_init(void) { int fd; - FILE *filep; - size_t records_read; char *fname; - struct kvp_record *record; - struct kvp_record *readp; - int num_blocks; int i; int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; @@ -249,61 +249,19 @@ static int kvp_file_init(void) for (i = 0; i < KVP_POOL_COUNT; i++) { fname = kvp_file_info[i].fname; - records_read = 0; - num_blocks = 1; sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i); fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */); if (fd == -1) return 1; - - filep = fopen(fname, "re"); - if (!filep) { - close(fd); - return 1; - } - - record = malloc(alloc_unit * num_blocks); - if (record == NULL) { - fclose(filep); - close(fd); - return 1; - } - for (;;) { - readp = &record[records_read]; - records_read += fread(readp, sizeof(struct kvp_record), - ENTRIES_PER_BLOCK, - filep); - - if (ferror(filep)) { - syslog(LOG_ERR, "Failed to read file, pool: %d", - i); - exit(EXIT_FAILURE); - } - - if (!feof(filep)) { - /* - * We have more data to read. - */ - num_blocks++; - record = realloc(record, alloc_unit * - num_blocks); - if (record == NULL) { - fclose(filep); - close(fd); - return 1; - } - continue; - } - break; - } kvp_file_info[i].fd = fd; - kvp_file_info[i].num_blocks = num_blocks; - kvp_file_info[i].records = record; - kvp_file_info[i].num_records = records_read; - fclose(filep); - + kvp_file_info[i].num_blocks = 1; + kvp_file_info[i].records = malloc(alloc_unit); + if (kvp_file_info[i].records == NULL) + return 1; + kvp_file_info[i].num_records = 0; + kvp_update_mem_state(i); } return 0; -- GitLab From 898fe40c6b72cce1e8fe205a7e9812548855a500 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 8 Nov 2017 10:23:11 -0500 Subject: [PATCH 1931/5498] isa: Prevent NULL dereference in isa_bus driver callbacks commit 5a244727f428a06634f22bb890e78024ab0c89f3 upstream. The isa_driver structure for an isa_bus device is stored in the device platform_data member of the respective device structure. This platform_data member may be reset to NULL if isa_driver match callback for the device fails, indicating a device unsupported by the ISA driver. This patch fixes a possible NULL pointer dereference if one of the isa_driver callbacks to attempted for an unsupported device. This error should not occur in practice since ISA devices are typically manually configured and loaded by the users, but we may as well prevent this error from popping up for the 0day testers. Fixes: a5117ba7da37 ("[PATCH] Driver model: add ISA bus") Signed-off-by: William Breathitt Gray Acked-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/base/isa.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/base/isa.c b/drivers/base/isa.c index 91dba65d7264..901d8185309e 100644 --- a/drivers/base/isa.c +++ b/drivers/base/isa.c @@ -39,7 +39,7 @@ static int isa_bus_probe(struct device *dev) { struct isa_driver *isa_driver = dev->platform_data; - if (isa_driver->probe) + if (isa_driver && isa_driver->probe) return isa_driver->probe(dev, to_isa_dev(dev)->id); return 0; @@ -49,7 +49,7 @@ static int isa_bus_remove(struct device *dev) { struct isa_driver *isa_driver = dev->platform_data; - if (isa_driver->remove) + if (isa_driver && isa_driver->remove) return isa_driver->remove(dev, to_isa_dev(dev)->id); return 0; @@ -59,7 +59,7 @@ static void isa_bus_shutdown(struct device *dev) { struct isa_driver *isa_driver = dev->platform_data; - if (isa_driver->shutdown) + if (isa_driver && isa_driver->shutdown) isa_driver->shutdown(dev, to_isa_dev(dev)->id); } @@ -67,7 +67,7 @@ static int isa_bus_suspend(struct device *dev, pm_message_t state) { struct isa_driver *isa_driver = dev->platform_data; - if (isa_driver->suspend) + if (isa_driver && isa_driver->suspend) return isa_driver->suspend(dev, to_isa_dev(dev)->id, state); return 0; @@ -77,7 +77,7 @@ static int isa_bus_resume(struct device *dev) { struct isa_driver *isa_driver = dev->platform_data; - if (isa_driver->resume) + if (isa_driver && isa_driver->resume) return isa_driver->resume(dev, to_isa_dev(dev)->id); return 0; -- GitLab From 25ea12ec0370ad88d356e84bca5a2a0f65b1bac6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 6 Dec 2017 09:50:08 +0000 Subject: [PATCH 1932/5498] efi: Move some sysfs files to be read-only by root commit af97a77bc01ce49a466f9d4c0125479e2e2230b6 upstream. Thanks to the scripts/leaking_addresses.pl script, it was found that some EFI values should not be readable by non-root users. So make them root-only, and to do that, add a __ATTR_RO_MODE() macro to make this easier, and use it in other places at the same time. Reported-by: Linus Torvalds Tested-by: Dave Young Signed-off-by: Greg Kroah-Hartman Signed-off-by: Ard Biesheuvel Cc: H. Peter Anvin Cc: Matt Fleming Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/20171206095010.24170-2-ard.biesheuvel@linaro.org Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/efi/efi.c | 3 +-- drivers/firmware/efi/runtime-map.c | 10 +++++----- include/linux/sysfs.h | 6 ++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 71e090c8c85e..297066df6946 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -96,8 +96,7 @@ static ssize_t systab_show(struct kobject *kobj, return str - buf; } -static struct kobj_attribute efi_attr_systab = - __ATTR(systab, 0400, systab_show, NULL); +static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400); #define EFI_FIELD(var) efi.var diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c index 87b8e3b900d2..1df70fd4787f 100644 --- a/drivers/firmware/efi/runtime-map.c +++ b/drivers/firmware/efi/runtime-map.c @@ -67,11 +67,11 @@ static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr, return map_attr->show(entry, buf); } -static struct map_attribute map_type_attr = __ATTR_RO(type); -static struct map_attribute map_phys_addr_attr = __ATTR_RO(phys_addr); -static struct map_attribute map_virt_addr_attr = __ATTR_RO(virt_addr); -static struct map_attribute map_num_pages_attr = __ATTR_RO(num_pages); -static struct map_attribute map_attribute_attr = __ATTR_RO(attribute); +static struct map_attribute map_type_attr = __ATTR_RO_MODE(type, 0400); +static struct map_attribute map_phys_addr_attr = __ATTR_RO_MODE(phys_addr, 0400); +static struct map_attribute map_virt_addr_attr = __ATTR_RO_MODE(virt_addr, 0400); +static struct map_attribute map_num_pages_attr = __ATTR_RO_MODE(num_pages, 0400); +static struct map_attribute map_attribute_attr = __ATTR_RO_MODE(attribute, 0400); /* * These are default attributes that are added for every memmap entry. diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index f97d0dbb59fa..70ceb2a14a57 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -82,6 +82,12 @@ struct attribute_group { .show = _name##_show, \ } +#define __ATTR_RO_MODE(_name, _mode) { \ + .attr = { .name = __stringify(_name), \ + .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ + .show = _name##_show, \ +} + #define __ATTR_WO(_name) { \ .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ .store = _name##_store, \ -- GitLab From 602a1c77f9f3466fb7d7fd4e2454b27008ccac83 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 8 Dec 2017 15:13:27 +0000 Subject: [PATCH 1933/5498] ASN.1: check for error from ASN1_OP_END__ACT actions commit 81a7be2cd69b412ab6aeacfe5ebf1bb6e5bce955 upstream. asn1_ber_decoder() was ignoring errors from actions associated with the opcodes ASN1_OP_END_SEQ_ACT, ASN1_OP_END_SET_ACT, ASN1_OP_END_SEQ_OF_ACT, and ASN1_OP_END_SET_OF_ACT. In practice, this meant the pkcs7_note_signed_info() action (since that was the only user of those opcodes). Fix it by checking for the error, just like the decoder does for actions associated with the other opcodes. This bug allowed users to leak slab memory by repeatedly trying to add a specially crafted "pkcs7_test" key (requires CONFIG_PKCS7_TEST_KEY). In theory, this bug could also be used to bypass module signature verification, by providing a PKCS#7 message that is misparsed such that a signature's ->authattrs do not contain its ->msgdigest. But it doesn't seem practical in normal cases, due to restrictions on the format of the ->authattrs. Fixes: 42d5ec27f873 ("X.509: Add an ASN.1 decoder") Signed-off-by: Eric Biggers Signed-off-by: David Howells Reviewed-by: James Morris Signed-off-by: Greg Kroah-Hartman --- lib/asn1_decoder.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c index 162b6d290622..7f453b6ff0d0 100644 --- a/lib/asn1_decoder.c +++ b/lib/asn1_decoder.c @@ -422,6 +422,8 @@ next_op: else act = machine[pc + 1]; ret = actions[act](context, hdr, 0, data + tdp, len); + if (ret < 0) + return ret; } pc += asn1_op_lengths[op]; goto next_op; -- GitLab From 228014b20bd8902b05942ce4db4197ce345296f3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 8 Dec 2017 15:13:27 +0000 Subject: [PATCH 1934/5498] KEYS: add missing permission check for request_key() destination commit 4dca6ea1d9432052afb06baf2e3ae78188a4410b upstream. When the request_key() syscall is not passed a destination keyring, it links the requested key (if constructed) into the "default" request-key keyring. This should require Write permission to the keyring. However, there is actually no permission check. This can be abused to add keys to any keyring to which only Search permission is granted. This is because Search permission allows joining the keyring. keyctl_set_reqkey_keyring(KEY_REQKEY_DEFL_SESSION_KEYRING) then will set the default request-key keyring to the session keyring. Then, request_key() can be used to add keys to the keyring. Both negatively and positively instantiated keys can be added using this method. Adding negative keys is trivial. Adding a positive key is a bit trickier. It requires that either /sbin/request-key positively instantiates the key, or that another thread adds the key to the process keyring at just the right time, such that request_key() misses it initially but then finds it in construct_alloc_key(). Fix this bug by checking for Write permission to the keyring in construct_get_dest_keyring() when the default keyring is being used. We don't do the permission check for non-default keyrings because that was already done by the earlier call to lookup_user_key(). Also, request_key_and_link() is currently passed a 'struct key *' rather than a key_ref_t, so the "possessed" bit is unavailable. We also don't do the permission check for the "requestor keyring", to continue to support the use case described by commit 8bbf4976b59f ("KEYS: Alter use of key instantiation link-to-keyring argument") where /sbin/request-key recursively calls request_key() to add keys to the original requestor's destination keyring. (I don't know of any users who actually do that, though...) Fixes: 3e30148c3d52 ("[PATCH] Keys: Make request-key create an authorisation key") Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- security/keys/request_key.c | 46 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 0c7aea4dea54..6096be6232d7 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -250,11 +250,12 @@ static int construct_key(struct key *key, const void *callout_info, * The keyring selected is returned with an extra reference upon it which the * caller must release. */ -static void construct_get_dest_keyring(struct key **_dest_keyring) +static int construct_get_dest_keyring(struct key **_dest_keyring) { struct request_key_auth *rka; const struct cred *cred = current_cred(); struct key *dest_keyring = *_dest_keyring, *authkey; + int ret; kenter("%p", dest_keyring); @@ -263,6 +264,8 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) /* the caller supplied one */ key_get(dest_keyring); } else { + bool do_perm_check = true; + /* use a default keyring; falling through the cases until we * find one that we actually have */ switch (cred->jit_keyring) { @@ -277,8 +280,10 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) dest_keyring = key_get(rka->dest_keyring); up_read(&authkey->sem); - if (dest_keyring) + if (dest_keyring) { + do_perm_check = false; break; + } } case KEY_REQKEY_DEFL_THREAD_KEYRING: @@ -313,11 +318,29 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) default: BUG(); } + + /* + * Require Write permission on the keyring. This is essential + * because the default keyring may be the session keyring, and + * joining a keyring only requires Search permission. + * + * However, this check is skipped for the "requestor keyring" so + * that /sbin/request-key can itself use request_key() to add + * keys to the original requestor's destination keyring. + */ + if (dest_keyring && do_perm_check) { + ret = key_permission(make_key_ref(dest_keyring, 1), + KEY_NEED_WRITE); + if (ret) { + key_put(dest_keyring); + return ret; + } + } } *_dest_keyring = dest_keyring; kleave(" [dk %d]", key_serial(dest_keyring)); - return; + return 0; } /* @@ -439,11 +462,15 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, kenter(""); - user = key_user_lookup(current_fsuid()); - if (!user) - return ERR_PTR(-ENOMEM); + ret = construct_get_dest_keyring(&dest_keyring); + if (ret) + goto error; - construct_get_dest_keyring(&dest_keyring); + user = key_user_lookup(current_fsuid()); + if (!user) { + ret = -ENOMEM; + goto error_put_dest_keyring; + } ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key); key_user_put(user); @@ -458,7 +485,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, } else if (ret == -EINPROGRESS) { ret = 0; } else { - goto couldnt_alloc_key; + goto error_put_dest_keyring; } key_put(dest_keyring); @@ -468,8 +495,9 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, construction_failed: key_negate_and_link(key, key_negative_timeout, NULL, NULL); key_put(key); -couldnt_alloc_key: +error_put_dest_keyring: key_put(dest_keyring); +error: kleave(" = %d", ret); return ERR_PTR(ret); } -- GitLab From 529614feec561ddd0f429c34639ce88c1ded6902 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 8 Dec 2017 15:13:27 +0000 Subject: [PATCH 1935/5498] X.509: reject invalid BIT STRING for subjectPublicKey commit 0f30cbea005bd3077bd98cd29277d7fc2699c1da upstream. Adding a specially crafted X.509 certificate whose subjectPublicKey ASN.1 value is zero-length caused x509_extract_key_data() to set the public key size to SIZE_MAX, as it subtracted the nonexistent BIT STRING metadata byte. Then, x509_cert_parse() called kmemdup() with that bogus size, triggering the WARN_ON_ONCE() in kmalloc_slab(). This appears to be harmless, but it still must be fixed since WARNs are never supposed to be user-triggerable. Fix it by updating x509_cert_parse() to validate that the value has a BIT STRING metadata byte, and that the byte is 0 which indicates that the number of bits in the bitstring is a multiple of 8. It would be nice to handle the metadata byte in asn1_ber_decoder() instead. But that would be tricky because in the general case a BIT STRING could be implicitly tagged, and/or could legitimately have a length that is not a whole number of bytes. Here was the WARN (cleaned up slightly): WARNING: CPU: 1 PID: 202 at mm/slab_common.c:971 kmalloc_slab+0x5d/0x70 mm/slab_common.c:971 Modules linked in: CPU: 1 PID: 202 Comm: keyctl Tainted: G B 4.14.0-09238-g1d3b78bbc6e9 #26 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.11.0-20171110_100015-anatol 04/01/2014 task: ffff880033014180 task.stack: ffff8800305c8000 Call Trace: __do_kmalloc mm/slab.c:3706 [inline] __kmalloc_track_caller+0x22/0x2e0 mm/slab.c:3726 kmemdup+0x17/0x40 mm/util.c:118 kmemdup include/linux/string.h:414 [inline] x509_cert_parse+0x2cb/0x620 crypto/asymmetric_keys/x509_cert_parser.c:106 x509_key_preparse+0x61/0x750 crypto/asymmetric_keys/x509_public_key.c:174 asymmetric_key_preparse+0xa4/0x150 crypto/asymmetric_keys/asymmetric_type.c:388 key_create_or_update+0x4d4/0x10a0 security/keys/key.c:850 SYSC_add_key security/keys/keyctl.c:122 [inline] SyS_add_key+0xe8/0x290 security/keys/keyctl.c:62 entry_SYSCALL_64_fastpath+0x1f/0x96 Fixes: 42d5ec27f873 ("X.509: Add an ASN.1 decoder") Signed-off-by: Eric Biggers Signed-off-by: David Howells Reviewed-by: James Morris Signed-off-by: Greg Kroah-Hartman --- crypto/asymmetric_keys/x509_cert_parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index a668d90302d3..4c4f4c936063 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -381,6 +381,8 @@ int x509_extract_key_data(void *context, size_t hdrlen, ctx->cert->pub->pkey_algo = PKEY_ALGO_RSA; /* Discard the BIT STRING metadata */ + if (vlen < 1 || *(const u8 *)value != 0) + return -EBADMSG; ctx->key = value + 1; ctx->key_size = vlen - 1; return 0; -- GitLab From 346178f1aa5e082f5cfbe94affcbfa1e9d6086df Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 1 Dec 2017 15:08:12 +0100 Subject: [PATCH 1936/5498] x86/PCI: Make broadcom_postcore_init() check acpi_disabled commit ddec3bdee05b06f1dda20ded003c3e10e4184cab upstream. acpi_os_get_root_pointer() may return a valid address even if acpi_disabled is set, but the host bridge information from the ACPI tables is not going to be used in that case and the Broadcom host bridge initialization should not be skipped then, So make broadcom_postcore_init() check acpi_disabled too to avoid this issue. Fixes: 6361d72b04d1 (x86/PCI: read Broadcom CNB20LE host bridge info before PCI scan) Reported-by: Dave Hansen Signed-off-by: Rafael J. Wysocki Signed-off-by: Thomas Gleixner Cc: Bjorn Helgaas Cc: Linux PCI Link: https://lkml.kernel.org/r/3186627.pxZj1QbYNg@aspire.rjw.lan Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/pci/broadcom_bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/pci/broadcom_bus.c b/arch/x86/pci/broadcom_bus.c index bb461cfd01ab..526536c81ddc 100644 --- a/arch/x86/pci/broadcom_bus.c +++ b/arch/x86/pci/broadcom_bus.c @@ -97,7 +97,7 @@ static int __init broadcom_postcore_init(void) * We should get host bridge information from ACPI unless the BIOS * doesn't support it. */ - if (acpi_os_get_root_pointer()) + if (!acpi_disabled && acpi_os_get_root_pointer()) return 0; #endif -- GitLab From 14416b2c878b989674761118db8072bf7f0c9501 Mon Sep 17 00:00:00 2001 From: Robb Glasser Date: Tue, 5 Dec 2017 09:16:55 -0800 Subject: [PATCH 1937/5498] ALSA: pcm: prevent UAF in snd_pcm_info commit 362bca57f5d78220f8b5907b875961af9436e229 upstream. When the device descriptor is closed, the `substream->runtime` pointer is freed. But another thread may be in the ioctl handler, case SNDRV_CTL_IOCTL_PCM_INFO. This case calls snd_pcm_info_user() which calls snd_pcm_info() which accesses the now freed `substream->runtime`. Note: this fixes CVE-2017-0861 Signed-off-by: Robb Glasser Signed-off-by: Nick Desaulniers Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index b917a47a7bb6..314a77a9048e 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -150,7 +150,9 @@ static int snd_pcm_control_ioctl(struct snd_card *card, err = -ENXIO; goto _error; } + mutex_lock(&pcm->open_mutex); err = snd_pcm_info_user(substream, info); + mutex_unlock(&pcm->open_mutex); _error: mutex_unlock(®ister_mutex); return err; -- GitLab From a94c6d0bb9e0b6aae587a22ba9a10ae80e242f5d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Nov 2017 10:08:28 +0100 Subject: [PATCH 1938/5498] ALSA: seq: Remove spurious WARN_ON() at timer check commit 43a3542870328601be02fcc9d27b09db467336ef upstream. The use of snd_BUG_ON() in ALSA sequencer timer may lead to a spurious WARN_ON() when a slave timer is deployed as its backend and a corresponding master timer stops meanwhile. The symptom was triggered by syzkaller spontaneously. Since the NULL timer is valid there, rip off snd_BUG_ON(). Reported-by: syzbot Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index a2468f1101d1..0e6210000fa9 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -355,7 +355,7 @@ static int initialize_timer(struct snd_seq_timer *tmr) unsigned long freq; t = tmr->timeri->timer; - if (snd_BUG_ON(!t)) + if (!t) return -EINVAL; freq = tmr->preferred_resolution; -- GitLab From 13de9b240ad98b69ce182237e8f4193f8df159e8 Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Mon, 4 Dec 2017 15:31:48 +0900 Subject: [PATCH 1939/5498] ALSA: usb-audio: Fix out-of-bound error commit 251552a2b0d454badc8f486e6d79100970c744b0 upstream. The snd_usb_copy_string_desc() retrieves the usb string corresponding to the index number through the usb_string(). The problem is that the usb_string() returns the length of the string (>= 0) when successful, but it can also return a negative value about the error case or status of usb_control_msg(). If iClockSource is '0' as shown below, usb_string() will returns -EINVAL. This will result in '0' being inserted into buf[-22], and the following KASAN out-of-bound error message will be output. AudioControl Interface Descriptor: bLength 8 bDescriptorType 36 bDescriptorSubtype 10 (CLOCK_SOURCE) bClockID 1 bmAttributes 0x07 Internal programmable Clock (synced to SOF) bmControls 0x07 Clock Frequency Control (read/write) Clock Validity Control (read-only) bAssocTerminal 0 iClockSource 0 To fix it, check usb_string()'return value and bail out. ================================================================== BUG: KASAN: stack-out-of-bounds in parse_audio_unit+0x1327/0x1960 [snd_usb_audio] Write of size 1 at addr ffff88007e66735a by task systemd-udevd/18376 CPU: 0 PID: 18376 Comm: systemd-udevd Not tainted 4.13.0+ #3 Hardware name: LG Electronics 15N540-RFLGL/White Tip Mountain, BIOS 15N5 Call Trace: dump_stack+0x63/0x8d print_address_description+0x70/0x290 ? parse_audio_unit+0x1327/0x1960 [snd_usb_audio] kasan_report+0x265/0x350 __asan_store1+0x4a/0x50 parse_audio_unit+0x1327/0x1960 [snd_usb_audio] ? save_stack+0xb5/0xd0 ? save_stack_trace+0x1b/0x20 ? save_stack+0x46/0xd0 ? kasan_kmalloc+0xad/0xe0 ? kmem_cache_alloc_trace+0xff/0x230 ? snd_usb_create_mixer+0xb0/0x4b0 [snd_usb_audio] ? usb_audio_probe+0x4de/0xf40 [snd_usb_audio] ? usb_probe_interface+0x1f5/0x440 ? driver_probe_device+0x3ed/0x660 ? build_feature_ctl+0xb10/0xb10 [snd_usb_audio] ? save_stack_trace+0x1b/0x20 ? init_object+0x69/0xa0 ? snd_usb_find_csint_desc+0xa8/0xf0 [snd_usb_audio] snd_usb_mixer_controls+0x1dc/0x370 [snd_usb_audio] ? build_audio_procunit+0x890/0x890 [snd_usb_audio] ? snd_usb_create_mixer+0xb0/0x4b0 [snd_usb_audio] ? kmem_cache_alloc_trace+0xff/0x230 ? usb_ifnum_to_if+0xbd/0xf0 snd_usb_create_mixer+0x25b/0x4b0 [snd_usb_audio] ? snd_usb_create_stream+0x255/0x2c0 [snd_usb_audio] usb_audio_probe+0x4de/0xf40 [snd_usb_audio] ? snd_usb_autosuspend.part.7+0x30/0x30 [snd_usb_audio] ? __pm_runtime_idle+0x90/0x90 ? kernfs_activate+0xa6/0xc0 ? usb_match_one_id_intf+0xdc/0x130 ? __pm_runtime_set_status+0x2d4/0x450 usb_probe_interface+0x1f5/0x440 Signed-off-by: Jaejoong Kim Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index efa045c6ad63..4e780d59a995 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -199,6 +199,10 @@ static int snd_usb_copy_string_desc(struct mixer_build *state, int index, char *buf, int maxlen) { int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + + if (len < 0) + return 0; + buf[len] = 0; return len; } -- GitLab From 7a3638bbec14afafd3322c7fc2f68cfad0ac3550 Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Mon, 4 Dec 2017 15:31:49 +0900 Subject: [PATCH 1940/5498] ALSA: usb-audio: Add check return value for usb_string() commit 89b89d121ffcf8d9546633b98ded9d18b8f75891 upstream. snd_usb_copy_string_desc() returns zero if usb_string() fails. In case of failure, we need to check the snd_usb_copy_string_desc()'s return value and add an exception case Signed-off-by: Jaejoong Kim Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 4e780d59a995..92ad24ef1c5b 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2099,13 +2099,14 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, if (len) ; else if (nameid) - snd_usb_copy_string_desc(state, nameid, kctl->id.name, + len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); - else { + else len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 0); - if (!len) - strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); + + if (!len) { + strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) append_ctl_name(kctl, " Clock Source"); -- GitLab From 8edcd25ef69fb30f7dfb0c1c649d5625b326746b Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 28 Sep 2017 15:14:01 +0100 Subject: [PATCH 1941/5498] iommu/vt-d: Fix scatterlist offset handling commit 29a90b70893817e2f2bb3cea40a29f5308e21b21 upstream. The intel-iommu DMA ops fail to correctly handle scatterlists where sg->offset is greater than PAGE_SIZE - the IOVA allocation is computed appropriately based on the page-aligned portion of the offset, but the mapping is set up relative to sg->page, which means it fails to actually cover the whole buffer (and in the worst case doesn't cover it at all): (sg->dma_address + sg->dma_len) ----+ sg->dma_address ---------+ | iov_pfn------+ | | | | | v v v iova: a b c d e f |--------|--------|--------|--------|--------| <...calculated....> [_____mapped______] pfn: 0 1 2 3 4 5 |--------|--------|--------|--------|--------| ^ ^ ^ | | | sg->page ----+ | | sg->offset --------------+ | (sg->offset + sg->length) ----------+ As a result, the caller ends up overrunning the mapping into whatever lies beyond, which usually goes badly: [ 429.645492] DMAR: DRHD: handling fault status reg 2 [ 429.650847] DMAR: [DMA Write] Request device [02:00.4] fault addr f2682000 ... Whilst this is a fairly rare occurrence, it can happen from the result of intermediate scatterlist processing such as scatterwalk_ffwd() in the crypto layer. Whilst that particular site could be fixed up, it still seems worthwhile to bring intel-iommu in line with other DMA API implementations in handling this robustly. To that end, fix the intel_map_sg() path to line up the mapping correctly (in units of MM pages rather than VT-d pages to match the aligned_nrpages() calculation) regardless of the offset, and use sg_phys() consistently for clarity. Reported-by: Harsh Jain Signed-off-by: Robin Murphy Reviewed by: Ashok Raj Tested by: Jacob Pan Signed-off-by: Alex Williamson Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/intel-iommu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c0af2c06bb7b..351da1da814f 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2016,10 +2016,12 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, uint64_t tmp; if (!sg_res) { + unsigned int pgoff = sg->offset & ~PAGE_MASK; + sg_res = aligned_nrpages(sg->offset, sg->length); - sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset; + sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + pgoff; sg->dma_length = sg->length; - pteval = page_to_phys(sg_page(sg)) | prot; + pteval = (sg_phys(sg) - pgoff) | prot; phys_pfn = pteval >> VTD_PAGE_SHIFT; } @@ -3326,7 +3328,7 @@ static int intel_nontranslate_map_sg(struct device *hddev, for_each_sg(sglist, sg, nelems, i) { BUG_ON(!sg_page(sg)); - sg->dma_address = page_to_phys(sg_page(sg)) + sg->offset; + sg->dma_address = sg_phys(sg); sg->dma_length = sg->length; } return nelems; -- GitLab From 620d5e915ec5a43642c27dfc4f0b84c6773998db Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Mon, 2 Mar 2015 14:13:36 +0000 Subject: [PATCH 1942/5498] kdb: Fix handling of kallsyms_symbol_next() return value commit c07d35338081d107e57cf37572d8cc931a8e32e2 upstream. kallsyms_symbol_next() returns a boolean (true on success). Currently kdb_read() tests the return value with an inequality that unconditionally evaluates to true. This is fixed in the obvious way and, since the conditional branch is supposed to be unreachable, we also add a WARN_ON(). Reported-by: Dan Carpenter Signed-off-by: Daniel Thompson Signed-off-by: Jason Wessel Signed-off-by: Greg Kroah-Hartman --- kernel/debug/kdb/kdb_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 7c70812caea5..681c8b42e013 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -349,7 +349,7 @@ poll_again: } kdb_printf("\n"); for (i = 0; i < count; i++) { - if (kallsyms_symbol_next(p_tmp, i) < 0) + if (WARN_ON(!kallsyms_symbol_next(p_tmp, i))) break; kdb_printf("%s ", p_tmp); *(p_tmp + len) = '\0'; -- GitLab From fc93b6293c4199c210fb5a102c3b61b351b86b24 Mon Sep 17 00:00:00 2001 From: Laurent Caumont Date: Sat, 11 Nov 2017 12:44:46 -0500 Subject: [PATCH 1943/5498] media: dvb: i2c transfers over usb cannot be done from stack commit 6d33377f2abbf9f0e561b116dd468d1c3ff36a6a upstream. Signed-off-by: Laurent Caumont Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/dvb-usb/dibusb-common.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index ef3a8f75f82e..7b15aea2723d 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -179,8 +179,20 @@ EXPORT_SYMBOL(dibusb_i2c_algo); int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val) { - u8 wbuf[1] = { offs }; - return dibusb_i2c_msg(d, 0x50, wbuf, 1, val, 1); + u8 *buf; + int rc; + + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = offs; + + rc = dibusb_i2c_msg(d, 0x50, &buf[0], 1, &buf[1], 1); + *val = buf[1]; + kfree(buf); + + return rc; } EXPORT_SYMBOL(dibusb_read_eeprom_byte); -- GitLab From 140db397a1440a325a144af1defa458e8330680b Mon Sep 17 00:00:00 2001 From: Kristina Martsenko Date: Thu, 16 Nov 2017 17:58:20 +0000 Subject: [PATCH 1944/5498] arm64: KVM: fix VTTBR_BADDR_MASK BUG_ON off-by-one commit 26aa7b3b1c0fb3f1a6176a0c1847204ef4355693 upstream. VTTBR_BADDR_MASK is used to sanity check the size and alignment of the VTTBR address. It seems to currently be off by one, thereby only allowing up to 47-bit addresses (instead of 48-bit) and also insufficiently checking the alignment. This patch fixes it. As an example, with 4k pages, before this patch we have: PHYS_MASK_SHIFT = 48 VTTBR_X = 37 - 24 = 13 VTTBR_BADDR_SHIFT = 13 - 1 = 12 VTTBR_BADDR_MASK = ((1 << 35) - 1) << 12 = 0x00007ffffffff000 Which is wrong, because the mask doesn't allow bit 47 of the VTTBR address to be set, and only requires the address to be 12-bit (4k) aligned, while it actually needs to be 13-bit (8k) aligned because we concatenate two 4k tables. With this patch, the mask becomes 0x0000ffffffffe000, which is what we want. Fixes: 0369f6a34b9f ("arm64: KVM: EL2 register definitions") Reviewed-by: Suzuki K Poulose Reviewed-by: Christoffer Dall Signed-off-by: Kristina Martsenko Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/kvm_arm.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index 8afb863f5a9e..333ddd45dd1f 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -160,8 +160,7 @@ #define VTTBR_X (37 - VTCR_EL2_T0SZ_40B) #endif -#define VTTBR_BADDR_SHIFT (VTTBR_X - 1) -#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT) +#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_X) #define VTTBR_VMID_SHIFT (UL(48)) #define VTTBR_VMID_MASK (UL(0xFF) << VTTBR_VMID_SHIFT) -- GitLab From 7389171fdc5976066573edd1fcf6c9a81d8df90f Mon Sep 17 00:00:00 2001 From: Andrew Honig Date: Fri, 1 Dec 2017 10:21:09 -0800 Subject: [PATCH 1945/5498] KVM: VMX: remove I/O port 0x80 bypass on Intel hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d59d51f088014f25c2562de59b9abff4f42a7468 upstream. This fixes CVE-2017-1000407. KVM allows guests to directly access I/O port 0x80 on Intel hosts. If the guest floods this port with writes it generates exceptions and instability in the host kernel, leading to a crash. With this change guest writes to port 0x80 on Intel will behave the same as they currently behave on AMD systems. Prevent the flooding by removing the code that sets port 0x80 as a passthrough port. This is essentially the same as upstream patch 99f85a28a78e96d28907fe036e1671a218fee597, except that patch was for AMD chipsets and this patch is for Intel. Signed-off-by: Andrew Honig Signed-off-by: Jim Mattson Fixes: fdef3ad1b386 ("KVM: VMX: Enable io bitmaps to avoid IO port 0x80 VMEXITs") Signed-off-by: Radim KrÄmář Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 66a6f57634be..863a473e6f36 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -9280,12 +9280,7 @@ static int __init vmx_init(void) memset(vmx_vmread_bitmap, 0xff, PAGE_SIZE); memset(vmx_vmwrite_bitmap, 0xff, PAGE_SIZE); - /* - * Allow direct access to the PC debug port (it is often used for I/O - * delays, but the vmexits simply slow things down). - */ memset(vmx_io_bitmap_a, 0xff, PAGE_SIZE); - clear_bit(0x80, vmx_io_bitmap_a); memset(vmx_io_bitmap_b, 0xff, PAGE_SIZE); -- GitLab From a8779c22fdd5c33ed43efdb4f3ccf7cf3c9b6a27 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 5 Dec 2017 14:56:42 +0000 Subject: [PATCH 1946/5498] arm64: fpsimd: Prevent registers leaking from dead tasks commit 071b6d4a5d343046f253a5a8835d477d93992002 upstream. Currently, loading of a task's fpsimd state into the CPU registers is skipped if that task's state is already present in the registers of that CPU. However, the code relies on the struct fpsimd_state * (and by extension struct task_struct *) to unambiguously identify a task. There is a particular case in which this doesn't work reliably: when a task exits, its task_struct may be recycled to describe a new task. Consider the following scenario: 1) Task P loads its fpsimd state onto cpu C. per_cpu(fpsimd_last_state, C) := P; P->thread.fpsimd_state.cpu := C; 2) Task X is scheduled onto C and loads its fpsimd state on C. per_cpu(fpsimd_last_state, C) := X; X->thread.fpsimd_state.cpu := C; 3) X exits, causing X's task_struct to be freed. 4) P forks a new child T, which obtains X's recycled task_struct. T == X. T->thread.fpsimd_state.cpu == C (inherited from P). 5) T is scheduled on C. T's fpsimd state is not loaded, because per_cpu(fpsimd_last_state, C) == T (== X) && T->thread.fpsimd_state.cpu == C. (This is the check performed by fpsimd_thread_switch().) So, T gets X's registers because the last registers loaded onto C were those of X, in (2). This patch fixes the problem by ensuring that the sched-in check fails in (5): fpsimd_flush_task_state(T) is called when T is forked, so that T->thread.fpsimd_state.cpu == C cannot be true. This relies on the fact that T is not schedulable until after copy_thread() completes. Once T's fpsimd state has been loaded on some CPU C there may still be other cpus D for which per_cpu(fpsimd_last_state, D) == &X->thread.fpsimd_state. But D is necessarily != C in this case, and the check in (5) must fail. An alternative fix would be to do refcounting on task_struct. This would result in each CPU holding a reference to the last task whose fpsimd state was loaded there. It's not clear whether this is preferable, and it involves higher overhead than the fix proposed in this patch. It would also move all the task_struct freeing work into the context switch critical section, or otherwise some deferred cleanup mechanism would need to be introduced, neither of which seems obviously justified. Fixes: 005f78cd8849 ("arm64: defer reloading a task's FPSIMD state to userland resume") Signed-off-by: Dave Martin Reviewed-by: Ard Biesheuvel [will: word-smithed the comment so it makes more sense] Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/process.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index fde9923af859..037ffc3a35d0 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -250,6 +250,15 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); + /* + * In case p was allocated the same task_struct pointer as some + * other recently-exited task, make sure p is disassociated from + * any cpu that may have run that now-exited task recently. + * Otherwise we could erroneously skip reloading the FPSIMD + * registers for p. + */ + fpsimd_flush_task_state(p); + if (likely(!(p->flags & PF_KTHREAD))) { *childregs = *current_pt_regs(); childregs->regs[0] = 0; -- GitLab From 6d40c9bf5733c9c6db79e0e6d398371955b525af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Bugge?= Date: Wed, 6 Dec 2017 17:18:28 +0100 Subject: [PATCH 1947/5498] rds: Fix NULL pointer dereference in __rds_rdma_map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f3069c6d33f6ae63a1668737bc78aaaa51bff7ca ] This is a fix for syzkaller719569, where memory registration was attempted without any underlying transport being loaded. Analysis of the case reveals that it is the setsockopt() RDS_GET_MR (2) and RDS_GET_MR_FOR_DEST (7) that are vulnerable. Here is an example stack trace when the bug is hit: BUG: unable to handle kernel NULL pointer dereference at 00000000000000c0 IP: __rds_rdma_map+0x36/0x440 [rds] PGD 2f93d03067 P4D 2f93d03067 PUD 2f93d02067 PMD 0 Oops: 0000 [#1] SMP Modules linked in: bridge stp llc tun rpcsec_gss_krb5 nfsv4 dns_resolver nfs fscache rds binfmt_misc sb_edac intel_powerclamp coretemp kvm_intel kvm irqbypass crct10dif_pclmul c rc32_pclmul ghash_clmulni_intel pcbc aesni_intel crypto_simd glue_helper cryptd iTCO_wdt mei_me sg iTCO_vendor_support ipmi_si mei ipmi_devintf nfsd shpchp pcspkr i2c_i801 ioatd ma ipmi_msghandler wmi lpc_ich mfd_core auth_rpcgss nfs_acl lockd grace sunrpc ip_tables ext4 mbcache jbd2 mgag200 i2c_algo_bit drm_kms_helper ixgbe syscopyarea ahci sysfillrect sysimgblt libahci mdio fb_sys_fops ttm ptp libata sd_mod mlx4_core drm crc32c_intel pps_core megaraid_sas i2c_core dca dm_mirror dm_region_hash dm_log dm_mod CPU: 48 PID: 45787 Comm: repro_set2 Not tainted 4.14.2-3.el7uek.x86_64 #2 Hardware name: Oracle Corporation ORACLE SERVER X5-2L/ASM,MOBO TRAY,2U, BIOS 31110000 03/03/2017 task: ffff882f9190db00 task.stack: ffffc9002b994000 RIP: 0010:__rds_rdma_map+0x36/0x440 [rds] RSP: 0018:ffffc9002b997df0 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ffff882fa2182580 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffc9002b997e40 RDI: ffff882fa2182580 RBP: ffffc9002b997e30 R08: 0000000000000000 R09: 0000000000000002 R10: ffff885fb29e3838 R11: 0000000000000000 R12: ffff882fa2182580 R13: ffff882fa2182580 R14: 0000000000000002 R15: 0000000020000ffc FS: 00007fbffa20b700(0000) GS:ffff882fbfb80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000000000c0 CR3: 0000002f98a66006 CR4: 00000000001606e0 Call Trace: rds_get_mr+0x56/0x80 [rds] rds_setsockopt+0x172/0x340 [rds] ? __fget_light+0x25/0x60 ? __fdget+0x13/0x20 SyS_setsockopt+0x80/0xe0 do_syscall_64+0x67/0x1b0 entry_SYSCALL64_slow_path+0x25/0x25 RIP: 0033:0x7fbff9b117f9 RSP: 002b:00007fbffa20aed8 EFLAGS: 00000293 ORIG_RAX: 0000000000000036 RAX: ffffffffffffffda RBX: 00000000000c84a4 RCX: 00007fbff9b117f9 RDX: 0000000000000002 RSI: 0000400000000114 RDI: 000000000000109b RBP: 00007fbffa20af10 R08: 0000000000000020 R09: 00007fbff9dd7860 R10: 0000000020000ffc R11: 0000000000000293 R12: 0000000000000000 R13: 00007fbffa20b9c0 R14: 00007fbffa20b700 R15: 0000000000000021 Code: 41 56 41 55 49 89 fd 41 54 53 48 83 ec 18 8b 87 f0 02 00 00 48 89 55 d0 48 89 4d c8 85 c0 0f 84 2d 03 00 00 48 8b 87 00 03 00 00 <48> 83 b8 c0 00 00 00 00 0f 84 25 03 00 0 0 48 8b 06 48 8b 56 08 The fix is to check the existence of an underlying transport in __rds_rdma_map(). Signed-off-by: HÃ¥kon Bugge Reported-by: syzbot Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/rds/rdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rds/rdma.c b/net/rds/rdma.c index 3738b1920c09..612c3050d514 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -184,7 +184,7 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, long i; int ret; - if (rs->rs_bound_addr == 0) { + if (rs->rs_bound_addr == 0 || !rs->rs_transport) { ret = -ENOTCONN; /* XXX not a great errno */ goto out; } -- GitLab From a3581a1b179fad3b3c1b4993dbde4e19fe97e739 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Thu, 30 Nov 2017 10:41:14 +0800 Subject: [PATCH 1948/5498] sit: update frag_off info [ Upstream commit f859b4af1c52493ec21173ccc73d0b60029b5b88 ] After parsing the sit netlink change info, we forget to update frag_off in ipip6_tunnel_update(). Fix it by assigning frag_off with new value. Reported-by: Jianlin Shi Signed-off-by: Hangbin Liu Acked-by: Nicolas Dichtel Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/sit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index eb36219f0a3c..a67dc0c28cdc 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1093,6 +1093,7 @@ static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p) ipip6_tunnel_link(sitn, t); t->parms.iph.ttl = p->iph.ttl; t->parms.iph.tos = p->iph.tos; + t->parms.iph.frag_off = p->iph.frag_off; if (t->parms.link != p->link) { t->parms.link = p->link; ipip6_tunnel_bind_dev(t->dev); -- GitLab From 18f0f8c1e866a5c22d2bebcc368c5217670753cf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 28 Nov 2017 08:03:30 -0800 Subject: [PATCH 1949/5498] net/packet: fix a race in packet_bind() and packet_notifier() [ Upstream commit 15fe076edea787807a7cdc168df832544b58eba6 ] syzbot reported crashes [1] and provided a C repro easing bug hunting. When/if packet_do_bind() calls __unregister_prot_hook() and releases po->bind_lock, another thread can run packet_notifier() and process an NETDEV_UP event. This calls register_prot_hook() and hooks again the socket right before first thread is able to grab again po->bind_lock. Fixes this issue by temporarily setting po->num to 0, as suggested by David Miller. [1] dev_remove_pack: ffff8801bf16fa80 not found ------------[ cut here ]------------ kernel BUG at net/core/dev.c:7945! ( BUG_ON(!list_empty(&dev->ptype_all)); ) invalid opcode: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: device syz0 entered promiscuous mode CPU: 0 PID: 3161 Comm: syzkaller404108 Not tainted 4.14.0+ #190 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 task: ffff8801cc57a500 task.stack: ffff8801cc588000 RIP: 0010:netdev_run_todo+0x772/0xae0 net/core/dev.c:7945 RSP: 0018:ffff8801cc58f598 EFLAGS: 00010293 RAX: ffff8801cc57a500 RBX: dffffc0000000000 RCX: ffffffff841f75b2 RDX: 0000000000000000 RSI: 1ffff100398b1ede RDI: ffff8801bf1f8810 device syz0 entered promiscuous mode RBP: ffff8801cc58f898 R08: 0000000000000001 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8801bf1f8cd8 R13: ffff8801cc58f870 R14: ffff8801bf1f8780 R15: ffff8801cc58f7f0 FS: 0000000001716880(0000) GS:ffff8801db400000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020b13000 CR3: 0000000005e25000 CR4: 00000000001406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: rtnl_unlock+0xe/0x10 net/core/rtnetlink.c:106 tun_detach drivers/net/tun.c:670 [inline] tun_chr_close+0x49/0x60 drivers/net/tun.c:2845 __fput+0x333/0x7f0 fs/file_table.c:210 ____fput+0x15/0x20 fs/file_table.c:244 task_work_run+0x199/0x270 kernel/task_work.c:113 exit_task_work include/linux/task_work.h:22 [inline] do_exit+0x9bb/0x1ae0 kernel/exit.c:865 do_group_exit+0x149/0x400 kernel/exit.c:968 SYSC_exit_group kernel/exit.c:979 [inline] SyS_exit_group+0x1d/0x20 kernel/exit.c:977 entry_SYSCALL_64_fastpath+0x1f/0x96 RIP: 0033:0x44ad19 Fixes: 30f7ea1c2b5f ("packet: race condition in packet_bind") Signed-off-by: Eric Dumazet Reported-by: syzbot Cc: Francesco Ruggeri Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/packet/af_packet.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d872be097c60..87488f2721fb 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2704,6 +2704,10 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, if (need_rehook) { if (po->running) { rcu_read_unlock(); + /* prevents packet_notifier() from calling + * register_prot_hook() + */ + po->num = 0; __unregister_prot_hook(sk, true); rcu_read_lock(); dev_curr = po->prot_hook.dev; @@ -2712,6 +2716,7 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, dev->ifindex); } + BUG_ON(po->running); po->num = proto; po->prot_hook.type = proto; -- GitLab From ba44cfc88fa15f48eae3dad2fd1549b7acfb1f5c Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 7 Dec 2017 23:21:06 -0500 Subject: [PATCH 1950/5498] Revert "drm/armada: Fix compile fail" This reverts commit 82f260d472c3b4dbb7324624e395c3e91f73a040. Not required on < 4.10. Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/armada/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile index c9c619727806..d6f43e06150a 100644 --- a/drivers/gpu/drm/armada/Makefile +++ b/drivers/gpu/drm/armada/Makefile @@ -5,5 +5,3 @@ armada-y += armada_510.o armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o obj-$(CONFIG_DRM_ARMADA) := armada.o - -CFLAGS_armada_trace.o := -I$(src) -- GitLab From ab1d27b51272e0a6c503064b5800b0d8e3e1644b Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 8 Dec 2017 00:11:47 -0500 Subject: [PATCH 1951/5498] Revert "s390/kbuild: enable modversions for symbols exported from asm" This reverts commit cabab3f9f5ca077535080b3252e6168935b914af. Not needed for < 4.9. Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/s390/include/asm/asm-prototypes.h | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 arch/s390/include/asm/asm-prototypes.h diff --git a/arch/s390/include/asm/asm-prototypes.h b/arch/s390/include/asm/asm-prototypes.h deleted file mode 100644 index 2c3413b0ca52..000000000000 --- a/arch/s390/include/asm/asm-prototypes.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _ASM_S390_PROTOTYPES_H - -#include -#include -#include -#include - -#endif /* _ASM_S390_PROTOTYPES_H */ -- GitLab From 99dcb707178e36a49a9d1e6d155cda3b1d4ed74b Mon Sep 17 00:00:00 2001 From: Sachin Sant Date: Sun, 26 Feb 2017 11:38:39 +0530 Subject: [PATCH 1952/5498] selftest/powerpc: Fix false failures for skipped tests [ Upstream commit a6d8a21596df041f36f4c2ccc260c459e3e851f1 ] Tests under alignment subdirectory are skipped when executed on previous generation hardware, but harness still marks them as failed. test: test_copy_unaligned tags: git_version:unknown [SKIP] Test skipped on line 26 skip: test_copy_unaligned selftests: copy_unaligned [FAIL] The MAGIC_SKIP_RETURN_VALUE value assigned to rc variable is retained till the program exit which causes the test to be marked as failed. This patch resets the value before returning to the main() routine. With this patch the test o/p is as follows: test: test_copy_unaligned tags: git_version:unknown [SKIP] Test skipped on line 26 skip: test_copy_unaligned selftests: copy_unaligned [PASS] Signed-off-by: Sachin Sant Signed-off-by: Michael Ellerman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/powerpc/harness.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c index 8ebc58a09311..d1ed7bab65a5 100644 --- a/tools/testing/selftests/powerpc/harness.c +++ b/tools/testing/selftests/powerpc/harness.c @@ -105,9 +105,11 @@ int test_harness(int (test_function)(void), char *name) rc = run_test(test_function, name); - if (rc == MAGIC_SKIP_RETURN_VALUE) + if (rc == MAGIC_SKIP_RETURN_VALUE) { test_skip(name); - else + /* so that skipped test is not marked as failed */ + rc = 0; + } else test_finish(name, rc); return rc; -- GitLab From 3e92917312918da12d89fc86cea1c977abc92e98 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Tue, 28 Feb 2017 10:55:30 +0000 Subject: [PATCH 1953/5498] usb: gadget: configs: plug memory leak [ Upstream commit 38355b2a44776c25b0f2ad466e8c51bb805b3032 ] When binding a gadget to a device, "name" is stored in gi->udc_name, but this does not happen when unregistering and the string is leaked. Signed-off-by: John Keeping Signed-off-by: Felipe Balbi Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/configfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index a7e1a96e69a5..156c252c5aab 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -266,6 +266,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi, ret = unregister_gadget(gi); if (ret) goto err; + kfree(name); } else { if (gi->udc_name) { ret = -EBUSY; -- GitLab From 4c09b39cd9934b94979103398a0dbd506579cb3f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 21 Feb 2017 22:33:11 +0100 Subject: [PATCH 1954/5498] USB: gadgetfs: Fix a potential memory leak in 'dev_config()' [ Upstream commit b6e7aeeaf235901c42ec35de4633c7c69501d303 ] 'kbuf' is allocated just a few lines above using 'memdup_user()'. If the 'if (dev->buf)' test fails, this memory is never released. Signed-off-by: Christophe JAILLET Signed-off-by: Felipe Balbi Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/inode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 368d07937848..e2d57e3d67c7 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1921,8 +1921,10 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) spin_lock_irq (&dev->lock); value = -EINVAL; - if (dev->buf) + if (dev->buf) { + kfree(kbuf); goto fail; + } dev->buf = kbuf; /* full or low speed config */ -- GitLab From 8b79fa3b43e18a67fd78d186e83ba674fb0424db Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 6 Mar 2017 15:26:54 -0500 Subject: [PATCH 1955/5498] libata: drop WARN from protocol error in ata_sff_qc_issue() [ Upstream commit 0580b762a4d6b70817476b90042813f8573283fa ] ata_sff_qc_issue() expects upper layers to never issue commands on a command protocol that it doesn't implement. While the assumption holds fine with the usual IO path, nothing filters based on the command protocol in the passthrough path (which was added later), allowing the warning to be tripped with a passthrough command with the right (well, wrong) protocol. Failing with AC_ERR_SYSTEM is the right thing to do anyway. Remove the unnecessary WARN. Reported-by: Dmitry Vyukov Link: http://lkml.kernel.org/r/CACT4Y+bXkvevNZU8uP6X0QVqsj6wNoUA_1exfTSOzc+SmUtMOA@mail.gmail.com Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-sff.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 12d337754e4a..58b0a58a7f1d 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1480,7 +1480,6 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) break; default: - WARN_ON_ONCE(1); return AC_ERR_SYSTEM; } -- GitLab From 3366c0d324f036fd2ee42cbc56892d3c5014b3a7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 6 Mar 2017 15:33:42 -0500 Subject: [PATCH 1956/5498] workqueue: trigger WARN if queue_delayed_work() is called with NULL @wq [ Upstream commit 637fdbae60d6cb9f6e963c1079d7e0445c86ff7d ] If queue_delayed_work() gets called with NULL @wq, the kernel will oops asynchronuosly on timer expiration which isn't too helpful in tracking down the offender. This actually happened with smc. __queue_delayed_work() already does several input sanity checks synchronously. Add NULL @wq check. Reported-by: Dave Jones Link: http://lkml.kernel.org/r/20170227171439.jshx3qplflyrgcv7@codemonkey.org.uk Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/workqueue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index a636e38c0813..39998408e705 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1452,6 +1452,7 @@ static void __queue_delayed_work(int cpu, struct workqueue_struct *wq, struct timer_list *timer = &dwork->timer; struct work_struct *work = &dwork->work; + WARN_ON_ONCE(!wq); WARN_ON_ONCE(timer->function != delayed_work_timer_fn || timer->data != (unsigned long)dwork); WARN_ON_ONCE(timer_pending(timer)); -- GitLab From 0cee435dc74d5b636ede23c3c7122f9866f7247d Mon Sep 17 00:00:00 2001 From: James Smart Date: Sat, 4 Mar 2017 09:30:25 -0800 Subject: [PATCH 1957/5498] scsi: lpfc: Fix crash during Hardware error recovery on SLI3 adapters [ Upstream commit 5d181531bc6169e19a02a27d202cf0e982db9d0e ] if REG_VPI fails, the driver was incorrectly issuing INIT_VFI (a SLI4 command) on a SLI3 adapter. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_els.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 4c25485aa934..c76279926ad6 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -7265,11 +7265,17 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; spin_unlock_irq(shost->host_lock); - if (vport->port_type == LPFC_PHYSICAL_PORT - && !(vport->fc_flag & FC_LOGO_RCVD_DID_CHNG)) - lpfc_issue_init_vfi(vport); - else + if (mb->mbxStatus == MBX_NOT_FINISHED) + break; + if ((vport->port_type == LPFC_PHYSICAL_PORT) && + !(vport->fc_flag & FC_LOGO_RCVD_DID_CHNG)) { + if (phba->sli_rev == LPFC_SLI_REV4) + lpfc_issue_init_vfi(vport); + else + lpfc_initial_flogi(vport); + } else { lpfc_initial_fdisc(vport); + } break; } } else { -- GitLab From 28657a1ae900b1f33620998803d8d8f57d552c01 Mon Sep 17 00:00:00 2001 From: Franck Demathieu Date: Mon, 6 Mar 2017 14:41:06 +0100 Subject: [PATCH 1958/5498] irqchip/crossbar: Fix incorrect type of register size [ Upstream commit 4b9de5da7e120c7f02395da729f0ec77ce7a6044 ] The 'size' variable is unsigned according to the dt-bindings. As this variable is used as integer in other places, create a new variable that allows to fix the following sparse issue (-Wtypesign): drivers/irqchip/irq-crossbar.c:279:52: warning: incorrect type in argument 3 (different signedness) drivers/irqchip/irq-crossbar.c:279:52: expected unsigned int [usertype] *out_value drivers/irqchip/irq-crossbar.c:279:52: got int * Signed-off-by: Franck Demathieu Signed-off-by: Marc Zyngier Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-crossbar.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index c03773bf02c7..8d332a36e560 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -176,7 +176,7 @@ static const struct irq_domain_ops routable_irq_domain_ops = { static int __init crossbar_of_init(struct device_node *node) { int i, size, reserved = 0; - u32 max = 0, entry; + u32 max = 0, entry, reg_size; const __be32 *irqsr; int ret = -ENOMEM; @@ -253,9 +253,9 @@ static int __init crossbar_of_init(struct device_node *node) if (!cb->register_offsets) goto err_irq_map; - of_property_read_u32(node, "ti,reg-size", &size); + of_property_read_u32(node, "ti,reg-size", ®_size); - switch (size) { + switch (reg_size) { case 1: cb->write = crossbar_writeb; break; @@ -281,7 +281,7 @@ static int __init crossbar_of_init(struct device_node *node) continue; cb->register_offsets[i] = reserved; - reserved += size; + reserved += reg_size; } of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); -- GitLab From ab8447147ba09ec7d44e6cfe3f2ad7bb5128bf13 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Mon, 6 Mar 2017 04:03:28 -0800 Subject: [PATCH 1959/5498] KVM: nVMX: reset nested_run_pending if the vCPU is going to be reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2f707d97982286b307ef2a9b034e19aabc1abb56 ] Reported by syzkaller: WARNING: CPU: 1 PID: 27742 at arch/x86/kvm/vmx.c:11029 nested_vmx_vmexit+0x5c35/0x74d0 arch/x86/kvm/vmx.c:11029 CPU: 1 PID: 27742 Comm: a.out Not tainted 4.10.0+ #229 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:15 [inline] dump_stack+0x2ee/0x3ef lib/dump_stack.c:51 panic+0x1fb/0x412 kernel/panic.c:179 __warn+0x1c4/0x1e0 kernel/panic.c:540 warn_slowpath_null+0x2c/0x40 kernel/panic.c:583 nested_vmx_vmexit+0x5c35/0x74d0 arch/x86/kvm/vmx.c:11029 vmx_leave_nested arch/x86/kvm/vmx.c:11136 [inline] vmx_set_msr+0x1565/0x1910 arch/x86/kvm/vmx.c:3324 kvm_set_msr+0xd4/0x170 arch/x86/kvm/x86.c:1099 do_set_msr+0x11e/0x190 arch/x86/kvm/x86.c:1128 __msr_io arch/x86/kvm/x86.c:2577 [inline] msr_io+0x24b/0x450 arch/x86/kvm/x86.c:2614 kvm_arch_vcpu_ioctl+0x35b/0x46a0 arch/x86/kvm/x86.c:3497 kvm_vcpu_ioctl+0x232/0x1120 arch/x86/kvm/../../../virt/kvm/kvm_main.c:2721 vfs_ioctl fs/ioctl.c:43 [inline] do_vfs_ioctl+0x1bf/0x1790 fs/ioctl.c:683 SYSC_ioctl fs/ioctl.c:698 [inline] SyS_ioctl+0x8f/0xc0 fs/ioctl.c:689 entry_SYSCALL_64_fastpath+0x1f/0xc2 The syzkaller folks reported a nested_run_pending warning during userspace clear VMX capability which is exposed to L1 before. The warning gets thrown while doing (*(uint32_t*)0x20aecfe8 = (uint32_t)0x1); (*(uint32_t*)0x20aecfec = (uint32_t)0x0); (*(uint32_t*)0x20aecff0 = (uint32_t)0x3a); (*(uint32_t*)0x20aecff4 = (uint32_t)0x0); (*(uint64_t*)0x20aecff8 = (uint64_t)0x0); r[29] = syscall(__NR_ioctl, r[4], 0x4008ae89ul, 0x20aecfe8ul, 0, 0, 0, 0, 0, 0); i.e. KVM_SET_MSR ioctl with struct kvm_msrs { .nmsrs = 1, .pad = 0, .entries = { {.index = MSR_IA32_FEATURE_CONTROL, .reserved = 0, .data = 0} } } The VMLANCH/VMRESUME emulation should be stopped since the CPU is going to reset here. This patch resets the nested_run_pending since the CPU is going to be reset hence there should be nothing pending. Reported-by: Dmitry Vyukov Suggested-by: Radim KrÄmář Cc: Paolo Bonzini Cc: Radim KrÄmář Cc: Dmitry Vyukov Cc: David Hildenbrand Signed-off-by: Wanpeng Li Reviewed-by: David Hildenbrand Reviewed-by: Jim Mattson Signed-off-by: Radim KrÄmář Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 863a473e6f36..bdbd5d3fc98f 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -9086,8 +9086,10 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason, */ static void vmx_leave_nested(struct kvm_vcpu *vcpu) { - if (is_guest_mode(vcpu)) + if (is_guest_mode(vcpu)) { + to_vmx(vcpu)->nested.nested_run_pending = 0; nested_vmx_vmexit(vcpu, -1, 0, 0); + } free_nested(to_vmx(vcpu)); } -- GitLab From 0faf52395fa258e39193fe6e4d6e13bb2341e96d Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 20 Feb 2017 12:30:11 +0000 Subject: [PATCH 1960/5498] arm: KVM: Survive unknown traps from guests [ Upstream commit f050fe7a9164945dd1c28be05bf00e8cfb082ccf ] Currently we BUG() if we see a HSR.EC value we don't recognise. As configurable disables/enables are added to the architecture (controlled by RES1/RES0 bits respectively), with associated synchronous exceptions, it may be possible for a guest to trigger exceptions with classes that we don't recognise. While we can't service these exceptions in a manner useful to the guest, we can avoid bringing down the host. Per ARM DDI 0406C.c, all currently unallocated HSR EC encodings are reserved, and per ARM DDI 0487A.k_iss10775, page G6-4395, EC values within the range 0x00 - 0x2c are reserved for future use with synchronous exceptions, and EC values within the range 0x2d - 0x3f may be used for either synchronous or asynchronous exceptions. The patch makes KVM handle any unknown EC by injecting an UNDEFINED exception into the guest, with a corresponding (ratelimited) warning in the host dmesg. We could later improve on this with with a new (opt-in) exit to the host userspace. Cc: Dave Martin Cc: Suzuki K Poulose Reviewed-by: Christoffer Dall Signed-off-by: Mark Rutland Signed-off-by: Marc Zyngier Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/include/asm/kvm_arm.h | 1 + arch/arm/kvm/handle_exit.c | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 816db0bf2dd8..151464c6bfdb 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h @@ -208,6 +208,7 @@ #define HSR_EC_IABT_HYP (0x21) #define HSR_EC_DABT (0x24) #define HSR_EC_DABT_HYP (0x25) +#define HSR_EC_MAX (0x3f) #define HSR_WFI_IS_WFE (1U << 0) diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c index a96a8043277c..b6ac50b85d72 100644 --- a/arch/arm/kvm/handle_exit.c +++ b/arch/arm/kvm/handle_exit.c @@ -98,7 +98,19 @@ static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run) return 1; } +static int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + u32 hsr = kvm_vcpu_get_hsr(vcpu); + + kvm_pr_unimpl("Unknown exception class: hsr: %#08x\n", + hsr); + + kvm_inject_undefined(vcpu); + return 1; +} + static exit_handle_fn arm_exit_handlers[] = { + [0 ... HSR_EC_MAX] = kvm_handle_unknown_ec, [HSR_EC_WFI] = kvm_handle_wfx, [HSR_EC_CP15_32] = kvm_handle_cp15_32, [HSR_EC_CP15_64] = kvm_handle_cp15_64, @@ -120,13 +132,6 @@ static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu) { u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu); - if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) || - !arm_exit_handlers[hsr_ec]) { - kvm_err("Unknown exception class: hsr: %#08x\n", - (unsigned int)kvm_vcpu_get_hsr(vcpu)); - BUG(); - } - return arm_exit_handlers[hsr_ec]; } -- GitLab From b263f04c208fd2b9c3845bce2576bdd17047f69b Mon Sep 17 00:00:00 2001 From: "Blomme, Maarten" Date: Thu, 2 Mar 2017 13:08:36 +0100 Subject: [PATCH 1961/5498] spi_ks8995: fix "BUG: key accdaa28 not in .data!" [ Upstream commit 4342696df764ec65dcdfbd0c10d90ea52505f8ba ] Signed-off-by: Maarten Blomme Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/spi_ks8995.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index eab57fc5b967..49dfb8abc43d 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -332,6 +332,7 @@ static int ks8995_probe(struct spi_device *spi) if (err) return err; + sysfs_attr_init(&ks->regs_attr.attr); err = sysfs_create_bin_file(&spi->dev.kobj, &ks->regs_attr); if (err) { dev_err(&spi->dev, "unable to create sysfs file, err=%d\n", -- GitLab From fc4281753409d6afd4851e864c35a3a74a62dd37 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Fri, 3 Mar 2017 17:08:30 +0100 Subject: [PATCH 1962/5498] bnx2x: fix possible overrun of VFPF multicast addresses array [ Upstream commit 22118d861cec5da6ed525aaf12a3de9bfeffc58f ] It is too late to check for the limit of the number of VF multicast addresses after they have already been copied to the req->multicast[] array, possibly overflowing it. Do the check before copying. Also fix the error path to not skip unlocking vf2pf_mutex. Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index b1d9c44aa56c..07eba80f6fb1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -826,7 +826,7 @@ int bnx2x_vfpf_set_mcast(struct net_device *dev) struct bnx2x *bp = netdev_priv(dev); struct vfpf_set_q_filters_tlv *req = &bp->vf2pf_mbox->req.set_q_filters; struct pfvf_general_resp_tlv *resp = &bp->vf2pf_mbox->resp.general_resp; - int rc, i = 0; + int rc = 0, i = 0; struct netdev_hw_addr *ha; if (bp->state != BNX2X_STATE_OPEN) { @@ -841,6 +841,15 @@ int bnx2x_vfpf_set_mcast(struct net_device *dev) /* Get Rx mode requested */ DP(NETIF_MSG_IFUP, "dev->flags = %x\n", dev->flags); + /* We support PFVF_MAX_MULTICAST_PER_VF mcast addresses tops */ + if (netdev_mc_count(dev) > PFVF_MAX_MULTICAST_PER_VF) { + DP(NETIF_MSG_IFUP, + "VF supports not more than %d multicast MAC addresses\n", + PFVF_MAX_MULTICAST_PER_VF); + rc = -EINVAL; + goto out; + } + netdev_for_each_mc_addr(ha, dev) { DP(NETIF_MSG_IFUP, "Adding mcast MAC: %pM\n", bnx2x_mc_addr(ha)); @@ -848,16 +857,6 @@ int bnx2x_vfpf_set_mcast(struct net_device *dev) i++; } - /* We support four PFVF_MAX_MULTICAST_PER_VF mcast - * addresses tops - */ - if (i >= PFVF_MAX_MULTICAST_PER_VF) { - DP(NETIF_MSG_IFUP, - "VF supports not more than %d multicast MAC addresses\n", - PFVF_MAX_MULTICAST_PER_VF); - return -EINVAL; - } - req->n_multicast = i; req->flags |= VFPF_SET_Q_FILTERS_MULTICAST_CHANGED; req->vf_qid = 0; @@ -882,7 +881,7 @@ int bnx2x_vfpf_set_mcast(struct net_device *dev) out: bnx2x_vfpf_finalize(bp, &req->first_tlv); - return 0; + return rc; } int bnx2x_vfpf_storm_rx_mode(struct bnx2x *bp) -- GitLab From c1351ce0b72109fa3fa66ab8496ed3110613a36b Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 5 Mar 2017 12:34:53 -0800 Subject: [PATCH 1963/5498] ipv6: reorder icmpv6_init() and ip6_mr_init() [ Upstream commit 15e668070a64bb97f102ad9cf3bccbca0545cda8 ] Andrey reported the following kernel crash: kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 0 PID: 14446 Comm: syz-executor6 Not tainted 4.10.0+ #82 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 task: ffff88001f311700 task.stack: ffff88001f6e8000 RIP: 0010:ip6mr_sk_done+0x15a/0x3d0 net/ipv6/ip6mr.c:1618 RSP: 0018:ffff88001f6ef418 EFLAGS: 00010202 RAX: dffffc0000000000 RBX: 1ffff10003edde8c RCX: ffffc900043ee000 RDX: 0000000000000004 RSI: ffffffff83e3b3f8 RDI: 0000000000000020 RBP: ffff88001f6ef508 R08: fffffbfff0dcc5d8 R09: 0000000000000000 R10: ffffffff86e62ec0 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: ffff88001f6ef4e0 R15: ffff8800380a0040 FS: 00007f7a52cec700(0000) GS:ffff88003ec00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 000000000061c500 CR3: 000000001f1ae000 CR4: 00000000000006f0 DR0: 0000000020000000 DR1: 0000000020000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000600 Call Trace: rawv6_close+0x4c/0x80 net/ipv6/raw.c:1217 inet_release+0xed/0x1c0 net/ipv4/af_inet.c:425 inet6_release+0x50/0x70 net/ipv6/af_inet6.c:432 sock_release+0x8d/0x1e0 net/socket.c:597 __sock_create+0x39d/0x880 net/socket.c:1226 sock_create_kern+0x3f/0x50 net/socket.c:1243 inet_ctl_sock_create+0xbb/0x280 net/ipv4/af_inet.c:1526 icmpv6_sk_init+0x163/0x500 net/ipv6/icmp.c:954 ops_init+0x10a/0x550 net/core/net_namespace.c:115 setup_net+0x261/0x660 net/core/net_namespace.c:291 copy_net_ns+0x27e/0x540 net/core/net_namespace.c:396 9pnet_virtio: no channels available for device ./file1 create_new_namespaces+0x437/0x9b0 kernel/nsproxy.c:106 unshare_nsproxy_namespaces+0xae/0x1e0 kernel/nsproxy.c:205 SYSC_unshare kernel/fork.c:2281 [inline] SyS_unshare+0x64e/0x1000 kernel/fork.c:2231 entry_SYSCALL_64_fastpath+0x1f/0xc2 This is because net->ipv6.mr6_tables is not initialized at that point, ip6mr_rules_init() is not called yet, therefore on the error path when we iterator the list, we trigger this oops. Fix this by reordering ip6mr_rules_init() before icmpv6_sk_init(). Reported-by: Andrey Konovalov Signed-off-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ipv6/af_inet6.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index ad95905e7a70..552fba77327d 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -887,12 +887,12 @@ static int __init inet6_init(void) err = register_pernet_subsys(&inet6_net_ops); if (err) goto register_pernet_fail; - err = icmpv6_init(); - if (err) - goto icmp_fail; err = ip6_mr_init(); if (err) goto ipmr_fail; + err = icmpv6_init(); + if (err) + goto icmp_fail; err = ndisc_init(); if (err) goto ndisc_fail; @@ -1010,10 +1010,10 @@ igmp_fail: ndisc_cleanup(); ndisc_fail: ip6_mr_cleanup(); -ipmr_fail: - icmpv6_cleanup(); icmp_fail: unregister_pernet_subsys(&inet6_net_ops); +ipmr_fail: + icmpv6_cleanup(); register_pernet_fail: sock_unregister(PF_INET6); rtnl_unregister_all(PF_INET6); -- GitLab From 3d9f055c6995a5ba47e835abba574c5b7d261855 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 5 Mar 2017 19:14:07 +0200 Subject: [PATCH 1964/5498] crypto: s5p-sss - Fix completing crypto request in IRQ handler [ Upstream commit 07de4bc88ce6a4d898cad9aa4c99c1df7e87702d ] In a regular interrupt handler driver was finishing the crypt/decrypt request by calling complete on crypto request. This is disallowed since converting to skcipher in commit b286d8b1a690 ("crypto: skcipher - Add skcipher walk interface") and causes a warning: WARNING: CPU: 0 PID: 0 at crypto/skcipher.c:430 skcipher_walk_first+0x13c/0x14c The interrupt is marked shared but in fact there are no other users sharing it. Thus the simplest solution seems to be to just use a threaded interrupt handler, after converting it to oneshot. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/s5p-sss.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c index 658fa533ced1..5f0e3bb9006d 100644 --- a/drivers/crypto/s5p-sss.c +++ b/drivers/crypto/s5p-sss.c @@ -682,8 +682,9 @@ static int s5p_aes_probe(struct platform_device *pdev) dev_warn(dev, "feed control interrupt is not available.\n"); goto err_irq; } - err = devm_request_irq(dev, pdata->irq_fc, s5p_aes_interrupt, - IRQF_SHARED, pdev->name, pdev); + err = devm_request_threaded_irq(dev, pdata->irq_fc, NULL, + s5p_aes_interrupt, IRQF_ONESHOT, + pdev->name, pdev); if (err < 0) { dev_warn(dev, "feed control interrupt is not available.\n"); goto err_irq; -- GitLab From 5f5c998f31abdf9b6b8da2b9e16a81697ad67201 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Mon, 6 Mar 2017 15:20:51 -0500 Subject: [PATCH 1965/5498] i2c: riic: fix restart condition [ Upstream commit 2501c1bb054290679baad0ff7f4f07c714251f4c ] While modifying the driver to use the STOP interrupt, the completion of the intermediate transfers need to wake the driver back up in order to initiate the next transfer (restart condition). Otherwise you get never ending interrupts and only the first transfer sent. Fixes: 71ccea095ea1 ("i2c: riic: correctly finish transfers") Reported-by: Simon Horman Signed-off-by: Chris Brandt Tested-by: Simon Horman Signed-off-by: Wolfram Sang Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-riic.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index 7a7b71e97ba4..43e88e3c1d27 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -218,8 +218,12 @@ static irqreturn_t riic_tend_isr(int irq, void *data) } if (riic->is_last || riic->err) { - riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER); + riic_clear_set_bit(riic, ICIER_TEIE, ICIER_SPIE, RIIC_ICIER); writeb(ICCR2_SP, riic->base + RIIC_ICCR2); + } else { + /* Transfer is complete, but do not send STOP */ + riic_clear_set_bit(riic, ICIER_TEIE, 0, RIIC_ICIER); + complete(&riic->msg_done); } return IRQ_HANDLED; -- GitLab From 8364904c104570a76045b23e78cbb438750a3c7e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 8 Mar 2017 14:56:05 +0100 Subject: [PATCH 1966/5498] axonram: Fix gendisk handling [ Upstream commit 672a2c87c83649fb0167202342ce85af9a3b4f1c ] It is invalid to call del_gendisk() when disk->queue is NULL. Fix error handling in axon_ram_probe() to avoid doing that. Also del_gendisk() does not drop a reference to gendisk allocated by alloc_disk(). That has to be done by put_disk(). Add that call where needed. Reported-by: Dan Carpenter Signed-off-by: Jan Kara Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/sysdev/axonram.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c index e8bb33b2d3cc..2f99da0f3d9d 100644 --- a/arch/powerpc/sysdev/axonram.c +++ b/arch/powerpc/sysdev/axonram.c @@ -283,7 +283,9 @@ failed: if (bank->disk->major > 0) unregister_blkdev(bank->disk->major, bank->disk->disk_name); - del_gendisk(bank->disk); + if (bank->disk->flags & GENHD_FL_UP) + del_gendisk(bank->disk); + put_disk(bank->disk); } device->dev.platform_data = NULL; if (bank->io_addr != 0) @@ -308,6 +310,7 @@ axon_ram_remove(struct platform_device *device) device_remove_file(&device->dev, &dev_attr_ecc); free_irq(bank->irq_id, device); del_gendisk(bank->disk); + put_disk(bank->disk); iounmap((void __iomem *) bank->io_addr); kfree(bank); -- GitLab From e78fac22bac11509db762cf68701defa3b127e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Lefaure?= Date: Wed, 8 Mar 2017 20:18:09 -0500 Subject: [PATCH 1967/5498] EDAC, i5000, i5400: Fix use of MTR_DRAM_WIDTH macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e61555c29c28a4a3b6ba6207f4a0883ee236004d ] The MTR_DRAM_WIDTH macro returns the data width. It is sometimes used as if it returned a boolean true if the width if 8. Fix the tests where MTR_DRAM_WIDTH is misused. Signed-off-by: Jérémy Lefaure Cc: linux-edac Link: http://lkml.kernel.org/r/20170309011809.8340-1-jeremy.lefaure@lse.epita.fr Signed-off-by: Borislav Petkov Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/edac/i5000_edac.c | 2 +- drivers/edac/i5400_edac.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 72e07e3cf718..2a09be5f4f86 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1293,7 +1293,7 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) dimm->mtype = MEM_FB_DDR2; /* ask what device type on this row */ - if (MTR_DRAM_WIDTH(mtr)) + if (MTR_DRAM_WIDTH(mtr) == 8) dimm->dtype = DEV_X8; else dimm->dtype = DEV_X4; diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 6ef6ad1ba16e..029dfe07b734 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1207,13 +1207,14 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) dimm->nr_pages = size_mb << 8; dimm->grain = 8; - dimm->dtype = MTR_DRAM_WIDTH(mtr) ? DEV_X8 : DEV_X4; + dimm->dtype = MTR_DRAM_WIDTH(mtr) == 8 ? + DEV_X8 : DEV_X4; dimm->mtype = MEM_FB_DDR2; /* * The eccc mechanism is SDDC (aka SECC), with * is similar to Chipkill. */ - dimm->edac_mode = MTR_DRAM_WIDTH(mtr) ? + dimm->edac_mode = MTR_DRAM_WIDTH(mtr) == 8 ? EDAC_S8ECD8ED : EDAC_S4ECD4ED; ndimms++; } -- GitLab From 326e406375da4288c8b1384b632034b770817d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Lefaure?= Date: Wed, 28 Jun 2017 20:57:29 -0400 Subject: [PATCH 1968/5498] EDAC, i5000, i5400: Fix definition of NRECMEMB register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a8c8261425649da58bdf08221570e5335ad33a31 ] In the i5000 and i5400 drivers, the NRECMEMB register is defined as a 16-bit value, which results in wrong shifts in the code, as reported by sparse. In the datasheets ([1], section 3.9.22.20 and [2], section 3.9.22.21), this register is a 32-bit register. A u32 value for the register fixes the wrong shifts warnings and matches the datasheet. Also fix the mask to access to the CAS bits [27:16] in the i5000 driver. [1]: https://www.intel.com/content/dam/doc/datasheet/5000p-5000v-5000z-chipset-memory-controller-hub-datasheet.pdf [2]: https://www.intel.se/content/dam/doc/datasheet/5400-chipset-memory-controller-hub-datasheet.pdf Signed-off-by: Jérémy Lefaure Cc: linux-edac Link: http://lkml.kernel.org/r/20170629005729.8478-1-jeremy.lefaure@lse.epita.fr Signed-off-by: Borislav Petkov Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/edac/i5000_edac.c | 6 +++--- drivers/edac/i5400_edac.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 2a09be5f4f86..16e0eb523439 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -227,7 +227,7 @@ #define NREC_RDWR(x) (((x)>>11) & 1) #define NREC_RANK(x) (((x)>>8) & 0x7) #define NRECMEMB 0xC0 -#define NREC_CAS(x) (((x)>>16) & 0xFFFFFF) +#define NREC_CAS(x) (((x)>>16) & 0xFFF) #define NREC_RAS(x) ((x) & 0x7FFF) #define NRECFGLOG 0xC4 #define NREEECFBDA 0xC8 @@ -371,7 +371,7 @@ struct i5000_error_info { /* These registers are input ONLY if there was a * Non-Recoverable Error */ u16 nrecmema; /* Non-Recoverable Mem log A */ - u16 nrecmemb; /* Non-Recoverable Mem log B */ + u32 nrecmemb; /* Non-Recoverable Mem log B */ }; @@ -407,7 +407,7 @@ static void i5000_get_error_info(struct mem_ctl_info *mci, NERR_FAT_FBD, &info->nerr_fat_fbd); pci_read_config_word(pvt->branchmap_werrors, NRECMEMA, &info->nrecmema); - pci_read_config_word(pvt->branchmap_werrors, + pci_read_config_dword(pvt->branchmap_werrors, NRECMEMB, &info->nrecmemb); /* Clear the error bits, by writing them back */ diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 029dfe07b734..2ea2f32e608b 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -368,7 +368,7 @@ struct i5400_error_info { /* These registers are input ONLY if there was a Non-Rec Error */ u16 nrecmema; /* Non-Recoverable Mem log A */ - u16 nrecmemb; /* Non-Recoverable Mem log B */ + u32 nrecmemb; /* Non-Recoverable Mem log B */ }; @@ -458,7 +458,7 @@ static void i5400_get_error_info(struct mem_ctl_info *mci, NERR_FAT_FBD, &info->nerr_fat_fbd); pci_read_config_word(pvt->branchmap_werrors, NRECMEMA, &info->nrecmema); - pci_read_config_word(pvt->branchmap_werrors, + pci_read_config_dword(pvt->branchmap_werrors, NRECMEMB, &info->nrecmemb); /* Clear the error bits, by writing them back */ -- GitLab From cf6924916a76bf20c48583219a8af998b2936101 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 17 Nov 2017 14:27:18 +0800 Subject: [PATCH 1969/5498] route: also update fnhe_genid when updating a route cache [ Upstream commit cebe84c6190d741045a322f5343f717139993c08 ] Now when ip route flush cache and it turn out all fnhe_genid != genid. If a redirect/pmtu icmp packet comes and the old fnhe is found and all it's members but fnhe_genid will be updated. Then next time when it looks up route and tries to rebind this fnhe to the new dst, the fnhe will be flushed due to fnhe_genid != genid. It causes this redirect/pmtu icmp packet acutally not to be applied. This patch is to also reset fnhe_genid when updating a route cache. Fixes: 5aad1de5ea2c ("ipv4: use separate genid for next hop exceptions") Acked-by: Hannes Frederic Sowa Signed-off-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ipv4/route.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2fe9459fbe24..a04d29d70e6a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -622,9 +622,12 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, struct fnhe_hash_bucket *hash; struct fib_nh_exception *fnhe; struct rtable *rt; + u32 genid, hval; unsigned int i; int depth; - u32 hval = fnhe_hashfun(daddr); + + genid = fnhe_genid(dev_net(nh->nh_dev)); + hval = fnhe_hashfun(daddr); spin_lock_bh(&fnhe_lock); @@ -647,6 +650,8 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, } if (fnhe) { + if (fnhe->fnhe_genid != genid) + fnhe->fnhe_genid = genid; if (gw) fnhe->fnhe_gw = gw; if (pmtu) { @@ -671,7 +676,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_next = hash->chain; rcu_assign_pointer(hash->chain, fnhe); } - fnhe->fnhe_genid = fnhe_genid(dev_net(nh->nh_dev)); + fnhe->fnhe_genid = genid; fnhe->fnhe_daddr = daddr; fnhe->fnhe_gw = gw; fnhe->fnhe_pmtu = pmtu; -- GitLab From 9d834e290d02a0e79442c4c60bb9913f559a6da2 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 17 Nov 2017 14:27:06 +0800 Subject: [PATCH 1970/5498] route: update fnhe_expires for redirect when the fnhe exists [ Upstream commit e39d5246111399dbc6e11cd39fd8580191b86c47 ] Now when creating fnhe for redirect, it sets fnhe_expires for this new route cache. But when updating the exist one, it doesn't do it. It will cause this fnhe never to be expired. Paolo already noticed it before, in Jianlin's test case, it became even worse: When ip route flush cache, the old fnhe is not to be removed, but only clean it's members. When redirect comes again, this fnhe will be found and updated, but never be expired due to fnhe_expires not being set. So fix it by simply updating fnhe_expires even it's for redirect. Fixes: aee06da6726d ("ipv4: use seqlock for nh_exceptions") Reported-by: Jianlin Shi Acked-by: Hannes Frederic Sowa Signed-off-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ipv4/route.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index a04d29d70e6a..79c9bacafc61 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -654,10 +654,9 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_genid = genid; if (gw) fnhe->fnhe_gw = gw; - if (pmtu) { + if (pmtu) fnhe->fnhe_pmtu = pmtu; - fnhe->fnhe_expires = max(1UL, expires); - } + fnhe->fnhe_expires = max(1UL, expires); /* Update all cached dsts too */ rt = rcu_dereference(fnhe->fnhe_rth_input); if (rt) -- GitLab From 129287d4bb82262fd98f508d76ecad6b9a9774dc Mon Sep 17 00:00:00 2001 From: Stephen Bates Date: Fri, 17 Nov 2017 15:28:16 -0800 Subject: [PATCH 1971/5498] lib/genalloc.c: make the avail variable an atomic_long_t [ Upstream commit 36a3d1dd4e16bcd0d2ddfb4a2ec7092f0ae0d931 ] If the amount of resources allocated to a gen_pool exceeds 2^32 then the avail atomic overflows and this causes problems when clients try and borrow resources from the pool. This is only expected to be an issue on 64 bit systems. Add the header to pull in atomic_long* operations. So that 32 bit systems continue to use atomic32_t but 64 bit systems can use atomic64_t. Link: http://lkml.kernel.org/r/1509033843-25667-1-git-send-email-sbates@raithlin.com Signed-off-by: Stephen Bates Reviewed-by: Logan Gunthorpe Reviewed-by: Mathieu Desnoyers Reviewed-by: Daniel Mentz Cc: Jonathan Corbet Cc: Andrew Morton Cc: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/genalloc.h | 3 ++- lib/genalloc.c | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h index 1ccaab44abcc..ec78cd93c0c1 100644 --- a/include/linux/genalloc.h +++ b/include/linux/genalloc.h @@ -31,6 +31,7 @@ #define __GENALLOC_H__ #include +#include struct device; struct device_node; @@ -66,7 +67,7 @@ struct gen_pool { */ struct gen_pool_chunk { struct list_head next_chunk; /* next chunk in pool */ - atomic_t avail; + atomic_long_t avail; phys_addr_t phys_addr; /* physical starting address of memory chunk */ unsigned long start_addr; /* start address of memory chunk */ unsigned long end_addr; /* end address of memory chunk (inclusive) */ diff --git a/lib/genalloc.c b/lib/genalloc.c index 2e65d206b01c..c627c3f19935 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -194,7 +194,7 @@ int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phy chunk->phys_addr = phys; chunk->start_addr = virt; chunk->end_addr = virt + size - 1; - atomic_set(&chunk->avail, size); + atomic_long_set(&chunk->avail, size); spin_lock(&pool->lock); list_add_rcu(&chunk->next_chunk, &pool->chunks); @@ -285,7 +285,7 @@ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) nbits = (size + (1UL << order) - 1) >> order; rcu_read_lock(); list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) { - if (size > atomic_read(&chunk->avail)) + if (size > atomic_long_read(&chunk->avail)) continue; end_bit = chunk_size(chunk) >> order; @@ -304,7 +304,7 @@ retry: addr = chunk->start_addr + ((unsigned long)start_bit << order); size = nbits << order; - atomic_sub(size, &chunk->avail); + atomic_long_sub(size, &chunk->avail); break; } rcu_read_unlock(); @@ -370,7 +370,7 @@ void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size) remain = bitmap_clear_ll(chunk->bits, start_bit, nbits); BUG_ON(remain); size = nbits << order; - atomic_add(size, &chunk->avail); + atomic_long_add(size, &chunk->avail); rcu_read_unlock(); return; } @@ -444,7 +444,7 @@ size_t gen_pool_avail(struct gen_pool *pool) rcu_read_lock(); list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) - avail += atomic_read(&chunk->avail); + avail += atomic_long_read(&chunk->avail); rcu_read_unlock(); return avail; } -- GitLab From b9a72f359befb7536667baef659dfac6d06f791f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 17 Nov 2017 15:27:35 -0800 Subject: [PATCH 1972/5498] dynamic-debug-howto: fix optional/omitted ending line number to be LARGE instead of 0 [ Upstream commit 1f3c790bd5989fcfec9e53ad8fa09f5b740c958f ] line-range is supposed to treat "1-" as "1-endoffile", so handle the special case by setting last_lineno to UINT_MAX. Fixes this error: dynamic_debug:ddebug_parse_query: last-line:0 < 1st-line:1 dynamic_debug:ddebug_exec_query: query parse failed Link: http://lkml.kernel.org/r/10a6a101-e2be-209f-1f41-54637824788e@infradead.org Signed-off-by: Randy Dunlap Acked-by: Jason Baron Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index dfba05521748..daec2e93a9ab 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -353,6 +353,10 @@ static int ddebug_parse_query(char *words[], int nwords, if (parse_lineno(last, &query->last_lineno) < 0) return -EINVAL; + /* special case for last lineno not specified */ + if (query->last_lineno == 0) + query->last_lineno = UINT_MAX; + if (query->last_lineno < query->first_lineno) { pr_err("last-line:%d < 1st-line:%d\n", query->last_lineno, -- GitLab From 7e8df53aae065a07007ca35350b5b9119239656a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 6 Nov 2017 15:28:04 -0500 Subject: [PATCH 1973/5498] NFS: Fix a typo in nfs_rename() [ Upstream commit d803224c84be067754db7fa58a93f36f61566493 ] On successful rename, the "old_dentry" is retained and is attached to the "new_dir", so we need to call nfs_set_verifier() accordingly. Signed-off-by: Trond Myklebust Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 90526b2148f0..de98eb5be9db 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2063,7 +2063,7 @@ out: if (new_inode != NULL) nfs_drop_nlink(new_inode); d_move(old_dentry, new_dentry); - nfs_set_verifier(new_dentry, + nfs_set_verifier(old_dentry, nfs_save_change_attribute(new_dir)); } else if (error == -ENOENT) nfs_dentry_handle_enoent(old_dentry); -- GitLab From fa54e27792a8182ef7cb7e313119110a6c191187 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 3 Nov 2017 13:46:06 -0400 Subject: [PATCH 1974/5498] sunrpc: Fix rpc_task_begin trace point [ Upstream commit b2bfe5915d5fe7577221031a39ac722a0a2a1199 ] The rpc_task_begin trace point always display a task ID of zero. Move the trace point call site so that it picks up the new task ID. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/sched.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index fe3441abdbe5..73cb858dad84 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -273,10 +273,9 @@ static inline void rpc_task_set_debuginfo(struct rpc_task *task) static void rpc_set_active(struct rpc_task *task) { - trace_rpc_task_begin(task->tk_client, task, NULL); - rpc_task_set_debuginfo(task); set_bit(RPC_TASK_ACTIVE, &task->tk_runstate); + trace_rpc_task_begin(task->tk_client, task, NULL); } /* -- GitLab From fdea3b85d3a04bd3af04a2db9be58f509c8e596b Mon Sep 17 00:00:00 2001 From: Pavel Tatashin Date: Wed, 15 Nov 2017 17:36:18 -0800 Subject: [PATCH 1975/5498] sparc64/mm: set fields in deferred pages [ Upstream commit 2a20aa171071a334d80c4e5d5af719d8374702fc ] Without deferred struct page feature (CONFIG_DEFERRED_STRUCT_PAGE_INIT), flags and other fields in "struct page"es are never changed prior to first initializing struct pages by going through __init_single_page(). With deferred struct page feature enabled there is a case where we set some fields prior to initializing: mem_init() { register_page_bootmem_info(); free_all_bootmem(); ... } When register_page_bootmem_info() is called only non-deferred struct pages are initialized. But, this function goes through some reserved pages which might be part of the deferred, and thus are not yet initialized. mem_init register_page_bootmem_info register_page_bootmem_info_node get_page_bootmem .. setting fields here .. such as: page->freelist = (void *)type; free_all_bootmem() free_low_memory_core_early() for_each_reserved_mem_region() reserve_bootmem_region() init_reserved_page() <- Only if this is deferred reserved page __init_single_pfn() __init_single_page() memset(0) <-- Loose the set fields here We end up with similar issue as in the previous patch, where currently we do not observe problem as memory is zeroed. But, if flag asserts are changed we can start hitting issues. Also, because in this patch series we will stop zeroing struct page memory during allocation, we must make sure that struct pages are properly initialized prior to using them. The deferred-reserved pages are initialized in free_all_bootmem(). Therefore, the fix is to switch the above calls. Link: http://lkml.kernel.org/r/20171013173214.27300-4-pasha.tatashin@oracle.com Signed-off-by: Pavel Tatashin Reviewed-by: Steven Sistare Reviewed-by: Daniel Jordan Reviewed-by: Bob Picco Acked-by: David S. Miller Acked-by: Michal Hocko Cc: Alexander Potapenko Cc: Andrey Ryabinin Cc: Ard Biesheuvel Cc: Catalin Marinas Cc: Christian Borntraeger Cc: Dmitry Vyukov Cc: Heiko Carstens Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Mark Rutland Cc: Matthew Wilcox Cc: Mel Gorman Cc: Michal Hocko Cc: Sam Ravnborg Cc: Thomas Gleixner Cc: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/sparc/mm/init_64.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 7ad1baa6c914..1f4492f6efb0 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -2215,9 +2215,16 @@ void __init mem_init(void) { high_memory = __va(last_valid_pfn << PAGE_SHIFT); - register_page_bootmem_info(); free_all_bootmem(); + /* + * Must be done after boot memory is put on freelist, because here we + * might set fields in deferred struct pages that have not yet been + * initialized, and free_all_bootmem() initializes all the reserved + * deferred pages for us. + */ + register_page_bootmem_info(); + /* * Set up the zero page, mark it reserved, so that page count * is not manipulated when freeing the page from user ptes. -- GitLab From e899383e43fe326e6398142afd2e3b7b9054d1e0 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 15 Nov 2017 16:55:54 +0800 Subject: [PATCH 1976/5498] sctp: do not free asoc when it is already dead in sctp_sendmsg [ Upstream commit ca3af4dd28cff4e7216e213ba3b671fbf9f84758 ] Now in sctp_sendmsg sctp_wait_for_sndbuf could schedule out without holding sock sk. It means the current asoc can be freed elsewhere, like when receiving an abort packet. If the asoc is just created in sctp_sendmsg and sctp_wait_for_sndbuf returns err, the asoc will be freed again due to new_asoc is not nil. An use-after-free issue would be triggered by this. This patch is to fix it by setting new_asoc with nil if the asoc is already dead when cpu schedules back, so that it will not be freed again in sctp_sendmsg. v1->v2: set new_asoc as nil in sctp_sendmsg instead of sctp_wait_for_sndbuf. Suggested-by: Neil Horman Reported-by: Dmitry Vyukov Signed-off-by: Xin Long Acked-by: Neil Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/sctp/socket.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 3f89cd063246..732647ca346e 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1950,8 +1950,14 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); if (!sctp_wspace(asoc)) { err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); - if (err) + if (err) { + if (err == -ESRCH) { + /* asoc is already dead. */ + new_asoc = NULL; + err = -EPIPE; + } goto out_free; + } } /* If an address is passed with the sendto/sendmsg call, it is used @@ -6999,10 +7005,11 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, for (;;) { prepare_to_wait_exclusive(&asoc->wait, &wait, TASK_INTERRUPTIBLE); + if (asoc->base.dead) + goto do_dead; if (!*timeo_p) goto do_nonblock; - if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING || - asoc->base.dead) + if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING) goto do_error; if (signal_pending(current)) goto do_interrupted; @@ -7027,6 +7034,10 @@ out: return err; +do_dead: + err = -ESRCH; + goto out; + do_error: err = -EPIPE; goto out; -- GitLab From 5faa4be6f9b46f2764f8cdf4900eff832fba8d52 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 15 Nov 2017 16:57:26 +0800 Subject: [PATCH 1977/5498] sctp: use the right sk after waking up from wait_buf sleep [ Upstream commit cea0cc80a6777beb6eb643d4ad53690e1ad1d4ff ] Commit dfcb9f4f99f1 ("sctp: deny peeloff operation on asocs with threads sleeping on it") fixed the race between peeloff and wait sndbuf by checking waitqueue_active(&asoc->wait) in sctp_do_peeloff(). But it actually doesn't work, as even if waitqueue_active returns false the waiting sndbuf thread may still not yet hold sk lock. After asoc is peeled off, sk is not asoc->base.sk any more, then to hold the old sk lock couldn't make assoc safe to access. This patch is to fix this by changing to hold the new sk lock if sk is not asoc->base.sk, meanwhile, also set the sk in sctp_sendmsg with the new sk. With this fix, there is no more race between peeloff and waitbuf, the check 'waitqueue_active' in sctp_do_peeloff can be removed. Thanks Marcelo and Neil for making this clear. v1->v2: fix it by changing to lock the new sock instead of adding a flag in asoc. Suggested-by: Neil Horman Signed-off-by: Xin Long Acked-by: Neil Horman Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/sctp/socket.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 732647ca346e..8e019692bb42 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -82,8 +82,8 @@ /* Forward declarations for internal helper functions. */ static int sctp_writeable(struct sock *sk); static void sctp_wfree(struct sk_buff *skb); -static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p, - size_t msg_len); +static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, + size_t msg_len, struct sock **orig_sk); static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p); static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); static int sctp_wait_for_accept(struct sock *sk, long timeo); @@ -1949,7 +1949,8 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); if (!sctp_wspace(asoc)) { - err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); + /* sk can be changed by peel off when waiting for buf. */ + err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len, &sk); if (err) { if (err == -ESRCH) { /* asoc is already dead. */ @@ -4479,12 +4480,6 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) if (!asoc) return -EINVAL; - /* If there is a thread waiting on more sndbuf space for - * sending on this asoc, it cannot be peeled. - */ - if (waitqueue_active(&asoc->wait)) - return -EBUSY; - /* An association cannot be branched off from an already peeled-off * socket, nor is this supported for tcp style sockets. */ @@ -6988,7 +6983,7 @@ void sctp_sock_rfree(struct sk_buff *skb) /* Helper function to wait for space in the sndbuf. */ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, - size_t msg_len) + size_t msg_len, struct sock **orig_sk) { struct sock *sk = asoc->base.sk; int err = 0; @@ -7022,11 +7017,17 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, release_sock(sk); current_timeo = schedule_timeout(current_timeo); lock_sock(sk); + if (sk != asoc->base.sk) { + release_sock(sk); + sk = asoc->base.sk; + lock_sock(sk); + } *timeo_p = current_timeo; } out: + *orig_sk = sk; finish_wait(&asoc->wait, &wait); /* Release the association's refcnt. */ -- GitLab From d22443f3dfee2c427d53a6ba14f7a270c2bb0290 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 14 Nov 2017 13:42:38 +0530 Subject: [PATCH 1978/5498] atm: horizon: Fix irq release error [ Upstream commit bde533f2ea607cbbbe76ef8738b36243939a7bc2 ] atm_dev_register() can fail here and passed parameters to free irq which is not initialised. Initialization of 'dev->irq' happened after the 'goto out_free_irq'. So using 'irq' insted of 'dev->irq' in free_irq(). Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/atm/horizon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c index 1dc0519333f2..27a9fa2718f7 100644 --- a/drivers/atm/horizon.c +++ b/drivers/atm/horizon.c @@ -2828,7 +2828,7 @@ out: return err; out_free_irq: - free_irq(dev->irq, dev); + free_irq(irq, dev); out_free: kfree(dev); out_release: -- GitLab From 63ec9868ebe393c5b04388bc2046fc96e4d94cac Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 10 Nov 2017 14:14:06 +1100 Subject: [PATCH 1979/5498] xfrm: Copy policy family in clone_policy [ Upstream commit 0e74aa1d79a5bbc663e03a2804399cae418a0321 ] The syzbot found an ancient bug in the IPsec code. When we cloned a socket policy (for example, for a child TCP socket derived from a listening socket), we did not copy the family field. This results in a live policy with a zero family field. This triggers a BUG_ON check in the af_key code when the cloned policy is retrieved. This patch fixes it by copying the family field over. Reported-by: syzbot Signed-off-by: Herbert Xu Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_policy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 253e7dda287b..fb84ff4aeee7 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1345,6 +1345,7 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir) newp->xfrm_nr = old->xfrm_nr; newp->index = old->index; newp->type = old->type; + newp->family = old->family; memcpy(newp->xfrm_vec, old->xfrm_vec, newp->xfrm_nr*sizeof(struct xfrm_tmpl)); write_lock_bh(&net->xfrm.xfrm_policy_lock); -- GitLab From 2db29fb2d2492068e26b97ce17cf059965bbdb56 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Thu, 2 Nov 2017 15:22:26 +0200 Subject: [PATCH 1980/5498] IB/mlx4: Increase maximal message size under UD QP [ Upstream commit 5f22a1d87c5315a98981ecf93cd8de226cffe6ca ] Maximal message should be used as a limit to the max message payload allowed, without the headers. The ConnectX-3 check is done against this value includes the headers. When the payload is 4K this will cause the NIC to drop packets. Increase maximal message to 8K as workaround, this shouldn't change current behaviour because we continue to set the MTU to 4k. To reproduce; set MTU to 4296 on the corresponding interface, for example: ifconfig eth0 mtu 4296 (both server and client) On server: ib_send_bw -c UD -d mlx4_0 -s 4096 -n 1000000 -i1 -m 4096 On client: ib_send_bw -d mlx4_0 -c UD -s 4096 -n 1000000 -i 1 -m 4096 Fixes: 6e0d733d9215 ("IB/mlx4: Allow 4K messages for UD QPs") Signed-off-by: Mark Bloch Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/mlx4/qp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 33cbb7611d66..890bbf984748 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1465,7 +1465,7 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, context->mtu_msgmax = (IB_MTU_4096 << 5) | ilog2(dev->dev->caps.max_gso_sz); else - context->mtu_msgmax = (IB_MTU_4096 << 5) | 12; + context->mtu_msgmax = (IB_MTU_4096 << 5) | 13; } else if (attr_mask & IB_QP_PATH_MTU) { if (attr->path_mtu < IB_MTU_256 || attr->path_mtu > IB_MTU_4096) { pr_err("path MTU (%u) is invalid\n", -- GitLab From 4adf3c3c63237599011675f352f6d89c363e375e Mon Sep 17 00:00:00 2001 From: Majd Dibbiny Date: Mon, 30 Oct 2017 14:23:13 +0200 Subject: [PATCH 1981/5498] IB/mlx5: Assign send CQ and recv CQ of UMR QP [ Upstream commit 31fde034a8bd964a5c7c1a5663fc87a913158db2 ] The UMR's QP is created by calling mlx5_ib_create_qp directly, and therefore the send CQ and the recv CQ on the ibqp weren't assigned. Assign them right after calling the mlx5_ib_create_qp to assure that any access to those pointers will work as expected and won't crash the system as might happen as part of reset flow. Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") Signed-off-by: Majd Dibbiny Reviewed-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/mlx5/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 871823cd9825..18645dcf5882 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1099,6 +1099,8 @@ static int create_umr_res(struct mlx5_ib_dev *dev) qp->real_qp = qp; qp->uobject = NULL; qp->qp_type = MLX5_IB_QPT_REG_UMR; + qp->send_cq = init_attr->send_cq; + qp->recv_cq = init_attr->recv_cq; attr->qp_state = IB_QPS_INIT; attr->port_num = 1; -- GitLab From bf24f2225c69884d99be4909208fb6053c406971 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 2 Nov 2017 15:27:48 +0000 Subject: [PATCH 1982/5498] afs: Connect up the CB.ProbeUuid [ Upstream commit f4b3526d83c40dd8bf5948b9d7a1b2c340f0dcc8 ] The handler for the CB.ProbeUuid operation in the cache manager is implemented, but isn't listed in the switch-statement of operation selection, so won't be used. Fix this by adding it. Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/cmservice.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 4b0eff6da674..83a8a33a0d73 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -115,6 +115,9 @@ bool afs_cm_incoming_call(struct afs_call *call) case CBProbe: call->type = &afs_SRXCBProbe; return true; + case CBProbeUuid: + call->type = &afs_SRXCBProbeUuid; + return true; case CBTellMeAboutYourself: call->type = &afs_SRXCBTellMeAboutYourself; return true; -- GitLab From db23d45956bc30964a2e2c8f0d93753ca3106934 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 1 Sep 2017 09:44:34 -0400 Subject: [PATCH 1983/5498] audit: ensure that 'audit=1' actually enables audit for PID 1 [ Upstream commit 173743dd99a49c956b124a74c8aacb0384739a4c ] Prior to this patch we enabled audit in audit_init(), which is too late for PID 1 as the standard initcalls are run after the PID 1 task is forked. This means that we never allocate an audit_context (see audit_alloc()) for PID 1 and therefore miss a lot of audit events generated by PID 1. This patch enables audit as early as possible to help ensure that when PID 1 is forked it can allocate an audit_context if required. Reviewed-by: Richard Guy Briggs Signed-off-by: Paul Moore Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/audit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index c6df9905f1c6..e02218c18a81 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -79,13 +79,13 @@ static int audit_initialized; #define AUDIT_OFF 0 #define AUDIT_ON 1 #define AUDIT_LOCKED 2 -u32 audit_enabled; -u32 audit_ever_enabled; +u32 audit_enabled = AUDIT_OFF; +u32 audit_ever_enabled = !!AUDIT_OFF; EXPORT_SYMBOL_GPL(audit_enabled); /* Default state when kernel boots without any parameters. */ -static u32 audit_default; +static u32 audit_default = AUDIT_OFF; /* If auditing cannot proceed, audit_failure selects what happens. */ static u32 audit_failure = AUDIT_FAIL_PRINTK; @@ -1173,8 +1173,6 @@ static int __init audit_init(void) skb_queue_head_init(&audit_skb_queue); skb_queue_head_init(&audit_skb_hold_queue); audit_initialized = AUDIT_INITIALIZED; - audit_enabled = audit_default; - audit_ever_enabled |= !!audit_default; audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); @@ -1191,6 +1189,8 @@ static int __init audit_enable(char *str) audit_default = !!simple_strtol(str, NULL, 0); if (!audit_default) audit_initialized = AUDIT_DISABLED; + audit_enabled = audit_default; + audit_ever_enabled = !!audit_enabled; pr_info("%s\n", audit_default ? "enabled (after initialization)" : "disabled (until reboot)"); -- GitLab From 8492c95e430d40e67057f7e626aba6a50b2ccdf6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 16 Nov 2017 17:58:21 +0000 Subject: [PATCH 1984/5498] arm: KVM: Fix VTTBR_BADDR_MASK BUG_ON off-by-one commit 5553b142be11e794ebc0805950b2e8313f93d718 upstream. VTTBR_BADDR_MASK is used to sanity check the size and alignment of the VTTBR address. It seems to currently be off by one, thereby only allowing up to 39-bit addresses (instead of 40-bit) and also insufficiently checking the alignment. This patch fixes it. This patch is the 32bit pendent of Kristina's arm64 fix, and she deserves the actual kudos for pinpointing that one. Fixes: f7ed45be3ba52 ("KVM: ARM: World-switch implementation") Reported-by: Kristina Martsenko Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Greg Kroah-Hartman --- arch/arm/include/asm/kvm_arm.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 151464c6bfdb..4b9a3d09181f 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h @@ -161,8 +161,7 @@ #else #define VTTBR_X (5 - KVM_T0SZ) #endif -#define VTTBR_BADDR_SHIFT (VTTBR_X - 1) -#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT) +#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_X) #define VTTBR_VMID_SHIFT (48LLU) #define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT) -- GitLab From 3130d5160d78c9ce8fa3857b53a69aca13a55ec6 Mon Sep 17 00:00:00 2001 From: Vincent Pelletier Date: Sun, 26 Nov 2017 06:52:53 +0000 Subject: [PATCH 1985/5498] usb: gadget: ffs: Forbid usb_ep_alloc_request from sleeping commit 30bf90ccdec1da9c8198b161ecbff39ce4e5a9ba upstream. Found using DEBUG_ATOMIC_SLEEP while submitting an AIO read operation: [ 100.853642] BUG: sleeping function called from invalid context at mm/slab.h:421 [ 100.861148] in_atomic(): 1, irqs_disabled(): 1, pid: 1880, name: python [ 100.867954] 2 locks held by python/1880: [ 100.867961] #0: (&epfile->mutex){....}, at: [] ffs_mutex_lock+0x27/0x30 [usb_f_fs] [ 100.868020] #1: (&(&ffs->eps_lock)->rlock){....}, at: [] ffs_epfile_io.isra.17+0x24b/0x590 [usb_f_fs] [ 100.868076] CPU: 1 PID: 1880 Comm: python Not tainted 4.14.0-edison+ #118 [ 100.868085] Hardware name: Intel Corporation Merrifield/BODEGA BAY, BIOS 542 2015.01.21:18.19.48 [ 100.868093] Call Trace: [ 100.868122] dump_stack+0x47/0x62 [ 100.868156] ___might_sleep+0xfd/0x110 [ 100.868182] __might_sleep+0x68/0x70 [ 100.868217] kmem_cache_alloc_trace+0x4b/0x200 [ 100.868248] ? dwc3_gadget_ep_alloc_request+0x24/0xe0 [dwc3] [ 100.868302] dwc3_gadget_ep_alloc_request+0x24/0xe0 [dwc3] [ 100.868343] usb_ep_alloc_request+0x16/0xc0 [udc_core] [ 100.868386] ffs_epfile_io.isra.17+0x444/0x590 [usb_f_fs] [ 100.868424] ? _raw_spin_unlock_irqrestore+0x27/0x40 [ 100.868457] ? kiocb_set_cancel_fn+0x57/0x60 [ 100.868477] ? ffs_ep0_poll+0xc0/0xc0 [usb_f_fs] [ 100.868512] ffs_epfile_read_iter+0xfe/0x157 [usb_f_fs] [ 100.868551] ? security_file_permission+0x9c/0xd0 [ 100.868587] ? rw_verify_area+0xac/0x120 [ 100.868633] aio_read+0x9d/0x100 [ 100.868692] ? __fget+0xa2/0xd0 [ 100.868727] ? __might_sleep+0x68/0x70 [ 100.868763] SyS_io_submit+0x471/0x680 [ 100.868878] do_int80_syscall_32+0x4e/0xd0 [ 100.868921] entry_INT80_32+0x2a/0x2a [ 100.868932] EIP: 0xb7fbb676 [ 100.868941] EFLAGS: 00000292 CPU: 1 [ 100.868951] EAX: ffffffda EBX: b7aa2000 ECX: 00000002 EDX: b7af8368 [ 100.868961] ESI: b7fbb660 EDI: b7aab000 EBP: bfb6c658 ESP: bfb6c638 [ 100.868973] DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b Signed-off-by: Vincent Pelletier Signed-off-by: Felipe Balbi Signed-off-by: Siqi Lin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a5d34216e962..2c46089ac423 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -816,7 +816,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) } if (io_data->aio) { - req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); + req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC); if (unlikely(!req)) goto error_lock; -- GitLab From e6318e33ba9471a6eb8c975c92a0015b9271ce7a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 16 Dec 2017 10:32:31 +0100 Subject: [PATCH 1986/5498] Linux 3.18.88 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bc657f28341b..5077b054e410 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 87 +SUBLEVEL = 88 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 46c5fa7fbbd35ad5d8458ef4e5a4e9423900d088 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Mon, 18 Dec 2017 11:06:19 +0530 Subject: [PATCH 1987/5498] defconfig: Enable F2FS for 8917 Enable F2FS related config options for MSM8917. Change-Id: Id6b71e93b13face254a499aa3174823364d78080 Signed-off-by: Sahitya Tummala --- arch/arm/configs/msm8937-perf_defconfig | 2 ++ arch/arm/configs/msm8937_defconfig | 2 ++ arch/arm64/configs/msm8937-perf_defconfig | 2 ++ arch/arm64/configs/msm8937_defconfig | 2 ++ 4 files changed, 8 insertions(+) diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig index 8efa7aeeb2d8..d5b2f99a7351 100644 --- a/arch/arm/configs/msm8937-perf_defconfig +++ b/arch/arm/configs/msm8937-perf_defconfig @@ -605,6 +605,8 @@ CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_SDCARD_FS=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig index fcbacbffb912..e47d73846b8e 100644 --- a/arch/arm/configs/msm8937_defconfig +++ b/arch/arm/configs/msm8937_defconfig @@ -621,6 +621,8 @@ CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_SDCARD_FS=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig index e1ca823804fc..e4cb9404e644 100644 --- a/arch/arm64/configs/msm8937-perf_defconfig +++ b/arch/arm64/configs/msm8937-perf_defconfig @@ -618,6 +618,8 @@ CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_SDCARD_FS=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig index 99fe22088bd8..2c5f551c9d0e 100644 --- a/arch/arm64/configs/msm8937_defconfig +++ b/arch/arm64/configs/msm8937_defconfig @@ -633,6 +633,8 @@ CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_SDCARD_FS=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y -- GitLab From 3bb4b820228d75d955753462ecd706036825b291 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 16 Oct 2017 14:23:55 +0530 Subject: [PATCH 1988/5498] ARM: dts: msm: Defer regulator disable for msm8953 GPU SMMU IOMMU driver takes vote on regulator for every unmap, attach or detach call and removes it once done. This is an overhead if GPU is in power collapse mode and huge number of unmap calls are pending from a given context. It would end up in turning ON/OFF the regulator those many times. To optimize this, do a deferred regulator disable about 80ms so that all subsequent requests to regulator enable will be just refcounted Change-Id: I69d5daf962cd1926b1e7d2717947afdf2280542b Signed-off-by: Sunil Khatri --- arch/arm/boot/dts/qcom/msm-arm-smmu-8953.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/msm-arm-smmu-8953.dtsi b/arch/arm/boot/dts/qcom/msm-arm-smmu-8953.dtsi index a2be26ab3c3a..b21f6a116d90 100644 --- a/arch/arm/boot/dts/qcom/msm-arm-smmu-8953.dtsi +++ b/arch/arm/boot/dts/qcom/msm-arm-smmu-8953.dtsi @@ -23,6 +23,7 @@ #global-interrupts = <1>; interrupts = <0 199 0>, <0 225 0>, <0 232 0>, <0 233 0>, <0 234 0>; + qcom,deferred-regulator-disable-delay = <80>; qcom,register-save; qcom,skip-init; qcom,dynamic; -- GitLab From 9d0562d74325dfaba0d130fac08e9fa75c2dcf06 Mon Sep 17 00:00:00 2001 From: Pallavi Date: Fri, 8 Dec 2017 15:16:31 +0530 Subject: [PATCH 1989/5498] ASoC: msm: Add QCHAT as Front-end DAI link Add QCHAT as Front-end DAI link to mdm9607 DAI link list. This DAI link supports QCHAT on mdm9607. CRs-Fixed: 2153783 Change-Id: Id3861664f371482454038491f42f52097238fede Signed-off-by: Pallavi --- sound/soc/msm/mdm9607.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/msm/mdm9607.c b/sound/soc/msm/mdm9607.c index 10c411bf35d5..80131af60d85 100644 --- a/sound/soc/msm/mdm9607.c +++ b/sound/soc/msm/mdm9607.c @@ -1927,6 +1927,24 @@ static struct snd_soc_dai_link mdm_dai[] = { /* this dainlink has playback support */ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, }, + { + .name = "QCHAT", + .stream_name = "QCHAT", + .cpu_dai_name = "QCHAT", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_QCHAT, + }, /* Backend DAI Links */ { .name = LPASS_BE_AFE_PCM_RX, -- GitLab From 8bf1bd2e30913ee6ced27bd0a45d1e034be3f5f9 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Fri, 15 Dec 2017 14:38:07 +0530 Subject: [PATCH 1990/5498] ARM: dts: msm: Defer regulator disable for msm8937 GPU SMMU IOMMU driver takes vote on regulator for every unmap, attach or detach call and removes it once done. This is an overhead if GPU is in power collapse mode and huge number of unmap calls are pending from a given context. It would end up in turning ON/OFF the regulator those many times. To optimize this, do a deferred regulator disable about 80ms so that all subsequent requests to regulator enable will be just refcounted. Change-Id: I3618b5fc5724cbb49ea6ada991e0d90093ced20f Signed-off-by: Sunil Khatri --- arch/arm/boot/dts/qcom/msm-arm-smmu-8937.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm-arm-smmu-8937.dtsi b/arch/arm/boot/dts/qcom/msm-arm-smmu-8937.dtsi index cd370936a015..9bb706e794c3 100644 --- a/arch/arm/boot/dts/qcom/msm-arm-smmu-8937.dtsi +++ b/arch/arm/boot/dts/qcom/msm-arm-smmu-8937.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,6 +23,7 @@ #global-interrupts = <1>; interrupts = <0 199 0>, <0 225 0>, <0 232 0>, <0 233 0>, <0 234 0>; + qcom,deferred-regulator-disable-delay = <80>; qcom,register-save; qcom,skip-init; qcom,dynamic; -- GitLab From accd0aedd7244fd7eeb1b907892ad6e33e488043 Mon Sep 17 00:00:00 2001 From: Venkata Prahlad Valluru Date: Wed, 6 Dec 2017 13:27:30 +0530 Subject: [PATCH 1991/5498] input: touchscreen: enable/disable wakeup gestures for synaptics v2.6 Add devicetree property for wakeup gesture enable. Override this choice through sysfs. Change-Id: I61e386e89dda7ac12291a9b0138f649f71678ff8 Signed-off-by: Venkata Prahlad Valluru --- .../bindings/input/touchscreen/synaptics_dsxv26_i2c.txt | 1 + .../touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c | 9 +++++---- .../touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h | 1 + .../touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c | 3 +++ include/linux/input/synaptics_dsx_v2_6.h | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt index f693d72442ff..505474c07ed4 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt @@ -21,6 +21,7 @@ Optional property: - synaptics,irq-on-state : status of irq gpio. - synaptics,cap-button-codes : virtual key code mappings to be used. - synaptics,vir-button-codes : virtual key code and the response region on panel. + - synaptics,wakeup-gestures-en: enable wakeup gestures. - synaptics,x-flip : modify orientation of the x axis. - synaptics,y-flip : modify orientation of the y axis. - synaptics,reset-delay-ms : reset delay for controller (ms), default 100. diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c index c12cd4cdf280..05878ea7c4cb 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c @@ -55,8 +55,6 @@ #define TYPE_B_PROTOCOL #endif -#define WAKEUP_GESTURE true - #define NO_0D_WHILE_2D #define REPORT_2D_Z #define REPORT_2D_W @@ -1013,8 +1011,10 @@ static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev, input = input > 0 ? 1 : 0; - if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) + if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) { rmi4_data->enable_wakeup_gesture = input; + rmi4_data->wakeup_gesture_en = input; + } return count; } @@ -3111,7 +3111,7 @@ flash_prog_mode: } if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) - rmi4_data->enable_wakeup_gesture = WAKEUP_GESTURE; + rmi4_data->enable_wakeup_gesture = rmi4_data->wakeup_gesture_en; else rmi4_data->enable_wakeup_gesture = false; @@ -3951,6 +3951,7 @@ static int synaptics_rmi4_probe(struct platform_device *pdev) rmi4_data->suspend = false; rmi4_data->irq_enabled = false; rmi4_data->fingers_on_2d = false; + rmi4_data->wakeup_gesture_en = bdata->wakeup_gesture_en; rmi4_data->reset_device = synaptics_rmi4_reset_device; rmi4_data->irq_enable = synaptics_rmi4_irq_enable; diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h index 7d92791afb25..7a5b3b5abb47 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h @@ -371,6 +371,7 @@ struct synaptics_rmi4_data { bool fb_ready; bool f11_wakeup_gesture; bool f12_wakeup_gesture; + bool wakeup_gesture_en; bool enable_wakeup_gesture; bool wedge_sensor; bool report_pressure; diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c index 563ce16885b3..b5af6be53693 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c @@ -81,6 +81,9 @@ static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) bdata->resume_in_workqueue = of_property_read_bool(np, "synaptics,resume-in-workqueue"); + bdata->wakeup_gesture_en = of_property_read_bool(np, + "synaptics,wakeup-gestures-en"); + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); if (retval < 0) bdata->pwr_reg_name = NULL; diff --git a/include/linux/input/synaptics_dsx_v2_6.h b/include/linux/input/synaptics_dsx_v2_6.h index 5d4bbedb5f1a..4fadb11e31e3 100644 --- a/include/linux/input/synaptics_dsx_v2_6.h +++ b/include/linux/input/synaptics_dsx_v2_6.h @@ -88,6 +88,7 @@ struct synaptics_dsx_board_data { bool y_flip; bool swap_axes; bool resume_in_workqueue; + bool wakeup_gesture_en; int irq_gpio; int irq_on_state; int power_gpio; -- GitLab From fc13240d8c9d1eaddda7a86c269ef47f91a28738 Mon Sep 17 00:00:00 2001 From: Venkata Prahlad Valluru Date: Wed, 6 Dec 2017 13:42:57 +0530 Subject: [PATCH 1992/5498] ARM: dts: msm: enable wakeup-gesture for 8909W BG v2 devices Add dt property to enable wakeup gesture for 8909W BG v2 devices. Change-Id: Ie639dd527283c6ac864488836c0436a97d839f65 Signed-off-by: Venkata Prahlad Valluru --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 1 + arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 4cee9ddec353..9df8fe727833 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -51,6 +51,7 @@ synaptics,power-delay-ms = <200>; synaptics,reset-delay-ms = <200>; synaptics,max-y-for-2d = <389>; + synaptics,wakeup-gestures-en; synaptics,resume-in-workqueue; synaptics,x-flip; synaptics,y-flip; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 53fdc56877b4..8c09768189d2 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -53,6 +53,7 @@ synaptics,power-delay-ms = <200>; synaptics,reset-delay-ms = <200>; synaptics,max-y-for-2d = <389>; + synaptics,wakeup-gestures-en; synaptics,resume-in-workqueue; synaptics,x-flip; synaptics,y-flip; -- GitLab From e28e17a468c3894197078c502e7a6005c70dee12 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 15 Dec 2017 13:14:08 +0800 Subject: [PATCH 1993/5498] ARM: dts: msm: Add battery data for Palladium 1500mAh battery Add the FG GEN3 battery profile for Palladium 1500mAh battery so that the devices with FG module uses this battery could load the profile and report battery SoC correctly. Change-Id: Iac7e24ce41338bee47e0e839bd4d8f81918cb07a Signed-off-by: Fenglin Wu --- ...fg-gen3-batterydata-palladium-1500mah.dtsi | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/fg-gen3-batterydata-palladium-1500mah.dtsi diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-palladium-1500mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-palladium-1500mah.dtsi new file mode 100644 index 000000000000..170f0397a5fb --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-palladium-1500mah.dtsi @@ -0,0 +1,81 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +qcom,palladium_1500mah_averaged_masterslave_nov28th2017 { + /* #Palladium_1500mAh_averaged_MasterSlave_Nov28th2017*/ + qcom,max-voltage-uv = <4200000>; + qcom,fg-cc-cv-threshold-mv = <4180>; + qcom,fastchg-current-ma = <1500>; + qcom,batt-id-kohm = <75>; + qcom,battery-beta = <3435>; + qcom,battery-type = "palladium_1500mah_averaged_masterslave_nov28th2017"; + qcom,checksum = <0x1C13>; + qcom,gui-version = "PM660GUI - 0.0.0.45"; + qcom,fg-profile-data = [ + A6 1F B2 05 + 1F 0A F7 FC + 22 1D 32 F2 + B9 02 1C 0D + 25 11 FB 2B + 88 4C 0A 62 + 65 00 00 00 + 0E 00 00 00 + 00 00 3D C4 + 5D C5 A4 C2 + 2A 00 08 00 + 0F C5 76 D4 + 71 FC 36 F3 + 7D 06 C4 03 + EB DD F0 22 + 1E 06 09 20 + 27 00 14 00 + 47 1F 5F FC + 9B 03 BE 06 + EE 1C 17 02 + F6 0D 27 03 + 22 11 0B 32 + 6D 4C 19 62 + 7D 00 00 00 + 0E 00 00 00 + 00 00 6E CC + 03 CA FB BC + 26 00 00 00 + E9 DB 76 D4 + 41 FD 89 EB + 3E 06 A9 F3 + C3 05 F3 13 + 9C 33 CC FF + 07 10 00 00 + BD 05 33 43 + 26 00 40 00 + 35 02 0A FA + FF 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; -- GitLab From cc5ea53a7cd4d66b35db84998c34f0dc607c4042 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 15 Dec 2017 13:40:54 +0800 Subject: [PATCH 1994/5498] ARM: dts: msm: Change battery profile for msm8909w-pm660 MTP device Palladium 1500mAh battery is the only battery module used on msm8909w-pm660 MTP device, change the battery data including files so that FG in PM660 can load the right battery profile and calculate the correct battery SoC. Change-Id: I588c59448553ec1d5f74648e0d9761d9bc7dbc19 Signed-off-by: Fenglin Wu --- arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index 46c553c577f7..e6535b112e6e 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -241,10 +241,10 @@ }; / { + /delete-node/ qcom,battery-data; mtp_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; - #include "fg-gen3-batterydata-itech-3000mah.dtsi" - #include "fg-gen3-batterydata-ascent-3450mah.dtsi" + #include "fg-gen3-batterydata-palladium-1500mah.dtsi" }; }; -- GitLab From 8b2e8c32d9c4de48380358478d28c1c5630a5a93 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 18 Dec 2017 11:31:37 +0530 Subject: [PATCH 1995/5498] soc: qcom: bgrsb: Add suspend/resume for bgrsb driver Add suspend/resume for rsb driver. LDO-11 disabled/enabled on suspend/resume. Change-Id: Id97591eec1a6d74a5d5a61ebdfacf700bc85b4a9 Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 51 +++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index 4105f372ca2f..df666100f9b4 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -162,7 +162,7 @@ static void bgrsb_glink_notify_state(void *handle, const void *priv, } } -static int bgrsb_configr_rsb(struct bgrsb_priv *dev) +static int bgrsb_configr_rsb(struct bgrsb_priv *dev, bool enable) { int rc = 0; struct bgrsb_msg req = {0}; @@ -181,7 +181,8 @@ static int bgrsb_configr_rsb(struct bgrsb_priv *dev) } req.cmd_id = 0x01; - req.data = 0x01; + req.data = enable ? 0x01 : 0x00; + rc = glink_tx(dev->handle, (void *)dev, &req, BGRSB_MSG_SIZE, GLINK_TX_REQ_INTENT); @@ -191,7 +192,7 @@ static int bgrsb_configr_rsb(struct bgrsb_priv *dev) } rc = wait_for_completion_timeout(&dev->tx_done, - msecs_to_jiffies(TIMEOUT_MS)); + msecs_to_jiffies(TIMEOUT_MS*2)); if (!rc) { pr_err("Timed out waiting sending command\n"); rc = -ETIMEDOUT; @@ -419,7 +420,7 @@ static void bgrsb_bgup_work(struct work_struct *work) pr_err("Glink channel connection time out\n"); return; } - rc = bgrsb_configr_rsb(dev); + rc = bgrsb_configr_rsb(dev, true); if (rc != 0) { pr_err("BG failed to configure RSB %d\n", rc); if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) @@ -720,6 +721,45 @@ static int bg_rsb_remove(struct platform_device *pdev) return 0; } +static int bg_rsb_resume(struct platform_device *pdev) +{ + struct bgrsb_priv *dev = platform_get_drvdata(pdev); + + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) + return 0; + + if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { + if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) { + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + pr_debug("RSB Cofigured\n"); + return 0; + } + pr_err("RSB failed to resume\n"); + } + return -EINVAL; +} + +static int bg_rsb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bgrsb_priv *dev = platform_get_drvdata(pdev); + + if (dev->bgrsb_current_state == BGRSB_STATE_INIT) + return 0; + + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) + return -EINVAL; + } + + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) { + dev->bgrsb_current_state = BGRSB_STATE_INIT; + pr_debug("RSB Init\n"); + return 0; + } + pr_err("RSB failed to suspend\n"); + return -EINVAL; +} + static const struct of_device_id bg_rsb_of_match[] = { { .compatible = "qcom,bg-rsb", }, { } @@ -732,7 +772,8 @@ static struct platform_driver bg_rsb_driver = { }, .probe = bg_rsb_probe, .remove = bg_rsb_remove, - + .resume = bg_rsb_resume, + .suspend = bg_rsb_suspend, }; module_platform_driver(bg_rsb_driver); -- GitLab From bf3b61748378e79063b62b9280ea0d5b3f6d122b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 29 Oct 2017 06:30:19 -0400 Subject: [PATCH 1996/5498] UPSTREAM: fscrypt: lock mutex before checking for bounce page pool fscrypt_initialize(), which allocates the global bounce page pool when an encrypted file is first accessed, uses "double-checked locking" to try to avoid locking fscrypt_init_mutex. However, it doesn't use any memory barriers, so it's theoretically possible for a thread to observe a bounce page pool which has not been fully initialized. This is a classic bug with "double-checked locking". While "only a theoretical issue" in the latest kernel, in pre-4.8 kernels the pointer that was checked was not even the last to be initialized, so it was easily possible for a crash (NULL pointer dereference) to happen. This was changed only incidentally by the large refactor to use fs/crypto/. Solve both problems in a trivial way that can easily be backported: just always take the mutex. It's theoretically less efficient, but it shouldn't be noticeable in practice as the mutex is only acquired very briefly once per encrypted file. Later I'd like to make this use a helper macro like DO_ONCE(). However, DO_ONCE() runs in atomic context, so we'd need to add a new macro that allows blocking. Cc: stable@vger.kernel.org # v4.1+ Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o (cherry-picked from commit a0b3bc855374c50b5ea85273553485af48caf2f7 and fixed up for android-3.18) Change-Id: I18c7231af7de2319883934d2e36ea54e1eb44466 Signed-off-by: Eric Biggers --- fs/crypto/crypto.c | 7 ++----- fs/ext4/crypto_key.c | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 5182c8e07eba..303e881232d1 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -410,11 +410,8 @@ int fscrypt_initialize(unsigned int cop_flags) { int i, res = -ENOMEM; - /* - * No need to allocate a bounce page pool if there already is one or - * this FS won't use it. - */ - if (cop_flags & FS_CFLG_OWN_PAGES || fscrypt_bounce_page_pool) + /* No need to allocate a bounce page pool if this FS won't use it. */ + if (cop_flags & FS_CFLG_OWN_PAGES) return 0; mutex_lock(&fscrypt_init_mutex); diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 998f6dac7a24..abcc7172414c 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -129,11 +129,9 @@ int ext4_get_encryption_info(struct inode *inode) if (ei->i_crypt_info) return 0; - if (!ext4_read_workqueue) { - res = ext4_init_crypto(); - if (res) - return res; - } + res = ext4_init_crypto(); + if (res) + return res; res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, -- GitLab From cb57d84347ccc82c1a357aa5d372fa1bc892e0e1 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Wed, 11 Oct 2017 10:12:19 +0530 Subject: [PATCH 1997/5498] mtd: msm_qpic_nand: Add support for performance stats Add support to collect driver level performance statistics on reads, writes and erases. Below are the commands to enable, reset and read the stats - echo 1 > /sys/module/msm_qpic_nand/parameters/enable_perfstats echo > /sys/devices/7980000.nand/perf_stats cat /sys/devices/7980000.nand/perf_stats The module parameter "enable_perfstats" can also be enabled during boot up by adding this to the kernel command line - "msm_qpic_nand.enable_perfstats=1". Change-Id: I20b35219a4bffd90682137969ecd5e854bea9a6b Signed-off-by: Sahitya Tummala --- drivers/mtd/devices/msm_qpic_nand.c | 203 ++++++++++++++++++++++++++++ drivers/mtd/devices/msm_qpic_nand.h | 22 ++- 2 files changed, 224 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c index b087c5e17c57..9ed3a104111d 100644 --- a/drivers/mtd/devices/msm_qpic_nand.c +++ b/drivers/mtd/devices/msm_qpic_nand.c @@ -18,6 +18,183 @@ #define QPIC_BAM_DEFAULT_IPC_LOGLVL 2 static bool enable_euclean; +static bool enable_perfstats; + +static ssize_t msm_nand_attr_perf_stats_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t msm_nand_attr_perf_stats_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static struct device_attribute dev_msm_nand_perf_stats = + __ATTR(perf_stats, S_IRUGO | S_IWUSR, + msm_nand_attr_perf_stats_show, msm_nand_attr_perf_stats_store); + +#define print_sysfs(fmt, ...) \ +{ \ + count += scnprintf(buf + count, PAGE_SIZE - count, \ + fmt, ##__VA_ARGS__); \ +} + +static ssize_t msm_nand_attr_perf_stats_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t count = 0; + struct msm_nand_info *info = dev_get_drvdata(dev); + + if (!enable_perfstats) { + print_sysfs("Performance stats is disabled\n"); + return count; + } + + spin_lock(&info->perf.lock); + print_sysfs("total_read_size = %llu\n", info->perf.total_read_size); + print_sysfs("total_write_size = %llu\n", info->perf.total_write_size); + print_sysfs("total_erase_blks = %llu\n\n", info->perf.total_erase_blks); + + print_sysfs("total_read_time_us = %lld\n", + ktime_to_us(info->perf.total_read_time)); + print_sysfs("total_write_time_us = %lld\n", + ktime_to_us(info->perf.total_write_time)); + print_sysfs("total_erase_time_us = %lld\n\n", + ktime_to_us(info->perf.total_erase_time)); + + print_sysfs("min_read_time_us = %lld\n", + ktime_to_us(info->perf.min_read_time)); + print_sysfs("min_write_time_us = %lld\n", + ktime_to_us(info->perf.min_write_time)); + print_sysfs("min_erase_time_us = %lld\n\n", + ktime_to_us(info->perf.min_erase_time)); + + print_sysfs("max_read_time_us = %lld\n", + ktime_to_us(info->perf.max_read_time)); + print_sysfs("max_write_time_us = %lld\n", + ktime_to_us(info->perf.max_write_time)); + print_sysfs("max_erase_time_us = %lld\n\n", + ktime_to_us(info->perf.max_erase_time)); + + spin_unlock(&info->perf.lock); + return count; +} + +static ssize_t msm_nand_attr_perf_stats_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msm_nand_info *info = dev_get_drvdata(dev); + + if (!enable_perfstats) { + pr_err("couldn't write as perf stats is disabled\n"); + return -EPERM; + } + + if (count > 1 || (count == 1 && *buf != '\n')) { + pr_err("write not permitted\n"); + return -EPERM; + } + + spin_lock(&info->perf.lock); + info->perf.min_read_time = ktime_set(KTIME_MAX, 0); + info->perf.min_write_time = ktime_set(KTIME_MAX, 0); + info->perf.min_erase_time = ktime_set(KTIME_MAX, 0); + + info->perf.max_read_time = ktime_set(0, 0); + info->perf.max_write_time = ktime_set(0, 0); + info->perf.max_erase_time = ktime_set(0, 0); + + info->perf.total_read_time = ktime_set(0, 0); + info->perf.total_write_time = ktime_set(0, 0); + info->perf.total_erase_time = ktime_set(0, 0); + + info->perf.total_read_size = 0; + info->perf.total_write_size = 0; + info->perf.total_erase_blks = 0; + spin_unlock(&info->perf.lock); + + return count; +} + +static void msm_nand_init_perf_stats(struct msm_nand_info *info) +{ + spin_lock_init(&info->perf.lock); + info->perf.min_read_time = ktime_set(KTIME_MAX, 0); + info->perf.min_write_time = ktime_set(KTIME_MAX, 0); + info->perf.min_erase_time = ktime_set(KTIME_MAX, 0); +} + +static void msm_nand_init_sysfs(struct device *dev) +{ + sysfs_attr_init(&dev_msm_nand_perf_stats); + if (device_create_file(dev, &dev_msm_nand_perf_stats)) + pr_err("Sysfs entry create failed"); +} + +static void msm_nand_cleanup_sysfs(struct device *dev) +{ + device_remove_file(dev, &dev_msm_nand_perf_stats); +} + +static void msm_nand_update_read_perf_stats(struct msm_nand_info *info, + ktime_t start, u32 size) +{ + ktime_t time_delta; + + time_delta = ktime_sub(ktime_get(), start); + + spin_lock(&info->perf.lock); + info->perf.total_read_size += size; + info->perf.total_read_time = ktime_add(info->perf.total_read_time, + time_delta); + if (ktime_after(time_delta, info->perf.max_read_time)) + info->perf.max_read_time = time_delta; + + if (ktime_before(time_delta, info->perf.min_read_time)) + info->perf.min_read_time = time_delta; + + spin_unlock(&info->perf.lock); +} + +static void msm_nand_update_write_perf_stats(struct msm_nand_info *info, + ktime_t start, u32 size) +{ + ktime_t time_delta; + + time_delta = ktime_sub(ktime_get(), start); + + spin_lock(&info->perf.lock); + info->perf.total_write_size += size; + info->perf.total_write_time = ktime_add(info->perf.total_write_time, + time_delta); + if (ktime_after(time_delta, info->perf.max_write_time)) + info->perf.max_write_time = time_delta; + + if (ktime_before(time_delta, info->perf.min_write_time)) + info->perf.min_write_time = time_delta; + + spin_unlock(&info->perf.lock); +} + +static void msm_nand_update_erase_perf_stats(struct msm_nand_info *info, + ktime_t start, u32 count) +{ + ktime_t time_delta; + + time_delta = ktime_sub(ktime_get(), start); + + spin_lock(&info->perf.lock); + info->perf.total_erase_blks += count; + info->perf.total_erase_time = ktime_add(info->perf.total_erase_time, + time_delta); + if (ktime_after(time_delta, info->perf.max_erase_time)) + info->perf.max_erase_time = time_delta; + + if (ktime_before(time_delta, info->perf.min_erase_time)) + info->perf.min_erase_time = time_delta; + + spin_unlock(&info->perf.lock); +} /* * Get the DMA memory for requested amount of size. It returns the pointer @@ -1599,6 +1776,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, struct sps_iovec iovec_temp; bool erased_page; uint64_t fix_data_in_pages = 0; + ktime_t start; /* * The following 6 commands will be sent only once for the first @@ -1624,6 +1802,9 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } *dma_buffer; struct msm_nand_rw_cmd_desc *cmd_list = NULL; + if (unlikely(enable_perfstats)) + start = ktime_get(); + memset(&rw_params, 0, sizeof(struct msm_nand_rw_params)); err = msm_nand_validate_mtd_params(mtd, true, from, ops, &rw_params); if (err) @@ -1931,6 +2112,8 @@ validate_mtd_params_failed: err, ops->retlen, ops->oobretlen); pr_debug("========================================================\n"); + if (unlikely(enable_perfstats) && likely(!err)) + msm_nand_update_read_perf_stats(info, start, ops->retlen); return err; } @@ -2151,6 +2334,8 @@ static int msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct msm_nand_rw_reg_data data; struct sps_iovec *iovec; struct sps_iovec iovec_temp; + ktime_t start; + /* * The following 7 commands will be sent only once : * For first codeword (CW) - addr0, addr1, dev0_cfg0, dev0_cfg1, @@ -2175,6 +2360,9 @@ static int msm_nand_write_oob(struct mtd_info *mtd, loff_t to, } *dma_buffer; struct msm_nand_rw_cmd_desc *cmd_list = NULL; + if (unlikely(enable_perfstats)) + start = ktime_get(); + memset(&rw_params, 0, sizeof(struct msm_nand_rw_params)); err = msm_nand_validate_mtd_params(mtd, false, to, ops, &rw_params); if (err) @@ -2350,6 +2538,8 @@ validate_mtd_params_failed: err, ops->retlen, ops->oobretlen); pr_debug("================================================\n"); + if (unlikely(enable_perfstats) && likely(!err)) + msm_nand_update_write_perf_stats(info, start, ops->retlen); return err; } @@ -2447,6 +2637,8 @@ static int msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) struct sps_iovec *iovec; struct sps_iovec iovec_temp; uint32_t total_cnt = 9; + ktime_t start; + /* * The following 9 commands are required to erase a page - * flash, addr0, addr1, cfg0, cfg1, exec, flash_status(read), @@ -2459,6 +2651,9 @@ static int msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) uint32_t flash_status; } *dma_buffer; + if (unlikely(enable_perfstats)) + start = ktime_get(); + if (mtd->writesize == PAGE_SIZE_2K) page = instr->addr >> 11; @@ -2573,6 +2768,8 @@ unlock_mutex: mutex_unlock(&info->lock); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); out: + if (unlikely(enable_perfstats) && likely(!err)) + msm_nand_update_erase_perf_stats(info, start, 1); return err; } @@ -3526,6 +3723,8 @@ static int msm_nand_probe(struct platform_device *pdev) info->nand_phys, info->bam_phys, info->bam_irq); pr_info("Allocated DMA buffer at virt_addr 0x%p, phys_addr 0x%x\n", info->nand_chip.dma_virt_addr, info->nand_chip.dma_phys_addr); + msm_nand_init_sysfs(dev); + msm_nand_init_perf_stats(info); goto out; free_bam: msm_nand_bam_free(info); @@ -3547,6 +3746,7 @@ static int msm_nand_remove(struct platform_device *pdev) { struct msm_nand_info *info = dev_get_drvdata(&pdev->dev); + msm_nand_cleanup_sysfs(&pdev->dev); if (pm_runtime_suspended(&(pdev)->dev)) pm_runtime_resume(&(pdev)->dev); @@ -3591,6 +3791,9 @@ static struct platform_driver msm_nand_driver = { module_param(enable_euclean, bool, 0644); MODULE_PARM_DESC(enable_euclean, "Set this parameter to enable reporting EUCLEAN to upper layer when the correctable bitflips are equal to the max correctable limit."); +module_param(enable_perfstats, bool, 0644); +MODULE_PARM_DESC(enable_perfstats, "Set this parameter to enable collection and reporting of performance data."); + module_platform_driver(msm_nand_driver); MODULE_ALIAS(DRIVER_NAME); diff --git a/drivers/mtd/devices/msm_qpic_nand.h b/drivers/mtd/devices/msm_qpic_nand.h index 48c625443ff6..20765a836ffa 100644 --- a/drivers/mtd/devices/msm_qpic_nand.h +++ b/drivers/mtd/devices/msm_qpic_nand.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #define PAGE_SIZE_2K 2048 @@ -296,6 +298,23 @@ struct msm_nand_clk_data { atomic_t curr_vote; }; +struct msm_nand_perf_stats { + u64 total_read_size; + u64 total_write_size; + u64 total_erase_blks; + ktime_t total_read_time; + ktime_t total_write_time; + ktime_t total_erase_time; + ktime_t min_read_time; + ktime_t min_write_time; + ktime_t min_erase_time; + ktime_t max_read_time; + ktime_t max_write_time; + ktime_t max_erase_time; + spinlock_t lock; +}; + + /* Structure that defines NANDc private data. */ struct msm_nand_info { struct mtd_info mtd; @@ -320,6 +339,7 @@ struct msm_nand_info { struct mutex lock; struct flash_identification flash_dev; struct msm_nand_clk_data clk_data; + struct msm_nand_perf_stats perf; u64 dma_mask; }; -- GitLab From b22f0aa9dede8838af20374987cbb20b39678244 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 10 Jan 2017 13:35:49 -0800 Subject: [PATCH 1998/5498] BACKPORT: arm64: Use __pa_symbol for kernel symbols (partial) (cherry-pick from commit 2077be6783b5936c3daa838d8addbb635667927f) __pa_symbol is technically the marcro that should be used for kernel symbols. Switch to this as a pre-requisite for DEBUG_VIRTUAL which will do bounds checking. Reviewed-by: Mark Rutland Tested-by: Mark Rutland Signed-off-by: Laura Abbott Signed-off-by: Will Deacon Bug: 20045882 Bug: 63737556 Orig-Change-Id: Ibef89e5935c9562fa69e946778c705636c1ca61e Change-Id: Ic60b1a5efb2a22317063666a7d271279707c8655 --- arch/arm64/include/asm/memory.h | 1 + arch/arm64/kernel/vdso.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 1b8624e61c28..f520918473f5 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -131,6 +131,7 @@ static inline void *phys_to_virt(phys_addr_t x) #define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #define virt_to_pfn(x) __phys_to_pfn(__virt_to_phys(x)) +#define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x)) /* * virt_to_page(k) convert a _valid_ virtual address to struct page * diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index b0d064c3ed35..fb29bbd3c41c 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -114,6 +114,7 @@ static struct vm_special_mapping vdso_spec[2]; static int __init vdso_init(void) { int i; + unsigned long pfn; if (memcmp(&vdso_start, "\177ELF", 4)) { pr_err("vDSO is not a valid ELF object!\n"); @@ -131,11 +132,13 @@ static int __init vdso_init(void) return -ENOMEM; /* Grab the vDSO data page. */ - vdso_pagelist[0] = virt_to_page(vdso_data); + vdso_pagelist[0] = phys_to_page(__pa_symbol(vdso_data)); /* Grab the vDSO code pages. */ + pfn = sym_to_pfn(&vdso_start); + for (i = 0; i < vdso_pages; i++) - vdso_pagelist[i + 1] = virt_to_page(&vdso_start + i * PAGE_SIZE); + vdso_pagelist[i + 1] = pfn_to_page(pfn + i); /* Populate the special mapping structures */ vdso_spec[0] = (struct vm_special_mapping) { -- GitLab From 55714df3ac800676d27db5fb5ec21527215f1ee5 Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Mon, 18 Dec 2017 10:42:41 -0800 Subject: [PATCH 1999/5498] socinfo: Add support for TTP platform Add enum and hw_platform text for the TTP platform. Change-Id: Ic2c89603df084b7b7b8744d7144f868545baf212 Signed-off-by: Gustavo Solaira --- drivers/soc/qcom/socinfo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 4f02a347e615..f1441ff9f76d 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -66,6 +66,7 @@ enum { HW_PLATFORM_STP = 23, HW_PLATFORM_SBC = 24, HW_PLATFORM_ADP = 25, + HW_PLATFORM_TTP = 30, HW_PLATFORM_INVALID }; @@ -87,6 +88,7 @@ const char *hw_platform[] = { [HW_PLATFORM_STP] = "STP", [HW_PLATFORM_SBC] = "SBC", [HW_PLATFORM_ADP] = "ADP", + [HW_PLATFORM_TTP] = "TTP", }; enum { -- GitLab From fa8a6888830663ea6e32057d8f3403c48b68579b Mon Sep 17 00:00:00 2001 From: Archana Obannagari Date: Tue, 12 Dec 2017 11:20:29 +0530 Subject: [PATCH 2000/5498] ARM: dts: msm: Remove GPU mempool for msm8917 For low memory devices reserving memory can lead to low-memory conditions. So remove GPU mempool to have memory with the system, which will help overall system performance in concurrent use cases. CRs-Fixed: 2146074 Change-Id: I3b84cf66c2868918838d001478180b6468411593 Signed-off-by: Archana Obannagari --- arch/arm/boot/dts/qcom/msm8917-gpu.dtsi | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8917-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8917-gpu.dtsi index e7891d61fd3f..6ee4cfbef8a9 100644 --- a/arch/arm/boot/dts/qcom/msm8917-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8917-gpu.dtsi @@ -109,26 +109,6 @@ coresight-child-list = <&funnel_mm>; coresight-child-ports = <6>; - /* GPU Mempools */ - qcom,gpu-mempools { - #address-cells= <1>; - #size-cells = <0>; - compatible = "qcom,gpu-mempools"; - - qcom,mempool-max-pages = <32768>; - - /* 4K Page Pool configuration */ - qcom,gpu-mempool@0 { - reg = <0>; - qcom,mempool-page-size = <4096>; - }; - /* 64K Page Pool configuration */ - qcom,gpu-mempool@1 { - reg = <1>; - qcom,mempool-page-size = <65536>; - }; - }; - /* Power levels */ qcom,gpu-pwrlevels { #address-cells = <1>; -- GitLab From 9ca4fbaa1816359f6c17104248cffa3bcd6a67a0 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Tue, 19 Dec 2017 17:33:04 +0530 Subject: [PATCH 2001/5498] ARM: dts: msm: Refactor IOT memory map configuration for apq8053 Refactor IOT based targets memory map to a separate file and include it directly in all apq8053 based IOT targets. Change-Id: I684f3c5ed750c1672c006df3ae44a82ece119332 Signed-off-by: Shiraz Hashim --- .../arm/boot/dts/qcom/apq8053-iot-memory.dtsi | 25 +++++++++++++++++++ arch/arm/boot/dts/qcom/apq8053-iot-mtp.dts | 13 +--------- arch/arm/boot/dts/qcom/apq8053-ipc.dts | 1 + 3 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi diff --git a/arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi b/arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi new file mode 100644 index 000000000000..f6e26b223bf0 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&modem_mem { + reg = <0x0 0x86c00000 0x0 0x1e00000>; +}; + +&adsp_fw_mem { + reg = <0x0 0x88a00000 0x0 0x1100000>; +}; + +&wcnss_fw_mem { + reg = <0x0 0x89b00000 0x0 0x700000>; +}; + diff --git a/arch/arm/boot/dts/qcom/apq8053-iot-mtp.dts b/arch/arm/boot/dts/qcom/apq8053-iot-mtp.dts index 6ef54702a16b..df640524d441 100644 --- a/arch/arm/boot/dts/qcom/apq8053-iot-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8053-iot-mtp.dts @@ -15,6 +15,7 @@ #include "apq8053.dtsi" #include "msm-pmi8950.dtsi" +#include "apq8053-iot-memory.dtsi" #include "msm8953-mtp.dtsi" #include "msm8953-pmi8950.dtsi" #include "msm8953-camera-sensor-mtp.dtsi" @@ -148,18 +149,6 @@ status = "okay"; }; -&modem_mem { - reg = <0x0 0x86c00000 0x0 0x1e00000>; -}; - -&adsp_fw_mem { - reg = <0x0 0x88a00000 0x0 0x1100000>; -}; - -&wcnss_fw_mem { - reg = <0x0 0x89b00000 0x0 0x700000>; -}; - &gpio_key_suspend { config { /delete-property/ bias-pull-up; diff --git a/arch/arm/boot/dts/qcom/apq8053-ipc.dts b/arch/arm/boot/dts/qcom/apq8053-ipc.dts index 23c6b011f1ff..713e4333b708 100644 --- a/arch/arm/boot/dts/qcom/apq8053-ipc.dts +++ b/arch/arm/boot/dts/qcom/apq8053-ipc.dts @@ -16,6 +16,7 @@ #include "apq8053.dtsi" #include "msm-pmi8950.dtsi" #include "msm8953-mdss-panels.dtsi" +#include "apq8053-iot-memory.dtsi" #include "msm8953-pmi8950.dtsi" #include "msm8953-ipc.dtsi" -- GitLab From 8ad727670d68207069aee6963246e811b03e1495 Mon Sep 17 00:00:00 2001 From: Gaurav Singhal Date: Tue, 19 Dec 2017 14:57:10 +0530 Subject: [PATCH 2002/5498] ARM: dts: msm: Add NFC device node for SDW2500 Device node changes required on SDW2500 HW describing the GPIO configuration for NFC controller chip. Change-Id: I8ae1a420a54e336702dca86900c1c3c45f4d6336 Signed-off-by: Gaurav Singhal --- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 24 +++++++++++++++++++++ arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 19 +++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index 635b6aed0465..22712e983a96 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -592,6 +592,30 @@ }; }; + nfcv2k_disable_active: nfcv2k_disable_active { + mux { + pins = "gpio52"; + function = "gpio"; + }; + config { + pins = "gpio52"; + drive-strength = <6>; + bias-pull-up; + }; + }; + + nfcv2k_disable_suspend: nfcv2k_disable_suspend { + mux { + pins = "gpio52"; + function = "gpio"; + }; + config { + pins = "gpio52"; + drive-strength = <6>; + bias-disable; + }; + }; + nfc_int_active: nfc_int_active { mux { pins = "gpio21"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index d5250ecf8d92..b8900c105c92 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -124,7 +124,24 @@ }; &i2c_2 { - status = "disabled"; + status = "okay"; + nq@28 { + compatible = "qcom,nq-nci"; + reg = <0x28>; + qcom,nq-irq = <&msm_gpio 50 0x00>; + qcom,nq-ven = <&msm_gpio 52 0x00>; + qcom,nq-firm = <&msm_gpio 38 0x00>; + qcom,nq-esepwr = <&msm_gpio 49 0x00>; + qcom,nq-clkreq = <&pm660_gpios 4 0x00>; + qcom,clk-src = "BBCLK3"; + interrupt-parent = <&msm_gpio>; + interrupts = <50 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active","nfc_suspend"; + pinctrl-0 = <&nfcw_int_active &nfcv2k_disable_active>; + pinctrl-1 = <&nfcw_int_suspend &nfcv2k_disable_suspend>; + clock-names = "ref_clk"; + }; }; &i2c_5 { -- GitLab From cfc5aaec3eeb7657f1d28285ebd140a377547e51 Mon Sep 17 00:00:00 2001 From: Prasad Malisetty Date: Sun, 5 Nov 2017 15:46:08 +0530 Subject: [PATCH 2003/5498] msm: mhi_dev: mhi dev net driver performance improvement In current mhi driver, data packet communication is happening between mhi device and host using DMA sync method. To improve the data throughputs for mhi device clients, added DMA async support in mhi driver to transfer data packets, events and ring element cache. Client can choose transfer mode either DMA sync, DMA async while requesting read/write packets. Change-Id: I3b5cdc6d497416ff4feaea7a9ff793c230b51121 Signed-off-by: Prasad Malisetty Signed-off-by: Chandra Devireddy Signed-off-by: Siva Kumar Akkireddi --- drivers/platform/msm/mhi_dev/mhi.c | 570 ++++++++++++++++----- drivers/platform/msm/mhi_dev/mhi.h | 109 +++- drivers/platform/msm/mhi_dev/mhi_dev_net.c | 326 ++++++++---- drivers/platform/msm/mhi_dev/mhi_ring.c | 33 +- drivers/platform/msm/mhi_dev/mhi_uci.c | 76 +-- 5 files changed, 810 insertions(+), 304 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index 6d7ea7d9d8b0..14cd067933ea 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -61,6 +61,9 @@ #define MHI_INIT 0 #define MHI_REINIT 1 +#define TR_RING_ELEMENT_SZ sizeof(struct mhi_dev_transfer_ring_element) +#define RING_ELEMENT_TYPE_SZ sizeof(union mhi_dev_ring_element_type) + enum mhi_msg_level mhi_msg_lvl = MHI_MSG_ERROR; enum mhi_msg_level mhi_ipc_msg_lvl = MHI_MSG_VERBOSE; void *mhi_ipc_log; @@ -73,11 +76,36 @@ static void mhi_update_state_info(uint32_t info); static int mhi_deinit(struct mhi_dev *mhi); static void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify); static int mhi_dev_pcie_notify_event; +static void mhi_dev_transfer_completion_cb(void *mreq); + +/* + * mhi_dev_ring_cache_completion_cb () - Call back function called + * by IPA driver when ring element cache is done + * + * @req : ring cache request + */ +static void mhi_dev_ring_cache_completion_cb(void *req) +{ + struct ring_cache_req *ring_req = NULL; + + if (req) + ring_req = (struct ring_cache_req *)req; + else { + pr_err("%s():ring cache req data is NULL\n", __func__); + return; + } + complete(ring_req->done); +} void mhi_dev_read_from_host(struct mhi_dev *mhi, struct mhi_addr *transfer) { int rc = 0; uint64_t bit_40 = ((u64) 1) << 40, host_addr_pa = 0, offset = 0; + struct ring_cache_req ring_req; + + DECLARE_COMPLETION(done); + + ring_req.done = &done; if (!mhi) { pr_err("invalid MHI ctx\n"); @@ -97,26 +125,31 @@ void mhi_dev_read_from_host(struct mhi_dev *mhi, struct mhi_addr *transfer) "device 0x%x <<-- host 0x%llx, size %d\n", transfer->phy_addr, host_addr_pa, (int) transfer->size); - - rc = ipa_dma_sync_memcpy((u64) transfer->phy_addr, - host_addr_pa, (int) transfer->size); + rc = ipa_dma_async_memcpy((u64)transfer->phy_addr, host_addr_pa, + (int)transfer->size, + mhi_dev_ring_cache_completion_cb, &ring_req); if (rc) pr_err("error while reading from host:%d\n", rc); + + wait_for_completion(&done); + } else { + memcpy(transfer->virt_addr, (void *) &transfer->device_va, + (int) transfer->size); } } EXPORT_SYMBOL(mhi_dev_read_from_host); -void mhi_dev_write_to_host(struct mhi_dev *mhi, - struct mhi_addr *transfer) +void mhi_dev_write_to_host(struct mhi_dev *mhi, struct mhi_addr *transfer, + struct event_req *ereq, enum mhi_dev_transfer_type tr_type) { int rc = 0; uint64_t bit_40 = ((u64) 1) << 40, host_addr_pa = 0, offset = 0; + dma_addr_t dma; if (!mhi) { pr_err("invalid MHI ctx\n"); return; } - if (mhi->config_iatu) { offset = (uint64_t) transfer->host_pa - mhi->ctrl_base.host_pa; /* Mapping the translated physical address on the device */ @@ -126,39 +159,55 @@ void mhi_dev_write_to_host(struct mhi_dev *mhi, } if (mhi->use_ipa) { - /* Copy the device content to a local device physical address */ - memcpy(mhi->dma_cache, transfer->virt_addr, transfer->size); mhi_log(MHI_MSG_VERBOSE, "device 0x%llx --> host 0x%llx, size %d\n", (uint64_t) mhi->cache_dma_handle, host_addr_pa, (int) transfer->size); - - rc = ipa_dma_sync_memcpy(host_addr_pa, - (u64) mhi->cache_dma_handle, (int) transfer->size); - if (rc) - pr_err("error while reading from host:%d\n", rc); + if (tr_type == MHI_DEV_DMA_ASYNC) { + dma = dma_map_single(&mhi->pdev->dev, + transfer->virt_addr, transfer->size, + DMA_TO_DEVICE); + if (ereq->event_type == SEND_EVENT_BUFFER) { + ereq->dma = dma; + ereq->dma_len = transfer->size; + } else if (ereq->event_type == SEND_EVENT_RD_OFFSET) { + ereq->event_rd_dma = dma; + } + rc = ipa_dma_async_memcpy(host_addr_pa, (uint64_t) dma, + (int)transfer->size, + ereq->client_cb, ereq); + if (rc) + pr_err("error while writing to host:%d\n", rc); + } else if (tr_type == MHI_DEV_DMA_SYNC) { + /* Copy the device content to a local device + * physical address */ + memcpy(mhi->dma_cache, transfer->virt_addr, + transfer->size); + rc = ipa_dma_sync_memcpy(host_addr_pa, + (u64) mhi->cache_dma_handle, + (int) transfer->size); + if (rc) + pr_err("error while writing to host:%d\n", rc); + } + } else { + memcpy((void *) &transfer->device_va, transfer->virt_addr, + transfer->size); + /* Update state before sending events */ + wmb(); } } EXPORT_SYMBOL(mhi_dev_write_to_host); int mhi_transfer_host_to_device(void *dev, uint64_t host_pa, uint32_t len, - struct mhi_dev *mhi) + struct mhi_dev *mhi, struct mhi_req *mreq) { int rc = 0; uint64_t bit_40 = ((u64) 1) << 40, host_addr_pa = 0, offset = 0; + struct mhi_dev_ring *ring = NULL; - if (!mhi) { - pr_err("Invalid mhi device\n"); - return -EINVAL; - } - - if (!dev) { - pr_err("Invalid virt device\n"); - return -EINVAL; - } - if (!host_pa) { - pr_err("Invalid host pa device\n"); + if (!mhi || !dev || !host_pa || !mreq) { + pr_err("%s():Invalid parameters\n", __func__); return -EINVAL; } @@ -172,26 +221,45 @@ int mhi_transfer_host_to_device(void *dev, uint64_t host_pa, uint32_t len, mhi_log(MHI_MSG_VERBOSE, "device 0x%llx <-- host 0x%llx, size %d\n", (uint64_t) mhi->read_dma_handle, host_addr_pa, (int) len); - rc = ipa_dma_sync_memcpy((u64) mhi->read_dma_handle, - host_addr_pa, (int) len); - if (rc) { - pr_err("error while reading from host:%d\n", rc); - return rc; - } - memcpy(dev, mhi->read_handle, len); + if (mreq->mode == IPA_DMA_SYNC) { + rc = ipa_dma_sync_memcpy((u64) mhi->read_dma_handle, + host_addr_pa, (int) len); + if (rc) { + pr_err("error while reading chan using sync:%d\n", rc); + return rc; + } + memcpy(dev, mhi->read_handle, len); + } else if (mreq->mode == IPA_DMA_ASYNC) { + ring = mreq->client->channel->ring; + mreq->dma = dma_map_single(&mhi->pdev->dev, dev, len, + DMA_FROM_DEVICE); + mhi_dev_ring_inc_index(ring, ring->rd_offset); + if (ring->rd_offset == ring->wr_offset) + mreq->snd_cmpl = 1; + else + mreq->snd_cmpl = 0; + rc = ipa_dma_async_memcpy(mreq->dma, host_addr_pa, + (int) len, mhi_dev_transfer_completion_cb, + mreq); + if (rc) { + pr_err("error while reading chan using async:%d\n", rc); + return rc; + } + } return rc; } EXPORT_SYMBOL(mhi_transfer_host_to_device); int mhi_transfer_device_to_host(uint64_t host_addr, void *dev, uint32_t len, - struct mhi_dev *mhi) + struct mhi_dev *mhi, struct mhi_req *req) { int rc = 0; uint64_t bit_40 = ((u64) 1) << 40, host_addr_pa = 0, offset = 0; + struct mhi_dev_ring *ring = NULL; - if (!mhi || !dev || !host_addr) { + if (!mhi || !dev || !req || !host_addr) { pr_err("%sInvalid parameters\n", __func__); return -EINVAL; } @@ -203,17 +271,25 @@ int mhi_transfer_device_to_host(uint64_t host_addr, void *dev, uint32_t len, } else { host_addr_pa = host_addr | bit_40; } - - memcpy(mhi->write_handle, dev, len); - mhi_log(MHI_MSG_VERBOSE, "device 0x%llx ---> host 0x%llx, size %d\n", - (uint64_t) mhi->write_dma_handle, host_addr_pa, (int) len); - rc = ipa_dma_sync_memcpy(host_addr_pa, - (u64) mhi->write_dma_handle, - (int) len); - if (rc) - pr_err("error while reading from host:%d\n", rc); + (uint64_t) mhi->write_dma_handle, + host_addr_pa, (int) len); + if (req->mode == IPA_DMA_SYNC) { + memcpy(mhi->write_handle, dev, len); + rc = ipa_dma_sync_memcpy(host_addr_pa, + (u64) mhi->write_dma_handle, (int) len); + } else if (req->mode == IPA_DMA_ASYNC) { + req->dma = dma_map_single(&mhi->pdev->dev, req->buf, + req->len, DMA_TO_DEVICE); + ring = req->client->channel->ring; + mhi_dev_ring_inc_index(ring, ring->rd_offset); + if (ring->rd_offset == ring->wr_offset) + req->snd_cmpl = 1; + rc = ipa_dma_async_memcpy(host_addr_pa, + (uint64_t) req->dma, (int) len, + mhi_dev_transfer_completion_cb, req); + } return rc; } EXPORT_SYMBOL(mhi_transfer_device_to_host); @@ -505,11 +581,8 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, struct mhi_dev_ring *ring = &mhi->ring[evnt_ring_idx]; union mhi_dev_ring_ctx *ctx; struct ep_pcie_msi_config cfg; - struct mhi_addr msi_addr; struct mhi_addr transfer_addr; - uint32_t data_buffer = 0; - memset(&msi_addr, 0, sizeof(msi_addr)); rc = ep_pcie_get_msi_config(mhi->phandle, &cfg); if (rc) { pr_err("Error retrieving pcie msi logic\n"); @@ -533,7 +606,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, mutex_lock(&mhi->mhi_event_lock); /* add the ring element */ - mhi_dev_add_element(ring, el); + mhi_dev_add_element(ring, el, NULL, 0); ring->ring_ctx_shadow->ev.rp = (ring->rd_offset * sizeof(union mhi_dev_ring_element_type)) + @@ -556,7 +629,7 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, transfer_addr.virt_addr = &ring->ring_ctx_shadow->ev.rp; transfer_addr.size = sizeof(uint64_t); - mhi_dev_write_to_host(mhi, &transfer_addr); + mhi_dev_write_to_host(mhi, &transfer_addr, NULL, MHI_DEV_DMA_SYNC); /* * rp update in host memory should be flushed * before sending a MSI to the host @@ -570,19 +643,6 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, mhi_log(MHI_MSG_VERBOSE, "evnt code :0x%x\n", el->evt_tr_comp.code); mhi_log(MHI_MSG_VERBOSE, "evnt type :0x%x\n", el->evt_tr_comp.type); mhi_log(MHI_MSG_VERBOSE, "evnt chid :0x%x\n", el->evt_tr_comp.chid); - - if (mhi->use_ipa) - msi_addr.host_pa = (uint64_t)((uint64_t)cfg.upper << 32) | - (uint64_t)cfg.lower; - else - msi_addr.device_va = (uintptr_t)((uint64_t)cfg.upper << 32) | - (uint64_t)cfg.lower; - - data_buffer = cfg.data + mhi_ctx->mhi_ep_msi_num + ctx->ev.msivec; - msi_addr.virt_addr = &data_buffer; - msi_addr.size = 4; - mhi_log(MHI_MSG_VERBOSE, "Sending MSI %d to 0x%llx as data = 0x%x\n", - ctx->ev.msivec, msi_addr.host_pa, data_buffer); rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec); if (rc) { pr_err("%s: error sending msi\n", __func__); @@ -591,6 +651,130 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring, return rc; } +/* + * mhi_dev_event_buf_completion_cb() -Cb function called by IPA driver + * when transfer completion event buffer copy is done. + * + * @req - event_req structure + */ + +static void mhi_dev_event_buf_completion_cb(void *req) +{ + struct event_req *ereq = NULL; + + if (req) { + ereq = (struct event_req *)req; + } else { + pr_err("%s():event req data is invalid\n", __func__); + return; + } + dma_unmap_single(&mhi_ctx->pdev->dev, ereq->dma, + ereq->dma_len, DMA_TO_DEVICE); +} + +/** + * mhi_dev_event_rd_offset_completion_cb() -CB function called by IPA driver + * when event rd_offset transfer is done. + * + * @req - event_req structure + */ + +static void mhi_dev_event_rd_offset_completion_cb(void *req) +{ + union mhi_dev_ring_ctx *ctx; + int rc = 0; + struct event_req *ereq = (struct event_req *)req; + struct mhi_dev_channel *ch = ereq->context; + struct mhi_dev *mhi = ch->ring->mhi_dev; + unsigned long flags; + + dma_unmap_single(&mhi_ctx->pdev->dev, ereq->event_rd_dma, + sizeof(uint64_t), DMA_TO_DEVICE); + ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[ereq->event_ring]; + rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec); + if (rc) + pr_err("%s: error sending in msi\n", __func__); + + /* return the event req to pre allocated pooled list */ + spin_lock_irqsave(&mhi->lock, flags); + list_add_tail(&ereq->list, &ch->event_req_buffers); + spin_unlock_irqrestore(&mhi->lock, flags); +} + +static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, + struct event_req *ereq, uint32_t evt_len) +{ + int rc = 0; + uint64_t evnt_ring_idx = mhi->ev_ring_start + evnt_ring; + struct mhi_dev_ring *ring = &mhi->ring[evnt_ring_idx]; + union mhi_dev_ring_ctx *ctx; + struct mhi_addr transfer_addr; + static int count; + + if (!ereq) { + pr_err("%s(): invalid event req\n", __func__); + return -EINVAL; + } + + if (count == 0) { + rc = ep_pcie_get_msi_config(mhi->phandle, &mhi->msi_cfg); + if (rc) { + pr_err("Error retrieving pcie msi logic\n"); + return rc; + } + count++; + } + + if (evnt_ring_idx > mhi->cfg.event_rings) { + pr_err("Invalid event ring idx: %lld\n", evnt_ring_idx); + return -EINVAL; + } + + ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[evnt_ring]; + if (mhi_ring_get_state(ring) == RING_STATE_UINT) { + rc = mhi_ring_start(ring, ctx, mhi); + if (rc) { + mhi_log(MHI_MSG_ERROR, + "error starting event ring %d\n", evnt_ring); + return rc; + } + } + + /* add the ring element */ + ereq->client_cb = mhi_dev_event_buf_completion_cb; + ereq->event_type = SEND_EVENT_BUFFER; + rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len); + if (rc) { + pr_err("%s(): error in adding element rc %d\n", __func__, rc); + return rc; + } + ring->ring_ctx_shadow->ev.rp = (ring->rd_offset * + sizeof(union mhi_dev_ring_element_type)) + + ring->ring_ctx->generic.rbase; + + mhi_log(MHI_MSG_VERBOSE, "ev.rp = %llx for %lld\n", + ring->ring_ctx_shadow->ev.rp, evnt_ring_idx); + + if (mhi->use_ipa) + transfer_addr.host_pa = (mhi->ev_ctx_shadow.host_pa + + sizeof(struct mhi_dev_ev_ctx) * + evnt_ring) + (uint32_t)&ring->ring_ctx->ev.rp - + (uint32_t)ring->ring_ctx; + else + transfer_addr.device_va = (mhi->ev_ctx_shadow.device_va + + sizeof(struct mhi_dev_ev_ctx) * + evnt_ring) + (uint32_t)&ring->ring_ctx->ev.rp - + (uint32_t)ring->ring_ctx; + + transfer_addr.virt_addr = &ring->ring_ctx_shadow->ev.rp; + transfer_addr.size = sizeof(uint64_t); + ereq->event_type = SEND_EVENT_RD_OFFSET; + ereq->client_cb = mhi_dev_event_rd_offset_completion_cb; + ereq->event_ring = evnt_ring; + mhi_dev_write_to_host(mhi, &transfer_addr, ereq, MHI_DEV_DMA_ASYNC); + return rc; +} + static int mhi_dev_send_completion_event(struct mhi_dev_channel *ch, uint32_t rd_ofst, uint32_t len, enum mhi_dev_cmd_completion_code code) @@ -724,7 +908,7 @@ static int mhi_dev_process_stop_cmd(struct mhi_dev_ring *ring, uint32_t ch_id, data_transfer.virt_addr = &mhi->ch_ctx_cache[ch_id].ch_state; /* update the channel state in the host */ - mhi_dev_write_to_host(mhi, &data_transfer); + mhi_dev_write_to_host(mhi, &data_transfer, NULL, MHI_DEV_DMA_SYNC); /* send the completion event to the host */ rc = mhi_dev_send_cmd_comp_event(mhi); @@ -800,7 +984,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi, host_addr.virt_addr = &mhi->ch_ctx_cache[ch_id].ch_state; host_addr.size = sizeof(enum mhi_dev_ch_ctx_state); - mhi_dev_write_to_host(mhi, &host_addr); + mhi_dev_write_to_host(mhi, &host_addr, NULL, MHI_DEV_DMA_SYNC); send_start_completion_event: rc = mhi_dev_send_cmd_comp_event(mhi); @@ -926,7 +1110,8 @@ send_start_completion_event: host_addr.size = sizeof(enum mhi_dev_ch_ctx_state); /* update the channel state in the host */ - mhi_dev_write_to_host(mhi, &host_addr); + mhi_dev_write_to_host(mhi, &host_addr, NULL, + MHI_DEV_DMA_SYNC); /* send the completion event to the host */ rc = mhi_dev_send_cmd_comp_event(mhi); @@ -1171,6 +1356,77 @@ static int mhi_dev_abort(struct mhi_dev *mhi) return rc; } +static void mhi_dev_transfer_completion_cb(void *mreq) +{ + struct mhi_dev_channel *ch; + struct mhi_dev_client *client; + union mhi_dev_ring_element_type *el; + int rc = 0; + struct mhi_req *req = (struct mhi_req *)mreq; + struct mhi_req *local_req = NULL; + union mhi_dev_ring_element_type *compl_ev = NULL; + struct mhi_dev *mhi = NULL; + unsigned long flags; + + client = req->client; + ch = client->channel; + mhi = ch->ring->mhi_dev; + el = req->el; + local_req = req; + ch->curr_ereq->context = ch; + + dma_unmap_single(&mhi_ctx->pdev->dev, req->dma, + req->len, DMA_FROM_DEVICE); + + /* Trigger client call back */ + req->client_cb(req); + + if (el->tre.ieot) { + compl_ev = ch->curr_ereq->tr_events + ch->curr_ereq->num_events; + compl_ev->evt_tr_comp.chid = ch->ch_id; + compl_ev->evt_tr_comp.type = + MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT; + compl_ev->evt_tr_comp.len = el->tre.len; + compl_ev->evt_tr_comp.code = MHI_CMD_COMPL_CODE_EOT; + compl_ev->evt_tr_comp.ptr = ch->ring->ring_ctx->generic.rbase + + local_req->rd_offset * TR_RING_ELEMENT_SZ; + ch->curr_ereq->num_events++; + + if (ch->curr_ereq->num_events >= MAX_TR_EVENTS || + local_req->snd_cmpl){ + mhi_log(MHI_MSG_VERBOSE, + "num of tr events %d for ch %d\n", + ch->curr_ereq->num_events, ch->ch_id); + rc = mhi_dev_send_multiple_tr_events(mhi, + mhi->ch_ctx_cache[ch->ch_id].err_indx, + ch->curr_ereq, (ch->curr_ereq->num_events* + sizeof(union mhi_dev_ring_element_type))); + if (rc) + mhi_log(MHI_MSG_ERROR, + "failed to send compl evts\n"); + if (!list_empty(&ch->event_req_buffers)) { + ch->curr_ereq = + container_of(ch->event_req_buffers.next, + struct event_req, list); + spin_lock_irqsave(&mhi->lock, flags); + list_del_init(&ch->curr_ereq->list); + spin_unlock_irqrestore(&mhi->lock, flags); + ch->curr_ereq->num_events = 0; + } else + pr_err("%s evt req buffers empty\n", __func__); + } + } else + mhi_log(MHI_MSG_ERROR, "ieot is not valid\n"); + + if (ch->state == MHI_DEV_CH_PENDING_STOP) { + ch->state = MHI_DEV_CH_STOPPED; + rc = mhi_dev_process_stop_cmd(ch->ring, ch->ch_id, mhi_ctx); + if (rc) + mhi_log(MHI_MSG_ERROR, + "Error while stopping channel (%d)\n", ch->ch_id); + } +} + static void mhi_dev_scheduler(struct work_struct *work) { struct mhi_dev *mhi = container_of(work, @@ -1481,7 +1737,8 @@ int mhi_dev_suspend(struct mhi_dev *mhi) data_transfer.virt_addr = &mhi->ch_ctx_cache[ch_id].ch_state; /* update the channel state in the host */ - mhi_dev_write_to_host(mhi, &data_transfer); + mhi_dev_write_to_host(mhi, &data_transfer, NULL, + MHI_DEV_DMA_SYNC); } @@ -1520,7 +1777,8 @@ int mhi_dev_resume(struct mhi_dev *mhi) data_transfer.virt_addr = &mhi->ch_ctx_cache[ch_id].ch_state; /* update the channel state in the host */ - mhi_dev_write_to_host(mhi, &data_transfer); + mhi_dev_write_to_host(mhi, &data_transfer, NULL, + MHI_DEV_DMA_SYNC); } mhi_update_state_info(MHI_STATE_CONNECTED); @@ -1691,22 +1949,21 @@ static int mhi_dev_check_tre_bytes_left(struct mhi_dev_channel *ch, return td_done; } -int mhi_dev_read_channel(struct mhi_dev_client *handle_client, - void *buf, uint32_t buf_size, uint32_t *chain) +int mhi_dev_read_channel(struct mhi_req *mreq) { struct mhi_dev_channel *ch; struct mhi_dev_ring *ring; union mhi_dev_ring_element_type *el; - uint32_t ch_id; size_t bytes_to_read, addr_offset; uint64_t read_from_loc; ssize_t bytes_read = 0; uint32_t write_to_loc = 0; - size_t usr_buf_remaining = buf_size; + size_t usr_buf_remaining; int td_done = 0, rc = 0; + struct mhi_dev_client *handle_client; - if (!handle_client) { - mhi_log(MHI_MSG_ERROR, "invalid client handle\n"); + if (!mreq) { + mhi_log(MHI_MSG_ERROR, "invalid mhi request\n"); return -ENXIO; } @@ -1715,10 +1972,15 @@ int mhi_dev_read_channel(struct mhi_dev_client *handle_client, return -ENODEV; } + if (!mreq->client) { + mhi_log(MHI_MSG_ERROR, "invalid mhi request\n"); + return -ENXIO; + } + handle_client = mreq->client; ch = handle_client->channel; + usr_buf_remaining = mreq->len; ring = ch->ring; - ch_id = ch->ch_id; - *chain = 0; + mreq->chain = 0; mutex_lock(&ch->ch_lock); @@ -1727,7 +1989,7 @@ int mhi_dev_read_channel(struct mhi_dev_client *handle_client, if (ch->tre_loc) { bytes_to_read = min(usr_buf_remaining, ch->tre_bytes_left); - *chain = 1; + mreq->chain = 1; mhi_log(MHI_MSG_VERBOSE, "remaining buffered data size %d\n", (int) ch->tre_bytes_left); @@ -1742,7 +2004,7 @@ int mhi_dev_read_channel(struct mhi_dev_client *handle_client, if (ch->state == MHI_DEV_CH_STOPPED) { mhi_log(MHI_MSG_VERBOSE, "channel (%d) already stopped\n", - ch_id); + mreq->chan); bytes_read = -1; goto exit; } @@ -1757,29 +2019,45 @@ int mhi_dev_read_channel(struct mhi_dev_client *handle_client, bytes_to_read = min(usr_buf_remaining, ch->tre_size); } + bytes_read += bytes_to_read; addr_offset = ch->tre_size - ch->tre_bytes_left; read_from_loc = ch->tre_loc + addr_offset; - write_to_loc = (uint32_t) buf + (buf_size - usr_buf_remaining); - - mhi_log(MHI_MSG_VERBOSE, "reading %d bytes from chan %d\n", - bytes_to_read, ch_id); - - mhi_transfer_host_to_device((void *) write_to_loc, - read_from_loc, bytes_to_read, mhi_ctx); - - bytes_read += bytes_to_read; + write_to_loc = (uint32_t) mreq->buf + + (mreq->len - usr_buf_remaining); ch->tre_bytes_left -= bytes_to_read; + mreq->el = el; + mreq->actual_len = bytes_read; + mreq->rd_offset = ring->rd_offset; + mhi_log(MHI_MSG_VERBOSE, "reading %d bytes from chan %d\n", + bytes_to_read, mreq->chan); + rc = mhi_transfer_host_to_device((void *) write_to_loc, + read_from_loc, bytes_to_read, mhi_ctx, mreq); + if (rc) { + mhi_log(MHI_MSG_ERROR, + "Error while reading chan (%d) rc %d\n", + mreq->chan, rc); + mutex_unlock(&ch->ch_lock); + return rc; + } usr_buf_remaining -= bytes_to_read; - td_done = mhi_dev_check_tre_bytes_left(ch, ring, el, chain); - } while (usr_buf_remaining && !td_done); + if (mreq->mode == IPA_DMA_ASYNC) { + ch->tre_bytes_left = 0; + ch->tre_loc = 0; + goto exit; + } else { + td_done = mhi_dev_check_tre_bytes_left(ch, ring, + el, &mreq->chain); + } + } while (usr_buf_remaining && !td_done); if (td_done && ch->state == MHI_DEV_CH_PENDING_STOP) { ch->state = MHI_DEV_CH_STOPPED; - rc = mhi_dev_process_stop_cmd(ring, ch_id, mhi_ctx); + rc = mhi_dev_process_stop_cmd(ring, mreq->chan , mhi_ctx); if (rc) { mhi_log(MHI_MSG_ERROR, - "Error while stopping channel (%d)\n", ch_id); - bytes_read = -1; + "Error while stopping channel (%d)\n", + mreq->chan); + bytes_read = -EIO; } } exit: @@ -1808,29 +2086,24 @@ static void skip_to_next_td(struct mhi_dev_channel *ch) } } -int mhi_dev_write_channel(struct mhi_dev_client *handle_client, - void *buf, size_t buf_size) +int mhi_dev_write_channel(struct mhi_req *wreq) { struct mhi_dev_channel *ch; struct mhi_dev_ring *ring; + struct mhi_dev_client *handle_client; union mhi_dev_ring_element_type *el; enum mhi_dev_cmd_completion_code code = MHI_CMD_COMPL_CODE_INVALID; int rc = 0; - uint64_t ch_id, skip_tres = 0, write_to_loc; + uint64_t skip_tres = 0, write_to_loc; uint32_t read_from_loc; - size_t usr_buf_remaining = buf_size; + size_t usr_buf_remaining; size_t usr_buf_offset = 0; size_t bytes_to_write = 0; size_t bytes_written = 0; uint32_t tre_len = 0, suspend_wait_timeout = 0; - if (!handle_client) { - pr_err("%s: invalid client handle\n", __func__); - return -ENXIO; - } - - if (!buf) { - pr_err("%s: invalid buffer to write data\n", __func__); + if (!wreq || !wreq->client || !wreq->buf) { + pr_err("%s: invalid parameters\n", __func__); return -ENXIO; } @@ -1839,6 +2112,7 @@ int mhi_dev_write_channel(struct mhi_dev_client *handle_client, return -ENODEV; } + usr_buf_remaining = wreq->len; mutex_lock(&mhi_ctx->mhi_write_test); if (atomic_read(&mhi_ctx->is_suspended)) { @@ -1854,31 +2128,29 @@ int mhi_dev_write_channel(struct mhi_dev_client *handle_client, } } - atomic_inc(&mhi_ctx->write_active); while (atomic_read(&mhi_ctx->is_suspended) && suspend_wait_timeout < MHI_SUSPEND_TIMEOUT) { /* wait for the suspend to finish */ msleep(MHI_SUSPEND_MIN); suspend_wait_timeout++; } - + handle_client = wreq->client; ch = handle_client->channel; ch->wr_request_active = true; ring = ch->ring; - ch_id = ch->ch_id; mutex_lock(&ch->ch_lock); if (ch->state == MHI_DEV_CH_STOPPED) { mhi_log(MHI_MSG_ERROR, - "channel (%lld) already stopped\n", ch_id); + "channel %d already stopped\n", wreq->chan); bytes_written = -1; goto exit; } if (ch->state == MHI_DEV_CH_PENDING_STOP) { - if (mhi_dev_process_stop_cmd(ring, ch_id, mhi_ctx) < 0) + if (mhi_dev_process_stop_cmd(ring, wreq->chan, mhi_ctx) < 0) bytes_written = -1; goto exit; } @@ -1888,20 +2160,38 @@ int mhi_dev_write_channel(struct mhi_dev_client *handle_client, do { if (ring->rd_offset == ring->wr_offset) { + mhi_log(MHI_MSG_ERROR, + "%s():rd & wr offsets are equal\n", + __func__); mhi_log(MHI_MSG_INFO, "No TREs available\n"); break; } el = &ring->ring_cache[ring->rd_offset]; tre_len = el->tre.len; + if (wreq->len > tre_len) { + pr_err("%s(): rlen = %d, tlen = %d: client buf > tre len\n", + __func__, wreq->len, tre_len); + bytes_written = -ENOMEM; + goto exit; + } bytes_to_write = min(usr_buf_remaining, tre_len); - usr_buf_offset = buf_size - bytes_to_write; - read_from_loc = (uint32_t) buf + usr_buf_offset; + usr_buf_offset = wreq->len - bytes_to_write; + read_from_loc = (uint32_t) wreq->buf + usr_buf_offset; write_to_loc = el->tre.data_buf_ptr; - mhi_transfer_device_to_host(write_to_loc, + wreq->rd_offset = ring->rd_offset; + wreq->el = el; + rc = mhi_transfer_device_to_host(write_to_loc, (void *) read_from_loc, - bytes_to_write, mhi_ctx); + bytes_to_write, + mhi_ctx, wreq); + if (rc) { + mhi_log(MHI_MSG_ERROR, + "Error while writing chan (%d) rc %d\n", + wreq->chan, rc); + goto exit; + } bytes_written += bytes_to_write; usr_buf_remaining -= bytes_to_write; @@ -1915,34 +2205,34 @@ int mhi_dev_write_channel(struct mhi_dev_client *handle_client, skip_tres = 1; code = MHI_CMD_COMPL_CODE_EOT; } - - if (mhi_dev_send_completion_event(ch, - ring->rd_offset, bytes_to_write, code) < 0) { - mhi_log(MHI_MSG_VERBOSE, - "error sending completion event ch_id:%lld\n", - ch_id); + if (wreq->mode == IPA_DMA_SYNC) { + rc = mhi_dev_send_completion_event(ch, + ring->rd_offset, bytes_to_write, code); + if (rc) + mhi_log(MHI_MSG_VERBOSE, + "err in snding cmpl evt ch:%d\n", + wreq->chan); + mhi_dev_ring_inc_index(ring, ring->rd_offset); } if (ch->state == MHI_DEV_CH_PENDING_STOP) break; - mhi_dev_ring_inc_index(ring, ring->rd_offset); } while (!skip_tres && usr_buf_remaining); if (skip_tres) skip_to_next_td(ch); if (ch->state == MHI_DEV_CH_PENDING_STOP) { - rc = mhi_dev_process_stop_cmd(ring, ch_id, mhi_ctx); + rc = mhi_dev_process_stop_cmd(ring, wreq->chan, mhi_ctx); if (rc) { mhi_log(MHI_MSG_ERROR, - "channel (%lld) stop failed\n", ch_id); + "channel %d stop failed\n", wreq->chan); } } exit: ch->wr_request_active = false; mutex_unlock(&ch->ch_lock); - atomic_dec(&mhi_ctx->write_active); mutex_unlock(&mhi_ctx->mhi_write_test); return bytes_written; } @@ -2238,7 +2528,6 @@ static int mhi_init(struct mhi_dev *mhi) int rc = 0, i = 0; struct platform_device *pdev = mhi->pdev; - rc = mhi_dev_mmio_init(mhi); if (rc) { pr_err("Failed to update the MMIO init\n"); @@ -2258,11 +2547,44 @@ static int mhi_init(struct mhi_dev *mhi) if (!mhi->ch) return -ENOMEM; - for (i = 0; i < mhi->cfg.channels; i++) + + for (i = 0; i < mhi->cfg.channels; i++) { mutex_init(&mhi->ch[i].ch_lock); + if (i == MHI_CLIENT_IP_SW_4_OUT || i == MHI_CLIENT_IP_SW_4_IN) { + int nreq = 0; + + INIT_LIST_HEAD(&mhi->ch[i].event_req_buffers); + while (nreq < MHI_MAX_EVT_REQ) { + struct event_req *ereq; + /* Pre allocate event requests */ + ereq = kzalloc(sizeof(struct event_req), + GFP_KERNEL); + if (!ereq) + return -ENOMEM; + + /* pre allocate buffers to queue + * transfer completion events + */ + ereq->tr_events = kzalloc(RING_ELEMENT_TYPE_SZ* + MAX_TR_EVENTS, GFP_KERNEL); + if (!ereq->tr_events) { + kfree(ereq); + return -ENOMEM; + } + list_add_tail(&ereq->list, + &mhi->ch[i].event_req_buffers); + nreq++; + } + mhi->ch[i].curr_ereq = + container_of(mhi->ch[i].event_req_buffers.next, + struct event_req, list); + list_del_init(&mhi->ch[i].curr_ereq->list); + } + } - mhi->mmio_backup = devm_kzalloc(&pdev->dev, MHI_DEV_MMIO_RANGE, - GFP_KERNEL); + spin_lock_init(&mhi->lock); + mhi->mmio_backup = devm_kzalloc(&pdev->dev, + MHI_DEV_MMIO_RANGE, GFP_KERNEL); if (!mhi->mmio_backup) return -ENOMEM; diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index 577ecb36a2c2..f35c3772c050 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -274,6 +274,13 @@ struct mhi_config { #define MHI_MASK_ROWS_CH_EV_DB 4 #define TRB_MAX_DATA_SIZE 8192 #define MHI_CTRL_STATE 25 +#define IPA_DMA_SYNC 1 +#define IPA_DMA_ASYNC 0 + +/*maximum trasnfer completion events buffer*/ +#define MAX_TR_EVENTS 50 +/*maximum event requests */ +#define MHI_MAX_EVT_REQ 50 /* Possible ring element types */ union mhi_dev_ring_element_type { @@ -358,6 +365,16 @@ enum mhi_ctrl_info { MHI_STATE_INVAL, }; +enum mhi_dev_tr_compl_evt_type { + SEND_EVENT_BUFFER, + SEND_EVENT_RD_OFFSET, +}; + +enum mhi_dev_transfer_type { + MHI_DEV_DMA_SYNC, + MHI_DEV_DMA_ASYNC, +}; + struct mhi_dev_channel; struct mhi_dev_ring { @@ -433,14 +450,30 @@ struct mhi_dev_client { uint32_t nr_iov; }; +struct ring_cache_req { + struct completion *done; + void *context; +}; + +struct event_req { + union mhi_dev_ring_element_type *tr_events; + u32 num_events; + dma_addr_t dma; + u32 dma_len; + dma_addr_t event_rd_dma; + void *context; + enum mhi_dev_tr_compl_evt_type event_type; + u32 event_ring; + void (*client_cb)(void *req); + struct list_head list; +}; + struct mhi_dev_channel { struct list_head list; struct list_head clients; /* synchronization for changing channel state, * adding/removing clients, mhi_dev callbacks, etc */ - spinlock_t lock; - struct mhi_dev_ring *ring; enum mhi_dev_channel_state state; @@ -450,6 +483,9 @@ struct mhi_dev_channel { /* client which the current inbound/outbound message is for */ struct mhi_dev_client *active_client; + struct list_head event_req_buffers; + struct event_req *curr_ereq; + /* current TRE being processed */ uint64_t tre_loc; /* current TRE size */ @@ -476,6 +512,7 @@ struct mhi_dev { struct mhi_config cfg; bool mmio_initialized; + spinlock_t lock; /* Host control base information */ struct mhi_host_addr host_addr; struct mhi_addr ctrl_base; @@ -502,6 +539,7 @@ struct mhi_dev { /* Scheduler work */ struct work_struct chdb_ctrl_work; + struct mutex mhi_lock; struct mutex mhi_event_lock; @@ -528,6 +566,7 @@ struct mhi_dev { u32 ifc_id; struct ep_pcie_hw *phandle; struct work_struct pcie_event; + struct ep_pcie_msi_config msi_cfg; atomic_t write_active; atomic_t is_suspended; @@ -566,6 +605,23 @@ struct mhi_dev { bool mhi_int; }; +struct mhi_req { + u32 chan; + u32 mode; + u32 chain; + void *buf; + dma_addr_t dma; + u32 snd_cmpl; + void *context; + size_t len; + size_t actual_len; + uint32_t rd_offset; + struct mhi_dev_client *client; + struct list_head list; + union mhi_dev_ring_element_type *el; + void (*client_cb)(void *req); +}; + enum mhi_msg_level { MHI_MSG_VERBOSE = 0x0, MHI_MSG_INFO = 0x1, @@ -674,24 +730,21 @@ int mhi_dev_close_channel(struct mhi_dev_client *handle_client); /** * mhi_dev_read_channel() - Channel read for a given client - * @handle_client: Client Handle issued during mhi_dev_open_channel - * @buf: Pointer to the buffer used by the MHI core to copy the data received - * from the Host. - * @buf_size: Size of the buffer pointer. - * @chain : Indicate if the recieved data is part of chained packet. + * @mreq: mreq is the client argument which includes meta info + * like write data location, buffer len, read offset, mode, + * chain and client call back function which will be invoked + * when data read is completed. */ -int mhi_dev_read_channel(struct mhi_dev_client *handle_client, - void *buf, uint32_t buf_size, uint32_t *chain); +int mhi_dev_read_channel(struct mhi_req *mreq); /** * mhi_dev_write_channel() - Channel write for a given software client. - * @handle_client: Client Handle issued during mhi_dev_open_channel - * @buf: Pointer to the buffer used by the MHI core to copy the data from the - * device to the host. - * @buf_size: Size of the buffer pointer. + * @wreq wreq is the client argument which includes meta info like + * client handle, read data location, buffer length, mode, + * and client call back function which will free the packet. + * when data write is completed. */ -int mhi_dev_write_channel(struct mhi_dev_client *handle_client, void *buf, - uint32_t buf_size); +int mhi_dev_write_channel(struct mhi_req *wreq); /** * mhi_dev_channel_isempty() - Checks if there is any pending TRE's to process. @@ -764,8 +817,8 @@ int mhi_dev_process_ring_element(struct mhi_dev_ring *ring, uint32_t offset); * @element: Transfer ring element to be copied to the host memory. */ int mhi_dev_add_element(struct mhi_dev_ring *ring, - union mhi_dev_ring_element_type *element); - + union mhi_dev_ring_element_type *element, + struct event_req *ereq, int evt_offset); /** * mhi_transfer_device_to_host() - memcpy equivalent API to transfer data * from device to the host. @@ -773,9 +826,10 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, * @src: Source virtual address. * @len: Numer of bytes to be transferred. * @mhi: MHI dev structure. + * @req: mhi_req structure */ int mhi_transfer_device_to_host(uint64_t dst_pa, void *src, uint32_t len, - struct mhi_dev *mhi); + struct mhi_dev *mhi, struct mhi_req *req); /** * mhi_transfer_host_to_dev() - memcpy equivalent API to transfer data @@ -784,9 +838,10 @@ int mhi_transfer_device_to_host(uint64_t dst_pa, void *src, uint32_t len, * @src_pa: Source physical address. * @len: Numer of bytes to be transferred. * @mhi: MHI dev structure. + * @req: mhi_req structure */ int mhi_transfer_host_to_device(void *device, uint64_t src_pa, uint32_t len, - struct mhi_dev *mhi); + struct mhi_dev *mhi, struct mhi_req *mreq); /** * mhi_dev_write_to_host() - Transfer data from device to host. @@ -795,9 +850,8 @@ int mhi_transfer_host_to_device(void *device, uint64_t src_pa, uint32_t len, * @buf: Data buffer that needs to be written to the host. * @size: Data buffer size. */ -void mhi_dev_write_to_host(struct mhi_dev *mhi, - struct mhi_addr *mhi_transfer); - +void mhi_dev_write_to_host(struct mhi_dev *mhi, struct mhi_addr *mhi_transfer, + struct event_req *ereq, enum mhi_dev_transfer_type type); /** * mhi_dev_read_from_host() - memcpy equivalent API to transfer data * from host to device. @@ -881,6 +935,7 @@ int mhi_dev_mmio_masked_read(struct mhi_dev *dev, uint32_t offset, * mhi_dev_mmio_enable_ctrl_interrupt() - Enable Control interrupt. * @dev: MHI device structure. */ + int mhi_dev_mmio_enable_ctrl_interrupt(struct mhi_dev *dev); /** @@ -1119,7 +1174,7 @@ int mhi_dev_send_ee_event(struct mhi_dev *mhi, /** * mhi_dev_syserr() - System error when unexpected events are received. * @dev: MHI device structure. - */ +*/ int mhi_dev_syserr(struct mhi_dev *mhi); /** @@ -1157,9 +1212,11 @@ int mhi_pcie_config_db_routing(struct mhi_dev *mhi); int mhi_uci_init(void); /** - * mhi_dev_net_interface_init() - Enable Network stack interface for MHI device - * which exposes the virtual network interface. - **/ + * mhi_dev_net_interface_init() - Initializes the mhi device network interface + * which exposes the virtual network interface (mhi_dev_net0). + * data packets will transfer between MHI host interface (mhi_swip) + * and mhi_dev_net interface using software path + */ int mhi_dev_net_interface_init(void); void mhi_dev_notify_a7_event(struct mhi_dev *mhi); diff --git a/drivers/platform/msm/mhi_dev/mhi_dev_net.c b/drivers/platform/msm/mhi_dev/mhi_dev_net.c index 088557b3556a..a1243271eb0c 100644 --- a/drivers/platform/msm/mhi_dev/mhi_dev_net.c +++ b/drivers/platform/msm/mhi_dev/mhi_dev_net.c @@ -30,8 +30,10 @@ #define MHI_NET_DRIVER_NAME "mhi_dev_net_drv" #define MHI_NET_DEV_NAME "mhi_dev_net%d" -#define MHI_NET_DEFAULT_MTU 4000 +#define MHI_NET_DEFAULT_MTU 8192 #define MHI_NET_IPC_PAGES (100) +#define MHI_MAX_RX_REQ (128) +#define MHI_MAX_TX_REQ (128) enum mhi_dev_net_dbg_lvl { MHI_VERBOSE = 0x1, @@ -44,7 +46,7 @@ enum mhi_dev_net_dbg_lvl { }; static enum mhi_dev_net_dbg_lvl mhi_net_msg_lvl = MHI_CRITICAL; -static enum mhi_dev_net_dbg_lvl mhi_net_ipc_log_lvl = MHI_INFO; +static enum mhi_dev_net_dbg_lvl mhi_net_ipc_log_lvl = MHI_VERBOSE; static void *mhi_net_ipc_log; enum mhi_chan_dir { @@ -92,16 +94,16 @@ struct mhi_dev_net_client { struct workqueue_struct *pending_pckt_wq; struct work_struct xmit_work; /*Read data from host work queue*/ - struct workqueue_struct *read_data_wq; - struct work_struct dev_read_wrk; - atomic_t pckt_queue_count; atomic_t rx_enabled; atomic_t tx_enabled; struct net_device *dev; struct sk_buff_head tx_buffers; + struct list_head rx_buffers; + struct list_head wr_req_buffers; struct mhi_dev_net_ctxt *net_ctxt; /*To check write channel is empty or not*/ - spinlock_t write_chan_lock; + spinlock_t wrt_lock; + spinlock_t rd_lock; struct mutex in_chan_lock; struct mutex out_chan_lock; }; @@ -115,17 +117,6 @@ struct mhi_dev_net_ctxt { static struct mhi_dev_net_ctxt mhi_net_ctxt; static ssize_t mhi_dev_net_client_read(struct mhi_dev_net_client *); -static void mhi_dev_net_rx_scheduler(struct work_struct *work) -{ - struct mhi_dev_net_client *mhi_dev_net_client = container_of(work, - struct mhi_dev_net_client, dev_read_wrk); - - if (mhi_dev_net_client) - mhi_dev_net_client_read(mhi_dev_net_client); - else - mhi_dev_net_log(MHI_CRITICAL, "mhi_dev_net client is NULL\n"); -} - static int mhi_dev_net_init_ch_attributes(struct mhi_dev_net_ctxt *mhi_ctxt) { u32 channel = 0; @@ -149,43 +140,61 @@ static int mhi_dev_net_init_ch_attributes(struct mhi_dev_net_ctxt *mhi_ctxt) return 0; } -static void process_queue_packets(struct work_struct *work) +static void mhi_dev_net_process_queue_packets(struct work_struct *work) { - u32 xfer_data = 0; - ktime_t start_time; - - struct mhi_dev_net_client *mhi_net_client = container_of(work, + struct mhi_dev_net_client *client = container_of(work, struct mhi_dev_net_client, xmit_work); - if (mhi_dev_channel_isempty(mhi_net_client->in_handle)) { - netif_stop_queue(mhi_net_client->dev); + unsigned long flags = 0; + int xfer_data = 0; + struct sk_buff *skb = NULL; + struct mhi_req *wreq = NULL; + + if (mhi_dev_channel_isempty(client->in_handle)) { + mhi_dev_net_log(MHI_INFO, "%s stop network xmmit\n", __func__); + netif_stop_queue(client->dev); return; } - while (!skb_queue_empty(&(mhi_net_client->tx_buffers))) { - struct sk_buff *skb = - skb_dequeue(&(mhi_net_client->tx_buffers)); - atomic_dec(&mhi_net_client->pckt_queue_count); + while (!((skb_queue_empty(&client->tx_buffers)) || + (list_empty(&client->wr_req_buffers)))) { + spin_lock_irqsave(&client->wrt_lock, flags); + skb = skb_dequeue(&(client->tx_buffers)); if (!skb) { - mhi_dev_net_log(MHI_CRITICAL, - "skb dequeue returned NULL\n"); + mhi_dev_net_log(MHI_INFO, + "SKB is NULL from dequeue\n"); + spin_unlock_irqrestore(&client->wrt_lock, flags); return; } - start_time = ktime_get(); - xfer_data = - mhi_dev_write_channel(mhi_net_client->in_handle, - skb->data, skb->len); - if (xfer_data != skb->len) { - pr_err("Failed to write skb len %d xfered data %d\n", - skb->len, xfer_data); + wreq = container_of(client->wr_req_buffers.next, + struct mhi_req, list); + list_del_init(&wreq->list); + + wreq->client = client->in_handle; + wreq->context = skb; + wreq->buf = skb->data; + wreq->len = skb->len; + wreq->chan = client->in_chan; + wreq->mode = IPA_DMA_ASYNC; + if (skb_queue_empty(&client->tx_buffers) || + list_empty(&client->wr_req_buffers)) { + wreq->snd_cmpl = 1; + } else + wreq->snd_cmpl = 0; + spin_unlock_irqrestore(&client->wrt_lock, flags); + xfer_data = mhi_dev_write_channel(wreq); + if (xfer_data <= 0) { + pr_err("%s(): Failed to write skb len %d\n", + __func__, skb->len); kfree_skb(skb); return; } - mhi_net_client->dev->stats.tx_packets++; - mhi_dev_net_log(MHI_VERBOSE, "write_chan time = %lld\n", - ktime_to_us(ktime_sub(ktime_get(), start_time))); - kfree_skb(skb); - /* Check if free buffers availability */ - if (mhi_dev_channel_isempty(mhi_net_client->in_handle)) { - netif_stop_queue(mhi_net_client->dev); + client->dev->stats.tx_packets++; + + /* Check if free buffers are available*/ + if (mhi_dev_channel_isempty(client->in_handle)) { + mhi_dev_net_log(MHI_INFO, + "%s buffers are full stop xmit\n", + __func__); + netif_stop_queue(client->dev); break; } } /* While TX queue is not empty */ @@ -193,26 +202,17 @@ static void process_queue_packets(struct work_struct *work) static void mhi_dev_net_event_notifier(struct mhi_dev_client_cb_reason *reason) { - struct mhi_dev_net_client *mhi_handle = NULL; + struct mhi_dev_net_client *client_handle = mhi_net_ctxt.client_handle; if (reason->reason == MHI_DEV_TRE_AVAILABLE) { - mhi_handle = mhi_net_ctxt.client_handle; - mhi_dev_net_log(MHI_VERBOSE, - "recived TRE available event for chan %d\n", - mhi_handle->in_chan); if (reason->ch_id % 2) { - spin_lock(&mhi_handle->write_chan_lock); - if (netif_queue_stopped(mhi_handle->dev)) { - if (atomic_read(&mhi_handle->pckt_queue_count)) - queue_work(mhi_handle->pending_pckt_wq, - &mhi_handle->xmit_work); - else - netif_wake_queue(mhi_handle->dev); + if (netif_queue_stopped(client_handle->dev)) { + netif_wake_queue(client_handle->dev); + queue_work(client_handle->pending_pckt_wq, + &client_handle->xmit_work); } - spin_unlock(&mhi_handle->write_chan_lock); } else - queue_work(mhi_handle->read_data_wq, - &mhi_handle->dev_read_wrk); + mhi_dev_net_client_read(client_handle); } } @@ -235,61 +235,144 @@ static __be16 mhi_dev_net_eth_type_trans(struct sk_buff *skb) return protocol; } +static void mhi_dev_net_read_completion_cb(void *req) +{ + struct mhi_dev_net_client *net_handle = + mhi_net_ctxt.client_handle; + struct mhi_req *mreq = + (struct mhi_req *)req; + struct sk_buff *skb = mreq->context; + unsigned long flags; + + skb->len = mreq->actual_len; + skb->protocol = + mhi_dev_net_eth_type_trans(skb); + skb_put(skb, mreq->actual_len); + net_handle->dev->stats.rx_packets++; + skb->dev = net_handle->dev; + netif_rx(skb); + spin_lock_irqsave(&net_handle->rd_lock, flags); + list_add_tail(&mreq->list, &net_handle->rx_buffers); + spin_unlock_irqrestore(&net_handle->rd_lock, flags); +} + static ssize_t mhi_dev_net_client_read(struct mhi_dev_net_client *mhi_handle) { int bytes_avail = 0; int ret_val = 0; u32 chan = 0; - uint32_t buf_size = TRB_MAX_DATA_SIZE; - uint32_t chained = 0; struct mhi_dev_client *client_handle = NULL; - struct sk_buff *skb_buff; - ktime_t start_time; + struct mhi_req *req; + struct sk_buff *skb; + unsigned long flags; client_handle = mhi_handle->out_handle; chan = mhi_handle->out_chan; if (!atomic_read(&mhi_handle->rx_enabled)) return -EPERM; - do { - start_time = ktime_get(); - skb_buff = alloc_skb(MHI_NET_DEFAULT_MTU, GFP_ATOMIC); - if (!skb_buff) { - mhi_dev_net_log(MHI_ERROR, - "Error while allocating skb\n"); - return -ENOMEM; + while (1) { + spin_lock_irqsave(&mhi_handle->rd_lock, flags); + if (list_empty(&mhi_handle->rx_buffers)) { + spin_unlock_irqrestore(&mhi_handle->rd_lock, flags); + break; } - bytes_avail = mhi_dev_read_channel(client_handle, - skb_buff->data, - buf_size, &chained); - mhi_dev_net_log(MHI_VERBOSE, - "dev_read_channel time = %lld\n", - ktime_to_us(ktime_sub(ktime_get(), start_time))); + + req = container_of(mhi_handle->rx_buffers.next, + struct mhi_req, list); + list_del_init(&req->list); + spin_unlock_irqrestore(&mhi_handle->rd_lock, flags); + skb = alloc_skb(MHI_NET_DEFAULT_MTU, GFP_ATOMIC); + if (skb == NULL) { + pr_err("%s(): skb alloc failed\n", __func__); + spin_lock_irqsave(&mhi_handle->rd_lock, flags); + list_add_tail(&req->list, &mhi_handle->rx_buffers); + spin_unlock_irqrestore(&mhi_handle->rd_lock, flags); + ret_val = -ENOMEM; + return ret_val; + } + + req->client = client_handle; + req->chan = chan; + req->buf = skb->data; + req->len = MHI_NET_DEFAULT_MTU; + req->context = skb; + req->mode = IPA_DMA_ASYNC; + bytes_avail = mhi_dev_read_channel(req); + if (bytes_avail < 0) { pr_err("Failed to read chan %d bytes_avail = %d\n", chan, bytes_avail); + spin_lock_irqsave(&mhi_handle->rd_lock, flags); + kfree_skb(skb); + list_add_tail(&req->list, &mhi_handle->rx_buffers); + spin_unlock_irqrestore(&mhi_handle->rd_lock, flags); ret_val = -EIO; - break; + return 0; } /* no data to send to network stack, break */ - if (!bytes_avail) - break; - - skb_buff->len = bytes_avail; - mhi_dev_net_log(MHI_VERBOSE, "reading frm chan %d buff size %d", - chan, buf_size); - mhi_dev_net_log(MHI_VERBOSE, "bytes_read %d chained %d", - bytes_avail, chained); - skb_buff->protocol = - mhi_dev_net_eth_type_trans(skb_buff); - skb_put(skb_buff, bytes_avail); - mhi_handle->dev->stats.rx_packets++; - skb_buff->dev = mhi_handle->dev; - start_time = ktime_get(); - netif_rx(skb_buff); - } while (1); + if (!bytes_avail) { + spin_lock_irqsave(&mhi_handle->rd_lock, flags); + kfree_skb(skb); + list_add_tail(&req->list, &mhi_handle->rx_buffers); + spin_unlock_irqrestore(&mhi_handle->rd_lock, flags); + return 0; + } + } /* coming out while only in case of no data or error */ - kfree_skb(skb_buff); - return ret_val; + return ret_val; + +} + +static void mhi_dev_net_write_completion_cb(void *req) +{ + struct mhi_dev_net_client *client_handle = mhi_net_ctxt.client_handle; + struct mhi_req *wreq = (struct mhi_req *)req; + struct sk_buff *skb = wreq->context; + unsigned long flags; + + kfree_skb(skb); + spin_lock_irqsave(&client_handle->wrt_lock, flags); + list_add_tail(&wreq->list, &client_handle->wr_req_buffers); + spin_unlock_irqrestore(&client_handle->wrt_lock, flags); +} + +static int mhi_dev_net_alloc_write_reqs(struct mhi_dev_net_client *client) +{ + int nreq = 0, rc = 0; + struct mhi_req *wreq; + + while (nreq < MHI_MAX_TX_REQ) { + wreq = kzalloc(sizeof(struct mhi_req), GFP_ATOMIC); + if (!wreq) + return -ENOMEM; + wreq->client_cb = mhi_dev_net_write_completion_cb; + list_add_tail(&wreq->list, &client->wr_req_buffers); + nreq++; + } + mhi_dev_net_log(MHI_INFO, + "mhi write reqs allocation success\n"); + return rc; + +} + +static int mhi_dev_net_alloc_read_reqs(struct mhi_dev_net_client *client) +{ + int nreq = 0, rc = 0; + struct mhi_req *mreq; + + while (nreq < MHI_MAX_RX_REQ) { + mreq = kzalloc(sizeof(struct mhi_req), GFP_ATOMIC); + if (!mreq) + return -ENOMEM; + mreq->len = TRB_MAX_DATA_SIZE; + mreq->client_cb = mhi_dev_net_read_completion_cb; + list_add_tail(&mreq->list, &client->rx_buffers); + nreq++; + } + mhi_dev_net_log(MHI_INFO, + "mhi read reqs allocation success\n"); + return rc; + } static int mhi_dev_net_open(struct net_device *dev) @@ -297,31 +380,33 @@ static int mhi_dev_net_open(struct net_device *dev) struct mhi_dev_net_client *mhi_dev_net_ptr = *(struct mhi_dev_net_client **)netdev_priv(dev); mhi_dev_net_log(MHI_INFO, - "%s mhi_net_dev interface is up for IN %d OUT %d\n", - __func__, mhi_dev_net_ptr->out_chan, + "mhi_net_dev interface is up for IN %d OUT %d\n", + mhi_dev_net_ptr->out_chan, mhi_dev_net_ptr->in_chan); netif_start_queue(dev); return 0; } -static int mhi_dev_net_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t mhi_dev_net_xmit(struct sk_buff *skb, struct net_device *dev) { struct mhi_dev_net_client *mhi_dev_net_ptr = *(struct mhi_dev_net_client **)netdev_priv(dev); + unsigned long flags; - mhi_dev_net_log(MHI_VERBOSE, "SKB received\n"); if (skb->len <= 0) { mhi_dev_net_log(MHI_ERROR, "Invalid skb received freeing skb\n"); kfree_skb(skb); return NETDEV_TX_OK; } + spin_lock_irqsave(&mhi_dev_net_ptr->wrt_lock, flags); skb_queue_tail(&(mhi_dev_net_ptr->tx_buffers), skb); - atomic_inc(&mhi_dev_net_ptr->pckt_queue_count); + spin_unlock_irqrestore(&mhi_dev_net_ptr->wrt_lock, flags); + queue_work(mhi_dev_net_ptr->pending_pckt_wq, &mhi_dev_net_ptr->xmit_work); - mhi_dev_net_log(MHI_VERBOSE, "Exiting from transmit function\n"); - return 0; + + return NETDEV_TX_OK; } static int mhi_dev_net_stop(struct net_device *dev) @@ -408,6 +493,8 @@ static int mhi_dev_net_open_channels(struct mhi_dev_net_client *client) { int rc = 0; int ret = 0; + struct list_head *cp, *q; + struct mhi_req *mreq; mhi_dev_net_log(MHI_DBG, "opening OUT %d IN %d channels\n", client->out_chan, @@ -442,6 +529,21 @@ static int mhi_dev_net_open_channels(struct mhi_dev_net_client *client) mutex_unlock(&client->out_chan_lock); mhi_dev_net_log(MHI_INFO, "IN %d, OUT %d channels are opened", client->in_chan, client->out_chan); + + INIT_LIST_HEAD(&client->rx_buffers); + INIT_LIST_HEAD(&client->wr_req_buffers); + /* pre allocate read request buffer */ + + ret = mhi_dev_net_alloc_read_reqs(client); + if (ret) { + pr_err("failed to allocate rx req buffers\n"); + goto rx_req_failed; + } + ret = mhi_dev_net_alloc_write_reqs(client); + if (ret) { + pr_err("failed to allocate write req buffers\n"); + goto tx_req_failed; + } if (atomic_read(&client->tx_enabled)) { ret = mhi_dev_net_enable_iface(client); if (ret < 0) @@ -449,10 +551,15 @@ static int mhi_dev_net_open_channels(struct mhi_dev_net_client *client) "failed to enable mhi_dev_net iface\n"); } return ret; +tx_req_failed: + list_for_each_safe(cp, q, &client->rx_buffers); + mreq = list_entry(cp, struct mhi_req, list); + list_del(cp); + kfree(mreq); +rx_req_failed: + mhi_dev_close_channel(client->in_handle); handle_in_err: mhi_dev_close_channel(client->out_handle); - mutex_unlock(&client->in_chan_lock); - mutex_unlock(&client->out_chan_lock); handle_not_rdy_err: mutex_unlock(&client->in_chan_lock); mutex_unlock(&client->out_chan_lock); @@ -488,7 +595,8 @@ static int mhi_dev_net_rgstr_client(struct mhi_dev_net_client *client, int idx) client->in_chan = idx + 1; mutex_init(&client->in_chan_lock); mutex_init(&client->out_chan_lock); - spin_lock_init(&client->write_chan_lock); + spin_lock_init(&client->wrt_lock); + spin_lock_init(&client->rd_lock); mhi_dev_net_log(MHI_INFO, "Registering out %d, In %d channels\n", client->out_chan, client->in_chan); @@ -517,12 +625,8 @@ int mhi_dev_net_interface_init(void) /*Process pending packet work queue*/ mhi_net_client->pending_pckt_wq = create_singlethread_workqueue("pending_xmit_pckt_wq"); - INIT_WORK(&mhi_net_client->xmit_work, process_queue_packets); - - /* read data from host when event trigger */ - mhi_net_client->read_data_wq = - create_singlethread_workqueue("dev_read_from_host_wq"); - INIT_WORK(&mhi_net_client->dev_read_wrk, mhi_dev_net_rx_scheduler); + INIT_WORK(&mhi_net_client->xmit_work, + mhi_dev_net_process_queue_packets); mhi_dev_net_log(MHI_INFO, "Registering for MHI transfer events from host\n"); diff --git a/drivers/platform/msm/mhi_dev/mhi_ring.c b/drivers/platform/msm/mhi_dev/mhi_ring.c index 88ed7af538a4..c4f2803dab83 100644 --- a/drivers/platform/msm/mhi_dev/mhi_ring.c +++ b/drivers/platform/msm/mhi_dev/mhi_ring.c @@ -79,7 +79,6 @@ int mhi_dev_fetch_ring_elements(struct mhi_dev_ring *ring, mhi_dev_read_from_host(ring->mhi_dev, &host_addr); } } - return 0; } @@ -266,10 +265,12 @@ int mhi_dev_process_ring(struct mhi_dev_ring *ring) EXPORT_SYMBOL(mhi_dev_process_ring); int mhi_dev_add_element(struct mhi_dev_ring *ring, - union mhi_dev_ring_element_type *element) + union mhi_dev_ring_element_type *element, + struct event_req *ereq, int evt_offset) { uint32_t old_offset = 0; struct mhi_addr host_addr; + uint32_t num_elem = 0; if (!ring || !element) { pr_err("%s: Invalid context\n", __func__); @@ -285,11 +286,18 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, old_offset = ring->rd_offset; - mhi_dev_ring_inc_index(ring, ring->rd_offset); + if (evt_offset) { + num_elem = evt_offset / + (sizeof(union mhi_dev_ring_element_type)); + ring->rd_offset += num_elem; + if (ring->rd_offset >= ring->ring_size) + ring->rd_offset -= ring->ring_size; + } else + mhi_dev_ring_inc_index(ring, ring->rd_offset); ring->ring_ctx->generic.rp = (ring->rd_offset * - sizeof(union mhi_dev_ring_element_type)) + - ring->ring_ctx->generic.rbase; + sizeof(union mhi_dev_ring_element_type)) + + ring->ring_ctx->generic.rbase; /* * Write the element, ring_base has to be the * iomap of the ring_base for memcpy @@ -303,14 +311,22 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, sizeof(union mhi_dev_ring_element_type) * old_offset; host_addr.virt_addr = element; - host_addr.size = sizeof(union mhi_dev_ring_element_type); + + if (evt_offset) + host_addr.size = evt_offset; + else + host_addr.size = sizeof(union mhi_dev_ring_element_type); mhi_log(MHI_MSG_VERBOSE, "adding element to ring (%d)\n", ring->id); mhi_log(MHI_MSG_VERBOSE, "rd_ofset %d\n", ring->rd_offset); mhi_log(MHI_MSG_VERBOSE, "type %d\n", element->generic.type); - mhi_dev_write_to_host(ring->mhi_dev, &host_addr); - + if (ereq) + mhi_dev_write_to_host(ring->mhi_dev, &host_addr, + ereq, MHI_DEV_DMA_ASYNC); + else + mhi_dev_write_to_host(ring->mhi_dev, &host_addr, + NULL, MHI_DEV_DMA_SYNC); return 0; } EXPORT_SYMBOL(mhi_dev_add_element); @@ -368,7 +384,6 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx, (union mhi_dev_ring_ctx *) (mhi->ch_ctx_shadow.device_va + (ring->id - mhi->ch_ring_start)*sizeof(union mhi_dev_ring_ctx)); - ring->ring_ctx_shadow = ring->ring_ctx; if (ring->type != RING_TYPE_ER) { diff --git a/drivers/platform/msm/mhi_dev/mhi_uci.c b/drivers/platform/msm/mhi_dev/mhi_uci.c index 834a3ad91a9d..41b1b03a37cc 100644 --- a/drivers/platform/msm/mhi_dev/mhi_uci.c +++ b/drivers/platform/msm/mhi_dev/mhi_uci.c @@ -198,6 +198,8 @@ static int mhi_uci_send_packet(struct mhi_dev_client **client_handle, void *buf, uintptr_t memcpy_result = 0; u32 data_inserted_so_far = 0; struct uci_client *uci_handle; + struct mhi_req ureq; + uci_handle = container_of(client_handle, struct uci_client, out_handle); @@ -220,9 +222,13 @@ static int mhi_uci_send_packet(struct mhi_dev_client **client_handle, void *buf, } else { data_loc = buf; } + ureq.client = *client_handle; + ureq.buf = data_loc; + ureq.len = size; + ureq.chan = uci_handle->out_chan; + ureq.mode = IPA_DMA_SYNC; - data_inserted_so_far = mhi_dev_write_channel(*client_handle, data_loc, - size); + data_inserted_so_far = mhi_dev_write_channel(&ureq); error_memcpy: kfree(data_loc); @@ -482,7 +488,7 @@ static ssize_t mhi_uci_ctrl_client_read(struct file *file, return size; } -static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, +static ssize_t mhi_uci_client_read(struct file *file, char __user *ubuf, size_t uspace_buf_size, loff_t *bytes_pending) { struct uci_client *uci_handle = NULL; @@ -490,39 +496,40 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, int bytes_avail = 0; int ret_val = 0; struct mutex *mutex; - u32 chan = 0; ssize_t bytes_copied = 0; u32 addr_offset = 0; - uint32_t buf_size; - uint32_t chained = 0; void *local_buf = NULL; + struct mhi_req ureq; - if (!file || !buf || !uspace_buf_size || + if (!file || !ubuf || !uspace_buf_size || !file->private_data) return -EINVAL; uci_handle = file->private_data; client_handle = uci_handle->in_handle; mutex = &uci_handle->in_chan_lock; - chan = uci_handle->in_chan; + ureq.chan = uci_handle->in_chan; mutex_lock(mutex); + ureq.client = client_handle; + ureq.buf = uci_handle->in_buf_list[0].addr; + ureq.len = uci_handle->in_buf_list[0].buf_size; + ureq.mode = IPA_DMA_SYNC; - local_buf = uci_handle->in_buf_list[0].addr; - buf_size = uci_handle->in_buf_list[0].buf_size; - - - uci_log(UCI_DBG_VERBOSE, "Client attempted read on chan %d\n", chan); + uci_log(UCI_DBG_VERBOSE, "Client attempted read on chan %d\n", + ureq.chan); do { if (!uci_handle->pkt_loc && !atomic_read(&uci_ctxt.mhi_disabled)) { - bytes_avail = mhi_dev_read_channel(client_handle, - local_buf, buf_size, &chained); + bytes_avail = mhi_dev_read_channel(&ureq); uci_log(UCI_DBG_VERBOSE, - "reading from mhi_core local_buf = %p,buf_size = 0x%x bytes_read = 0x%x\n", - local_buf, buf_size, bytes_avail); + "reading from mhi_core local_buf = %p", + local_buf); + uci_log(UCI_DBG_VERBOSE, + "buf_size = 0x%x bytes_read = 0x%x\n", + ureq.len, bytes_avail); if (bytes_avail < 0) { uci_log(UCI_DBG_ERROR, @@ -533,13 +540,14 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, } if (bytes_avail > 0) { - uci_handle->pkt_loc = (void *)local_buf; - uci_handle->pkt_size = bytes_avail; + uci_handle->pkt_loc = (void *) ureq.buf; + uci_handle->pkt_size = ureq.actual_len; *bytes_pending = (loff_t)uci_handle->pkt_size; uci_log(UCI_DBG_VERBOSE, - "Got pkt of size 0x%x at addr %p, chan %d\n", - uci_handle->pkt_size, local_buf, chan); + "Got pkt of sz 0x%x at adr %p, ch %d\n", + uci_handle->pkt_size, + ureq.buf, ureq.chan); } else { uci_handle->pkt_loc = 0; uci_handle->pkt_size = 0; @@ -551,7 +559,7 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, uci_log(UCI_DBG_VERBOSE, "No data read_data_ready %d, chan %d\n", atomic_read(&uci_handle->read_data_ready), - chan); + ureq.chan); ret_val = wait_event_interruptible(uci_handle->read_wq, (!mhi_dev_channel_isempty(client_handle))); @@ -561,17 +569,17 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, goto error; } uci_log(UCI_DBG_VERBOSE, - "Thread woke up. Got data on chan %d read_data_ready %d\n", - chan, + "wk up Got data on ch %d read_data_ready %d\n", + ureq.chan, atomic_read(&uci_handle->read_data_ready)); /* A valid packet was returned from MHI */ } else if (bytes_avail > 0) { uci_log(UCI_DBG_VERBOSE, - "Got packet: avail pkts %d phy_adr %p, chan %d\n", + "Got packet: avail pkts %d phy_adr %p, ch %d\n", atomic_read(&uci_handle->read_data_ready), - local_buf, - chan); + ureq.buf, + ureq.chan); break; /* * MHI did not return a valid packet, but we have one @@ -580,16 +588,16 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, } else { uci_log(UCI_DBG_CRITICAL, "chan %d err: avail pkts %d phy_adr %p", - chan, + ureq.chan, atomic_read(&uci_handle->read_data_ready), - local_buf); + ureq.buf); return -EIO; } } while (!uci_handle->pkt_loc); if (uspace_buf_size >= *bytes_pending) { addr_offset = uci_handle->pkt_size - *bytes_pending; - if (copy_to_user(buf, uci_handle->pkt_loc + addr_offset, + if (copy_to_user(ubuf, uci_handle->pkt_loc + addr_offset, *bytes_pending)) { ret_val = -EIO; goto error; @@ -598,10 +606,10 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, bytes_copied = *bytes_pending; *bytes_pending = 0; uci_log(UCI_DBG_VERBOSE, "Copied 0x%x of 0x%x, chan %d\n", - bytes_copied, (u32)*bytes_pending, chan); + bytes_copied, (u32)*bytes_pending, ureq.chan); } else { addr_offset = uci_handle->pkt_size - *bytes_pending; - if (copy_to_user(buf, (void *) (uintptr_t)uci_handle->pkt_loc + + if (copy_to_user(ubuf, (void *) (uintptr_t)uci_handle->pkt_loc + addr_offset, uspace_buf_size)) { ret_val = -EIO; goto error; @@ -611,13 +619,13 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, uci_log(UCI_DBG_VERBOSE, "Copied 0x%x of 0x%x,chan %d\n", bytes_copied, (u32)*bytes_pending, - chan); + ureq.chan); } /* We finished with this buffer, map it back */ if (*bytes_pending == 0) { uci_log(UCI_DBG_VERBOSE, "All data consumed. Pkt loc %p ,chan %d\n", - uci_handle->pkt_loc, chan); + uci_handle->pkt_loc, ureq.chan); uci_handle->pkt_loc = 0; uci_handle->pkt_size = 0; } -- GitLab From 61a22e720ed6fc041cad108c429fd17343fd6f2e Mon Sep 17 00:00:00 2001 From: David Dai Date: Tue, 19 Dec 2017 14:50:41 -0800 Subject: [PATCH 2004/5498] msm: msm_bus: Disallow single node paths Reject clients that attempt to register paths that have the same source and destination. Add additional null ptr checks for bus nodes that have no fabrice devices attached to them. Change-Id: I13320371b2b19f6047853ffc7ffe529c0bfbd620 Signed-off-by: David Dai --- drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c b/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c index 6d9dc58cd5df..486e017b8328 100644 --- a/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c +++ b/drivers/platform/msm/msm_bus/msm_bus_arb_adhoc.c @@ -552,7 +552,8 @@ static uint64_t aggregate_bus_req(struct msm_bus_node_device_type *bus_dev, uint64_t max_ab = 0; uint64_t sum_ab = 0; - if (!bus_dev || !to_msm_bus_node(bus_dev->node_info->bus_device)) { + if (!bus_dev || !bus_dev->node_info->bus_device || + !to_msm_bus_node(bus_dev->node_info->bus_device)) { MSM_BUS_ERR("Bus node pointer is Invalid"); goto exit_agg_bus_req; } @@ -1016,7 +1017,7 @@ static uint32_t register_client_adhoc(struct msm_bus_scale_pdata *pdata) src = pdata->usecase->vectors[i].src; dest = pdata->usecase->vectors[i].dst; - if ((src < 0) || (dest < 0)) { + if ((src < 0) || (dest < 0) || (src == dest)) { MSM_BUS_ERR("%s:Invalid src/dst.src %d dest %d", __func__, src, dest); goto exit_invalid_data; -- GitLab From c56d354cf3b0f4c578675c08155c2399a47b75ae Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Tue, 28 Nov 2017 15:15:20 -0800 Subject: [PATCH 2005/5498] msm: mhi_dev: Update channel state on start command After receiving channel start command update channel state. This prevents channel read to fail after a channel reset followed by start command. Change-Id: Id534a4c65e8f5bba02fff814c1d763635ff9fb7e Signed-off-by: Siddartha Mohanadoss --- drivers/platform/msm/mhi_dev/mhi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index 6d7ea7d9d8b0..e949e20c13ec 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -779,6 +779,7 @@ static void mhi_dev_process_cmd_ring(struct mhi_dev *mhi, /* set the channel to running */ mhi->ch_ctx_cache[ch_id].ch_state = MHI_DEV_CH_STATE_RUNNING; + mhi->ch[ch_id].state = MHI_DEV_CH_STARTED; mhi->ch[ch_id].ch_id = ch_id; mhi->ch[ch_id].ring = &mhi->ring[mhi->ch_ring_start + ch_id]; mhi->ch[ch_id].ch_type = mhi->ch_ctx_cache[ch_id].ch_type; -- GitLab From 4a3e9d4dc88603e7630f0c5fe8ac19ac22427adc Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Fri, 13 Oct 2017 11:00:08 -0700 Subject: [PATCH 2006/5498] msm: mhi_dev: Add UCI channel Add MBIM channels 12 and 13 among the UCI channel lists so these are exposed as device nodes for MBIM clients to transfer data. Change-Id: I1f8dbc94cde39adc3a13297c4ceb2060e7991d81 Signed-off-by: Siddartha Mohanadoss --- drivers/platform/msm/mhi_dev/mhi_uci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/msm/mhi_dev/mhi_uci.c b/drivers/platform/msm/mhi_dev/mhi_uci.c index 834a3ad91a9d..33368192009d 100644 --- a/drivers/platform/msm/mhi_dev/mhi_uci.c +++ b/drivers/platform/msm/mhi_dev/mhi_uci.c @@ -702,6 +702,8 @@ static int uci_init_client_attributes(struct mhi_uci_ctxt_t *uci_ctxt) case MHI_CLIENT_SAHARA_IN: case MHI_CLIENT_EFS_OUT: case MHI_CLIENT_EFS_IN: + case MHI_CLIENT_MBIM_OUT: + case MHI_CLIENT_MBIM_IN: case MHI_CLIENT_QMI_OUT: case MHI_CLIENT_QMI_IN: case MHI_CLIENT_IP_CTRL_0_OUT: -- GitLab From ae983ddc5ca078e8afadc1ca25484c329a016eb7 Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Thu, 30 Nov 2017 16:43:41 +0530 Subject: [PATCH 2007/5498] leds: qpnp-wled: Disable the current sink conditionally Disable the current sinks only if there is any change in the configuration. Do not disable otherwise. This can avoid the unnecessary flicker seen on the display during the bootup. Change-Id: I2bcf6b542eec7c55f957cc62e98d85da9e7f6b40 Signed-off-by: Kiran Gunda --- drivers/leds/leds-qpnp-wled.c | 254 +++++++++++++++++++++------------- 1 file changed, 160 insertions(+), 94 deletions(-) diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index 9fbab4e45d58..094d90a2e79f 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -997,6 +997,162 @@ static irqreturn_t qpnp_wled_sc_irq(int irq, void *_wled) return IRQ_HANDLED; } +static int qpnp_wled_sink_config(struct qpnp_wled *wled) +{ + int rc, i, temp; + u8 reg = 0, sink_reg = 0, sink_cfg = 0, fs_reg = 0, fs_temp = 0; + u8 mod_reg = 0, mod_temp = 0, delay_reg = 0, delay_temp = 0, val = 0; + bool module_enable; + + rc = qpnp_wled_read_reg(wled, ®, + QPNP_WLED_MODULE_EN_REG(wled->ctrl_base)); + if (rc < 0) + return rc; + + module_enable = !!(reg & BIT(7)); + + rc = qpnp_wled_read_reg(wled, &sink_reg, + QPNP_WLED_CURR_SINK_REG(wled->sink_base)); + if (rc < 0) + return rc; + + for (i = 0; i < wled->num_strings; i++) { + if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) { + dev_err(&wled->spmi->dev, "Invalid string number\n"); + return -EINVAL; + } + + /* MODULATOR */ + rc = qpnp_wled_read_reg(wled, &mod_reg, + QPNP_WLED_MOD_EN_REG(wled->sink_base, + wled->strings[i])); + if (rc < 0) + return rc; + + mod_temp = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT); + + if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) + mod_temp &= QPNP_WLED_GATE_DRV_MASK; + else + mod_temp |= ~QPNP_WLED_GATE_DRV_MASK; + + /* SYNC DELAY */ + if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US) + wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US; + + rc = qpnp_wled_read_reg(wled, &delay_reg, + QPNP_WLED_SYNC_DLY_REG(wled->sink_base, + wled->strings[i]) + ); + if (rc < 0) + return rc; + + delay_temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US; + + /* FULL SCALE CURRENT */ + if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA) + wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA; + + rc = qpnp_wled_read_reg(wled, &fs_reg, + QPNP_WLED_FS_CURR_REG(wled->sink_base, + wled->strings[i]) + ); + if (rc < 0) + return rc; + + fs_temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA; + + if (mod_reg != mod_temp || + (delay_reg & ~QPNP_WLED_SYNC_DLY_MASK) != delay_temp || + (fs_reg & ~QPNP_WLED_FS_CURR_MASK) != fs_temp) { + if (module_enable) { + /* Disable module */ + rc = qpnp_wled_module_en(wled, wled->ctrl_base, + false); + if (rc < 0) + return rc; + + module_enable = 0; + + val = 0; + /* Disable all the sinks */ + rc = qpnp_wled_write_reg(wled, &val, + QPNP_WLED_CURR_SINK_REG + (wled->sink_base)); + if (rc < 0) + return rc; + } + + rc = qpnp_wled_write_reg(wled, &mod_temp, + QPNP_WLED_MOD_EN_REG( + wled->sink_base, + wled->strings[i])); + if (rc < 0) + return rc; + + delay_reg &= QPNP_WLED_SYNC_DLY_MASK; + delay_reg |= delay_temp; + rc = qpnp_wled_write_reg(wled, &delay_reg, + QPNP_WLED_SYNC_DLY_REG( + wled->sink_base, + wled->strings[i])); + if (rc < 0) + return rc; + + fs_reg &= QPNP_WLED_FS_CURR_MASK; + fs_reg |= fs_temp; + rc = qpnp_wled_write_reg(wled, &fs_reg, + QPNP_WLED_FS_CURR_REG( + wled->sink_base, + wled->strings[i])); + if (rc < 0) + return rc; + } + + /* CABC */ + rc = qpnp_wled_read_reg(wled, ®, + QPNP_WLED_CABC_REG(wled->sink_base, + wled->strings[i])); + if (rc < 0) + return rc; + + reg &= QPNP_WLED_CABC_MASK; + reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT); + rc = qpnp_wled_write_reg(wled, ®, + QPNP_WLED_CABC_REG(wled->sink_base, + wled->strings[i])); + if (rc < 0) + return rc; + + temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT; + sink_cfg |= (1 << temp); + } + + if (sink_reg != sink_cfg) { + if (module_enable) { + /* Disable module */ + rc = qpnp_wled_module_en(wled, wled->ctrl_base, false); + if (rc < 0) + return rc; + + module_enable = 0; + } + + /* Disable all the sinks */ + rc = qpnp_wled_write_reg(wled, &sink_cfg, + QPNP_WLED_CURR_SINK_REG( + wled->sink_base)); + if (rc < 0) + return rc; + } + + /* Enable module */ + if (!module_enable) + rc = qpnp_wled_module_en(wled, wled->ctrl_base, true); + + return rc; +} + /* Configure WLED registers */ static int qpnp_wled_config(struct qpnp_wled *wled) { @@ -1258,99 +1414,9 @@ static int qpnp_wled_config(struct qpnp_wled *wled) if (rc) return rc; - /* disable all current sinks and enable selected strings */ - reg = 0x00; - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_CURR_SINK_REG(wled->sink_base)); - - for (i = 0; i < wled->num_strings; i++) { - if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) { - dev_err(&wled->spmi->dev, "Invalid string number\n"); - return -EINVAL; - } - - /* MODULATOR */ - rc = qpnp_wled_read_reg(wled, ®, - QPNP_WLED_MOD_EN_REG(wled->sink_base, - wled->strings[i])); - if (rc < 0) - return rc; - reg &= QPNP_WLED_MOD_EN_MASK; - reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT); - - if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) - reg &= QPNP_WLED_GATE_DRV_MASK; - else - reg |= ~QPNP_WLED_GATE_DRV_MASK; - - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_MOD_EN_REG(wled->sink_base, - wled->strings[i])); - if (rc) - return rc; - - /* SYNC DELAY */ - if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US) - wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US; - - rc = qpnp_wled_read_reg(wled, ®, - QPNP_WLED_SYNC_DLY_REG(wled->sink_base, - wled->strings[i])); - if (rc < 0) - return rc; - reg &= QPNP_WLED_SYNC_DLY_MASK; - temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US; - reg |= temp; - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_SYNC_DLY_REG(wled->sink_base, - wled->strings[i])); - if (rc) - return rc; - - /* FULL SCALE CURRENT */ - if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA) - wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA; - - rc = qpnp_wled_read_reg(wled, ®, - QPNP_WLED_FS_CURR_REG(wled->sink_base, - wled->strings[i])); - if (rc < 0) - return rc; - reg &= QPNP_WLED_FS_CURR_MASK; - temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA; - reg |= temp; - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_FS_CURR_REG(wled->sink_base, - wled->strings[i])); - if (rc) - return rc; - - /* CABC */ - rc = qpnp_wled_read_reg(wled, ®, - QPNP_WLED_CABC_REG(wled->sink_base, - wled->strings[i])); - if (rc < 0) - return rc; - reg &= QPNP_WLED_CABC_MASK; - reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT); - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_CABC_REG(wled->sink_base, - wled->strings[i])); - if (rc) - return rc; - - /* Enable CURRENT SINK */ - rc = qpnp_wled_read_reg(wled, ®, - QPNP_WLED_CURR_SINK_REG(wled->sink_base)); - if (rc < 0) - return rc; - temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT; - reg |= (1 << temp); - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_CURR_SINK_REG(wled->sink_base)); - if (rc) - return rc; - } + rc = qpnp_wled_sink_config(wled); + if (rc < 0) + return rc; rc = qpnp_wled_sync_reg_toggle(wled); if (rc < 0) { -- GitLab From 97017d59158086689488bdcfcafb59654a6f10da Mon Sep 17 00:00:00 2001 From: Tharun Kumar Merugu Date: Sat, 9 Dec 2017 21:39:29 +0530 Subject: [PATCH 2008/5498] msm: ADSPRPC: use access_ok to validate pointers Check the validity of the pointer in user space that you intend to access. access_ok function simply checks that the address is likely in user space, not in the kernel. Change-Id: I936f73a2c2029f9e7ca12cc8fc06d0698e6710c0 Signed-off-by: Tharun Kumar Merugu --- drivers/char/adsprpc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 58f7455952d2..7b6438d0fb57 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1443,6 +1443,10 @@ static int fastrpc_init_process(struct fastrpc_file *fl, inbuf.pgid = current->tgid; inbuf.namelen = strlen(current->comm) + 1; inbuf.filelen = init->filelen; + VERIFY(err, access_ok(0, (void __user *)init->file, + init->filelen)); + if (err) + goto bail; if (init->filelen) { VERIFY(err, !fastrpc_mmap_create(fl, init->filefd, init->file, init->filelen, mflags, &file)); @@ -1450,6 +1454,10 @@ static int fastrpc_init_process(struct fastrpc_file *fl, goto bail; } inbuf.pageslen = 1; + VERIFY(err, access_ok(1, (void __user *)init->mem, + init->memlen)); + if (err) + goto bail; VERIFY(err, !fastrpc_mmap_create(fl, init->memfd, init->mem, init->memlen, mflags, &mem)); if (err) -- GitLab From 252b343a9789151293ad1da4a1ac0851bf31a22e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 28 Nov 2017 18:01:38 -0800 Subject: [PATCH 2009/5498] crypto: hmac - require that the underlying hash algorithm is unkeyed commit af3ff8045bbf3e32f1a448542e73abb4c8ceb6f1 upstream. Because the HMAC template didn't check that its underlying hash algorithm is unkeyed, trying to use "hmac(hmac(sha3-512-generic))" through AF_ALG or through KEYCTL_DH_COMPUTE resulted in the inner HMAC being used without having been keyed, resulting in sha3_update() being called without sha3_init(), causing a stack buffer overflow. This is a very old bug, but it seems to have only started causing real problems when SHA-3 support was added (requires CONFIG_CRYPTO_SHA3) because the innermost hash's state is ->import()ed from a zeroed buffer, and it just so happens that other hash algorithms are fine with that, but SHA-3 is not. However, there could be arch or hardware-dependent hash algorithms also affected; I couldn't test everything. Fix the bug by introducing a function crypto_shash_alg_has_setkey() which tests whether a shash algorithm is keyed. Then update the HMAC template to require that its underlying hash algorithm is unkeyed. Here is a reproducer: #include #include int main() { int algfd; struct sockaddr_alg addr = { .salg_type = "hash", .salg_name = "hmac(hmac(sha3-512-generic))", }; char key[4096] = { 0 }; algfd = socket(AF_ALG, SOCK_SEQPACKET, 0); bind(algfd, (const struct sockaddr *)&addr, sizeof(addr)); setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key)); } Here was the KASAN report from syzbot: BUG: KASAN: stack-out-of-bounds in memcpy include/linux/string.h:341 [inline] BUG: KASAN: stack-out-of-bounds in sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161 Write of size 4096 at addr ffff8801cca07c40 by task syzkaller076574/3044 CPU: 1 PID: 3044 Comm: syzkaller076574 Not tainted 4.14.0-mm1+ #25 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:17 [inline] dump_stack+0x194/0x257 lib/dump_stack.c:53 print_address_description+0x73/0x250 mm/kasan/report.c:252 kasan_report_error mm/kasan/report.c:351 [inline] kasan_report+0x25b/0x340 mm/kasan/report.c:409 check_memory_region_inline mm/kasan/kasan.c:260 [inline] check_memory_region+0x137/0x190 mm/kasan/kasan.c:267 memcpy+0x37/0x50 mm/kasan/kasan.c:303 memcpy include/linux/string.h:341 [inline] sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161 crypto_shash_update+0xcb/0x220 crypto/shash.c:109 shash_finup_unaligned+0x2a/0x60 crypto/shash.c:151 crypto_shash_finup+0xc4/0x120 crypto/shash.c:165 hmac_finup+0x182/0x330 crypto/hmac.c:152 crypto_shash_finup+0xc4/0x120 crypto/shash.c:165 shash_digest_unaligned+0x9e/0xd0 crypto/shash.c:172 crypto_shash_digest+0xc4/0x120 crypto/shash.c:186 hmac_setkey+0x36a/0x690 crypto/hmac.c:66 crypto_shash_setkey+0xad/0x190 crypto/shash.c:64 shash_async_setkey+0x47/0x60 crypto/shash.c:207 crypto_ahash_setkey+0xaf/0x180 crypto/ahash.c:200 hash_setkey+0x40/0x90 crypto/algif_hash.c:446 alg_setkey crypto/af_alg.c:221 [inline] alg_setsockopt+0x2a1/0x350 crypto/af_alg.c:254 SYSC_setsockopt net/socket.c:1851 [inline] SyS_setsockopt+0x189/0x360 net/socket.c:1830 entry_SYSCALL_64_fastpath+0x1f/0x96 Reported-by: syzbot Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/hmac.c | 6 +++++- crypto/shash.c | 5 +++-- include/crypto/internal/hash.h | 8 ++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/crypto/hmac.c b/crypto/hmac.c index 72e38c098bb3..ba07fb6221ae 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -194,11 +194,15 @@ static int hmac_create(struct crypto_template *tmpl, struct rtattr **tb) salg = shash_attr_alg(tb[1], 0, 0); if (IS_ERR(salg)) return PTR_ERR(salg); + alg = &salg->base; + /* The underlying hash algorithm must be unkeyed */ err = -EINVAL; + if (crypto_shash_alg_has_setkey(salg)) + goto out_put_alg; + ds = salg->digestsize; ss = salg->statesize; - alg = &salg->base; if (ds > alg->cra_blocksize || ss < alg->cra_blocksize) goto out_put_alg; diff --git a/crypto/shash.c b/crypto/shash.c index 17510eaf0a36..73c065321867 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -24,11 +24,12 @@ static const struct crypto_type crypto_shash_type; -static int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, - unsigned int keylen) +int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, + unsigned int keylen) { return -ENOSYS; } +EXPORT_SYMBOL_GPL(shash_no_setkey); static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key, unsigned int keylen) diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index a25414ce2898..9779c35f8454 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -83,6 +83,14 @@ int ahash_register_instance(struct crypto_template *tmpl, struct ahash_instance *inst); void ahash_free_instance(struct crypto_instance *inst); +int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, + unsigned int keylen); + +static inline bool crypto_shash_alg_has_setkey(struct shash_alg *alg) +{ + return alg->setkey != shash_no_setkey; +} + int crypto_init_ahash_spawn(struct crypto_ahash_spawn *spawn, struct hash_alg_common *alg, struct crypto_instance *inst); -- GitLab From ebd52f8b6422b920b4d1697d90679a2bb4b48a0b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 28 Nov 2017 20:56:59 -0800 Subject: [PATCH 2010/5498] crypto: salsa20 - fix blkcipher_walk API usage commit ecaaab5649781c5a0effdaf298a925063020500e upstream. When asked to encrypt or decrypt 0 bytes, both the generic and x86 implementations of Salsa20 crash in blkcipher_walk_done(), either when doing 'kfree(walk->buffer)' or 'free_page((unsigned long)walk->page)', because walk->buffer and walk->page have not been initialized. The bug is that Salsa20 is calling blkcipher_walk_done() even when nothing is in 'walk.nbytes'. But blkcipher_walk_done() is only meant to be called when a nonzero number of bytes have been provided. The broken code is part of an optimization that tries to make only one call to salsa20_encrypt_bytes() to process inputs that are not evenly divisible by 64 bytes. To fix the bug, just remove this "optimization" and use the blkcipher_walk API the same way all the other users do. Reproducer: #include #include #include int main() { int algfd, reqfd; struct sockaddr_alg addr = { .salg_type = "skcipher", .salg_name = "salsa20", }; char key[16] = { 0 }; algfd = socket(AF_ALG, SOCK_SEQPACKET, 0); bind(algfd, (void *)&addr, sizeof(addr)); reqfd = accept(algfd, 0, 0); setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key)); read(reqfd, key, sizeof(key)); } Reported-by: syzbot Fixes: eb6f13eb9f81 ("[CRYPTO] salsa20_generic: Fix multi-page processing") Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- arch/x86/crypto/salsa20_glue.c | 7 ------- crypto/salsa20_generic.c | 7 ------- 2 files changed, 14 deletions(-) diff --git a/arch/x86/crypto/salsa20_glue.c b/arch/x86/crypto/salsa20_glue.c index 399a29d067d6..cb91a64a99e7 100644 --- a/arch/x86/crypto/salsa20_glue.c +++ b/arch/x86/crypto/salsa20_glue.c @@ -59,13 +59,6 @@ static int encrypt(struct blkcipher_desc *desc, salsa20_ivsetup(ctx, walk.iv); - if (likely(walk.nbytes == nbytes)) - { - salsa20_encrypt_bytes(ctx, walk.src.virt.addr, - walk.dst.virt.addr, nbytes); - return blkcipher_walk_done(desc, &walk, 0); - } - while (walk.nbytes >= 64) { salsa20_encrypt_bytes(ctx, walk.src.virt.addr, walk.dst.virt.addr, diff --git a/crypto/salsa20_generic.c b/crypto/salsa20_generic.c index f550b5d94630..d7da0eea5622 100644 --- a/crypto/salsa20_generic.c +++ b/crypto/salsa20_generic.c @@ -188,13 +188,6 @@ static int encrypt(struct blkcipher_desc *desc, salsa20_ivsetup(ctx, walk.iv); - if (likely(walk.nbytes == nbytes)) - { - salsa20_encrypt_bytes(ctx, walk.dst.virt.addr, - walk.src.virt.addr, nbytes); - return blkcipher_walk_done(desc, &walk, 0); - } - while (walk.nbytes >= 64) { salsa20_encrypt_bytes(ctx, walk.dst.virt.addr, walk.src.virt.addr, -- GitLab From 5bcc2f7c8d3f095d04a420c862128e5db5d2003d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 14 Dec 2017 15:32:38 -0800 Subject: [PATCH 2011/5498] autofs: fix careless error in recent commit commit 302ec300ef8a545a7fc7f667e5fd743b091c2eeb upstream. Commit ecc0c469f277 ("autofs: don't fail mount for transient error") was meant to replace an 'if' with a 'switch', but instead added the 'switch' leaving the case in place. Link: http://lkml.kernel.org/r/87zi6wstmw.fsf@notabene.neil.brown.name Fixes: ecc0c469f277 ("autofs: don't fail mount for transient error") Reported-by: Ben Hutchings Signed-off-by: NeilBrown Cc: Ian Kent Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/autofs4/waitq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 8a2dcfc08c1c..d5d0f819b64a 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -174,7 +174,6 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, mutex_unlock(&sbi->wq_mutex); - if (autofs4_write(sbi, pipe, &pkt, pktsz)) switch (ret = autofs4_write(sbi, pipe, &pkt, pktsz)) { case 0: break; -- GitLab From a342e5368eeab3beb6ea156a53ccece114e9d883 Mon Sep 17 00:00:00 2001 From: David Kozub Date: Tue, 5 Dec 2017 22:40:04 +0100 Subject: [PATCH 2012/5498] USB: uas and storage: Add US_FL_BROKEN_FUA for another JMicron JMS567 ID commit 62354454625741f0569c2cbe45b2d192f8fd258e upstream. There is another JMS567-based USB3 UAS enclosure (152d:0578) that fails with the following error: [sda] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE [sda] tag#0 Sense Key : Illegal Request [current] [sda] tag#0 Add. Sense: Invalid field in cdb The issue occurs both with UAS (occasionally) and mass storage (immediately after mounting a FS on a disk in the enclosure). Enabling US_FL_BROKEN_FUA quirk solves this issue. This patch adds an UNUSUAL_DEV with US_FL_BROKEN_FUA for the enclosure for both UAS and mass storage. Signed-off-by: David Kozub Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ drivers/usb/storage/unusual_uas.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 4fefd76d2320..2b6f7e5e52c3 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2131,6 +2131,13 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), +/* Reported by David Kozub */ +UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA), + /* * Patch by Constantin Baranov * Report by Andreas Koenecke. diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 32c84be6af60..9299c0779999 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -139,6 +139,13 @@ UNUSUAL_DEV(0x174c, 0x5106, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_UAS), +/* Reported-by: David Kozub */ +UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA), + /* Reported-by: Hans de Goede */ UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, "VIA", -- GitLab From dd3ad5f60d520da135bf4dce5adcecf400e2db64 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 12 Dec 2017 14:25:13 -0500 Subject: [PATCH 2013/5498] USB: core: prevent malicious bNumInterfaces overflow commit 48a4ff1c7bb5a32d2e396b03132d20d552c0eca7 upstream. A malicious USB device with crafted descriptors can cause the kernel to access unallocated memory by setting the bNumInterfaces value too high in a configuration descriptor. Although the value is adjusted during parsing, this adjustment is skipped in one of the error return paths. This patch prevents the problem by setting bNumInterfaces to 0 initially. The existing code already sets it to the proper value after parsing is complete. Signed-off-by: Alan Stern Reported-by: Andrey Konovalov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 1c62d31ed896..4a9680fe5907 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -450,6 +450,9 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, unsigned iad_num = 0; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); + nintf = nintf_orig = config->desc.bNumInterfaces; + config->desc.bNumInterfaces = 0; // Adjusted later + if (config->desc.bDescriptorType != USB_DT_CONFIG || config->desc.bLength < USB_DT_CONFIG_SIZE || config->desc.bLength > size) { @@ -463,7 +466,6 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, buffer += config->desc.bLength; size -= config->desc.bLength; - nintf = nintf_orig = config->desc.bNumInterfaces; if (nintf > USB_MAXINTERFACES) { dev_warn(ddev, "config %d has too many interfaces: %d, " "using maximum allowed: %d\n", -- GitLab From f749066bec4019a7a5f7eee22b56314958161c1e Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 7 Dec 2017 14:16:50 -0700 Subject: [PATCH 2014/5498] usbip: fix stub_send_ret_submit() vulnerability to null transfer_buffer commit be6123df1ea8f01ee2f896a16c2b7be3e4557a5a upstream. stub_send_ret_submit() handles urb with a potential null transfer_buffer, when it replays a packet with potential malicious data that could contain a null buffer. Add a check for the condition when actual_length > 0 and transfer_buffer is null. Reported-by: Secunia Research Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_tx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index 021003c4de53..af858d52608a 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -178,6 +178,13 @@ static int stub_send_ret_submit(struct stub_device *sdev) memset(&pdu_header, 0, sizeof(pdu_header)); memset(&msg, 0, sizeof(msg)); + if (urb->actual_length > 0 && !urb->transfer_buffer) { + dev_err(&sdev->udev->dev, + "urb: actual_length %d transfer_buffer null\n", + urb->actual_length); + return -1; + } + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) iovnum = 2 + urb->number_of_packets; else -- GitLab From d5ba16a80d96c1df57b98487143fed90374d5514 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 8 Dec 2017 18:10:05 +0200 Subject: [PATCH 2015/5498] xhci: Don't add a virt_dev to the devs array before it's fully allocated commit 5d9b70f7d52eb14bb37861c663bae44de9521c35 upstream. Avoid null pointer dereference if some function is walking through the devs array accessing members of a new virt_dev that is mid allocation. Add the virt_dev to xhci->devs[i] _after_ the virt_device and all its members are properly allocated. issue found by KASAN: null-ptr-deref in xhci_find_slot_id_by_port "Quick analysis suggests that xhci_alloc_virt_device() is not mutex protected. If so, there is a time frame where xhci->devs[slot_id] is set but not fully initialized. Specifically, xhci->devs[i]->udev can be NULL." Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index e67d186b0ee9..b72263661d04 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -978,10 +978,9 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, return 0; } - xhci->devs[slot_id] = kzalloc(sizeof(*xhci->devs[slot_id]), flags); - if (!xhci->devs[slot_id]) + dev = kzalloc(sizeof(*dev), flags); + if (!dev) return 0; - dev = xhci->devs[slot_id]; /* Allocate the (output) device context that will be used in the HC. */ dev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags); @@ -1029,9 +1028,17 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, &xhci->dcbaa->dev_context_ptrs[slot_id], le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id])); + xhci->devs[slot_id] = dev; + return 1; fail: - xhci_free_virt_device(xhci, slot_id); + + if (dev->in_ctx) + xhci_free_container_ctx(xhci, dev->in_ctx); + if (dev->out_ctx) + xhci_free_container_ctx(xhci, dev->out_ctx); + kfree(dev); + return 0; } -- GitLab From 66d63dc137c9b04cf1b9a3102ce79ca15b139764 Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Mon, 11 Dec 2017 15:00:57 -0500 Subject: [PATCH 2016/5498] ext4: fix crash when a directory's i_size is too small commit 9d5afec6b8bd46d6ed821aa1579634437f58ef1f upstream. On a ppc64 machine, when mounting a fuzzed ext2 image (generated by fsfuzzer) the following call trace is seen, VFS: brelse: Trying to free free buffer WARNING: CPU: 1 PID: 6913 at /root/repos/linux/fs/buffer.c:1165 .__brelse.part.6+0x24/0x40 .__brelse.part.6+0x20/0x40 (unreliable) .ext4_find_entry+0x384/0x4f0 .ext4_lookup+0x84/0x250 .lookup_slow+0xdc/0x230 .walk_component+0x268/0x400 .path_lookupat+0xec/0x2d0 .filename_lookup+0x9c/0x1d0 .vfs_statx+0x98/0x140 .SyS_newfstatat+0x48/0x80 system_call+0x58/0x6c This happens because the directory that ext4_find_entry() looks up has inode->i_size that is less than the block size of the filesystem. This causes 'nblocks' to have a value of zero. ext4_bread_batch() ends up not reading any of the directory file's blocks. This renders the entries in bh_use[] array to continue to have garbage data. buffer_uptodate() on bh_use[0] can then return a zero value upon which brelse() function is invoked. This commit fixes the bug by returning -ENOENT when the directory file has no associated blocks. Reported-by: Abdul Haleem Signed-off-by: Chandan Rajendra Signed-off-by: Greg Kroah-Hartman --- fs/ext4/namei.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index d3a22a11ac76..ebd059aba343 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1244,6 +1244,10 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, "falling back\n")); } nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb); + if (!nblocks) { + ret = NULL; + goto cleanup_and_exit; + } start = EXT4_I(dir)->i_dir_start_lookup; if (start >= nblocks) start = 0; -- GitLab From bd154dc611b343418d45753c3e101492a7ca13fa Mon Sep 17 00:00:00 2001 From: David Jeffery Date: Thu, 12 Feb 2015 16:45:31 +0000 Subject: [PATCH 2017/5498] Don't leak a key reference if request_key() tries to use a revoked keyring commit d0709f1e66e8066c4ac6a54620ec116aa41937c0 upstream. If a request_key() call to allocate and fill out a key attempts to insert the key structure into a revoked keyring, the key will leak, using memory and part of the user's key quota until the system reboots. This is from a failure of construct_alloc_key() to decrement the key's reference count after the attempt to insert into the requested keyring is rejected. key_put() needs to be called in the link_prealloc_failed callpath to ensure the unused key is released. Signed-off-by: David Jeffery Signed-off-by: David Howells Signed-off-by: James Morris Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- security/keys/request_key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 6096be6232d7..2e6eefda4f50 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -437,6 +437,7 @@ link_check_failed: link_prealloc_failed: mutex_unlock(&user->cons_lock); + key_put(key); kleave(" = %d [prelink]", ret); return ret; -- GitLab From 337cde8f541783dbc7fb0f974cd82798ba77794c Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 19 Oct 2015 11:20:28 +0100 Subject: [PATCH 2018/5498] KEYS: Don't permit request_key() to construct a new keyring commit 911b79cde95c7da0ec02f48105358a36636b7a71 upstream. If request_key() is used to find a keyring, only do the search part - don't do the construction part if the keyring was not found by the search. We don't really want keyrings in the negative instantiated state since the rejected/negative instantiation error value in the payload is unioned with keyring metadata. Now the kernel gives an error: request_key("keyring", "#selinux,bdekeyring", "keyring", KEY_SPEC_USER_SESSION_KEYRING) = -1 EPERM (Operation not permitted) Signed-off-by: David Howells Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman --- security/keys/request_key.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 2e6eefda4f50..3ca46e5e43dc 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -467,6 +467,9 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, if (ret) goto error; + if (ctx->index_key.type == &key_type_keyring) + return ERR_PTR(-EPERM); + user = key_user_lookup(current_fsuid()); if (!user) { ret = -ENOMEM; -- GitLab From 406e7e045fb87d29a3ec4293898e76821427edc3 Mon Sep 17 00:00:00 2001 From: Ilan peer Date: Mon, 26 Dec 2016 18:17:36 +0200 Subject: [PATCH 2019/5498] mac80211: Fix addition of mesh configuration element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 57629915d568c522ac1422df7bba4bee5b5c7a7c upstream. The code was setting the capabilities byte to zero, after it was already properly set previously. Fix it. The bug was found while debugging hwsim mesh tests failures that happened since the commit mentioned below. Fixes: 76f43b4c0a93 ("mac80211: Remove invalid flag operations in mesh TSF synchronization") Signed-off-by: Ilan Peer Reviewed-by: Masashi Honma Signed-off-by: Johannes Berg Cc: Richard Schütz Cc: Mathias Kretschmer Signed-off-by: Greg Kroah-Hartman --- net/mac80211/mesh.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 30bea9dcafed..a70c970a743a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -289,8 +289,6 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ *pos |= ifmsh->ps_peers_deep_sleep ? IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; - *pos++ = 0x00; - return 0; } -- GitLab From b5ed9b970a89a5f6fcbeaaf50e281e9606aae958 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 22 Feb 2017 15:23:22 -0300 Subject: [PATCH 2020/5498] usb: phy: isp1301: Add OF device ID table [ Upstream commit fd567653bdb908009b650f079bfd4b63169e2ac4 ] The driver doesn't have a struct of_device_id table but supported devices are registered via Device Trees. This is working on the assumption that a I2C device registered via OF will always match a legacy I2C device ID and that the MODALIAS reported will always be of the form i2c:. But this could change in the future so the correct approach is to have an OF device ID table if the devices are registered via OF. Signed-off-by: Javier Martinez Canillas Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-isp1301.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 8a55b37d1a02..d2ed59a38354 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -32,6 +32,12 @@ static const struct i2c_device_id isp1301_id[] = { { } }; +static const struct of_device_id isp1301_of_match[] = { + {.compatible = "nxp,isp1301" }, + { }, +}; +MODULE_DEVICE_TABLE(of, isp1301_of_match); + static struct i2c_client *isp1301_i2c_client; static int __isp1301_write(struct isp1301 *isp, u8 reg, u8 value, u8 clear) @@ -129,6 +135,7 @@ static int isp1301_remove(struct i2c_client *client) static struct i2c_driver isp1301_driver = { .driver = { .name = DRV_NAME, + .of_match_table = of_match_ptr(isp1301_of_match), }, .probe = isp1301_probe, .remove = isp1301_remove, -- GitLab From bbfc83efb91c5f1f3b393367356105d3a6f5f17a Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Thu, 9 Mar 2017 16:58:43 -0800 Subject: [PATCH 2021/5498] net: bcmgenet: correct the RBUF_OVFL_CNT and RBUF_ERR_CNT MIB values [ Upstream commit ffff71328a3c321f7c14cc1edd33577717037744 ] The location of the RBUF overflow and error counters has moved between different version of the GENET MAC. This commit corrects the driver to read from the correct locations depending on the version of the GENET MAC. Fixes: 1c1008c793fa ("net: bcmgenet: add main driver file") Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../net/ethernet/broadcom/genet/bcmgenet.c | 60 ++++++++++++++++--- .../net/ethernet/broadcom/genet/bcmgenet.h | 10 +++- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 936448e4894c..ecce5d16f6e7 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1,7 +1,7 @@ /* * Broadcom GENET (Gigabit Ethernet) controller driver * - * Copyright (c) 2014 Broadcom Corporation + * Copyright (c) 2014-2017 Broadcom * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -610,8 +610,9 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { STAT_GENET_RUNT("rx_runt_bytes", mib.rx_runt_bytes), /* Misc UniMAC counters */ STAT_GENET_MISC("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt, - UMAC_RBUF_OVFL_CNT), - STAT_GENET_MISC("rbuf_err_cnt", mib.rbuf_err_cnt, UMAC_RBUF_ERR_CNT), + UMAC_RBUF_OVFL_CNT_V1), + STAT_GENET_MISC("rbuf_err_cnt", mib.rbuf_err_cnt, + UMAC_RBUF_ERR_CNT_V1), STAT_GENET_MISC("mdf_err_cnt", mib.mdf_err_cnt, UMAC_MDF_ERR_CNT), }; @@ -651,6 +652,45 @@ static void bcmgenet_get_strings(struct net_device *dev, u32 stringset, } } +static u32 bcmgenet_update_stat_misc(struct bcmgenet_priv *priv, u16 offset) +{ + u16 new_offset; + u32 val; + + switch (offset) { + case UMAC_RBUF_OVFL_CNT_V1: + if (GENET_IS_V2(priv)) + new_offset = RBUF_OVFL_CNT_V2; + else + new_offset = RBUF_OVFL_CNT_V3PLUS; + + val = bcmgenet_rbuf_readl(priv, new_offset); + /* clear if overflowed */ + if (val == ~0) + bcmgenet_rbuf_writel(priv, 0, new_offset); + break; + case UMAC_RBUF_ERR_CNT_V1: + if (GENET_IS_V2(priv)) + new_offset = RBUF_ERR_CNT_V2; + else + new_offset = RBUF_ERR_CNT_V3PLUS; + + val = bcmgenet_rbuf_readl(priv, new_offset); + /* clear if overflowed */ + if (val == ~0) + bcmgenet_rbuf_writel(priv, 0, new_offset); + break; + default: + val = bcmgenet_umac_readl(priv, offset); + /* clear if overflowed */ + if (val == ~0) + bcmgenet_umac_writel(priv, 0, offset); + break; + } + + return val; +} + static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv) { int i, j = 0; @@ -674,10 +714,16 @@ static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv) UMAC_MIB_START + j + offset); break; case BCMGENET_STAT_MISC: - val = bcmgenet_umac_readl(priv, s->reg_offset); - /* clear if overflowed */ - if (val == ~0) - bcmgenet_umac_writel(priv, 0, s->reg_offset); + if (GENET_IS_V1(priv)) { + val = bcmgenet_umac_readl(priv, s->reg_offset); + /* clear if overflowed */ + if (val == ~0) + bcmgenet_umac_writel(priv, 0, + s->reg_offset); + } else { + val = bcmgenet_update_stat_misc(priv, + s->reg_offset); + } break; } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index eeda0281c684..942271f9ab19 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Broadcom Corporation + * Copyright (c) 2014-2017 Broadcom * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -196,7 +196,9 @@ struct bcmgenet_mib_counters { #define MDIO_REG_SHIFT 16 #define MDIO_REG_MASK 0x1F -#define UMAC_RBUF_OVFL_CNT 0x61C +#define UMAC_RBUF_OVFL_CNT_V1 0x61C +#define RBUF_OVFL_CNT_V2 0x80 +#define RBUF_OVFL_CNT_V3PLUS 0x94 #define UMAC_MPD_CTRL 0x620 #define MPD_EN (1 << 0) @@ -206,7 +208,9 @@ struct bcmgenet_mib_counters { #define UMAC_MPD_PW_MS 0x624 #define UMAC_MPD_PW_LS 0x628 -#define UMAC_RBUF_ERR_CNT 0x634 +#define UMAC_RBUF_ERR_CNT_V1 0x634 +#define RBUF_ERR_CNT_V2 0x84 +#define RBUF_ERR_CNT_V3PLUS 0x98 #define UMAC_MDF_ERR_CNT 0x638 #define UMAC_MDF_CTRL 0x650 #define UMAC_MDF_ADDR 0x654 -- GitLab From 1a34a1137f7693d9dbd3ad108dd9a13fa44cdc57 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Thu, 9 Mar 2017 16:58:44 -0800 Subject: [PATCH 2022/5498] net: bcmgenet: correct MIB access of UniMAC RUNT counters [ Upstream commit 1ad3d225e5a40ca6c586989b4baaca710544c15a ] The gap between the Tx status counters and the Rx RUNT counters is now being added to allow correct reporting of the registers. Fixes: 1c1008c793fa ("net: bcmgenet: add main driver file") Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index ecce5d16f6e7..9b49d6ebf58e 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -705,13 +705,16 @@ static void bcmgenet_update_mib_counters(struct bcmgenet_priv *priv) switch (s->type) { case BCMGENET_STAT_NETDEV: continue; - case BCMGENET_STAT_MIB_RX: - case BCMGENET_STAT_MIB_TX: case BCMGENET_STAT_RUNT: - if (s->type != BCMGENET_STAT_MIB_RX) - offset = BCMGENET_STAT_OFFSET; + offset += BCMGENET_STAT_OFFSET; + /* fall through */ + case BCMGENET_STAT_MIB_TX: + offset += BCMGENET_STAT_OFFSET; + /* fall through */ + case BCMGENET_STAT_MIB_RX: val = bcmgenet_umac_readl(priv, UMAC_MIB_START + j + offset); + offset = 0; /* Reset Offset */ break; case BCMGENET_STAT_MISC: if (GENET_IS_V1(priv)) { -- GitLab From f73422f3a6e833ec44f0f37211781eab50223f71 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Thu, 9 Mar 2017 16:58:48 -0800 Subject: [PATCH 2023/5498] net: bcmgenet: Power up the internal PHY before probing the MII [ Upstream commit 6be371b053dc86f11465cc1abce2e99bda0a0574 ] When using the internal PHY it must be powered up when the MII is probed or the PHY will not be detected. Since the PHY is powered up at reset this has not been a problem. However, when the kernel is restarted with kexec the PHY will likely be powered down when the kernel starts so it will not be detected and the Ethernet link will not be established. This commit explicitly powers up the internal PHY when the GENET driver is probed to correct this behavior. Fixes: 1c1008c793fa ("net: bcmgenet: add main driver file") Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 9b49d6ebf58e..87f0f6e0a15c 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2598,6 +2598,7 @@ static int bcmgenet_probe(struct platform_device *pdev) const void *macaddr; struct resource *r; int err = -EIO; + const char *phy_mode_str; /* Up to GENET_MAX_MQ_CNT + 1 TX queues and a single RX queue */ dev = alloc_etherdev_mqs(sizeof(*priv), GENET_MAX_MQ_CNT + 1, 1); @@ -2685,6 +2686,13 @@ static int bcmgenet_probe(struct platform_device *pdev) if (IS_ERR(priv->clk_wol)) dev_warn(&priv->pdev->dev, "failed to get enet-wol clock\n"); + /* If this is an internal GPHY, power it on now, before UniMAC is + * brought out of reset as absolutely no UniMAC activity is allowed + */ + if (dn && !of_property_read_string(dn, "phy-mode", &phy_mode_str) && + !strcasecmp(phy_mode_str, "internal")) + bcmgenet_power_up(priv, GENET_POWER_PASSIVE); + err = reset_umac(priv); if (err) goto err_clk_disable; -- GitLab From 537546e6e440a2f5a0636880fc6d4a497d65016a Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 10 Mar 2017 11:36:39 +1100 Subject: [PATCH 2024/5498] NFSD: fix nfsd_minorversion(.., NFSD_AVAIL) [ Upstream commit 928c6fb3a9bfd6c5b287aa3465226add551c13c0 ] Current code will return 1 if the version is supported, and -1 if it isn't. This is confusing and inconsistent with the one place where this is used. So change to return 1 if it is supported, and zero if not. i.e. an error is never returned. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfssvc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index a89654bd08c5..4dbd42384655 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -150,7 +150,8 @@ int nfsd_vers(int vers, enum vers_op change) int nfsd_minorversion(u32 minorversion, enum vers_op change) { - if (minorversion > NFSD_SUPPORTED_MINOR_VERSION) + if (minorversion > NFSD_SUPPORTED_MINOR_VERSION && + change != NFSD_AVAIL) return -1; switch(change) { case NFSD_SET: -- GitLab From c1ecb5aba022bc00434642a70e085eb86e92952d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 10 Mar 2017 11:36:39 +1100 Subject: [PATCH 2025/5498] NFSD: fix nfsd_reset_versions for NFSv4. [ Upstream commit 800a938f0bf9130c8256116649c0cc5806bfb2fd ] If you write "-2 -3 -4" to the "versions" file, it will notice that no versions are enabled, and nfsd_reset_versions() is called. This enables all major versions, not no minor versions. So we lose the invariant that NFSv4 is only advertised when at least one minor is enabled. Fix the code to explicitly enable minor versions for v4, change it to use nfsd_vers() to test and set, and simplify the code. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfssvc.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 4dbd42384655..91d7dd9fff01 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -329,23 +329,20 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net) void nfsd_reset_versions(void) { - int found_one = 0; int i; - for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) { - if (nfsd_program.pg_vers[i]) - found_one = 1; - } - - if (!found_one) { - for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) - nfsd_program.pg_vers[i] = nfsd_version[i]; -#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) - for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) - nfsd_acl_program.pg_vers[i] = - nfsd_acl_version[i]; -#endif - } + for (i = 0; i < NFSD_NRVERS; i++) + if (nfsd_vers(i, NFSD_TEST)) + return; + + for (i = 0; i < NFSD_NRVERS; i++) + if (i != 4) + nfsd_vers(i, NFSD_SET); + else { + int minor = 0; + while (nfsd_minorversion(minor, NFSD_SET) >= 0) + minor++; + } } /* -- GitLab From 97a63f608dd59c0750c899e536c59f7e112a5a44 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 28 Feb 2017 17:14:41 -0800 Subject: [PATCH 2026/5498] Input: i8042 - add TUXEDO BU1406 (N24_25BU) to the nomux list [ Upstream commit a4c2a13129f7c5bcf81704c06851601593303fd5 ] TUXEDO BU1406 does not implement active multiplexing mode properly, and takes around 550 ms in i8042_set_mux_mode(). Given that the device does not have external AUX port, there is no downside in disabling the MUX mode. Reported-by: Paul Menzel Suggested-by: Vojtech Pavlik Reviewed-by: Marcos Paulo de Souza Signed-off-by: Dmitry Torokhov Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 3f9ad6414c1b..7ddf667729e3 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -514,6 +514,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), }, }, + { + /* TUXEDO BU1406 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"), + }, + }, { } }; -- GitLab From 78dbf84bec784539abf8753b89058118f9e4e059 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 13 Mar 2017 13:42:03 +0100 Subject: [PATCH 2027/5498] net: wimax/i2400m: fix NULL-deref at probe [ Upstream commit 6e526fdff7be4f13b24f929a04c0e9ae6761291e ] Make sure to check the number of endpoints to avoid dereferencing a NULL-pointer or accessing memory beyond the endpoint array should a malicious device lack the expected endpoints. The endpoints are specifically dereferenced in the i2400m_bootrom_init path during probe (e.g. in i2400mu_tx_bulk_out). Fixes: f398e4240fce ("i2400m/USB: probe/disconnect, dev init/shutdown and reset backends") Cc: Inaky Perez-Gonzalez Signed-off-by: Johan Hovold Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/wimax/i2400m/usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index e7f5910a6519..f8eb66ef2944 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -467,6 +467,9 @@ int i2400mu_probe(struct usb_interface *iface, struct i2400mu *i2400mu; struct usb_device *usb_dev = interface_to_usbdev(iface); + if (iface->cur_altsetting->desc.bNumEndpoints < 4) + return -ENODEV; + if (usb_dev->speed != USB_SPEED_HIGH) dev_err(dev, "device not connected as high speed\n"); -- GitLab From 1f08ebd8815e18e47c028cf8652ddea2cf6a88d7 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 13 Mar 2017 14:30:29 -0700 Subject: [PATCH 2028/5498] dmaengine: Fix array index out of bounds warning in __get_unmap_pool() [ Upstream commit 23f963e91fd81f44f6b316b1c24db563354c6be8 ] This fixes the following warning when building with clang and CONFIG_DMA_ENGINE_RAID=n : drivers/dma/dmaengine.c:1102:11: error: array index 2 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds] return &unmap_pool[2]; ^ ~ drivers/dma/dmaengine.c:1083:1: note: array 'unmap_pool' declared here static struct dmaengine_unmap_pool unmap_pool[] = { ^ drivers/dma/dmaengine.c:1104:11: error: array index 3 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds] return &unmap_pool[3]; ^ ~ drivers/dma/dmaengine.c:1083:1: note: array 'unmap_pool' declared here static struct dmaengine_unmap_pool unmap_pool[] = { Signed-off-by: Matthias Kaehlcke Reviewed-by: Dan Williams Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/dma/dmaengine.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 24bfaf0b92ba..6433baf641cb 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -976,12 +976,14 @@ static struct dmaengine_unmap_pool *__get_unmap_pool(int nr) switch (order) { case 0 ... 1: return &unmap_pool[0]; +#if IS_ENABLED(CONFIG_DMA_ENGINE_RAID) case 2 ... 4: return &unmap_pool[1]; case 5 ... 7: return &unmap_pool[2]; case 8: return &unmap_pool[3]; +#endif default: BUG(); return NULL; -- GitLab From 8111e1b213c4d3ff584668a873004791b4151255 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Tue, 14 Mar 2017 08:58:08 -0400 Subject: [PATCH 2029/5498] net: Resend IGMP memberships upon peer notification. [ Upstream commit 37c343b4f4e70e9dc328ab04903c0ec8d154c1a4 ] When we notify peers of potential changes, it's also good to update IGMP memberships. For example, during VM migration, updating IGMP memberships will redirect existing multicast streams to the VM at the new location. Signed-off-by: Vladislav Yasevich Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/core/dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/core/dev.c b/net/core/dev.c index 93e36e9102e9..cae21f769212 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1248,6 +1248,7 @@ void netdev_notify_peers(struct net_device *dev) { rtnl_lock(); call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, dev); + call_netdevice_notifiers(NETDEV_RESEND_IGMP, dev); rtnl_unlock(); } EXPORT_SYMBOL(netdev_notify_peers); -- GitLab From 6debc3f440ad213935e6b09200b918c6a0f7bba0 Mon Sep 17 00:00:00 2001 From: Stafford Horne Date: Mon, 13 Mar 2017 07:44:45 +0900 Subject: [PATCH 2030/5498] openrisc: fix issue handling 8 byte get_user calls [ Upstream commit 154e67cd8e8f964809d0e75e44bb121b169c75b3 ] Was getting the following error with allmodconfig: ERROR: "__get_user_bad" [lib/test_user_copy.ko] undefined! This was simply a missing break statement, causing an unwanted fall through. Signed-off-by: Stafford Horne Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/openrisc/include/asm/uaccess.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h index d441480a4af4..6f6b1036e064 100644 --- a/arch/openrisc/include/asm/uaccess.h +++ b/arch/openrisc/include/asm/uaccess.h @@ -215,7 +215,7 @@ do { \ case 1: __get_user_asm(x, ptr, retval, "l.lbz"); break; \ case 2: __get_user_asm(x, ptr, retval, "l.lhz"); break; \ case 4: __get_user_asm(x, ptr, retval, "l.lwz"); break; \ - case 8: __get_user_asm2(x, ptr, retval); \ + case 8: __get_user_asm2(x, ptr, retval); break; \ default: (x) = __get_user_bad(); \ } \ } while (0) -- GitLab From 6bcc81fd8f24d964a3ea58e065fe3aad2b6942f1 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 14 Mar 2017 14:42:03 -0400 Subject: [PATCH 2031/5498] drm/radeon/si: add dpm quirk for Oland MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0f424de1fd9bc4ab24bd1fe5430ab5618e803e31 ] OLAND 0x1002:0x6604 0x1028:0x066F 0x00 seems to have problems with higher sclks. Acked-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/si_dpm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index e684508d5188..e54d26e54c37 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2988,6 +2988,12 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, max_sclk = 75000; max_mclk = 80000; } + } else if (rdev->family == CHIP_OLAND) { + if ((rdev->pdev->device == 0x6604) && + (rdev->pdev->subsystem_vendor == 0x1028) && + (rdev->pdev->subsystem_device == 0x066F)) { + max_sclk = 75000; + } } /* Apply dpm quirks */ while (p && p->chip_device != 0) { -- GitLab From 8a0c5968d934e59319ac109b673e3ce0d2aef193 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Thu, 2 Mar 2017 15:10:59 +0100 Subject: [PATCH 2032/5498] sched/deadline: Use deadline instead of period when calculating overflow [ Upstream commit 2317d5f1c34913bac5971d93d69fb6c31bb74670 ] I was testing Daniel's changes with his test case, and tweaked it a little. Instead of having the runtime equal to the deadline, I increased the deadline ten fold. Daniel's test case had: attr.sched_runtime = 2 * 1000 * 1000; /* 2 ms */ attr.sched_deadline = 2 * 1000 * 1000; /* 2 ms */ attr.sched_period = 2 * 1000 * 1000 * 1000; /* 2 s */ To make it more interesting, I changed it to: attr.sched_runtime = 2 * 1000 * 1000; /* 2 ms */ attr.sched_deadline = 20 * 1000 * 1000; /* 20 ms */ attr.sched_period = 2 * 1000 * 1000 * 1000; /* 2 s */ The results were rather surprising. The behavior that Daniel's patch was fixing came back. The task started using much more than .1% of the CPU. More like 20%. Looking into this I found that it was due to the dl_entity_overflow() constantly returning true. That's because it uses the relative period against relative runtime vs the absolute deadline against absolute runtime. runtime / (deadline - t) > dl_runtime / dl_period There's even a comment mentioning this, and saying that when relative deadline equals relative period, that the equation is the same as using deadline instead of period. That comment is backwards! What we really want is: runtime / (deadline - t) > dl_runtime / dl_deadline We care about if the runtime can make its deadline, not its period. And then we can say "when the deadline equals the period, the equation is the same as using dl_period instead of dl_deadline". After correcting this, now when the task gets enqueued, it can throttle correctly, and Daniel's fix to the throttling of sleeping deadline tasks works even when the runtime and deadline are not the same. Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Daniel Bristot de Oliveira Cc: Juri Lelli Cc: Linus Torvalds Cc: Luca Abeni Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Romulo Silva de Oliveira Cc: Steven Rostedt Cc: Thomas Gleixner Cc: Tommaso Cucinotta Link: http://lkml.kernel.org/r/02135a27f1ae3fe5fd032568a5a2f370e190e8d7.1488392936.git.bristot@redhat.com Signed-off-by: Ingo Molnar Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/sched/deadline.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 40a97c3d8aba..254ce905efa3 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -368,13 +368,13 @@ static void replenish_dl_entity(struct sched_dl_entity *dl_se, * * This function returns true if: * - * runtime / (deadline - t) > dl_runtime / dl_period , + * runtime / (deadline - t) > dl_runtime / dl_deadline , * * IOW we can't recycle current parameters. * - * Notice that the bandwidth check is done against the period. For + * Notice that the bandwidth check is done against the deadline. For * task with deadline equal to period this is the same of using - * dl_deadline instead of dl_period in the equation above. + * dl_period instead of dl_deadline in the equation above. */ static bool dl_entity_overflow(struct sched_dl_entity *dl_se, struct sched_dl_entity *pi_se, u64 t) @@ -399,7 +399,7 @@ static bool dl_entity_overflow(struct sched_dl_entity *dl_se, * of anything below microseconds resolution is actually fiction * (but still we want to give the user that illusion >;). */ - left = (pi_se->dl_period >> DL_SCALE) * (dl_se->runtime >> DL_SCALE); + left = (pi_se->dl_deadline >> DL_SCALE) * (dl_se->runtime >> DL_SCALE); right = ((dl_se->deadline - t) >> DL_SCALE) * (pi_se->dl_runtime >> DL_SCALE); -- GitLab From 1e7f83cd53feb19a9bf7ca680ff2edf85647b692 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 15 Mar 2017 21:11:46 -0400 Subject: [PATCH 2033/5498] drm/radeon: reinstate oland workaround for sclk [ Upstream commit 66822d815ae61ecb2d9dba9031517e8a8476969d ] Higher sclks seem to be unstable on some boards. bug: https://bugs.freedesktop.org/show_bug.cgi?id=100222 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/si_dpm.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index e54d26e54c37..2113869e8678 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2989,9 +2989,13 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, max_mclk = 80000; } } else if (rdev->family == CHIP_OLAND) { - if ((rdev->pdev->device == 0x6604) && - (rdev->pdev->subsystem_vendor == 0x1028) && - (rdev->pdev->subsystem_device == 0x066F)) { + if ((rdev->pdev->revision == 0xC7) || + (rdev->pdev->revision == 0x80) || + (rdev->pdev->revision == 0x81) || + (rdev->pdev->revision == 0x83) || + (rdev->pdev->revision == 0x87) || + (rdev->pdev->device == 0x6604) || + (rdev->pdev->device == 0x6605)) { max_sclk = 75000; } } -- GitLab From 28cd05a2efe27f5ea1c2b1c5ffef8d350a5ef1eb Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Mar 2017 16:27:43 +0000 Subject: [PATCH 2034/5498] afs: Fix missing put_page() [ Upstream commit 29c8bbbd6e21daa0997d1c3ee886b897ee7ad652 ] In afs_writepages_region(), inside the loop where we find dirty pages to deal with, one of the if-statements is missing a put_page(). Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/write.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/afs/write.c b/fs/afs/write.c index ab6adfd52516..cbc6d8372369 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -504,6 +504,7 @@ static int afs_writepages_region(struct address_space *mapping, if (PageWriteback(page) || !PageDirty(page)) { unlock_page(page); + put_page(page); continue; } -- GitLab From 15fbd461977aa362d04cab42e9f3ea28935f999c Mon Sep 17 00:00:00 2001 From: Marc Dionne Date: Thu, 16 Mar 2017 16:27:43 +0000 Subject: [PATCH 2035/5498] afs: Populate group ID from vnode status [ Upstream commit 6186f0788b31f44affceeedc7b48eb10faea120d ] The group was hard coded to GLOBAL_ROOT_GID; use the group ID that was received from the server. Signed-off-by: Marc Dionne Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 294671288449..63e353928969 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -69,7 +69,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) set_nlink(inode, vnode->status.nlink); inode->i_uid = vnode->status.owner; - inode->i_gid = GLOBAL_ROOT_GID; + inode->i_gid = vnode->status.group; inode->i_size = vnode->status.size; inode->i_ctime.tv_sec = vnode->status.mtime_server; inode->i_ctime.tv_nsec = 0; -- GitLab From 24a7a4f0f8ddf3746f5fb7f07888fed8fde625aa Mon Sep 17 00:00:00 2001 From: Marc Dionne Date: Thu, 16 Mar 2017 16:27:44 +0000 Subject: [PATCH 2036/5498] afs: Adjust mode bits processing [ Upstream commit 627f46943ff90bcc32ddeb675d881c043c6fa2ae ] Mode bits for an afs file should not be enforced in the usual way. For files, the absence of user bits can restrict file access with respect to what is granted by the server. These bits apply regardless of the owner or the current uid; the rest of the mode bits (group, other) are ignored. Signed-off-by: Marc Dionne Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/security.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/afs/security.c b/fs/afs/security.c index 8d010422dc89..bfa9d3428383 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -340,17 +340,22 @@ int afs_permission(struct inode *inode, int mask) } else { if (!(access & AFS_ACE_LOOKUP)) goto permission_denied; + if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR)) + goto permission_denied; if (mask & (MAY_EXEC | MAY_READ)) { if (!(access & AFS_ACE_READ)) goto permission_denied; + if (!(inode->i_mode & S_IRUSR)) + goto permission_denied; } else if (mask & MAY_WRITE) { if (!(access & AFS_ACE_WRITE)) goto permission_denied; + if (!(inode->i_mode & S_IWUSR)) + goto permission_denied; } } key_put(key); - ret = generic_permission(inode, mask); _leave(" = %d", ret); return ret; -- GitLab From 14a74dccf0f3d8be25768ea7ce7d8b0a48279d64 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Mar 2017 16:27:45 +0000 Subject: [PATCH 2037/5498] afs: Flush outstanding writes when an fd is closed [ Upstream commit 58fed94dfb17e89556b5705f20f90e5b2971b6a1 ] Flush outstanding writes in afs when an fd is closed. This is what NFS and CIFS do. Reported-by: Marc Dionne Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/file.c | 1 + fs/afs/internal.h | 1 + fs/afs/write.c | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/fs/afs/file.c b/fs/afs/file.c index 932ce07948b3..71c03645dcea 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -29,6 +29,7 @@ static int afs_readpages(struct file *filp, struct address_space *mapping, const struct file_operations afs_file_operations = { .open = afs_open, + .flush = afs_flush, .release = afs_release, .llseek = generic_file_llseek, .read = new_sync_read, diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 71d5982312f3..5cb5c320b966 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -749,6 +749,7 @@ extern int afs_writepages(struct address_space *, struct writeback_control *); extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *); extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *); extern int afs_writeback_all(struct afs_vnode *); +extern int afs_flush(struct file *, fl_owner_t); extern int afs_fsync(struct file *, loff_t, loff_t, int); diff --git a/fs/afs/write.c b/fs/afs/write.c index cbc6d8372369..24f11d2deeb2 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -742,6 +742,20 @@ out: return ret; } +/* + * Flush out all outstanding writes on a file opened for writing when it is + * closed. + */ +int afs_flush(struct file *file, fl_owner_t id) +{ + _enter(""); + + if ((file->f_mode & FMODE_WRITE) == 0) + return 0; + + return vfs_fsync(file, 0); +} + /* * notification that a previously read-only page is about to become writable * - if it returns an error, the caller will deliver a bus error signal -- GitLab From 916426b19350c36e0a4a8a9b4e457e1099b9505e Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Mar 2017 16:27:47 +0000 Subject: [PATCH 2038/5498] afs: Fix the maths in afs_fs_store_data() [ Upstream commit 146a1192783697810b63a1e41c4d59fc93387340 ] afs_fs_store_data() works out of the size of the write it's going to make, but it uses 32-bit unsigned subtraction in one place that gets automatically cast to loff_t. However, if to < offset, then the number goes negative, but as the result isn't signed, this doesn't get sign-extended to 64-bits when placed in a loff_t. Fix by casting the operands to loff_t. Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/fsclient.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index c2e930ec2888..4abafa196d66 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -1225,7 +1225,7 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb, _enter(",%x,{%x:%u},,", key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode); - size = to - offset; + size = (loff_t)to - (loff_t)offset; if (first != last) size += (loff_t)(last - first) << PAGE_SHIFT; pos = (loff_t)first << PAGE_SHIFT; -- GitLab From b422c709f72bec2787723aa7c2afdca4246df2f3 Mon Sep 17 00:00:00 2001 From: Marc Dionne Date: Thu, 16 Mar 2017 16:27:47 +0000 Subject: [PATCH 2039/5498] afs: Populate and use client modification time [ Upstream commit ab94f5d0dd6fd82e7eeca5e7c8096eaea0a0261f ] The inode timestamps should be set from the client time in the status received from the server, rather than the server time which is meant for internal server use. Set AFS_SET_MTIME and populate the mtime for operations that take an input status, such as file/dir creation and StoreData. If an input time is not provided the server will set the vnode times based on the current server time. In a situation where the server has some skew with the client, this could lead to the client seeing a timestamp in the future for a file that it just created or wrote. Signed-off-by: Marc Dionne Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/fsclient.c | 18 +++++++++--------- fs/afs/inode.c | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 4abafa196d66..614465130a06 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -105,7 +105,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, vnode->vfs_inode.i_mode = mode; } - vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server; + vnode->vfs_inode.i_ctime.tv_sec = status->mtime_client; vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; vnode->vfs_inode.i_version = data_version; @@ -703,8 +703,8 @@ int afs_fs_create(struct afs_server *server, memset(bp, 0, padsz); bp = (void *) bp + padsz; } - *bp++ = htonl(AFS_SET_MODE); - *bp++ = 0; /* mtime */ + *bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME); + *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ *bp++ = htonl(mode & S_IALLUGO); /* unix mode */ @@ -981,8 +981,8 @@ int afs_fs_symlink(struct afs_server *server, memset(bp, 0, c_padsz); bp = (void *) bp + c_padsz; } - *bp++ = htonl(AFS_SET_MODE); - *bp++ = 0; /* mtime */ + *bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME); + *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ *bp++ = htonl(S_IRWXUGO); /* unix mode */ @@ -1192,8 +1192,8 @@ static int afs_fs_store_data64(struct afs_server *server, *bp++ = htonl(vnode->fid.vnode); *bp++ = htonl(vnode->fid.unique); - *bp++ = 0; /* mask */ - *bp++ = 0; /* mtime */ + *bp++ = htonl(AFS_SET_MTIME); /* mask */ + *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ *bp++ = 0; /* unix mode */ @@ -1269,8 +1269,8 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb, *bp++ = htonl(vnode->fid.vnode); *bp++ = htonl(vnode->fid.unique); - *bp++ = 0; /* mask */ - *bp++ = 0; /* mtime */ + *bp++ = htonl(AFS_SET_MTIME); /* mask */ + *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ *bp++ = 0; /* owner */ *bp++ = 0; /* group */ *bp++ = 0; /* unix mode */ diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 63e353928969..0d3ac8f7c9a3 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -71,7 +71,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) inode->i_uid = vnode->status.owner; inode->i_gid = vnode->status.group; inode->i_size = vnode->status.size; - inode->i_ctime.tv_sec = vnode->status.mtime_server; + inode->i_ctime.tv_sec = vnode->status.mtime_client; inode->i_ctime.tv_nsec = 0; inode->i_atime = inode->i_mtime = inode->i_ctime; inode->i_blocks = 0; -- GitLab From b1718fdda0f2d47634c0e8e96b0a77cfc34d4424 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Mar 2017 16:27:48 +0000 Subject: [PATCH 2040/5498] afs: Fix page leak in afs_write_begin() [ Upstream commit 6d06b0d25209c80e99c1e89700f1e09694a3766b ] afs_write_begin() leaks a ref and a lock on a page if afs_fill_page() fails. Fix the leak by unlocking and releasing the page in the error path. Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/write.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/afs/write.c b/fs/afs/write.c index 24f11d2deeb2..9f816624e212 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -149,12 +149,12 @@ int afs_write_begin(struct file *file, struct address_space *mapping, kfree(candidate); return -ENOMEM; } - *pagep = page; - /* page won't leak in error case: it eventually gets cleaned off LRU */ if (!PageUptodate(page) && len != PAGE_CACHE_SIZE) { ret = afs_fill_page(vnode, key, index << PAGE_CACHE_SHIFT, page); if (ret < 0) { + unlock_page(page); + put_page(page); kfree(candidate); _leave(" = %d [prep]", ret); return ret; @@ -162,6 +162,9 @@ int afs_write_begin(struct file *file, struct address_space *mapping, SetPageUptodate(page); } + /* page won't leak in error case: it eventually gets cleaned off LRU */ + *pagep = page; + try_again: spin_lock(&vnode->writeback_lock); -- GitLab From dea197784167ddcd4a53f63b194f1a0c0ebf36e9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 16 Mar 2017 16:27:48 +0000 Subject: [PATCH 2041/5498] afs: Fix afs_kill_pages() [ Upstream commit 7286a35e893176169b09715096a4aca557e2ccd2 ] Fix afs_kill_pages() in two ways: (1) If a writeback has been partially flushed, then if we try and kill the pages it contains, some of them may no longer be undergoing writeback and end_page_writeback() will assert. Fix this by checking to see whether the page in question is actually undergoing writeback before ending that writeback. (2) The loop that scans for pages to kill doesn't increase the first page index, and so the loop may not terminate, but it will try to process the same pages over and over again. Fix this by increasing the first page index to one after the last page we processed. Signed-off-by: David Howells Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/afs/write.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/afs/write.c b/fs/afs/write.c index 9f816624e212..c4817b2f6aa7 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -300,10 +300,14 @@ static void afs_kill_pages(struct afs_vnode *vnode, bool error, ASSERTCMP(pv.nr, ==, count); for (loop = 0; loop < count; loop++) { - ClearPageUptodate(pv.pages[loop]); + struct page *page = pv.pages[loop]; + ClearPageUptodate(page); if (error) - SetPageError(pv.pages[loop]); - end_page_writeback(pv.pages[loop]); + SetPageError(page); + if (PageWriteback(page)) + end_page_writeback(page); + if (page->index >= first) + first = page->index + 1; } __pagevec_release(&pv); -- GitLab From 1062f3998ecba87df9211e6bcc0a16baf001a19a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 15 Mar 2017 22:53:37 +0100 Subject: [PATCH 2042/5498] perf symbols: Fix symbols__fixup_end heuristic for corner cases [ Upstream commit e7ede72a6d40cb3a30c087142d79381ca8a31dab ] The current symbols__fixup_end() heuristic for the last entry in the rb tree is suboptimal as it leads to not being able to recognize the symbol in the call graph in a couple of corner cases, for example: i) If the symbol has a start address (f.e. exposed via kallsyms) that is at a page boundary, then the roundup(curr->start, 4096) for the last entry will result in curr->start == curr->end with a symbol length of zero. ii) If the symbol has a start address that is shortly before a page boundary, then also here, curr->end - curr->start will just be very few bytes, where it's unrealistic that we could perform a match against. Instead, change the heuristic to roundup(curr->start, 4096) + 4096, so that we can catch such corner cases and have a better chance to find that specific symbol. It's still just best effort as the real end of the symbol is unknown to us (and could even be at a larger offset than the current range), but better than the current situation. Alexei reported that he recently run into case i) with a JITed eBPF program (these are all page aligned) as the last symbol which wasn't properly shown in the call graph (while other eBPF program symbols in the rb tree were displayed correctly). Since this is a generic issue, lets try to improve the heuristic a bit. Reported-and-Tested-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann Fixes: 2e538c4a1847 ("perf tools: Improve kernel/modules symbol lookup") Link: http://lkml.kernel.org/r/bb5c80d27743be6f12afc68405f1956a330e1bc9.1489614365.git.daniel@iogearbox.net Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/symbol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 078331140d8c..dcf94cfa5420 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -191,7 +191,7 @@ void symbols__fixup_end(struct rb_root *symbols) /* Last entry */ if (curr->end == curr->start) - curr->end = roundup(curr->start, 4096); + curr->end = roundup(curr->start, 4096) + 4096; } void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) -- GitLab From 7480adadb8ba2bc82af61fc4f1fe0a91ed5c8331 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Wed, 8 Mar 2017 14:39:15 -0500 Subject: [PATCH 2043/5498] NFSv4.1 respect server's max size in CREATE_SESSION [ Upstream commit 033853325fe3bdc70819a8b97915bd3bca41d3af ] Currently client doesn't respect max sizes server returns in CREATE_SESSION. nfs4_session_set_rwsize() gets called and server->rsize, server->wsize are 0 so they never get set to the sizes returned by the server. Signed-off-by: Olga Kornievskaia Signed-off-by: Anna Schumaker Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 0e11fe80e5b9..723c656ebd28 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -894,9 +894,9 @@ static void nfs4_session_set_rwsize(struct nfs_server *server) server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; - if (server->rsize > server_resp_sz) + if (!server->rsize || server->rsize > server_resp_sz) server->rsize = server_resp_sz; - if (server->wsize > server_rqst_sz) + if (!server->wsize || server->wsize > server_rqst_sz) server->wsize = server_rqst_sz; #endif /* CONFIG_NFS_V4_1 */ } -- GitLab From f23c756da69845cc186c28f1df966c0fdbad8551 Mon Sep 17 00:00:00 2001 From: Zygo Blaxell Date: Fri, 10 Mar 2017 16:45:44 -0500 Subject: [PATCH 2044/5498] btrfs: add missing memset while reading compressed inline extents [ Upstream commit e1699d2d7bf6e6cce3e1baff19f9dd4595a58664 ] This is a story about 4 distinct (and very old) btrfs bugs. Commit c8b978188c ("Btrfs: Add zlib compression support") added three data corruption bugs for inline extents (bugs #1-3). Commit 93c82d5750 ("Btrfs: zero page past end of inline file items") fixed bug #1: uncompressed inline extents followed by a hole and more extents could get non-zero data in the hole as they were read. The fix was to add a memset in btrfs_get_extent to zero out the hole. Commit 166ae5a418 ("btrfs: fix inline compressed read err corruption") fixed bug #2: compressed inline extents which contained non-zero bytes might be replaced with zero bytes in some cases. This patch removed an unhelpful memset from uncompress_inline, but the case where memset is required was missed. There is also a memset in the decompression code, but this only covers decompressed data that is shorter than the ram_bytes from the extent ref record. This memset doesn't cover the region between the end of the decompressed data and the end of the page. It has also moved around a few times over the years, so there's no single patch to refer to. This patch fixes bug #3: compressed inline extents followed by a hole and more extents could get non-zero data in the hole as they were read (i.e. bug #3 is the same as bug #1, but s/uncompressed/compressed/). The fix is the same: zero out the hole in the compressed case too, by putting a memset back in uncompress_inline, but this time with correct parameters. The last and oldest bug, bug #0, is the cause of the offending inline extent/hole/extent pattern. Bug #0 is a subtle and mostly-harmless quirk of behavior somewhere in the btrfs write code. In a few special cases, an inline extent and hole are allowed to persist where they normally would be combined with later extents in the file. A fast reproducer for bug #0 is presented below. A few offending extents are also created in the wild during large rsync transfers with the -S flag. A Linux kernel build (git checkout; make allyesconfig; make -j8) will produce a handful of offending files as well. Once an offending file is created, it can present different content to userspace each time it is read. Bug #0 is at least 4 and possibly 8 years old. I verified every vX.Y kernel back to v3.5 has this behavior. There are fossil records of this bug's effects in commits all the way back to v2.6.32. I have no reason to believe bug #0 wasn't present at the beginning of btrfs compression support in v2.6.29, but I can't easily test kernels that old to be sure. It is not clear whether bug #0 is worth fixing. A fix would likely require injecting extra reads into currently write-only paths, and most of the exceptional cases caused by bug #0 are already handled now. Whether we like them or not, bug #0's inline extents followed by holes are part of the btrfs de-facto disk format now, and we need to be able to read them without data corruption or an infoleak. So enough about bug #0, let's get back to bug #3 (this patch). An example of on-disk structure leading to data corruption found in the wild: item 61 key (606890 INODE_ITEM 0) itemoff 9662 itemsize 160 inode generation 50 transid 50 size 47424 nbytes 49141 block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0 flags 0x0(none) item 62 key (606890 INODE_REF 603050) itemoff 9642 itemsize 20 inode ref index 3 namelen 10 name: DB_File.so item 63 key (606890 EXTENT_DATA 0) itemoff 8280 itemsize 1362 inline extent data size 1341 ram 4085 compress(zlib) item 64 key (606890 EXTENT_DATA 4096) itemoff 8227 itemsize 53 extent data disk byte 5367308288 nr 20480 extent data offset 0 nr 45056 ram 45056 extent compression(zlib) Different data appears in userspace during each read of the 11 bytes between 4085 and 4096. The extent in item 63 is not long enough to fill the first page of the file, so a memset is required to fill the space between item 63 (ending at 4085) and item 64 (beginning at 4096) with zero. Here is a reproducer from Liu Bo, which demonstrates another method of creating the same inline extent and hole pattern: Using 'page_poison=on' kernel command line (or enable CONFIG_PAGE_POISONING) run the following: # touch foo # chattr +c foo # xfs_io -f -c "pwrite -W 0 1000" foo # xfs_io -f -c "falloc 4 8188" foo # od -x foo # echo 3 >/proc/sys/vm/drop_caches # od -x foo This produce the following on my box: Correct output: file contains 1000 data bytes followed by zeros: 0000000 cdcd cdcd cdcd cdcd cdcd cdcd cdcd cdcd * 0001740 cdcd cdcd cdcd cdcd 0000 0000 0000 0000 0001760 0000 0000 0000 0000 0000 0000 0000 0000 * 0020000 Actual output: the data after the first 1000 bytes will be different each run: 0000000 cdcd cdcd cdcd cdcd cdcd cdcd cdcd cdcd * 0001740 cdcd cdcd cdcd cdcd 6c63 7400 635f 006d 0001760 5f74 6f43 7400 435f 0053 5f74 7363 7400 0002000 435f 0056 5f74 6164 7400 645f 0062 5f74 (...) Signed-off-by: Zygo Blaxell Reviewed-by: Liu Bo Reviewed-by: Chris Mason Signed-off-by: Chris Mason Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/inode.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cc9f18f277b9..577a47a50da7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6325,6 +6325,20 @@ static noinline int uncompress_inline(struct btrfs_path *path, max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size); ret = btrfs_decompress(compress_type, tmp, page, extent_offset, inline_size, max_size); + + /* + * decompression code contains a memset to fill in any space between the end + * of the uncompressed data and the end of max_size in case the decompressed + * data ends up shorter than ram_bytes. That doesn't cover the hole between + * the end of an inline extent and the beginning of the next block, so we + * cover that region here. + */ + + if (max_size + pg_offset < PAGE_SIZE) { + char *map = kmap(page); + memset(map + pg_offset + max_size, 0, PAGE_SIZE - max_size - pg_offset); + kunmap(page); + } kfree(tmp); return ret; } -- GitLab From d523df492e12dcc47b3f3ffe506eb5fb9f6a3273 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Wed, 1 Mar 2017 23:13:26 -0600 Subject: [PATCH 2045/5498] target: Use system workqueue for ALUA transitions [ Upstream commit 207ee84133c00a8a2a5bdec94df4a5b37d78881c ] If tcmu-runner is processing a STPG and needs to change the kernel's ALUA state then we cannot use the same work queue for task management requests and ALUA transitions, because we could deadlock. The problem occurs when a STPG times out before tcmu-runner is able to call into target_tg_pt_gp_alua_access_state_store-> core_alua_do_port_transition -> core_alua_do_transition_tg_pt -> queue_work. In this case, the tmr is on the work queue waiting for the STPG to complete, but the STPG transition is now queued behind the waiting tmr. Note: This bug will also be fixed by this patch: http://www.spinics.net/lists/target-devel/msg14560.html which switches the tmr code to use the system workqueues. For both, I am not sure if we need a dedicated workqueue since it is not a performance path and I do not think we need WQ_MEM_RECLAIM to make forward progress to free up memory like the block layer does. Signed-off-by: Mike Christie Signed-off-by: Nicholas Bellinger Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_alua.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index fb87780929d2..701f94ae5eef 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -1126,13 +1126,11 @@ static int core_alua_do_transition_tg_pt( unsigned long transition_tmo; transition_tmo = tg_pt_gp->tg_pt_gp_implicit_trans_secs * HZ; - queue_delayed_work(tg_pt_gp->tg_pt_gp_dev->tmr_wq, - &tg_pt_gp->tg_pt_gp_transition_work, - transition_tmo); + schedule_delayed_work(&tg_pt_gp->tg_pt_gp_transition_work, + transition_tmo); } else { tg_pt_gp->tg_pt_gp_transition_complete = &wait; - queue_delayed_work(tg_pt_gp->tg_pt_gp_dev->tmr_wq, - &tg_pt_gp->tg_pt_gp_transition_work, 0); + schedule_delayed_work(&tg_pt_gp->tg_pt_gp_transition_work, 0); wait_for_completion(&wait); tg_pt_gp->tg_pt_gp_transition_complete = NULL; } -- GitLab From b3146dbdd20de973514d276101da6f8ae8872b30 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 9 Nov 2017 18:09:33 +0100 Subject: [PATCH 2046/5498] fbdev: controlfb: Add missing modes to fix out of bounds access [ Upstream commit ac831a379d34109451b3c41a44a20ee10ecb615f ] Dan's static analysis says: drivers/video/fbdev/controlfb.c:560 control_setup() error: buffer overflow 'control_mac_modes' 20 <= 21 Indeed, control_mac_modes[] has only 20 elements, while VMODE_MAX is 22, which may lead to an out of bounds read when parsing vmode commandline options. The bug was introduced in v2.4.5.6, when 2 new modes were added to macmodes.h, but control_mac_modes[] wasn't updated: https://kernel.opensuse.org/cgit/kernel/diff/include/video/macmodes.h?h=v2.5.2&id=29f279c764808560eaceb88fef36cbc35c529aad Augment control_mac_modes[] with the two new video modes to fix this. Reported-by: Dan Carpenter Signed-off-by: Geert Uytterhoeven Cc: Dan Carpenter Cc: Benjamin Herrenschmidt Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/controlfb.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/fbdev/controlfb.h b/drivers/video/fbdev/controlfb.h index 6026c60fc100..261522fabdac 100644 --- a/drivers/video/fbdev/controlfb.h +++ b/drivers/video/fbdev/controlfb.h @@ -141,5 +141,7 @@ static struct max_cmodes control_mac_modes[] = { {{ 1, 2}}, /* 1152x870, 75Hz */ {{ 0, 1}}, /* 1280x960, 75Hz */ {{ 0, 1}}, /* 1280x1024, 75Hz */ + {{ 1, 2}}, /* 1152x768, 60Hz */ + {{ 0, 1}}, /* 1600x1024, 60Hz */ }; -- GitLab From 9a907ea5f7dbccc3b2cc810ad606d3c87919460b Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 9 Nov 2017 18:09:30 +0100 Subject: [PATCH 2047/5498] video: udlfb: Fix read EDID timeout [ Upstream commit c98769475575c8a585f5b3952f4b5f90266f699b ] While usb_control_msg function expects timeout in miliseconds, a value of HZ is used. Replace it with USB_CTRL_GET_TIMEOUT and also fix error message which looks like: udlfb: Read EDID byte 78 failed err ffffff92 as error is either negative errno or number of bytes transferred use %d format specifier. Returned EDID is in second byte, so return error when less than two bytes are received. Fixes: 18dffdf8913a ("staging: udlfb: enhance EDID and mode handling support") Signed-off-by: Ladislav Michl Cc: Bernie Thompson Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/udlfb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index 046d51d83d74..ef7d23bb2e28 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -769,11 +769,11 @@ static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len) for (i = 0; i < len; i++) { ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), (0x02), - (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, - HZ); - if (ret < 1) { - pr_err("Read EDID byte %d failed err %x\n", i, ret); + usb_rcvctrlpipe(dev->udev, 0), 0x02, + (0x80 | (0x02 << 5)), i << 8, 0xA1, + rbuf, 2, USB_CTRL_GET_TIMEOUT); + if (ret < 2) { + pr_err("Read EDID byte %d failed: %d\n", i, ret); i--; break; } -- GitLab From 16793c323d1667a16bea348fe16d83f88b93161d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 9 Nov 2017 18:09:28 +0100 Subject: [PATCH 2048/5498] video: fbdev: au1200fb: Release some resources if a memory allocation fails [ Upstream commit 451f130602619a17c8883dd0b71b11624faffd51 ] We should go through the error handling code instead of returning -ENOMEM directly. Signed-off-by: Christophe JAILLET Cc: Tejun Heo Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/au1200fb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/au1200fb.c b/drivers/video/fbdev/au1200fb.c index 18600d4e1b3f..9b8b3c31f605 100644 --- a/drivers/video/fbdev/au1200fb.c +++ b/drivers/video/fbdev/au1200fb.c @@ -1699,7 +1699,8 @@ static int au1200fb_drv_probe(struct platform_device *dev) if (!fbdev->fb_mem) { print_err("fail to allocate frambuffer (size: %dK))", fbdev->fb_len / 1024); - return -ENOMEM; + ret = -ENOMEM; + goto failed; } /* -- GitLab From 044cc5ec17d93236c5f61dbcf1222651a23825bf Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 9 Nov 2017 18:09:28 +0100 Subject: [PATCH 2049/5498] video: fbdev: au1200fb: Return an error code if a memory allocation fails [ Upstream commit 8cae353e6b01ac3f18097f631cdbceb5ff28c7f3 ] 'ret' is known to be 0 at this point. In case of memory allocation error in 'framebuffer_alloc()', return -ENOMEM instead. Signed-off-by: Christophe JAILLET Cc: Tejun Heo Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/au1200fb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/au1200fb.c b/drivers/video/fbdev/au1200fb.c index 9b8b3c31f605..d68e04ec46de 100644 --- a/drivers/video/fbdev/au1200fb.c +++ b/drivers/video/fbdev/au1200fb.c @@ -1680,8 +1680,10 @@ static int au1200fb_drv_probe(struct platform_device *dev) fbi = framebuffer_alloc(sizeof(struct au1200fb_device), &dev->dev); - if (!fbi) + if (!fbi) { + ret = -ENOMEM; goto failed; + } _au1200fb_infos[plane] = fbi; fbdev = fbi->par; -- GitLab From 0e8096628d11b521d3ed66c349d7bde4678909d7 Mon Sep 17 00:00:00 2001 From: Qiang Date: Thu, 28 Sep 2017 11:54:34 +0800 Subject: [PATCH 2050/5498] PCI/PME: Handle invalid data when reading Root Status [ Upstream commit 3ad3f8ce50914288731a3018b27ee44ab803e170 ] PCIe PME and native hotplug share the same interrupt number, so hotplug interrupts are also processed by PME. In some cases, e.g., a Link Down interrupt, a device may be present but unreachable, so when we try to read its Root Status register, the read fails and we get all ones data (0xffffffff). Previously, we interpreted that data as PCI_EXP_RTSTA_PME being set, i.e., "some device has asserted PME," so we scheduled pcie_pme_work_fn(). This caused an infinite loop because pcie_pme_work_fn() tried to handle PME requests until PCI_EXP_RTSTA_PME is cleared, but with the link down, PCI_EXP_RTSTA_PME can't be cleared. Check for the invalid 0xffffffff data everywhere we read the Root Status register. 1469d17dd341 ("PCI: pciehp: Handle invalid data when reading from non-existent devices") added similar checks in the hotplug driver. Signed-off-by: Qiang Zheng [bhelgaas: changelog, also check in pcie_pme_work_fn(), use "~0" to follow other similar checks] Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pcie/pme.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 63fc63911295..deb903112974 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -233,6 +233,9 @@ static void pcie_pme_work_fn(struct work_struct *work) break; pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); + if (rtsta == (u32) ~0) + break; + if (rtsta & PCI_EXP_RTSTA_PME) { /* * Clear PME status of the port. If there are other @@ -280,7 +283,7 @@ static irqreturn_t pcie_pme_irq(int irq, void *context) spin_lock_irqsave(&data->lock, flags); pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); - if (!(rtsta & PCI_EXP_RTSTA_PME)) { + if (rtsta == (u32) ~0 || !(rtsta & PCI_EXP_RTSTA_PME)) { spin_unlock_irqrestore(&data->lock, flags); return IRQ_NONE; } -- GitLab From b4c9f0c128359d0b20c6be9ea1c125dda492417a Mon Sep 17 00:00:00 2001 From: Shriya Date: Fri, 13 Oct 2017 10:06:41 +0530 Subject: [PATCH 2051/5498] powerpc/powernv/cpufreq: Fix the frequency read by /proc/cpuinfo [ Upstream commit cd77b5ce208c153260ed7882d8910f2395bfaabd ] The call to /proc/cpuinfo in turn calls cpufreq_quick_get() which returns the last frequency requested by the kernel, but may not reflect the actual frequency the processor is running at. This patch makes a call to cpufreq_get() instead which returns the current frequency reported by the hardware. Fixes: fb5153d05a7d ("powerpc: powernv: Implement ppc_md.get_proc_freq()") Signed-off-by: Shriya Signed-off-by: Michael Ellerman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/powernv/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 3f9546d8a51f..2512c15db42b 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -319,7 +319,7 @@ static unsigned long pnv_get_proc_freq(unsigned int cpu) { unsigned long ret_freq; - ret_freq = cpufreq_quick_get(cpu) * 1000ul; + ret_freq = cpufreq_get(cpu) * 1000ul; /* * If the backend cpufreq driver does not exist, -- GitLab From b1a66b1947e5c201f8c6860e11607faf80f9b6bc Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" Date: Fri, 22 Sep 2017 16:58:00 -0700 Subject: [PATCH 2052/5498] powerpc/opal: Fix EBUSY bug in acquiring tokens [ Upstream commit 71e24d7731a2903b1ae2bba2b2971c654d9c2aa6 ] The current code checks the completion map to look for the first token that is complete. In some cases, a completion can come in but the token can still be on lease to the caller processing the completion. If this completed but unreleased token is the first token found in the bitmap by another tasks trying to acquire a token, then the __test_and_set_bit call will fail since the token will still be on lease. The acquisition will then fail with an EBUSY. This patch reorganizes the acquisition code to look at the opal_async_token_map for an unleased token. If the token has no lease it must have no outstanding completions so we should never see an EBUSY, unless we have leased out too many tokens. Since opal_async_get_token_inrerruptible is protected by a semaphore, we will practically never see EBUSY anymore. Fixes: 8d7248232208 ("powerpc/powernv: Infrastructure to support OPAL async completion") Signed-off-by: William A. Kennington III Signed-off-by: Michael Ellerman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/powernv/opal-async.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/powernv/opal-async.c b/arch/powerpc/platforms/powernv/opal-async.c index e462ab947d16..6e557771d9aa 100644 --- a/arch/powerpc/platforms/powernv/opal-async.c +++ b/arch/powerpc/platforms/powernv/opal-async.c @@ -39,18 +39,18 @@ int __opal_async_get_token(void) int token; spin_lock_irqsave(&opal_async_comp_lock, flags); - token = find_first_bit(opal_async_complete_map, opal_max_async_tokens); + token = find_first_zero_bit(opal_async_token_map, opal_max_async_tokens); if (token >= opal_max_async_tokens) { token = -EBUSY; goto out; } - if (__test_and_set_bit(token, opal_async_token_map)) { + if (!__test_and_clear_bit(token, opal_async_complete_map)) { token = -EBUSY; goto out; } - __clear_bit(token, opal_async_complete_map); + __set_bit(token, opal_async_token_map); out: spin_unlock_irqrestore(&opal_async_comp_lock, flags); -- GitLab From 55bae20a7382543719207c84bf9e17487777ced8 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Wed, 18 Oct 2017 11:16:47 +0200 Subject: [PATCH 2053/5498] powerpc/ipic: Fix status get and status clear [ Upstream commit 6b148a7ce72a7f87c81cbcde48af014abc0516a9 ] IPIC Status is provided by register IPIC_SERSR and not by IPIC_SERMR which is the mask register. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/sysdev/ipic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index b50f97811c25..b32900f8fbf3 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c @@ -844,12 +844,12 @@ void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq) u32 ipic_get_mcp_status(void) { - return ipic_read(primary_ipic->regs, IPIC_SERMR); + return ipic_read(primary_ipic->regs, IPIC_SERSR); } void ipic_clear_mcp_status(u32 mask) { - ipic_write(primary_ipic->regs, IPIC_SERMR, mask); + ipic_write(primary_ipic->regs, IPIC_SERSR, mask); } /* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ -- GitLab From 8867d3f1bb32ae974c92b76529769d424d32c451 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 31 Oct 2017 11:03:17 -0700 Subject: [PATCH 2054/5498] target/iscsi: Fix a race condition in iscsit_add_reject_from_cmd() [ Upstream commit cfe2b621bb18d86e93271febf8c6e37622da2d14 ] Avoid that cmd->se_cmd.se_tfo is read after a command has already been freed. Signed-off-by: Bart Van Assche Cc: Christoph Hellwig Cc: Mike Christie Reviewed-by: Hannes Reinecke Signed-off-by: Nicholas Bellinger Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index f0ace220753e..9582f082152c 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -674,6 +674,7 @@ static int iscsit_add_reject_from_cmd( unsigned char *buf) { struct iscsi_conn *conn; + const bool do_put = cmd->se_cmd.se_tfo != NULL; if (!cmd->conn) { pr_err("cmd->conn is NULL for ITT: 0x%08x\n", @@ -704,7 +705,7 @@ static int iscsit_add_reject_from_cmd( * Perform the kref_put now if se_cmd has already been setup by * scsit_setup_scsi_cmd() */ - if (cmd->se_cmd.se_tfo != NULL) { + if (do_put) { pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n"); target_put_sess_cmd(&cmd->se_cmd); } -- GitLab From 75d2d7e7906c8f28bc0bbc4445de4cc750a52a3a Mon Sep 17 00:00:00 2001 From: tangwenji Date: Fri, 15 Sep 2017 16:03:13 +0800 Subject: [PATCH 2055/5498] iscsi-target: fix memory leak in lio_target_tiqn_addtpg() [ Upstream commit 12d5a43b2dffb6cd28062b4e19024f7982393288 ] tpg must free when call core_tpg_register() return fail Signed-off-by: tangwenji Signed-off-by: Nicholas Bellinger Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target_configfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index bf8c6e446b68..c0bfd1624ebb 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1458,7 +1458,7 @@ static struct se_portal_group *lio_target_tiqn_addtpg( wwn, &tpg->tpg_se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); if (ret < 0) - return NULL; + goto free_out; ret = iscsit_tpg_add_portal_group(tiqn, tpg); if (ret != 0) @@ -1470,6 +1470,7 @@ static struct se_portal_group *lio_target_tiqn_addtpg( return &tpg->tpg_se_tpg; out: core_tpg_deregister(&tpg->tpg_se_tpg); +free_out: kfree(tpg); return NULL; } -- GitLab From e6da104cbddcfa80c3afae7be60d259799909431 Mon Sep 17 00:00:00 2001 From: tangwenji Date: Thu, 24 Aug 2017 19:59:37 +0800 Subject: [PATCH 2056/5498] target:fix condition return in core_pr_dump_initiator_port() [ Upstream commit 24528f089d0a444070aa4f715ace537e8d6bf168 ] When is pr_reg->isid_present_at_reg is false,this function should return. This fixes a regression originally introduced by: commit d2843c173ee53cf4c12e7dfedc069a5bc76f0ac5 Author: Andy Grover Date: Thu May 16 10:40:55 2013 -0700 target: Alter core_pr_dump_initiator_port for ease of use Signed-off-by: tangwenji Signed-off-by: Nicholas Bellinger Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_pr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 45837a4e950d..cba541860e70 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -58,8 +58,10 @@ void core_pr_dump_initiator_port( char *buf, u32 size) { - if (!pr_reg->isid_present_at_reg) + if (!pr_reg->isid_present_at_reg) { buf[0] = '\0'; + return; + } snprintf(buf, size, ",i,0x%s", pr_reg->pr_reg_isid); } -- GitLab From 8948ed2f5fe4e6d567ba74426c8de6bdb46f8a1a Mon Sep 17 00:00:00 2001 From: Jiang Yi Date: Fri, 11 Aug 2017 11:29:44 +0800 Subject: [PATCH 2057/5498] target/file: Do not return error for UNMAP if length is zero [ Upstream commit 594e25e73440863981032d76c9b1e33409ceff6e ] The function fd_execute_unmap() in target_core_file.c calles ret = file->f_op->fallocate(file, mode, pos, len); Some filesystems implement fallocate() to return error if length is zero (e.g. btrfs) but according to SCSI Block Commands spec UNMAP should return success for zero length. Signed-off-by: Jiang Yi Signed-off-by: Nicholas Bellinger Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 9233b653cc72..98dd0044dca3 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -592,6 +592,10 @@ fd_do_unmap(struct se_cmd *cmd, void *priv, sector_t lba, sector_t nolb) struct inode *inode = file->f_mapping->host; int ret; + if (!nolb) { + return 0; + } + if (cmd->se_dev->dev_attrib.pi_prot_type) { ret = fd_do_prot_unmap(cmd, lba, nolb); if (ret) -- GitLab From 5ae69f4575f0de451d5517a901824240aa552ccf Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Fri, 3 Nov 2017 11:45:18 +0000 Subject: [PATCH 2058/5498] arm-ccn: perf: Prevent module unload while PMU is in use [ Upstream commit c7f5828bf77dcbd61d51f4736c1d5aa35663fbb4 ] When the PMU driver is built as a module, the perf expects the pmu->module to be valid, so that the driver is prevented from being unloaded while it is in use. Fix the CCN pmu driver to fill in this field. Fixes: a33b0daab73a0 ("bus: ARM CCN PMU driver") Cc: Pawel Moll Cc: Will Deacon Acked-by: Mark Rutland Signed-off-by: Suzuki K Poulose Signed-off-by: Will Deacon Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/bus/arm-ccn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c index 4d523cfe51ce..31342fccd290 100644 --- a/drivers/bus/arm-ccn.c +++ b/drivers/bus/arm-ccn.c @@ -1157,6 +1157,7 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn) /* Perf driver registration */ ccn->dt.pmu = (struct pmu) { + .module = THIS_MODULE, .attr_groups = arm_ccn_pmu_attr_groups, .task_ctx_nr = perf_invalid_context, .event_init = arm_ccn_pmu_event_init, -- GitLab From 340a510c65d98f0ae90629db2bd5432c41cf676c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 3 Nov 2017 12:21:21 +0100 Subject: [PATCH 2059/5498] mm: Handle 0 flags in _calc_vm_trans() macro [ Upstream commit 592e254502041f953e84d091eae2c68cba04c10b ] _calc_vm_trans() does not handle the situation when some of the passed flags are 0 (which can happen if these VM flags do not make sense for the architecture). Improve the _calc_vm_trans() macro to return 0 in such situation. Since all passed flags are constant, this does not add any runtime overhead. Signed-off-by: Jan Kara Signed-off-by: Dan Williams Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- include/linux/mman.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/mman.h b/include/linux/mman.h index 16373c8f5f57..369bc3405a6d 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -63,8 +63,9 @@ static inline int arch_validate_prot(unsigned long prot) * ("bit1" and "bit2" must be single bits) */ #define _calc_vm_trans(x, bit1, bit2) \ + ((!(bit1) || !(bit2)) ? 0 : \ ((bit1) <= (bit2) ? ((x) & (bit1)) * ((bit2) / (bit1)) \ - : ((x) & (bit1)) / ((bit1) / (bit2))) + : ((x) & (bit1)) / ((bit1) / (bit2)))) /* * Combine the mmap "prot" argument into "vm_flags" used internally. -- GitLab From 069848c7547bc103d805bda93bd3dfb8189adbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 19 Sep 2017 04:48:10 +0200 Subject: [PATCH 2060/5498] clk: tegra: Fix cclk_lp divisor register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 54eff2264d3e9fd7e3987de1d7eba1d3581c631e ] According to comments in code and common sense, cclk_lp uses its own divisor, not cclk_g's. Fixes: b08e8c0ecc42 ("clk: tegra: add clock support for Tegra30") Signed-off-by: MichaÅ‚ MirosÅ‚aw Acked-By: Peter De Schrijver Signed-off-by: Thierry Reding Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/clk/tegra/clk-tegra30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 5bbacd01094f..92f2121837c3 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1063,7 +1063,7 @@ static void __init tegra30_super_clk_init(void) * U71 divider of cclk_lp. */ clk = tegra_clk_register_divider("pll_p_out3_cclklp", "pll_p_out3", - clk_base + SUPER_CCLKG_DIVIDER, 0, + clk_base + SUPER_CCLKLP_DIVIDER, 0, TEGRA_DIVIDER_INT, 16, 8, 1, NULL); clk_register_clkdev(clk, "pll_p_out3_cclklp", NULL); -- GitLab From 4c1eff22ffb3df2c626e958b04edb296f57935ef Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Tue, 31 Oct 2017 18:25:37 +0800 Subject: [PATCH 2061/5498] ppp: Destroy the mutex when cleanup [ Upstream commit f02b2320b27c16b644691267ee3b5c110846f49e ] The mutex_destroy only makes sense when enable DEBUG_MUTEX. For the good readbility, it's better to invoke it in exit func when the init func invokes mutex_init. Signed-off-by: Gao Feng Acked-by: Guillaume Nault Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ppp/ppp_generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 3dd1c19756ec..088cbd8e229b 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -916,6 +916,7 @@ static __net_exit void ppp_exit_net(struct net *net) { struct ppp_net *pn = net_generic(net, ppp_net_id); + mutex_destroy(&pn->all_ppp_mutex); idr_destroy(&pn->units_idr); } -- GitLab From 9aa8bbc15ab1a58fe5b790fec31d237b0d88d0e5 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 19 Oct 2017 19:05:58 +0200 Subject: [PATCH 2062/5498] thermal/drivers/step_wise: Fix temperature regulation misbehavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 07209fcf33542c1ff1e29df2dbdf8f29cdaacb10 ] There is a particular situation when the cooling device is cpufreq and the heat dissipation is not efficient enough where the temperature increases little by little until reaching the critical threshold and leading to a SoC reset. The behavior is reproducible on a hikey6220 with bad heat dissipation (eg. stacked with other boards). Running a simple C program doing while(1); for each CPU of the SoC makes the temperature to reach the passive regulation trip point and ends up to the maximum allowed temperature followed by a reset. This issue has been also reported by running the libhugetlbfs test suite. What is observed is a ping pong between two cpu frequencies, 1.2GHz and 900MHz while the temperature continues to grow. It appears the step wise governor calls get_target_state() the first time with the throttle set to true and the trend to 'raising'. The code selects logically the next state, so the cpu frequency decreases from 1.2GHz to 900MHz, so far so good. The temperature decreases immediately but still stays greater than the trip point, then get_target_state() is called again, this time with the throttle set to true *and* the trend to 'dropping'. From there the algorithm assumes we have to step down the state and the cpu frequency jumps back to 1.2GHz. But the temperature is still higher than the trip point, so get_target_state() is called with throttle=1 and trend='raising' again, we jump to 900MHz, then get_target_state() is called with throttle=1 and trend='dropping', we jump to 1.2GHz, etc ... but the temperature does not stabilizes and continues to increase. [ 237.922654] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=1,throttle=1 [ 237.922678] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=1,throttle=1 [ 237.922690] thermal cooling_device0: cur_state=0 [ 237.922701] thermal cooling_device0: old_target=0, target=1 [ 238.026656] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=2,throttle=1 [ 238.026680] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=2,throttle=1 [ 238.026694] thermal cooling_device0: cur_state=1 [ 238.026707] thermal cooling_device0: old_target=1, target=0 [ 238.134647] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=1,throttle=1 [ 238.134667] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=1,throttle=1 [ 238.134679] thermal cooling_device0: cur_state=0 [ 238.134690] thermal cooling_device0: old_target=0, target=1 In this situation the temperature continues to increase while the trend is oscillating between 'dropping' and 'raising'. We need to keep the current state untouched if the throttle is set, so the temperature can decrease or a higher state could be selected, thus preventing this oscillation. Keeping the next_target untouched when 'throttle' is true at 'dropping' time fixes the issue. The following traces show the governor does not change the next state if trend==2 (dropping) and throttle==1. [ 2306.127987] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=1,throttle=1 [ 2306.128009] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=1,throttle=1 [ 2306.128021] thermal cooling_device0: cur_state=0 [ 2306.128031] thermal cooling_device0: old_target=0, target=1 [ 2306.231991] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=2,throttle=1 [ 2306.232016] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=2,throttle=1 [ 2306.232030] thermal cooling_device0: cur_state=1 [ 2306.232042] thermal cooling_device0: old_target=1, target=1 [ 2306.335982] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=0,throttle=1 [ 2306.336006] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=0,throttle=1 [ 2306.336021] thermal cooling_device0: cur_state=1 [ 2306.336034] thermal cooling_device0: old_target=1, target=1 [ 2306.439984] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=2,throttle=1 [ 2306.440008] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=2,throttle=0 [ 2306.440022] thermal cooling_device0: cur_state=1 [ 2306.440034] thermal cooling_device0: old_target=1, target=0 [ ... ] After a while, if the temperature continues to increase, the next state becomes 2 which is 720MHz on the hikey. That results in the temperature stabilizing around the trip point. [ 2455.831982] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=1,throttle=1 [ 2455.832006] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=1,throttle=0 [ 2455.832019] thermal cooling_device0: cur_state=1 [ 2455.832032] thermal cooling_device0: old_target=1, target=1 [ 2455.935985] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=0,throttle=1 [ 2455.936013] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=0,throttle=0 [ 2455.936027] thermal cooling_device0: cur_state=1 [ 2455.936040] thermal cooling_device0: old_target=1, target=1 [ 2456.043984] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=0,throttle=1 [ 2456.044009] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=0,throttle=0 [ 2456.044023] thermal cooling_device0: cur_state=1 [ 2456.044036] thermal cooling_device0: old_target=1, target=1 [ 2456.148001] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=1,throttle=1 [ 2456.148028] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=1,throttle=1 [ 2456.148042] thermal cooling_device0: cur_state=1 [ 2456.148055] thermal cooling_device0: old_target=1, target=2 [ 2456.252009] thermal thermal_zone0: Trip0[type=1,temp=65000]:trend=2,throttle=1 [ 2456.252041] thermal thermal_zone0: Trip1[type=1,temp=75000]:trend=2,throttle=0 [ 2456.252058] thermal cooling_device0: cur_state=2 [ 2456.252075] thermal cooling_device0: old_target=2, target=1 IOW, this change is needed to keep the state for a cooling device if the temperature trend is oscillating while the temperature increases slightly. Without this change, the situation above leads to a catastrophic crash by a hardware reset on hikey. This issue has been reported to happen on an OMAP dra7xx also. Signed-off-by: Daniel Lezcano Cc: Keerthy Cc: John Stultz Cc: Leo Yan Tested-by: Keerthy Reviewed-by: Keerthy Signed-off-by: Eduardo Valentin Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/thermal/step_wise.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index dc4b69671d87..eed5b448fbf9 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -31,8 +31,7 @@ * If the temperature is higher than a trip point, * a. if the trend is THERMAL_TREND_RAISING, use higher cooling * state for this trip point - * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling - * state for this trip point + * b. if the trend is THERMAL_TREND_DROPPING, do nothing * c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit * for this trip point * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit @@ -94,9 +93,11 @@ static unsigned long get_target_state(struct thermal_instance *instance, if (!throttle) next_target = THERMAL_NO_TARGET; } else { - next_target = cur_state - 1; - if (next_target > instance->upper) - next_target = instance->upper; + if (!throttle) { + next_target = cur_state - 1; + if (next_target > instance->upper) + next_target = instance->upper; + } } break; case THERMAL_TREND_DROP_FULL: -- GitLab From 00dd6166c1975a230d780b0f2bdd1750a598f679 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 20 Sep 2017 08:30:04 -0500 Subject: [PATCH 2063/5498] GFS2: Take inode off order_write list when setting jdata flag [ Upstream commit cc555b09d8c3817aeebda43a14ab67049a5653f7 ] This patch fixes a deadlock caused when the jdata flag is set for inodes that are already on the ordered write list. Since it is on the ordered write list, log_flush calls gfs2_ordered_write which calls filemap_fdatawrite. But since the inode had the jdata flag set, that calls gfs2_jdata_writepages, which tries to start a new transaction. A new transaction cannot be started because it tries to acquire the log_flush rwsem which is already locked by the log flush operation. The bottom line is: We cannot switch an inode from ordered to jdata until we eliminate any ordered data pages (via log flush) or any log_flush operation afterward will create the circular dependency above. So we need to flush the log before setting the diskflags to switch the file mode, then we need to remove the inode from the ordered writes list. Before this patch, the log flush was done for jdata->ordered, but that's wrong. If we're going from jdata to ordered, we don't need to call gfs2_log_flush because the call to filemap_fdatawrite will do it for us: filemap_fdatawrite() -> __filemap_fdatawrite_range() __filemap_fdatawrite_range() -> do_writepages() do_writepages() -> gfs2_jdata_writepages() gfs2_jdata_writepages() -> gfs2_log_flush() This patch modifies function do_gfs2_set_flags so that if a file has its jdata flag set, and it's already on the ordered write list, the log will be flushed and it will be removed from the list before setting the flag. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher Acked-by: Abhijith Das Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/gfs2/file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 80dd44dca028..3d67eed5d5fc 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -256,7 +256,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) goto out; } if ((flags ^ new_flags) & GFS2_DIF_JDATA) { - if (flags & GFS2_DIF_JDATA) + if (new_flags & GFS2_DIF_JDATA) gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); error = filemap_fdatawrite(inode->i_mapping); if (error) @@ -264,6 +264,8 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) error = filemap_fdatawait(inode->i_mapping); if (error) goto out; + if (new_flags & GFS2_DIF_JDATA) + gfs2_ordered_del_inode(ip); } error = gfs2_trans_begin(sdp, RES_DINODE, 0); if (error) -- GitLab From 5df0ce4b14be6bd7ecbebc72bee58b2059333228 Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Mon, 30 Oct 2017 14:46:35 -0700 Subject: [PATCH 2064/5498] bcache: explicitly destroy mutex while exiting [ Upstream commit 330a4db89d39a6b43f36da16824eaa7a7509d34d ] mutex_destroy does nothing most of time, but it's better to call it to make the code future proof and it also has some meaning for like mutex debug. As Coly pointed out in a previous review, bcache_exit() may not be able to handle all the references properly if userspace registers cache and backing devices right before bch_debug_init runs and bch_debug_init failes later. So not exposing userspace interface until everything is ready to avoid that issue. Signed-off-by: Liang Chen Reviewed-by: Michael Lyle Reviewed-by: Coly Li Reviewed-by: Eric Wheeler Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/super.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 8e5666ac8a6a..9c56cf714b22 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -2120,6 +2120,7 @@ static void bcache_exit(void) if (bcache_major) unregister_blkdev(bcache_major, "bcache"); unregister_reboot_notifier(&reboot); + mutex_destroy(&bch_register_lock); } static int __init bcache_init(void) @@ -2138,14 +2139,15 @@ static int __init bcache_init(void) bcache_major = register_blkdev(0, "bcache"); if (bcache_major < 0) { unregister_reboot_notifier(&reboot); + mutex_destroy(&bch_register_lock); return bcache_major; } if (!(bcache_wq = create_workqueue("bcache")) || !(bcache_kobj = kobject_create_and_add("bcache", fs_kobj)) || - sysfs_create_files(bcache_kobj, files) || bch_request_init() || - bch_debug_init(bcache_kobj)) + bch_debug_init(bcache_kobj) || + sysfs_create_files(bcache_kobj, files)) goto err; return 0; -- GitLab From d4ec687b9721e56dedf152041c1ed19ffe34020c Mon Sep 17 00:00:00 2001 From: "tang.junhui" Date: Mon, 30 Oct 2017 14:46:34 -0700 Subject: [PATCH 2065/5498] bcache: fix wrong cache_misses statistics [ Upstream commit c157313791a999646901b3e3c6888514ebc36d62 ] Currently, Cache missed IOs are identified by s->cache_miss, but actually, there are many situations that missed IOs are not assigned a value for s->cache_miss in cached_dev_cache_miss(), for example, a bypassed IO (s->iop.bypass = 1), or the cache_bio allocate failed. In these situations, it will go to out_put or out_submit, and s->cache_miss is null, which leads bch_mark_cache_accounting() to treat this IO as a hit IO. [ML: applied by 3-way merge] Signed-off-by: tang.junhui Reviewed-by: Michael Lyle Reviewed-by: Coly Li Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/request.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index ead0e6072af8..92c7692f191d 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -464,6 +464,7 @@ struct search { unsigned recoverable:1; unsigned write:1; unsigned read_dirty_data:1; + unsigned cache_missed:1; unsigned long start_time; @@ -651,6 +652,7 @@ static inline struct search *search_alloc(struct bio *bio, s->orig_bio = bio; s->cache_miss = NULL; + s->cache_missed = 0; s->d = d; s->recoverable = 1; s->write = (bio->bi_rw & REQ_WRITE) != 0; @@ -774,7 +776,7 @@ static void cached_dev_read_done_bh(struct closure *cl) struct cached_dev *dc = container_of(s->d, struct cached_dev, disk); bch_mark_cache_accounting(s->iop.c, s->d, - !s->cache_miss, s->iop.bypass); + !s->cache_missed, s->iop.bypass); trace_bcache_read(s->orig_bio, !s->cache_miss, s->iop.bypass); if (s->iop.error) @@ -793,6 +795,8 @@ static int cached_dev_cache_miss(struct btree *b, struct search *s, struct cached_dev *dc = container_of(s->d, struct cached_dev, disk); struct bio *miss, *cache_bio; + s->cache_missed = 1; + if (s->cache_miss || s->iop.bypass) { miss = bio_next_split(bio, sectors, GFP_NOIO, s->d->bio_split); ret = miss == bio ? MAP_DONE : MAP_CONTINUE; -- GitLab From a5045f7d107784ae3d8c484e2086c343e4f41fb9 Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Thu, 26 Oct 2017 09:31:16 -0700 Subject: [PATCH 2066/5498] xfs: fix log block underflow during recovery cycle verification [ Upstream commit 9f2a4505800607e537e9dd9dea4f55c4b0c30c7a ] It is possible for mkfs to format very small filesystems with too small of an internal log with respect to the various minimum size and block count requirements. If this occurs when the log happens to be smaller than the scan window used for cycle verification and the scan wraps the end of the log, the start_blk calculation in xlog_find_head() underflows and leads to an attempt to scan an invalid range of log blocks. This results in log recovery failure and a failed mount. Since there may be filesystems out in the wild with this kind of geometry, we cannot simply refuse to mount. Instead, cap the scan window for cycle verification to the size of the physical log. This ensures that the cycle verification proceeds as expected when the scan wraps the end of the log. Reported-by: Zorro Lang Signed-off-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/xfs/xfs_log_recover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 315f96c88091..24126244171c 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -740,7 +740,7 @@ xlog_find_head( * in the in-core log. The following number can be made tighter if * we actually look at the block size of the filesystem. */ - num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log); + num_scan_bblks = min_t(int, log_bbnum, XLOG_TOTAL_REC_SHIFT(log)); if (head_blk >= num_scan_bblks) { /* * We are guaranteed that the entire check can be performed -- GitLab From a36596b1ac5832610a30f33ebf7813de99203b8d Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 11 Oct 2017 15:35:56 -0600 Subject: [PATCH 2067/5498] PCI: Detach driver before procfs & sysfs teardown on device remove [ Upstream commit 16b6c8bb687cc3bec914de09061fcb8411951fda ] When removing a device, for example a VF being removed due to SR-IOV teardown, a "soft" hot-unplug via 'echo 1 > remove' in sysfs, or an actual hot-unplug, we first remove the procfs and sysfs attributes for the device before attempting to release the device from any driver bound to it. Unbinding the driver from the device can take time. The device might need to write out data or it might be actively in use. If it's in use by userspace through a vfio driver, the unbind might block until the user releases the device. This leads to a potentially non-trivial amount of time where the device exists, but we've torn down the interfaces that userspace uses to examine devices, for instance lspci might generate this sort of error: pcilib: Cannot open /sys/bus/pci/devices/0000:01:0a.3/config lspci: Unable to read the standard configuration space header of device 0000:01:0a.3 We don't seem to have any dependence on this teardown ordering in the kernel, so let's unbind the driver first, which is also more symmetric with the instantiation of the device in pci_bus_add_device(). Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/remove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 8bd76c9ba21c..edb4e3a83918 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -20,9 +20,9 @@ static void pci_stop_dev(struct pci_dev *dev) pci_pme_active(dev, false); if (dev->is_added) { + device_release_driver(&dev->dev); pci_proc_detach_device(dev); pci_remove_sysfs_dev_files(dev); - device_release_driver(&dev->dev); dev->is_added = 0; } -- GitLab From ba3e1b3553c512d1f8c0f3c23e800d009983e898 Mon Sep 17 00:00:00 2001 From: nixiaoming Date: Fri, 15 Sep 2017 17:45:56 +0800 Subject: [PATCH 2068/5498] tty fix oops when rmmod 8250 [ Upstream commit c79dde629d2027ca80329c62854a7635e623d527 ] After rmmod 8250.ko tty_kref_put starts kwork (release_one_tty) to release proc interface oops when accessing driver->driver_name in proc_tty_unregister_driver Use jprobe, found driver->driver_name point to 8250.ko static static struct uart_driver serial8250_reg .driver_name= serial, Use name in proc_dir_entry instead of driver->driver_name to fix oops test on linux 4.1.12: BUG: unable to handle kernel paging request at ffffffffa01979de IP: [] strchr+0x0/0x30 PGD 1a0d067 PUD 1a0e063 PMD 851c1f067 PTE 0 Oops: 0000 [#1] PREEMPT SMP Modules linked in: ... ... [last unloaded: 8250] CPU: 7 PID: 116 Comm: kworker/7:1 Tainted: G O 4.1.12 #1 Hardware name: Insyde RiverForest/Type2 - Board Product Name1, BIOS NE5KV904 12/21/2015 Workqueue: events release_one_tty task: ffff88085b684960 ti: ffff880852884000 task.ti: ffff880852884000 RIP: 0010:[] [] strchr+0x0/0x30 RSP: 0018:ffff880852887c90 EFLAGS: 00010282 RAX: ffffffff81a5eca0 RBX: ffffffffa01979de RCX: 0000000000000004 RDX: ffff880852887d10 RSI: 000000000000002f RDI: ffffffffa01979de RBP: ffff880852887cd8 R08: 0000000000000000 R09: ffff88085f5d94d0 R10: 0000000000000195 R11: 0000000000000000 R12: ffffffffa01979de R13: ffff880852887d00 R14: ffffffffa01979de R15: ffff88085f02e840 FS: 0000000000000000(0000) GS:ffff88085f5c0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffffffa01979de CR3: 0000000001a0c000 CR4: 00000000001406e0 Stack: ffffffff812349b1 ffff880852887cb8 ffff880852887d10 ffff88085f5cd6c2 ffff880852800a80 ffffffffa01979de ffff880852800a84 0000000000000010 ffff88085bb28bd8 ffff880852887d38 ffffffff812354f0 ffff880852887d08 Call Trace: [] ? __xlate_proc_name+0x71/0xd0 [] remove_proc_entry+0x40/0x180 [] ? _raw_spin_lock_irqsave+0x41/0x60 [] ? destruct_tty_driver+0x60/0xe0 [] proc_tty_unregister_driver+0x28/0x40 [] destruct_tty_driver+0x88/0xe0 [] tty_driver_kref_put+0x1d/0x20 [] release_one_tty+0x5a/0xd0 [] process_one_work+0x139/0x420 [] worker_thread+0x121/0x450 [] ? process_scheduled_works+0x40/0x40 [] kthread+0xec/0x110 [] ? tg_rt_schedulable+0x210/0x220 [] ? kthread_freezable_should_stop+0x80/0x80 [] ret_from_fork+0x42/0x70 [] ? kthread_freezable_should_stop+0x80/0x80 Signed-off-by: nixiaoming Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/proc/proc_tty.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index 15f327bed8c6..7340c36978a3 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -14,6 +14,7 @@ #include #include #include +#include "internal.h" /* * The /proc/tty directory inodes... @@ -164,7 +165,7 @@ void proc_tty_unregister_driver(struct tty_driver *driver) if (!ent) return; - remove_proc_entry(driver->driver_name, proc_tty_driver); + remove_proc_entry(ent->name, proc_tty_driver); driver->proc_entry = NULL; } -- GitLab From 9df8ffc3035abc95311464775037d9690e8316a0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Oct 2017 11:57:15 +0200 Subject: [PATCH 2069/5498] pinctrl: adi2: Fix Kconfig build problem [ Upstream commit 1c363531dd814dc4fe10865722bf6b0f72ce4673 ] The build robot is complaining on Blackfin: drivers/pinctrl/pinctrl-adi2.c: In function 'port_setup': >> drivers/pinctrl/pinctrl-adi2.c:221:21: error: dereferencing pointer to incomplete type 'struct gpio_port_t' writew(readw(®s->port_fer) & ~BIT(offset), ^~ drivers/pinctrl/pinctrl-adi2.c: In function 'adi_gpio_ack_irq': >> drivers/pinctrl/pinctrl-adi2.c:266:18: error: dereferencing pointer to incomplete type 'struct bfin_pint_regs' if (readl(®s->invert_set) & pintbit) ^~ It seems the driver need to include and to compile. The Blackfin architecture was re-defining the Kconfig PINCTRL symbol which is not OK, so replaced this with PINCTRL_BLACKFIN_ADI2 which selects PINCTRL and PINCTRL_ADI2 just like most arches do. Further, the old GPIO driver symbol GPIO_ADI was possible to select at the same time as selecting PINCTRL. This was not working because the arch-local header contains an explicit #ifndef PINCTRL clause making compilation break if you combine them. The same is true for DEBUG_MMRS. Make sure the ADI2 pinctrl driver is not selected at the same time as the old GPIO implementation. (This should be converted to use gpiolib or pincontrol and move to drivers/...) Also make sure the old GPIO_ADI driver or DEBUG_MMRS is not selected at the same time as the new PINCTRL implementation, and only make PINCTRL_ADI2 selectable for the Blackfin families that actually have it. This way it is still possible to add e.g. I2C-based pin control expanders on the Blackfin. Cc: Steven Miao Cc: Huanhuan Feng Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/blackfin/Kconfig | 7 +++++-- arch/blackfin/Kconfig.debug | 1 + drivers/pinctrl/Kconfig | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index af76634f8d98..934573cc1134 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -318,11 +318,14 @@ config BF53x config GPIO_ADI def_bool y + depends on !PINCTRL depends on (BF51x || BF52x || BF53x || BF538 || BF539 || BF561) -config PINCTRL +config PINCTRL_BLACKFIN_ADI2 def_bool y - depends on BF54x || BF60x + depends on (BF54x || BF60x) + select PINCTRL + select PINCTRL_ADI2 config MEM_MT48LC64M4A2FB_7E bool diff --git a/arch/blackfin/Kconfig.debug b/arch/blackfin/Kconfig.debug index f3337ee03621..a93cf06a4d6f 100644 --- a/arch/blackfin/Kconfig.debug +++ b/arch/blackfin/Kconfig.debug @@ -17,6 +17,7 @@ config DEBUG_VERBOSE config DEBUG_MMRS tristate "Generate Blackfin MMR tree" + depends on !PINCTRL select DEBUG_FS help Create a tree of Blackfin MMRs via the debugfs tree. If diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c6a66de6ed72..b916a0eb799f 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -28,7 +28,8 @@ config DEBUG_PINCTRL config PINCTRL_ADI2 bool "ADI pin controller driver" - depends on BLACKFIN + depends on (BF54x || BF60x) + depends on !GPIO_ADI select PINMUX select IRQ_DOMAIN help -- GitLab From 57886e81b8bbb5d9d15c6aaab70d5ce63674de36 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 17 Oct 2017 16:18:36 +1100 Subject: [PATCH 2070/5498] raid5: Set R5_Expanded on parity devices as well as data. [ Upstream commit 235b6003fb28f0dd8e7ed8fbdb088bb548291766 ] When reshaping a fully degraded raid5/raid6 to a larger nubmer of devices, the new device(s) are not in-sync and so that can make the newly grown stripe appear to be "failed". To avoid this, we set the R5_Expanded flag to say "Even though this device is not fully in-sync, this block is safe so don't treat the device as failed for this stripe". This flag is set for data devices, not not for parity devices. Consequently, if you have a RAID6 with two devices that are partly recovered and a spare, and start a reshape to include the spare, then when the reshape gets past the point where the recovery was up to, it will think the stripes are failed and will get into an infinite loop, failing to make progress. So when contructing parity on an EXPAND_READY stripe, set R5_Expanded. Reported-by: Curt Signed-off-by: NeilBrown Signed-off-by: Shaohua Li Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/raid5.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 094f36064ff0..cf178f475131 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -1454,8 +1454,11 @@ static void ops_complete_reconstruct(void *stripe_head_ref) struct r5dev *dev = &sh->dev[i]; if (dev->written || i == pd_idx || i == qd_idx) { - if (!discard && !test_bit(R5_SkipCopy, &dev->flags)) + if (!discard && !test_bit(R5_SkipCopy, &dev->flags)) { set_bit(R5_UPTODATE, &dev->flags); + if (test_bit(STRIPE_EXPAND_READY, &sh->state)) + set_bit(R5_Expanded, &dev->flags); + } if (fua) set_bit(R5_WantFUA, &dev->flags); if (sync) -- GitLab From 47e43e619077ec024130020aa34deb3d9deb9561 Mon Sep 17 00:00:00 2001 From: Kurt Garloff Date: Tue, 17 Oct 2017 09:10:45 +0200 Subject: [PATCH 2071/5498] scsi: scsi_devinfo: Add REPORTLUN2 to EMC SYMMETRIX blacklist entry [ Upstream commit 909cf3e16a5274fe2127cf3cea5c8dba77b2c412 ] All EMC SYMMETRIX support REPORT_LUNS, even if configured to report SCSI-2 for whatever reason. Signed-off-by: Kurt Garloff Signed-off-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_devinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 7439304f01d0..2ae6f98e382b 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -160,7 +160,7 @@ static struct { {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */ {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */ {"EMC", "Invista", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, + {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_REPORTLUN2}, {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, {"easyRAID", "16P", NULL, BLIST_NOREPORTLUN}, {"easyRAID", "X6P", NULL, BLIST_NOREPORTLUN}, -- GitLab From cae8f2343a45475d0ba606fdd457ffce8cb5db01 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 Oct 2017 10:50:37 +0300 Subject: [PATCH 2072/5498] scsi: bfa: integer overflow in debugfs [ Upstream commit 3e351275655d3c84dc28abf170def9786db5176d ] We could allocate less memory than intended because we do: bfad->regdata = kzalloc(len << 2, GFP_KERNEL); The shift can overflow leading to a crash. This is debugfs code so the impact is very small. I fixed the network version of this in March with commit 13e2d5187f6b ("bna: integer overflow bug in debugfs"). Fixes: ab2a9ba189e8 ("[SCSI] bfa: add debugfs support") Signed-off-by: Dan Carpenter Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/bfa/bfad_debugfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c index 8e83d0474fe7..996f3170a7ab 100644 --- a/drivers/scsi/bfa/bfad_debugfs.c +++ b/drivers/scsi/bfa/bfad_debugfs.c @@ -254,7 +254,8 @@ bfad_debugfs_write_regrd(struct file *file, const char __user *buf, struct bfad_s *bfad = port->bfad; struct bfa_s *bfa = &bfad->bfa; struct bfa_ioc_s *ioc = &bfa->ioc; - int addr, len, rc, i; + int addr, rc, i; + u32 len; u32 *regbuf; void __iomem *rb, *reg_addr; unsigned long flags; @@ -274,7 +275,7 @@ bfad_debugfs_write_regrd(struct file *file, const char __user *buf, } rc = sscanf(kern_buf, "%x:%x", &addr, &len); - if (rc < 2) { + if (rc < 2 || len > (UINT_MAX >> 2)) { printk(KERN_INFO "bfad[%d]: %s failed to read user buf\n", bfad->inst_no, __func__); -- GitLab From 58617c76ed62f0b9a9b6b5a9b791ee5314937d1e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 16 Oct 2017 11:38:11 +0200 Subject: [PATCH 2073/5498] udf: Avoid overflow when session starts at large offset [ Upstream commit abdc0eb06964fe1d2fea6dd1391b734d0590365d ] When session starts beyond offset 2^31 the arithmetics in udf_check_vsd() would overflow. Make sure the computation is done in large enough type. Reported-by: Cezary Sliwa Signed-off-by: Jan Kara Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/udf/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index e229315bbf7a..592918e7aaba 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -706,7 +706,7 @@ static loff_t udf_check_vsd(struct super_block *sb) else sectorsize = sb->s_blocksize; - sector += (sbi->s_session << sb->s_blocksize_bits); + sector += (((loff_t)sbi->s_session) << sb->s_blocksize_bits); udf_debug("Starting at sector %u (%ld byte sectors)\n", (unsigned int)(sector >> sb->s_blocksize_bits), -- GitLab From 2d7c1288825f5c6fc85502d3a32bb1a9806354ff Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 13 Oct 2017 13:40:24 -0700 Subject: [PATCH 2074/5498] macvlan: Only deliver one copy of the frame to the macvlan interface [ Upstream commit dd6b9c2c332b40f142740d1b11fb77c653ff98ea ] This patch intoduces a slight adjustment for macvlan to address the fact that in source mode I was seeing two copies of any packet addressed to the macvlan interface being delivered where there should have been only one. The issue appears to be that one copy was delivered based on the source MAC address and then the second copy was being delivered based on the destination MAC address. To fix it I am just treating a unicast address match as though it is not a match since source based macvlan isn't supposed to be matching based on the destination MAC anyway. Fixes: 79cf79abce71 ("macvlan: add source mode") Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/macvlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index bfb0b6ec8c56..c0818ec3d427 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -440,7 +440,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) struct macvlan_dev, list); else vlan = macvlan_hash_lookup(port, eth->h_dest); - if (vlan == NULL) + if (!vlan || vlan->mode == MACVLAN_MODE_SOURCE) return RX_HANDLER_PASS; dev = vlan->dev; -- GitLab From 07aa0bc0072e49c71caad90a3ab74aa34ccd64e4 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Wed, 27 Sep 2017 09:13:34 +0800 Subject: [PATCH 2075/5498] ath9k: fix tx99 potential info leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit ee0a47186e2fa9aa1c56cadcea470ca0ba8c8692 ] When the user sets count to zero the string buffer would remain completely uninitialized which causes the kernel to parse its own stack data, potentially leading to an info leak. In addition to that, the string might be not terminated properly when the user data does not contain a 0-terminator. Signed-off-by: Miaoqing Pan Reviewed-by: Christoph Böhmwalder Signed-off-by: Kalle Valo Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/tx99.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index 2ea3589f709f..b7ab8455ffbd 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -180,6 +180,9 @@ static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, ssize_t len; int r; + if (count < 1) + return -EINVAL; + if (sc->cur_chan->nvifs > 1) return -EOPNOTSUPP; @@ -187,6 +190,8 @@ static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, if (copy_from_user(buf, user_buf, len)) return -EFAULT; + buf[len] = '\0'; + if (strtobool(buf, &start)) return -EINVAL; -- GitLab From 29d2c6506bc347732e509940c243a56891c12c1c Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Tue, 5 Dec 2017 08:45:30 -0600 Subject: [PATCH 2076/5498] usb: musb: da8xx: fix babble condition handling commit bd3486ded7a0c313a6575343e6c2b21d14476645 upstream. When babble condition happens, the musb controller might automatically turns off VBUS. On DA8xx platform, the controller generates drvvbus interrupt for turning off VBUS along with the babble interrupt. In this case, we should handle the babble interrupt first and recover from the babble condition. This change ignores the drvvbus interrupt if babble interrupt is also generated at the same time, so the babble recovery routine works properly. Signed-off-by: Bin Liu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/da8xx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 058775e647ad..ee53bcb7ef0f 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -350,7 +350,15 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; portstate(musb->port1_status |= USB_PORT_STAT_POWER); del_timer(&otg_workaround); - } else { + } else if (!(musb->int_usb & MUSB_INTR_BABBLE)){ + /* + * When babble condition happens, drvvbus interrupt + * is also generated. Ignore this drvvbus interrupt + * and let babble interrupt handler recovers the + * controller; otherwise, the host-mode flag is lost + * due to the MUSB_DEV_MODE() call below and babble + * recovery logic will not called. + */ musb->is_active = 0; MUSB_DEV_MODE(musb); otg->default_a = 0; -- GitLab From 1e6e21c74c7c866db0abdfc452937724c3d80bdc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 Dec 2017 10:01:34 +0100 Subject: [PATCH 2077/5498] Linux 3.18.89 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5077b054e410..f959c14c92bf 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 88 +SUBLEVEL = 89 EXTRAVERSION = NAME = Diseased Newt -- GitLab From d858c9a812760c526ec5eb5f6f7594311040bb12 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Mon, 27 Nov 2017 18:07:15 +0530 Subject: [PATCH 2078/5498] ASoC:msm: Add routing controls for SVA to work on BG -Enable use of PRI_TDM_TX2 an PRI_TDM_TX_3 ports for SVA usecases. Change-Id: I43d0c7df8592d5b093dbce4c54d309374d479ebb Signed-off-by: Ashish Jain --- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 80 +++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index dfa74d5d6e63..98d669a908e8 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -94,13 +94,16 @@ enum { #define SLIMBUS_5_TX_TEXT "SLIMBUS_5_TX" #define TERT_MI2S_TX_TEXT "TERT_MI2S_TX" #define QUAT_MI2S_TX_TEXT "QUAT_MI2S_TX" +#define PRI_TDM_TX_3_TEXT "PRI_TDM_TX_3" +#define PRI_TDM_TX_2_TEXT "PRI_TDM_TX_2" #define ADM_LSM_TX_TEXT "ADM_LSM_TX" #define LSM_FUNCTION_TEXT "LSM Function" static const char * const lsm_port_text[] = { "None", SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT, SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT, - TERT_MI2S_TX_TEXT, QUAT_MI2S_TX_TEXT, ADM_LSM_TX_TEXT + TERT_MI2S_TX_TEXT, QUAT_MI2S_TX_TEXT, ADM_LSM_TX_TEXT, + PRI_TDM_TX_2_TEXT, PRI_TDM_TX_3_TEXT, }; struct msm_pcm_route_bdai_pp_params { @@ -2042,6 +2045,12 @@ static int msm_routing_lsm_port_put(struct snd_kcontrol *kcontrol, case 9: lsm_port = ADM_LSM_PORT_ID; break; + case 10: + lsm_port = AFE_PORT_ID_PRIMARY_TDM_TX_2; + break; + case 11: + lsm_port = AFE_PORT_ID_PRIMARY_TDM_TX_3; + break; default: pr_err("Default lsm port"); break; @@ -8971,6 +8980,12 @@ static const struct snd_kcontrol_new lsm1_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new lsm2_mixer_controls[] = { @@ -8995,6 +9010,12 @@ static const struct snd_kcontrol_new lsm2_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new lsm3_mixer_controls[] = { @@ -9019,6 +9040,12 @@ static const struct snd_kcontrol_new lsm3_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new lsm4_mixer_controls[] = { @@ -9043,6 +9070,12 @@ static const struct snd_kcontrol_new lsm4_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new lsm5_mixer_controls[] = { @@ -9067,6 +9100,12 @@ static const struct snd_kcontrol_new lsm5_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new lsm6_mixer_controls[] = { @@ -9091,6 +9130,12 @@ static const struct snd_kcontrol_new lsm6_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new lsm7_mixer_controls[] = { @@ -9115,6 +9160,12 @@ static const struct snd_kcontrol_new lsm7_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new lsm8_mixer_controls[] = { @@ -9139,6 +9190,12 @@ static const struct snd_kcontrol_new lsm8_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), }; static const struct snd_kcontrol_new slim_fm_switch_mixer_controls = @@ -9233,6 +9290,10 @@ static const struct snd_kcontrol_new lsm_controls[] = { msm_routing_lsm_func_get, msm_routing_lsm_func_put), SOC_ENUM_EXT(QUAT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(PRI_TDM_TX_2_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(PRI_TDM_TX_3_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), /* kcontrol of lsm_port */ SOC_ENUM_EXT("LSM1 Port", lsm_port_enum, msm_routing_lsm_port_get, @@ -12528,6 +12589,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM1 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"LSM1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM1 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM1 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, {"LSM1_UL_HL", NULL, "LSM1 Mixer"}, {"LSM2 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, @@ -12537,6 +12600,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM2 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"LSM2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM2 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM2 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"LSM2_UL_HL", NULL, "LSM2 Mixer"}, @@ -12547,6 +12613,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM3 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"LSM3 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM3 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM3 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, {"LSM3_UL_HL", NULL, "LSM3 Mixer"}, @@ -12557,6 +12625,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM4 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM4 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"LSM4 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM4 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM4 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, {"LSM4_UL_HL", NULL, "LSM4 Mixer"}, {"LSM5 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, @@ -12566,6 +12636,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM5 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"LSM5 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM5 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM5 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, {"LSM5_UL_HL", NULL, "LSM5 Mixer"}, {"LSM6 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, @@ -12574,6 +12646,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM6 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, {"LSM6 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM6 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM6 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, {"LSM6_UL_HL", NULL, "LSM6 Mixer"}, @@ -12583,6 +12657,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM7 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, {"LSM7 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM7 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM7 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM7 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, {"LSM7_UL_HL", NULL, "LSM7 Mixer"}, @@ -12592,6 +12668,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LSM8 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, {"LSM8 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, {"LSM8 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM8 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"LSM8 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, {"LSM8_UL_HL", NULL, "LSM8 Mixer"}, -- GitLab From bce8a5bfccb5c03d49b16c643a016806d16531b0 Mon Sep 17 00:00:00 2001 From: jiangjia Date: Thu, 14 Dec 2017 17:38:07 +0800 Subject: [PATCH 2079/5498] ARM: dts: msm: Modify modem and cnss memory region for SDW2500 As modem need 6MB more memory to support CDMA, it need to change the reserved memory of modem. And as the reserved memory of modem and cnss are together, it also need to modify the reserved memory of cnss. Change-Id: Ib18332970af9ac4193d5d61752b2beb54296e4d6 Signed-off-by: Jiangjiang Shen --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index d5250ecf8d92..e3fa0279164c 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -111,6 +111,14 @@ status = "okay"; }; +&modem_adsp_mem { + reg = <0x0 0x88000000 0x0 0x05600000>; +}; + +&peripheral_mem { + reg = <0x0 0x8d600000 0x0 0x0500000>; +}; + &i2c_1 { status = "disabled"; }; -- GitLab From 583039650d71e628b31a2d2930f95042d9c28e00 Mon Sep 17 00:00:00 2001 From: raghavendra ambadas Date: Thu, 21 Dec 2017 09:38:50 +0530 Subject: [PATCH 2080/5498] msm: mdss: send correct bpp value for BW calc After bootup the BW vote is higher due to wrong bpp value used for calculation. Due to higher bpp value, BW voting is very high before first suspend. Change-Id: I4e7bc33bcdbab7708127bed09895af149971253e Signed-off-by: Raghavendra Ambadas --- drivers/video/msm/mdss/mdp3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index 41c9962a1469..7fbe90e07baf 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -2282,7 +2282,8 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) pr_err("invalid bus handle %d\n", bus_handle->handle); return -EINVAL; } - mdp3_calc_dma_res(panel_info, &mdp_clk_rate, &ab, &ib, panel_info->bpp); + mdp3_calc_dma_res(panel_info, &mdp_clk_rate, &ab, + &ib, MAX_BPP_SUPPORTED); mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE, MDP3_CLIENT_DMA_P); -- GitLab From beeb90f88d6dd0b2c1468281653c0311943f8c64 Mon Sep 17 00:00:00 2001 From: jiangjia Date: Wed, 20 Dec 2017 16:29:42 +0800 Subject: [PATCH 2081/5498] defconfig: msm8909: enable lz4 for zram As lz4 is faster than the default lzo, enable lz4 for zram. Change-Id: Iaa77730abe489e3238c39c0fc0934a553fde7129 Signed-off-by: Jiangjiang Shen --- arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index d7f3d0cfdd4d..2f35e47622b7 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -213,6 +213,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_NAND=y CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_QSEECOM=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index ef18722a7b95..1ba662efbb3f 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -217,6 +217,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_NAND=y CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_QSEECOM=y -- GitLab From 0034265c44437b289bbbcefe64b0ee31254d74d3 Mon Sep 17 00:00:00 2001 From: Apurva Srivastava Date: Mon, 11 Dec 2017 18:22:32 +0530 Subject: [PATCH 2082/5498] Kernel : Enable HID Support Add support for HID drivers in kernel. CRs-Fixed: 2158790 Change-Id: I12471e9e366ce90a4c6446054a6ac0b9d5d26a36 Signed-off-by: Apurva Srivastava --- arch/arm/configs/mdm9607-perf_defconfig | 6 ++++++ arch/arm/configs/mdm9607_defconfig | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/arch/arm/configs/mdm9607-perf_defconfig b/arch/arm/configs/mdm9607-perf_defconfig index b826967a072a..620699914b91 100644 --- a/arch/arm/configs/mdm9607-perf_defconfig +++ b/arch/arm/configs/mdm9607-perf_defconfig @@ -257,6 +257,12 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_SOC=y CONFIG_SND_SOC_MDM9607=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y diff --git a/arch/arm/configs/mdm9607_defconfig b/arch/arm/configs/mdm9607_defconfig index f0d85ba839c5..2d71e5de991b 100644 --- a/arch/arm/configs/mdm9607_defconfig +++ b/arch/arm/configs/mdm9607_defconfig @@ -257,6 +257,12 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_SOC=y CONFIG_SND_SOC_MDM9607=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y -- GitLab From e282251b985bf3646e12c82e2d6cd540c9b7eb50 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 5 Oct 2017 23:39:39 +0530 Subject: [PATCH 2083/5498] msm: ipa: Fix to validate routing table index for filter exception Fix to validate routing table index for filter action IPA_PASS_TO_EXCEPTION case to avoid ipa assert during commit filter rule. Change-Id: I957f7ffc415ea1a042f6b3a948e94410d41b2262 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa_flt.c | 10 ++++++++++ drivers/platform/msm/ipa/ipa_v3/ipa_flt.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c index c0af295c7362..834f028d3e48 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c @@ -1039,6 +1039,11 @@ static int __ipa_add_flt_rule(struct ipa_flt_tbl *tbl, enum ipa_ip_type ip, goto error; } } + } else { + if (rule->rt_tbl_idx > 0) { + IPAERR_RL("invalid RT tbl\n"); + goto error; + } } entry = kmem_cache_zalloc(ipa_ctx->flt_rule_cache, GFP_KERNEL); @@ -1160,6 +1165,11 @@ static int __ipa_mdfy_flt_rule(struct ipa_flt_rule_mdfy *frule, goto error; } } + } else { + if (frule->rule.rt_tbl_idx > 0) { + IPAERR_RL("invalid RT tbl\n"); + goto error; + } } entry->rule = frule->rule; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 0577c8713760..dc168e59cd80 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -1096,6 +1096,11 @@ static int __ipa_validate_flt_rule(const struct ipa_flt_rule *rule, goto error; } } + } else { + if (rule->rt_tbl_idx > 0) { + IPAERR("invalid RT tbl\n"); + goto error; + } } if (rule->rule_id) { @@ -1347,6 +1352,11 @@ static int __ipa_mdfy_flt_rule(struct ipa_flt_rule_mdfy *frule, goto error; } } + } else { + if (frule->rule.rt_tbl_idx > 0) { + IPAERR("invalid RT tbl\n"); + goto error; + } } entry->rule = frule->rule; -- GitLab From 81e31dcbca49f2ac9fc7e76df120b372f30a1530 Mon Sep 17 00:00:00 2001 From: Rajesh Kemisetti Date: Sat, 12 Nov 2016 00:43:22 +0530 Subject: [PATCH 2084/5498] msm: kgsl: Check MMU type for memory and pagetable operations Check MMU type for below operations to make NOMMU functional: - adreno_iommu_set_pt_ctx() tries to set pagetable during context switch without really checking on type of MMU. - skip tracking of gpuaddr in case of NoMMU during kgsl_mem_entry_track_gpuaddr(). - In case of nommu the function kgsl_allocate_global() should always allocate contiguous memory from CMA. Change-Id: I8cb59e1475376167c7a8a60c54df0939597f5083 Signed-off-by: Rajesh Kemisetti --- drivers/gpu/msm/adreno_iommu.c | 11 +++++++++++ drivers/gpu/msm/kgsl.c | 4 +++- drivers/gpu/msm/kgsl_sharedmem.h | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/adreno_iommu.c b/drivers/gpu/msm/adreno_iommu.c index aa00dcb84185..4bb7f6286664 100644 --- a/drivers/gpu/msm/adreno_iommu.c +++ b/drivers/gpu/msm/adreno_iommu.c @@ -856,6 +856,17 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, int result = 0; int cpu_path = 0; + /* Just do the context switch incase of NOMMU */ + if (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE) { + if ((!(flags & ADRENO_CONTEXT_SWITCH_FORCE_GPU)) && + adreno_isidle(device)) + _set_ctxt_cpu(rb, drawctxt); + else + result = _set_ctxt_gpu(rb, drawctxt); + + return result; + } + if (rb->drawctxt_active) cur_pt = rb->drawctxt_active->base.proc_priv->pagetable; diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index a58d6e8b2080..0af8590f0f60 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -346,8 +346,10 @@ static int kgsl_mem_entry_track_gpuaddr(struct kgsl_device *device, /* * If SVM is enabled for this object then the address needs to be * assigned elsewhere + * Also do not proceed further in case of NoMMU. */ - if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) + if (kgsl_memdesc_use_cpu_map(&entry->memdesc) || + (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE)) return 0; pagetable = kgsl_memdesc_is_secured(&entry->memdesc) ? diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 1ec39c221dd1..47b4decbbf05 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -268,7 +268,8 @@ static inline int kgsl_allocate_global(struct kgsl_device *device, memdesc->flags = flags; memdesc->priv = priv; - if ((memdesc->priv & KGSL_MEMDESC_CONTIG) != 0) + if (((memdesc->priv & KGSL_MEMDESC_CONTIG) != 0) || + (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE)) ret = kgsl_sharedmem_alloc_contig(device, memdesc, (size_t) size); else { -- GitLab From a59008538828c5a4ad2c56d4fca76589c03593ad Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 20 Dec 2017 16:21:00 +0100 Subject: [PATCH 2085/5498] ANDROID: binder: Remove obsolete proc waitqueue. It was no longer being used. Change-Id: I7fc42b76f688a459ad990f59fbd7006b96bb91a6 Signed-off-by: Martijn Coenen --- drivers/staging/android/binder.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 0ed2fb01dc9e..f95b559de0f8 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -509,8 +509,6 @@ struct binder_priority { * (protected by @inner_lock) * @todo: list of work for this process * (protected by @inner_lock) - * @wait: wait queue head to wait for proc work - * (invariant after initialized) * @stats: per-process binder statistics * (atomics, no lock needed) * @delivered_death: list of delivered death notification @@ -551,7 +549,6 @@ struct binder_proc { bool is_dead; struct list_head todo; - wait_queue_head_t wait; struct binder_stats stats; struct list_head delivered_death; int max_threads; -- GitLab From 1514d9b155edd3539032ad2d66e1cab4bd1338c4 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 21 Dec 2017 15:04:45 +0530 Subject: [PATCH 2086/5498] soc: qcom: bgrsb: Add support to configure RSB on BG Add support for configure resolution and report interval time for RSB on BG Change-Id: I26efabbbe5c528435501d76789743bd0cbfc2ebd Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 266 +++++++++++++++++++++++--------------- 1 file changed, 162 insertions(+), 104 deletions(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index df666100f9b4..578e9ebcf65b 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -43,10 +43,10 @@ #define BGRSB_BGWEAR_SUBSYS "bg-wear" +#define BGRSB_POWER_CALIBRATION 2 #define BGRSB_POWER_ENABLE 1 #define BGRSB_POWER_DISABLE 0 - struct bgrsb_regulator { struct regulator *regldo11; struct regulator *regldo15; @@ -91,6 +91,8 @@ struct bgrsb_priv { struct work_struct rsb_up_work; struct work_struct rsb_down_work; + struct work_struct rsb_calibration_work; + struct work_struct glink_work; struct workqueue_struct *bgrsb_event_wq; @@ -113,6 +115,9 @@ struct bgrsb_priv { struct device *ldev; wait_queue_head_t link_state_wait; + + uint32_t calbrtion_intrvl; + uint32_t calbrtion_cpi; }; static void *bgrsb_drv; @@ -162,65 +167,6 @@ static void bgrsb_glink_notify_state(void *handle, const void *priv, } } -static int bgrsb_configr_rsb(struct bgrsb_priv *dev, bool enable) -{ - int rc = 0; - struct bgrsb_msg req = {0}; - uint32_t resp = 0; - - mutex_lock(&dev->glink_mutex); - init_completion(&dev->bg_resp_cmplt); - init_completion(&dev->tx_done); - - rc = glink_queue_rx_intent(dev->handle, - (void *)dev, BGRSB_GLINK_INTENT_SIZE); - - if (rc) { - pr_err("Failed to queue intent\n"); - goto err_ret; - } - - req.cmd_id = 0x01; - req.data = enable ? 0x01 : 0x00; - - - rc = glink_tx(dev->handle, (void *)dev, &req, - BGRSB_MSG_SIZE, GLINK_TX_REQ_INTENT); - if (rc) { - pr_err("Failed to send command\n"); - goto err_ret; - } - - rc = wait_for_completion_timeout(&dev->tx_done, - msecs_to_jiffies(TIMEOUT_MS*2)); - if (!rc) { - pr_err("Timed out waiting sending command\n"); - rc = -ETIMEDOUT; - goto err_ret; - } - - - rc = wait_for_completion_timeout(&dev->bg_resp_cmplt, - msecs_to_jiffies(TIMEOUT_MS)); - if (!rc) { - pr_err("Timed out waiting for response\n"); - rc = -ETIMEDOUT; - goto err_ret; - } - - resp = *(uint32_t *)dev->rx_buf; - if (!(resp == 0x01)) { - pr_err("Bad RSB Configure response\n"); - rc = -EINVAL; - goto err_ret; - } - rc = 0; - -err_ret: - mutex_unlock(&dev->glink_mutex); - return rc; -} - static void bgrsb_glink_notify_tx_done(void *handle, const void *priv, const void *pkt_priv, const void *ptr) { @@ -405,6 +351,74 @@ static void bgrsb_bgdown_work(struct work_struct *work) dev->bgrsb_current_state = BGRSB_STATE_INIT; } +static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) +{ + int rc = 0; + uint8_t resp = 0; + + if (!dev->chnl_state) + return -ENODEV; + + mutex_lock(&dev->glink_mutex); + init_completion(&dev->tx_done); + init_completion(&dev->bg_resp_cmplt); + + rc = glink_queue_rx_intent(dev->handle, + (void *)dev, BGRSB_GLINK_INTENT_SIZE); + + if (rc) { + pr_err("Failed to queue intent\n"); + goto err_ret; + } + + rc = glink_tx(dev->handle, (void *)dev, msg, + len, GLINK_TX_REQ_INTENT); + if (rc) { + pr_err("Failed to send command\n"); + goto err_ret; + } + + rc = wait_for_completion_timeout(&dev->tx_done, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("Timed out waiting for Command to send\n"); + rc = -ETIMEDOUT; + goto err_ret; + } + + rc = wait_for_completion_timeout(&dev->bg_resp_cmplt, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("Timed out waiting for response\n"); + rc = -ETIMEDOUT; + goto err_ret; + } + + resp = *(uint8_t *)dev->rx_buf; + if (!(resp == 0x01)) { + pr_err("Bad RSB response\n"); + rc = -EINVAL; + goto err_ret; + } + rc = 0; + +err_ret: + mutex_unlock(&dev->glink_mutex); + return rc; +} + +static int bgrsb_configr_rsb(struct bgrsb_priv *dev, bool enable) +{ + int rc = 0; + struct bgrsb_msg req = {0}; + + req.cmd_id = 0x01; + req.data = enable ? 0x01 : 0x00; + + rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); + return rc; +} + static void bgrsb_bgup_work(struct work_struct *work) { int rc = 0; @@ -415,7 +429,7 @@ static void bgrsb_bgup_work(struct work_struct *work) rc = wait_event_timeout(dev->link_state_wait, (dev->chnl_state == true), - msecs_to_jiffies(TIMEOUT_MS*4)); + msecs_to_jiffies(TIMEOUT_MS*2)); if (rc == 0) { pr_err("Glink channel connection time out\n"); return; @@ -482,38 +496,6 @@ static int bgrsb_ssr_register(struct bgrsb_priv *dev) return 0; } -static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) -{ - int rc = 0; - - if (!dev->chnl_state) - return -ENODEV; - - mutex_lock(&dev->glink_mutex); - init_completion(&dev->tx_done); - - rc = glink_tx(dev->handle, (void *)dev, msg, - len, GLINK_TX_REQ_INTENT); - if (rc) { - pr_err("Failed to send command\n"); - goto err_ret; - } - - rc = wait_for_completion_timeout(&dev->tx_done, - msecs_to_jiffies(TIMEOUT_MS)); - if (!rc) { - pr_err("Timed out waiting for Command to send\n"); - rc = -ETIMEDOUT; - goto err_ret; - } - rc = 0; - -err_ret: - mutex_unlock(&dev->glink_mutex); - return rc; -} - - static void bgrsb_enable_rsb(struct work_struct *work) { int rc = 0; @@ -567,29 +549,104 @@ static void bgrsb_disable_rsb(struct work_struct *work) } } -static int store_enable(struct device *pdev, struct device_attribute *attr, - const char *buff, size_t count) +static void bgrsb_calibration(struct work_struct *work) { - long pwr_st; + int rc = 0; + struct bgrsb_msg req = {0}; + struct bgrsb_priv *dev = + container_of(work, struct bgrsb_priv, + rsb_calibration_work); + + req.cmd_id = 0x03; + req.data = dev->calbrtion_cpi; + + rc = bgrsb_tx_msg(dev, &req, 5); + if (rc != 0) { + pr_err("Failed to send resolution value to BG\n"); + return; + } + + req.cmd_id = 0x04; + req.data = dev->calbrtion_intrvl; + + rc = bgrsb_tx_msg(dev, &req, 5); + if (rc != 0) { + pr_err("Failed to send interval value to BG\n"); + return; + } + pr_debug("RSB Calibbered\n"); +} + +static int split_bg_work(struct bgrsb_priv *dev, char *str) +{ + long val; int ret; - struct bgrsb_priv *dev = dev_get_drvdata(pdev); + char *tmp; + + tmp = strsep(&str, ":"); + if (!tmp) + return -EINVAL; - ret = kstrtol(buff, 10, &pwr_st); + ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; - if (pwr_st == BGRSB_POWER_ENABLE) { - if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) - return 0; - queue_work(dev->bgrsb_wq, &dev->rsb_up_work); - } else if (pwr_st == BGRSB_POWER_DISABLE) { + switch (val) { + case BGRSB_POWER_DISABLE: if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) return 0; queue_work(dev->bgrsb_wq, &dev->rsb_down_work); + break; + case BGRSB_POWER_ENABLE: + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) + return 0; + queue_work(dev->bgrsb_wq, &dev->rsb_up_work); + break; + case BGRSB_POWER_CALIBRATION: + tmp = strsep(&str, ":"); + if (!tmp) + return -EINVAL; + + ret = kstrtol(tmp, 10, &val); + if (ret < 0) + return ret; + + dev->calbrtion_intrvl = (uint32_t)val; + + tmp = strsep(&str, ":"); + if (!tmp) + return -EINVAL; + + ret = kstrtol(tmp, 10, &val); + if (ret < 0) + return ret; + + dev->calbrtion_cpi = (uint32_t)val; + + queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work); + break; } return 0; } +static int store_enable(struct device *pdev, struct device_attribute *attr, + const char *buff, size_t count) +{ + int rc; + struct bgrsb_priv *dev = dev_get_drvdata(pdev); + char *arr = kstrdup(buff, GFP_KERNEL); + + if (!arr) + goto err_ret; + + rc = split_bg_work(dev, arr); + if (rc != 0) + pr_err("Not able to process request\n"); + +err_ret: + return count; +} + static int show_enable(struct device *dev, struct device_attribute *attr, char *buff) { @@ -640,6 +697,7 @@ static int bgrsb_init(struct bgrsb_priv *dev) INIT_WORK(&dev->bg_down_work, bgrsb_bgdown_work); INIT_WORK(&dev->rsb_up_work, bgrsb_enable_rsb); INIT_WORK(&dev->rsb_down_work, bgrsb_disable_rsb); + INIT_WORK(&dev->rsb_calibration_work, bgrsb_calibration); return 0; @@ -771,7 +829,7 @@ static struct platform_driver bg_rsb_driver = { .of_match_table = bg_rsb_of_match, }, .probe = bg_rsb_probe, - .remove = bg_rsb_remove, + .remove = bg_rsb_remove, .resume = bg_rsb_resume, .suspend = bg_rsb_suspend, }; -- GitLab From 2aa3296397e4622c89e8729c6f14d82e4b5a79a1 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Thu, 14 Dec 2017 17:01:10 +0800 Subject: [PATCH 2087/5498] ARM: dts: msm: camera bringup for 8009 SDW2500 Add configuration for camera sensor nodes with required properties for wearable device and make necessary dtsi changes for camera sub-system when using pm660. Change-Id: Ia0061888b74b0de458fb6def88ea7f9d3ed16072 Signed-off-by: Shuai Wang --- .../msm8909w-pm660-camera-sensor-wtp-v1.dtsi | 85 ++++++++ .../boot/dts/qcom/msm8909w-pm660-camera.dtsi | 200 ++++++++++++++++++ .../dts/qcom/msm8909w-pm660-regulator.dtsi | 6 +- 3 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/msm8909w-pm660-camera-sensor-wtp-v1.dtsi create mode 100644 arch/arm/boot/dts/qcom/msm8909w-pm660-camera.dtsi diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-camera-sensor-wtp-v1.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-camera-sensor-wtp-v1.dtsi new file mode 100644 index 000000000000..de01e27796a9 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-camera-sensor-wtp-v1.dtsi @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&i2c_2 { + status = "okay"; + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x2>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <90>; + cam_vio-supply = <&pm660_l13>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <1800000>; + qcom,cam-vreg-max-voltage = <1800000>; + qcom,cam-vreg-op-mode = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk1_default + &cam_sensor_front_default>; + pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front_sleep>; + gpios = <&msm_gpio 27 0>, + <&msm_gpio 28 0>, + <&msm_gpio 23 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", + "CAM_RESET", + "CAM_VANA"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "ok"; + clocks = <&clock_gcc clk_mclk1_clk_src>, + <&clock_gcc clk_gcc_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <90>; + cam_vio-supply = <&pm660_l13>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <1800000>; + qcom,cam-vreg-max-voltage = <1800000>; + qcom,cam-vreg-op-mode = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk1_default + &cam_sensor_front_default>; + pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front_sleep>; + gpios = <&msm_gpio 27 0>, + <&msm_gpio 28 0>, + <&msm_gpio 23 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", + "CAM_RESET", + "CAM_VANA"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + status = "ok"; + clocks = <&clock_gcc clk_mclk1_clk_src>, + <&clock_gcc clk_gcc_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-camera.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-camera.dtsi new file mode 100644 index 000000000000..fff81944062d --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-camera.dtsi @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + qcom,msm-cam@1800000{ + compatible = "qcom,msm-cam"; + reg = <0x1b00000 0x40000>; + reg-names = "msm-cam"; + status = "ok"; + bus-vectors = "suspend", "svs", "nominal", "turbo"; + qcom,bus-votes = <0 320000000 640000000 640000000>; + }; + + qcom,csiphy@1b0ac00 { + cell-index = <0>; + compatible = "qcom,csiphy-v3.1", "qcom,csiphy"; + reg = <0x1b0ac00 0x200>, + <0x1b00030 0x4>; + reg-names = "csiphy", "csiphy_clk_mux"; + interrupts = <0 78 0>; + interrupt-names = "csiphy"; + clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>, + <&clock_gcc clk_gcc_camss_ispif_ahb_clk>, + <&clock_gcc clk_csi0phytimer_clk_src>, + <&clock_gcc clk_gcc_camss_csi0phytimer_clk>, + <&clock_gcc clk_camss_top_ahb_clk_src>, + <&clock_gcc clk_gcc_camss_csi0phy_clk>, + <&clock_gcc clk_gcc_camss_csi1phy_clk>, + <&clock_gcc clk_gcc_camss_ahb_clk>; + clock-names = "camss_top_ahb_clk", "ispif_ahb_clk", + "csiphy_timer_src_clk", "csiphy_timer_clk", + "camss_ahb_src", "csi0_phy_clk", "csi1_phy_clk", + "camss_ahb_clk"; + qcom,clock-rates = <0 0 200000000 0 0 0 0 0>; + }; + + qcom,csid@1b08000 { + cell-index = <0>; + compatible = "qcom,csid-v3.1", "qcom,csid"; + reg = <0x1b08000 0x100>; + reg-names = "csid"; + interrupts = <0 49 0>; + interrupt-names = "csid"; + qcom,csi-vdd-voltage = <1200000>; + qcom,mipi-csi-vdd-supply = <&pm660_l2>; + clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>, + <&clock_gcc clk_gcc_camss_top_ahb_clk>, + <&clock_gcc clk_gcc_camss_csi0_ahb_clk>, + <&clock_gcc clk_csi0_clk_src>, + <&clock_gcc clk_gcc_camss_csi0_clk>, + <&clock_gcc clk_gcc_camss_csi0pix_clk>, + <&clock_gcc clk_gcc_camss_csi0rdi_clk>, + <&clock_gcc clk_gcc_camss_ahb_clk>; + clock-names = "ispif_ahb_clk", "camss_top_ahb_clk", + "csi_ahb_clk", "csi_src_clk", + "csi_clk", "csi_pix_clk", + "csi_rdi_clk", "camss_ahb_clk"; + qcom,clock-rates = <40000000 0 0 200000000 0 0 0 0>; + }; + + qcom,csid@1b08400 { + cell-index = <1>; + compatible = "qcom,csid-v3.1", "qcom,csid"; + reg = <0x1b08400 0x100>; + reg-names = "csid"; + interrupts = <0 50 0>; + interrupt-names = "csid"; + qcom,csi-vdd-voltage = <1200000>; + qcom,mipi-csi-vdd-supply = <&pm660_l2>; + clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>, + <&clock_gcc clk_gcc_camss_top_ahb_clk>, + <&clock_gcc clk_gcc_camss_csi1_ahb_clk>, + <&clock_gcc clk_csi1_clk_src>, + <&clock_gcc clk_gcc_camss_csi1_clk>, + <&clock_gcc clk_gcc_camss_csi1pix_clk>, + <&clock_gcc clk_gcc_camss_csi1rdi_clk>, + <&clock_gcc clk_gcc_camss_ahb_clk>, + <&clock_gcc clk_gcc_camss_csi1phy_clk>; + clock-names = "ispif_ahb_clk", "camss_top_ahb_clk", + "csi_ahb_clk", "csi_src_clk", + "csi_clk", "csi_pix_clk", + "csi_rdi_clk", "camss_ahb_clk", "camss_csi1_phy"; + qcom,clock-rates = <40000000 0 0 200000000 0 0 0 0 0>; + }; + + qcom,ispif@1b0a000 { + cell-index = <0>; + compatible = "qcom,ispif"; + reg = <0x1b0a000 0x500>, + <0x1b00020 0x10>; + reg-names = "ispif", "csi_clk_mux"; + interrupts = <0 51 0>; + interrupt-names = "ispif"; + qcom,num-isps = <0x1>; + vfe0_vdd_supply = <&gdsc_vfe>; + clocks = <&clock_gcc clk_gcc_camss_top_ahb_clk>, + <&clock_gcc clk_gcc_camss_ispif_ahb_clk>, + + <&clock_gcc clk_csi0_clk_src>, + <&clock_gcc clk_gcc_camss_csi0_clk>, + <&clock_gcc clk_gcc_camss_csi0rdi_clk>, + <&clock_gcc clk_gcc_camss_csi0pix_clk>, + <&clock_gcc clk_csi1_clk_src>, + <&clock_gcc clk_gcc_camss_csi1_clk>, + <&clock_gcc clk_gcc_camss_csi1rdi_clk>, + <&clock_gcc clk_gcc_camss_csi1pix_clk>, + <&clock_gcc clk_vfe0_clk_src>, + <&clock_gcc clk_gcc_camss_vfe0_clk>, + <&clock_gcc clk_gcc_camss_csi_vfe0_clk>; + + clock-names = "camss_top_ahb_clk", "ispif_ahb_clk", + "csi0_src_clk", "csi0_clk", + "csi0_rdi_clk", "csi0_pix_clk", + "csi1_src_clk", "csi1_clk", + "csi1_rdi_clk", "csi1_pix_clk", + "vfe0_clk_src", "camss_vfe_vfe0_clk", + "camss_csi_vfe0_clk"; + qcom,clock-rates = <0 40000000 + 200000000 0 0 0 + 200000000 0 0 0 + 0 0 0>; + qcom,clock-control = "NO_SET_RATE", "SET_RATE", + "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", + "SET_RATE", "NO_SET_RATE", "NO_SET_RATE", "NO_SET_RATE", + "INIT_RATE", "NO_SET_RATE", "NO_SET_RATE"; + }; + + qcom,vfe@1b10000 { + cell-index = <0>; + compatible = "qcom,vfe32"; + reg = <0x1b10000 0x830>, + <0x1b40000 0x200>; + reg-names = "vfe", "vfe_vbif"; + interrupts = <0 52 0>; + interrupt-names = "vfe"; + vdd-supply = <&gdsc_vfe>; + clocks = <&clock_gcc clk_gcc_camss_ispif_ahb_clk>, + <&clock_gcc clk_vfe0_clk_src>, + <&clock_gcc clk_gcc_camss_vfe0_clk>, + <&clock_gcc clk_gcc_camss_csi_vfe0_clk>, + <&clock_gcc clk_gcc_camss_vfe_ahb_clk>, + <&clock_gcc clk_gcc_camss_vfe_axi_clk>, + <&clock_gcc clk_gcc_camss_ahb_clk>, + <&clock_gcc clk_gcc_camss_top_ahb_clk>; + clock-names = "camss_top_ahb_clk", "vfe_clk_src", + "camss_vfe_vfe_clk", "camss_csi_vfe_clk", "iface_clk", + "bus_clk", "camss_ahb_clk", "ispif_ahb_clk"; + qcom,clock-rates = <40000000 266670000 0 0 0 0 0 0>; + + qos-entries = <8>; + qos-regs = <0x7BC 0x7C0 0x7C4 0x7C8 0x7CC 0x7D0 + 0x7D4 0x798>; + qos-settings = <0xAAA5AAA5 0xAAA5AAA5 0xAAA5AAA5 + 0xAAA5AAA5 0xAAA5AAA5 0xAAA5AAA5 + 0xAAA5AAA5 0x00010000>; + vbif-entries = <1>; + vbif-regs = <0x04>; + vbif-settings = <0x1>; + ds-entries = <15>; + ds-regs = <0x7D8 0x7DC 0x7E0 0x7E4 0x7E8 + 0x7EC 0x7F0 0x7F4 0x7F8 0x7FC 0x800 + 0x804 0x808 0x80C 0x810>; + ds-settings = <0xCCCC1111 0xCCCC1111 0xCCCC1111 + 0xCCCC1111 0xCCCC1111 0xCCCC1111 + 0xCCCC1111 0xCCCC1111 0xCCCC1111 + 0xCCCC1111 0xCCCC1111 0xCCCC1111 + 0xCCCC1111 0xCCCC1111 0x00000103>; + + bus-util-factor = <1024>; + }; + + qcom,cam_smmu { + status = "ok"; + compatible = "qcom,msm-cam-smmu"; + msm_cam_smmu_cb1: msm_cam_smmu_cb1 { + compatible = "qcom,qsmmu-cam-cb"; + iommus = <&apps_iommu 0x400>; + label = "vfe"; + qcom,scratch-buf-support; + }; + }; + + qcom,irqrouter@1b00000 { + status = "ok"; + cell-index = <0>; + compatible = "qcom,irqrouter"; + reg = <0x1b00000 0x100>; + reg-names = "irqrouter"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi index c30f40e7e55a..daf68da460d3 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi @@ -130,9 +130,9 @@ rpm-regulator-ldoa2 { status = "okay"; pm660_l2: regulator-l2 { - regulator-min-microvolt = <1300000>; - regulator-max-microvolt = <1300000>; - qcom,init-voltage = <1300000>; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + qcom,init-voltage = <1200000>; status = "okay"; }; }; -- GitLab From 3cd61cf695d1e73d702829bfae7a4f5247d5cc09 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Thu, 14 Dec 2017 21:28:00 +0800 Subject: [PATCH 2088/5498] ARM: dts: msm: add camera sensor support for 8009 SDW2500 include the configuration file for camera sensor nodes and camera sub modules in dts file. Change-Id: Id3873e0099d01da884252ba5e10460e60f5dd126 Signed-off-by: Shuai Wang --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index d5250ecf8d92..471bbc0630dd 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -18,6 +18,8 @@ #include "msm8909w.dtsi" #include "msm8909w-memory.dtsi" #include "msm8909w-pm660-mtp.dtsi" +#include "msm8909w-pm660-camera.dtsi" +#include "msm8909w-pm660-camera-sensor-wtp-v1.dtsi" / { model = "Qualcomm Technologies, Inc. MSM8909W-PM660 V1 WTP"; -- GitLab From a6104efdc866b54c8ee3a229c2baabf52d4ed9e6 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 7 Dec 2017 15:55:02 +0530 Subject: [PATCH 2089/5498] soc: qcom: Enable wcd glink driver for BG Enable wcd glink driver for platform with BG support. Also add support to configure the glink edge which is used for glink communication between the msm and the target device. Change-Id: I1b4a888a40a8f4187bc384494fe3ac97a864756a Signed-off-by: Ashish Jain --- .../bindings/sound/qcom-audio-dev.txt | 4 +++ drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/wcd-dsp-glink.c | 27 +++++++++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 4d0b90f0e5ca..3810d7c57633 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -411,6 +411,9 @@ Required properties: Required properties: - compatible : "qcom,wcd-dsp-glink" + - qcom,msm-codec-glink-edge: Name of the glink edge which is used + for IPC. + If no name is set, it defaults to "wdsp" Example: @@ -689,6 +692,7 @@ Example: wcd_dsp_glink { compatible = "qcom,wcd-dsp-glink"; + qcom,msm-codec-glink-edge = "bg"; }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 67827f911426..d1edcdb78917 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -932,7 +932,7 @@ config MSM_BG_GLINK config WCD_DSP_GLINK tristate "WCD DSP GLINK Driver" depends on MSM_GLINK - default y if SND_SOC_WCD934X=y + default y if (SND_SOC_WCD934X=y || MSM_BG_GLINK=y) help This option enables driver which provides communication interface between MSM and WCD DSP over glink transport protocol. This driver diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index 03eef82918d6..951ce04dfff7 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "sound/wcd-dsp-glink.h" @@ -40,12 +41,14 @@ #define RESP_QUEUE_SIZE 3 #define QOS_PKT_SIZE 1024 #define TIMEOUT_MS 1000 +#define GLINK_EDGE_MAX 16 struct wdsp_glink_dev { struct class *cls; struct device *dev; struct cdev cdev; dev_t dev_num; + char glink_edge[GLINK_EDGE_MAX]; }; struct wdsp_glink_rsp_que { @@ -115,6 +118,7 @@ struct wdsp_glink_priv { u8 no_of_channels; struct work_struct ch_open_cls_wrk; struct workqueue_struct *work_queue; + const char *glink_edge; wait_queue_head_t link_state_wait; @@ -401,7 +405,7 @@ static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch) if (!ch->handle) { memset(&open_cfg, 0, sizeof(open_cfg)); open_cfg.options = GLINK_OPT_INITIAL_XPORT; - open_cfg.edge = WDSP_EDGE; + open_cfg.edge = wpriv->glink_edge; open_cfg.notify_rx = wdsp_glink_notify_rx; open_cfg.notify_tx_done = wdsp_glink_notify_tx_done; open_cfg.notify_tx_abort = wdsp_glink_notify_tx_abort; @@ -511,10 +515,8 @@ static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info, wpriv = (struct wdsp_glink_priv *)priv; - mutex_lock(&wpriv->glink_mutex); wpriv->glink_state.link_state = cb_info->link_state; wake_up(&wpriv->link_state_wait); - mutex_unlock(&wpriv->glink_mutex); queue_work(wpriv->work_queue, &wpriv->ch_open_cls_wrk); } @@ -631,7 +633,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, /* Register glink link_state notification */ link_info.glink_link_state_notif_cb = wdsp_glink_link_state_cb; link_info.transport = NULL; - link_info.edge = WDSP_EDGE; + link_info.edge = wpriv->glink_edge; wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; wpriv->glink_state.handle = glink_register_link_state_cb(&link_info, @@ -971,7 +973,7 @@ static int wdsp_glink_open(struct inode *inode, struct file *file) ret = -EINVAL; goto err_wq; } - + wpriv->glink_edge = wdev->glink_edge; wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; init_completion(&wpriv->rsp_complete); init_waitqueue_head(&wpriv->link_state_wait); @@ -1087,6 +1089,7 @@ static int wdsp_glink_probe(struct platform_device *pdev) { int ret; struct wdsp_glink_dev *wdev; + const char *str = NULL; wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); if (!wdev) { @@ -1126,6 +1129,20 @@ static int wdsp_glink_probe(struct platform_device *pdev) __func__, ret); goto err_cdev_add; } + ret = of_property_read_string(pdev->dev.of_node, + "qcom,msm-codec-glink-edge", &str); + if (ret < 0) { + strlcpy(wdev->glink_edge, WDSP_EDGE, GLINK_EDGE_MAX); + dev_info(&pdev->dev, + "%s: qcom,msm-codec-glink-edge not set use default %s\n", + __func__, wdev->glink_edge); + ret = 0; + } else { + strlcpy(wdev->glink_edge, str, GLINK_EDGE_MAX); + dev_info(&pdev->dev, "%s: glink edge is %s\n", __func__, + wdev->glink_edge); + } + platform_set_drvdata(pdev, wdev); goto done; -- GitLab From b562e91c26f5eeb135070a7c8b41d90b9df53722 Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Mon, 18 Dec 2017 18:12:35 -0800 Subject: [PATCH 2090/5498] msm: ipa: Remove unwanted gsi debug register reads This changes removes the unwanted gsi dedug register reads, to which IPA driver dont have access to. Change-Id: I6efb1045275a9bdb238e6184ec676013ab1678bc Acked-by: Jyothi Jayanthi Signed-off-by: Michael Adisumarta --- drivers/platform/msm/gsi/gsi_dbg.c | 140 ----------------------------- 1 file changed, 140 deletions(-) diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c index f5e23c68d713..66a68cd31163 100644 --- a/drivers/platform/msm/gsi/gsi_dbg.c +++ b/drivers/platform/msm/gsi/gsi_dbg.c @@ -261,123 +261,6 @@ static ssize_t gsi_dump_ch(struct file *file, return count; } -static ssize_t gsi_dump_ee(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - uint32_t val; - - val = gsi_readl(gsi_ctx->base + - GSI_GSI_MANAGER_EE_QOS_n_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d QOS 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_GSI_STATUS_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d STATUS 0x%x\n", gsi_ctx->per.ee, val); - if (gsi_ctx->per.ver == GSI_VER_1_0) { - val = gsi_readl(gsi_ctx->base + - GSI_V1_0_EE_n_GSI_HW_PARAM_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d HW_PARAM 0x%x\n", gsi_ctx->per.ee, val); - } else if (gsi_ctx->per.ver == GSI_VER_1_2) { - val = gsi_readl(gsi_ctx->base + - GSI_V1_2_EE_n_GSI_HW_PARAM_0_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d HW_PARAM_0 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_V1_2_EE_n_GSI_HW_PARAM_1_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d HW_PARAM_1 0x%x\n", gsi_ctx->per.ee, val); - } else if (gsi_ctx->per.ver == GSI_VER_1_3) { - val = gsi_readl(gsi_ctx->base + - GSI_V1_3_EE_n_GSI_HW_PARAM_0_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d HW_PARAM_0 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_V1_3_EE_n_GSI_HW_PARAM_1_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d HW_PARAM_1 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_V1_3_EE_n_GSI_HW_PARAM_2_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d HW_PARAM_2 0x%x\n", gsi_ctx->per.ee, val); - } else { - WARN_ON(1); - } - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_GSI_SW_VERSION_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d SW_VERSION 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_GSI_MCS_CODE_VER_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d MCS_CODE_VER 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_TYPE_IRQ_MSK_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d TYPE_IRQ_MSK 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_SRC_GSI_CH_IRQ_MSK_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d CH_IRQ_MSK 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_SRC_EV_CH_IRQ_MSK_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d EV_IRQ_MSK 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d IEOB_IRQ_MSK 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_GLOB_IRQ_EN_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d GLOB_IRQ_EN 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_GSI_IRQ_EN_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d GSI_IRQ_EN 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_INTSET_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d INTSET 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_MSI_BASE_LSB_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d MSI_BASE_LSB 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_MSI_BASE_MSB_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d MSI_BASE_MSB 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_INT_VEC_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d INT_VEC 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_SCRATCH_0_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d SCR0 0x%x\n", gsi_ctx->per.ee, val); - val = gsi_readl(gsi_ctx->base + - GSI_EE_n_CNTXT_SCRATCH_1_OFFS(gsi_ctx->per.ee)); - TERR("EE%2d SCR1 0x%x\n", gsi_ctx->per.ee, val); - - return count; -} - -static ssize_t gsi_dump_map(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - struct gsi_chan_ctx *ctx; - uint32_t val1; - uint32_t val2; - int i; - - TERR("EVT bitmap 0x%lx\n", gsi_ctx->evt_bmap); - for (i = 0; i < gsi_ctx->max_ch; i++) { - ctx = &gsi_ctx->chan[i]; - - if (ctx->allocated) { - TERR("VIRT CH%2d -> VIRT EV%2d\n", ctx->props.ch_id, - ctx->evtr ? ctx->evtr->id : GSI_NO_EVT_ERINDEX); - val1 = gsi_readl(gsi_ctx->base + - GSI_GSI_DEBUG_EE_n_CH_k_VP_TABLE_OFFS(i, - gsi_ctx->per.ee)); - TERR("VIRT CH%2d -> PHYS CH%2d\n", ctx->props.ch_id, - val1 & - GSI_GSI_DEBUG_EE_n_CH_k_VP_TABLE_PHY_CH_BMSK); - if (ctx->evtr) { - val2 = gsi_readl(gsi_ctx->base + - GSI_GSI_DEBUG_EE_n_EV_k_VP_TABLE_OFFS( - ctx->evtr->id, gsi_ctx->per.ee)); - TERR("VRT EV%2d -> PHYS EV%2d\n", ctx->evtr->id, - val2 & - GSI_GSI_DEBUG_EE_n_CH_k_VP_TABLE_PHY_CH_BMSK); - } - TERR("\n"); - } - } - - return count; -} - static void gsi_dump_ch_stats(struct gsi_chan_ctx *ctx) { if (!ctx->allocated) @@ -787,14 +670,6 @@ const struct file_operations gsi_ch_dump_ops = { .write = gsi_dump_ch, }; -const struct file_operations gsi_ee_dump_ops = { - .write = gsi_dump_ee, -}; - -const struct file_operations gsi_map_ops = { - .write = gsi_dump_map, -}; - const struct file_operations gsi_stats_ops = { .write = gsi_dump_stats, }; @@ -822,7 +697,6 @@ const struct file_operations gsi_ipc_low_ops = { void gsi_debugfs_init(void) { static struct dentry *dfile; - const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH; const mode_t write_only_mode = S_IWUSR | S_IWGRP; dent = debugfs_create_dir("gsi", 0); @@ -845,20 +719,6 @@ void gsi_debugfs_init(void) goto fail; } - dfile = debugfs_create_file("ee_dump", read_only_mode, dent, - 0, &gsi_ee_dump_ops); - if (!dfile || IS_ERR(dfile)) { - TERR("fail to create ee_dump file\n"); - goto fail; - } - - dfile = debugfs_create_file("map", read_only_mode, dent, - 0, &gsi_map_ops); - if (!dfile || IS_ERR(dfile)) { - TERR("fail to create map file\n"); - goto fail; - } - dfile = debugfs_create_file("stats", write_only_mode, dent, 0, &gsi_stats_ops); if (!dfile || IS_ERR(dfile)) { -- GitLab From 5699b99aa91f07b41af815ca8267db396444e8a6 Mon Sep 17 00:00:00 2001 From: Wenjun Zhang Date: Thu, 14 Dec 2017 04:27:58 -0500 Subject: [PATCH 2091/5498] ARM: dts: msm: add reset pin func for sdw2500 Modify Synaptics touch panel device tree to config reset pin for MSM8909W SDW2500 devices. Change-Id: I23806ec1b9a47f1dbbfb6c580ad805df7918ab3a Signed-off-by: Wenjun Zhang --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 3c71c6e0e1cd..23a62e4b278f 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -51,12 +51,15 @@ synaptics,irq-on-state = <0>; synaptics,irq-flags = <0x2008>; synaptics,power-delay-ms = <200>; - synaptics,reset-delay-ms = <200>; synaptics,max-y-for-2d = <389>; synaptics,resume-in-workqueue; synaptics,x-flip; synaptics,y-flip; - /delete-property/ synaptics,reset-gpio; + + synaptics,reset-gpio = <&msm_gpio 31 0x0>; + synaptics,reset-delay-ms = <200>; + synaptics,reset-on-state = <0>; + synaptics,reset-active-ms = <20>; /delete-property/ synaptics,display-coords; /delete-property/ synaptics,panel-coords; /delete-property/ synaptics,power-down; @@ -172,20 +175,20 @@ &ts_reset_suspend { mux { - pins = "gpio16"; + pins = "gpio31"; }; config { - pins = "gpio16"; + pins = "gpio31"; }; }; &ts_release { mux { - pins = "gpio98", "gpio16"; + pins = "gpio98", "gpio31"; }; config { - pins = "gpio98", "gpio16"; + pins = "gpio98", "gpio31"; }; }; -- GitLab From 90960ab9f1825a597489c9a5908f62356fbf7afe Mon Sep 17 00:00:00 2001 From: Steve Capper Date: Mon, 4 Dec 2017 14:13:05 +0000 Subject: [PATCH 2092/5498] arm64: Initialise high_memory global variable earlier commit f24e5834a2c3f6c5f814a417f858226f0a010ade upstream. The high_memory global variable is used by cma_declare_contiguous(.) before it is defined. We don't notice this as we compute __pa(high_memory - 1), and it looks like we're processing a VA from the direct linear map. This problem becomes apparent when we flip the kernel virtual address space and the linear map is moved to the bottom of the kernel VA space. This patch moves the initialisation of high_memory before it used. Fixes: f7426b983a6a ("mm: cma: adjust address limit to avoid hitting low/high memory boundary") Signed-off-by: Steve Capper Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/mm/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 43245e15413e..ed8affdf8f58 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -155,6 +155,8 @@ void __init arm64_memblock_init(void) /* 4GB maximum for 32-bit only capable devices */ if (IS_ENABLED(CONFIG_ZONE_DMA)) dma_phys_limit = max_zone_dma_phys(); + + high_memory = __va(memblock_end_of_DRAM() - 1) + 1; dma_contiguous_reserve(dma_phys_limit); memblock_allow_resize(); @@ -177,7 +179,6 @@ void __init bootmem_init(void) sparse_init(); zone_sizes_init(min, max); - high_memory = __va((max << PAGE_SHIFT) - 1) + 1; max_pfn = max_low_pfn = max; } -- GitLab From 1c21f19a9d56f6e5da926dd6c89e81f8f5d376d8 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 9 Mar 2017 13:29:13 +0100 Subject: [PATCH 2093/5498] ALSA: hda - add support for docking station for HP 820 G2 [ Upstream commit 04d5466a976b096364a39a63ac264c1b3a5f8fa1 ] This tested patch adds missing initialization for Line-In/Out PINs for the docking station for HP 820 G2. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fefc502f5bf5..d4de56cad5ac 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4683,6 +4683,7 @@ enum { ALC286_FIXUP_HP_GPIO_LED, ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, ALC280_FIXUP_HP_DOCK_PINS, + ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, ALC280_FIXUP_HP_9480M, ALC288_FIXUP_DELL_HEADSET_MODE, ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, @@ -5216,6 +5217,16 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC280_FIXUP_HP_GPIO4 }, + [ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x21011020 }, /* line-out */ + { 0x18, 0x2181103f }, /* line-in */ + { }, + }, + .chained = true, + .chain_id = ALC269_FIXUP_HP_GPIO_MIC1_LED + }, [ALC280_FIXUP_HP_9480M] = { .type = HDA_FIXUP_FUNC, .v.func = alc280_fixup_hp_9480m, @@ -5388,7 +5399,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), @@ -5544,6 +5555,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"}, {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, + {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"}, {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, -- GitLab From 9577b6c79531b3336566b4b8366982a377b1658c Mon Sep 17 00:00:00 2001 From: Vaidyanathan Srinivasan Date: Sun, 19 Mar 2017 00:51:59 +0530 Subject: [PATCH 2094/5498] cpuidle: Validate cpu_dev in cpuidle_add_sysfs() [ Upstream commit ad0a45fd9c14feebd000b6e84189d0edff265170 ] If a given cpu is not in cpu_present and cpu hotplug is disabled, arch can skip setting up the cpu_dev. Arch cpuidle driver should pass correct cpu mask for registration, but failing to do so by the driver causes error to propagate and crash like this: [ 30.076045] Unable to handle kernel paging request for data at address 0x00000048 [ 30.076100] Faulting instruction address: 0xc0000000007b2f30 cpu 0x4d: Vector: 300 (Data Access) at [c000003feb18b670] pc: c0000000007b2f30: kobject_get+0x20/0x70 lr: c0000000007b3c94: kobject_add_internal+0x54/0x3f0 sp: c000003feb18b8f0 msr: 9000000000009033 dar: 48 dsisr: 40000000 current = 0xc000003fd2ed8300 paca = 0xc00000000fbab500 softe: 0 irq_happened: 0x01 pid = 1, comm = swapper/0 Linux version 4.11.0-rc2-svaidy+ (sv@sagarika) (gcc version 6.2.0 20161005 (Ubuntu 6.2.0-5ubuntu12) ) #10 SMP Sun Mar 19 00:08:09 IST 2017 enter ? for help [c000003feb18b960] c0000000007b3c94 kobject_add_internal+0x54/0x3f0 [c000003feb18b9f0] c0000000007b43a4 kobject_init_and_add+0x64/0xa0 [c000003feb18ba70] c000000000e284f4 cpuidle_add_sysfs+0xb4/0x130 [c000003feb18baf0] c000000000e26038 cpuidle_register_device+0x118/0x1c0 [c000003feb18bb30] c000000000e26c48 cpuidle_register+0x78/0x120 [c000003feb18bbc0] c00000000168fd9c powernv_processor_idle_init+0x110/0x1c4 [c000003feb18bc40] c00000000000cff8 do_one_initcall+0x68/0x1d0 [c000003feb18bd00] c0000000016242f4 kernel_init_freeable+0x280/0x360 [c000003feb18bdc0] c00000000000d864 kernel_init+0x24/0x160 [c000003feb18be30] c00000000000b4e8 ret_from_kernel_thread+0x5c/0x74 Validating cpu_dev fixes the crash and reports correct error message like: [ 30.163506] Failed to register cpuidle device for cpu136 [ 30.173329] Registration of powernv driver failed. Signed-off-by: Vaidyanathan Srinivasan [ rjw: Comment massage ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cpuidle/sysfs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 832a2c3f01ff..9e98a5fbbc1d 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -613,6 +613,18 @@ int cpuidle_add_sysfs(struct cpuidle_device *dev) struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); int error; + /* + * Return if cpu_device is not setup for this CPU. + * + * This could happen if the arch did not set up cpu_device + * since this CPU is not in cpu_present mask and the + * driver did not send a correct CPU mask during registration. + * Without this check we would end up passing bogus + * value for &cpu_dev->kobj in kobject_init_and_add() + */ + if (!cpu_dev) + return -ENODEV; + kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); if (!kdev) return -ENOMEM; -- GitLab From 40ce0aa9a77ecef7ada777f71d35e24b6ba542e7 Mon Sep 17 00:00:00 2001 From: hayeswang Date: Tue, 14 Mar 2017 14:15:20 +0800 Subject: [PATCH 2095/5498] r8152: fix the list rx_done may be used without initialization [ Upstream commit 98d068ab52b4b11d403995ed14154660797e7136 ] The list rx_done would be initialized when the linking on occurs. Therefore, if a napi is scheduled without any linking on before, the following kernel panic would happen. BUG: unable to handle kernel NULL pointer dereference at 000000000000008 IP: [] r8152_poll+0xe1e/0x1210 [r8152] PGD 0 Oops: 0002 [#1] SMP Signed-off-by: Hayes Wang Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/r8152.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index c6554c7a8147..31cb1cda7166 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1252,6 +1252,7 @@ static int alloc_all_mem(struct r8152 *tp) spin_lock_init(&tp->tx_lock); INIT_LIST_HEAD(&tp->rx_done); INIT_LIST_HEAD(&tp->tx_free); + INIT_LIST_HEAD(&tp->rx_done); skb_queue_head_init(&tp->tx_queue); for (i = 0; i < RTL8152_MAX_RX; i++) { -- GitLab From 6957a306cb57aaad18a51b29e62e32640b8cdbc0 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 14 Mar 2017 18:25:57 +0800 Subject: [PATCH 2096/5498] crypto: deadlock between crypto_alg_sem/rtnl_mutex/genl_mutex [ Upstream commit 8a0f5ccfb33b0b8b51de65b7b3bf342ba10b4fb6 ] On Tue, Mar 14, 2017 at 10:44:10AM +0100, Dmitry Vyukov wrote: > > Yes, please. > Disregarding some reports is not a good way long term. Please try this patch. ---8<--- Subject: netlink: Annotate nlk cb_mutex by protocol Currently all occurences of nlk->cb_mutex are annotated by lockdep as a single class. This causes a false lcokdep cycle involving genl and crypto_user. This patch fixes it by dividing cb_mutex into individual classes based on the netlink protocol. As genl and crypto_user do not use the same netlink protocol this breaks the false dependency loop. Reported-by: Dmitry Vyukov Signed-off-by: Herbert Xu Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netlink/af_netlink.c | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 1dfd8d17a574..2d80cbde47ba 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -93,6 +93,44 @@ EXPORT_SYMBOL_GPL(nl_table); static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); +static struct lock_class_key nlk_cb_mutex_keys[MAX_LINKS]; + +static const char *const nlk_cb_mutex_key_strings[MAX_LINKS + 1] = { + "nlk_cb_mutex-ROUTE", + "nlk_cb_mutex-1", + "nlk_cb_mutex-USERSOCK", + "nlk_cb_mutex-FIREWALL", + "nlk_cb_mutex-SOCK_DIAG", + "nlk_cb_mutex-NFLOG", + "nlk_cb_mutex-XFRM", + "nlk_cb_mutex-SELINUX", + "nlk_cb_mutex-ISCSI", + "nlk_cb_mutex-AUDIT", + "nlk_cb_mutex-FIB_LOOKUP", + "nlk_cb_mutex-CONNECTOR", + "nlk_cb_mutex-NETFILTER", + "nlk_cb_mutex-IP6_FW", + "nlk_cb_mutex-DNRTMSG", + "nlk_cb_mutex-KOBJECT_UEVENT", + "nlk_cb_mutex-GENERIC", + "nlk_cb_mutex-17", + "nlk_cb_mutex-SCSITRANSPORT", + "nlk_cb_mutex-ECRYPTFS", + "nlk_cb_mutex-RDMA", + "nlk_cb_mutex-CRYPTO", + "nlk_cb_mutex-SMC", + "nlk_cb_mutex-23", + "nlk_cb_mutex-24", + "nlk_cb_mutex-25", + "nlk_cb_mutex-26", + "nlk_cb_mutex-27", + "nlk_cb_mutex-28", + "nlk_cb_mutex-29", + "nlk_cb_mutex-30", + "nlk_cb_mutex-31", + "nlk_cb_mutex-MAX_LINKS" +}; + static int netlink_dump(struct sock *sk); static void netlink_skb_destructor(struct sk_buff *skb); @@ -548,6 +586,9 @@ static int __netlink_create(struct net *net, struct socket *sock, } else { nlk->cb_mutex = &nlk->cb_def_mutex; mutex_init(nlk->cb_mutex); + lockdep_set_class_and_name(nlk->cb_mutex, + nlk_cb_mutex_keys + protocol, + nlk_cb_mutex_key_strings[protocol]); } init_waitqueue_head(&nlk->wait); -- GitLab From 9829550de5aaaabb2c3490fbdd3f0a71fa123cf7 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Sun, 19 Mar 2017 09:19:57 -0700 Subject: [PATCH 2097/5498] net: qmi_wwan: Add USB IDs for MDM6600 modem on Motorola Droid 4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4071898bf0f4d79ff353db327af2a15123272548 ] This gets qmicli working with the MDM6600 modem. Cc: Bjørn Mork Reviewed-by: Sebastian Reichel Tested-by: Sebastian Reichel Signed-off-by: Tony Lindgren Acked-by: Bjørn Mork Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/qmi_wwan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index b11f8eaba406..f098cc9707aa 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -460,6 +460,10 @@ static const struct usb_device_id products[] = { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x69), .driver_info = (unsigned long)&qmi_wwan_info, }, + { /* Motorola Mapphone devices with MDM6600 */ + USB_VENDOR_AND_INTERFACE_INFO(0x22b8, USB_CLASS_VENDOR_SPEC, 0xfb, 0xff), + .driver_info = (unsigned long)&qmi_wwan_info, + }, /* 2. Combined interface devices matching on class+protocol */ { /* Huawei E367 and possibly others in "Windows mode" */ -- GitLab From af2dec74ed463bbf8194272d6ea3b003cd9934ba Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 8 Mar 2017 16:05:44 +0200 Subject: [PATCH 2098/5498] usb: gadget: f_uvc: Sanity check wMaxPacketSize for SuperSpeed [ Upstream commit 16bb05d98c904a4f6c5ce7e2d992299f794acbf2 ] As per USB3.0 Specification "Table 9-20. Standard Endpoint Descriptor", for interrupt and isochronous endpoints, wMaxPacketSize must be set to 1024 if the endpoint defines bMaxBurst to be greater than zero. Reviewed-by: Laurent Pinchart Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index f4a0b25d1810..5e679fec4e33 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -612,6 +612,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); + /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */ + if (opts->streaming_maxburst && + (opts->streaming_maxpacket % 1024) != 0) { + opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024); + INFO(cdev, "overriding streaming_maxpacket to %d\n", + opts->streaming_maxpacket); + } + /* Fill in the FS/HS/SS Video Streaming specific descriptors from the * module parameters. * -- GitLab From cc77cb865cfe83b1d7f412a49c6a8bc01a21e280 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 10 Mar 2017 15:39:32 -0600 Subject: [PATCH 2099/5498] usb: gadget: udc: remove pointer dereference after free [ Upstream commit 1f459262b0e1649a1e5ad12fa4c66eb76c2220ce ] Remove pointer dereference after free. Addresses-Coverity-ID: 1091173 Acked-by: Michal Nazarewicz Signed-off-by: Gustavo A. R. Silva Signed-off-by: Felipe Balbi Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pch_udc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index ccbe3d4a2a50..190b7103ae62 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1533,7 +1533,6 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev, td = phys_to_virt(addr); addr2 = (dma_addr_t)td->next; pci_pool_free(dev->data_requests, td, addr); - td->next = 0x00; addr = addr2; } req->chain_len = 1; -- GitLab From 09bb6f008d020f991406db5c9cba52441d0ba0a3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 21 Mar 2017 13:32:37 +0100 Subject: [PATCH 2100/5498] netfilter: nfnl_cthelper: fix runtime expectation policy updates [ Upstream commit 2c422257550f123049552b39f7af6e3428a60f43 ] We only allow runtime updates of expectation policies for timeout and maximum number of expectations, otherwise reject the update. Signed-off-by: Pablo Neira Ayuso Acked-by: Liping Zhang Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 86 +++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 6d10002d23f8..3e697de2d154 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -255,6 +255,89 @@ err: return ret; } +static int +nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy, + struct nf_conntrack_expect_policy *new_policy, + const struct nlattr *attr) +{ + struct nlattr *tb[NFCTH_POLICY_MAX + 1]; + int err; + + err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, + nfnl_cthelper_expect_pol); + if (err < 0) + return err; + + if (!tb[NFCTH_POLICY_NAME] || + !tb[NFCTH_POLICY_EXPECT_MAX] || + !tb[NFCTH_POLICY_EXPECT_TIMEOUT]) + return -EINVAL; + + if (nla_strcmp(tb[NFCTH_POLICY_NAME], policy->name)) + return -EBUSY; + + new_policy->max_expected = + ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + new_policy->timeout = + ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT])); + + return 0; +} + +static int nfnl_cthelper_update_policy_all(struct nlattr *tb[], + struct nf_conntrack_helper *helper) +{ + struct nf_conntrack_expect_policy new_policy[helper->expect_class_max + 1]; + struct nf_conntrack_expect_policy *policy; + int i, err; + + /* Check first that all policy attributes are well-formed, so we don't + * leave things in inconsistent state on errors. + */ + for (i = 0; i < helper->expect_class_max + 1; i++) { + + if (!tb[NFCTH_POLICY_SET + i]) + return -EINVAL; + + err = nfnl_cthelper_update_policy_one(&helper->expect_policy[i], + &new_policy[i], + tb[NFCTH_POLICY_SET + i]); + if (err < 0) + return err; + } + /* Now we can safely update them. */ + for (i = 0; i < helper->expect_class_max + 1; i++) { + policy = (struct nf_conntrack_expect_policy *) + &helper->expect_policy[i]; + policy->max_expected = new_policy->max_expected; + policy->timeout = new_policy->timeout; + } + + return 0; +} + +static int nfnl_cthelper_update_policy(struct nf_conntrack_helper *helper, + const struct nlattr *attr) +{ + struct nlattr *tb[NFCTH_POLICY_SET_MAX + 1]; + unsigned int class_max; + int err; + + err = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr, + nfnl_cthelper_expect_policy_set); + if (err < 0) + return err; + + if (!tb[NFCTH_POLICY_SET_NUM]) + return -EINVAL; + + class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM])); + if (helper->expect_class_max + 1 != class_max) + return -EBUSY; + + return nfnl_cthelper_update_policy_all(tb, helper); +} + static int nfnl_cthelper_update(const struct nlattr * const tb[], struct nf_conntrack_helper *helper) @@ -265,8 +348,7 @@ nfnl_cthelper_update(const struct nlattr * const tb[], return -EBUSY; if (tb[NFCTH_POLICY]) { - ret = nfnl_cthelper_parse_expect_policy(helper, - tb[NFCTH_POLICY]); + ret = nfnl_cthelper_update_policy(helper, tb[NFCTH_POLICY]); if (ret < 0) return ret; } -- GitLab From e3298dc00fc4032a3065bc6ee232a49a3342463c Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Tue, 21 Mar 2017 15:07:10 +0800 Subject: [PATCH 2101/5498] netfilter: nfnl_cthelper: Fix memory leak [ Upstream commit f83bf8da1135ca635aac8f062cad3f001fcf3a26 ] We have memory leaks of nf_conntrack_helper & expect_policy. Signed-off-by: Jeffy Chen Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 3e697de2d154..f24d2cec02fb 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -216,7 +216,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[], ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]); if (ret < 0) - goto err; + goto err1; strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN); helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); @@ -247,10 +247,12 @@ nfnl_cthelper_create(const struct nlattr * const tb[], ret = nf_conntrack_helper_register(helper); if (ret < 0) - goto err; + goto err2; return 0; -err: +err2: + kfree(helper->expect_policy); +err1: kfree(helper); return ret; } @@ -696,6 +698,8 @@ nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb, found = true; nf_conntrack_helper_unregister(cur); + kfree(cur->expect_policy); + kfree(cur); } } /* Make sure we return success if we flush and there is no helpers */ @@ -759,6 +763,8 @@ static void __exit nfnl_cthelper_exit(void) continue; nf_conntrack_helper_unregister(cur); + kfree(cur->expect_policy); + kfree(cur); } } } -- GitLab From 3835e2fc38b42de47baeeca8d30aa31bd46e1ec8 Mon Sep 17 00:00:00 2001 From: Dick Kennedy Date: Thu, 23 Mar 2017 08:47:18 -0400 Subject: [PATCH 2102/5498] scsi: lpfc: Fix PT2PT PRLI reject [ Upstream commit a71e3cdcfce4880a4578915e110e3eaed1659765 ] lpfc cannot establish connection with targets that send PRLI in P2P configurations. If lpfc rejects a PRLI that is sent from a target the target will not resend and will reject the PRLI send from the initiator. [mkp: applied by hand] Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_els.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index c76279926ad6..b55bc56c0e78 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -6870,7 +6870,8 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, did, vport->port_state, ndlp->nlp_flag); phba->fc_stat.elsRcvPRLI++; - if (vport->port_state < LPFC_DISC_AUTH) { + if ((vport->port_state < LPFC_DISC_AUTH) && + (vport->fc_flag & FC_FABRIC)) { rjt_err = LSRJT_UNABLE_TPC; rjt_exp = LSEXP_NOTHING_MORE; break; -- GitLab From 06afa2da5165b854926ea5bf70d88c950ba0128b Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Mon, 20 Mar 2017 21:18:55 -0700 Subject: [PATCH 2103/5498] KVM: x86: correct async page present tracepoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 24dccf83a121b8a4ad5c2ad383a8184ef6c266ee ] After async pf setup successfully, there is a broadcast wakeup w/ special token 0xffffffff which tells vCPU that it should wake up all processes waiting for APFs though there is no real process waiting at the moment. The async page present tracepoint print prematurely and fails to catch the special token setup. This patch fixes it by moving the async page present tracepoint after the special token setup. Before patch: qemu-system-x86-8499 [006] ...1 5973.473292: kvm_async_pf_ready: token 0x0 gva 0x0 After patch: qemu-system-x86-8499 [006] ...1 5973.473292: kvm_async_pf_ready: token 0xffffffff gva 0x0 Cc: Paolo Bonzini Cc: Radim KrÄmář Signed-off-by: Wanpeng Li Signed-off-by: Paolo Bonzini Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 26329e8c9855..c8105be39b00 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -7783,11 +7783,11 @@ void kvm_arch_async_page_present(struct kvm_vcpu *vcpu, { struct x86_exception fault; - trace_kvm_async_pf_ready(work->arch.token, work->gva); if (work->wakeup_all) work->arch.token = ~0; /* broadcast wakeup */ else kvm_del_async_pf_gfn(vcpu, work->arch.gfn); + trace_kvm_async_pf_ready(work->arch.token, work->gva); if ((vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED) && !apf_put_user(vcpu, KVM_PV_REASON_PAGE_READY)) { -- GitLab From 7593d5f9c7eb036d705dad425fb84598b94da498 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Mar 2017 21:03:01 -0500 Subject: [PATCH 2104/5498] ARM: dts: ti: fix PCI bus dtc warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 7d79f6098d82f8c09914d7799bc96891ad9c3baf ] dtc recently added PCI bus checks. Fix these warnings. Signed-off-by: Rob Herring Cc: "Benoît Cousson" Cc: Tony Lindgren Cc: linux-omap@vger.kernel.org Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/dra7.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index 8ba02cb2955f..68053ce2fd22 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -118,6 +118,7 @@ device_type = "pci"; ranges = <0x81000000 0 0 0x03000 0 0x00010000 0x82000000 0 0x20013000 0x13000 0 0xffed000>; + bus-range = <0x00 0xff>; #interrupt-cells = <1>; num-lanes = <1>; ti,hwmods = "pcie1"; @@ -153,6 +154,7 @@ device_type = "pci"; ranges = <0x81000000 0 0 0x03000 0 0x00010000 0x82000000 0 0x30013000 0x13000 0 0xffed000>; + bus-range = <0x00 0xff>; #interrupt-cells = <1>; num-lanes = <1>; ti,hwmods = "pcie2"; -- GitLab From a21b0e491956e34b61d14503ae8edf989a343741 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Mar 2017 16:03:11 +0100 Subject: [PATCH 2105/5498] hwmon: (asus_atk0110) fix uninitialized data access [ Upstream commit a2125d02443e9a4e68bcfd9f8004fa23239e8329 ] The latest gcc-7 snapshot adds a warning to point out that when atk_read_value_old or atk_read_value_new fails, we copy uninitialized data into sensor->cached_value: drivers/hwmon/asus_atk0110.c: In function 'atk_input_show': drivers/hwmon/asus_atk0110.c:651:26: error: 'value' may be used uninitialized in this function [-Werror=maybe-uninitialized] Adding an error check avoids this. All versions of the driver are affected. Fixes: 2c03d07ad54d ("hwmon: Add Asus ATK0110 support") Signed-off-by: Arnd Bergmann Reviewed-by: Luca Tettamanti Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/asus_atk0110.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index cccef87963e0..975c43d446f8 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -646,6 +646,9 @@ static int atk_read_value(struct atk_sensor_data *sensor, u64 *value) else err = atk_read_value_new(sensor, value); + if (err) + return err; + sensor->is_valid = true; sensor->last_updated = jiffies; sensor->cached_value = *value; -- GitLab From de34e1a4d3e1f9b8a2cb8ae6aa0c19d43016bec3 Mon Sep 17 00:00:00 2001 From: Peter Stein Date: Fri, 17 Feb 2017 00:00:50 -0800 Subject: [PATCH 2106/5498] HID: xinmo: fix for out of range for THT 2P arcade controller. [ Upstream commit 9257821c5a1dc57ef3a37f7cbcebaf548395c964 ] There is a new clone of the XIN MO arcade controller which has same issue with out of range like the original. This fix will solve the issue where 2 directions on the joystick are not recognized by the new THT 2P arcade controller with device ID 0x75e1. In details the new device ID is added the hid-id list and the hid-xinmo source code. Signed-off-by: Peter Stein Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-xinmo.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 34dda44cb910..39debe2f0c35 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1977,6 +1977,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_THT_2P_ARCADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 213f616e7a1f..334390bae33d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1000,6 +1000,7 @@ #define USB_VENDOR_ID_XIN_MO 0x16c0 #define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE 0x05e1 +#define USB_DEVICE_ID_THT_2P_ARCADE 0x75e1 #define USB_VENDOR_ID_XIROKU 0x1477 #define USB_DEVICE_ID_XIROKU_SPX 0x1006 diff --git a/drivers/hid/hid-xinmo.c b/drivers/hid/hid-xinmo.c index 7df5227a7e61..9ad7731d2e10 100644 --- a/drivers/hid/hid-xinmo.c +++ b/drivers/hid/hid-xinmo.c @@ -46,6 +46,7 @@ static int xinmo_event(struct hid_device *hdev, struct hid_field *field, static const struct hid_device_id xinmo_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_THT_2P_ARCADE) }, { } }; -- GitLab From 5e694f6fca1061a9d0a246fa24bf0401e4d64550 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 23 Mar 2017 14:55:09 +0100 Subject: [PATCH 2107/5498] s390/qeth: no ETH header for outbound AF_IUCV [ Upstream commit acd9776b5c45ef02d1a210969a6fcc058afb76e3 ] With AF_IUCV traffic, the skb passed to hard_start_xmit() has a 14 byte slot at skb->data, intended for an ETH header. qeth_l3_fill_af_iucv_hdr() fills this ETH header... and then immediately moves it to the skb's headroom, where it disappears and is never seen again. But it's still possible for us to return NETDEV_TX_BUSY after the skb has been modified. Since we didn't get a private copy of the skb, the next time the skb is delivered to hard_start_xmit() it no longer has the expected layout (we moved the ETH header to the headroom, so skb->data now starts at the IUCV_TRANS header). So when qeth_l3_fill_af_iucv_hdr() does another round of rebuilding, the resulting qeth header ends up all wrong. On transmission, the buffer is then rejected by the HiperSockets device with SBALF15 = x'04'. When this error is passed back to af_iucv as TX_NOTIFY_UNREACHABLE, it tears down the offending socket. As the ETH header for AF_IUCV serves no purpose, just align the code to what we do for IP traffic on L3 HiperSockets: keep the ETH header at skb->data, and pass down data_offset = ETH_HLEN to qeth_fill_buffer(). When mapping the payload into the SBAL elements, the ETH header is then stripped off. This avoids the skb manipulations in qeth_l3_fill_af_iucv_hdr(), and any buffer re-entering hard_start_xmit() after NETDEV_TX_BUSY is now processed properly. Signed-off-by: Julian Wiedmann Signed-off-by: Ursula Braun Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/s390/net/qeth_l3_main.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index e5f8031ba317..b70c8b139b58 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2769,17 +2769,13 @@ static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card, char daddr[16]; struct af_iucv_trans_hdr *iucv_hdr; - skb_pull(skb, 14); - card->dev->header_ops->create(skb, card->dev, 0, - card->dev->dev_addr, card->dev->dev_addr, - card->dev->addr_len); - skb_pull(skb, 14); - iucv_hdr = (struct af_iucv_trans_hdr *)skb->data; memset(hdr, 0, sizeof(struct qeth_hdr)); hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; hdr->hdr.l3.ext_flags = 0; - hdr->hdr.l3.length = skb->len; + hdr->hdr.l3.length = skb->len - ETH_HLEN; hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST; + + iucv_hdr = (struct af_iucv_trans_hdr *) (skb->data + ETH_HLEN); memset(daddr, 0, sizeof(daddr)); daddr[0] = 0xfe; daddr[1] = 0x80; @@ -2962,10 +2958,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && (skb_shinfo(skb)->nr_frags == 0)) { new_skb = skb; - if (new_skb->protocol == ETH_P_AF_IUCV) - data_offset = 0; - else - data_offset = ETH_HLEN; + data_offset = ETH_HLEN; hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); if (!hdr) goto tx_drop; -- GitLab From cec966b21ae719be842bc98dc037a19709d0c57d Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 24 Mar 2017 09:38:03 -0700 Subject: [PATCH 2108/5498] net: Do not allow negative values for busy_read and busy_poll sysctl interfaces [ Upstream commit 95f255211396958c718aef8c45e3923b5211ea7b ] This change basically codifies what I think was already the limitations on the busy_poll and busy_read sysctl interfaces. We weren't checking the lower bounds and as such could input negative values. The behavior when that was used was dependent on the architecture. In order to prevent any issues with that I am just disabling support for values less than 0 since this way we don't have to worry about any odd behaviors. By limiting the sysctl values this way it also makes it consistent with how we handle the SO_BUSY_POLL socket option since the value appears to be reported as a signed integer value and negative values are rejected. Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/core/sysctl_net_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index cd386d2fd039..4f00a8eaf704 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -331,14 +331,16 @@ static struct ctl_table net_core_table[] = { .data = &sysctl_net_busy_poll, .maxlen = sizeof(unsigned int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, }, { .procname = "busy_read", .data = &sysctl_net_busy_read, .maxlen = sizeof(unsigned int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, }, #endif #ifdef CONFIG_NET_SCHED -- GitLab From 7570a653051aeddce707b0d4cd4d6ecc91f4bbc6 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 24 Mar 2017 15:01:42 -0700 Subject: [PATCH 2109/5498] i40e: Do not enable NAPI on q_vectors that have no rings [ Upstream commit 13a8cd191a2b470cfd435b3b57dbd21aa65ff78c ] When testing the epoll w/ busy poll code I found that I could get into a state where the i40e driver had q_vectors w/ active NAPI that had no rings. This was resulting in a divide by zero error. To correct it I am updating the driver code so that we only support NAPI on q_vectors that have 1 or more rings allocated to them. Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/i40e/i40e_main.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 81cd60dd8a2f..f7ed3dbd9800 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3814,8 +3814,12 @@ static void i40e_napi_enable_all(struct i40e_vsi *vsi) if (!vsi->netdev) return; - for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) - napi_enable(&vsi->q_vectors[q_idx]->napi); + for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) { + struct i40e_q_vector *q_vector = vsi->q_vectors[q_idx]; + + if (q_vector->rx.ring || q_vector->tx.ring) + napi_enable(&q_vector->napi); + } } /** @@ -3829,8 +3833,12 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi) if (!vsi->netdev) return; - for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) - napi_disable(&vsi->q_vectors[q_idx]->napi); + for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) { + struct i40e_q_vector *q_vector = vsi->q_vectors[q_idx]; + + if (q_vector->rx.ring || q_vector->tx.ring) + napi_disable(&q_vector->napi); + } } /** -- GitLab From cba690fe68c6555161fbaf78750f9ab696919a69 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 25 Mar 2017 01:48:08 +0300 Subject: [PATCH 2110/5498] irda: vlsi_ir: fix check for DMA mapping errors [ Upstream commit 6ac3b77a6ffff7513ff86b684aa256ea01c0e5b5 ] vlsi_alloc_ring() checks for DMA mapping errors by comparing returned address with zero, while pci_dma_mapping_error() should be used. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/irda/vlsi_ir.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index a2e556168286..aceedd7aeb2a 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -426,8 +426,9 @@ static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr memset(rd, 0, sizeof(*rd)); rd->hw = hwmap + i; rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA); - if (rd->buf == NULL || - !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) { + if (rd->buf) + busaddr = pci_map_single(pdev, rd->buf, len, dir); + if (rd->buf == NULL || pci_dma_mapping_error(pdev, busaddr)) { if (rd->buf) { IRDA_ERROR("%s: failed to create PCI-MAP for %p", __func__, rd->buf); @@ -438,8 +439,7 @@ static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr rd = r->rd + j; busaddr = rd_get_addr(rd); rd_set_addr_status(rd, 0, 0); - if (busaddr) - pci_unmap_single(pdev, busaddr, len, dir); + pci_unmap_single(pdev, busaddr, len, dir); kfree(rd->buf); rd->buf = NULL; } -- GitLab From 6237e86d4d285cb9a0e36b56296141e2398cb30f Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sat, 25 Mar 2017 12:09:15 +0800 Subject: [PATCH 2111/5498] netfilter: nfnl_cthelper: fix a race when walk the nf_ct_helper_hash table [ Upstream commit 83d90219a5df8d950855ce73229a97b63605c317 ] The nf_ct_helper_hash table is protected by nf_ct_helper_mutex, while nfct_helper operation is protected by nfnl_lock(NFNL_SUBSYS_CTHELPER). So it's possible that one CPU is walking the nf_ct_helper_hash for cthelper add/get/del, another cpu is doing nf_conntrack_helpers_unregister at the same time. This is dangrous, and may cause use after free error. Note, delete operation will flush all cthelpers added via nfnetlink, so using rcu to do protect is not easy. Now introduce a dummy list to record all the cthelpers added via nfnetlink, then we can walk the dummy list instead of walking the nf_ct_helper_hash. Also, keep nfnl_cthelper_dump_table unchanged, it may be invoked without nfnl_lock(NFNL_SUBSYS_CTHELPER) held. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 177 +++++++++++++---------------- 1 file changed, 81 insertions(+), 96 deletions(-) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index f24d2cec02fb..8d34a488efc0 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -32,6 +32,13 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Pablo Neira Ayuso "); MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers"); +struct nfnl_cthelper { + struct list_head list; + struct nf_conntrack_helper helper; +}; + +static LIST_HEAD(nfnl_cthelper_list); + static int nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) @@ -205,14 +212,16 @@ nfnl_cthelper_create(const struct nlattr * const tb[], struct nf_conntrack_tuple *tuple) { struct nf_conntrack_helper *helper; + struct nfnl_cthelper *nfcth; int ret; if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN]) return -EINVAL; - helper = kzalloc(sizeof(struct nf_conntrack_helper), GFP_KERNEL); - if (helper == NULL) + nfcth = kzalloc(sizeof(*nfcth), GFP_KERNEL); + if (nfcth == NULL) return -ENOMEM; + helper = &nfcth->helper; ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]); if (ret < 0) @@ -249,11 +258,12 @@ nfnl_cthelper_create(const struct nlattr * const tb[], if (ret < 0) goto err2; + list_add_tail(&nfcth->list, &nfnl_cthelper_list); return 0; err2: kfree(helper->expect_policy); err1: - kfree(helper); + kfree(nfcth); return ret; } @@ -379,7 +389,8 @@ nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb, const char *helper_name; struct nf_conntrack_helper *cur, *helper = NULL; struct nf_conntrack_tuple tuple; - int ret = 0, i; + struct nfnl_cthelper *nlcth; + int ret = 0; if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE]) return -EINVAL; @@ -390,31 +401,22 @@ nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb, if (ret < 0) return ret; - rcu_read_lock(); - for (i = 0; i < nf_ct_helper_hsize && !helper; i++) { - hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) { + list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { + cur = &nlcth->helper; - /* skip non-userspace conntrack helpers. */ - if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) - continue; + if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) + continue; - if (strncmp(cur->name, helper_name, - NF_CT_HELPER_NAME_LEN) != 0) - continue; + if ((tuple.src.l3num != cur->tuple.src.l3num || + tuple.dst.protonum != cur->tuple.dst.protonum)) + continue; - if ((tuple.src.l3num != cur->tuple.src.l3num || - tuple.dst.protonum != cur->tuple.dst.protonum)) - continue; + if (nlh->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; - if (nlh->nlmsg_flags & NLM_F_EXCL) { - ret = -EEXIST; - goto err; - } - helper = cur; - break; - } + helper = cur; + break; } - rcu_read_unlock(); if (helper == NULL) ret = nfnl_cthelper_create(tb, &tuple); @@ -422,9 +424,6 @@ nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb, ret = nfnl_cthelper_update(tb, helper); return ret; -err: - rcu_read_unlock(); - return ret; } static int @@ -588,11 +587,12 @@ static int nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const tb[]) { - int ret = -ENOENT, i; + int ret = -ENOENT; struct nf_conntrack_helper *cur; struct sk_buff *skb2; char *helper_name = NULL; struct nf_conntrack_tuple tuple; + struct nfnl_cthelper *nlcth; bool tuple_set = false; if (nlh->nlmsg_flags & NLM_F_DUMP) { @@ -613,45 +613,39 @@ nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb, tuple_set = true; } - for (i = 0; i < nf_ct_helper_hsize; i++) { - hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) { + list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { + cur = &nlcth->helper; + if (helper_name && + strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) + continue; - /* skip non-userspace conntrack helpers. */ - if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) - continue; + if (tuple_set && + (tuple.src.l3num != cur->tuple.src.l3num || + tuple.dst.protonum != cur->tuple.dst.protonum)) + continue; - if (helper_name && strncmp(cur->name, helper_name, - NF_CT_HELPER_NAME_LEN) != 0) { - continue; - } - if (tuple_set && - (tuple.src.l3num != cur->tuple.src.l3num || - tuple.dst.protonum != cur->tuple.dst.protonum)) - continue; - - skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (skb2 == NULL) { - ret = -ENOMEM; - break; - } + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) { + ret = -ENOMEM; + break; + } - ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, - NFNL_MSG_TYPE(nlh->nlmsg_type), - NFNL_MSG_CTHELPER_NEW, cur); - if (ret <= 0) { - kfree_skb(skb2); - break; - } + ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, + NFNL_MSG_TYPE(nlh->nlmsg_type), + NFNL_MSG_CTHELPER_NEW, cur); + if (ret <= 0) { + kfree_skb(skb2); + break; + } - ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, - MSG_DONTWAIT); - if (ret > 0) - ret = 0; + ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, + MSG_DONTWAIT); + if (ret > 0) + ret = 0; - /* this avoids a loop in nfnetlink. */ - return ret == -EAGAIN ? -ENOBUFS : ret; - } + /* this avoids a loop in nfnetlink. */ + return ret == -EAGAIN ? -ENOBUFS : ret; } return ret; } @@ -662,10 +656,10 @@ nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb, { char *helper_name = NULL; struct nf_conntrack_helper *cur; - struct hlist_node *tmp; struct nf_conntrack_tuple tuple; bool tuple_set = false, found = false; - int i, j = 0, ret; + struct nfnl_cthelper *nlcth, *n; + int j = 0, ret; if (tb[NFCTH_NAME]) helper_name = nla_data(tb[NFCTH_NAME]); @@ -678,30 +672,27 @@ nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb, tuple_set = true; } - for (i = 0; i < nf_ct_helper_hsize; i++) { - hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i], - hnode) { - /* skip non-userspace conntrack helpers. */ - if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) - continue; + list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { + cur = &nlcth->helper; + j++; - j++; + if (helper_name && + strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) + continue; - if (helper_name && strncmp(cur->name, helper_name, - NF_CT_HELPER_NAME_LEN) != 0) { - continue; - } - if (tuple_set && - (tuple.src.l3num != cur->tuple.src.l3num || - tuple.dst.protonum != cur->tuple.dst.protonum)) - continue; + if (tuple_set && + (tuple.src.l3num != cur->tuple.src.l3num || + tuple.dst.protonum != cur->tuple.dst.protonum)) + continue; - found = true; - nf_conntrack_helper_unregister(cur); - kfree(cur->expect_policy); - kfree(cur); - } + found = true; + nf_conntrack_helper_unregister(cur); + kfree(cur->expect_policy); + + list_del(&nlcth->list); + kfree(nlcth); } + /* Make sure we return success if we flush and there is no helpers */ return (found || j == 0) ? 0 : -ENOENT; } @@ -750,22 +741,16 @@ err_out: static void __exit nfnl_cthelper_exit(void) { struct nf_conntrack_helper *cur; - struct hlist_node *tmp; - int i; + struct nfnl_cthelper *nlcth, *n; nfnetlink_subsys_unregister(&nfnl_cthelper_subsys); - for (i=0; iflags & NF_CT_HELPER_F_USERSPACE)) - continue; + list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { + cur = &nlcth->helper; - nf_conntrack_helper_unregister(cur); - kfree(cur->expect_policy); - kfree(cur); - } + nf_conntrack_helper_unregister(cur); + kfree(cur->expect_policy); + kfree(nlcth); } } -- GitLab From 4c8bc54b2d41c785a3a34a036887eafcc4d3d202 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Sat, 25 Mar 2017 18:24:36 +0800 Subject: [PATCH 2112/5498] netfilter: nf_nat_snmp: Fix panic when snmp_trap_helper fails to register [ Upstream commit 75c689dca98851d65ef5a27e5ce26b625b68751c ] In the commit 93557f53e1fb ("netfilter: nf_conntrack: nf_conntrack snmp helper"), the snmp_helper is replaced by nf_nat_snmp_hook. So the snmp_helper is never registered. But it still tries to unregister the snmp_helper, it could cause the panic. Now remove the useless snmp_helper and the unregister call in the error handler. Fixes: 93557f53e1fb ("netfilter: nf_conntrack: nf_conntrack snmp helper") Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/nf_nat_snmp_basic.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c index cc626e1b06d3..64a8bbc06f23 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c @@ -1260,16 +1260,6 @@ static const struct nf_conntrack_expect_policy snmp_exp_policy = { .timeout = 180, }; -static struct nf_conntrack_helper snmp_helper __read_mostly = { - .me = THIS_MODULE, - .help = help, - .expect_policy = &snmp_exp_policy, - .name = "snmp", - .tuple.src.l3num = AF_INET, - .tuple.src.u.udp.port = cpu_to_be16(SNMP_PORT), - .tuple.dst.protonum = IPPROTO_UDP, -}; - static struct nf_conntrack_helper snmp_trap_helper __read_mostly = { .me = THIS_MODULE, .help = help, @@ -1288,17 +1278,10 @@ static struct nf_conntrack_helper snmp_trap_helper __read_mostly = { static int __init nf_nat_snmp_basic_init(void) { - int ret = 0; - BUG_ON(nf_nat_snmp_hook != NULL); RCU_INIT_POINTER(nf_nat_snmp_hook, help); - ret = nf_conntrack_helper_register(&snmp_trap_helper); - if (ret < 0) { - nf_conntrack_helper_unregister(&snmp_helper); - return ret; - } - return ret; + return nf_conntrack_helper_register(&snmp_trap_helper); } static void __exit nf_nat_snmp_basic_fini(void) -- GitLab From d8f3c88f1ff711e17b66ff1f8fa143da19a0be41 Mon Sep 17 00:00:00 2001 From: "Reizer, Eyal" Date: Sun, 26 Mar 2017 08:53:10 +0000 Subject: [PATCH 2113/5498] ARM: dts: am335x-evmsk: adjust mmc2 param to allow suspend [ Upstream commit 9bcf53f34a2c1cebc45cc12e273dcd5f51fbc099 ] mmc2 used for wl12xx was missing the keep-power-in suspend parameter. As a result the board couldn't reach suspend state. Signed-off-by: Eyal Reizer Signed-off-by: Tony Lindgren Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/am335x-evmsk.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts index df5fee6b6b4b..730d9fc81474 100644 --- a/arch/arm/boot/dts/am335x-evmsk.dts +++ b/arch/arm/boot/dts/am335x-evmsk.dts @@ -645,6 +645,7 @@ ti,non-removable; bus-width = <4>; cap-power-off-card; + keep-power-in-suspend; pinctrl-names = "default"; pinctrl-0 = <&mmc2_pins>; }; -- GitLab From 98482e926f7ed0609f4a81203899397ffaf5816e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 28 Mar 2017 12:11:07 +0200 Subject: [PATCH 2114/5498] isdn: kcapi: avoid uninitialized data [ Upstream commit af109a2cf6a9a6271fa420ae2d64d72d86c92b7d ] gcc-7 points out that the AVMB1_ADDCARD ioctl results in an unintialized value ending up in the cardnr parameter: drivers/isdn/capi/kcapi.c: In function 'old_capi_manufacturer': drivers/isdn/capi/kcapi.c:1042:24: error: 'cdef.cardnr' may be used uninitialized in this function [-Werror=maybe-uninitialized] cparams.cardnr = cdef.cardnr; This has been broken since before the start of the git history, so either the value is not used for anything important, or the ioctl command doesn't get called in practice. Setting the cardnr to zero avoids the warning and makes sure we have consistent behavior. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/capi/kcapi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index 823f6985b260..dd7e38ac29bd 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c @@ -1032,6 +1032,7 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data) sizeof(avmb1_carddef)))) return -EFAULT; cdef.cardtype = AVM_CARDTYPE_B1; + cdef.cardnr = 0; } else { if ((retval = copy_from_user(&cdef, data, sizeof(avmb1_extcarddef)))) -- GitLab From f7ce6712521ea6f34d17ad5a2bf4a74200b5e781 Mon Sep 17 00:00:00 2001 From: Adam Wallis Date: Tue, 28 Mar 2017 15:55:28 +0300 Subject: [PATCH 2115/5498] xhci: plat: Register shutdown for xhci_plat [ Upstream commit b07c12517f2aed0add8ce18146bb426b14099392 ] Shutdown should be called for xhci_plat devices especially for situations where kexec might be used by stopping DMA transactions. Signed-off-by: Adam Wallis Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 54941bd3fe09..bd9654e9f0a7 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -252,6 +252,7 @@ MODULE_DEVICE_TABLE(of, usb_xhci_of_match); static struct platform_driver usb_xhci_driver = { .probe = xhci_plat_probe, .remove = xhci_plat_remove, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-hcd", .pm = DEV_PM_OPS, -- GitLab From c1c9ad2e8f25f76b4218064408680ce8051da6f8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 29 Mar 2017 17:12:47 +0100 Subject: [PATCH 2116/5498] ARM: dma-mapping: disallow dma_get_sgtable() for non-kernel managed memory [ Upstream commit 916a008b4b8ecc02fbd035cfb133773dba1ff3d7 ] dma_get_sgtable() tries to create a scatterlist table containing valid struct page pointers for the coherent memory allocation passed in to it. However, memory can be declared via dma_declare_coherent_memory(), or via other reservation schemes which means that coherent memory is not guaranteed to be backed by struct pages. In such cases, the resulting scatterlist table contains pointers to invalid pages, which causes kernel oops later. This patch adds detection of such memory, and refuses to create a scatterlist table for such memory. Reported-by: Shuah Khan Signed-off-by: Russell King Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm/mm/dma-mapping.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index e8907117861e..685b6f3b2b8b 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -749,13 +749,31 @@ static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_add __arm_dma_free(dev, size, cpu_addr, handle, attrs, true); } +/* + * The whole dma_get_sgtable() idea is fundamentally unsafe - it seems + * that the intention is to allow exporting memory allocated via the + * coherent DMA APIs through the dma_buf API, which only accepts a + * scattertable. This presents a couple of problems: + * 1. Not all memory allocated via the coherent DMA APIs is backed by + * a struct page + * 2. Passing coherent DMA memory into the streaming APIs is not allowed + * as we will try to flush the memory through a different alias to that + * actually being used (and the flushes are redundant.) + */ int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt, void *cpu_addr, dma_addr_t handle, size_t size, struct dma_attrs *attrs) { - struct page *page = pfn_to_page(dma_to_pfn(dev, handle)); + unsigned long pfn = dma_to_pfn(dev, handle); + struct page *page; int ret; + /* If the PFN is not valid, we do not have a struct page */ + if (!pfn_valid(pfn)) + return -ENXIO; + + page = pfn_to_page(pfn); + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); if (unlikely(ret)) return ret; -- GitLab From 6a6c5199570ad24fedb00292751645ca36104cce Mon Sep 17 00:00:00 2001 From: Vaidyanathan Srinivasan Date: Thu, 23 Mar 2017 20:52:46 +0530 Subject: [PATCH 2117/5498] cpuidle: powernv: Pass correct drv->cpumask for registration [ Upstream commit 293d264f13cbde328d5477f49e3103edbc1dc191 ] drv->cpumask defaults to cpu_possible_mask in __cpuidle_driver_init(). On PowerNV platform cpu_present could be less than cpu_possible in cases where firmware detects the cpu, but it is not available to the OS. When CONFIG_HOTPLUG_CPU=n, such cpus are not hotplugable at runtime and hence we skip creating cpu_device. This breaks cpuidle on powernv where register_cpu() is not called for cpus in cpu_possible_mask that cannot be hot-added at runtime. Trying cpuidle_register_device() on cpu without cpu_device will cause crash like this: cpu 0xf: Vector: 380 (Data SLB Access) at [c000000ff1503490] pc: c00000000022c8bc: string+0x34/0x60 lr: c00000000022ed78: vsnprintf+0x284/0x42c sp: c000000ff1503710 msr: 9000000000009033 dar: 6000000060000000 current = 0xc000000ff1480000 paca = 0xc00000000fe82d00 softe: 0 irq_happened: 0x01 pid = 1, comm = swapper/8 Linux version 4.11.0-rc2 (sv@sagarika) (gcc version 4.9.4 (Buildroot 2017.02-00004-gc28573e) ) #15 SMP Fri Mar 17 19:32:02 IST 2017 enter ? for help [link register ] c00000000022ed78 vsnprintf+0x284/0x42c [c000000ff1503710] c00000000022ebb8 vsnprintf+0xc4/0x42c (unreliable) [c000000ff1503800] c00000000022ef40 vscnprintf+0x20/0x44 [c000000ff1503830] c0000000000ab61c vprintk_emit+0x94/0x2cc [c000000ff15038a0] c0000000000acc9c vprintk_func+0x60/0x74 [c000000ff15038c0] c000000000619694 printk+0x38/0x4c [c000000ff15038e0] c000000000224950 kobject_get+0x40/0x60 [c000000ff1503950] c00000000022507c kobject_add_internal+0x60/0x2c4 [c000000ff15039e0] c000000000225350 kobject_init_and_add+0x70/0x78 [c000000ff1503a60] c00000000053c288 cpuidle_add_sysfs+0x9c/0xe0 [c000000ff1503ae0] c00000000053aeac cpuidle_register_device+0xd4/0x12c [c000000ff1503b30] c00000000053b108 cpuidle_register+0x98/0xcc [c000000ff1503bc0] c00000000085eaf0 powernv_processor_idle_init+0x140/0x1e0 [c000000ff1503c60] c00000000000cd60 do_one_initcall+0xc0/0x15c [c000000ff1503d20] c000000000833e84 kernel_init_freeable+0x1a0/0x25c [c000000ff1503dc0] c00000000000d478 kernel_init+0x24/0x12c [c000000ff1503e30] c00000000000b564 ret_from_kernel_thread+0x5c/0x78 This patch fixes the bug by passing correct cpumask from powernv-cpuidle driver. Signed-off-by: Vaidyanathan Srinivasan Reviewed-by: Gautham R. Shenoy Acked-by: Michael Ellerman [ rjw: Comment massage ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cpuidle/cpuidle-powernv.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 7d3a3497dd4c..fe860907b854 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -154,6 +154,24 @@ static int powernv_cpuidle_driver_init(void) drv->state_count += 1; } + /* + * On the PowerNV platform cpu_present may be less than cpu_possible in + * cases when firmware detects the CPU, but it is not available to the + * OS. If CONFIG_HOTPLUG_CPU=n, then such CPUs are not hotplugable at + * run time and hence cpu_devices are not created for those CPUs by the + * generic topology_init(). + * + * drv->cpumask defaults to cpu_possible_mask in + * __cpuidle_driver_init(). This breaks cpuidle on PowerNV where + * cpu_devices are not created for CPUs in cpu_possible_mask that + * cannot be hot-added later at run time. + * + * Trying cpuidle_register_device() on a CPU without a cpu_device is + * incorrect, so pass a correct CPU mask to the generic cpuidle driver. + */ + + drv->cpumask = (struct cpumask *)cpu_present_mask; + return 0; } -- GitLab From 8e31e3e457c15584894e3ee68b0c3cd2af01519e Mon Sep 17 00:00:00 2001 From: Derek Basehore Date: Tue, 29 Aug 2017 13:34:34 -0700 Subject: [PATCH 2118/5498] backlight: pwm_bl: Fix overflow condition [ Upstream commit 5d0c49acebc9488e37db95f1d4a55644e545ffe7 ] This fixes an overflow condition that can happen with high max brightness and period values in compute_duty_cycle. This fixes it by using a 64 bit variable for computing the duty cycle. Signed-off-by: Derek Basehore Acked-by: Thierry Reding Reviewed-by: Brian Norris Signed-off-by: Lee Jones Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/video/backlight/pwm_bl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index cb5ae4c08469..3610e2ff5763 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -78,14 +78,17 @@ static void pwm_backlight_power_off(struct pwm_bl_data *pb) static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness) { unsigned int lth = pb->lth_brightness; - int duty_cycle; + u64 duty_cycle; if (pb->levels) duty_cycle = pb->levels[brightness]; else duty_cycle = brightness; - return (duty_cycle * (pb->period - lth) / pb->scale) + lth; + duty_cycle *= pb->period - lth; + do_div(duty_cycle, pb->scale); + + return duty_cycle + lth; } static int pwm_backlight_update_status(struct backlight_device *bl) -- GitLab From 4d3444fe2656d3edb8db115d2d51f54f7fa14b98 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Wed, 4 Oct 2017 01:00:08 +0200 Subject: [PATCH 2119/5498] crypto: crypto4xx - increase context and scatter ring buffer elements [ Upstream commit 778f81d6cdb7d25360f082ac0384d5103f04eca5 ] If crypto4xx is used in conjunction with dm-crypt, the available ring buffer elements are not enough to handle the load properly. On an aes-cbc-essiv:sha256 encrypted swap partition the read performance is abyssal: (tested with hdparm -t) /dev/mapper/swap_crypt: Timing buffered disk reads: 14 MB in 3.68 seconds = 3.81 MB/sec The patch increases both PPC4XX_NUM_SD and PPC4XX_NUM_PD to 256. This improves the performance considerably: /dev/mapper/swap_crypt: Timing buffered disk reads: 104 MB in 3.03 seconds = 34.31 MB/sec Furthermore, PPC4XX_LAST_SD, PPC4XX_LAST_GD and PPC4XX_LAST_PD can be easily calculated from their respective PPC4XX_NUM_* constant. Signed-off-by: Christian Lamparter Signed-off-by: Herbert Xu Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/amcc/crypto4xx_core.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h index bac0bdeb4b5f..b6529b9fcbe2 100644 --- a/drivers/crypto/amcc/crypto4xx_core.h +++ b/drivers/crypto/amcc/crypto4xx_core.h @@ -32,12 +32,12 @@ #define PPC405EX_CE_RESET 0x00000008 #define CRYPTO4XX_CRYPTO_PRIORITY 300 -#define PPC4XX_LAST_PD 63 -#define PPC4XX_NUM_PD 64 -#define PPC4XX_LAST_GD 1023 +#define PPC4XX_NUM_PD 256 +#define PPC4XX_LAST_PD (PPC4XX_NUM_PD - 1) #define PPC4XX_NUM_GD 1024 -#define PPC4XX_LAST_SD 63 -#define PPC4XX_NUM_SD 64 +#define PPC4XX_LAST_GD (PPC4XX_NUM_GD - 1) +#define PPC4XX_NUM_SD 256 +#define PPC4XX_LAST_SD (PPC4XX_NUM_SD - 1) #define PPC4XX_SD_BUFFER_SIZE 2048 #define PD_ENTRY_INUSE 1 -- GitLab From 8a58da6d17db972f17870afabee9b0e607382793 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Tue, 10 Oct 2017 12:42:56 -0500 Subject: [PATCH 2120/5498] net: phy: at803x: Change error to EINVAL for invalid MAC [ Upstream commit fc7556877d1748ac00958822a0a3bba1d4bd9e0d ] Change the return error code to EINVAL if the MAC address is not valid in the set_wol function. Signed-off-by: Dan Murphy Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/at803x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index fdc1b418fa6a..5012708271ca 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -105,7 +105,7 @@ static int at803x_set_wol(struct phy_device *phydev, mac = (const u8 *) ndev->dev_addr; if (!is_valid_ether_addr(mac)) - return -EFAULT; + return -EINVAL; for (i = 0; i < 3; i++) { phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, -- GitLab From ac8283db32cb8a24846ae157a329d2949dde5f66 Mon Sep 17 00:00:00 2001 From: David Daney Date: Fri, 8 Sep 2017 10:10:31 +0200 Subject: [PATCH 2121/5498] PCI: Avoid bus reset if bridge itself is broken [ Upstream commit 357027786f3523d26f42391aa4c075b8495e5d28 ] When checking to see if a PCI bus can safely be reset, we previously checked to see if any of the children had their PCI_DEV_FLAGS_NO_BUS_RESET flag set. Children marked with that flag are known not to behave well after a bus reset. Some PCIe root port bridges also do not behave well after a bus reset, sometimes causing the devices behind the bridge to become unusable. Add a check for PCI_DEV_FLAGS_NO_BUS_RESET being set in the bridge device to allow these bridges to be flagged, and prevent their secondary buses from being reset. Signed-off-by: David Daney [jglauber@cavium.com: fixed typo] Signed-off-by: Jan Glauber Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Williamson Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d3decb52eb7e..1563cfadeaef 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3580,6 +3580,10 @@ static bool pci_bus_resetable(struct pci_bus *bus) { struct pci_dev *dev; + + if (bus->self && (bus->self->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)) + return false; + list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || (dev->subordinate && !pci_bus_resetable(dev->subordinate))) -- GitLab From 98c3d06cbff5a943f9c2d135092f8177262fbfcf Mon Sep 17 00:00:00 2001 From: Varun Prakash Date: Wed, 11 Oct 2017 19:33:07 +0530 Subject: [PATCH 2122/5498] scsi: cxgb4i: fix Tx skb leak [ Upstream commit 9b3a081fb62158b50bcc90522ca2423017544367 ] In case of connection reset Tx skb queue can have some skbs which are not transmitted so purge Tx skb queue in release_offload_resources() to avoid skb leak. Signed-off-by: Varun Prakash Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 15081257cfc8..1cfb05bea4c4 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -1262,6 +1262,7 @@ static void release_offload_resources(struct cxgbi_sock *csk) csk, csk->state, csk->flags, csk->tid); cxgbi_sock_free_cpl_skbs(csk); + cxgbi_sock_purge_write_queue(csk); if (csk->wr_cred != csk->wr_max_cred) { cxgbi_sock_purge_wr_queue(csk); cxgbi_sock_reset_wr_list(csk); -- GitLab From 9e2dfe98004a67b9d0450bc41777d2a8e0cb326c Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 4 Oct 2017 10:57:52 -0500 Subject: [PATCH 2123/5498] PCI: Create SR-IOV virtfn/physfn links before attaching driver [ Upstream commit 27d6162944b9b34c32cd5841acd21786637ee743 ] When creating virtual functions, create the "virtfn%u" and "physfn" links in sysfs *before* attaching the driver instead of after. When we attach the driver to the new virtual network interface first, there is a race when the driver attaches to the new sends out an "add" udev event, and the network interface naming software (biosdevname or systemd, for example) tries to look at these links. Signed-off-by: Stuart Hayes Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/iov.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 4d109c07294a..9c0254638cc8 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -106,7 +106,6 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) pci_device_add(virtfn, virtfn->bus); mutex_unlock(&iov->dev->sriov->lock); - pci_bus_add_device(virtfn); sprintf(buf, "virtfn%u", id); rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); if (rc) @@ -117,6 +116,8 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); + pci_bus_add_device(virtfn); + return 0; failed2: -- GitLab From 6d117334cea2f10bec8023520e50fc3de49631b7 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 27 Aug 2017 08:39:51 +0200 Subject: [PATCH 2124/5498] igb: check memory allocation failure [ Upstream commit 18eb86362a52f0af933cc0fd5e37027317eb2d1c ] Check memory allocation failures and return -ENOMEM in such cases, as already done for other memory allocations in this function. This avoids NULL pointers dereference. Signed-off-by: Christophe JAILLET Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igb/igb_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 2ba73fc6d328..3b3911e511ac 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3001,6 +3001,8 @@ static int igb_sw_init(struct igb_adapter *adapter) /* Setup and initialize a copy of the hw vlan table array */ adapter->shadow_vfta = kcalloc(E1000_VLAN_FILTER_TBL_SIZE, sizeof(u32), GFP_ATOMIC); + if (!adapter->shadow_vfta) + return -ENOMEM; /* This call may decrease the number of queues */ if (igb_init_interrupt_scheme(adapter, true)) { -- GitLab From ca6f17da0b134bbc71d143dc5e059f20809c11dd Mon Sep 17 00:00:00 2001 From: Gabriele Paoloni Date: Thu, 28 Sep 2017 15:33:05 +0100 Subject: [PATCH 2125/5498] PCI/AER: Report non-fatal errors only to the affected endpoint [ Upstream commit 86acc790717fb60fb51ea3095084e331d8711c74 ] Previously, if an non-fatal error was reported by an endpoint, we called report_error_detected() for the endpoint, every sibling on the bus, and their descendents. If any of them did not implement the .error_detected() method, do_recovery() failed, leaving all these devices unrecovered. For example, the system described in the bugzilla below has two devices: 0000:74:02.0 [19e5:a230] SAS controller, driver has .error_detected() 0000:74:03.0 [19e5:a235] SATA controller, driver lacks .error_detected() When a device such as 74:02.0 reported a non-fatal error, do_recovery() failed because 74:03.0 lacked an .error_detected() method. But per PCIe r3.1, sec 6.2.2.2.2, such an error does not compromise the Link and does not affect 74:03.0: Non-fatal errors are uncorrectable errors which cause a particular transaction to be unreliable but the Link is otherwise fully functional. Isolating Non-fatal from Fatal errors provides Requester/Receiver logic in a device or system management software the opportunity to recover from the error without resetting the components on the Link and disturbing other transactions in progress. Devices not associated with the transaction in error are not impacted by the error. Report non-fatal errors only to the endpoint that reported them. We really want to check for AER_NONFATAL here, but the current code structure doesn't allow that. Looking for pci_channel_io_normal is the best we can do now. Link: https://bugzilla.kernel.org/show_bug.cgi?id=197055 Fixes: 6c2b374d7485 ("PCI-Express AER implemetation: AER core and aerdriver") Signed-off-by: Gabriele Paoloni Signed-off-by: Dongdong Liu [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pcie/aer/aerdrv_core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index b60a325234c5..cca4b4789ac4 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -360,7 +360,14 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, * If the error is reported by an end point, we think this * error is related to the upstream link of the end point. */ - pci_walk_bus(dev->bus, cb, &result_data); + if (state == pci_channel_io_normal) + /* + * the error is non fatal so the bus is ok, just invoke + * the callback for the function that logged the error. + */ + cb(dev, &result_data); + else + pci_walk_bus(dev->bus, cb, &result_data); } return result_data.result; -- GitLab From 6ea3e1c5114cc07dc1c637e053c5c83e22ca25ae Mon Sep 17 00:00:00 2001 From: Dick Kennedy Date: Fri, 29 Sep 2017 17:34:42 -0700 Subject: [PATCH 2126/5498] scsi: lpfc: Fix secure firmware updates [ Upstream commit 184fc2b9a8bcbda9c14d0a1e7fbecfc028c7702e ] Firmware update fails with: status x17 add_status x56 on the final write If multiple DMA buffers are used for the download, some firmware revs have difficulty with signatures and crcs split across the dma buffer boundaries. Resolve by making all writes be a single 4k page in length. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_hw4.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index f432ec180cf8..37b14a1278be 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -2951,7 +2951,7 @@ struct lpfc_mbx_get_port_name { #define MB_CEQ_STATUS_QUEUE_FLUSHING 0x4 #define MB_CQE_STATUS_DMA_FAILED 0x5 -#define LPFC_MBX_WR_CONFIG_MAX_BDE 8 +#define LPFC_MBX_WR_CONFIG_MAX_BDE 1 struct lpfc_mbx_wr_object { struct mbox_header header; union { -- GitLab From a6af431eb78989e1965feb4934eeadec0064526e Mon Sep 17 00:00:00 2001 From: Dick Kennedy Date: Fri, 29 Sep 2017 17:34:32 -0700 Subject: [PATCH 2127/5498] scsi: lpfc: PLOGI failures during NPIV testing [ Upstream commit e8bcf0ae4c0346fdc78ebefe0eefcaa6a6622d38 ] Local Reject/Invalid RPI errors seen during discovery. Temporary RPI cleanup was occurring regardless of SLI rev. It's only necessary on SLI-4. Adjust the test for whether cleanup is necessary. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/lpfc/lpfc_hbadisc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 5452f1f4220e..908b2a4fa3cb 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -4737,7 +4737,8 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_cancel_retry_delay_tmo(vport, ndlp); if ((ndlp->nlp_flag & NLP_DEFER_RM) && !(ndlp->nlp_flag & NLP_REG_LOGIN_SEND) && - !(ndlp->nlp_flag & NLP_RPI_REGISTERED)) { + !(ndlp->nlp_flag & NLP_RPI_REGISTERED) && + phba->sli_rev != LPFC_SLI_REV4) { /* For this case we need to cleanup the default rpi * allocated by the firmware. */ -- GitLab From 510f29192b5514cea135581f1b64dda71f8de6a2 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 2 Oct 2017 07:17:50 -0700 Subject: [PATCH 2128/5498] fm10k: ensure we process SM mbx when processing VF mbx [ Upstream commit 17a91809942ca32c70026d2d5ba3348a2c4fdf8f ] When we process VF mailboxes, the driver is likely going to also queue up messages to the switch manager. This process merely queues up the FIFO, but doesn't actually begin the transmission process. Because we hold the mailbox lock during this VF processing, the PF<->SM mailbox is not getting processed at this time. Ensure that we actually process the PF<->SM mailbox in between each PF<->VF mailbox. This should ensure prompt transmission of the messages queued up after each VF message is received and handled. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/fm10k/fm10k_iov.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c index 060190864238..682d99350342 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c @@ -145,6 +145,9 @@ process_mbx: struct fm10k_mbx_info *mbx = &vf_info->mbx; u16 glort = vf_info->glort; + /* process the SM mailbox first to drain outgoing messages */ + hw->mbx.ops.process(hw, &hw->mbx); + /* verify port mapping is valid, if not reset port */ if (vf_info->vf_flags && !fm10k_glort_valid_pf(hw, glort)) hw->iov.ops.reset_lport(hw, vf_info); -- GitLab From 8da23652fa4353a2e5281f444aeaa639b452630a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 25 Dec 2017 14:20:09 +0100 Subject: [PATCH 2129/5498] Linux 3.18.90 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f959c14c92bf..abbfbbb7a029 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 89 +SUBLEVEL = 90 EXTRAVERSION = NAME = Diseased Newt -- GitLab From c644529b522f21bc1660debeb12631de6b21a9ba Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 7 Dec 2017 15:28:36 +0530 Subject: [PATCH 2130/5498] ASoC: bg: Add support to start/stop keyword detection in BG Add a mixer control to indicate bg codec to start/stop keyword detection in BG. Change-Id: I2ebb5770319eb127d178cc4da8c4bc28777f1623 Signed-off-by: Ashish Jain --- sound/soc/codecs/bg_codec.c | 378 +++++++++++++++++++++--------------- 1 file changed, 224 insertions(+), 154 deletions(-) diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index 34f36570ab2e..a4f66d8108b4 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -33,6 +33,7 @@ #include #include "pktzr.h" +#define SAMPLE_RATE_48KHZ 48000 #define BG_RATES_MAX (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ @@ -41,8 +42,6 @@ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S24_3LE) - - enum { BG_AIF1_PB = 0, BG_AIF2_PB, @@ -80,6 +79,7 @@ struct bg_cdc_priv { unsigned long status_mask; struct bg_hw_params hw_params; int src[NUM_CODEC_DAIS]; + bool hwd_started; }; struct codec_ssn_rt_setup_t { @@ -94,6 +94,129 @@ struct graphite_basic_rsp_result { uint32_t status; }; +static uint32_t get_active_session_id(int dai_id) + +{ + uint32_t active_session; + + if ((dai_id >= NUM_CODEC_DAIS) || (dai_id < 0)) { + pr_err("%s invalid dai id\n", __func__); + return 0; + } + + switch (dai_id) { + case BG_AIF1_PB: + active_session = 0x0001; + break; + case BG_AIF1_CAP: + active_session = 0x00010000; + break; + case BG_AIF2_PB: + active_session = 0x0001; + break; + case BG_AIF2_CAP: + active_session = 0x00020000; + break; + case BG_AIF3_PB: + active_session = 0x0002; + break; + case BG_AIF3_CAP: + /* BG MIC 1 is at slot 3 of the TDM packet */ + active_session = 0x00020000; + break; + case BG_AIF4_PB: + active_session = 0x0004; + break; + case BG_AIF4_CAP: + /* BG MIC 0 is at slot 4 of the TDM packet */ + active_session = 0x00010000; + break; + default: + active_session = 0; + } + pr_debug("active_session selected %x", active_session); + return active_session; +} + +static int _bg_codec_hw_params(struct bg_cdc_priv *bg_cdc) +{ + struct bg_hw_params hw_params; + struct pktzr_cmd_rsp rsp; + int ret = 0; + + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return -ENOMEM; + memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); + /* Send command to BG to set_params */ + ret = pktzr_cmd_set_params(&hw_params, sizeof(hw_params), &rsp); + if (ret < 0) + pr_err("pktzr cmd set params failed with error %d\n", ret); + + kfree(rsp.buf); + + return ret; +} + +static int _bg_codec_start(struct bg_cdc_priv *bg_cdc, int dai_id) +{ + struct pktzr_cmd_rsp rsp; + struct codec_ssn_rt_setup_t codec_start; + int ret = 0; + + rsp.buf = NULL; + codec_start.active_session = get_active_session_id(dai_id); + if (codec_start.active_session == 0) { + pr_err("%s:Invalid dai id %d", __func__, dai_id); + return -EINVAL; + } + codec_start.route_to_bg = bg_cdc->src[dai_id]; + pr_debug("%s active_session %x route_to_bg %d\n", + __func__, codec_start.active_session, codec_start.route_to_bg); + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return -ENOMEM; + ret = pktzr_cmd_start(&codec_start, sizeof(codec_start), &rsp); + if (ret < 0) + pr_err("pktzr cmd start failed %d\n", ret); + + kfree(rsp.buf); + + return ret; +} + +static int _bg_codec_stop(struct bg_cdc_priv *bg_cdc, int dai_id) +{ + struct pktzr_cmd_rsp rsp; + struct codec_ssn_rt_setup_t codec_start; + int ret = 0; + + rsp.buf = NULL; + codec_start.active_session = get_active_session_id(dai_id); + if (codec_start.active_session == 0) { + pr_err("%s:Invalid dai id %d", __func__, dai_id); + return -EINVAL; + } + codec_start.route_to_bg = bg_cdc->src[dai_id]; + pr_debug("%s active_session %x route_to_bg %d\n", + __func__, codec_start.active_session, codec_start.route_to_bg); + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return -ENOMEM; + ret = pktzr_cmd_stop(&codec_start, sizeof(codec_start), &rsp); + if (ret < 0) + pr_err("pktzr cmd stop failed with error %d\n", ret); + + kfree(rsp.buf); + + return ret; +} + + + static int bg_get_src(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -136,6 +259,83 @@ static int bg_put_src(struct snd_kcontrol *kcontrol, return 0; } +static int bg_get_hwd_state(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(codec); + int dai_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + + if ((bg_cdc == NULL) || (dai_id >= NUM_CODEC_DAIS) || + (dai_id < 0)) { + pr_err("%s invalid input\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = bg_cdc->hwd_started; + dev_dbg(codec->dev, "%s: dai_id: %d hwd_enable: %d\n", __func__, + dai_id, bg_cdc->hwd_started); + + return 0; +} + +static int bg_put_hwd_state(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(codec); + int dai_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + uint32_t active_session_id = 0; + int ret = 0; + + if ((bg_cdc == NULL) || (dai_id >= NUM_CODEC_DAIS) || + (dai_id < 0)) { + pr_err("%s bgcdc is null or invalid dai id\n", __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: dai_id: %d hwd_enable %ld\n", __func__, + dai_id, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] && (!bg_cdc->hwd_started)) { + /* enable bg hwd */ + bg_cdc->hw_params.tx_sample_rate = SAMPLE_RATE_48KHZ; + bg_cdc->hw_params.tx_bit_width = 16; + bg_cdc->hw_params.tx_num_channels = 1; + active_session_id = get_active_session_id(dai_id); + if (active_session_id == 0) { + pr_err("%s:Invalid dai id %d", __func__, dai_id); + return -EINVAL; + } + bg_cdc->hw_params.active_session = active_session_id; + /* Send command to BG for HW params */ + ret = _bg_codec_hw_params(bg_cdc); + if (ret < 0) { + pr_err("_bg_codec_hw_params fail for dai %d", dai_id); + return ret; + } + /* Send command to BG to start session */ + ret = _bg_codec_start(bg_cdc, dai_id); + if (ret < 0) { + pr_err("_bg_codec_start fail for dai %d", dai_id); + return ret; + } + bg_cdc->hwd_started = true; + } else if (bg_cdc->hwd_started && + (ucontrol->value.integer.value[0] == 0)) { + /*hwd was on, this is a command to stop it*/ + bg_cdc->hwd_started = false; + ret = _bg_codec_stop(bg_cdc, dai_id); + if (ret < 0) { + pr_err("bg_codec_stop failed for dai %d\n", dai_id); + return ret; + } + } + + return ret; +} static const struct snd_kcontrol_new bg_snd_controls[] = { SOC_SINGLE_EXT("RX_0 SRC", BG_AIF1_PB, 0, 1, 0, @@ -154,9 +354,12 @@ static const struct snd_kcontrol_new bg_snd_controls[] = { bg_get_src, bg_put_src), SOC_SINGLE_EXT("TX_3 DST", BG_AIF4_CAP, 0, 1, 0, bg_get_src, bg_put_src), + SOC_SINGLE_EXT("TX_2 HWD", BG_AIF3_CAP, 0, 1, 0, + bg_get_hwd_state, bg_put_hwd_state), + SOC_SINGLE_EXT("TX_3 HWD", BG_AIF4_CAP, 0, 1, 0, + bg_get_hwd_state, bg_put_hwd_state), }; - static int bg_cdc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -192,48 +395,18 @@ static int bg_cdc_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); - struct bg_hw_params hw_params; - struct pktzr_cmd_rsp rsp; int ret = 0; - rsp.buf = NULL; - pr_debug("%s: dai_name = %s DAI-ID %x rate %d width %d num_ch %d\n", __func__, dai->name, dai->id, params_rate(params), params_width(params), params_channels(params)); - switch (dai->id) { - case BG_AIF1_PB: - bg_cdc->hw_params.active_session = 0x0001; - break; - case BG_AIF1_CAP: - bg_cdc->hw_params.active_session = 0x00010000; - break; - case BG_AIF2_PB: - bg_cdc->hw_params.active_session = 0x0001; - break; - case BG_AIF2_CAP: - bg_cdc->hw_params.active_session = 0x00020000; - break; - case BG_AIF3_PB: - bg_cdc->hw_params.active_session = 0x0002; - break; - case BG_AIF3_CAP: - bg_cdc->hw_params.active_session = 0x00010000; - break; - case BG_AIF4_PB: - bg_cdc->hw_params.active_session = 0x0004; - break; - case BG_AIF4_CAP: - bg_cdc->hw_params.active_session = 0x00020000; - break; - default: + bg_cdc->hw_params.active_session = get_active_session_id(dai->id); + if (bg_cdc->hw_params.active_session == 0) { pr_err("%s:Invalid dai id %d", __func__, dai->id); - ret = -EINVAL; - goto exit; + return -EINVAL; } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { bg_cdc->hw_params.rx_sample_rate = params_rate(params); bg_cdc->hw_params.rx_bit_width = params_width(params); @@ -244,99 +417,36 @@ static int bg_cdc_hw_params(struct snd_pcm_substream *substream, bg_cdc->hw_params.tx_num_channels = params_channels(params); } - /* check if RX, TX sampling freq is same if not return error. */ /* Send command to BG for HW params */ - rsp.buf_size = sizeof(struct graphite_basic_rsp_result); - rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); - if (!rsp.buf) - return -ENOMEM; - memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); - /* Send command to BG to start session */ - ret = pktzr_cmd_set_params(&hw_params, sizeof(hw_params), &rsp); - if (ret < 0) { - pr_err("pktzr cmd set params failed\n"); - goto exit; - } -exit: - if (rsp.buf) - kzfree(rsp.buf); + + ret = _bg_codec_hw_params(bg_cdc); + if (ret < 0) + pr_err("_bg_codec_hw_params failed for dai %d", dai->id); return ret; } static int bg_cdc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct pktzr_cmd_rsp rsp; int ret = 0; - struct codec_ssn_rt_setup_t codec_start; struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); pr_debug("%s: dai_name = %s DAI-ID %x\n", __func__, dai->name, dai->id); - rsp.buf = NULL; - - switch (dai->id) { - case BG_AIF1_PB: - codec_start.active_session = 0x0001; - break; - case BG_AIF1_CAP: - codec_start.active_session = 0x00010000; - break; - case BG_AIF2_PB: - codec_start.active_session = 0x0001; - break; - case BG_AIF2_CAP: - codec_start.active_session = 0x00020000; - break; - case BG_AIF3_PB: - codec_start.active_session = 0x0002; - break; - case BG_AIF3_CAP: - codec_start.active_session = 0x00010000; - break; - case BG_AIF4_PB: - codec_start.active_session = 0x0004; - break; - case BG_AIF4_CAP: - codec_start.active_session = 0x00020000; - break; - default: - pr_err("%s:Invalid dai id %d", __func__, dai->id); - ret = -EINVAL; - goto exit; - } + ret = _bg_codec_stop(bg_cdc, dai->id); + if (ret < 0) + pr_err("bg_codec_stop failed for dai %d\n", dai->id); - codec_start.route_to_bg = bg_cdc->src[dai->id]; - pr_debug("%s active_session %x route_to_bg %d\n", - __func__, codec_start.active_session, codec_start.route_to_bg); - rsp.buf_size = sizeof(struct graphite_basic_rsp_result); - rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); - if (!rsp.buf) - return -ENOMEM; - /* Send command to BG to stop session */ - ret = pktzr_cmd_stop(&codec_start, - sizeof(codec_start), &rsp); - if (ret < 0) { - pr_err("pktzr cmd close failed\n"); - goto exit; - } -exit: - if (rsp.buf) - kzfree(rsp.buf); return ret; } static int bg_cdc_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct pktzr_cmd_rsp rsp; int ret = 0; - struct codec_ssn_rt_setup_t codec_start; struct bg_cdc_priv *bg_cdc = snd_soc_codec_get_drvdata(dai->codec); - rsp.buf = NULL; - - + /* check if RX, TX sampling freq is same if not return error. */ if (test_bit(PLAYBACK, &bg_cdc->status_mask) && test_bit(CAPTURE, &bg_cdc->status_mask)) { if ((bg_cdc->hw_params.rx_sample_rate != @@ -350,57 +460,17 @@ static int bg_cdc_prepare(struct snd_pcm_substream *substream, bg_cdc->hw_params.rx_bit_width); return -EINVAL; } + } else if (test_bit(CAPTURE, &bg_cdc->status_mask) && + bg_cdc->hwd_started){ + pr_err("Cannot enable recording if hwd is in progress"); + return -EINVAL; } - switch (dai->id) { - case BG_AIF1_PB: - codec_start.active_session = 0x0001; - break; - case BG_AIF1_CAP: - codec_start.active_session = 0x00010000; - break; - case BG_AIF2_PB: - codec_start.active_session = 0x0001; - break; - case BG_AIF2_CAP: - codec_start.active_session = 0x00020000; - break; - case BG_AIF3_PB: - codec_start.active_session = 0x0002; - break; - case BG_AIF3_CAP: - codec_start.active_session = 0x00010000; - break; - case BG_AIF4_PB: - codec_start.active_session = 0x0004; - break; - case BG_AIF4_CAP: - codec_start.active_session = 0x00020000; - break; - default: - pr_err("%s:Invalid dai id %d", __func__, dai->id); - ret = -EINVAL; - goto exit; - } - - codec_start.route_to_bg = bg_cdc->src[dai->id]; - pr_debug("%s active_session %x route_to_bg %d\n", - __func__, codec_start.active_session, codec_start.route_to_bg); - rsp.buf_size = sizeof(struct graphite_basic_rsp_result); - rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); - if (!rsp.buf) - return -ENOMEM; /* Send command to BG to start session */ - ret = pktzr_cmd_start(&codec_start, sizeof(codec_start), &rsp); - if (ret < 0) { - pr_err("pktzr cmd start failed\n"); - goto exit; - } -exit: - if (rsp.buf) - kzfree(rsp.buf); + ret = _bg_codec_start(bg_cdc, dai->id); + if (ret < 0) + pr_err("_bg_codec_start failed for dai %d", dai->id); return ret; - } static int bg_cdc_set_channel_map(struct snd_soc_dai *dai, -- GitLab From bc3759e1d58419e54dff0b25af2c8ccef32dbe0c Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Thu, 14 Dec 2017 12:49:05 +0530 Subject: [PATCH 2131/5498] msm: ipa: Change error value for set quota Currently IPA send common error -EFAULT to user space in case of IOCTL fails. Change error value for set quota based on error received from modem. Change-Id: Ib6ba487a186245ddf752cd08de12293af1ea1bb9 Acked-by: Pooja Kumari Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 2 +- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c | 12 ++++++++---- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 2 +- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c | 12 ++++++++---- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 3bc09aea5b8e..00af4cf2a920 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2561,7 +2561,7 @@ int rmnet_ipa_set_data_quota(struct wan_ioctl_set_data_quota *data) if (index == MAX_NUM_OF_MUX_CHANNEL) { IPAWANERR("%s is an invalid iface name\n", data->interface_name); - return -EFAULT; + return -ENODEV; } mux_id = mux_channel[index].mux_id; diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c index 7161b3ab78f3..f80c3911853f 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c @@ -60,7 +60,7 @@ static dev_t device; static long wan_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int retval = 0; + int retval = 0, rc = 0; u32 pyld_sz; u8 *param = NULL; @@ -183,10 +183,14 @@ static long wan_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retval = -EFAULT; break; } - if (rmnet_ipa_set_data_quota( - (struct wan_ioctl_set_data_quota *)param)) { + rc = rmnet_ipa_set_data_quota( + (struct wan_ioctl_set_data_quota *)param); + if (rc != 0) { IPAWANERR("WAN_IOC_SET_DATA_QUOTA failed\n"); - retval = -EFAULT; + if (rc == -ENODEV) + retval = -ENODEV; + else + retval = -EFAULT; break; } if (copy_to_user((u8 *)arg, param, pyld_sz)) { diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 9a12d6716e4e..49bcefcbd29b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -2720,7 +2720,7 @@ int rmnet_ipa3_set_data_quota(struct wan_ioctl_set_data_quota *data) if (index == MAX_NUM_OF_MUX_CHANNEL) { IPAWANERR("%s is an invalid iface name\n", data->interface_name); - return -EFAULT; + return -ENODEV; } mux_id = rmnet_ipa3_ctx->mux_channel[index].mux_id; diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c index c428e18ac7f8..a05da95f8e5b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c @@ -71,7 +71,7 @@ static long ipa3_wan_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int retval = 0; + int retval = 0, rc = 0; u32 pyld_sz; u8 *param = NULL; @@ -246,10 +246,14 @@ static long ipa3_wan_ioctl(struct file *filp, retval = -EFAULT; break; } - if (rmnet_ipa3_set_data_quota( - (struct wan_ioctl_set_data_quota *)param)) { + rc = rmnet_ipa3_set_data_quota( + (struct wan_ioctl_set_data_quota *)param); + if (rc != 0) { IPAWANERR("WAN_IOC_SET_DATA_QUOTA failed\n"); - retval = -EFAULT; + if (retval == -ENODEV) + retval = -ENODEV; + else + retval = -EFAULT; break; } if (copy_to_user((u8 *)arg, param, pyld_sz)) { -- GitLab From 8c19fdd8d59c26eedac4898b35c1ead3ae45e17d Mon Sep 17 00:00:00 2001 From: Wenjun Zhang Date: Thu, 14 Dec 2017 05:05:36 -0500 Subject: [PATCH 2132/5498] input: touchpanel: Fix abnormal crash caused by Synaptics TP There was a abnormal kernel crash when TP resumes. The root cause is that the call of reset_device in TP reusme function may fail sometimes and watchdog's timeout occurred to make kernel crash. After removing this useless reset-device by undefine FB_READY_RESET, TP can work normally. Change-Id: I1c9e15631b619ce2b2426cb93da99d9b988a0aed Signed-off-by: Wenjun Zhang --- .../input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c index c12cd4cdf280..a474297bdc33 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c @@ -68,7 +68,6 @@ #define IGNORE_FN_INIT_FAILURE -#define FB_READY_RESET #define FB_READY_WAIT_MS 100 #define FB_READY_TIMEOUT_S 30 @@ -4611,6 +4610,7 @@ static int synaptics_rmi4_resume(struct device *dev) #ifdef FB_READY_RESET int retval; #endif + int retval; struct synaptics_rmi4_exp_fhandler *exp_fhandler; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); -- GitLab From 7be996c649894713922e839c7a4917b91ffa1e19 Mon Sep 17 00:00:00 2001 From: Raghavendra Kakarla Date: Thu, 21 Dec 2017 16:24:31 +0530 Subject: [PATCH 2133/5498] oc: qcom: rpm-smd-debug: Fix potential memory leaks Fix memory leak due to rpm request not freed during error conditions. Change-Id: I440a58bf452e76c8886f7bcd8f89b24698a301e9 Signed-off-by: Raghavendra Kakarla --- drivers/soc/qcom/rpm-smd-debug.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/rpm-smd-debug.c b/drivers/soc/qcom/rpm-smd-debug.c index aaf7b415d1f1..acc92e252601 100644 --- a/drivers/soc/qcom/rpm-smd-debug.c +++ b/drivers/soc/qcom/rpm-smd-debug.c @@ -90,23 +90,23 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer, cmp += pos; if (sscanf(cmp, "%5s %n", key_str, &pos) != 1) { pr_err("Invalid number of arguments passed\n"); - goto err; + goto err_request; } if (strlen(key_str) > 4) { pr_err("Key value cannot be more than 4 charecters"); - goto err; + goto err_request; } key = string_to_uint(key_str); if (!key) { pr_err("Key values entered incorrectly\n"); - goto err; + goto err_request; } cmp += pos; if (sscanf(cmp, "%u %n", &data, &pos) != 1) { pr_err("Invalid number of arguments passed\n"); - goto err; + goto err_request; } if (msm_rpm_add_kvp_data(req, key, -- GitLab From 6f41e8ac2641ed6a2701ee9f1cd1742841b29e05 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Wed, 27 Dec 2017 11:49:24 +0530 Subject: [PATCH 2134/5498] soc: qcom: bgrsb: Disables LDO-15 only if RSB is enabled on BG down Disable LDO-15 only if it RSB is enabled and disables LDO-11 only if RSB in configured state Change-Id: I22922f864dc561a2b0e9f95cb2e8ba9c2a96701d Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index 578e9ebcf65b..46ca407b24fc 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -346,9 +346,20 @@ static void bgrsb_bgdown_work(struct work_struct *work) struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_down_work); - bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15); - bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11); - dev->bgrsb_current_state = BGRSB_STATE_INIT; + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) == 0) + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + else + pr_err("Failed to unvote LDO-15 on BG down\n"); + } + + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) { + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) + dev->bgrsb_current_state = BGRSB_STATE_INIT; + else + pr_err("Failed to unvote LDO-11 on BG down\n"); + } + pr_debug("RSB current state is : %d\n", dev->bgrsb_current_state); } static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) -- GitLab From 09f8d0200a419bd34c5dfa25be782981d48e5035 Mon Sep 17 00:00:00 2001 From: Baochu Xu Date: Mon, 18 Dec 2017 16:26:27 +0800 Subject: [PATCH 2135/5498] ARM: dts: msm: disable dm-verity on SDW2500 Disable dm-verity on SDW2500 for SDW2500 don't support dm-verity feature. Change-Id: I4d8e6ebff6b1f67cec5d78e50b2bd2329a140e7c Signed-off-by: Baochu Xu --- arch/arm/boot/dts/qcom/msm8909.dtsi | 4 ++-- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 8865bae1ccee..0122e682bb5a 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -112,7 +112,7 @@ compatible = "android,firmware"; fstab { compatible = "android,fstab"; - vendor { + vendor_fstab: vendor { compatible = "android,vendor"; dev = "/dev/block/platform/soc/7824900.sdhci/by-name/vendor"; type = "ext4"; @@ -120,7 +120,7 @@ fsmgr_flags = "wait,verify"; status = "ok"; }; - system { + system_fstab: system { compatible = "android,system"; dev = "/dev/block/platform/soc/7824900.sdhci/by-name/system"; type = "ext4"; diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index d5250ecf8d92..c6b30475d0ba 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -195,3 +195,11 @@ pins = "gpio98", "gpio16"; }; }; + +&system_fstab { + fsmgr_flags = "wait"; +}; + +&vendor_fstab { + fsmgr_flags = "wait"; +}; -- GitLab From e4e5be7ec42ca3b1ea5f1021a0633fc5e8f5b856 Mon Sep 17 00:00:00 2001 From: Baochu Xu Date: Tue, 19 Dec 2017 19:18:52 +0800 Subject: [PATCH 2136/5498] ARM: dts: msm: modify backlight gpio on SDW2500 Change backlight gpio from gpio_52 to gpio_37 for NFC device need to use gpio_52. Change-Id: If883600a5146f43c541ede606f07886e63b0b9aa Signed-off-by: Baochu Xu --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index c6b30475d0ba..4ce2df973184 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -203,3 +203,8 @@ &vendor_fstab { fsmgr_flags = "wait"; }; + +&mdss_dsi0{ + qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>; + qcom,platform-bklight-en-gpio = <&msm_gpio 37 0>; +}; -- GitLab From f4c4233b5caa02975de1e4cb091567fb6dc48a3d Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Wed, 27 Dec 2017 16:47:00 +0530 Subject: [PATCH 2137/5498] soc:qcom: glink_bgcom_xprt: Reserve space for TX_READ_NOTIF cmd Glink bgcom transport is not reserving space for sending blocked signal, when fifo is full. Reserve space for TX_READ_NOTIF cmd. CRs-Fixed: 2164523 Change-Id: If107962d1736ec4c03532a86e9b0f1099cc14cc0 Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/glink_bgcom_xprt.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/soc/qcom/glink_bgcom_xprt.c b/drivers/soc/qcom/glink_bgcom_xprt.c index 9dbc1ec0fae6..682eb28736ff 100644 --- a/drivers/soc/qcom/glink_bgcom_xprt.c +++ b/drivers/soc/qcom/glink_bgcom_xprt.c @@ -50,6 +50,8 @@ #define BGCOM_TO_MASTER_FIFO_READY 0x00000004 #define BGCOM_AHB_READY 0x00000008 #define WORD_SIZE 4 +#define TX_BLOCKED_CMD_RESERVE 16 +#define FIFO_FULL_RESERVE (TX_BLOCKED_CMD_RESERVE/WORD_SIZE) #define BGCOM_LINKUP (BGCOM_APPLICATION_RUNNING \ | BGCOM_TO_SLAVE_FIFO_READY \ | BGCOM_TO_MASTER_FIFO_READY \ @@ -216,6 +218,11 @@ static int glink_bgcom_get_tx_avail(struct edge_info *einfo) mutex_lock(&einfo->tx_avail_lock); tx_avail = einfo->fifo_fill.tx_avail; + if (tx_avail < FIFO_FULL_RESERVE) + tx_avail = 0; + else + tx_avail -= FIFO_FULL_RESERVE; + mutex_unlock(&einfo->tx_avail_lock); return tx_avail; } @@ -555,13 +562,11 @@ static void process_rx_cmd(struct edge_info *einfo, * tx_wakeup_worker() - worker function to wakeup tx blocked thread * @work: kwork associated with the edge to process commands on. */ -static void tx_wakeup_worker(struct work_struct *work) +static void tx_wakeup_worker(struct edge_info *einfo) { - struct edge_info *einfo; int rcu_id; struct bgcom_fifo_fill fifo_fill; - einfo = container_of(work, struct edge_info, wakeup_work); mutex_lock(&einfo->tx_avail_lock); bgcom_reg_read(einfo->bgcom_handle, BGCOM_REG_FIFO_FILL, 1, &fifo_fill); @@ -1440,7 +1445,7 @@ static void glink_bgcom_event_handler(void *handle, break; case BGCOM_EVENT_TO_SLAVE_FIFO_FREE: if (einfo->water_mark_reached) - queue_work(system_unbound_wq, &einfo->wakeup_work); + tx_wakeup_worker(einfo); break; case BGCOM_EVENT_RESET_OCCURRED: einfo->bgcom_status = BGCOM_RESET; @@ -1592,7 +1597,6 @@ static int glink_bgcom_probe(struct platform_device *pdev) init_xprt_cfg(einfo, subsys_name); init_xprt_if(einfo); - INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); init_kthread_worker(&einfo->kworker); init_srcu_struct(&einfo->use_ref); mutex_init(&einfo->write_lock); @@ -1646,7 +1650,6 @@ bgcom_open_fail: glink_core_unregister_transport(&einfo->xprt_if); reg_xprt_fail: flush_kthread_worker(&einfo->kworker); - flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; kthread_fail: -- GitLab From 8c97ea87af6770985ab58a14a4cfe1c2a9b15761 Mon Sep 17 00:00:00 2001 From: Alok Chauhan Date: Tue, 26 Dec 2017 17:06:28 +0530 Subject: [PATCH 2138/5498] msm: emac: move init adapter to post phase2 clocks enablement EMAC driver maintain some of the field in its internal structure after reading it from EMAC HW core. So move init adapter to post enablement of phase2 clocks to avoid any unclock access. Change-Id: I8f5a226197f6227fccb9fc7eb6fb6a55dd0d4f85 Signed-off-by: Alok Chauhan --- drivers/net/ethernet/qualcomm/emac/emac_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/emac/emac_main.c b/drivers/net/ethernet/qualcomm/emac/emac_main.c index c077330919d8..e748214fed8c 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac_main.c +++ b/drivers/net/ethernet/qualcomm/emac/emac_main.c @@ -3093,8 +3093,6 @@ static int emac_probe(struct platform_device *pdev) if (ret) goto err_clk_init; - hw_ver = emac_reg_r32(hw, EMAC, EMAC_CORE_HW_VERSION); - netdev->watchdog_timeo = EMAC_WATCHDOG_TIME; netdev->irq = adpt->irq[0].irq; @@ -3114,9 +3112,6 @@ static int emac_probe(struct platform_device *pdev) emac_set_ethtool_ops(netdev); - /* init adapter */ - emac_init_adapter(adpt); - /* init internal phy */ ret = emac_phy_config_internal(pdev, adpt); if (ret) @@ -3127,6 +3122,11 @@ static int emac_probe(struct platform_device *pdev) if (ret) goto err_clk_init; + hw_ver = emac_reg_r32(hw, EMAC, EMAC_CORE_HW_VERSION); + + /* init adapter */ + emac_init_adapter(adpt); + /* Configure MDIO lines */ ret = adpt->gpio_on(adpt, true, true); if (ret) -- GitLab From 607af61989c8ddc15458a5bdecb3bb37a4ee527e Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 19 Dec 2017 18:09:52 +0530 Subject: [PATCH 2139/5498] ARM: dts: msm: Add wcd-glink driver node for msm8909w BG v2 Add wcd-glink driver node for hotword detection communication with BG. Change-Id: Ic4cff9c1be49b4f05e7bc4bb7024c7db3a917a61 Signed-off-by: Ashish Jain --- arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi index d7e1f0f785eb..e6a16036f2b1 100644 --- a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi @@ -177,4 +177,9 @@ qcom,msm-cpudai-tdm-data-align = <0>; }; }; + + wdsp_glink: qcom,wcd-dsp-glink { + compatible = "qcom,wcd-dsp-glink"; + qcom,msm-codec-glink-edge = "bg"; + }; }; -- GitLab From 76793d5ed03d87dcff8fd4182de28785a0a8f8fe Mon Sep 17 00:00:00 2001 From: Shadab Naseem Date: Fri, 24 Nov 2017 10:23:13 +0530 Subject: [PATCH 2140/5498] ARM: dts: msm: Add support for apq8009 Excelpoint board variant Add support for apq8009 with pm8916 on Excelpoint carrier board for 1GB variant. Change-Id: I9b51728cacb21e6303165a97ed5d223e2ed45e16 Signed-off-by: Shadab Naseem --- arch/arm/boot/dts/qcom/Makefile | 1 + ...pq8009-mtp-wcd9326-excelpoint-refboard.dts | 120 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index e2ed50e51cff..edbf8ad12f8c 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -352,6 +352,7 @@ dtb-$(CONFIG_ARCH_MSM8909) += msm8909-pm8916-mtp.dtb \ apq8009-robot-refboard.dtb \ apq8009-robot-rome.dtb \ apq8009-mtp-drone.dtb \ + apq8009-mtp-wcd9326-excelpoint-refboard.dtb \ msm8909-mtp.dtb \ msm8909-1gb-mtp.dtb \ msm8909-1gb-rcm.dtb \ diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts new file mode 100644 index 000000000000..e032cdc389ba --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +#include "msm8909-mtp.dtsi" +#include "msm8909-pm8916.dtsi" +#include "msm8909-pm8916-mtp.dtsi" +#include "apq8009-audio-external_codec.dtsi" +#include "apq8009-memory.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8009 Excelpoint RefBoard"; + compatible = "qcom,apq8009-mtp", "qcom,apq8009", "qcom,mtp"; + qcom,msm-id = <265 2>; + qcom,board-id= <8 0x14>; +}; + +&audio_codec_mtp { + status = "disabled"; +}; + +&soc { + sound-9335 { + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "DMIC2", "MIC BIAS3", + "MIC BIAS3", "Digital Mic2", + "DMIC3", "MIC BIAS3", + "MIC BIAS3", "Digital Mic3", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; + }; + + i2c@78b8000 { + wcd9xxx_codec@d { + qcom,cdc-reset-gpio = <&msm_gpio 27 0>; + }; + }; + + i2c@78b9000 { + synaptics@20 { + status = "disabled"; + }; + }; +}; + +&i2c_4 { + smb1360_otg_supply: smb1360-chg-fg@14 { + compatible = "qcom,smb1360-chg-fg"; + reg = <0x14>; + interrupt-parent = <&msm_gpio>; + interrupts = <58 8>; + pinctrl-names = "default"; + pinctrl-0 = <&smb_int_default>; + qcom,charging-disabled; + qcom,empty-soc-disabled; + qcom,chg-inhibit-disabled; + qcom,float-voltage-mv = <4200>; + qcom,iterm-ma = <200>; + qcom,recharge-thresh-mv = <100>; + qcom,thermal-mitigation = <1500 700 600 0>; + regulator-name = "smb1360_otg_vreg"; + }; +}; + +&pm8916_chg { + status = "ok"; + qcom,use-external-charger; +}; + +&pm8916_bms { + status = "ok"; + qcom,disable-bms; +}; + +&usb_otg { + interrupts = <0 134 0>,<0 140 0>,<0 136 0>; + interrupt-names = "core_irq", "async_irq", "phy_irq"; + + qcom,hsusb-otg-mode = <3>; + vbus_otg-supply = <&smb1360_otg_supply>; +}; + +&mdss_fb0 { + status = "disabled"; + /delete-node/ qcom,cont-splash-memory; +}; + +&mdss_mdp { + status = "disabled"; +}; + +&mdss_dsi0_pll { + status = "disabled"; +}; + +&mdss_dsi0 { + status = "disabled"; +}; + +/delete-node/ &cont_splash_mem; -- GitLab From 495287ce67849429bbe5660038b4c5f421cfe948 Mon Sep 17 00:00:00 2001 From: Srinivasarao P Date: Thu, 28 Dec 2017 16:17:54 +0530 Subject: [PATCH 2141/5498] mm: reintroduce the config ARCH_HAS_ELF_RANDOMIZE Config ARCH_HAS_ELF_RANDOMIZE is originally introduced in 'commit 2b68f6caeac2 ("mm: expose arch_mmap_rnd when available")'. It got deleted while resolving some merge conflicts. Adding it again to fix CTS issue. Change-Id: I99e45144536bdb68b0690a978fabc9c09540674d Signed-off-by: Srinivasarao P --- arch/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index c484676a9b8b..da35741f4a1e 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -561,6 +561,13 @@ config ARCH_MMAP_RND_COMPAT_BITS This value can be changed after boot using the /proc/sys/vm/mmap_rnd_compat_bits tunable +config ARCH_HAS_ELF_RANDOMIZE + bool + help + An architecture supports choosing randomized locations for + stack, mmap, brk, and ET_DYN. Defined functions: + - arch_mmap_rnd() + # # ABI hall of shame # -- GitLab From 8de9265bf03f0ab4718974eb1b726e9ab3762d8b Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Tue, 19 Dec 2017 19:15:19 +0200 Subject: [PATCH 2142/5498] msm: ipa: Fix the handling of default IPA header Default IPA header is added or deleted from the driver directly and not by user space application. This change prevents adding/deleting it from user application which may cause inconsistencies in the driver. Also the change fixes the header reset function to skip on the correct default header. Change-Id: Ic813433655411f1447db8b0c15efdf64038d8c26 CRs-fixed: 2151146 Signed-off-by: Ghanim Fodi --- drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c | 28 +++++++++++++++++------ drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c | 28 +++++++++++++++++------ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index a30b12047b35..2432dba2f619 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -910,8 +910,17 @@ int __ipa_del_hdr(u32 hdr_hdl, bool by_user) return -EINVAL; } - if (by_user) + if (by_user) { + if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { + IPADBG("Trying to delete hdr %s offset=%u\n", + entry->name, entry->offset_entry->offset); + if (!entry->offset_entry->offset) { + IPAERR("User cannot delete default header\n"); + return -EPERM; + } + } entry->user_deleted = true; + } if (--entry->ref_cnt) { IPADBG("hdr_hdl %x ref_cnt %d\n", hdr_hdl, entry->ref_cnt); @@ -1234,13 +1243,18 @@ int ipa2_reset_hdr(void) /* do not remove the default header */ if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { - if (entry->is_hdr_proc_ctx) { - mutex_unlock(&ipa_ctx->lock); - WARN_ON(1); - IPAERR("default header is proc ctx\n"); - return -EFAULT; + IPADBG("Trying to remove hdr %s offset=%u\n", + entry->name, entry->offset_entry->offset); + if (!entry->offset_entry->offset) { + if (entry->is_hdr_proc_ctx) { + mutex_unlock(&ipa_ctx->lock); + WARN_ON(1); + IPAERR("default header is proc ctx\n"); + return -EFAULT; + } + IPADBG("skip default header\n"); + continue; } - continue; } if (ipa_id_find(entry->id) == NULL) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index 95a377759dfe..ec8790c52556 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -692,8 +692,17 @@ int __ipa3_del_hdr(u32 hdr_hdl, bool by_user) return -EINVAL; } - if (by_user) + if (by_user) { + if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { + IPADBG("Trying to delete hdr %s offset=%u\n", + entry->name, entry->offset_entry->offset); + if (!entry->offset_entry->offset) { + IPAERR("User cannot delete default header\n"); + return -EPERM; + } + } entry->user_deleted = true; + } if (--entry->ref_cnt) { IPADBG("hdr_hdl %x ref_cnt %d\n", hdr_hdl, entry->ref_cnt); @@ -992,13 +1001,18 @@ int ipa3_reset_hdr(void) /* do not remove the default header */ if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { - if (entry->is_hdr_proc_ctx) { - IPAERR("default header is proc ctx\n"); - mutex_unlock(&ipa3_ctx->lock); - WARN_ON_RATELIMIT_IPA(1); - return -EFAULT; + IPADBG("Trying to remove hdr %s offset=%u\n", + entry->name, entry->offset_entry->offset); + if (!entry->offset_entry->offset) { + if (entry->is_hdr_proc_ctx) { + IPAERR("default header is proc ctx\n"); + mutex_unlock(&ipa3_ctx->lock); + WARN_ON_RATELIMIT_IPA(1); + return -EFAULT; + } + IPADBG("skip default header\n"); + continue; } - continue; } if (ipa3_id_find(entry->id) == NULL) { -- GitLab From 3b2df3a4e8982559fb9f48c4ff4c599987b08abc Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Thu, 14 Dec 2017 14:52:17 +0530 Subject: [PATCH 2143/5498] ASoC: msm: enable TDM mics with gpio controlled TDM mics are controlled by switch drive corresponding gpio to output high to enable switch. CRs-Fixed: 2164870 Change-Id: I169d5726d43494922aa027fb28c6e9eef7dcf0c5 Signed-off-by: Surendar karka --- .../bindings/sound/qcom-audio-dev.txt | 3 +++ sound/soc/msm/apq8009-i2s-ext-codec.c | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 3810d7c57633..01277a7454c6 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -1997,6 +1997,9 @@ Optional properties: - qcom,wsa-devs: This property contains list of wsa codec names. The names should comply with the wsa nodes configurations. - qcom,wsa-aux-dev-prefix: This property contains list of wsa codec prefixes. +- qcom,tdm-i2s-switch-enable: For chipsets where tdm mics are controlled by + switch, drive corresponding gpio to output high + to enable switch. Example: sound { diff --git a/sound/soc/msm/apq8009-i2s-ext-codec.c b/sound/soc/msm/apq8009-i2s-ext-codec.c index b6790e2682a9..8b10471d4832 100644 --- a/sound/soc/msm/apq8009-i2s-ext-codec.c +++ b/sound/soc/msm/apq8009-i2s-ext-codec.c @@ -1,4 +1,4 @@ - /* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + /* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2733,6 +2733,7 @@ static int apq8009_asoc_machine_probe(struct platform_device *pdev) const char *mclk = "qcom,msm-mclk-freq"; const char *type = NULL; int ret, id; + int tdm_i2s_switch_enable = -EINVAL; pdata = devm_kzalloc(&pdev->dev, sizeof(struct apq8009_asoc_mach_data), GFP_KERNEL); @@ -2830,6 +2831,23 @@ static int apq8009_asoc_machine_probe(struct platform_device *pdev) ret); goto err; } + + tdm_i2s_switch_enable = of_get_named_gpio(pdev->dev.of_node, + "qcom,tdm-i2s-switch-enable", 0); + if (tdm_i2s_switch_enable >= 0) { + dev_dbg(&pdev->dev, "%s: tdm switch gpio %d", __func__, + tdm_i2s_switch_enable); + ret = gpio_request(tdm_i2s_switch_enable, "TDM_RESET"); + if (ret) { + pr_err("%s: Failed to request gpio\n", __func__); + goto err; + } + gpio_direction_output(tdm_i2s_switch_enable, 1); + } else + dev_err(&pdev->dev, "Looking up %s property in node %s failed\n", + "qcom,tdm-i2s-switch-enable", + pdev->dev.of_node->full_name); + return 0; err: if (pdata->vaddr_gpio_mux_spkr_ctl) -- GitLab From 1e62e22d884995129a5b1c53f9c49d80601999ff Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Thu, 28 Dec 2017 10:04:53 +0800 Subject: [PATCH 2144/5498] power_supply: Add REAL_TYPE power_supply_property Add REAL_TYPE power_supply property to record the real time charger type. Change-Id: I5fb2e3e3e782bcac0f8dd6071a830bcf370ebbd4 Signed-off-by: Fenglin Wu --- drivers/power/power_supply_sysfs.c | 4 +++- include/linux/power_supply.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 45d1bef8a1ea..0fd9357b0cea 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -114,7 +114,8 @@ static ssize_t power_supply_show_property(struct device *dev, return sprintf(buf, "%s\n", technology_text[value.intval]); else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) return sprintf(buf, "%s\n", capacity_level_text[value.intval]); - else if (off == POWER_SUPPLY_PROP_TYPE) + else if (off == POWER_SUPPLY_PROP_TYPE || + off == POWER_SUPPLY_PROP_REAL_TYPE) return sprintf(buf, "%s\n", type_text[value.intval]); else if (off == POWER_SUPPLY_PROP_SCOPE) return sprintf(buf, "%s\n", scope_text[value.intval]); @@ -305,6 +306,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(die_health), POWER_SUPPLY_ATTR(connector_health), POWER_SUPPLY_ATTR(hw_current_max), + POWER_SUPPLY_ATTR(real_type), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 1377722fd63a..7d3c3df65886 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -259,6 +259,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_DIE_HEALTH, POWER_SUPPLY_PROP_CONNECTOR_HEALTH, POWER_SUPPLY_PROP_HW_CURRENT_MAX, + POWER_SUPPLY_PROP_REAL_TYPE, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ -- GitLab From 254ef3c7968ab06bf01d9e0cd434ad84fe3f2a7f Mon Sep 17 00:00:00 2001 From: Trishansh Bhardwaj Date: Fri, 22 Dec 2017 11:53:26 +0530 Subject: [PATCH 2145/5498] msm: camera: Prevent buffer overread in write_logsync. If userspace issues write with string of length 21 or more then there is a chance that kernel will overread lbuf array. This change makes sure that lbuf is NULL terminated. Change-Id: I9ad6d5a607b2ff1f293512be9746ee554b076b10 Signed-off-by: Trishansh Bhardwaj --- drivers/media/platform/msm/camera_v2/msm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 828eca671c42..a2a14335ae34 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -1273,7 +1273,7 @@ static ssize_t write_logsync(struct file *file, const char __user *buf, uint64_t seq_num = 0; int ret; - if (copy_from_user(lbuf, buf, sizeof(lbuf))) + if (copy_from_user(lbuf, buf, sizeof(lbuf) - 1)) return -EFAULT; ret = sscanf(lbuf, "%llu", &seq_num); -- GitLab From 4aee80b3d1c9dfe096775b573c7eb647819d845f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Dec 2017 13:31:16 +0100 Subject: [PATCH 2146/5498] ACPI: APEI / ERST: Fix missing error handling in erst_reader() commit bb82e0b4a7e96494f0c1004ce50cec3d7b5fb3d1 upstream. The commit f6f828513290 ("pstore: pass allocated memory region back to caller") changed the check of the return value from erst_read() in erst_reader() in the following way: if (len == -ENOENT) goto skip; - else if (len < 0) { - rc = -1; + else if (len < sizeof(*rcd)) { + rc = -EIO; goto out; This introduced another bug: since the comparison with sizeof() is cast to unsigned, a negative len value doesn't hit any longer. As a result, when an error is returned from erst_read(), the code falls through, and it may eventually lead to some weird thing like memory corruption. This patch adds the negative error value check more explicitly for addressing the issue. Fixes: f6f828513290 (pstore: pass allocated memory region back to caller) Tested-by: Jerry Tang Signed-off-by: Takashi Iwai Acked-by: Kees Cook Reviewed-by: Borislav Petkov Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/apei/erst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index ed65e9c4b5b0..ba4930c0e98c 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -1023,7 +1023,7 @@ skip: /* The record may be cleared by others, try read next record */ if (len == -ENOENT) goto skip; - else if (len < sizeof(*rcd)) { + else if (len < 0 || len < sizeof(*rcd)) { rc = -EIO; goto out; } -- GitLab From 19ddbcf647c7368e91b1710b1e82cd6cdaa2e0dd Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 30 Nov 2017 13:39:27 +0100 Subject: [PATCH 2147/5498] crypto: mcryptd - protect the per-CPU queue with a lock commit 9abffc6f2efe46c3564c04312e52e07622d40e51 upstream. mcryptd_enqueue_request() grabs the per-CPU queue struct and protects access to it with disabled preemption. Then it schedules a worker on the same CPU. The worker in mcryptd_queue_worker() guards access to the same per-CPU variable with disabled preemption. If we take CPU-hotplug into account then it is possible that between queue_work_on() and the actual invocation of the worker the CPU goes down and the worker will be scheduled on _another_ CPU. And here the preempt_disable() protection does not work anymore. The easiest thing is to add a spin_lock() to guard access to the list. Another detail: mcryptd_queue_worker() is not processing more than MCRYPTD_BATCH invocation in a row. If there are still items left, then it will invoke queue_work() to proceed with more later. *I* would suggest to simply drop that check because it does not use a system workqueue and the workqueue is already marked as "CPU_INTENSIVE". And if preemption is required then the scheduler should do it. However if queue_work() is used then the work item is marked as CPU unbound. That means it will try to run on the local CPU but it may run on another CPU as well. Especially with CONFIG_DEBUG_WQ_FORCE_RR_CPU=y. Again, the preempt_disable() won't work here but lock which was introduced will help. In order to keep work-item on the local CPU (and avoid RR) I changed it to queue_work_on(). Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/mcryptd.c | 23 ++++++++++------------- include/crypto/mcryptd.h | 1 + 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c index 02a1c10fa909..8cd2bc32c7fb 100644 --- a/crypto/mcryptd.c +++ b/crypto/mcryptd.c @@ -80,6 +80,7 @@ static int mcryptd_init_queue(struct mcryptd_queue *queue, pr_debug("cpu_queue #%d %p\n", cpu, queue->cpu_queue); crypto_init_queue(&cpu_queue->queue, max_cpu_qlen); INIT_WORK(&cpu_queue->work, mcryptd_queue_worker); + spin_lock_init(&cpu_queue->q_lock); } return 0; } @@ -103,15 +104,16 @@ static int mcryptd_enqueue_request(struct mcryptd_queue *queue, int cpu, err; struct mcryptd_cpu_queue *cpu_queue; - cpu = get_cpu(); - cpu_queue = this_cpu_ptr(queue->cpu_queue); - rctx->tag.cpu = cpu; + cpu_queue = raw_cpu_ptr(queue->cpu_queue); + spin_lock(&cpu_queue->q_lock); + cpu = smp_processor_id(); + rctx->tag.cpu = smp_processor_id(); err = crypto_enqueue_request(&cpu_queue->queue, request); pr_debug("enqueue request: cpu %d cpu_queue %p request %p\n", cpu, cpu_queue, request); + spin_unlock(&cpu_queue->q_lock); queue_work_on(cpu, kcrypto_wq, &cpu_queue->work); - put_cpu(); return err; } @@ -164,16 +166,11 @@ static void mcryptd_queue_worker(struct work_struct *work) cpu_queue = container_of(work, struct mcryptd_cpu_queue, work); i = 0; while (i < MCRYPTD_BATCH || single_task_running()) { - /* - * preempt_disable/enable is used to prevent - * being preempted by mcryptd_enqueue_request() - */ - local_bh_disable(); - preempt_disable(); + + spin_lock_bh(&cpu_queue->q_lock); backlog = crypto_get_backlog(&cpu_queue->queue); req = crypto_dequeue_request(&cpu_queue->queue); - preempt_enable(); - local_bh_enable(); + spin_unlock_bh(&cpu_queue->q_lock); if (!req) { mcryptd_opportunistic_flush(); @@ -188,7 +185,7 @@ static void mcryptd_queue_worker(struct work_struct *work) ++i; } if (cpu_queue->queue.qlen) - queue_work(kcrypto_wq, &cpu_queue->work); + queue_work_on(smp_processor_id(), kcrypto_wq, &cpu_queue->work); } void mcryptd_flusher(struct work_struct *__work) diff --git a/include/crypto/mcryptd.h b/include/crypto/mcryptd.h index c23ee1f7ee80..c2ff077168d3 100644 --- a/include/crypto/mcryptd.h +++ b/include/crypto/mcryptd.h @@ -26,6 +26,7 @@ static inline struct mcryptd_ahash *__mcryptd_ahash_cast( struct mcryptd_cpu_queue { struct crypto_queue queue; + spinlock_t q_lock; struct work_struct work; }; -- GitLab From d3cc4822354dd4d4fd0601bb0c794ce536ee7c37 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 11 Nov 2017 16:38:43 +0100 Subject: [PATCH 2148/5498] mfd: twl4030-audio: Fix sibling-node lookup commit 0a423772de2f3d7b00899987884f62f63ae00dcb upstream. A helper purported to look up a child node based on its name was using the wrong of-helper and ended up prematurely freeing the parent of-node while leaking any matching node. To make things worse, any matching node would not even necessarily be a child node as the whole device tree was searched depth-first starting at the parent. Fixes: 019a7e6b7b31 ("mfd: twl4030-audio: Add DT support") Signed-off-by: Johan Hovold Acked-by: Peter Ujfalusi Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/twl4030-audio.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index 07fe542e6fc0..9372f5e6b677 100644 --- a/drivers/mfd/twl4030-audio.c +++ b/drivers/mfd/twl4030-audio.c @@ -159,13 +159,18 @@ unsigned int twl4030_audio_get_mclk(void) EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); static bool twl4030_audio_has_codec(struct twl4030_audio_data *pdata, - struct device_node *node) + struct device_node *parent) { + struct device_node *node; + if (pdata && pdata->codec) return true; - if (of_find_node_by_name(node, "codec")) + node = of_get_child_by_name(parent, "codec"); + if (node) { + of_node_put(node); return true; + } return false; } -- GitLab From 8651a4705a0a4aad43c436af7f05245ebb557c33 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 11 Nov 2017 16:38:44 +0100 Subject: [PATCH 2149/5498] mfd: twl6040: Fix child-node lookup commit 85e9b13cbb130a3209f21bd7933933399c389ffe upstream. Fix child-node lookup during probe, which ended up searching the whole device tree depth-first starting at the parent rather than just matching on its children. To make things worse, the parent node was prematurely freed, while the child node was leaked. Note that the CONFIG_OF compile guard can be removed as of_get_child_by_name() provides a !CONFIG_OF implementation which always fails. Fixes: 37e13cecaa14 ("mfd: Add support for Device Tree to twl6040") Fixes: ca2cad6ae38e ("mfd: Fix twl6040 build failure") Signed-off-by: Johan Hovold Acked-by: Peter Ujfalusi Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/twl6040.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index 9687645162ae..902acc48bc6c 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -97,12 +97,16 @@ static struct reg_default twl6040_patch[] = { }; -static bool twl6040_has_vibra(struct device_node *node) +static bool twl6040_has_vibra(struct device_node *parent) { -#ifdef CONFIG_OF - if (of_find_node_by_name(node, "vibra")) + struct device_node *node; + + node = of_get_child_by_name(parent, "vibra"); + if (node) { + of_node_put(node); return true; -#endif + } + return false; } -- GitLab From db66b2dc2c8b766d17d4b0fc5091b8723a508976 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Dec 2017 16:44:12 +0100 Subject: [PATCH 2150/5498] ALSA: rawmidi: Avoid racy info ioctl via ctl device commit c1cfd9025cc394fd137a01159d74335c5ac978ce upstream. The rawmidi also allows to obtaining the information via ioctl of ctl API. It means that user can issue an ioctl to the rawmidi device even when it's being removed as long as the control device is present. Although the code has some protection via the global register_mutex, its range is limited to the search of the corresponding rawmidi object, and the mutex is already unlocked at accessing the rawmidi object. This may lead to a use-after-free. For avoiding it, this patch widens the application of register_mutex to the whole snd_rawmidi_info_select() function. We have another mutex per rawmidi object, but this operation isn't very hot path, so it shouldn't matter from the performance POV. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/rawmidi.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 857741431fc6..f7be2556eb93 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -589,15 +589,14 @@ static int snd_rawmidi_info_user(struct snd_rawmidi_substream *substream, return 0; } -int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info) +static int __snd_rawmidi_info_select(struct snd_card *card, + struct snd_rawmidi_info *info) { struct snd_rawmidi *rmidi; struct snd_rawmidi_str *pstr; struct snd_rawmidi_substream *substream; - mutex_lock(®ister_mutex); rmidi = snd_rawmidi_search(card, info->device); - mutex_unlock(®ister_mutex); if (!rmidi) return -ENXIO; if (info->stream < 0 || info->stream > 1) @@ -613,6 +612,16 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info } return -ENXIO; } + +int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info) +{ + int ret; + + mutex_lock(®ister_mutex); + ret = __snd_rawmidi_info_select(card, info); + mutex_unlock(®ister_mutex); + return ret; +} EXPORT_SYMBOL(snd_rawmidi_info_select); static int snd_rawmidi_info_select_user(struct snd_card *card, -- GitLab From f11d60dc210fc981f5f52969690281dd33cb49b7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Dec 2017 23:36:57 +0100 Subject: [PATCH 2151/5498] ALSA: usb-audio: Fix the missing ctl name suffix at parsing SU commit 5a15f289ee87eaf33f13f08a4909ec99d837ec5f upstream. The commit 89b89d121ffc ("ALSA: usb-audio: Add check return value for usb_string()") added the check of the return value from snd_usb_copy_string_desc(), which is correct per se, but it introduced a regression. In the original code, either the "Clock Source", "Playback Source" or "Capture Source" suffix is added after the terminal string, while the commit changed it to add the suffix only when get_term_name() is failing. It ended up with an incorrect ctl name like "PCM" instead of "PCM Capture Source". Also, even the original code has a similar bug: when the ctl name is generated from snd_usb_copy_string_desc() for the given iSelector, it also doesn't put the suffix. This patch addresses these issues: the suffix is added always when no static mapping is found. Also the patch tries to put more comments and cleans up the if/else block for better readability in order to avoid the same pitfall again. Fixes: 89b89d121ffc ("ALSA: usb-audio: Add check return value for usb_string()") Reported-and-tested-by: Mauro Santos Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 92ad24ef1c5b..91c98bbc04fa 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2094,20 +2094,25 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, kctl->private_value = (unsigned long)namelist; kctl->private_free = usb_mixer_selector_elem_free; - nameid = uac_selector_unit_iSelector(desc); + /* check the static mapping table at first */ len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); - if (len) - ; - else if (nameid) - len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, - sizeof(kctl->id.name)); - else - len = get_term_name(state, &state->oterm, - kctl->id.name, sizeof(kctl->id.name), 0); - if (!len) { - strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); + /* no mapping ? */ + /* if iSelector is given, use it */ + nameid = uac_selector_unit_iSelector(desc); + if (nameid) + len = snd_usb_copy_string_desc(state, nameid, + kctl->id.name, + sizeof(kctl->id.name)); + /* ... or pick up the terminal name at next */ + if (!len) + len = get_term_name(state, &state->oterm, + kctl->id.name, sizeof(kctl->id.name), 0); + /* ... or use the fixed string "USB" as the last resort */ + if (!len) + strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); + /* and add the proper suffix */ if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) append_ctl_name(kctl, " Clock Source"); else if ((state->oterm.type & 0xff00) == 0x0100) -- GitLab From 59a2e05a53a68adb0ce2a6fd122d62422965ab09 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Dec 2017 03:07:18 +0100 Subject: [PATCH 2152/5498] PCI / PM: Force devices to D0 in pci_pm_thaw_noirq() commit 5839ee7389e893a31e4e3c9cf17b50d14103c902 upstream. It is incorrect to call pci_restore_state() for devices in low-power states (D1-D3), as that involves the restoration of MSI setup which requires MMIO to be operational and that is only the case in D0. However, pci_pm_thaw_noirq() may do that if the driver's "freeze" callbacks put the device into a low-power state, so fix it by making it force devices into D0 via pci_set_power_state() instead of trying to "update" their power state which is pointless. Fixes: e60514bd4485 (PCI/PM: Restore the status of PCI devices across hibernation) Reported-by: Thomas Gleixner Reported-by: Maarten Lankhorst Tested-by: Thomas Gleixner Tested-by: Maarten Lankhorst Signed-off-by: Rafael J. Wysocki Acked-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e7adfbd2d482..0ad37e69746d 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -924,7 +924,12 @@ static int pci_pm_thaw_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - pci_update_current_state(pci_dev, PCI_D0); + /* + * pci_restore_state() requires the device to be in D0 (because of MSI + * restoration among other things), so force it into D0 in case the + * driver's "freeze" callbacks put it into a low-power state directly. + */ + pci_set_power_state(pci_dev, PCI_D0); pci_restore_state(pci_dev); if (drv && drv->pm && drv->pm->thaw_noirq) -- GitLab From 96a44e59c94d38366c7f9246e382a358c31fed04 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 12 Dec 2017 21:52:26 +0100 Subject: [PATCH 2153/5498] parisc: Hide Diva-built-in serial aux and graphics card commit bcf3f1752a622f1372d3252d0fea8855d89812e7 upstream. Diva GSP card has built-in serial AUX port and ATI graphic card which simply don't work and which both don't have external connectors. User Guides even mention that those devices shouldn't be used. So, prevent that Linux drivers try to enable those devices. Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/parisc/lba_pci.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 4590515a275c..23817b0c88cb 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -1652,3 +1652,36 @@ void lba_set_iregs(struct parisc_device *lba, u32 ibase, u32 imask) iounmap(base_addr); } + +/* + * The design of the Diva management card in rp34x0 machines (rp3410, rp3440) + * seems rushed, so that many built-in components simply don't work. + * The following quirks disable the serial AUX port and the built-in ATI RV100 + * Radeon 7000 graphics card which both don't have any external connectors and + * thus are useless, and even worse, e.g. the AUX port occupies ttyS0 and as + * such makes those machines the only PARISC machines on which we can't use + * ttyS0 as boot console. + */ +static void quirk_diva_ati_card(struct pci_dev *dev) +{ + if (dev->subsystem_vendor != PCI_VENDOR_ID_HP || + dev->subsystem_device != 0x1292) + return; + + dev_info(&dev->dev, "Hiding Diva built-in ATI card"); + dev->device = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QY, + quirk_diva_ati_card); + +static void quirk_diva_aux_disable(struct pci_dev *dev) +{ + if (dev->subsystem_vendor != PCI_VENDOR_ID_HP || + dev->subsystem_device != 0x1291) + return; + + dev_info(&dev->dev, "Hiding Diva built-in AUX serial device"); + dev->device = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, + quirk_diva_aux_disable); -- GitLab From 02952cd43e16b4be10e5f49a03fc3256fb10de1e Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Thu, 7 Dec 2017 00:30:08 -0800 Subject: [PATCH 2154/5498] KVM: X86: Fix load RFLAGS w/o the fixed bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d73235d17ba63b53dc0e1051dbc10a1f1be91b71 upstream. *** Guest State *** CR0: actual=0x0000000000000030, shadow=0x0000000060000010, gh_mask=fffffffffffffff7 CR4: actual=0x0000000000002050, shadow=0x0000000000000000, gh_mask=ffffffffffffe871 CR3 = 0x00000000fffbc000 RSP = 0x0000000000000000 RIP = 0x0000000000000000 RFLAGS=0x00000000 DR7 = 0x0000000000000400 ^^^^^^^^^^ The failed vmentry is triggered by the following testcase when ept=Y: #include #include #include #include #include #include #include long r[5]; int main() { r[2] = open("/dev/kvm", O_RDONLY); r[3] = ioctl(r[2], KVM_CREATE_VM, 0); r[4] = ioctl(r[3], KVM_CREATE_VCPU, 7); struct kvm_regs regs = { .rflags = 0, }; ioctl(r[4], KVM_SET_REGS, ®s); ioctl(r[4], KVM_RUN, 0); } X86 RFLAGS bit 1 is fixed set, userspace can simply clearing bit 1 of RFLAGS with KVM_SET_REGS ioctl which results in vmentry fails. This patch fixes it by oring X86_EFLAGS_FIXED during ioctl. Suggested-by: Jim Mattson Reviewed-by: David Hildenbrand Reviewed-by: Quan Xu Cc: Paolo Bonzini Cc: Radim KrÄmář Cc: Jim Mattson Signed-off-by: Wanpeng Li Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c8105be39b00..0dcbe370374f 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6644,7 +6644,7 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) #endif kvm_rip_write(vcpu, regs->rip); - kvm_set_rflags(vcpu, regs->rflags); + kvm_set_rflags(vcpu, regs->rflags | X86_EFLAGS_FIXED); vcpu->arch.exception.pending = false; -- GitLab From b907723a77bd22b75fe4708668ec70378c831915 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Tue, 12 Dec 2017 17:59:15 +0530 Subject: [PATCH 2155/5498] powerpc/perf: Dereference BHRB entries safely commit f41d84dddc66b164ac16acf3f584c276146f1c48 upstream. It's theoretically possible that branch instructions recorded in BHRB (Branch History Rolling Buffer) entries have already been unmapped before they are processed by the kernel. Hence, trying to dereference such memory location will result in a crash. eg: Unable to handle kernel paging request for data at address 0xd000000019c41764 Faulting instruction address: 0xc000000000084a14 NIP [c000000000084a14] branch_target+0x4/0x70 LR [c0000000000eb828] record_and_restart+0x568/0x5c0 Call Trace: [c0000000000eb3b4] record_and_restart+0xf4/0x5c0 (unreliable) [c0000000000ec378] perf_event_interrupt+0x298/0x460 [c000000000027964] performance_monitor_exception+0x54/0x70 [c000000000009ba4] performance_monitor_common+0x114/0x120 Fix it by deferefencing the addresses safely. Fixes: 691231846ceb ("powerpc/perf: Fix setting of "to" addresses for BHRB") Suggested-by: Naveen N. Rao Signed-off-by: Ravi Bangoria Reviewed-by: Naveen N. Rao [mpe: Use probe_kernel_read() which is clearer, tweak change log] Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/perf/core-book3s.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index ffa07eb5fff0..ba5f0580d5b0 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -396,8 +396,12 @@ static __u64 power_pmu_bhrb_to(u64 addr) int ret; __u64 target; - if (is_kernel_addr(addr)) - return branch_target((unsigned int *)addr); + if (is_kernel_addr(addr)) { + if (probe_kernel_read(&instr, (void *)addr, sizeof(instr))) + return 0; + + return branch_target(&instr); + } /* Userspace: need copy instruction here then translate it */ pagefault_disable(); -- GitLab From d0473d5cca66b3d7684029d68944baab69a3b560 Mon Sep 17 00:00:00 2001 From: Yelena Krivosheev Date: Tue, 19 Dec 2017 17:59:45 +0100 Subject: [PATCH 2156/5498] net: mvneta: clear interface link status on port disable commit 4423c18e466afdfb02a36ee8b9f901d144b3c607 upstream. When port connect to PHY in polling mode (with poll interval 1 sec), port and phy link status must be synchronize in order don't loss link change event. [gregory.clement@free-electrons.com: add fixes tag] Fixes: c5aff18204da ("net: mvneta: driver for Marvell Armada 370/XP network unit") Signed-off-by: Yelena Krivosheev Tested-by: Dmitri Epshtein Signed-off-by: Gregory CLEMENT Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/mvneta.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index e6a7b50a4781..a53e7c6172a3 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -851,6 +851,10 @@ static void mvneta_port_disable(struct mvneta_port *pp) val &= ~MVNETA_GMAC0_PORT_ENABLE; mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); + pp->link = 0; + pp->duplex = -1; + pp->speed = 0; + udelay(200); } -- GitLab From 973b645673b626beed3b20aedddf2ff51b8fcab8 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 22 Dec 2017 20:38:57 -0500 Subject: [PATCH 2157/5498] tracing: Remove extra zeroing out of the ring buffer page commit 6b7e633fe9c24682df550e5311f47fb524701586 upstream. The ring_buffer_read_page() takes care of zeroing out any extra data in the page that it returns. There's no need to zero it out again from the consumer. It was removed from one consumer of this function, but read_buffers_splice_read() did not remove it, and worse, it contained a nasty bug because of it. Fixes: 2711ca237a084 ("ring-buffer: Move zeroing out excess in page to ring buffer code") Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index e61b862309af..10286ffd9112 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5502,7 +5502,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, .spd_release = buffer_spd_release, }; struct buffer_ref *ref; - int entries, size, i; + int entries, i; ssize_t ret = 0; mutex_lock(&trace_types_lock); @@ -5563,14 +5563,6 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, break; } - /* - * zero out any left over data, this is going to - * user land. - */ - size = ring_buffer_page_len(ref->page); - if (size < PAGE_SIZE) - memset(ref->page + size, 0, PAGE_SIZE - size); - page = virt_to_page(ref->page); spd.pages[i] = page; -- GitLab From f9e16c238bd6da1d858d50c1ab81c8431578877a Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 26 Dec 2017 20:07:34 -0500 Subject: [PATCH 2158/5498] tracing: Fix possible double free on failure of allocating trace buffer commit 4397f04575c44e1440ec2e49b6302785c95fd2f8 upstream. Jing Xia and Chunyan Zhang reported that on failing to allocate part of the tracing buffer, memory is freed, but the pointers that point to them are not initialized back to NULL, and later paths may try to free the freed memory again. Jing and Chunyan fixed one of the locations that does this, but missed a spot. Link: http://lkml.kernel.org/r/20171226071253.8968-1-chunyan.zhang@spreadtrum.com Fixes: 737223fbca3b1 ("tracing: Consolidate buffer allocation code") Reported-by: Jing Xia Reported-by: Chunyan Zhang Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 10286ffd9112..34fb8fc15f59 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6244,6 +6244,7 @@ allocate_trace_buffer(struct trace_array *tr, struct trace_buffer *buf, int size buf->data = alloc_percpu(struct trace_array_cpu); if (!buf->data) { ring_buffer_free(buf->buffer); + buf->buffer = NULL; return -ENOMEM; } -- GitLab From 42c1b3e6f205d9766a4b43f373db3b0e1adaf060 Mon Sep 17 00:00:00 2001 From: Jing Xia Date: Tue, 26 Dec 2017 15:12:53 +0800 Subject: [PATCH 2159/5498] tracing: Fix crash when it fails to alloc ring buffer commit 24f2aaf952ee0b59f31c3a18b8b36c9e3d3c2cf5 upstream. Double free of the ring buffer happens when it fails to alloc new ring buffer instance for max_buffer if TRACER_MAX_TRACE is configured. The root cause is that the pointer is not set to NULL after the buffer is freed in allocate_trace_buffers(), and the freeing of the ring buffer is invoked again later if the pointer is not equal to Null, as: instance_mkdir() |-allocate_trace_buffers() |-allocate_trace_buffer(tr, &tr->trace_buffer...) |-allocate_trace_buffer(tr, &tr->max_buffer...) // allocate fail(-ENOMEM),first free // and the buffer pointer is not set to null |-ring_buffer_free(tr->trace_buffer.buffer) // out_free_tr |-free_trace_buffers() |-free_trace_buffer(&tr->trace_buffer); //if trace_buffer is not null, free again |-ring_buffer_free(buf->buffer) |-rb_free_cpu_buffer(buffer->buffers[cpu]) // ring_buffer_per_cpu is null, and // crash in ring_buffer_per_cpu->pages Link: http://lkml.kernel.org/r/20171226071253.8968-1-chunyan.zhang@spreadtrum.com Fixes: 737223fbca3b1 ("tracing: Consolidate buffer allocation code") Signed-off-by: Jing Xia Signed-off-by: Chunyan Zhang Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/trace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 34fb8fc15f59..c8717cde9249 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6268,7 +6268,9 @@ static int allocate_trace_buffers(struct trace_array *tr, int size) allocate_snapshot ? size : 1); if (WARN_ON(ret)) { ring_buffer_free(tr->trace_buffer.buffer); + tr->trace_buffer.buffer = NULL; free_percpu(tr->trace_buffer.data); + tr->trace_buffer.data = NULL; return -ENOMEM; } tr->allocated_snapshot = allocate_snapshot; -- GitLab From 5beacbc73691287ef10609a64ae2ab11e34f3139 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 22 Dec 2017 20:32:35 -0500 Subject: [PATCH 2160/5498] ring-buffer: Mask out the info bits when returning buffer page length commit 45d8b80c2ac5d21cd1e2954431fb676bc2b1e099 upstream. Two info bits were added to the "commit" part of the ring buffer data page when returned to be consumed. This was to inform the user space readers that events have been missed, and that the count may be stored at the end of the page. What wasn't handled, was the splice code that actually called a function to return the length of the data in order to zero out the rest of the page before sending it up to user space. These data bits were returned with the length making the value negative, and that negative value was not checked. It was compared to PAGE_SIZE, and only used if the size was less than PAGE_SIZE. Luckily PAGE_SIZE is unsigned long which made the compare an unsigned compare, meaning the negative size value did not end up causing a large portion of memory to be randomly zeroed out. Fixes: 66a8cb95ed040 ("ring-buffer: Add place holder recording of dropped events") Signed-off-by: Steven Rostedt (VMware) Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ring_buffer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index f4856805ca89..68fcf6829bc5 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -336,6 +336,8 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data); /* Missed count stored at end */ #define RB_MISSED_STORED (1 << 30) +#define RB_MISSED_FLAGS (RB_MISSED_EVENTS|RB_MISSED_STORED) + struct buffer_data_page { u64 time_stamp; /* page time stamp */ local_t commit; /* write committed index */ @@ -387,7 +389,9 @@ static void rb_init_page(struct buffer_data_page *bpage) */ size_t ring_buffer_page_len(void *page) { - return local_read(&((struct buffer_data_page *)page)->commit) + struct buffer_data_page *bpage = page; + + return (local_read(&bpage->commit) & ~RB_MISSED_FLAGS) + BUF_PAGE_HDR_SIZE; } -- GitLab From c8d7e04ac14c4d4543f19e9b9edd8b7a1b63f9d6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 13 Nov 2017 12:12:56 +0100 Subject: [PATCH 2161/5498] ASoC: twl4030: fix child-node lookup commit 15f8c5f2415bfac73f33a14bcd83422bcbfb5298 upstream. Fix child-node lookup during probe, which ended up searching the whole device tree depth-first starting at the parent rather than just matching on its children. To make things worse, the parent codec node was also prematurely freed, while the child node was leaked. Fixes: 2d6d649a2e0f ("ASoC: twl4030: Support for DT booted kernel") Signed-off-by: Johan Hovold Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/twl4030.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index b6b0cb399599..d048cda405e7 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -232,7 +232,7 @@ static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec) struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); struct device_node *twl4030_codec_node = NULL; - twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node, + twl4030_codec_node = of_get_child_by_name(codec->dev->parent->of_node, "codec"); if (!pdata && twl4030_codec_node) { @@ -241,9 +241,11 @@ static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec) GFP_KERNEL); if (!pdata) { dev_err(codec->dev, "Can not allocate memory\n"); + of_node_put(twl4030_codec_node); return NULL; } twl4030_setup_pdata_of(pdata, twl4030_codec_node); + of_node_put(twl4030_codec_node); } return pdata; -- GitLab From 20a0462fffafa1d549e93c16d5b9749ed5441e99 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 29 Dec 2017 17:34:43 -0800 Subject: [PATCH 2162/5498] kbuild: add '-fno-stack-check' to kernel build options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3ce120b16cc548472f80cf8644f90eda958cf1b6 upstream. It appears that hardened gentoo enables "-fstack-check" by default for gcc. That doesn't work _at_all_ for the kernel, because the kernel stack doesn't act like a user stack at all: it's much smaller, and it doesn't auto-expand on use. So the extra "probe one page below the stack" code generated by -fstack-check just breaks the kernel in horrible ways, causing infinite double faults etc. [ I have to say, that the particular code gcc generates looks very stupid even for user space where it works, but that's a separate issue. ] Reported-and-tested-by: Alexander Tsoy Reported-and-tested-by: Toralf Förster Cc: Dave Hansen Cc: Jiri Kosina Cc: Andy Lutomirski Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index abbfbbb7a029..02d29c610cb1 100644 --- a/Makefile +++ b/Makefile @@ -762,6 +762,9 @@ KBUILD_CFLAGS += $(call cc-disable-warning, pointer-sign) # disable invalid "can't wrap" optimizations for signed / pointers KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow) +# Make sure -fstack-check isn't enabled (like gentoo apparently did) +KBUILD_CFLAGS += $(call cc-option,-fno-stack-check,) + # conserve stack if available KBUILD_CFLAGS += $(call cc-option,-fconserve-stack) -- GitLab From c8b5bd50bfad60a85dcdfe0c4dc746735a44f101 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 11 Dec 2017 07:17:39 -0800 Subject: [PATCH 2163/5498] ipv4: igmp: guard against silly MTU values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b5476022bbada3764609368f03329ca287528dc8 ] IPv4 stack reacts to changes to small MTU, by disabling itself under RTNL. But there is a window where threads not using RTNL can see a wrong device mtu. This can lead to surprises, in igmp code where it is assumed the mtu is suitable. Fix this by reading device mtu once and checking IPv4 minimal MTU. This patch adds missing IPV4_MIN_MTU define, to not abuse ETH_MIN_MTU anymore. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/ip.h | 2 ++ net/ipv4/devinet.c | 2 +- net/ipv4/igmp.c | 24 +++++++++++++++--------- net/ipv4/ip_tunnel.c | 4 ++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/net/ip.h b/include/net/ip.h index 7f0cd17668ae..55da8ad90582 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -33,6 +33,8 @@ #include #include +#define IPV4_MIN_MTU 68 /* RFC 791 */ + struct sock; struct inet_skb_parm { diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 464293c34b7a..618d55f4aa77 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1328,7 +1328,7 @@ skip: static bool inetdev_valid_mtu(unsigned int mtu) { - return mtu >= 68; + return mtu >= IPV4_MIN_MTU; } static void inetdev_send_gratuitous_arp(struct net_device *dev, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 1ea62ccfa884..66b0381d6c18 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -402,16 +402,17 @@ static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) } static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, - int type, struct igmpv3_grec **ppgr) + int type, struct igmpv3_grec **ppgr, unsigned int mtu) { struct net_device *dev = pmc->interface->dev; struct igmpv3_report *pih; struct igmpv3_grec *pgr; - if (!skb) - skb = igmpv3_newpack(dev, dev->mtu); - if (!skb) - return NULL; + if (!skb) { + skb = igmpv3_newpack(dev, mtu); + if (!skb) + return NULL; + } pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec)); pgr->grec_type = type; pgr->grec_auxwords = 0; @@ -433,10 +434,15 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, struct igmpv3_grec *pgr = NULL; struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; int scount, stotal, first, isquery, truncate; + unsigned int mtu; if (pmc->multiaddr == IGMP_ALL_HOSTS) return skb; + mtu = READ_ONCE(dev->mtu); + if (mtu < IPV4_MIN_MTU) + return skb; + isquery = type == IGMPV3_MODE_IS_INCLUDE || type == IGMPV3_MODE_IS_EXCLUDE; truncate = type == IGMPV3_MODE_IS_EXCLUDE || @@ -457,7 +463,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { if (skb) igmpv3_sendpack(skb); - skb = igmpv3_newpack(dev, dev->mtu); + skb = igmpv3_newpack(dev, mtu); } } first = 1; @@ -484,12 +490,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, pgr->grec_nsrcs = htons(scount); if (skb) igmpv3_sendpack(skb); - skb = igmpv3_newpack(dev, dev->mtu); + skb = igmpv3_newpack(dev, mtu); first = 1; scount = 0; } if (first) { - skb = add_grhead(skb, pmc, type, &pgr); + skb = add_grhead(skb, pmc, type, &pgr, mtu); first = 0; } if (!skb) @@ -523,7 +529,7 @@ empty_source: igmpv3_sendpack(skb); skb = NULL; /* add_grhead will get a new one */ } - skb = add_grhead(skb, pmc, type, &pgr); + skb = add_grhead(skb, pmc, type, &pgr, mtu); } } if (pgr) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 682257242971..7e01368f51ed 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -395,8 +395,8 @@ static int ip_tunnel_bind_dev(struct net_device *dev) dev->needed_headroom = t_hlen + hlen; mtu -= (dev->hard_header_len + t_hlen); - if (mtu < 68) - mtu = 68; + if (mtu < IPV4_MIN_MTU) + mtu = IPV4_MIN_MTU; return mtu; } -- GitLab From ee26de88cb43110b94e40878eb5e20d7dd57f751 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 11 Dec 2017 07:03:38 -0800 Subject: [PATCH 2164/5498] ipv6: mcast: better catch silly mtu values [ Upstream commit b9b312a7a451e9c098921856e7cfbc201120e1a7 ] syzkaller reported crashes in IPv6 stack [1] Xin Long found that lo MTU was set to silly values. IPv6 stack reacts to changes to small MTU, by disabling itself under RTNL. But there is a window where threads not using RTNL can see a wrong device mtu. This can lead to surprises, in mld code where it is assumed the mtu is suitable. Fix this by reading device mtu once and checking IPv6 minimal MTU. [1] skbuff: skb_over_panic: text:0000000010b86b8d len:196 put:20 head:000000003b477e60 data:000000000e85441e tail:0xd4 end:0xc0 dev:lo ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:104! invalid opcode: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 1 PID: 0 Comm: swapper/1 Not tainted 4.15.0-rc2-mm1+ #39 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:skb_panic+0x15c/0x1f0 net/core/skbuff.c:100 RSP: 0018:ffff8801db307508 EFLAGS: 00010286 RAX: 0000000000000082 RBX: ffff8801c517e840 RCX: 0000000000000000 RDX: 0000000000000082 RSI: 1ffff1003b660e61 RDI: ffffed003b660e95 RBP: ffff8801db307570 R08: 1ffff1003b660e23 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: ffffffff85bd4020 R13: ffffffff84754ed2 R14: 0000000000000014 R15: ffff8801c4e26540 FS: 0000000000000000(0000) GS:ffff8801db300000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000463610 CR3: 00000001c6698000 CR4: 00000000001406e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: skb_over_panic net/core/skbuff.c:109 [inline] skb_put+0x181/0x1c0 net/core/skbuff.c:1694 add_grhead.isra.24+0x42/0x3b0 net/ipv6/mcast.c:1695 add_grec+0xa55/0x1060 net/ipv6/mcast.c:1817 mld_send_cr net/ipv6/mcast.c:1903 [inline] mld_ifc_timer_expire+0x4d2/0x770 net/ipv6/mcast.c:2448 call_timer_fn+0x23b/0x840 kernel/time/timer.c:1320 expire_timers kernel/time/timer.c:1357 [inline] __run_timers+0x7e1/0xb60 kernel/time/timer.c:1660 run_timer_softirq+0x4c/0xb0 kernel/time/timer.c:1686 __do_softirq+0x29d/0xbb2 kernel/softirq.c:285 invoke_softirq kernel/softirq.c:365 [inline] irq_exit+0x1d3/0x210 kernel/softirq.c:405 exiting_irq arch/x86/include/asm/apic.h:540 [inline] smp_apic_timer_interrupt+0x16b/0x700 arch/x86/kernel/apic/apic.c:1052 apic_timer_interrupt+0xa9/0xb0 arch/x86/entry/entry_64.S:920 Signed-off-by: Eric Dumazet Reported-by: syzbot Tested-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/mcast.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 5aedf76fe287..9cc16fa524a6 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1670,16 +1670,16 @@ static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel) } static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, - int type, struct mld2_grec **ppgr) + int type, struct mld2_grec **ppgr, unsigned int mtu) { - struct net_device *dev = pmc->idev->dev; struct mld2_report *pmr; struct mld2_grec *pgr; - if (!skb) - skb = mld_newpack(pmc->idev, dev->mtu); - if (!skb) - return NULL; + if (!skb) { + skb = mld_newpack(pmc->idev, mtu); + if (!skb) + return NULL; + } pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec)); pgr->grec_type = type; pgr->grec_auxwords = 0; @@ -1702,10 +1702,15 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, struct mld2_grec *pgr = NULL; struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list; int scount, stotal, first, isquery, truncate; + unsigned int mtu; if (pmc->mca_flags & MAF_NOREPORT) return skb; + mtu = READ_ONCE(dev->mtu); + if (mtu < IPV6_MIN_MTU) + return skb; + isquery = type == MLD2_MODE_IS_INCLUDE || type == MLD2_MODE_IS_EXCLUDE; truncate = type == MLD2_MODE_IS_EXCLUDE || @@ -1726,7 +1731,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { if (skb) mld_sendpack(skb); - skb = mld_newpack(idev, dev->mtu); + skb = mld_newpack(idev, mtu); } } first = 1; @@ -1753,12 +1758,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, pgr->grec_nsrcs = htons(scount); if (skb) mld_sendpack(skb); - skb = mld_newpack(idev, dev->mtu); + skb = mld_newpack(idev, mtu); first = 1; scount = 0; } if (first) { - skb = add_grhead(skb, pmc, type, &pgr); + skb = add_grhead(skb, pmc, type, &pgr, mtu); first = 0; } if (!skb) @@ -1792,7 +1797,7 @@ empty_source: mld_sendpack(skb); skb = NULL; /* add_grhead will get a new one */ } - skb = add_grhead(skb, pmc, type, &pgr); + skb = add_grhead(skb, pmc, type, &pgr, mtu); } } if (pgr) -- GitLab From 70d931c38016ffcff31093ec9f8d0d7312d17fcc Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Mon, 11 Dec 2017 11:13:45 -0800 Subject: [PATCH 2165/5498] net: igmp: Use correct source address on IGMPv3 reports [ Upstream commit a46182b00290839fa3fa159d54fd3237bd8669f0 ] Closing a multicast socket after the final IPv4 address is deleted from an interface can generate a membership report that uses the source IP from a different interface. The following test script, run from an isolated netns, reproduces the issue: #!/bin/bash ip link add dummy0 type dummy ip link add dummy1 type dummy ip link set dummy0 up ip link set dummy1 up ip addr add 10.1.1.1/24 dev dummy0 ip addr add 192.168.99.99/24 dev dummy1 tcpdump -U -i dummy0 & socat EXEC:"sleep 2" \ UDP4-DATAGRAM:239.101.1.68:8889,ip-add-membership=239.0.1.68:10.1.1.1 & sleep 1 ip addr del 10.1.1.1/24 dev dummy0 sleep 5 kill %tcpdump RFC 3376 specifies that the report must be sent with a valid IP source address from the destination subnet, or from address 0.0.0.0. Add an extra check to make sure this is the case. Signed-off-by: Kevin Cernekee Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/igmp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 66b0381d6c18..71882e83bbbd 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -89,6 +89,7 @@ #include #include #include +#include #include #include @@ -318,6 +319,23 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) return scount; } +/* source address selection per RFC 3376 section 4.2.13 */ +static __be32 igmpv3_get_srcaddr(struct net_device *dev, + const struct flowi4 *fl4) +{ + struct in_device *in_dev = __in_dev_get_rcu(dev); + + if (!in_dev) + return htonl(INADDR_ANY); + + for_ifa(in_dev) { + if (inet_ifa_match(fl4->saddr, ifa)) + return fl4->saddr; + } endfor_ifa(in_dev); + + return htonl(INADDR_ANY); +} + static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) { struct sk_buff *skb; @@ -366,7 +384,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) pip->frag_off = htons(IP_DF); pip->ttl = 1; pip->daddr = fl4.daddr; - pip->saddr = fl4.saddr; + pip->saddr = igmpv3_get_srcaddr(dev, &fl4); pip->protocol = IPPROTO_IGMP; pip->tot_len = 0; /* filled in later */ ip_select_ident(skb, NULL); -- GitLab From 5594e3eba3ee62dd06c317086c4ea0491d5502c7 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 6 Dec 2017 12:12:27 -0800 Subject: [PATCH 2166/5498] netlink: Add netns check on taps [ Upstream commit 93c647643b48f0131f02e45da3bd367d80443291 ] Currently, a nlmon link inside a child namespace can observe systemwide netlink activity. Filter the traffic so that nlmon can only sniff netlink messages from its own netns. Test case: vpnns -- bash -c "ip link add nlmon0 type nlmon; \ ip link set nlmon0 up; \ tcpdump -i nlmon0 -q -w /tmp/nlmon.pcap -U" & sudo ip xfrm state add src 10.1.1.1 dst 10.1.1.2 proto esp \ spi 0x1 mode transport \ auth sha1 0x6162633132330000000000000000000000000000 \ enc aes 0x00000000000000000000000000000000 grep --binary abc123 /tmp/nlmon.pcap Signed-off-by: Kevin Cernekee Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/netlink/af_netlink.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2d80cbde47ba..19ec93dff753 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -270,6 +270,9 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb, struct sock *sk = skb->sk; int ret = -ENOMEM; + if (!net_eq(dev_net(dev), sock_net(sk))) + return 0; + dev_hold(dev); if (is_vmalloc_addr(skb->head)) -- GitLab From c0311793a848e5d3992fd74890fee5408c00fb07 Mon Sep 17 00:00:00 2001 From: Sebastian Sjoholm Date: Mon, 11 Dec 2017 21:51:14 +0100 Subject: [PATCH 2167/5498] net: qmi_wwan: add Sierra EM7565 1199:9091 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit aceef61ee56898cfa7b6960fb60b9326c3860441 ] Sierra Wireless EM7565 is an Qualcomm MDM9x50 based M.2 modem. The USB id is added to qmi_wwan.c to allow QMI communication with the EM7565. Signed-off-by: Sebastian Sjoholm Acked-by: Bjørn Mork Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index f098cc9707aa..cdaeb9295afc 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -778,6 +778,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */ {QMI_FIXED_INTF(0x1199, 0x907b, 8)}, /* Sierra Wireless EM74xx */ {QMI_FIXED_INTF(0x1199, 0x907b, 10)}, /* Sierra Wireless EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9091, 8)}, /* Sierra Wireless EM7565 */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ -- GitLab From f07f65cdff98f093b4f2302696cbaf6fb84f6856 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Mon, 11 Dec 2017 00:05:46 -0800 Subject: [PATCH 2168/5498] tcp md5sig: Use skb's saddr when replying to an incoming segment [ Upstream commit 30791ac41927ebd3e75486f9504b6d2280463bf0 ] The MD5-key that belongs to a connection is identified by the peer's IP-address. When we are in tcp_v4(6)_reqsk_send_ack(), we are replying to an incoming segment from tcp_check_req() that failed the seq-number checks. Thus, to find the correct key, we need to use the skb's saddr and not the daddr. This bug seems to have been there since quite a while, but probably got unnoticed because the consequences are not catastrophic. We will call tcp_v4_reqsk_send_ack only to send a challenge-ACK back to the peer, thus the connection doesn't really fail. Fixes: 9501f9722922 ("tcp md5sig: Let the caller pass appropriate key for tcp_v{4,6}_do_calc_md5_hash().") Signed-off-by: Christoph Paasch Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d3d62be2376b..488bd86e2f71 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -810,7 +810,7 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, tcp_time_stamp, req->ts_recent, 0, - tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr, + tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->saddr, AF_INET), inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, ip_hdr(skb)->tos); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index b8e05f16659c..4dfac3865600 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1001,7 +1001,7 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, tcp_rsk(req)->rcv_nxt, req->rcv_wnd, tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if, - tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), + tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr), 0, 0); } -- GitLab From 13550e44cf624e75e57f073fe753b0c45bd2cbf1 Mon Sep 17 00:00:00 2001 From: Brian King Date: Fri, 15 Dec 2017 15:21:50 -0600 Subject: [PATCH 2169/5498] tg3: Fix rx hang on MTU change with 5717/5719 [ Upstream commit 748a240c589824e9121befb1cba5341c319885bc ] This fixes a hang issue seen when changing the MTU size from 1500 MTU to 9000 MTU on both 5717 and 5719 chips. In discussion with Broadcom, they've indicated that these chipsets have the same phy as the 57766 chipset, so the same workarounds apply. This has been tested by IBM on both Power 8 and Power 9 systems as well as by Broadcom on x86 hardware and has been confirmed to resolve the hang issue. Signed-off-by: Brian King Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/tg3.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index a79e73781c0b..05a2f3b304c3 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -14202,7 +14202,9 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu) /* Reset PHY, otherwise the read DMA engine will be in a mode that * breaks all requests to 256 bytes. */ - if (tg3_asic_rev(tp) == ASIC_REV_57766) + if (tg3_asic_rev(tp) == ASIC_REV_57766 || + tg3_asic_rev(tp) == ASIC_REV_5717 || + tg3_asic_rev(tp) == ASIC_REV_5719) reset_phy = true; err = tg3_restart_hw(tp, reset_phy); -- GitLab From a11d92b0779db90c0b0b1617bda3f81aad2438b3 Mon Sep 17 00:00:00 2001 From: Tobias Jordan Date: Wed, 6 Dec 2017 15:23:23 +0100 Subject: [PATCH 2170/5498] net: mvmdio: disable/unprepare clocks in EPROBE_DEFER case [ Upstream commit 589bf32f09852041fbd3b7ce1a9e703f95c230ba ] add appropriate calls to clk_disable_unprepare() by jumping to out_mdio in case orion_mdio_probe() returns -EPROBE_DEFER. Found by Linux Driver Verification project (linuxtesting.org). Fixes: 3d604da1e954 ("net: mvmdio: get and enable optional clock") Signed-off-by: Tobias Jordan Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/mvmdio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index fc2fb25343f4..c122b3b99cd8 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -241,7 +241,8 @@ static int orion_mdio_probe(struct platform_device *pdev) dev->regs + MVMDIO_ERR_INT_MASK); } else if (dev->err_interrupt == -EPROBE_DEFER) { - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto out_mdio; } mutex_init(&dev->lock); -- GitLab From 986444eaddc5919c37319efaa86271539989ef36 Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Fri, 22 Dec 2017 10:15:20 -0800 Subject: [PATCH 2171/5498] sctp: Replace use of sockets_allocated with specified macro. [ Upstream commit 8cb38a602478e9f806571f6920b0a3298aabf042 ] The patch(180d8cd942ce) replaces all uses of struct sock fields' memory_pressure, memory_allocated, sockets_allocated, and sysctl_mem to accessor macros. But the sockets_allocated field of sctp sock is not replaced at all. Then replace it now for unifying the code. Fixes: 180d8cd942ce ("foundations of per-cgroup memory pressure controlling.") Cc: Glauber Costa Signed-off-by: Tonghao Zhang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 8e019692bb42..4477a5443f65 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4166,7 +4166,7 @@ static int sctp_init_sock(struct sock *sk) SCTP_DBG_OBJCNT_INC(sock); local_bh_disable(); - percpu_counter_inc(&sctp_sockets_allocated); + sk_sockets_allocated_inc(sk); sock_prot_inuse_add(net, sk->sk_prot, 1); /* Nothing can fail after this block, otherwise @@ -4210,7 +4210,7 @@ static void sctp_destroy_sock(struct sock *sk) } sctp_endpoint_free(sp->ep); local_bh_disable(); - percpu_counter_dec(&sctp_sockets_allocated); + sk_sockets_allocated_dec(sk); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); local_bh_enable(); } -- GitLab From 000c7141a1feace09bf4c0f65008e51fa69ecede Mon Sep 17 00:00:00 2001 From: Mohamed Ghannam Date: Sun, 10 Dec 2017 03:50:58 +0000 Subject: [PATCH 2172/5498] net: ipv4: fix for a race condition in raw_sendmsg [ Upstream commit 8f659a03a0ba9289b9aeb9b4470e6fb263d6f483 ] inet->hdrincl is racy, and could lead to uninitialized stack pointer usage, so its value should be read only once. Fixes: c008ba5bdc9f ("ipv4: Avoid reading user iov twice after raw_probe_proto_opt") Signed-off-by: Mohamed Ghannam Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/raw.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 2ec944318859..b127807c0bbf 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -483,11 +483,16 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, u8 tos; int err; struct ip_options_data opt_copy; + int hdrincl; err = -EMSGSIZE; if (len > 0xFFFF) goto out; + /* hdrincl should be READ_ONCE(inet->hdrincl) + * but READ_ONCE() doesn't work with bit fields + */ + hdrincl = inet->hdrincl; /* * Check the flags. */ @@ -560,7 +565,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, /* Linux does not mangle headers on raw sockets, * so that IP options + IP_HDRINCL is non-sense. */ - if (inet->hdrincl) + if (hdrincl) goto done; if (ipc.opt->opt.srr) { if (!daddr) @@ -582,12 +587,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, - inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, + hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk) | - (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), + (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), daddr, saddr, 0, 0); - if (!inet->hdrincl) { + if (!hdrincl) { err = raw_probe_proto_opt(&fl4, msg); if (err) goto done; @@ -609,7 +614,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, goto do_confirm; back_from_confirm: - if (inet->hdrincl) + if (hdrincl) err = raw_send_hdrinc(sk, &fl4, msg->msg_iov, len, &rt, msg->msg_flags); -- GitLab From 07f8410606532d8942ade7ffd9a4917fe1b8543c Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Thu, 14 Dec 2017 16:54:45 +0100 Subject: [PATCH 2173/5498] USB: serial: option: add support for Telit ME910 PID 0x1101 commit 08933099e6404f588f81c2050bfec7313e06eeaf upstream. This patch adds support for PID 0x1101 of Telit ME910. Signed-off-by: Daniele Palmas Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 71d032822cd9..9db1338002a2 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -276,6 +276,7 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_LE922_USBCFG3 0x1043 #define TELIT_PRODUCT_LE922_USBCFG5 0x1045 #define TELIT_PRODUCT_ME910 0x1100 +#define TELIT_PRODUCT_ME910_DUAL_MODEM 0x1101 #define TELIT_PRODUCT_LE920 0x1200 #define TELIT_PRODUCT_LE910 0x1201 #define TELIT_PRODUCT_LE910_USBCFG4 0x1206 @@ -644,6 +645,11 @@ static const struct option_blacklist_info telit_me910_blacklist = { .reserved = BIT(1) | BIT(3), }; +static const struct option_blacklist_info telit_me910_dual_modem_blacklist = { + .sendsetup = BIT(0), + .reserved = BIT(3), +}; + static const struct option_blacklist_info telit_le910_blacklist = { .sendsetup = BIT(0), .reserved = BIT(1) | BIT(2), @@ -1242,6 +1248,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), .driver_info = (kernel_ulong_t)&telit_me910_blacklist }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), + .driver_info = (kernel_ulong_t)&telit_me910_dual_modem_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910), .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4), -- GitLab From 98372ee1e8d0f56961e46a6e11eec1b12de14596 Mon Sep 17 00:00:00 2001 From: Dmitry Fleytman Dmitry Fleytman Date: Tue, 19 Dec 2017 06:02:04 +0200 Subject: [PATCH 2174/5498] usb: Add device quirk for Logitech HD Pro Webcam C925e commit 7f038d256c723dd390d2fca942919573995f4cfd upstream. Commit e0429362ab15 ("usb: Add device quirk for Logitech HD Pro Webcams C920 and C930e") introduced quirk to workaround an issue with some Logitech webcams. There is one more model that has the same issue - C925e, so applying the same quirk as well. See aforementioned commit message for detailed explanation of the problem. Signed-off-by: Dmitry Fleytman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 1ef86b4e7927..6d0eb45bf5ce 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -53,10 +53,11 @@ static const struct usb_device_id usb_quirk_list[] = { /* Microsoft LifeCam-VX700 v2.0 */ { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, - /* Logitech HD Pro Webcams C920, C920-C and C930e */ + /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */ { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x046d, 0x085b), .driver_info = USB_QUIRK_DELAY_INIT }, /* Logitech ConferenceCam CC3000e */ { USB_DEVICE(0x046d, 0x0847), .driver_info = USB_QUIRK_DELAY_INIT }, -- GitLab From 2a8c1cf8f57be43251bab1bae78e5ac38e11e799 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 12 Dec 2017 16:11:30 +0100 Subject: [PATCH 2175/5498] usb: add RESET_RESUME for ELSA MicroLink 56K commit b9096d9f15c142574ebebe8fbb137012bb9d99c2 upstream. This modem needs this quirk to operate. It produces timeouts when resumed without reset. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 6d0eb45bf5ce..e34b84e0d782 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -148,6 +148,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Genesys Logic hub, internally used by KY-688 USB 3.1 Type-C Hub */ { USB_DEVICE(0x05e3, 0x0612), .driver_info = USB_QUIRK_NO_LPM }, + /* ELSA MicroLink 56K */ + { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */ { USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM }, -- GitLab From 6b3d91d5b5071ebc1453f02e126f3df805030ecf Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Thu, 21 Dec 2017 15:06:15 +0200 Subject: [PATCH 2176/5498] usb: xhci: Add XHCI_TRUST_TX_LENGTH for Renesas uPD720201 commit da99706689481717998d1d48edd389f339eea979 upstream. When plugging in a USB webcam I see the following message: xhci_hcd 0000:04:00.0: WARN Successful completion on short TX: needs XHCI_TRUST_TX_LENGTH quirk? handle_tx_event: 913 callbacks suppressed All is quiet again with this patch (and I've done a fair but of soak testing with the camera since). Signed-off-by: Daniel Thompson Acked-by: Ard Biesheuvel Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 6bc448701a9c..e8b6bef4e78d 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -171,6 +171,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_TRUST_TX_LENGTH; xhci->quirks |= XHCI_BROKEN_STREAMS; } + if (pdev->vendor == PCI_VENDOR_ID_RENESAS && + pdev->device == 0x0014) + xhci->quirks |= XHCI_TRUST_TX_LENGTH; if (pdev->vendor == PCI_VENDOR_ID_RENESAS && pdev->device == 0x0015) xhci->quirks |= XHCI_RESET_ON_RESUME; -- GitLab From 95a9e2bf54b89e00a989c4c6c83efbd3cb972516 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 20 Dec 2017 17:57:06 -0800 Subject: [PATCH 2177/5498] n_tty: fix EXTPROC vs ICANON interaction with TIOCINQ (aka FIONREAD) commit 966031f340185eddd05affcf72b740549f056348 upstream. We added support for EXTPROC back in 2010 in commit 26df6d13406d ("tty: Add EXTPROC support for LINEMODE") and the intent was to allow it to override some (all?) ICANON behavior. Quoting from that original commit message: There is a new bit in the termios local flag word, EXTPROC. When this bit is set, several aspects of the terminal driver are disabled. Input line editing, character echo, and mapping of signals are all disabled. This allows the telnetd to turn off these functions when in linemode, but still keep track of what state the user wants the terminal to be in. but the problem turns out that "several aspects of the terminal driver are disabled" is a bit ambiguous, and you can really confuse the n_tty layer by setting EXTPROC and then causing some of the ICANON invariants to no longer be maintained. This fixes at least one such case (TIOCINQ) becoming unhappy because of the confusion over whether ICANON really means ICANON when EXTPROC is set. This basically makes TIOCINQ match the case of read: if EXTPROC is set, we ignore ICANON. Also, make sure to reset the ICANON state ie EXTPROC changes, not just if ICANON changes. Fixes: 26df6d13406d ("tty: Add EXTPROC support for LINEMODE") Reported-by: Tetsuo Handa Reported-by: syzkaller Cc: Jiri Slaby Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 7b64a5fdd916..c5bd4ca18b09 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1809,7 +1809,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { struct n_tty_data *ldata = tty->disc_data; - if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) { + if (!old || (old->c_lflag ^ tty->termios.c_lflag) & (ICANON | EXTPROC)) { bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); ldata->line_start = ldata->read_tail; if (!L_ICANON(tty) || !read_cnt(ldata)) { @@ -2525,7 +2525,7 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: down_write(&tty->termios_rwsem); - if (L_ICANON(tty)) + if (L_ICANON(tty) && !L_EXTPROC(tty)) retval = inq_canon(ldata); else retval = read_cnt(ldata); -- GitLab From 788ccf7552c82179135470473d9035f7c8b0b188 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Jan 2018 20:05:10 +0100 Subject: [PATCH 2178/5498] Linux 3.18.91 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 02d29c610cb1..d114d0641a7e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 90 +SUBLEVEL = 91 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 73cbcb8a02f903e2d8ad11bce1f6d51fc80a191d Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 7 Nov 2016 14:34:44 -0800 Subject: [PATCH 2179/5498] BACKPORT: aio: mark AIO pseudo-fs noexec This ensures that do_mmap() won't implicitly make AIO memory mappings executable if the READ_IMPLIES_EXEC personality flag is set. Such behavior is problematic because the security_mmap_file LSM hook doesn't catch this case, potentially permitting an attacker to bypass a W^X policy enforced by SELinux. I have tested the patch on my machine. To test the behavior, compile and run this: #define _GNU_SOURCE #include #include #include #include #include #include #include int main(void) { personality(READ_IMPLIES_EXEC); aio_context_t ctx = 0; if (syscall(__NR_io_setup, 1, &ctx)) err(1, "io_setup"); char cmd[1000]; sprintf(cmd, "cat /proc/%d/maps | grep -F '/[aio]'", (int)getpid()); system(cmd); return 0; } In the output, "rw-s" is good, "rwxs" is bad. Signed-off-by: Jann Horn Signed-off-by: Linus Torvalds (cherry picked from commit 22f6b4d34fcf039c63a94e7670e0da24f8575a5a) Bug: 31711619 Change-Id: Ib4ffd30b61f1d9ba629049f65a21afbf94e25cfd --- fs/aio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/aio.c b/fs/aio.c index 58caa7e5d81c..67a0365ed76b 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -229,6 +229,7 @@ static int __init aio_setup(void) aio_mnt = kern_mount(&aio_fs); if (IS_ERR(aio_mnt)) panic("Failed to create aio fs mount."); + aio_mnt->mnt_flags |= MNT_NOEXEC; if (bdi_init(&aio_fs_backing_dev_info)) panic("Failed to init aio fs backing dev info."); -- GitLab From 5b972c262ab6eca01564e8f8dfb27c6acd21ca9d Mon Sep 17 00:00:00 2001 From: Liangliang Lu Date: Wed, 20 Dec 2017 11:13:00 +0800 Subject: [PATCH 2180/5498] usb: gadget: ffs: change gfp_flags to GFP_ATOMIC usb_ep_alloc_request() may sleep with current flag GFP_KERNEL, but it is in atomic context, change flag to GFP_ATOMIC to fix this issue. Change-Id: Ic43a25f0a504d7fed104e636a9a614a4398820ab Signed-off-by: Liangliang Lu --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index e05444c1c0f8..aed70e0882a3 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -866,7 +866,7 @@ retry: } if (io_data->aio) { - req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); + req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC); if (unlikely(!req)) goto error_lock; -- GitLab From fd825dd8ffd9c4873f80438c3030dd21c204512d Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 20 Dec 2017 16:59:11 -0800 Subject: [PATCH 2181/5498] ANDROID: sdcardfs: notify lower file of opens fsnotify_open is not called within dentry_open, so we need to call it ourselves. Change-Id: Ia7f323b3d615e6ca5574e114e8a5d7973fb4c119 Signed-off-by: Daniel Rosenberg Bug: 70706497 --- fs/sdcardfs/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 0fe08c7856a5..0c6e4659d4e6 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -18,6 +18,7 @@ * General Public License. */ +#include #include "sdcardfs.h" #ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE #include @@ -259,6 +260,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) fput(lower_file); /* fput calls dput for lower_dentry */ } } else { + fsnotify_open(lower_file); sdcardfs_set_lower_file(file, lower_file); } -- GitLab From a56758feb0ba81d25de1edb7a5536a7042cc0a8f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 2 Jan 2018 14:44:49 -0800 Subject: [PATCH 2182/5498] ANDROID: sdcardfs: Add default_normal option The default_normal option causes mounts with the gid set to AID_SDCARD_RW to have user specific gids, as in the normal case. Signed-off-by: Daniel Rosenberg Change-Id: I9619b8ac55f41415df943484dc8db1ea986cef6f Bug: 64672411 --- fs/sdcardfs/main.c | 6 ++++++ fs/sdcardfs/sdcardfs.h | 3 ++- fs/sdcardfs/super.c | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index edb77a91974f..50e186b83341 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -33,6 +33,7 @@ enum { Opt_userid, Opt_reserved_mb, Opt_gid_derivation, + Opt_default_normal, Opt_err, }; @@ -45,6 +46,7 @@ static const match_table_t sdcardfs_tokens = { {Opt_userid, "userid=%d"}, {Opt_multiuser, "multiuser"}, {Opt_gid_derivation, "derive_gid"}, + {Opt_default_normal, "default_normal"}, {Opt_reserved_mb, "reserved_mb=%u"}, {Opt_err, NULL} }; @@ -68,6 +70,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->reserved_mb = 0; /* by default, gid derivation is off */ opts->gid_derivation = false; + vfsopts->default_normal = false; *debug = 0; @@ -122,6 +125,8 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_gid_derivation: opts->gid_derivation = true; break; + case Opt_default_normal: + vfsopts->default_normal = true; /* unknown option */ default: if (!silent) @@ -175,6 +180,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, return 0; vfsopts->mask = option; break; + case Opt_default_normal: case Opt_multiuser: case Opt_userid: case Opt_fsuid: diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 39490322c609..5ef5ddfc725e 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -226,6 +226,7 @@ struct sdcardfs_mount_options { struct sdcardfs_vfsmount_options { gid_t gid; mode_t mask; + bool default_normal; }; extern int parse_options_remount(struct super_block *sb, char *options, int silent, @@ -417,7 +418,7 @@ static inline int get_gid(struct vfsmount *mnt, { struct sdcardfs_vfsmount_options *opts = mnt->data; - if (opts->gid == AID_SDCARD_RW) + if (opts->gid == AID_SDCARD_RW && !opts->default_normal) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index b89947d878e3..a28b40f5adc8 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -304,6 +304,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_printf(m, ",userid=%u", opts->fs_user_id); if (opts->gid_derivation) seq_puts(m, ",derive_gid"); + if (vfsopts->default_normal) + seq_puts(m, ",default_normal"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); -- GitLab From 6488f903bfd73a5710e6a9d18c8b68e719599d0a Mon Sep 17 00:00:00 2001 From: jiangjia Date: Tue, 2 Jan 2018 14:33:42 +0800 Subject: [PATCH 2183/5498] defconfig: msm8909w: enable MEMCG Enable memory cgroup for msm8909w. Change-Id: I2bd2d741693ff838535f3cdbb61e46c4b1468d54 Signed-off-by: Jiangjiang Shen --- arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 2f35e47622b7..c68c00843384 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -11,6 +11,7 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y +CONFIG_MEMCG=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 1ba662efbb3f..95fb62fbc4a0 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -11,6 +11,7 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y +CONFIG_MEMCG=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y -- GitLab From 0f04c7ee24a4385e3cce48faf0c762771e502e7b Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Mon, 11 Dec 2017 12:05:49 +0530 Subject: [PATCH 2184/5498] ASoC: bg: pass calibiration data to BG Add support to send smartpa and mic config params to BG. Change-Id: I753e3e9abc5092a255529a3d75898a6f8eabb06f Signed-off-by: Bala Kishore Pati --- include/uapi/sound/msmcal-hwdep.h | 6 +- sound/soc/codecs/bg_codec.c | 129 +++++++++++++- sound/soc/codecs/bg_codec.h | 282 ++++++++++++++++++++++++++++++ sound/soc/codecs/pktzr.c | 8 +- sound/soc/codecs/pktzr.h | 6 +- sound/soc/codecs/wcdcal-hwdep.c | 6 +- 6 files changed, 430 insertions(+), 7 deletions(-) create mode 100644 sound/soc/codecs/bg_codec.h diff --git a/include/uapi/sound/msmcal-hwdep.h b/include/uapi/sound/msmcal-hwdep.h index 2a294824fb00..fbf7f1ef6dfa 100644 --- a/include/uapi/sound/msmcal-hwdep.h +++ b/include/uapi/sound/msmcal-hwdep.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,9 +20,13 @@ enum wcd_cal_type { WCD9XXX_MAD_CAL, WCD9XXX_MBHC_CAL, WCD9XXX_VBAT_CAL, + BG_CODEC_MIC_CAL, + BG_CODEC_SPEAKER_CAL, WCD9XXX_MAX_CAL, }; +#define BG_CAL_SUPPORT BG_CODEC_SPEAKER_CAL + struct wcdcal_ioctl_buffer { __u32 size; __u8 __user *buffer; diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index a4f66d8108b4..a98e909096b3 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -31,7 +31,9 @@ #include #include #include +#include "bg_codec.h" #include "pktzr.h" +#include "wcdcal-hwdep.h" #define SAMPLE_RATE_48KHZ 48000 @@ -41,6 +43,7 @@ #define BG_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S24_3LE) +#define BG_BLOB_DATA_SIZE 3136 enum { BG_AIF1_PB = 0, @@ -78,8 +81,15 @@ struct bg_cdc_priv { struct delayed_work bg_cdc_pktzr_init_work; unsigned long status_mask; struct bg_hw_params hw_params; + /* cal info for codec */ + struct fw_info *fw_data; + struct firmware_cal *hwdep_spk_cal; + struct firmware_cal *hwdep_mic_cal; + /* Lock to protect init cal */ + struct mutex bg_cal_lock; int src[NUM_CODEC_DAIS]; bool hwd_started; + bool bg_cal_updated; }; struct codec_ssn_rt_setup_t { @@ -138,12 +148,108 @@ static uint32_t get_active_session_id(int dai_id) return active_session; } +static int bg_cdc_cal(struct bg_cdc_priv *bg_cdc) +{ + u8 *init_params = NULL, *init_head = NULL; + struct pktzr_cmd_rsp rsp; + u32 mic_blob_size = sizeof(app_mic_init_params); + u32 spk_blob_size = sizeof(smart_pa_init_params); + int ret = 0; + + mutex_lock(&bg_cdc->bg_cal_lock); + init_params = kzalloc(BG_BLOB_DATA_SIZE, GFP_KERNEL); + if (!init_params) { + ret = -ENOMEM; + goto err2; + } + init_head = init_params; + + bg_cdc->hwdep_mic_cal = wcdcal_get_fw_cal(bg_cdc->fw_data, + BG_CODEC_MIC_CAL); + + if (bg_cdc->hwdep_mic_cal && + (mic_blob_size < bg_cdc->hwdep_mic_cal->size)) + bg_cdc->hwdep_mic_cal = NULL; + + bg_cdc->hwdep_spk_cal = wcdcal_get_fw_cal(bg_cdc->fw_data, + BG_CODEC_SPEAKER_CAL); + if (bg_cdc->hwdep_spk_cal && + (spk_blob_size < bg_cdc->hwdep_spk_cal->size)) + bg_cdc->hwdep_spk_cal = NULL; + + if (bg_cdc->hwdep_mic_cal) { + pr_debug("%s:mic cal size %d", __func__, + bg_cdc->hwdep_mic_cal->size); + memcpy(init_params, &bg_cdc->hwdep_mic_cal->size, + sizeof(bg_cdc->hwdep_mic_cal->size)); + init_params += sizeof(bg_cdc->hwdep_mic_cal->size); + memcpy(init_params, bg_cdc->hwdep_mic_cal->data, + bg_cdc->hwdep_mic_cal->size); + init_params += bg_cdc->hwdep_mic_cal->size; + } else { + pr_debug("%s:default mic cal size %d", __func__, mic_blob_size); + memcpy(init_params, &mic_blob_size, + sizeof(mic_blob_size)); + init_params += sizeof(mic_blob_size); + memcpy(init_params, app_mic_init_params, + sizeof(app_mic_init_params)); + init_params += sizeof(app_mic_init_params); + } + if (bg_cdc->hwdep_spk_cal) { + pr_debug("%s: spk cal size %d", __func__, + bg_cdc->hwdep_spk_cal->size); + memcpy(init_params, &bg_cdc->hwdep_spk_cal->size, + sizeof(bg_cdc->hwdep_spk_cal->size)); + init_params += sizeof(bg_cdc->hwdep_spk_cal->size); + memcpy(init_params, bg_cdc->hwdep_spk_cal->data, + bg_cdc->hwdep_spk_cal->size); + } else { + pr_debug("%s: default spk cal size %d", __func__, + spk_blob_size); + memcpy(init_params, &spk_blob_size, + sizeof(spk_blob_size)); + init_params += sizeof(spk_blob_size); + memcpy(init_params, smart_pa_init_params, + sizeof(smart_pa_init_params)); + } + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) { + ret = -ENOMEM; + goto err1; + } + /* Send command to BG to init session */ + ret = pktzr_cmd_init_params(init_head, BG_BLOB_DATA_SIZE, &rsp); + if (ret < 0) { + pr_err("pktzr cmd set params failed\n"); + goto err; + } + bg_cdc->bg_cal_updated = true; +err: + kfree(rsp.buf); +err1: + kfree(init_head); +err2: + mutex_unlock(&bg_cdc->bg_cal_lock); + return ret; +} + static int _bg_codec_hw_params(struct bg_cdc_priv *bg_cdc) { struct bg_hw_params hw_params; struct pktzr_cmd_rsp rsp; int ret = 0; + if (!bg_cdc->bg_cal_updated) { + ret = bg_cdc_cal(bg_cdc); + if (ret < 0) { + pr_err("%s:failed to send cal data", __func__); + return ret; + } + } else { + pr_debug("%s:cal data already sent to BG", __func__); + } + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); if (!rsp.buf) @@ -636,7 +742,7 @@ static void bg_cdc_pktzr_init(struct work_struct *work) struct bg_cdc_priv *bg_cdc; struct delayed_work *dwork; int num_of_intents = 2; - uint32_t size[2] = {2048, 2048}; + uint32_t size[2] = {4096, 4096}; struct bg_glink_ch_cfg ch_info[1] = { {"CODEC_CHANNEL", num_of_intents, size} }; @@ -670,17 +776,36 @@ static void bg_cdc_pktzr_init(struct work_struct *work) static int bg_cdc_codec_probe(struct snd_soc_codec *codec) { struct bg_cdc_priv *bg_cdc = dev_get_drvdata(codec->dev); + int ret; schedule_delayed_work(&bg_cdc->bg_cdc_pktzr_init_work, msecs_to_jiffies(400)); + bg_cdc->fw_data = devm_kzalloc(codec->dev, + sizeof(*(bg_cdc->fw_data)), GFP_KERNEL); + + bg_cdc->bg_cal_updated = false; + mutex_init(&bg_cdc->bg_cal_lock); + + set_bit(BG_CODEC_MIC_CAL, bg_cdc->fw_data->cal_bit); + set_bit(BG_CODEC_SPEAKER_CAL, bg_cdc->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(bg_cdc->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + devm_kfree(codec->dev, bg_cdc->fw_data); + } return 0; } static int bg_cdc_codec_remove(struct snd_soc_codec *codec) { + struct bg_cdc_priv *bg_cdc = dev_get_drvdata(codec->dev); pr_debug("In func %s\n", __func__); pktzr_deinit(); + mutex_destroy(&bg_cdc->bg_cal_lock); + kfree(bg_cdc->fw_data); return 0; } diff --git a/sound/soc/codecs/bg_codec.h b/sound/soc/codecs/bg_codec.h new file mode 100644 index 000000000000..964ecd464f02 --- /dev/null +++ b/sound/soc/codecs/bg_codec.h @@ -0,0 +1,282 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __BG_CODEC_H_ +#define __BG_CODEC_H_ + +static uint8_t smart_pa_init_params[] = { + 0xea, 0x06, 0x2a, 0x00, 0x50, 0x41, 0x31, 0x5f, 0x30, 0x30, 0xea, 0x06, + 0x23, 0x3f, 0x59, 0x22, 0x4e, 0x58, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x65, 0x6d, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x54, 0x46, 0x41, 0x39, + 0x38, 0x39, 0x37, 0x00, 0x97, 0x22, 0xb0, 0x00, 0x00, 0x32, 0x03, 0x00, + 0x70, 0x00, 0x01, 0xfb, 0x00, 0x71, 0x40, 0x00, 0x61, 0x40, 0x7d, 0x04, + 0x3a, 0x88, 0x00, 0x37, 0x6a, 0x3e, 0xd2, 0xfe, 0xb5, 0x00, 0x58, 0x7c, + 0x3b, 0x80, 0x10, 0xa5, 0xb5, 0x00, 0x54, 0xff, 0xb5, 0x00, 0x54, 0x3e, + 0x3a, 0x80, 0x00, 0x0f, 0x3b, 0x80, 0x03, 0x27, 0x8b, 0xa0, 0x42, 0x00, + 0x9b, 0xa8, 0xd4, 0xbf, 0xa4, 0x02, 0xd4, 0x3d, 0x3b, 0x80, 0x10, 0x25, + 0xb5, 0x00, 0x40, 0x48, 0xb5, 0x00, 0x54, 0x3d, 0x3a, 0x88, 0x00, 0x0c, + 0xb5, 0x00, 0x40, 0x08, 0x30, 0x90, 0x07, 0xfc, 0xb5, 0x00, 0x4a, 0x49, + 0x3b, 0x80, 0x11, 0xb4, 0xb5, 0x00, 0x48, 0x48, 0x9b, 0xa7, 0xd4, 0x3d, + 0x9b, 0x4b, 0xf8, 0x03, 0xb5, 0x00, 0x42, 0x00, 0xd0, 0x0c, 0x7e, 0x9c, + 0x31, 0x80, 0x20, 0xda, 0xa5, 0x81, 0x7e, 0x9c, 0xb5, 0x00, 0x67, 0x04, + 0x31, 0x90, 0x01, 0x96, 0x3b, 0x80, 0x02, 0x67, 0xb5, 0x00, 0x7e, 0x00, + 0xb5, 0x00, 0x58, 0x3c, 0x3c, 0xd8, 0x03, 0x00, 0x8b, 0x80, 0x7d, 0x7c, + 0xb5, 0x00, 0x7e, 0x9c, 0x6a, 0xff, 0xfd, 0x27, 0x3a, 0xa3, 0x00, 0x36, + 0x6b, 0x3e, 0x46, 0x9b, 0x3a, 0xad, 0x00, 0x05, 0x6a, 0xbd, 0xd6, 0xfe, + 0x3a, 0xad, 0x00, 0x04, 0xb5, 0x00, 0x40, 0x28, 0x82, 0x00, 0x54, 0xfa, + 0xb5, 0x00, 0x58, 0x79, 0x3b, 0x42, 0x40, 0x31, 0x61, 0x4c, 0x55, 0xfd, + 0x3b, 0x80, 0x02, 0x60, 0x3c, 0xc9, 0xff, 0xd9, 0x3b, 0x20, 0x40, 0x37, + 0x80, 0xf4, 0x54, 0x3f, 0x3a, 0x80, 0x00, 0x37, 0xb5, 0x00, 0x42, 0x80, + 0x30, 0x80, 0x09, 0xc6, 0x3b, 0x80, 0x01, 0xcc, 0x9b, 0xc3, 0xf4, 0x59, + 0x7a, 0x0c, 0xd4, 0xbb, 0x3b, 0x80, 0x16, 0xe4, 0xb5, 0x00, 0x55, 0x3f, + 0x9b, 0x00, 0xd4, 0x3d, 0x3a, 0x88, 0x00, 0x2a, 0x80, 0xa0, 0x43, 0x80, + 0x6a, 0x7e, 0xd4, 0x3b, 0xfb, 0x00, 0x71, 0x40, 0x3e, 0x30, 0xa0, 0x07, + 0xf3, 0x31, 0x80, 0x01, 0x91, 0x3b, 0x80, 0x04, 0x13, 0x80, 0xd4, 0x74, + 0x59, 0x7c, 0x0a, 0xd5, 0x3d, 0x31, 0x00, 0x00, 0x37, 0xb5, 0x00, 0x40, + 0x05, 0x90, 0x03, 0xc1, 0x10, 0x31, 0x0f, 0xff, 0xa2, 0xa2, 0x08, 0x42, + 0x85, 0x3a, 0x88, 0x00, 0x33, 0x60, 0x44, 0x43, 0x80, 0x3a, 0x99, 0x00, + 0x4f, 0x3b, 0x44, 0x40, 0x52, 0x60, 0x0c, 0x54, 0x3f, 0x82, 0x14, 0x7e, + 0x9c, 0x3b, 0x62, 0x40, 0x75, 0x30, 0x20, 0x02, 0x00, 0xa2, 0x15, 0x7e, + 0x9c, 0x3b, 0x62, 0x40, 0x75, 0x82, 0x04, 0x7e, 0x9c, 0x3b, 0x62, 0x40, + 0x5b, 0x82, 0x00, 0x7e, 0x9c, 0x3b, 0x62, 0x40, 0x5b, 0x82, 0x14, 0x7e, + 0x9c, 0x3b, 0x62, 0x40, 0x71, 0x30, 0x00, 0x02, 0x00, 0xa2, 0x14, 0x7e, + 0x9c, 0x3b, 0x62, 0x40, 0x71, 0x3a, 0x88, 0x00, 0x5c, 0x61, 0x86, 0xfb, + 0x24, 0xb5, 0x00, 0x54, 0x3a, 0x3b, 0x80, 0x00, 0x47, 0xb5, 0x00, 0x54, + 0xff, 0x6a, 0x5f, 0x54, 0x3f, 0x31, 0x0f, 0xff, 0xa2, 0x3a, 0x91, 0x00, + 0x15, 0x61, 0x48, 0x42, 0x45, 0x6a, 0x5d, 0x43, 0x00, 0xb5, 0x00, 0x54, + 0x7f, 0x3b, 0x80, 0x02, 0x0c, 0xb5, 0x00, 0x54, 0x3a, 0x61, 0x40, 0x54, + 0x3f, 0x3a, 0x88, 0x00, 0x34, 0x71, 0x05, 0x55, 0x3a, 0xb5, 0x00, 0x54, + 0xff, 0x3b, 0x80, 0x02, 0x4e, 0x3c, 0xc9, 0xff, 0xd9, 0x6a, 0x1f, 0xd8, + 0x39, 0x3b, 0x00, 0x40, 0x97, 0xb5, 0x00, 0x7e, 0x40, 0x9b, 0x00, 0xd8, + 0x39, 0xb5, 0x00, 0x54, 0x3c, 0x3b, 0x00, 0x40, 0x97, 0xb5, 0x00, 0x40, + 0x40, 0x30, 0x20, 0x01, 0x00, 0x3a, 0x80, 0x00, 0x5e, 0x7f, 0x20, 0xc1, + 0x41, 0xb5, 0x00, 0x7e, 0x40, 0x30, 0x20, 0x04, 0x00, 0x3a, 0x80, 0xff, + 0xb0, 0xaa, 0x7d, 0x42, 0x00, 0xfb, 0x00, 0x71, 0x40, 0x7c, 0x38, 0x0c, + 0x07, 0xf9, 0x38, 0x0a, 0x07, 0xfa, 0xd2, 0x7d, 0xe1, 0x03, 0x3a, 0x80, + 0x00, 0x43, 0xba, 0xe3, 0xe0, 0xc3, 0xa0, 0x6e, 0xfb, 0x04, 0xba, 0x41, + 0x61, 0x47, 0x9b, 0xeb, 0xc1, 0xc1, 0xb9, 0xc1, 0x41, 0xc5, 0x3c, 0xd8, + 0x06, 0x04, 0xb0, 0x7f, 0x7e, 0x9c, 0x3c, 0xc3, 0x40, 0x8a, 0xb5, 0x00, + 0x54, 0xbc, 0xa6, 0x49, 0xd8, 0x39, 0xb4, 0x4a, 0xfe, 0x9c, 0x82, 0x10, + 0x61, 0x76, 0xbf, 0xb2, 0xfe, 0x9c, 0x82, 0x04, 0x7e, 0x9c, 0x95, 0x75, + 0x7e, 0x9c, 0x3b, 0x42, 0x40, 0x94, 0x90, 0x6d, 0x7e, 0x9c, 0x9b, 0x40, + 0xc1, 0xc0, 0x82, 0x00, 0x7e, 0x9c, 0x3b, 0x63, 0x40, 0x96, 0x3b, 0x00, + 0x40, 0x97, 0xb5, 0x00, 0x7e, 0x48, 0xb5, 0x00, 0x41, 0x48, 0x3c, 0xd8, + 0x03, 0x00, 0x8b, 0x80, 0x7d, 0x59, 0xb5, 0x00, 0x7e, 0x9c, 0x3a, 0x89, + 0x00, 0x05, 0x3a, 0x91, 0x00, 0x23, 0xb5, 0x00, 0x43, 0x10, 0x82, 0x18, + 0x7d, 0x07, 0x3a, 0x92, 0x00, 0x21, 0xb5, 0x00, 0x58, 0x7d, 0x3b, 0x42, + 0x40, 0xd5, 0x6a, 0xbf, 0xd4, 0x7e, 0x3a, 0x81, 0x00, 0x2b, 0x61, 0x80, + 0xd4, 0x7c, 0xb5, 0x00, 0x40, 0x80, 0xb0, 0xc7, 0x64, 0x06, 0x30, 0x80, + 0x01, 0x80, 0x3b, 0x80, 0x02, 0xa4, 0x90, 0xb8, 0xf6, 0x00, 0x61, 0x80, + 0x54, 0x3c, 0xb5, 0x00, 0x78, 0x44, 0x3b, 0x80, 0x19, 0x93, 0xa0, 0xb3, + 0x54, 0xfc, 0x9b, 0xcc, 0x54, 0x3c, 0x6a, 0x1f, 0x42, 0x80, 0x30, 0x10, + 0x80, 0x00, 0xb2, 0xa6, 0xd2, 0x7c, 0xa6, 0x17, 0x7e, 0x9c, 0x39, 0x02, + 0x22, 0x1e, 0x3a, 0x80, 0x00, 0x0d, 0xaa, 0x60, 0xc3, 0x02, 0xba, 0x20, + 0xdb, 0xf9, 0xd0, 0x4d, 0x5d, 0xfa, 0x3b, 0x80, 0x13, 0xdc, 0xba, 0x40, + 0x51, 0x7b, 0x9b, 0xa8, 0x50, 0xba, 0xfb, 0x00, 0x71, 0x40, 0xba, 0xb5, + 0x00, 0x5a, 0xb9, 0x82, 0x04, 0x54, 0x7a, 0xbf, 0x10, 0x7e, 0x9c, 0xbb, + 0x20, 0x50, 0x3c, 0xa4, 0x26, 0xfe, 0x9c, 0xb5, 0x00, 0x61, 0x41, 0xd0, + 0x4c, 0xfa, 0xc8, 0x38, 0x0a, 0x07, 0xfb, 0xa4, 0x27, 0x7e, 0x9c, 0x30, + 0x80, 0x07, 0xfc, 0x3b, 0x80, 0x02, 0xcd, 0xb5, 0x00, 0x50, 0xf9, 0x30, + 0x80, 0x07, 0xfc, 0x69, 0x5d, 0xf8, 0x01, 0xb5, 0x00, 0x53, 0x3c, 0xa2, + 0x1a, 0xc0, 0x80, 0x38, 0x6a, 0x07, 0xfc, 0x3b, 0x46, 0x40, 0xd0, 0x60, + 0x60, 0x54, 0x3a, 0xb5, 0x00, 0x50, 0xb9, 0xa2, 0x18, 0xfe, 0x9c, 0x3b, + 0x64, 0x40, 0xd5, 0xb5, 0x00, 0x54, 0x3e, 0x3a, 0x80, 0x00, 0x59, 0xb5, + 0x00, 0x43, 0x00, 0x99, 0xd8, 0xfe, 0x9c, 0x7f, 0x20, 0x43, 0x41, 0x7c, + 0x01, 0xd4, 0x3e, 0x3a, 0x88, 0x00, 0x09, 0x61, 0x85, 0xd4, 0xfe, 0xb5, + 0x00, 0x42, 0x80, 0x30, 0x00, 0x80, 0x00, 0x31, 0x80, 0x01, 0x61, 0x3b, + 0x80, 0x01, 0x4d, 0x9b, 0x23, 0x43, 0x88, 0x8b, 0x00, 0x54, 0x3f, 0x38, + 0x68, 0x22, 0x00, 0x3c, 0xd8, 0x06, 0x04, 0x20, 0x17, 0x40, 0xe3, 0xb5, + 0x00, 0x43, 0x00, 0x96, 0xb8, 0xfe, 0x9c, 0xb4, 0x02, 0xfe, 0x9c, 0x9b, + 0xe9, 0xd4, 0x3e, 0x70, 0x9a, 0x42, 0x80, 0xd0, 0x24, 0x7e, 0x9c, 0x3a, + 0x80, 0xff, 0xf7, 0xa4, 0x03, 0xc2, 0x80, 0xb5, 0x00, 0x61, 0x00, 0xd0, + 0x04, 0x7e, 0x9c, 0x30, 0x71, 0x6a, 0x0a, 0x31, 0x80, 0x01, 0x67, 0x3b, + 0x80, 0x01, 0x4d, 0x9b, 0x23, 0x60, 0x00, 0xb5, 0x00, 0x58, 0x3d, 0x3c, + 0xd8, 0x03, 0x00, 0x8b, 0x80, 0x7d, 0x79, 0xb5, 0x00, 0x7e, 0x9c, 0x6c, + 0x3f, 0xfd, 0x02, 0x3b, 0x80, 0x05, 0x9a, 0xb5, 0x00, 0x7e, 0x9c, 0x39, + 0x84, 0x80, 0x09, 0x30, 0x50, 0x00, 0x10, 0xfb, 0x00, 0x71, 0x40, 0xf8, + 0xa8, 0xba, 0xfe, 0x9c, 0x82, 0x14, 0x7e, 0x9c, 0x3b, 0x42, 0x41, 0x0c, + 0x38, 0x09, 0x05, 0x12, 0x38, 0x09, 0x00, 0xa4, 0x39, 0x84, 0x80, 0x09, + 0x30, 0x50, 0x00, 0x80, 0xa8, 0x1a, 0xfe, 0x9c, 0xa2, 0x02, 0xfe, 0x9c, + 0x3b, 0x62, 0x41, 0x0c, 0x39, 0x84, 0x80, 0x07, 0xb5, 0x00, 0x7a, 0x87, + 0xa8, 0x9a, 0x7e, 0x9c, 0xb5, 0x00, 0x64, 0x04, 0x30, 0x80, 0x22, 0x7b, + 0x61, 0x40, 0x76, 0x00, 0x38, 0x08, 0x00, 0xb1, 0xb0, 0x96, 0x7e, 0x9c, + 0x3b, 0x00, 0x41, 0x0d, 0x38, 0x09, 0x00, 0xa3, 0x38, 0x09, 0x00, 0xa3, + 0x38, 0x08, 0x00, 0xa3, 0x30, 0x80, 0x00, 0xfe, 0x6a, 0x3f, 0x42, 0x41, + 0x38, 0x0a, 0x05, 0x12, 0x3b, 0x80, 0x0b, 0xae, 0x30, 0x80, 0x00, 0xa6, + 0x9b, 0xa4, 0xd4, 0x3e, 0xb5, 0x00, 0x42, 0x40, 0x30, 0x90, 0x06, 0xbf, + 0x3b, 0x80, 0x0c, 0xbe, 0x30, 0x80, 0x00, 0xfe, 0x39, 0x84, 0x80, 0x06, + 0x30, 0x40, 0x00, 0x80, 0xa8, 0x9a, 0x7a, 0xa9, 0xa5, 0x92, 0xfe, 0x9c, + 0x39, 0x84, 0x80, 0x05, 0x38, 0x0a, 0x00, 0xfd, 0xa9, 0x93, 0x7e, 0x9c, + 0xa2, 0x16, 0x7e, 0x9c, 0x3b, 0x62, 0x41, 0x2d, 0x38, 0x0c, 0x00, 0xfa, + 0x30, 0x80, 0x00, 0xa6, 0x3b, 0x80, 0x0b, 0x53, 0x38, 0x0a, 0x00, 0xee, + 0x39, 0x84, 0x80, 0x06, 0x30, 0x40, 0x00, 0x80, 0xa8, 0x9a, 0x7a, 0xa9, + 0xa5, 0x92, 0xd8, 0x3f, 0x39, 0x84, 0x80, 0x05, 0xa9, 0x93, 0x7e, 0x9c, + 0x3b, 0x00, 0x41, 0x2e, 0x38, 0x09, 0x00, 0xfd, 0xb5, 0x00, 0x58, 0x3f, + 0x3c, 0xd8, 0x03, 0x00, 0x7f, 0x4e, 0x7d, 0x7e, 0x39, 0x84, 0x80, 0x06, + 0x30, 0x00, 0xff, 0x00, 0x39, 0x86, 0x80, 0x06, 0x30, 0x20, 0x00, 0x20, + 0xa8, 0x18, 0x7d, 0x01, 0xa8, 0x5d, 0x58, 0x7f, 0xfb, 0x00, 0x71, 0x41, + 0x36, 0x38, 0x02, 0x22, 0x03, 0x34, 0x00, 0x00, 0xc9, 0x34, 0x00, 0x00, + 0xc7, 0x34, 0x20, 0x00, 0xee, 0x34, 0x00, 0x00, 0xef, 0x34, 0x00, 0x07, + 0x72, 0x34, 0x00, 0x07, 0x73, 0x34, 0x00, 0x07, 0x74, 0x34, 0x05, 0x00, + 0xf1, 0x34, 0x00, 0x00, 0xf4, 0x34, 0x00, 0x00, 0xa4, 0x38, 0x03, 0x00, + 0xf9, 0x38, 0x01, 0x00, 0xf6, 0x38, 0x01, 0x00, 0xf5, 0x38, 0x05, 0x00, + 0xf7, 0xb5, 0x00, 0x7a, 0xa6, 0x3b, 0x80, 0x09, 0x55, 0x38, 0x05, 0x00, + 0xf8, 0xb5, 0x00, 0x7a, 0xa7, 0x3b, 0x80, 0x09, 0x55, 0x38, 0x09, 0x00, + 0xec, 0x39, 0x84, 0x80, 0x04, 0x30, 0x00, 0xf0, 0x00, 0x38, 0x04, 0x00, + 0xec, 0x30, 0x10, 0xff, 0x00, 0xa8, 0x18, 0x7a, 0x6c, 0xa6, 0xa1, 0xfe, + 0x9c, 0xa8, 0x28, 0xfa, 0x48, 0xa3, 0x15, 0x7e, 0x9c, 0x38, 0x0b, 0x00, + 0xfb, 0xa5, 0x25, 0x7e, 0x9c, 0x3b, 0x46, 0x41, 0x59, 0xa9, 0x10, 0xfe, + 0x9c, 0x38, 0x01, 0x00, 0xed, 0x34, 0x08, 0x00, 0xfb, 0x38, 0x20, 0x00, + 0xfb, 0xb5, 0x00, 0x60, 0x10, 0x93, 0x01, 0xfe, 0x9c, 0x3b, 0x65, 0x41, + 0x5e, 0x34, 0x01, 0x00, 0xef, 0x39, 0x84, 0x80, 0x06, 0x30, 0x00, 0x00, + 0x80, 0x30, 0x90, 0x22, 0x60, 0x30, 0x80, 0x22, 0x57, 0xa8, 0x18, 0x7a, + 0x49, 0xa5, 0x01, 0x76, 0x48, 0x60, 0x44, 0x76, 0x00, 0x39, 0x84, 0x80, + 0x05, 0xa9, 0x03, 0x41, 0x00, 0x38, 0x03, 0x00, 0xfa, 0x38, 0x05, 0x00, + 0xee, 0x3b, 0x80, 0x07, 0xde, 0x38, 0x01, 0x00, 0xfd, 0x3b, 0x80, 0x07, + 0xed, 0xb5, 0x00, 0x7e, 0x9c, 0x39, 0x84, 0x80, 0x04, 0x96, 0x9a, 0x58, + 0x3f, 0x3c, 0xd8, 0x03, 0x00, 0x98, 0x10, 0xfd, 0x7f, 0x38, 0x01, 0x00, + 0xc6, 0x3a, 0x80, 0x00, 0x10, 0xb5, 0x00, 0x42, 0x80, 0x27, 0x00, 0x71, + 0x41, 0x74, 0x38, 0x08, 0x07, 0xfa, 0x38, 0x0c, 0x07, 0xf9, 0xd2, 0xbc, + 0x7e, 0x9c, 0x3a, 0x80, 0x00, 0x43, 0xb5, 0x00, 0x61, 0x40, 0xa0, 0x96, + 0x7e, 0x9c, 0xb5, 0x00, 0x42, 0x41, 0x3c, 0xd8, 0x03, 0x00, 0x7f, 0x20, + 0x42, 0x41, 0x03, 0x00, 0x70, 0x00, 0x05, 0x03, 0x00, 0x70, 0x00, 0x07, + 0x72, 0x00, 0x71, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0xf6, 0x00, + 0x40, 0xf3, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x08, 0x8f, 0x00, + 0x41, 0x30, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x08, 0xcb, 0x00, + 0x40, 0xf3, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x0c, 0xce, 0x00, + 0x40, 0x00, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x0c, 0xfd, 0x00, + 0x40, 0x00, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x01, 0x00, + 0x41, 0x72, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x1a, 0x00, + 0x40, 0x00, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x40, 0x00, + 0x40, 0x23, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x01, 0x00, 0x1a, 0xa0, 0x00, + 0x40, 0x9a, 0x00, 0x3b, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x03, 0x12, 0x00, 0x71, 0x07, 0xf9, 0x40, 0x00, 0x00, 0x06, 0x40, 0x00, + 0x7f, 0xa8, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x02, 0xee, 0x06, 0x00, 0x71, + 0x0d, 0x7f, 0x02, 0x02, 0x01, 0x03, 0x00, 0x70, 0x00, 0x00, 0xed, 0x00, + 0x24, 0x00, 0x43, 0x4f, 0x31, 0x5f, 0x30, 0x33, 0xed, 0x00, 0x0b, 0xba, + 0xd8, 0xf6, 0x54, 0x46, 0x41, 0x39, 0x38, 0x39, 0x37, 0x00, 0x64, 0x65, + 0x76, 0x6b, 0x69, 0x74, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x5f, 0x4e, 0x31, + 0x42, 0x00, 0x08, 0xfd, 0x71, 0x00, 0x9b, 0x85, 0x00, 0x21, 0x48, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x02, 0x1a, 0xe6, 0x40, 0x1b, 0x40, 0xd4, 0x1c, + 0x59, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x4b, 0x00, 0x01, 0x4b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x10, + 0x00, 0x00, 0x01, 0xeb, 0x85, 0x00, 0x01, 0x06, 0x00, 0x19, 0x9a, 0x00, + 0x80, 0x00, 0x00, 0x0c, 0xcd, 0x02, 0x80, 0x00, 0x00, 0x00, 0x01, 0x05, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x07, 0xc2, 0x8f, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x19, 0x9a, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x47, 0xae, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x02, 0x00, 0x00, 0x18, 0xec, 0x00, 0x00, 0x00, + 0x03, 0xd7, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x05, 0x00, 0x00, 0x72, 0x00, 0x80, 0x00, 0x02, + 0x80, 0x00, 0x00, 0x00, 0x72, 0x06, 0x66, 0x66, 0x19, 0x99, 0x9a, 0x00, + 0x80, 0x00, 0x14, 0x00, 0x00, 0x02, 0xcc, 0xcd, 0x04, 0x00, 0x00, 0xf0, + 0x01, 0x49, 0x00, 0x53, 0x50, 0x31, 0x5f, 0x30, 0x30, 0xf0, 0x01, 0x88, + 0x24, 0x42, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0xff, 0xff, 0x24, 0x00, 0x00, 0x1e, 0xff, 0xff, + 0x15, 0x00, 0x00, 0x43, 0xff, 0xff, 0xcd, 0x00, 0x00, 0x8a, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xd8, 0x00, 0x00, 0x6f, 0xff, 0xff, + 0xf2, 0x00, 0x00, 0x2e, 0xff, 0xff, 0xff, 0x00, 0x00, 0x41, 0xff, 0xff, + 0x8e, 0x00, 0x00, 0x58, 0xff, 0xff, 0x56, 0x00, 0x00, 0x82, 0xff, 0xff, + 0x84, 0xff, 0xff, 0xfb, 0xff, 0xff, 0x75, 0x00, 0x00, 0x77, 0xff, 0xff, + 0xdd, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x22, 0xff, 0xff, 0xb1, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x1d, 0xff, 0xff, 0xae, 0x00, 0x00, 0x40, 0xff, 0xff, + 0xd4, 0xff, 0xff, 0x4d, 0xff, 0xff, 0xda, 0xff, 0xff, 0xf1, 0xff, 0xff, + 0xca, 0xff, 0xff, 0xa9, 0xff, 0xff, 0x80, 0xff, 0xff, 0xec, 0x00, 0x00, + 0x06, 0xff, 0xff, 0x45, 0x00, 0x00, 0x9d, 0xff, 0xff, 0x1d, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe5, 0xff, 0xff, 0xba, 0xff, 0xff, 0x9b, 0xff, 0xff, + 0xd5, 0x00, 0x00, 0x16, 0xff, 0xff, 0xe9, 0xff, 0xff, 0xf8, 0xff, 0xff, + 0xfa, 0xff, 0xff, 0xba, 0x00, 0x00, 0xc9, 0xff, 0xff, 0x99, 0x00, 0x00, + 0x88, 0xff, 0xff, 0x72, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x42, 0xff, 0xff, + 0xcd, 0x00, 0x00, 0x81, 0xff, 0xff, 0xc3, 0xff, 0xff, 0x9a, 0xff, 0xff, + 0xea, 0x00, 0x00, 0x29, 0x00, 0x00, 0x47, 0xff, 0xff, 0xe8, 0xff, 0xff, + 0xd3, 0xff, 0xff, 0x95, 0x00, 0x00, 0x95, 0xff, 0xff, 0xf1, 0x00, 0x00, + 0x1e, 0xff, 0xfe, 0xb9, 0xff, 0xff, 0x5c, 0xff, 0xff, 0x74, 0xff, 0xff, + 0xc4, 0x00, 0x01, 0x36, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x94, 0xff, 0xff, + 0xa2, 0xff, 0xff, 0x73, 0xff, 0xfd, 0x8a, 0xff, 0xff, 0x27, 0xff, 0xff, + 0x3c, 0x00, 0x02, 0xbb, 0x00, 0x02, 0x6b, 0x00, 0x04, 0xf5, 0x00, 0x00, + 0xc7, 0xff, 0xff, 0xed, 0xff, 0xf8, 0x02, 0xff, 0xfb, 0x03, 0xff, 0xf7, + 0x01, 0x00, 0x03, 0xcd, 0x00, 0x04, 0xa6, 0x00, 0x13, 0x65, 0x00, 0x08, + 0x69, 0x00, 0x0b, 0x6a, 0xff, 0xee, 0x7f, 0xff, 0xf0, 0x6d, 0xff, 0xd7, + 0x48, 0xff, 0xf9, 0x69, 0xff, 0xf8, 0x5f, 0x00, 0x37, 0x71, 0x00, 0x27, + 0x58, 0x00, 0x4a, 0x07, 0xff, 0xf0, 0xc9, 0xff, 0xeb, 0x7e, 0xff, 0x77, + 0xd3, 0xff, 0xb5, 0xd6, 0xff, 0x9f, 0x85, 0x00, 0x5e, 0x75, 0x00, 0x7d, + 0x8a, 0x01, 0x17, 0xa4, 0x00, 0x7b, 0x58, 0x00, 0x42, 0x9e, 0xfe, 0xe7, + 0x69, 0x07, 0xb9, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x6c, 0xcc, + 0xcd, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x5c, + 0x29, 0x00, 0x03, 0x9f, 0x00, 0x03, 0xa2, 0x04, 0x00, 0x00, 0x00, 0x50, + 0xa4, 0x1c, 0xc0, 0x00, 0x00, 0x6f, 0x69, 0x7b, 0x00, 0x24, 0x00, 0x50, + 0x52, 0x31, 0x5f, 0x30, 0x30, 0x7b, 0x00, 0xbb, 0x00, 0x0c, 0x6e, 0x4e, + 0x58, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x76, 0x6b, 0x69, + 0x74, 0x00, 0x00, 0x76, 0x65, 0x72, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x70, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x41, 0x89, 0x00, + 0x2b, 0xb0, 0x00, 0x00, 0xd2, 0x00, 0x10, 0x62, 0x01, 0x00, 0x00, 0x09, + 0x80, 0x00, 0x0d, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, + 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x00, 0x40, 0x00, 0x00, + 0x01, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x48, 0x00, + 0x01, 0x48, 0x08, 0x00, 0x00, 0x07, 0x85, 0x1f, 0x00, 0x07, 0xae, 0x00, + 0x00, 0x05, 0x65, 0x01, 0x25, 0x00, 0x45, 0x51, 0x31, 0x5f, 0x30, 0x30, + 0x65, 0x01, 0x7a, 0xa3, 0x05, 0xcf, 0x4e, 0x58, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x64, 0x65, 0x76, 0x6b, 0x69, 0x74, 0x00, 0x00, 0x76, 0x65, + 0x72, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x08, 0x00, 0x00, 0x01, 0xc1, 0xb8, + 0xb8, 0x7e, 0x35, 0xf9, 0x0f, 0xdb, 0x09, 0xe0, 0x49, 0xee, 0x0f, 0xdb, + 0x09, 0x01, 0x03, 0x00, 0x00, 0x7a, 0x43, 0x9a, 0x99, 0x99, 0x3f, 0x00, + 0x00, 0x40, 0xc1, 0x00, 0x00, 0x01, 0xc2, 0x53, 0x4d, 0x7d, 0x80, 0x9c, + 0x3d, 0x31, 0xe8, 0x82, 0x7f, 0x64, 0x40, 0x7a, 0xcb, 0x01, 0x07, 0x00, + 0x00, 0xc8, 0x43, 0xd7, 0x05, 0xb5, 0x3f, 0xd8, 0x00, 0x40, 0x40, 0x00, + 0x00, 0x00, 0xea, 0xaa, 0xa0, 0x00, 0x00, 0x00, 0x25, 0x55, 0x58, 0x4a, + 0xaa, 0xb0, 0x25, 0x55, 0x58, 0x01, 0x02, 0x00, 0x80, 0x3b, 0x46, 0x52, + 0x33, 0x33, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd3, 0x30, + 0x5c, 0x42, 0x3d, 0x71, 0x31, 0x02, 0x9c, 0xbd, 0xc2, 0x8f, 0x3b, 0xcd, + 0x08, 0x01, 0x07, 0x00, 0xc0, 0xda, 0x45, 0x00, 0x00, 0x80, 0x40, 0x00, + 0x00, 0xa0, 0xc0, 0x00, 0x00, 0x01, 0xc8, 0xd8, 0xe0, 0x36, 0x18, 0x21, + 0x35, 0x53, 0xf4, 0xc9, 0xe7, 0xdf, 0x41, 0xd3, 0x2c, 0x01, 0x07, 0x00, + 0x40, 0x03, 0x46, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, + 0x00, 0x01, 0xdb, 0x65, 0x78, 0x54, 0x5f, 0x94, 0x19, 0xf1, 0x50, 0xab, + 0xa0, 0x6c, 0x4a, 0xa9, 0x38, 0x00, 0x07, 0xf4, 0x7f, 0x89, 0x45, 0xdb, + 0xff, 0x7f, 0x3f, 0xff, 0xff, 0x9f, 0x40, 0x00, 0x00, 0x01, 0xdf, 0xff, + 0xee, 0x54, 0xaa, 0x22, 0x2f, 0x9d, 0xf6, 0x86, 0x11, 0xe5, 0x55, 0xa6, + 0x15, 0x00, 0x05, 0x4d, 0x60, 0x6a, 0x45, 0xf3, 0x04, 0xb5, 0x3f, 0x00, + 0x00, 0x40, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7a, 0x44, 0xf3, 0x04, 0xb5, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x44, 0xf3, + 0x04, 0xb5, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xcf, + 0x54, 0x01, 0x01, 0x00, 0x00, 0x7a, 0x44, 0xf3, 0x04, 0xb5, 0x3f, 0x00, + 0x00, 0xa0, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x1b, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x21, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x3e, 0x00, 0x78, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, + 0x01, 0x00, 0x00, 0x00 +}; + +static uint8_t app_mic_init_params[] = { + 0x60, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x06, 0x00, 0x00, 0x00 +}; + +#endif diff --git a/sound/soc/codecs/pktzr.c b/sound/soc/codecs/pktzr.c index 33c2b4eddf5f..8193a6f06301 100644 --- a/sound/soc/codecs/pktzr.c +++ b/sound/soc/codecs/pktzr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -207,6 +207,12 @@ int pktzr_cmd_data(void *payload, uint32_t size, void *priv_data) return pktzr_send_pkt(payload, size, priv_data, PKTZR_CMD_DATA, false); } +int pktzr_cmd_init_params(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp) +{ + return pktzr_send_pkt(payload, size, rsp, PKTZR_CMD_INIT_PARAM, true); +} + int pktzr_init(void *pdev, struct bg_glink_ch_cfg *ch_info, int num_channels, pktzr_data_cmd_cb_fn func) { diff --git a/sound/soc/codecs/pktzr.h b/sound/soc/codecs/pktzr.h index efeee3f7576c..f8c2675e4ccb 100644 --- a/sound/soc/codecs/pktzr.h +++ b/sound/soc/codecs/pktzr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,7 +37,7 @@ #define PKTZR_CMD_EVENT 0x000B #define PKTZR_CMD_DATA 0x000C #define PKTZR_CMDRSP_DATA 0x000D - +#define PKTZR_CMD_INIT_PARAM 0x0029 typedef int (*pktzr_data_cmd_cb_fn)(void *buf, uint32_t len, void *priv_data, bool *is_basic_rsp); @@ -68,4 +68,6 @@ extern int pktzr_cmd_data(void *payload, uint32_t size, void *priv_data); extern int pktzr_cmd_set_params(void *payload, uint32_t size, struct pktzr_cmd_rsp *rsp); +extern int pktzr_cmd_init_params(void *payload, uint32_t size, + struct pktzr_cmd_rsp *rsp); #endif diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c index 906756e5aa53..172067e9e729 100644 --- a/sound/soc/codecs/wcdcal-hwdep.c +++ b/sound/soc/codecs/wcdcal-hwdep.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,8 @@ const int cal_size_info[WCD9XXX_MAX_CAL] = { [WCD9XXX_MBHC_CAL] = 4096, [WCD9XXX_MAD_CAL] = 4096, [WCD9XXX_VBAT_CAL] = 72, + [BG_CODEC_MIC_CAL] = 20, + [BG_CODEC_SPEAKER_CAL] = 3077, }; const char *cal_name_info[WCD9XXX_MAX_CAL] = { @@ -31,6 +33,8 @@ const char *cal_name_info[WCD9XXX_MAX_CAL] = { [WCD9XXX_MBHC_CAL] = "mbhc", [WCD9XXX_MAD_CAL] = "mad", [WCD9XXX_VBAT_CAL] = "vbat", + [BG_CODEC_MIC_CAL] = "bgmic", + [BG_CODEC_SPEAKER_CAL] = "bgspk", }; struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data, -- GitLab From 921b0c1cc4565094c1879d4ba750a51aee37ffd4 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 2 Jan 2018 23:51:28 +0530 Subject: [PATCH 2185/5498] ASoC: bg: Fix double free of list node Avoid double free of list node in BG packetizer. Change-Id: I695834a0b06df270fc80031ca96d7e25c2d89a33 Signed-off-by: Ashish Jain --- sound/soc/codecs/pktzr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/pktzr.c b/sound/soc/codecs/pktzr.c index 8193a6f06301..672c79366158 100644 --- a/sound/soc/codecs/pktzr.c +++ b/sound/soc/codecs/pktzr.c @@ -144,7 +144,6 @@ static int pktzr_send_pkt(void *payload, uint32_t size, void *rsp, rc = bg_cdc_glink_write(ppriv->ch_info[0], pkt_hdr, pkt_size); if (rc < 0) { pr_err("%s: Failed to send command over glink\n", __func__); - list_del(&pnode->list); goto exit; } -- GitLab From a9d35cef8040a5142d91bedb814b9ed3bc5efe7f Mon Sep 17 00:00:00 2001 From: Jin Fu Date: Thu, 4 Jan 2018 16:40:01 +0800 Subject: [PATCH 2186/5498] ARM: dts: msm: enable TP's wakeup-gestures func for sdw2500 Modify Synaptics touch panel device tree to enable TP's wakeup-gestures func for MSM8909W SDW2500 devices. Change-Id: I42f45da6e06bfe702025f813d318c7dbd6542a64 Signed-off-by: Jin Fu --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index a4ed0e1aa404..1d280c975549 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -54,6 +54,7 @@ synaptics,irq-flags = <0x2008>; synaptics,power-delay-ms = <200>; synaptics,max-y-for-2d = <389>; + synaptics,wakeup-gestures-en = <1>; synaptics,resume-in-workqueue; synaptics,x-flip; synaptics,y-flip; -- GitLab From eb3679058627af172b3f46f94e187696c33f7828 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 10 Nov 2017 14:20:32 -0800 Subject: [PATCH 2187/5498] usb: f_gsi: Use required RNDIS IAD descriptors with full speed descriptors gsi_eth_fs_function is array of pointers to set of RNDIS interface related descriptors. gsi_eth_fs_function's first descriptor is pointing itself instead of required RNDIS IAD descriptor. This results into out-of-bound read access while copying these set of descriptors with usb_copy_descriptors() API. Fix this issue by using required RNDIS IAD descriptor here. Change-Id: Ic604221febc43eb8a22d8de99fb8cead74e13b41 Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_gsi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h index 2a4944cb128f..c64b8f8328b2 100644 --- a/drivers/usb/gadget/function/f_gsi.h +++ b/drivers/usb/gadget/function/f_gsi.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. - + * Copyright (c) 2015-2016, 2018 The Linux Foundation. All rights reserved. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. @@ -519,7 +519,7 @@ static struct usb_endpoint_descriptor rndis_gsi_fs_out_desc = { }; static struct usb_descriptor_header *gsi_eth_fs_function[] = { - (struct usb_descriptor_header *) &gsi_eth_fs_function, + (struct usb_descriptor_header *) &rndis_gsi_iad_descriptor, /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_gsi_control_intf, (struct usb_descriptor_header *) &rndis_gsi_header_desc, -- GitLab From 12cff7c6824762ab4b4fea849f05c5ae7866937b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 22 Jun 2017 12:14:40 -0700 Subject: [PATCH 2188/5498] fscrypt: make ->dummy_context() return bool This makes it consistent with ->is_encrypted(), ->empty_dir(), and fscrypt_dummy_context_enabled(). Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt_common.h index 768203e248f0..08c2b87e6814 100644 --- a/include/linux/fscrypt_common.h +++ b/include/linux/fscrypt_common.h @@ -88,7 +88,7 @@ struct fscrypt_operations { const char *key_prefix; int (*get_context)(struct inode *, void *, size_t); int (*set_context)(struct inode *, const void *, size_t, void *); - int (*dummy_context)(struct inode *); + bool (*dummy_context)(struct inode *); bool (*is_encrypted)(struct inode *); bool (*empty_dir)(struct inode *); unsigned (*max_namelen)(struct inode *); -- GitLab From db32474d104c4beed5daadd171f2ab825189d4d7 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:46:18 -0700 Subject: [PATCH 2189/5498] fscrypt: fix dereference of NULL user_key_payload When an fscrypt-encrypted file is opened, we request the file's master key from the keyrings service as a logon key, then access its payload. However, a revoked key has a NULL payload, and we failed to check for this. request_key() *does* skip revoked keys, but there is still a window where the key can be revoked before we acquire its semaphore. Fix it by checking for a NULL payload, treating it like a key which was already revoked at the time it was requested. Fixes: 88bd6ccdcdd6 ("ext4 crypto: add encryption key management facilities") Reviewed-by: James Morris Cc: [v4.1+] Signed-off-by: Eric Biggers Signed-off-by: David Howells --- fs/crypto/keyinfo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index be24dc401ac9..80e7fdfc7804 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -122,6 +122,11 @@ static int validate_user_key(struct fscrypt_info *crypt_info, goto out; } ukp = user_key_payload(keyring_key); + if (!ukp) { + /* key was revoked before we acquired its semaphore */ + res = -EKEYREVOKED; + goto out; + } if (ukp->datalen != sizeof(struct fscrypt_key)) { res = -EINVAL; goto out; -- GitLab From 0f461ce2849e1d0e8649b75aa7922922216d56d4 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 9 Oct 2017 12:15:34 -0700 Subject: [PATCH 2190/5498] fscrypt: clean up include file mess Filesystems have to include different header files based on whether they are compiled with encryption support or not. That's nasty and messy. Instead, rationalise the headers so we have a single include fscrypt.h and let it decide what internal implementation to include based on the __FS_HAS_ENCRYPTION define. Filesystems set __FS_HAS_ENCRYPTION to 1 before including linux/fscrypt.h if they are built with encryption support. Otherwise, they must set __FS_HAS_ENCRYPTION to 0. Add guards to prevent fscrypt_supp.h and fscrypt_notsupp.h from being directly included by filesystems. Signed-off-by: Dave Chinner [EB: use 1 and 0 rather than defined/undefined] Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fscrypt_private.h | 3 +- fs/ext4/ext4.h | 3 ++ fs/f2fs/f2fs.h | 8 ++-- include/linux/{fscrypt_common.h => fscrypt.h} | 41 +++++++++++++------ include/linux/fscrypt_notsupp.h | 7 +++- include/linux/fscrypt_supp.h | 7 ++-- 6 files changed, 45 insertions(+), 24 deletions(-) rename include/linux/{fscrypt_common.h => fscrypt.h} (80%) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4e4f60f134b4..7c2478ec7031 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -11,7 +11,8 @@ #ifndef _FSCRYPT_PRIVATE_H #define _FSCRYPT_PRIVATE_H -#include +#define __FS_HAS_ENCRYPTION 1 +#include #include /* Encryption parameters */ diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index bd997b3d6a6f..c37fa001514e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -36,6 +36,9 @@ #include #endif +#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) +#include + /* * The fourth extended filesystem constants/structures */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 98c8319908fb..ded6a1c9140d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -23,14 +23,12 @@ #include #include #include -#ifdef CONFIG_F2FS_FS_ENCRYPTION -#include -#else -#include -#endif #include #include +#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_F2FS_FS_ENCRYPTION) +#include + #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) #define f2fs_down_write(x, y) down_write_nest_lock(x, y) diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt.h similarity index 80% rename from include/linux/fscrypt_common.h rename to include/linux/fscrypt.h index 08c2b87e6814..3ef043fe3692 100644 --- a/include/linux/fscrypt_common.h +++ b/include/linux/fscrypt.h @@ -1,14 +1,17 @@ /* - * fscrypt_common.h: common declarations for per-file encryption + * fscrypt.h: declarations for per-file encryption + * + * Filesystems that implement per-file encryption include this header + * file with the __FS_HAS_ENCRYPTION set according to whether that filesystem + * is being built with encryption support or not. * * Copyright (C) 2015, Google, Inc. * * Written by Michael Halcrow, 2015. * Modified by Jaegeuk Kim, 2015. */ - -#ifndef _LINUX_FSCRYPT_COMMON_H -#define _LINUX_FSCRYPT_COMMON_H +#ifndef _LINUX_FSCRYPT_H +#define _LINUX_FSCRYPT_H #include #include @@ -127,23 +130,35 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) return false; } +#if __FS_HAS_ENCRYPTION + static inline struct page *fscrypt_control_page(struct page *page) { -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) return ((struct fscrypt_ctx *)page_private(page))->w.control_page; -#else +} + +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return (inode->i_crypt_info != NULL); +} + +#include + +#else /* !__FS_HAS_ENCRYPTION */ + +static inline struct page *fscrypt_control_page(struct page *page) +{ WARN_ON_ONCE(1); return ERR_PTR(-EINVAL); -#endif } -static inline int fscrypt_has_encryption_key(const struct inode *inode) +static inline bool fscrypt_has_encryption_key(const struct inode *inode) { -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - return (inode->i_crypt_info != NULL); -#else return 0; -#endif } -#endif /* _LINUX_FSCRYPT_COMMON_H */ +#include +#endif /* __FS_HAS_ENCRYPTION */ + + +#endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index ec406aed2f2f..2d0b6960831e 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -3,13 +3,16 @@ * * This stubs out the fscrypt functions for filesystems configured without * encryption support. + * + * Do not include this file directly. Use fscrypt.h instead! */ +#ifndef _LINUX_FSCRYPT_H +#error "Incorrect include of linux/fscrypt_notsupp.h!" +#endif #ifndef _LINUX_FSCRYPT_NOTSUPP_H #define _LINUX_FSCRYPT_NOTSUPP_H -#include - /* crypto.c */ static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 6828dc6111fa..0c16ecb7e721 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -1,14 +1,15 @@ /* * fscrypt_supp.h * - * This is included by filesystems configured with encryption support. + * Do not include this file directly. Use fscrypt.h instead! */ +#ifndef _LINUX_FSCRYPT_H +#error "Incorrect include of linux/fscrypt_supp.h!" +#endif #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H -#include - /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); -- GitLab From 12f169fd26835709fe2d34eb064dfb860236b575 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:35 -0700 Subject: [PATCH 2191/5498] fs, fscrypt: add an S_ENCRYPTED inode flag Introduce a flag S_ENCRYPTED which can be set in ->i_flags to indicate that the inode is encrypted using the fscrypt (fs/crypto/) mechanism. Checking this flag will give the same information that inode->i_sb->s_cop->is_encrypted(inode) currently does, but will be more efficient. This will be useful for adding higher-level helper functions for filesystems to use. For example we'll be able to replace this: if (ext4_encrypted_inode(inode)) { ret = fscrypt_get_encryption_info(inode); if (ret) return ret; if (!fscrypt_has_encryption_key(inode)) return -ENOKEY; } with this: ret = fscrypt_require_key(inode); if (ret) return ret; ... since we'll be able to retain the fast path for unencrypted files as a single flag check, using an inline function. This wasn't possible before because we'd have had to frequently call through the ->i_sb->s_cop->is_encrypted function pointer, even when the encryption support was disabled or not being used. Note: we don't define S_ENCRYPTED to 0 if CONFIG_FS_ENCRYPTION is disabled because we want to continue to return an error if an encrypted file is accessed without encryption support, rather than pretending that it is unencrypted. Reviewed-by: Chao Yu Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/f2fs/f2fs.h | 1 + fs/f2fs/inode.c | 5 ++++- include/linux/fs.h | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ded6a1c9140d..49add87dbc63 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3181,6 +3181,7 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION file_set_encrypt(inode); + inode->i_flags |= S_ENCRYPTED; #endif } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 1ae5b61cf2bc..9eb3cc2486d4 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -43,8 +43,11 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_NOATIME; if (flags & FS_DIRSYNC_FL) new_fl |= S_DIRSYNC; + if (f2fs_encrypted_inode(inode)) + new_fl |= S_ENCRYPTED; inode_set_flags(inode, new_fl, - S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); + S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC| + S_ENCRYPTED); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) diff --git a/include/linux/fs.h b/include/linux/fs.h index 72dea272c4ac..124c0a320bc7 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1619,6 +1619,7 @@ struct super_operations { #define S_IMA 1024 /* Inode has an associated IMA struct */ #define S_AUTOMOUNT 2048 /* Automount/referral quasi-directory */ #define S_NOSEC 4096 /* no suid or xattr security attributes */ +#define S_ENCRYPTED 16384 /* Encrypted file (using fs/crypto/) */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -1656,6 +1657,7 @@ struct super_operations { #define IS_IMA(inode) ((inode)->i_flags & S_IMA) #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) +#define IS_ENCRYPTED(inode) ((inode)->i_flags & S_ENCRYPTED) #define IS_WHITEOUT(inode) (S_ISCHR(inode->i_mode) && \ (inode)->i_rdev == WHITEOUT_DEV) -- GitLab From d824e3a72ddc3d81a204afca4e5fdaafceeb5c83 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:36 -0700 Subject: [PATCH 2192/5498] fscrypt: switch from ->is_encrypted() to IS_ENCRYPTED() IS_ENCRYPTED() now gives the same information as i_sb->s_cop->is_encrypted() but is more efficient, since IS_ENCRYPTED() is just a simple flag check. Prepare to remove ->is_encrypted() by switching all callers to IS_ENCRYPTED(). Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/crypto.c | 2 +- fs/crypto/fname.c | 3 +-- fs/crypto/keyinfo.c | 2 +- fs/crypto/policy.c | 6 +++--- include/linux/fscrypt_notsupp.h | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 5182c8e07eba..90b89ffb8918 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -340,7 +340,7 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) return -ECHILD; dir = dget_parent(dentry); - if (!d_inode(dir)->i_sb->s_cop->is_encrypted(d_inode(dir))) { + if (!IS_ENCRYPTED(d_inode(dir))) { dput(dir); return 0; } diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index cef1d3e8c783..84778bba5ac5 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -382,8 +382,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, memset(fname, 0, sizeof(struct fscrypt_name)); fname->usr_fname = iname; - if (!dir->i_sb->s_cop->is_encrypted(dir) || - fscrypt_is_dot_dotdot(iname)) { + if (!IS_ENCRYPTED(dir) || fscrypt_is_dot_dotdot(iname)) { fname->disk_name.name = (unsigned char *)iname->name; fname->disk_name.len = iname->len; return 0; diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 80e7fdfc7804..b4fdc3f29c28 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -286,7 +286,7 @@ int fscrypt_get_encryption_info(struct inode *inode) res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); if (res < 0) { if (!fscrypt_dummy_context_enabled(inode) || - inode->i_sb->s_cop->is_encrypted(inode)) + IS_ENCRYPTED(inode)) return res; /* Fake up a context for an unencrypted directory */ memset(&ctx, 0, sizeof(ctx)); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 9914d51dff86..2f2c53f2e136 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -109,7 +109,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) struct fscrypt_policy policy; int res; - if (!inode->i_sb->s_cop->is_encrypted(inode)) + if (!IS_ENCRYPTED(inode)) return -ENODATA; res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); @@ -166,11 +166,11 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) return 1; /* No restrictions if the parent directory is unencrypted */ - if (!cops->is_encrypted(parent)) + if (!IS_ENCRYPTED(parent)) return 1; /* Encrypted directories must not contain unencrypted files */ - if (!cops->is_encrypted(child)) + if (!IS_ENCRYPTED(child)) return 0; /* diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 2d0b6960831e..7b390e356f7f 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -100,7 +100,7 @@ static inline int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, int lookup, struct fscrypt_name *fname) { - if (dir->i_sb->s_cop->is_encrypted(dir)) + if (IS_ENCRYPTED(dir)) return -EOPNOTSUPP; memset(fname, 0, sizeof(struct fscrypt_name)); -- GitLab From 7319c5ee62741adf695dcb1dae7a867a2c14dfb9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:37 -0700 Subject: [PATCH 2193/5498] fscrypt: remove ->is_encrypted() Now that all callers of fscrypt_operations.is_encrypted() have been switched to IS_ENCRYPTED(), remove ->is_encrypted(). Reviewed-by: Chao Yu Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/f2fs/super.c | 2 -- include/linux/fscrypt.h | 1 - 2 files changed, 3 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b06b3ead8cb1..601d3140b5b5 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1788,13 +1788,11 @@ static const struct fscrypt_operations f2fs_cryptops = { .key_prefix = "f2fs:", .get_context = f2fs_get_context, .set_context = f2fs_set_context, - .is_encrypted = f2fs_encrypted_inode, .empty_dir = f2fs_empty_dir, .max_namelen = f2fs_max_namelen, }; #else static const struct fscrypt_operations f2fs_cryptops = { - .is_encrypted = f2fs_encrypted_inode, }; #endif diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 3ef043fe3692..a3d2181356b9 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -92,7 +92,6 @@ struct fscrypt_operations { int (*get_context)(struct inode *, void *, size_t); int (*set_context)(struct inode *, const void *, size_t, void *); bool (*dummy_context)(struct inode *); - bool (*is_encrypted)(struct inode *); bool (*empty_dir)(struct inode *); unsigned (*max_namelen)(struct inode *); }; -- GitLab From 7fa20786d09a4248b217f595452897fa2d6b2ba4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:38 -0700 Subject: [PATCH 2194/5498] fscrypt: remove unneeded empty fscrypt_operations structs In the case where a filesystem has been configured without encryption support, there is no longer any need to initialize ->s_cop at all, since none of the methods are ever called. Reviewed-by: Chao Yu Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/f2fs/super.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 601d3140b5b5..a945db43369e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1791,9 +1791,6 @@ static const struct fscrypt_operations f2fs_cryptops = { .empty_dir = f2fs_empty_dir, .max_namelen = f2fs_max_namelen, }; -#else -static const struct fscrypt_operations f2fs_cryptops = { -}; #endif static struct inode *f2fs_nfs_get_inode(struct super_block *sb, @@ -2524,7 +2521,9 @@ try_onemore: #endif sb->s_op = &f2fs_sops; +#ifdef CONFIG_F2FS_FS_ENCRYPTION sb->s_cop = &f2fs_cryptops; +#endif sb->s_xattr = f2fs_xattr_handlers; sb->s_export_op = &f2fs_export_ops; sb->s_magic = F2FS_SUPER_MAGIC; -- GitLab From 63aa023948218f32f8dce5970afd0b8da07fe972 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:39 -0700 Subject: [PATCH 2195/5498] fscrypt: new helper function - fscrypt_require_key() Add a helper function which checks if an inode is encrypted, and if so, tries to set up its encryption key. This is a pattern which is duplicated in multiple places in each of ext4, f2fs, and ubifs --- for example, when a regular file is asked to be opened or truncated. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index a3d2181356b9..5455f2c68958 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -159,5 +159,30 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) #include #endif /* __FS_HAS_ENCRYPTION */ +/** + * fscrypt_require_key - require an inode's encryption key + * @inode: the inode we need the key for + * + * If the inode is encrypted, set up its encryption key if not already done. + * Then require that the key be present and return -ENOKEY otherwise. + * + * No locks are needed, and the key will live as long as the struct inode --- so + * it won't go away from under you. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int fscrypt_require_key(struct inode *inode) +{ + if (IS_ENCRYPTED(inode)) { + int err = fscrypt_get_encryption_info(inode); + + if (err) + return err; + if (!fscrypt_has_encryption_key(inode)) + return -ENOKEY; + } + return 0; +} #endif /* _LINUX_FSCRYPT_H */ -- GitLab From 25c01cbc07c1d8879cbf8c44d911ae7d8d74790a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:40 -0700 Subject: [PATCH 2196/5498] fscrypt: new helper function - fscrypt_file_open() Add a helper function which prepares to open a regular file which may be encrypted. It handles setting up the file's encryption key, then checking that the file's encryption policy matches that of its parent directory (if the parent directory is encrypted). It may be set as the ->open() method or it can be called from another ->open() method. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/Makefile | 2 +- fs/crypto/hooks.c | 49 +++++++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 9 ++++++ include/linux/fscrypt_supp.h | 3 ++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 fs/crypto/hooks.c diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index 9f6607f17b53..cb496989a6b6 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o -fscrypto-y := crypto.o fname.o policy.o keyinfo.o +fscrypto-y := crypto.o fname.o hooks.o keyinfo.o policy.o fscrypto-$(CONFIG_BLOCK) += bio.o diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c new file mode 100644 index 000000000000..aee93502eb65 --- /dev/null +++ b/fs/crypto/hooks.c @@ -0,0 +1,49 @@ +/* + * fs/crypto/hooks.c + * + * Encryption hooks for higher-level filesystem operations. + */ + +#include +#include "fscrypt_private.h" + +/** + * fscrypt_file_open - prepare to open a possibly-encrypted regular file + * @inode: the inode being opened + * @filp: the struct file being set up + * + * Currently, an encrypted regular file can only be opened if its encryption key + * is available; access to the raw encrypted contents is not supported. + * Therefore, we first set up the inode's encryption key (if not already done) + * and return an error if it's unavailable. + * + * We also verify that if the parent directory (from the path via which the file + * is being opened) is encrypted, then the inode being opened uses the same + * encryption policy. This is needed as part of the enforcement that all files + * in an encrypted directory tree use the same encryption policy, as a + * protection against certain types of offline attacks. Note that this check is + * needed even when opening an *unencrypted* file, since it's forbidden to have + * an unencrypted file in an encrypted directory. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + */ +int fscrypt_file_open(struct inode *inode, struct file *filp) +{ + int err; + struct dentry *dir; + + err = fscrypt_require_key(inode); + if (err) + return err; + + dir = dget_parent(filp->f_path.dentry); + if (IS_ENCRYPTED(d_inode(dir)) && + !fscrypt_has_permitted_context(d_inode(dir), inode)) { + pr_warn_ratelimited("fscrypt: inconsistent encryption contexts: %lu/%lu", + d_inode(dir)->i_ino, inode->i_ino); + err = -EPERM; + } + dput(dir); + return err; +} +EXPORT_SYMBOL_GPL(fscrypt_file_open); diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 7b390e356f7f..162da6517ac4 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -177,4 +177,13 @@ static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, return -EOPNOTSUPP; } +/* hooks.c */ + +static inline int fscrypt_file_open(struct inode *inode, struct file *filp) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 0c16ecb7e721..f825a060cb8d 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -147,4 +147,7 @@ extern void fscrypt_pullback_bio_page(struct page **, bool); extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, unsigned int); +/* hooks.c */ +extern int fscrypt_file_open(struct inode *inode, struct file *filp); + #endif /* _LINUX_FSCRYPT_SUPP_H */ -- GitLab From 1b4465a3a7a84f12d5515505237778e9f92c2d14 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:41 -0700 Subject: [PATCH 2197/5498] fscrypt: new helper function - fscrypt_prepare_link() Introduce a helper function which prepares to link an inode into a possibly-encrypted directory. It handles setting up the target directory's encryption key, then verifying that the link won't violate the constraint that all files in an encrypted directory tree use the same encryption policy. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/hooks.c | 15 +++++++++++++++ include/linux/fscrypt.h | 27 +++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 6 ++++++ include/linux/fscrypt_supp.h | 1 + 4 files changed, 49 insertions(+) diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index aee93502eb65..16d415edab9c 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -47,3 +47,18 @@ int fscrypt_file_open(struct inode *inode, struct file *filp) return err; } EXPORT_SYMBOL_GPL(fscrypt_file_open); + +int __fscrypt_prepare_link(struct inode *inode, struct inode *dir) +{ + int err; + + err = fscrypt_require_key(dir); + if (err) + return err; + + if (!fscrypt_has_permitted_context(dir, inode)) + return -EPERM; + + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_link); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 5455f2c68958..d3817f006f4a 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -185,4 +185,31 @@ static inline int fscrypt_require_key(struct inode *inode) return 0; } +/** + * fscrypt_prepare_link - prepare to link an inode into a possibly-encrypted directory + * @old_dentry: an existing dentry for the inode being linked + * @dir: the target directory + * @dentry: negative dentry for the target filename + * + * A new link can only be added to an encrypted directory if the directory's + * encryption key is available --- since otherwise we'd have no way to encrypt + * the filename. Therefore, we first set up the directory's encryption key (if + * not already done) and return an error if it's unavailable. + * + * We also verify that the link will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if the directory's encryption key is missing, + * -EPERM if the link would result in an inconsistent encryption policy, or + * another -errno code. + */ +static inline int fscrypt_prepare_link(struct dentry *old_dentry, + struct inode *dir, + struct dentry *dentry) +{ + if (IS_ENCRYPTED(dir)) + return __fscrypt_prepare_link(d_inode(old_dentry), dir); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 162da6517ac4..d7d1039eb6b5 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -186,4 +186,10 @@ static inline int fscrypt_file_open(struct inode *inode, struct file *filp) return 0; } +static inline int __fscrypt_prepare_link(struct inode *inode, + struct inode *dir) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index f825a060cb8d..e1713c6ccb92 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -149,5 +149,6 @@ extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, /* hooks.c */ extern int fscrypt_file_open(struct inode *inode, struct file *filp); +extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- GitLab From 95b5e0ec575d1b1844c4bbf575c72ae061d0bffd Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:42 -0700 Subject: [PATCH 2198/5498] fscrypt: new helper function - fscrypt_prepare_rename() Introduce a helper function which prepares to rename a file into a possibly encrypted directory. It handles loading the encryption keys for the source and target directories if needed, and it handles enforcing that if the target directory (and the source directory for a cross-rename) is encrypted, then the file being moved into the directory has the same encryption policy as its containing directory. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/hooks.c | 30 ++++++++++++++++++++++++++++++ include/linux/fscrypt.h | 33 +++++++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 9 +++++++++ include/linux/fscrypt_supp.h | 5 +++++ 4 files changed, 77 insertions(+) diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 16d415edab9c..00292fb35ef9 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -62,3 +62,33 @@ int __fscrypt_prepare_link(struct inode *inode, struct inode *dir) return 0; } EXPORT_SYMBOL_GPL(__fscrypt_prepare_link); + +int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + int err; + + err = fscrypt_require_key(old_dir); + if (err) + return err; + + err = fscrypt_require_key(new_dir); + if (err) + return err; + + if (old_dir != new_dir) { + if (IS_ENCRYPTED(new_dir) && + !fscrypt_has_permitted_context(new_dir, + d_inode(old_dentry))) + return -EPERM; + + if ((flags & RENAME_EXCHANGE) && + IS_ENCRYPTED(old_dir) && + !fscrypt_has_permitted_context(old_dir, + d_inode(new_dentry))) + return -EPERM; + } + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index d3817f006f4a..46484e686835 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -212,4 +212,37 @@ static inline int fscrypt_prepare_link(struct dentry *old_dentry, return 0; } +/** + * fscrypt_prepare_rename - prepare for a rename between possibly-encrypted directories + * @old_dir: source directory + * @old_dentry: dentry for source file + * @new_dir: target directory + * @new_dentry: dentry for target location (may be negative unless exchanging) + * @flags: rename flags (we care at least about %RENAME_EXCHANGE) + * + * Prepare for ->rename() where the source and/or target directories may be + * encrypted. A new link can only be added to an encrypted directory if the + * directory's encryption key is available --- since otherwise we'd have no way + * to encrypt the filename. A rename to an existing name, on the other hand, + * *is* cryptographically possible without the key. However, we take the more + * conservative approach and just forbid all no-key renames. + * + * We also verify that the rename will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if an encryption key is missing, -EPERM if the + * rename would cause inconsistent encryption policies, or another -errno code. + */ +static inline int fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + if (IS_ENCRYPTED(old_dir) || IS_ENCRYPTED(new_dir)) + return __fscrypt_prepare_rename(old_dir, old_dentry, + new_dir, new_dentry, flags); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index d7d1039eb6b5..6af378d8126e 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -192,4 +192,13 @@ static inline int __fscrypt_prepare_link(struct inode *inode, return -EOPNOTSUPP; } +static inline int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index e1713c6ccb92..552b39d40113 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -150,5 +150,10 @@ extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, /* hooks.c */ extern int fscrypt_file_open(struct inode *inode, struct file *filp); extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); +extern int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- GitLab From 03c10b3f37621251400c8c8122a7d7f9ee0d4e20 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:43 -0700 Subject: [PATCH 2199/5498] fscrypt: new helper function - fscrypt_prepare_lookup() Introduce a helper function which prepares to look up the given dentry in the given directory. If the directory is encrypted, it handles loading the directory's encryption key, setting the dentry's ->d_op to fscrypt_d_ops, and setting DCACHE_ENCRYPTED_WITH_KEY if the directory's encryption key is available. Note: once all filesystems switch over to this, we'll be able to move fscrypt_d_ops and fscrypt_set_encrypted_dentry() to fscrypt_private.h. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/hooks.c | 18 ++++++++++++++++++ include/linux/fscrypt.h | 28 ++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 6 ++++++ include/linux/fscrypt_supp.h | 1 + 4 files changed, 53 insertions(+) diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 00292fb35ef9..396561212dbd 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -92,3 +92,21 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, return 0; } EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename); + +int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry) +{ + int err = fscrypt_get_encryption_info(dir); + + if (err) + return err; + + if (fscrypt_has_encryption_key(dir)) { + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY; + spin_unlock(&dentry->d_lock); + } + + d_set_d_op(dentry, &fscrypt_d_ops); + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 46484e686835..5094d1c42cbb 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -245,4 +245,32 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir, return 0; } +/** + * fscrypt_prepare_lookup - prepare to lookup a name in a possibly-encrypted directory + * @dir: directory being searched + * @dentry: filename being looked up + * @flags: lookup flags + * + * Prepare for ->lookup() in a directory which may be encrypted. Lookups can be + * done with or without the directory's encryption key; without the key, + * filenames are presented in encrypted form. Therefore, we'll try to set up + * the directory's encryption key, but even without it the lookup can continue. + * + * To allow invalidating stale dentries if the directory's encryption key is + * added later, we also install a custom ->d_revalidate() method and use the + * DCACHE_ENCRYPTED_WITH_KEY flag to indicate whether a given dentry is a + * plaintext name (flag set) or a ciphertext name (flag cleared). + * + * Return: 0 on success, -errno if a problem occurred while setting up the + * encryption key + */ +static inline int fscrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + if (IS_ENCRYPTED(dir)) + return __fscrypt_prepare_lookup(dir, dentry); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 6af378d8126e..c4c6bf2c390e 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -201,4 +201,10 @@ static inline int __fscrypt_prepare_rename(struct inode *old_dir, return -EOPNOTSUPP; } +static inline int __fscrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 552b39d40113..4ccecca36747 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -155,5 +155,6 @@ extern int __fscrypt_prepare_rename(struct inode *old_dir, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags); +extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- GitLab From 16e02cc0ccae7c2996e7c8dc02332311df15b1dd Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:44 -0700 Subject: [PATCH 2200/5498] fscrypt: new helper function - fscrypt_prepare_setattr() Introduce a helper function for filesystems to call when processing ->setattr() on a possibly-encrypted inode. It handles enforcing that an encrypted file can only be truncated if its encryption key is available. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 5094d1c42cbb..6a939e0d30af 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -273,4 +273,29 @@ static inline int fscrypt_prepare_lookup(struct inode *dir, return 0; } +/** + * fscrypt_prepare_setattr - prepare to change a possibly-encrypted inode's attributes + * @dentry: dentry through which the inode is being changed + * @attr: attributes to change + * + * Prepare for ->setattr() on a possibly-encrypted inode. On an encrypted file, + * most attribute changes are allowed even without the encryption key. However, + * without the encryption key we do have to forbid truncates. This is needed + * because the size being truncated to may not be a multiple of the filesystem + * block size, and in that case we'd have to decrypt the final block, zero the + * portion past i_size, and re-encrypt it. (We *could* allow truncating to a + * filesystem block boundary, but it's simpler to just forbid all truncates --- + * and we already forbid all other contents modifications without the key.) + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int fscrypt_prepare_setattr(struct dentry *dentry, + struct iattr *attr) +{ + if (attr->ia_valid & ATTR_SIZE) + return fscrypt_require_key(d_inode(dentry)); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ -- GitLab From 89682e2b3157d663df0b564171c40e8f16afb4de Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 29 Oct 2017 06:30:19 -0400 Subject: [PATCH 2201/5498] fscrypt: lock mutex before checking for bounce page pool fscrypt_initialize(), which allocates the global bounce page pool when an encrypted file is first accessed, uses "double-checked locking" to try to avoid locking fscrypt_init_mutex. However, it doesn't use any memory barriers, so it's theoretically possible for a thread to observe a bounce page pool which has not been fully initialized. This is a classic bug with "double-checked locking". While "only a theoretical issue" in the latest kernel, in pre-4.8 kernels the pointer that was checked was not even the last to be initialized, so it was easily possible for a crash (NULL pointer dereference) to happen. This was changed only incidentally by the large refactor to use fs/crypto/. Solve both problems in a trivial way that can easily be backported: just always take the mutex. It's theoretically less efficient, but it shouldn't be noticeable in practice as the mutex is only acquired very briefly once per encrypted file. Later I'd like to make this use a helper macro like DO_ONCE(). However, DO_ONCE() runs in atomic context, so we'd need to add a new macro that allows blocking. Cc: stable@vger.kernel.org # v4.1+ Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/crypto.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 90b89ffb8918..fdccdc7cb63e 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -410,11 +410,8 @@ int fscrypt_initialize(unsigned int cop_flags) { int i, res = -ENOMEM; - /* - * No need to allocate a bounce page pool if there already is one or - * this FS won't use it. - */ - if (cop_flags & FS_CFLG_OWN_PAGES || fscrypt_bounce_page_pool) + /* No need to allocate a bounce page pool if this FS won't use it. */ + if (cop_flags & FS_CFLG_OWN_PAGES) return 0; mutex_lock(&fscrypt_init_mutex); -- GitLab From fb0097fefa09249c23d03bc2cd342c99aa64486a Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Wed, 18 Oct 2017 08:00:38 +0100 Subject: [PATCH 2202/5498] crypto: introduce crypto wait for async op Invoking a possibly async. crypto op and waiting for completion while correctly handling backlog processing is a common task in the crypto API implementation and outside users of it. This patch adds a generic implementation for doing so in preparation for using it across the board instead of hand rolled versions. Signed-off-by: Gilad Ben-Yossef CC: Eric Biggers CC: Jonathan Cameron Signed-off-by: Herbert Xu --- crypto/api.c | 13 +++++++++++++ include/linux/crypto.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/crypto/api.c b/crypto/api.c index 7db2e89a3114..937f74878a6e 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "internal.h" LIST_HEAD(crypto_alg_list); @@ -601,5 +602,17 @@ int crypto_has_alg(const char *name, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(crypto_has_alg); +void crypto_req_done(struct crypto_async_request *req, int err) +{ + struct crypto_wait *wait = req->data; + + if (err == -EINPROGRESS) + return; + + wait->err = err; + complete(&wait->completion); +} +EXPORT_SYMBOL_GPL(crypto_req_done); + MODULE_DESCRIPTION("Cryptographic core API"); MODULE_LICENSE("GPL"); diff --git a/include/linux/crypto.h b/include/linux/crypto.h index dc34dfc766b5..7284dbfcbffd 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -24,6 +24,7 @@ #include #include #include +#include /* * Autoloaded crypto modules should only use a prefixed name to avoid allowing @@ -323,6 +324,45 @@ struct crypto_alg { struct module *cra_module; }; +/* + * A helper struct for waiting for completion of async crypto ops + */ +struct crypto_wait { + struct completion completion; + int err; +}; + +/* + * Macro for declaring a crypto op async wait object on stack + */ +#define DECLARE_CRYPTO_WAIT(_wait) \ + struct crypto_wait _wait = { \ + COMPLETION_INITIALIZER_ONSTACK((_wait).completion), 0 } + +/* + * Async ops completion helper functioons + */ +void crypto_req_done(struct crypto_async_request *req, int err); + +static inline int crypto_wait_req(int err, struct crypto_wait *wait) +{ + switch (err) { + case -EINPROGRESS: + case -EBUSY: + wait_for_completion(&wait->completion); + reinit_completion(&wait->completion); + err = wait->err; + break; + }; + + return err; +} + +static inline void crypto_init_wait(struct crypto_wait *wait) +{ + init_completion(&wait->completion); +} + /* * Algorithm registration interface. */ -- GitLab From b9dad2c0a3c4cef790e59d071404eeb5443e56b2 Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Wed, 18 Oct 2017 08:00:44 +0100 Subject: [PATCH 2203/5498] fscrypt: move to generic async completion fscrypt starts several async. crypto ops and waiting for them to complete. Move it over to generic code doing the same. Signed-off-by: Gilad Ben-Yossef Signed-off-by: Herbert Xu --- fs/crypto/crypto.c | 28 ++++------------------------ fs/crypto/fname.c | 36 ++++++------------------------------ fs/crypto/fscrypt_private.h | 9 --------- fs/crypto/keyinfo.c | 21 +++------------------ 4 files changed, 13 insertions(+), 81 deletions(-) diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index fdccdc7cb63e..7024fcf4e589 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -126,21 +126,6 @@ struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) } EXPORT_SYMBOL(fscrypt_get_ctx); -/** - * page_crypt_complete() - completion callback for page crypto - * @req: The asynchronous cipher request context - * @res: The result of the cipher operation - */ -static void page_crypt_complete(struct crypto_async_request *req, int res) -{ - struct fscrypt_completion_result *ecr = req->data; - - if (res == -EINPROGRESS) - return; - ecr->res = res; - complete(&ecr->completion); -} - int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, u64 lblk_num, struct page *src_page, struct page *dest_page, unsigned int len, @@ -151,7 +136,7 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, u8 padding[FS_IV_SIZE - sizeof(__le64)]; } iv; struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct scatterlist dst, src; struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; @@ -179,7 +164,7 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, ablkcipher_request_set_callback( req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - page_crypt_complete, &ecr); + crypto_req_done, &wait); sg_init_table(&dst, 1); sg_set_page(&dst, dest_page, len, offs); @@ -187,14 +172,9 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, sg_set_page(&src, src_page, len, offs); ablkcipher_request_set_crypt(req, &src, &dst, len, &iv); if (rw == FS_DECRYPT) - res = crypto_ablkcipher_decrypt(req); + res = crypto_wait_req(crypto_ablkcipher_decrypt(req), &wait); else - res = crypto_ablkcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - BUG_ON(req->base.data != &ecr); - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_encrypt(req), &wait); ablkcipher_request_free(req); if (res) { printk_ratelimited(KERN_ERR diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 84778bba5ac5..690426912d0e 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -14,21 +14,6 @@ #include #include "fscrypt_private.h" -/** - * fname_crypt_complete() - completion callback for filename crypto - * @req: The asynchronous cipher request context - * @res: The result of the cipher operation - */ -static void fname_crypt_complete(struct crypto_async_request *req, int res) -{ - struct fscrypt_completion_result *ecr = req->data; - - if (res == -EINPROGRESS) - return; - ecr->res = res; - complete(&ecr->completion); -} - /** * fname_encrypt() - encrypt a filename * @@ -40,7 +25,7 @@ static int fname_encrypt(struct inode *inode, const struct qstr *iname, struct fscrypt_str *oname) { struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; @@ -76,17 +61,12 @@ static int fname_encrypt(struct inode *inode, } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - fname_crypt_complete, &ecr); + crypto_req_done, &wait); sg_init_one(&sg, oname->name, cryptlen); ablkcipher_request_set_crypt(req, &sg, &sg, cryptlen, iv); /* Do the encryption */ - res = crypto_ablkcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - /* Request is being completed asynchronously; wait for it */ - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_encrypt(req), &wait); ablkcipher_request_free(req); if (res < 0) { printk_ratelimited(KERN_ERR @@ -110,7 +90,7 @@ static int fname_decrypt(struct inode *inode, struct fscrypt_str *oname) { struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct scatterlist src_sg, dst_sg; struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; @@ -131,7 +111,7 @@ static int fname_decrypt(struct inode *inode, } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - fname_crypt_complete, &ecr); + crypto_req_done, &wait); /* Initialize IV */ memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); @@ -140,11 +120,7 @@ static int fname_decrypt(struct inode *inode, sg_init_one(&src_sg, iname->name, iname->len); sg_init_one(&dst_sg, oname->name, oname->len); ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); - res = crypto_ablkcipher_decrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_decrypt(req), &wait); ablkcipher_request_free(req); if (res < 0) { printk_ratelimited(KERN_ERR diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 7c2478ec7031..acb0ccc65698 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -70,15 +70,6 @@ typedef enum { #define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 #define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002 -struct fscrypt_completion_result { - struct completion completion; - int res; -}; - -#define DECLARE_FS_COMPLETION_RESULT(ecr) \ - struct fscrypt_completion_result ecr = { \ - COMPLETION_INITIALIZER_ONSTACK((ecr).completion), 0 } - static inline void inode_lock(struct inode *inode) { mutex_lock(&inode->i_mutex); diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index b4fdc3f29c28..8c3bca16cfa7 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -17,17 +17,6 @@ static struct crypto_shash *essiv_hash_tfm; -static void derive_crypt_complete(struct crypto_async_request *req, int rc) -{ - struct fscrypt_completion_result *ecr = req->data; - - if (rc == -EINPROGRESS) - return; - - ecr->res = rc; - complete(&ecr->completion); -} - /** * derive_key_aes() - Derive a key using AES-128-ECB * @deriving_key: Encryption key used for derivation. @@ -42,7 +31,7 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], { int res = 0; struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct scatterlist src_sg, dst_sg; struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, 0); @@ -59,7 +48,7 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - derive_crypt_complete, &ecr); + crypto_req_done, &wait); res = crypto_ablkcipher_setkey(tfm, deriving_key, FS_AES_128_ECB_KEY_SIZE); if (res < 0) @@ -69,11 +58,7 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], sg_init_one(&dst_sg, derived_raw_key, source_key->size); ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size, NULL); - res = crypto_ablkcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_encrypt(req), &wait); out: if (req) ablkcipher_request_free(req); -- GitLab From 82a3d23084c0e479ba30333691a8eb9f3e3b8a7e Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 4 Jan 2018 15:02:09 +0530 Subject: [PATCH 2204/5498] ASoC: msm: msm_bg: Avoid register access if ADSP not ready Access of adsp registers results in a crash if accessed without adsp being up. Check if adsp is up and only then access adsp. Change-Id: I139b0ac897422ca52532a8d1fc7f6f6c63031ede Signed-off-by: Ashish Jain --- sound/soc/msm/msm_bg.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/soc/msm/msm_bg.c b/sound/soc/msm/msm_bg.c index 9d858286b61c..2a9369ef2de8 100644 --- a/sound/soc/msm/msm_bg.c +++ b/sound/soc/msm/msm_bg.c @@ -1,5 +1,5 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -960,6 +960,12 @@ static int msm_tdm_startup(struct snd_pcm_substream *substream) substream->name, substream->stream); pr_debug("dai id = 0x%x", cpu_dai->id); + if (!q6core_is_adsp_ready()) { + pr_err_ratelimited("%s: ADSP Audio isn't ready\n", + __func__); + return -EINVAL; + } + switch (cpu_dai->id) { case AFE_PORT_ID_PRIMARY_TDM_RX: case AFE_PORT_ID_PRIMARY_TDM_RX_1: @@ -1049,6 +1055,11 @@ static void msm_tdm_shutdown(struct snd_pcm_substream *substream) pr_err("Failed to set spkr_vreg mode.\n"); goto err; } + + if (!q6core_is_adsp_ready()) { + pr_err("%s(): adsp not ready\n", __func__); + goto err; + } /* Reset Configuration of mux for Primary TDM */ if (pdata->vaddr_gpio_mux_pcm_ctl) { val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); -- GitLab From 6dbe7aa6d6758f28b2d3b0ebb146f6431b178f47 Mon Sep 17 00:00:00 2001 From: Srinu Gorle Date: Fri, 5 Jan 2018 13:09:13 +0530 Subject: [PATCH 2205/5498] ARM: dts: msm: correct venus clock frequencies for 8909 Due to venus clock frequencies are placed in wrong order, turbo clock voting is observed for lower resolutions. Correct clock rates supported by hardware in appropriate order. Change-Id: Icc837b891ec6ce9c0cf5373fbf2f6795dc712804 Signed-off-by: Srinu Gorle --- arch/arm/boot/dts/qcom/msm8909.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 0122e682bb5a..e0f8b40da2c3 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -612,7 +612,7 @@ <0x9181000 0x1000>; qcom,max-hw-load = <244800>; /* 1080p@30 */ qcom,firmware-name = "venus"; - qcom,allowed-clock-rates = <133330000 266670000 307200000>; + qcom,allowed-clock-rates = <307200000 266670000 133330000>; qcom,clock-freq-tbl { qcom,profile-enc { qcom,codec-mask = <0x55555555>; -- GitLab From 2d41a5778bf2ab90725f30d4186817416dab8b49 Mon Sep 17 00:00:00 2001 From: Umang Agrawal Date: Wed, 3 Jan 2018 19:28:30 +0530 Subject: [PATCH 2206/5498] power: qpnp-charger: Fix null pointer dereference error Fix to prevent null pointer dereference error in the led brightness set function by an initial check to determine whether the power_supply structure is defined or not. CRs-Fixed: 2166164 Change-Id: Ifcd6e55aa78c4c4d4dc539ac6ffe67263d198b47 Signed-off-by: Umang Agrawal --- drivers/power/qpnp-smbcharger.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/power/qpnp-smbcharger.c b/drivers/power/qpnp-smbcharger.c index 328877a87951..8120fb1514d2 100644 --- a/drivers/power/qpnp-smbcharger.c +++ b/drivers/power/qpnp-smbcharger.c @@ -4243,10 +4243,12 @@ static void smbchg_chg_led_brightness_set(struct led_classdev *cdev, reg = (value > LED_OFF) ? CHG_LED_ON << CHG_LED_SHIFT : CHG_LED_OFF << CHG_LED_SHIFT; - if (value > LED_OFF) - power_supply_set_hi_power_state(chip->bms_psy, 1); - else - power_supply_set_hi_power_state(chip->bms_psy, 0); + if (chip->bms_psy) { + if (value > LED_OFF) + power_supply_set_hi_power_state(chip->bms_psy, 1); + else + power_supply_set_hi_power_state(chip->bms_psy, 0); + } pr_smb(PR_STATUS, "set the charger led brightness to value=%d\n", @@ -4289,11 +4291,16 @@ static void smbchg_chg_led_blink_set(struct smbchg_chip *chip, u8 reg; int rc; + if (chip->bms_psy) { + if (blinking == 0) + power_supply_set_hi_power_state(chip->bms_psy, 0); + else + power_supply_set_hi_power_state(chip->bms_psy, 1); + } + if (blinking == 0) { reg = CHG_LED_OFF << CHG_LED_SHIFT; - power_supply_set_hi_power_state(chip->bms_psy, 0); } else { - power_supply_set_hi_power_state(chip->bms_psy, 1); if (blinking == 1) reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT; else if (blinking == 2) -- GitLab From bf1da2e96cbcdfbdacf8e541d22ad58bcd1ef4df Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Wed, 13 Dec 2017 17:22:13 -0800 Subject: [PATCH 2207/5498] msm: ipa4: Mhi test case fix Add offset for even_ring_bufs. Fix a buff offset error with 1pa3_write used in MHI test case. Remove support for aggr_open tests for IPAv4.0 and greater. CRs-fixed: 2158591 Change-Id: Ie207dc234787013163a1eecf11c8bde8bf7bcce4 Signed-off-by: Michael Adisumarta --- drivers/platform/msm/ipa/ipa_v3/ipa.c | 2 +- drivers/platform/msm/ipa/test/ipa_test_mhi.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index aae19811f662..eea7d7302b71 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4608,7 +4608,7 @@ static ssize_t ipa3_write(struct file *file, const char __user *buf, } if (count > 0) - dbg_buff[count - 1] = '\0'; + dbg_buff[count] = '\0'; /* Prevent consequent calls from trying to load the FW again. */ if (ipa3_is_ready()) diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c index 43c8b3a818c3..65a5cf7b83cc 100644 --- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c +++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c @@ -612,7 +612,8 @@ static int ipa_mhi_test_config_channel_context( p_events[ev_ring_idx].rp = (u32)event_ring_bufs[ev_ring_idx].phys_base; p_events[ev_ring_idx].wp = - (u32)event_ring_bufs[ev_ring_idx].phys_base; + (u32)event_ring_bufs[ev_ring_idx].phys_base + + event_ring_bufs[ev_ring_idx].size - 16; } else { IPA_UT_LOG("Skip configuring event ring - already done\n"); } @@ -3261,11 +3262,11 @@ IPA_UT_DEFINE_SUITE_START(mhi, "MHI for GSI", IPA_UT_ADD_TEST(suspend_resume_with_open_aggr, "several suspend/resume iterations with open aggregation frame", ipa_mhi_test_in_loop_suspend_resume_aggr_open, - true, IPA_HW_v3_0, IPA_HW_MAX), + true, IPA_HW_v3_0, IPA_HW_v3_5_1), IPA_UT_ADD_TEST(force_suspend_resume_with_open_aggr, "several force suspend/resume iterations with open aggregation frame", ipa_mhi_test_in_loop_force_suspend_resume_aggr_open, - true, IPA_HW_v3_0, IPA_HW_MAX), + true, IPA_HW_v3_0, IPA_HW_v3_5_1), IPA_UT_ADD_TEST(suspend_resume_with_host_wakeup, "several suspend and host wakeup resume iterations", ipa_mhi_test_in_loop_suspend_host_wakeup, -- GitLab From 7d85ad0a577bce59e734cb528c60504805d468dd Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Wed, 1 Nov 2017 18:16:48 -0700 Subject: [PATCH 2208/5498] msm: ipa: dynamic memory leak fix This is a fix for dynamic memory leak seen with incorrectly allocating memory of a different size than with intended size. Change-Id: I821442ee6728ea90ceab7644e194f4e06369333a Acked-by: Jyothi Jayanthi Signed-off-by: Michael Adisumarta --- drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c index 477d8427f9cf..9ceced0c924f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c @@ -616,8 +616,9 @@ static void ipa_save_uc_smmu_mapping_pa(int res_idx, phys_addr_t pa, unsigned long iova, size_t len) { IPADBG("--res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx, - &pa, iova, len); - wdi_res[res_idx].res = kzalloc(sizeof(struct ipa_wdi_res), GFP_KERNEL); + &pa, iova, len); + wdi_res[res_idx].res = kzalloc(sizeof(*wdi_res[res_idx].res), + GFP_KERNEL); if (!wdi_res[res_idx].res) BUG(); wdi_res[res_idx].nents = 1; @@ -643,7 +644,8 @@ static void ipa_save_uc_smmu_mapping_sgt(int res_idx, struct sg_table *sgt, return; } - wdi_res[res_idx].res = kcalloc(sgt->nents, sizeof(struct ipa_wdi_res), + wdi_res[res_idx].res = kcalloc(sgt->nents, + sizeof(*wdi_res[res_idx].res), GFP_KERNEL); if (!wdi_res[res_idx].res) BUG(); -- GitLab From a969e0ffc304ee3ac90d3dc0c75b9349cd86c700 Mon Sep 17 00:00:00 2001 From: Shihuan Liu Date: Thu, 28 Sep 2017 17:46:41 -0700 Subject: [PATCH 2209/5498] msm: ipa: add new IPA filtering bitmap Add new IPA filtering bitmap to match inner IP type and inner IPv4 address in L2TP use case. Change-Id: I30afbfba6fb0150ab90826eb2543540699ab895b Acked-by: Shihuan Liu Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c | 11 +++- drivers/platform/msm/ipa/ipa_v2/ipa_utils.c | 61 +++++++++++++++++++ drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c | 11 +++- drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 59 ++++++++++++++++++ include/uapi/linux/msm_ipa.h | 2 + 5 files changed, 142 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index 4c4a97f52987..460243dced7b 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -580,6 +580,15 @@ static int ipa_attrib_dump(struct ipa_rule_attrib *attrib, if (attrib->attrib_mask & IPA_FLT_MAC_ETHER_TYPE) pr_err("ether_type:%x ", attrib->ether_type); + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IP_TYPE) + pr_err("l2tp inner ip type: %d ", attrib->type); + + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IPV4_DST_ADDR) { + addr[0] = htonl(attrib->u.v4.dst_addr); + mask[0] = htonl(attrib->u.v4.dst_addr_mask); + pr_err("dst_addr:%pI4 dst_addr_mask:%pI4 ", addr, mask); + } + pr_err("\n"); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index f354ced5c509..6901da855727 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -1527,6 +1527,37 @@ int ipa_generate_hw_rule(enum ipa_ip_type ip, ihl_ofst_meq32++; } + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IP_TYPE) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + return -EPERM; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 22 => offset of IP type after v6 header */ + *buf = ipa_write_8(22, *buf); + *buf = ipa_write_32(0xF0000000, *buf); + if (attrib->type == 0x40) + *buf = ipa_write_32(0x40000000, *buf); + else + *buf = ipa_write_32(0x60000000, *buf); + *buf = ipa_pad_to_32(*buf); + ihl_ofst_meq32++; + } + + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IPV4_DST_ADDR) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + return -EPERM; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 38 => offset of inner IPv4 addr */ + *buf = ipa_write_8(38, *buf); + *buf = ipa_write_32(attrib->u.v4.dst_addr_mask, *buf); + *buf = ipa_write_32(attrib->u.v4.dst_addr, *buf); + *buf = ipa_pad_to_32(*buf); + ihl_ofst_meq32++; + } + if (attrib->attrib_mask & IPA_FLT_SRC_PORT) { if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) { IPAERR("ran out of ihl_rng16 eq\n"); @@ -2126,6 +2157,36 @@ int ipa_generate_flt_eq(enum ipa_ip_type ip, ihl_ofst_meq32++; } + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IP_TYPE) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + return -EPERM; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 22 => offset of inner IP type after v6 header */ + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].offset = 22; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].mask = + 0xF0000000; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].value = + (u32)attrib->type << 24; + ihl_ofst_meq32++; + } + + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IPV4_DST_ADDR) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + return -EPERM; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 38 => offset of inner IPv4 addr */ + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].offset = 38; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].mask = + attrib->u.v4.dst_addr_mask; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].value = + attrib->u.v4.dst_addr; + ihl_ofst_meq32++; + } + if (attrib->attrib_mask & IPA_FLT_SRC_PORT) { if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) { IPAERR_RL("ran out of ihl_rng16 eq\n"); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index 25159acf6e9b..10a1347e1d80 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -514,6 +514,15 @@ static int ipa3_attrib_dump(struct ipa_rule_attrib *attrib, if (attrib->attrib_mask & IPA_FLT_TCP_SYN_L2TP) pr_err("tcp syn l2tp "); + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IP_TYPE) + pr_err("l2tp inner ip type: %d ", attrib->type); + + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IPV4_DST_ADDR) { + addr[0] = htonl(attrib->u.v4.dst_addr); + mask[0] = htonl(attrib->u.v4.dst_addr_mask); + pr_err("dst_addr:%pI4 dst_addr_mask:%pI4 ", addr, mask); + } + pr_err("\n"); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index b471e4da984e..e80c62c440e8 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -2736,6 +2736,35 @@ static int ipa3_generate_hw_rule_ip6(u16 *en_rule, ihl_ofst_meq32 += 2; } + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IP_TYPE) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + goto err; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 22 => offset of IP type after v6 header */ + extra = ipa3_write_8(22, extra); + rest = ipa3_write_32(0xF0000000, rest); + if (attrib->type == 0x40) + rest = ipa3_write_32(0x40000000, rest); + else + rest = ipa3_write_32(0x60000000, rest); + ihl_ofst_meq32++; + } + + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IPV4_DST_ADDR) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + goto err; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 38 => offset of inner IPv4 addr */ + extra = ipa3_write_8(38, extra); + rest = ipa3_write_32(attrib->u.v4.dst_addr_mask, rest); + rest = ipa3_write_32(attrib->u.v4.dst_addr, rest); + ihl_ofst_meq32++; + } + if (attrib->attrib_mask & IPA_FLT_META_DATA) { *en_rule |= IPA_METADATA_COMPARE; rest = ipa3_write_32(attrib->meta_data_mask, rest); @@ -3555,6 +3584,36 @@ int ipa3_generate_flt_eq_ip6(enum ipa_ip_type ip, ihl_ofst_meq32 += 2; } + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IP_TYPE) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + return -EPERM; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 22 => offset of inner IP type after v6 header */ + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].offset = 22; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].mask = + 0xF0000000; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].value = + (u32)attrib->type << 24; + ihl_ofst_meq32++; + } + + if (attrib->attrib_mask & IPA_FLT_L2TP_INNER_IPV4_DST_ADDR) { + if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) { + IPAERR("ran out of ihl_meq32 eq\n"); + return -EPERM; + } + *en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32]; + /* 38 => offset of inner IPv4 addr */ + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].offset = 38; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].mask = + attrib->u.v4.dst_addr_mask; + eq_atrb->ihl_offset_meq_32[ihl_ofst_meq32].value = + attrib->u.v4.dst_addr; + ihl_ofst_meq32++; + } + if (attrib->attrib_mask & IPA_FLT_MAC_ETHER_TYPE) { if (ipa_ofst_meq32[ofst_meq32] == -1) { IPAERR("ran out of meq128 eq\n"); diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index 081bbd91bfec..63231835704b 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -166,6 +166,8 @@ #define IPA_FLT_MAC_DST_ADDR_L2TP (1ul << 22) #define IPA_FLT_TCP_SYN (1ul << 23) #define IPA_FLT_TCP_SYN_L2TP (1ul << 24) +#define IPA_FLT_L2TP_INNER_IP_TYPE (1ul << 25) +#define IPA_FLT_L2TP_INNER_IPV4_DST_ADDR (1ul << 26) /** * maximal number of NAT PDNs in the PDN config table -- GitLab From e0c5ba8cd917ff1bd7d6ea3925359b96650ecdfc Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 13 Sep 2016 11:16:06 +0100 Subject: [PATCH 2210/5498] UPSTREAM: arm64: tlbflush.h: add __tlbi() macro As with dsb() and isb(), add a __tlbi() helper so that we can avoid distracting asm boilerplate every time we want a TLBI. As some TLBI operations take an argument while others do not, some pre-processor is used to handle these two cases with different assembly blocks. The existing tlbflush.h code is moved over to use the helper. Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Marc Zyngier [ rename helper to __tlbi, update comment and commit log ] Signed-off-by: Punit Agrawal Reviewed-by: Will Deacon Signed-off-by: Will Deacon (cherry picked from commit db68f3e7594aca77632d56c449bd36c6c931d59a) Change-Id: I9b94aff5efd20e3485dfa3a2780e1f8130e60d52 Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/tlbflush.h | 34 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index b460ae28e346..deab52374119 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -24,6 +24,24 @@ #include #include +/* + * Raw TLBI operations. + * + * Where necessary, use the __tlbi() macro to avoid asm() + * boilerplate. Drivers and most kernel code should use the TLB + * management routines in preference to the macro below. + * + * The macro can be used as __tlbi(op) or __tlbi(op, arg), depending + * on whether a particular TLBI operation takes an argument or + * not. The macros handles invoking the asm with or without the + * register argument as appropriate. + */ +#define __TLBI_0(op, arg) asm ("tlbi " #op) +#define __TLBI_1(op, arg) asm ("tlbi " #op ", %0" : : "r" (arg)) +#define __TLBI_N(op, arg, n, ...) __TLBI_##n(op, arg) + +#define __tlbi(op, ...) __TLBI_N(op, ##__VA_ARGS__, 1, 0) + /* * TLB Management * ============== @@ -66,7 +84,7 @@ static inline void local_flush_tlb_all(void) { dsb(nshst); - asm("tlbi vmalle1"); + __tlbi(vmalle1); dsb(nsh); isb(); } @@ -74,7 +92,7 @@ static inline void local_flush_tlb_all(void) static inline void flush_tlb_all(void) { dsb(ishst); - asm("tlbi vmalle1is"); + __tlbi(vmalle1is); dsb(ish); isb(); } @@ -84,7 +102,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) unsigned long asid = ASID(mm) << 48; dsb(ishst); - asm("tlbi aside1is, %0" : : "r" (asid)); + __tlbi(aside1is, asid); dsb(ish); } @@ -94,7 +112,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr = uaddr >> 12 | (ASID(vma->vm_mm) << 48); dsb(ishst); - asm("tlbi vale1is, %0" : : "r" (addr)); + __tlbi(vale1is, addr); dsb(ish); } @@ -122,9 +140,9 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) { if (last_level) - asm("tlbi vale1is, %0" : : "r"(addr)); + __tlbi(vale1is, addr); else - asm("tlbi vae1is, %0" : : "r"(addr)); + __tlbi(vae1is, addr); } dsb(ish); } @@ -149,7 +167,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) - asm("tlbi vaae1is, %0" : : "r"(addr)); + __tlbi(vaae1is, addr); dsb(ish); isb(); } @@ -163,7 +181,7 @@ static inline void __flush_tlb_pgtable(struct mm_struct *mm, { unsigned long addr = uaddr >> 12 | (ASID(mm) << 48); - asm("tlbi vae1is, %0" : : "r" (addr)); + __tlbi(vae1is, addr); dsb(ish); } -- GitLab From 1ee4be715741bbfbf5dd194da3958e7a4ea87807 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 19 Jul 2017 17:24:49 +0100 Subject: [PATCH 2211/5498] UPSTREAM: arm64: factor out entry stack manipulation In subsequent patches, we will detect stack overflow in our exception entry code, by verifying the SP after it has been decremented to make space for the exception regs. This verification code is small, and we can minimize its impact by placing it directly in the vectors. To avoid redundant modification of the SP, we also need to move the initial decrement of the SP into the vectors. As a preparatory step, this patch introduces kernel_ventry, which performs this decrement, and updates the entry code accordingly. Subsequent patches will fold SP verification into kernel_ventry. There should be no functional change as a result of this patch. Signed-off-by: Ard Biesheuvel [Mark: turn into prep patch, expand commit msg] Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Tested-by: Laura Abbott Cc: Catalin Marinas Cc: James Morse (cherry picked from commit b11e5759bfac0c474d95ec4780b1566350e64cad) Change-Id: I5883da81b374498f2f9e16ccb596b22c5568f2fe Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry.S | 47 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index d16c670e83ff..b3e65d463857 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -69,8 +69,13 @@ #define BAD_FIQ 2 #define BAD_ERROR 3 - .macro kernel_entry, el, regsize = 64 + .macro kernel_ventry label + .align 7 sub sp, sp, #S_FRAME_SIZE + b \label + .endm + + .macro kernel_entry, el, regsize = 64 .if \regsize == 32 mov w0, w0 // zero upper 32 bits of x0 .endif @@ -270,31 +275,31 @@ tsk .req x28 // current thread_info .align 11 ENTRY(vectors) - ventry el1_sync_invalid // Synchronous EL1t - ventry el1_irq_invalid // IRQ EL1t - ventry el1_fiq_invalid // FIQ EL1t - ventry el1_error_invalid // Error EL1t + kernel_ventry el1_sync_invalid // Synchronous EL1t + kernel_ventry el1_irq_invalid // IRQ EL1t + kernel_ventry el1_fiq_invalid // FIQ EL1t + kernel_ventry el1_error_invalid // Error EL1t - ventry el1_sync // Synchronous EL1h - ventry el1_irq // IRQ EL1h - ventry el1_fiq_invalid // FIQ EL1h - ventry el1_error_invalid // Error EL1h + kernel_ventry el1_sync // Synchronous EL1h + kernel_ventry el1_irq // IRQ EL1h + kernel_ventry el1_fiq_invalid // FIQ EL1h + kernel_ventry el1_error_invalid // Error EL1h - ventry el0_sync // Synchronous 64-bit EL0 - ventry el0_irq // IRQ 64-bit EL0 - ventry el0_fiq_invalid // FIQ 64-bit EL0 - ventry el0_error_invalid // Error 64-bit EL0 + kernel_ventry el0_sync // Synchronous 64-bit EL0 + kernel_ventry el0_irq // IRQ 64-bit EL0 + kernel_ventry el0_fiq_invalid // FIQ 64-bit EL0 + kernel_ventry el0_error_invalid // Error 64-bit EL0 #ifdef CONFIG_COMPAT - ventry el0_sync_compat // Synchronous 32-bit EL0 - ventry el0_irq_compat // IRQ 32-bit EL0 - ventry el0_fiq_invalid_compat // FIQ 32-bit EL0 - ventry el0_error_invalid_compat // Error 32-bit EL0 + kernel_ventry el0_sync_compat // Synchronous 32-bit EL0 + kernel_ventry el0_irq_compat // IRQ 32-bit EL0 + kernel_ventry el0_fiq_invalid_compat // FIQ 32-bit EL0 + kernel_ventry el0_error_invalid_compat // Error 32-bit EL0 #else - ventry el0_sync_invalid // Synchronous 32-bit EL0 - ventry el0_irq_invalid // IRQ 32-bit EL0 - ventry el0_fiq_invalid // FIQ 32-bit EL0 - ventry el0_error_invalid // Error 32-bit EL0 + kernel_ventry el0_sync_invalid // Synchronous 32-bit EL0 + kernel_ventry el0_irq_invalid // IRQ 32-bit EL0 + kernel_ventry el0_fiq_invalid // FIQ 32-bit EL0 + kernel_ventry el0_error_invalid // Error 32-bit EL0 #endif END(vectors) -- GitLab From 6e0432e6d0844d05427166f0f7a3edad37362535 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 26 Nov 2015 13:49:39 +0000 Subject: [PATCH 2212/5498] UPSTREAM: arm64: mm: keep reserved ASIDs in sync with mm after multiple rollovers Under some unusual context-switching patterns, it is possible to end up with multiple threads from the same mm running concurrently with different ASIDs: 1. CPU x schedules task t with mm p containing ASID a and generation g This task doesn't block and the CPU doesn't context switch. So: * per_cpu(active_asid, x) = {g,a} * p->context.id = {g,a} 2. Some other CPU generates an ASID rollover. The global generation is now (g + 1). CPU x is still running t, with no context switch and so per_cpu(reserved_asid, x) = {g,a} 3. CPU y schedules task t', which shares mm p with t. The generation mismatches, so we take the slowpath and hit the reserved ASID from CPU x. p is then updated so that p->context.id = {g + 1,a} 4. CPU y schedules some other task u, which has an mm != p. 5. Some other CPU generates *another* CPU rollover. The global generation is now (g + 2). CPU x is still running t, with no context switch and so per_cpu(reserved_asid, x) = {g,a}. 6. CPU y once again schedules task t', but now *fails* to hit the reserved ASID from CPU x because of the generation mismatch. This results in a new ASID being allocated, despite the fact that t is still running on CPU x with the same mm. Consequently, TLBIs (e.g. as a result of CoW) will not be synchronised between the two threads. This patch fixes the problem by updating all of the matching reserved ASIDs when we hit on the slowpath (i.e. in step 3 above). This keeps the reserved ASIDs in-sync with the mm and avoids the problem. Reported-by: Tony Thompson Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas (cherry picked from commit 0ebea8088095f1c18c1d1de284ccc4c479ca21c1) Change-Id: Id87ffcec384f415d921cab56bde00d2f5771ce18 Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/mm/context.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 43cff0e6f52e..25128089c386 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -76,13 +76,28 @@ static void flush_context(unsigned int cpu) __flush_icache_all(); } -static int is_reserved_asid(u64 asid) +static bool check_update_reserved_asid(u64 asid, u64 newasid) { int cpu; - for_each_possible_cpu(cpu) - if (per_cpu(reserved_asids, cpu) == asid) - return 1; - return 0; + bool hit = false; + + /* + * Iterate over the set of reserved ASIDs looking for a match. + * If we find one, then we can update our mm to use newasid + * (i.e. the same ASID in the current generation) but we can't + * exit the loop early, since we need to ensure that all copies + * of the old ASID are updated to reflect the mm. Failure to do + * so could result in us missing the reserved ASID in a future + * generation. + */ + for_each_possible_cpu(cpu) { + if (per_cpu(reserved_asids, cpu) == asid) { + hit = true; + per_cpu(reserved_asids, cpu) = newasid; + } + } + + return hit; } static u64 new_context(struct mm_struct *mm, unsigned int cpu) @@ -92,12 +107,14 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) u64 generation = atomic64_read(&asid_generation); if (asid != 0) { + u64 newasid = generation | (asid & ~ASID_MASK); + /* * If our current ASID was active during a rollover, we * can continue to use it and this was just a false alarm. */ - if (is_reserved_asid(asid)) - return generation | (asid & ~ASID_MASK); + if (check_update_reserved_asid(asid, newasid)) + return newasid; /* * We had a valid ASID in a previous life, so try to re-use @@ -105,7 +122,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) */ asid &= ~ASID_MASK; if (!__test_and_set_bit(asid, asid_map)) - goto bump_gen; + return newasid; } /* @@ -129,10 +146,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) set_asid: __set_bit(asid, asid_map); cur_idx = asid; - -bump_gen: - asid |= generation; - return asid; + return asid | generation; } void check_and_switch_context(struct mm_struct *mm, unsigned int cpu) -- GitLab From 76ec6821bd1f90a316904ed189ba7bf7988025c1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 18 Apr 2016 17:09:44 +0200 Subject: [PATCH 2213/5498] UPSTREAM: arm64: introduce mov_q macro to move a constant into a 64-bit register Implement a macro mov_q that can be used to move an immediate constant into a 64-bit register, using between 2 and 4 movz/movk instructions (depending on the operand) Acked-by: Catalin Marinas Signed-off-by: Ard Biesheuvel Signed-off-by: Will Deacon (cherry picked from commit 30b5ba5cf333cc650e474eaf2cc1ae91bc7cf89f) Change-Id: I3f1b4ce25ced9bb678429d538272a85006ed0e16 Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/assembler.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index c66a176f4ee8..34381f380943 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -321,4 +321,24 @@ lr .req x30 // link register mrs \rd, sp_el0 .endm + /* + * mov_q - move an immediate constant into a 64-bit register using + * between 2 and 4 movz/movk instructions (depending on the + * magnitude and sign of the operand) + */ + .macro mov_q, reg, val + .if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff) + movz \reg, :abs_g1_s:\val + .else + .if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff) + movz \reg, :abs_g2_s:\val + .else + movz \reg, :abs_g3:\val + movk \reg, :abs_g2_nc:\val + .endif + movk \reg, :abs_g1_nc:\val + .endif + movk \reg, :abs_g0_nc:\val + .endm + #endif /* __ASM_ASSEMBLER_H */ -- GitLab From e906a21034395aaee1c38cafed3bd44ad76cdb5d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 9 Nov 2015 09:55:46 +0100 Subject: [PATCH 2214/5498] BACKPORT: arm64: fix R/O permissions of FDT mapping The mapping permissions of the FDT are set to 'PAGE_KERNEL | PTE_RDONLY' in an attempt to map the FDT as read-only. However, not only does this break at build time under STRICT_MM_TYPECHECKS (since the two terms are of different types in that case), it also results in both the PTE_WRITE and PTE_RDONLY attributes to be set, which means the region is still writable under ARMv8.1 DBM (and an attempted write will simply clear the PT_RDONLY bit). So instead, define PAGE_KERNEL_RO (which already has an established meaning across architectures) and use that instead. Signed-off-by: Ard Biesheuvel Signed-off-by: Catalin Marinas (cherry picked from commit fb226c3d7c77b4f99cee675795cc0e70937c56ee) Change-Id: I3ab7382c7e5539d93b21dd262e0f7f128330722a [ghackmann@google.com: only take the PAGE_KERNEL_RO definition, since the FDT mapping code in question doesn't exist in 3.18] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/pgtable.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 7fbc40f7a4d0..09205291f007 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -70,6 +70,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #define _PAGE_DEFAULT (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL)) #define PAGE_KERNEL __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE) +#define PAGE_KERNEL_RO __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_RDONLY) #define PAGE_KERNEL_EXEC __pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_WRITE) #define PAGE_HYP __pgprot(_PAGE_DEFAULT | PTE_HYP) -- GitLab From b92b29768a633895b09f1df0cfd11ea4cdcc8343 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 12:56:18 +0100 Subject: [PATCH 2215/5498] FROMLIST: arm64: mm: Use non-global mappings for kernel space In preparation for unmapping the kernel whilst running in userspace, make the kernel mappings non-global so we can avoid expensive TLB invalidation on kernel exit to userspace. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit e046eb0c9bf26d94be9e4592c00c7a78b0fa9bfd) Change-Id: If53d6db042f8fefff3ecf8a7658291e1f1ac659f [ghackmann@google.com: apply pgtable-prot.h changes to pgtable.h instead] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/kernel-pgtable.h | 12 ++++++++++-- arch/arm64/include/asm/pgtable.h | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h index d06d7f06a583..865887934504 100644 --- a/arch/arm64/include/asm/kernel-pgtable.h +++ b/arch/arm64/include/asm/kernel-pgtable.h @@ -61,8 +61,16 @@ /* * Initial memory map attributes. */ -#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) -#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) +#define _SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) +#define _SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) + +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define SWAPPER_PTE_FLAGS (_SWAPPER_PTE_FLAGS | PTE_NG) +#define SWAPPER_PMD_FLAGS (_SWAPPER_PMD_FLAGS | PMD_SECT_NG) +#else +#define SWAPPER_PTE_FLAGS _SWAPPER_PTE_FLAGS +#define SWAPPER_PMD_FLAGS _SWAPPER_PMD_FLAGS +#endif #ifdef CONFIG_ARM64_64K_PAGES #define SWAPPER_MM_MMUFLAGS (PTE_ATTRINDX(MT_NORMAL) | SWAPPER_PTE_FLAGS) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 09205291f007..78cbc4db7224 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -56,8 +56,16 @@ extern void __pmd_error(const char *file, int line, unsigned long val); extern void __pud_error(const char *file, int line, unsigned long val); extern void __pgd_error(const char *file, int line, unsigned long val); -#define PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) -#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) +#define _PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) +#define _PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) + +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define PROT_DEFAULT (_PROT_DEFAULT | PTE_NG) +#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_SECT_NG) +#else +#define PROT_DEFAULT _PROT_DEFAULT +#define PROT_SECT_DEFAULT _PROT_SECT_DEFAULT +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ #define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE)) #define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL_NC)) @@ -68,18 +76,19 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #define PROT_SECT_NORMAL_EXEC (PROT_SECT_DEFAULT | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL)) #define _PAGE_DEFAULT (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL)) +#define _HYP_PAGE_DEFAULT (_PAGE_DEFAULT & ~PTE_NG) #define PAGE_KERNEL __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE) #define PAGE_KERNEL_RO __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_RDONLY) #define PAGE_KERNEL_EXEC __pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_WRITE) -#define PAGE_HYP __pgprot(_PAGE_DEFAULT | PTE_HYP) +#define PAGE_HYP __pgprot(_HYP_PAGE_DEFAULT | PTE_HYP) #define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP) #define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY) #define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN) -#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_PXN | PTE_UXN) +#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_NG | PTE_PXN | PTE_UXN) #define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE) #define PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE) #define PAGE_COPY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) -- GitLab From 735e1554285ab9fb564a96642849571f9c7e85b7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 13:04:48 +0100 Subject: [PATCH 2216/5498] FROMLIST: arm64: mm: Temporarily disable ARM64_SW_TTBR0_PAN We're about to rework the way ASIDs are allocated, switch_mm is implemented and low-level kernel entry/exit is handled, so keep the ARM64_SW_TTBR0_PAN code out of the way whilst we do the heavy lifting. It will be re-enabled in a subsequent patch. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 376133b7edc20f237a42e4c72415cc9e8c0a9704) Change-Id: I38d3f7a66b1d52abcea3e23b1e80277b03c6dbe0 Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 3a2ce0de004c..46379f00bdba 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -597,6 +597,7 @@ endif config ARM64_SW_TTBR0_PAN bool "Emulate Privileged Access Never using TTBR0_EL1 switching" + depends on BROKEN # Temporary while switch_mm is reworked help Enabling this option prevents the kernel from accessing user-space memory directly by pointing TTBR0_EL1 to a reserved -- GitLab From 6de449786ee41fb95db207ef68cf865ca8929218 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 13:19:09 +0100 Subject: [PATCH 2217/5498] FROMLIST: arm64: mm: Move ASID from TTBR0 to TTBR1 In preparation for mapping kernelspace and userspace with different ASIDs, move the ASID to TTBR1 and update switch_mm to context-switch TTBR0 via an invalid mapping (the zero page). Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 7655abb953860485940d4de74fb45a8192149bb6) Change-Id: Id8a18e16dfab5c8b7bc31174b14100142a6af3b0 [ghackmann@google.com: adjust context] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/mmu_context.h | 7 +++++++ arch/arm64/include/asm/pgtable-hwdef.h | 2 ++ arch/arm64/include/asm/proc-fns.h | 6 ------ arch/arm64/mm/proc.S | 9 ++++++--- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index b61e47aea3a1..e1ad8da4dde7 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -58,6 +58,13 @@ static inline void cpu_set_reserved_ttbr0(void) : "r" (ttbr)); } +static inline void cpu_switch_mm(pgd_t *pgd, struct mm_struct *mm) +{ + BUG_ON(pgd == swapper_pg_dir); + cpu_set_reserved_ttbr0(); + cpu_do_switch_mm(virt_to_phys(pgd),mm); +} + /* * TCR.T0SZ value to use when the ID map is active. Usually equals * TCR_T0SZ(VA_BITS), unless system RAM is positioned very high in diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h index 5e50782f625d..bfe379d13b83 100644 --- a/arch/arm64/include/asm/pgtable-hwdef.h +++ b/arch/arm64/include/asm/pgtable-hwdef.h @@ -164,6 +164,8 @@ #define TCR_TG1_16K (UL(1) << 30) #define TCR_TG1_4K (UL(2) << 30) #define TCR_TG1_64K (UL(3) << 30) + +#define TCR_A1 (UL(1) << 22) #define TCR_ASID16 (UL(1) << 36) #define TCR_TBI0 (UL(1) << 37) diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h index b5240e868784..d35dcebb121e 100644 --- a/arch/arm64/include/asm/proc-fns.h +++ b/arch/arm64/include/asm/proc-fns.h @@ -35,12 +35,6 @@ extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr); #include -#define cpu_switch_mm(pgd,mm) \ -do { \ - BUG_ON(pgd == swapper_pg_dir); \ - cpu_do_switch_mm(virt_to_phys(pgd),mm); \ -} while (0) - #define cpu_get_pgd() \ ({ \ unsigned long pg; \ diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index ce2c71a52d05..31c0d8cc2613 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -127,9 +127,12 @@ ENDPROC(cpu_do_resume) * - pgd_phys - physical address of new TTB */ ENTRY(cpu_do_switch_mm) + mrs x2, ttbr1_el1 mmid x1, x1 // get mm->context.id - bfi x0, x1, #48, #16 // set the ASID - msr ttbr0_el1, x0 // set TTBR0 + bfi x2, x1, #48, #16 // set the ASID + msr ttbr1_el1, x2 // in TTBR1 (since TCR.A1 is set) + isb + msr ttbr0_el1, x0 // now update TTBR0 isb ret ENDPROC(cpu_do_switch_mm) @@ -182,7 +185,7 @@ ENTRY(__cpu_setup) * both user and kernel. */ ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \ - TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 + TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 | TCR_A1 tcr_set_idmap_t0sz x10, x9 /* -- GitLab From 4ea2126b109f510b78d03b051980634b16451c2c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 13:58:16 +0100 Subject: [PATCH 2218/5498] FROMLIST: arm64: mm: Fix and re-enable ARM64_SW_TTBR0_PAN With the ASID now installed in TTBR1, we can re-enable ARM64_SW_TTBR0_PAN by ensuring that we switch to a reserved ASID of zero when disabling user access and restore the active user ASID on the uaccess enable path. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 27a921e75711d924617269e0ba4adb8bae9fd0d1) Change-Id: I3b06e02766753c59fac975363a2ead5c5e45b8f3 [ghackmann@google.com: adjust context, applying asm-uaccess.h changes to uaccess.h] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/Kconfig | 1 - arch/arm64/include/asm/uaccess.h | 46 +++++++++++++++++++++++--------- arch/arm64/kernel/entry.S | 4 +-- arch/arm64/lib/clear_user.S | 2 +- arch/arm64/lib/copy_from_user.S | 2 +- arch/arm64/lib/copy_in_user.S | 2 +- arch/arm64/lib/copy_to_user.S | 2 +- arch/arm64/mm/cache.S | 2 +- arch/arm64/xen/hypercall.S | 2 +- 9 files changed, 42 insertions(+), 21 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 46379f00bdba..3a2ce0de004c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -597,7 +597,6 @@ endif config ARM64_SW_TTBR0_PAN bool "Emulate Privileged Access Never using TTBR0_EL1 switching" - depends on BROKEN # Temporary while switch_mm is reworked help Enabling this option prevents the kernel from accessing user-space memory directly by pointing TTBR0_EL1 to a reserved diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 21963026efd0..cfef719287d9 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -136,15 +136,19 @@ static inline void __uaccess_ttbr0_disable(void) { unsigned long ttbr; + ttbr = read_sysreg(ttbr1_el1); /* reserved_ttbr0 placed at the end of swapper_pg_dir */ - ttbr = read_sysreg(ttbr1_el1) + SWAPPER_DIR_SIZE; - write_sysreg(ttbr, ttbr0_el1); + write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); + isb(); + /* Set reserved ASID */ + ttbr &= ~(0xffffUL << 48); + write_sysreg(ttbr, ttbr1_el1); isb(); } static inline void __uaccess_ttbr0_enable(void) { - unsigned long flags; + unsigned long flags, ttbr0, ttbr1; /* * Disable interrupts to avoid preemption between reading the 'ttbr0' @@ -152,7 +156,16 @@ static inline void __uaccess_ttbr0_enable(void) * roll-over and an update of 'ttbr0'. */ local_irq_save(flags); - write_sysreg(current_thread_info()->ttbr0, ttbr0_el1); + ttbr0 = current_thread_info()->ttbr0; + + /* Restore active ASID */ + ttbr1 = read_sysreg(ttbr1_el1); + ttbr1 |= ttbr0 & (0xffffUL << 48); + write_sysreg(ttbr1, ttbr1_el1); + isb(); + + /* Restore user page table */ + write_sysreg(ttbr0, ttbr0_el1); isb(); local_irq_restore(flags); } @@ -440,11 +453,20 @@ extern __must_check long strnlen_user(const char __user *str, long n); add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb + sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE + bic \tmp1, \tmp1, #(0xffff << 48) + msr ttbr1_el1, \tmp1 // set reserved ASID + isb .endm - .macro __uaccess_ttbr0_enable, tmp1 + .macro __uaccess_ttbr0_enable, tmp1, tmp2 get_thread_info \tmp1 ldr \tmp1, [\tmp1, #TSK_TI_TTBR0] // load saved TTBR0_EL1 + mrs \tmp2, ttbr1_el1 + extr \tmp2, \tmp2, \tmp1, #48 + ror \tmp2, \tmp2, #16 + msr ttbr1_el1, \tmp2 // set the active ASID + isb msr ttbr0_el1, \tmp1 // set the non-PAN TTBR0_EL1 isb .endm @@ -455,18 +477,18 @@ alternative_if_not ARM64_HAS_PAN alternative_else_nop_endif .endm - .macro uaccess_ttbr0_enable, tmp1, tmp2 + .macro uaccess_ttbr0_enable, tmp1, tmp2, tmp3 alternative_if_not ARM64_HAS_PAN - save_and_disable_irq \tmp2 // avoid preemption - __uaccess_ttbr0_enable \tmp1 - restore_irq \tmp2 + save_and_disable_irq \tmp3 // avoid preemption + __uaccess_ttbr0_enable \tmp1, \tmp2 + restore_irq \tmp3 alternative_else_nop_endif .endm #else .macro uaccess_ttbr0_disable, tmp1 .endm - .macro uaccess_ttbr0_enable, tmp1, tmp2 + .macro uaccess_ttbr0_enable, tmp1, tmp2, tmp3 .endm #endif @@ -480,8 +502,8 @@ alternative_if ARM64_ALT_PAN_NOT_UAO alternative_else_nop_endif .endm - .macro uaccess_enable_not_uao, tmp1, tmp2 - uaccess_ttbr0_enable \tmp1, \tmp2 + .macro uaccess_enable_not_uao, tmp1, tmp2, tmp3 + uaccess_ttbr0_enable \tmp1, \tmp2, \tmp3 alternative_if ARM64_ALT_PAN_NOT_UAO SET_PSTATE_PAN(0) alternative_else_nop_endif diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index b3e65d463857..77fd1976e364 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -129,7 +129,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif .if \el != 0 - mrs x21, ttbr0_el1 + mrs x21, ttbr1_el1 tst x21, #0xffff << 48 // Check for the reserved ASID orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR b.eq 1f // TTBR0 access already disabled @@ -193,7 +193,7 @@ alternative_else_nop_endif tbnz x22, #22, 1f // Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set .endif - __uaccess_ttbr0_enable x0 + __uaccess_ttbr0_enable x0, x1 1: .if \el != 0 and x22, x22, #~PSR_PAN_BIT // ARMv8.0 CPUs do not understand this bit diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S index d7150e30438a..dd65ca253eb4 100644 --- a/arch/arm64/lib/clear_user.S +++ b/arch/arm64/lib/clear_user.S @@ -30,7 +30,7 @@ * Alignment fixed up by hardware. */ ENTRY(__clear_user) - uaccess_enable_not_uao x2, x3 + uaccess_enable_not_uao x2, x3, x4 mov x2, x1 // save the size for fixup return subs x1, x1, #8 b.mi 2f diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 90154f3f7f2a..1ff23f81e242 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -64,7 +64,7 @@ end .req x5 ENTRY(__arch_copy_from_user) - uaccess_enable_not_uao x3, x4 + uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" uaccess_disable_not_uao x3 diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S index 718b1c4e2f85..074d52fcd75b 100644 --- a/arch/arm64/lib/copy_in_user.S +++ b/arch/arm64/lib/copy_in_user.S @@ -65,7 +65,7 @@ end .req x5 ENTRY(__copy_in_user) - uaccess_enable_not_uao x3, x4 + uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" uaccess_disable_not_uao x3 diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S index e99e31c9acac..67118444cde0 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -63,7 +63,7 @@ end .req x5 ENTRY(__arch_copy_to_user) - uaccess_enable_not_uao x3, x4 + uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" uaccess_disable_not_uao x3 diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 1c10c80ff2d1..0344dc389925 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -48,7 +48,7 @@ ENTRY(flush_icache_range) * - end - virtual end address of region */ ENTRY(__flush_cache_user_range) - uaccess_ttbr0_enable x2, x3 + uaccess_ttbr0_enable x2, x3, x4 dcache_line_size x2, x3 sub x3, x2, #1 bic x4, x0, x3 diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index b96db5dafec4..27b38711023b 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -98,7 +98,7 @@ ENTRY(privcmd_call) * need the explicit uaccess_enable/disable if the TTBR0 PAN emulation * is enabled (it implies that hardware UAO and PAN disabled). */ - uaccess_ttbr0_enable x6, x7 + uaccess_ttbr0_enable x6, x7, x8 hvc XEN_IMM /* -- GitLab From b7032349724a4bd7da6e6426f8742db8fc6095aa Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 14:10:28 +0100 Subject: [PATCH 2219/5498] FROMLIST: arm64: mm: Allocate ASIDs in pairs In preparation for separate kernel/user ASIDs, allocate them in pairs for each mm_struct. The bottom bit distinguishes the two: if it is set, then the ASID will map only userspace. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 0c8ea531b7740754cf374ca8b7510655f569c5e3) Change-Id: I283c99292b165e04ff1b6b9cb5806805974ae915 [ghackmann@google.com: adjust context] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/mmu.h | 2 ++ arch/arm64/mm/context.c | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 77c3851deae1..05dadefe8720 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -16,6 +16,8 @@ #ifndef __ASM_MMU_H #define __ASM_MMU_H +#define USER_ASID_FLAG (UL(1) << 48) + typedef struct { atomic64_t id; void *vdso; diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 25128089c386..10d68e438a37 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -38,7 +38,16 @@ static cpumask_t tlb_flush_pending; #define ASID_MASK (~GENMASK(asid_bits - 1, 0)) #define ASID_FIRST_VERSION (1UL << asid_bits) -#define NUM_USER_ASIDS ASID_FIRST_VERSION + +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define NUM_USER_ASIDS (ASID_FIRST_VERSION >> 1) +#define asid2idx(asid) (((asid) & ~ASID_MASK) >> 1) +#define idx2asid(idx) (((idx) << 1) & ~ASID_MASK) +#else +#define NUM_USER_ASIDS (ASID_FIRST_VERSION) +#define asid2idx(asid) ((asid) & ~ASID_MASK) +#define idx2asid(idx) asid2idx(idx) +#endif static void flush_context(unsigned int cpu) { @@ -65,7 +74,7 @@ static void flush_context(unsigned int cpu) */ if (asid == 0) asid = per_cpu(reserved_asids, i); - __set_bit(asid & ~ASID_MASK, asid_map); + __set_bit(asid2idx(asid), asid_map); per_cpu(reserved_asids, i) = asid; } @@ -120,16 +129,16 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) * We had a valid ASID in a previous life, so try to re-use * it if possible. */ - asid &= ~ASID_MASK; - if (!__test_and_set_bit(asid, asid_map)) + if (!__test_and_set_bit(asid2idx(asid), asid_map)) return newasid; } /* * Allocate a free ASID. If we can't find one, take a note of the - * currently active ASIDs and mark the TLBs as requiring flushes. - * We always count from ASID #1, as we use ASID #0 when setting a - * reserved TTBR0 for the init_mm. + * currently active ASIDs and mark the TLBs as requiring flushes. We + * always count from ASID #2 (index 1), as we use ASID #0 when setting + * a reserved TTBR0 for the init_mm and we allocate ASIDs in even/odd + * pairs. */ asid = find_next_zero_bit(asid_map, NUM_USER_ASIDS, cur_idx); if (asid != NUM_USER_ASIDS) @@ -146,7 +155,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) set_asid: __set_bit(asid, asid_map); cur_idx = asid; - return asid | generation; + return idx2asid(asid) | generation; } void check_and_switch_context(struct mm_struct *mm, unsigned int cpu) -- GitLab From 1434f49fc0dae1dc85f22f92d21e189422370185 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 13:58:08 +0000 Subject: [PATCH 2220/5498] FROMLIST: arm64: mm: Add arm64_kernel_unmapped_at_el0 helper In order for code such as TLB invalidation to operate efficiently when the decision to map the kernel at EL0 is determined at runtime, this patch introduces a helper function, arm64_kernel_unmapped_at_el0, to determine whether or not the kernel is mapped whilst running in userspace. Currently, this just reports the value of CONFIG_UNMAP_KERNEL_AT_EL0, but will later be hooked up to a fake CPU capability using a static key. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit fc0e1299da548b32440051f58f08e0c1eb7edd0b) Change-Id: I0f48eadf55ee97f09553380a62d9fffe54d9dc83 Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/mmu.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 05dadefe8720..25109120a81f 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -18,6 +18,8 @@ #define USER_ASID_FLAG (UL(1) << 48) +#ifndef __ASSEMBLY__ + typedef struct { atomic64_t id; void *vdso; @@ -30,6 +32,11 @@ typedef struct { */ #define ASID(mm) ((mm)->context.id.counter & 0xffff) +static inline bool arm64_kernel_unmapped_at_el0(void) +{ + return IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0); +} + extern void paging_init(void); extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); extern void init_mem_pgprot(void); @@ -37,4 +44,5 @@ extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot); +#endif /* !__ASSEMBLY__ */ #endif -- GitLab From eed2e002366647f8aefb9dc938a402294e16396d Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 14:13:33 +0100 Subject: [PATCH 2221/5498] FROMLIST: arm64: mm: Invalidate both kernel and user ASIDs when performing TLBI Since an mm has both a kernel and a user ASID, we need to ensure that broadcast TLB maintenance targets both address spaces so that things like CoW continue to work with the uaccess primitives in the kernel. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 9b0de864b5bc298ea53005ad812f3386f81aee9c) Change-Id: I2369f242a6461795349568cc68ae6324244e6709 Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/tlbflush.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index deab52374119..ad6bd8b26ada 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -23,6 +23,7 @@ #include #include +#include /* * Raw TLBI operations. @@ -42,6 +43,11 @@ #define __tlbi(op, ...) __TLBI_N(op, ##__VA_ARGS__, 1, 0) +#define __tlbi_user(op, arg) do { \ + if (arm64_kernel_unmapped_at_el0()) \ + __tlbi(op, (arg) | USER_ASID_FLAG); \ +} while (0) + /* * TLB Management * ============== @@ -103,6 +109,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) dsb(ishst); __tlbi(aside1is, asid); + __tlbi_user(aside1is, asid); dsb(ish); } @@ -113,6 +120,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, dsb(ishst); __tlbi(vale1is, addr); + __tlbi_user(vale1is, addr); dsb(ish); } @@ -139,10 +147,13 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) { - if (last_level) + if (last_level) { __tlbi(vale1is, addr); - else + __tlbi_user(vale1is, addr); + } else { __tlbi(vae1is, addr); + __tlbi_user(vae1is, addr); + } } dsb(ish); } @@ -182,6 +193,7 @@ static inline void __flush_tlb_pgtable(struct mm_struct *mm, unsigned long addr = uaddr >> 12 | (ASID(mm) << 48); __tlbi(vae1is, addr); + __tlbi_user(vae1is, addr); dsb(ish); } -- GitLab From 6415b1f83e554dc76816cbb76ed20e4e73633f33 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:07:40 +0000 Subject: [PATCH 2222/5498] FROMLIST: arm64: entry: Add exception trampoline page for exceptions from EL0 To allow unmapping of the kernel whilst running at EL0, we need to point the exception vectors at an entry trampoline that can map/unmap the kernel on entry/exit respectively. This patch adds the trampoline page, although it is not yet plugged into the vector table and is therefore unused. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit c7b9adaf85f818d747eeff5145eb4095ccd587fb) Change-Id: Idd27ab26f1ec1db2ff756fc33ebb782201806f7c [ghackmann@google.com: adjust context] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry.S | 85 +++++++++++++++++++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 17 +++++++ 2 files changed, 102 insertions(+) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 77fd1976e364..48b21da43823 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -810,6 +811,90 @@ __ni_sys_trace: bl do_ni_syscall b __sys_trace_return +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +/* + * Exception vectors trampoline. + */ + .pushsection ".entry.tramp.text", "ax" + + .macro tramp_map_kernel, tmp + mrs \tmp, ttbr1_el1 + sub \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) + bic \tmp, \tmp, #USER_ASID_FLAG + msr ttbr1_el1, \tmp + .endm + + .macro tramp_unmap_kernel, tmp + mrs \tmp, ttbr1_el1 + add \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) + orr \tmp, \tmp, #USER_ASID_FLAG + msr ttbr1_el1, \tmp + /* + * We avoid running the post_ttbr_update_workaround here because the + * user and kernel ASIDs don't have conflicting mappings, so any + * "blessing" as described in: + * + * http://lkml.kernel.org/r/56BB848A.6060603@caviumnetworks.com + * + * will not hurt correctness. Whilst this may partially defeat the + * point of using split ASIDs in the first place, it avoids + * the hit of invalidating the entire I-cache on every return to + * userspace. + */ + .endm + + .macro tramp_ventry, regsize = 64 + .align 7 +1: + .if \regsize == 64 + msr tpidrro_el0, x30 // Restored in kernel_ventry + .endif + tramp_map_kernel x30 + ldr x30, =vectors + prfm plil1strm, [x30, #(1b - tramp_vectors)] + msr vbar_el1, x30 + add x30, x30, #(1b - tramp_vectors) + isb + br x30 + .endm + + .macro tramp_exit, regsize = 64 + adr x30, tramp_vectors + msr vbar_el1, x30 + tramp_unmap_kernel x30 + .if \regsize == 64 + mrs x30, far_el1 + .endif + eret + .endm + + .align 11 +ENTRY(tramp_vectors) + .space 0x400 + + tramp_ventry + tramp_ventry + tramp_ventry + tramp_ventry + + tramp_ventry 32 + tramp_ventry 32 + tramp_ventry 32 + tramp_ventry 32 +END(tramp_vectors) + +ENTRY(tramp_exit_native) + tramp_exit +END(tramp_exit_native) + +ENTRY(tramp_exit_compat) + tramp_exit 32 +END(tramp_exit_compat) + + .ltorg + .popsection // .entry.tramp.text +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ + /* * Special system call wrappers. */ diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 724ed30e4992..aef78912a6b6 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -34,6 +34,17 @@ jiffies = jiffies_64; *(.hyp.text) \ VMLINUX_SYMBOL(__hyp_text_end) = .; +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define TRAMP_TEXT \ + . = ALIGN(PAGE_SIZE); \ + VMLINUX_SYMBOL(__entry_tramp_text_start) = .; \ + *(.entry.tramp.text) \ + . = ALIGN(PAGE_SIZE); \ + VMLINUX_SYMBOL(__entry_tramp_text_end) = .; +#else +#define TRAMP_TEXT +#endif + /* * The size of the PE/COFF section that covers the kernel image, which * runs from stext to _edata, must be a round multiple of the PE/COFF @@ -91,6 +102,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT HYPERVISOR_TEXT + TRAMP_TEXT *(.fixup) *(.gnu.warning) . = ALIGN(16); @@ -161,6 +173,11 @@ SECTIONS . += RESERVED_TTBR0_SIZE; #endif +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + tramp_pg_dir = .; + . += PAGE_SIZE; +#endif + _end = .; STABS_DEBUG -- GitLab From 771dd6d68c7b40528926d01fb1d6a8a47416e83c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:14:17 +0000 Subject: [PATCH 2223/5498] FROMLIST: arm64: mm: Map entry trampoline into trampoline and kernel page tables The exception entry trampoline needs to be mapped at the same virtual address in both the trampoline page table (which maps nothing else) and also the kernel page table, so that we can swizzle TTBR1_EL1 on exceptions from and return to EL0. This patch maps the trampoline at a fixed virtual address in the fixmap area of the kernel virtual address space, which allows the kernel proper to be randomized with respect to the trampoline when KASLR is enabled. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 51a0048beb449682d632d0af52a515adb9f9882e) Change-Id: I31b2dcdf4db36c3e31181fe43ccb984f9efb6ac6 [ghackmann@google.com: - adjust context - tweak __create_pgd_mapping() call to match 3.18 APIs - expand definition of PGD_SIZE, which is private to pgd.c in 3.18] - open-code pgd_pgtable_alloc(), based on version in 4.9] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/fixmap.h | 4 ++++ arch/arm64/include/asm/pgtable.h | 1 + arch/arm64/kernel/asm-offsets.c | 5 +++++ arch/arm64/mm/mmu.c | 37 ++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h index defa0ff98250..cc9b9a6a238c 100644 --- a/arch/arm64/include/asm/fixmap.h +++ b/arch/arm64/include/asm/fixmap.h @@ -33,6 +33,10 @@ enum fixed_addresses { FIX_HOLE, FIX_EARLYCON_MEM_BASE, +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + FIX_ENTRY_TRAMP_TEXT, +#define TRAMP_VALIAS (__fix_to_virt(FIX_ENTRY_TRAMP_TEXT)) +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ __end_of_permanent_fixed_addresses, /* diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 78cbc4db7224..26d143064dff 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -479,6 +479,7 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; extern pgd_t idmap_pg_dir[PTRS_PER_PGD]; +extern pgd_t tramp_pg_dir[PTRS_PER_PGD]; /* * Encode and decode a swap entry: diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 92a5d25bff1a..a865573461e1 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,10 @@ int main(void) DEFINE(SLEEP_SAVE_SP_SZ, sizeof(struct sleep_save_sp)); DEFINE(SLEEP_SAVE_SP_PHYS, offsetof(struct sleep_save_sp, save_ptr_stash_phys)); DEFINE(SLEEP_SAVE_SP_VIRT, offsetof(struct sleep_save_sp, save_ptr_stash)); +#endif + BLANK(); +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + DEFINE(TRAMP_VALIAS, TRAMP_VALIAS); #endif return 0; } diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 6147b780f805..c81313791fc6 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -428,6 +428,43 @@ void fixup_init(void) PAGE_KERNEL); } +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +static void __init *pgd_pgtable_alloc(unsigned long size) +{ + void *ptr; + BUG_ON(size != PAGE_SIZE); + + ptr = (void *)__get_free_page(PGALLOC_GFP); + if (!ptr || !pgtable_page_ctor(virt_to_page(ptr))) + BUG(); + + /* Ensure the zeroed page is visible to the page table walker */ + dsb(ishst); + return ptr; +} + +static int __init map_entry_trampoline(void) +{ + extern char __entry_tramp_text_start[]; + + pgprot_t prot = PAGE_KERNEL_EXEC; + phys_addr_t pa_start = __pa_symbol(__entry_tramp_text_start); + + /* The trampoline is always mapped and can therefore be global */ + pgprot_val(prot) &= ~PTE_NG; + + /* Map only the text into the trampoline page table */ + memset(tramp_pg_dir, 0, PTRS_PER_PGD * sizeof(pgd_t)); + __create_mapping(NULL, tramp_pg_dir + pgd_index(TRAMP_VALIAS), pa_start, + TRAMP_VALIAS, PAGE_SIZE, prot, pgd_pgtable_alloc); + + /* ...as well as the kernel page table */ + __set_fixmap(FIX_ENTRY_TRAMP_TEXT, pa_start, prot); + return 0; +} +core_initcall(map_entry_trampoline); +#endif + /* * paging_init() sets up the page tables, initialises the zone memory * maps and sets up the zero page. -- GitLab From eb8401717f414fba0530373960a02fb1c2fad54f Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:20:21 +0000 Subject: [PATCH 2224/5498] FROMLIST: arm64: entry: Explicitly pass exception level to kernel_ventry macro We will need to treat exceptions from EL0 differently in kernel_ventry, so rework the macro to take the exception level as an argument and construct the branch target using that. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 5b1f7fe41909cde40decad9f0e8ee585777a0538) Change-Id: Iab10d2237e24c008d05856a4bd953504de6e10a8 [ghackmann@google.com: adjust context and kernel entry point names] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry.S | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 48b21da43823..aa70731b7ada 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -70,10 +70,10 @@ #define BAD_FIQ 2 #define BAD_ERROR 3 - .macro kernel_ventry label + .macro kernel_ventry, el, label, regsize = 64 .align 7 sub sp, sp, #S_FRAME_SIZE - b \label + b el\()\el\()_\label .endm .macro kernel_entry, el, regsize = 64 @@ -276,31 +276,31 @@ tsk .req x28 // current thread_info .align 11 ENTRY(vectors) - kernel_ventry el1_sync_invalid // Synchronous EL1t - kernel_ventry el1_irq_invalid // IRQ EL1t - kernel_ventry el1_fiq_invalid // FIQ EL1t - kernel_ventry el1_error_invalid // Error EL1t + kernel_ventry 1, sync_invalid // Synchronous EL1t + kernel_ventry 1, irq_invalid // IRQ EL1t + kernel_ventry 1, fiq_invalid // FIQ EL1t + kernel_ventry 1, error_invalid // Error EL1t - kernel_ventry el1_sync // Synchronous EL1h - kernel_ventry el1_irq // IRQ EL1h - kernel_ventry el1_fiq_invalid // FIQ EL1h - kernel_ventry el1_error_invalid // Error EL1h + kernel_ventry 1, sync // Synchronous EL1h + kernel_ventry 1, irq // IRQ EL1h + kernel_ventry 1, fiq_invalid // FIQ EL1h + kernel_ventry 1, error_invalid // Error EL1h - kernel_ventry el0_sync // Synchronous 64-bit EL0 - kernel_ventry el0_irq // IRQ 64-bit EL0 - kernel_ventry el0_fiq_invalid // FIQ 64-bit EL0 - kernel_ventry el0_error_invalid // Error 64-bit EL0 + kernel_ventry 0, sync // Synchronous 64-bit EL0 + kernel_ventry 0, irq // IRQ 64-bit EL0 + kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0 + kernel_ventry 0, error_invalid // Error 64-bit EL0 #ifdef CONFIG_COMPAT - kernel_ventry el0_sync_compat // Synchronous 32-bit EL0 - kernel_ventry el0_irq_compat // IRQ 32-bit EL0 - kernel_ventry el0_fiq_invalid_compat // FIQ 32-bit EL0 - kernel_ventry el0_error_invalid_compat // Error 32-bit EL0 + kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0 + kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0 + kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0 + kernel_ventry 0, error_invalid_compat, 32 // Error 32-bit EL0 #else - kernel_ventry el0_sync_invalid // Synchronous 32-bit EL0 - kernel_ventry el0_irq_invalid // IRQ 32-bit EL0 - kernel_ventry el0_fiq_invalid // FIQ 32-bit EL0 - kernel_ventry el0_error_invalid // Error 32-bit EL0 + kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0 + kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0 + kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0 + kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0 #endif END(vectors) -- GitLab From 5ed26716ffb768970fe8ffbd68b050ce10a62431 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:24:29 +0000 Subject: [PATCH 2225/5498] FROMLIST: arm64: entry: Hook up entry trampoline to exception vectors Hook up the entry trampoline to our exception vectors so that all exceptions from and returns to EL0 go via the trampoline, which swizzles the vector base register accordingly. Transitioning to and from the kernel clobbers x30, so we use tpidrro_el0 and far_el1 as scratch registers for native tasks. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 4bf3286d29f3a88425d8d8cd53428cbb8f865f04) Change-Id: Id1e175bdaa0ec2bf8e59f941502183907902a710 [ghackmann@google.com: adjust context, replacing alternative_if_not ARM64_WORKAROUND_845719 block with upstream version] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry.S | 49 ++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index aa70731b7ada..a2a9244e958c 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -72,10 +72,26 @@ .macro kernel_ventry, el, label, regsize = 64 .align 7 +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + .if \el == 0 + .if \regsize == 64 + mrs x30, tpidrro_el0 + msr tpidrro_el0, xzr + .else + mov x30, xzr + .endif + .endif +#endif + sub sp, sp, #S_FRAME_SIZE b el\()\el\()_\label .endm + .macro tramp_alias, dst, sym + mov_q \dst, TRAMP_VALIAS + add \dst, \dst, #(\sym - .entry.tramp.text) + .endm + .macro kernel_entry, el, regsize = 64 .if \regsize == 32 mov w0, w0 // zero upper 32 bits of x0 @@ -205,24 +221,20 @@ alternative_else_nop_endif .if \el == 0 ldr x23, [sp, #S_SP] // load return stack pointer msr sp_el0, x23 + tst x22, #PSR_MODE32_BIT // native task? + b.eq 3f + #ifdef CONFIG_ARM64_ERRATUM_845719 -alternative_if_not ARM64_WORKAROUND_845719 - nop - nop -#ifdef CONFIG_PID_IN_CONTEXTIDR - nop -#endif -alternative_else - tbz x22, #4, 1f +alternative_if ARM64_WORKAROUND_845719 #ifdef CONFIG_PID_IN_CONTEXTIDR mrs x29, contextidr_el1 msr contextidr_el1, x29 #else msr contextidr_el1, xzr #endif -1: -alternative_endif +alternative_else_nop_endif #endif +3: .endif msr elr_el1, x21 // set up the return data @@ -244,7 +256,22 @@ alternative_endif ldp x28, x29, [sp, #16 * 14] ldr lr, [sp, #S_LR] add sp, sp, #S_FRAME_SIZE // restore sp - eret // return to kernel + +#ifndef CONFIG_UNMAP_KERNEL_AT_EL0 + eret +#else + .if \el == 0 + bne 4f + msr far_el1, x30 + tramp_alias x30, tramp_exit_native + br x30 +4: + tramp_alias x30, tramp_exit_compat + br x30 + .else + eret + .endif +#endif .endm /* -- GitLab From 1766a613ec0c59cb5f463364a20b74596c636768 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:29:19 +0000 Subject: [PATCH 2226/5498] FROMLIST: arm64: erratum: Work around Falkor erratum #E1003 in trampoline code We rely on an atomic swizzling of TTBR1 when transitioning from the entry trampoline to the kernel proper on an exception. We can't rely on this atomicity in the face of Falkor erratum #E1003, so on affected cores we can issue a TLB invalidation to invalidate the walk cache prior to jumping into the kernel. There is still the possibility of a TLB conflict here due to conflicting walk cache entries prior to the invalidation, but this doesn't appear to be the case on these CPUs in practice. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit d1777e686ad10ba7c594304429c6045fb79255a1) Change-Id: Ia6c7ffd47745c179738250afa01cb8bf8594b235 [ghackmann@google.com: replace runtime alternative_if with a compile-time check for Code Aurora's out-of-tree CONFIG_ARCH_MSM8996. Kryo needs this workaround too, and 4.4 doesn't have any of the upstream Falkor errata infrastructure needed to detect this at boot time.] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry.S | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index a2a9244e958c..17c58ae19ed3 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -849,6 +849,16 @@ __ni_sys_trace: sub \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) bic \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp +#ifdef CONFIG_ARCH_MSM8996 + /* ASID already in \tmp[63:48] */ + movk \tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12) + movk \tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12) + /* 2MB boundary containing the vectors, so we nobble the walk cache */ + movk \tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12) + isb + tlbi vae1, \tmp + dsb nsh +#endif /* CONFIG_ARCH_MSM8996 */ .endm .macro tramp_unmap_kernel, tmp -- GitLab From f47c91dcfa2accf4fd04a39398acbdc1237ca87e Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:33:28 +0000 Subject: [PATCH 2227/5498] FROMLIST: arm64: tls: Avoid unconditional zeroing of tpidrro_el0 for native tasks When unmapping the kernel at EL0, we use tpidrro_el0 as a scratch register during exception entry from native tasks and subsequently zero it in the kernel_ventry macro. We can therefore avoid zeroing tpidrro_el0 in the context-switch path for native tasks using the entry trampoline. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 18011eac28c7cb31c87b86b7d0e5b01894405c7f) Change-Id: Ief7b4099f055420a7a23c8dcf497269192f5fb58 [ghackmann@google.com: - adjust context - replace task_user_tls() accessor with equivalent field from 3.18] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/process.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 2357737bdb06..ba16b1dc9171 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -362,25 +362,18 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, static void tls_thread_switch(struct task_struct *next) { - unsigned long tpidr, tpidrro; - if (!is_compat_task()) { + unsigned long tpidr; asm("mrs %0, tpidr_el0" : "=r" (tpidr)); current->thread.tp_value = tpidr; } - if (is_compat_thread(task_thread_info(next))) { - tpidr = 0; - tpidrro = next->thread.tp_value; - } else { - tpidr = next->thread.tp_value; - tpidrro = 0; - } + if (is_compat_thread(task_thread_info(next))) + write_sysreg(next->thread.tp_value, tpidrro_el0); + else if (!arm64_kernel_unmapped_at_el0()) + write_sysreg(0, tpidrro_el0); - asm( - " msr tpidr_el0, %0\n" - " msr tpidrro_el0, %1" - : : "r" (tpidr), "r" (tpidrro)); + write_sysreg(next->thread.tp_value, tpidr_el0); } /* Restore the UAO state depending on next's addr_limit */ -- GitLab From 2b4c5c1fdab1aa2ef29514eb71920c8fe77aa62b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:38:19 +0000 Subject: [PATCH 2228/5498] FROMLIST: arm64: entry: Add fake CPU feature for unmapping the kernel at EL0 Allow explicit disabling of the entry trampoline on the kernel command line (kpti=off) by adding a fake CPU feature (ARM64_UNMAP_KERNEL_AT_EL0) that can be used to toggle the alternative sequences in our entry code and avoid use of the trampoline altogether if desired. This also allows us to make use of a static key in arm64_kernel_unmapped_at_el0(). Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit ea1e3de85e94d711f63437c04624aa0e8de5c8b3) Change-Id: I11cb874d12a7d0921f452c62b0752e0028a8e0a7 [ghackmann@google.com: - adjust context - apply cpucaps.h changes to cpufeature.h - replace cpus_have_const_cap() with cpus_have_cap(), and include the header that declares it - tweak unmap_kernel_at_el0() declaration to match 3.18 APIs] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/cpufeature.h | 3 ++- arch/arm64/include/asm/mmu.h | 5 +++- arch/arm64/kernel/cpufeature.c | 39 +++++++++++++++++++++++++++++ arch/arm64/kernel/entry.S | 9 ++++--- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index a0789bfc4ac6..ba6983e901d5 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -29,8 +29,9 @@ #define ARM64_HAS_PAN 4 #define ARM64_HAS_UAO 5 #define ARM64_ALT_PAN_NOT_UAO 6 +#define ARM64_UNMAP_KERNEL_AT_EL0 23 -#define ARM64_NCAPS 7 +#define ARM64_NCAPS 24 #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 25109120a81f..1ea12daa82d0 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -20,6 +20,8 @@ #ifndef __ASSEMBLY__ +#include + typedef struct { atomic64_t id; void *vdso; @@ -34,7 +36,8 @@ typedef struct { static inline bool arm64_kernel_unmapped_at_el0(void) { - return IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0); + return IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0) && + cpus_have_cap(ARM64_UNMAP_KERNEL_AT_EL0); } extern void paging_init(void); diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 58347534d765..ec7e68c2c73f 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -617,6 +617,39 @@ has_cpuid_feature(const struct arm64_cpu_capabilities *entry) return feature_matches(val, entry); } +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */ + +static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry) +{ + /* Forced on command line? */ + if (__kpti_forced) { + pr_info_once("kernel page table isolation forced %s by command line option\n", + __kpti_forced > 0 ? "ON" : "OFF"); + return __kpti_forced > 0; + } + + /* Useful for KASLR robustness */ + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) + return true; + + return false; +} + +static int __init parse_kpti(char *str) +{ + bool enabled; + int ret = strtobool(str, &enabled); + + if (ret) + return ret; + + __kpti_forced = enabled ? 1 : -1; + return 0; +} +__setup("kpti=", parse_kpti); +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ + static const struct arm64_cpu_capabilities arm64_features[] = { { .desc = "GIC system register CPU interface", @@ -654,6 +687,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = cpufeature_pan_not_uao, }, #endif /* CONFIG_ARM64_PAN */ +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + { + .capability = ARM64_UNMAP_KERNEL_AT_EL0, + .matches = unmap_kernel_at_el0, + }, +#endif {}, }; diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 17c58ae19ed3..3bf732fa4b9b 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -73,6 +73,7 @@ .macro kernel_ventry, el, label, regsize = 64 .align 7 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +alternative_if ARM64_UNMAP_KERNEL_AT_EL0 .if \el == 0 .if \regsize == 64 mrs x30, tpidrro_el0 @@ -81,6 +82,7 @@ mov x30, xzr .endif .endif +alternative_else_nop_endif #endif sub sp, sp, #S_FRAME_SIZE @@ -257,10 +259,9 @@ alternative_else_nop_endif ldr lr, [sp, #S_LR] add sp, sp, #S_FRAME_SIZE // restore sp -#ifndef CONFIG_UNMAP_KERNEL_AT_EL0 - eret -#else .if \el == 0 +alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0 +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 bne 4f msr far_el1, x30 tramp_alias x30, tramp_exit_native @@ -268,10 +269,10 @@ alternative_else_nop_endif 4: tramp_alias x30, tramp_exit_compat br x30 +#endif .else eret .endif -#endif .endm /* -- GitLab From 3ec3abcbf78fa2ab4251c5fdbffc2bafff922204 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:41:01 +0000 Subject: [PATCH 2229/5498] FROMLIST: arm64: Kconfig: Add CONFIG_UNMAP_KERNEL_AT_EL0 Add a Kconfig entry to control use of the entry trampoline, which allows us to unmap the kernel whilst running in userspace and improve the robustness of KASLR. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 084eb77cd3a81134d02500977dc0ecc9277dc97d) Change-Id: Iac41787b660dde902f32325afd2f454da600b60d Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/Kconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 3a2ce0de004c..ad927b44ceca 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -527,6 +527,19 @@ config FORCE_MAX_ZONEORDER default "14" if (ARM64_64K_PAGES && TRANSPARENT_HUGEPAGE) default "11" +config UNMAP_KERNEL_AT_EL0 + bool "Unmap kernel when running in userspace (aka \"KAISER\")" + default y + help + Some attacks against KASLR make use of the timing difference between + a permission fault which could arise from a page table entry that is + present in the TLB, and a translation fault which always requires a + page table walk. This option defends against these attacks by unmapping + the kernel whilst running in userspace, therefore forcing translation + faults for all of kernel space. + + If unsure, say Y. + menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" depends on COMPAT -- GitLab From 7b87b5746ca28b470af1dc2f019c252e7619cd3a Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 1 Dec 2017 17:33:48 +0000 Subject: [PATCH 2230/5498] FROMLIST: arm64: mm: Introduce TTBR_ASID_MASK for getting at the ASID in the TTBR There are now a handful of open-coded masks to extract the ASID from a TTBR value, so introduce a TTBR_ASID_MASK and use that instead. Suggested-by: Mark Rutland Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit b519538dfefc2f8478a1bcb458459c861d431784) Change-Id: I538071c8ec96dca587205c78839c07b6c772fa91 [ghackmann@google.com: adjust context, applying asm-uaccess.h changes to uaccess.h instead] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/mmu.h | 1 + arch/arm64/include/asm/uaccess.h | 7 ++++--- arch/arm64/kernel/entry.S | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 1ea12daa82d0..7925edad4bf4 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -17,6 +17,7 @@ #define __ASM_MMU_H #define USER_ASID_FLAG (UL(1) << 48) +#define TTBR_ASID_MASK (UL(0xffff) << 48) #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index cfef719287d9..e7cb0e6cbdaa 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -20,6 +20,7 @@ #include #include +#include #include #ifndef __ASSEMBLY__ @@ -141,7 +142,7 @@ static inline void __uaccess_ttbr0_disable(void) write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); isb(); /* Set reserved ASID */ - ttbr &= ~(0xffffUL << 48); + ttbr &= ~TTBR_ASID_MASK; write_sysreg(ttbr, ttbr1_el1); isb(); } @@ -160,7 +161,7 @@ static inline void __uaccess_ttbr0_enable(void) /* Restore active ASID */ ttbr1 = read_sysreg(ttbr1_el1); - ttbr1 |= ttbr0 & (0xffffUL << 48); + ttbr1 |= ttbr0 & TTBR_ASID_MASK; write_sysreg(ttbr1, ttbr1_el1); isb(); @@ -454,7 +455,7 @@ extern __must_check long strnlen_user(const char __user *str, long n); msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE - bic \tmp1, \tmp1, #(0xffff << 48) + bic \tmp1, \tmp1, #TTBR_ASID_MASK msr ttbr1_el1, \tmp1 // set reserved ASID isb .endm diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 3bf732fa4b9b..fffb9cd7ec29 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -149,7 +149,7 @@ alternative_else_nop_endif .if \el != 0 mrs x21, ttbr1_el1 - tst x21, #0xffff << 48 // Check for the reserved ASID + tst x21, #TTBR_ASID_MASK // Check for the reserved ASID orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR b.eq 1f // TTBR0 access already disabled and x23, x23, #~PSR_PAN_BIT // Clear the emulated PAN in the saved SPSR -- GitLab From 90523e83073be8fe4b6afd6de4f8467bc0c4dc86 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 6 Dec 2017 11:24:02 +0000 Subject: [PATCH 2231/5498] FROMLIST: arm64: kaslr: Put kernel vectors address in separate data page The literal pool entry for identifying the vectors base is the only piece of information in the trampoline page that identifies the true location of the kernel. This patch moves it into a page-aligned region of the .rodata section and maps this adjacent to the trampoline text via an additional fixmap entry, which protects against any accidental leakage of the trampoline contents. Suggested-by: Ard Biesheuvel Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 6c27c4082f4f70b9f41df4d0adf51128b40351df) Change-Id: Iffe72dc5e7ee171d83a7b916a16146e35ddf904e [ghackmann@google.com: - adjust context - replace ARM64_WORKAROUND_QCOM_FALKOR_E1003 alternative with compile-time CONFIG_ARCH_MSM8996 check] Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/fixmap.h | 1 + arch/arm64/kernel/entry.S | 16 ++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 4 ++++ arch/arm64/mm/mmu.c | 10 +++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h index cc9b9a6a238c..6c5bf65c6f4a 100644 --- a/arch/arm64/include/asm/fixmap.h +++ b/arch/arm64/include/asm/fixmap.h @@ -34,6 +34,7 @@ enum fixed_addresses { FIX_HOLE, FIX_EARLYCON_MEM_BASE, #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + FIX_ENTRY_TRAMP_DATA, FIX_ENTRY_TRAMP_TEXT, #define TRAMP_VALIAS (__fix_to_virt(FIX_ENTRY_TRAMP_TEXT)) #endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index fffb9cd7ec29..80f1c9f1f9ae 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -888,7 +888,15 @@ __ni_sys_trace: msr tpidrro_el0, x30 // Restored in kernel_ventry .endif tramp_map_kernel x30 +#ifdef CONFIG_RANDOMIZE_BASE + adr x30, tramp_vectors + PAGE_SIZE +#ifndef CONFIG_ARCH_MSM8996 + isb +#endif + ldr x30, [x30] +#else ldr x30, =vectors +#endif prfm plil1strm, [x30, #(1b - tramp_vectors)] msr vbar_el1, x30 add x30, x30, #(1b - tramp_vectors) @@ -931,6 +939,14 @@ END(tramp_exit_compat) .ltorg .popsection // .entry.tramp.text +#ifdef CONFIG_RANDOMIZE_BASE + .pushsection ".rodata", "a" + .align PAGE_SHIFT + .globl __entry_tramp_data_start +__entry_tramp_data_start: + .quad vectors + .popsection // .rodata +#endif /* CONFIG_RANDOMIZE_BASE */ #endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ /* diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index aef78912a6b6..498f431bb444 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -191,6 +191,10 @@ SECTIONS ASSERT(((__hyp_idmap_text_start + PAGE_SIZE) > __hyp_idmap_text_end), "HYP init code too big") +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +ASSERT((__entry_tramp_text_end - __entry_tramp_text_start) == PAGE_SIZE, + "Entry trampoline text too big") +#endif /* * If padding is applied before .head.text, virt<->phys conversions will fail. */ diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index c81313791fc6..7f8ae8a21454 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -458,8 +458,16 @@ static int __init map_entry_trampoline(void) __create_mapping(NULL, tramp_pg_dir + pgd_index(TRAMP_VALIAS), pa_start, TRAMP_VALIAS, PAGE_SIZE, prot, pgd_pgtable_alloc); - /* ...as well as the kernel page table */ + /* Map both the text and data into the kernel page table */ __set_fixmap(FIX_ENTRY_TRAMP_TEXT, pa_start, prot); + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { + extern char __entry_tramp_data_start[]; + + __set_fixmap(FIX_ENTRY_TRAMP_DATA, + __pa_symbol(__entry_tramp_data_start), + PAGE_KERNEL_RO); + } + return 0; } core_initcall(map_entry_trampoline); -- GitLab From 5baaf50ec1ec2b93291afd2ee9b84a82b3ad30c9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 16:15:59 +0000 Subject: [PATCH 2232/5498] arm64: use RET instruction for exiting the trampoline Speculation attacks against the entry trampoline can potentially resteer the speculative instruction stream through the indirect branch and into arbitrary gadgets within the kernel. This patch defends against these attacks by forcing a misprediction through the return stack: a dummy BL instruction loads an entry into the stack, so that the predicted program flow of the subsequent RET instruction is to a branch-to-self instruction which is finally resolved as a branch to the kernel vectors with speculation suppressed. Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/entry.S | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 80f1c9f1f9ae..ec2a0f820931 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -887,6 +887,9 @@ __ni_sys_trace: .if \regsize == 64 msr tpidrro_el0, x30 // Restored in kernel_ventry .endif + bl 2f + b . +2: tramp_map_kernel x30 #ifdef CONFIG_RANDOMIZE_BASE adr x30, tramp_vectors + PAGE_SIZE @@ -901,7 +904,7 @@ __ni_sys_trace: msr vbar_el1, x30 add x30, x30, #(1b - tramp_vectors) isb - br x30 + ret .endm .macro tramp_exit, regsize = 64 -- GitLab From 2897537bc331dba2c79238a18d62d11fd372a2a9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 16:19:39 +0000 Subject: [PATCH 2233/5498] arm64: Kconfig: Reword UNMAP_KERNEL_AT_EL0 kconfig entry Although CONFIG_UNMAP_KERNEL_AT_EL0 does make KASLR more robust, it's actually more useful as a mitigation against speculation attacks that can leak arbitrary kernel data to userspace through speculation. Reword the Kconfig help message to reflect this, and make the option depend on EXPERT so that it is on by default for the majority of users. Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/Kconfig | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index ad927b44ceca..e1dcafa82559 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -528,15 +528,14 @@ config FORCE_MAX_ZONEORDER default "11" config UNMAP_KERNEL_AT_EL0 - bool "Unmap kernel when running in userspace (aka \"KAISER\")" + bool "Unmap kernel when running in userspace (aka \"KAISER\")" if EXPERT default y help - Some attacks against KASLR make use of the timing difference between - a permission fault which could arise from a page table entry that is - present in the TLB, and a translation fault which always requires a - page table walk. This option defends against these attacks by unmapping - the kernel whilst running in userspace, therefore forcing translation - faults for all of kernel space. + Speculation attacks against some high-performance processors can + be used to bypass MMU permission checks and leak kernel data to + userspace. This can be defended against by unmapping the kernel + when running in userspace, mapping it back in on exception entry + via a trampoline page in the vector table. If unsure, say Y. -- GitLab From 5bdd8eace2b7669536b81bcba5e653459406a1bd Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Fri, 5 Jan 2018 17:30:41 +0200 Subject: [PATCH 2234/5498] msm: ipa3: fix event ring credit replenish at MHI unit test As MHI event ring filled with credits during channel start, queue more credits during a transfer only if they are needed for the transfer. Change-Id: I1b8cb55ce0dd1fa869b18ede1be104211b367132 Signed-off-by: Ghanim Fodi --- drivers/platform/msm/ipa/test/ipa_test_mhi.c | 56 +++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c index 43c8b3a818c3..92626ae4a2fc 100644 --- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c +++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1303,6 +1303,7 @@ static int ipa_mhi_test_q_transfer_re(struct ipa_mem_buffer *mmio, u32 next_wp_ofst; int i; u32 num_of_ed_to_queue; + u32 avail_ev; IPA_UT_LOG("Entry\n"); @@ -1340,6 +1341,8 @@ static int ipa_mhi_test_q_transfer_re(struct ipa_mem_buffer *mmio, wp_ofst = (u32)(p_events[event_ring_index].wp - p_events[event_ring_index].rbase); + rp_ofst = (u32)(p_events[event_ring_index].rp - + p_events[event_ring_index].rbase); if (p_events[event_ring_index].rlen & 0xFFFFFFFF00000000) { IPA_UT_LOG("invalid ev rlen %llu\n", @@ -1347,23 +1350,48 @@ static int ipa_mhi_test_q_transfer_re(struct ipa_mem_buffer *mmio, return -EFAULT; } - next_wp_ofst = (wp_ofst + num_of_ed_to_queue * - sizeof(struct ipa_mhi_event_ring_element)) % - (u32)p_events[event_ring_index].rlen; + if (wp_ofst > rp_ofst) { + avail_ev = (wp_ofst - rp_ofst) / + sizeof(struct ipa_mhi_event_ring_element); + } else { + avail_ev = (u32)p_events[event_ring_index].rlen - + (rp_ofst - wp_ofst); + avail_ev /= sizeof(struct ipa_mhi_event_ring_element); + } + + IPA_UT_LOG("wp_ofst=0x%x rp_ofst=0x%x rlen=%llu avail_ev=%u\n", + wp_ofst, rp_ofst, p_events[event_ring_index].rlen, avail_ev); + + if (num_of_ed_to_queue > ((u32)p_events[event_ring_index].rlen / + sizeof(struct ipa_mhi_event_ring_element))) { + IPA_UT_LOG("event ring too small for %u credits\n", + num_of_ed_to_queue); + return -EFAULT; + } + + if (num_of_ed_to_queue > avail_ev) { + IPA_UT_LOG("Need to add event credits (needed=%u)\n", + num_of_ed_to_queue - avail_ev); - /* set next WP */ - p_events[event_ring_index].wp = - (u32)p_events[event_ring_index].rbase + next_wp_ofst; + next_wp_ofst = (wp_ofst + (num_of_ed_to_queue - avail_ev) * + sizeof(struct ipa_mhi_event_ring_element)) % + (u32)p_events[event_ring_index].rlen; - /* write value to event ring doorbell */ - IPA_UT_LOG("DB to event 0x%llx: base %pa ofst 0x%x\n", - p_events[event_ring_index].wp, - &(gsi_ctx->per.phys_addr), GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS( + /* set next WP */ + p_events[event_ring_index].wp = + (u32)p_events[event_ring_index].rbase + next_wp_ofst; + + /* write value to event ring doorbell */ + IPA_UT_LOG("DB to event 0x%llx: base %pa ofst 0x%x\n", + p_events[event_ring_index].wp, + &(gsi_ctx->per.phys_addr), + GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS( event_ring_index + IPA_MHI_GSI_ER_START, 0)); - iowrite32(p_events[event_ring_index].wp, - test_mhi_ctx->gsi_mmio + - GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS( + iowrite32(p_events[event_ring_index].wp, + test_mhi_ctx->gsi_mmio + + GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS( event_ring_index + IPA_MHI_GSI_ER_START, 0)); + } for (i = 0; i < buf_array_size; i++) { /* calculate virtual pointer for current WP and RP */ -- GitLab From 090095bf61a2a40378a5f60f8793335d9b5892bb Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Sun, 7 Jan 2018 18:24:50 +0200 Subject: [PATCH 2235/5498] msm: ipa3: Do not configure USB pipes in MHI mode IPA MHI and IPA USB are use-cases that do not co-exist. In case of MHI mode, do not configure USB pipes so if USB composition includes some tethering function, it will not interfere with MHI functionality. Change-Id: I2b1d74e27163fd613d289d8436e8907e447ed6b4 Signed-off-by: Ghanim Fodi --- drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 29 +++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index b471e4da984e..a0f453a9c09f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -775,11 +775,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_3_5_MHI][IPA_CLIENT_HSIC4_PROD] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_USB4_PROD] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_HSIC5_PROD] = IPA_CLIENT_NOT_USED, - [IPA_3_5_MHI][IPA_CLIENT_USB_PROD] = { - 0, IPA_v3_5_MHI_GROUP_DDR, true, - IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP, - QMB_MASTER_SELECT_DDR, - { 0, 7, 8, 16, IPA_EE_AP } }, + [IPA_3_5_MHI][IPA_CLIENT_USB_PROD] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_UC_USB_PROD] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_A5_WLAN_AMPDU_PROD] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_A2_EMBEDDED_PROD] = IPA_CLIENT_NOT_USED, @@ -872,16 +868,8 @@ static const struct ipa_ep_configuration ipa3_ep_mapping [IPA_3_5_MHI][IPA_CLIENT_USB4_CONS] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_WLAN4_CONS] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_HSIC5_CONS] = IPA_CLIENT_NOT_USED, - [IPA_3_5_MHI][IPA_CLIENT_USB_CONS] = { - 17, IPA_v3_5_MHI_GROUP_DDR, false, - IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_DDR, - { 17, 11, 8, 8, IPA_EE_AP } }, - [IPA_3_5_MHI][IPA_CLIENT_USB_DPL_CONS] = { - 14, IPA_v3_5_MHI_GROUP_DDR, false, - IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_DDR, - { 14, 10, 4, 6, IPA_EE_AP } }, + [IPA_3_5_MHI][IPA_CLIENT_USB_CONS] = IPA_CLIENT_NOT_USED, + [IPA_3_5_MHI][IPA_CLIENT_USB_DPL_CONS] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_A2_EMBEDDED_CONS] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_A2_TETHERED_CONS] = IPA_CLIENT_NOT_USED, [IPA_3_5_MHI][IPA_CLIENT_A5_LAN_WAN_CONS] = IPA_CLIENT_NOT_USED, @@ -1257,10 +1245,12 @@ int ipa3_get_clients_from_rm_resource( switch (resource) { case IPA_RM_RESOURCE_USB_CONS: - clients->names[i++] = IPA_CLIENT_USB_CONS; + if (ipa3_get_ep_mapping(IPA_CLIENT_USB_CONS) != -1) + clients->names[i++] = IPA_CLIENT_USB_CONS; break; case IPA_RM_RESOURCE_USB_DPL_CONS: - clients->names[i++] = IPA_CLIENT_USB_DPL_CONS; + if (ipa3_get_ep_mapping(IPA_CLIENT_USB_DPL_CONS) != -1) + clients->names[i++] = IPA_CLIENT_USB_DPL_CONS; break; case IPA_RM_RESOURCE_HSIC_CONS: if (ipa3_get_ep_mapping(IPA_CLIENT_HSIC1_CONS) != -1) @@ -1287,7 +1277,8 @@ int ipa3_get_clients_from_rm_resource( clients->names[i++] = IPA_CLIENT_ETHERNET_CONS; break; case IPA_RM_RESOURCE_USB_PROD: - clients->names[i++] = IPA_CLIENT_USB_PROD; + if (ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD) != -1) + clients->names[i++] = IPA_CLIENT_USB_PROD; break; case IPA_RM_RESOURCE_HSIC_PROD: if (ipa3_get_ep_mapping(IPA_CLIENT_HSIC1_PROD) != -1) -- GitLab From 1dfb678f190bc318eae7f3104e1c425903470bf3 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Fri, 5 Jan 2018 14:38:00 +0530 Subject: [PATCH 2236/5498] soc: qcom: bgcom: fixes Glink faliure on BG soft reset. Sends BG ssr notification to Glink to clear channels state Change-Id: If759973d2e22a754896c16d97cf432bdb6eb14d6 Signed-off-by: Arjun Singh --- drivers/soc/qcom/bgcom.h | 4 +++- drivers/soc/qcom/bgcom_interface.c | 3 ++- drivers/soc/qcom/bgcom_spi.c | 9 ++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/bgcom.h b/drivers/soc/qcom/bgcom.h index 1c2c3b9074fc..bf0a2c92630a 100644 --- a/drivers/soc/qcom/bgcom.h +++ b/drivers/soc/qcom/bgcom.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -208,4 +208,6 @@ int bgcom_resume(void **handle); int bgcom_set_spi_state(enum bgcom_spi_state state); +void bgcom_bgdown_handler(void); + #endif /* BGCOM_H */ diff --git a/drivers/soc/qcom/bgcom_interface.c b/drivers/soc/qcom/bgcom_interface.c index bf180456a152..5acab00f3c84 100644 --- a/drivers/soc/qcom/bgcom_interface.c +++ b/drivers/soc/qcom/bgcom_interface.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -285,6 +285,7 @@ static int ssr_bg_cb(struct notifier_block *this, switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: bge.e_type = BG_BEFORE_POWER_DOWN; + bgcom_bgdown_handler(); bgcom_set_spi_state(BGCOM_SPI_BUSY); send_uevent(&bge); break; diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c index bc9b845bf7c5..772264f20565 100644 --- a/drivers/soc/qcom/bgcom_spi.c +++ b/drivers/soc/qcom/bgcom_spi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -261,6 +261,13 @@ void send_event(enum bgcom_event_type event, } } +void bgcom_bgdown_handler(void) +{ + send_event(BGCOM_EVENT_RESET_OCCURRED, NULL); + g_slav_status_reg = 0; +} +EXPORT_SYMBOL(bgcom_bgdown_handler); + static void parse_fifo(uint8_t *data, union bgcom_event_data_type *event_data) { uint16_t p_len; -- GitLab From 45ef48cea39254bfc947889e2ea9595c74e16f27 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Wed, 27 Dec 2017 17:08:29 +0530 Subject: [PATCH 2237/5498] msm: ais: sensor: actuator: avoid accessing out of bound memory Issue: When total_steps is updated, after that, copy_from_user fails with an error, then, i2c_reg_tbl is not allocated. In this case, when calling msm_actuator_parse_i2c_params, it lead to out-of-bound memory write. Fix: 1) Assign total_steps to zero when error from copying. 2) Add NULL pointer check for i2c tbl. 3) Fixing the issue where the function can return with an error code leaving "a_ctrl->i2c_reg_tbl" and "a_ctrl->total_steps" out of sync. Change-Id: Ia01851c8c5fda3a466cada885cae5c0651857b16 Signed-off-by: Rahul Sharma --- .../msm/ais/sensor/actuator/msm_actuator.c | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c index 933541962e07..f68d031a9dcd 100644 --- a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c @@ -56,6 +56,10 @@ static int32_t msm_actuator_piezo_set_default_focus( struct msm_camera_i2c_reg_setting reg_setting; CDBG("Enter\n"); + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return -EFAULT; + } if (a_ctrl->curr_step_pos != 0) { a_ctrl->i2c_tbl_index = 0; @@ -539,6 +543,12 @@ static int32_t msm_actuator_piezo_move_focus( return -EFAULT; } + + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return -EFAULT; + } + if (dest_step_position > a_ctrl->total_steps) { pr_err("Step pos greater than total steps = %d\n", dest_step_position); @@ -596,6 +606,12 @@ static int32_t msm_actuator_move_focus( pr_err("Invalid direction = %d\n", dir); return -EFAULT; } + + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return -EFAULT; + } + if (dest_step_pos > a_ctrl->total_steps) { pr_err("Step pos greater than total steps = %d\n", dest_step_pos); @@ -1179,7 +1195,8 @@ static int32_t msm_actuator_set_position( } if (!a_ctrl || !a_ctrl->func_tbl || - !a_ctrl->func_tbl->actuator_parse_i2c_params) { + !a_ctrl->func_tbl->actuator_parse_i2c_params || + !a_ctrl->i2c_reg_tbl) { pr_err("failed. NULL actuator pointers."); return -EFAULT; } @@ -1291,7 +1308,6 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, a_ctrl->region_size = set_info->af_tuning_params.region_size; a_ctrl->pwd_step = set_info->af_tuning_params.pwd_step; - a_ctrl->total_steps = set_info->af_tuning_params.total_steps; if (copy_from_user(&a_ctrl->region_params, (void *)set_info->af_tuning_params.region_params, @@ -1306,7 +1322,6 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, cci_client->sid = set_info->actuator_params.i2c_addr >> 1; cci_client->retries = 3; - cci_client->id_map = 0; cci_client->cci_i2c_master = a_ctrl->cci_master; cci_client->i2c_freq_mode = set_info->actuator_params.i2c_freq_mode; @@ -1339,6 +1354,8 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, return -ENOMEM; } + a_ctrl->total_steps = set_info->af_tuning_params.total_steps; + if (copy_from_user(&a_ctrl->reg_tbl, (void *)set_info->actuator_params.reg_tbl_params, a_ctrl->reg_tbl_size * -- GitLab From 04200bc963088467b6d6130f3f8b23950f798e2e Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 4 Jan 2018 18:15:51 +0530 Subject: [PATCH 2238/5498] ASoC: bg: Register codec only once ADSP is up Sound card should not get registered till ADSP is up, hence defer codec registration till the ADSP is up. Add a flag in packetizer to indicate init complete. Change-Id: I846049b586698ea6b29a651a9c2c65311a478723 Signed-off-by: Ashish Jain --- drivers/soc/qcom/qdsp6v2/apr_v2.c | 3 ++- drivers/soc/qcom/qdsp6v2/apr_v3.c | 3 ++- sound/soc/codecs/bg_codec.c | 11 ++++++++++- sound/soc/codecs/pktzr.c | 14 +++++++------- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/soc/qcom/qdsp6v2/apr_v2.c b/drivers/soc/qcom/qdsp6v2/apr_v2.c index 2d6ea825c811..631eaa15b257 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_v2.c +++ b/drivers/soc/qcom/qdsp6v2/apr_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,6 +25,7 @@ enum apr_subsys_state apr_get_subsys_state(void) { return apr_get_q6_state(); } +EXPORT_SYMBOL(apr_get_subsys_state); void apr_set_subsys_state(void) { diff --git a/drivers/soc/qcom/qdsp6v2/apr_v3.c b/drivers/soc/qcom/qdsp6v2/apr_v3.c index 6bb913edf3ec..085d01def050 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_v3.c +++ b/drivers/soc/qcom/qdsp6v2/apr_v3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,6 +25,7 @@ enum apr_subsys_state apr_get_subsys_state(void) { return apr_get_modem_state(); } +EXPORT_SYMBOL(apr_get_subsys_state); void apr_set_subsys_state(void) { diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index a4f66d8108b4..3730ee016b5a 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "pktzr.h" @@ -739,6 +740,14 @@ static int bg_cdc_probe(struct platform_device *pdev) { struct bg_cdc_priv *bg_cdc; int ret = 0; + int adsp_state; + + adsp_state = apr_get_subsys_state(); + if (adsp_state != APR_SUBSYS_LOADED) { + pr_err("%s:Adsp is not loaded yet %d\n", + __func__, adsp_state); + return -EPROBE_DEFER; + } bg_cdc = kzalloc(sizeof(struct bg_cdc_priv), GFP_KERNEL); diff --git a/sound/soc/codecs/pktzr.c b/sound/soc/codecs/pktzr.c index 33c2b4eddf5f..197c6f98c949 100644 --- a/sound/soc/codecs/pktzr.c +++ b/sound/soc/codecs/pktzr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -65,6 +65,7 @@ struct pktzr_priv { struct platform_device *pdev; pktzr_data_cmd_cb_fn data_cmd_cb; int num_channels; + bool pktzr_init_complete; }; static struct pktzr_priv *ppriv; @@ -103,8 +104,8 @@ static int pktzr_send_pkt(void *payload, uint32_t size, void *rsp, int rc = 0; pr_debug("%s: cmd=%d sync=%d size=%d\n", __func__, cmd, sync_cmd, size); - if (!ppriv) { - pr_err("packetizer not initialized"); + if (!ppriv || !ppriv->pktzr_init_complete) { + pr_err_ratelimited("packetizer not initialized\n"); return -EINVAL; } mutex_lock(&ppriv->pktzr_lock); @@ -237,22 +238,21 @@ int pktzr_init(void *pdev, struct bg_glink_ch_cfg *ch_info, int num_channels, pr_err("%s: Failed to open channel\n", __func__); goto err; } - ppriv->token = i; } ppriv->num_channels = num_channels; ppriv->data_cmd_cb = func; - ppriv->token = 0; ppriv->pdev = pdev; init_completion(&ppriv->thread_complete); mutex_init(&ppriv->pktzr_lock); INIT_LIST_HEAD(&ppriv->ch_list); + ppriv->pktzr_init_complete = true; done: return 0; err: for (i = 0; i < ppriv->token; i++) - bg_cdc_channel_close(ppriv->pdev, ppriv->ch_info[i]); - ppriv->token = 0; + bg_cdc_channel_close(pdev, ppriv->ch_info[i]); + if (ppriv) kzfree(ppriv); ppriv = NULL; -- GitLab From 7bccd28ea90d2445f1ff9526297f00562ce55264 Mon Sep 17 00:00:00 2001 From: Skylar Chang Date: Tue, 21 Nov 2017 10:11:34 -0800 Subject: [PATCH 2239/5498] msm: ipa3: fix holb sequence for SSR The correct sequence for enabling HOLB drop is first to suspend the pipe and then to HOLB drop. Change-Id: I78b7b268eec230a4993e446bd90846f800364e06 CRs-Fixed: 2141518 Acked-by: Ady Abraham Signed-off-by: Mohammed Javid Signed-off-by: Skylar Chang --- drivers/platform/msm/ipa/ipa_v3/ipa.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index aae19811f662..42af8dd0f3de 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -2091,6 +2091,12 @@ static void ipa3_q6_avoid_holb(void) if (ep_idx == -1) continue; + /* from IPA 4.0 pipe suspend is not supported */ + if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) + ipahal_write_reg_n_fields( + IPA_ENDP_INIT_CTRL_n, + ep_idx, &ep_suspend); + /* * ipa3_cfg_ep_holb is not used here because we are * setting HOLB on Q6 pipes, and from APPS perspective @@ -2103,10 +2109,6 @@ static void ipa3_q6_avoid_holb(void) ipahal_write_reg_n_fields( IPA_ENDP_INIT_HOL_BLOCK_EN_n, ep_idx, &ep_holb); - - ipahal_write_reg_n_fields( - IPA_ENDP_INIT_CTRL_n, - ep_idx, &ep_suspend); } } } -- GitLab From d74d8eb4536cbe715691fa0a7c615e8fe3ecc5bf Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Feb 2017 11:48:58 +0000 Subject: [PATCH 2240/5498] BACKPORT: arm64: Add CNTVCT_EL0 trap handler Since people seem to make a point in breaking the userspace visible counter, we have no choice but to trap the access. Add the required handler. Acked-by: Thomas Gleixner Acked-by: Mark Rutland Signed-off-by: Marc Zyngier (cherry picked from commit 6126ce0588eb5a0752d5c8b5796a7fca324fd887) Change-Id: I0705f47c85a78040df38df18f51a4a22500b904d Signed-off-by: Neeraj Upadhyay --- arch/arm64/include/asm/esr.h | 37 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/entry.S | 12 +++++++++++- arch/arm64/kernel/traps.c | 19 ++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 7cff572082d0..311eacfd4224 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -134,6 +134,43 @@ #define ESR_ELx_COND_MASK (UL(0xF) << ESR_ELx_COND_SHIFT) #define ESR_ELx_WFx_ISS_WFE (UL(1) << 0) +/* ISS field definitions for System instruction traps */ +#define ESR_ELx_SYS64_ISS_RES0_SHIFT 22 +#define ESR_ELx_SYS64_ISS_RES0_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_RES0_SHIFT) +#define ESR_ELx_SYS64_ISS_DIR_MASK 0x1 +#define ESR_ELx_SYS64_ISS_DIR_READ 0x1 +#define ESR_ELx_SYS64_ISS_DIR_WRITE 0x0 + +#define ESR_ELx_SYS64_ISS_RT_SHIFT 5 +#define ESR_ELx_SYS64_ISS_RT_MASK (UL(0x1f) << ESR_ELx_SYS64_ISS_RT_SHIFT) +#define ESR_ELx_SYS64_ISS_CRM_SHIFT 1 +#define ESR_ELx_SYS64_ISS_CRM_MASK (UL(0xf) << ESR_ELx_SYS64_ISS_CRM_SHIFT) +#define ESR_ELx_SYS64_ISS_CRN_SHIFT 10 +#define ESR_ELx_SYS64_ISS_CRN_MASK (UL(0xf) << ESR_ELx_SYS64_ISS_CRN_SHIFT) +#define ESR_ELx_SYS64_ISS_OP1_SHIFT 14 +#define ESR_ELx_SYS64_ISS_OP1_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_OP1_SHIFT) +#define ESR_ELx_SYS64_ISS_OP2_SHIFT 17 +#define ESR_ELx_SYS64_ISS_OP2_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_OP2_SHIFT) +#define ESR_ELx_SYS64_ISS_OP0_SHIFT 20 +#define ESR_ELx_SYS64_ISS_OP0_MASK (UL(0x3) << ESR_ELx_SYS64_ISS_OP0_SHIFT) +#define ESR_ELx_SYS64_ISS_SYS_MASK (ESR_ELx_SYS64_ISS_OP0_MASK | \ + ESR_ELx_SYS64_ISS_OP1_MASK | \ + ESR_ELx_SYS64_ISS_OP2_MASK | \ + ESR_ELx_SYS64_ISS_CRN_MASK | \ + ESR_ELx_SYS64_ISS_CRM_MASK) +#define ESR_ELx_SYS64_ISS_SYS_VAL(op0, op1, op2, crn, crm) \ + (((op0) << ESR_ELx_SYS64_ISS_OP0_SHIFT) | \ + ((op1) << ESR_ELx_SYS64_ISS_OP1_SHIFT) | \ + ((op2) << ESR_ELx_SYS64_ISS_OP2_SHIFT) | \ + ((crn) << ESR_ELx_SYS64_ISS_CRN_SHIFT) | \ + ((crm) << ESR_ELx_SYS64_ISS_CRM_SHIFT)) + +#define ESR_ELx_SYS64_ISS_SYS_OP_MASK (ESR_ELx_SYS64_ISS_SYS_MASK | \ + ESR_ELx_SYS64_ISS_DIR_MASK) + +#define ESR_ELx_SYS64_ISS_SYS_CNTVCT (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 2, 14, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + #ifndef __ASSEMBLY__ #include diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 9adad63e4bf8..4c58ac76bc8c 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -484,7 +484,7 @@ el0_sync: cmp x24, #ESR_ELx_EC_FP_EXC64 // FP/ASIMD exception b.eq el0_fpsimd_exc cmp x24, #ESR_ELx_EC_SYS64 // configurable trap - b.eq el0_undef + b.eq el0_sys cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception b.eq el0_sp_pc cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception @@ -621,6 +621,16 @@ el0_undef: mov x0, sp bl do_undefinstr b ret_to_user +el0_sys: + /* + * System instructions, for trapped cache maintenance instructions + */ + enable_dbg_and_irq + ct_user_exit + mov x0, x25 + mov x1, sp + bl do_sysinstr + b ret_to_user el0_dbg: /* * Debug exception handling diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index bef6100f5c55..4e983fa2e8fa 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -399,6 +399,25 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) arm64_notify_die("Oops - undefined instruction", regs, &info, 0); } +static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs) +{ + int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT; + + if (rt != 31) + regs->regs[rt] = arch_counter_get_cntvct(); + regs->pc += 4; +} + +asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs) +{ + if ((esr & ESR_ELx_SYS64_ISS_SYS_OP_MASK) == ESR_ELx_SYS64_ISS_SYS_CNTVCT) { + cntvct_read_handler(esr, regs); + return; + } + + do_undefinstr(regs); +} + long compat_arm_syscall(struct pt_regs *regs); asmlinkage long do_ni_syscall(struct pt_regs *regs) -- GitLab From 3158d448e260f86b62de35696020a761844a1346 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 24 Apr 2017 09:04:03 +0100 Subject: [PATCH 2241/5498] BACKPORT: arm64: Add CNTFRQ_EL0 trap handler We now trap accesses to CNTVCT_EL0 when the counter is broken enough to require the kernel to mediate the access. But it turns out that some existing userspace (such as OpenMPI) do probe for the counter frequency, leading to an UNDEF exception as CNTVCT_EL0 and CNTFRQ_EL0 share the same control bit. The fix is to handle the exception the same way we do for CNTVCT_EL0. Fixes: a86bd139f2ae ("arm64: arch_timer: Enable CNTVCT_EL0 trap if workaround is enabled") Reported-by: Hanjun Guo Tested-by: Hanjun Guo Reviewed-by: Hanjun Guo Signed-off-by: Marc Zyngier Signed-off-by: Catalin Marinas (cherry picked from commit 9842119a238bfb92cbab63258dabb54f0e7b111b) Change-Id: I1bde5dc8f2fbdc94f66bc6606b88ad1817788080 Signed-off-by: Neeraj Upadhyay --- arch/arm64/include/asm/esr.h | 3 +++ arch/arm64/kernel/traps.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 311eacfd4224..f1d9b6261f55 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -171,6 +171,9 @@ #define ESR_ELx_SYS64_ISS_SYS_CNTVCT (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 2, 14, 0) | \ ESR_ELx_SYS64_ISS_DIR_READ) +#define ESR_ELx_SYS64_ISS_SYS_CNTFRQ (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 0, 14, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + #ifndef __ASSEMBLY__ #include diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 4e983fa2e8fa..fe4f786242de 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -408,11 +408,23 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs) regs->pc += 4; } +static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs) +{ + int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT; + + if (rt != 31) + regs->regs[rt] = read_sysreg(cntfrq_el0); + regs->pc += 4; +} + asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs) { if ((esr & ESR_ELx_SYS64_ISS_SYS_OP_MASK) == ESR_ELx_SYS64_ISS_SYS_CNTVCT) { cntvct_read_handler(esr, regs); return; + } else if ((esr & ESR_ELx_SYS64_ISS_SYS_OP_MASK) == ESR_ELx_SYS64_ISS_SYS_CNTFRQ) { + cntfrq_read_handler(esr, regs); + return; } do_undefinstr(regs); -- GitLab From 1ad7cba07e31f9d2d7cd65aea5bfefdc248be0f8 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 4 Oct 2017 09:31:34 -0700 Subject: [PATCH 2242/5498] arm64: issue isb when trapping CNTVCT_EL0 access Change-Id: I6005a6e944494257bfc2243fde2f7a09c3fd76c6 Signed-off-by: Neeraj Upadhyay --- arch/arm64/kernel/traps.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index fe4f786242de..b42991af2e28 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -403,6 +404,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs) { int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT; + isb(); if (rt != 31) regs->regs[rt] = arch_counter_get_cntvct(); regs->pc += 4; -- GitLab From 261ef84fa0933abc1634ed3fbf7e838eb7c070d9 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 19 Sep 2017 10:55:17 -0700 Subject: [PATCH 2243/5498] clocksource: arch_timer: make virtual counter access configurable Change-Id: Ibdb1fd768b748002b90bfc165612c12c8311f8a2 Signed-off-by: Neeraj Upadhyay --- drivers/clocksource/Kconfig | 8 ++++++++ drivers/clocksource/arm_arch_timer.c | 8 ++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 90420600e1eb..2a6b2a8e1578 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -110,6 +110,14 @@ config ARM_ARCH_TIMER_EVTSTREAM This must be disabled for hardware validation purposes to detect any hardware anomalies of missing events. +config ARM_ARCH_TIMER_VCT_ACCESS + bool "Support for ARM architected timer virtual counter access in userspace" + default !ARM64 + depends on ARM_ARCH_TIMER + help + This option enables support for reading the ARM architected timer's + virtual counter in userspace. + config ARM_GLOBAL_TIMER bool select CLKSRC_OF if OF diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 0f69d44258c8..891b07e4ac95 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -340,8 +340,12 @@ static void arch_counter_set_user_access(void) | ARCH_TIMER_VIRT_EVT_EN); /* Enable user access to the virtual and physical counters */ - cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN | ARCH_TIMER_USR_PCT_ACCESS_EN - | ARCH_TIMER_USR_VT_ACCESS_EN; + cntkctl |= ARCH_TIMER_USR_PCT_ACCESS_EN | ARCH_TIMER_USR_VT_ACCESS_EN; + + if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS)) + cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; + else + cntkctl &= ~ARCH_TIMER_USR_VCT_ACCESS_EN; arch_timer_set_cntkctl(cntkctl); } -- GitLab From a039a83eb4525b45858c486d4fb79eb4697d2d4b Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Thu, 4 Jan 2018 15:27:47 +0530 Subject: [PATCH 2244/5498] ASoC: msm: qdspqv2: add check for TDM clock programming Configure TDM clock based on AFE EBIT flag on platforms where it is supported. Change-Id: I900a749b8aae7a87b99b30e1edd20393eb7c4c3b Signed-off-by: Bala Kishore Pati --- sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 29 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 429559ae39c1..84b059b27846 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -5126,11 +5126,15 @@ static int msm_dai_q6_dai_tdm_remove(struct snd_soc_dai *dai) dev_err(dai->dev, "fail to disable AFE group 0x%x\n", group_id); } - rc = msm_dai_q6_tdm_set_clk(tdm_dai_data, - dai->id, false); - if (IS_ERR_VALUE(rc)) { - dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n", + if (!(tdm_dai_data->afe_ebit_unsupported && + !tdm_dai_data->clk_set.clk_freq_in_hz)) { + rc = msm_dai_q6_tdm_set_clk(tdm_dai_data, + dai->id, false); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, + "%s: fail to disable AFE clk 0x%x\n", __func__, dai->id); + } } } } @@ -5767,8 +5771,10 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, if (atomic_read(group_ref) == 0) { afe_port_group_enable(group_id, NULL, false); + if (!(dai_data->afe_ebit_unsupported && + !dai_data->clk_set.clk_freq_in_hz)) msm_dai_q6_tdm_set_clk(dai_data, - dai->id, false); + dai->id, false); } dev_err(dai->dev, "%s: fail AFE port 0x%x\n", __func__, sec_port_id); @@ -5882,12 +5888,15 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, "%s: failed to disable grp 0x%x\n", __func__, group_id); } - rc = msm_dai_q6_tdm_set_clk(dai_data, - dai->id, false); - if (IS_ERR_VALUE(rc)) { - dev_err(dai->dev, + if (!(dai_data->afe_ebit_unsupported && + !dai_data->clk_set.clk_freq_in_hz)) { + rc = msm_dai_q6_tdm_set_clk(dai_data, + dai->id, false); + if (IS_ERR_VALUE(rc)) { + dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n", __func__, dai->id); + } } } if ((dai_data->num_group_ports > 1) && -- GitLab From 1887463e2a95482c536ad6e804affd7235d326d4 Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Thu, 4 Jan 2018 17:23:47 +0530 Subject: [PATCH 2245/5498] soc:qcom: glink_bgcom_xprt: Send tx blocked signal in tx_data In tx_data tx_blocked signal is not sent, when there is no space in fifo. This leads to glink_tx thread never waking up. Tx blocked signal is sent from tx_data. CRs-Fixed: 2168515 Change-Id: I4657eb0bb5a43d4c06ac2797b16c202b6f5bbcd0 Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/glink_bgcom_xprt.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/glink_bgcom_xprt.c b/drivers/soc/qcom/glink_bgcom_xprt.c index 682eb28736ff..8886851dfbb8 100644 --- a/drivers/soc/qcom/glink_bgcom_xprt.c +++ b/drivers/soc/qcom/glink_bgcom_xprt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -293,13 +293,19 @@ static void send_tx_blocked_signal(struct edge_info *einfo) uint64_t reserved3; }; struct read_notif_request read_notif_req = {0}; + int size_in_word = sizeof(read_notif_req)/WORD_SIZE; + void *src = &read_notif_req; + int ret; read_notif_req.cmd = READ_NOTIF_CMD; - if (!einfo->tx_blocked_signal_sent) { einfo->tx_blocked_signal_sent = true; - glink_bgcom_xprt_tx_cmd_safe(einfo, &read_notif_req, - sizeof(read_notif_req)); + ret = bgcom_fifo_write(einfo->bgcom_handle, size_in_word, src); + if (ret < 0) { + GLINK_ERR("%s: Err %d send blocked\n", __func__, ret); + return; + } + glink_bgcom_update_tx_avail(einfo, size_in_word); } } @@ -1271,8 +1277,10 @@ static int tx_data(struct glink_transport_if *if_ptr, uint16_t cmd_id, /* Need enough space to write the command */ if (glink_bgcom_get_tx_avail(einfo) <= sizeof(cmd)/WORD_SIZE) { einfo->tx_resume_needed = true; + send_tx_blocked_signal(einfo); mutex_unlock(&einfo->write_lock); srcu_read_unlock(&einfo->use_ref, rcu_id); + GLINK_ERR("%s: No Space in Fifo\n", __func__); return -EAGAIN; } cmd.addr = 0; -- GitLab From 549ad1577c59551debd938933e356ad991914870 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 8 Jan 2018 13:57:36 -0800 Subject: [PATCH 2246/5498] ANDROID: sdcardfs: Fix missing break on default_normal Signed-off-by: Daniel Rosenberg Bug: 64672411 Change-Id: I98796df95dc9846adb77a11f49a1a254fb1618b1 --- fs/sdcardfs/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 50e186b83341..89ca92191355 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -127,6 +127,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; case Opt_default_normal: vfsopts->default_normal = true; + break; /* unknown option */ default: if (!silent) -- GitLab From d8681082436593ab12cba5180a48444fa3d32417 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 20 Dec 2016 17:08:10 +0800 Subject: [PATCH 2247/5498] ASoC: msm: qdspv2: add spin lock to protect ac ac could get freed during the execution of q6asm_callback. And kernel panic happens. Add spinlock to protect ac to avoid kernel panic. Change-Id: Ie49c8a3979231552ba7d5f207aab0d95ffdc2a72 Signed-off-by: Meng Wang --- sound/soc/msm/qdsp6v2/q6asm.c | 142 ++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 31 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 730eb14d5adf..074a8cdfa75b 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -60,8 +60,13 @@ struct asm_mmap { }; static struct asm_mmap this_mmap; + +struct audio_session { + struct audio_client *ac; + spinlock_t session_lock; +}; /* session id: 0 reserved */ -static struct audio_client *session[SESSION_MAX+1]; +static struct audio_session session[SESSION_MAX + 1]; struct asm_no_wait_node { struct list_head list; @@ -417,9 +422,10 @@ int q6asm_mmap_apr_dereg(void) static int q6asm_session_alloc(struct audio_client *ac) { int n; + for (n = 1; n <= SESSION_MAX; n++) { - if (!session[n]) { - session[n] = ac; + if (!session[n].ac) { + session[n].ac = ac; return n; } } @@ -427,28 +433,40 @@ static int q6asm_session_alloc(struct audio_client *ac) return -ENOMEM; } -static bool q6asm_is_valid_audio_client(struct audio_client *ac) +static unsigned int q6asm_get_session_id_from_audio_client( + struct audio_client *ac) { - int n; + unsigned int n; + for (n = 1; n <= SESSION_MAX; n++) { - if (session[n] == ac) - return 1; + if (session[n].ac == ac) + return n; } return 0; } +static bool q6asm_is_valid_audio_client(struct audio_client *ac) +{ + return q6asm_get_session_id_from_audio_client(ac) ? 1 : 0; +} + static void q6asm_session_free(struct audio_client *ac) { struct list_head *ptr, *next; struct asm_no_wait_node *node; unsigned long flags; + int session_id; pr_debug("%s: sessionid[%d]\n", __func__, ac->session); + session_id = ac->session; rtac_remove_popp_from_adm_devices(ac->session); - session[ac->session] = 0; + spin_lock_irqsave(&(session[session_id].session_lock), flags); + session[ac->session].ac = NULL; ac->session = 0; ac->perf_mode = LEGACY_PCM_MODE; ac->fptr_cache_ops = NULL; + ac->cb = NULL; + ac->priv = NULL; spin_lock_irqsave(&ac->no_wait_que_spinlock, flags); list_for_each_safe(ptr, next, &ac->no_wait_que) { @@ -457,6 +475,9 @@ static void q6asm_session_free(struct audio_client *ac) kfree(node); } spin_unlock_irqrestore(&ac->no_wait_que_spinlock, flags); + + kfree(ac); + spin_unlock_irqrestore(&(session[session_id].session_lock), flags); return; } @@ -1015,8 +1036,6 @@ void q6asm_audio_client_free(struct audio_client *ac) pr_debug("%s: APR De-Register\n", __func__); /*done:*/ - kfree(ac); - ac = NULL; mutex_unlock(&session_lock); return; @@ -1085,6 +1104,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) if (n <= 0) { pr_err("%s: ASM Session alloc fail n=%d\n", __func__, n); mutex_unlock(&session_lock); + kfree(ac); goto fail_session; } ac->session = n; @@ -1164,7 +1184,6 @@ fail_apr2: fail_apr1: q6asm_session_free(ac); fail_session: - kfree(ac); return NULL; } @@ -1179,11 +1198,11 @@ struct audio_client *q6asm_get_audio_client(int session_id) goto err; } - if (!session[session_id]) { + if (!(session[session_id].ac)) { pr_err("%s: session not active: %d\n", __func__, session_id); goto err; } - return session[session_id]; + return session[session_id].ac; err: return NULL; } @@ -1388,6 +1407,7 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) uint32_t i = IN; uint32_t *payload; unsigned long dsp_flags; + unsigned long flags; struct asm_buffer_node *buf_node = NULL; struct list_head *ptr, *next; @@ -1436,9 +1456,16 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) return 0; } sid = (data->token >> 8) & 0x0F; + if ((sid > 0 && sid <= SESSION_MAX)) + spin_lock_irqsave(&(session[sid].session_lock), flags); + ac = q6asm_get_audio_client(sid); if (!ac) { pr_debug("%s: session[%d] already freed\n", __func__, sid); + if ((sid > 0 && + sid <= SESSION_MAX)) + spin_unlock_irqrestore( + &(session[sid].session_lock), flags); return 0; } @@ -1488,6 +1515,10 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) __func__, payload[0]); break; } + if ((sid > 0 && + sid <= SESSION_MAX)) + spin_unlock_irqrestore( + &(session[sid].session_lock), flags); return 0; } @@ -1523,6 +1554,10 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) if (ac->cb) ac->cb(data->opcode, data->token, data->payload, ac->priv); + if ((sid > 0 && sid <= SESSION_MAX)) + spin_unlock_irqrestore( + &(session[sid].session_lock), flags); + return 0; } @@ -1601,7 +1636,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) uint32_t *payload; uint32_t wakeup_flag = 1; int32_t ret = 0; - + unsigned long flags; + int session_id; if (ac == NULL) { pr_err("%s: ac NULL\n", __func__); @@ -1611,15 +1647,21 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) pr_err("%s: data NULL\n", __func__); return -EINVAL; } - if (!q6asm_is_valid_audio_client(ac)) { - pr_err("%s: audio client pointer is invalid, ac = %pK\n", - __func__, ac); + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (session_id <= 0) { + pr_err("%s: Session ID is invalid, session = %d\n", __func__, + session_id); return -EINVAL; } - if (ac->session <= 0 || ac->session > 8) { - pr_err("%s: Session ID is invalid, session = %d\n", __func__, - ac->session); + spin_lock_irqsave(&(session[session_id].session_lock), flags); + + if (!q6asm_is_valid_audio_client(ac)) { + pr_err("%s: audio client pointer is invalid, ac = %pK\n", + __func__, ac); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return -EINVAL; } @@ -1634,7 +1676,6 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) } if (data->opcode == RESET_EVENTS) { - mutex_lock(&ac->cmd_lock); atomic_set(&ac->reset, 1); if (ac->apr == NULL) ac->apr = ac->apr2; @@ -1651,7 +1692,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->cmd_state_pp, 0); wake_up(&ac->time_wait); wake_up(&ac->cmd_wait); - mutex_unlock(&ac->cmd_lock); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1662,9 +1704,16 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) data->dest_port); if ((data->opcode != ASM_DATA_EVENT_RENDERED_EOS) && (data->opcode != ASM_DATA_EVENT_EOS) && - (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) + (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { + if (payload == NULL) { + pr_err("%s: payload is null\n", __func__); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return -EINVAL; + } dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n", __func__, payload[0], payload[1], data->opcode); + } if (data->opcode == APR_BASIC_RSP_RESULT) { token = data->token; switch (payload[0]) { @@ -1686,6 +1735,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) ret = q6asm_is_valid_session(data, priv); if (ret != 0) { pr_err("%s: session invalid %d\n", __func__, ret); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return ret; } case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2: @@ -1720,6 +1771,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) payload[1]); wake_up(&ac->cmd_wait); } + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return 0; } if (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2) { @@ -1749,6 +1803,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->mem_state, payload[1]); wake_up(&ac->mem_wait); } + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return 0; } if (atomic_read(&ac->mem_state) && wakeup_flag) { @@ -1785,6 +1842,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0]); break; } + + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1798,6 +1858,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -1811,6 +1874,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0], payload[1]); spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } token = data->token; @@ -1882,6 +1948,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->io_mode & SYNC_IO_MODE) { if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -1968,7 +2037,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->cb) ac->cb(data->opcode, data->token, data->payload, ac->priv); - + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -2144,11 +2214,16 @@ int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, uint32_t pkt_size, uint32_t cmd_flg, uint32_t stream_id) { + unsigned long flags; + dev_vdbg(ac->dev, "%s: pkt_size=%d cmd_flg=%d session=%d stream_id=%d\n", __func__, pkt_size, cmd_flg, ac->session, stream_id); mutex_lock(&ac->cmd_lock); + spin_lock_irqsave(&(session[ac->session].session_lock), flags); if (ac->apr == NULL) { pr_err("%s: AC APR handle NULL", __func__); + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); mutex_unlock(&ac->cmd_lock); return; } @@ -2166,6 +2241,8 @@ static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, hdr->token = ac->session; } hdr->pkt_size = pkt_size; + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); mutex_unlock(&ac->cmd_lock); return; } @@ -7975,7 +8052,7 @@ int q6asm_get_apr_service_id(int session_id) return -EINVAL; } - return ((struct apr_svc *)session[session_id]->apr)->id; + return ((struct apr_svc *)(session[session_id].ac)->apr)->id; } int q6asm_get_asm_topology(int session_id) @@ -7986,12 +8063,12 @@ int q6asm_get_asm_topology(int session_id) pr_err("%s: invalid session_id = %d\n", __func__, session_id); goto done; } - if (session[session_id] == NULL) { + if (session[session_id].ac == NULL) { pr_err("%s: session not created for session id = %d\n", __func__, session_id); goto done; } - topology = session[session_id]->topology; + topology = (session[session_id].ac)->topology; done: return topology; } @@ -8004,12 +8081,12 @@ int q6asm_get_asm_app_type(int session_id) pr_err("%s: invalid session_id = %d\n", __func__, session_id); goto done; } - if (session[session_id] == NULL) { + if (session[session_id].ac == NULL) { pr_err("%s: session not created for session id = %d\n", __func__, session_id); goto done; } - app_type = session[session_id]->app_type; + app_type = (session[session_id].ac)->app_type; done: return app_type; } @@ -8366,7 +8443,10 @@ static int __init q6asm_init(void) int lcnt, ret; pr_debug("%s:\n", __func__); - memset(session, 0, sizeof(session)); + memset(session, 0, sizeof(struct audio_session) * + (SESSION_MAX + 1)); + for (lcnt = 0; lcnt <= SESSION_MAX; lcnt++) + spin_lock_init(&(session[lcnt].session_lock)); set_custom_topology = 1; /*setup common client used for cal mem map */ -- GitLab From 94ac1ae4d0d9b1785337a4e219c1351eb2a5f1c9 Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Mon, 8 Jan 2018 22:24:08 +0530 Subject: [PATCH 2248/5498] defconfig: msm: Enable SYSMON communication interface for msm8909w To provide communication among multiple subsystems enable SYSMON communication interface. Change-Id: Ife0c654a830a9c45f6391a8e83c86bc0f73df15c Signed-off-by: Jitendra Sharma --- arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 2f35e47622b7..9990f1e1b5e3 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -424,6 +424,7 @@ CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_BGRSB=y CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_SYSMON_COMM=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y CONFIG_MSM_PIL_SSR_BG=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index 1ba662efbb3f..e28283cdff0c 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -432,6 +432,7 @@ CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_BGRSB=y CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_SYSMON_COMM=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y CONFIG_MSM_PIL_SSR_BG=y -- GitLab From 3fe4af4ee36658001082e9d07a3348d0dc861b5f Mon Sep 17 00:00:00 2001 From: Tirupathi Reddy Date: Fri, 30 Jun 2017 09:48:45 +0530 Subject: [PATCH 2249/5498] regulator: core: fix a possible race in disable_work handling A race condition between queueing and processing the disable_work instances results in having a work instance in the queue and the deferred_disables variable of regulator device structure having a value '0'. If no new regulator_disable_deferred() call later from clients, the deferred_disables variable value remains '0' and hits BUG() in regulator_disable_work() when the queued instance scheduled for processing the work. The race occurs as below: Core-0 Core-1 ..... /* deferred_disables = 2 */ ..... ..... /* disable_work is queued */ ..... ..... ..... regulator_disable_deferred: regulator_disable_work: mutex_lock(&rdev->mutex); ..... rdev->deferred_disables++; mutex_lock(&rdev->mutex); mutex_unlock(&rdev->mutex); ..... queue_delayed_work(...) count =rdev->deferred_disables; ..... rdev->deferred_disables = 0; ..... ..... ..... mutex_unlock(&rdev->mutex); ..... ..... ..... return; ..... ..... /* No new regulator_disable_deferred() calls from clients */ /* The newly queued instance is scheduled for processing */ ..... ..... regulator_disable_work: ..... mutex_lock(&rdev->mutex); BUG_ON(!rdev->deferred_disables); /* deferred_disables = 0 */ The race is fixed by removing the work instance that is queued while processing the previous queued instance. Cancel the newly queued instance from disable_work() handler just after reset the deferred_disables variable to value '0'. Also move the work queueing step before mutex_unlock in regulator_disable_deferred(). Also use mod_delayed_work() in the pace of queue_delayed_work() as queue_delayed_work() always uses the delay requested in the first call when multiple consumers call regulator_disable_deferred() close in time and does not guarantee the semantics of regulator_disable_deferred(). CRs-Fixed: 2064610 Change-Id: Iacaddc5e2f5c9998c4d038bdc10c4587cbf4812e Signed-off-by: Tirupathi Reddy --- drivers/regulator/core.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 0f2f6939ce10..7b2a4b6ddb87 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2152,6 +2152,14 @@ static void regulator_disable_work(struct work_struct *work) count = rdev->deferred_disables; rdev->deferred_disables = 0; + /* + * Workqueue functions queue the new work instance while the previous + * work instance is being processed. Cancel the queued work instance + * as the work instance under processing does the job of the queued + * work instance. + */ + cancel_delayed_work(&rdev->disable_work); + for (i = 0; i < count; i++) { ret = _regulator_disable(rdev); if (ret != 0) @@ -2186,7 +2194,6 @@ static void regulator_disable_work(struct work_struct *work) int regulator_disable_deferred(struct regulator *regulator, int ms) { struct regulator_dev *rdev = regulator->rdev; - int ret; if (regulator->always_on) return 0; @@ -2196,15 +2203,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) mutex_lock(&rdev->mutex); rdev->deferred_disables++; + mod_delayed_work(system_power_efficient_wq, &rdev->disable_work, + msecs_to_jiffies(ms)); mutex_unlock(&rdev->mutex); - ret = queue_delayed_work(system_power_efficient_wq, - &rdev->disable_work, - msecs_to_jiffies(ms)); - if (ret < 0) - return ret; - else - return 0; + return 0; } EXPORT_SYMBOL_GPL(regulator_disable_deferred); -- GitLab From b5ad84b35729f714db169ce5f72f29e200778e16 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Feb 2017 11:48:58 +0000 Subject: [PATCH 2250/5498] BACKPORT: arm64: Add CNTVCT_EL0 trap handler Since people seem to make a point in breaking the userspace visible counter, we have no choice but to trap the access. Add the required handler. Acked-by: Thomas Gleixner Acked-by: Mark Rutland Signed-off-by: Marc Zyngier (cherry picked from commit 6126ce0588eb5a0752d5c8b5796a7fca324fd887) Change-Id: I0705f47c85a78040df38df18f51a4a22500b904d Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/esr.h | 37 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/entry.S | 12 +++++++++++- arch/arm64/kernel/traps.c | 19 ++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 7cff572082d0..311eacfd4224 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -134,6 +134,43 @@ #define ESR_ELx_COND_MASK (UL(0xF) << ESR_ELx_COND_SHIFT) #define ESR_ELx_WFx_ISS_WFE (UL(1) << 0) +/* ISS field definitions for System instruction traps */ +#define ESR_ELx_SYS64_ISS_RES0_SHIFT 22 +#define ESR_ELx_SYS64_ISS_RES0_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_RES0_SHIFT) +#define ESR_ELx_SYS64_ISS_DIR_MASK 0x1 +#define ESR_ELx_SYS64_ISS_DIR_READ 0x1 +#define ESR_ELx_SYS64_ISS_DIR_WRITE 0x0 + +#define ESR_ELx_SYS64_ISS_RT_SHIFT 5 +#define ESR_ELx_SYS64_ISS_RT_MASK (UL(0x1f) << ESR_ELx_SYS64_ISS_RT_SHIFT) +#define ESR_ELx_SYS64_ISS_CRM_SHIFT 1 +#define ESR_ELx_SYS64_ISS_CRM_MASK (UL(0xf) << ESR_ELx_SYS64_ISS_CRM_SHIFT) +#define ESR_ELx_SYS64_ISS_CRN_SHIFT 10 +#define ESR_ELx_SYS64_ISS_CRN_MASK (UL(0xf) << ESR_ELx_SYS64_ISS_CRN_SHIFT) +#define ESR_ELx_SYS64_ISS_OP1_SHIFT 14 +#define ESR_ELx_SYS64_ISS_OP1_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_OP1_SHIFT) +#define ESR_ELx_SYS64_ISS_OP2_SHIFT 17 +#define ESR_ELx_SYS64_ISS_OP2_MASK (UL(0x7) << ESR_ELx_SYS64_ISS_OP2_SHIFT) +#define ESR_ELx_SYS64_ISS_OP0_SHIFT 20 +#define ESR_ELx_SYS64_ISS_OP0_MASK (UL(0x3) << ESR_ELx_SYS64_ISS_OP0_SHIFT) +#define ESR_ELx_SYS64_ISS_SYS_MASK (ESR_ELx_SYS64_ISS_OP0_MASK | \ + ESR_ELx_SYS64_ISS_OP1_MASK | \ + ESR_ELx_SYS64_ISS_OP2_MASK | \ + ESR_ELx_SYS64_ISS_CRN_MASK | \ + ESR_ELx_SYS64_ISS_CRM_MASK) +#define ESR_ELx_SYS64_ISS_SYS_VAL(op0, op1, op2, crn, crm) \ + (((op0) << ESR_ELx_SYS64_ISS_OP0_SHIFT) | \ + ((op1) << ESR_ELx_SYS64_ISS_OP1_SHIFT) | \ + ((op2) << ESR_ELx_SYS64_ISS_OP2_SHIFT) | \ + ((crn) << ESR_ELx_SYS64_ISS_CRN_SHIFT) | \ + ((crm) << ESR_ELx_SYS64_ISS_CRM_SHIFT)) + +#define ESR_ELx_SYS64_ISS_SYS_OP_MASK (ESR_ELx_SYS64_ISS_SYS_MASK | \ + ESR_ELx_SYS64_ISS_DIR_MASK) + +#define ESR_ELx_SYS64_ISS_SYS_CNTVCT (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 2, 14, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + #ifndef __ASSEMBLY__ #include diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index ec2a0f820931..c2c3c0d95cb0 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -518,7 +518,7 @@ el0_sync: cmp x24, #ESR_ELx_EC_FP_EXC64 // FP/ASIMD exception b.eq el0_fpsimd_exc cmp x24, #ESR_ELx_EC_SYS64 // configurable trap - b.eq el0_undef + b.eq el0_sys cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception b.eq el0_sp_pc cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception @@ -644,6 +644,16 @@ el0_undef: mov x0, sp bl do_undefinstr b ret_to_user +el0_sys: + /* + * System instructions, for trapped cache maintenance instructions + */ + enable_dbg_and_irq + ct_user_exit + mov x0, x25 + mov x1, sp + bl do_sysinstr + b ret_to_user el0_dbg: /* * Debug exception handling diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index b0b9ea7aed4f..6b377655b25e 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -346,6 +346,25 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) arm64_notify_die("Oops - undefined instruction", regs, &info, 0); } +static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs) +{ + int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT; + + if (rt != 31) + regs->regs[rt] = arch_counter_get_cntvct(); + regs->pc += 4; +} + +asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs) +{ + if ((esr & ESR_ELx_SYS64_ISS_SYS_OP_MASK) == ESR_ELx_SYS64_ISS_SYS_CNTVCT) { + cntvct_read_handler(esr, regs); + return; + } + + do_undefinstr(regs); +} + long compat_arm_syscall(struct pt_regs *regs); asmlinkage long do_ni_syscall(struct pt_regs *regs) -- GitLab From eece7769c37456695c9cb6d7e05a5a7d8faf8849 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 24 Apr 2017 09:04:03 +0100 Subject: [PATCH 2251/5498] BACKPORT: arm64: Add CNTFRQ_EL0 trap handler We now trap accesses to CNTVCT_EL0 when the counter is broken enough to require the kernel to mediate the access. But it turns out that some existing userspace (such as OpenMPI) do probe for the counter frequency, leading to an UNDEF exception as CNTVCT_EL0 and CNTFRQ_EL0 share the same control bit. The fix is to handle the exception the same way we do for CNTVCT_EL0. Fixes: a86bd139f2ae ("arm64: arch_timer: Enable CNTVCT_EL0 trap if workaround is enabled") Reported-by: Hanjun Guo Tested-by: Hanjun Guo Reviewed-by: Hanjun Guo Signed-off-by: Marc Zyngier Signed-off-by: Catalin Marinas (cherry picked from commit 9842119a238bfb92cbab63258dabb54f0e7b111b) Change-Id: I1bde5dc8f2fbdc94f66bc6606b88ad1817788080 Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/esr.h | 3 +++ arch/arm64/kernel/traps.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 311eacfd4224..f1d9b6261f55 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -171,6 +171,9 @@ #define ESR_ELx_SYS64_ISS_SYS_CNTVCT (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 2, 14, 0) | \ ESR_ELx_SYS64_ISS_DIR_READ) +#define ESR_ELx_SYS64_ISS_SYS_CNTFRQ (ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 0, 14, 0) | \ + ESR_ELx_SYS64_ISS_DIR_READ) + #ifndef __ASSEMBLY__ #include diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 6b377655b25e..2d21335cc68c 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -355,11 +355,23 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs) regs->pc += 4; } +static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs) +{ + int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT; + + if (rt != 31) + regs->regs[rt] = read_sysreg(cntfrq_el0); + regs->pc += 4; +} + asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs) { if ((esr & ESR_ELx_SYS64_ISS_SYS_OP_MASK) == ESR_ELx_SYS64_ISS_SYS_CNTVCT) { cntvct_read_handler(esr, regs); return; + } else if ((esr & ESR_ELx_SYS64_ISS_SYS_OP_MASK) == ESR_ELx_SYS64_ISS_SYS_CNTFRQ) { + cntfrq_read_handler(esr, regs); + return; } do_undefinstr(regs); -- GitLab From e07b57a9b8eb03e66ee1322be22780acc343c3c6 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 4 Oct 2017 09:31:34 -0700 Subject: [PATCH 2252/5498] arm64: issue isb when trapping CNTVCT_EL0 access Change-Id: I6005a6e944494257bfc2243fde2f7a09c3fd76c6 Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/traps.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 2d21335cc68c..d97ae53d4b61 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -350,6 +351,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs) { int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT; + isb(); if (rt != 31) regs->regs[rt] = arch_counter_get_cntvct(); regs->pc += 4; -- GitLab From f5613a0ab370defd476b90b8b6655b4f9639f50c Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 19 Sep 2017 10:55:17 -0700 Subject: [PATCH 2253/5498] clocksource: arch_timer: make virtual counter access configurable Change-Id: Ibdb1fd768b748002b90bfc165612c12c8311f8a2 Signed-off-by: Greg Kroah-Hartman --- drivers/clocksource/Kconfig | 8 ++++++++ drivers/clocksource/arm_arch_timer.c | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 90420600e1eb..2a6b2a8e1578 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -110,6 +110,14 @@ config ARM_ARCH_TIMER_EVTSTREAM This must be disabled for hardware validation purposes to detect any hardware anomalies of missing events. +config ARM_ARCH_TIMER_VCT_ACCESS + bool "Support for ARM architected timer virtual counter access in userspace" + default !ARM64 + depends on ARM_ARCH_TIMER + help + This option enables support for reading the ARM architected timer's + virtual counter in userspace. + config ARM_GLOBAL_TIMER bool select CLKSRC_OF if OF diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 84b4c8b7fbd1..fdf3959f81c4 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -339,7 +339,10 @@ static void arch_counter_set_user_access(void) | ARCH_TIMER_USR_PCT_ACCESS_EN); /* Enable user access to the virtual counter */ - cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; + if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS)) + cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; + else + cntkctl &= ~ARCH_TIMER_USR_VCT_ACCESS_EN; arch_timer_set_cntkctl(cntkctl); } -- GitLab From f73a1ef7d7b53690db663f9b31a5e3297132e99d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 9 Jan 2018 16:52:25 -0800 Subject: [PATCH 2254/5498] fscrypt: resolve some cherry-pick bugs - remove wrong linux/fscrypt.h declared in ext4 - remove obsolete function Fixes: 734f0d241d2b ("fscrypt: clean up include file mess") Signed-off-by: Jaegeuk Kim --- fs/ext4/ext4.h | 3 --- include/linux/fscrypt.h | 11 ----------- 2 files changed, 14 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index c37fa001514e..bd997b3d6a6f 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -36,9 +36,6 @@ #include #endif -#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) -#include - /* * The fourth extended filesystem constants/structures */ diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 6a939e0d30af..8641e56b8f8a 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -49,17 +49,6 @@ struct fscrypt_symlink_data { char encrypted_path[1]; } __packed; -/** - * This function is used to calculate the disk space required to - * store a filename of length l in encrypted symlink format. - */ -static inline u32 fscrypt_symlink_data_len(u32 l) -{ - if (l < FS_CRYPTO_BLOCK_SIZE) - l = FS_CRYPTO_BLOCK_SIZE; - return (l + sizeof(struct fscrypt_symlink_data) - 1); -} - struct fscrypt_str { unsigned char *name; u32 len; -- GitLab From c784814151089e0fe218c59bf12e2981e1826147 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Thu, 28 Dec 2017 16:28:05 +0530 Subject: [PATCH 2255/5498] ASoC: wcd9335: set codec TX path to tri-state I2S TX line is shared between the Codec and TDM mics, tristate the WCD mics to avoid PCM interference as the end product uses only TDM mics. CRs-Fixed: 2164870 Change-Id: I167e8af416defc631ca10a9e6e0cbe565d5a8acd Signed-off-by: Surendar karka --- .../devicetree/bindings/sound/taiko_codec.txt | 4 +++ drivers/mfd/wcd9xxx-core.c | 4 ++- include/linux/mfd/wcd9xxx/pdata.h | 3 +- sound/soc/codecs/wcd9335.c | 28 ++++++++++++++++++- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt index 90bef273363d..5f8d2938b187 100644 --- a/Documentation/devicetree/bindings/sound/taiko_codec.txt +++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt @@ -536,6 +536,10 @@ Tasha audio CODEC in I2C mode - qcom,cdc-dmic-sample-rate - Specifies dmic sample rate. - qcom,cdc-variant - Specifies codec variant. + - qcom,wcd9xxx-mic-tristate: For chipsets where I2S TX line is shared between + the Codec and TDM mics, tristate the WCD mics to + avoid PCM interference as the end product uses + only TDM mics. Example: i2c_3: i2c@78B7000 { /* BLSP1 QUP3 */ diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c index de1b1ba28f83..c8c5efd3c4b9 100644 --- a/drivers/mfd/wcd9xxx-core.c +++ b/drivers/mfd/wcd9xxx-core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2881,6 +2881,8 @@ static struct wcd9xxx_pdata *wcd9xxx_populate_dt_pdata(struct device *dev) else pdata->cdc_variant = WCD9XXX; } + pdata->wcd9xxx_mic_tristate = of_property_read_bool(dev->of_node, + "qcom,wcd9xxx-mic-tristate"); return pdata; err: diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h index f3cbfbe80c7c..d876386d8ace 100644 --- a/include/linux/mfd/wcd9xxx/pdata.h +++ b/include/linux/mfd/wcd9xxx/pdata.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -192,6 +192,7 @@ struct wcd9xxx_pdata { u32 mic_unmute_delay; enum codec_variant cdc_variant; u16 use_pinctrl; + bool wcd9xxx_mic_tristate; }; #endif diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 7ad240383596..cf5af8ea6a3d 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -851,6 +851,15 @@ static const struct tasha_reg_mask_val tasha_spkr_mode1[] = { {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, }; +static const struct tasha_reg_mask_val tasha_high_impedance[] = { + {WCD9335_TLMM_I2S_TX_SD0_PINCFG, 0x1F, 0x0C}, + {WCD9335_TLMM_I2S_TX_SD1_PINCFG, 0x1F, 0x0C}, + {WCD9335_TLMM_I2S_TX_SCK_PINCFG, 0x1F, 0x0C}, + {WCD9335_TLMM_I2S_TX_WS_PINCFG, 0x1F, 0x0C}, + {WCD9335_TEST_DEBUG_PIN_CTL_OE_1, 0xE0, 0xE0}, + {WCD9335_TEST_DEBUG_PIN_CTL_OE_2, 0x01, 0x01}, +}; + /** * tasha_set_spkr_gain_offset - offset the speaker path * gain with the given offset value. @@ -954,6 +963,20 @@ static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag) } } +static void tasha_set_high_impedance_mode(struct snd_soc_codec *codec) +{ + const struct tasha_reg_mask_val *regs; + int i, size; + + dev_dbg(codec->dev, "%s: setting TX I2S in Hi-Z mode\n", __func__); + regs = tasha_high_impedance; + size = ARRAY_SIZE(tasha_high_impedance); + + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); +} + static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha) { if (TASHA_IS_2_0(tasha->wcd9xxx->version) && @@ -13941,6 +13964,9 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) mutex_unlock(&codec->mutex); snd_soc_dapm_sync(dapm); + if (pdata->wcd9xxx_mic_tristate) + tasha_set_high_impedance_mode(codec); + return ret; err_pdata: -- GitLab From 7a519b3f856b7657e1f1d14b3f7c4080ee5a2c47 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Tue, 26 Dec 2017 18:00:39 +0530 Subject: [PATCH 2256/5498] ARM: dts: msm: Update modem size for BG targets Re-factor and update memory carveout configuration for BlackGhost apq and msm targets into separate file and include it directly. Increase modem firmware region by 2MB for these targets. Change-Id: I0421e6e0fd2c9580f8cf8f769287b6afafa81b2a Signed-off-by: Shiraz Hashim --- arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts | 4 ++-- .../arm/boot/dts/qcom/apq8009w-bg-memory.dtsi | 24 +++++++++++++++++++ arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v1.dts | 4 ++-- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 4 ++-- .../arm/boot/dts/qcom/msm8909w-bg-memory.dtsi | 24 +++++++++++++++++++ arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts | 4 ++-- arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 4 ++-- 7 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi create mode 100644 arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts index bc2aba2a79da..390f9c4af2d4 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,7 +17,7 @@ #include "msm8909w-gpu.dtsi" #include "msm8909w.dtsi" #include "msm8909w-pm660-mtp.dtsi" -#include "apq8009w-memory.dtsi" +#include "apq8009w-bg-memory.dtsi" #include "msm8909-audio-bg_codec.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi b/arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi new file mode 100644 index 000000000000..aab25f897437 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&external_image_mem { + reg = <0x0 0x87b00000 0x0 0x0500000>; +}; + +&modem_adsp_mem { + reg = <0x0 0x88000000 0x0 0x02300000>; +}; + +&peripheral_mem { + reg = <0x0 0x8a300000 0x0 0x0500000>; +}; diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v1.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v1.dts index 55e9719d4a61..8c86cc099f6a 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v1.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,7 +18,7 @@ #include "msm8909-pm8916.dtsi" #include "msm8909-pm8916-mtp.dtsi" #include "msm8909w.dtsi" -#include "apq8009w-memory.dtsi" +#include "apq8009w-bg-memory.dtsi" / { model = "Qualcomm Technologies, Inc. APQ8009W-PM8916 BLACKGHOST WTP"; diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 0ed328e5f9f5..ed04e197a670 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,7 +17,7 @@ #include "msm8909w-gpu.dtsi" #include "msm8909w.dtsi" #include "msm8909w-pm660-mtp.dtsi" -#include "apq8009w-memory.dtsi" +#include "apq8009w-bg-memory.dtsi" #include "msm8909-audio-bg_codec.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi b/arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi new file mode 100644 index 000000000000..9dea1e10da0f --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&external_image_mem { + reg = <0x0 0x87b00000 0x0 0x0500000>; +}; + +&modem_adsp_mem { + reg = <0x0 0x88000000 0x0 0x05200000>; +}; + +&peripheral_mem { + reg = <0x0 0x8d200000 0x0 0x0500000>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts index 303120c72155..6f9a75627984 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v1.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,7 +18,7 @@ #include "msm8909-pm8916.dtsi" #include "msm8909-pm8916-mtp.dtsi" #include "msm8909w.dtsi" -#include "msm8909w-memory.dtsi" +#include "msm8909w-bg-memory.dtsi" #include "msm8909-audio-bg_codec.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 40e3a3f969d5..84c3ac427bf5 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,7 +16,7 @@ #include "msm8909-mtp.dtsi" #include "msm8909w-gpu.dtsi" #include "msm8909w.dtsi" -#include "msm8909w-memory.dtsi" +#include "msm8909w-bg-memory.dtsi" #include "msm8909w-pm660-mtp.dtsi" #include "msm8909-audio-bg_codec.dtsi" -- GitLab From 515605b44044f09bb8388891d3774e77b793ef37 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Tue, 9 Jan 2018 19:45:54 +0530 Subject: [PATCH 2257/5498] msm: kgsl: Create GPU device node based on gfx fuse Create GPU device node only if the fuse is not blown. If the fuse is blown to non zero value then fail creation of GPU device node and return with an error from adreno_probe(). Change-Id: Id1c0f1d9c1e22ddd1e81cca5b267966b28319064 Signed-off-by: Sunil Khatri --- .../devicetree/bindings/gpu/adreno.txt | 6 ++++ drivers/gpu/msm/adreno.c | 34 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index 0a5fb5807f00..f75395e7b824 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -124,6 +124,12 @@ Optional Properties: mask - mask for the relevant bits in the efuse register. shift - number of bits to right shift to get the speed bin value. +- qcom,gpu-disable-fuse: GPU disable fuse + + offset - offset of the efuse register from the base. + mask - mask for the relevant bits in the efuse register. + shift - number of bits to right shift to get the disable_gpu + fuse bit value. - qcom,highest-bank-bit: Specify the bit of the highest DDR bank. This is programmed into protected registers and also diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 31fb578e76c5..020d832220bb 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -955,6 +955,33 @@ adreno_ocmem_free(struct adreno_device *adreno_dev) } #endif +static bool adreno_is_gpu_disabled(struct adreno_device *adreno_dev) +{ + unsigned int row0; + unsigned int pte_row0_msb[3]; + int ret; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + if (of_property_read_u32_array(device->pdev->dev.of_node, + "qcom,gpu-disable-fuse", pte_row0_msb, 3)) + return false; + /* + * Read the fuse value to disable GPU driver if fuse + * is blown. By default(fuse value is 0) GPU is enabled. + */ + if (adreno_efuse_map(adreno_dev)) + return false; + + ret = adreno_efuse_read_u32(adreno_dev, pte_row0_msb[0], &row0); + adreno_efuse_unmap(adreno_dev); + + if (ret) + return false; + + return (row0 >> pte_row0_msb[2]) & + pte_row0_msb[1] ? true : false; +} + static int adreno_probe(struct platform_device *pdev) { struct kgsl_device *device; @@ -971,6 +998,11 @@ static int adreno_probe(struct platform_device *pdev) device = KGSL_DEVICE(adreno_dev); device->pdev = pdev; + if (adreno_is_gpu_disabled(adreno_dev)) { + pr_err("adreno: GPU is disabled on this device"); + return -ENODEV; + } + /* Get the chip ID from the DT and set up target specific parameters */ adreno_identify_gpu(adreno_dev); -- GitLab From acfe04d9d1e7c7d80d28ebec1e3c1dc6d1f047ad Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 13 Dec 2017 12:52:15 +0530 Subject: [PATCH 2258/5498] ARM: dts: msm: Add property to disable GPU driver for msm8909 Disable GPU driver for A304 based on the fuse value. Change-Id: I67602c510ff20594f0ee42b656f50fe547d4c39e Signed-off-by: Sunil Khatri --- arch/arm/boot/dts/qcom/msm8909-gpu.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi index 9f4a397188de..44684d317bb0 100644 --- a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -57,6 +57,7 @@ qcom,idle-timeout = <80>; //msec qcom,strtstp-sleepwake; + qcom,gpu-disable-fuse = <0x44 0x00000001 27>; /* * Clocks = KGSL_CLK_CORE | KGSL_CLK_IFACE | -- GitLab From bfbe9b937ebae73cf18cacfec8498b6488513e3d Mon Sep 17 00:00:00 2001 From: Hardik Arya Date: Mon, 11 Sep 2017 11:37:33 +0530 Subject: [PATCH 2259/5498] diag: Add NULL pointer checks for mask info Currently there is a possibility of NULL pointer dereference issue due to NULL pointers checks are missing for mask info. The patch fixes the issue by adding NULL pointer checks. CRs-Fixed: 2108911 Change-Id: I097aeacc90c8d88034bf799ee4e398ddda5e23f1 Signed-off-by: Hardik Arya --- drivers/char/diag/diag_masks.c | 128 ++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 8 deletions(-) diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 7b2fa5814731..fe2115873294 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -500,6 +500,11 @@ static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } if (!diag_apps_responds()) return 0; @@ -605,7 +610,11 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } - + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } if (!diag_apps_responds()) return 0; @@ -618,6 +627,12 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, rsp.status = MSG_STATUS_FAIL; rsp.padding = 0; mask = (struct diag_msg_mask_t *)mask_info->ptr; + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + mutex_unlock(&driver->msg_mask_lock); + return -EINVAL; + } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { if ((req->ssid_first < mask->ssid_first) || (req->ssid_first > mask->ssid_last_tools)) { @@ -664,11 +679,23 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } req = (struct diag_msg_build_mask_t *)src_buf; mutex_lock(&mask_info->lock); mutex_lock(&driver->msg_mask_lock); mask = (struct diag_msg_mask_t *)mask_info->ptr; + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); + return -EINVAL; + } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { if (i < (driver->msg_mask_tbl_count - 1)) { mask_next = mask; @@ -779,6 +806,11 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } req = (struct diag_msg_config_rsp_t *)src_buf; @@ -786,6 +818,13 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, mutex_lock(&driver->msg_mask_lock); mask = (struct diag_msg_mask_t *)mask_info->ptr; + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); + return -EINVAL; + } mask_info->status = (req->rt_mask) ? DIAG_CTRL_MASK_ALL_ENABLED : DIAG_CTRL_MASK_ALL_DISABLED; for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { @@ -877,7 +916,11 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } - + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } req = (struct diag_event_mask_config_t *)src_buf; mask_len = EVENT_COUNT_TO_BYTES(req->num_bits); if (mask_len <= 0 || mask_len > event_mask.mask_len) { @@ -933,6 +976,11 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } toggle = *(src_buf + 1); mutex_lock(&mask_info->lock); @@ -988,6 +1036,11 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } if (!diag_apps_responds()) return 0; @@ -1007,6 +1060,11 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, write_len += rsp_header_len; log_item = (struct diag_log_mask_t *)mask_info->ptr; + if (!log_item->ptr) { + pr_err("diag: Invalid input in %s, mask: %pK\n", + __func__, log_item); + return -EINVAL; + } for (i = 0; i < MAX_EQUIP_ID; i++, log_item++) { if (log_item->equip_id != req->equip_id) continue; @@ -1114,11 +1172,20 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } req = (struct diag_log_config_req_t *)src_buf; read_len += req_header_len; mask = (struct diag_log_mask_t *)mask_info->ptr; - + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + return -EINVAL; + } if (req->equip_id >= MAX_EQUIP_ID) { pr_err("diag: In %s, Invalid logging mask request, equip_id: %d\n", __func__, req->equip_id); @@ -1234,9 +1301,17 @@ static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, mask_info); return -EINVAL; } - + if (!mask_info->ptr) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", + __func__, mask_info->ptr); + return -EINVAL; + } mask = (struct diag_log_mask_t *)mask_info->ptr; - + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + return -EINVAL; + } for (i = 0; i < MAX_EQUIP_ID; i++, mask++) { mutex_lock(&mask->lock); memset(mask->ptr, 0, mask->range); @@ -1500,7 +1575,7 @@ static int __diag_mask_init(struct diag_mask_info *mask_info, int mask_len, static void __diag_mask_exit(struct diag_mask_info *mask_info) { - if (!mask_info) + if (!mask_info || !mask_info->ptr) return; mutex_lock(&mask_info->lock); @@ -1557,11 +1632,17 @@ void diag_log_mask_free(struct diag_mask_info *mask_info) int i; struct diag_log_mask_t *mask = NULL; - if (!mask_info) + if (!mask_info || !mask_info->ptr) return; mutex_lock(&mask_info->lock); mask = (struct diag_log_mask_t *)mask_info->ptr; + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + mutex_unlock(&mask_info->lock); + return; + } for (i = 0; i < MAX_EQUIP_ID; i++, mask++) { kfree(mask->ptr); mask->ptr = NULL; @@ -1636,11 +1717,18 @@ void diag_msg_mask_free(struct diag_mask_info *mask_info) int i; struct diag_msg_mask_t *mask = NULL; - if (!mask_info) + if (!mask_info || !mask_info->ptr) return; mutex_lock(&mask_info->lock); mutex_lock(&driver->msg_mask_lock); mask = (struct diag_msg_mask_t *)mask_info->ptr; + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); + return; + } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { kfree(mask->ptr); mask->ptr = NULL; @@ -1807,6 +1895,11 @@ int diag_copy_to_user_msg_mask(char __user *buf, size_t count, if (!mask_info) return -EIO; + if (!mask_info->ptr || !mask_info->update_buf) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK, mask_info->update_buf: %pK\n", + __func__, mask_info->ptr, mask_info->update_buf); + return -EINVAL; + } mutex_lock(&driver->diag_maskclear_mutex); if (driver->mask_clear) { DIAG_LOG(DIAG_DEBUG_PERIPHERALS, @@ -1819,6 +1912,13 @@ int diag_copy_to_user_msg_mask(char __user *buf, size_t count, mutex_lock(&driver->msg_mask_lock); mask = (struct diag_msg_mask_t *)(mask_info->ptr); + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&mask_info->lock); + return -EINVAL; + } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { ptr = mask_info->update_buf; len = 0; @@ -1879,8 +1979,20 @@ int diag_copy_to_user_log_mask(char __user *buf, size_t count, if (!mask_info) return -EIO; + if (!mask_info->ptr || !mask_info->update_buf) { + pr_err("diag: In %s, invalid input mask_info->ptr: %pK, mask_info->update_buf: %pK\n", + __func__, mask_info->ptr, mask_info->update_buf); + return -EINVAL; + } + mutex_lock(&mask_info->lock); mask = (struct diag_log_mask_t *)(mask_info->ptr); + if (!mask->ptr) { + pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", + __func__, mask->ptr); + mutex_unlock(&mask_info->lock); + return -EINVAL; + } for (i = 0; i < MAX_EQUIP_ID; i++, mask++) { ptr = mask_info->update_buf; len = 0; -- GitLab From 5056d30e4e90dc92fc88f086af645674e8371ef6 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Wed, 14 Jun 2017 12:40:34 +0530 Subject: [PATCH 2260/5498] msm:ipa: Fix to IPA static analysis constant null deferenced Adding code changes to reslove IPA static analysis issue Constant NULL dereferenced by passing to other function. Change-Id: I07bd7ac42929576429fc4548cfce7d202d77364f Acked-by: Pooja Kumari Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 3 ++- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 00af4cf2a920..6908cd3cc96b 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2692,7 +2692,7 @@ int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, kfree(req); kfree(resp); return rc; - } else if (reset) { + } else if (data == NULL) { kfree(req); kfree(resp); return 0; @@ -2823,6 +2823,7 @@ int rmnet_ipa_query_tethering_stats_all( return rc; } + /** * ipa_broadcast_quota_reach_ind() - Send Netlink broadcast on Quota * @mux_id - The MUX ID on which the quota has been reached diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 49bcefcbd29b..53e4e4549e2e 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -2850,7 +2850,7 @@ int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, kfree(req); kfree(resp); return rc; - } else if (reset) { + } else if (data == NULL) { kfree(req); kfree(resp); return 0; -- GitLab From ec7214a251ed5e4ac41f65edd53aa427f187ad77 Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Wed, 10 Jan 2018 17:59:48 +0530 Subject: [PATCH 2261/5498] defconfig: msm: Enable SYSMON communication interface for sdx To provide communication among multiple subsystems, enable SYSMON communication interface. Change-Id: I077b7086fad7ee8a8ba311e2292b478b1a785a57 Signed-off-by: Jitendra Sharma --- arch/arm/configs/sdx-perf_defconfig | 1 + arch/arm/configs/sdx_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/sdx-perf_defconfig b/arch/arm/configs/sdx-perf_defconfig index b6b8df2cf35a..afe3644289c4 100644 --- a/arch/arm/configs/sdx-perf_defconfig +++ b/arch/arm/configs/sdx-perf_defconfig @@ -363,6 +363,7 @@ CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_SYSMON_COMM=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y CONFIG_MSM_PIL_MSS_QDSP6V5=y diff --git a/arch/arm/configs/sdx_defconfig b/arch/arm/configs/sdx_defconfig index fe39c0b70d01..55a178617c29 100644 --- a/arch/arm/configs/sdx_defconfig +++ b/arch/arm/configs/sdx_defconfig @@ -374,6 +374,7 @@ CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_SYSMON_COMM=y CONFIG_MSM_PIL=y CONFIG_MSM_PIL_SSR_GENERIC=y CONFIG_MSM_PIL_MSS_QDSP6V5=y -- GitLab From 69162a8ea9998f82bb835494e5929f9270345c92 Mon Sep 17 00:00:00 2001 From: Amit Nischal Date: Fri, 29 Dec 2017 16:15:56 +0530 Subject: [PATCH 2262/5498] clk: msm: Add support for bb_clk3 for MSM8909w-PM660 device For MSM8909w-PM660, there is requirement to add RPM managed bb_clk3 clock for NFC operation so add support for the same. Change-Id: I27ea6508396c877a3f33c97411d638fb2a1009fa Signed-off-by: Amit Nischal --- .../bindings/arm/msm/clock-controller.txt | 1 + drivers/clk/msm/clock-rpm-8909.c | 72 +++++++++++++++++-- include/dt-bindings/clock/msm-clocks-8909.h | 4 +- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt index 5d376058a39c..c81ee698f107 100644 --- a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt +++ b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt @@ -33,6 +33,7 @@ Required properties: "qcom,rpmcc-8916" "qcom,rpmcc-8936" "qcom,rpmcc-8909" + "qcom,rpmcc-8909-pm660" "qcom,cc-debug-8916" "qcom,cc-debug-8936" "qcom,cc-debug-8909" diff --git a/drivers/clk/msm/clock-rpm-8909.c b/drivers/clk/msm/clock-rpm-8909.c index f2705927bab9..f58db455823c 100644 --- a/drivers/clk/msm/clock-rpm-8909.c +++ b/drivers/clk/msm/clock-rpm-8909.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -50,6 +50,7 @@ /* XO clock */ #define BB_CLK1_ID 1 #define BB_CLK2_ID 2 +#define BB_CLK3_ID 3 #define RF_CLK2_ID 5 static void __iomem *virt_base; @@ -68,10 +69,12 @@ DEFINE_CLK_RPM_SMD_QDSS(qdss_clk, qdss_a_clk, RPM_MISC_CLK_TYPE, QDSS_ID); /* SMD_XO_BUFFER */ DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk1, bb_clk1_a, BB_CLK1_ID); DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk2, bb_clk2_a, BB_CLK2_ID); +DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk3, bb_clk3_a, BB_CLK3_ID); DEFINE_CLK_RPM_SMD_XO_BUFFER(rf_clk2, rf_clk2_a, RF_CLK2_ID); DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk1_pin, bb_clk1_a_pin, BB_CLK1_ID); DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk2_pin, bb_clk2_a_pin, BB_CLK2_ID); +DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk3_pin, bb_clk3_a_pin, BB_CLK3_ID); DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(rf_clk2_pin, rf_clk2_a_pin, RF_CLK2_ID); /* Voter clocks */ @@ -171,15 +174,69 @@ static struct clk_lookup msm_clocks_rpm[] = { CLK_LIST(rpm_debug_mux), }; +/* Lookup Table for MSM8909w-PM660 MTP */ +static struct clk_lookup msm_clocks_rpm_8909_pm660[] = { + CLK_LIST(xo_clk_src), + CLK_LIST(xo_a_clk_src), + CLK_LIST(xo_otg_clk), + CLK_LIST(xo_lpm_clk), + CLK_LIST(xo_pil_mss_clk), + CLK_LIST(xo_pil_pronto_clk), + CLK_LIST(xo_wlan_clk), + + CLK_LIST(snoc_msmbus_clk), + CLK_LIST(snoc_msmbus_a_clk), + CLK_LIST(snoc_mm_msmbus_clk), + CLK_LIST(snoc_mm_msmbus_a_clk), + CLK_LIST(pcnoc_msmbus_clk), + CLK_LIST(pcnoc_msmbus_a_clk), + CLK_LIST(bimc_msmbus_clk), + CLK_LIST(bimc_msmbus_a_clk), + CLK_LIST(pcnoc_keepalive_a_clk), + + CLK_LIST(pcnoc_usb_a_clk), + CLK_LIST(snoc_usb_a_clk), + CLK_LIST(bimc_usb_a_clk), + + /* CoreSight clocks */ + CLK_LIST(qdss_clk), + CLK_LIST(qdss_a_clk), + + CLK_LIST(snoc_clk), + CLK_LIST(pcnoc_clk), + CLK_LIST(bimc_clk), + CLK_LIST(snoc_a_clk), + CLK_LIST(pcnoc_a_clk), + CLK_LIST(bimc_a_clk), + CLK_LIST(qpic_clk), + CLK_LIST(qpic_a_clk), + + CLK_LIST(bb_clk1), + CLK_LIST(bb_clk2), + CLK_LIST(bb_clk3), + CLK_LIST(rf_clk2), + + CLK_LIST(bb_clk1_pin), + CLK_LIST(bb_clk2_pin), + CLK_LIST(bb_clk3_pin), + CLK_LIST(rf_clk2_pin), + + /* RPM debug Mux*/ + CLK_LIST(rpm_debug_mux), +}; + static int msm_rpmcc_8909_probe(struct platform_device *pdev) { struct resource *res; - int ret; + int ret, is_8909_pm660 = 0; ret = enable_rpm_scaling(); if (ret) return ret; + is_8909_pm660 = of_device_is_compatible(pdev->dev.of_node, + "qcom,rpmcc-8909-pm660"); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base"); if (!res) { dev_err(&pdev->dev, "Unable to get register base\n"); @@ -192,8 +249,14 @@ static int msm_rpmcc_8909_probe(struct platform_device *pdev) return -ENOMEM; } - ret = of_msm_clock_register(pdev->dev.of_node, msm_clocks_rpm, - ARRAY_SIZE(msm_clocks_rpm)); + if (is_8909_pm660) + ret = of_msm_clock_register(pdev->dev.of_node, + msm_clocks_rpm_8909_pm660, + ARRAY_SIZE(msm_clocks_rpm_8909_pm660)); + else + ret = of_msm_clock_register(pdev->dev.of_node, msm_clocks_rpm, + ARRAY_SIZE(msm_clocks_rpm)); + if (ret) { dev_err(&pdev->dev, "Unable to register RPM clocks\n"); return ret; @@ -214,6 +277,7 @@ static int msm_rpmcc_8909_probe(struct platform_device *pdev) static struct of_device_id msm_clk_rpm_match_table[] = { { .compatible = "qcom,rpmcc-8909" }, + { .compatible = "qcom,rpmcc-8909-pm660" }, {} }; diff --git a/include/dt-bindings/clock/msm-clocks-8909.h b/include/dt-bindings/clock/msm-clocks-8909.h index 23cdd18b01a3..7124c4e8fcbf 100644 --- a/include/dt-bindings/clock/msm-clocks-8909.h +++ b/include/dt-bindings/clock/msm-clocks-8909.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -209,6 +209,8 @@ #define clk_bb_clk1_pin 0x6dd0a779 #define clk_bb_clk2 0xfe15cb87 #define clk_bb_clk2_pin 0x498938e5 +#define clk_bb_clk3 0x3a9e99a8 +#define clk_bb_clk3_pin 0x3a96c14c #define clk_rf_clk1 0xaabeea5a #define clk_rf_clk1_pin 0x8f463562 #define clk_rf_clk2 0x24a30992 -- GitLab From a9c40d0644d1ff19f904a8e149f37c8e490c5974 Mon Sep 17 00:00:00 2001 From: Amit Nischal Date: Fri, 29 Dec 2017 16:29:22 +0530 Subject: [PATCH 2263/5498] ARM: dts: msm: Add support for bb_clk3 for MSM8909w-PM660 device There is a requirement to add bb_clk3 clock for NFC operation so add support for the same by registering extra clocks only for MSM8909w-PM660 device with the help of overriding compatible property for RPM clock node. Change-Id: I66d5b47a9712477eb4cf9ad47e436d70f5522b93 Signed-off-by: Amit Nischal --- arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index e6535b112e6e..c597ff13b98f 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -168,6 +168,10 @@ qcom,clock-a7@0b011050 { cpu-vdd-supply = <&apc_vreg_corner>; }; + + qcom,rpmcc@1800000 { + compatible = "qcom,rpmcc-8909-pm660"; + }; }; &sdhc_1 { -- GitLab From ee84bcbaa537f7c899011e0b25e92ea3e9dfb6d1 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Tue, 9 Jan 2018 14:54:19 +0530 Subject: [PATCH 2264/5498] ASoC: audio-ext-clk: enable lpass_mclk for LPASS_CLK_VER_2 Sound card is not getting detected as mclk ID is not properly set for AVS service version LPASS_CLK_VER_2. Enable lpass_mclk for LPASS_CLK_VER_2 AVS service version. CRs-Fixed: 2169190 Change-Id: I4c4545a75bc5bbdfd86a36989ca20462292c94ec Signed-off-by: Surendar karka --- sound/soc/codecs/audio-ext-clk.c | 69 +++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c index fda1158682d9..4004fa74c9ba 100644 --- a/sound/soc/codecs/audio-ext-clk.c +++ b/sound/soc/codecs/audio-ext-clk.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -229,31 +229,62 @@ done: return ret; } -static int audio_ext_set_lpass_mclk_v2(enum clk_enablement enable) +static int audio_ext_set_lpass_mclk_v2(struct clk *clk, + enum clk_enablement enable) { struct afe_clk_set m_clk = lpass_default2; struct afe_clk_set ibit_clk = lpass_default2; - int ret = 0; + struct audio_ext_lpass_mclk *audio_lpass_mclk; + int ret = 0, val = 0; pr_debug("%s: Setting clock using v2, enable(%d)\n", __func__, enable); - /* Set both mclk and ibit clocks when using LPASS_CLK_VER_2 */ - m_clk.enable = enable; - ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, &m_clk); - if (ret < 0) { - pr_err("%s: afe_set_lpass_clock_v2 failed for mclk_3 with ret %d\n", - __func__, ret); + audio_lpass_mclk = container_of(clk, struct audio_ext_lpass_mclk, c); + if (audio_lpass_mclk == NULL) { + pr_err("%s: audio_lpass_mclk is NULL\n", __func__); + ret = -EINVAL; goto done; } - ibit_clk.clk_id = Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT; - ibit_clk.clk_freq_in_hz = Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ; - ibit_clk.enable = enable; - ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, &ibit_clk); - if (ret < 0) { - pr_err("%s: afe_set_lpass_clock_v2 failed for ibit with ret %d\n", - __func__, ret); - goto err_ibit_clk_set; + if (!audio_lpass_mclk->lpass_clock) { + /* Set both mclk and ibit clocks when using LPASS_CLK_VER_2 */ + m_clk.enable = enable; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &m_clk); + if (ret < 0) { + pr_err("%s: afe_set_lpass_clock_v2 failed for mclk_3\n" + "with ret %d\n", __func__, ret); + goto done; + } + + ibit_clk.clk_id = Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT; + ibit_clk.clk_freq_in_hz = Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ; + ibit_clk.enable = enable; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &ibit_clk); + if (ret < 0) { + pr_err("%s: afe_set_lpass_clock_v2 failed for ibit\n" + "with ret %d\n", __func__, ret); + goto err_ibit_clk_set; + } + } else { + if (audio_lpass_mclk->lpass_csr_gpio_mux_spkrctl_vaddr && + enable) { + val = ioread32(audio_lpass_mclk-> + lpass_csr_gpio_mux_spkrctl_vaddr); + val = val | 0x00000002; + iowrite32(val, audio_lpass_mclk-> + lpass_csr_gpio_mux_spkrctl_vaddr); + } + + digital_cdc_core_clk.enable = enable; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s: afe_set_digital_codec_core_clock failed\n" + " with ret %d\n", __func__, ret); + goto done; + } } ret = 0; @@ -299,7 +330,7 @@ static int audio_ext_lpass_mclk_prepare(struct clk *clk) lpass_clk_ver = afe_get_lpass_clk_ver(); if (lpass_clk_ver >= LPASS_CLK_VER_2) - ret = audio_ext_set_lpass_mclk_v2(CLK_ENABLE); + ret = audio_ext_set_lpass_mclk_v2(clk, CLK_ENABLE); else ret = audio_ext_set_lpass_mclk_v1(clk, CLK_ENABLE); @@ -336,7 +367,7 @@ static void audio_ext_lpass_mclk_unprepare(struct clk *clk) lpass_clk_ver = afe_get_lpass_clk_ver(); if (lpass_clk_ver >= LPASS_CLK_VER_2) - ret = audio_ext_set_lpass_mclk_v2(CLK_DISABLE); + ret = audio_ext_set_lpass_mclk_v2(clk, CLK_DISABLE); else ret = audio_ext_set_lpass_mclk_v1(clk, CLK_DISABLE); -- GitLab From 00291c4c3b0cd84e34fe8df857d3c4c734adb547 Mon Sep 17 00:00:00 2001 From: Pangyen Chen Date: Thu, 21 Dec 2017 13:03:44 +0800 Subject: [PATCH 2265/5498] ANDROID dm-verity: fix root device init timing issue. [Detail] Depending on storage initialization speed, root block device may not be ready when configuring it to enable dm-verity, this causes dm-verity critical error and system reboots. To mitigate this issue, we added retry and timeout mechanism, where timeout value is 2s. Bug: 68731686 Test: We can reproduce the issue before we apply this patch but we cannot reproduce the issue after we apply this patch. Device can always boot up to homescreen after we apply this patch. Change-Id: Ia360a63c5c90f7d81ef1465f308995fb69406a83 Signed-off-by: Iris Chang --- drivers/md/dm-android-verity.c | 18 +++++++++++++++--- drivers/md/dm-verity.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index a3ad2dd4efee..c677d2f885d2 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -727,9 +727,21 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) dev = name_to_dev_t(target_device); if (!dev) { - DMERR("no dev found for %s", target_device); - handle_error(); - return -EINVAL; + const unsigned int timeout_ms = DM_VERITY_WAIT_DEV_TIMEOUT_MS; + unsigned int wait_time_ms = 0; + + DMERR("android_verity_ctr: retry %s\n", target_device); + while (driver_probe_done() != 0 || + (dev = name_to_dev_t(target_device)) == 0) { + msleep(100); + wait_time_ms += 100; + if (wait_time_ms > timeout_ms) { + DMERR("android_verity_ctr: retry timeout(%dms)\n", timeout_ms); + DMERR("no dev found for %s", target_device); + handle_error(); + return -EINVAL; + } + } } if (is_eng()) diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 47a96a56794b..a15208c37ac5 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -16,6 +16,7 @@ #include #include +#define DM_VERITY_WAIT_DEV_TIMEOUT_MS (2000) #define DM_VERITY_MAX_LEVELS 63 enum verity_mode { -- GitLab From b240cc6ed4a062e7b0eeae09b1d87ada6a7e67a2 Mon Sep 17 00:00:00 2001 From: Mahesh Sivasubramanian Date: Tue, 28 Nov 2017 10:06:17 -0700 Subject: [PATCH 2266/5498] drivers: cpuidle: lpm-levels: Fix untrusted pointer dereference. The list_for_each macro was not used correctly, where the intermediate variable would be LIST_POISON, resulting in a untrusted pointer dereference. Switch to using list_for_each_entry_safe to for safe removal of a list entry. Change-Id: I0e0fd5dd9f251b5093d6e9d6335387512ec59249 Signed-off-by: Mahesh Sivasubramanian --- drivers/cpuidle/lpm-levels-of.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 4d40849abb94..5d25efba5d77 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -823,14 +823,12 @@ failed: void free_cluster_node(struct lpm_cluster *cluster) { - struct list_head *list; int i; + struct lpm_cluster *cl, *m; - list_for_each(list, &cluster->child) { - struct lpm_cluster *n; - n = list_entry(list, typeof(*n), list); - list_del(list); - free_cluster_node(n); + list_for_each_entry_safe(cl, m, &cluster->child, list) { + list_del(&cl->list); + free_cluster_node(cl); }; if (cluster->cpu) { -- GitLab From bb60fc3f720698c5995dafa5a40cdfa049a3c1ce Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 12 Jan 2018 13:55:13 -0800 Subject: [PATCH 2267/5498] ANDROID: base: dd: Export driver_probe_done() Patch ANDROID dm-verity: fix root device init timing issue is using driver_probe_done() Change-Id: I3fff593af2bc63f99d87b8037995d9defc5e68ad Signed-off-by: Dmitry Shmidt --- drivers/base/dd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index cdc779cf79a3..7d9d016a3cfc 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -361,6 +361,7 @@ int driver_probe_done(void) return -EBUSY; return 0; } +EXPORT_SYMBOL_GPL(driver_probe_done); /** * wait_for_device_probe -- GitLab From 62e0d38acf81e53acf4e4f5a0bede33afbf63ca6 Mon Sep 17 00:00:00 2001 From: Artem Borisov Date: Sat, 13 Jan 2018 18:03:40 +0300 Subject: [PATCH 2268/5498] ANDROID: uid_sys_stats: fix the comment It is not uid_cputime.c anymore. Change-Id: I7effc2a449c1f9cba9d86a7b122a9c05fc266405 Signed-off-by: Artem Borisov --- drivers/misc/uid_sys_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 06182aa03b39..b9d25bf6175a 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -1,4 +1,4 @@ -/* drivers/misc/uid_cputime.c +/* drivers/misc/uid_sys_stats.c * * Copyright (C) 2014 - 2015 Google, Inc. * -- GitLab From 20e373417b64afbb71577310d92715778132ac02 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Fri, 12 Jan 2018 12:59:31 +0530 Subject: [PATCH 2269/5498] msm: ipa: Fix to check only reset IPA stats can have data as NULL Only reset IPA stats can have tether_stat data as NULL. Query of of tether stat without allocating memory for structure wan_ioctl_query_tether_stats should fail. Change-Id: Iab0fa98ffdcb2b32d75f4e8cb88f250002d2a787 Acked-by: Pooja Kumari Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 7 ++++--- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 6908cd3cc96b..c83b97b3c9c8 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2652,8 +2652,9 @@ int rmnet_ipa_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, /* prevent string buffer overflows */ data->upstreamIface[IFNAMSIZ-1] = '\0'; data->tetherIface[IFNAMSIZ-1] = '\0'; - } else if (reset != false) { - /* Data can be NULL for reset stats, checking reset != False */ + } else if (reset == false) { + /* only reset can have data == NULL*/ + IPAWANERR("query without allocate tether_stats strucutre\n"); return -EINVAL; } diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 53e4e4549e2e..a02ef60cc7b9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2810,8 +2810,9 @@ int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data, /* prevent string buffer overflows */ data->upstreamIface[IFNAMSIZ-1] = '\0'; data->tetherIface[IFNAMSIZ-1] = '\0'; - } else if (reset != false) { - /* Data can be NULL for reset stats, checking reset != False */ + } else if (reset == false) { + /* only reset can have data == NULL */ + IPAWANERR("query without allocate tether_stats strucutre\n"); return -EINVAL; } -- GitLab From 97261052a945abb550d5cd3f112c4bb389d1ae45 Mon Sep 17 00:00:00 2001 From: Rashi Bindra Date: Wed, 3 Jan 2018 16:16:38 +0530 Subject: [PATCH 2270/5498] ARM: dts: msm: Add support for FHD+ Video mode panel on msm8953 Add changes to add panel init sequence, on/off commands and other panel properties for FHD+ Video Mode Panel. Change-Id: Icb72bc4c9300a753de741155cb292d7b5a5f9325 Signed-off-by: Rashi Bindra --- ...si-panel-lgd-incell-sw49106-fhd-video.dtsi | 115 ++++++++++++++++++ arch/arm/boot/dts/qcom/msm8953-cdp.dtsi | 10 +- .../boot/dts/qcom/msm8953-mdss-panels.dtsi | 13 +- arch/arm/boot/dts/qcom/msm8953-mtp.dtsi | 10 +- 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/dsi-panel-lgd-incell-sw49106-fhd-video.dtsi diff --git a/arch/arm/boot/dts/qcom/dsi-panel-lgd-incell-sw49106-fhd-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-lgd-incell-sw49106-fhd-video.dtsi new file mode 100644 index 000000000000..8db5317f2106 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-lgd-incell-sw49106-fhd-video.dtsi @@ -0,0 +1,115 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + dsi_lgd_incell_sw49106_fhd_video: + qcom,mdss_dsi_lgd_incell_sw49106_fhd_video { + qcom,mdss-dsi-panel-name = + "lgd incell sw49106 fhd video"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2160>; + qcom,mdss-dsi-h-front-porch = <8>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <92>; + qcom,mdss-dsi-v-front-porch = <170>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [F8 3C 28 00 6E 72 2E + 40 30 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x02>; + qcom,mdss-dsi-t-clk-pre = <0x2D>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-on-command = [05 01 00 00 0B 00 02 35 00 + 15 01 00 00 00 00 02 36 00 + 15 01 00 00 00 00 02 51 FF + 15 01 00 00 00 00 02 53 24 + 15 01 00 00 00 00 02 55 80 + 39 01 00 00 00 00 02 B0 AC + 39 01 00 00 00 00 06 B1 46 00 80 14 85 + 39 01 00 00 00 00 08 B3 05 08 14 00 1C 00 02 + 39 01 00 00 00 00 10 B4 83 08 00 04 04 04 04 00 + 00 00 00 00 00 00 00 + 39 01 00 00 00 00 13 B5 03 1E 0B 02 29 00 00 00 + 00 04 00 24 00 10 10 10 10 00 + 39 01 00 00 00 00 0A B6 00 72 39 13 08 67 00 60 46 + 39 01 00 00 00 00 05 B7 00 50 37 04 + 39 01 00 00 00 00 0C B8 70 38 14 ED 08 04 00 01 + 0A A0 00 + 39 01 00 00 00 00 06 C0 8A 8F 18 C1 12 + 39 01 00 00 00 00 07 C1 01 00 30 C2 C7 0F + 39 01 00 00 00 00 03 C2 2A 00 + 39 01 00 00 00 00 07 C3 05 0E 0E 50 88 09 + 39 01 00 00 00 00 04 C4 A2 E8 F4 + 39 01 00 00 00 00 05 C5 C2 2A 4E 08 + 39 01 00 00 00 00 03 C6 15 01 + 39 01 00 00 00 00 07 CA 00 00 03 84 55 F5 + 39 01 00 00 00 00 03 CB 3F A0 + 39 01 00 00 00 00 09 CC F0 03 10 55 11 FC 34 34 + 39 01 00 00 00 00 07 CD 11 50 50 90 00 F3 + 39 01 00 00 00 00 07 CE A0 28 28 34 00 AB + 39 01 00 00 00 00 10 D0 10 1B 22 2A 35 42 4A 53 4D + 44 34 23 10 03 81 + 39 01 00 00 00 00 10 D1 09 15 1C 25 31 3F 47 52 4F + 45 34 22 0E 01 83 + 39 01 00 00 00 00 10 D2 10 1B 22 29 34 41 49 52 4E + 44 34 23 10 03 81 + 39 01 00 00 00 00 10 D3 09 15 1C 24 30 3E 46 51 50 + 45 34 22 0E 01 83 + 39 01 00 00 00 00 10 D4 10 1B 22 2A 35 42 4A 53 4D + 44 34 23 10 03 81 + 39 01 00 00 00 00 10 D5 09 15 1C 25 31 3F 47 52 4F + 45 34 22 0E 01 83 + 39 01 00 00 00 00 0D E5 24 23 11 10 00 0A 08 06 04 + 11 0E 23 + 39 01 00 00 00 00 0D E6 24 23 11 10 01 0B 09 07 05 + 11 0E 23 + 39 01 00 00 00 00 07 E7 15 16 17 18 19 1A + 39 01 00 00 00 00 07 E8 1B 1C 1D 1E 1F 20 + 39 01 00 00 00 00 05 ED 00 01 53 0C + 39 01 00 00 00 00 03 F0 B2 00 + 39 01 00 00 00 00 05 F2 01 00 17 00 + 39 01 00 00 64 00 07 F3 00 50 90 C9 00 01 + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 05 00 02 29 00]; + qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00 + 05 01 00 00 64 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-reset-sequence = <1 200>, <0 200>, <1 200>; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-post-init-delay = <1>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8953-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8953-cdp.dtsi index cadf277b61ba..01d462da5a9e 100644 --- a/arch/arm/boot/dts/qcom/msm8953-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8953-cdp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -216,6 +216,14 @@ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_clk_mode"; }; +&dsi_lgd_incell_sw49106_fhd_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-min-refresh-rate = <48>; + qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; +}; + &blsp1_uart0 { status = "ok"; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/qcom/msm8953-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8953-mdss-panels.dtsi index 4fa5cd19a17c..aedcc94e27e1 100644 --- a/arch/arm/boot/dts/qcom/msm8953-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8953-mdss-panels.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,6 +23,7 @@ #include "dsi-panel-truly-wuxga-video.dtsi" #include "dsi-panel-lt8912-480p-video.dtsi" #include "dsi-panel-lt8912-1080p-video.dtsi" +#include "dsi-panel-lgd-incell-sw49106-fhd-video.dtsi" &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { @@ -104,3 +105,13 @@ 24 1f 08 09 05 03 04 a0 24 1c 08 09 05 03 04 a0]; }; + +&dsi_lgd_incell_sw49106_fhd_video { + qcom,mdss-dsi-panel-timings-phy-v2 = [24 1F 08 09 05 03 04 a0 + 24 1F 08 09 05 03 04 a0 + 24 1F 08 09 05 03 04 a0 + 24 1F 08 09 05 03 04 a0 + 24 1B 08 09 05 03 04 a0]; + qcom,mdss-dsi-t-clk-post = <0x0d>; + qcom,mdss-dsi-t-clk-pre = <0x30>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8953-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8953-mtp.dtsi index af1d61ae20b4..d805dd67b87d 100644 --- a/arch/arm/boot/dts/qcom/msm8953-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8953-mtp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -173,6 +173,14 @@ qcom,panel-roi-alignment = <2 2 4 2 1080 2>; }; +&dsi_lgd_incell_sw49106_fhd_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,mdss-dsi-min-refresh-rate = <48>; + qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; +}; + &blsp1_uart0 { status = "ok"; pinctrl-names = "default"; -- GitLab From 902b0b3ac0e2d7bebc4bb3cf3786a86ac39595a9 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 22 Jun 2017 12:14:40 -0700 Subject: [PATCH 2271/5498] fscrypt: updates on 4.15-rc4 Cherry-picked from origin/upstream-f2fs-stable-linux-3.18.y: f73a1ef7d7b5 fscrypt: resolve some cherry-pick bugs b9dad2c0a3c4 fscrypt: move to generic async completion fb0097fefa09 crypto: introduce crypto wait for async op 89682e2b3157 fscrypt: lock mutex before checking for bounce page pool 16e02cc0ccae fscrypt: new helper function - fscrypt_prepare_setattr() 03c10b3f3762 fscrypt: new helper function - fscrypt_prepare_lookup() 95b5e0ec575d fscrypt: new helper function - fscrypt_prepare_rename() 1b4465a3a7a8 fscrypt: new helper function - fscrypt_prepare_link() 25c01cbc07c1 fscrypt: new helper function - fscrypt_file_open() 63aa02394821 fscrypt: new helper function - fscrypt_require_key() 7fa20786d09a fscrypt: remove unneeded empty fscrypt_operations structs 7319c5ee6274 fscrypt: remove ->is_encrypted() d824e3a72ddc fscrypt: switch from ->is_encrypted() to IS_ENCRYPTED() 12f169fd2683 fs, fscrypt: add an S_ENCRYPTED inode flag 0f461ce2849e fscrypt: clean up include file mess db32474d104c fscrypt: fix dereference of NULL user_key_payload 12cff7c68247 fscrypt: make ->dummy_context() return bool Change-Id: Iaf8f427637741c70f529a7571f9376695d441f1a Signed-off-by: Jaegeuk Kim --- crypto/api.c | 13 ++ fs/crypto/Makefile | 2 +- fs/crypto/crypto.c | 30 +--- fs/crypto/fname.c | 39 +---- fs/crypto/fscrypt_private.h | 12 +- fs/crypto/hooks.c | 112 ++++++++++++ fs/crypto/keyinfo.c | 23 +-- fs/crypto/policy.c | 6 +- fs/f2fs/f2fs.h | 9 +- fs/f2fs/inode.c | 5 +- fs/f2fs/super.c | 7 +- include/linux/crypto.h | 40 +++++ include/linux/fs.h | 2 + include/linux/fscrypt.h | 290 ++++++++++++++++++++++++++++++++ include/linux/fscrypt_common.h | 149 ---------------- include/linux/fscrypt_notsupp.h | 39 ++++- include/linux/fscrypt_supp.h | 17 +- 17 files changed, 539 insertions(+), 256 deletions(-) create mode 100644 fs/crypto/hooks.c create mode 100644 include/linux/fscrypt.h delete mode 100644 include/linux/fscrypt_common.h diff --git a/crypto/api.c b/crypto/api.c index 7db2e89a3114..937f74878a6e 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "internal.h" LIST_HEAD(crypto_alg_list); @@ -601,5 +602,17 @@ int crypto_has_alg(const char *name, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(crypto_has_alg); +void crypto_req_done(struct crypto_async_request *req, int err) +{ + struct crypto_wait *wait = req->data; + + if (err == -EINPROGRESS) + return; + + wait->err = err; + complete(&wait->completion); +} +EXPORT_SYMBOL_GPL(crypto_req_done); + MODULE_DESCRIPTION("Cryptographic core API"); MODULE_LICENSE("GPL"); diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index 9f6607f17b53..cb496989a6b6 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o -fscrypto-y := crypto.o fname.o policy.o keyinfo.o +fscrypto-y := crypto.o fname.o hooks.o keyinfo.o policy.o fscrypto-$(CONFIG_BLOCK) += bio.o diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 303e881232d1..7024fcf4e589 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -126,21 +126,6 @@ struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) } EXPORT_SYMBOL(fscrypt_get_ctx); -/** - * page_crypt_complete() - completion callback for page crypto - * @req: The asynchronous cipher request context - * @res: The result of the cipher operation - */ -static void page_crypt_complete(struct crypto_async_request *req, int res) -{ - struct fscrypt_completion_result *ecr = req->data; - - if (res == -EINPROGRESS) - return; - ecr->res = res; - complete(&ecr->completion); -} - int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, u64 lblk_num, struct page *src_page, struct page *dest_page, unsigned int len, @@ -151,7 +136,7 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, u8 padding[FS_IV_SIZE - sizeof(__le64)]; } iv; struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct scatterlist dst, src; struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; @@ -179,7 +164,7 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, ablkcipher_request_set_callback( req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - page_crypt_complete, &ecr); + crypto_req_done, &wait); sg_init_table(&dst, 1); sg_set_page(&dst, dest_page, len, offs); @@ -187,14 +172,9 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, sg_set_page(&src, src_page, len, offs); ablkcipher_request_set_crypt(req, &src, &dst, len, &iv); if (rw == FS_DECRYPT) - res = crypto_ablkcipher_decrypt(req); + res = crypto_wait_req(crypto_ablkcipher_decrypt(req), &wait); else - res = crypto_ablkcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - BUG_ON(req->base.data != &ecr); - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_encrypt(req), &wait); ablkcipher_request_free(req); if (res) { printk_ratelimited(KERN_ERR @@ -340,7 +320,7 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) return -ECHILD; dir = dget_parent(dentry); - if (!d_inode(dir)->i_sb->s_cop->is_encrypted(d_inode(dir))) { + if (!IS_ENCRYPTED(d_inode(dir))) { dput(dir); return 0; } diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index cef1d3e8c783..690426912d0e 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -14,21 +14,6 @@ #include #include "fscrypt_private.h" -/** - * fname_crypt_complete() - completion callback for filename crypto - * @req: The asynchronous cipher request context - * @res: The result of the cipher operation - */ -static void fname_crypt_complete(struct crypto_async_request *req, int res) -{ - struct fscrypt_completion_result *ecr = req->data; - - if (res == -EINPROGRESS) - return; - ecr->res = res; - complete(&ecr->completion); -} - /** * fname_encrypt() - encrypt a filename * @@ -40,7 +25,7 @@ static int fname_encrypt(struct inode *inode, const struct qstr *iname, struct fscrypt_str *oname) { struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; @@ -76,17 +61,12 @@ static int fname_encrypt(struct inode *inode, } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - fname_crypt_complete, &ecr); + crypto_req_done, &wait); sg_init_one(&sg, oname->name, cryptlen); ablkcipher_request_set_crypt(req, &sg, &sg, cryptlen, iv); /* Do the encryption */ - res = crypto_ablkcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - /* Request is being completed asynchronously; wait for it */ - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_encrypt(req), &wait); ablkcipher_request_free(req); if (res < 0) { printk_ratelimited(KERN_ERR @@ -110,7 +90,7 @@ static int fname_decrypt(struct inode *inode, struct fscrypt_str *oname) { struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct scatterlist src_sg, dst_sg; struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_ablkcipher *tfm = ci->ci_ctfm; @@ -131,7 +111,7 @@ static int fname_decrypt(struct inode *inode, } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - fname_crypt_complete, &ecr); + crypto_req_done, &wait); /* Initialize IV */ memset(iv, 0, FS_CRYPTO_BLOCK_SIZE); @@ -140,11 +120,7 @@ static int fname_decrypt(struct inode *inode, sg_init_one(&src_sg, iname->name, iname->len); sg_init_one(&dst_sg, oname->name, oname->len); ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); - res = crypto_ablkcipher_decrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_decrypt(req), &wait); ablkcipher_request_free(req); if (res < 0) { printk_ratelimited(KERN_ERR @@ -382,8 +358,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, memset(fname, 0, sizeof(struct fscrypt_name)); fname->usr_fname = iname; - if (!dir->i_sb->s_cop->is_encrypted(dir) || - fscrypt_is_dot_dotdot(iname)) { + if (!IS_ENCRYPTED(dir) || fscrypt_is_dot_dotdot(iname)) { fname->disk_name.name = (unsigned char *)iname->name; fname->disk_name.len = iname->len; return 0; diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4e4f60f134b4..acb0ccc65698 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -11,7 +11,8 @@ #ifndef _FSCRYPT_PRIVATE_H #define _FSCRYPT_PRIVATE_H -#include +#define __FS_HAS_ENCRYPTION 1 +#include #include /* Encryption parameters */ @@ -69,15 +70,6 @@ typedef enum { #define FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 #define FS_CTX_HAS_BOUNCE_BUFFER_FL 0x00000002 -struct fscrypt_completion_result { - struct completion completion; - int res; -}; - -#define DECLARE_FS_COMPLETION_RESULT(ecr) \ - struct fscrypt_completion_result ecr = { \ - COMPLETION_INITIALIZER_ONSTACK((ecr).completion), 0 } - static inline void inode_lock(struct inode *inode) { mutex_lock(&inode->i_mutex); diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c new file mode 100644 index 000000000000..396561212dbd --- /dev/null +++ b/fs/crypto/hooks.c @@ -0,0 +1,112 @@ +/* + * fs/crypto/hooks.c + * + * Encryption hooks for higher-level filesystem operations. + */ + +#include +#include "fscrypt_private.h" + +/** + * fscrypt_file_open - prepare to open a possibly-encrypted regular file + * @inode: the inode being opened + * @filp: the struct file being set up + * + * Currently, an encrypted regular file can only be opened if its encryption key + * is available; access to the raw encrypted contents is not supported. + * Therefore, we first set up the inode's encryption key (if not already done) + * and return an error if it's unavailable. + * + * We also verify that if the parent directory (from the path via which the file + * is being opened) is encrypted, then the inode being opened uses the same + * encryption policy. This is needed as part of the enforcement that all files + * in an encrypted directory tree use the same encryption policy, as a + * protection against certain types of offline attacks. Note that this check is + * needed even when opening an *unencrypted* file, since it's forbidden to have + * an unencrypted file in an encrypted directory. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + */ +int fscrypt_file_open(struct inode *inode, struct file *filp) +{ + int err; + struct dentry *dir; + + err = fscrypt_require_key(inode); + if (err) + return err; + + dir = dget_parent(filp->f_path.dentry); + if (IS_ENCRYPTED(d_inode(dir)) && + !fscrypt_has_permitted_context(d_inode(dir), inode)) { + pr_warn_ratelimited("fscrypt: inconsistent encryption contexts: %lu/%lu", + d_inode(dir)->i_ino, inode->i_ino); + err = -EPERM; + } + dput(dir); + return err; +} +EXPORT_SYMBOL_GPL(fscrypt_file_open); + +int __fscrypt_prepare_link(struct inode *inode, struct inode *dir) +{ + int err; + + err = fscrypt_require_key(dir); + if (err) + return err; + + if (!fscrypt_has_permitted_context(dir, inode)) + return -EPERM; + + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_link); + +int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + int err; + + err = fscrypt_require_key(old_dir); + if (err) + return err; + + err = fscrypt_require_key(new_dir); + if (err) + return err; + + if (old_dir != new_dir) { + if (IS_ENCRYPTED(new_dir) && + !fscrypt_has_permitted_context(new_dir, + d_inode(old_dentry))) + return -EPERM; + + if ((flags & RENAME_EXCHANGE) && + IS_ENCRYPTED(old_dir) && + !fscrypt_has_permitted_context(old_dir, + d_inode(new_dentry))) + return -EPERM; + } + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename); + +int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry) +{ + int err = fscrypt_get_encryption_info(dir); + + if (err) + return err; + + if (fscrypt_has_encryption_key(dir)) { + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY; + spin_unlock(&dentry->d_lock); + } + + d_set_d_op(dentry, &fscrypt_d_ops); + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 80e7fdfc7804..8c3bca16cfa7 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -17,17 +17,6 @@ static struct crypto_shash *essiv_hash_tfm; -static void derive_crypt_complete(struct crypto_async_request *req, int rc) -{ - struct fscrypt_completion_result *ecr = req->data; - - if (rc == -EINPROGRESS) - return; - - ecr->res = rc; - complete(&ecr->completion); -} - /** * derive_key_aes() - Derive a key using AES-128-ECB * @deriving_key: Encryption key used for derivation. @@ -42,7 +31,7 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], { int res = 0; struct ablkcipher_request *req = NULL; - DECLARE_FS_COMPLETION_RESULT(ecr); + DECLARE_CRYPTO_WAIT(wait); struct scatterlist src_sg, dst_sg; struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, 0); @@ -59,7 +48,7 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - derive_crypt_complete, &ecr); + crypto_req_done, &wait); res = crypto_ablkcipher_setkey(tfm, deriving_key, FS_AES_128_ECB_KEY_SIZE); if (res < 0) @@ -69,11 +58,7 @@ static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE], sg_init_one(&dst_sg, derived_raw_key, source_key->size); ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size, NULL); - res = crypto_ablkcipher_encrypt(req); - if (res == -EINPROGRESS || res == -EBUSY) { - wait_for_completion(&ecr.completion); - res = ecr.res; - } + res = crypto_wait_req(crypto_ablkcipher_encrypt(req), &wait); out: if (req) ablkcipher_request_free(req); @@ -286,7 +271,7 @@ int fscrypt_get_encryption_info(struct inode *inode) res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); if (res < 0) { if (!fscrypt_dummy_context_enabled(inode) || - inode->i_sb->s_cop->is_encrypted(inode)) + IS_ENCRYPTED(inode)) return res; /* Fake up a context for an unencrypted directory */ memset(&ctx, 0, sizeof(ctx)); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 9914d51dff86..2f2c53f2e136 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -109,7 +109,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) struct fscrypt_policy policy; int res; - if (!inode->i_sb->s_cop->is_encrypted(inode)) + if (!IS_ENCRYPTED(inode)) return -ENODATA; res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); @@ -166,11 +166,11 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) return 1; /* No restrictions if the parent directory is unencrypted */ - if (!cops->is_encrypted(parent)) + if (!IS_ENCRYPTED(parent)) return 1; /* Encrypted directories must not contain unencrypted files */ - if (!cops->is_encrypted(child)) + if (!IS_ENCRYPTED(child)) return 0; /* diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e40eb01797e8..2ea0f8447d6f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -23,14 +23,12 @@ #include #include #include -#ifdef CONFIG_F2FS_FS_ENCRYPTION -#include -#else -#include -#endif #include #include +#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_F2FS_FS_ENCRYPTION) +#include + #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) #define f2fs_down_write(x, y) down_write_nest_lock(x, y) @@ -3183,6 +3181,7 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION file_set_encrypt(inode); + inode->i_flags |= S_ENCRYPTED; #endif } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 1ae5b61cf2bc..9eb3cc2486d4 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -43,8 +43,11 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_NOATIME; if (flags & FS_DIRSYNC_FL) new_fl |= S_DIRSYNC; + if (f2fs_encrypted_inode(inode)) + new_fl |= S_ENCRYPTED; inode_set_flags(inode, new_fl, - S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); + S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC| + S_ENCRYPTED); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b06b3ead8cb1..a945db43369e 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1788,14 +1788,9 @@ static const struct fscrypt_operations f2fs_cryptops = { .key_prefix = "f2fs:", .get_context = f2fs_get_context, .set_context = f2fs_set_context, - .is_encrypted = f2fs_encrypted_inode, .empty_dir = f2fs_empty_dir, .max_namelen = f2fs_max_namelen, }; -#else -static const struct fscrypt_operations f2fs_cryptops = { - .is_encrypted = f2fs_encrypted_inode, -}; #endif static struct inode *f2fs_nfs_get_inode(struct super_block *sb, @@ -2526,7 +2521,9 @@ try_onemore: #endif sb->s_op = &f2fs_sops; +#ifdef CONFIG_F2FS_FS_ENCRYPTION sb->s_cop = &f2fs_cryptops; +#endif sb->s_xattr = f2fs_xattr_handlers; sb->s_export_op = &f2fs_export_ops; sb->s_magic = F2FS_SUPER_MAGIC; diff --git a/include/linux/crypto.h b/include/linux/crypto.h index dc34dfc766b5..7284dbfcbffd 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -24,6 +24,7 @@ #include #include #include +#include /* * Autoloaded crypto modules should only use a prefixed name to avoid allowing @@ -323,6 +324,45 @@ struct crypto_alg { struct module *cra_module; }; +/* + * A helper struct for waiting for completion of async crypto ops + */ +struct crypto_wait { + struct completion completion; + int err; +}; + +/* + * Macro for declaring a crypto op async wait object on stack + */ +#define DECLARE_CRYPTO_WAIT(_wait) \ + struct crypto_wait _wait = { \ + COMPLETION_INITIALIZER_ONSTACK((_wait).completion), 0 } + +/* + * Async ops completion helper functioons + */ +void crypto_req_done(struct crypto_async_request *req, int err); + +static inline int crypto_wait_req(int err, struct crypto_wait *wait) +{ + switch (err) { + case -EINPROGRESS: + case -EBUSY: + wait_for_completion(&wait->completion); + reinit_completion(&wait->completion); + err = wait->err; + break; + }; + + return err; +} + +static inline void crypto_init_wait(struct crypto_wait *wait) +{ + init_completion(&wait->completion); +} + /* * Algorithm registration interface. */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 16c0394c45c2..c3bccd812021 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1633,6 +1633,7 @@ struct super_operations { #define S_IMA 1024 /* Inode has an associated IMA struct */ #define S_AUTOMOUNT 2048 /* Automount/referral quasi-directory */ #define S_NOSEC 4096 /* no suid or xattr security attributes */ +#define S_ENCRYPTED 16384 /* Encrypted file (using fs/crypto/) */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -1670,6 +1671,7 @@ struct super_operations { #define IS_IMA(inode) ((inode)->i_flags & S_IMA) #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) +#define IS_ENCRYPTED(inode) ((inode)->i_flags & S_ENCRYPTED) #define IS_WHITEOUT(inode) (S_ISCHR(inode->i_mode) && \ (inode)->i_rdev == WHITEOUT_DEV) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h new file mode 100644 index 000000000000..8641e56b8f8a --- /dev/null +++ b/include/linux/fscrypt.h @@ -0,0 +1,290 @@ +/* + * fscrypt.h: declarations for per-file encryption + * + * Filesystems that implement per-file encryption include this header + * file with the __FS_HAS_ENCRYPTION set according to whether that filesystem + * is being built with encryption support or not. + * + * Copyright (C) 2015, Google, Inc. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ +#ifndef _LINUX_FSCRYPT_H +#define _LINUX_FSCRYPT_H + +#include +#include +#include +#include +#include +#include +#include + +#define FS_CRYPTO_BLOCK_SIZE 16 + +struct fscrypt_info; + +struct fscrypt_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + u8 flags; /* Flags */ +}; + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct fscrypt_symlink_data { + __le16 len; + char encrypted_path[1]; +} __packed; + +struct fscrypt_str { + unsigned char *name; + u32 len; +}; + +struct fscrypt_name { + const struct qstr *usr_fname; + struct fscrypt_str disk_name; + u32 hash; + u32 minor_hash; + struct fscrypt_str crypto_buf; +}; + +#define FSTR_INIT(n, l) { .name = n, .len = l } +#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) + +/* + * fscrypt superblock flags + */ +#define FS_CFLG_OWN_PAGES (1U << 1) + +/* + * crypto opertions for filesystems + */ +struct fscrypt_operations { + unsigned int flags; + const char *key_prefix; + int (*get_context)(struct inode *, void *, size_t); + int (*set_context)(struct inode *, const void *, size_t, void *); + bool (*dummy_context)(struct inode *); + bool (*empty_dir)(struct inode *); + unsigned (*max_namelen)(struct inode *); +}; + +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + if (inode->i_sb->s_cop->dummy_context && + inode->i_sb->s_cop->dummy_context(inode)) + return true; + return false; +} + +static inline bool fscrypt_valid_enc_modes(u32 contents_mode, + u32 filenames_mode) +{ + if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && + filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) + return true; + + if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && + filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) + return true; + + return false; +} + +static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) +{ + if (str->len == 1 && str->name[0] == '.') + return true; + + if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') + return true; + + return false; +} + +#if __FS_HAS_ENCRYPTION + +static inline struct page *fscrypt_control_page(struct page *page) +{ + return ((struct fscrypt_ctx *)page_private(page))->w.control_page; +} + +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return (inode->i_crypt_info != NULL); +} + +#include + +#else /* !__FS_HAS_ENCRYPTION */ + +static inline struct page *fscrypt_control_page(struct page *page) +{ + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +} + +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return 0; +} + +#include +#endif /* __FS_HAS_ENCRYPTION */ + +/** + * fscrypt_require_key - require an inode's encryption key + * @inode: the inode we need the key for + * + * If the inode is encrypted, set up its encryption key if not already done. + * Then require that the key be present and return -ENOKEY otherwise. + * + * No locks are needed, and the key will live as long as the struct inode --- so + * it won't go away from under you. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int fscrypt_require_key(struct inode *inode) +{ + if (IS_ENCRYPTED(inode)) { + int err = fscrypt_get_encryption_info(inode); + + if (err) + return err; + if (!fscrypt_has_encryption_key(inode)) + return -ENOKEY; + } + return 0; +} + +/** + * fscrypt_prepare_link - prepare to link an inode into a possibly-encrypted directory + * @old_dentry: an existing dentry for the inode being linked + * @dir: the target directory + * @dentry: negative dentry for the target filename + * + * A new link can only be added to an encrypted directory if the directory's + * encryption key is available --- since otherwise we'd have no way to encrypt + * the filename. Therefore, we first set up the directory's encryption key (if + * not already done) and return an error if it's unavailable. + * + * We also verify that the link will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if the directory's encryption key is missing, + * -EPERM if the link would result in an inconsistent encryption policy, or + * another -errno code. + */ +static inline int fscrypt_prepare_link(struct dentry *old_dentry, + struct inode *dir, + struct dentry *dentry) +{ + if (IS_ENCRYPTED(dir)) + return __fscrypt_prepare_link(d_inode(old_dentry), dir); + return 0; +} + +/** + * fscrypt_prepare_rename - prepare for a rename between possibly-encrypted directories + * @old_dir: source directory + * @old_dentry: dentry for source file + * @new_dir: target directory + * @new_dentry: dentry for target location (may be negative unless exchanging) + * @flags: rename flags (we care at least about %RENAME_EXCHANGE) + * + * Prepare for ->rename() where the source and/or target directories may be + * encrypted. A new link can only be added to an encrypted directory if the + * directory's encryption key is available --- since otherwise we'd have no way + * to encrypt the filename. A rename to an existing name, on the other hand, + * *is* cryptographically possible without the key. However, we take the more + * conservative approach and just forbid all no-key renames. + * + * We also verify that the rename will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if an encryption key is missing, -EPERM if the + * rename would cause inconsistent encryption policies, or another -errno code. + */ +static inline int fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + if (IS_ENCRYPTED(old_dir) || IS_ENCRYPTED(new_dir)) + return __fscrypt_prepare_rename(old_dir, old_dentry, + new_dir, new_dentry, flags); + return 0; +} + +/** + * fscrypt_prepare_lookup - prepare to lookup a name in a possibly-encrypted directory + * @dir: directory being searched + * @dentry: filename being looked up + * @flags: lookup flags + * + * Prepare for ->lookup() in a directory which may be encrypted. Lookups can be + * done with or without the directory's encryption key; without the key, + * filenames are presented in encrypted form. Therefore, we'll try to set up + * the directory's encryption key, but even without it the lookup can continue. + * + * To allow invalidating stale dentries if the directory's encryption key is + * added later, we also install a custom ->d_revalidate() method and use the + * DCACHE_ENCRYPTED_WITH_KEY flag to indicate whether a given dentry is a + * plaintext name (flag set) or a ciphertext name (flag cleared). + * + * Return: 0 on success, -errno if a problem occurred while setting up the + * encryption key + */ +static inline int fscrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + if (IS_ENCRYPTED(dir)) + return __fscrypt_prepare_lookup(dir, dentry); + return 0; +} + +/** + * fscrypt_prepare_setattr - prepare to change a possibly-encrypted inode's attributes + * @dentry: dentry through which the inode is being changed + * @attr: attributes to change + * + * Prepare for ->setattr() on a possibly-encrypted inode. On an encrypted file, + * most attribute changes are allowed even without the encryption key. However, + * without the encryption key we do have to forbid truncates. This is needed + * because the size being truncated to may not be a multiple of the filesystem + * block size, and in that case we'd have to decrypt the final block, zero the + * portion past i_size, and re-encrypt it. (We *could* allow truncating to a + * filesystem block boundary, but it's simpler to just forbid all truncates --- + * and we already forbid all other contents modifications without the key.) + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int fscrypt_prepare_setattr(struct dentry *dentry, + struct iattr *attr) +{ + if (attr->ia_valid & ATTR_SIZE) + return fscrypt_require_key(d_inode(dentry)); + return 0; +} + +#endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt_common.h deleted file mode 100644 index 768203e248f0..000000000000 --- a/include/linux/fscrypt_common.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * fscrypt_common.h: common declarations for per-file encryption - * - * Copyright (C) 2015, Google, Inc. - * - * Written by Michael Halcrow, 2015. - * Modified by Jaegeuk Kim, 2015. - */ - -#ifndef _LINUX_FSCRYPT_COMMON_H -#define _LINUX_FSCRYPT_COMMON_H - -#include -#include -#include -#include -#include -#include -#include - -#define FS_CRYPTO_BLOCK_SIZE 16 - -struct fscrypt_info; - -struct fscrypt_ctx { - union { - struct { - struct page *bounce_page; /* Ciphertext page */ - struct page *control_page; /* Original page */ - } w; - struct { - struct bio *bio; - struct work_struct work; - } r; - struct list_head free_list; /* Free list */ - }; - u8 flags; /* Flags */ -}; - -/** - * For encrypted symlinks, the ciphertext length is stored at the beginning - * of the string in little-endian format. - */ -struct fscrypt_symlink_data { - __le16 len; - char encrypted_path[1]; -} __packed; - -/** - * This function is used to calculate the disk space required to - * store a filename of length l in encrypted symlink format. - */ -static inline u32 fscrypt_symlink_data_len(u32 l) -{ - if (l < FS_CRYPTO_BLOCK_SIZE) - l = FS_CRYPTO_BLOCK_SIZE; - return (l + sizeof(struct fscrypt_symlink_data) - 1); -} - -struct fscrypt_str { - unsigned char *name; - u32 len; -}; - -struct fscrypt_name { - const struct qstr *usr_fname; - struct fscrypt_str disk_name; - u32 hash; - u32 minor_hash; - struct fscrypt_str crypto_buf; -}; - -#define FSTR_INIT(n, l) { .name = n, .len = l } -#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) -#define fname_name(p) ((p)->disk_name.name) -#define fname_len(p) ((p)->disk_name.len) - -/* - * fscrypt superblock flags - */ -#define FS_CFLG_OWN_PAGES (1U << 1) - -/* - * crypto opertions for filesystems - */ -struct fscrypt_operations { - unsigned int flags; - const char *key_prefix; - int (*get_context)(struct inode *, void *, size_t); - int (*set_context)(struct inode *, const void *, size_t, void *); - int (*dummy_context)(struct inode *); - bool (*is_encrypted)(struct inode *); - bool (*empty_dir)(struct inode *); - unsigned (*max_namelen)(struct inode *); -}; - -static inline bool fscrypt_dummy_context_enabled(struct inode *inode) -{ - if (inode->i_sb->s_cop->dummy_context && - inode->i_sb->s_cop->dummy_context(inode)) - return true; - return false; -} - -static inline bool fscrypt_valid_enc_modes(u32 contents_mode, - u32 filenames_mode) -{ - if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && - filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) - return true; - - if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && - filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) - return true; - - return false; -} - -static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) -{ - if (str->len == 1 && str->name[0] == '.') - return true; - - if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') - return true; - - return false; -} - -static inline struct page *fscrypt_control_page(struct page *page) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - return ((struct fscrypt_ctx *)page_private(page))->w.control_page; -#else - WARN_ON_ONCE(1); - return ERR_PTR(-EINVAL); -#endif -} - -static inline int fscrypt_has_encryption_key(const struct inode *inode) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - return (inode->i_crypt_info != NULL); -#else - return 0; -#endif -} - -#endif /* _LINUX_FSCRYPT_COMMON_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index ec406aed2f2f..c4c6bf2c390e 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -3,13 +3,16 @@ * * This stubs out the fscrypt functions for filesystems configured without * encryption support. + * + * Do not include this file directly. Use fscrypt.h instead! */ +#ifndef _LINUX_FSCRYPT_H +#error "Incorrect include of linux/fscrypt_notsupp.h!" +#endif #ifndef _LINUX_FSCRYPT_NOTSUPP_H #define _LINUX_FSCRYPT_NOTSUPP_H -#include - /* crypto.c */ static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) @@ -97,7 +100,7 @@ static inline int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, int lookup, struct fscrypt_name *fname) { - if (dir->i_sb->s_cop->is_encrypted(dir)) + if (IS_ENCRYPTED(dir)) return -EOPNOTSUPP; memset(fname, 0, sizeof(struct fscrypt_name)); @@ -174,4 +177,34 @@ static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, return -EOPNOTSUPP; } +/* hooks.c */ + +static inline int fscrypt_file_open(struct inode *inode, struct file *filp) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + +static inline int __fscrypt_prepare_link(struct inode *inode, + struct inode *dir) +{ + return -EOPNOTSUPP; +} + +static inline int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + return -EOPNOTSUPP; +} + +static inline int __fscrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 6828dc6111fa..4ccecca36747 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -1,14 +1,15 @@ /* * fscrypt_supp.h * - * This is included by filesystems configured with encryption support. + * Do not include this file directly. Use fscrypt.h instead! */ +#ifndef _LINUX_FSCRYPT_H +#error "Incorrect include of linux/fscrypt_supp.h!" +#endif #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H -#include - /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); @@ -146,4 +147,14 @@ extern void fscrypt_pullback_bio_page(struct page **, bool); extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, unsigned int); +/* hooks.c */ +extern int fscrypt_file_open(struct inode *inode, struct file *filp); +extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); +extern int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags); +extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry); + #endif /* _LINUX_FSCRYPT_SUPP_H */ -- GitLab From b044d4502f88e7a4f231730cc69f53480f53a7e3 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Wed, 10 Jan 2018 13:34:37 +0530 Subject: [PATCH 2272/5498] ASoC: msm8x16-wcd: Fix mute if compander is disabled Compander disable sequence does not get called after headphone playback on FM. This results in mute at one channel. Compander clock should be disabled at the end to resolve the mute. CRs-Fixed: 2102126 Change-Id: I268728ec07cf394cbd215456e7aa0aaa2a4e4f1a Signed-off-by: Vatsal Bucha --- sound/soc/codecs/msm8x16-wcd.c | 92 ++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/sound/soc/codecs/msm8x16-wcd.c b/sound/soc/codecs/msm8x16-wcd.c index ba000656105d..f7ea943f7666 100644 --- a/sound/soc/codecs/msm8x16-wcd.c +++ b/sound/soc/codecs/msm8x16-wcd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -3885,11 +3885,20 @@ static int msm8x16_wcd_codec_config_compander(struct snd_soc_codec *codec, int interp_n, int event) { struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec); + int comp_ch_bits_set = 0x03; dev_dbg(codec->dev, "%s: event %d shift %d, enabled %d\n", __func__, event, interp_n, msm8x16_wcd->comp_enabled[interp_n]); + /* compander is invalid */ + if (msm8x16_wcd->comp_enabled[interp_n] != COMPANDER_1 && + msm8x16_wcd->comp_enabled[interp_n]) { + dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__, + msm8x16_wcd->comp_enabled[interp_n]); + return 0; + } + /* compander is not enabled */ if (!msm8x16_wcd->comp_enabled[interp_n]) { if (interp_n < MSM8X16_WCD_RX3) @@ -3900,55 +3909,50 @@ static int msm8x16_wcd_codec_config_compander(struct snd_soc_codec *codec, return 0; } - switch (msm8x16_wcd->comp_enabled[interp_n]) { - case COMPANDER_1: - if (SND_SOC_DAPM_EVENT_ON(event)) { - if (get_codec_version(msm8x16_wcd) >= DIANGU) - snd_soc_update_bits(codec, - MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC, - 0x08, 0x08); - /* Enable Compander Clock */ - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_COMP0_B2_CTL, 0x0F, 0x09); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_CLK_RX_B2_CTL, 0x01, 0x01); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_COMP0_B1_CTL, - 1 << interp_n, 1 << interp_n); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_COMP0_B3_CTL, 0xFF, 0x01); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_COMP0_B2_CTL, 0xF0, 0x50); - /* add sleep for compander to settle */ - usleep_range(1000, 1100); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_COMP0_B3_CTL, 0xFF, 0x28); + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (get_codec_version(msm8x16_wcd) >= DIANGU) snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_COMP0_B2_CTL, 0xF0, 0xB0); + MSM8X16_WCD_A_ANALOG_RX_COM_BIAS_DAC, + 0x08, 0x08); + /* Enable Compander Clock */ + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_COMP0_B2_CTL, 0x0F, 0x09); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_CLK_RX_B2_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_COMP0_B1_CTL, + 1 << interp_n, 1 << interp_n); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_COMP0_B3_CTL, 0xFF, 0x01); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_COMP0_B2_CTL, 0xF0, 0x50); + /* add sleep for compander to settle */ + usleep_range(1000, 1100); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_COMP0_B3_CTL, 0xFF, 0x28); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_COMP0_B2_CTL, 0xF0, 0xB0); - /* Enable Compander GPIO */ - if (msm8x16_wcd->codec_hph_comp_gpio) - msm8x16_wcd->codec_hph_comp_gpio(1); - } else if (SND_SOC_DAPM_EVENT_OFF(event)) { - /* Disable Compander GPIO */ - if (msm8x16_wcd->codec_hph_comp_gpio) - msm8x16_wcd->codec_hph_comp_gpio(0); + /* Enable Compander GPIO */ + if (msm8x16_wcd->codec_hph_comp_gpio) + msm8x16_wcd->codec_hph_comp_gpio(1); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + /* Disable Compander GPIO */ + if (msm8x16_wcd->codec_hph_comp_gpio) + msm8x16_wcd->codec_hph_comp_gpio(0); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_COMP0_B1_CTL, + 1 << interp_n, 0); + comp_ch_bits_set = snd_soc_read(codec, + MSM8X16_WCD_A_CDC_COMP0_B1_CTL); + if ((comp_ch_bits_set & 0x03) == 0x00) { snd_soc_update_bits(codec, MSM8X16_WCD_A_CDC_COMP0_B2_CTL, 0x0F, 0x05); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_COMP0_B1_CTL, - 1 << interp_n, 0); - snd_soc_update_bits(codec, - MSM8X16_WCD_A_CDC_CLK_RX_B2_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM8X16_WCD_A_CDC_CLK_RX_B2_CTL, 0x01, 0x00); } - break; - default: - dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__, - msm8x16_wcd->comp_enabled[interp_n]); - break; - }; - + } return 0; } -- GitLab From 5a7a92d3a1f09fb13703acf96b74c999a0891eb1 Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Wed, 24 May 2017 10:28:27 +0800 Subject: [PATCH 2273/5498] ANDROID: Kconfig: add depends for UID_SYS_STATS uid_io depends on TASK_XACCT and TASK_IO_ACCOUNTING. So add depends in Kconfig before compiling code. Change-Id: Ie6bf57ec7c2eceffadf4da0fc2aca001ce10c36e Signed-off-by: Ganesh Mahendran --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 19f412632a16..878f09df7bf5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -521,7 +521,7 @@ config VEXPRESS_SYSCFG config UID_SYS_STATS bool "Per-UID statistics" - depends on PROFILING + depends on PROFILING && TASK_XACCT && TASK_IO_ACCOUNTING help Per UID based cpu time statistics exported to /proc/uid_cputime Per UID based io statistics exported to /proc/uid_io -- GitLab From dbadae43e24f738b0b0987377e246003b81e889d Mon Sep 17 00:00:00 2001 From: gaolez Date: Mon, 8 Jan 2018 14:15:25 +0800 Subject: [PATCH 2274/5498] msm: wlan: Support ETSI13 regulatory domain ETSI13 regulatory domain is created to support channel 149~173 with 14dBm tx power, including 46 countries: AL,AT,BE,BA,BG,HR,CY,CZ,DK, EE,FI,FR,GF,PF,DE,GR,HU,IS,IE,IT,LV,LI,LT,LU,MK,MT,MQ,MD,MC,ME,NL, AN,NO,PL,PT,RO,PM,VC,RS,SK,SI,ES,SE,CH,TR,GB. Change-Id: Idc575983645670e9410d831f2eb80186326926f8 Signed-off-by: Gaole Zhang --- net/wireless/db.txt | 335 ++++++-------------------------------------- 1 file changed, 46 insertions(+), 289 deletions(-) diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 9675fbd3a17d..d9774a51ab5a 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -40,6 +40,7 @@ country AL: DFS-ETSI (5150 - 5250 @ 80), (23), AUTO-BW (5250 - 5350 @ 80), (23), DFS, AUTO-BW (5470 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country AM: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -51,6 +52,7 @@ country AN: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country AR: (2402 - 2482 @ 40), (36) @@ -72,15 +74,7 @@ country AT: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -108,6 +102,7 @@ country BA: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -126,15 +121,7 @@ country BE: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -150,15 +137,7 @@ country BG: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -242,15 +221,7 @@ country CH: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) country CI: DFS-FCC (2402 - 2482 @ 40), (20) @@ -301,15 +272,7 @@ country CY: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -322,15 +285,7 @@ country CZ: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -352,15 +307,7 @@ country DE: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -369,15 +316,7 @@ country DK: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -411,15 +350,7 @@ country EE: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -433,15 +364,7 @@ country ES: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -456,15 +379,7 @@ country FI: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -480,15 +395,7 @@ country FR: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -497,15 +404,7 @@ country GB: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -528,6 +427,7 @@ country GF: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country GH: DFS-FCC (2402 - 2482 @ 40), (20) @@ -559,15 +459,7 @@ country GR: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -606,15 +498,7 @@ country HR: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -630,15 +514,7 @@ country HU: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -652,16 +528,7 @@ country IE: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -686,15 +553,7 @@ country IS: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -703,15 +562,7 @@ country IT: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -799,15 +650,7 @@ country LI: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) country LK: DFS-FCC (2402 - 2482 @ 40), (20) @@ -827,15 +670,7 @@ country LT: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -844,15 +679,7 @@ country LU: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -861,15 +688,7 @@ country LV: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -883,18 +702,21 @@ country MC: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country MD: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country ME: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country MF: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -914,6 +736,7 @@ country MK: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -943,6 +766,7 @@ country MQ: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country MR: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -955,15 +779,7 @@ country MT: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1024,15 +840,7 @@ country NL: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1041,15 +849,7 @@ country NO: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1089,6 +889,7 @@ country PF: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country PG: DFS-FCC (2402 - 2482 @ 40), (20) @@ -1113,15 +914,7 @@ country PL: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1130,6 +923,7 @@ country PM: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country PR: DFS-FCC (2402 - 2472 @ 40), (30) @@ -1143,15 +937,7 @@ country PT: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1184,15 +970,7 @@ country RO: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1203,6 +981,7 @@ country RS: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1232,15 +1011,7 @@ country SE: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1256,15 +1027,7 @@ country SI: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1273,15 +1036,7 @@ country SK: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1339,6 +1094,7 @@ country TR: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1422,6 +1178,7 @@ country VC: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country VE: DFS-FCC (2402 - 2482 @ 40), (30) -- GitLab From cf044174536f929ec0ee3da3e75796dc121701cd Mon Sep 17 00:00:00 2001 From: Roberto Granados Dorado Date: Mon, 11 Dec 2017 16:32:28 -0800 Subject: [PATCH 2275/5498] ASoC: msm: Add HFP routing for SLIM-I2S bridge This change adds the routing for HFP use case when BT chip is connected to WCD as SLIM-I2S bridge. CRs-Fixed: 2172613 Change-Id: I46213cf688e60692be86eabce1a767f73ad7da1a Signed-off-by: Vatsal Bucha --- sound/soc/msm/msm-dai-fe.c | 27 ++++++++++++++++++++++ sound/soc/msm/msm8952-dai-links.c | 15 ++++++++++++ sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 10 ++++++++ 3 files changed, 52 insertions(+) diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index b8f666d0ce15..cefd3865037b 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -446,6 +446,33 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .name = "SLIMBUS1_HOSTLESS", .probe = fe_dai_probe, }, + { + .playback = { + .stream_name = "SLIMBUS2_HOSTLESS Playback", + .aif_name = "SLIM2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "SLIMBUS2_HOSTLESS Capture", + .aif_name = "SLIM2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS2_HOSTLESS", + .probe = fe_dai_probe, + }, { .playback = { .stream_name = "SLIMBUS3_HOSTLESS Playback", diff --git a/sound/soc/msm/msm8952-dai-links.c b/sound/soc/msm/msm8952-dai-links.c index b304ba0d1b00..2ce654862034 100644 --- a/sound/soc/msm/msm8952-dai-links.c +++ b/sound/soc/msm/msm8952-dai-links.c @@ -714,6 +714,21 @@ static struct snd_soc_dai_link msm8952_common_fe_dai[] = { .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", }, + { + .name = "SLIMBUS_2 Hostless", + .stream_name = "SLIMBUS_2 Hostless", + .cpu_dai_name = "SLIMBUS2_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, {/* hw:x,12 */ .name = "MSM8952 LowLatency", .stream_name = "MultiMedia5", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index dfa74d5d6e63..8e2484abf0c2 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -4124,6 +4124,9 @@ static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_4_RX, MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_4_RX, MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), @@ -10128,6 +10131,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIM1_UL_HL", "SLIMBUS1_HOSTLESS Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM2_DL_HL", "SLIMBUS2_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM2_UL_HL", "SLIMBUS2_HOSTLESS Capture", + 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("SLIM3_DL_HL", "SLIMBUS3_HOSTLESS Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("SLIM3_UL_HL", "SLIMBUS3_HOSTLESS Capture", @@ -11184,6 +11191,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_4_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"SLIMBUS_4_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, {"SLIMBUS_4_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, {"SLIMBUS_4_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, {"SLIMBUS_4_RX", NULL, "SLIMBUS_4_RX Audio Mixer"}, @@ -12517,6 +12525,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_6_RX", NULL, "SLIMBUS6_DL_HL"}, {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"}, {"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"}, + {"SLIM2_UL_HL", NULL, "SLIMBUS_2_TX"}, {"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"}, {"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"}, @@ -12967,6 +12976,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"SLIMBUS_0_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_2_TX", "SLIMBUS_2_TX"}, {"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"SLIMBUS_0_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, {"SLIMBUS_0_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, -- GitLab From 3b356a491c9f163ea78f4118b6f647b887327a34 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 13 Sep 2016 11:16:06 +0100 Subject: [PATCH 2276/5498] UPSTREAM: arm64: tlbflush.h: add __tlbi() macro As with dsb() and isb(), add a __tlbi() helper so that we can avoid distracting asm boilerplate every time we want a TLBI. As some TLBI operations take an argument while others do not, some pre-processor is used to handle these two cases with different assembly blocks. The existing tlbflush.h code is moved over to use the helper. Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Marc Zyngier [ rename helper to __tlbi, update comment and commit log ] Signed-off-by: Punit Agrawal Reviewed-by: Will Deacon Signed-off-by: Will Deacon (cherry picked from commit db68f3e7594aca77632d56c449bd36c6c931d59a) Signed-off-by: Greg Hackmann Change-Id: I9fc215c78f2ae648e4df6ba7189cec7e453800e4 Git-Commit: e0c5ba8cd917ff1bd7d6ea3925359b96650ecdfc Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/tlbflush.h | 38 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index d937f518120f..0e0fe68d2156 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -24,6 +24,24 @@ #include #include +/* + * Raw TLBI operations. + * + * Where necessary, use the __tlbi() macro to avoid asm() + * boilerplate. Drivers and most kernel code should use the TLB + * management routines in preference to the macro below. + * + * The macro can be used as __tlbi(op) or __tlbi(op, arg), depending + * on whether a particular TLBI operation takes an argument or + * not. The macros handles invoking the asm with or without the + * register argument as appropriate. + */ +#define __TLBI_0(op, arg) asm ("tlbi " #op) +#define __TLBI_1(op, arg) asm ("tlbi " #op ", %0" : : "r" (arg)) +#define __TLBI_N(op, arg, n, ...) __TLBI_##n(op, arg) + +#define __tlbi(op, ...) __TLBI_N(op, ##__VA_ARGS__, 1, 0) + /* * TLB Management * ============== @@ -66,7 +84,7 @@ static inline void local_flush_tlb_all(void) { dsb(nshst); - asm("tlbi vmalle1"); + __tlbi(vmalle1); dsb(nsh); isb(); } @@ -74,7 +92,7 @@ static inline void local_flush_tlb_all(void) static inline void flush_tlb_all(void) { dsb(ishst); - asm("tlbi vmalle1is"); + __tlbi(vmalle1is); dsb(ish); isb(); } @@ -83,14 +101,14 @@ static inline void flush_tlb_mm(struct mm_struct *mm) { #ifdef CONFIG_ARCH_MSM8994_V1_TLBI_WA dsb(); - asm("tlbi vmalle1is"); + __tlbi(vmalle1is); dsb(); isb(); #else unsigned long asid = (unsigned long)ASID(mm) << 48; dsb(ishst); - asm("tlbi aside1is, %0" : : "r" (asid)); + __tlbi(aside1is, asid); dsb(ish); #endif } @@ -100,7 +118,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, { #ifdef CONFIG_ARCH_MSM8994_V1_TLBI_WA dsb(); - asm("tlbi vmalle1is"); + __tlbi(vmalle1is); dsb(); isb(); #else @@ -108,7 +126,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, ((unsigned long)ASID(vma->vm_mm) << 48); dsb(ishst); - asm("tlbi vale1is, %0" : : "r" (addr)); + __tlbi(vale1is, addr); dsb(ish); #endif } @@ -137,9 +155,9 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) { if (last_level) - asm("tlbi vale1is, %0" : : "r"(addr)); + __tlbi(vale1is, addr); else - asm("tlbi vae1is, %0" : : "r"(addr)); + __tlbi(vae1is, addr); } dsb(ish); } @@ -164,7 +182,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) - asm("tlbi vaae1is, %0" : : "r"(addr)); + __tlbi(vaae1is, addr); dsb(ish); isb(); } @@ -178,7 +196,7 @@ static inline void __flush_tlb_pgtable(struct mm_struct *mm, { unsigned long addr = uaddr >> 12 | (ASID(mm) << 48); - asm("tlbi vae1is, %0" : : "r" (addr)); + __tlbi(vae1is, addr); dsb(ish); } -- GitLab From 11024a4bc2ea0e30e17f9512cd4f85f98f1b4d31 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 19 Jul 2017 17:24:49 +0100 Subject: [PATCH 2277/5498] UPSTREAM: arm64: factor out entry stack manipulation In subsequent patches, we will detect stack overflow in our exception entry code, by verifying the SP after it has been decremented to make space for the exception regs. This verification code is small, and we can minimize its impact by placing it directly in the vectors. To avoid redundant modification of the SP, we also need to move the initial decrement of the SP into the vectors. As a preparatory step, this patch introduces kernel_ventry, which performs this decrement, and updates the entry code accordingly. Subsequent patches will fold SP verification into kernel_ventry. There should be no functional change as a result of this patch. Signed-off-by: Ard Biesheuvel [Mark: turn into prep patch, expand commit msg] Signed-off-by: Mark Rutland Reviewed-by: Will Deacon Tested-by: Laura Abbott Cc: Catalin Marinas Cc: James Morse (cherry picked from commit b11e5759bfac0c474d95ec4780b1566350e64cad) Signed-off-by: Greg Hackmann Change-Id: I1a1d01327c005ec1b92e939f122557a86893e01c Git-Commit: 1ee4be715741bbfbf5dd194da3958e7a4ea87807 Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/kernel/entry.S | 47 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 9adad63e4bf8..e6eb3ce251c1 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -69,8 +69,13 @@ #define BAD_FIQ 2 #define BAD_ERROR 3 - .macro kernel_entry, el, regsize = 64 + .macro kernel_ventry label + .align 7 sub sp, sp, #S_FRAME_SIZE + b \label + .endm + + .macro kernel_entry, el, regsize = 64 .if \regsize == 32 mov w0, w0 // zero upper 32 bits of x0 .endif @@ -270,31 +275,31 @@ tsk .req x28 // current thread_info .align 11 ENTRY(vectors) - ventry el1_sync_invalid // Synchronous EL1t - ventry el1_irq_invalid // IRQ EL1t - ventry el1_fiq_invalid // FIQ EL1t - ventry el1_error_invalid // Error EL1t + kernel_ventry el1_sync_invalid // Synchronous EL1t + kernel_ventry el1_irq_invalid // IRQ EL1t + kernel_ventry el1_fiq_invalid // FIQ EL1t + kernel_ventry el1_error_invalid // Error EL1t - ventry el1_sync // Synchronous EL1h - ventry el1_irq // IRQ EL1h - ventry el1_fiq_invalid // FIQ EL1h - ventry el1_error_invalid // Error EL1h + kernel_ventry el1_sync // Synchronous EL1h + kernel_ventry el1_irq // IRQ EL1h + kernel_ventry el1_fiq_invalid // FIQ EL1h + kernel_ventry el1_error_invalid // Error EL1h - ventry el0_sync // Synchronous 64-bit EL0 - ventry el0_irq // IRQ 64-bit EL0 - ventry el0_fiq_invalid // FIQ 64-bit EL0 - ventry el0_error_invalid // Error 64-bit EL0 + kernel_ventry el0_sync // Synchronous 64-bit EL0 + kernel_ventry el0_irq // IRQ 64-bit EL0 + kernel_ventry el0_fiq_invalid // FIQ 64-bit EL0 + kernel_ventry el0_error_invalid // Error 64-bit EL0 #ifdef CONFIG_COMPAT - ventry el0_sync_compat // Synchronous 32-bit EL0 - ventry el0_irq_compat // IRQ 32-bit EL0 - ventry el0_fiq_invalid_compat // FIQ 32-bit EL0 - ventry el0_error_invalid_compat // Error 32-bit EL0 + kernel_ventry el0_sync_compat // Synchronous 32-bit EL0 + kernel_ventry el0_irq_compat // IRQ 32-bit EL0 + kernel_ventry el0_fiq_invalid_compat // FIQ 32-bit EL0 + kernel_ventry el0_error_invalid_compat // Error 32-bit EL0 #else - ventry el0_sync_invalid // Synchronous 32-bit EL0 - ventry el0_irq_invalid // IRQ 32-bit EL0 - ventry el0_fiq_invalid // FIQ 32-bit EL0 - ventry el0_error_invalid // Error 32-bit EL0 + kernel_ventry el0_sync_invalid // Synchronous 32-bit EL0 + kernel_ventry el0_irq_invalid // IRQ 32-bit EL0 + kernel_ventry el0_fiq_invalid // FIQ 32-bit EL0 + kernel_ventry el0_error_invalid // Error 32-bit EL0 #endif END(vectors) -- GitLab From 9edf428adeff4d4afb8eede2c6083a684bea57c9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 26 Nov 2015 13:49:39 +0000 Subject: [PATCH 2278/5498] UPSTREAM: arm64: mm: keep reserved ASIDs in sync with mm after multiple rollovers Under some unusual context-switching patterns, it is possible to end up with multiple threads from the same mm running concurrently with different ASIDs: 1. CPU x schedules task t with mm p containing ASID a and generation g This task doesn't block and the CPU doesn't context switch. So: * per_cpu(active_asid, x) = {g,a} * p->context.id = {g,a} 2. Some other CPU generates an ASID rollover. The global generation is now (g + 1). CPU x is still running t, with no context switch and so per_cpu(reserved_asid, x) = {g,a} 3. CPU y schedules task t', which shares mm p with t. The generation mismatches, so we take the slowpath and hit the reserved ASID from CPU x. p is then updated so that p->context.id = {g + 1,a} 4. CPU y schedules some other task u, which has an mm != p. 5. Some other CPU generates *another* CPU rollover. The global generation is now (g + 2). CPU x is still running t, with no context switch and so per_cpu(reserved_asid, x) = {g,a}. 6. CPU y once again schedules task t', but now *fails* to hit the reserved ASID from CPU x because of the generation mismatch. This results in a new ASID being allocated, despite the fact that t is still running on CPU x with the same mm. Consequently, TLBIs (e.g. as a result of CoW) will not be synchronised between the two threads. This patch fixes the problem by updating all of the matching reserved ASIDs when we hit on the slowpath (i.e. in step 3 above). This keeps the reserved ASIDs in-sync with the mm and avoids the problem. Reported-by: Tony Thompson Reviewed-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas (cherry picked from commit 0ebea8088095f1c18c1d1de284ccc4c479ca21c1) Signed-off-by: Greg Hackmann Change-Id: Ib8bb01f3349e3f2b2cba433c09a0303d2e1ef799 Git-Commit: 6e0432e6d0844d05427166f0f7a3edad37362535 Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/mm/context.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 43cff0e6f52e..25128089c386 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -76,13 +76,28 @@ static void flush_context(unsigned int cpu) __flush_icache_all(); } -static int is_reserved_asid(u64 asid) +static bool check_update_reserved_asid(u64 asid, u64 newasid) { int cpu; - for_each_possible_cpu(cpu) - if (per_cpu(reserved_asids, cpu) == asid) - return 1; - return 0; + bool hit = false; + + /* + * Iterate over the set of reserved ASIDs looking for a match. + * If we find one, then we can update our mm to use newasid + * (i.e. the same ASID in the current generation) but we can't + * exit the loop early, since we need to ensure that all copies + * of the old ASID are updated to reflect the mm. Failure to do + * so could result in us missing the reserved ASID in a future + * generation. + */ + for_each_possible_cpu(cpu) { + if (per_cpu(reserved_asids, cpu) == asid) { + hit = true; + per_cpu(reserved_asids, cpu) = newasid; + } + } + + return hit; } static u64 new_context(struct mm_struct *mm, unsigned int cpu) @@ -92,12 +107,14 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) u64 generation = atomic64_read(&asid_generation); if (asid != 0) { + u64 newasid = generation | (asid & ~ASID_MASK); + /* * If our current ASID was active during a rollover, we * can continue to use it and this was just a false alarm. */ - if (is_reserved_asid(asid)) - return generation | (asid & ~ASID_MASK); + if (check_update_reserved_asid(asid, newasid)) + return newasid; /* * We had a valid ASID in a previous life, so try to re-use @@ -105,7 +122,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) */ asid &= ~ASID_MASK; if (!__test_and_set_bit(asid, asid_map)) - goto bump_gen; + return newasid; } /* @@ -129,10 +146,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) set_asid: __set_bit(asid, asid_map); cur_idx = asid; - -bump_gen: - asid |= generation; - return asid; + return asid | generation; } void check_and_switch_context(struct mm_struct *mm, unsigned int cpu) -- GitLab From a4cd37172b97f4a194697c25269a6962d45f3656 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 18 Apr 2016 17:09:44 +0200 Subject: [PATCH 2279/5498] UPSTREAM: arm64: introduce mov_q macro to move a constant into a 64-bit register Implement a macro mov_q that can be used to move an immediate constant into a 64-bit register, using between 2 and 4 movz/movk instructions (depending on the operand) Acked-by: Catalin Marinas Signed-off-by: Ard Biesheuvel Signed-off-by: Will Deacon (cherry picked from commit 30b5ba5cf333cc650e474eaf2cc1ae91bc7cf89f) Signed-off-by: Greg Hackmann Change-Id: Ib7db6db569cfda7ce19bb2f9fc4cf336e862cd1d Git-Commit: 76ec6821bd1f90a316904ed189ba7bf7988025c1 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/assembler.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index c66a176f4ee8..34381f380943 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -321,4 +321,24 @@ lr .req x30 // link register mrs \rd, sp_el0 .endm + /* + * mov_q - move an immediate constant into a 64-bit register using + * between 2 and 4 movz/movk instructions (depending on the + * magnitude and sign of the operand) + */ + .macro mov_q, reg, val + .if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff) + movz \reg, :abs_g1_s:\val + .else + .if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff) + movz \reg, :abs_g2_s:\val + .else + movz \reg, :abs_g3:\val + movk \reg, :abs_g2_nc:\val + .endif + movk \reg, :abs_g1_nc:\val + .endif + movk \reg, :abs_g0_nc:\val + .endm + #endif /* __ASM_ASSEMBLER_H */ -- GitLab From 0744db8e041167da17281281f662d7df32cdf613 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 9 Nov 2015 09:55:46 +0100 Subject: [PATCH 2280/5498] BACKPORT: arm64: fix R/O permissions of FDT mapping The mapping permissions of the FDT are set to 'PAGE_KERNEL | PTE_RDONLY' in an attempt to map the FDT as read-only. However, not only does this break at build time under STRICT_MM_TYPECHECKS (since the two terms are of different types in that case), it also results in both the PTE_WRITE and PTE_RDONLY attributes to be set, which means the region is still writable under ARMv8.1 DBM (and an attempted write will simply clear the PT_RDONLY bit). So instead, define PAGE_KERNEL_RO (which already has an established meaning across architectures) and use that instead. Signed-off-by: Ard Biesheuvel Signed-off-by: Catalin Marinas (cherry picked from commit fb226c3d7c77b4f99cee675795cc0e70937c56ee) [ghackmann@google.com: only take the PAGE_KERNEL_RO definition, since the FDT mapping code in question doesn't exist in 3.18] Signed-off-by: Greg Hackmann Change-Id: I9c0e0e342f94bd76cff34643aa9f9a9327b43fb5 Git-Commit: e906a21034395aaee1c38cafed3bd44ad76cdb5d Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/pgtable.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 0ee178f2ece7..395f5a23a2f0 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -78,6 +78,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #define _PAGE_DEFAULT (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL)) #define PAGE_KERNEL __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE) +#define PAGE_KERNEL_RO __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_RDONLY) #define PAGE_KERNEL_EXEC __pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_WRITE) #define PAGE_HYP __pgprot(_PAGE_DEFAULT | PTE_HYP) -- GitLab From 4b076838614baeb150e95d682d51c46e077a6333 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 12:56:18 +0100 Subject: [PATCH 2281/5498] FROMLIST: arm64: mm: Use non-global mappings for kernel space In preparation for unmapping the kernel whilst running in userspace, make the kernel mappings non-global so we can avoid expensive TLB invalidation on kernel exit to userspace. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit e046eb0c9bf26d94be9e4592c00c7a78b0fa9bfd) [ghackmann@google.com: apply pgtable-prot.h changes to pgtable.h instead] Signed-off-by: Greg Hackmann Change-Id: I3f3f0ee0194f315f301f239e05e31ffe0fb733ad Git-Commit: b92b29768a633895b09f1df0cfd11ea4cdcc8343 Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/kernel-pgtable.h | 12 ++++++++++-- arch/arm64/include/asm/pgtable.h | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h index d06d7f06a583..865887934504 100644 --- a/arch/arm64/include/asm/kernel-pgtable.h +++ b/arch/arm64/include/asm/kernel-pgtable.h @@ -61,8 +61,16 @@ /* * Initial memory map attributes. */ -#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) -#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) +#define _SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) +#define _SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) + +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define SWAPPER_PTE_FLAGS (_SWAPPER_PTE_FLAGS | PTE_NG) +#define SWAPPER_PMD_FLAGS (_SWAPPER_PMD_FLAGS | PMD_SECT_NG) +#else +#define SWAPPER_PTE_FLAGS _SWAPPER_PTE_FLAGS +#define SWAPPER_PMD_FLAGS _SWAPPER_PMD_FLAGS +#endif #ifdef CONFIG_ARM64_64K_PAGES #define SWAPPER_MM_MMUFLAGS (PTE_ATTRINDX(MT_NORMAL) | SWAPPER_PTE_FLAGS) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 395f5a23a2f0..eb6b0692d59b 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -63,8 +63,16 @@ extern void __pmd_error(const char *file, int line, unsigned long val); extern void __pud_error(const char *file, int line, unsigned long val); extern void __pgd_error(const char *file, int line, unsigned long val); -#define PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) -#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) +#define _PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) +#define _PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) + +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define PROT_DEFAULT (_PROT_DEFAULT | PTE_NG) +#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_SECT_NG) +#else +#define PROT_DEFAULT _PROT_DEFAULT +#define PROT_SECT_DEFAULT _PROT_SECT_DEFAULT +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ #define PROT_DEVICE_nGnRnE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRnE)) #define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE)) @@ -76,18 +84,19 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #define PROT_SECT_NORMAL_EXEC (PROT_SECT_DEFAULT | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL)) #define _PAGE_DEFAULT (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL)) +#define _HYP_PAGE_DEFAULT (_PAGE_DEFAULT & ~PTE_NG) #define PAGE_KERNEL __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE) #define PAGE_KERNEL_RO __pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_RDONLY) #define PAGE_KERNEL_EXEC __pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_WRITE) -#define PAGE_HYP __pgprot(_PAGE_DEFAULT | PTE_HYP) +#define PAGE_HYP __pgprot(_HYP_PAGE_DEFAULT | PTE_HYP) #define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP) #define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY) #define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN) -#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_PXN | PTE_UXN) +#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_NG | PTE_PXN | PTE_UXN) #define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE) #define PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE) #define PAGE_COPY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) -- GitLab From 281b8707446125fe5ffa0a8685835459f5fc2f04 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 13:04:48 +0100 Subject: [PATCH 2282/5498] FROMLIST: arm64: mm: Temporarily disable ARM64_SW_TTBR0_PAN We're about to rework the way ASIDs are allocated, switch_mm is implemented and low-level kernel entry/exit is handled, so keep the ARM64_SW_TTBR0_PAN code out of the way whilst we do the heavy lifting. It will be re-enabled in a subsequent patch. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 376133b7edc20f237a42e4c72415cc9e8c0a9704) Signed-off-by: Greg Hackmann Change-Id: I9ae16925550c34c36f2fa414830d06dc1457b626 Git-Commit: 735e1554285ab9fb564a96642849571f9c7e85b7 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 0444366b29d7..65af6d7045ed 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -871,6 +871,7 @@ endif config ARM64_SW_TTBR0_PAN bool "Emulate Privileged Access Never using TTBR0_EL1 switching" + depends on BROKEN # Temporary while switch_mm is reworked help Enabling this option prevents the kernel from accessing user-space memory directly by pointing TTBR0_EL1 to a reserved -- GitLab From 42f462dbc0c79e49a3cbdf86820ba552c252df5e Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 13:19:09 +0100 Subject: [PATCH 2283/5498] FROMLIST: arm64: mm: Move ASID from TTBR0 to TTBR1 In preparation for mapping kernelspace and userspace with different ASIDs, move the ASID to TTBR1 and update switch_mm to context-switch TTBR0 via an invalid mapping (the zero page). Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 7655abb953860485940d4de74fb45a8192149bb6) [ghackmann@google.com: adjust context] Signed-off-by: Greg Hackmann Change-Id: I374cd4154416e27a7c9d2a968dfc896f4bfc2763 Git-Commit: 6de449786ee41fb95db207ef68cf865ca8929218 Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/mmu_context.h | 7 +++++++ arch/arm64/include/asm/pgtable-hwdef.h | 2 ++ arch/arm64/include/asm/proc-fns.h | 6 ------ arch/arm64/mm/proc.S | 9 ++++++--- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 1f7dcb26bdfa..2156ee710274 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -63,6 +63,13 @@ static inline void cpu_set_reserved_ttbr0(void) : "r" (ttbr)); } +static inline void cpu_switch_mm(pgd_t *pgd, struct mm_struct *mm) +{ + BUG_ON(pgd == swapper_pg_dir); + cpu_set_reserved_ttbr0(); + cpu_do_switch_mm(virt_to_phys(pgd),mm); +} + /* * TCR.T0SZ value to use when the ID map is active. Usually equals * TCR_T0SZ(VA_BITS), unless system RAM is positioned very high in diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h index 5e50782f625d..bfe379d13b83 100644 --- a/arch/arm64/include/asm/pgtable-hwdef.h +++ b/arch/arm64/include/asm/pgtable-hwdef.h @@ -164,6 +164,8 @@ #define TCR_TG1_16K (UL(1) << 30) #define TCR_TG1_4K (UL(2) << 30) #define TCR_TG1_64K (UL(3) << 30) + +#define TCR_A1 (UL(1) << 22) #define TCR_ASID16 (UL(1) << 36) #define TCR_TBI0 (UL(1) << 37) diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h index b5240e868784..d35dcebb121e 100644 --- a/arch/arm64/include/asm/proc-fns.h +++ b/arch/arm64/include/asm/proc-fns.h @@ -35,12 +35,6 @@ extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr); #include -#define cpu_switch_mm(pgd,mm) \ -do { \ - BUG_ON(pgd == swapper_pg_dir); \ - cpu_do_switch_mm(virt_to_phys(pgd),mm); \ -} while (0) - #define cpu_get_pgd() \ ({ \ unsigned long pg; \ diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index d579089a1649..3cbf35a95821 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -127,9 +127,12 @@ ENDPROC(cpu_do_resume) * - pgd_phys - physical address of new TTB */ ENTRY(cpu_do_switch_mm) + mrs x2, ttbr1_el1 mmid x1, x1 // get mm->context.id - bfi x0, x1, #48, #16 // set the ASID - msr ttbr0_el1, x0 // set TTBR0 + bfi x2, x1, #48, #16 // set the ASID + msr ttbr1_el1, x2 // in TTBR1 (since TCR.A1 is set) + isb + msr ttbr0_el1, x0 // now update TTBR0 isb ret ENDPROC(cpu_do_switch_mm) @@ -182,7 +185,7 @@ ENTRY(__cpu_setup) * both user and kernel. */ ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \ - TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 + TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 | TCR_A1 tcr_set_idmap_t0sz x10, x9 /* -- GitLab From f61d4652cfb678025529fc40e1abd15ceaac51a1 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 13:58:16 +0100 Subject: [PATCH 2284/5498] FROMLIST: arm64: mm: Fix and re-enable ARM64_SW_TTBR0_PAN With the ASID now installed in TTBR1, we can re-enable ARM64_SW_TTBR0_PAN by ensuring that we switch to a reserved ASID of zero when disabling user access and restore the active user ASID on the uaccess enable path. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 27a921e75711d924617269e0ba4adb8bae9fd0d1) [ghackmann@google.com: adjust context, applying asm-uaccess.h changes to uaccess.h] Signed-off-by: Greg Hackmann Change-Id: Iec7bff74f0605cea58793f2d1cb0d6546fa571e2 Git-Commit: 4ea2126b109f510b78d03b051980634b16451c2c Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/Kconfig | 1 - arch/arm64/include/asm/uaccess.h | 46 +++++++++++++++++++++++--------- arch/arm64/kernel/entry.S | 4 +-- arch/arm64/lib/clear_user.S | 2 +- arch/arm64/lib/copy_from_user.S | 2 +- arch/arm64/lib/copy_in_user.S | 2 +- arch/arm64/lib/copy_to_user.S | 2 +- arch/arm64/mm/cache.S | 2 +- arch/arm64/xen/hypercall.S | 2 +- 9 files changed, 42 insertions(+), 21 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 65af6d7045ed..0444366b29d7 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -871,7 +871,6 @@ endif config ARM64_SW_TTBR0_PAN bool "Emulate Privileged Access Never using TTBR0_EL1 switching" - depends on BROKEN # Temporary while switch_mm is reworked help Enabling this option prevents the kernel from accessing user-space memory directly by pointing TTBR0_EL1 to a reserved diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index 21963026efd0..cfef719287d9 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -136,15 +136,19 @@ static inline void __uaccess_ttbr0_disable(void) { unsigned long ttbr; + ttbr = read_sysreg(ttbr1_el1); /* reserved_ttbr0 placed at the end of swapper_pg_dir */ - ttbr = read_sysreg(ttbr1_el1) + SWAPPER_DIR_SIZE; - write_sysreg(ttbr, ttbr0_el1); + write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); + isb(); + /* Set reserved ASID */ + ttbr &= ~(0xffffUL << 48); + write_sysreg(ttbr, ttbr1_el1); isb(); } static inline void __uaccess_ttbr0_enable(void) { - unsigned long flags; + unsigned long flags, ttbr0, ttbr1; /* * Disable interrupts to avoid preemption between reading the 'ttbr0' @@ -152,7 +156,16 @@ static inline void __uaccess_ttbr0_enable(void) * roll-over and an update of 'ttbr0'. */ local_irq_save(flags); - write_sysreg(current_thread_info()->ttbr0, ttbr0_el1); + ttbr0 = current_thread_info()->ttbr0; + + /* Restore active ASID */ + ttbr1 = read_sysreg(ttbr1_el1); + ttbr1 |= ttbr0 & (0xffffUL << 48); + write_sysreg(ttbr1, ttbr1_el1); + isb(); + + /* Restore user page table */ + write_sysreg(ttbr0, ttbr0_el1); isb(); local_irq_restore(flags); } @@ -440,11 +453,20 @@ extern __must_check long strnlen_user(const char __user *str, long n); add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb + sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE + bic \tmp1, \tmp1, #(0xffff << 48) + msr ttbr1_el1, \tmp1 // set reserved ASID + isb .endm - .macro __uaccess_ttbr0_enable, tmp1 + .macro __uaccess_ttbr0_enable, tmp1, tmp2 get_thread_info \tmp1 ldr \tmp1, [\tmp1, #TSK_TI_TTBR0] // load saved TTBR0_EL1 + mrs \tmp2, ttbr1_el1 + extr \tmp2, \tmp2, \tmp1, #48 + ror \tmp2, \tmp2, #16 + msr ttbr1_el1, \tmp2 // set the active ASID + isb msr ttbr0_el1, \tmp1 // set the non-PAN TTBR0_EL1 isb .endm @@ -455,18 +477,18 @@ alternative_if_not ARM64_HAS_PAN alternative_else_nop_endif .endm - .macro uaccess_ttbr0_enable, tmp1, tmp2 + .macro uaccess_ttbr0_enable, tmp1, tmp2, tmp3 alternative_if_not ARM64_HAS_PAN - save_and_disable_irq \tmp2 // avoid preemption - __uaccess_ttbr0_enable \tmp1 - restore_irq \tmp2 + save_and_disable_irq \tmp3 // avoid preemption + __uaccess_ttbr0_enable \tmp1, \tmp2 + restore_irq \tmp3 alternative_else_nop_endif .endm #else .macro uaccess_ttbr0_disable, tmp1 .endm - .macro uaccess_ttbr0_enable, tmp1, tmp2 + .macro uaccess_ttbr0_enable, tmp1, tmp2, tmp3 .endm #endif @@ -480,8 +502,8 @@ alternative_if ARM64_ALT_PAN_NOT_UAO alternative_else_nop_endif .endm - .macro uaccess_enable_not_uao, tmp1, tmp2 - uaccess_ttbr0_enable \tmp1, \tmp2 + .macro uaccess_enable_not_uao, tmp1, tmp2, tmp3 + uaccess_ttbr0_enable \tmp1, \tmp2, \tmp3 alternative_if ARM64_ALT_PAN_NOT_UAO SET_PSTATE_PAN(0) alternative_else_nop_endif diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index e6eb3ce251c1..2f01978ab780 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -129,7 +129,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif .if \el != 0 - mrs x21, ttbr0_el1 + mrs x21, ttbr1_el1 tst x21, #0xffff << 48 // Check for the reserved ASID orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR b.eq 1f // TTBR0 access already disabled @@ -193,7 +193,7 @@ alternative_else_nop_endif tbnz x22, #22, 1f // Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set .endif - __uaccess_ttbr0_enable x0 + __uaccess_ttbr0_enable x0, x1 1: .if \el != 0 and x22, x22, #~PSR_PAN_BIT // ARMv8.0 CPUs do not understand this bit diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S index d7150e30438a..dd65ca253eb4 100644 --- a/arch/arm64/lib/clear_user.S +++ b/arch/arm64/lib/clear_user.S @@ -30,7 +30,7 @@ * Alignment fixed up by hardware. */ ENTRY(__clear_user) - uaccess_enable_not_uao x2, x3 + uaccess_enable_not_uao x2, x3, x4 mov x2, x1 // save the size for fixup return subs x1, x1, #8 b.mi 2f diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 90154f3f7f2a..1ff23f81e242 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -64,7 +64,7 @@ end .req x5 ENTRY(__arch_copy_from_user) - uaccess_enable_not_uao x3, x4 + uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" uaccess_disable_not_uao x3 diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S index 718b1c4e2f85..074d52fcd75b 100644 --- a/arch/arm64/lib/copy_in_user.S +++ b/arch/arm64/lib/copy_in_user.S @@ -65,7 +65,7 @@ end .req x5 ENTRY(__copy_in_user) - uaccess_enable_not_uao x3, x4 + uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" uaccess_disable_not_uao x3 diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S index e99e31c9acac..67118444cde0 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -63,7 +63,7 @@ end .req x5 ENTRY(__arch_copy_to_user) - uaccess_enable_not_uao x3, x4 + uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" uaccess_disable_not_uao x3 diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 4f71e90bf7a5..98134de8dec7 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -158,7 +158,7 @@ ENTRY(flush_icache_range) * - end - virtual end address of region */ ENTRY(__flush_cache_user_range) - uaccess_ttbr0_enable x2, x3 + uaccess_ttbr0_enable x2, x3, x4 dcache_line_size x2, x3 sub x3, x2, #1 bic x4, x0, x3 diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index b96db5dafec4..27b38711023b 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -98,7 +98,7 @@ ENTRY(privcmd_call) * need the explicit uaccess_enable/disable if the TTBR0 PAN emulation * is enabled (it implies that hardware UAO and PAN disabled). */ - uaccess_ttbr0_enable x6, x7 + uaccess_ttbr0_enable x6, x7, x8 hvc XEN_IMM /* -- GitLab From b98611c82c7a9fdd27eaf253009828e10b5bd4bf Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 14:10:28 +0100 Subject: [PATCH 2285/5498] FROMLIST: arm64: mm: Allocate ASIDs in pairs In preparation for separate kernel/user ASIDs, allocate them in pairs for each mm_struct. The bottom bit distinguishes the two: if it is set, then the ASID will map only userspace. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 0c8ea531b7740754cf374ca8b7510655f569c5e3) [ghackmann@google.com: adjust context] Signed-off-by: Greg Hackmann Change-Id: Ie9dd8ca183007c486ef13fd3c560223d263bef4b Git-Commit: b7032349724a4bd7da6e6426f8742db8fc6095aa Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/mmu.h | 2 ++ arch/arm64/mm/context.c | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 539194f0b4a0..8b051b4fcaa5 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -16,6 +16,8 @@ #ifndef __ASM_MMU_H #define __ASM_MMU_H +#define USER_ASID_FLAG (UL(1) << 48) + typedef struct { atomic64_t id; void *vdso; diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 25128089c386..10d68e438a37 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -38,7 +38,16 @@ static cpumask_t tlb_flush_pending; #define ASID_MASK (~GENMASK(asid_bits - 1, 0)) #define ASID_FIRST_VERSION (1UL << asid_bits) -#define NUM_USER_ASIDS ASID_FIRST_VERSION + +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define NUM_USER_ASIDS (ASID_FIRST_VERSION >> 1) +#define asid2idx(asid) (((asid) & ~ASID_MASK) >> 1) +#define idx2asid(idx) (((idx) << 1) & ~ASID_MASK) +#else +#define NUM_USER_ASIDS (ASID_FIRST_VERSION) +#define asid2idx(asid) ((asid) & ~ASID_MASK) +#define idx2asid(idx) asid2idx(idx) +#endif static void flush_context(unsigned int cpu) { @@ -65,7 +74,7 @@ static void flush_context(unsigned int cpu) */ if (asid == 0) asid = per_cpu(reserved_asids, i); - __set_bit(asid & ~ASID_MASK, asid_map); + __set_bit(asid2idx(asid), asid_map); per_cpu(reserved_asids, i) = asid; } @@ -120,16 +129,16 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) * We had a valid ASID in a previous life, so try to re-use * it if possible. */ - asid &= ~ASID_MASK; - if (!__test_and_set_bit(asid, asid_map)) + if (!__test_and_set_bit(asid2idx(asid), asid_map)) return newasid; } /* * Allocate a free ASID. If we can't find one, take a note of the - * currently active ASIDs and mark the TLBs as requiring flushes. - * We always count from ASID #1, as we use ASID #0 when setting a - * reserved TTBR0 for the init_mm. + * currently active ASIDs and mark the TLBs as requiring flushes. We + * always count from ASID #2 (index 1), as we use ASID #0 when setting + * a reserved TTBR0 for the init_mm and we allocate ASIDs in even/odd + * pairs. */ asid = find_next_zero_bit(asid_map, NUM_USER_ASIDS, cur_idx); if (asid != NUM_USER_ASIDS) @@ -146,7 +155,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu) set_asid: __set_bit(asid, asid_map); cur_idx = asid; - return asid | generation; + return idx2asid(asid) | generation; } void check_and_switch_context(struct mm_struct *mm, unsigned int cpu) -- GitLab From aff9ddcd1268836d7a2273abd3e100aabb334ac1 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 13:58:08 +0000 Subject: [PATCH 2286/5498] FROMLIST: arm64: mm: Add arm64_kernel_unmapped_at_el0 helper In order for code such as TLB invalidation to operate efficiently when the decision to map the kernel at EL0 is determined at runtime, this patch introduces a helper function, arm64_kernel_unmapped_at_el0, to determine whether or not the kernel is mapped whilst running in userspace. Currently, this just reports the value of CONFIG_UNMAP_KERNEL_AT_EL0, but will later be hooked up to a fake CPU capability using a static key. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit fc0e1299da548b32440051f58f08e0c1eb7edd0b) Signed-off-by: Greg Hackmann Change-Id: I5f8c15e5f8ba0c521d0ed7515c003598bf3d7cf8 Git-Commit: 1434f49fc0dae1dc85f22f92d21e189422370185 Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/mmu.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 8b051b4fcaa5..d94389667066 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -18,6 +18,8 @@ #define USER_ASID_FLAG (UL(1) << 48) +#ifndef __ASSEMBLY__ + typedef struct { atomic64_t id; void *vdso; @@ -30,6 +32,11 @@ typedef struct { */ #define ASID(mm) ((mm)->context.id.counter & 0xffff) +static inline bool arm64_kernel_unmapped_at_el0(void) +{ + return IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0); +} + extern void paging_init(void); extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); extern void init_mem_pgprot(void); @@ -38,4 +45,5 @@ extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot); +#endif /* !__ASSEMBLY__ */ #endif -- GitLab From c2d5fd02ca648680b2f2b477cb15dbd8b05432ea Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 10 Aug 2017 14:13:33 +0100 Subject: [PATCH 2287/5498] FROMLIST: arm64: mm: Invalidate both kernel and user ASIDs when performing TLBI Since an mm has both a kernel and a user ASID, we need to ensure that broadcast TLB maintenance targets both address spaces so that things like CoW continue to work with the uaccess primitives in the kernel. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 9b0de864b5bc298ea53005ad812f3386f81aee9c) Signed-off-by: Greg Hackmann Change-Id: I1f665f7b609872424071ecef14d6fab2b2d721ea Git-Commit: eed2e002366647f8aefb9dc938a402294e16396d Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/tlbflush.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index 0e0fe68d2156..0cfeecfa60ab 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -23,6 +23,7 @@ #include #include +#include /* * Raw TLBI operations. @@ -42,6 +43,11 @@ #define __tlbi(op, ...) __TLBI_N(op, ##__VA_ARGS__, 1, 0) +#define __tlbi_user(op, arg) do { \ + if (arm64_kernel_unmapped_at_el0()) \ + __tlbi(op, (arg) | USER_ASID_FLAG); \ +} while (0) + /* * TLB Management * ============== @@ -109,6 +115,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) dsb(ishst); __tlbi(aside1is, asid); + __tlbi_user(aside1is, asid); dsb(ish); #endif } @@ -127,6 +134,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, dsb(ishst); __tlbi(vale1is, addr); + __tlbi_user(vale1is, addr); dsb(ish); #endif } @@ -154,10 +162,13 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) { - if (last_level) + if (last_level) { __tlbi(vale1is, addr); - else + __tlbi_user(vale1is, addr); + } else { __tlbi(vae1is, addr); + __tlbi_user(vae1is, addr); + } } dsb(ish); } @@ -197,6 +208,7 @@ static inline void __flush_tlb_pgtable(struct mm_struct *mm, unsigned long addr = uaddr >> 12 | (ASID(mm) << 48); __tlbi(vae1is, addr); + __tlbi_user(vae1is, addr); dsb(ish); } -- GitLab From 7693c4909732a8eded612bf1bd88fba8b745fe1c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:07:40 +0000 Subject: [PATCH 2288/5498] FROMLIST: arm64: entry: Add exception trampoline page for exceptions from EL0 To allow unmapping of the kernel whilst running at EL0, we need to point the exception vectors at an entry trampoline that can map/unmap the kernel on entry/exit respectively. This patch adds the trampoline page, although it is not yet plugged into the vector table and is therefore unused. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit c7b9adaf85f818d747eeff5145eb4095ccd587fb) [ghackmann@google.com: adjust context] Signed-off-by: Greg Hackmann Change-Id: Ib74f0a15fb60db15f0b7f85d3007c1c60f8ff04e Git-Commit: 6415b1f83e554dc76816cbb76ed20e4e73633f33 Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/kernel/entry.S | 85 +++++++++++++++++++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 17 +++++++ 2 files changed, 102 insertions(+) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 2f01978ab780..4080c287e623 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -848,6 +849,90 @@ __ni_sys_trace: bl do_ni_syscall b __sys_trace_return +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +/* + * Exception vectors trampoline. + */ + .pushsection ".entry.tramp.text", "ax" + + .macro tramp_map_kernel, tmp + mrs \tmp, ttbr1_el1 + sub \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) + bic \tmp, \tmp, #USER_ASID_FLAG + msr ttbr1_el1, \tmp + .endm + + .macro tramp_unmap_kernel, tmp + mrs \tmp, ttbr1_el1 + add \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) + orr \tmp, \tmp, #USER_ASID_FLAG + msr ttbr1_el1, \tmp + /* + * We avoid running the post_ttbr_update_workaround here because the + * user and kernel ASIDs don't have conflicting mappings, so any + * "blessing" as described in: + * + * http://lkml.kernel.org/r/56BB848A.6060603@caviumnetworks.com + * + * will not hurt correctness. Whilst this may partially defeat the + * point of using split ASIDs in the first place, it avoids + * the hit of invalidating the entire I-cache on every return to + * userspace. + */ + .endm + + .macro tramp_ventry, regsize = 64 + .align 7 +1: + .if \regsize == 64 + msr tpidrro_el0, x30 // Restored in kernel_ventry + .endif + tramp_map_kernel x30 + ldr x30, =vectors + prfm plil1strm, [x30, #(1b - tramp_vectors)] + msr vbar_el1, x30 + add x30, x30, #(1b - tramp_vectors) + isb + br x30 + .endm + + .macro tramp_exit, regsize = 64 + adr x30, tramp_vectors + msr vbar_el1, x30 + tramp_unmap_kernel x30 + .if \regsize == 64 + mrs x30, far_el1 + .endif + eret + .endm + + .align 11 +ENTRY(tramp_vectors) + .space 0x400 + + tramp_ventry + tramp_ventry + tramp_ventry + tramp_ventry + + tramp_ventry 32 + tramp_ventry 32 + tramp_ventry 32 + tramp_ventry 32 +END(tramp_vectors) + +ENTRY(tramp_exit_native) + tramp_exit +END(tramp_exit_native) + +ENTRY(tramp_exit_compat) + tramp_exit 32 +END(tramp_exit_compat) + + .ltorg + .popsection // .entry.tramp.text +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ + /* * Special system call wrappers. */ diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index a38f929fc6d0..1ded30f007b4 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -40,6 +40,17 @@ jiffies = jiffies_64; *(.hyp.text) \ VMLINUX_SYMBOL(__hyp_text_end) = .; +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +#define TRAMP_TEXT \ + . = ALIGN(PAGE_SIZE); \ + VMLINUX_SYMBOL(__entry_tramp_text_start) = .; \ + *(.entry.tramp.text) \ + . = ALIGN(PAGE_SIZE); \ + VMLINUX_SYMBOL(__entry_tramp_text_end) = .; +#else +#define TRAMP_TEXT +#endif + /* * The size of the PE/COFF section that covers the kernel image, which * runs from stext to _edata, must be a round multiple of the PE/COFF @@ -97,6 +108,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT HYPERVISOR_TEXT + TRAMP_TEXT *(.fixup) *(.gnu.warning) . = ALIGN(16); @@ -167,6 +179,11 @@ SECTIONS . += RESERVED_TTBR0_SIZE; #endif +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + tramp_pg_dir = .; + . += PAGE_SIZE; +#endif + _end = .; STABS_DEBUG -- GitLab From c8d8620c1d8b1f904012fb27e88a5741751f4d4d Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:14:17 +0000 Subject: [PATCH 2289/5498] FROMLIST: arm64: mm: Map entry trampoline into trampoline and kernel page tables The exception entry trampoline needs to be mapped at the same virtual address in both the trampoline page table (which maps nothing else) and also the kernel page table, so that we can swizzle TTBR1_EL1 on exceptions from and return to EL0. This patch maps the trampoline at a fixed virtual address in the fixmap area of the kernel virtual address space, which allows the kernel proper to be randomized with respect to the trampoline when KASLR is enabled. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 51a0048beb449682d632d0af52a515adb9f9882e) [ghackmann@google.com: - adjust context - tweak __create_pgd_mapping() call to match 3.18 APIs - expand definition of PGD_SIZE, which is private to pgd.c in 3.18] - open-code pgd_pgtable_alloc(), based on version in 4.9] Signed-off-by: Greg Hackmann Change-Id: I85f681757feeb43c6cb55fce10a619e24d31c9bd Git-Commit: 771dd6d68c7b40528926d01fb1d6a8a47416e83c Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: fixes for force pages feature] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/fixmap.h | 4 ++++ arch/arm64/include/asm/pgtable.h | 1 + arch/arm64/kernel/asm-offsets.c | 5 +++++ arch/arm64/mm/mmu.c | 37 ++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h index defa0ff98250..cc9b9a6a238c 100644 --- a/arch/arm64/include/asm/fixmap.h +++ b/arch/arm64/include/asm/fixmap.h @@ -33,6 +33,10 @@ enum fixed_addresses { FIX_HOLE, FIX_EARLYCON_MEM_BASE, +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + FIX_ENTRY_TRAMP_TEXT, +#define TRAMP_VALIAS (__fix_to_virt(FIX_ENTRY_TRAMP_TEXT)) +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ __end_of_permanent_fixed_addresses, /* diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index eb6b0692d59b..705a01da6a96 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -487,6 +487,7 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; extern pgd_t idmap_pg_dir[PTRS_PER_PGD]; +extern pgd_t tramp_pg_dir[PTRS_PER_PGD]; /* * Encode and decode a swap entry: diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 92a5d25bff1a..a865573461e1 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,10 @@ int main(void) DEFINE(SLEEP_SAVE_SP_SZ, sizeof(struct sleep_save_sp)); DEFINE(SLEEP_SAVE_SP_PHYS, offsetof(struct sleep_save_sp, save_ptr_stash_phys)); DEFINE(SLEEP_SAVE_SP_VIRT, offsetof(struct sleep_save_sp, save_ptr_stash)); +#endif + BLANK(); +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + DEFINE(TRAMP_VALIAS, TRAMP_VALIAS); #endif return 0; } diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index c7c81ab26c95..b5d7dbc41e49 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -670,6 +670,43 @@ void fixup_init(void) PAGE_KERNEL); } +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +static void __init *pgd_pgtable_alloc(unsigned long size) +{ + void *ptr; + BUG_ON(size != PAGE_SIZE); + + ptr = (void *)__get_free_page(PGALLOC_GFP); + if (!ptr || !pgtable_page_ctor(virt_to_page(ptr))) + BUG(); + + /* Ensure the zeroed page is visible to the page table walker */ + dsb(ishst); + return ptr; +} + +static int __init map_entry_trampoline(void) +{ + extern char __entry_tramp_text_start[]; + + pgprot_t prot = PAGE_KERNEL_EXEC; + phys_addr_t pa_start = __pa_symbol(__entry_tramp_text_start); + + /* The trampoline is always mapped and can therefore be global */ + pgprot_val(prot) &= ~PTE_NG; + + /* Map only the text into the trampoline page table */ + memset(tramp_pg_dir, 0, PTRS_PER_PGD * sizeof(pgd_t)); + __create_mapping(NULL, tramp_pg_dir + pgd_index(TRAMP_VALIAS), pa_start, + TRAMP_VALIAS, PAGE_SIZE, prot, pgd_pgtable_alloc, false); + + /* ...as well as the kernel page table */ + __set_fixmap(FIX_ENTRY_TRAMP_TEXT, pa_start, prot); + return 0; +} +core_initcall(map_entry_trampoline); +#endif + /* * paging_init() sets up the page tables, initialises the zone memory * maps and sets up the zero page. -- GitLab From 4880b3ed21d970d53af707cad640c76eb480389b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:20:21 +0000 Subject: [PATCH 2290/5498] FROMLIST: arm64: entry: Explicitly pass exception level to kernel_ventry macro We will need to treat exceptions from EL0 differently in kernel_ventry, so rework the macro to take the exception level as an argument and construct the branch target using that. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 5b1f7fe41909cde40decad9f0e8ee585777a0538) [ghackmann@google.com: adjust context and kernel entry point names] Signed-off-by: Greg Hackmann Change-Id: I264359bfbac842d1673edd939c4997a3f68714b9 Git-Commit: eb8401717f414fba0530373960a02fb1c2fad54f Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/kernel/entry.S | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 4080c287e623..2ed0f6f8014c 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -70,10 +70,10 @@ #define BAD_FIQ 2 #define BAD_ERROR 3 - .macro kernel_ventry label + .macro kernel_ventry, el, label, regsize = 64 .align 7 sub sp, sp, #S_FRAME_SIZE - b \label + b el\()\el\()_\label .endm .macro kernel_entry, el, regsize = 64 @@ -276,31 +276,31 @@ tsk .req x28 // current thread_info .align 11 ENTRY(vectors) - kernel_ventry el1_sync_invalid // Synchronous EL1t - kernel_ventry el1_irq_invalid // IRQ EL1t - kernel_ventry el1_fiq_invalid // FIQ EL1t - kernel_ventry el1_error_invalid // Error EL1t + kernel_ventry 1, sync_invalid // Synchronous EL1t + kernel_ventry 1, irq_invalid // IRQ EL1t + kernel_ventry 1, fiq_invalid // FIQ EL1t + kernel_ventry 1, error_invalid // Error EL1t - kernel_ventry el1_sync // Synchronous EL1h - kernel_ventry el1_irq // IRQ EL1h - kernel_ventry el1_fiq_invalid // FIQ EL1h - kernel_ventry el1_error_invalid // Error EL1h + kernel_ventry 1, sync // Synchronous EL1h + kernel_ventry 1, irq // IRQ EL1h + kernel_ventry 1, fiq_invalid // FIQ EL1h + kernel_ventry 1, error_invalid // Error EL1h - kernel_ventry el0_sync // Synchronous 64-bit EL0 - kernel_ventry el0_irq // IRQ 64-bit EL0 - kernel_ventry el0_fiq_invalid // FIQ 64-bit EL0 - kernel_ventry el0_error_invalid // Error 64-bit EL0 + kernel_ventry 0, sync // Synchronous 64-bit EL0 + kernel_ventry 0, irq // IRQ 64-bit EL0 + kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0 + kernel_ventry 0, error_invalid // Error 64-bit EL0 #ifdef CONFIG_COMPAT - kernel_ventry el0_sync_compat // Synchronous 32-bit EL0 - kernel_ventry el0_irq_compat // IRQ 32-bit EL0 - kernel_ventry el0_fiq_invalid_compat // FIQ 32-bit EL0 - kernel_ventry el0_error_invalid_compat // Error 32-bit EL0 + kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0 + kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0 + kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0 + kernel_ventry 0, error_invalid_compat, 32 // Error 32-bit EL0 #else - kernel_ventry el0_sync_invalid // Synchronous 32-bit EL0 - kernel_ventry el0_irq_invalid // IRQ 32-bit EL0 - kernel_ventry el0_fiq_invalid // FIQ 32-bit EL0 - kernel_ventry el0_error_invalid // Error 32-bit EL0 + kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0 + kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0 + kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0 + kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0 #endif END(vectors) -- GitLab From 1831462dc1b8682b8b4ef2ef2b90a756626d1bd7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:24:29 +0000 Subject: [PATCH 2291/5498] FROMLIST: arm64: entry: Hook up entry trampoline to exception vectors Hook up the entry trampoline to our exception vectors so that all exceptions from and returns to EL0 go via the trampoline, which swizzles the vector base register accordingly. Transitioning to and from the kernel clobbers x30, so we use tpidrro_el0 and far_el1 as scratch registers for native tasks. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 4bf3286d29f3a88425d8d8cd53428cbb8f865f04) [ghackmann@google.com: adjust context, replacing alternative_if_not ARM64_WORKAROUND_845719 block with upstream version] Signed-off-by: Greg Hackmann Change-Id: I0f50f6ce0e28f2ca8d98a93c96809adf71a2f5e4 Git-Commit: 5ed26716ffb768970fe8ffbd68b050ce10a62431 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/kernel/entry.S | 49 ++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 2ed0f6f8014c..18f660679335 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -72,10 +72,26 @@ .macro kernel_ventry, el, label, regsize = 64 .align 7 +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + .if \el == 0 + .if \regsize == 64 + mrs x30, tpidrro_el0 + msr tpidrro_el0, xzr + .else + mov x30, xzr + .endif + .endif +#endif + sub sp, sp, #S_FRAME_SIZE b el\()\el\()_\label .endm + .macro tramp_alias, dst, sym + mov_q \dst, TRAMP_VALIAS + add \dst, \dst, #(\sym - .entry.tramp.text) + .endm + .macro kernel_entry, el, regsize = 64 .if \regsize == 32 mov w0, w0 // zero upper 32 bits of x0 @@ -205,24 +221,20 @@ alternative_else_nop_endif .if \el == 0 ldr x23, [sp, #S_SP] // load return stack pointer msr sp_el0, x23 + tst x22, #PSR_MODE32_BIT // native task? + b.eq 3f + #ifdef CONFIG_ARM64_ERRATUM_845719 -alternative_if_not ARM64_WORKAROUND_845719 - nop - nop -#ifdef CONFIG_PID_IN_CONTEXTIDR - nop -#endif -alternative_else - tbz x22, #4, 1f +alternative_if ARM64_WORKAROUND_845719 #ifdef CONFIG_PID_IN_CONTEXTIDR mrs x29, contextidr_el1 msr contextidr_el1, x29 #else msr contextidr_el1, xzr #endif -1: -alternative_endif +alternative_else_nop_endif #endif +3: .endif msr elr_el1, x21 // set up the return data @@ -244,7 +256,22 @@ alternative_endif ldp x28, x29, [sp, #16 * 14] ldr lr, [sp, #S_LR] add sp, sp, #S_FRAME_SIZE // restore sp - eret // return to kernel + +#ifndef CONFIG_UNMAP_KERNEL_AT_EL0 + eret +#else + .if \el == 0 + bne 4f + msr far_el1, x30 + tramp_alias x30, tramp_exit_native + br x30 +4: + tramp_alias x30, tramp_exit_compat + br x30 + .else + eret + .endif +#endif .endm /* -- GitLab From a91c3dbb41afa1e080b545e3c0c3f2653f7f2c72 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:29:19 +0000 Subject: [PATCH 2292/5498] FROMLIST: arm64: erratum: Work around Falkor erratum #E1003 in trampoline code We rely on an atomic swizzling of TTBR1 when transitioning from the entry trampoline to the kernel proper on an exception. We can't rely on this atomicity in the face of Falkor erratum #E1003, so on affected cores we can issue a TLB invalidation to invalidate the walk cache prior to jumping into the kernel. There is still the possibility of a TLB conflict here due to conflicting walk cache entries prior to the invalidation, but this doesn't appear to be the case on these CPUs in practice. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit d1777e686ad10ba7c594304429c6045fb79255a1) [ghackmann@google.com: replace runtime alternative_if with a compile-time check for Code Aurora's out-of-tree CONFIG_ARCH_MSM8996. Kryo needs this workaround too, and 4.4 doesn't have any of the upstream Falkor errata infrastructure needed to detect this at boot time.] Signed-off-by: Greg Hackmann Change-Id: I22423d93396d012e4326f2b56e4da39c27aaa665 Git-Commit: 1766a613ec0c59cb5f463364a20b74596c636768 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/kernel/entry.S | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 18f660679335..44d9194332e7 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -887,6 +887,16 @@ __ni_sys_trace: sub \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) bic \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp +#ifdef CONFIG_ARCH_MSM8996 + /* ASID already in \tmp[63:48] */ + movk \tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12) + movk \tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12) + /* 2MB boundary containing the vectors, so we nobble the walk cache */ + movk \tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12) + isb + tlbi vae1, \tmp + dsb nsh +#endif /* CONFIG_ARCH_MSM8996 */ .endm .macro tramp_unmap_kernel, tmp -- GitLab From 95a231c6565b2a902611f5b9c067adfd6fcf8b1c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:33:28 +0000 Subject: [PATCH 2293/5498] FROMLIST: arm64: tls: Avoid unconditional zeroing of tpidrro_el0 for native tasks When unmapping the kernel at EL0, we use tpidrro_el0 as a scratch register during exception entry from native tasks and subsequently zero it in the kernel_ventry macro. We can therefore avoid zeroing tpidrro_el0 in the context-switch path for native tasks using the entry trampoline. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 18011eac28c7cb31c87b86b7d0e5b01894405c7f) [ghackmann@google.com: - adjust context - replace task_user_tls() accessor with equivalent field from 3.18] Signed-off-by: Greg Hackmann Change-Id: Ib1ffb34e6f3a722ca111a8fb1811cbc79ffd22fe Git-Commit: f47c91dcfa2accf4fd04a39398acbdc1237ca87e Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/kernel/process.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 9c478de7032b..f11d7e4b375b 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -357,25 +357,18 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, static void tls_thread_switch(struct task_struct *next) { - unsigned long tpidr, tpidrro; - if (!is_compat_task()) { + unsigned long tpidr; asm("mrs %0, tpidr_el0" : "=r" (tpidr)); current->thread.tp_value = tpidr; } - if (is_compat_thread(task_thread_info(next))) { - tpidr = 0; - tpidrro = next->thread.tp_value; - } else { - tpidr = next->thread.tp_value; - tpidrro = 0; - } + if (is_compat_thread(task_thread_info(next))) + write_sysreg(next->thread.tp_value, tpidrro_el0); + else if (!arm64_kernel_unmapped_at_el0()) + write_sysreg(0, tpidrro_el0); - asm( - " msr tpidr_el0, %0\n" - " msr tpidrro_el0, %1" - : : "r" (tpidr), "r" (tpidrro)); + write_sysreg(next->thread.tp_value, tpidr_el0); } /* Restore the UAO state depending on next's addr_limit */ -- GitLab From 2b95a6fd65158382c7066b1da2e1e59607897180 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:38:19 +0000 Subject: [PATCH 2294/5498] FROMLIST: arm64: entry: Add fake CPU feature for unmapping the kernel at EL0 Allow explicit disabling of the entry trampoline on the kernel command line (kpti=off) by adding a fake CPU feature (ARM64_UNMAP_KERNEL_AT_EL0) that can be used to toggle the alternative sequences in our entry code and avoid use of the trampoline altogether if desired. This also allows us to make use of a static key in arm64_kernel_unmapped_at_el0(). Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit ea1e3de85e94d711f63437c04624aa0e8de5c8b3) [ghackmann@google.com: - adjust context - apply cpucaps.h changes to cpufeature.h - replace cpus_have_const_cap() with cpus_have_cap(), and include the header that declares it - tweak unmap_kernel_at_el0() declaration to match 3.18 APIs] Signed-off-by: Greg Hackmann Change-Id: Iae8a237330d5d66188eb76199fd139c0fb7ba97f Git-Commit: 2b4c5c1fdab1aa2ef29514eb71920c8fe77aa62b Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/cpufeature.h | 3 ++- arch/arm64/include/asm/mmu.h | 5 +++- arch/arm64/kernel/cpufeature.c | 39 +++++++++++++++++++++++++++++ arch/arm64/kernel/entry.S | 9 ++++--- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index a0789bfc4ac6..ba6983e901d5 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -29,8 +29,9 @@ #define ARM64_HAS_PAN 4 #define ARM64_HAS_UAO 5 #define ARM64_ALT_PAN_NOT_UAO 6 +#define ARM64_UNMAP_KERNEL_AT_EL0 23 -#define ARM64_NCAPS 7 +#define ARM64_NCAPS 24 #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index d94389667066..bdd3fdc9b243 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -20,6 +20,8 @@ #ifndef __ASSEMBLY__ +#include + typedef struct { atomic64_t id; void *vdso; @@ -34,7 +36,8 @@ typedef struct { static inline bool arm64_kernel_unmapped_at_el0(void) { - return IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0); + return IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0) && + cpus_have_cap(ARM64_UNMAP_KERNEL_AT_EL0); } extern void paging_init(void); diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 58347534d765..ec7e68c2c73f 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -617,6 +617,39 @@ has_cpuid_feature(const struct arm64_cpu_capabilities *entry) return feature_matches(val, entry); } +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */ + +static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry) +{ + /* Forced on command line? */ + if (__kpti_forced) { + pr_info_once("kernel page table isolation forced %s by command line option\n", + __kpti_forced > 0 ? "ON" : "OFF"); + return __kpti_forced > 0; + } + + /* Useful for KASLR robustness */ + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) + return true; + + return false; +} + +static int __init parse_kpti(char *str) +{ + bool enabled; + int ret = strtobool(str, &enabled); + + if (ret) + return ret; + + __kpti_forced = enabled ? 1 : -1; + return 0; +} +__setup("kpti=", parse_kpti); +#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ + static const struct arm64_cpu_capabilities arm64_features[] = { { .desc = "GIC system register CPU interface", @@ -654,6 +687,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = cpufeature_pan_not_uao, }, #endif /* CONFIG_ARM64_PAN */ +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + { + .capability = ARM64_UNMAP_KERNEL_AT_EL0, + .matches = unmap_kernel_at_el0, + }, +#endif {}, }; diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 44d9194332e7..79ae751a2add 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -73,6 +73,7 @@ .macro kernel_ventry, el, label, regsize = 64 .align 7 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +alternative_if ARM64_UNMAP_KERNEL_AT_EL0 .if \el == 0 .if \regsize == 64 mrs x30, tpidrro_el0 @@ -81,6 +82,7 @@ mov x30, xzr .endif .endif +alternative_else_nop_endif #endif sub sp, sp, #S_FRAME_SIZE @@ -257,10 +259,9 @@ alternative_else_nop_endif ldr lr, [sp, #S_LR] add sp, sp, #S_FRAME_SIZE // restore sp -#ifndef CONFIG_UNMAP_KERNEL_AT_EL0 - eret -#else .if \el == 0 +alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0 +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 bne 4f msr far_el1, x30 tramp_alias x30, tramp_exit_native @@ -268,10 +269,10 @@ alternative_else_nop_endif 4: tramp_alias x30, tramp_exit_compat br x30 +#endif .else eret .endif -#endif .endm /* -- GitLab From 412edb14a132f5c4aa2130e139a45aefec6f5759 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 14:41:01 +0000 Subject: [PATCH 2295/5498] FROMLIST: arm64: Kconfig: Add CONFIG_UNMAP_KERNEL_AT_EL0 Add a Kconfig entry to control use of the entry trampoline, which allows us to unmap the kernel whilst running in userspace and improve the robustness of KASLR. Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 084eb77cd3a81134d02500977dc0ecc9277dc97d) Signed-off-by: Greg Hackmann Change-Id: Iff788c61b8a993088ed41d6251363e767dfa4e71 Git-Commit: 3ec3abcbf78fa2ab4251c5fdbffc2bafff922204 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/Kconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 0444366b29d7..33f309a5bda5 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -801,6 +801,19 @@ config FORCE_MAX_ZONEORDER default "14" if (ARM64_64K_PAGES && TRANSPARENT_HUGEPAGE) default "11" +config UNMAP_KERNEL_AT_EL0 + bool "Unmap kernel when running in userspace (aka \"KAISER\")" + default y + help + Some attacks against KASLR make use of the timing difference between + a permission fault which could arise from a page table entry that is + present in the TLB, and a translation fault which always requires a + page table walk. This option defends against these attacks by unmapping + the kernel whilst running in userspace, therefore forcing translation + faults for all of kernel space. + + If unsure, say Y. + menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" depends on COMPAT -- GitLab From cba940a2fc414b53d0e654f468322c56646ba8b5 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 1 Dec 2017 17:33:48 +0000 Subject: [PATCH 2296/5498] FROMLIST: arm64: mm: Introduce TTBR_ASID_MASK for getting at the ASID in the TTBR There are now a handful of open-coded masks to extract the ASID from a TTBR value, so introduce a TTBR_ASID_MASK and use that instead. Suggested-by: Mark Rutland Reviewed-by: Mark Rutland Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit b519538dfefc2f8478a1bcb458459c861d431784) [ghackmann@google.com: adjust context, applying asm-uaccess.h changes to uaccess.h instead] Signed-off-by: Greg Hackmann Change-Id: Ia600fb2e89a40fd4e9e0b2efca9b09f166ad73ec Git-Commit: 7b87b5746ca28b470af1dc2f019c252e7619cd3a Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/mmu.h | 1 + arch/arm64/include/asm/uaccess.h | 7 ++++--- arch/arm64/kernel/entry.S | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index bdd3fdc9b243..2fbe8cc639c7 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -17,6 +17,7 @@ #define __ASM_MMU_H #define USER_ASID_FLAG (UL(1) << 48) +#define TTBR_ASID_MASK (UL(0xffff) << 48) #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index cfef719287d9..e7cb0e6cbdaa 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -20,6 +20,7 @@ #include #include +#include #include #ifndef __ASSEMBLY__ @@ -141,7 +142,7 @@ static inline void __uaccess_ttbr0_disable(void) write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); isb(); /* Set reserved ASID */ - ttbr &= ~(0xffffUL << 48); + ttbr &= ~TTBR_ASID_MASK; write_sysreg(ttbr, ttbr1_el1); isb(); } @@ -160,7 +161,7 @@ static inline void __uaccess_ttbr0_enable(void) /* Restore active ASID */ ttbr1 = read_sysreg(ttbr1_el1); - ttbr1 |= ttbr0 & (0xffffUL << 48); + ttbr1 |= ttbr0 & TTBR_ASID_MASK; write_sysreg(ttbr1, ttbr1_el1); isb(); @@ -454,7 +455,7 @@ extern __must_check long strnlen_user(const char __user *str, long n); msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE - bic \tmp1, \tmp1, #(0xffff << 48) + bic \tmp1, \tmp1, #TTBR_ASID_MASK msr ttbr1_el1, \tmp1 // set reserved ASID isb .endm diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 79ae751a2add..696d8a111819 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -149,7 +149,7 @@ alternative_else_nop_endif .if \el != 0 mrs x21, ttbr1_el1 - tst x21, #0xffff << 48 // Check for the reserved ASID + tst x21, #TTBR_ASID_MASK // Check for the reserved ASID orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR b.eq 1f // TTBR0 access already disabled and x23, x23, #~PSR_PAN_BIT // Clear the emulated PAN in the saved SPSR -- GitLab From c3b6e93079bcf34f5a89c4740ff4ab8e376ef63c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 6 Dec 2017 11:24:02 +0000 Subject: [PATCH 2297/5498] FROMLIST: arm64: kaslr: Put kernel vectors address in separate data page The literal pool entry for identifying the vectors base is the only piece of information in the trampoline page that identifies the true location of the kernel. This patch moves it into a page-aligned region of the .rodata section and maps this adjacent to the trampoline text via an additional fixmap entry, which protects against any accidental leakage of the trampoline contents. Suggested-by: Ard Biesheuvel Tested-by: Laura Abbott Tested-by: Shanker Donthineni Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 6c27c4082f4f70b9f41df4d0adf51128b40351df) [ghackmann@google.com: - adjust context - replace ARM64_WORKAROUND_QCOM_FALKOR_E1003 alternative with compile-time CONFIG_ARCH_MSM8996 check] Signed-off-by: Greg Hackmann Change-Id: Id96c1b0eaca8d2ff2e91320ac13d76b18d05fdd5 Git-Commit: 90523e83073be8fe4b6afd6de4f8467bc0c4dc86 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/fixmap.h | 1 + arch/arm64/kernel/entry.S | 16 ++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 4 ++++ arch/arm64/mm/mmu.c | 10 +++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h index cc9b9a6a238c..6c5bf65c6f4a 100644 --- a/arch/arm64/include/asm/fixmap.h +++ b/arch/arm64/include/asm/fixmap.h @@ -34,6 +34,7 @@ enum fixed_addresses { FIX_HOLE, FIX_EARLYCON_MEM_BASE, #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + FIX_ENTRY_TRAMP_DATA, FIX_ENTRY_TRAMP_TEXT, #define TRAMP_VALIAS (__fix_to_virt(FIX_ENTRY_TRAMP_TEXT)) #endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 696d8a111819..8306dc3065c9 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -926,7 +926,15 @@ __ni_sys_trace: msr tpidrro_el0, x30 // Restored in kernel_ventry .endif tramp_map_kernel x30 +#ifdef CONFIG_RANDOMIZE_BASE + adr x30, tramp_vectors + PAGE_SIZE +#ifndef CONFIG_ARCH_MSM8996 + isb +#endif + ldr x30, [x30] +#else ldr x30, =vectors +#endif prfm plil1strm, [x30, #(1b - tramp_vectors)] msr vbar_el1, x30 add x30, x30, #(1b - tramp_vectors) @@ -969,6 +977,14 @@ END(tramp_exit_compat) .ltorg .popsection // .entry.tramp.text +#ifdef CONFIG_RANDOMIZE_BASE + .pushsection ".rodata", "a" + .align PAGE_SHIFT + .globl __entry_tramp_data_start +__entry_tramp_data_start: + .quad vectors + .popsection // .rodata +#endif /* CONFIG_RANDOMIZE_BASE */ #endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */ /* diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 1ded30f007b4..de800b0cc545 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -197,6 +197,10 @@ SECTIONS ASSERT(((__hyp_idmap_text_start + PAGE_SIZE) > __hyp_idmap_text_end), "HYP init code too big") +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +ASSERT((__entry_tramp_text_end - __entry_tramp_text_start) == PAGE_SIZE, + "Entry trampoline text too big") +#endif /* * If padding is applied before .head.text, virt<->phys conversions will fail. */ diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index b5d7dbc41e49..17b6b4021b20 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -700,8 +700,16 @@ static int __init map_entry_trampoline(void) __create_mapping(NULL, tramp_pg_dir + pgd_index(TRAMP_VALIAS), pa_start, TRAMP_VALIAS, PAGE_SIZE, prot, pgd_pgtable_alloc, false); - /* ...as well as the kernel page table */ + /* Map both the text and data into the kernel page table */ __set_fixmap(FIX_ENTRY_TRAMP_TEXT, pa_start, prot); + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { + extern char __entry_tramp_data_start[]; + + __set_fixmap(FIX_ENTRY_TRAMP_DATA, + __pa_symbol(__entry_tramp_data_start), + PAGE_KERNEL_RO); + } + return 0; } core_initcall(map_entry_trampoline); -- GitLab From d294c771d9133ecc0d1e40b65427d6a5f1ff7729 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 16:15:59 +0000 Subject: [PATCH 2298/5498] arm64: use RET instruction for exiting the trampoline Speculation attacks against the entry trampoline can potentially resteer the speculative instruction stream through the indirect branch and into arbitrary gadgets within the kernel. This patch defends against these attacks by forcing a misprediction through the return stack: a dummy BL instruction loads an entry into the stack, so that the predicted program flow of the subsequent RET instruction is to a branch-to-self instruction which is finally resolved as a branch to the kernel vectors with speculation suppressed. Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman Change-Id: Ief67c5d2c48cbc8cf8506a79e396d0d36df2fd51 Git-Commit: 5baaf50ec1ec2b93291afd2ee9b84a82b3ad30c9 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/kernel/entry.S | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 8306dc3065c9..e292e5b01391 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -925,6 +925,9 @@ __ni_sys_trace: .if \regsize == 64 msr tpidrro_el0, x30 // Restored in kernel_ventry .endif + bl 2f + b . +2: tramp_map_kernel x30 #ifdef CONFIG_RANDOMIZE_BASE adr x30, tramp_vectors + PAGE_SIZE @@ -939,7 +942,7 @@ __ni_sys_trace: msr vbar_el1, x30 add x30, x30, #(1b - tramp_vectors) isb - br x30 + ret .endm .macro tramp_exit, regsize = 64 -- GitLab From e76d753e208808b9143aacebccbca9c56da7bb4d Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 14 Nov 2017 16:19:39 +0000 Subject: [PATCH 2299/5498] arm64: Kconfig: Reword UNMAP_KERNEL_AT_EL0 kconfig entry Although CONFIG_UNMAP_KERNEL_AT_EL0 does make KASLR more robust, it's actually more useful as a mitigation against speculation attacks that can leak arbitrary kernel data to userspace through speculation. Reword the Kconfig help message to reflect this, and make the option depend on EXPERT so that it is on by default for the majority of users. Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman Change-Id: I2056d452aa0015384a5b09a67b0a22e548b42e1e Git-Commit: 2897537bc331dba2c79238a18d62d11fd372a2a9 Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: trivial merge conflicts] Signed-off-by: Vinayak Menon --- arch/arm64/Kconfig | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 33f309a5bda5..0625f78596bb 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -802,15 +802,14 @@ config FORCE_MAX_ZONEORDER default "11" config UNMAP_KERNEL_AT_EL0 - bool "Unmap kernel when running in userspace (aka \"KAISER\")" + bool "Unmap kernel when running in userspace (aka \"KAISER\")" if EXPERT default y help - Some attacks against KASLR make use of the timing difference between - a permission fault which could arise from a page table entry that is - present in the TLB, and a translation fault which always requires a - page table walk. This option defends against these attacks by unmapping - the kernel whilst running in userspace, therefore forcing translation - faults for all of kernel space. + Speculation attacks against some high-performance processors can + be used to bypass MMU permission checks and leak kernel data to + userspace. This can be defended against by unmapping the kernel + when running in userspace, mapping it back in on exception entry + via a trampoline page in the vector table. If unsure, say Y. -- GitLab From 10c842afc23fbe1216f0e6d70892ed8aeb3c3718 Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Fri, 5 Jan 2018 11:03:42 +0530 Subject: [PATCH 2300/5498] clocksource: arch_timer: Disable user access to the physical counter Disable user access to physical counter. This reverts commit 63cb2598d5ba ("clocksource: arch_timer: Enable user access to the physical counter"). This could potentially break the userspace applications using physical counters; but all those usages should move to using virtual counter, to get the timing information. Change-Id: I653816a93515507a400ff23dbaa4442bf614a79b Signed-off-by: Neeraj Upadhyay --- drivers/clocksource/arm_arch_timer.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 891b07e4ac95..ef939675c9ca 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -334,13 +334,14 @@ static void arch_counter_set_user_access(void) { u32 cntkctl = arch_timer_get_cntkctl(); - /* Disable user access to the timers */ + /* Disable user access to the timers and the physical counter */ /* Also disable virtual event stream */ cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN - | ARCH_TIMER_VIRT_EVT_EN); + | ARCH_TIMER_VIRT_EVT_EN + | ARCH_TIMER_USR_PCT_ACCESS_EN); - /* Enable user access to the virtual and physical counters */ - cntkctl |= ARCH_TIMER_USR_PCT_ACCESS_EN | ARCH_TIMER_USR_VT_ACCESS_EN; + /* Enable user access to the virtual counter */ + cntkctl |= ARCH_TIMER_USR_VT_ACCESS_EN; if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS)) cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; -- GitLab From f2837c047ce5fc69dea45e2be7043bc865bfb1e7 Mon Sep 17 00:00:00 2001 From: Arun Kumar Neelakantam Date: Wed, 10 Jan 2018 12:00:39 +0530 Subject: [PATCH 2301/5498] ARM: dts: msm: Add no-cpu-affinity for BAM DMUX on MSM8909 BAM DMUX work queue is bound to CPU0 to support the network stack requirements to avoid the out of order packet issue. If CPU0 is busy by other high priority works then bounding workqueue to CPU0 is causing delay in BAM DMUX work queue. So, remove the CPU affinity to let the scheduler to decide the CPU for BAM DMUX work queue and enabling the RPS feature in network stack to support out of order packet issue. Change-Id: I4800a3843d04b6f2b9bdea60e4b4e095a70722eb Signed-off-by: Arun Kumar Neelakantam Signed-off-by: Nirmal Abraham --- arch/arm/boot/dts/qcom/msm8909.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 0122e682bb5a..dea732b9bee4 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -779,6 +779,7 @@ qcom,rx-ring-size = <32>; qcom,max-rx-mtu = <4096>; qcom,fast-shutdown; + qcom,no-cpu-affinity; }; qcom,smdtty { -- GitLab From ae18a5e356cfae52a6178eb168230bdd64804a2a Mon Sep 17 00:00:00 2001 From: Meera Gande Date: Tue, 9 Jan 2018 18:24:51 +0530 Subject: [PATCH 2302/5498] mm-camera2:isp2: Add support for 12bit-plain16 raw format VFE hardware is capable of handling 12 bit plain16 raw image format. As this support is not there in software,failure is returned whenever the raw format is 12bit plain16. Change-Id: I7989b51294efc10d56f483cf6bb67075179620ea Signed-off-by: Meera Gande --- .../media/platform/msm/camera_v2/isp/msm_isp40.c | 6 +++++- .../platform/msm/camera_v2/isp/msm_isp_axi_util.c | 10 +++++++++- .../platform/msm/camera_v2/isp/msm_isp_util.c | 14 +++++++++++++- include/uapi/media/msmb_isp.h | 4 ++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index c67e8f0b0ff3..7fa045c0c0fc 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1218,6 +1218,10 @@ static void msm_vfe40_cfg_fetch_engine(struct vfe_device *vfe_dev, case V4L2_PIX_FMT_P16GBRG10: case V4L2_PIX_FMT_P16GRBG10: case V4L2_PIX_FMT_P16RGGB10: + case V4L2_PIX_FMT_P16BGGR12: + case V4L2_PIX_FMT_P16GBRG12: + case V4L2_PIX_FMT_P16GRBG12: + case V4L2_PIX_FMT_P16RGGB12: main_unpack_pattern = 0xB210; break; default: diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index f4292ad406ad..9772540f7e59 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -183,6 +183,10 @@ int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data, case V4L2_PIX_FMT_P16GBRG10: case V4L2_PIX_FMT_P16GRBG10: case V4L2_PIX_FMT_P16RGGB10: + case V4L2_PIX_FMT_P16BGGR12: + case V4L2_PIX_FMT_P16GBRG12: + case V4L2_PIX_FMT_P16GRBG12: + case V4L2_PIX_FMT_P16RGGB12: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_META: case V4L2_PIX_FMT_META10: @@ -326,6 +330,10 @@ static uint32_t msm_isp_axi_get_plane_size( case V4L2_PIX_FMT_P16GBRG10: case V4L2_PIX_FMT_P16GRBG10: case V4L2_PIX_FMT_P16RGGB10: + case V4L2_PIX_FMT_P16BGGR12: + case V4L2_PIX_FMT_P16GBRG12: + case V4L2_PIX_FMT_P16GRBG12: + case V4L2_PIX_FMT_P16RGGB12: size = plane_cfg[plane_idx].output_height * plane_cfg[plane_idx].output_width; break; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index e7f7f4db9f5f..a78dec333738 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1601,6 +1601,10 @@ int msm_isp_cal_word_per_line(uint32_t output_format, case V4L2_PIX_FMT_P16GBRG10: case V4L2_PIX_FMT_P16GRBG10: case V4L2_PIX_FMT_P16RGGB10: + case V4L2_PIX_FMT_P16BGGR12: + case V4L2_PIX_FMT_P16GBRG12: + case V4L2_PIX_FMT_P16GRBG12: + case V4L2_PIX_FMT_P16RGGB12: val = CAL_WORD(pixel_per_line, 1, 4); break; case V4L2_PIX_FMT_NV24: @@ -1667,6 +1671,10 @@ enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format) case V4L2_PIX_FMT_P16GBRG10: case V4L2_PIX_FMT_P16GRBG10: case V4L2_PIX_FMT_P16RGGB10: + case V4L2_PIX_FMT_P16BGGR12: + case V4L2_PIX_FMT_P16GBRG12: + case V4L2_PIX_FMT_P16GRBG12: + case V4L2_PIX_FMT_P16RGGB12: return PLAIN16; default: msm_isp_print_fourcc_error(__func__, output_format); @@ -1751,6 +1759,10 @@ int msm_isp_get_bit_per_pixel(uint32_t output_format) case V4L2_PIX_FMT_QGRBG12: case V4L2_PIX_FMT_QRGGB12: case V4L2_PIX_FMT_Y12: + case V4L2_PIX_FMT_P16BGGR12: + case V4L2_PIX_FMT_P16GBRG12: + case V4L2_PIX_FMT_P16GRBG12: + case V4L2_PIX_FMT_P16RGGB12: return 12; case V4L2_PIX_FMT_SBGGR14: case V4L2_PIX_FMT_SGBRG14: diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h index 2387aedcc05f..429e35339a0a 100644 --- a/include/uapi/media/msmb_isp.h +++ b/include/uapi/media/msmb_isp.h @@ -917,6 +917,10 @@ struct msm_isp_ahb_clk_cfg { #define V4L2_PIX_FMT_P16GBRG10 v4l2_fourcc('P', 'G', 'B', '0') #define V4L2_PIX_FMT_P16GRBG10 v4l2_fourcc('P', 'G', 'R', '0') #define V4L2_PIX_FMT_P16RGGB10 v4l2_fourcc('P', 'R', 'G', '0') +#define V4L2_PIX_FMT_P16BGGR12 v4l2_fourcc('P', 'B', 'G', '2') +#define V4L2_PIX_FMT_P16GBRG12 v4l2_fourcc('P', 'G', 'B', '2') +#define V4L2_PIX_FMT_P16GRBG12 v4l2_fourcc('P', 'G', 'R', '2') +#define V4L2_PIX_FMT_P16RGGB12 v4l2_fourcc('P', 'R', 'G', '2') #define V4L2_PIX_FMT_NV14 v4l2_fourcc('N', 'V', '1', '4') #define V4L2_PIX_FMT_NV41 v4l2_fourcc('N', 'V', '4', '1') #define V4L2_PIX_FMT_META v4l2_fourcc('Q', 'M', 'E', 'T') -- GitLab From 7bf407cadd10b7811c7cf76997b1afaedaaf4393 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 4 Jan 2018 16:17:49 -0800 Subject: [PATCH 2303/5498] kernel/acct.c: fix the acct->needcheck check in check_free_space() commit 4d9570158b6260f449e317a5f9ed030c2504a615 upstream. As Tsukada explains, the time_is_before_jiffies(acct->needcheck) check is very wrong, we need time_is_after_jiffies() to make sys_acct() work. Ignoring the overflows, the code should "goto out" if needcheck > jiffies, while currently it checks "needcheck < jiffies" and thus in the likely case check_free_space() does nothing until jiffies overflow. In particular this means that sys_acct() is simply broken, acct_on() sets acct->needcheck = jiffies and expects that check_free_space() should set acct->active = 1 after the free-space check, but this won't happen if jiffies increments in between. This was broken by commit 32dc73086015 ("get rid of timer in kern/acct.c") in 2011, then another (correct) commit 795a2f22a8ea ("acct() should honour the limits from the very beginning") made the problem more visible. Link: http://lkml.kernel.org/r/20171213133940.GA6554@redhat.com Fixes: 32dc73086015 ("get rid of timer in kern/acct.c") Reported-by: TSUKADA Koutaro Suggested-by: TSUKADA Koutaro Signed-off-by: Oleg Nesterov Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/acct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/acct.c b/kernel/acct.c index 33738ef972f3..4a71a814bd35 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -96,7 +96,7 @@ static int check_free_space(struct bsd_acct_struct *acct) { struct kstatfs sbuf; - if (time_is_before_jiffies(acct->needcheck)) + if (time_is_after_jiffies(acct->needcheck)) goto out; /* May block */ -- GitLab From 381d85f6ab95230909b23c8589e41758b7ad6fb1 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 19 Dec 2017 19:09:07 +0100 Subject: [PATCH 2304/5498] crypto: n2 - cure use after free commit 203f45003a3d03eea8fa28d74cfc74c354416fdb upstream. queue_cache_init is first called for the Control Word Queue (n2_crypto_probe). At that time, queue_cache[0] is NULL and a new kmem_cache will be allocated. If the subsequent n2_register_algs call fails, the kmem_cache will be released in queue_cache_destroy, but queue_cache_init[0] is not set back to NULL. So when the Module Arithmetic Unit gets probed next (n2_mau_probe), queue_cache_init will not allocate a kmem_cache again, but leave it as its bogus value, causing a BUG() to trigger when queue_cache[0] is eventually passed to kmem_cache_zalloc: n2_crypto: Found N2CP at /virtual-devices@100/n2cp@7 n2_crypto: Registered NCS HVAPI version 2.0 called queue_cache_init n2_crypto: md5 alg registration failed n2cp f028687c: /virtual-devices@100/n2cp@7: Unable to register algorithms. called queue_cache_destroy n2cp: probe of f028687c failed with error -22 n2_crypto: Found NCP at /virtual-devices@100/ncp@6 n2_crypto: Registered NCS HVAPI version 2.0 called queue_cache_init kernel BUG at mm/slab.c:2993! Call Trace: [0000000000604488] kmem_cache_alloc+0x1a8/0x1e0 (inlined) kmem_cache_zalloc (inlined) new_queue (inlined) spu_queue_setup (inlined) handle_exec_unit [0000000010c61eb4] spu_mdesc_scan+0x1f4/0x460 [n2_crypto] [0000000010c62b80] n2_mau_probe+0x100/0x220 [n2_crypto] [000000000084b174] platform_drv_probe+0x34/0xc0 Signed-off-by: Jan Engelhardt Acked-by: David S. Miller Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/n2_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c index f8e3207fecb1..d62a148075bf 100644 --- a/drivers/crypto/n2_core.c +++ b/drivers/crypto/n2_core.c @@ -1641,6 +1641,7 @@ static int queue_cache_init(void) CWQ_ENTRY_SIZE, 0, NULL); if (!queue_cache[HV_NCS_QTYPE_CWQ - 1]) { kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_MAU - 1]); + queue_cache[HV_NCS_QTYPE_MAU - 1] = NULL; return -ENOMEM; } return 0; @@ -1650,6 +1651,8 @@ static void queue_cache_destroy(void) { kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_MAU - 1]); kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_CWQ - 1]); + queue_cache[HV_NCS_QTYPE_MAU - 1] = NULL; + queue_cache[HV_NCS_QTYPE_CWQ - 1] = NULL; } static int spu_queue_register(struct spu_queue *p, unsigned long q_type) -- GitLab From 8881d834c3337c6c4b257449b1dfb4a9a4c1b2bb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 2 Jan 2018 10:02:19 +0000 Subject: [PATCH 2305/5498] fscache: Fix the default for fscache_maybe_release_page() commit 98801506552593c9b8ac11021b0cdad12cab4f6b upstream. Fix the default for fscache_maybe_release_page() for when the cookie isn't valid or the page isn't cached. It mustn't return false as that indicates the page cannot yet be freed. The problem with the default is that if, say, there's no cache, but a network filesystem's pages are using up almost all the available memory, a system can OOM because the filesystem ->releasepage() op will not allow them to be released as fscache_maybe_release_page() incorrectly prevents it. This can be tested by writing a sequence of 512MiB files to an AFS mount. It does not affect NFS or CIFS because both of those wrap the call in a check of PG_fscache and it shouldn't bother Ceph as that only has PG_private set whilst writeback is in progress. This might be an issue for 9P, however. Note that the pages aren't entirely stuck. Removing a file or unmounting will clear things because that uses ->invalidatepage() instead. Fixes: 201a15428bd5 ("FS-Cache: Handle pages pending storage that get evicted under OOM conditions") Reported-by: Marc Dionne Signed-off-by: David Howells Reviewed-by: Jeff Layton Acked-by: Al Viro Tested-by: Marc Dionne Signed-off-by: Greg Kroah-Hartman --- include/linux/fscache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 115bb81912cc..94a8aae8f9e2 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -764,7 +764,7 @@ bool fscache_maybe_release_page(struct fscache_cookie *cookie, { if (fscache_cookie_valid(cookie) && PageFsCache(page)) return __fscache_maybe_release_page(cookie, page, gfp); - return false; + return true; } /** -- GitLab From 8deeda774d94439a5d33652fda2c2057cfd7322b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 17 Nov 2017 15:30:01 -0800 Subject: [PATCH 2306/5498] kernel/signal.c: protect the traced SIGNAL_UNKILLABLE tasks from SIGKILL commit 628c1bcba204052d19b686b5bac149a644cdb72e upstream. The comment in sig_ignored() says "Tracers may want to know about even ignored signals" but SIGKILL can not be reported to debugger and it is just wrong to return 0 in this case: SIGKILL should only kill the SIGNAL_UNKILLABLE task if it comes from the parent ns. Change sig_ignored() to ignore ->ptrace if sig == SIGKILL and rely on sig_task_ignored(). SISGTOP coming from within the namespace is not really right too but at least debugger can intercept it, and we can't drop it here because this will break "gdb -p 1": ptrace_attach() won't work. Perhaps we will add another ->ptrace check later, we will see. Link: http://lkml.kernel.org/r/20171103184206.GB21036@redhat.com Signed-off-by: Oleg Nesterov Tested-by: Kyle Huey Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/signal.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 2e1c5d375a0f..b956332ded42 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -88,13 +88,15 @@ static int sig_ignored(struct task_struct *t, int sig, bool force) if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig)) return 0; - if (!sig_task_ignored(t, sig, force)) - return 0; - /* - * Tracers may want to know about even ignored signals. + * Tracers may want to know about even ignored signal unless it + * is SIGKILL which can't be reported anyway but can be ignored + * by SIGNAL_UNKILLABLE task. */ - return !t->ptrace; + if (t->ptrace && sig != SIGKILL) + return 0; + + return sig_task_ignored(t, sig, force); } /* -- GitLab From 64c64936d42b910a4f3f74f00f420b6fa06792c9 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 17 Nov 2017 15:30:04 -0800 Subject: [PATCH 2307/5498] kernel/signal.c: protect the SIGNAL_UNKILLABLE tasks from !sig_kernel_only() signals commit ac25385089f673560867eb5179228a44ade0cfc1 upstream. Change sig_task_ignored() to drop the SIG_DFL && !sig_kernel_only() signals even if force == T. This simplifies the next change and this matches the same check in get_signal() which will drop these signals anyway. Link: http://lkml.kernel.org/r/20171103184227.GC21036@redhat.com Signed-off-by: Oleg Nesterov Tested-by: Kyle Huey Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/signal.c b/kernel/signal.c index b956332ded42..63602c815a58 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -72,7 +72,7 @@ static int sig_task_ignored(struct task_struct *t, int sig, bool force) handler = sig_handler(t, sig); if (unlikely(t->signal->flags & SIGNAL_UNKILLABLE) && - handler == SIG_DFL && !force) + handler == SIG_DFL && !(force && sig_kernel_only(sig))) return 1; return sig_handler_ignored(handler, sig); -- GitLab From 946519d82067612ad95251522ab57cda4147ac22 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 17 Nov 2017 15:30:08 -0800 Subject: [PATCH 2308/5498] kernel/signal.c: remove the no longer needed SIGNAL_UNKILLABLE check in complete_signal() commit 426915796ccaf9c2bd9bb06dc5702225957bc2e5 upstream. complete_signal() checks SIGNAL_UNKILLABLE before it starts to destroy the thread group, today this is wrong in many ways. If nothing else, fatal_signal_pending() should always imply that the whole thread group (except ->group_exit_task if it is not NULL) is killed, this check breaks the rule. After the previous changes we can rely on sig_task_ignored(); sig_fatal(sig) && SIGNAL_UNKILLABLE can only be true if we actually want to kill this task and sig == SIGKILL OR it is traced and debugger can intercept the signal. This should hopefully fix the problem reported by Dmitry. This test-case static int init(void *arg) { for (;;) pause(); } int main(void) { char stack[16 * 1024]; for (;;) { int pid = clone(init, stack + sizeof(stack)/2, CLONE_NEWPID | SIGCHLD, NULL); assert(pid > 0); assert(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); assert(waitpid(-1, NULL, WSTOPPED) == pid); assert(ptrace(PTRACE_DETACH, pid, 0, SIGSTOP) == 0); assert(syscall(__NR_tkill, pid, SIGKILL) == 0); assert(pid == wait(NULL)); } } triggers the WARN_ON_ONCE(!(task->jobctl & JOBCTL_STOP_PENDING)) in task_participate_group_stop(). do_signal_stop()->signal_group_exit() checks SIGNAL_GROUP_EXIT and return false, but task_set_jobctl_pending() checks fatal_signal_pending() and does not set JOBCTL_STOP_PENDING. And his should fix the minor security problem reported by Kyle, SECCOMP_RET_TRACE can miss fatal_signal_pending() the same way if the task is the root of a pid namespace. Link: http://lkml.kernel.org/r/20171103184246.GD21036@redhat.com Signed-off-by: Oleg Nesterov Reported-by: Dmitry Vyukov Reported-by: Kyle Huey Reviewed-by: Kees Cook Tested-by: Kyle Huey Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 63602c815a58..b7df30e8066c 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -970,9 +970,9 @@ static void complete_signal(int sig, struct task_struct *p, int group) * then start taking the whole group down immediately. */ if (sig_fatal(p, sig) && - !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) && + !(signal->flags & SIGNAL_GROUP_EXIT) && !sigismember(&t->real_blocked, sig) && - (sig == SIGKILL || !t->ptrace)) { + (sig == SIGKILL || !p->ptrace)) { /* * This signal will be fatal to the whole group. */ -- GitLab From 2431a63ebd0fecb0c9281a8ec78be5898d5145ea Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Sat, 25 Nov 2017 16:48:41 -0800 Subject: [PATCH 2309/5498] Input: elantech - add new icbody type 15 commit 10d900303f1c3a821eb0bef4e7b7ece16768fba4 upstream. The touchpad of Lenovo Thinkpad L480 reports it's version as 15. Signed-off-by: Aaron Ma Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/elantech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 47a1a20f1b16..b46103878b6d 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1558,7 +1558,7 @@ static int elantech_set_properties(struct elantech_data *etd) case 5: etd->hw_version = 3; break; - case 6 ... 14: + case 6 ... 15: etd->hw_version = 4; break; default: -- GitLab From ce9c95f1f30a0067bec5fdcda42bbba29a8d6409 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Wed, 13 Dec 2017 19:52:23 +0100 Subject: [PATCH 2310/5498] can: gs_usb: fix return value of the "set_bittiming" callback commit d5b42e6607661b198d8b26a0c30969605b1bf5c7 upstream. The "set_bittiming" callback treats a positive return value as error! For that reason "can_changelink()" will quit silently after setting the bittiming values without processing ctrlmode, restart-ms, etc. Signed-off-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/gs_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 94adbcf74e7c..4c13320f7a1e 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -430,7 +430,7 @@ static int gs_usb_set_bittiming(struct net_device *netdev) dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)", rc); - return rc; + return (rc > 0) ? 0 : rc; } static void gs_usb_xmit_callback(struct urb *urb) -- GitLab From a314ae1825b7b202928db9ad7eaece7bad8c0010 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 3 Jan 2018 13:39:15 -0800 Subject: [PATCH 2311/5498] IB/srpt: Disable RDMA access by the initiator commit bec40c26041de61162f7be9d2ce548c756ce0f65 upstream. With the SRP protocol all RDMA operations are initiated by the target. Since no RDMA operations are initiated by the initiator, do not grant the initiator permission to submit RDMA reads or writes to the target. Signed-off-by: Bart Van Assche Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/srpt/ib_srpt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 9d72100a0394..587311887d94 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -959,8 +959,7 @@ static int srpt_init_ch_qp(struct srpt_rdma_ch *ch, struct ib_qp *qp) return -ENOMEM; attr->qp_state = IB_QPS_INIT; - attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_READ | - IB_ACCESS_REMOTE_WRITE; + attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE; attr->port_num = ch->sport->port; attr->pkey_index = 0; -- GitLab From 0313868ccbc3fbc942d1b4be90dc4dd6aa8a8b05 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 11 Dec 2017 22:51:35 +0000 Subject: [PATCH 2312/5498] MIPS: Factor out NT_PRFPREG regset access helpers commit a03fe72572c12e98f4173f8a535f32468e48b6ec upstream. In preparation to fix a commit 72b22bbad1e7 ("MIPS: Don't assume 64-bit FP registers for FP regset") FCSR access regression factor out NT_PRFPREG regset access helpers for the non-MSA and the MSA variants respectively, to avoid having to deal with excessive indentation in the actual fix. No functional change, however use `target->thread.fpu.fpr[0]' rather than `target->thread.fpu.fpr[i]' for FGR holding type size determination as there's no `i' variable to refer to anymore, and for the factored out `i' variable declaration use `unsigned int' rather than `unsigned' as its type, following the common style. Signed-off-by: Maciej W. Rozycki Fixes: 72b22bbad1e7 ("MIPS: Don't assume 64-bit FP registers for FP regset") Cc: James Hogan Cc: Paul Burton Cc: Alex Smith Cc: Dave Martin Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/17925/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 106 +++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index d23073284ad9..0522e717b738 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -400,25 +400,36 @@ static int gpr64_set(struct task_struct *target, #endif /* CONFIG_64BIT */ -static int fpr_get(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) +/* + * Copy the floating-point context to the supplied NT_PRFPREG buffer, + * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots + * correspond 1:1 to buffer slots. + */ +static int fpr_get_fpa(struct task_struct *target, + unsigned int *pos, unsigned int *count, + void **kbuf, void __user **ubuf) { - unsigned i; - int err; - u64 fpr_val; - - /* XXX fcr31 */ + return user_regset_copyout(pos, count, kbuf, ubuf, + &target->thread.fpu, + 0, sizeof(elf_fpregset_t)); +} - if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); +/* + * Copy the floating-point context to the supplied NT_PRFPREG buffer, + * CONFIG_CPU_HAS_MSA variant. Only lower 64 bits of FP context's + * general register slots are copied to buffer slots. + */ +static int fpr_get_msa(struct task_struct *target, + unsigned int *pos, unsigned int *count, + void **kbuf, void __user **ubuf) +{ + unsigned int i; + u64 fpr_val; + int err; for (i = 0; i < NUM_FPU_REGS; i++) { fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); - err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + err = user_regset_copyout(pos, count, kbuf, ubuf, &fpr_val, i * sizeof(elf_fpreg_t), (i + 1) * sizeof(elf_fpreg_t)); if (err) @@ -428,25 +439,54 @@ static int fpr_get(struct task_struct *target, return 0; } -static int fpr_set(struct task_struct *target, +/* Copy the floating-point context to the supplied NT_PRFPREG buffer. */ +static int fpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, - const void *kbuf, const void __user *ubuf) + void *kbuf, void __user *ubuf) { - unsigned i; int err; - u64 fpr_val; /* XXX fcr31 */ - if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) - return user_regset_copyin(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); + if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) + err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf); + else + err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf); + + return err; +} + +/* + * Copy the supplied NT_PRFPREG buffer to the floating-point context, + * !CONFIG_CPU_HAS_MSA variant. Buffer slots correspond 1:1 to FP + * context's general register slots. + */ +static int fpr_set_fpa(struct task_struct *target, + unsigned int *pos, unsigned int *count, + const void **kbuf, const void __user **ubuf) +{ + return user_regset_copyin(pos, count, kbuf, ubuf, + &target->thread.fpu, + 0, sizeof(elf_fpregset_t)); +} + +/* + * Copy the supplied NT_PRFPREG buffer to the floating-point context, + * CONFIG_CPU_HAS_MSA variant. Buffer slots are copied to lower 64 + * bits only of FP context's general register slots. + */ +static int fpr_set_msa(struct task_struct *target, + unsigned int *pos, unsigned int *count, + const void **kbuf, const void __user **ubuf) +{ + unsigned int i; + u64 fpr_val; + int err; BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); - for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) { - err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + for (i = 0; i < NUM_FPU_REGS && *count >= sizeof(elf_fpreg_t); i++) { + err = user_regset_copyin(pos, count, kbuf, ubuf, &fpr_val, i * sizeof(elf_fpreg_t), (i + 1) * sizeof(elf_fpreg_t)); if (err) @@ -457,6 +497,24 @@ static int fpr_set(struct task_struct *target, return 0; } +/* Copy the supplied NT_PRFPREG buffer to the floating-point context. */ +static int fpr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int err; + + /* XXX fcr31 */ + + if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) + err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf); + else + err = fpr_set_msa(target, &pos, &count, &kbuf, &ubuf); + + return err; +} + enum mips_regset { REGSET_GPR, REGSET_FPR, -- GitLab From 80aaed846ef9d812fde6eea2bf0a6966e325d862 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 11 Dec 2017 22:52:15 +0000 Subject: [PATCH 2313/5498] MIPS: Guard against any partial write attempt with PTRACE_SETREGSET commit dc24d0edf33c3e15099688b6bbdf7bdc24bf6e91 upstream. Complement commit d614fd58a283 ("mips/ptrace: Preserve previous registers for short regset write") and ensure that no partial register write attempt is made with PTRACE_SETREGSET, as we do not preinitialize any temporaries used to hold incoming register data and consequently random data could be written. It is the responsibility of the caller, such as `ptrace_regset', to arrange for writes to span whole registers only, so here we only assert that it has indeed happened. Signed-off-by: Maciej W. Rozycki Fixes: 72b22bbad1e7 ("MIPS: Don't assume 64-bit FP registers for FP regset") Cc: James Hogan Cc: Paul Burton Cc: Alex Smith Cc: Dave Martin Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/17926/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 0522e717b738..53c3f4de45d1 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -497,7 +497,15 @@ static int fpr_set_msa(struct task_struct *target, return 0; } -/* Copy the supplied NT_PRFPREG buffer to the floating-point context. */ +/* + * Copy the supplied NT_PRFPREG buffer to the floating-point context. + * + * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', + * which is supposed to have been guaranteed by the kernel before + * calling us, e.g. in `ptrace_regset'. We enforce that requirement, + * so that we can safely avoid preinitializing temporaries for + * partial register writes. + */ static int fpr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -505,6 +513,8 @@ static int fpr_set(struct task_struct *target, { int err; + BUG_ON(count % sizeof(elf_fpreg_t)); + /* XXX fcr31 */ if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) -- GitLab From fd8a39fd186ea603666e80cf04c8c1b1524693b4 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 11 Dec 2017 22:53:14 +0000 Subject: [PATCH 2314/5498] MIPS: Consistently handle buffer counter with PTRACE_SETREGSET commit 80b3ffce0196ea50068885d085ff981e4b8396f4 upstream. Update commit d614fd58a283 ("mips/ptrace: Preserve previous registers for short regset write") bug and consistently consume all data supplied to `fpr_set_msa' with the ptrace(2) PTRACE_SETREGSET request, such that a zero data buffer counter is returned where insufficient data has been given to fill a whole number of FP general registers. In reality this is not going to happen, as the caller is supposed to only supply data covering a whole number of registers and it is verified in `ptrace_regset' and again asserted in `fpr_set', however structuring code such that the presence of trailing partial FP general register data causes `fpr_set_msa' to return with a non-zero data buffer counter makes it appear that this trailing data will be used if there are subsequent writes made to FP registers, which is going to be the case with the FCSR once the missing write to that register has been fixed. Fixes: d614fd58a283 ("mips/ptrace: Preserve previous registers for short regset write") Signed-off-by: Maciej W. Rozycki Cc: James Hogan Cc: Paul Burton Cc: Alex Smith Cc: Dave Martin Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org # v4.11+ Patchwork: https://patchwork.linux-mips.org/patch/17927/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 53c3f4de45d1..d7f92db94470 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -485,7 +485,7 @@ static int fpr_set_msa(struct task_struct *target, int err; BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); - for (i = 0; i < NUM_FPU_REGS && *count >= sizeof(elf_fpreg_t); i++) { + for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) { err = user_regset_copyin(pos, count, kbuf, ubuf, &fpr_val, i * sizeof(elf_fpreg_t), (i + 1) * sizeof(elf_fpreg_t)); -- GitLab From 0cc4b3b0bb375604371e925319b5feb33f9a0ca2 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 11 Dec 2017 22:54:33 +0000 Subject: [PATCH 2315/5498] MIPS: Fix an FCSR access API regression with NT_PRFPREG and MSA commit be07a6a1188372b6d19a3307ec33211fc9c9439d upstream. Fix a commit 72b22bbad1e7 ("MIPS: Don't assume 64-bit FP registers for FP regset") public API regression, then activated by commit 1db1af84d6df ("MIPS: Basic MSA context switching support"), that caused the FCSR register not to be read or written for CONFIG_CPU_HAS_MSA kernel configurations (regardless of actual presence or absence of the MSA feature in a given processor) with ptrace(2) PTRACE_GETREGSET and PTRACE_SETREGSET requests nor recorded in core dumps. This is because with !CONFIG_CPU_HAS_MSA configurations the whole of `elf_fpregset_t' array is bulk-copied as it is, which includes the FCSR in one half of the last, 33rd slot, whereas with CONFIG_CPU_HAS_MSA configurations array elements are copied individually, and then only the leading 32 FGR slots while the remaining slot is ignored. Correct the code then such that only FGR slots are copied in the respective !MSA and MSA helpers an then the FCSR slot is handled separately in common code. Use `ptrace_setfcr31' to update the FCSR too, so that the read-only mask is respected. Retrieving a correct value of FCSR is important in debugging not only for the human to be able to get the right interpretation of the situation, but for correct operation of GDB as well. This is because the condition code bits in FSCR are used by GDB to determine the location to place a breakpoint at when single-stepping through an FPU branch instruction. If such a breakpoint is placed incorrectly (i.e. with the condition reversed), then it will be missed, likely causing the debuggee to run away from the control of GDB and consequently breaking the process of investigation. Fortunately GDB continues using the older PTRACE_GETFPREGS ptrace(2) request which is unaffected, so the regression only really hits with post-mortem debug sessions using a core dump file, in which case execution, and consequently single-stepping through branches is not possible. Of course core files created by buggy kernels out there will have the value of FCSR recorded clobbered, but such core files cannot be corrected and the person using them simply will have to be aware that the value of FCSR retrieved is not reliable. Which also means we can likely get away without defining a replacement API which would ensure a correct value of FSCR to be retrieved, or none at all. This is based on previous work by Alex Smith, extensively rewritten. Signed-off-by: Alex Smith Signed-off-by: James Hogan Signed-off-by: Maciej W. Rozycki Fixes: 72b22bbad1e7 ("MIPS: Don't assume 64-bit FP registers for FP regset") Cc: Paul Burton Cc: Dave Martin Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/17928/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 47 ++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index d7f92db94470..1848877f4dc9 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -403,7 +403,7 @@ static int gpr64_set(struct task_struct *target, /* * Copy the floating-point context to the supplied NT_PRFPREG buffer, * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots - * correspond 1:1 to buffer slots. + * correspond 1:1 to buffer slots. Only general registers are copied. */ static int fpr_get_fpa(struct task_struct *target, unsigned int *pos, unsigned int *count, @@ -411,13 +411,14 @@ static int fpr_get_fpa(struct task_struct *target, { return user_regset_copyout(pos, count, kbuf, ubuf, &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); + 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); } /* * Copy the floating-point context to the supplied NT_PRFPREG buffer, * CONFIG_CPU_HAS_MSA variant. Only lower 64 bits of FP context's - * general register slots are copied to buffer slots. + * general register slots are copied to buffer slots. Only general + * registers are copied. */ static int fpr_get_msa(struct task_struct *target, unsigned int *pos, unsigned int *count, @@ -439,20 +440,29 @@ static int fpr_get_msa(struct task_struct *target, return 0; } -/* Copy the floating-point context to the supplied NT_PRFPREG buffer. */ +/* + * Copy the floating-point context to the supplied NT_PRFPREG buffer. + * Choose the appropriate helper for general registers, and then copy + * the FCSR register separately. + */ static int fpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { + const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); int err; - /* XXX fcr31 */ - if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf); else err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf); + if (err) + return err; + + err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fcr31, + fcr31_pos, fcr31_pos + sizeof(u32)); return err; } @@ -460,7 +470,7 @@ static int fpr_get(struct task_struct *target, /* * Copy the supplied NT_PRFPREG buffer to the floating-point context, * !CONFIG_CPU_HAS_MSA variant. Buffer slots correspond 1:1 to FP - * context's general register slots. + * context's general register slots. Only general registers are copied. */ static int fpr_set_fpa(struct task_struct *target, unsigned int *pos, unsigned int *count, @@ -468,13 +478,14 @@ static int fpr_set_fpa(struct task_struct *target, { return user_regset_copyin(pos, count, kbuf, ubuf, &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); + 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); } /* * Copy the supplied NT_PRFPREG buffer to the floating-point context, * CONFIG_CPU_HAS_MSA variant. Buffer slots are copied to lower 64 - * bits only of FP context's general register slots. + * bits only of FP context's general register slots. Only general + * registers are copied. */ static int fpr_set_msa(struct task_struct *target, unsigned int *pos, unsigned int *count, @@ -499,6 +510,8 @@ static int fpr_set_msa(struct task_struct *target, /* * Copy the supplied NT_PRFPREG buffer to the floating-point context. + * Choose the appropriate helper for general registers, and then copy + * the FCSR register separately. * * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', * which is supposed to have been guaranteed by the kernel before @@ -511,16 +524,28 @@ static int fpr_set(struct task_struct *target, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { + const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); + u32 fcr31; int err; BUG_ON(count % sizeof(elf_fpreg_t)); - /* XXX fcr31 */ - if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf); else err = fpr_set_msa(target, &pos, &count, &kbuf, &ubuf); + if (err) + return err; + + if (count > 0) { + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fcr31, + fcr31_pos, fcr31_pos + sizeof(u32)); + if (err) + return err; + + target->thread.fpu.fcr31 = fcr31 & ~FPU_CSR_ALL_X; + } return err; } -- GitLab From 1eaff733141a52dce42b2bcce8db7643843ba121 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 11 Dec 2017 22:56:54 +0000 Subject: [PATCH 2316/5498] MIPS: Disallow outsized PTRACE_SETREGSET NT_PRFPREG regset accesses commit c8c5a3a24d395b14447a9a89d61586a913840a3b upstream. Complement commit c23b3d1a5311 ("MIPS: ptrace: Change GP regset to use correct core dump register layout") and also reject outsized PTRACE_SETREGSET requests to the NT_PRFPREG regset, like with the NT_PRSTATUS regset. Signed-off-by: Maciej W. Rozycki Fixes: c23b3d1a5311 ("MIPS: ptrace: Change GP regset to use correct core dump register layout") Cc: James Hogan Cc: Paul Burton Cc: Alex Smith Cc: Dave Martin Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/17930/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 1848877f4dc9..ae02cd1a038e 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -530,6 +530,9 @@ static int fpr_set(struct task_struct *target, BUG_ON(count % sizeof(elf_fpreg_t)); + if (pos + count > sizeof(elf_fpregset_t)) + return -EIO; + if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf); else -- GitLab From c3b291e7fb8d9256c56d932dfbb1fc847a5f8a2c Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 11 Dec 2017 22:55:40 +0000 Subject: [PATCH 2317/5498] MIPS: Also verify sizeof `elf_fpreg_t' with PTRACE_SETREGSET commit 006501e039eec411842bb3150c41358867d320c2 upstream. Complement commit d614fd58a283 ("mips/ptrace: Preserve previous registers for short regset write") and like with the PTRACE_GETREGSET ptrace(2) request also apply a BUILD_BUG_ON check for the size of the `elf_fpreg_t' type in the PTRACE_SETREGSET request handler. Signed-off-by: Maciej W. Rozycki Fixes: d614fd58a283 ("mips/ptrace: Preserve previous registers for short regset write") Cc: James Hogan Cc: Paul Burton Cc: Alex Smith Cc: Dave Martin Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/17929/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index ae02cd1a038e..8b19ef037253 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -428,6 +428,7 @@ static int fpr_get_msa(struct task_struct *target, u64 fpr_val; int err; + BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); for (i = 0; i < NUM_FPU_REGS; i++) { fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); err = user_regset_copyout(pos, count, kbuf, ubuf, -- GitLab From 2f9cf5cd5580046fe9ff97dae32f9c753500d4ea Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 11 Jan 2017 21:09:50 +0100 Subject: [PATCH 2318/5498] perf/core: Fix concurrent sys_perf_event_open() vs. 'move_group' race commit 321027c1fe77f892f4ea07846aeae08cefbbb290 upstream. Di Shen reported a race between two concurrent sys_perf_event_open() calls where both try and move the same pre-existing software group into a hardware context. The problem is exactly that described in commit: f63a8daa5812 ("perf: Fix event->ctx locking") ... where, while we wait for a ctx->mutex acquisition, the event->ctx relation can have changed under us. That very same commit failed to recognise sys_perf_event_context() as an external access vector to the events and thereby didn't apply the established locking rules correctly. So while one sys_perf_event_open() call is stuck waiting on mutex_lock_double(), the other (which owns said locks) moves the group about. So by the time the former sys_perf_event_open() acquires the locks, the context we've acquired is stale (and possibly dead). Apply the established locking rules as per perf_event_ctx_lock_nested() to the mutex_lock_double() for the 'move_group' case. This obviously means we need to validate state after we acquire the locks. Reported-by: Di Shen (Keen Lab) Tested-by: John Dias Signed-off-by: Peter Zijlstra (Intel) Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Kees Cook Cc: Linus Torvalds Cc: Min Chong Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Fixes: f63a8daa5812 ("perf: Fix event->ctx locking") Link: http://lkml.kernel.org/r/20170106131444.GZ3174@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar [bwh: Backported to 3.16: - Use ACCESS_ONCE() instead of READ_ONCE() - Test perf_event::group_flags instead of group_caps - Add the err_locked cleanup block, which we didn't need before - Adjust context] Signed-off-by: Ben Hutchings Signed-off-by: Suren Baghdasaryan Signed-off-by: Amit Pundir Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 61 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 9b12efcefdf7..de3303aab7d6 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7414,6 +7414,37 @@ static void mutex_lock_double(struct mutex *a, struct mutex *b) mutex_lock_nested(b, SINGLE_DEPTH_NESTING); } +/* + * Variation on perf_event_ctx_lock_nested(), except we take two context + * mutexes. + */ +static struct perf_event_context * +__perf_event_ctx_lock_double(struct perf_event *group_leader, + struct perf_event_context *ctx) +{ + struct perf_event_context *gctx; + +again: + rcu_read_lock(); + gctx = ACCESS_ONCE(group_leader->ctx); + if (!atomic_inc_not_zero(&gctx->refcount)) { + rcu_read_unlock(); + goto again; + } + rcu_read_unlock(); + + mutex_lock_double(&gctx->mutex, &ctx->mutex); + + if (group_leader->ctx != gctx) { + mutex_unlock(&ctx->mutex); + mutex_unlock(&gctx->mutex); + put_ctx(gctx); + goto again; + } + + return gctx; +} + /** * sys_perf_event_open - open a performance event, associate it to a task/cpu * @@ -7626,14 +7657,31 @@ SYSCALL_DEFINE5(perf_event_open, } if (move_group) { - gctx = group_leader->ctx; + gctx = __perf_event_ctx_lock_double(group_leader, ctx); + + /* + * Check if we raced against another sys_perf_event_open() call + * moving the software group underneath us. + */ + if (!(group_leader->group_flags & PERF_GROUP_SOFTWARE)) { + /* + * If someone moved the group out from under us, check + * if this new event wound up on the same ctx, if so + * its the regular !move_group case, otherwise fail. + */ + if (gctx != ctx) { + err = -EINVAL; + goto err_locked; + } else { + perf_event_ctx_unlock(group_leader, gctx); + move_group = 0; + } + } /* * See perf_event_ctx_lock() for comments on the details * of swizzling perf_event::ctx. */ - mutex_lock_double(&gctx->mutex, &ctx->mutex); - perf_remove_from_context(group_leader, false); /* @@ -7674,7 +7722,7 @@ SYSCALL_DEFINE5(perf_event_open, perf_unpin_context(ctx); if (move_group) { - mutex_unlock(&gctx->mutex); + perf_event_ctx_unlock(group_leader, gctx); put_ctx(gctx); } mutex_unlock(&ctx->mutex); @@ -7703,6 +7751,11 @@ SYSCALL_DEFINE5(perf_event_open, fd_install(event_fd, event_file); return event_fd; +err_locked: + if (move_group) + perf_event_ctx_unlock(group_leader, gctx); + mutex_unlock(&ctx->mutex); + fput(event_file); err_context: perf_unpin_context(ctx); put_ctx(ctx); -- GitLab From 17e27ff45ed9c9c87ae918178953e8ac0018522a Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Wed, 3 Jan 2018 14:31:38 -0800 Subject: [PATCH 2319/5498] kvm: vmx: Scrub hardware GPRs at VM-exit commit 0cb5b30698fdc8f6b4646012e3acb4ddce430788 upstream. Guest GPR values are live in the hardware GPRs at VM-exit. Do not leave any guest values in hardware GPRs after the guest GPR values are saved to the vcpu_vmx structure. This is a partial mitigation for CVE 2017-5715 and CVE 2017-5753. Specifically, it defeats the Project Zero PoC for CVE 2017-5715. Suggested-by: Eric Northup Signed-off-by: Jim Mattson Reviewed-by: Eric Northup Reviewed-by: Benjamin Serebrin Reviewed-by: Andrew Honig [Paolo: Add AMD bits, Signed-off-by: Tom Lendacky ] Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm.c | 19 +++++++++++++++++++ arch/x86/kvm/vmx.c | 14 +++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index c59e8f5c2e2f..36414d13289f 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -3945,6 +3945,25 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) "mov %%r13, %c[r13](%[svm]) \n\t" "mov %%r14, %c[r14](%[svm]) \n\t" "mov %%r15, %c[r15](%[svm]) \n\t" +#endif + /* + * Clear host registers marked as clobbered to prevent + * speculative use. + */ + "xor %%" _ASM_BX ", %%" _ASM_BX " \n\t" + "xor %%" _ASM_CX ", %%" _ASM_CX " \n\t" + "xor %%" _ASM_DX ", %%" _ASM_DX " \n\t" + "xor %%" _ASM_SI ", %%" _ASM_SI " \n\t" + "xor %%" _ASM_DI ", %%" _ASM_DI " \n\t" +#ifdef CONFIG_X86_64 + "xor %%r8, %%r8 \n\t" + "xor %%r9, %%r9 \n\t" + "xor %%r10, %%r10 \n\t" + "xor %%r11, %%r11 \n\t" + "xor %%r12, %%r12 \n\t" + "xor %%r13, %%r13 \n\t" + "xor %%r14, %%r14 \n\t" + "xor %%r15, %%r15 \n\t" #endif "pop %%" _ASM_BP : diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index bdbd5d3fc98f..c1fd6d3d4394 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -7653,6 +7653,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) /* Save guest registers, load host registers, keep flags */ "mov %0, %c[wordsize](%%" _ASM_SP ") \n\t" "pop %0 \n\t" + "setbe %c[fail](%0)\n\t" "mov %%" _ASM_AX ", %c[rax](%0) \n\t" "mov %%" _ASM_BX ", %c[rbx](%0) \n\t" __ASM_SIZE(pop) " %c[rcx](%0) \n\t" @@ -7669,12 +7670,23 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) "mov %%r13, %c[r13](%0) \n\t" "mov %%r14, %c[r14](%0) \n\t" "mov %%r15, %c[r15](%0) \n\t" + "xor %%r8d, %%r8d \n\t" + "xor %%r9d, %%r9d \n\t" + "xor %%r10d, %%r10d \n\t" + "xor %%r11d, %%r11d \n\t" + "xor %%r12d, %%r12d \n\t" + "xor %%r13d, %%r13d \n\t" + "xor %%r14d, %%r14d \n\t" + "xor %%r15d, %%r15d \n\t" #endif "mov %%cr2, %%" _ASM_AX " \n\t" "mov %%" _ASM_AX ", %c[cr2](%0) \n\t" + "xor %%eax, %%eax \n\t" + "xor %%ebx, %%ebx \n\t" + "xor %%esi, %%esi \n\t" + "xor %%edi, %%edi \n\t" "pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t" - "setbe %c[fail](%0) \n\t" ".pushsection .rodata \n\t" ".global vmx_return \n\t" "vmx_return: " _ASM_PTR " 2b \n\t" -- GitLab From ae62a913c1c887f92579e5902c1baaec24ea673b Mon Sep 17 00:00:00 2001 From: Vikas C Sajjan Date: Thu, 16 Nov 2017 21:43:44 +0530 Subject: [PATCH 2320/5498] x86/acpi: Handle SCI interrupts above legacy space gracefully commit 252714155f04c5d16989cb3aadb85fd1b5772f99 upstream. Platforms which support only IOAPIC mode, pass the SCI information above the legacy space (0-15) via the FADT mechanism and not via MADT. In such cases mp_override_legacy_irq() which is invoked from acpi_sci_ioapic_setup() to register SCI interrupts fails for interrupts greater equal 16, since it is meant to handle only the legacy space and emits error "Invalid bus_irq %u for legacy override". Add a new function to handle SCI interrupts >= 16 and invoke it conditionally in acpi_sci_ioapic_setup(). The code duplication due to this new function will be cleaned up in a separate patch. Co-developed-by: Sunil V L Signed-off-by: Vikas C Sajjan Signed-off-by: Sunil V L Signed-off-by: Thomas Gleixner Tested-by: Abdul Lateef Attar Acked-by: Rafael J. Wysocki Cc: linux-pm@vger.kernel.org Cc: kkamagui@gmail.com Cc: linux-acpi@vger.kernel.org Link: https://lkml.kernel.org/r/1510848825-21965-2-git-send-email-vikas.cha.sajjan@hpe.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/acpi/boot.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 690a25967a10..0a609dc5306c 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -445,6 +445,34 @@ static struct irq_domain_ops acpi_irqdomain_ops = { .unmap = mp_irqdomain_unmap, }; +static int __init mp_register_ioapic_irq(u8 bus_irq, u8 polarity, + u8 trigger, u32 gsi) +{ + struct mpc_intsrc mp_irq; + int ioapic, pin; + + /* Convert 'gsi' to 'ioapic.pin'(INTIN#) */ + ioapic = mp_find_ioapic(gsi); + if (ioapic < 0) { + pr_warn("Failed to find ioapic for gsi : %u\n", gsi); + return ioapic; + } + + pin = mp_find_ioapic_pin(ioapic, gsi); + + mp_irq.type = MP_INTSRC; + mp_irq.irqtype = mp_INT; + mp_irq.irqflag = (trigger << 2) | polarity; + mp_irq.srcbus = MP_ISA_BUS; + mp_irq.srcbusirq = bus_irq; + mp_irq.dstapic = mpc_ioapic_id(ioapic); + mp_irq.dstirq = pin; + + mp_save_irq(&mp_irq); + + return 0; +} + static int __init acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end) { @@ -489,7 +517,10 @@ static void __init acpi_sci_ioapic_setup(u8 bus_irq, u16 polarity, u16 trigger, if (acpi_sci_flags & ACPI_MADT_POLARITY_MASK) polarity = acpi_sci_flags & ACPI_MADT_POLARITY_MASK; - mp_override_legacy_irq(bus_irq, polarity, trigger, gsi); + if (bus_irq < NR_IRQS_LEGACY) + mp_override_legacy_irq(bus_irq, polarity, trigger, gsi); + else + mp_register_ioapic_irq(bus_irq, polarity, trigger, gsi); /* * stash over-ride to indicate we've been here -- GitLab From f8c035f6db23713160ad1886b7d7fe1d28d1b63a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 1 Jan 2018 09:50:50 +0100 Subject: [PATCH 2321/5498] ALSA: pcm: Remove incorrect snd_BUG_ON() usages commit fe08f34d066f4404934a509b6806db1a4f700c86 upstream. syzkaller triggered kernel warnings through PCM OSS emulation at closing a stream: WARNING: CPU: 0 PID: 3502 at sound/core/pcm_lib.c:1635 snd_pcm_hw_param_first+0x289/0x690 sound/core/pcm_lib.c:1635 Call Trace: .... snd_pcm_hw_param_near.constprop.27+0x78d/0x9a0 sound/core/oss/pcm_oss.c:457 snd_pcm_oss_change_params+0x17d3/0x3720 sound/core/oss/pcm_oss.c:969 snd_pcm_oss_make_ready+0xaa/0x130 sound/core/oss/pcm_oss.c:1128 snd_pcm_oss_sync+0x257/0x830 sound/core/oss/pcm_oss.c:1638 snd_pcm_oss_release+0x20b/0x280 sound/core/oss/pcm_oss.c:2431 __fput+0x327/0x7e0 fs/file_table.c:210 .... This happens while it tries to open and set up the aloop device concurrently. The warning above (invoked from snd_BUG_ON() macro) is to detect the unexpected logical error where snd_pcm_hw_refine() call shouldn't fail. The theory is true for the case where the hw_params config rules are static. But for an aloop device, the hw_params rule condition does vary dynamically depending on the connected target; when another device is opened and changes the parameters, the device connected in another side is also affected, and it caused the error from snd_pcm_hw_refine(). That is, the simplest "solution" for this is to remove the incorrect assumption of static rules, and treat such an error as a normal error path. As there are a couple of other places using snd_BUG_ON() incorrectly, this patch removes these spurious snd_BUG_ON() calls. Reported-by: syzbot+6f11c7e2a1b91d466432@syzkaller.appspotmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/oss/pcm_oss.c | 1 - sound/core/pcm_lib.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index f29f1ce4a455..68c272e65f43 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -465,7 +465,6 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, v = snd_pcm_hw_param_last(pcm, params, var, dir); else v = snd_pcm_hw_param_first(pcm, params, var, dir); - snd_BUG_ON(v < 0); return v; } diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 693ab89cc9a2..23e31ae9623f 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1633,7 +1633,7 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); - if (snd_BUG_ON(err < 0)) + if (err < 0) return err; } return snd_pcm_hw_param_value(params, var, dir); @@ -1680,7 +1680,7 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); - if (snd_BUG_ON(err < 0)) + if (err < 0) return err; } return snd_pcm_hw_param_value(params, var, dir); -- GitLab From fbff9ddad4271f814019397c7cc6b43e9354ecb3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jan 2018 16:39:27 +0100 Subject: [PATCH 2322/5498] ALSA: pcm: Add missing error checks in OSS emulation plugin builder commit 6708913750344a900f2e73bfe4a4d6dbbce4fe8d upstream. In the OSS emulation plugin builder where the frame size is parsed in the plugin chain, some places miss the possible errors returned from the plugin src_ or dst_frames callback. This patch papers over such places. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/oss/pcm_plugin.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 727ac44d39f4..a84a1d3d23e5 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -591,18 +591,26 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st snd_pcm_sframes_t frames = size; plugin = snd_pcm_plug_first(plug); - while (plugin && frames > 0) { + while (plugin) { + if (frames <= 0) + return frames; if ((next = plugin->next) != NULL) { snd_pcm_sframes_t frames1 = frames; - if (plugin->dst_frames) + if (plugin->dst_frames) { frames1 = plugin->dst_frames(plugin, frames); + if (frames1 <= 0) + return frames1; + } if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { return err; } if (err != frames1) { frames = err; - if (plugin->src_frames) + if (plugin->src_frames) { frames = plugin->src_frames(plugin, frames1); + if (frames <= 0) + return frames; + } } } else dst_channels = NULL; -- GitLab From 220d72342a8014f743331163b70a1253dfc69ed7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jan 2018 13:58:31 +0100 Subject: [PATCH 2323/5498] ALSA: pcm: Abort properly at pending signal in OSS read/write loops commit 29159a4ed7044c52e3e2cf1a9fb55cec4745c60b upstream. The loops for read and write in PCM OSS emulation have no proper check of pending signals, and they keep processing even after user tries to break. This results in a very long delay, often seen as RCU stall when a huge unprocessed bytes remain queued. The bug could be easily triggered by syzkaller. As a simple workaround, this patch adds the proper check of pending signals and aborts the loop appropriately. Reported-by: syzbot+993cb4cfcbbff3947c21@syzkaller.appspotmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/oss/pcm_oss.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 68c272e65f43..6709a1f380a1 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1417,6 +1417,10 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha tmp != runtime->oss.period_bytes) break; } + if (signal_pending(current)) { + tmp = -ERESTARTSYS; + goto err; + } } mutex_unlock(&runtime->oss.params_lock); return xfer; @@ -1502,6 +1506,10 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use bytes -= tmp; xfer += tmp; } + if (signal_pending(current)) { + tmp = -ERESTARTSYS; + goto err; + } } mutex_unlock(&runtime->oss.params_lock); return xfer; -- GitLab From cb8ee4961860b4ec488d20d28db4f8e6e1410de3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jan 2018 14:03:53 +0100 Subject: [PATCH 2324/5498] ALSA: pcm: Allow aborting mutex lock at OSS read/write loops commit 900498a34a3ac9c611e9b425094c8106bdd7dc1c upstream. PCM OSS read/write loops keep taking the mutex lock for the whole read/write, and this might take very long when the exceptionally high amount of data is given. Also, since it invokes with mutex_lock(), the concurrent read/write becomes unbreakable. This patch tries to address these issues by replacing mutex_lock() with mutex_lock_interruptible(), and also splits / re-takes the lock at each read/write period chunk, so that it can switch the context more finely if requested. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/oss/pcm_oss.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 6709a1f380a1..96612762d623 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1370,8 +1370,11 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) return tmp; - mutex_lock(&runtime->oss.params_lock); while (bytes > 0) { + if (mutex_lock_interruptible(&runtime->oss.params_lock)) { + tmp = -ERESTARTSYS; + break; + } if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { tmp = bytes; if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) @@ -1415,18 +1418,18 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha xfer += tmp; if ((substream->f_flags & O_NONBLOCK) != 0 && tmp != runtime->oss.period_bytes) - break; + tmp = -EAGAIN; } + err: + mutex_unlock(&runtime->oss.params_lock); + if (tmp < 0) + break; if (signal_pending(current)) { tmp = -ERESTARTSYS; - goto err; + break; } + tmp = 0; } - mutex_unlock(&runtime->oss.params_lock); - return xfer; - - err: - mutex_unlock(&runtime->oss.params_lock); return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; } @@ -1474,8 +1477,11 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) return tmp; - mutex_lock(&runtime->oss.params_lock); while (bytes > 0) { + if (mutex_lock_interruptible(&runtime->oss.params_lock)) { + tmp = -ERESTARTSYS; + break; + } if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { if (runtime->oss.buffer_used == 0) { tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); @@ -1506,16 +1512,16 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use bytes -= tmp; xfer += tmp; } + err: + mutex_unlock(&runtime->oss.params_lock); + if (tmp < 0) + break; if (signal_pending(current)) { tmp = -ERESTARTSYS; - goto err; + break; } + tmp = 0; } - mutex_unlock(&runtime->oss.params_lock); - return xfer; - - err: - mutex_unlock(&runtime->oss.params_lock); return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; } -- GitLab From 5517ed304acd60f267906c590c5879f718de9f88 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jan 2018 16:09:47 +0100 Subject: [PATCH 2325/5498] ALSA: aloop: Release cable upon open error path commit 9685347aa0a5c2869058ca6ab79fd8e93084a67f upstream. The aloop runtime object and its assignment in the cable are left even when opening a substream fails. This doesn't mean any memory leak, but it still keeps the invalid pointer that may be referred by the another side of the cable spontaneously, which is a potential Oops cause. Clean up the cable assignment and the empty cable upon the error path properly. Fixes: 597603d615d2 ("ALSA: introduce the snd-aloop module for the PCM loopback") Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/drivers/aloop.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 2a16c86a60b3..65beb96a0273 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -659,12 +659,31 @@ static int rule_channels(struct snd_pcm_hw_params *params, return snd_interval_refine(hw_param_interval(params, rule->var), &t); } +static void free_cable(struct snd_pcm_substream *substream) +{ + struct loopback *loopback = substream->private_data; + int dev = get_cable_index(substream); + struct loopback_cable *cable; + + cable = loopback->cables[substream->number][dev]; + if (!cable) + return; + if (cable->streams[!substream->stream]) { + /* other stream is still alive */ + cable->streams[substream->stream] = NULL; + } else { + /* free the cable */ + loopback->cables[substream->number][dev] = NULL; + kfree(cable); + } +} + static int loopback_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback *loopback = substream->private_data; struct loopback_pcm *dpcm; - struct loopback_cable *cable; + struct loopback_cable *cable = NULL; int err = 0; int dev = get_cable_index(substream); @@ -683,7 +702,6 @@ static int loopback_open(struct snd_pcm_substream *substream) if (!cable) { cable = kzalloc(sizeof(*cable), GFP_KERNEL); if (!cable) { - kfree(dpcm); err = -ENOMEM; goto unlock; } @@ -725,6 +743,10 @@ static int loopback_open(struct snd_pcm_substream *substream) else runtime->hw = cable->hw; unlock: + if (err < 0) { + free_cable(substream); + kfree(dpcm); + } mutex_unlock(&loopback->cable_lock); return err; } @@ -733,20 +755,10 @@ static int loopback_close(struct snd_pcm_substream *substream) { struct loopback *loopback = substream->private_data; struct loopback_pcm *dpcm = substream->runtime->private_data; - struct loopback_cable *cable; - int dev = get_cable_index(substream); loopback_timer_stop(dpcm); mutex_lock(&loopback->cable_lock); - cable = loopback->cables[substream->number][dev]; - if (cable->streams[!substream->stream]) { - /* other stream is still alive */ - cable->streams[substream->stream] = NULL; - } else { - /* free the cable */ - loopback->cables[substream->number][dev] = NULL; - kfree(cable); - } + free_cable(substream); mutex_unlock(&loopback->cable_lock); return 0; } -- GitLab From 1720189b6d20edc7a0afa8a77d3db779e918f1b1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jan 2018 16:15:33 +0100 Subject: [PATCH 2326/5498] ALSA: aloop: Fix inconsistent format due to incomplete rule commit b088b53e20c7d09b5ab84c5688e609f478e5c417 upstream. The extra hw constraint rule for the formats the aloop driver introduced has a slight flaw, where it doesn't return a positive value when the mask got changed. It came from the fact that it's basically a copy&paste from snd_hw_constraint_mask64(). The original code is supposed to be a single-shot and it modifies the mask bits only once and never after, while what we need for aloop is the dynamic hw rule that limits the mask bits. This difference results in the inconsistent state, as the hw_refine doesn't apply the dependencies fully. The worse and surprisingly result is that it causes a crash in OSS emulation when multiple full-duplex reads/writes are performed concurrently (I leave why it triggers Oops to readers as a homework). For fixing this, replace a few open-codes with the standard snd_mask_*() macros. Reported-by: syzbot+3902b5220e8ca27889ca@syzkaller.appspotmail.com Fixes: b1c73fc8e697 ("ALSA: snd-aloop: Fix hw_params restrictions and checking") Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/drivers/aloop.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 65beb96a0273..fef1b0da2826 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -623,14 +624,12 @@ static int rule_format(struct snd_pcm_hw_params *params, { struct snd_pcm_hardware *hw = rule->private; - struct snd_mask *maskp = hw_param_mask(params, rule->var); + struct snd_mask m; - maskp->bits[0] &= (u_int32_t)hw->formats; - maskp->bits[1] &= (u_int32_t)(hw->formats >> 32); - memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ - if (! maskp->bits[0] && ! maskp->bits[1]) - return -EINVAL; - return 0; + snd_mask_none(&m); + m.bits[0] = (u_int32_t)hw->formats; + m.bits[1] = (u_int32_t)(hw->formats >> 32); + return snd_mask_refine(hw_param_mask(params, rule->var), &m); } static int rule_rate(struct snd_pcm_hw_params *params, -- GitLab From 48c270963471627cbafc7dadee0a7916a6de5942 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jan 2018 17:38:54 +0100 Subject: [PATCH 2327/5498] ALSA: aloop: Fix racy hw constraints adjustment commit 898dfe4687f460ba337a01c11549f87269a13fa2 upstream. The aloop driver tries to update the hw constraints of the connected target on the cable of the opened PCM substream. This is done by adding the extra hw constraints rules referring to the substream runtime->hw fields, while the other substream may update the runtime hw of another side on the fly. This is, however, racy and may result in the inconsistent values when both PCM streams perform the prepare concurrently. One of the reason is that it overwrites the other's runtime->hw field; which is not only racy but also broken when it's called before the open of another side finishes. And, since the reference to runtime->hw isn't protected, the concurrent write may give the partial value update and become inconsistent. This patch is an attempt to fix and clean up: - The prepare doesn't change the runtime->hw of other side any longer, but only update the cable->hw that is referred commonly. - The extra rules refer to the loopback_pcm object instead of the runtime->hw. The actual hw is deduced from cable->hw. - The extra rules take the cable_lock to protect against the race. Fixes: b1c73fc8e697 ("ALSA: snd-aloop: Fix hw_params restrictions and checking") Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/drivers/aloop.c | 51 ++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index fef1b0da2826..61a3160af532 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -307,19 +307,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static void params_change_substream(struct loopback_pcm *dpcm, - struct snd_pcm_runtime *runtime) -{ - struct snd_pcm_runtime *dst_runtime; - - if (dpcm == NULL || dpcm->substream == NULL) - return; - dst_runtime = dpcm->substream->runtime; - if (dst_runtime == NULL) - return; - dst_runtime->hw = dpcm->cable->hw; -} - static void params_change(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -331,10 +318,6 @@ static void params_change(struct snd_pcm_substream *substream) cable->hw.rate_max = runtime->rate; cable->hw.channels_min = runtime->channels; cable->hw.channels_max = runtime->channels; - params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK], - runtime); - params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE], - runtime); } static int loopback_prepare(struct snd_pcm_substream *substream) @@ -622,24 +605,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream) static int rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - - struct snd_pcm_hardware *hw = rule->private; + struct loopback_pcm *dpcm = rule->private; + struct loopback_cable *cable = dpcm->cable; struct snd_mask m; snd_mask_none(&m); - m.bits[0] = (u_int32_t)hw->formats; - m.bits[1] = (u_int32_t)(hw->formats >> 32); + mutex_lock(&dpcm->loopback->cable_lock); + m.bits[0] = (u_int32_t)cable->hw.formats; + m.bits[1] = (u_int32_t)(cable->hw.formats >> 32); + mutex_unlock(&dpcm->loopback->cable_lock); return snd_mask_refine(hw_param_mask(params, rule->var), &m); } static int rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_pcm_hardware *hw = rule->private; + struct loopback_pcm *dpcm = rule->private; + struct loopback_cable *cable = dpcm->cable; struct snd_interval t; - t.min = hw->rate_min; - t.max = hw->rate_max; + mutex_lock(&dpcm->loopback->cable_lock); + t.min = cable->hw.rate_min; + t.max = cable->hw.rate_max; + mutex_unlock(&dpcm->loopback->cable_lock); t.openmin = t.openmax = 0; t.integer = 0; return snd_interval_refine(hw_param_interval(params, rule->var), &t); @@ -648,11 +636,14 @@ static int rule_rate(struct snd_pcm_hw_params *params, static int rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_pcm_hardware *hw = rule->private; + struct loopback_pcm *dpcm = rule->private; + struct loopback_cable *cable = dpcm->cable; struct snd_interval t; - t.min = hw->channels_min; - t.max = hw->channels_max; + mutex_lock(&dpcm->loopback->cable_lock); + t.min = cable->hw.channels_min; + t.max = cable->hw.channels_max; + mutex_unlock(&dpcm->loopback->cable_lock); t.openmin = t.openmax = 0; t.integer = 0; return snd_interval_refine(hw_param_interval(params, rule->var), &t); @@ -718,19 +709,19 @@ static int loopback_open(struct snd_pcm_substream *substream) /* are cached -> they do not reflect the actual state */ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - rule_format, &runtime->hw, + rule_format, dpcm, SNDRV_PCM_HW_PARAM_FORMAT, -1); if (err < 0) goto unlock; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - rule_rate, &runtime->hw, + rule_rate, dpcm, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto unlock; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - rule_channels, &runtime->hw, + rule_channels, dpcm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto unlock; -- GitLab From a74040812453fd933760c8fa8a546a68455966a6 Mon Sep 17 00:00:00 2001 From: Vikas C Sajjan Date: Thu, 16 Nov 2017 21:43:45 +0530 Subject: [PATCH 2328/5498] x86/acpi: Reduce code duplication in mp_override_legacy_irq() commit 4ee2ec1b122599f7b10c849fa7915cebb37b7edb upstream. The new function mp_register_ioapic_irq() is a subset of the code in mp_override_legacy_irq(). Replace the code duplication by invoking mp_register_ioapic_irq() from mp_override_legacy_irq(). Signed-off-by: Vikas C Sajjan Signed-off-by: Thomas Gleixner Reviewed-by: Thomas Gleixner Acked-by: Rafael J. Wysocki Cc: linux-pm@vger.kernel.org Cc: kkamagui@gmail.com Cc: linux-acpi@vger.kernel.org Link: https://lkml.kernel.org/r/1510848825-21965-3-git-send-email-vikas.cha.sajjan@hpe.com Cc: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/acpi/boot.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 0a609dc5306c..f460a63473f0 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -308,13 +308,12 @@ acpi_parse_lapic_nmi(struct acpi_subtable_header * header, const unsigned long e #ifdef CONFIG_X86_IO_APIC #define MP_ISA_BUS 0 +static int __init mp_register_ioapic_irq(u8 bus_irq, u8 polarity, + u8 trigger, u32 gsi); + static void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, u32 gsi) { - int ioapic; - int pin; - struct mpc_intsrc mp_irq; - /* * Check bus_irq boundary. */ @@ -323,14 +322,6 @@ static void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, return; } - /* - * Convert 'gsi' to 'ioapic.pin'. - */ - ioapic = mp_find_ioapic(gsi); - if (ioapic < 0) - return; - pin = mp_find_ioapic_pin(ioapic, gsi); - /* * TBD: This check is for faulty timer entries, where the override * erroneously sets the trigger to level, resulting in a HUGE @@ -339,16 +330,8 @@ static void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, if ((bus_irq == 0) && (trigger == 3)) trigger = 1; - mp_irq.type = MP_INTSRC; - mp_irq.irqtype = mp_INT; - mp_irq.irqflag = (trigger << 2) | polarity; - mp_irq.srcbus = MP_ISA_BUS; - mp_irq.srcbusirq = bus_irq; /* IRQ */ - mp_irq.dstapic = mpc_ioapic_id(ioapic); /* APIC ID */ - mp_irq.dstirq = pin; /* INTIN# */ - - mp_save_irq(&mp_irq); - + if (mp_register_ioapic_irq(bus_irq, polarity, trigger, gsi) < 0) + return; /* * Reset default identity mapping if gsi is also an legacy IRQ, * otherwise there will be more than one entry with the same GSI -- GitLab From 0982b64e77d961fc21ef18907cd63d45a7ca62e5 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 9 Jan 2018 13:40:41 -0800 Subject: [PATCH 2329/5498] 8021q: fix a memory leak for VLAN 0 device [ Upstream commit 78bbb15f2239bc8e663aa20bbe1987c91a0b75f6 ] A vlan device with vid 0 is allow to creat by not able to be fully cleaned up by unregister_vlan_dev() which checks for vlan_id!=0. Also, VLAN 0 is probably not a valid number and it is kinda "reserved" for HW accelerating devices, but it is probably too late to reject it from creation even if makes sense. Instead, just remove the check in unregister_vlan_dev(). Reported-by: Dmitry Vyukov Fixes: ad1afb003939 ("vlan_dev: VLAN 0 should be treated as "no vlan tag" (802.1p packet)") Cc: Vlad Yasevich Cc: Ben Hutchings Signed-off-by: Cong Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/8021q/vlan.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 6b9c7eaca478..5bf39d5c2691 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -111,12 +111,7 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) vlan_gvrp_uninit_applicant(real_dev); } - /* Take it out of our own structures, but be sure to interlock with - * HW accelerating devices or SW vlan input packet processing if - * VLAN is not 0 (leave it there for 802.1p). - */ - if (vlan_id) - vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); + vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); /* Get rid of the vlan's reference to real_dev */ dev_put(real_dev); -- GitLab From a7b25c9f6ea92d582bda195eca561b9cb605ea91 Mon Sep 17 00:00:00 2001 From: Mohamed Ghannam Date: Tue, 2 Jan 2018 19:44:34 +0000 Subject: [PATCH 2330/5498] RDS: Heap OOB write in rds_message_alloc_sgs() [ Upstream commit c095508770aebf1b9218e77026e48345d719b17c ] When args->nr_local is 0, nr_pages gets also 0 due some size calculation via rds_rm_size(), which is later used to allocate pages for DMA, this bug produces a heap Out-Of-Bound write access to a specific memory region. Signed-off-by: Mohamed Ghannam Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/rds/rdma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/rds/rdma.c b/net/rds/rdma.c index 612c3050d514..3e6b133c5e96 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -516,6 +516,9 @@ int rds_rdma_extra_size(struct rds_rdma_args *args) local_vec = (struct rds_iovec __user *)(unsigned long) args->local_vec_addr; + if (args->nr_local == 0) + return -EINVAL; + /* figure out the number of pages in the vector */ for (i = 0; i < args->nr_local; i++) { if (copy_from_user(&vec, &local_vec[i], -- GitLab From 3396f2bef5ede06f73c0be43975ce837767785ed Mon Sep 17 00:00:00 2001 From: Mohamed Ghannam Date: Wed, 3 Jan 2018 21:06:06 +0000 Subject: [PATCH 2331/5498] RDS: null pointer dereference in rds_atomic_free_op [ Upstream commit 7d11f77f84b27cef452cee332f4e469503084737 ] set rm->atomic.op_active to 0 when rds_pin_pages() fails or the user supplied address is invalid, this prevents a NULL pointer usage in rds_atomic_free_op() Signed-off-by: Mohamed Ghannam Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/rds/rdma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/rds/rdma.c b/net/rds/rdma.c index 3e6b133c5e96..b1ec96bca937 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -866,6 +866,7 @@ int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm, err: if (page) put_page(page); + rm->atomic.op_active = 0; kfree(rm->atomic.op_notifier); return ret; -- GitLab From 7469b22a4216c1a1da201b51e3933352c163c061 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 3 Jan 2018 20:09:49 +0300 Subject: [PATCH 2332/5498] sh_eth: fix TSU resource handling [ Upstream commit dfe8266b8dd10e12a731c985b725fcf7f0e537f0 ] When switching the driver to the managed device API, I managed to break the case of a dual Ether devices sharing a single TSU: the 2nd Ether port wouldn't probe. Iwamatsu-san has tried to fix this but his patch was buggy and he then dropped the ball... The solution is to limit calling devm_request_mem_region() to the first of the two ports sharing the same TSU, so devm_ioremap_resource() can't be used anymore for the TSU resource... Fixes: d5e07e69218f ("sh_eth: use managed device API") Reported-by: Nobuhiro Iwamatsu Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/renesas/sh_eth.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 24e5d2135663..1170def88e02 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2890,10 +2890,29 @@ static int sh_eth_drv_probe(struct platform_device *pdev) /* ioremap the TSU registers */ if (mdp->cd->tsu) { struct resource *rtsu; + rtsu = platform_get_resource(pdev, IORESOURCE_MEM, 1); - mdp->tsu_addr = devm_ioremap_resource(&pdev->dev, rtsu); - if (IS_ERR(mdp->tsu_addr)) { - ret = PTR_ERR(mdp->tsu_addr); + if (!rtsu) { + dev_err(&pdev->dev, "no TSU resource\n"); + ret = -ENODEV; + goto out_release; + } + /* We can only request the TSU region for the first port + * of the two sharing this TSU for the probe to succeed... + */ + if (devno % 2 == 0 && + !devm_request_mem_region(&pdev->dev, rtsu->start, + resource_size(rtsu), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "can't request TSU resource.\n"); + ret = -EBUSY; + goto out_release; + } + mdp->tsu_addr = devm_ioremap(&pdev->dev, rtsu->start, + resource_size(rtsu)); + if (!mdp->tsu_addr) { + dev_err(&pdev->dev, "TSU region ioremap() failed.\n"); + ret = -ENOMEM; goto out_release; } mdp->port = devno % 2; -- GitLab From 7247c48a9a138ea0af35b05373135e60ab19e411 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Thu, 4 Jan 2018 21:06:49 +0300 Subject: [PATCH 2333/5498] sh_eth: fix SH7757 GEther initialization [ Upstream commit 5133550296d43236439494aa955bfb765a89f615 ] Renesas SH7757 has 2 Fast and 2 Gigabit Ether controllers, while the 'sh_eth' driver can only reset and initialize TSU of the first controller pair. Shimoda-san tried to solve that adding the 'needs_init' member to the 'struct sh_eth_plat_data', however the platform code still never sets this flag. I think that we can infer this information from the 'devno' variable (set to 'platform_device::id') and reset/init the Ether controller pair only for an even 'devno'; therefore 'sh_eth_plat_data::needs_init' can be removed... Fixes: 150647fb2c31 ("net: sh_eth: change the condition of initialization") Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/renesas/sh_eth.c | 4 ++-- include/linux/sh_eth.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 1170def88e02..b89d7c16991d 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2919,8 +2919,8 @@ static int sh_eth_drv_probe(struct platform_device *pdev) ndev->features = NETIF_F_HW_VLAN_CTAG_FILTER; } - /* initialize first or needed device */ - if (!devno || pd->needs_init) { + /* Need to init only the first port of the two sharing a TSU */ + if (devno % 2 == 0) { if (mdp->cd->chip_reset) mdp->cd->chip_reset(ndev); diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h index 8c9131db2b25..b050ef51e27e 100644 --- a/include/linux/sh_eth.h +++ b/include/linux/sh_eth.h @@ -16,7 +16,6 @@ struct sh_eth_plat_data { unsigned char mac_addr[ETH_ALEN]; unsigned no_ether_link:1; unsigned ether_link_active_low:1; - unsigned needs_init:1; }; #endif -- GitLab From 540eac0e594e216d7f85011286ee4bd234930ccc Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 3 Jan 2018 16:46:29 +0100 Subject: [PATCH 2334/5498] net: stmmac: enable EEE in MII, GMII or RGMII only [ Upstream commit 879626e3a52630316d817cbda7cec9a5446d1d82 ] Note in the databook - Section 4.4 - EEE : " The EEE feature is not supported when the MAC is configured to use the TBI, RTBI, SMII, RMII or SGMII single PHY interface. Even if the MAC supports multiple PHY interfaces, you should activate the EEE mode only when the MAC is operating with GMII, MII, or RGMII interface." Applying this restriction solves a stability issue observed on Amlogic gxl platforms operating with RMII interface and the internal PHY. Fixes: 83bf79b6bb64 ("stmmac: disable at run-time the EEE if not supported") Signed-off-by: Jerome Brunet Tested-by: Arnaud Patard Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 ++++++ include/linux/phy.h | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c769da8d6f3a..103ae8ef8643 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -277,8 +277,14 @@ bool stmmac_eee_init(struct stmmac_priv *priv) { char *phy_bus_name = priv->plat->phy_bus_name; unsigned long flags; + int interface = priv->plat->interface; bool ret = false; + if ((interface != PHY_INTERFACE_MODE_MII) && + (interface != PHY_INTERFACE_MODE_GMII) && + !phy_interface_mode_is_rgmii(interface)) + goto out; + /* Using PCS we cannot dial with the phy registers at this stage * so we do not support extra feature like EEE. */ diff --git a/include/linux/phy.h b/include/linux/phy.h index bc79f855fc32..2b3ca63e1f4c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -677,6 +677,17 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad, return mdiobus_write(phydev->bus, phydev->addr, regnum, val); } +/** + * phy_interface_mode_is_rgmii - Convenience function for testing if a + * PHY interface mode is RGMII (all variants) + * @mode: the phy_interface_t enum + */ +static inline bool phy_interface_mode_is_rgmii(phy_interface_t mode) +{ + return mode >= PHY_INTERFACE_MODE_RGMII && + mode <= PHY_INTERFACE_MODE_RGMII_TXID; +}; + /** * phy_write_mmd_indirect - writes data to the MMD registers * @phydev: The PHY device -- GitLab From 4cefa6f752ab132baa929f59151b284cb1539bbe Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 29 Dec 2017 14:30:19 -0600 Subject: [PATCH 2335/5498] crypto: algapi - fix NULL dereference in crypto_remove_spawns() commit 9a00674213a3f00394f4e3221b88f2d21fc05789 upstream. syzkaller triggered a NULL pointer dereference in crypto_remove_spawns() via a program that repeatedly and concurrently requests AEADs "authenc(cmac(des3_ede-asm),pcbc-aes-aesni)" and hashes "cmac(des3_ede)" through AF_ALG, where the hashes are requested as "untested" (CRYPTO_ALG_TESTED is set in ->salg_mask but clear in ->salg_feat; this causes the template to be instantiated for every request). Although AF_ALG users really shouldn't be able to request an "untested" algorithm, the NULL pointer dereference is actually caused by a longstanding race condition where crypto_remove_spawns() can encounter an instance which has had spawn(s) "grabbed" but hasn't yet been registered, resulting in ->cra_users still being NULL. We probably should properly initialize ->cra_users earlier, but that would require updating many templates individually. For now just fix the bug in a simple way that can easily be backported: make crypto_remove_spawns() treat a NULL ->cra_users list as empty. Reported-by: syzbot Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/algapi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crypto/algapi.c b/crypto/algapi.c index 314cc745f2f8..bfa509412edf 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -159,6 +159,18 @@ void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list, spawn->alg = NULL; spawns = &inst->alg.cra_users; + + /* + * We may encounter an unregistered instance here, since + * an instance's spawns are set up prior to the instance + * being registered. An unregistered instance will have + * NULL ->cra_users.next, since ->cra_users isn't + * properly initialized until registration. But an + * unregistered instance cannot have any users, so treat + * it the same as ->cra_users being empty. + */ + if (spawns->next == NULL) + break; } } while ((spawns = crypto_more_spawns(alg, &stack, &top, &secondary_spawns))); -- GitLab From 44cd2c5e9144dc2a072a1aa0d1bf3a13389b79c2 Mon Sep 17 00:00:00 2001 From: Jia Zhang Date: Mon, 1 Jan 2018 10:04:47 +0800 Subject: [PATCH 2336/5498] x86/microcode/intel: Extend BDW late-loading with a revision check commit b94b7373317164402ff7728d10f7023127a02b60 upstream. Instead of blacklisting all model 79 CPUs when attempting a late microcode loading, limit that only to CPUs with microcode revisions < 0x0b000021 because only on those late loading may cause a system hang. For such processors either: a) a BIOS update which might contain a newer microcode revision or b) the early microcode loading method should be considered. Processors with revisions 0x0b000021 or higher will not experience such hangs. For more details, see erratum BDF90 in document #334165 (Intel Xeon Processor E7-8800/4800 v4 Product Family Specification Update) from September 2017. [ bp: Heavily massage commit message and pr_* statements. ] Fixes: 723f2828a98c ("x86/microcode/intel: Disable late loading on model 79") Signed-off-by: Jia Zhang Signed-off-by: Borislav Petkov Signed-off-by: Thomas Gleixner Acked-by: Tony Luck Cc: x86-ml Link: http://lkml.kernel.org/r/1514772287-92959-1-git-send-email-qianyue.zj@alibaba-inc.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/microcode/intel.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index 0dd7bcae3f5b..bd9603279524 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -271,8 +271,17 @@ static bool is_blacklisted(unsigned int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); - if (c->x86 == 6 && c->x86_model == 79) { - pr_err_once("late loading on model 79 is disabled.\n"); + /* + * Late loading on model 79 with microcode revision less than 0x0b000021 + * may result in a system hang. This behavior is documented in item + * BDF90, #334165 (Intel Xeon Processor E7-8800/4800 v4 Product Family). + */ + if (c->x86 == 6 && + c->x86_model == 79 && + c->x86_mask == 0x01 && + c->microcode < 0x0b000021) { + pr_err_once("Erratum BDF90: late loading with revision < 0x0b000021 (0x%x) disabled.\n", c->microcode); + pr_err_once("Please consider either early loading through initrd/built-in or a potential BIOS update.\n"); return true; } -- GitLab From 879eea20fd6aaf8f8a4cb2ece7a7e958e9527896 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 27 Oct 2017 12:32:59 -0700 Subject: [PATCH 2337/5498] iscsi-target: Make TASK_REASSIGN use proper se_cmd->cmd_kref commit ae072726f6109bb1c94841d6fb3a82dde298ea85 upstream. Since commit 59b6986dbf fixed a potential NULL pointer dereference by allocating a se_tmr_req for ISCSI_TM_FUNC_TASK_REASSIGN, the se_tmr_req is currently leaked by iscsit_free_cmd() because no iscsi_cmd->se_cmd.se_tfo was associated. To address this, treat ISCSI_TM_FUNC_TASK_REASSIGN like any other TMR and call transport_init_se_cmd() + target_get_sess_cmd() to setup iscsi_cmd->se_cmd.se_tfo with se_cmd->cmd_kref of 2. This will ensure normal release operation once se_cmd->cmd_kref reaches zero and target_release_cmd_kref() is invoked, se_tmr_req will be released via existing target_free_cmd_mem() and core_tmr_release_req() code. Reported-by: Donald White Cc: Donald White Cc: Mike Christie Cc: Hannes Reinecke Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 9582f082152c..185059773f1b 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1750,7 +1750,6 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct iscsi_tmr_req *tmr_req; struct iscsi_tm *hdr; int out_of_order_cmdsn = 0, ret; - bool sess_ref = false; u8 function, tcm_function = TMR_UNKNOWN; hdr = (struct iscsi_tm *) buf; @@ -1792,19 +1791,17 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, buf); } + transport_init_se_cmd(&cmd->se_cmd, &lio_target_fabric_configfs->tf_ops, + conn->sess->se_sess, 0, DMA_NONE, + MSG_SIMPLE_TAG, cmd->sense_buffer + 2); + + target_get_sess_cmd(&cmd->se_cmd, true); + /* * TASK_REASSIGN for ERL=2 / connection stays inside of * LIO-Target $FABRIC_MOD */ if (function != ISCSI_TM_FUNC_TASK_REASSIGN) { - transport_init_se_cmd(&cmd->se_cmd, - &lio_target_fabric_configfs->tf_ops, - conn->sess->se_sess, 0, DMA_NONE, - MSG_SIMPLE_TAG, cmd->sense_buffer + 2); - - target_get_sess_cmd(&cmd->se_cmd, true); - sess_ref = true; - switch (function) { case ISCSI_TM_FUNC_ABORT_TASK: tcm_function = TMR_ABORT_TASK; @@ -1943,12 +1940,8 @@ attach: * For connection recovery, this is also the default action for * TMR TASK_REASSIGN. */ - if (sess_ref) { - pr_debug("Handle TMR, using sess_ref=true check\n"); - target_put_sess_cmd(&cmd->se_cmd); - } - iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); + target_put_sess_cmd(&cmd->se_cmd); return 0; } EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd); -- GitLab From 7f244e55cb7111479695a50020ec4619b9ef8657 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 27 Oct 2017 22:19:26 -0800 Subject: [PATCH 2338/5498] target: Avoid early CMD_T_PRE_EXECUTE failures during ABORT_TASK commit 1c21a48055a67ceb693e9c2587824a8de60a217c upstream. This patch fixes bug where early se_cmd exceptions that occur before backend execution can result in use-after-free if/when a subsequent ABORT_TASK occurs for the same tag. Since an early se_cmd exception will have had se_cmd added to se_session->sess_cmd_list via target_get_sess_cmd(), it will not have CMD_T_COMPLETE set by the usual target_complete_cmd() backend completion path. This causes a subsequent ABORT_TASK + __target_check_io_state() to signal ABORT_TASK should proceed. As core_tmr_abort_task() executes, it will bring the outstanding se_cmd->cmd_kref count down to zero releasing se_cmd, after se_cmd has already been queued with error status into fabric driver response path code. To address this bug, introduce a CMD_T_PRE_EXECUTE bit that is set at target_get_sess_cmd() time, and cleared immediately before backend driver dispatch in target_execute_cmd() once CMD_T_ACTIVE is set. Then, check CMD_T_PRE_EXECUTE within __target_check_io_state() to determine when an early exception has occured, and avoid aborting this se_cmd since it will have already been queued into fabric driver response path code. Reported-by: Donald White Cc: Donald White Cc: Mike Christie Cc: Hannes Reinecke Signed-off-by: Nicholas Bellinger Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_tmr.c | 9 +++++++++ drivers/target/target_core_transport.c | 2 ++ include/target/target_core_base.h | 1 + 3 files changed, 12 insertions(+) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 9b1a4000d0f9..93724738000a 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -137,6 +137,15 @@ static bool __target_check_io_state(struct se_cmd *se_cmd, spin_unlock(&se_cmd->t_state_lock); return false; } + if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) { + if (se_cmd->scsi_status) { + pr_debug("Attempted to abort io tag: %u early failure" + " status: 0x%02x\n", se_cmd->se_tfo->get_task_tag(se_cmd), + se_cmd->scsi_status); + spin_unlock(&se_cmd->t_state_lock); + return false; + } + } if (sess->sess_tearing_down || se_cmd->cmd_wait_set) { pr_debug("Attempted to abort io tag: %u already shutdown," " skipping\n", se_cmd->se_tfo->get_task_tag(se_cmd)); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index e59c8b3fd4f1..26afe1c74ef4 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1796,6 +1796,7 @@ void target_execute_cmd(struct se_cmd *cmd) } cmd->t_state = TRANSPORT_PROCESSING; + cmd->transport_state &= ~CMD_T_PRE_EXECUTE; cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); /* @@ -2436,6 +2437,7 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref) ret = -ESHUTDOWN; goto out; } + se_cmd->transport_state |= CMD_T_PRE_EXECUTE; list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); out: spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index ab4bff088898..ea5e809825f8 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -538,6 +538,7 @@ struct se_cmd { #define CMD_T_BUSY (1 << 9) #define CMD_T_TAS (1 << 10) #define CMD_T_FABRIC_STOP (1 << 11) +#define CMD_T_PRE_EXECUTE (1 << 12) spinlock_t t_state_lock; struct completion t_transport_stop_comp; -- GitLab From e27472ac7ec96208d485101d3476361a866957fe Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 13 Jan 2018 18:45:25 +0100 Subject: [PATCH 2339/5498] Revert "can: kvaser_usb: free buf in error paths" This reverts commit 70d9dccf50152b0d7bfb2697d8c51e9fab9f782c which was commit 435019b48033138581a6171093b181fc6b4d3d30 upstream. Jimmy Assarsson asks that it be reverted as it's not correct there. Reported-by: Jimmy Assarsson Cc: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/usb/kvaser_usb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 4745164e4325..355914a31591 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -602,7 +602,6 @@ static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv, if (err) { netdev_err(netdev, "Error transmitting URB\n"); usb_unanchor_urb(urb); - kfree(buf); usb_free_urb(urb); kfree(buf); return err; @@ -1389,7 +1388,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, atomic_dec(&priv->active_tx_urbs); usb_unanchor_urb(urb); - kfree(buf); stats->tx_dropped++; -- GitLab From 7c56a67fe75748b541692630fd0a503284cde7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Elio=20Petten=C3=B2?= Date: Fri, 29 Dec 2017 09:54:25 +0000 Subject: [PATCH 2340/5498] USB: serial: cp210x: add IDs for LifeScan OneTouch Verio IQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4307413256ac1e09b8f53e8715af3df9e49beec3 upstream. Add IDs for the OneTouch Verio IQ that comes with an embedded USB-to-serial converter. Signed-off-by: Diego Elio Pettenò Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index c454c2fec2f7..b9c533560384 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -119,6 +119,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ { USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */ + { USB_DEVICE(0x10C4, 0x85A7) }, /* LifeScan OneTouch Verio IQ */ { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ -- GitLab From 8b3193a1f8d1b31bffcabc918c9bcfb32ea1572d Mon Sep 17 00:00:00 2001 From: Christian Holl Date: Wed, 3 Jan 2018 19:53:02 +0100 Subject: [PATCH 2341/5498] USB: serial: cp210x: add new device ID ELV ALC 8xxx commit d14ac576d10f865970bb1324d337e5e24d79aaf4 upstream. This adds the ELV ALC 8xxx Battery Charging device to the list of USB IDs of drivers/usb/serial/cp210x.c Signed-off-by: Christian Holl Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index b9c533560384..e0e112ca07ac 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -169,6 +169,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */ + { USB_DEVICE(0x18EF, 0xE030) }, /* ELV ALC 8xxx Battery Charger */ { USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */ { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */ -- GitLab From a52b2f87f76cfb381613656461a52e0b222bf976 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 11 Jan 2018 14:47:40 +0100 Subject: [PATCH 2342/5498] usb: misc: usb3503: make sure reset is low for at least 100us commit b8626f1dc29d3eee444bfaa92146ec7b291ef41c upstream. When using a GPIO which is high by default, and initialize the driver in USB Hub mode, initialization fails with: [ 111.757794] usb3503 0-0008: SP_ILOCK failed (-5) The reason seems to be that the chip is not properly reset. Probe does initialize reset low, however some lines later the code already set it back high, which is not long enouth. Make sure reset is asserted for at least 100us by inserting a delay after initializing the reset pin during probe. Signed-off-by: Stefan Agner Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index ae7e1206ca54..215d89096c0a 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -291,6 +291,8 @@ static int usb3503_probe(struct usb3503 *hub) if (gpio_is_valid(hub->gpio_reset)) { err = devm_gpio_request_one(dev, hub->gpio_reset, GPIOF_OUT_INIT_LOW, "usb3503 reset"); + /* Datasheet defines a hardware reset to be at least 100us */ + usleep_range(100, 10000); if (err) { dev_err(dev, "unable to request GPIO %d as reset pin (%d)\n", -- GitLab From 0edffa0be83085ae5b123ed945687b8b2434daa4 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Mon, 8 Jan 2018 15:46:41 -0600 Subject: [PATCH 2343/5498] USB: fix usbmon BUG trigger commit 46eb14a6e1585d99c1b9f58d0e7389082a5f466b upstream. Automated tests triggered this by opening usbmon and accessing the mmap while simultaneously resizing the buffers. This bug was with us since 2006, because typically applications only size the buffers once and thus avoid racing. Reported by Kirill A. Shutemov. Reported-by: Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 9a62e89d6dc0..bbec84dd34fb 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1000,7 +1000,9 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg break; case MON_IOCQ_RING_SIZE: + mutex_lock(&rp->fetch_lock); ret = rp->b_size; + mutex_unlock(&rp->fetch_lock); break; case MON_IOCT_RING_SIZE: @@ -1227,12 +1229,16 @@ static int mon_bin_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) unsigned long offset, chunk_idx; struct page *pageptr; + mutex_lock(&rp->fetch_lock); offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rp->b_size) + if (offset >= rp->b_size) { + mutex_unlock(&rp->fetch_lock); return VM_FAULT_SIGBUS; + } chunk_idx = offset / CHUNK_SIZE; pageptr = rp->b_vec[chunk_idx].pg; get_page(pageptr); + mutex_unlock(&rp->fetch_lock); vmf->page = pageptr; return 0; } -- GitLab From f54053fa86082bae970269a63b7d0461a968b460 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Fri, 22 Dec 2017 17:00:06 -0700 Subject: [PATCH 2344/5498] usbip: remove kernel addresses from usb device and urb debug msgs commit e1346fd87c71a1f61de1fe476ec8df1425ac931c upstream. usbip_dump_usb_device() and usbip_dump_urb() print kernel addresses. Remove kernel addresses from usb device and urb debug msgs and improve the message content. Instead of printing parent device and bus addresses, print parent device and bus names. Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/usbip_common.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index e40da7759a0e..9752b93f754e 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -103,7 +103,7 @@ static void usbip_dump_usb_device(struct usb_device *udev) dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)", udev->devnum, udev->devpath, usb_speed_string(udev->speed)); - pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport); + pr_debug("tt hub ttport %d\n", udev->ttport); dev_dbg(dev, " "); for (i = 0; i < 16; i++) @@ -136,12 +136,8 @@ static void usbip_dump_usb_device(struct usb_device *udev) } pr_debug("\n"); - dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus); - - dev_dbg(dev, - "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n", - &udev->descriptor, udev->config, - udev->actconfig, udev->rawdescriptors); + dev_dbg(dev, "parent %s, bus %s\n", dev_name(&udev->parent->dev), + udev->bus->bus_name); dev_dbg(dev, "have_langid %d, string_langid %d\n", udev->have_langid, udev->string_langid); @@ -249,9 +245,6 @@ void usbip_dump_urb(struct urb *urb) dev = &urb->dev->dev; - dev_dbg(dev, " urb :%p\n", urb); - dev_dbg(dev, " dev :%p\n", urb->dev); - usbip_dump_usb_device(urb->dev); dev_dbg(dev, " pipe :%08x ", urb->pipe); @@ -260,11 +253,9 @@ void usbip_dump_urb(struct urb *urb) dev_dbg(dev, " status :%d\n", urb->status); dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags); - dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer); dev_dbg(dev, " transfer_buffer_length:%d\n", urb->transfer_buffer_length); dev_dbg(dev, " actual_length :%d\n", urb->actual_length); - dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet); if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL) usbip_dump_usb_ctrlrequest( @@ -274,8 +265,6 @@ void usbip_dump_urb(struct urb *urb) dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets); dev_dbg(dev, " interval :%d\n", urb->interval); dev_dbg(dev, " error_count :%d\n", urb->error_count); - dev_dbg(dev, " context :%p\n", urb->context); - dev_dbg(dev, " complete :%p\n", urb->complete); } EXPORT_SYMBOL_GPL(usbip_dump_urb); -- GitLab From 6dc42f889217a0a077bc75c6fa5239ade762fff4 Mon Sep 17 00:00:00 2001 From: Viktor Slavkovic Date: Mon, 8 Jan 2018 10:43:03 -0800 Subject: [PATCH 2345/5498] staging: android: ashmem: fix a race condition in ASHMEM_SET_SIZE ioctl commit 443064cb0b1fb4569fe0a71209da7625129fb760 upstream. A lock-unlock is missing in ASHMEM_SET_SIZE ioctl which can result in a race condition when mmap is called. After the !asma->file check, before setting asma->size, asma->file can be set in mmap. That would result in having different asma->size than the mapped memory size. Combined with ASHMEM_UNPIN ioctl and shrinker invocation, this can result in memory corruption. Signed-off-by: Viktor Slavkovic Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/ashmem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 658d640022be..8b0211d7d7a6 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -759,10 +759,12 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case ASHMEM_SET_SIZE: ret = -EINVAL; + mutex_lock(&ashmem_mutex); if (!asma->file) { ret = 0; asma->size = (size_t) arg; } + mutex_unlock(&ashmem_mutex); break; case ASHMEM_GET_SIZE: ret = asma->size; -- GitLab From e1ed1d1e0af521d54957953de2c7276b9cb24033 Mon Sep 17 00:00:00 2001 From: Ben Seri Date: Fri, 8 Dec 2017 15:14:47 +0100 Subject: [PATCH 2346/5498] Bluetooth: Prevent stack info leak from the EFS element. commit 06e7e776ca4d36547e503279aeff996cbb292c16 upstream. In the function l2cap_parse_conf_rsp and in the function l2cap_parse_conf_req the following variable is declared without initialization: struct l2cap_conf_efs efs; In addition, when parsing input configuration parameters in both of these functions, the switch case for handling EFS elements may skip the memcpy call that will write to the efs variable: ... case L2CAP_CONF_EFS: if (olen == sizeof(efs)) memcpy(&efs, (void *)val, olen); ... The olen in the above if is attacker controlled, and regardless of that if, in both of these functions the efs variable would eventually be added to the outgoing configuration request that is being built: l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); So by sending a configuration request, or response, that contains an L2CAP_CONF_EFS element, but with an element length that is not sizeof(efs) - the memcpy to the uninitialized efs variable can be avoided, and the uninitialized variable would be returned to the attacker (16 bytes). This issue has been assigned CVE-2017-1000410 Cc: Marcel Holtmann Cc: Gustavo Padovan Cc: Johan Hedberg Signed-off-by: Ben Seri Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap_core.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 238b3b93a66a..0ae6e32ffc17 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3317,9 +3317,10 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data break; case L2CAP_CONF_EFS: - remote_efs = 1; - if (olen == sizeof(efs)) + if (olen == sizeof(efs)) { + remote_efs = 1; memcpy(&efs, (void *) val, olen); + } break; case L2CAP_CONF_EWS: @@ -3538,16 +3539,17 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, break; case L2CAP_CONF_EFS: - if (olen == sizeof(efs)) + if (olen == sizeof(efs)) { memcpy(&efs, (void *)val, olen); - if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) - return -ECONNREFUSED; + if (chan->local_stype != L2CAP_SERV_NOTRAFIC && + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) + return -ECONNREFUSED; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), - (unsigned long) &efs, endptr - ptr); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), + (unsigned long) &efs, endptr - ptr); + } break; case L2CAP_CONF_FCS: -- GitLab From 7c07fbcb384f8e5ee682a66477c2f8423e77260f Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 6 Jan 2018 00:56:44 +0800 Subject: [PATCH 2347/5498] uas: ignore UAS for Norelsys NS1068(X) chips commit 928afc85270753657b5543e052cc270c279a3fe9 upstream. The UAS mode of Norelsys NS1068(X) is reported to fail to work on several platforms with the following error message: xhci-hcd xhci-hcd.0.auto: ERROR Transfer event for unknown stream ring slot 1 ep 8 xhci-hcd xhci-hcd.0.auto: @00000000bf04a400 00000000 00000000 1b000000 01098001 And when trying to mount a partition on the disk the disk will disconnect from the USB controller, then after re-connecting the device will be offlined and not working at all. Falling back to USB mass storage can solve this problem, so ignore UAS function of this chip. Signed-off-by: Icenowy Zheng Acked-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_uas.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 9299c0779999..baf671aef9d0 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -153,6 +153,13 @@ UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: Icenowy Zheng */ +UNUSUAL_DEV(0x2537, 0x1068, 0x0000, 0x9999, + "Norelsys", + "NS1068X", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + /* Reported-by: Takeo Nakayama */ UNUSUAL_DEV(0x357d, 0x7788, 0x0000, 0x9999, "JMicron", -- GitLab From 55492dc6122f42a057fef1641e48c7ac489fd4e0 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Mon, 11 Dec 2017 16:26:40 +0900 Subject: [PATCH 2348/5498] e1000e: Fix e1000_check_for_copper_link_ich8lan return value. commit 4110e02eb45ea447ec6f5459c9934de0a273fb91 upstream. e1000e_check_for_copper_link() and e1000_check_for_copper_link_ich8lan() are the two functions that may be assigned to mac.ops.check_for_link when phy.media_type == e1000_media_type_copper. Commit 19110cfbb34d ("e1000e: Separate signaling for link check/link up") changed the meaning of the return value of check_for_link for copper media but only adjusted the first function. This patch adjusts the second function likewise. Reported-by: Christian Hesse Reported-by: Gabriel C Link: https://bugzilla.kernel.org/show_bug.cgi?id=198047 Fixes: 19110cfbb34d ("e1000e: Separate signaling for link check/link up") Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Tested-by: Christian Hesse Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/e1000e/ich8lan.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index feb618468d15..227f83371e84 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -1299,6 +1299,9 @@ out: * Checks to see of the link status of the hardware has changed. If a * change in link status has been detected, then we read the PHY registers * to get the current speed/duplex if link exists. + * + * Returns a negative error code (-E1000_ERR_*) or 0 (link down) or 1 (link + * up). **/ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) { @@ -1313,7 +1316,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) * Change or Rx Sequence Error interrupt. */ if (!mac->get_link_status) - return 0; + return 1; /* First we want to see if the MII Status Register reports * link. If so, then we want to get the current speed/duplex @@ -1452,10 +1455,12 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) * different link partner. */ ret_val = e1000e_config_fc_after_link_up(hw); - if (ret_val) + if (ret_val) { e_dbg("Error configuring flow control\n"); + return ret_val; + } - return ret_val; + return 1; } static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter) -- GitLab From a5d35deca214e095bf9d1745aa6c00dd7ced0517 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 17 Jan 2018 09:29:32 +0100 Subject: [PATCH 2349/5498] Linux 3.18.92 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d114d0641a7e..d2e18e2dc1fb 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 91 +SUBLEVEL = 92 EXTRAVERSION = NAME = Diseased Newt -- GitLab From db9a92f0cd67c6c1683f4257ab3174d1d434aa89 Mon Sep 17 00:00:00 2001 From: Haibin Liu Date: Tue, 5 Dec 2017 15:06:18 +0800 Subject: [PATCH 2350/5498] msm: sensor: actuator: add null pointer check for i2c array Issue: i2c_reg_tbl may be null under error condition when set param. then, other actuator function still may use the i2c_reg_tbl as null. Fix: 1) the assignment total_steps follow on kmalloc buffer. 2) Add NULL pointer check for i2c tbl. CRs-Fixed: 2152401 Change-Id: Ieec3d88e6dae0177787da0906f53d59ac4f5a624 Signed-off-by: Haibin Liu --- .../camera_v2/sensor/actuator/msm_actuator.c | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 1f55d389d43e..7f1c7ebfb55d 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -56,6 +56,11 @@ static int32_t msm_actuator_piezo_set_default_focus( struct msm_camera_i2c_reg_setting reg_setting; CDBG("Enter\n"); + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return -EFAULT; + } + if (a_ctrl->curr_step_pos != 0) { a_ctrl->i2c_tbl_index = 0; a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, @@ -533,6 +538,11 @@ static int32_t msm_actuator_piezo_move_focus( return -EFAULT; } + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return -EFAULT; + } + if (dest_step_position > a_ctrl->total_steps) { pr_err("Step pos greater than total steps = %d\n", dest_step_position); @@ -590,6 +600,10 @@ static int32_t msm_actuator_move_focus( pr_err("Invalid direction = %d\n", dir); return -EFAULT; } + if (a_ctrl->i2c_reg_tbl == NULL) { + pr_err("failed. i2c reg tabl is NULL"); + return -EFAULT; + } if (dest_step_pos > a_ctrl->total_steps) { pr_err("Step pos greater than total steps = %d\n", dest_step_pos); @@ -1169,7 +1183,8 @@ static int32_t msm_actuator_set_position( } if (!a_ctrl || !a_ctrl->func_tbl || - !a_ctrl->func_tbl->actuator_parse_i2c_params) { + !a_ctrl->func_tbl->actuator_parse_i2c_params || + !a_ctrl->i2c_reg_tbl) { pr_err("failed. NULL actuator pointers."); return -EFAULT; } @@ -1279,12 +1294,10 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, a_ctrl->region_size = set_info->af_tuning_params.region_size; a_ctrl->pwd_step = set_info->af_tuning_params.pwd_step; - a_ctrl->total_steps = set_info->af_tuning_params.total_steps; if (copy_from_user(&a_ctrl->region_params, (void *)set_info->af_tuning_params.region_params, a_ctrl->region_size * sizeof(struct region_params_t))) { - a_ctrl->total_steps = 0; pr_err("Error copying region_params\n"); return -EFAULT; } @@ -1317,6 +1330,7 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, (a_ctrl->i2c_reg_tbl != NULL)) { kfree(a_ctrl->i2c_reg_tbl); } + a_ctrl->i2c_reg_tbl = NULL; a_ctrl->i2c_reg_tbl = kmalloc(sizeof(struct msm_camera_i2c_reg_array) * @@ -1326,6 +1340,8 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, return -ENOMEM; } + a_ctrl->total_steps = set_info->af_tuning_params.total_steps; + if (copy_from_user(&a_ctrl->reg_tbl, (void *)set_info->actuator_params.reg_tbl_params, a_ctrl->reg_tbl_size * -- GitLab From cd5826171e34329b73f61fd623b2e161f5d25407 Mon Sep 17 00:00:00 2001 From: Tim Jiang Date: Thu, 30 Nov 2017 03:33:38 -0500 Subject: [PATCH 2351/5498] bluetooth: Add QCA9379 power control support To support QCA9379 chipset. Change-Id: If4bf4fc33c065da3ec86fa4ee66446051580ae90 Signed-off-by: Tim Jiang --- .../devicetree/bindings/bluetooth/bluetooth_power.txt | 1 + drivers/bluetooth/bluetooth-power.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt b/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt index 7b89497f4638..49233d46a877 100644 --- a/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt +++ b/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt @@ -6,6 +6,7 @@ Required properties: - compatible: Should be set to one of the following: qca,ar3002 qca,qca6174 + qca,qca9379 qca,wcn3990 - qca,bt-reset-gpio: GPIO pin to bring BT Controller out of reset diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c index 3f2d53fa5a90..1fb3c61cea75 100644 --- a/drivers/bluetooth/bluetooth-power.c +++ b/drivers/bluetooth/bluetooth-power.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2010, 2013-2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2010, 2013-2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -36,6 +36,7 @@ static struct of_device_id bt_power_match_table[] = { { .compatible = "qca,ar3002" }, { .compatible = "qca,qca6174" }, + { .compatible = "qca,qca9379" }, { .compatible = "qca,wcn3990" }, {} }; -- GitLab From eca542a9235066a412c212b7f56fb86498dc4a0e Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 15 Jan 2018 10:27:14 +0800 Subject: [PATCH 2352/5498] power: qpnp-smbcharger: Report charger types in REAL_TYPE property Use POWER_SUPPLY_PROP_REAL_TYPE property to report charger types between charger and USB drivers. Update usb_psy POWER_SUPPLY_PROP_TYPE property to POWER_SUPPLY_TYPE_USB_DCP if it is HVDCP/HVDCP3 type and ignore reporting POWER_SUPPLY_TYPE_UNKNOWN. This helps the battery HAL to recongize HVDCP/HVDCP3 as AC chargers. Change-Id: Id0116608270dd8c608136d5dede5a09f3538e128 Signed-off-by: Fenglin Wu --- drivers/power/qpnp-smbcharger.c | 13 +++++++---- drivers/usb/dwc3/dwc3-msm.c | 34 ++++++++++++++++++++++------ drivers/usb/phy/phy-msm-usb.c | 39 +++++++++++++++++++++++++-------- include/linux/usb/msm_hsusb.h | 3 ++- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/drivers/power/qpnp-smbcharger.c b/drivers/power/qpnp-smbcharger.c index 328877a87951..696f2e332090 100644 --- a/drivers/power/qpnp-smbcharger.c +++ b/drivers/power/qpnp-smbcharger.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -4653,6 +4653,7 @@ static int smbchg_change_usb_supply_type(struct smbchg_chip *chip, enum power_supply_type type) { int rc, current_limit_ma; + union power_supply_propval propval; /* * if the type is not unknown, set the type before changing ICL vote @@ -4691,8 +4692,12 @@ static int smbchg_change_usb_supply_type(struct smbchg_chip *chip, goto out; } - if (!chip->skip_usb_notification) - power_supply_set_supply_type(chip->usb_psy, type); + if (!chip->skip_usb_notification) { + propval.intval = type; + chip->usb_psy->set_property(chip->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, + &propval); + } /* * otherwise if it is unknown, remove vote @@ -6064,7 +6069,7 @@ static void smbchg_external_power_changed(struct power_supply *psy) current_limit = prop.intval / 1000; rc = chip->usb_psy->get_property(chip->usb_psy, - POWER_SUPPLY_PROP_TYPE, &prop); + POWER_SUPPLY_PROP_REAL_TYPE, &prop); read_usb_type(chip, &usb_type_name, &usb_supply_type); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 3b553decbf43..de2a90887e74 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -238,6 +238,7 @@ struct dwc3_msm { u32 bus_perf_client; struct msm_bus_scale_pdata *bus_scale_table; struct power_supply usb_psy; + enum power_supply_type usb_supply_type; unsigned int online; bool in_host_mode; unsigned int voltage_max; @@ -2511,6 +2512,9 @@ static int dwc3_msm_power_get_property_usb(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: val->intval = mdwc->online; break; + case POWER_SUPPLY_PROP_REAL_TYPE: + val->intval = mdwc->usb_supply_type; + break; case POWER_SUPPLY_PROP_TYPE: val->intval = psy->type; break; @@ -2619,10 +2623,22 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy, mdwc->bc1p2_current_max); } break; - case POWER_SUPPLY_PROP_TYPE: - psy->type = val->intval; - - switch (psy->type) { + case POWER_SUPPLY_PROP_REAL_TYPE: + mdwc->usb_supply_type = val->intval; + /* + * Update TYPE property to DCP for HVDCP/HVDCP3 charger types + * so that they can be recongized as AC chargers by healthd. + * Don't report UNKNOWN charger type to prevent healthd missing + * detecting this power_supply status change. + */ + if (mdwc->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 + || mdwc->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP) + psy->type = POWER_SUPPLY_TYPE_USB_DCP; + else if (mdwc->usb_supply_type == POWER_SUPPLY_TYPE_UNKNOWN) + psy->type = POWER_SUPPLY_TYPE_USB; + else + psy->type = mdwc->usb_supply_type; + switch (mdwc->usb_supply_type) { case POWER_SUPPLY_TYPE_USB: mdwc->chg_type = DWC3_SDP_CHARGER; mdwc->voltage_max = MICRO_5V; @@ -2674,7 +2690,7 @@ dwc3_msm_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_VOLTAGE_MAX: case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: - case POWER_SUPPLY_PROP_TYPE: + case POWER_SUPPLY_PROP_REAL_TYPE: return 1; default: break; @@ -2698,6 +2714,7 @@ static enum power_supply_property dwc3_msm_pm_power_props_usb[] = { POWER_SUPPLY_PROP_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_USB_OTG, + POWER_SUPPLY_PROP_REAL_TYPE, }; static irqreturn_t dwc3_pmic_id_irq(int irq, void *data) @@ -3754,6 +3771,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) { enum power_supply_type power_supply_type; + union power_supply_propval propval; if (mdwc->charging_disabled) return 0; @@ -3778,7 +3796,9 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) else power_supply_type = POWER_SUPPLY_TYPE_UNKNOWN; - power_supply_set_supply_type(&mdwc->usb_psy, power_supply_type); + propval.intval = power_supply_type; + mdwc->usb_psy.set_property(&mdwc->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &propval); skip_psy_type: diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index ab1a1029b738..a5273f020d86 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2017, Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2018, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1718,7 +1718,7 @@ static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode) static int msm_otg_notify_chg_type(struct msm_otg *motg) { static int charger_type; - + union power_supply_propval propval; /* * TODO * Unify OTG driver charger types and power supply charger types @@ -1745,7 +1745,10 @@ static int msm_otg_notify_chg_type(struct msm_otg *motg) pr_debug("setting usb power supply type %d\n", charger_type); msm_otg_dbg_log_event(&motg->phy, "SET USB PWR SUPPLY TYPE", motg->chg_type, charger_type); - power_supply_set_supply_type(psy, charger_type); + + propval.intval = charger_type; + psy->set_property(psy, POWER_SUPPLY_PROP_REAL_TYPE, &propval); + return 0; } @@ -3530,6 +3533,9 @@ static int otg_power_get_property_usb(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: val->intval = motg->online; break; + case POWER_SUPPLY_PROP_REAL_TYPE: + val->intval = motg->usb_supply_type; + break; case POWER_SUPPLY_PROP_TYPE: val->intval = psy->type; break; @@ -3555,7 +3561,8 @@ static int otg_power_set_property_usb(struct power_supply *psy, struct msm_otg *motg = container_of(psy, struct msm_otg, usb_psy); struct msm_otg_platform_data *pdata = motg->pdata; - msm_otg_dbg_log_event(&motg->phy, "SET PWR PROPERTY", psp, psy->type); + msm_otg_dbg_log_event(&motg->phy, "SET PWR PROPERTY", + psp, motg->usb_supply_type); switch (psp) { case POWER_SUPPLY_PROP_USB_OTG: motg->id_state = val->intval ? USB_ID_GROUND : USB_ID_FLOAT; @@ -3599,9 +3606,21 @@ static int otg_power_set_property_usb(struct power_supply *psy, msm_otg_notify_charger(motg, motg->bc1p2_current_max); } break; - case POWER_SUPPLY_PROP_TYPE: - psy->type = val->intval; - + case POWER_SUPPLY_PROP_REAL_TYPE: + motg->usb_supply_type = val->intval; + /* + * Update TYPE property to DCP for HVDCP/HVDCP3 charger types + * so that they can be recongized as AC chargers by healthd. + * Don't report UNKNOWN charger type to prevent healthd missing + * detecting this power_supply status change. + */ + if (motg->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 + || motg->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP) + psy->type = POWER_SUPPLY_TYPE_USB_DCP; + else if (motg->usb_supply_type == POWER_SUPPLY_TYPE_UNKNOWN) + psy->type = POWER_SUPPLY_TYPE_USB; + else + psy->type = motg->usb_supply_type; /* * If charger detection is done by the USB driver, * motg->chg_type is already assigned in the @@ -3616,7 +3635,7 @@ static int otg_power_set_property_usb(struct power_supply *psy, if (motg->chg_state == USB_CHG_STATE_DETECTED) break; - switch (psy->type) { + switch (motg->usb_supply_type) { case POWER_SUPPLY_TYPE_USB: motg->chg_type = USB_SDP_CHARGER; motg->voltage_max = MICRO_5V; @@ -3655,7 +3674,7 @@ static int otg_power_set_property_usb(struct power_supply *psy, dev_dbg(motg->phy.dev, "%s: charger type = %s\n", __func__, chg_to_string(motg->chg_type)); msm_otg_dbg_log_event(&motg->phy, "SET CHARGER TYPE ", - motg->chg_type, psy->type); + motg->chg_type, motg->usb_supply_type); break; case POWER_SUPPLY_PROP_HEALTH: if (val->intval > POWER_SUPPLY_HEALTH_HOT) @@ -3683,6 +3702,7 @@ static int otg_power_property_is_writeable_usb(struct power_supply *psy, case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: case POWER_SUPPLY_PROP_USB_OTG: + case POWER_SUPPLY_PROP_REAL_TYPE: return 1; default: break; @@ -3707,6 +3727,7 @@ static enum power_supply_property otg_pm_power_props_usb[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_DP_DM, POWER_SUPPLY_PROP_USB_OTG, + POWER_SUPPLY_PROP_REAL_TYPE, }; const struct file_operations msm_otg_bus_fops = { diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 049f312603ad..7414d3984561 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -2,7 +2,7 @@ * * Copyright (C) 2008 Google, Inc. * Author: Brian Swetland - * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -523,6 +523,7 @@ struct msm_otg { #define PHY_REGULATORS_LPM BIT(4) int reset_counter; struct power_supply usb_psy; + enum power_supply_type usb_supply_type; unsigned int online; unsigned int host_mode; unsigned int voltage_max; -- GitLab From 6fa17ec14a5308553bdfcd338e19b168599152e6 Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Tue, 16 Jan 2018 18:07:09 +0530 Subject: [PATCH 2353/5498] ARM: dts: msm: tasha codec bringup for apq8009 excel board Set codec reset gpio pin and regulator for codec bringup on excel board. CRs-Fixed: 2164870 Change-Id: I216d861077118c8adfa86f3fdc794759c87c0002 Signed-off-by: Surendar karka --- ...pq8009-mtp-wcd9326-excelpoint-refboard.dts | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts index e032cdc389ba..aad63d5e567b 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -32,6 +32,8 @@ &soc { sound-9335 { + qcom,model = "apq8009-tashalite-snd-card-tdm"; + qcom,tdm-i2s-switch-enable = <&msm_gpio 88 0>; qcom,audio-routing = "AIF4 VI", "MCLK", "RX_BIAS", "MCLK", @@ -52,7 +54,16 @@ i2c@78b8000 { wcd9xxx_codec@d { - qcom,cdc-reset-gpio = <&msm_gpio 27 0>; + qcom,wcd9xxx-mic-tristate; + qcom,cdc-reset-gpio = <&msm_gpio 23 0>; + + cdc-vdd-buck-supply = <&wcd_vdd_buck_1p8>; + qcom,cdc-vdd-buck-voltage = <0 0>; + qcom,cdc-vdd-buck-current = <250000>; + + cdc-buck-sido-supply = <&wcd_vdd_buck_1p8>; + qcom,cdc-buck-sido-voltage = <0 0>; + qcom,cdc-buck-sido-current = <250000>; }; }; @@ -61,6 +72,17 @@ status = "disabled"; }; }; + + wcd_vdd_buck_1p8: wcd_vdd_buck_1p8 { + compatible = "regulator-fixed"; + regulator-name = "wcd_vdd_buck_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + status = "ok"; + enable-active-high; + gpio = <&pm8916_gpios 4 0>; + vin-supply = <&pm8916_s4>; + }; }; &i2c_4 { @@ -82,6 +104,17 @@ }; }; +&pm8916_gpios { + gpio@c300 { + qcom,mode = <1>; /* DIGITAL OUT */ + qcom,pull = <1>; /* No Pull */ + qcom,vin-sel = <2>; /* 1.8 */ + qcom,src-sel = <0>; /* CONSTANT */ + qcom,master-en = <1>; /* ENABLE GPIO */ + status = "okay"; + }; +}; + &pm8916_chg { status = "ok"; qcom,use-external-charger; -- GitLab From 8f693f99300d5a8b765b08fd572805adae0192ee Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 11 Jan 2018 18:38:08 +0530 Subject: [PATCH 2354/5498] soc: qcom: bgdaemon: Add support for BG soft reset on Modem down When Modem goes down, to re-establish the connections, BG-Daemon toggles the bg-reset gpio to reset BG. Change-Id: Idb319fb157e04c78a7db207fa450e5930c1e6dd8 Signed-off-by: Arjun Singh --- .../bindings/soc/qcom/bg_daemon.txt | 14 ++++ drivers/soc/qcom/bg_rsb.c | 8 ++- drivers/soc/qcom/bgcom_interface.c | 68 +++++++++++++++++++ include/uapi/linux/bgcom_interface.h | 4 ++ 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt diff --git a/Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt b/Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt new file mode 100644 index 000000000000..149a01ad7365 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt @@ -0,0 +1,14 @@ +Qualcomm Technologies Inc. bg-daemon + +BG-Daemon : When Modem goes down, to re-establish the connections, +BG-Daemon toggles the bg-reset gpio to reset BG. + +Required properties: +- compatible : should be "qcom,bg-daemon" +- qcom,bg-reset-gpio : gpio for the apps processor use to soft reset BG + +Example: + qcom,bg-daemon { + compatible = "qcom,bg-daemon"; + qcom,bg-reset-gpio = <&pm660_gpios 5 0>; + }; diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index 46ca407b24fc..bd9a6bc251a6 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -544,8 +544,6 @@ static void bgrsb_disable_rsb(struct work_struct *work) rsb_down_work); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { - if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) - return; req.cmd_id = 0x02; req.data = 0x00; @@ -555,6 +553,10 @@ static void bgrsb_disable_rsb(struct work_struct *work) pr_err("Failed to send disable command to BG\n"); return; } + + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) + return; + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Disabled\n"); } diff --git a/drivers/soc/qcom/bgcom_interface.c b/drivers/soc/qcom/bgcom_interface.c index 5acab00f3c84..c3ed3f08176a 100644 --- a/drivers/soc/qcom/bgcom_interface.c +++ b/drivers/soc/qcom/bgcom_interface.c @@ -27,6 +27,11 @@ #include #include "bgcom.h" #include "linux/bgcom_interface.h" +#include +#include +#include +#include +#include #define BGCOM "bg_com_dev" @@ -52,6 +57,8 @@ static char *ssr_domains[] = { "modem", }; +static unsigned bgreset_gpio; + static DEFINE_MUTEX(bg_char_mutex); static struct cdev bg_cdev; static struct class *bg_class; @@ -169,6 +176,15 @@ static int bgchar_write_cmd(struct bg_ui_data *fui_obj_msg, int type) return ret; } +static int bg_soft_reset(void) +{ + /*pull down reset gpio */ + gpio_direction_output(bgreset_gpio, 0); + msleep(50); + gpio_set_value(bgreset_gpio, 1); + return 0; +} + static long bg_com_ioctl(struct file *filp, unsigned int ui_bgcom_cmd, unsigned long arg) { @@ -205,6 +221,9 @@ static long bg_com_ioctl(struct file *filp, case SET_SPI_BUSY: ret = bgcom_set_spi_state(BGCOM_SPI_BUSY); break; + case BG_SOFT_RESET: + ret = bg_soft_reset(); + break; default: ret = -ENOIOCTLCMD; } @@ -222,6 +241,50 @@ static int bgcom_char_close(struct inode *inode, struct file *file) return ret; } +static int bg_daemon_probe(struct platform_device *pdev) +{ + struct device_node *node; + unsigned reset_gpio; + + node = pdev->dev.of_node; + + reset_gpio = of_get_named_gpio(node, "qcom,bg-reset-gpio", 0); + if (!gpio_is_valid(reset_gpio)) { + pr_err("gpio %d found is not valid\n", reset_gpio); + goto err_ret; + } + + if (gpio_request(reset_gpio, "bg_reset_gpio")) { + pr_err("gpio %d request failed\n", reset_gpio); + goto err_ret; + } + + if (gpio_direction_output(reset_gpio, 1)) { + pr_err("gpio %d direction not set\n", reset_gpio); + goto err_ret; + } + + pr_info("bg-soft-reset gpio successfully requested\n"); + bgreset_gpio = reset_gpio; + +err_ret: + return 0; +} + +static const struct of_device_id bg_daemon_of_match[] = { + { .compatible = "qcom,bg-daemon", }, + { } +}; +MODULE_DEVICE_TABLE(of, bg_daemon_of_match); + +static struct platform_driver bg_daemon_driver = { + .probe = bg_daemon_probe, + .driver = { + .name = "bg-daemon", + .of_match_table = bg_daemon_of_match, + }, +}; + static const struct file_operations fops = { .owner = THIS_MODULE, .open = bgcom_char_open, @@ -261,6 +324,10 @@ static int __init init_bg_com_dev(void) pr_err("device create failed\n"); return PTR_ERR(dev_ret); } + + if (platform_driver_register(&bg_daemon_driver)) + pr_err("%s: failed to register bg-daemon register\n", __func__); + return 0; } @@ -270,6 +337,7 @@ static void __exit exit_bg_com_dev(void) class_destroy(bg_class); cdev_del(&bg_cdev); unregister_chrdev_region(bg_dev, 1); + platform_driver_unregister(&bg_daemon_driver); } /** diff --git a/include/uapi/linux/bgcom_interface.h b/include/uapi/linux/bgcom_interface.h index d6b16082fedd..ea1d453d2c18 100644 --- a/include/uapi/linux/bgcom_interface.h +++ b/include/uapi/linux/bgcom_interface.h @@ -6,6 +6,7 @@ #define BGCOM_SET_SPI_FREE 3 #define BGCOM_SET_SPI_BUSY 4 #define BGCOM_REG_WRITE 5 +#define BGCOM_SOFT_RESET 6 #define EXCHANGE_CODE 'V' struct bg_ui_data { @@ -41,4 +42,7 @@ enum bg_event_type { #define REG_WRITE \ _IOWR(EXCHANGE_CODE, BGCOM_REG_WRITE, \ struct bg_ui_data) +#define BG_SOFT_RESET \ + _IOWR(EXCHANGE_CODE, BGCOM_SOFT_RESET, \ + struct bg_ui_data) #endif -- GitLab From a3b181f7881e43eb3b2637eb0d256df640b40c35 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Fri, 29 Dec 2017 10:25:42 +0800 Subject: [PATCH 2355/5498] ASoc: codecs: Add digital codec driver for msm8909w MSM8909w wtp device has digital codec only. Current codec driver has both analog and digital codec driver mixed. Add digital codec driver for msm8909w wtp device. Change-Id: I392971e35f97b55fb34a68a89031beedc821393c Signed-off-by: Meng Wang --- .../bindings/sound/qcom-audio-dev.txt | 27 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/msm-digital-cdc-registers.h | 131 ++ sound/soc/codecs/msm-digital-cdc-regmap.c | 390 +++ sound/soc/codecs/msm-digital-cdc.c | 2094 +++++++++++++++++ sound/soc/codecs/msm-digital-cdc.h | 172 ++ 7 files changed, 2820 insertions(+) create mode 100644 sound/soc/codecs/msm-digital-cdc-registers.h create mode 100644 sound/soc/codecs/msm-digital-cdc-regmap.c create mode 100644 sound/soc/codecs/msm-digital-cdc.c create mode 100644 sound/soc/codecs/msm-digital-cdc.h diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 01277a7454c6..fa8c3c1d6e74 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2785,3 +2785,30 @@ Example: qcom,msm-glink-channels = <4>; }; }; + +* Digital Codec Driver. + +Required properties: +- compatible : "qcom,msm-digital-codec" +- reg : This property provides the digital codec register address. +- cdc-vdd-digit-supply : Power supply that controls dmic and external + speaker. +- qcom,cdc-vdd-digit-voltage : dmic and external speaker supply's voltage + level in mV +- qcom,cdc-vdd-digit-current : dmic and external speaker max current level + in mA +- qcom,cdc-on-demand-supplies : List of supplies which can be enabled + dynamically. Supplies in this list are off by default. + + +Example: + + msm_digital_codec: msm-dig-codec@771c000 { + compatible = "qcom,msm-digital-codec"; + reg = <0x0771c000 0x0>; + + cdc-vdd-digital-supply = <&pm660_l11>; + qcom,cdc-vdd-digital-voltage = <1800000 1800000>; + qcom,cdc-vdd-digital-current = <5000>; + qcom,cdc-on-demand-supplies = "cdc-vdd-digital"; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3dd0ecca92a1..c936371c7f87 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -652,6 +652,10 @@ config SND_SOC_WSA881X_ANALOG config SND_SOC_MSM8X16_WCD tristate +config SND_SOC_DIGITAL_CDC + tristate + select REGMAP_MMIO + config SND_SOC_WCD9XXX tristate default y if SND_SOC_WCD9320=y || SND_SOC_WCD9330=y || SND_SOC_WCD9335=y diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 1836dde6403c..39e419308fb9 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -126,6 +126,7 @@ snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o snd-soc-wsa881x-sensor-objs := wsa881x-temp-sensor.o snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o snd-soc-msm8952-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o +snd-soc-msm8909-cdc-objs := msm-digital-cdc.o msm-digital-cdc-regmap.o snd-soc-wsa881x-analog-objs := wsa881x-analog.o wsa881x-tables-analog.o snd-soc-wsa881x-analog-objs += wsa881x-regmap-analog.o wsa881x-irq.o snd-soc-wl1273-objs := wl1273.o @@ -315,6 +316,7 @@ obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock.o obj-$(CONFIG_SND_SOC_WCD9XXX) += snd-soc-wcd9xxx.o obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o obj-$(CONFIG_SND_SOC_MSM8X16_WCD) += snd-soc-msm8952-wcd.o msm8916-wcd-irq.o +obj-$(CONFIG_SND_SOC_DIGITAL_CDC) += snd-soc-msm8909-cdc.o msm8916-wcd-irq.o obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o diff --git a/sound/soc/codecs/msm-digital-cdc-registers.h b/sound/soc/codecs/msm-digital-cdc-registers.h new file mode 100644 index 000000000000..2063d09cf4f2 --- /dev/null +++ b/sound/soc/codecs/msm-digital-cdc-registers.h @@ -0,0 +1,131 @@ + /* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef DIGITAL_CDC_REGISTERS_H +#define DIGITAL_CDC_REGISTERS_H + +#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL (0x00) +#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL (0x04) +#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL (0x08) +#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL (0x0C) +#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL (0x10) +#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL (0x14) +#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL (0x18) +#define MSM89XX_CDC_CORE_CLK_OTHR_CTL (0x1C) +#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL (0x20) +#define MSM89XX_CDC_CORE_CLK_MCLK_CTL (0x24) +#define MSM89XX_CDC_CORE_CLK_PDM_CTL (0x28) +#define MSM89XX_CDC_CORE_CLK_SD_CTL (0x2C) +#define MSM89XX_CDC_CORE_RX1_B1_CTL (0x40) +#define MSM89XX_CDC_CORE_RX2_B1_CTL (0x60) +#define MSM89XX_CDC_CORE_RX3_B1_CTL (0x80) +#define MSM89XX_CDC_CORE_RX1_B2_CTL (0x44) +#define MSM89XX_CDC_CORE_RX2_B2_CTL (0x64) +#define MSM89XX_CDC_CORE_RX3_B2_CTL (0x84) +#define MSM89XX_CDC_CORE_RX1_B3_CTL (0x48) +#define MSM89XX_CDC_CORE_RX2_B3_CTL (0x68) +#define MSM89XX_CDC_CORE_RX3_B3_CTL (0x88) +#define MSM89XX_CDC_CORE_RX1_B4_CTL (0x4C) +#define MSM89XX_CDC_CORE_RX2_B4_CTL (0x6C) +#define MSM89XX_CDC_CORE_RX3_B4_CTL (0x8C) +#define MSM89XX_CDC_CORE_RX1_B5_CTL (0x50) +#define MSM89XX_CDC_CORE_RX2_B5_CTL (0x70) +#define MSM89XX_CDC_CORE_RX3_B5_CTL (0x90) +#define MSM89XX_CDC_CORE_RX1_B6_CTL (0x54) +#define MSM89XX_CDC_CORE_RX2_B6_CTL (0x74) +#define MSM89XX_CDC_CORE_RX3_B6_CTL (0x94) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL (0x58) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL (0x78) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL (0x98) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL (0x5C) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL (0x7C) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL (0x9C) +#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE (0xA0) +#define MSM89XX_CDC_CORE_TOP_CTL (0xA4) +#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL (0xE0) +#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL (0xE4) +#define MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG (0xE8) +#define MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG (0xEC) +#define MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG (0xF0) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL (0x100) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL (0x140) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL (0x104) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL (0x144) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL (0x108) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL (0x148) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL (0x10C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL (0x14C) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL (0x110) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL (0x150) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL (0x114) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL (0x154) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL (0x118) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL (0x158) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL (0x11C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL (0x15C) +#define MSM89XX_CDC_CORE_IIR1_CTL (0x120) +#define MSM89XX_CDC_CORE_IIR2_CTL (0x160) +#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL (0x124) +#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL (0x164) +#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL (0x128) +#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL (0x168) +#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL (0x12C) +#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL (0x16C) +#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL (0x180) +#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL (0x184) +#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL (0x188) +#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL (0x18C) +#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL (0x190) +#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL (0x194) +#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL (0x198) +#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL (0x19C) +#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL (0x1A0) +#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL (0x1A8) +#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL (0x1AC) +#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL (0x1B0) +#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL (0x1B4) +#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL (0x1B8) +#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL (0x1BC) +#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL (0x1C0) +#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL (0x1C4) +#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL (0x1C8) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER (0x280) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER (0x2A0) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER (0x2C0) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER (0x2E0) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN (0x284) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN (0x2A4) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN (0x2C4) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN (0x2E4) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG (0x288) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG (0x2A8) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG (0x2C8) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG (0x2E8) +#define MSM89XX_CDC_CORE_TX1_MUX_CTL (0x28C) +#define MSM89XX_CDC_CORE_TX2_MUX_CTL (0x2AC) +#define MSM89XX_CDC_CORE_TX3_MUX_CTL (0x2CC) +#define MSM89XX_CDC_CORE_TX4_MUX_CTL (0x2EC) +#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL (0x290) +#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL (0x2B0) +#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL (0x2D0) +#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL (0x2F0) +#define MSM89XX_CDC_CORE_TX1_DMIC_CTL (0x294) +#define MSM89XX_CDC_CORE_TX2_DMIC_CTL (0x2B4) +#define MSM89XX_CDC_CORE_TX3_DMIC_CTL (0x2D4) +#define MSM89XX_CDC_CORE_TX4_DMIC_CTL (0x2F4) + +#define MSM89XX_CDC_CORE_NUM_REGISTERS \ + (MSM89XX_CDC_CORE_TX4_DMIC_CTL+1) +#define MSM89XX_CDC_CORE_MAX_REGISTER \ + (MSM89XX_CDC_CORE_NUM_REGISTERS-1) +#define MSM89XX_CDC_CORE_CACHE_SIZE \ + MSM89XX_CDC_CORE_NUM_REGISTERS +#endif diff --git a/sound/soc/codecs/msm-digital-cdc-regmap.c b/sound/soc/codecs/msm-digital-cdc-regmap.c new file mode 100644 index 000000000000..466fa972e543 --- /dev/null +++ b/sound/soc/codecs/msm-digital-cdc-regmap.c @@ -0,0 +1,390 @@ +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "msm-digital-cdc.h" +#include "msm-digital-cdc-registers.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ +struct reg_default + msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE] = { + {MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_OTHR_CTL, 0x04}, + {MSM89XX_CDC_CORE_CLK_RX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_SD_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX2_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX3_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX1_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_TOP_GAIN_UPDATE, 0x00}, + {MSM89XX_CDC_CORE_TOP_CTL, 0x01}, + {MSM89XX_CDC_CORE_DEBUG_DESER1_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_DESER2_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR2_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX1_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX2_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX3_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX4_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_DMIC_CTL, 0x00}, +}; + +static const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE] = { + [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1, + [MSM89XX_CDC_CORE_TOP_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1, +}; + +static const u8 msm89xx_cdc_core_reg_writeable[MSM89XX_CDC_CORE_CACHE_SIZE] = { + [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1, + [MSM89XX_CDC_CORE_TOP_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1, +}; + +bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_cdc_core_reg_readable[reg]; +} + +bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_cdc_core_reg_writeable[reg]; +} + +bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MSM89XX_CDC_CORE_RX1_B1_CTL: + case MSM89XX_CDC_CORE_RX2_B1_CTL: + case MSM89XX_CDC_CORE_RX3_B1_CTL: + case MSM89XX_CDC_CORE_RX1_B6_CTL: + case MSM89XX_CDC_CORE_RX2_B6_CTL: + case MSM89XX_CDC_CORE_RX3_B6_CTL: + case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL: + case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL: + case MSM89XX_CDC_CORE_CLK_MCLK_CTL: + case MSM89XX_CDC_CORE_CLK_PDM_CTL: + return true; + default: + return false; + } +} diff --git a/sound/soc/codecs/msm-digital-cdc.c b/sound/soc/codecs/msm-digital-cdc.c new file mode 100644 index 000000000000..25b26136c35a --- /dev/null +++ b/sound/soc/codecs/msm-digital-cdc.c @@ -0,0 +1,2094 @@ +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-digital-cdc-registers.h" +#include "msm-digital-cdc.h" +#include "../../../drivers/base/regmap/internal.h" + +#define DRV_NAME "msm_digital_codec" +#define MCLK_RATE_9P6MHZ 9600000 +#define MCLK_RATE_12P288MHZ 12288000 +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static unsigned long rx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, +}; + +static unsigned long tx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, +}; + +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 +#define CODEC_DT_MAX_PROP_SIZE 40 +#define MSM_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = MSM_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, + S_IRUGO | S_IWUSR | S_IWGRP); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static struct snd_soc_codec *registered_digcodec; +static struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + +static int msm_digit_cdc_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +/* Codec supports 2 IIR filters */ +enum { + IIR1 = 0, + IIR2, + IIR_MAX, +}; + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-digital", +}; + +int msm_digcdc_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm) +{ + dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n", + __func__, mclk_enable, dapm); + if (mclk_enable) { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x01); + } else { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x00); + } + + return 0; +} + +static int msm_digcdc_clock_control(bool flag) +{ + int ret = -EINVAL; + struct msm_asoc_mach_data *pdata = NULL; + struct msm_dig_priv *msm_dig_cdc = + snd_soc_codec_get_drvdata(registered_digcodec); + + pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); + + if (flag) { + mutex_lock(&pdata->cdc_mclk_mutex); + + if (atomic_read(&pdata->mclk_enabled) == false) { + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s:failed to enable the MCLK\n", + __func__); + if (ret == -ENODEV) + msm_dig_cdc->regmap->cache_only = true; + return ret; + } + msm_dig_cdc->regmap->cache_only = false; + pr_debug("enabled digital codec core clk\n"); + atomic_set(&pdata->mclk_enabled, true); + schedule_delayed_work(&pdata->disable_mclk_work, + 50); + } + } else { + mutex_unlock(&pdata->cdc_mclk_mutex); + dev_dbg(registered_digcodec->dev, + "disable MCLK, workq to disable set already\n"); + } + return 0; +} + +static void enable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(true); +} + +static void disable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(false); + pr_debug("disable mclk happens in workq\n"); +} + +static int msm_dig_cdc_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = + dapm_kcontrol_get_wlist(kcontrol); + struct snd_soc_dapm_widget *w = wlist->widgets[0]; + struct snd_soc_codec *codec = w->codec; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int dec_mux, decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + u16 tx_mux_ctl_reg; + u8 adc_dmic_sel = 0x0; + int ret = 0; + char *dec_num; + + if (ucontrol->value.enumerated.item[0] > e->items) { + dev_err(codec->dev, "%s: Invalid enum value: %d\n", + __func__, ucontrol->value.enumerated.item[0]); + return -EINVAL; + } + dec_mux = ucontrol->value.enumerated.item[0]; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) { + dev_err(codec->dev, "%s: failed to copy string\n", + __func__); + return -ENOMEM; + } + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid DEC selected\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_dbg(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n" + , __func__, w->name, decimator, dec_mux); + + switch (decimator) { + case 1: + case 2: + if ((dec_mux == 4) || (dec_mux == 5)) + adc_dmic_sel = 0x1; + else + adc_dmic_sel = 0x0; + break; + default: + dev_err(codec->dev, "%s: Invalid Decimator = %u\n", + __func__, decimator); + ret = -EINVAL; + goto out; + } + + tx_mux_ctl_reg = + MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1); + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel); + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + +out: + kfree(widget_name); + return ret; +} + +static int msm_dig_cdc_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= MSM89XX_RX_MAX) { + dev_err(codec->dev, "%s: wrong RX index: %d\n", + __func__, w->shift); + return -EINVAL; + } + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* apply the digital gain after the interpolator is enabled*/ + if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg)) + snd_soc_write(codec, + rx_digital_gain_reg[w->shift], + snd_soc_read(codec, + rx_digital_gain_reg[w->shift]) + ); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + /* + * disable the mute enabled during the PMD of this device + */ + if ((w->shift == 0) && + (msm_dig_cdc->mute_mask & HPHL_PA_DISABLE)) { + pr_debug("disabling HPHL mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(HPHL_PA_DISABLE); + } else if ((w->shift == 1) && + (msm_dig_cdc->mute_mask & HPHR_PA_DISABLE)) { + pr_debug("disabling HPHR mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(HPHR_PA_DISABLE); + } else if ((w->shift == 2) && + (msm_dig_cdc->mute_mask & SPKR_PA_DISABLE)) { + pr_debug("disabling SPKR mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(SPKR_PA_DISABLE); + } + } + return 0; +} + +static int msm_dig_cdc_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_dig_cdc_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx), + (1 << band_idx), (value << band_idx)); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + ((snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0)); + + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + + 64 * iir_idx)) & 0x3f) << 24); + + return value; + +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 24) & 0x3F); + +} + +static int msm_dig_cdc_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static int msm_dig_cdc_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static void tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct snd_soc_codec *codec; + struct msm_dig_priv *msm_dig_cdc; + u16 tx_mux_ctl_reg; + u8 hpf_cut_of_freq; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + codec = hpf_work->dig_cdc->codec; + msm_dig_cdc = hpf_work->dig_cdc; + hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq; + + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + (hpf_work->decimator - 1) * 32; + + dev_dbg(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq); + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4); +} + +static int msm_dig_cdc_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + int value = 0, reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->shift == 0) + reg = MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL; + else if (w->shift == 1) + reg = MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL; + else + goto ret; + value = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, value); + break; + default: + pr_err("%s: event = %d not expected\n", __func__, event); + } +ret: + return 0; +} + +static int msm_dig_cdc_set_interpolator_rate(struct snd_soc_dai *dai, + u8 rx_fs_rate_reg_val, + u32 sample_rate) +{ + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_RX1_B5_CTL, 0xF0, rx_fs_rate_reg_val); + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_RX2_B5_CTL, 0xF0, rx_fs_rate_reg_val); + return 0; +} + +static int msm_dig_cdc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 tx_fs_rate, rx_fs_rate, rx_clk_fs_rate; + int ret; + + dev_dbg(dai->codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params), params_format(params)); + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0x00; + rx_fs_rate = 0x00; + rx_clk_fs_rate = 0x00; + break; + case 16000: + tx_fs_rate = 0x20; + rx_fs_rate = 0x20; + rx_clk_fs_rate = 0x01; + break; + case 32000: + tx_fs_rate = 0x40; + rx_fs_rate = 0x40; + rx_clk_fs_rate = 0x02; + break; + case 44100: + case 48000: + tx_fs_rate = 0x60; + rx_fs_rate = 0x60; + rx_clk_fs_rate = 0x03; + break; + case 96000: + tx_fs_rate = 0x80; + rx_fs_rate = 0x80; + rx_clk_fs_rate = 0x04; + break; + case 192000: + tx_fs_rate = 0xA0; + rx_fs_rate = 0xA0; + rx_clk_fs_rate = 0x05; + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid sampling rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x0F, rx_clk_fs_rate); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = msm_dig_cdc_set_interpolator_rate(dai, rx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(dai->codec->dev, + "%s: set decimator rate failed %d\n", __func__, + ret); + return ret; + } + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + } + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x00); + break; + default: + dev_err(dai->codec->dev, "%s: wrong format selected\n", + __func__); + return -EINVAL; + } + return 0; +} + +static int msm_dig_cdc_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + u8 dmic_clk_en; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + int ret; + char *dmic_num = strpbrk(w->name, "1234"); + + if (dmic_num == NULL) { + dev_err(codec->dev, "%s: Invalid DMIC\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(dmic_num, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid DMIC line on the codec\n", __func__); + return -EINVAL; + } + + switch (dmic) { + case 1: + case 2: + dmic_clk_en = 0x01; + dmic_clk_cnt = &(dig_cdc->dmic_1_2_clk_cnt); + dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL; + dev_dbg(codec->dev, + "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x0E, 0x04); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL + (dmic - 1) * 0x20, + 0x07, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + return 0; +} + +static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct msm_asoc_mach_data *pdata = NULL; + unsigned int decimator; + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + int ret = 0, i; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + int offset; + char *dec_num; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid Decimator\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, + "%s(): widget = %s dec_name = %s decimator = %u\n", __func__, + w->name, dec_name, decimator); + + if (w->reg == MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL) { + dec_reset_reg = MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL; + offset = 0; + } else { + dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + 32 * (decimator - 1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enableable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm_dig_cdc->dec_active[i] = true; + } + + dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg); + + dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4; + + tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq = + dec_hpf_cut_of_freq; + + if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) { + /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + CF_MIN_3DB_150HZ << 4); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x00); + + schedule_delayed_work( + &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq != + CF_MIN_3DB_150HZ) { + schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork, + msecs_to_jiffies(300)); + } + /* apply the digital gain after the decimator is enabled*/ + if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg)) + snd_soc_write(codec, + tx_digital_gain_reg[w->shift + offset], + snd_soc_read(codec, + tx_digital_gain_reg[w->shift + offset]) + ); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + msleep(20); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork); + cancel_delayed_work_sync( + &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm_dig_cdc->dec_active[i] = false; + } + break; + } +out: + kfree(widget_name); + return ret; +} + +static int msm_dig_cdc_event_notify(struct notifier_block *block, + unsigned long val, + void *data) +{ + enum dig_cdc_notify_event event = (enum dig_cdc_notify_event)val; + struct snd_soc_codec *codec = registered_digcodec; + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + struct msm_asoc_mach_data *pdata = NULL; + int ret = -EINVAL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + switch (event) { + case DIG_CDC_EVENT_CLK_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03); + if (pdata->mclk_freq == MCLK_RATE_12P288MHZ || + pdata->native_clk_set) + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x00); + else if (pdata->mclk_freq == MCLK_RATE_9P6MHZ) + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01); + break; + case DIG_CDC_EVENT_CLK_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x00); + break; + case DIG_CDC_EVENT_RX1_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= HPHL_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX1_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~HPHL_PA_DISABLE); + break; + case DIG_CDC_EVENT_RX2_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= HPHR_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX2_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~HPHR_PA_DISABLE); + break; + case DIG_CDC_EVENT_RX3_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= SPKR_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX3_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~SPKR_PA_DISABLE); + break; + case DIG_CDC_EVENT_PRE_RX1_INT_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x80); + break; + case DIG_CDC_EVENT_PRE_RX2_INT_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x80); + break; + case DIG_CDC_EVENT_POST_RX1_INT_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x00); + break; + case DIG_CDC_EVENT_POST_RX2_INT_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x00); + break; + case DIG_CDC_EVENT_SSR_DOWN: + regcache_cache_only(msm_dig_cdc->regmap, true); + break; + case DIG_CDC_EVENT_SSR_UP: + regcache_cache_only(msm_dig_cdc->regmap, false); + regcache_mark_dirty(msm_dig_cdc->regmap); + + mutex_lock(&pdata->cdc_mclk_mutex); + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s:failed to enable the MCLK\n", + __func__); + mutex_unlock(&pdata->cdc_mclk_mutex); + break; + } + mutex_unlock(&pdata->cdc_mclk_mutex); + + regcache_sync(msm_dig_cdc->regmap); + + mutex_lock(&pdata->cdc_mclk_mutex); + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_core_clk); + mutex_unlock(&pdata->cdc_mclk_mutex); + break; + case DIG_CDC_EVENT_INVALID: + default: + break; + } + return 0; +} + +static void msm_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct snd_soc_codec *codec = NULL; + struct msm_dig_priv *dig_cdc; + struct delayed_work *delayed_work; + u16 tx_vol_ctl_reg = 0; + u8 decimator = 0, i; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + dig_cdc = tx_mute_dwork->dig_cdc; + codec = dig_cdc->codec; + + for (i = 0; i < (NUM_DECIMATORS - 1); i++) { + if (dig_cdc->dec_active[i]) + decimator = i + 1; + if (decimator && decimator < NUM_DECIMATORS) { + /* unmute decimators corresponding to Tx DAI's*/ + tx_vol_ctl_reg = + MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + snd_soc_update_bits(codec, tx_vol_ctl_reg, + 0x01, 0x00); + } + decimator = 0; + } +} + +static void msm_digit_cdc_update_digit_regulator( + const struct msm_dig_priv *msm_cdc, + const char *name, + struct on_demand_supply *digit_supply) +{ + int i; + struct msm_cdc_pdata *pdata = msm_cdc->dev->platform_data; + + for (i = 0; i < msm_cdc->num_of_supplies; i++) { + if (msm_cdc->supplies[i].supply && + !strcmp(msm_cdc->supplies[i].supply, name)) { + digit_supply->supply = + msm_cdc->supplies[i].consumer; + digit_supply->min_uv = pdata->regulator[i].min_uv; + digit_supply->max_uv = pdata->regulator[i].max_uv; + digit_supply->optimum_ua = + pdata->regulator[i].optimum_ua; + return; + } + } + + dev_err(msm_cdc->dev, "Error: regulator not found:%s\n", name); +} + +static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + struct snd_soc_dapm_context *dapm = &codec->dapm; + int i; + + msm_dig_cdc->codec = codec; + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_hpf_work[i].dig_cdc = msm_dig_cdc; + tx_hpf_work[i].decimator = i + 1; + INIT_DELAYED_WORK(&tx_hpf_work[i].dwork, + tx_hpf_corner_freq_callback); + msm_dig_cdc->tx_mute_dwork[i].dig_cdc = msm_dig_cdc; + msm_dig_cdc->tx_mute_dwork[i].decimator = i + 1; + INIT_DELAYED_WORK(&msm_dig_cdc->tx_mute_dwork[i].dwork, + msm_tx_mute_update_callback); + } + + /* Register event notifier */ + msm_dig_cdc->nblock.notifier_call = msm_dig_cdc_event_notify; + + registered_digcodec = codec; + + msm_digit_cdc_update_digit_regulator( + msm_dig_cdc, + on_demand_supply_name[ON_DEMAND_DIGIT], + &msm_dig_cdc->on_demand_list[ON_DEMAND_DIGIT]); + atomic_set(&msm_dig_cdc->on_demand_list[ON_DEMAND_DIGIT].ref, + 0); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "ADC1_IN"); + snd_soc_dapm_ignore_suspend(dapm, "ADC2_IN"); + snd_soc_dapm_ignore_suspend(dapm, "ADC3_IN"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX1"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX2"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX3"); + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int msm_dig_cdc_soc_remove(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + iounmap(msm_dig_cdc->dig_base); + return 0; +} + +static const struct snd_soc_dapm_route audio_dig_map[] = { + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + {"I2S TX3", NULL, "TX_I2S_CLK"}, + {"I2S TX4", NULL, "TX_I2S_CLK"}, + + {"I2S TX1", NULL, "DEC1 MUX"}, + {"I2S TX2", NULL, "DEC2 MUX"}, + {"I2S TX3", NULL, "I2S TX2 INP1"}, + {"I2S TX4", NULL, "I2S TX2 INP2"}, + + {"I2S TX2 INP1", "RX_MIX1", "RX1 MIX2"}, + {"I2S TX2 INP2", "RX_MIX2", "RX2 MIX2"}, + {"I2S TX2 INP2", "RX_MIX3", "RX3 MIX1"}, + + {"PDM_OUT_RX1", NULL, "RX1 CHAIN"}, + {"PDM_OUT_RX2", NULL, "RX2 CHAIN"}, + {"PDM_OUT_RX3", NULL, "RX3 CHAIN"}, + + {"RX1 CHAIN", NULL, "RX1 MIX2"}, + {"RX2 CHAIN", NULL, "RX2 MIX2"}, + {"RX3 CHAIN", NULL, "RX3 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX1 MIX2", NULL, "RX1 MIX1"}, + {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, + {"RX2 MIX2", NULL, "RX2 MIX1"}, + {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP2", "IIR1", "IIR1"}, + {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "ADC1", "ADC1_IN"}, + {"DEC1 MUX", "ADC2", "ADC2_IN"}, + {"DEC1 MUX", "ADC3", "ADC3_IN"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "ADC1", "ADC1_IN"}, + {"DEC2 MUX", "ADC2", "ADC2_IN"}, + {"DEC2 MUX", "ADC3", "ADC3_IN"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR2", NULL, "IIR2 INP1 MUX"}, + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, +}; + + +static const char * const i2s_tx2_inp1_text[] = { + "ZERO", "RX_MIX1" +}; + +static const char * const i2s_tx2_inp2_text[] = { + "ZERO", "RX_MIX2", "RX_MIX3" +}; + +static const char * const rx_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" +}; + +static const char * const rx_mix2_text[] = { + "ZERO", "IIR1", "IIR2" +}; + +static const char * const dec_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2" +}; + +static const char * const iir_inp1_text[] = { + "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" +}; + +/* I2S TX MUXes */ +static const struct soc_enum i2s_tx2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 2, 3, i2s_tx2_inp1_text); + +static const struct soc_enum i2s_tx2_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 0, 4, i2s_tx2_inp2_text); + +/* RX1 MIX1 */ +static const struct soc_enum rx_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, + 0, 6, rx_mix1_text); + +/* RX1 MIX2 */ +static const struct soc_enum rx_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX2 MIX1 */ +static const struct soc_enum rx2_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +/* RX2 MIX2 */ +static const struct soc_enum rx2_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX3 MIX1 */ +static const struct soc_enum rx3_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +/* DEC */ +static const struct soc_enum dec1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 0, 8, dec_mux_text); + +static const struct soc_enum dec2_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 3, 8, dec_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, + 0, 8, iir_inp1_text); + +static const struct soc_enum iir2_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, + 0, 8, iir_inp1_text); + +/*cut of frequency for high pass filter*/ +static const char * const cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" +}; + +static const struct soc_enum cf_rxmix1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX1_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX2_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix3_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX3_B4_CTL, 0, 3, cf_text); + +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum); + +#define MSM89XX_DEC_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = msm_dig_cdc_put_dec_enum, \ + .private_value = (unsigned long)&xenum } + +static const struct snd_kcontrol_new dec1_mux = + MSM89XX_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum); + +static const struct snd_kcontrol_new dec2_mux = + MSM89XX_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum); + +static const struct snd_kcontrol_new i2s_tx2_inp1_mux = + SOC_DAPM_ENUM("I2S TX2 INP1 Mux", i2s_tx2_inp1_chain_enum); + +static const struct snd_kcontrol_new i2s_tx2_inp2_mux = + SOC_DAPM_ENUM("I2S TX2 INP2 Mux", i2s_tx2_inp2_chain_enum); + +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + +static const struct snd_kcontrol_new iir2_inp1_mux = + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); + +static const struct snd_kcontrol_new rx_mix1_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp2_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp3_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp3_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp3_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx1_mix2_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix2_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); + +static const struct snd_soc_dapm_widget msm_dig_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER_E("RX1 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX1, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX2, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 MIX1", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX3, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX1 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX3 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx1_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix2_inp1_mux), + + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM89XX_CDC_CORE_CLK_OTHR_CTL, + 2, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC2 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR1", MSM89XX_CDC_CORE_CLK_SD_CTL, 0, 0, NULL, 0, + msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR2", MSM89XX_CDC_CORE_CLK_SD_CTL, 1, 0, NULL, 0, + msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DIGIT_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_DIGIT, 0, + msm_digit_cdc_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("I2S TX2 INP1", SND_SOC_NOPM, 0, 0, + &i2s_tx2_inp1_mux), + SND_SOC_DAPM_MUX("I2S TX2 INP2", SND_SOC_NOPM, 0, 0, + &i2s_tx2_inp2_mux), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("ADC1_IN"), + SND_SOC_DAPM_INPUT("ADC2_IN"), + SND_SOC_DAPM_INPUT("ADC3_IN"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX1"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX2"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX3"), +}; + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX1_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX2_MUX_CTL, 4, 3, cf_text); + +static const struct snd_kcontrol_new msm_dig_snd_controls[] = { + SOC_SINGLE_SX_TLV("DEC1 Volume", + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", + MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("RX1 Digital Volume", + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + + SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + + SOC_SINGLE("RX1 HPF Switch", + MSM89XX_CDC_CORE_RX1_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX2 HPF Switch", + MSM89XX_CDC_CORE_RX2_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX3 HPF Switch", + MSM89XX_CDC_CORE_RX3_B5_CTL, 2, 1, 0), + + SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum), + SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum), + SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum), + + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_SINGLE("TX1 HPF Switch", + MSM89XX_CDC_CORE_TX1_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX2 HPF Switch", + MSM89XX_CDC_CORE_TX2_MUX_CTL, 3, 1, 0), +}; + +static struct snd_soc_dai_ops msm_dig_dai_ops = { + .hw_params = msm_dig_cdc_hw_params, +}; + + +static struct snd_soc_dai_driver msm_codec_dais[] = { + { + .name = "msm_dig_cdc_dai_rx1", + .id = AIF1_PB, + .playback = { /* Support maximum range */ + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_max = 192000, + .rate_min = 8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .ops = &msm_dig_dai_ops, + }, + { + .name = "msm_dig_cdc_dai_tx1", + .id = AIF1_CAP, + .capture = { /* Support maximum range */ + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_dig_dai_ops, + }, +}; + +static struct regmap *msm_digital_get_regmap(struct device *dev) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev); + + return msm_dig_cdc->regmap; +} + +static int msm_dig_cdc_suspend(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + msm_dig_cdc->dapm_bias_off = 1; + return 0; +} + +static int msm_dig_cdc_resume(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + msm_dig_cdc->dapm_bias_off = 0; + return 0; +} + +static struct snd_soc_codec_driver soc_msm_dig_codec = { + .probe = msm_dig_cdc_soc_probe, + .remove = msm_dig_cdc_soc_remove, + .suspend = msm_dig_cdc_suspend, + .resume = msm_dig_cdc_resume, + .controls = msm_dig_snd_controls, + .num_controls = ARRAY_SIZE(msm_dig_snd_controls), + .dapm_widgets = msm_dig_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_dig_dapm_widgets), + .dapm_routes = audio_dig_map, + .num_dapm_routes = ARRAY_SIZE(audio_dig_map), + .get_regmap = msm_digital_get_regmap, +}; + +static const struct regmap_config msm_digital_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 8, + .lock = enable_digital_callback, + .unlock = disable_digital_callback, + .cache_type = REGCACHE_FLAT, + .reg_defaults = msm89xx_cdc_core_defaults, + .num_reg_defaults = MSM89XX_CDC_CORE_MAX_REGISTER, + .writeable_reg = msm89xx_cdc_core_writeable_reg, + .readable_reg = msm89xx_cdc_core_readable_reg, + .volatile_reg = msm89xx_cdc_core_volatile_reg, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .max_register = MSM89XX_CDC_CORE_MAX_REGISTER, +}; + +static int msm_digit_cdc_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = w->codec; + struct msm_dig_priv *msm_cdc = + snd_soc_codec_get_drvdata(codec); + struct on_demand_supply *supply; + + if (w->shift >= ON_DEMAND_SUPPLIES_MAX) { + dev_err(codec->dev, "%s: error index > MAX Demand supplies", + __func__); + ret = -EINVAL; + goto out; + } + dev_dbg(codec->dev, "%s: supply: %s event: %d ref: %d\n", + __func__, on_demand_supply_name[w->shift], event, + atomic_read(&(msm_cdc->on_demand_list[w->shift].ref))); + + supply = &msm_cdc->on_demand_list[w->shift]; + WARN_ONCE(!supply->supply, "%s isn't defined\n", + on_demand_supply_name[w->shift]); + if (!supply->supply) { + dev_err(codec->dev, "%s: err supply not present ond for %d", + __func__, w->shift); + goto out; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (atomic_inc_return(&supply->ref) == 1) { + ret = regulator_enable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to enable %s\n", + __func__, + on_demand_supply_name[w->shift]); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (atomic_read(&supply->ref) == 0) { + dev_dbg(codec->dev, "%s: %s supply has been disabled.\n", + __func__, on_demand_supply_name[w->shift]); + goto out; + } + if (atomic_dec_return(&supply->ref) == 0) { + ret = regulator_disable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to disable %s\n", + __func__, + on_demand_supply_name[w->shift]); + } + break; + default: + break; + } +out: + return ret; +} + +static int msm_digit_cdc_init_supplies(struct msm_dig_priv *msm_cdc, + struct msm_cdc_pdata *pdata) +{ + int ret; + int i; + + msm_cdc->supplies = devm_kzalloc(msm_cdc->dev, + sizeof(struct regulator_bulk_data) * + ARRAY_SIZE(pdata->regulator), + GFP_KERNEL); + if (!msm_cdc->supplies) { + ret = -ENOMEM; + goto err; + } + + msm_cdc->num_of_supplies = 0; + if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) { + dev_err(msm_cdc->dev, "%s: Array Size out of bound\n", + __func__); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) { + if (pdata->regulator[i].name) { + msm_cdc->supplies[i].supply = + pdata->regulator[i].name; + msm_cdc->num_of_supplies++; + } + } + + ret = devm_regulator_bulk_get(msm_cdc->dev, + msm_cdc->num_of_supplies, + msm_cdc->supplies); + if (ret != 0) { + dev_err(msm_cdc->dev, + "Failed to get supplies: err = %d\n", + ret); + goto err_supplies; + } + + for (i = 0; i < msm_cdc->num_of_supplies; i++) { + if (regulator_count_voltages( + msm_cdc->supplies[i].consumer) <= 0) + continue; + ret = regulator_set_voltage( + msm_cdc->supplies[i].consumer, + pdata->regulator[i].min_uv, + pdata->regulator[i].max_uv); + if (ret) { + dev_err(msm_cdc->dev, + "Setting regulator voltage failed for regulator %s err = %d\n", + msm_cdc->supplies[i].supply, ret); + goto err_supplies; + } + ret = regulator_set_optimum_mode(msm_cdc->supplies[i].consumer, + pdata->regulator[i].optimum_ua); + if (ret < 0) { + dev_err(msm_cdc->dev, + "Setting regulator optimum mode failed for regulator %s err = %d\n", + msm_cdc->supplies[i].supply, ret); + goto err_supplies; + } else { + ret = 0; + } + } + + return ret; + +err_supplies: + kfree(msm_cdc->supplies); +err: + return ret; +} +static int msm_digit_cdc_dt_parse_vreg_info(struct device *dev, + struct msm_cdc_regulator *vreg, const char *vreg_name) +{ + int len, ret = 0; + const __be32 *prop; + char prop_name[CODEC_DT_MAX_PROP_SIZE]; + struct device_node *regnode = NULL; + u32 prop_val; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", + vreg_name); + regnode = of_parse_phandle(dev->of_node, prop_name, 0); + + if (!regnode) { + dev_err(dev, "Looking up %s property in node %s failed\n", + prop_name, dev->of_node->full_name); + return -ENODEV; + } + + dev_dbg(dev, "Looking up %s property in node %s\n", + prop_name, dev->of_node->full_name); + + vreg->name = vreg_name; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-voltage", vreg_name); + prop = of_get_property(dev->of_node, prop_name, &len); + + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_err(dev, "%s %s property\n", + prop ? "invalid format" : "no", prop_name); + return -EINVAL; + } + vreg->min_uv = be32_to_cpup(&prop[0]); + vreg->max_uv = be32_to_cpup(&prop[1]); + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-current", vreg_name); + + ret = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + return -EFAULT; + } + vreg->optimum_ua = prop_val; + + dev_dbg(dev, "%s: vol=[%d %d]uV, curr=[%d]uA\n\n", vreg->name, + vreg->min_uv, vreg->max_uv, vreg->optimum_ua); + return 0; +} + +static struct msm_cdc_pdata *msm_digit_cdc_populate_dt_pdata( + struct device *dev) +{ + struct msm_cdc_pdata *pdata; + int ret, ond_cnt, i, idx = 0; + const char *name = NULL; + const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + ond_cnt = of_property_count_strings(dev->of_node, ond_prop_name); + if (IS_ERR_VALUE(ond_cnt)) + ond_cnt = 0; + + WARN_ON(ond_cnt < 0); + + if (ond_cnt > ARRAY_SIZE(pdata->regulator)) { + ret = -EINVAL; + goto err; + } + + for (i = 0; i < ond_cnt; i++, idx++) { + ret = of_property_read_string_index(dev->of_node, ond_prop_name, + i, &name); + if (ret) { + dev_err(dev, "%s: err parsing on_demand for %s idx %d\n", + __func__, ond_prop_name, i); + goto err; + } + + dev_dbg(dev, "%s: Found on-demand cdc supply %s\n", __func__, + name); + ret = msm_digit_cdc_dt_parse_vreg_info(dev, + &pdata->regulator[idx], + name); + if (ret) { + dev_err(dev, "%s: err parsing vreg on_demand for %s idx %d\n", + __func__, name, idx); + goto err; + } + } + + return pdata; +err: + devm_kfree(dev, pdata); + dev_err(dev, "%s: Failed to populate DT data ret = %d\n", + __func__, ret); + return NULL; +} + +static void msm_digit_cdc_disable_supplies(struct msm_dig_priv *msm_cdc, + struct msm_cdc_pdata *pdata) +{ + int i; + + regulator_bulk_disable(msm_cdc->num_of_supplies, + msm_cdc->supplies); + for (i = 0; i < msm_cdc->num_of_supplies; i++) { + if (regulator_count_voltages( + msm_cdc->supplies[i].consumer) <= 0) + continue; + regulator_set_voltage(msm_cdc->supplies[i].consumer, 0, + pdata->regulator[i].max_uv); + regulator_set_optimum_mode(msm_cdc->supplies[i].consumer, 0); + } + regulator_bulk_free(msm_cdc->num_of_supplies, + msm_cdc->supplies); + kfree(msm_cdc->supplies); +} + +static int msm_dig_cdc_probe(struct platform_device *pdev) +{ + int ret = 0; + u32 dig_cdc_addr; + struct msm_dig_priv *msm_dig_cdc; + struct msm_cdc_pdata *pdata; + int adsp_state; + + adsp_state = apr_get_subsys_state(); + if (adsp_state != APR_SUBSYS_LOADED) { + dev_err(&pdev->dev, "Adsp is not loaded yet %d\n", + adsp_state); + return -EPROBE_DEFER; + } + + device_init_wakeup(&pdev->dev, true); + + if (pdev->dev.of_node) { + dev_dbg(&pdev->dev, "%s:Platform data from device tree\n", + __func__); + pdata = msm_digit_cdc_populate_dt_pdata(&pdev->dev); + pdev->dev.platform_data = pdata; + } else { + dev_dbg(&pdev->dev, "%s:Platform data from board file\n", + __func__); + pdata = pdev->dev.platform_data; + } + if (pdata == NULL) { + dev_err(&pdev->dev, "%s:Platform data failed to populate\n", + __func__); + goto rtn; + } + + msm_dig_cdc = devm_kzalloc(&pdev->dev, sizeof(struct msm_dig_priv), + GFP_KERNEL); + if (!msm_dig_cdc) + return -ENOMEM; + + msm_dig_cdc->dev = &pdev->dev; + ret = msm_digit_cdc_init_supplies(msm_dig_cdc, pdata); + if (ret) { + dev_err(&pdev->dev, "%s: Fail to enable Codec supplies\n", + __func__); + goto rtn; + } + + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &dig_cdc_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + goto err_supplies; + } + + msm_dig_cdc->dig_base = ioremap(dig_cdc_addr, + MSM89XX_CDC_CORE_MAX_REGISTER); + if (msm_dig_cdc->dig_base == NULL) { + dev_err(&pdev->dev, "%s ioremap failed\n", __func__); + ret = -ENOMEM; + goto err_supplies; + } + + msm_dig_cdc->regmap = + devm_regmap_init_mmio_clk(&pdev->dev, NULL, + msm_dig_cdc->dig_base, &msm_digital_regmap_config); + + dev_set_drvdata(&pdev->dev, msm_dig_cdc); + snd_soc_register_codec(&pdev->dev, &soc_msm_dig_codec, + msm_codec_dais, ARRAY_SIZE(msm_codec_dais)); + + return ret; +err_supplies: + msm_digit_cdc_disable_supplies(msm_dig_cdc, pdata); +rtn: + return ret; +} + +static int msm_dig_cdc_remove(struct platform_device *pdev) +{ + struct msm_dig_priv *msm_cdc = dev_get_drvdata(&pdev->dev); + struct msm_cdc_pdata *pdata = msm_cdc->dev->platform_data; + + snd_soc_unregister_codec(&pdev->dev); + msm_digit_cdc_disable_supplies(msm_cdc, pdata); + return 0; +} + +#ifdef CONFIG_PM +static int msm_dig_suspend(struct device *dev) +{ + struct msm_asoc_mach_data *pdata; + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev); + + if (!registered_digcodec || !msm_dig_cdc) { + pr_debug("%s:digcodec not initialized, return\n", __func__); + return 0; + } + pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); + if (!pdata) { + pr_debug("%s:card not initialized, return\n", __func__); + return 0; + } + if (msm_dig_cdc->dapm_bias_off) { + pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n", + __func__, atomic_read(&pdata->mclk_rsc_ref), + atomic_read(&pdata->mclk_enabled)); + + if (atomic_read(&pdata->mclk_enabled) == true) { + cancel_delayed_work_sync( + &pdata->disable_mclk_work); + mutex_lock(&pdata->cdc_mclk_mutex); + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_core_clk); + atomic_set(&pdata->mclk_enabled, false); + mutex_unlock(&pdata->cdc_mclk_mutex); + } + } + + return 0; +} + +static int msm_dig_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops msm_dig_pm_ops = { + .suspend_late = msm_dig_suspend, + .resume_early = msm_dig_resume, +}; +#endif + +static const struct of_device_id msm_dig_cdc_of_match[] = { + {.compatible = "qcom,msm-digital-codec"}, + {}, +}; + +static struct platform_driver msm_digcodec_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = msm_dig_cdc_of_match, +#ifdef CONFIG_PM + .pm = &msm_dig_pm_ops, +#endif + }, + .probe = msm_dig_cdc_probe, + .remove = msm_dig_cdc_remove, +}; +module_platform_driver(msm_digcodec_driver); + +MODULE_DESCRIPTION("MSM Audio Digital codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm-digital-cdc.h b/sound/soc/codecs/msm-digital-cdc.h new file mode 100644 index 000000000000..34af32b23827 --- /dev/null +++ b/sound/soc/codecs/msm-digital-cdc.h @@ -0,0 +1,172 @@ +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef MSM_DIGITAL_CDC_H +#define MSM_DIGITAL_CDC_H + +#include +#include "msm-digital-cdc-registers.h" +#include +#include +#include + +#define HPHL_PA_DISABLE (0x01 << 1) +#define HPHR_PA_DISABLE (0x01 << 2) +#define SPKR_PA_DISABLE (0x01 << 3) + +#define NUM_DECIMATORS 2 + +#define MAX_REGULATOR 2 + +extern struct reg_default + msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE]; + +bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg); +bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg); +bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg); + +enum { + MSM89XX_RX1 = 0, + MSM89XX_RX2, + MSM89XX_RX3, + MSM89XX_RX_MAX, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +/* Support different hph modes */ +enum { + NORMAL_MODE = 0, + HD2_MODE, +}; + +enum dig_cdc_notify_event { + DIG_CDC_EVENT_INVALID, + DIG_CDC_EVENT_CLK_ON, + DIG_CDC_EVENT_CLK_OFF, + DIG_CDC_EVENT_RX1_MUTE_ON, + DIG_CDC_EVENT_RX1_MUTE_OFF, + DIG_CDC_EVENT_RX2_MUTE_ON, + DIG_CDC_EVENT_RX2_MUTE_OFF, + DIG_CDC_EVENT_RX3_MUTE_ON, + DIG_CDC_EVENT_RX3_MUTE_OFF, + DIG_CDC_EVENT_PRE_RX1_INT_ON, + DIG_CDC_EVENT_PRE_RX2_INT_ON, + DIG_CDC_EVENT_POST_RX1_INT_OFF, + DIG_CDC_EVENT_POST_RX2_INT_OFF, + DIG_CDC_EVENT_SSR_DOWN, + DIG_CDC_EVENT_SSR_UP, + DIG_CDC_EVENT_LAST, +}; + +enum { + ON_DEMAND_DIGIT = 0, + ON_DEMAND_SUPPLIES_MAX, +}; + +struct tx_mute_work { + struct msm_dig_priv *dig_cdc; + u32 decimator; + struct delayed_work dwork; +}; + +struct msm_cdc_regulator { + const char *name; + int min_uv; + int max_uv; + int optimum_ua; + bool ondemand; + struct regulator *regulator; +}; + +struct on_demand_supply { + struct regulator *supply; + atomic_t ref; + int min_uv; + int max_uv; + int optimum_ua; +}; + +struct msm_dig_ctrl_data { + struct platform_device *dig_pdev; +}; + +struct msm_dig_priv { + struct device *dev; + struct snd_soc_codec *codec; + s32 dmic_1_2_clk_cnt; + bool dec_active[NUM_DECIMATORS]; + int version; + /* Entry for version info */ + struct snd_info_entry *entry; + char __iomem *dig_base; + struct regmap *regmap; + struct notifier_block nblock; + u32 mute_mask; + int dapm_bias_off; + struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; + struct msm_dig_ctrl_data *dig_ctrl_data; + struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; + u32 num_of_supplies; + struct regulator_bulk_data *supplies; +}; + +struct msm_cdc_pdata { + struct msm_cdc_regulator regulator[MAX_REGULATOR]; +}; +struct hpf_work { + struct msm_dig_priv *dig_cdc; + u32 decimator; + u8 tx_hpf_cut_of_freq; + struct delayed_work dwork; +}; + +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +struct msm_asoc_mach_data { + struct device_node *pdm_gpio_p; /* used by pinctrl API */ + struct device_node *dmic_gpio_p; /* used by pinctrl API */ + struct snd_soc_codec *codec; + int mclk_freq; + bool native_clk_set; + int lb_mode; + int snd_card_val; + atomic_t int_mclk0_rsc_ref; + atomic_t int_mclk0_enabled; + struct mutex cdc_mclk_mutex; + struct delayed_work disable_int_mclk0_work; + int afe_clk_ver; + int ext_pa; + atomic_t mclk_rsc_ref; + atomic_t mclk_enabled; + struct delayed_work disable_mclk_work; + struct afe_digital_clk_cfg digital_cdc_clk; + struct afe_clk_set digital_cdc_core_clk; + void __iomem *vaddr_gpio_mux_spkr_ctl; + void __iomem *vaddr_gpio_mux_mic_ctl; + void __iomem *vaddr_gpio_mux_quin_ctl; + void __iomem *vaddr_gpio_mux_pcm_ctl; +}; + +extern int msm_digcdc_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm); +#endif -- GitLab From 1533a8d68c9bbd6e1910f39c26e1511bd2df2006 Mon Sep 17 00:00:00 2001 From: Raghavendra Rao Ananta Date: Thu, 11 Jan 2018 17:33:07 -0800 Subject: [PATCH 2356/5498] perf: Avoid cpu hotplug during event deletion The event will not be successfully deleted if there was a CPU hotplug going on. The change prevents that from happening. Moreover, the change also disables the ARM PMU irq when the CPU is coming down. This is due to the reason that the deletion of perf event depends on freeing the irqs, which would happen if the irq is disabled in the first place. Change-Id: Ifc3860f723f76d1560e2c9d3fd507e5ead20462a Signed-off-by: Raghavendra Rao Ananta --- arch/arm/include/asm/pmu.h | 1 + arch/arm/kernel/perf_event_cpu.c | 23 ++++++++++++++++++----- kernel/events/core.c | 3 +++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index 0b648c541293..f86ef1f1e223 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -102,6 +102,7 @@ struct arm_pmu { int (*request_irq)(struct arm_pmu *, irq_handler_t handler); void (*free_irq)(struct arm_pmu *); int (*map_event)(struct perf_event *event); + int percpu_irq; int num_events; atomic_t active_events; struct mutex reserve_mutex; diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c index e7418fe4cd9b..6f2a3959ac4b 100644 --- a/arch/arm/kernel/perf_event_cpu.c +++ b/arch/arm/kernel/perf_event_cpu.c @@ -200,15 +200,27 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu) static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, void *hcpu) { - if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) + unsigned long masked_action = action & ~CPU_TASKS_FROZEN; + + if (!cpu_pmu) return NOTIFY_DONE; - if (cpu_pmu && cpu_pmu->reset) - cpu_pmu->reset(cpu_pmu); - else + if ((masked_action != CPU_DYING) && + (masked_action != CPU_STARTING)) return NOTIFY_DONE; - return NOTIFY_OK; + switch (masked_action) { + case CPU_DYING: + if (cpu_pmu->plat_device) + disable_percpu_irq(cpu_pmu->percpu_irq); + break; + case CPU_STARTING: + if (cpu_pmu->reset) + cpu_pmu->reset(cpu_pmu); + break; + } + + return NOTIFY_DONE; } static struct notifier_block cpu_pmu_hotplug_notifier = { @@ -312,6 +324,7 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) cpu_pmu = pmu; cpu_pmu->plat_device = pdev; + cpu_pmu->percpu_irq = platform_get_irq(pdev, 0); if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) { init_fn = of_id->data; diff --git a/kernel/events/core.c b/kernel/events/core.c index 34f41e7d492c..51d742c84bb4 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3626,7 +3626,10 @@ static int perf_release(struct inode *inode, struct file *file) if ((event->state == PERF_EVENT_STATE_OFF) && event->attr.constraint_duplicate) event->state = PERF_EVENT_STATE_ACTIVE; + + get_online_cpus(); put_event(file->private_data); + put_online_cpus(); return 0; } -- GitLab From 0bfb4642f8e8a37d18595bb6067f38fe4b38b425 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 2 Jan 2018 18:19:39 +0000 Subject: [PATCH 2357/5498] FROMLIST: arm64: Move post_ttbr_update_workaround to C code We will soon need to invoke a CPU-specific function pointer after changing page tables, so move post_ttbr_update_workaround out into C code to make this possible. Signed-off-by: Marc Zyngier Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 400a169447ad2268b023637a118fba27246bcc19) Change-Id: Ic21e59001470a2e88db7291eb5f6393f8a64a7dd [ghackmann@google.com: 3.18 doesn't support CPUs that need the Cavium errata, so for now post_ttbr_update_workaround() is an empty stub that will be used in a later patch series.] Signed-off-by: Greg Hackmann --- arch/arm64/kernel/entry.S | 10 ++++++++++ arch/arm64/mm/context.c | 5 +++++ arch/arm64/mm/proc.S | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index c2c3c0d95cb0..ab284aaf680a 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -213,6 +213,16 @@ alternative_else_nop_endif .endif __uaccess_ttbr0_enable x0, x1 + + .if \el == 0 + /* + * Enable errata workarounds only if returning to user. The only + * workaround currently required for TTBR0_EL1 changes are for the + * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache + * corruption). + */ + bl post_ttbr_update_workaround + .endif 1: .if \el != 0 and x22, x22, #~PSR_PAN_BIT // ARMv8.0 CPUs do not understand this bit diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 10d68e438a37..cc3664b088de 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -199,6 +199,11 @@ switch_mm_fastpath: cpu_switch_mm(mm->pgd, mm); } +/* Errata workaround post TTBRx_EL1 update. */ +asmlinkage void post_ttbr_update_workaround(void) +{ +} + static int asids_init(void) { int fld = cpuid_feature_extract_field(read_cpuid(SYS_ID_AA64MMFR0_EL1), 4); diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 31c0d8cc2613..f24e67abbc1f 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -134,7 +134,7 @@ ENTRY(cpu_do_switch_mm) isb msr ttbr0_el1, x0 // now update TTBR0 isb - ret + b post_ttbr_update_workaround // Back to C code... ENDPROC(cpu_do_switch_mm) .section ".text.init", #alloc, #execinstr -- GitLab From cf43f2035a3f491b9977999909a76b1444b2be0c Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 10 Jan 2018 13:18:30 +0000 Subject: [PATCH 2358/5498] FROMLIST: arm64: kpti: Fix the interaction between ASID switching and software PAN With ARM64_SW_TTBR0_PAN enabled, the exception entry code checks the active ASID to decide whether user access was enabled (non-zero ASID) when the exception was taken. On return from exception, if user access was previously disabled, it re-instates TTBR0_EL1 from the per-thread saved value (updated in switch_mm() or efi_set_pgd()). Commit 7655abb95386 ("arm64: mm: Move ASID from TTBR0 to TTBR1") makes a TTBR0_EL1 + ASID switching non-atomic. Subsequently, commit 27a921e75711 ("arm64: mm: Fix and re-enable ARM64_SW_TTBR0_PAN") changes the __uaccess_ttbr0_disable() function and asm macro to first write the reserved TTBR0_EL1 followed by the ASID=0 update in TTBR1_EL1. If an exception occurs between these two, the exception return code will re-instate a valid TTBR0_EL1. Similar scenario can happen in cpu_switch_mm() between setting the reserved TTBR0_EL1 and the ASID update in cpu_do_switch_mm(). This patch reverts the entry.S check for ASID == 0 to TTBR0_EL1 and disables the interrupts around the TTBR0_EL1 and ASID switching code in __uaccess_ttbr0_disable(). It also ensures that, when returning from the EFI runtime services, efi_set_pgd() doesn't leave a non-zero ASID in TTBR1_EL1 by using uaccess_ttbr0_{enable,disable}. The accesses to current_thread_info()->ttbr0 are updated to use READ_ONCE/WRITE_ONCE. As a safety measure, __uaccess_ttbr0_enable() always masks out any existing non-zero ASID TTBR1_EL1 before writing in the new ASID. Fixes: 27a921e75711 ("arm64: mm: Fix and re-enable ARM64_SW_TTBR0_PAN") Acked-by: Will Deacon Reported-by: Ard Biesheuvel Tested-by: Ard Biesheuvel Reviewed-by: James Morse Tested-by: James Morse Co-developed-by: Marc Zyngier Signed-off-by: Catalin Marinas (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 6b88a32c7af68895134872cdec3b6bfdb532d94e) Change-Id: I1597fe926e4d7fc0f2c19dc63efbd359b5033796 [ghackmann@google.com: - adjust context - apply asm-uaccess.h changes to uaccess.h, and efi.h changes to efi.c] Signed-off-by: Greg Hackmann --- arch/arm64/include/asm/mmu_context.h | 7 +++++-- arch/arm64/include/asm/uaccess.h | 21 +++++++++++++-------- arch/arm64/kernel/efi.c | 12 +++++++----- arch/arm64/kernel/entry.S | 2 +- arch/arm64/lib/clear_user.S | 2 +- arch/arm64/lib/copy_from_user.S | 2 +- arch/arm64/lib/copy_in_user.S | 2 +- arch/arm64/lib/copy_to_user.S | 2 +- arch/arm64/mm/cache.S | 2 +- arch/arm64/mm/proc.S | 3 +++ arch/arm64/xen/hypercall.S | 2 +- 11 files changed, 35 insertions(+), 22 deletions(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index e1ad8da4dde7..f27b3315be41 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -130,9 +130,10 @@ static inline void update_saved_ttbr0(struct task_struct *tsk, struct mm_struct *mm) { if (system_uses_ttbr0_pan()) { + u64 ttbr; BUG_ON(mm->pgd == swapper_pg_dir); - task_thread_info(tsk)->ttbr0 = - virt_to_phys(mm->pgd) | ASID(mm) << 48; + ttbr = virt_to_phys(mm->pgd) | ASID(mm) << 48; + WRITE_ONCE(task_thread_info(tsk)->ttbr0, ttbr); } } #else @@ -179,4 +180,6 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, #define deactivate_mm(tsk,mm) do { } while (0) #define activate_mm(prev,next) switch_mm(prev, next, current) +void post_ttbr_update_workaround(void); + #endif diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index e7cb0e6cbdaa..998ca85840df 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -135,16 +135,18 @@ static inline void set_fs(mm_segment_t fs) #ifdef CONFIG_ARM64_SW_TTBR0_PAN static inline void __uaccess_ttbr0_disable(void) { - unsigned long ttbr; + unsigned long flags, ttbr; + local_irq_save(flags); ttbr = read_sysreg(ttbr1_el1); + ttbr &= ~TTBR_ASID_MASK; /* reserved_ttbr0 placed at the end of swapper_pg_dir */ write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); isb(); /* Set reserved ASID */ - ttbr &= ~TTBR_ASID_MASK; write_sysreg(ttbr, ttbr1_el1); isb(); + local_irq_restore(flags); } static inline void __uaccess_ttbr0_enable(void) @@ -157,10 +159,11 @@ static inline void __uaccess_ttbr0_enable(void) * roll-over and an update of 'ttbr0'. */ local_irq_save(flags); - ttbr0 = current_thread_info()->ttbr0; + ttbr0 = READ_ONCE(current_thread_info()->ttbr0); /* Restore active ASID */ ttbr1 = read_sysreg(ttbr1_el1); + ttbr1 &= ~TTBR_ASID_MASK; /* safety measure */ ttbr1 |= ttbr0 & TTBR_ASID_MASK; write_sysreg(ttbr1, ttbr1_el1); isb(); @@ -451,11 +454,11 @@ extern __must_check long strnlen_user(const char __user *str, long n); #ifdef CONFIG_ARM64_SW_TTBR0_PAN .macro __uaccess_ttbr0_disable, tmp1 mrs \tmp1, ttbr1_el1 // swapper_pg_dir + bic \tmp1, \tmp1, #TTBR_ASID_MASK add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE - bic \tmp1, \tmp1, #TTBR_ASID_MASK msr ttbr1_el1, \tmp1 // set reserved ASID isb .endm @@ -472,9 +475,11 @@ extern __must_check long strnlen_user(const char __user *str, long n); isb .endm - .macro uaccess_ttbr0_disable, tmp1 + .macro uaccess_ttbr0_disable, tmp1, tmp2 alternative_if_not ARM64_HAS_PAN + save_and_disable_irq \tmp2 // avoid preemption __uaccess_ttbr0_disable \tmp1 + restore_irq \tmp2 alternative_else_nop_endif .endm @@ -486,7 +491,7 @@ alternative_if_not ARM64_HAS_PAN alternative_else_nop_endif .endm #else - .macro uaccess_ttbr0_disable, tmp1 + .macro uaccess_ttbr0_disable, tmp1, tmp2 .endm .macro uaccess_ttbr0_enable, tmp1, tmp2, tmp3 @@ -496,8 +501,8 @@ alternative_else_nop_endif /* * These macros are no-ops when UAO is present. */ - .macro uaccess_disable_not_uao, tmp1 - uaccess_ttbr0_disable \tmp1 + .macro uaccess_disable_not_uao, tmp1, tmp2 + uaccess_ttbr0_disable \tmp1, \tmp2 alternative_if ARM64_ALT_PAN_NOT_UAO SET_PSTATE_PAN(1) alternative_else_nop_endif diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 7d13642092d1..1c6b036838ec 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -345,12 +345,14 @@ static void efi_set_pgd(struct mm_struct *mm) if (mm != current->active_mm) { /* * Update the current thread's saved ttbr0 since it is - * restored as part of a return from exception. Set - * the hardware TTBR0_EL1 using cpu_switch_mm() - * directly to enable potential errata workarounds. + * restored as part of a return from exception. Enable + * access to the valid TTBR0_EL1 and invoke the errata + * workaround directly since there is no return from + * exception when invoking the EFI run-time services. */ update_saved_ttbr0(current, mm); - cpu_switch_mm(mm->pgd, mm); + uaccess_ttbr0_enable(); + post_ttbr_update_workaround(); } else { /* * Defer the switch to the current thread's TTBR0_EL1 @@ -358,7 +360,7 @@ static void efi_set_pgd(struct mm_struct *mm) * thread's saved ttbr0 corresponding to its active_mm * (if different from init_mm). */ - cpu_set_reserved_ttbr0(); + uaccess_ttbr0_disable(); if (current->active_mm != &init_mm) update_saved_ttbr0(current, current->active_mm); } diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index ab284aaf680a..5929dd6602d8 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -148,7 +148,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif .if \el != 0 - mrs x21, ttbr1_el1 + mrs x21, ttbr0_el1 tst x21, #TTBR_ASID_MASK // Check for the reserved ASID orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR b.eq 1f // TTBR0 access already disabled diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S index dd65ca253eb4..07c7ad97ee28 100644 --- a/arch/arm64/lib/clear_user.S +++ b/arch/arm64/lib/clear_user.S @@ -50,7 +50,7 @@ uao_user_alternative 9f, strh, sttrh, wzr, x0, 2 b.mi 5f uao_user_alternative 9f, strb, sttrb, wzr, x0, 0 5: mov x0, #0 - uaccess_disable_not_uao x2 + uaccess_disable_not_uao x2, x3 ret ENDPROC(__clear_user) diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 1ff23f81e242..683adc358be7 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -67,7 +67,7 @@ ENTRY(__arch_copy_from_user) uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" - uaccess_disable_not_uao x3 + uaccess_disable_not_uao x3, x4 mov x0, #0 // Nothing to copy ret ENDPROC(__arch_copy_from_user) diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S index 074d52fcd75b..e8bfaf19f778 100644 --- a/arch/arm64/lib/copy_in_user.S +++ b/arch/arm64/lib/copy_in_user.S @@ -68,7 +68,7 @@ ENTRY(__copy_in_user) uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" - uaccess_disable_not_uao x3 + uaccess_disable_not_uao x3, x4 mov x0, #0 ret ENDPROC(__copy_in_user) diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S index 67118444cde0..f6cfcc0441de 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -66,7 +66,7 @@ ENTRY(__arch_copy_to_user) uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" - uaccess_disable_not_uao x3 + uaccess_disable_not_uao x3, x4 mov x0, #0 ret ENDPROC(__arch_copy_to_user) diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 0344dc389925..4059ad662ee2 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -70,7 +70,7 @@ USER(9f, ic ivau, x4 ) // invalidate I line PoU 9: // ignore any faulting cache operation dsb ish isb - uaccess_ttbr0_disable x1 + uaccess_ttbr0_disable x1, x2 ret ENDPROC(flush_icache_range) ENDPROC(__flush_cache_user_range) diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index f24e67abbc1f..4c2d56b92648 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -129,6 +129,9 @@ ENDPROC(cpu_do_resume) ENTRY(cpu_do_switch_mm) mrs x2, ttbr1_el1 mmid x1, x1 // get mm->context.id +#ifdef CONFIG_ARM64_SW_TTBR0_PAN + bfi x0, x1, #48, #16 // set the ASID field in TTBR0 +#endif bfi x2, x1, #48, #16 // set the ASID msr ttbr1_el1, x2 // in TTBR1 (since TCR.A1 is set) isb diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index 27b38711023b..a396beb7829b 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -104,6 +104,6 @@ ENTRY(privcmd_call) /* * Disable userspace access from kernel once the hyp call completed. */ - uaccess_ttbr0_disable x6 + uaccess_ttbr0_disable x6, x7 ret ENDPROC(privcmd_call); -- GitLab From d1a7dbb3999e9a080bf9e1eecebc7a846b0d10a7 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Wed, 17 Jan 2018 13:19:15 +0530 Subject: [PATCH 2359/5498] msm: ipa: Return error -ENODEV for set data quota failure If set data quota fails due to invalid interface name, return -ENODEV error. Change-Id: I45f4082cb8026d3757bd4df237e34df14750ea29 Acked-by: Pooja Kumari Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c index a05da95f8e5b..fe7d8c875146 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -250,7 +250,7 @@ static long ipa3_wan_ioctl(struct file *filp, (struct wan_ioctl_set_data_quota *)param); if (rc != 0) { IPAWANERR("WAN_IOC_SET_DATA_QUOTA failed\n"); - if (retval == -ENODEV) + if (rc == -ENODEV) retval = -ENODEV; else retval = -EFAULT; -- GitLab From cdc2029ee6beba3dfb31c4ecdede05621017d968 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 9 Jan 2018 15:32:17 +0800 Subject: [PATCH 2360/5498] ASoc: msm: Add new machine driver for msm8909w MSM8909w wtp device has digital codec only. Current machine driver for wtp device has analog and digital codec settings mixed. Add new machine driver for msm8909w wtp device. Change-Id: I414b92080cf9002b02c956a35ba7a78cb82fc996 Signed-off-by: Meng Wang --- .../bindings/sound/qcom-audio-dev.txt | 191 ++ sound/soc/msm/Kconfig | 1 + sound/soc/msm/Makefile | 2 +- sound/soc/msm/msm8909.c | 2694 +++++++++++++++++ 4 files changed, 2887 insertions(+), 1 deletion(-) create mode 100644 sound/soc/msm/msm8909.c diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index fa8c3c1d6e74..86b293ae29f8 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -1604,6 +1604,197 @@ Example: "SpkrRight", "SpkrLeft"; }; +* MSM8909 ASoC Machine driver + +Required properties: +- compatible : "qcom,msm8909-audio-codec" +- qcom,model : The user-visible name of this sound card. +- reg : Offset and length of the register region(s) for MI2S/PCM MUX +- reg-names : Register region name(s) referenced in reg above + Required register resource entries are: + "csr_gp_io_mux_mic_ctl": Physical address of MUX that controls + controls LPA IF tertiary, quad, PCM0, Digital Codec + and Secondary TLMM mux setting for mic path operation. + "csr_gp_io_mux_spkr_ctl": Physical address of MUX that controls + IF primary, secondary, Digital Codec and Primary TLMM + setting for speaker path operation. + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel": Physical address of MUX + that controls the mux between LPA IF Quad and PCM0 + path to secondary TLMM +- qcom,msm-hs-micbias-type : This property is used to recognize the headset + micbias type, internal or external. +- qcom,msm-ext-pa : This property is used to inform machine driver about + the connection of external PA over available MI2S interfaces, + following values can be given to this property. + primary -> Primary MI2S interface + secondary -> Secondary MI2S interface + tertiary -> Tertiary MI2S interface + quaternary -> Quaternary MI2S interface +- qcom,msm-mbhc-hphl-swh: This property is used to distinguish headset HPHL +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,msm-mbhc-gnd-swh: This property is used to distinguish headset GND +switch type on target typically the switch type will be normally open or +normally close, value for this property 0 for normally close and 1 for +normally open. +- qcom,audio-routing : A list of the connections between audio components. +- qcom,msm-gpios : Lists down all the gpio sets that are supported. +- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets +mentioned in qcom,msm-gpios. +- pinctrl-names : The combinations of gpio sets from above that are supported in +the flavor. +- pinctrl-# : Pinctrl states as mentioned in pinctrl-names. + +Optional properties: +- qcom,msm-afe-clk-ver: Provides detail of AFE clock API version. +- qcom,hdmi-dba-codec-rx: Boolean. specifies if HDMI DBA audio support is enabled or not. +- qcom,split-a2dp: Boolean. specifies if split a2dp audio support is enabled or not. +- qcom,prim-auxpcm-gpio-clk : GPIO on which Primary AUXPCM clk signal is coming. +- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming. +- qcom,prim-auxpcm-gpio-din : GPIO on which Primary AUXPCM DIN signal is coming. +- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming. +- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port +- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming. +- qcom,msm-micbias1-ext-cap : Boolean. Enable micbias1 external +capacitor mode. +- qcom,msm-micbias2-ext-cap : Boolean. Enable micbias2 external +capacitor mode. +- qcom,msm-spk-ext-pa : GPIO which enables external speaker pa. +- qcom,subsys-name: This value provides the subsystem name where codec + is present. This property enables the codec driver to + register and receive subsytem restart notification from subsystem + and follow appropriate steps to ensure codec is in proper state + after subsytem restart. By default codec driver register + with ADSP subsystem. + +To Configure External Audio Switch +- qcom,msm-ext-audio-switch : GPIO which controls external switch that switches + audio path between headset and speakers. +- ext-switch-vdd-supply : Power supply that control external audio switch +- qcom,ext-switch-vdd-voltage : Minimum and maximum voltage in uV to set for + power supply. +- qcom,ext-switch-vdd-op-mode : Maxmum # of uA current the switch will draw + from the power supply. +Example: + qcom,msm-ext-audio-switch = <&msm_gpio 2 0>; - gpio # and active_state + ext-switch-vdd-supply = <&pm8950_l13>; - Power Rail + qcom,ext-switch-vdd-voltage = <3075000 3075000>; - Min, Max uV voltage + qcom,ext-switch-vdd-op-mode = <5000>; - Operational current uA + Additional needs to add two additional qcom,audio-routings + "HEADPHONE", "VDD_EXT_AUDIO_SWITCH" + "SPK_OUT", "VDD_EXT_AUDIO_SWITCH" + +- qcom,msm-mclk-freq : This property is used to inform machine driver about +mclk frequency needs to be configured for internal and external PA. +- asoc-platform: This is phandle list containing the references to platform device + nodes that are used as part of the sound card dai-links. +- asoc-platform-names: This property contains list of platform names. The order of + the platform names should match to that of the phandle order + given in "asoc-platform". +- asoc-cpu: This is phandle list containing the references to cpu dai device nodes + that are used as part of the sound card dai-links. +- asoc-cpu-names: This property contains list of cpu dai names. The order of the + cpu dai names should match to that of the phandle order given. +- asoc-codec: This is phandle list containing the references to codec dai device + nodes that are used as part of the sound card dai-links. +- asoc-codec-names: This property contains list of codec dai names. The order of the + codec dai names should match to that of the phandle order given + in "asoc-codec". +- asoc-wsa-codec-names: This property contains list of wsa codec names. The names + should comply with the wsa nodes configurations. +- asoc-wsa-codec-prefixes: This property contains list of wsa codec prefixes. +- msm-vdd-wsa-switch-supply: WSA codec supply's regulator device tree node. +- qcom,msm-vdd-wsa-switch-voltage: WSA codec supply's voltage level in mV. +- qcom,msm-vdd-wsa-switch-current: WSA codec max current level in mA. + +Example: + sound { + compatible = "qcom,msm8909-audio-codec"; + qcom,model = "msm8909-snd-card"; + reg = <0xc051000 0x4>, + <0xc051004 0x4>, + <0xc055000 0x4>; + reg-names = "csr_gp_io_mux_mic_ctl", + "csr_gp_io_mux_spkr_ctl", + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel"; + qcom,msm-afe-clk-ver = <1>; + qcom,msm-ext-pa = "primary"; + qcom,hdmi-dba-codec-rx; + qcom,split-a2dp; + qcom,msm-mclk-freq = <9600000>; + qcom,msm-mbhc-hphl-swh = <0>; + qcom,msm-mbhc-gnd-swh = <0>; + qcom,msm-hs-micbias-type = "internal"; + qcom,msm-micbias1-ext-cap; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "SPK_RX_BIAS", "MCLK", + "INT_LDO_H", "MCLK", + "MIC BIAS External", "Handset Mic", + "MIC BIAS Internal2", "Headset Mic", + "MIC BIAS External", "Secondary Mic", + "AMIC1", "MIC BIAS External", + "AMIC2", "MIC BIAS Internal2", + "AMIC3", "MIC BIAS External"; + qcom,msm-gpios = + "pri_i2s", + "us_eu_gpio"; + qcom,pinctrl-names = + "all_off", + "pri_i2s_act", + "us_eu_gpio_act", + "pri_i2s_us_eu_gpio_act"; + pinctrl-names = + "all_off", + "pri_i2s_act", + "us_eu_gpio_act", + "pri_i2s_us_eu_gpio_act"; + pinctrl-0 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_sus>; + pinctrl-1 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_sus>; + pinctrl-2 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_act>; + pinctrl-3 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_act>; + qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>; + qcom,prim-auxpcm-gpio-clk = <&msm_gpio 63 0>; + qcom,prim-auxpcm-gpio-sync = <&msm_gpio 64 0>; + qcom,prim-auxpcm-gpio-din = <&msm_gpio 65 0>; + qcom,prim-auxpcm-gpio-dout = <&msm_gpio 66 0>; + qcom,prim-auxpcm-gpio-set = "prim-gpio-prim"; + qcom,tapan-codec-9302; + asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>, + <&loopback>, <&compress>, <&hostless>, + <&afe>, <&lsm>, <&routing>, <&lpa>; + asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", + "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", + "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", + "msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa"; + asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>, + <&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>, + <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, + <&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>, + <&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, + <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, + <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, + <&incall_music_2_rx>; + asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8", + "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", + "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", + "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", + "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", + "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391", + "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393", + "msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289", + "msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293", + "msm-dai-q6-dev.224", "msm-dai-q6-dev.225", + "msm-dai-q6-dev.241", "msm-dai-q6-dev.240", + "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", + "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770"; + asoc-codec = <&stub>, <&pm8916_tombak_dig>; + asoc-codec-names = "msm-stub-codec.1", "tombak_codec"; + asoc-wsa-codec-names = "wsa881x-i2c-codec.8-000f"; + asoc-wsa-codec-prefixes = "SpkrMono"; + }; + * MSM8952 ASoC Machine driver Required properties: diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig index f7db6b7a98d9..0e8e6f284985 100644 --- a/sound/soc/msm/Kconfig +++ b/sound/soc/msm/Kconfig @@ -268,6 +268,7 @@ config SND_SOC_MSM8909 select MSM_QDSP6_APRV3 select MSM_QDSP6V2_CODECS select SND_SOC_MSM8X16_WCD + select SND_SOC_DIGITAL_CDC select SND_SOC_WSA881X_ANALOG select SND_SOC_WSA881X select SND_SOC_WCD9335 diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile index c276461f83d9..1a8cf06b201b 100644 --- a/sound/soc/msm/Makefile +++ b/sound/soc/msm/Makefile @@ -44,7 +44,7 @@ snd-soc-mdm9607-objs := mdm9607.o obj-$(CONFIG_SND_SOC_MDM9607) += snd-soc-mdm9607.o # for MSM 8909 sound card driver -snd-soc-msm8909-objs := msm8952.o msm-audio-pinctrl.o apq8009-i2s-ext-codec.o msm_bg.o +snd-soc-msm8909-objs := msm8952.o msm-audio-pinctrl.o apq8009-i2s-ext-codec.o msm_bg.o msm8909.o obj-$(CONFIG_SND_SOC_MSM8909) += snd-soc-msm8909.o # for MDM9640 sound card driver diff --git a/sound/soc/msm/msm8909.c b/sound/soc/msm/msm8909.c new file mode 100644 index 000000000000..d548470f3e68 --- /dev/null +++ b/sound/soc/msm/msm8909.c @@ -0,0 +1,2694 @@ +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "msm-audio-pinctrl.h" +#include "../codecs/msm-digital-cdc.h" + +#define DRV_NAME "msm8909-asoc-wcd" + +#define BTSCO_RATE_8KHZ 8000 +#define BTSCO_RATE_16KHZ 16000 + +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_192KHZ 192000 + +#define PRI_MI2S_ID (1 << 0) +#define SEC_MI2S_ID (1 << 1) +#define TER_MI2S_ID (1 << 2) +#define QUAT_MI2S_ID (1 << 3) +#define QUIN_MI2S_ID (1 << 4) + +#define DEFAULT_MCLK_RATE 9600000 + +#define MSM_DT_MAX_PROP_SIZE 80 + +enum btsco_rates { + RATE_8KHZ_ID, + RATE_16KHZ_ID, +}; + +static int msm8909_auxpcm_rate = 8000; +static int msm_btsco_rate = BTSCO_RATE_8KHZ; +static int msm_btsco_ch = 1; +static int msm_ter_mi2s_tx_ch = 1; +static int msm_pri_mi2s_rx_ch = 1; +static int msm_proxy_rx_ch = 2; +static int mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int mi2s_rx_bits_per_sample = 16; +static int mi2s_rx_sample_rate = SAMPLING_RATE_48KHZ; +static int mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int mi2s_tx_bits_per_sample = 16; +static int mi2s_tx_sample_rate = SAMPLING_RATE_48KHZ; + +static atomic_t quat_mi2s_clk_ref; +static atomic_t quin_mi2s_clk_ref; +static atomic_t auxpcm_mi2s_clk_ref; + +static int msm_enable_dig_cdc_clk(struct snd_soc_codec *codec, int enable, + bool dapm); +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +static struct afe_clk_cfg mi2s_rx_clk_v1 = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_OSR_CLK_12_P288_MHZ, + Q6AFE_LPASS_CLK_SRC_INTERNAL, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + Q6AFE_LPASS_MODE_CLK1_VALID, + 0, +}; + +static struct afe_clk_cfg mi2s_tx_clk_v1 = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_OSR_CLK_12_P288_MHZ, + Q6AFE_LPASS_CLK_SRC_INTERNAL, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + Q6AFE_LPASS_MODE_CLK1_VALID, + 0, +}; + +static struct afe_clk_set mi2s_tx_clk = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static struct afe_clk_set mi2s_rx_clk = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static const char *const mi2s_ch_text[] = {"One", "Two"}; +static const char *const btsco_rate_text[] = {"BTSCO_RATE_8KHZ", + "BTSCO_RATE_16KHZ"}; +static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *mi2s_rx_sample_rate_text[] = {"KHZ_48", + "KHZ_96", "KHZ_192"}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm8909_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + int ret = 0; + + pdata = snd_soc_card_get_drvdata(w->codec->component.card); + pr_debug("%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = msm_gpioset_activate(CLIENT_WCD_INT, "dmic"); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %sd", + __func__, "cdc_lines_dmic_act"); + return false; + } + break; + case SND_SOC_DAPM_POST_PMD: + ret = msm_gpioset_suspend(CLIENT_WCD_INT, "dmic"); + if (ret) + pr_err("%s: failed to disable the dmic gpios: %d\n", + __func__, ret); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm8909_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("MCLK", -1, SND_SOC_NOPM, 0, 0, + msm_mclk_event, SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIC("Digital Mic1", msm8909_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic2", msm8909_dmic_event), +}; + +static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, + msm_proxy_rx_ch); + ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1; + return 0; +} + +static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, + msm_proxy_rx_ch); + return 0; +} + +static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = msm8909_auxpcm_rate; + channels->min = channels->max = 1; + + return 0; +} + +static int msm_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s: Num of channels = %d Sample rate = %d\n", __func__, + msm_pri_mi2s_rx_ch, mi2s_rx_sample_rate); + rate->min = rate->max = mi2s_rx_sample_rate; + channels->min = channels->max = msm_pri_mi2s_rx_ch; + + return 0; +} + +static int msm_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s(), channel:%d\n", __func__, msm_ter_mi2s_tx_ch); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_bit_format); + rate->min = rate->max = 48000; + channels->min = channels->max = msm_ter_mi2s_tx_ch; + + return 0; +} + +static int msm_senary_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s()\n", __func__); + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + +static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = msm_btsco_rate; + channels->min = channels->max = msm_btsco_ch; + + return 0; +} + +static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch); + + if (channels->max < 2) + channels->min = channels->max = 2; + channels->min = channels->max = msm_proxy_rx_ch; + rate->min = rate->max = 48000; + return 0; +} + +static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + rate->min = rate->max = 48000; + return 0; +} + +static int msm_mi2s_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_bit_format); + else + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_bit_format); + return 0; +} + +static int msm8909_get_clk_id(int port_id) +{ + switch (port_id) { + case AFE_PORT_ID_PRIMARY_MI2S_RX: + return Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT; + case AFE_PORT_ID_SECONDARY_MI2S_RX: + return Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT; + case AFE_PORT_ID_TERTIARY_MI2S_TX: + return Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT; + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + return Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT; + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + return Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT; + case AFE_PORT_ID_SENARY_MI2S_TX: + return Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT; + default: + pr_err("%s: invalid port_id: 0x%x\n", __func__, port_id); + return -EINVAL; + } +} + +static int msm8909_get_port_id(int be_id) +{ + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + return AFE_PORT_ID_PRIMARY_MI2S_RX; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + return AFE_PORT_ID_SECONDARY_MI2S_RX; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + return AFE_PORT_ID_TERTIARY_MI2S_TX; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + return AFE_PORT_ID_QUATERNARY_MI2S_RX; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + return AFE_PORT_ID_QUATERNARY_MI2S_TX; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + return AFE_PORT_ID_QUINARY_MI2S_RX; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + return AFE_PORT_ID_QUINARY_MI2S_TX; + case MSM_BACKEND_DAI_SENARY_MI2S_TX: + return AFE_PORT_ID_SENARY_MI2S_TX; + default: + pr_err("%s: Invalid be_id: %d\n", __func__, be_id); + return -EINVAL; + } +} + +static bool is_mi2s_rx_port(int port_id) +{ + bool ret = false; + + switch (port_id) { + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + ret = true; + break; + default: + break; + } + return ret; +} + +static uint32_t get_mi2s_clk_val(int port_id) +{ + uint32_t clk_val = 0; + + /* + * Derive clock value based on sample rate, bits per sample and + * channel count is used as 2 + */ + if (is_mi2s_rx_port(port_id)) + clk_val = (mi2s_rx_sample_rate * mi2s_rx_bits_per_sample * 2); + else + clk_val = (mi2s_tx_sample_rate * mi2s_tx_bits_per_sample * 2); + + pr_debug("%s: MI2S bit clock value: 0x%0x\n", __func__, clk_val); + return clk_val; +} + +static int msm_mi2s_sclk_ctl(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + int port_id = 0; + + port_id = msm8909_get_port_id(rtd->dai_link->be_id); + if (port_id < 0) { + pr_err("%s: Invalid port_id\n", __func__); + return -EINVAL; + } + if (enable) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { + mi2s_rx_clk_v1.clk_val1 = + get_mi2s_clk_val(port_id); + ret = afe_set_lpass_clock(port_id, + &mi2s_rx_clk_v1); + } else { + mi2s_rx_clk.enable = enable; + mi2s_rx_clk.clk_id = + msm8909_get_clk_id(port_id); + mi2s_rx_clk.clk_freq_in_hz = + get_mi2s_clk_val(port_id); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_rx_clk); + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { + mi2s_tx_clk_v1.clk_val1 = + get_mi2s_clk_val(port_id); + ret = afe_set_lpass_clock(port_id, + &mi2s_tx_clk_v1); + } else { + mi2s_tx_clk.enable = enable; + mi2s_tx_clk.clk_id = + msm8909_get_clk_id(port_id); + mi2s_tx_clk.clk_freq_in_hz = + get_mi2s_clk_val(port_id); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_tx_clk); + } + } else { + pr_err("%s:Not valid substream.\n", __func__); + } + + if (ret < 0) + pr_err("%s:afe_set_lpass_clock_v2 failed\n", __func__); + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { + mi2s_rx_clk_v1.clk_val1 = + Q6AFE_LPASS_IBIT_CLK_DISABLE; + ret = afe_set_lpass_clock(port_id, + &mi2s_rx_clk_v1); + } else { + mi2s_rx_clk.enable = enable; + mi2s_rx_clk.clk_id = + msm8909_get_clk_id(port_id); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_rx_clk); + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { + mi2s_tx_clk_v1.clk_val1 = + Q6AFE_LPASS_IBIT_CLK_DISABLE; + ret = afe_set_lpass_clock(port_id, + &mi2s_tx_clk_v1); + } else { + mi2s_tx_clk.enable = enable; + mi2s_tx_clk.clk_id = + msm8909_get_clk_id(port_id); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_tx_clk); + } + } else { + pr_err("%s:Not valid substream.\n", __func__); + } + + if (ret < 0) + pr_err("%s:afe_set_lpass_clock_v2 failed\n", __func__); + } + return ret; +} + +static int msm_enable_dig_cdc_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: enable %d mclk ref counter %d\n", + __func__, enable, + atomic_read(&pdata->mclk_rsc_ref)); + if (enable) { + if (!atomic_read(&pdata->mclk_rsc_ref)) { + cancel_delayed_work_sync( + &pdata->disable_mclk_work); + mutex_lock(&pdata->cdc_mclk_mutex); + if (atomic_read(&pdata->mclk_enabled) == false) { + if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { + pdata->digital_cdc_clk.clk_val = + pdata->mclk_freq; + ret = afe_set_digital_codec_core_clock( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_clk); + } else { + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_core_clk); + } + if (ret < 0) { + pr_err("%s: failed to enable CCLK\n", + __func__); + mutex_unlock(&pdata->cdc_mclk_mutex); + return ret; + } + pr_debug("enabled digital codec core clk\n"); + atomic_set(&pdata->mclk_enabled, true); + } else + pr_debug("digital codec core clk is already enabled\n"); + mutex_unlock(&pdata->cdc_mclk_mutex); + } + atomic_inc(&pdata->mclk_rsc_ref); + } else { + cancel_delayed_work_sync(&pdata->disable_mclk_work); + mutex_lock(&pdata->cdc_mclk_mutex); + if (atomic_read(&pdata->mclk_enabled) == true) { + if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { + pdata->digital_cdc_clk.clk_val = 0; + ret = afe_set_digital_codec_core_clock( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_clk); + } else { + pdata->digital_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_core_clk); + } + if (ret < 0) + pr_err("%s: failed to disable CCLK\n", + __func__); + atomic_set(&pdata->mclk_enabled, false); + } + mutex_unlock(&pdata->cdc_mclk_mutex); + } + return ret; +} + +static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_btsco_rate = %d", __func__, msm_btsco_rate); + ucontrol->value.integer.value[0] = msm_btsco_rate; + return 0; +} + +static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case RATE_8KHZ_ID: + msm_btsco_rate = BTSCO_RATE_8KHZ; + break; + case RATE_16KHZ_ID: + msm_btsco_rate = BTSCO_RATE_16KHZ; + break; + default: + msm_btsco_rate = BTSCO_RATE_8KHZ; + break; + } + + pr_debug("%s: msm_btsco_rate = %d\n", __func__, msm_btsco_rate); + return 0; +} + +static int mi2s_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (mi2s_rx_bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: mi2s_rx_bit_format = %d, ucontrol value = %ld\n", + __func__, mi2s_rx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int mi2s_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 2: + mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + mi2s_rx_bits_per_sample = 32; + break; + case 1: + mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + mi2s_rx_bits_per_sample = 32; + break; + case 0: + default: + mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + mi2s_rx_bits_per_sample = 16; + break; + } + return 0; +} + +static int mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 3: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S32_LE; + mi2s_tx_bits_per_sample = 32; + break; + case 2: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; + mi2s_tx_bits_per_sample = 32; + break; + case 1: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE; + mi2s_tx_bits_per_sample = 32; + break; + case 0: + default: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE; + mi2s_tx_bits_per_sample = 16; + break; + } + return 0; +} + +static int mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + switch (mi2s_tx_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: mi2s_tx_bit_format = %d, ucontrol value = %ld\n", + __func__, mi2s_tx_bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_pri_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_pri_mi2s_rx_ch = %d\n", __func__, + msm_pri_mi2s_rx_ch); + ucontrol->value.integer.value[0] = msm_pri_mi2s_rx_ch - 1; + return 0; +} + +static int msm_pri_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_pri_mi2s_rx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_pri_mi2s_rx_ch = %d\n", __func__, msm_pri_mi2s_rx_ch); + return 1; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val = 0; + + switch (mi2s_rx_sample_rate) { + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: sample_rate_val = %d\n", __func__, + sample_rate_val); + + return 0; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + mi2s_rx_sample_rate = SAMPLING_RATE_96KHZ; + break; + case 2: + mi2s_rx_sample_rate = SAMPLING_RATE_192KHZ; + break; + case 0: + default: + mi2s_rx_sample_rate = SAMPLING_RATE_48KHZ; + break; + } + pr_debug("%s: mi2s_rx_sample_rate = %d\n", __func__, + mi2s_rx_sample_rate); + return 0; +} + +static int msm_ter_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_ter_mi2s_tx_ch = %d\n", __func__, + msm_ter_mi2s_tx_ch); + ucontrol->value.integer.value[0] = msm_ter_mi2s_tx_ch - 1; + return 0; +} + +static int msm_ter_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_ter_mi2s_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_ter_mi2s_tx_ch = %d\n", __func__, msm_ter_mi2s_tx_ch); + return 1; +} + +static const struct soc_enum msm_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(bit_format_text), + bit_format_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mi2s_ch_text), + mi2s_ch_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(btsco_rate_text), + btsco_rate_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(proxy_rx_ch_text), + proxy_rx_ch_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mi2s_rx_sample_rate_text), + mi2s_rx_sample_rate_text), +}; + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("MI2S_RX Format", msm_snd_enum[0], + mi2s_rx_bit_format_get, mi2s_rx_bit_format_put), + SOC_ENUM_EXT("MI2S_TX Format", msm_snd_enum[0], + mi2s_tx_bit_format_get, mi2s_tx_bit_format_put), + SOC_ENUM_EXT("MI2S_TX Channels", msm_snd_enum[1], + msm_ter_mi2s_tx_ch_get, msm_ter_mi2s_tx_ch_put), + SOC_ENUM_EXT("MI2S_RX Channels", msm_snd_enum[1], + msm_pri_mi2s_rx_ch_get, msm_pri_mi2s_rx_ch_put), + SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_snd_enum[2], + msm_btsco_rate_get, msm_btsco_rate_put), + SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[3], + msm_proxy_rx_ch_get, msm_proxy_rx_ch_put), + SOC_ENUM_EXT("MI2S_RX SampleRate", msm_snd_enum[4], + mi2s_rx_sample_rate_get, mi2s_rx_sample_rate_put), +}; + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(w->codec->component.card); + pr_debug("%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_POST_PMD: + pr_debug("%s: mclk_res_ref = %d\n", + __func__, atomic_read(&pdata->mclk_rsc_ref)); + if (atomic_read(&pdata->mclk_rsc_ref) == 0) { + pr_debug("%s: disabling MCLK\n", __func__); + /* disable the codec mclk config*/ + msm_digcdc_mclk_enable(w->codec, 0, true); + msm_enable_dig_cdc_clk(w->codec, 0, true); + } + break; + default: + pr_err("%s: invalid DAPM event %d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + int ret = 0, val = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (!q6core_is_adsp_ready()) { + pr_err("%s(): adsp not ready\n", __func__); + return -EINVAL; + } + + /* + * configure the slave select to + * invalid state for internal codec + */ + if (pdata->vaddr_gpio_mux_spkr_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_spkr_ctl); + val = val | 0x00010000; + iowrite32(val, pdata->vaddr_gpio_mux_spkr_ctl); + } + + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + val = val | 0x00200000; + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } + + ret = msm_mi2s_sclk_ctl(substream, true); + if (ret < 0) { + pr_err("%s: failed to enable sclk %d\n", + __func__, ret); + return ret; + } + ret = msm_enable_dig_cdc_clk(codec, 1, true); + if (ret < 0) { + pr_err("failed to enable mclk\n"); + return ret; + } + /* Enable the codec mclk config */ + msm_digcdc_mclk_enable(codec, 1, true); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret); + + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = msm_mi2s_sclk_ctl(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed; ret=%d\n", __func__, + ret); + if (atomic_read(&pdata->mclk_rsc_ref) > 0) { + atomic_dec(&pdata->mclk_rsc_ref); + pr_debug("%s: decrementing mclk_res_ref %d\n", + __func__, atomic_read(&pdata->mclk_rsc_ref)); + } +} + +static int msm_prim_auxpcm_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + int ret = 0, val = 0; + + pr_debug("%s(): substream = %s\n", + __func__, substream->name); + + if (!q6core_is_adsp_ready()) { + pr_err("%s(): adsp not ready\n", __func__); + return -EINVAL; + } + + /* mux config to route the AUX MI2S */ + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + val = val | 0x2; + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } + if (pdata->vaddr_gpio_mux_pcm_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); + val = val | 0x1; + iowrite32(val, pdata->vaddr_gpio_mux_pcm_ctl); + } + msm_enable_dig_cdc_clk(codec, 1, true); + atomic_inc(&auxpcm_mi2s_clk_ref); + + /* enable the gpio's used for the external AUXPCM interface */ + ret = msm_gpioset_activate(CLIENT_WCD_INT, "quat_i2s"); + if (ret < 0) + pr_err("%s(): configure gpios failed = %s\n", + __func__, "quat_i2s"); + return ret; +} + +static void msm_prim_auxpcm_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_codec *codec = rtd->codec; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + pr_debug("%s(): substream = %s\n", + __func__, substream->name); + if (atomic_read(&pdata->mclk_rsc_ref) > 0) { + atomic_dec(&pdata->mclk_rsc_ref); + pr_debug("%s: decrementing mclk_res_ref %d\n", + __func__, atomic_read(&pdata->mclk_rsc_ref)); + } + if (atomic_read(&auxpcm_mi2s_clk_ref) > 0) + atomic_dec(&auxpcm_mi2s_clk_ref); + if ((atomic_read(&auxpcm_mi2s_clk_ref) == 0) && + (atomic_read(&pdata->mclk_rsc_ref) == 0)) { + msm_enable_dig_cdc_clk(codec, 0, true); + } + ret = msm_gpioset_suspend(CLIENT_WCD_INT, "quat_i2s"); + if (ret < 0) + pr_err("%s(): configure gpios failed = %s\n", + __func__, "quat_i2s"); +} + +static int msm_sec_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0, val = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + pr_info("%s: Secondary Mi2s does not support capture\n", + __func__); + return 0; + } + + if (!q6core_is_adsp_ready()) { + pr_err("%s(): adsp not ready\n", __func__); + return -EINVAL; + } + + if ((pdata->ext_pa & SEC_MI2S_ID) == SEC_MI2S_ID) { + if (pdata->vaddr_gpio_mux_spkr_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_spkr_ctl); + val = val | 0x0004835c; + iowrite32(val, pdata->vaddr_gpio_mux_spkr_ctl); + } + ret = msm_mi2s_sclk_ctl(substream, true); + if (ret < 0) { + pr_err("failed to enable sclk\n"); + return ret; + } + pr_debug("%s(): SEC I2S gpios turned on = %s\n", __func__, + "sec_i2s"); + ret = msm_gpioset_activate(CLIENT_WCD_INT, "sec_i2s"); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %sd", + __func__, "sec_i2s"); + goto err; + } + } else { + pr_err("%s: error codec type\n", __func__); + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed\n", __func__); + ret = msm_gpioset_suspend(CLIENT_WCD_INT, "sec_i2s"); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "sec_i2s"); + goto err; + } + } + return ret; +err: + ret = msm_mi2s_sclk_ctl(substream, false); + if (ret < 0) + pr_err("failed to disable sclk\n"); + return ret; +} + +static void msm_sec_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if ((pdata->ext_pa & SEC_MI2S_ID) == SEC_MI2S_ID) { + ret = msm_gpioset_suspend(CLIENT_WCD_INT, "sec_i2s"); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated: %sd", + __func__, "sec_i2s"); + return; + } + ret = msm_mi2s_sclk_ctl(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed\n", __func__); + } +} + +static int msm_quat_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0, val = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (!q6core_is_adsp_ready()) { + pr_err("%s(): adsp not ready\n", __func__); + return -EINVAL; + } + + if (pdata->vaddr_gpio_mux_mic_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_mic_ctl); + val = val | 0x02020002; + iowrite32(val, pdata->vaddr_gpio_mux_mic_ctl); + } + ret = msm_mi2s_sclk_ctl(substream, true); + if (ret < 0) { + pr_err("failed to enable sclk\n"); + return ret; + } + ret = msm_gpioset_activate(CLIENT_WCD_INT, "quat_i2s"); + if (ret < 0) { + pr_err("failed to enable codec gpios\n"); + goto err; + } + if (atomic_inc_return(&quat_mi2s_clk_ref) == 1) { + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed\n", __func__); + } + return ret; +err: + ret = msm_mi2s_sclk_ctl(substream, false); + if (ret < 0) + pr_err("failed to disable sclk\n"); + return ret; +} + +static void msm_quat_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + ret = msm_mi2s_sclk_ctl(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed\n", __func__); + if (atomic_read(&quat_mi2s_clk_ref) > 0) + atomic_dec(&quat_mi2s_clk_ref); + ret = msm_gpioset_suspend(CLIENT_WCD_INT, "quat_i2s"); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "quat_i2s"); + return; + } +} + +static int msm_quin_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0, val = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (!q6core_is_adsp_ready()) { + pr_err("%s(): adsp not ready\n", __func__); + return -EINVAL; + } + + if (pdata->vaddr_gpio_mux_quin_ctl) { + val = ioread32(pdata->vaddr_gpio_mux_quin_ctl); + val = val | 0x00000001; + iowrite32(val, pdata->vaddr_gpio_mux_quin_ctl); + } else { + return -EINVAL; + } + ret = msm_mi2s_sclk_ctl(substream, true); + if (ret < 0) { + pr_err("failed to enable sclk\n"); + return ret; + } + ret = msm_gpioset_activate(CLIENT_WCD_INT, "quin_i2s"); + if (ret < 0) { + pr_err("failed to enable codec gpios\n"); + goto err; + } + if (atomic_inc_return(&quin_mi2s_clk_ref) == 1) { + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed\n", __func__); + } + return ret; +err: + ret = msm_mi2s_sclk_ctl(substream, false); + if (ret < 0) + pr_err("failed to disable sclk\n"); + return ret; +} + +static void msm_quin_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + ret = msm_mi2s_sclk_ctl(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed\n", __func__); + if (atomic_read(&quin_mi2s_clk_ref) > 0) + atomic_dec(&quin_mi2s_clk_ref); + ret = msm_gpioset_suspend(CLIENT_WCD_INT, "quin_i2s"); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "quin_i2s"); + return; + } +} + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + + snd_soc_dapm_new_controls(dapm, msm8909_dapm_widgets, + ARRAY_SIZE(msm8909_dapm_widgets)); + + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + + snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static struct snd_soc_ops msm8909_quat_mi2s_be_ops = { + .startup = msm_quat_mi2s_snd_startup, + .hw_params = msm_mi2s_snd_hw_params, + .shutdown = msm_quat_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm8909_quin_mi2s_be_ops = { + .startup = msm_quin_mi2s_snd_startup, + .hw_params = msm_mi2s_snd_hw_params, + .shutdown = msm_quin_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm8909_sec_mi2s_be_ops = { + .startup = msm_sec_mi2s_snd_startup, + .hw_params = msm_mi2s_snd_hw_params, + .shutdown = msm_sec_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm8909_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .hw_params = msm_mi2s_snd_hw_params, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_pri_auxpcm_be_ops = { + .startup = msm_prim_auxpcm_startup, + .shutdown = msm_prim_auxpcm_shutdown, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm8909_dai[] = { + /* FrontEnd DAI Links */ + {/* hw:x,0 */ + .name = "MSM8909 Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + {/* hw:x,1 */ + .name = "MSM8909 Media2", + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + {/* hw:x,2 */ + .name = "Circuit-Switch Voice", + .stream_name = "CS-Voice", + .cpu_dai_name = "CS-VOICE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_CS_VOICE, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,3 */ + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_VOIP, + }, + {/* hw:x,4 */ + .name = "MSM8X16 ULL", + .stream_name = "ULL", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + {/* hw:x,5 */ + .name = "Primary MI2S_RX Hostless", + .stream_name = "Primary MI2S_RX Hostless", + .cpu_dai_name = "PRI_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,6 */ + .name = "INT_FM Hostless", + .stream_name = "INT_FM Hostless", + .cpu_dai_name = "INT_FM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,7 */ + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + {/* hw:x,8 */ + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + {/* hw:x,9 */ + .name = "MSM8909 Compress1", + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + {/* hw:x,10 */ + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,11 */ + .name = "Tertiary MI2S_TX Hostless", + .stream_name = "Tertiary MI2S_TX Hostless", + .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,12 */ + .name = "MSM8x16 LowLatency", + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + }, + {/* hw:x,13 */ + .name = "Voice2", + .stream_name = "Voice2", + .cpu_dai_name = "Voice2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICE2, + }, + {/* hw:x,14 */ + .name = "MSM8x16 Media9", + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* This dailink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { /* hw:x,15 */ + .name = "VoLTE", + .stream_name = "VoLTE", + .cpu_dai_name = "VoLTE", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOLTE, + }, + { /* hw:x,16 */ + .name = "VoWLAN", + .stream_name = "VoWLAN", + .cpu_dai_name = "VoWLAN", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOWLAN, + }, + {/* hw:x,17 */ + .name = "INT_HFP_BT Hostless", + .stream_name = "INT_HFP_BT Hostless", + .cpu_dai_name = "INT_HFP_BT_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,18 */ + .name = "MSM8916 HFP", + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + /* LSM FE */ + {/* hw:x,19 */ + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM1, + }, + {/* hw:x,20 */ + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM2, + }, + {/* hw:x,21 */ + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM3, + }, + {/* hw:x,22 */ + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM4, + }, + {/* hw:x,23 */ + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_LSM5, + }, + { /* hw:x,24 */ + .name = "MSM8X16 Compress2", + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { /* hw:x,25 */ + .name = "QUAT_MI2S Hostless", + .stream_name = "QUAT_MI2S Hostless", + .cpu_dai_name = "QUAT_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,26 */ + .name = LPASS_BE_SENARY_MI2S_TX, + .stream_name = "Senary_mi2s Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.6", + .platform_name = "msm-pcm-hostless", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .be_id = MSM_BACKEND_DAI_SENARY_MI2S_TX, + .be_hw_params_fixup = msm_senary_tx_be_hw_params_fixup, + .ops = &msm8909_mi2s_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, + {/* hw:x,27 */ + .name = "MSM8X16 Compress3", + .stream_name = "Compress3", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + {/* hw:x,28 */ + .name = "MSM8X16 Compress4", + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + {/* hw:x,29 */ + .name = "MSM8X16 Compress5", + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + {/* hw:x,30 */ + .name = "MSM8X16 Compress6", + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + {/* hw:x,31 */ + .name = "MSM8X16 Compress7", + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + {/* hw:x,32 */ + .name = "MSM8X16 Compress8", + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + {/* hw:x,33 */ + .name = "MSM8X16 Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + {/* hw:x,34 */ + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + {/* hw:x,35 */ + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + {/* hw:x,36 */ + .name = "MSM8916 HFP Loopback2", + .stream_name = "MultiMedia8", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + {/* hw:x,37 */ + .name = "QCHAT", + .stream_name = "QCHAT", + .cpu_dai_name = "QCHAT", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_QCHAT, + }, + {/* hw:x,38 */ + .name = "MSM8X16 Compress10", + .stream_name = "Compress10", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, + {/* hw:x,39 */ + .name = "MSM8X16 Compress11", + .stream_name = "Compress11", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, + {/* hw:x,40 */ + .name = "MSM8X16 Compress12", + .stream_name = "Compress12", + .cpu_dai_name = "MultiMedia19", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA19, + }, + {/* hw:x,41 */ + .name = "MSM8X16 Compress13", + .stream_name = "Compress13", + .cpu_dai_name = "MultiMedia28", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA28, + }, + {/* hw:x,42 */ + .name = "MSM8X16 Compress14", + .stream_name = "Compress14", + .cpu_dai_name = "MultiMedia29", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA29, + }, + /* Backend I2S DAI Links */ + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .no_pcm = 1, + .dpcm_playback = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup, + .ops = &msm8909_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup, + .ops = &msm8909_sec_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-dig-codec", + .codec_dai_name = "msm_dig_cdc_dai_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_tx_be_hw_params_fixup, + .ops = &msm8909_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup, + .ops = &msm8909_quat_mi2s_be_ops, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm8909_quat_mi2s_be_ops, + .ignore_suspend = 1, + }, + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_auxpcm_be_params_fixup, + .ops = &msm_pri_auxpcm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_auxpcm_be_params_fixup, + .ops = &msm_pri_auxpcm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT_BT_SCO_RX, + .stream_name = "Internal BT-SCO Playback", + .cpu_dai_name = "msm-dai-q6-dev.12288", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX, + .be_hw_params_fixup = msm_btsco_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT_BT_SCO_TX, + .stream_name = "Internal BT-SCO Capture", + .cpu_dai_name = "msm-dai-q6-dev.12289", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX, + .be_hw_params_fixup = msm_btsco_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT_FM_RX, + .stream_name = "Internal FM Playback", + .cpu_dai_name = "msm-dai-q6-dev.12292", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_INT_FM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT_FM_TX, + .stream_name = "Internal FM Capture", + .cpu_dai_name = "msm-dai-q6-dev.12293", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INT_FM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + + { + .name = LPASS_BE_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.5", + .platform_name = "msm-pcm-routing", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUINARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm8909_quin_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; +static struct snd_soc_dai_link msm8909_hdmi_dba_dai_link[] = { + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.5", + .platform_name = "msm-pcm-routing", + .codec_dai_name = "msm_hdmi_dba_codec_rx_dai", + .codec_name = "msm-hdmi-dba-codec-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup, + .ops = &msm8909_quin_mi2s_be_ops, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm8909_quin_dai_link[] = { + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.5", + .platform_name = "msm-pcm-routing", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup, + .ops = &msm8909_quin_mi2s_be_ops, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm8909_split_a2dp_dai_link[] = { + { + .name = LPASS_BE_INT_BT_A2DP_RX, + .stream_name = "Internal BT-A2DP Playback", + .cpu_dai_name = "msm-dai-q6-dev.12290", + .platform_name = "msm-pcm-routing", + .codec_dai_name = "msm-stub-rx", + .codec_name = "msm-stub-codec.1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_INT_BT_A2DP_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm8909_dai_links[ + ARRAY_SIZE(msm8909_dai) + + ARRAY_SIZE(msm8909_hdmi_dba_dai_link) + + ARRAY_SIZE(msm8909_split_a2dp_dai_link)]; + +static struct snd_soc_card bear_card = { + /* snd_soc_card_msm8909 */ + .name = "msm8909-snd-card", + .dai_link = msm8909_dai, + .num_links = ARRAY_SIZE(msm8909_dai), +}; + +static void msm_disable_mclk(struct work_struct *work) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct delayed_work *dwork; + int ret = 0; + + dwork = to_delayed_work(work); + pdata = container_of(dwork, struct msm_asoc_mach_data, + disable_mclk_work); + mutex_lock(&pdata->cdc_mclk_mutex); + pr_debug("%s: mclk_enabled %d mclk_rsc_ref %d\n", __func__, + atomic_read(&pdata->mclk_enabled), + atomic_read(&pdata->mclk_rsc_ref)); + + if (atomic_read(&pdata->mclk_enabled) == true + && atomic_read(&pdata->mclk_rsc_ref) == 0) { + pr_debug("Disable the mclk\n"); + if (pdata->afe_clk_ver == AFE_CLK_VERSION_V1) { + pdata->digital_cdc_clk.clk_val = 0; + ret = afe_set_digital_codec_core_clock( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_clk); + + } else { + pdata->digital_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_PRIMARY_MI2S_RX, + &pdata->digital_cdc_core_clk); + } + if (ret < 0) + pr_err("%s failed to disable the CCLK\n", __func__); + atomic_set(&pdata->mclk_enabled, false); + } + mutex_unlock(&pdata->cdc_mclk_mutex); +} + +static int msm8909_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *phandle; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto cpu_dai; + } + phandle = of_parse_phandle(cdev->of_node, + "asoc-platform", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = phandle; + dai_link[i].platform_name = NULL; + } +cpu_dai: + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index < 0) + goto codec_dai; + phandle = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = phandle; + dai_link[i].cpu_dai_name = NULL; + } +codec_dai: + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + phandle = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for codec dai %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = phandle; + dai_link[i].codec_name = NULL; + } + } +err: + return ret; +} + +static struct snd_soc_card *msm8909_populate_sndcard_dailinks( + struct device *dev) +{ + struct snd_soc_card *card = &bear_card; + struct snd_soc_dai_link *dailink; + int len1; + + card->name = dev_name(dev); + len1 = ARRAY_SIZE(msm8909_dai); + memcpy(msm8909_dai_links, msm8909_dai, sizeof(msm8909_dai)); + dailink = msm8909_dai_links; + if (of_property_read_bool(dev->of_node, + "qcom,hdmi-dba-codec-rx")) { + dev_dbg(dev, "%s(): hdmi audio support present\n", + __func__); + memcpy(dailink + len1, msm8909_hdmi_dba_dai_link, + sizeof(msm8909_hdmi_dba_dai_link)); + len1 += ARRAY_SIZE(msm8909_hdmi_dba_dai_link); + } else { + dev_dbg(dev, "%s(): No hdmi dba present, add quin dai\n", + __func__); + memcpy(dailink + len1, msm8909_quin_dai_link, + sizeof(msm8909_quin_dai_link)); + len1 += ARRAY_SIZE(msm8909_quin_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,split-a2dp")) { + dev_dbg(dev, "%s(): split a2dp support present\n", + __func__); + memcpy(dailink + len1, msm8909_split_a2dp_dai_link, + sizeof(msm8909_split_a2dp_dai_link)); + len1 += ARRAY_SIZE(msm8909_split_a2dp_dai_link); + } + card->dai_link = dailink; + card->num_links = len1; + return card; +} + +static int msm8909_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata = NULL; + const char *mclk = "qcom,msm-mclk-freq"; + const char *ext_pa = "qcom,msm-ext-pa"; + const char *ext_pa_str = NULL; + int num_strings; + int ret, id, i, val; + struct resource *muxsel; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_mux_mic_ctl"); + if (!muxsel) { + dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); + ret = -ENODEV; + goto err1; + } + pdata->vaddr_gpio_mux_mic_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_mic_ctl == NULL) { + pr_err("%s ioremap failure for muxsel virt addr\n", + __func__); + ret = -ENOMEM; + goto err1; + } + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_mux_spkr_ctl"); + if (!muxsel) { + dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); + ret = -ENODEV; + goto err; + } + pdata->vaddr_gpio_mux_spkr_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_spkr_ctl == NULL) { + pr_err("%s ioremap failure for muxsel virt addr\n", + __func__); + ret = -ENOMEM; + goto err; + } + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel"); + if (!muxsel) { + dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); + ret = -ENODEV; + goto err; + } + pdata->vaddr_gpio_mux_pcm_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_pcm_ctl == NULL) { + pr_err("%s ioremap failure for muxsel virt addr\n", + __func__); + ret = -ENOMEM; + goto err; + } + + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "csr_gp_io_mux_quin_ctl"); + if (!muxsel) { + dev_dbg(&pdev->dev, "MUX addr invalid for MI2S\n"); + goto parse_mclk_freq; + } + pdata->vaddr_gpio_mux_quin_ctl = + ioremap(muxsel->start, resource_size(muxsel)); + if (pdata->vaddr_gpio_mux_quin_ctl == NULL) { + pr_err("%s ioremap failure for muxsel virt addr\n", + __func__); + ret = -ENOMEM; + goto err; + } +parse_mclk_freq: + ret = of_property_read_u32(pdev->dev.of_node, mclk, &id); + if (ret) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, mclk); + id = DEFAULT_MCLK_RATE; + } + pdata->mclk_freq = id; + /*reading the gpio configurations from dtsi file*/ + ret = msm_gpioset_initialize(CLIENT_WCD_INT, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, + "%s: error reading dtsi files%d\n", __func__, ret); + goto err; + } + + card = msm8909_populate_sndcard_dailinks(&pdev->dev); + dev_info(&pdev->dev, "default codec configured\n"); + num_strings = of_property_count_strings(pdev->dev.of_node, + ext_pa); + if (num_strings < 0) { + dev_err(&pdev->dev, + "%s: missing %s in dt node or length is incorrect\n", + __func__, ext_pa); + goto err; + } + for (i = 0; i < num_strings; i++) { + ret = of_property_read_string_index(pdev->dev.of_node, + ext_pa, i, &ext_pa_str); + if (ret) { + dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n", + __func__, ext_pa, i, ret); + goto err; + } + if (!strcmp(ext_pa_str, "primary")) + pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID); + else if (!strcmp(ext_pa_str, "secondary")) + pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID); + else if (!strcmp(ext_pa_str, "tertiary")) + pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID); + else if (!strcmp(ext_pa_str, "quaternary")) + pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID); + else if (!strcmp(ext_pa_str, "quinary")) + pdata->ext_pa = (pdata->ext_pa | QUIN_MI2S_ID); + } + pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa); + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-afe-clk-ver", &val); + if (ret) + pdata->afe_clk_ver = AFE_CLK_VERSION_V2; + else + pdata->afe_clk_ver = val; + /* initialize the mclk */ + pdata->digital_cdc_clk.i2s_cfg_minor_version = + AFE_API_VERSION_I2S_CONFIG; + pdata->digital_cdc_clk.clk_val = pdata->mclk_freq; + pdata->digital_cdc_clk.clk_root = 5; + pdata->digital_cdc_clk.reserved = 0; + /* initialize the digital codec core clk */ + pdata->digital_cdc_core_clk.clk_set_minor_version = + AFE_API_VERSION_I2S_CONFIG; + pdata->digital_cdc_core_clk.clk_id = + Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE; + pdata->digital_cdc_core_clk.clk_freq_in_hz = + pdata->mclk_freq; + pdata->digital_cdc_core_clk.clk_attri = + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + pdata->digital_cdc_core_clk.clk_root = + Q6AFE_LPASS_CLK_ROOT_DEFAULT; + pdata->digital_cdc_core_clk.enable = 1; + + /* Initialize loopback mode to false */ + pdata->lb_mode = false; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) + goto err; + + /* initialize timer */ + INIT_DELAYED_WORK(&pdata->disable_mclk_work, msm_disable_mclk); + mutex_init(&pdata->cdc_mclk_mutex); + atomic_set(&pdata->mclk_rsc_ref, 0); + atomic_set(&pdata->mclk_enabled, false); + atomic_set(&quat_mi2s_clk_ref, 0); + atomic_set(&quin_mi2s_clk_ref, 0); + atomic_set(&auxpcm_mi2s_clk_ref, 0); + + ret = snd_soc_of_parse_audio_routing(card, + "qcom,audio-routing"); + if (ret) + goto err; + + ret = msm8909_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + + return 0; +err: + if (pdata->vaddr_gpio_mux_spkr_ctl) + iounmap(pdata->vaddr_gpio_mux_spkr_ctl); + if (pdata->vaddr_gpio_mux_mic_ctl) + iounmap(pdata->vaddr_gpio_mux_mic_ctl); + if (pdata->vaddr_gpio_mux_pcm_ctl) + iounmap(pdata->vaddr_gpio_mux_pcm_ctl); + if (pdata->vaddr_gpio_mux_quin_ctl) + iounmap(pdata->vaddr_gpio_mux_quin_ctl); +err1: + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm8909_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->vaddr_gpio_mux_spkr_ctl) + iounmap(pdata->vaddr_gpio_mux_spkr_ctl); + if (pdata->vaddr_gpio_mux_mic_ctl) + iounmap(pdata->vaddr_gpio_mux_mic_ctl); + if (pdata->vaddr_gpio_mux_pcm_ctl) + iounmap(pdata->vaddr_gpio_mux_pcm_ctl); + if (pdata->vaddr_gpio_mux_quin_ctl) + iounmap(pdata->vaddr_gpio_mux_quin_ctl); + snd_soc_unregister_card(card); + mutex_destroy(&pdata->cdc_mclk_mutex); + return 0; +} + +static const struct of_device_id msm8909_asoc_machine_of_match[] = { + { .compatible = "qcom,msm8909-audio-codec", }, + {}, +}; + +static struct platform_driver msm8909_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = msm8909_asoc_machine_of_match, + }, + .probe = msm8909_asoc_machine_probe, + .remove = msm8909_asoc_machine_remove, +}; + +static int __init msm8909_machine_init(void) +{ + return platform_driver_register(&msm8909_asoc_machine_driver); +} +late_initcall(msm8909_machine_init); + +static void __exit msm8909_machine_exit(void) +{ + return platform_driver_unregister(&msm8909_asoc_machine_driver); +} +module_exit(msm8909_machine_exit); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, msm8909_asoc_machine_of_match); -- GitLab From 97fbac83eb88d08fb107891c1498b996bbe603f2 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Thu, 9 Nov 2017 16:22:06 +0800 Subject: [PATCH 2361/5498] ARM: dts: msm: change sound card config for msm8909w DMIC0_CLK/DATA are connected to GPIO4/5, add pinctrl setting to enable DMIC0. As msm8909w wtp has different hardware design with msm8909 mtp, update digital codec and sound card config for msm8909w wtp device. Change-Id: I74e814052c61effca00580f27ef70880397b542d Signed-off-by: Meng Wang --- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 56 +++++++++++++++++++- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 57 +++++++++++++++++++-- 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index 22712e983a96..25688ff48824 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1888,6 +1888,60 @@ }; }; + cdc-dmic-lines { + cdc_dmic0_clk_act: dmic0_clk_on { + mux { + pins = "gpio4"; + function = "dmic0_clk"; + }; + + config { + pins = "gpio4"; + drive-strength = <8>; + bias-pull-none; + }; + }; + + cdc_dmic0_clk_sus: dmic0_clk_off { + mux { + pins = "gpio4"; + function = "gpio"; + }; + + config { + pins = "gpio4"; + drive-strength = <2>; + bias-disable; + }; + }; + + cdc_dmic0_data_act: dmic0_data_on { + mux { + pins = "gpio5"; + function = "dmic0_data"; + }; + + config { + pins = "gpio5"; + drive-strength = <8>; + bias-pull-none; + }; + }; + + cdc_dmic0_data_sus: dmic0_data_off { + mux { + pins = "gpio5"; + function = "gpio"; + }; + + config { + pins = "gpio5"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + cdc-pdm-lines { cdc_pdm_lines_act: pdm_lines_on { mux { diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 1d280c975549..a550459e9a32 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -105,10 +105,18 @@ qcom,thermal-handle = <&msm_thermal_freq>; }; }; -}; -&audio_codec_mtp { - /delete-property/ asoc-codec; + msm_digital_codec: msm-dig-codec@771c000 { + compatible = "qcom,msm-digital-codec"; + reg = <0x0771c000 0x0>; + + cdc-vdd-digital-supply = <&pm660_l11>; + qcom,cdc-vdd-digital-voltage = <1800000 1800000>; + qcom,cdc-vdd-digital-current = <5000>; + qcom,cdc-on-demand-supplies = "cdc-vdd-digital"; + + qcom,subsys-name = "modem"; + }; }; &qcom_seecom { @@ -239,3 +247,46 @@ qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>; qcom,platform-bklight-en-gpio = <&msm_gpio 37 0>; }; + +&dai_mi2s3 { + qcom,msm-mi2s-rx-lines = <1>; + qcom,msm-mi2s-tx-lines = <2>; +}; + +&audio_codec_mtp { + compatible = "qcom,msm8909-audio-codec"; + qcom,model = "msm8909w-wtp-snd-card"; + qcom,msm-ext-pa = "quaternary"; + /delete-property/qcom,split-a2dp; + qcom,audio-routing = + "CDC_CONN", "MCLK", + "QUAT_MI2S_RX", "DIGIT_REGULATOR", + "TX_I2S_CLK", "DIGIT_REGULATOR", + "DMIC1", "Digital Mic1", + "DMIC2", "Digital Mic2"; + qcom,msm-gpios = + "quat_i2s", + "dmic"; + qcom,pinctrl-names = + "all_off", + "quat_i2s_act", + "dmic_act", + "quat_i2s_dmic_act"; + pinctrl-names = + "all_off", + "quat_i2s_act", + "dmic_act", + "quat_i2s_dmic_act"; + pinctrl-0 = <&quat_mi2s_sleep &quat_mi2s_din_sleep + &cdc_dmic0_clk_sus &cdc_dmic0_data_sus>; + pinctrl-1 = <&quat_mi2s_active &quat_mi2s_din_active + &cdc_dmic0_clk_sus &cdc_dmic0_data_sus>; + pinctrl-2 = <&quat_mi2s_sleep &quat_mi2s_din_sleep + &cdc_dmic0_clk_act &cdc_dmic0_data_act>; + pinctrl-3 = <&quat_mi2s_active &quat_mi2s_din_active + &cdc_dmic0_clk_act &cdc_dmic0_data_act>; + /delete-property/qcom,cdc-us-euro-gpios; + + asoc-codec = <&stub_codec>, <&msm_digital_codec>; + asoc-codec-names = "msm-stub-codec.1", "msm-dig-codec"; +}; -- GitLab From 6a851911b111ae23ffcb0a5c0a9447e4568c283c Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 18 Jan 2018 16:17:16 -0800 Subject: [PATCH 2362/5498] ANDROID: sdcardfs: Move default_normal to superblock Moving default_normal from mount info to superblock info as it doesn't need to change between mount points. Signed-off-by: Daniel Rosenberg Bug: 72158116 Change-Id: I16c6a0577c601b4f7566269f7e189fcf697afd4e --- fs/sdcardfs/inode.c | 7 ++++--- fs/sdcardfs/main.c | 4 ++-- fs/sdcardfs/sdcardfs.h | 10 ++++++---- fs/sdcardfs/super.c | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 36d54d6fa16d..f33ab0aa7944 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -643,7 +643,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma */ copy_attrs(&tmp, inode); tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, inode->i_sb, top)); tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(inode), top); data_put(top); @@ -720,7 +720,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct */ copy_attrs(&tmp, inode); tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, dentry->d_sb, top)); tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(inode), top); tmp.i_size = i_size_read(inode); @@ -821,6 +821,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct sdcardfs_inode_data *top = top_data_get(info); + struct super_block *sb = inode->i_sb; if (!top) return -EINVAL; @@ -830,7 +831,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top); stat->nlink = inode->i_nlink; stat->uid = make_kuid(&init_user_ns, top->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, sb, top)); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 89ca92191355..379037e2f70d 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -70,7 +70,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->reserved_mb = 0; /* by default, gid derivation is off */ opts->gid_derivation = false; - vfsopts->default_normal = false; + opts->default_normal = false; *debug = 0; @@ -126,7 +126,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->gid_derivation = true; break; case Opt_default_normal: - vfsopts->default_normal = true; + opts->default_normal = true; break; /* unknown option */ default: diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 5ef5ddfc725e..13855986a02b 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -220,13 +220,13 @@ struct sdcardfs_mount_options { userid_t fs_user_id; bool multiuser; bool gid_derivation; + bool default_normal; unsigned int reserved_mb; }; struct sdcardfs_vfsmount_options { gid_t gid; mode_t mask; - bool default_normal; }; extern int parse_options_remount(struct super_block *sb, char *options, int silent, @@ -414,11 +414,13 @@ static inline void set_top(struct sdcardfs_inode_info *info, } static inline int get_gid(struct vfsmount *mnt, + struct super_block *sb, struct sdcardfs_inode_data *data) { - struct sdcardfs_vfsmount_options *opts = mnt->data; + struct sdcardfs_vfsmount_options *vfsopts = mnt->data; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(sb); - if (opts->gid == AID_SDCARD_RW && !opts->default_normal) + if (vfsopts->gid == AID_SDCARD_RW && !sbi->options.default_normal) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing @@ -427,7 +429,7 @@ static inline int get_gid(struct vfsmount *mnt, */ return AID_SDCARD_RW; else - return multiuser_get_uid(data->userid, opts->gid); + return multiuser_get_uid(data->userid, vfsopts->gid); } static inline int get_mode(struct vfsmount *mnt, diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index a28b40f5adc8..87d6f836592e 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -304,7 +304,7 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_printf(m, ",userid=%u", opts->fs_user_id); if (opts->gid_derivation) seq_puts(m, ",derive_gid"); - if (vfsopts->default_normal) + if (opts->default_normal) seq_puts(m, ",default_normal"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); -- GitLab From aee56b2c9b87b25a0a542a1d05d74b85c707ca3c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 2 Jan 2018 18:19:39 +0000 Subject: [PATCH 2363/5498] FROMLIST: arm64: Move post_ttbr_update_workaround to C code We will soon need to invoke a CPU-specific function pointer after changing page tables, so move post_ttbr_update_workaround out into C code to make this possible. Signed-off-by: Marc Zyngier Signed-off-by: Will Deacon (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 400a169447ad2268b023637a118fba27246bcc19) Change-Id: Ic21e59001470a2e88db7291eb5f6393f8a64a7dd [ghackmann@google.com: 3.18 doesn't support CPUs that need the Cavium errata, so for now post_ttbr_update_workaround() is an empty stub that will be used in a later patch series.] Signed-off-by: Greg Hackmann Git-Commit: 0bfb4642f8e8a37d18595bb6067f38fe4b38b425 Git-repo: git://android.googlesource.com/kernel/common.git Signed-off-by: Vinayak Menon --- arch/arm64/kernel/entry.S | 10 ++++++++++ arch/arm64/mm/context.c | 5 +++++ arch/arm64/mm/proc.S | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index e292e5b01391..0f3fcba02de5 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -213,6 +213,16 @@ alternative_else_nop_endif .endif __uaccess_ttbr0_enable x0, x1 + + .if \el == 0 + /* + * Enable errata workarounds only if returning to user. The only + * workaround currently required for TTBR0_EL1 changes are for the + * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache + * corruption). + */ + bl post_ttbr_update_workaround + .endif 1: .if \el != 0 and x22, x22, #~PSR_PAN_BIT // ARMv8.0 CPUs do not understand this bit diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 10d68e438a37..cc3664b088de 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -199,6 +199,11 @@ switch_mm_fastpath: cpu_switch_mm(mm->pgd, mm); } +/* Errata workaround post TTBRx_EL1 update. */ +asmlinkage void post_ttbr_update_workaround(void) +{ +} + static int asids_init(void) { int fld = cpuid_feature_extract_field(read_cpuid(SYS_ID_AA64MMFR0_EL1), 4); diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 3cbf35a95821..b3b4e8ee8e54 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -134,7 +134,7 @@ ENTRY(cpu_do_switch_mm) isb msr ttbr0_el1, x0 // now update TTBR0 isb - ret + b post_ttbr_update_workaround // Back to C code... ENDPROC(cpu_do_switch_mm) .section ".text.init", #alloc, #execinstr -- GitLab From 2d1ef1ecec8f5915b46f5c73b11a732ddbfd9b31 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 10 Jan 2018 13:18:30 +0000 Subject: [PATCH 2364/5498] FROMLIST: arm64: kpti: Fix the interaction between ASID switching and software PAN With ARM64_SW_TTBR0_PAN enabled, the exception entry code checks the active ASID to decide whether user access was enabled (non-zero ASID) when the exception was taken. On return from exception, if user access was previously disabled, it re-instates TTBR0_EL1 from the per-thread saved value (updated in switch_mm() or efi_set_pgd()). Commit 7655abb95386 ("arm64: mm: Move ASID from TTBR0 to TTBR1") makes a TTBR0_EL1 + ASID switching non-atomic. Subsequently, commit 27a921e75711 ("arm64: mm: Fix and re-enable ARM64_SW_TTBR0_PAN") changes the __uaccess_ttbr0_disable() function and asm macro to first write the reserved TTBR0_EL1 followed by the ASID=0 update in TTBR1_EL1. If an exception occurs between these two, the exception return code will re-instate a valid TTBR0_EL1. Similar scenario can happen in cpu_switch_mm() between setting the reserved TTBR0_EL1 and the ASID update in cpu_do_switch_mm(). This patch reverts the entry.S check for ASID == 0 to TTBR0_EL1 and disables the interrupts around the TTBR0_EL1 and ASID switching code in __uaccess_ttbr0_disable(). It also ensures that, when returning from the EFI runtime services, efi_set_pgd() doesn't leave a non-zero ASID in TTBR1_EL1 by using uaccess_ttbr0_{enable,disable}. The accesses to current_thread_info()->ttbr0 are updated to use READ_ONCE/WRITE_ONCE. As a safety measure, __uaccess_ttbr0_enable() always masks out any existing non-zero ASID TTBR1_EL1 before writing in the new ASID. Fixes: 27a921e75711 ("arm64: mm: Fix and re-enable ARM64_SW_TTBR0_PAN") Acked-by: Will Deacon Reported-by: Ard Biesheuvel Tested-by: Ard Biesheuvel Reviewed-by: James Morse Tested-by: James Morse Co-developed-by: Marc Zyngier Signed-off-by: Catalin Marinas (cherry picked from git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git commit 6b88a32c7af68895134872cdec3b6bfdb532d94e) Change-Id: I1597fe926e4d7fc0f2c19dc63efbd359b5033796 [ghackmann@google.com: - adjust context - apply asm-uaccess.h changes to uaccess.h, and efi.h changes to efi.c] Signed-off-by: Greg Hackmann Git-Commit: cf43f2035a3f491b9977999909a76b1444b2be0c Git-repo: git://android.googlesource.com/kernel/common.git [vinmenon@codeaurora.org: changes to mmu_context.h and efi.c due to extra SW PAN fixes on this branch] Signed-off-by: Vinayak Menon --- arch/arm64/include/asm/mmu_context.h | 4 +++- arch/arm64/include/asm/uaccess.h | 21 +++++++++++++-------- arch/arm64/kernel/efi.c | 12 +++++++----- arch/arm64/kernel/entry.S | 2 +- arch/arm64/lib/clear_user.S | 2 +- arch/arm64/lib/copy_from_user.S | 2 +- arch/arm64/lib/copy_in_user.S | 2 +- arch/arm64/lib/copy_to_user.S | 2 +- arch/arm64/mm/cache.S | 2 +- arch/arm64/mm/proc.S | 3 +++ arch/arm64/xen/hypercall.S | 2 +- 11 files changed, 33 insertions(+), 21 deletions(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 2156ee710274..3c092a7c5482 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -130,7 +130,7 @@ static inline void update_saved_ttbr0(struct task_struct *tsk, else ttbr = virt_to_phys(mm->pgd) | ASID(mm) << 48; - task_thread_info(tsk)->ttbr0 = ttbr; + WRITE_ONCE(task_thread_info(tsk)->ttbr0, ttbr); } #else static inline void update_saved_ttbr0(struct task_struct *tsk, @@ -184,4 +184,6 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, #define deactivate_mm(tsk,mm) do { } while (0) #define activate_mm(prev,next) switch_mm(prev, next, current) +void post_ttbr_update_workaround(void); + #endif diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index e7cb0e6cbdaa..998ca85840df 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -135,16 +135,18 @@ static inline void set_fs(mm_segment_t fs) #ifdef CONFIG_ARM64_SW_TTBR0_PAN static inline void __uaccess_ttbr0_disable(void) { - unsigned long ttbr; + unsigned long flags, ttbr; + local_irq_save(flags); ttbr = read_sysreg(ttbr1_el1); + ttbr &= ~TTBR_ASID_MASK; /* reserved_ttbr0 placed at the end of swapper_pg_dir */ write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); isb(); /* Set reserved ASID */ - ttbr &= ~TTBR_ASID_MASK; write_sysreg(ttbr, ttbr1_el1); isb(); + local_irq_restore(flags); } static inline void __uaccess_ttbr0_enable(void) @@ -157,10 +159,11 @@ static inline void __uaccess_ttbr0_enable(void) * roll-over and an update of 'ttbr0'. */ local_irq_save(flags); - ttbr0 = current_thread_info()->ttbr0; + ttbr0 = READ_ONCE(current_thread_info()->ttbr0); /* Restore active ASID */ ttbr1 = read_sysreg(ttbr1_el1); + ttbr1 &= ~TTBR_ASID_MASK; /* safety measure */ ttbr1 |= ttbr0 & TTBR_ASID_MASK; write_sysreg(ttbr1, ttbr1_el1); isb(); @@ -451,11 +454,11 @@ extern __must_check long strnlen_user(const char __user *str, long n); #ifdef CONFIG_ARM64_SW_TTBR0_PAN .macro __uaccess_ttbr0_disable, tmp1 mrs \tmp1, ttbr1_el1 // swapper_pg_dir + bic \tmp1, \tmp1, #TTBR_ASID_MASK add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE - bic \tmp1, \tmp1, #TTBR_ASID_MASK msr ttbr1_el1, \tmp1 // set reserved ASID isb .endm @@ -472,9 +475,11 @@ extern __must_check long strnlen_user(const char __user *str, long n); isb .endm - .macro uaccess_ttbr0_disable, tmp1 + .macro uaccess_ttbr0_disable, tmp1, tmp2 alternative_if_not ARM64_HAS_PAN + save_and_disable_irq \tmp2 // avoid preemption __uaccess_ttbr0_disable \tmp1 + restore_irq \tmp2 alternative_else_nop_endif .endm @@ -486,7 +491,7 @@ alternative_if_not ARM64_HAS_PAN alternative_else_nop_endif .endm #else - .macro uaccess_ttbr0_disable, tmp1 + .macro uaccess_ttbr0_disable, tmp1, tmp2 .endm .macro uaccess_ttbr0_enable, tmp1, tmp2, tmp3 @@ -496,8 +501,8 @@ alternative_else_nop_endif /* * These macros are no-ops when UAO is present. */ - .macro uaccess_disable_not_uao, tmp1 - uaccess_ttbr0_disable \tmp1 + .macro uaccess_disable_not_uao, tmp1, tmp2 + uaccess_ttbr0_disable \tmp1, \tmp2 alternative_if ARM64_ALT_PAN_NOT_UAO SET_PSTATE_PAN(1) alternative_else_nop_endif diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 0e4f402578ee..df060c5da8d4 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -345,19 +345,21 @@ static void efi_set_pgd(struct mm_struct *mm) if (mm != current->active_mm) { /* * Update the current thread's saved ttbr0 since it is - * restored as part of a return from exception. Set - * the hardware TTBR0_EL1 using cpu_switch_mm() - * directly to enable potential errata workarounds. + * restored as part of a return from exception. Enable + * access to the valid TTBR0_EL1 and invoke the errata + * workaround directly since there is no return from + * exception when invoking the EFI run-time services. */ update_saved_ttbr0(current, mm); - cpu_switch_mm(mm->pgd, mm); + uaccess_ttbr0_enable(); + post_ttbr_update_workaround(); } else { /* * Defer the switch to the current thread's TTBR0_EL1 * until uaccess_enable(). Restore the current * thread's saved ttbr0 corresponding to its active_mm */ - cpu_set_reserved_ttbr0(); + uaccess_ttbr0_disable(); update_saved_ttbr0(current, current->active_mm); } } diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 0f3fcba02de5..7b3e2f79b34a 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -148,7 +148,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif .if \el != 0 - mrs x21, ttbr1_el1 + mrs x21, ttbr0_el1 tst x21, #TTBR_ASID_MASK // Check for the reserved ASID orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR b.eq 1f // TTBR0 access already disabled diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S index dd65ca253eb4..07c7ad97ee28 100644 --- a/arch/arm64/lib/clear_user.S +++ b/arch/arm64/lib/clear_user.S @@ -50,7 +50,7 @@ uao_user_alternative 9f, strh, sttrh, wzr, x0, 2 b.mi 5f uao_user_alternative 9f, strb, sttrb, wzr, x0, 0 5: mov x0, #0 - uaccess_disable_not_uao x2 + uaccess_disable_not_uao x2, x3 ret ENDPROC(__clear_user) diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 1ff23f81e242..683adc358be7 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -67,7 +67,7 @@ ENTRY(__arch_copy_from_user) uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" - uaccess_disable_not_uao x3 + uaccess_disable_not_uao x3, x4 mov x0, #0 // Nothing to copy ret ENDPROC(__arch_copy_from_user) diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S index 074d52fcd75b..e8bfaf19f778 100644 --- a/arch/arm64/lib/copy_in_user.S +++ b/arch/arm64/lib/copy_in_user.S @@ -68,7 +68,7 @@ ENTRY(__copy_in_user) uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" - uaccess_disable_not_uao x3 + uaccess_disable_not_uao x3, x4 mov x0, #0 ret ENDPROC(__copy_in_user) diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S index 67118444cde0..f6cfcc0441de 100644 --- a/arch/arm64/lib/copy_to_user.S +++ b/arch/arm64/lib/copy_to_user.S @@ -66,7 +66,7 @@ ENTRY(__arch_copy_to_user) uaccess_enable_not_uao x3, x4, x5 add end, x0, x2 #include "copy_template.S" - uaccess_disable_not_uao x3 + uaccess_disable_not_uao x3, x4 mov x0, #0 ret ENDPROC(__arch_copy_to_user) diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 98134de8dec7..a8850de00235 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -180,7 +180,7 @@ USER(9f, ic ivau, x4 ) // invalidate I line PoU 9: // ignore any faulting cache operation dsb ish isb - uaccess_ttbr0_disable x1 + uaccess_ttbr0_disable x1, x2 ret ENDPROC(flush_icache_range) ENDPROC(__flush_cache_user_range) diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index b3b4e8ee8e54..b6245c71bd07 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -129,6 +129,9 @@ ENDPROC(cpu_do_resume) ENTRY(cpu_do_switch_mm) mrs x2, ttbr1_el1 mmid x1, x1 // get mm->context.id +#ifdef CONFIG_ARM64_SW_TTBR0_PAN + bfi x0, x1, #48, #16 // set the ASID field in TTBR0 +#endif bfi x2, x1, #48, #16 // set the ASID msr ttbr1_el1, x2 // in TTBR1 (since TCR.A1 is set) isb diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index 27b38711023b..a396beb7829b 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -104,6 +104,6 @@ ENTRY(privcmd_call) /* * Disable userspace access from kernel once the hyp call completed. */ - uaccess_ttbr0_disable x6 + uaccess_ttbr0_disable x6, x7 ret ENDPROC(privcmd_call); -- GitLab From 804871301ae67a42f92f18149fde2b968f123735 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 11 Jan 2018 18:49:32 +0530 Subject: [PATCH 2365/5498] ARM: dts: msm: Add bg-daemon node for 8909w variants Adds bg-daemon node for bg_daemon driver which toggle the gpio on modem down event. Change-Id: I85bcee93c3fc82477f7a6b07ec1dc3c4db1af33a Signed-off-by: Arjun Singh --- arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts | 7 ++++++- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 7 ++++++- arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 7 ++++++- arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 12 +++++++++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts index 390f9c4af2d4..2237a2aba626 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -150,6 +150,11 @@ vdd-ldo1-supply = <&pm660_l11>; vdd-ldo2-supply = <&pm660_l15>; }; + + qcom,bg-daemon { + compatible = "qcom,bg-daemon"; + qcom,bg-reset-gpio = <&pm660_gpios 5 0>; + }; }; &i2c_1 { diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index ed04e197a670..3d275f9e1133 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -151,6 +151,11 @@ vdd-ldo1-supply = <&pm660_l11>; vdd-ldo2-supply = <&pm660_l15>; }; + + qcom,bg-daemon { + compatible = "qcom,bg-daemon"; + qcom,bg-reset-gpio = <&pm660_gpios 5 0>; + }; }; &i2c_1 { diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 84c3ac427bf5..5336ccd4420c 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -164,6 +164,11 @@ vdd-ldo2-supply = <&pm660_l15>; }; + qcom,bg-daemon { + compatible = "qcom,bg-daemon"; + qcom,bg-reset-gpio = <&pm660_gpios 5 0>; + }; + qcom,bcl { compatible = "qcom,bcl"; qcom,bcl-enable; diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index c597ff13b98f..5e3906e85282 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -229,6 +229,16 @@ qcom,master-en = <1>; qcom,out-strength = <2>; }; + + gpio@c400 { + status = "ok"; + qcom,mode = <1>; + qcom,pull = <5>; + qcom,vin-sel = <0>; + qcom,src-sel = <2>; + qcom,master-en = <1>; + qcom,out-strength = <2>; + }; }; &pm660_misc { -- GitLab From 87464e3946286f3a09029b16d0550fad4c3a30ae Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Fri, 19 Jan 2018 02:32:17 +0530 Subject: [PATCH 2366/5498] msm:ipa: Return mux id only for valid interface Currently value of MAX_NUM_OF_MUX_CHANNEL is 10 but number of valid interfaces is 8. So empty interface is also getting mux id. Return mux id only for valid interfaces. Change-Id: I7852df0aa0ccee781c1bf6857a4183b99194f3ee Acked-by: Pooja Kumari Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 2 +- drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index c83b97b3c9c8..c781e1173f83 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -760,7 +760,7 @@ static int find_vchannel_name_index(const char *vchannel_name) { int i; - for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) { + for (i = 0; i < rmnet_index; i++) { if (0 == strcmp(mux_channel[i].vchannel_name, vchannel_name)) return i; } diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index a02ef60cc7b9..9a8ec0d0ab4c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -785,7 +785,7 @@ static int find_vchannel_name_index(const char *vchannel_name) { int i; - for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) { + for (i = 0; i < rmnet_ipa3_ctx->rmnet_index; i++) { if (0 == strcmp(rmnet_ipa3_ctx->mux_channel[i].vchannel_name, vchannel_name)) return i; -- GitLab From ebcbb5273439872086764fb595b8f05d360c9ab7 Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Tue, 16 Jan 2018 14:51:15 +0530 Subject: [PATCH 2367/5498] dwc3: debugfs: Add check for length before copy data from userspace Add boundary check before copying data from userspace buffer to dwc3 local buffer. The third parameter passed to copy_from_user() should be minimum of the two values between userpsace buffer size count and (local_buffer size - 1). The last one byte in local_buffer should be reserved for null terminator. Change-Id: I9b2e3db4d5ad6b5f14515cadafa6264f9e8b786c Signed-off-by: Vijayavardhan Vennapusa --- drivers/usb/dwc3/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index fb252ecc9a97..9beaebc0eb80 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -634,7 +634,7 @@ static ssize_t dwc3_store_ep_num(struct file *file, const char __user *ubuf, unsigned int num, dir, temp; unsigned long flags; - if (copy_from_user(kbuf, ubuf, count > 10 ? 10 : count)) + if (copy_from_user(kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) return -EFAULT; if (sscanf(kbuf, "%u %u", &num, &dir) != 2) -- GitLab From 91dcfae4ad052efbbfb1d9b4b8c5e5a1b23bb53a Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 15 Jan 2018 22:18:42 +0530 Subject: [PATCH 2368/5498] soc: qcom: bgcom: Fix Glink stuck after device suspend Provides the fixes for Glink stuck issue after device suspend. Change-Id: I299edf54f2934b71495913acb1ddca0fd22df98e Signed-off-by: Arjun Singh --- drivers/soc/qcom/bgcom_spi.c | 57 +++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c index 772264f20565..beeb44424a65 100644 --- a/drivers/soc/qcom/bgcom_spi.c +++ b/drivers/soc/qcom/bgcom_spi.c @@ -47,7 +47,7 @@ #define HED_EVENT_SIZE_LEN (0x02) #define HED_EVENT_DATA_STRT_LEN (0x05) -#define MAX_RETRY 3 +#define MAX_RETRY 20 enum bgcom_state { /*BGCOM Staus ready*/ @@ -109,6 +109,8 @@ static enum bgcom_spi_state spi_state; static struct workqueue_struct *wq; static DECLARE_WORK(input_work , send_input_events); +static struct mutex bg_resume_mutex; + static void augmnt_fifo(uint8_t *data, int pos) { data[pos] = '\0'; @@ -462,6 +464,11 @@ int bgcom_ahb_read(void *handle, uint32_t ahb_start_addr, return -EBUSY; } + if (bgcom_resume(&handle)) { + pr_err("Failed to resume\n"); + return -EBUSY; + } + size = num_words*BG_SPI_WORD_SIZE; txn_len = BG_SPI_AHB_READ_CMD_LEN + size; @@ -518,6 +525,11 @@ int bgcom_ahb_write(void *handle, uint32_t ahb_start_addr, return -EBUSY; } + if (bgcom_resume(&handle)) { + pr_err("Failed to resume\n"); + return -EBUSY; + } + size = num_words*BG_SPI_WORD_SIZE; txn_len = BG_SPI_AHB_CMD_LEN + size; @@ -562,6 +574,11 @@ int bgcom_fifo_write(void *handle, uint32_t num_words, return -EBUSY; } + if (bgcom_resume(&handle)) { + pr_err("Failed to resume\n"); + return -EBUSY; + } + size = num_words*BG_SPI_WORD_SIZE; txn_len = BG_SPI_WRITE_CMND_LEN + size; @@ -724,11 +741,26 @@ int bgcom_reg_read(void *handle, uint8_t reg_start_addr, } EXPORT_SYMBOL(bgcom_reg_read); +static int is_bg_resume(void *handle) +{ + uint32_t txn_len; + int ret; + uint8_t tx_buf[8] = {0}; + uint8_t rx_buf[8] = {0}; + uint32_t cmnd_reg = 0; + + txn_len = 0x08; + tx_buf[0] = 0x05; + ret = bgcom_transfer(handle, tx_buf, rx_buf, txn_len); + if (!ret) + memcpy(&cmnd_reg, rx_buf+BG_SPI_READ_LEN, 0x04); + return cmnd_reg & BIT(31); +} + int bgcom_resume(void **handle) { struct bg_spi_priv *bg_spi; struct bg_context *cntx; - uint32_t cmnd_reg = 0; int retry = 0; if (*handle == NULL) @@ -737,12 +769,11 @@ int bgcom_resume(void **handle) cntx = *handle; bg_spi = cntx->bg_spi; + mutex_lock(&bg_resume_mutex); if (bg_spi->bg_state == BGCOM_STATE_ACTIVE) - return 0; - + goto unlock; do { - bgcom_reg_read(*handle, BG_STATUS_REG, 1, &cmnd_reg); - if (cmnd_reg & BIT(31)) { + if (is_bg_resume(*handle)) { bg_spi->bg_state = BGCOM_STATE_ACTIVE; break; } @@ -750,29 +781,37 @@ int bgcom_resume(void **handle) ++retry; } while (retry < MAX_RETRY); +unlock: + mutex_unlock(&bg_resume_mutex); + pr_info("BG Resumed in %d retries\n", retry); return (retry == MAX_RETRY ? -ETIMEDOUT : 0); } EXPORT_SYMBOL(bgcom_resume); int bgcom_suspend(void **handle) { - int ret; struct bg_spi_priv *bg_spi; struct bg_context *cntx; uint32_t cmnd_reg = 0; + int ret = 0; if (*handle == NULL) return -EINVAL; cntx = *handle; bg_spi = cntx->bg_spi; + mutex_lock(&bg_resume_mutex); if (bg_spi->bg_state == BGCOM_STATE_SUSPEND) - return 0; + goto unlock; cmnd_reg |= BIT(31); ret = bgcom_reg_write(*handle, BG_CMND_REG, 1, &cmnd_reg); if (ret == 0) bg_spi->bg_state = BGCOM_STATE_SUSPEND; + +unlock: + mutex_unlock(&bg_resume_mutex); + pr_info("suspended with : %d\n", ret); return ret; } EXPORT_SYMBOL(bgcom_suspend); @@ -877,6 +916,8 @@ static void bg_spi_init(struct bg_spi_priv *bg_spi) bg_spi->bg_state = BGCOM_STATE_ACTIVE; bg_com_drv = &bg_spi->lhandle; + + mutex_init(&bg_resume_mutex); } static int bg_spi_probe(struct spi_device *spi) -- GitLab From d43c4366726eac24f8690b92de2a513d34e23b34 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Fri, 12 Jan 2018 13:32:37 +0530 Subject: [PATCH 2369/5498] msm: ipa: Fix to unsigned integer underflow Added code changes to fix the unsigned integer underflow leads to accessing unmapped memory. Change-Id: I8148aebd3597ec6ae8c184199afe816f3d80636e Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c | 10 +++++++++- drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index 460243dced7b..9a7d6ad1a272 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -1454,7 +1454,11 @@ static ssize_t ipa_read_nat4(struct file *file, pr_err("Table Size:%d\n", ipa_ctx->nat_mem.size_base_tables); - pr_err("Expansion Table Size:%d\n", + if (!ipa_ctx->nat_mem.size_expansion_tables) + pr_err("Expansion Table Size:%d\n", + ipa_ctx->nat_mem.size_expansion_tables); + else + pr_err("Expansion Table Size:%d\n", ipa_ctx->nat_mem.size_expansion_tables-1); if (!ipa_ctx->nat_mem.is_sys_mem) @@ -1469,6 +1473,8 @@ static ssize_t ipa_read_nat4(struct file *file, pr_err("\nBase Table:\n"); } else { + if (!ipa_ctx->nat_mem.size_expansion_tables) + continue; tbl_size = ipa_ctx->nat_mem.size_expansion_tables-1; base_tbl = (u32 *)ipa_ctx->nat_mem.ipv4_expansion_rules_addr; @@ -1568,6 +1574,8 @@ static ssize_t ipa_read_nat4(struct file *file, pr_err("\nIndex Table:\n"); } else { + if (!ipa_ctx->nat_mem.size_expansion_tables) + continue; tbl_size = ipa_ctx->nat_mem.size_expansion_tables-1; indx_tbl = (u32 *)ipa_ctx->nat_mem.index_table_expansion_addr; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index 10a1347e1d80..267f4c32816a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -1439,7 +1439,11 @@ static ssize_t ipa3_read_nat4(struct file *file, pr_err("Table Size:%d\n", ipa3_ctx->nat_mem.size_base_tables); - pr_err("Expansion Table Size:%d\n", + if (!ipa3_ctx->nat_mem.size_expansion_tables) + pr_err("Expansion Table Size:%d\n", + ipa3_ctx->nat_mem.size_expansion_tables); + else + pr_err("Expansion Table Size:%d\n", ipa3_ctx->nat_mem.size_expansion_tables-1); if (!ipa3_ctx->nat_mem.is_sys_mem) @@ -1454,6 +1458,8 @@ static ssize_t ipa3_read_nat4(struct file *file, pr_err("\nBase Table:\n"); } else { + if (!ipa3_ctx->nat_mem.size_expansion_tables) + continue; tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1; base_tbl = (u32 *)ipa3_ctx->nat_mem.ipv4_expansion_rules_addr; @@ -1553,6 +1559,8 @@ static ssize_t ipa3_read_nat4(struct file *file, pr_err("\nIndex Table:\n"); } else { + if (!ipa3_ctx->nat_mem.size_expansion_tables) + continue; tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1; indx_tbl = (u32 *)ipa3_ctx->nat_mem.index_table_expansion_addr; -- GitLab From 6ec29312c439459fc0e2df973e12887b673ef0ab Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Mon, 15 Jan 2018 18:43:33 +0800 Subject: [PATCH 2370/5498] ASoC: msm: qdsp6v2: update backend name Some backend names are not there. Delete them from current be_name. When the return value of adm_populate_channel_weight is 0, it should keep running, not return error. Change-Id: I447b81d6edfc89db6cb3742c1719e745c6071c12 Signed-off-by: Meng Wang --- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 58 ++++++++++------------ sound/soc/msm/qdsp6v2/q6adm.c | 4 +- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 50dbc6b95f69..9af1eb9c978c 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2330,37 +2330,31 @@ static const char *const be_name[] = { "INT_FM_RX", "INT_FM_TX", "AFE_PCM_RX", "AFE_PCM_TX", "AUXPCM_RX", "AUXPCM_TX", "VOICE_PLAYBACK_TX", "VOICE2_PLAYBACK_TX", "INCALL_RECORD_RX", "INCALL_RECORD_TX", "MI2S_RX", "MI2S_TX", -"SEC_I2S_RX", "SLIM_1_RX", "SLIM_1_TX", "SLIM_2_RX", -"SLIM_2_TX", "SLIM_3_RX", "SLIM_3_TX", "SLIM_4_RX", -"SLIM_4_TX", "SLIM_5_RX", "SLIM_5_TX", "SLIM_6_RX", -"SLIM_6_TX", "SLIM_7_RX", "SLIM_7_TX", "SLIM_8_RX", -"SLIM_8_TX", "EXTPROC_RX", "EXTPROC_TX", "EXPROC_EC_TX", -"QUAT_MI2S_RX", "QUAT_MI2S_TX", "SECOND_MI2S_RX", "SECOND_MI2S_TX", -"PRI_MI2S_RX", "PRI_MI2S_TX", "TERT_MI2S_RX", "TERT_MI2S_TX", -"AUDIO_I2S_RX", "SEC_AUXPCM_RX", "SEC_AUXPCM_TX", "SPDIF_RX", -"SECOND_MI2S_RX_SD1", "QUIN_MI2S_RX", "QUIN_MI2S_TX", "SENARY_MI2S_TX", -"PRI_TDM_RX_0", "PRI_TDM_TX_0", "PRI_TDM_RX_1", "PRI_TDM_TX_1", -"PRI_TDM_RX_2", "PRI_TDM_TX_2", "PRI_TDM_RX_3", "PRI_TDM_TX_3", -"PRI_TDM_RX_4", "PRI_TDM_TX_4", "PRI_TDM_RX_5", "PRI_TDM_TX_5", -"PRI_TDM_RX_6", "PRI_TDM_TX_6", "PRI_TDM_RX_7", "PRI_TDM_TX_7", -"SEC_TDM_RX_0", "SEC_TDM_TX_0", "SEC_TDM_RX_1", "SEC_TDM_TX_1", -"SEC_TDM_RX_2", "SEC_TDM_TX_2", "SEC_TDM_RX_3", "SEC_TDM_TX_3", -"SEC_TDM_RX_4", "SEC_TDM_TX_4", "SEC_TDM_RX_5", "SEC_TDM_TX_5", -"SEC_TDM_RX_6", "SEC_TDM_TX_6", "SEC_TDM_RX_7", "SEC_TDM_TX_7", -"TERT_TDM_RX_0", "TERT_TDM_TX_0", "TERT_TDM_RX_1", "TERT_TDM_TX_1", -"TERT_TDM_RX_2", "TERT_TDM_TX_2", "TERT_TDM_RX_3", "TERT_TDM_TX_3", -"TERT_TDM_RX_4", "TERT_TDM_TX_4", "TERT_TDM_RX_5", "TERT_TDM_TX_5", -"TERT_TDM_RX_6", "TERT_TDM_TX_6", "TERT_TDM_RX_7", "TERT_TDM_TX_7", -"QUAT_TDM_RX_0", "QUAT_TDM_TX_0", "QUAT_TDM_RX_1", "QUAT_TDM_TX_1", -"QUAT_TDM_RX_2", "QUAT_TDM_TX_2", "QUAT_TDM_RX_3", "QUAT_TDM_TX_3", -"QUAT_TDM_RX_4", "QUAT_TDM_TX_4", "QUAT_TDM_RX_5", "QUAT_TDM_TX_5", -"QUAT_TDM_RX_6", "QUAT_TDM_TX_6", "QUAT_TDM_RX_7", "QUAT_TDM_TX_7", -"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX", -"TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX", -"INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX", -"INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX", -"INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX", -"INT6_MI2S_RX", "INT6_MI2S_TX" +"SEC_I2S_RX", "SLIM_1_RX", "SLIM_1_TX", "SLIM_4_RX", +"SLIM_4_TX", "SLIM_3_RX", "SLIM_3_TX", "SLIM_5_TX", +"EXTPROC_RX", "EXTPROC_TX", "EXPROC_EC_TX", "QUAT_MI2S_RX", +"QUAT_MI2S_TX", "SECOND_MI2S_RX", "SECOND_MI2S_TX", "PRI_MI2S_RX", +"PRI_MI2S_TX", "TERT_MI2S_RX", "TERT_MI2S_TX", "AUDIO_I2S_RX", +"SEC_AUXPCM_RX", "SEC_AUXPCM_TX", "SLIM_6_RX", "SLIM_6_TX", +"SPDIF_RX", "SECOND_MI2S_RX_SD1", "SLIM_5_RX", "QUIN_MI2S_RX", +"QUIN_MI2S_TX", "SENARY_MI2S_TX", "PRI_TDM_RX_0", "PRI_TDM_TX_0", +"PRI_TDM_RX_1", "PRI_TDM_TX_1", "PRI_TDM_RX_2", "PRI_TDM_TX_2", +"PRI_TDM_RX_3", "PRI_TDM_TX_3", "PRI_TDM_RX_4", "PRI_TDM_TX_4", +"PRI_TDM_RX_5", "PRI_TDM_TX_5", "PRI_TDM_RX_6", "PRI_TDM_TX_6", +"PRI_TDM_RX_7", "PRI_TDM_TX_7", "SEC_TDM_RX_0", "SEC_TDM_TX_0", +"SEC_TDM_RX_1", "SEC_TDM_TX_1", "SEC_TDM_RX_2", "SEC_TDM_TX_2", +"SEC_TDM_RX_3", "SEC_TDM_TX_3", "SEC_TDM_RX_4", "SEC_TDM_TX_4", +"SEC_TDM_RX_5", "SEC_TDM_TX_5", "SEC_TDM_RX_6", "SEC_TDM_TX_6", +"SEC_TDM_RX_7", "SEC_TDM_TX_7", "TERT_TDM_RX_0", "TERT_TDM_TX_0", +"TERT_TDM_RX_1", "TERT_TDM_TX_1", "TERT_TDM_RX_2", "TERT_TDM_TX_2", +"TERT_TDM_RX_3", "TERT_TDM_TX_3", "TERT_TDM_RX_4", "TERT_TDM_TX_4", +"TERT_TDM_RX_5", "TERT_TDM_TX_5", "TERT_TDM_RX_6", "TERT_TDM_TX_6", +"TERT_TDM_RX_7", "TERT_TDM_TX_7", "QUAT_TDM_RX_0", "QUAT_TDM_TX_0", +"QUAT_TDM_RX_1", "QUAT_TDM_TX_1", "QUAT_TDM_RX_2", "QUAT_TDM_TX_2", +"QUAT_TDM_RX_3", "QUAT_TDM_TX_3", "QUAT_TDM_RX_4", "QUAT_TDM_TX_4", +"QUAT_TDM_RX_5", "QUAT_TDM_TX_5", "QUAT_TDM_RX_6", "QUAT_TDM_TX_6", +"QUAT_TDM_RX_7", "QUAT_TDM_TX_7", "INT_BT_A2DP_RX", "SLIM_2_TX", +"AFE_LOOPBACK_TX" }; static SOC_ENUM_SINGLE_DECL(mm1_channel_mux, diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index f1917e703d89..6c31314bf548 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -721,7 +721,7 @@ int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, index = index + ch_mixer->input_channels[channel_index]; ret = adm_populate_channel_weight(&adm_pspd_params[index], ch_mixer, channel_index); - if (!ret) { + if (ret) { pr_err("%s: fail to get channel weight with error %d\n", __func__, ret); goto fail_cmd; -- GitLab From c7c00d3c10bbb6d6825596619c09548bb01226df Mon Sep 17 00:00:00 2001 From: Jitendra Sharma Date: Fri, 12 Jan 2018 11:55:55 +0530 Subject: [PATCH 2371/5498] Revert "ARM: dts: msm: Remove modem wdog property for SDX20" This reverts commit 1d218311b7d435809a6e1a6c1d851c04aa96caa9. Reverting this as now changes will be made to register modem wdog in HLOS side Change-Id: Iac20aba15cdf02ba7a15575d1166fff25b1920a5 Signed-off-by: Jitendra Sharma --- arch/arm/boot/dts/qcom/sdx20.dtsi | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/sdx20.dtsi b/arch/arm/boot/dts/qcom/sdx20.dtsi index fac893dbbbea..3a9288e88021 100644 --- a/arch/arm/boot/dts/qcom/sdx20.dtsi +++ b/arch/arm/boot/dts/qcom/sdx20.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -393,7 +393,6 @@ &mss { /delete-property/qcom,qdsp6v61-1-1; qcom,qdsp6v62-1-4; - /delete-property/interrupts; }; &mhi_device { -- GitLab From 7553829eef1dfcd2716e782cb75ea701865df5d0 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 26 Nov 2015 14:00:47 +0200 Subject: [PATCH 2372/5498] mmc: sdio: Fix invalid vdd in voltage switch power cycle [ Upstream commit d9bfbb95ed598a09cf336adb0f190ee0ff802f0d ] The 'ocr' parameter passed to mmc_set_signal_voltage() defines the power-on voltage used when power cycling after a failure to set the voltage. However, in the case of mmc_sdio_init_card(), the value passed has the R4_18V_PRESENT flag set which is not valid for power-on and results in an invalid vdd. Fix by passing the card's ocr value which does not have the flag. Signed-off-by: Adrian Hunter Cc: stable@vger.kernel.org # v3.13+ Signed-off-by: Ulf Hansson Signed-off-by: Sasha Levin Git-commit: e6cbcb008a279d7794fba01799ecfb6be391484f Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git Change-Id: I1d49eed013cabd918e18450593fd4a7dcdf03ef9 Signed-off-by: Veerabhadrarao Badiganti --- drivers/mmc/core/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 486d7c427e3a..5651cf63f8bd 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -742,7 +742,7 @@ try_again: */ if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) { err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, - ocr); + ocr_card); if (err == -EAGAIN) { sdio_reset(host); mmc_go_idle(host); -- GitLab From e26defa54dc54c0121716dcea7119753866a27db Mon Sep 17 00:00:00 2001 From: Vaishnavi Kommaraju Date: Fri, 19 Jan 2018 17:31:10 +0530 Subject: [PATCH 2373/5498] ASoC: wcd_cpe_core: Add mutex lock for CPE session Add mutex lock to ensure atomic access to core handle in CPE alloc and dealloc sessions. CRs-Fixed: 2169403 Change-Id: I7e046f349cc56ee06706cf15651dac3fdfe9d9a6 Signed-off-by: Vaishnavi Kommaraju --- sound/soc/codecs/wcd_cpe_core.c | 13 ++++++++++++- sound/soc/codecs/wcd_cpe_core.h | 5 ++++- sound/soc/codecs/wcd_cpe_services.c | 4 +++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index 0f680b2f0362..195b3d220d5c 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1945,6 +1945,7 @@ struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, init_completion(&core->online_compl); init_waitqueue_head(&core->ssr_entry.offline_poll_wait); mutex_init(&core->ssr_lock); + mutex_init(&core->session_lock); core->cpe_users = 0; core->cpe_clk_ref = 0; @@ -3398,6 +3399,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( * If this is the first session to be allocated, * only then register the afe service. */ + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); if (!wcd_cpe_lsm_session_active()) afe_register_service = true; @@ -3412,6 +3414,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( dev_err(core->dev, "%s: max allowed sessions already allocated\n", __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3420,6 +3423,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( dev_err(core->dev, "%s: Failed to enable cpe, err = %d\n", __func__, ret); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3466,6 +3470,8 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( init_completion(&session->cmd_comp); lsm_sessions[session_id] = session; + + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return session; err_afe_mode_cmd: @@ -3480,6 +3486,7 @@ err_ret: err_session_alloc: wcd_cpe_vote(core, false); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3629,9 +3636,11 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, struct wcd_cpe_core *core = core_handle; int ret = 0; + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); if (!session) { dev_err(core->dev, "%s: Invalid lsm session\n", __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return -EINVAL; } @@ -3642,6 +3651,7 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, "%s: Wrong session id %d max allowed = %d\n", __func__, session->id, WCD_CPE_LSM_MAX_SESSIONS); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return -EINVAL; } @@ -3662,6 +3672,7 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, "%s: Failed to un-vote cpe, err = %d\n", __func__, ret); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return ret; } diff --git a/sound/soc/codecs/wcd_cpe_core.h b/sound/soc/codecs/wcd_cpe_core.h index 77027d58b178..d534b5caee08 100644 --- a/sound/soc/codecs/wcd_cpe_core.h +++ b/sound/soc/codecs/wcd_cpe_core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -169,6 +169,9 @@ struct wcd_cpe_core { /* mutex to protect cpe ssr status variables */ struct mutex ssr_lock; + /* mutex to protect cpe session status variables */ + struct mutex session_lock; + /* Store the calibration data needed for cpe */ struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX]; diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c index 3fff18e49d10..845b4f85782a 100644 --- a/sound/soc/codecs/wcd_cpe_services.c +++ b/sound/soc/codecs/wcd_cpe_services.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -615,8 +615,10 @@ static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info, return CPE_SVC_INVALID_HANDLE; } + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); list_del(&(n->list)); kfree(reg_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); return CPE_SVC_SUCCESS; } -- GitLab From 7a83b561dc5a4b895a298dc8c575a9873753f7bd Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 14:34:38 -0800 Subject: [PATCH 2374/5498] ANDROID: xattr: Pass EOPNOTSUPP to permission2 The permission call for xattr operations happens regardless of whether or not the xattr functions are implemented. The xattr functions currently don't have support for permission2. Passing EOPNOTSUPP as the mount point in xattr_permission allows us to return EOPNOTSUPP early in permission2, if the filesystem supports it. Change-Id: I9d07e4cd633cf40af60450ffbff7ac5c1b4e8c2c Signed-off-by: Daniel Rosenberg Bug: 35848445 --- fs/sdcardfs/inode.c | 2 ++ fs/xattr.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index f33ab0aa7944..5ed714d66b3a 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -627,6 +627,8 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma struct inode tmp; struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode)); + if (IS_ERR(mnt)) + return PTR_ERR(mnt); if (!top) return -EINVAL; diff --git a/fs/xattr.c b/fs/xattr.c index d536edbd377e..f3ae92ffcf1c 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -70,7 +70,7 @@ xattr_permission(struct inode *inode, const char *name, int mask) return -EPERM; } - return inode_permission(inode, mask); + return inode_permission2(ERR_PTR(-EOPNOTSUPP), inode, mask); } /** -- GitLab From c7c6d417f4dbe161a7411cf319d1761654b41419 Mon Sep 17 00:00:00 2001 From: jiangjia Date: Wed, 24 Jan 2018 13:25:34 +0800 Subject: [PATCH 2375/5498] ARM: dts: msm: remove camera's led for msm8909w As there is no led for camera in msm8909w and led_flash crashes system sometimes, so delete the led_flash0. Change-Id: I21a4ebb02243b32a37a887c108c07680e237be8f Signed-off-by: Jiangjiang Shen --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index a550459e9a32..7e373bb516c2 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -290,3 +290,7 @@ asoc-codec = <&stub_codec>, <&msm_digital_codec>; asoc-codec-names = "msm-stub-codec.1", "msm-dig-codec"; }; + +&led_flash0{ + status = "disabled"; +}; -- GitLab From f0b486a101a126890620c79b683380e41ab82b9b Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 27 Dec 2017 19:23:25 +0530 Subject: [PATCH 2376/5498] ASOC: bg: Enable voting for regulator from bg codec driver BG codec driver now votes and removes its vote based on if the system is going in suspend state or resuming. Also once the regulator is enabled, ensure that the BG codec is configured again with init params. Change-Id: I026bec9919c52da03787096a1ddeb0f1d370d539 Signed-off-by: Ashish Jain --- .../bindings/sound/qcom-audio-dev.txt | 1 + sound/soc/codecs/bg_codec.c | 162 +++++++++++++++++- 2 files changed, 154 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 86b293ae29f8..cb593afb9117 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2964,6 +2964,7 @@ Required properties: - compatible :"qcom,bg-cdc-glink" - qcom,msm-glink-channels: Number of glink channels available to communicate with the glink client +- vdd-spkr-supply: BG codec supply's speaker regulator device tree node. Example: diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index 2d5c9f357b68..f5fe90e1eb33 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include "bg_codec.h" #include "pktzr.h" #include "wcdcal-hwdep.h" @@ -45,6 +47,7 @@ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S24_3LE) #define BG_BLOB_DATA_SIZE 3136 +#define SPEAK_VREG_NAME "vdd-spkr" enum { BG_AIF1_PB = 0, @@ -82,15 +85,18 @@ struct bg_cdc_priv { struct delayed_work bg_cdc_pktzr_init_work; unsigned long status_mask; struct bg_hw_params hw_params; + struct notifier_block bg_pm_nb; /* cal info for codec */ struct fw_info *fw_data; struct firmware_cal *hwdep_spk_cal; struct firmware_cal *hwdep_mic_cal; /* Lock to protect init cal */ - struct mutex bg_cal_lock; + struct mutex bg_cdc_lock; int src[NUM_CODEC_DAIS]; bool hwd_started; bool bg_cal_updated; + struct regulator *spkr_vreg; + uint16_t num_sessions; }; struct codec_ssn_rt_setup_t { @@ -149,6 +155,38 @@ static uint32_t get_active_session_id(int dai_id) return active_session; } +static int bg_cdc_enable_regulator(struct regulator *spkr_vreg, bool enable) +{ + int ret = 0; + + if (enable) { + ret = regulator_set_voltage( + spkr_vreg, 1800000, 1800000); + if (ret) { + pr_err("VDD-speaker set voltage failed error=%d\n", + ret); + goto err_vreg_regulator; + } else { + ret = regulator_enable(spkr_vreg); + if (ret) { + pr_err("VDD-speaker voltage failed %d\n", ret); + goto err_vreg_regulator; + } + } + } else { + /* Set regulator to standby mode */ + ret = regulator_disable(spkr_vreg); + if (ret < 0) { + pr_err("Failed to set spkr_vreg mode %d\n", ret); + goto err_vreg_regulator; + } + } + return 0; + +err_vreg_regulator: + return ret; +} + static int bg_cdc_cal(struct bg_cdc_priv *bg_cdc) { u8 *init_params = NULL, *init_head = NULL; @@ -157,7 +195,6 @@ static int bg_cdc_cal(struct bg_cdc_priv *bg_cdc) u32 spk_blob_size = sizeof(smart_pa_init_params); int ret = 0; - mutex_lock(&bg_cdc->bg_cal_lock); init_params = kzalloc(BG_BLOB_DATA_SIZE, GFP_KERNEL); if (!init_params) { ret = -ENOMEM; @@ -231,7 +268,6 @@ err: err1: kfree(init_head); err2: - mutex_unlock(&bg_cdc->bg_cal_lock); return ret; } @@ -241,15 +277,23 @@ static int _bg_codec_hw_params(struct bg_cdc_priv *bg_cdc) struct pktzr_cmd_rsp rsp; int ret = 0; + mutex_lock(&bg_cdc->bg_cdc_lock); if (!bg_cdc->bg_cal_updated) { + ret = bg_cdc_enable_regulator(bg_cdc->spkr_vreg, true); + if (ret < 0) { + pr_err("%s: enable_regulator failed %d\n", __func__, + ret); + goto err; + } ret = bg_cdc_cal(bg_cdc); if (ret < 0) { pr_err("%s:failed to send cal data", __func__); - return ret; + goto err; } } else { pr_debug("%s:cal data already sent to BG", __func__); } + mutex_unlock(&bg_cdc->bg_cdc_lock); rsp.buf_size = sizeof(struct graphite_basic_rsp_result); rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); @@ -262,7 +306,9 @@ static int _bg_codec_hw_params(struct bg_cdc_priv *bg_cdc) pr_err("pktzr cmd set params failed with error %d\n", ret); kfree(rsp.buf); - + return ret; +err: + mutex_unlock(&bg_cdc->bg_cdc_lock); return ret; } @@ -317,8 +363,18 @@ static int _bg_codec_stop(struct bg_cdc_priv *bg_cdc, int dai_id) if (ret < 0) pr_err("pktzr cmd stop failed with error %d\n", ret); - kfree(rsp.buf); + mutex_lock(&bg_cdc->bg_cdc_lock); + if (bg_cdc->num_sessions > 0) + bg_cdc->num_sessions--; + if (bg_cdc->num_sessions == 0) { + /* Reset the regulator mode if this is the last session */ + ret = regulator_set_optimum_mode(bg_cdc->spkr_vreg, 0); + if (ret < 0) + pr_err("Failed to set spkr_vreg mode%d\n", ret); + } + mutex_unlock(&bg_cdc->bg_cdc_lock); + kfree(rsp.buf); return ret; } @@ -573,6 +629,18 @@ static int bg_cdc_prepare(struct snd_pcm_substream *substream, return -EINVAL; } + mutex_lock(&bg_cdc->bg_cdc_lock); + if (bg_cdc->num_sessions == 0) { + /* set regulator to normal mode */ + ret = regulator_set_optimum_mode(bg_cdc->spkr_vreg, 100000); + if (ret < 0) { + pr_err("Fail to set spkr_vreg mode%d\n", ret); + mutex_unlock(&bg_cdc->bg_cdc_lock); + return ret; + } + } + bg_cdc->num_sessions++; + mutex_unlock(&bg_cdc->bg_cdc_lock); /* Send command to BG to start session */ ret = _bg_codec_start(bg_cdc, dai->id); if (ret < 0) @@ -758,12 +826,14 @@ static void bg_cdc_pktzr_init(struct work_struct *work) ret = pktzr_init(bg_cdc->pdev_child, ch_info, 1, data_cmd_rsp); if (ret < 0) { dev_err(bg_cdc->dev, "%s: failed in pktzr_init\n", __func__); - /*return ret*/; + return; } /* Send open command */ rsp.buf_size = sizeof(struct graphite_basic_rsp_result); rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return; memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); /* Send command to BG to start session */ ret = pktzr_cmd_open(&hw_params, sizeof(hw_params), &rsp); @@ -786,7 +856,6 @@ static int bg_cdc_codec_probe(struct snd_soc_codec *codec) sizeof(*(bg_cdc->fw_data)), GFP_KERNEL); bg_cdc->bg_cal_updated = false; - mutex_init(&bg_cdc->bg_cal_lock); set_bit(BG_CODEC_MIC_CAL, bg_cdc->fw_data->cal_bit); set_bit(BG_CODEC_SPEAKER_CAL, bg_cdc->fw_data->cal_bit); @@ -805,11 +874,68 @@ static int bg_cdc_codec_remove(struct snd_soc_codec *codec) struct bg_cdc_priv *bg_cdc = dev_get_drvdata(codec->dev); pr_debug("In func %s\n", __func__); pktzr_deinit(); - mutex_destroy(&bg_cdc->bg_cal_lock); kfree(bg_cdc->fw_data); return 0; } +static int bg_cdc_pm_suspend(struct bg_cdc_priv *bg_cdc) +{ + + /* Do not remove the regulator vote if a session is active */ + if (bg_cdc->num_sessions > 0) { + pr_debug("audio session in progress don't devote\n"); + return 0; + } + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, false); + bg_cdc->bg_cal_updated = false; + return 0; +} + +static int bg_cdc_pm_resume(struct bg_cdc_priv *bg_cdc) +{ + struct bg_hw_params hw_params; + struct pktzr_cmd_rsp rsp; + int ret = 0; + + mutex_lock(&bg_cdc->bg_cdc_lock); + if (!bg_cdc->bg_cal_updated) { + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, true); + /* Send open command */ + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + if (!rsp.buf) + return 0; + memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); + /* Send command to BG to start session */ + ret = pktzr_cmd_open(&hw_params, sizeof(hw_params), &rsp); + if (ret < 0) + pr_err("pktzr cmd open failed\n"); + + if (rsp.buf) + kzfree(rsp.buf); + bg_cdc_cal(bg_cdc); + } + mutex_unlock(&bg_cdc->bg_cdc_lock); + return 0; +} + +static int bg_pm_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct bg_cdc_priv *bg_cdc = + container_of(nb, struct bg_cdc_priv, bg_pm_nb); + switch (event) { + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + return bg_cdc_pm_resume(bg_cdc); + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + return bg_cdc_pm_suspend(bg_cdc); + default: + return NOTIFY_DONE; + } +} + static struct snd_soc_codec_driver soc_codec_dev_bg_cdc = { .probe = bg_cdc_codec_probe, .remove = bg_cdc_codec_remove, @@ -882,6 +1008,18 @@ static int bg_cdc_probe(struct platform_device *pdev) bg_cdc->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, bg_cdc); + if (of_get_property( + pdev->dev.of_node, + SPEAK_VREG_NAME "-supply", NULL)) { + bg_cdc->spkr_vreg = regulator_get(&pdev->dev, SPEAK_VREG_NAME); + if (IS_ERR(bg_cdc->spkr_vreg)) { + ret = PTR_ERR(bg_cdc->spkr_vreg); + pr_err("VDD-speaker get failed error=%d\n", ret); + goto err_cdc_reg; + } + dev_dbg(&pdev->dev, "%s: got regulator handle\n", __func__); + } + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bg_cdc, bg_cdc_dai, ARRAY_SIZE(bg_cdc_dai)); if (ret) { @@ -895,6 +1033,9 @@ static int bg_cdc_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&bg_cdc->bg_cdc_pktzr_init_work, bg_cdc_pktzr_init); schedule_work(&bg_cdc->bg_cdc_add_child_devices_work); + mutex_init(&bg_cdc->bg_cdc_lock); + bg_cdc->bg_pm_nb.notifier_call = bg_pm_event; + register_pm_notifier(&bg_cdc->bg_pm_nb); dev_dbg(&pdev->dev, "%s: BG driver probe done\n", __func__); return ret; @@ -912,6 +1053,9 @@ static int bg_cdc_remove(struct platform_device *pdev) bg_cdc = platform_get_drvdata(pdev); snd_soc_unregister_codec(&pdev->dev); + mutex_destroy(&bg_cdc->bg_cdc_lock); + unregister_pm_notifier(&bg_cdc->bg_pm_nb); + regulator_put(bg_cdc->spkr_vreg); kfree(bg_cdc); return 0; } -- GitLab From 91fb13e39e62e6f8e4aab23e1e9dbbb77158d30b Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 27 Dec 2017 19:26:54 +0530 Subject: [PATCH 2377/5498] ASoC: msm: msm_bg: Remove regulator control from machine driver Moving the control to enable or disable the regulator to codec driver as it also has to configure the codec with initial params. Change-Id: I63f506b09b76af0a8cd1331ebf1fd731a269f2cc Signed-off-by: Ashish Jain Signed-off-by: Swetha Vucha --- sound/soc/msm/msm_bg.c | 46 ++---------------------------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/sound/soc/msm/msm_bg.c b/sound/soc/msm/msm_bg.c index 2a9369ef2de8..d4f62c302142 100644 --- a/sound/soc/msm/msm_bg.c +++ b/sound/soc/msm/msm_bg.c @@ -147,7 +147,6 @@ struct msm8916_asoc_mach_data { void __iomem *vaddr_gpio_mux_pcm_ctl; void __iomem *vaddr_gpio_mux_sec_pcm_ctl; struct device_node *pri_mi2s_gpio_p; - struct regulator *spkr_vreg; }; static inline int param_is_mask(int p) @@ -985,13 +984,6 @@ static int msm_tdm_startup(struct snd_pcm_substream *substream) case AFE_PORT_ID_PRIMARY_TDM_TX_7: atomic_inc(&pdata->primary_tdm_ref_count); if (atomic_read(&pdata->primary_tdm_ref_count) == 1) { - /* Set regulator to normal mode */ - ret = regulator_set_optimum_mode(pdata->spkr_vreg, - 100000); - if (ret < 0) { - pr_err("Failed to set spkr_vreg mode.\n"); - goto err; - } /* Configure mux for Primary TDM */ if (pdata->vaddr_gpio_mux_pcm_ctl) { val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); @@ -1026,7 +1018,7 @@ static void msm_tdm_shutdown(struct snd_pcm_substream *substream) struct snd_soc_card *card = rtd->card; struct msm8916_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); - int ret = 0, val = 0; + int val = 0; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -1049,17 +1041,11 @@ static void msm_tdm_shutdown(struct snd_pcm_substream *substream) if (atomic_read(&pdata->primary_tdm_ref_count) > 0) atomic_dec(&pdata->primary_tdm_ref_count); if (atomic_read(&pdata->primary_tdm_ref_count) == 0) { - /* Set regulator to standby mode */ - ret = regulator_set_optimum_mode(pdata->spkr_vreg, 0); - if (ret < 0) { - pr_err("Failed to set spkr_vreg mode.\n"); - goto err; - } - if (!q6core_is_adsp_ready()) { pr_err("%s(): adsp not ready\n", __func__); goto err; } + /* Reset Configuration of mux for Primary TDM */ if (pdata->vaddr_gpio_mux_pcm_ctl) { val = ioread32(pdata->vaddr_gpio_mux_pcm_ctl); @@ -2238,36 +2224,8 @@ static int msm_bg_asoc_machine_probe(struct platform_device *pdev) ret); goto err; } - if (of_get_property( - pdev->dev.of_node, - SPEAK_VREG_NAME "-supply", NULL)) { - pdata->spkr_vreg = regulator_get(&pdev->dev, SPEAK_VREG_NAME); - if (IS_ERR(pdata->spkr_vreg)) { - ret = PTR_ERR(pdata->spkr_vreg); - pr_err("VDD-speaker get failed error=%d\n", ret); - goto err; - } - - ret = regulator_set_voltage( - pdata->spkr_vreg, 1800000, 1800000); - if (ret) { - pr_err("VDD-speaker set voltage failed error=%d\n", - ret); - goto err_vreg_regulator; - } else { - ret = regulator_enable(pdata->spkr_vreg); - if (ret) { - pr_err("VDD-speaker voltage enable\n" - "\nfailed error=%d\n", ret); - goto err_vreg_regulator; - } - } - } atomic_set(&pdata->primary_tdm_ref_count, 0); return 0; - -err_vreg_regulator: - regulator_put(pdata->spkr_vreg); err: if (pdata->vaddr_gpio_mux_spkr_ctl) iounmap(pdata->vaddr_gpio_mux_spkr_ctl); -- GitLab From f27345089735462085f537428e9e6bfeb44db757 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Wed, 27 Dec 2017 19:31:28 +0530 Subject: [PATCH 2378/5498] ARM: dts: msm: Moved bg_codec node to BG specific file for MSM8909 Move bg_codec node to BG specific file used for MSM8909w.Also add vdd-spkr to bg_codec driver. Change-Id: If1f08969d9a749938c59d2ea596f473e7e0036d2 Signed-off-by: Ashish Jain --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 4 ++-- arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi | 11 ++++++++++- arch/arm/boot/dts/qcom/msm8909-mtp.dtsi | 11 +---------- arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index ed04e197a670..6f5ddb7c6dc7 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -200,11 +200,11 @@ &audio_codec_bg { status = "ok"; - vdd-spkr-supply = <&pm660_l11>; }; &bg_cdc { status = "ok"; + vdd-spkr-supply = <&pm660_l11>; }; &blsp1_uart1 { diff --git a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi index e6a16036f2b1..44ac7a8b3105 100644 --- a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -182,4 +182,13 @@ compatible = "qcom,wcd-dsp-glink"; qcom,msm-codec-glink-edge = "bg"; }; + + bg_cdc: bg_codec { + status = "disabled"; + compatible = "qcom,bg-codec"; + qcom,bg-glink { + compatible = "qcom,bg-cdc-glink"; + qcom,msm-glink-channels = <4>; + }; + }; }; diff --git a/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi index 3e8f6153d484..b1511811408a 100644 --- a/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -285,15 +285,6 @@ asoc-codec = <&stub_codec>, <&pm8909_conga_dig>; asoc-codec-names = "msm-stub-codec.1", "cajon_codec"; }; - - bg_cdc: bg_codec { - status = "disabled"; - compatible = "qcom,bg-codec"; - qcom,bg-glink { - compatible = "qcom,bg-cdc-glink"; - qcom,msm-glink-channels = <4>; - }; - }; }; &blsp1_uart1 { diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 84c3ac427bf5..586868458653 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -189,11 +189,11 @@ &audio_codec_bg { status = "ok"; - vdd-spkr-supply = <&pm660_l11>; }; &bg_cdc { status = "ok"; + vdd-spkr-supply = <&pm660_l11>; }; &i2c_1 { -- GitLab From a70a5148ca65871b14e35868307ee74111b99f67 Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Wed, 24 Jan 2018 15:56:33 +0530 Subject: [PATCH 2379/5498] defconfig: arm: Enable ring buffer logging in mmc driver Enable MMC_CONFIG_RING_BUFFER flag to enable ring buffer logging in mmc driver. Change-Id: I0eaa9987003cdf371e1e42fbf149f4c93f8bfd8c Signed-off-by: Veerabhadrarao Badiganti --- arch/arm/configs/msm8909_defconfig | 1 + arch/arm/configs/msm8937_defconfig | 1 + arch/arm/configs/msmcortex_defconfig | 1 + arch/arm64/configs/msm8937_defconfig | 1 + arch/arm64/configs/msmcortex_defconfig | 1 + 5 files changed, 5 insertions(+) diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index fd24d26e7751..7448772bd9a5 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -419,6 +419,7 @@ CONFIG_USB_G_ANDROID=y CONFIG_MMC=y CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_CLKGATE=y +CONFIG_MMC_RING_BUFFER=y CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y CONFIG_MMC_BLOCK_MINORS=32 diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig index 40135cd4ca32..8117e38ee43c 100644 --- a/arch/arm/configs/msm8937_defconfig +++ b/arch/arm/configs/msm8937_defconfig @@ -485,6 +485,7 @@ CONFIG_USB_G_ANDROID=y CONFIG_MMC=y CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_CLKGATE=y +CONFIG_MMC_RING_BUFFER=y CONFIG_MMC_PARANOID_SD_INIT=y CONFIG_MMC_BLOCK_MINORS=32 CONFIG_MMC_BLOCK_DEFERRED_RESUME=y diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index 8abdef5487f9..7676984730e4 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -470,6 +470,7 @@ CONFIG_USB_G_ANDROID=y CONFIG_MMC=y CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_CLKGATE=y +CONFIG_MMC_RING_BUFFER=y CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y CONFIG_MMC_BLOCK_MINORS=32 diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig index 2c5f551c9d0e..8c62bcbd47ec 100644 --- a/arch/arm64/configs/msm8937_defconfig +++ b/arch/arm64/configs/msm8937_defconfig @@ -483,6 +483,7 @@ CONFIG_USB_G_ANDROID=y CONFIG_MMC=y CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_CLKGATE=y +CONFIG_MMC_RING_BUFFER=y CONFIG_MMC_PARANOID_SD_INIT=y CONFIG_MMC_BLOCK_MINORS=32 CONFIG_MMC_BLOCK_DEFERRED_RESUME=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index e71da71c255e..e872625ba7ac 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -481,6 +481,7 @@ CONFIG_USB_G_ANDROID=y CONFIG_MMC=y CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_CLKGATE=y +CONFIG_MMC_RING_BUFFER=y CONFIG_MMC_PARANOID_SD_INIT=y CONFIG_MMC_BLOCK_MINORS=32 CONFIG_MMC_TEST=m -- GitLab From e7837516a45961eed143110d06128b7e89572cf3 Mon Sep 17 00:00:00 2001 From: Deeraj Soman Date: Fri, 19 Jan 2018 15:57:55 +0530 Subject: [PATCH 2380/5498] ASoC: msm: Add machine driver support for 32bit capture Add machine driver support for 32bit caputre. Change-Id: I9974c4e1a752cce566bb78216b9a9d4e12666341 Signed-off-by: Deeraj Soman --- sound/soc/msm/apq8009-i2s-ext-codec.c | 14 ++++++++++++-- sound/soc/msm/msm8952-slimbus.c | 11 +++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/sound/soc/msm/apq8009-i2s-ext-codec.c b/sound/soc/msm/apq8009-i2s-ext-codec.c index 8b10471d4832..a7126b0beca3 100644 --- a/sound/soc/msm/apq8009-i2s-ext-codec.c +++ b/sound/soc/msm/apq8009-i2s-ext-codec.c @@ -324,7 +324,8 @@ static struct snd_soc_dapm_route wcd9335_audio_paths[] = { {"MIC BIAS4", NULL, "MCLK"}, }; -static char const *rx_bit_format_text[] = {"S16_LE", "S24_3LE", "S24_LE"}; +static char const *rx_bit_format_text[] = {"S16_LE", "S24_3LE", "S24_LE", + "S32_LE"}; static const char *const mi2s_tx_ch_text[] = {"One", "Two", "Three", "Four"}; static char const *pri_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", "KHZ_192", "KHZ_8", @@ -632,6 +633,10 @@ static int mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol, { switch (mi2s_tx_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_LE: ucontrol->value.integer.value[0] = 2; break; @@ -657,6 +662,10 @@ static int mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (ucontrol->value.integer.value[0]) { + case 3: + mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S32_LE; + tx_bits_per_sample = 32; + break; case 2: mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE; tx_bits_per_sample = 32; @@ -1208,7 +1217,8 @@ static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol, } static const struct soc_enum msm_snd_enum[] = { - SOC_ENUM_SINGLE_EXT(3, rx_bit_format_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_bit_format_text), + rx_bit_format_text), SOC_ENUM_SINGLE_EXT(4, mi2s_tx_ch_text), SOC_ENUM_SINGLE_EXT(6, pri_rx_sample_rate_text), SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_ch_text), diff --git a/sound/soc/msm/msm8952-slimbus.c b/sound/soc/msm/msm8952-slimbus.c index 7fccff15cac3..67b66e9f9e43 100644 --- a/sound/soc/msm/msm8952-slimbus.c +++ b/sound/soc/msm/msm8952-slimbus.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1042,6 +1042,9 @@ static int slim0_tx_bit_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (slim0_tx_bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; case SNDRV_PCM_FORMAT_S24_3LE: ucontrol->value.integer.value[0] = 2; break; @@ -1065,6 +1068,9 @@ static int slim0_tx_bit_format_put(struct snd_kcontrol *kcontrol, int rc = 0; switch (ucontrol->value.integer.value[0]) { + case 3: + slim0_tx_bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; case 2: slim0_tx_bit_format = SNDRV_PCM_FORMAT_S24_3LE; break; @@ -1799,7 +1805,8 @@ static const char *const slim4_tx_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; static const char *const vi_feed_ch_text[] = {"One", "Two"}; -static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; static char const *slim0_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", "KHZ_192", "KHZ_44P1", "KHZ_16"}; static char const *slim4_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96", -- GitLab From 94032db885afbe403b4a4fa0ce2b129b5505f28e Mon Sep 17 00:00:00 2001 From: Hyojun Kim Date: Thu, 21 Dec 2017 09:57:41 -0800 Subject: [PATCH 2381/5498] blkdev: Refactoring block io latency histogram codes The current io_latency_state structure includes entries for read and write requests. There are special types of write commands such as sync and discard (trim) commands, and the current implementation is not general enough if we want to separate latency histogram for such special commands. This change makes io_latency_state structure request-type neutral. It also changes to print the latency average. Signed-off-by: Hyojun Kim --- block/blk-core.c | 93 +++++++++++++-------------------------- drivers/mmc/core/core.c | 22 ++++++--- drivers/scsi/ufs/ufshcd.c | 23 ++++++---- drivers/scsi/ufs/ufshcd.h | 3 +- include/linux/blkdev.h | 39 +++++----------- include/linux/mmc/host.h | 3 +- 6 files changed, 75 insertions(+), 108 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 8ac2b531d468..d6f74ddd0234 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -3335,76 +3335,43 @@ int __init blk_dev_init(void) * TODO : If necessary, we can make the histograms per-cpu and aggregate * them when printing them out. */ -void -blk_zero_latency_hist(struct io_latency_state *s) -{ - memset(s->latency_y_axis_read, 0, - sizeof(s->latency_y_axis_read)); - memset(s->latency_y_axis_write, 0, - sizeof(s->latency_y_axis_write)); - s->latency_reads_elems = 0; - s->latency_writes_elems = 0; -} -EXPORT_SYMBOL(blk_zero_latency_hist); - ssize_t -blk_latency_hist_show(struct io_latency_state *s, char *buf) +blk_latency_hist_show(char* name, struct io_latency_state *s, char *buf, + int buf_size) { int i; int bytes_written = 0; u_int64_t num_elem, elem; int pct; - - num_elem = s->latency_reads_elems; - if (num_elem > 0) { - bytes_written += scnprintf(buf + bytes_written, - PAGE_SIZE - bytes_written, - "IO svc_time Read Latency Histogram (n = %llu):\n", - num_elem); - for (i = 0; - i < ARRAY_SIZE(latency_x_axis_us); - i++) { - elem = s->latency_y_axis_read[i]; - pct = div64_u64(elem * 100, num_elem); - bytes_written += scnprintf(buf + bytes_written, - PAGE_SIZE - bytes_written, - "\t< %5lluus%15llu%15d%%\n", - latency_x_axis_us[i], - elem, pct); - } - /* Last element in y-axis table is overflow */ - elem = s->latency_y_axis_read[i]; - pct = div64_u64(elem * 100, num_elem); - bytes_written += scnprintf(buf + bytes_written, - PAGE_SIZE - bytes_written, - "\t> %5dms%15llu%15d%%\n", 10, - elem, pct); - } - num_elem = s->latency_writes_elems; - if (num_elem > 0) { - bytes_written += scnprintf(buf + bytes_written, - PAGE_SIZE - bytes_written, - "IO svc_time Write Latency Histogram (n = %llu):\n", - num_elem); - for (i = 0; - i < ARRAY_SIZE(latency_x_axis_us); - i++) { - elem = s->latency_y_axis_write[i]; - pct = div64_u64(elem * 100, num_elem); - bytes_written += scnprintf(buf + bytes_written, - PAGE_SIZE - bytes_written, - "\t< %5lluus%15llu%15d%%\n", - latency_x_axis_us[i], - elem, pct); - } - /* Last element in y-axis table is overflow */ - elem = s->latency_y_axis_write[i]; - pct = div64_u64(elem * 100, num_elem); - bytes_written += scnprintf(buf + bytes_written, - PAGE_SIZE - bytes_written, - "\t> %5dms%15llu%15d%%\n", 10, - elem, pct); + u_int64_t average; + + num_elem = s->latency_elems; + if (num_elem > 0) { + average = div64_u64(s->latency_sum, s->latency_elems); + bytes_written += scnprintf(buf + bytes_written, + buf_size - bytes_written, + "IO svc_time %s Latency Histogram (n = %llu," + " average = %llu):\n", name, num_elem, average); + for (i = 0; + i < ARRAY_SIZE(latency_x_axis_us); + i++) { + elem = s->latency_y_axis[i]; + pct = div64_u64(elem * 100, num_elem); + bytes_written += scnprintf(buf + bytes_written, + PAGE_SIZE - bytes_written, + "\t< %6lluus%15llu%15d%%\n", + latency_x_axis_us[i], + elem, pct); + } + /* Last element in y-axis table is overflow */ + elem = s->latency_y_axis[i]; + pct = div64_u64(elem * 100, num_elem); + bytes_written += scnprintf(buf + bytes_written, + PAGE_SIZE - bytes_written, + "\t>=%6lluus%15llu%15d%%\n", + latency_x_axis_us[i - 1], elem, pct); } + return bytes_written; } EXPORT_SYMBOL(blk_latency_hist_show); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 8f7f855d2473..ba89f38ad896 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -174,9 +174,10 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) completion = ktime_get(); delta_us = ktime_us_delta(completion, mrq->io_start); - blk_update_latency_hist(&host->io_lat_s, - (mrq->data->flags & MMC_DATA_READ), - delta_us); + blk_update_latency_hist( + (mrq->data->flags & MMC_DATA_READ) ? + &host->io_lat_read : + &host->io_lat_write, delta_us); } #endif trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data); @@ -2826,8 +2827,14 @@ static ssize_t latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mmc_host *host = cls_dev_to_mmc_host(dev); + size_t written_bytes; - return blk_latency_hist_show(&host->io_lat_s, buf); + written_bytes = blk_latency_hist_show("Read", &host->io_lat_read, + buf, PAGE_SIZE); + written_bytes += blk_latency_hist_show("Write", &host->io_lat_write, + buf + written_bytes, PAGE_SIZE - written_bytes); + + return written_bytes; } /* @@ -2845,9 +2852,10 @@ latency_hist_store(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 0, &value)) return -EINVAL; - if (value == BLK_IO_LAT_HIST_ZERO) - blk_zero_latency_hist(&host->io_lat_s); - else if (value == BLK_IO_LAT_HIST_ENABLE || + if (value == BLK_IO_LAT_HIST_ZERO) { + memset(&host->io_lat_read, 0, sizeof(host->io_lat_read)); + memset(&host->io_lat_write, 0, sizeof(host->io_lat_write)); + } else if (value == BLK_IO_LAT_HIST_ENABLE || value == BLK_IO_LAT_HIST_DISABLE) host->latency_hist_enabled = value; return count; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 64b1b2ba3a44..3185707303ae 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3096,10 +3096,10 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) completion = ktime_get(); delta_us = ktime_us_delta(completion, req->lat_hist_io_start); - /* rq_data_dir() => true if WRITE */ - blk_update_latency_hist(&hba->io_lat_s, - (rq_data_dir(req) == READ), - delta_us); + blk_update_latency_hist( + (rq_data_dir(req) == READ) ? + &hba->io_lat_read : + &hba->io_lat_write, delta_us); } } /* Do not touch lrbp after scsi done */ @@ -5291,9 +5291,10 @@ latency_hist_store(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 0, &value)) return -EINVAL; - if (value == BLK_IO_LAT_HIST_ZERO) - blk_zero_latency_hist(&hba->io_lat_s); - else if (value == BLK_IO_LAT_HIST_ENABLE || + if (value == BLK_IO_LAT_HIST_ZERO) { + memset(&hba->io_lat_read, 0, sizeof(hba->io_lat_read)); + memset(&hba->io_lat_write, 0, sizeof(hba->io_lat_write)); + } else if (value == BLK_IO_LAT_HIST_ENABLE || value == BLK_IO_LAT_HIST_DISABLE) hba->latency_hist_enabled = value; return count; @@ -5304,8 +5305,14 @@ latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ufs_hba *hba = dev_get_drvdata(dev); + size_t written_bytes; - return blk_latency_hist_show(&hba->io_lat_s, buf); + written_bytes = blk_latency_hist_show("Read", &hba->io_lat_read, + buf, PAGE_SIZE); + written_bytes += blk_latency_hist_show("Write", &hba->io_lat_write, + buf + written_bytes, PAGE_SIZE - written_bytes); + + return written_bytes; } static DEVICE_ATTR(latency_hist, S_IRUGO | S_IWUSR, diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 241810c83099..ae7b8d8a5604 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -475,7 +475,8 @@ struct ufs_hba { bool is_sys_suspended; int latency_hist_enabled; - struct io_latency_state io_lat_s; + struct io_latency_state io_lat_read; + struct io_latency_state io_lat_write; }; /* Returns true if clocks can be gated. Otherwise false */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 35370ef46364..e0771996a2fa 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1660,43 +1660,26 @@ static const u_int64_t latency_x_axis_us[] = { #define BLK_IO_LAT_HIST_ZERO 2 struct io_latency_state { - u_int64_t latency_y_axis_read[ARRAY_SIZE(latency_x_axis_us) + 1]; - u_int64_t latency_reads_elems; - u_int64_t latency_y_axis_write[ARRAY_SIZE(latency_x_axis_us) + 1]; - u_int64_t latency_writes_elems; + u_int64_t latency_y_axis[ARRAY_SIZE(latency_x_axis_us) + 1]; + u_int64_t latency_elems; + u_int64_t latency_sum; }; static inline void -blk_update_latency_hist(struct io_latency_state *s, - int read, - u_int64_t delta_us) +blk_update_latency_hist(struct io_latency_state *s, u_int64_t delta_us) { int i; - for (i = 0; i < ARRAY_SIZE(latency_x_axis_us); i++) { - if (delta_us < (u_int64_t)latency_x_axis_us[i]) { - if (read) - s->latency_y_axis_read[i]++; - else - s->latency_y_axis_write[i]++; + for (i = 0; i < ARRAY_SIZE(latency_x_axis_us); i++) + if (delta_us < (u_int64_t)latency_x_axis_us[i]) break; - } - } - if (i == ARRAY_SIZE(latency_x_axis_us)) { - /* Overflowed the histogram */ - if (read) - s->latency_y_axis_read[i]++; - else - s->latency_y_axis_write[i]++; - } - if (read) - s->latency_reads_elems++; - else - s->latency_writes_elems++; + s->latency_y_axis[i]++; + s->latency_elems++; + s->latency_sum += delta_us; } -void blk_zero_latency_hist(struct io_latency_state *s); -ssize_t blk_latency_hist_show(struct io_latency_state *s, char *buf); +ssize_t blk_latency_hist_show(char* name, struct io_latency_state *s, + char *buf, int buf_size); #else /* CONFIG_BLOCK */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 8aa0fab5823a..c6c0412d66ea 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -387,7 +387,8 @@ struct mmc_host { #ifdef CONFIG_BLOCK int latency_hist_enabled; - struct io_latency_state io_lat_s; + struct io_latency_state io_lat_read; + struct io_latency_state io_lat_write; #endif unsigned long private[0] ____cacheline_aligned; -- GitLab From 1b369437a1f90beab7c91b2eedd28afa4b0f1908 Mon Sep 17 00:00:00 2001 From: Saranya Chidura Date: Thu, 13 Apr 2017 11:16:07 +0530 Subject: [PATCH 2382/5498] defconfig: msm: add coresight config for perf on apq8053 Add the CORESIGHT CONFIG options for perf defconfig on APQ8053. Change-Id: I3f6192660e5c22d29c9536e4cfca95a494b05f28 Signed-off-by: Saranya Chidura --- arch/arm64/configs/apq8053_IoE-perf_defconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/configs/apq8053_IoE-perf_defconfig b/arch/arm64/configs/apq8053_IoE-perf_defconfig index cc1d1e38c7e2..ab2556f6703f 100644 --- a/arch/arm64/configs/apq8053_IoE-perf_defconfig +++ b/arch/arm64/configs/apq8053_IoE-perf_defconfig @@ -552,6 +552,17 @@ CONFIG_PWM=y CONFIG_PWM_QPNP=y CONFIG_ARM_GIC_PANIC_HANDLER=y CONFIG_ARM_GIC_V3_ACL=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_EVENT=y +CONFIG_CORESIGHT_FUSE=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_CTI_SAVE_DISABLE=y +CONFIG_CORESIGHT_TMC=y +CONFIG_CORESIGHT_TPIU=y +CONFIG_CORESIGHT_FUNNEL=y +CONFIG_CORESIGHT_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_HWEVENT=y CONFIG_SENSORS_SSC=y CONFIG_MSM_TZ_LOG=y CONFIG_EXT4_FS=y -- GitLab From de2542744b759e65ecdd701099b75c6a807e6c35 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 16 Jan 2018 16:12:19 +0530 Subject: [PATCH 2383/5498] ASoC: msm: bg: Fix mutex_unlock to avoid deadlock Ensure mutex is released in case of a failure in glink transaction to avoid deadlock for the next glink transaction. Change-Id: I91b9ce90930b41874f2be937b0469dc93cefad65 Signed-off-by: Ashish Jain --- sound/soc/codecs/pktzr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/pktzr.c b/sound/soc/codecs/pktzr.c index 672c79366158..26e5fa43bda1 100644 --- a/sound/soc/codecs/pktzr.c +++ b/sound/soc/codecs/pktzr.c @@ -137,6 +137,7 @@ static int pktzr_send_pkt(void *payload, uint32_t size, void *rsp, INIT_LIST_HEAD(&pnode->list); pr_debug("ppriv->token = %d\n", ppriv->token); list_add_tail(&pnode->list, &ppriv->ch_list); + mutex_unlock(&ppriv->pktzr_lock); if (cmd == PKTZR_CMD_DATA) rc = bg_cdc_glink_write(ppriv->ch_info[1], pkt_hdr, pkt_size); @@ -150,7 +151,6 @@ static int pktzr_send_pkt(void *payload, uint32_t size, void *rsp, if (sync_cmd) { pr_debug("%s: command sent waiting!\n", __func__); - mutex_unlock(&ppriv->pktzr_lock); rc = wait_for_completion_timeout(&ppriv->thread_complete, MSM_BG_THREAD_TIMEOUT); if (!rc) { @@ -168,10 +168,12 @@ static int pktzr_send_pkt(void *payload, uint32_t size, void *rsp, exit: /* Free memory */ kfree(pkt_hdr); + mutex_lock(&ppriv->pktzr_lock); if (pnode) { list_del(&pnode->list); kfree(pnode); } + mutex_unlock(&ppriv->pktzr_lock); return rc; } -- GitLab From f4f9f8bfcc8887f33055e77094c63af467839459 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Thu, 18 Jan 2018 17:19:27 +0530 Subject: [PATCH 2384/5498] soc: qcom: bgrsb: supports button configuration on BG Buttons can be enabled/disabled on BG. BG sends button event to msm only if it is enabled. Change-Id: Ia44f221728d7eeef07f61c3a686be0d228200f3e Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 83 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index bd9a6bc251a6..a491b2c8632f 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -43,6 +43,7 @@ #define BGRSB_BGWEAR_SUBSYS "bg-wear" +#define BGRSB_BTTN_CONFIGURE 5 #define BGRSB_POWER_CALIBRATION 2 #define BGRSB_POWER_ENABLE 1 #define BGRSB_POWER_DISABLE 0 @@ -92,6 +93,7 @@ struct bgrsb_priv { struct work_struct rsb_down_work; struct work_struct rsb_calibration_work; + struct work_struct bttn_configr_work; struct work_struct glink_work; @@ -118,12 +120,17 @@ struct bgrsb_priv { uint32_t calbrtion_intrvl; uint32_t calbrtion_cpi; + + uint8_t bttn_configs; }; static void *bgrsb_drv; int bgrsb_send_input(struct event *evnt) { + uint8_t press_code; + uint8_t value; + struct bgrsb_priv *dev = container_of(bgrsb_drv, struct bgrsb_priv, lhndl); @@ -133,10 +140,44 @@ int bgrsb_send_input(struct event *evnt) if (evnt->sub_id == 1) { input_report_rel(dev->input, REL_WHEEL, evnt->evnt_data); input_sync(dev->input); - } else - pr_debug("event: type[%d] , data: %d\n", + } else if (evnt->sub_id == 2) { + + press_code = (uint8_t) evnt->evnt_data; + value = (uint8_t) (evnt->evnt_data >> 8); + + switch (press_code) { + case 0x1: + if (value == 0) { + input_report_key(dev->input, KEY_VOLUMEDOWN, 1); + input_sync(dev->input); + } else { + input_report_key(dev->input, KEY_VOLUMEDOWN, 0); + input_sync(dev->input); + } + break; + case 0x2: + if (value == 0) { + input_report_key(dev->input, KEY_VOLUMEUP, 1); + input_sync(dev->input); + } else { + input_report_key(dev->input, KEY_VOLUMEUP, 0); + input_sync(dev->input); + } + break; + case 0x3: + if (value == 0) { + input_report_key(dev->input, KEY_POWER, 1); + input_sync(dev->input); + } else { + input_report_key(dev->input, KEY_POWER, 0); + input_sync(dev->input); + } + break; + default: + pr_info("event: type[%d] , data: %d\n", evnt->sub_id, evnt->evnt_data); - + } + } return 0; } EXPORT_SYMBOL(bgrsb_send_input); @@ -590,6 +631,27 @@ static void bgrsb_calibration(struct work_struct *work) pr_debug("RSB Calibbered\n"); } +static void bgrsb_buttn_configration(struct work_struct *work) +{ + int rc = 0; + struct bgrsb_msg req = {0}; + struct bgrsb_priv *dev = + container_of(work, struct bgrsb_priv, + bttn_configr_work); + + req.cmd_id = 0x05; + req.data = dev->bttn_configs; + + rc = bgrsb_tx_msg(dev, &req, 5); + if (rc != 0) { + pr_err("Failed to send button configuration cmnd to BG\n"); + return; + } + + dev->bttn_configs = 0; + pr_debug("Button configured\n"); +} + static int split_bg_work(struct bgrsb_priv *dev, char *str) { long val; @@ -638,6 +700,18 @@ static int split_bg_work(struct bgrsb_priv *dev, char *str) queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work); break; + case BGRSB_BTTN_CONFIGURE: + tmp = strsep(&str, ":"); + if (!tmp) + return -EINVAL; + + ret = kstrtol(tmp, 10, &val); + if (ret < 0) + return ret; + + dev->bttn_configs = (uint8_t)val; + queue_work(dev->bgrsb_wq, &dev->bttn_configr_work); + break; } return 0; } @@ -711,6 +785,7 @@ static int bgrsb_init(struct bgrsb_priv *dev) INIT_WORK(&dev->rsb_up_work, bgrsb_enable_rsb); INIT_WORK(&dev->rsb_down_work, bgrsb_disable_rsb); INIT_WORK(&dev->rsb_calibration_work, bgrsb_calibration); + INIT_WORK(&dev->bttn_configr_work, bgrsb_buttn_configration); return 0; @@ -745,6 +820,8 @@ static int bg_rsb_probe(struct platform_device *pdev) goto err_ret_dev; input_set_capability(input, EV_REL, REL_WHEEL); + input_set_capability(input, EV_KEY, KEY_VOLUMEUP); + input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN); input->name = "bg-spi"; rc = input_register_device(input); -- GitLab From 4b1c7d07e1b72b38b1b0f11fd965ef52d55109c4 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 12 Jan 2018 14:31:20 -0800 Subject: [PATCH 2385/5498] ANDROID: ext4: don't put symlink in pagecache into highmem This is a partial backport of 21fc61c73c3903c4c312d0802da01ec2b323d174. ext4_encrypted_follow_link uses kmap() for cpage caddr = kmap(cpage); _ext4_fname_disk_to_usr calls virt_to_page on the kmapped address. _ext4_fname_disk_to_usr() ext4_fname_decrypt() sg_init_one() sg_init_one(&src_sg, iname->name, iname->len); sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf)); Bug: 71602077 Change-Id: If86e58dd6126dbe5dd6a234d7ffe71bb638a07cd Signed-off-by: Al Viro Signed-off-by: Jin Qian --- fs/ext4/inode.c | 1 + fs/ext4/namei.c | 1 + fs/ext4/symlink.c | 10 +++------- fs/f2fs/f2fs.h | 5 ----- fs/inode.c | 6 ++++++ include/linux/fs.h | 2 ++ 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 50a26cefbd8b..baa2a8020fc3 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4342,6 +4342,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext4_symlink_inode_operations; ext4_set_aops(inode); } + inode_nohighmem(inode); } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { inode->i_op = &ext4_special_inode_operations; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index c3d581a3e091..7fa9a67ee5c9 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3132,6 +3132,7 @@ static int ext4_symlink(struct inode *dir, if ((disk_link.len > EXT4_N_BLOCKS * 4)) { inode->i_op = &ext4_symlink_inode_operations; + inode_nohighmem(inode); ext4_set_aops(inode); /* * We cannot call page_symlink() with transaction started diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 4f68e83cd25d..b98518e9ba3f 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -49,7 +49,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) cpage = read_mapping_page(inode->i_mapping, 0, NULL); if (IS_ERR(cpage)) return cpage; - caddr = kmap(cpage); + caddr = page_address(cpage); caddr[size] = 0; } @@ -80,16 +80,12 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) if (res <= plen) paddr[res] = '\0'; nd_set_link(nd, paddr); - if (cpage) { - kunmap(cpage); + if (cpage) page_cache_release(cpage); - } return NULL; errout: - if (cpage) { - kunmap(cpage); + if (cpage) page_cache_release(cpage); - } kfree(paddr); return ERR_PTR(res); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2ea0f8447d6f..180056d49b40 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -201,11 +201,6 @@ static inline struct dentry *file_dentry(const struct file *file) return file->f_path.dentry; } -static inline void inode_nohighmem(struct inode *inode) -{ - mapping_set_gfp_mask(inode->i_mapping, GFP_USER); -} - /** * current_time - Return FS time * @inode: inode. diff --git a/fs/inode.c b/fs/inode.c index e906da6eb807..0e1ee20e1838 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1951,3 +1951,9 @@ void inode_set_flags(struct inode *inode, unsigned int flags, new_flags) != old_flags)); } EXPORT_SYMBOL(inode_set_flags); + +void inode_nohighmem(struct inode *inode) +{ + mapping_set_gfp_mask(inode->i_mapping, GFP_USER); +} +EXPORT_SYMBOL(inode_nohighmem); diff --git a/include/linux/fs.h b/include/linux/fs.h index c3bccd812021..e3c98c3626e6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2860,4 +2860,6 @@ static inline bool dir_relax(struct inode *inode) return !IS_DEADDIR(inode); } +extern void inode_nohighmem(struct inode *inode); + #endif /* _LINUX_FS_H */ -- GitLab From 2984f035b004dddafdeecbe8d894d63ee06ca01d Mon Sep 17 00:00:00 2001 From: Baochu Xu Date: Tue, 5 Dec 2017 11:04:45 +0800 Subject: [PATCH 2386/5498] ARM: dts: msm: enable SPI4 node for SDW2500 Enable SPI4 instance to support SPI display. Signed-off-by: Baochu Xu Change-Id: I6ad0337d77733f30e89e3dfe21eac24fe33556cd --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 7e373bb516c2..71f4a9aafa2e 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -73,6 +73,9 @@ /delete-node/ it7260@46; }; + spi@78b8000 { /* BLSP1 QUP4 */ + status = "ok"; + }; qcom,msm-ssc-sensors { compatible = "qcom,msm-ssc-sensors"; -- GitLab From 44522309387efbb260de892df61252bda3775159 Mon Sep 17 00:00:00 2001 From: Greg KH Date: Wed, 8 Mar 2017 19:03:44 +0100 Subject: [PATCH 2387/5498] UPSTREAM: eventpoll.h: add missing epoll event masks [resend due to me forgetting to cc: linux-api the first time around I posted these back on Feb 23] From: Greg Kroah-Hartman For some reason these values are not in the uapi header file, so any libc has to define it themselves. To prevent them from needing to do this, just have the kernel provide the correct values. Reported-by: Elliott Hughes Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 7e040726850a106587485c21bdacc0bfc8a0cbed) Change-Id: I7b1370668faeeb7597288b29dde5bc6d63c95be6 Signed-off-by: Greg Hackmann --- include/uapi/linux/eventpoll.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/uapi/linux/eventpoll.h b/include/uapi/linux/eventpoll.h index bc81fb2e1f0e..6f04cb419115 100644 --- a/include/uapi/linux/eventpoll.h +++ b/include/uapi/linux/eventpoll.h @@ -26,6 +26,19 @@ #define EPOLL_CTL_DEL 2 #define EPOLL_CTL_MOD 3 +/* Epoll event masks */ +#define EPOLLIN 0x00000001 +#define EPOLLPRI 0x00000002 +#define EPOLLOUT 0x00000004 +#define EPOLLERR 0x00000008 +#define EPOLLHUP 0x00000010 +#define EPOLLRDNORM 0x00000040 +#define EPOLLRDBAND 0x00000080 +#define EPOLLWRNORM 0x00000100 +#define EPOLLWRBAND 0x00000200 +#define EPOLLMSG 0x00000400 +#define EPOLLRDHUP 0x00002000 + /* * Request the handling of system wakeup events so as to prevent system suspends * from happening while those events are being processed. -- GitLab From 06cf5aff2301442b6ec345266f1993d5b03fd0a6 Mon Sep 17 00:00:00 2001 From: Tim Jiang Date: Fri, 1 Dec 2017 03:22:18 -0500 Subject: [PATCH 2388/5498] ARM: dts: msm: Enable QCA9379 bluetooth device for apq8009 Enable blsp1_uart2_hs which provide communication between QCA9379 BT and HOST. BT enable is controlled through msm gpio 47. So, add device node for it. Change-Id: I9fb7b5afb26db1faa3def0c068bc01a143a76c84 Signed-off-by: Tim Jiang --- ...pq8009-mtp-wcd9326-excelpoint-refboard.dts | 83 ++++++++++++++++++ .../dts/qcom/apq8009-mtp-wcd9326-refboard.dts | 85 ++++++++++++++++++- 2 files changed, 167 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts index aad63d5e567b..6a168bd74859 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts @@ -30,6 +30,34 @@ status = "disabled"; }; +&msm_gpio { + hsuart_active: default { + mux { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + function = "blsp_uart2"; + }; + + config { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + drive-strength = <16>; + bias-disable; + }; + }; + + hsuart_sleep: sleep { + mux { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + function = "blsp_uart2"; + }; + + config { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + drive-strength = <2>; + bias-disable; + }; + }; +}; + &soc { sound-9335 { qcom,model = "apq8009-tashalite-snd-card-tdm"; @@ -83,6 +111,45 @@ gpio = <&pm8916_gpios 4 0>; vin-supply = <&pm8916_s4>; }; + + blsp1_uart2_hs: uart@78b0000 { + compatible = "qcom,msm-hsuart-v14"; + reg = <0x78b0000 0x200>, + <0x7884000 0x1f000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp1_uart2_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 108 0 + 1 &intc 0 238 0 + 2 &msm_gpio 21 0>; + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xfd>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc clk_gcc_blsp1_uart2_apps_clk>, + <&clock_gcc clk_gcc_blsp1_ahb_clk>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&hsuart_sleep>; + pinctrl-1 = <&hsuart_active>; + qcom,bam-tx-ep-pipe-index = <2>; + qcom,bam-rx-ep-pipe-index = <3>; + qcom,msm-bus,name = "blsp1_uart2_hs"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + status = "ok"; + }; + + bluetooth: bt_qca9379 { + compatible = "qca,qca9379"; + qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */ + }; }; &i2c_4 { @@ -150,4 +217,20 @@ status = "disabled"; }; +&i2c_1 { + status = "disabled"; +}; + +&i2c_2 { + status = "disabled"; +}; + +&i2c_5 { + status = "disabled"; +}; + +&spi_0 { + status = "disabled"; +}; + /delete-node/ &cont_splash_mem; diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts index d5a6304165f6..397f22ca48fe 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -30,6 +30,34 @@ status = "disabled"; }; +&msm_gpio { + hsuart_active: default { + mux { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + function = "blsp_uart2"; + }; + + config { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + drive-strength = <16>; + bias-disable; + }; + }; + + hsuart_sleep: sleep { + mux { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + function = "blsp_uart2"; + }; + + config { + pins = "gpio20", "gpio21", "gpio111", "gpio112"; + drive-strength = <2>; + bias-disable; + }; + }; +}; + &soc { sound-9335 { qcom,audio-routing = @@ -61,6 +89,45 @@ status = "disabled"; }; }; + + blsp1_uart2_hs: uart@78b0000 { + compatible = "qcom,msm-hsuart-v14"; + reg = <0x78b0000 0x200>, + <0x7884000 0x1f000>; + reg-names = "core_mem", "bam_mem"; + interrupt-names = "core_irq", "bam_irq", "wakeup_irq"; + #address-cells = <0>; + interrupt-parent = <&blsp1_uart2_hs>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 108 0 + 1 &intc 0 238 0 + 2 &msm_gpio 21 0>; + qcom,inject-rx-on-wakeup; + qcom,rx-char-to-inject = <0xfd>; + qcom,master-id = <86>; + clock-names = "core_clk", "iface_clk"; + clocks = <&clock_gcc clk_gcc_blsp1_uart2_apps_clk>, + <&clock_gcc clk_gcc_blsp1_ahb_clk>; + pinctrl-names = "sleep", "default"; + pinctrl-0 = <&hsuart_sleep>; + pinctrl-1 = <&hsuart_active>; + qcom,bam-tx-ep-pipe-index = <2>; + qcom,bam-rx-ep-pipe-index = <3>; + qcom,msm-bus,name = "blsp1_uart2_hs"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <86 512 0 0>, + <86 512 500 800>; + status = "ok"; + }; + + bluetooth: bt_qca9379 { + compatible = "qca,qca9379"; + qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */ + }; }; &i2c_4 { @@ -117,4 +184,20 @@ status = "disabled"; }; +&i2c_1 { + status = "disabled"; +}; + +&i2c_2 { + status = "disabled"; +}; + +&i2c_5 { + status = "disabled"; +}; + +&spi_0 { + status = "disabled"; +}; + /delete-node/ &cont_splash_mem; -- GitLab From 8b23b7e40655543bf2a12da6abef757a7dc482d4 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Tue, 23 Jan 2018 17:33:02 +0530 Subject: [PATCH 2389/5498] msm: ipa: dynamic memory leak fix This is a fix for dynamic memory leak seen with incorrectly allocating memory of a different size than with intended size. Change-Id: I350719dadad9fd5c7f35a334e81c8d9f2298f888 Acked-by: Jyothi Jayanthi Acked-by: Ashok Vuyyuru Signed-off-by: Michael Adisumarta Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c index f7ac631dbb1f..797f616c69df 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -581,7 +581,8 @@ static void ipa_save_uc_smmu_mapping_pa(int res_idx, phys_addr_t pa, { IPADBG("--res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx, &pa, iova, len); - wdi_res[res_idx].res = kzalloc(sizeof(struct ipa_wdi_res), GFP_KERNEL); + wdi_res[res_idx].res = kzalloc(sizeof(*wdi_res[res_idx].res), + GFP_KERNEL); if (!wdi_res[res_idx].res) BUG(); wdi_res[res_idx].nents = 1; @@ -607,8 +608,8 @@ static void ipa_save_uc_smmu_mapping_sgt(int res_idx, struct sg_table *sgt, return; } - wdi_res[res_idx].res = kcalloc(sgt->nents, sizeof(struct ipa_wdi_res), - GFP_KERNEL); + wdi_res[res_idx].res = kcalloc(sgt->nents, + sizeof(*wdi_res[res_idx].res), GFP_KERNEL); if (!wdi_res[res_idx].res) BUG(); wdi_res[res_idx].nents = sgt->nents; -- GitLab From 830008f596ee9584d1232970001773f5b80c5efc Mon Sep 17 00:00:00 2001 From: Archana Sriram Date: Wed, 24 Jan 2018 13:20:26 +0530 Subject: [PATCH 2390/5498] ARM: dts: msm: Enable bus control for GPU on msm8909 Enabled bus control for GPU on msm8909 so that devfreq governor is used to vote on behalf of GPU through bw voting device gpubw-dev instead of GPU directly voting on msm-bus through grp3d device. Change-Id: I0a7dabdfd23f5a3122a270449ee1cc46f328847d Signed-off-by: Archana Sriram --- arch/arm/boot/dts/qcom/msm8909-gpu.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi index 44684d317bb0..2943d5d7b310 100644 --- a/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-gpu.dtsi @@ -75,6 +75,7 @@ /* Bus Scale Settings */ qcom,gpubw-dev = <&gpubw>; + qcom,bus-control; qcom,msm-bus,name = "grp3d"; qcom,msm-bus,num-cases = <4>; qcom,msm-bus,num-paths = <1>; -- GitLab From cc212d6e7308f19241ccfae0cf0d78e2998b77c6 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Thu, 25 Jan 2018 17:17:51 +0530 Subject: [PATCH 2391/5498] ASoC: bg: Ensure to check vote for regulator Ensure disabling of regulator done only when it is already enabled. Change-Id: Ibd9b7de7f8fef32e3087c2fac2cccf5486dab390 Signed-off-by: Bala Kishore Pati --- sound/soc/codecs/bg_codec.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index f5fe90e1eb33..b6135763bc87 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -288,7 +288,7 @@ static int _bg_codec_hw_params(struct bg_cdc_priv *bg_cdc) ret = bg_cdc_cal(bg_cdc); if (ret < 0) { pr_err("%s:failed to send cal data", __func__); - goto err; + goto err1; } } else { pr_debug("%s:cal data already sent to BG", __func__); @@ -307,6 +307,8 @@ static int _bg_codec_hw_params(struct bg_cdc_priv *bg_cdc) kfree(rsp.buf); return ret; +err1: + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, false); err: mutex_unlock(&bg_cdc->bg_cdc_lock); return ret; @@ -886,8 +888,10 @@ static int bg_cdc_pm_suspend(struct bg_cdc_priv *bg_cdc) pr_debug("audio session in progress don't devote\n"); return 0; } - bg_cdc_enable_regulator(bg_cdc->spkr_vreg, false); - bg_cdc->bg_cal_updated = false; + if (bg_cdc->bg_cal_updated) { + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, false); + bg_cdc->bg_cal_updated = false; + } return 0; } @@ -904,19 +908,29 @@ static int bg_cdc_pm_resume(struct bg_cdc_priv *bg_cdc) rsp.buf_size = sizeof(struct graphite_basic_rsp_result); rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); if (!rsp.buf) - return 0; + goto err; memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); /* Send command to BG to start session */ ret = pktzr_cmd_open(&hw_params, sizeof(hw_params), &rsp); - if (ret < 0) + if (ret < 0) { pr_err("pktzr cmd open failed\n"); - - if (rsp.buf) - kzfree(rsp.buf); - bg_cdc_cal(bg_cdc); + goto err1; + } + ret = bg_cdc_cal(bg_cdc); + if (ret < 0) { + pr_err("calibiration failed\n"); + goto err1; + } + kfree(rsp.buf); } mutex_unlock(&bg_cdc->bg_cdc_lock); return 0; +err1: + kfree(rsp.buf); +err: + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, false); + mutex_unlock(&bg_cdc->bg_cdc_lock); + return 0; } static int bg_pm_event(struct notifier_block *nb, -- GitLab From bf9148fbb54a75d174ce94ed17bfc4a537d1f19f Mon Sep 17 00:00:00 2001 From: Venkata Prahlad Valluru Date: Wed, 10 Jan 2018 14:33:45 +0530 Subject: [PATCH 2392/5498] ARM: dts: msm: disable internal pull up for 8909w BG Touchscreen interrupt line is provided with external pull-up. Pull down during touchscreen suspend causes leakage current. Disable bias so that external pull-up alone drive the line. Change-Id: Ic9fe4758de9c0912e98406107001dfb059faa11b Signed-off-by: Venkata Prahlad Valluru --- arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 2 ++ arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index acbd20c2075d..4b27a2b74515 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -236,6 +236,8 @@ config { pins = "gpio98"; + /delete-property/ bias-pull-down; + bias-disable; /* No PULL */ }; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 7ad3d1c25be5..5d72b455fee8 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -266,6 +266,8 @@ config { pins = "gpio98"; + /delete-property/ bias-pull-down; + bias-disable; /* No PULL */ }; }; -- GitLab From 22eeba34db6309bfa8b5cb11502478816b1aca41 Mon Sep 17 00:00:00 2001 From: gaolez Date: Mon, 29 Jan 2018 14:01:06 +0800 Subject: [PATCH 2393/5498] msm: wlan: ETSI13 shall support 144 channel ETSI13 shall support 144 channel Change-Id: I620c8487d4754d79e0b8af25695a3b85c057a85f Signed-off-by: Gaole Zhang --- net/wireless/db.txt | 184 ++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/net/wireless/db.txt b/net/wireless/db.txt index d9774a51ab5a..5e0ffb1f2135 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -39,8 +39,8 @@ country AL: DFS-ETSI (2402 - 2482 @ 40), (20) (5150 - 5250 @ 80), (23), AUTO-BW (5250 - 5350 @ 80), (23), DFS, AUTO-BW - (5470 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5470 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country AM: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -51,8 +51,8 @@ country AN: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country AR: (2402 - 2482 @ 40), (36) @@ -73,8 +73,8 @@ country AT: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -101,8 +101,8 @@ country BA: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -120,8 +120,8 @@ country BE: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -136,8 +136,8 @@ country BG: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -220,8 +220,8 @@ country CH: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country CI: DFS-FCC (2402 - 2482 @ 40), (20) @@ -271,8 +271,8 @@ country CY: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -284,8 +284,8 @@ country CZ: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -306,8 +306,8 @@ country DE: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -315,8 +315,8 @@ country DK: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -349,8 +349,8 @@ country EE: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -363,8 +363,8 @@ country ES: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -378,8 +378,8 @@ country FI: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -394,8 +394,8 @@ country FR: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -403,8 +403,8 @@ country GB: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -426,8 +426,8 @@ country GF: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country GH: DFS-FCC (2402 - 2482 @ 40), (20) @@ -458,8 +458,8 @@ country GR: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -497,8 +497,8 @@ country HR: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -513,8 +513,8 @@ country HU: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -527,8 +527,8 @@ country IE: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -552,8 +552,8 @@ country IS: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -561,8 +561,8 @@ country IT: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -649,8 +649,8 @@ country LI: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country LK: DFS-FCC (2402 - 2482 @ 40), (20) @@ -669,8 +669,8 @@ country LT: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -678,8 +678,8 @@ country LU: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -687,8 +687,8 @@ country LV: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -701,22 +701,22 @@ country MC: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MD: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country ME: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MF: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -735,8 +735,8 @@ country MK: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -765,8 +765,8 @@ country MQ: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MR: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -778,8 +778,8 @@ country MT: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -839,8 +839,8 @@ country NL: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -848,8 +848,8 @@ country NO: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -888,8 +888,8 @@ country PF: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country PG: DFS-FCC (2402 - 2482 @ 40), (20) @@ -913,8 +913,8 @@ country PL: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -922,8 +922,8 @@ country PM: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country PR: DFS-FCC (2402 - 2472 @ 40), (30) @@ -936,8 +936,8 @@ country PT: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -969,8 +969,8 @@ country RO: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -980,8 +980,8 @@ country RS: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1010,8 +1010,8 @@ country SE: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1026,8 +1026,8 @@ country SI: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1035,8 +1035,8 @@ country SK: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1093,8 +1093,8 @@ country TR: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (40), NO-OUTDOOR @@ -1177,8 +1177,8 @@ country VC: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5725 - 5875 @ 80), (14) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country VE: DFS-FCC (2402 - 2482 @ 40), (30) -- GitLab From 94b2ae997e9774d10d2d88e23cfb3e0fe4449d6f Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Wed, 10 Jan 2018 11:52:37 +0530 Subject: [PATCH 2394/5498] arm: traps: emulate a MRRC instruction reading CNTVCT register In addition to emulating CNTPCT access, emulate CNTVCT access too, so that userspace can get CNTVCT value, if the direct counter read is disabled. Also, keep direct access disabled by default for userspace. Change-Id: I70263c129386314880cb28d1e561146ce62d52b8 Signed-off-by: Neeraj Upadhyay --- arch/arm/include/asm/traps.h | 2 +- arch/arm/kernel/traps.c | 24 +++++++++++++----------- arch/arm64/include/asm/traps.h | 2 +- drivers/clocksource/Kconfig | 2 +- drivers/clocksource/arm_arch_timer.c | 2 +- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h index f9e412b97fbf..91aba35a3f5a 100644 --- a/arch/arm/include/asm/traps.h +++ b/arch/arm/include/asm/traps.h @@ -46,7 +46,7 @@ static inline int in_exception_text(unsigned long ptr) return in ? : __in_irqentry_text(ptr); } -extern void get_pct_hook_init(void); +extern void get_timer_count_hook_init(void); extern void __init early_trap_init(void *); extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame); extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index e1678962cbf4..5567ec9ce6e5 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -764,12 +764,13 @@ late_initcall(arm_mrc_hook_init); #endif -static int get_pct_trap(struct pt_regs *regs, unsigned int instr) +static int get_timer_count_trap(struct pt_regs *regs, unsigned int instr) { - u64 cntpct; + u64 cval; unsigned int res; int rd = (instr >> 12) & 0xF; int rn = (instr >> 16) & 0xF; + int read_virtual = (instr >> 4) & 1; res = arm_check_condition(instr, regs->ARM_cpsr); if (res == ARM_OPCODE_CONDTEST_FAIL) { @@ -779,26 +780,27 @@ static int get_pct_trap(struct pt_regs *regs, unsigned int instr) if (rd == 15 || rn == 15) return 1; - cntpct = arch_counter_get_cntpct(); - regs->uregs[rd] = cntpct; - regs->uregs[rn] = cntpct >> 32; + cval = read_virtual ? + arch_counter_get_cntvct() : arch_counter_get_cntpct(); + regs->uregs[rd] = cval; + regs->uregs[rn] = cval >> 32; regs->ARM_pc += 4; return 0; } -static struct undef_hook get_pct_hook = { - .instr_mask = 0x0ff00fff, +static struct undef_hook get_timer_count_hook = { + .instr_mask = 0x0ff00fef, .instr_val = 0x0c500f0e, .cpsr_mask = MODE_MASK, .cpsr_val = USR_MODE, - .fn = get_pct_trap, + .fn = get_timer_count_trap, }; -void get_pct_hook_init(void) +void get_timer_count_hook_init(void) { - register_undef_hook(&get_pct_hook); + register_undef_hook(&get_timer_count_hook); } -EXPORT_SYMBOL(get_pct_hook_init); +EXPORT_SYMBOL(get_timer_count_hook_init); void __bad_xchg(volatile void *ptr, int size) { diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h index 4a952d92bd12..dee1900254c5 100644 --- a/arch/arm64/include/asm/traps.h +++ b/arch/arm64/include/asm/traps.h @@ -43,5 +43,5 @@ static inline int in_exception_text(unsigned long ptr) ptr < (unsigned long)&__exception_text_end; } -static inline void get_pct_hook_init(void) {} +static inline void get_timer_count_hook_init(void) {} #endif diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 2a6b2a8e1578..497b2f5434a1 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -112,7 +112,7 @@ config ARM_ARCH_TIMER_EVTSTREAM config ARM_ARCH_TIMER_VCT_ACCESS bool "Support for ARM architected timer virtual counter access in userspace" - default !ARM64 + default n depends on ARM_ARCH_TIMER help This option enables support for reading the ARM architected timer's diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index ef939675c9ca..8e5810ffb813 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -832,7 +832,7 @@ static void __init arch_timer_mem_init(struct device_node *np) arch_timer_detect_rate(base, np); arch_timer_mem_register(base, irq); arch_timer_common_init(); - get_pct_hook_init(); + get_timer_count_hook_init(); } CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", arch_timer_mem_init); -- GitLab From 165268058f4ab00ffb65baba7296bb43dde2da6b Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Fri, 1 Dec 2017 14:49:04 +0530 Subject: [PATCH 2395/5498] ARM: dts: msm: Enable USB in peripheral by default for APQ8009 Changes to enable certain variants of APQ8009 by default in peripheral mode. Change-Id: I75369a8c0cf7a36c2950ac1f1c3127a0f87dd2e2 Signed-off-by: Sriharsha Allenki --- .../dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts | 4 +++- arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts index aad63d5e567b..f6f6c031b2a0 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts @@ -129,7 +129,9 @@ interrupts = <0 134 0>,<0 140 0>,<0 136 0>; interrupt-names = "core_irq", "async_irq", "phy_irq"; - qcom,hsusb-otg-mode = <3>; + qcom,hsusb-otg-mode = <1>; /* peripheral mode */ + qcom,hsusb-otg-otg-control= <3>; + qcom,hsusb-otg-default-mode= <1>; /* peripheral mode */ vbus_otg-supply = <&smb1360_otg_supply>; }; diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts index d5a6304165f6..a70cf870dff9 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -96,7 +96,9 @@ interrupts = <0 134 0>,<0 140 0>,<0 136 0>; interrupt-names = "core_irq", "async_irq", "phy_irq"; - qcom,hsusb-otg-mode = <3>; + qcom,hsusb-otg-mode = <1>; /* peripheral mode */ + qcom,hsusb-otg-otg-control= <3>; + qcom,hsusb-otg-default-mode= <1>; /* peripheral mode */ vbus_otg-supply = <&smb1360_otg_supply>; }; -- GitLab From cf0bcd96596343e90deab27d5453e5ec15359cfa Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Fri, 5 Jan 2018 11:22:03 +0530 Subject: [PATCH 2396/5498] mmc: sdhci-msm: Remove printing tlmm physical address Remove the debug log which leaks physical address, eventhough tlmm is no longer used. Change-Id: Ie6968c46ea063e9192e2ccef053107ee7d8f5775 Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 57284d270a5a..0de2f3965b55 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2,7 +2,7 @@ * drivers/mmc/host/sdhci-msm.c - Qualcomm MSM SDHCI Platform * driver source file * - * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -4157,8 +4157,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto vreg_deinit; } writel_relaxed(readl_relaxed(tlmm_mem) | 0x2, tlmm_mem); - dev_dbg(&pdev->dev, "tlmm reg %pa value 0x%08x\n", - &tlmm_memres->start, readl_relaxed(tlmm_mem)); } /* -- GitLab From 016500b4546ba0642cc7f79e700543e3c4ca07ae Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Mon, 11 Dec 2017 10:50:29 +0530 Subject: [PATCH 2397/5498] mmc: cmdq-hci: Change unnecessary pr_err logs to pr_debug Now that CMDQ device and driver are stable, Change unnecessary pr_err logs to pr_debug. Also use %pK instead of %p while printing addresses to avoid leaking of kernel addresses. Change-Id: I95683ffe0410daef86dd9a376f5769d8f40ae990 Signed-off-by: Vijay Viswanath --- drivers/mmc/host/cmdq_hci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index 7e079e628c20..00bc37c1f891 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -331,7 +331,7 @@ static int cmdq_host_alloc_tdl(struct cmdq_host *cq_host) if (!cq_host->desc_base || !cq_host->trans_desc_base) return -ENOMEM; - pr_info("desc-base: 0x%p trans-base: 0x%p\n desc_dma 0x%llx trans_dma: 0x%llx\n", + pr_debug("desc-base: 0x%pK trans-base: 0x%pK\n desc_dma 0x%llx trans_dma: 0x%llx\n", cq_host->desc_base, cq_host->trans_desc_base, (unsigned long long)cq_host->desc_dma_base, (unsigned long long) cq_host->trans_desc_dma_base); -- GitLab From 22d2388c717eac7f12d004371af2106ff1f13e08 Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Mon, 11 Dec 2017 10:52:49 +0530 Subject: [PATCH 2398/5498] mmc: sdhci-msm: Avoid leaking kernel address from mmc platform driver Remove printing the address of sdhci_msm driver variables to avoid leaking the kernel addresses. This is no longer required as the sdhci_msm driver has become stable. Change-Id: I88d9d66d9d00adad8ec79df689ae80f4aae97b7e Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 57284d270a5a..6a3542cf08a3 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -3657,11 +3657,10 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host, group->latency = latency[i].latency[SDHCI_PERFORMANCE_MODE]; pm_qos_add_request(&group->req, PM_QOS_CPU_DMA_LATENCY, group->latency); - pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d (0x%p)\n", + pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d\n", __func__, i, group->req.cpus_affine.bits[0], - group->latency, - &latency[i].latency[SDHCI_PERFORMANCE_MODE]); + group->latency); } msm_host->pm_qos_prev_cpu = -1; msm_host->pm_qos_group_enable = true; -- GitLab From d4bf4f57cd89417ce652cb12b8b63c7c3b74d979 Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Thu, 25 Jan 2018 11:57:25 +0530 Subject: [PATCH 2399/5498] phy-msm-qusb-v2: Add change_dpdm callback for USB2 PHY Add change_dpdm callback for phy-msmqusb-v2.c driver so that PMIC calls this callback before charger detection through USB power supply. This is required to remove pull downs from QUSB2 PHY to avoid interference while PMIC doing charger detection. Change-Id: If155230f151154188d2a61d96f29622ea40baf49 Signed-off-by: Vijayavardhan Vennapusa --- drivers/usb/phy/phy-msm-qusb-v2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index 27cb9d4650ae..65ca1b8913cc 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1046,6 +1046,7 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.init = qusb_phy_init; qphy->phy.set_suspend = qusb_phy_set_suspend; qphy->phy.shutdown = qusb_phy_shutdown; + qphy->phy.change_dpdm = qusb_phy_update_dpdm; qphy->phy.type = USB_PHY_TYPE_USB2; qphy->phy.notify_connect = qusb_phy_notify_connect; qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; -- GitLab From 79cb3360176ace71fdb1b46119e31c7f0661ebda Mon Sep 17 00:00:00 2001 From: Vijayanand Jitta Date: Mon, 29 Jan 2018 10:44:39 +0530 Subject: [PATCH 2400/5498] ARM: dts: msm: Reduce modem carveout size for apq8053 Reduce modem carveout size from 30 MB to 24 MB due to modem optimizations for apq8053. Change-Id: Ifdd10ed187fb2ec3fc88a198ee8f7e5aa5732a61 Signed-off-by: Vijayanand Jitta --- arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi b/arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi index f6e26b223bf0..4a6f4a39cd49 100644 --- a/arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi +++ b/arch/arm/boot/dts/qcom/apq8053-iot-memory.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,14 +12,14 @@ */ &modem_mem { - reg = <0x0 0x86c00000 0x0 0x1e00000>; + reg = <0x0 0x86c00000 0x0 0x1800000>; }; &adsp_fw_mem { - reg = <0x0 0x88a00000 0x0 0x1100000>; + reg = <0x0 0x88400000 0x0 0x1100000>; }; &wcnss_fw_mem { - reg = <0x0 0x89b00000 0x0 0x700000>; + reg = <0x0 0x89500000 0x0 0x700000>; }; -- GitLab From b15d4600ddebb5198587e22d84cc462af1a3c05f Mon Sep 17 00:00:00 2001 From: Rohit Rangwani Date: Tue, 30 Jan 2018 17:01:39 +0530 Subject: [PATCH 2401/5498] ARM: dts: msm: Add clk entry for NFC on 8909w-wtp Add LNBBCLK3 pin clock entry for NFC on msm8909w-wtp platform. Change-Id: I3752a80871b3a040b7d66f7a3923218b9b5d6fdc Signed-off-by: Rohit Rangwani --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 71f4a9aafa2e..7176aff53fe0 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -165,6 +165,7 @@ pinctrl-names = "nfc_active","nfc_suspend"; pinctrl-0 = <&nfcw_int_active &nfcv2k_disable_active>; pinctrl-1 = <&nfcw_int_suspend &nfcv2k_disable_suspend>; + clocks = <&clock_rpm clk_bb_clk3_pin>; clock-names = "ref_clk"; }; }; -- GitLab From 4f9498e4a2865f22b8fbadfa0bfc734e47ab64e1 Mon Sep 17 00:00:00 2001 From: Chen Neng Date: Thu, 25 Jan 2018 20:41:07 +0800 Subject: [PATCH 2402/5498] ARM: dts: msm: remove actuator probe during bootup to avoid latency issue The 8909w doesn't have actuator in the camera modules, so no actuator device tree should be configured in device tree. The actuator probe process will also add some time in bootup and control the power supply, so remove the actuator config in 8909w. Change-Id: I02e8579330b30248ead936ffd6b6a3da1868825f Signed-off-by: Neng Chen --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 7e373bb516c2..ad6bc1b2dbf4 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -294,3 +294,7 @@ &led_flash0{ status = "disabled"; }; + +&actuator0{ + status = "disabled"; +}; -- GitLab From 3758d94e951332186183a6e20bfac257183965da Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Apr 2016 17:35:29 +0200 Subject: [PATCH 2403/5498] gcov: disable for COMPILE_TEST commit cc622420798c4bcf093785d872525087a7798db9 upstream. Enabling gcov is counterproductive to compile testing: it significantly increases the kernel image size, compile time, and it produces lots of false positive "may be used uninitialized" warnings as the result of missed optimizations. This is in line with how UBSAN_SANITIZE_ALL and PROFILE_ALL_BRANCHES work, both of which have similar problems. With an ARM allmodconfig kernel, I see the build time drop from 283 minutes CPU time to 225 minutes, and the vmlinux size drops from 43MB to 26MB. Signed-off-by: Arnd Bergmann Acked-by: Peter Oberparleiter Signed-off-by: Michal Marek Signed-off-by: Greg Kroah-Hartman --- kernel/gcov/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/gcov/Kconfig b/kernel/gcov/Kconfig index 3b7408759bdf..23153a692637 100644 --- a/kernel/gcov/Kconfig +++ b/kernel/gcov/Kconfig @@ -34,6 +34,7 @@ config GCOV_KERNEL config GCOV_PROFILE_ALL bool "Profile entire Kernel" + depends on !COMPILE_TEST depends on GCOV_KERNEL depends on SUPERH || S390 || X86 || PPC || MICROBLAZE || ARM || ARM64 default n -- GitLab From 7cb4b606421771e5c3f0de7677dfc0e14f8a6c9e Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 7 Apr 2017 09:34:12 +0200 Subject: [PATCH 2404/5498] scsi: sg: disable SET_FORCE_LOW_DMA commit 745dfa0d8ec26b24f3304459ff6e9eacc5c8351b upstream. The ioctl SET_FORCE_LOW_DMA has never worked since the initial git check-in, and the respective setting is nowadays handled correctly. So disable it entirely. Signed-off-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Tested-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sg.c | 30 +++++++++--------------------- include/scsi/sg.h | 1 - 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 09b670555620..783040af3ead 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -160,7 +160,6 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ struct list_head rq_list; /* head of request list */ struct fasync_struct *async_qp; /* used by asynchronous notification */ Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ - char low_dma; /* as in parent but possibly overridden to 1 */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */ @@ -947,24 +946,14 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) /* strange ..., for backward compatibility */ return sfp->timeout_user; case SG_SET_FORCE_LOW_DMA: - result = get_user(val, ip); - if (result) - return result; - if (val) { - sfp->low_dma = 1; - if ((0 == sfp->low_dma) && !sfp->res_in_use) { - val = (int) sfp->reserve.bufflen; - sg_remove_scat(sfp, &sfp->reserve); - sg_build_reserve(sfp, val); - } - } else { - if (atomic_read(&sdp->detaching)) - return -ENODEV; - sfp->low_dma = sdp->device->host->unchecked_isa_dma; - } + /* + * N.B. This ioctl never worked properly, but failed to + * return an error value. So returning '0' to keep compability + * with legacy applications. + */ return 0; case SG_GET_LOW_DMA: - return put_user((int) sfp->low_dma, ip); + return put_user((int) sdp->device->host->unchecked_isa_dma, ip); case SG_GET_SCSI_ID: if (!access_ok(VERIFY_WRITE, p, sizeof (sg_scsi_id_t))) return -EFAULT; @@ -1916,6 +1905,7 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) int sg_tablesize = sfp->parentdp->sg_tablesize; int blk_size = buff_size, order; gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN; + struct sg_device *sdp = sfp->parentdp; if (blk_size < 0) return -EFAULT; @@ -1941,7 +1931,7 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) scatter_elem_sz_prev = num; } - if (sfp->low_dma) + if (sdp->device->host->unchecked_isa_dma) gfp_mask |= GFP_DMA; if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) @@ -2204,8 +2194,6 @@ sg_add_sfp(Sg_device * sdp) sfp->timeout = SG_DEFAULT_TIMEOUT; sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER; sfp->force_packid = SG_DEF_FORCE_PACK_ID; - sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ? - sdp->device->host->unchecked_isa_dma : 1; sfp->cmd_q = SG_DEF_COMMAND_Q; sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; sfp->parentdp = sdp; @@ -2664,7 +2652,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) jiffies_to_msecs(fp->timeout), fp->reserve.bufflen, (int) fp->reserve.k_use_sg, - (int) fp->low_dma); + (int) sdp->device->host->unchecked_isa_dma); seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", (int) fp->cmd_q, (int) fp->force_packid, (int) fp->keep_orphan); diff --git a/include/scsi/sg.h b/include/scsi/sg.h index 750e5db7c6bf..0bb73deebbb4 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -194,7 +194,6 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ #define SG_DEFAULT_RETRIES 0 /* Defaults, commented if they differ from original sg driver */ -#define SG_DEF_FORCE_LOW_DMA 0 /* was 1 -> memory below 16MB on i386 */ #define SG_DEF_FORCE_PACK_ID 0 #define SG_DEF_KEEP_ORPHAN 0 #define SG_DEF_RESERVED_SIZE SG_SCATTER_SZ /* load time option */ -- GitLab From ad211e59c68389b9203f3834c65da7bfe9e6874a Mon Sep 17 00:00:00 2001 From: Li Jinyue Date: Thu, 14 Dec 2017 17:04:54 +0800 Subject: [PATCH 2405/5498] futex: Prevent overflow by strengthen input validation commit fbe0e839d1e22d88810f3ee3e2f1479be4c0aa4a upstream. UBSAN reports signed integer overflow in kernel/futex.c: UBSAN: Undefined behaviour in kernel/futex.c:2041:18 signed integer overflow: 0 - -2147483648 cannot be represented in type 'int' Add a sanity check to catch negative values of nr_wake and nr_requeue. Signed-off-by: Li Jinyue Signed-off-by: Thomas Gleixner Cc: peterz@infradead.org Cc: dvhart@infradead.org Link: https://lkml.kernel.org/r/1513242294-31786-1-git-send-email-lijinyue@huawei.com Signed-off-by: Greg Kroah-Hartman --- kernel/futex.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/futex.c b/kernel/futex.c index 54ebb63711f4..168f369c5eea 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -1514,6 +1514,9 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags, struct futex_hash_bucket *hb1, *hb2; struct futex_q *this, *next; + if (nr_wake < 0 || nr_requeue < 0) + return -EINVAL; + if (requeue_pi) { /* * Requeue PI only works on two distinct uaddrs. This -- GitLab From cdbebd4716bc50d2d965fdefc16d007239402197 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 Jan 2018 23:48:05 +0100 Subject: [PATCH 2406/5498] ALSA: pcm: Remove yet superfluous WARN_ON() commit 23b19b7b50fe1867da8d431eea9cd3e4b6328c2c upstream. muldiv32() contains a snd_BUG_ON() (which is morphed as WARN_ON() with debug option) for checking the case of 0 / 0. This would be helpful if this happens only as a logical error; however, since the hw refine is performed with any data set provided by user, the inconsistent values that can trigger such a condition might be passed easily. Actually, syzbot caught this by passing some zero'ed old hw_params ioctl. So, having snd_BUG_ON() there is simply superfluous and rather harmful to give unnecessary confusions. Let's get rid of it. Reported-by: syzbot+7e6ee55011deeebce15d@syzkaller.appspotmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm_lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 23e31ae9623f..8f04ccc44eee 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -644,7 +644,6 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b, { u_int64_t n = (u_int64_t) a * b; if (c == 0) { - snd_BUG_ON(!n); *r = 0; return UINT_MAX; } -- GitLab From 82ddf73182bbc2eff323ee35faffd16c75b335d4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 10 Jan 2018 10:53:18 +0100 Subject: [PATCH 2407/5498] ALSA: hda - Apply the existing quirk to iMac 14,1 commit 031f335cda879450095873003abb03ae8ed3b74a upstream. iMac 14,1 requires the same quirk as iMac 12,2, using GPIO 2 and 3 for headphone and speaker output amps. Add the codec SSID quirk entry (106b:0600) accordingly. BugLink: http://lkml.kernel.org/r/CAEw6Zyteav09VGHRfD5QwsfuWv5a43r0tFBNbfcHXoNrxVz7ew@mail.gmail.com Reported-by: Freaky Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_cirrus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 087228a0bcea..19678da4f546 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -394,6 +394,7 @@ static const struct snd_pci_quirk cs420x_fixup_tbl[] = { /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/ /* codec SSID */ + SND_PCI_QUIRK(0x106b, 0x0600, "iMac 14,1", CS420X_IMAC27_122), SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81), SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101), -- GitLab From 60319ec102135dafcd5fdaa009cfa35f4efe3d85 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 29 Dec 2017 18:13:05 -0600 Subject: [PATCH 2408/5498] af_key: fix buffer overread in verify_address_len() commit 06b335cb51af018d5feeff5dd4fd53847ddb675a upstream. If a message sent to a PF_KEY socket ended with one of the extensions that takes a 'struct sadb_address' but there were not enough bytes remaining in the message for the ->sa_family member of the 'struct sockaddr' which is supposed to follow, then verify_address_len() read past the end of the message, into uninitialized memory. Fix it by returning -EINVAL in this case. This bug was found using syzkaller with KMSAN. Reproducer: #include #include #include int main() { int sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); char buf[24] = { 0 }; struct sadb_msg *msg = (void *)buf; struct sadb_address *addr = (void *)(msg + 1); msg->sadb_msg_version = PF_KEY_V2; msg->sadb_msg_type = SADB_DELETE; msg->sadb_msg_len = 3; addr->sadb_address_len = 1; addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; write(sock, buf, 24); } Reported-by: Alexander Potapenko Signed-off-by: Eric Biggers Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/key/af_key.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/key/af_key.c b/net/key/af_key.c index a1bf6d825d4f..9098eba1ee1a 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -401,6 +401,11 @@ static int verify_address_len(const void *p) #endif int len; + if (sp->sadb_address_len < + DIV_ROUND_UP(sizeof(*sp) + offsetofend(typeof(*addr), sa_family), + sizeof(uint64_t))) + return -EINVAL; + switch (addr->sa_family) { case AF_INET: len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t)); -- GitLab From cf1feaf5e555cf6f55b4f0e0b336707084d703b4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 29 Dec 2017 18:15:23 -0600 Subject: [PATCH 2409/5498] af_key: fix buffer overread in parse_exthdrs() commit 4e765b4972af7b07adcb1feb16e7a525ce1f6b28 upstream. If a message sent to a PF_KEY socket ended with an incomplete extension header (fewer than 4 bytes remaining), then parse_exthdrs() read past the end of the message, into uninitialized memory. Fix it by returning -EINVAL in this case. Reproducer: #include #include #include int main() { int sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); char buf[17] = { 0 }; struct sadb_msg *msg = (void *)buf; msg->sadb_msg_version = PF_KEY_V2; msg->sadb_msg_type = SADB_DELETE; msg->sadb_msg_len = 2; write(sock, buf, 17); } Signed-off-by: Eric Biggers Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/key/af_key.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/key/af_key.c b/net/key/af_key.c index 9098eba1ee1a..65cce8ceaead 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -516,6 +516,9 @@ static int parse_exthdrs(struct sk_buff *skb, const struct sadb_msg *hdr, void * uint16_t ext_type; int ext_len; + if (len < sizeof(*ehdr)) + return -EINVAL; + ext_len = ehdr->sadb_ext_len; ext_len *= sizeof(uint64_t); ext_type = ehdr->sadb_ext_type; -- GitLab From d3c49810dc8d90bcff410c4e635ef817af2b2fc1 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Fri, 17 Nov 2017 15:29:21 -0800 Subject: [PATCH 2410/5498] pipe: avoid round_pipe_size() nr_pages overflow on 32-bit commit d3f14c485867cfb2e0c48aa88c41d0ef4bf5209c upstream. round_pipe_size() contains a right-bit-shift expression which may overflow, which would cause undefined results in a subsequent roundup_pow_of_two() call. static inline unsigned int round_pipe_size(unsigned int size) { unsigned long nr_pages; nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; return roundup_pow_of_two(nr_pages) << PAGE_SHIFT; } PAGE_SIZE is defined as (1UL << PAGE_SHIFT), so: - 4 bytes wide on 32-bit (0 to 0xffffffff) - 8 bytes wide on 64-bit (0 to 0xffffffffffffffff) That means that 32-bit round_pipe_size(), nr_pages may overflow to 0: size=0x00000000 nr_pages=0x0 size=0x00000001 nr_pages=0x1 size=0xfffff000 nr_pages=0xfffff size=0xfffff001 nr_pages=0x0 << ! size=0xffffffff nr_pages=0x0 << ! This is bad because roundup_pow_of_two(n) is undefined when n == 0! 64-bit is not a problem as the unsigned int size is 4 bytes wide (similar to 32-bit) and the larger, 8 byte wide unsigned long, is sufficient to handle the largest value of the bit shift expression: size=0xffffffff nr_pages=100000 Modify round_pipe_size() to return 0 if n == 0 and updates its callers to handle accordingly. Link: http://lkml.kernel.org/r/1507658689-11669-3-git-send-email-joe.lawrence@redhat.com Signed-off-by: Joe Lawrence Reported-by: Mikulas Patocka Reviewed-by: Mikulas Patocka Cc: Al Viro Cc: Jens Axboe Cc: Michael Kerrisk Cc: Randy Dunlap Cc: Josh Poimboeuf Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Dong Jinguang Signed-off-by: Greg Kroah-Hartman --- fs/pipe.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index e3ba6c3a1743..a0cb84474fce 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1002,6 +1002,9 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) { struct pipe_buffer *bufs; + if (!nr_pages) + return -EINVAL; + /* * We can shrink the pipe, if arg >= pipe->nrbufs. Since we don't * expect a lot of shrink+grow operations, just free and allocate @@ -1046,13 +1049,19 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) /* * Currently we rely on the pipe array holding a power-of-2 number - * of pages. + * of pages. Returns 0 on error. */ static inline unsigned int round_pipe_size(unsigned int size) { unsigned long nr_pages; + if (size < pipe_min_size) + size = pipe_min_size; + nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (nr_pages == 0) + return 0; + return roundup_pow_of_two(nr_pages) << PAGE_SHIFT; } @@ -1063,13 +1072,18 @@ static inline unsigned int round_pipe_size(unsigned int size) int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf, size_t *lenp, loff_t *ppos) { + unsigned int rounded_pipe_max_size; int ret; ret = proc_dointvec_minmax(table, write, buf, lenp, ppos); if (ret < 0 || !write) return ret; - pipe_max_size = round_pipe_size(pipe_max_size); + rounded_pipe_max_size = round_pipe_size(pipe_max_size); + if (rounded_pipe_max_size == 0) + return -EINVAL; + + pipe_max_size = rounded_pipe_max_size; return ret; } -- GitLab From a499bc7e0351608016bd85fa580b870b383effeb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 8 Jan 2018 17:20:18 -0800 Subject: [PATCH 2411/5498] Input: 88pm860x-ts - fix child-node lookup commit 906bf7daa0618d0ef39f4872ca42218c29a3631f upstream. Fix child node-lookup during probe, which ended up searching the whole device tree depth-first starting at parent rather than just matching on its children. To make things worse, the parent node was prematurely freed, while the child node was leaked. Fixes: 2e57d56747e6 ("mfd: 88pm860x: Device tree support") Signed-off-by: Johan Hovold Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/88pm860x-ts.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index 0d4a9fad4a78..5b4efcdff47d 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -126,7 +126,7 @@ static int pm860x_touch_dt_init(struct platform_device *pdev, int data, n, ret; if (!np) return -ENODEV; - np = of_find_node_by_name(np, "touch"); + np = of_get_child_by_name(np, "touch"); if (!np) { dev_err(&pdev->dev, "Can't find touch node\n"); return -EINVAL; @@ -144,13 +144,13 @@ static int pm860x_touch_dt_init(struct platform_device *pdev, if (data) { ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); if (ret < 0) - return -EINVAL; + goto err_put_node; } /* set tsi prebias time */ if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) { ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); if (ret < 0) - return -EINVAL; + goto err_put_node; } /* set prebias & prechg time of pen detect */ data = 0; @@ -161,10 +161,18 @@ static int pm860x_touch_dt_init(struct platform_device *pdev, if (data) { ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); if (ret < 0) - return -EINVAL; + goto err_put_node; } of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x); + + of_node_put(np); + return 0; + +err_put_node: + of_node_put(np); + + return -EINVAL; } #else #define pm860x_touch_dt_init(x, y, z) (-1) -- GitLab From e9765832134fac7d451ebe7bd2e67d16405c9581 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Mon, 9 May 2016 17:01:01 -0700 Subject: [PATCH 2412/5498] Input: twl6040-vibra - fix DT node memory management commit c52c545ead97fcc2f4f8ea38f1ae3c23211e09a8 upstream. commit e7ec014a47e4 ("Input: twl6040-vibra - update for device tree support") made the separate vibra DT node to a subnode of the twl6040. It now calls of_find_node_by_name() to locate the "vibra" subnode. This function has a side effect to call of_node_put on() for the twl6040 parent node passed in as a parameter. This causes trouble later on. Solution: we must call of_node_get() before of_find_node_by_name() Signed-off-by: H. Nikolaus Schaller Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/twl6040-vibra.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 6d26eecc278c..9ab85a2551c4 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -264,6 +264,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev) int vddvibr_uV = 0; int error; + of_node_get(twl6040_core_dev->of_node); twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node, "vibra"); if (!twl6040_core_node) { -- GitLab From 12d0592008fd830a61c4cbce9c793e30c4b0d8a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 8 Jan 2018 17:17:48 -0800 Subject: [PATCH 2413/5498] Input: twl6040-vibra - fix child-node lookup commit dcaf12a8b0bbdbfcfa2be8dff2c4948d9844b4ad upstream. Fix child-node lookup during probe, which ended up searching the whole device tree depth-first starting at parent rather than just matching on its children. Later sanity checks on node properties (which would likely be missing) should prevent this from causing much trouble however, especially as the original premature free of the parent node has already been fixed separately (but that "fix" was apparently never backported to stable). Fixes: e7ec014a47e4 ("Input: twl6040-vibra - update for device tree support") Fixes: c52c545ead97 ("Input: twl6040-vibra - fix DT node memory management") Signed-off-by: Johan Hovold Acked-by: Peter Ujfalusi Tested-by: H. Nikolaus Schaller (on Pyra OMAP5 hardware) Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/twl6040-vibra.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 9ab85a2551c4..7eb23e644fac 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -264,8 +264,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev) int vddvibr_uV = 0; int error; - of_node_get(twl6040_core_dev->of_node); - twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node, + twl6040_core_node = of_get_child_by_name(twl6040_core_dev->of_node, "vibra"); if (!twl6040_core_node) { dev_err(&pdev->dev, "parent of node is missing?\n"); -- GitLab From 5ada5b8763130373d961149e3b2950f5f1199c90 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Wed, 29 Jul 2015 14:02:19 -0700 Subject: [PATCH 2414/5498] Input: twl4030-vibra - fix ERROR: Bad of_node_put() warning commit e661d0a04462dd98667f8947141bd8defab5b34a upstream. Fix following: [ 8.862274] ERROR: Bad of_node_put() on /ocp/i2c@48070000/twl@48/audio [ 8.869293] CPU: 0 PID: 1003 Comm: modprobe Not tainted 4.2.0-rc2-letux+ #1175 [ 8.876922] Hardware name: Generic OMAP36xx (Flattened Device Tree) [ 8.883514] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 8.891693] [] (show_stack) from [] (dump_stack+0x78/0x94) [ 8.899322] [] (dump_stack) from [] (kobject_release+0x68/0x7c) [ 8.907409] [] (kobject_release) from [] (twl4030_vibra_probe+0x74/0x188 [twl4030_vibra]) [ 8.917877] [] (twl4030_vibra_probe [twl4030_vibra]) from [] (platform_drv_probe+0x48/0x90) [ 8.928497] [] (platform_drv_probe) from [] (really_probe+0xd4/0x238) [ 8.937103] [] (really_probe) from [] (driver_probe_device+0x30/0x48) [ 8.945678] [] (driver_probe_device) from [] (__driver_attach+0x68/0x8c) [ 8.954589] [] (__driver_attach) from [] (bus_for_each_dev+0x50/0x84) [ 8.963226] [] (bus_for_each_dev) from [] (bus_add_driver+0xcc/0x1e4) [ 8.971832] [] (bus_add_driver) from [] (driver_register+0x9c/0xe0) [ 8.980255] [] (driver_register) from [] (do_one_initcall+0x100/0x1b8) [ 8.988983] [] (do_one_initcall) from [] (do_init_module+0x58/0x1c0) [ 8.997497] [] (do_init_module) from [] (SyS_init_module+0x54/0x64) [ 9.005950] [] (SyS_init_module) from [] (ret_fast_syscall+0x0/0x54) [ 9.015838] input: twl4030:vibrator as /devices/platform/68000000.ocp/48070000.i2c/i2c-0/0-0048/48070000.i2c:twl@48:audio/input/input2 node passed to of_find_node_by_name is put inside that function and new node is returned if found. Free returned node not already freed node. Signed-off-by: Marek Belisko Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/twl4030-vibra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 960ef2a70910..f932098431fa 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -185,7 +185,8 @@ static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, if (pdata && pdata->coexist) return true; - if (of_find_node_by_name(node, "codec")) { + node = of_find_node_by_name(node, "codec"); + if (node) { of_node_put(node); return true; } -- GitLab From b70f4ccbc41d3abfebb600976e2f534a756685ee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 8 Jan 2018 17:15:06 -0800 Subject: [PATCH 2415/5498] Input: twl4030-vibra - fix sibling-node lookup commit 5b189201993ab03001a398de731045bfea90c689 upstream. A helper purported to look up a child node based on its name was using the wrong of-helper and ended up prematurely freeing the parent of-node while searching the whole device tree depth-first starting at the parent node. Fixes: 64b9e4d803b1 ("input: twl4030-vibra: Support for DT booted kernel") Fixes: e661d0a04462 ("Input: twl4030-vibra - fix ERROR: Bad of_node_put() warning") Signed-off-by: Johan Hovold Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/twl4030-vibra.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index f932098431fa..c434a8521d65 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -180,12 +180,14 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, twl4030_vibra_suspend, twl4030_vibra_resume); static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, - struct device_node *node) + struct device_node *parent) { + struct device_node *node; + if (pdata && pdata->coexist) return true; - node = of_find_node_by_name(node, "codec"); + node = of_get_child_by_name(parent, "codec"); if (node) { of_node_put(node); return true; -- GitLab From 40dfccf9bf3729554d3a22df78b748a91bbcd58b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Jan 2018 11:12:05 +0100 Subject: [PATCH 2416/5498] phy: work around 'phys' references to usb-nop-xceiv devices commit b7563e2796f8b23c98afcfea7363194227fa089d upstream. Stefan Wahren reports a problem with a warning fix that was merged for v4.15: we had lots of device nodes with a 'phys' property pointing to a device node that is not compliant with the binding documented in Documentation/devicetree/bindings/phy/phy-bindings.txt This generally works because USB HCD drivers that support both the generic phy subsystem and the older usb-phy subsystem ignore most errors from phy_get() and related calls and then use the usb-phy driver instead. However, it turns out that making the usb-nop-xceiv device compatible with the generic-phy binding changes the phy_get() return code from -EINVAL to -EPROBE_DEFER, and the dwc2 usb controller driver for bcm2835 now returns -EPROBE_DEFER from its probe function rather than ignoring the failure, breaking all USB support on raspberry-pi when CONFIG_GENERIC_PHY is enabled. The same code is used in the dwc3 driver and the usb_add_hcd() function, so a reasonable assumption would be that many other platforms are affected as well. I have reviewed all the related patches and concluded that "usb-nop-xceiv" is the only USB phy that is affected by the change, and since it is by far the most commonly referenced phy, all the other USB phy drivers appear to be used in ways that are are either safe in DT (they don't use the 'phys' property), or in the driver (they already ignore -EPROBE_DEFER from generic-phy when usb-phy is available). To work around the problem, this adds a special case to _of_phy_get() so we ignore any PHY node that is compatible with "usb-nop-xceiv", as we know that this can never load no matter how much we defer. In the future, we might implement a generic-phy driver for "usb-nop-xceiv" and then remove this workaround. Since we generally want older kernels to also want to work with the fixed devicetree files, it would be good to backport the patch into stable kernels as well (3.13+ are possibly affected), even though they don't contain any of the patches that may have caused regressions. Fixes: 014d6da6cb25 ARM: dts: bcm283x: Fix DTC warnings about missing phy-cells Fixes: c5bbf358b790 arm: dts: nspire: Add missing #phy-cells to usb-nop-xceiv Fixes: 44e5dced2ef6 arm: dts: marvell: Add missing #phy-cells to usb-nop-xceiv Fixes: f568f6f554b8 ARM: dts: omap: Add missing #phy-cells to usb-nop-xceiv Fixes: d745d5f277bf ARM: dts: imx51-zii-rdu1: Add missing #phy-cells to usb-nop-xceiv Fixes: 915fbe59cbf2 ARM: dts: imx: Add missing #phy-cells to usb-nop-xceiv Link: https://marc.info/?l=linux-usb&m=151518314314753&w=2 Link: https://patchwork.kernel.org/patch/10158145/ Cc: Felipe Balbi Cc: Eric Anholt Tested-by: Stefan Wahren Acked-by: Rob Herring Tested-by: Hans Verkuil Acked-by: Kishon Vijay Abraham I Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/phy/phy-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 75c62907b99d..c7d277099cf2 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -319,6 +319,10 @@ static struct phy *_of_phy_get(struct device_node *np, int index) if (ret) return ERR_PTR(-ENODEV); + /* This phy type handled by the usb-phy subsystem for now */ + if (of_device_is_compatible(args.np, "usb-nop-xceiv")) + return ERR_PTR(-ENODEV); + mutex_lock(&phy_provider_mutex); phy_provider = of_phy_provider_lookup(args.np); if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) { -- GitLab From e89b22a8b329b84ae4447373436f600fba28b437 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 4 Jan 2018 17:53:12 +0100 Subject: [PATCH 2417/5498] ARM: dts: kirkwood: fix pin-muxing of MPP7 on OpenBlocks A7 commit 56aeb07c914a616ab84357d34f8414a69b140cdf upstream. MPP7 is currently muxed as "gpio", but this function doesn't exist for MPP7, only "gpo" is available. This causes the following error: kirkwood-pinctrl f1010000.pin-controller: unsupported function gpio on pin mpp7 pinctrl core: failed to register map default (6): invalid type given kirkwood-pinctrl f1010000.pin-controller: error claiming hogs: -22 kirkwood-pinctrl f1010000.pin-controller: could not claim hogs: -22 kirkwood-pinctrl f1010000.pin-controller: unable to register pinctrl driver kirkwood-pinctrl: probe of f1010000.pin-controller failed with error -22 So the pinctrl driver is not probed, all device drivers (including the UART driver) do a -EPROBE_DEFER, and therefore the system doesn't really boot (well, it boots, but with no UART, and no devices that require pin-muxing). Back when the Device Tree file for this board was introduced, the definition was already wrong. The pinctrl driver also always described as "gpo" this function for MPP7. However, between Linux 4.10 and 4.11, a hog pin failing to be muxed was turned from a simple warning to a hard error that caused the entire pinctrl driver probe to bail out. This is probably the result of commit 6118714275f0a ("pinctrl: core: Fix pinctrl_register_and_init() with pinctrl_enable()"). This commit fixes the Device Tree to use the proper "gpo" function for MPP7, which fixes the boot of OpenBlocks A7, which was broken since Linux 4.11. Fixes: f24b56cbcd9d ("ARM: kirkwood: add support for OpenBlocks A7 platform") Signed-off-by: Thomas Petazzoni Reviewed-by: Andrew Lunn Signed-off-by: Gregory CLEMENT Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/kirkwood-openblocks_a7.dts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/kirkwood-openblocks_a7.dts b/arch/arm/boot/dts/kirkwood-openblocks_a7.dts index d5e3bc518968..d57f48543f76 100644 --- a/arch/arm/boot/dts/kirkwood-openblocks_a7.dts +++ b/arch/arm/boot/dts/kirkwood-openblocks_a7.dts @@ -53,7 +53,8 @@ }; pinctrl: pin-controller@10000 { - pinctrl-0 = <&pmx_dip_switches &pmx_gpio_header>; + pinctrl-0 = <&pmx_dip_switches &pmx_gpio_header + &pmx_gpio_header_gpo>; pinctrl-names = "default"; pmx_uart0: pmx-uart0 { @@ -85,11 +86,16 @@ * ground. */ pmx_gpio_header: pmx-gpio-header { - marvell,pins = "mpp17", "mpp7", "mpp29", "mpp28", + marvell,pins = "mpp17", "mpp29", "mpp28", "mpp35", "mpp34", "mpp40"; marvell,function = "gpio"; }; + pmx_gpio_header_gpo: pxm-gpio-header-gpo { + marvell,pins = "mpp7"; + marvell,function = "gpo"; + }; + pmx_gpio_init: pmx-init { marvell,pins = "mpp38"; marvell,function = "gpio"; -- GitLab From 2da34a807ad1a3d75eb13477f111eca3f46ace64 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 20 Dec 2017 09:56:06 +0000 Subject: [PATCH 2418/5498] dm btree: fix serious bug in btree_split_beneath() commit bc68d0a43560e950850fc69b58f0f8254b28f6d6 upstream. When inserting a new key/value pair into a btree we walk down the spine of btree nodes performing the following 2 operations: i) space for a new entry ii) adjusting the first key entry if the new key is lower than any in the node. If the _root_ node is full, the function btree_split_beneath() allocates 2 new nodes, and redistibutes the root nodes entries between them. The root node is left with 2 entries corresponding to the 2 new nodes. btree_split_beneath() then adjusts the spine to point to one of the two new children. This means the first key is never adjusted if the new key was lower, ie. operation (ii) gets missed out. This can result in the new key being 'lost' for a period; until another low valued key is inserted that will uncover it. This is a serious bug, and quite hard to make trigger in normal use. A reproducing test case ("thin create devices-in-reverse-order") is available as part of the thin-provision-tools project: https://github.com/jthornber/thin-provisioning-tools/blob/master/functional-tests/device-mapper/dm-tests.scm#L593 Fix the issue by changing btree_split_beneath() so it no longer adjusts the spine. Instead it unlocks both the new nodes, and lets the main loop in btree_insert_raw() relock the appropriate one and make any neccessary adjustments. Reported-by: Monty Pavel Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/persistent-data/dm-btree.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index 360c22d44647..f2a8e4c69d9f 100644 --- a/drivers/md/persistent-data/dm-btree.c +++ b/drivers/md/persistent-data/dm-btree.c @@ -572,23 +572,8 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key) pn->keys[1] = rn->keys[0]; memcpy_disk(value_ptr(pn, 1), &val, sizeof(__le64)); - /* - * rejig the spine. This is ugly, since it knows too - * much about the spine - */ - if (s->nodes[0] != new_parent) { - unlock_block(s->info, s->nodes[0]); - s->nodes[0] = new_parent; - } - if (key < le64_to_cpu(rn->keys[0])) { - unlock_block(s->info, right); - s->nodes[1] = left; - } else { - unlock_block(s->info, left); - s->nodes[1] = right; - } - s->count = 2; - + unlock_block(s->info, left); + unlock_block(s->info, right); return 0; } -- GitLab From 7ed7a1ce7e03214e3c98998c36f99034fb8f6575 Mon Sep 17 00:00:00 2001 From: Dennis Yang Date: Tue, 12 Dec 2017 18:21:40 +0800 Subject: [PATCH 2419/5498] dm thin metadata: THIN_MAX_CONCURRENT_LOCKS should be 6 commit 490ae017f54e55bde382d45ea24bddfb6d1a0aaf upstream. For btree removal, there is a corner case that a single thread could takes 6 locks which is more than THIN_MAX_CONCURRENT_LOCKS(5) and leads to deadlock. A btree removal might eventually call rebalance_children()->rebalance3() to rebalance entries of three neighbor child nodes when shadow_spine has already acquired two write locks. In rebalance3(), it tries to shadow and acquire the write locks of all three child nodes. However, shadowing a child node requires acquiring a read lock of the original child node and a write lock of the new block. Although the read lock will be released after block shadowing, shadowing the third child node in rebalance3() could still take the sixth lock. (2 write locks for shadow_spine + 2 write locks for the first two child nodes's shadow + 1 write lock for the last child node's shadow + 1 read lock for the last child node) Signed-off-by: Dennis Yang Acked-by: Joe Thornber Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 07fd9ca52372..3f3ab7586dc0 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -81,10 +81,14 @@ #define SECTOR_TO_BLOCK_SHIFT 3 /* + * For btree insert: * 3 for btree insert + * 2 for btree lookup used within space map + * For btree remove: + * 2 for shadow spine + + * 4 for rebalance 3 child node */ -#define THIN_MAX_CONCURRENT_LOCKS 5 +#define THIN_MAX_CONCURRENT_LOCKS 6 /* This should be plenty */ #define SPACE_MAP_ROOT_SIZE 128 -- GitLab From 0e88a123df05b3254dcba42ab857a1ed63c46ff3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 16 Jan 2018 10:23:47 +0000 Subject: [PATCH 2420/5498] arm64: KVM: Fix SMCCC handling of unimplemented SMC/HVC calls commit acfb3b883f6d6a4b5d27ad7fdded11f6a09ae6dd upstream. KVM doesn't follow the SMCCC when it comes to unimplemented calls, and inject an UNDEF instead of returning an error. Since firmware calls are now used for security mitigation, they are becoming more common, and the undef is counter productive. Instead, let's follow the SMCCC which states that -1 must be returned to the caller when getting an unknown function number. Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/handle_exit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index 34b8bd0711e9..e738bff5eec1 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -34,7 +34,7 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) ret = kvm_psci_call(vcpu); if (ret < 0) { - kvm_inject_undefined(vcpu); + *vcpu_reg(vcpu, 0) = ~0UL; return 1; } @@ -43,7 +43,7 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) { - kvm_inject_undefined(vcpu); + *vcpu_reg(vcpu, 0) = ~0UL; return 1; } -- GitLab From 66b64a9358537aea643014b549521f03a10e13a8 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sun, 29 Oct 2017 16:27:21 +0100 Subject: [PATCH 2421/5498] MIPS: AR7: ensure the port type's FCR value is used commit 0a5191efe06b5103909206e4fbcff81d30283f8e upstream. Since commit aef9a7bd9b67 ("serial/uart/8250: Add tunable RX interrupt trigger I/F of FIFO buffers"), the port's default FCR value isn't used in serial8250_do_set_termios anymore, but copied over once in serial8250_config_port and then modified as needed. Unfortunately, serial8250_config_port will never be called if the port is shared between kernel and userspace, and the port's flag doesn't have UPF_BOOT_AUTOCONF, which would trigger a serial8250_config_port as well. This causes garbled output from userspace: [ 5.220000] random: procd urandom read with 49 bits of entropy available ers [kee Fix this by forcing it to be configured on boot, resulting in the expected output: [ 5.250000] random: procd urandom read with 50 bits of entropy available Press the [f] key and hit [enter] to enter failsafe mode Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level Fixes: aef9a7bd9b67 ("serial/uart/8250: Add tunable RX interrupt trigger I/F of FIFO buffers") Signed-off-by: Jonas Gorski Cc: Greg Kroah-Hartman Cc: Yoshihiro YUNOMAE Cc: Florian Fainelli Cc: Nicolas Schichan Cc: linux-mips@linux-mips.org Cc: linux-serial@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/17544/ Signed-off-by: Ralf Baechle Cc: James Hogan Signed-off-by: Greg Kroah-Hartman --- arch/mips/ar7/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c index e4f47d3e55bb..131f3eeb8c0b 100644 --- a/arch/mips/ar7/platform.c +++ b/arch/mips/ar7/platform.c @@ -581,7 +581,7 @@ static int __init ar7_register_uarts(void) uart_port.type = PORT_AR7; uart_port.uartclk = clk_get_rate(bus_clk) / 2; uart_port.iotype = UPIO_MEM32; - uart_port.flags = UPF_FIXED_TYPE; + uart_port.flags = UPF_FIXED_TYPE | UPF_BOOT_AUTOCONF; uart_port.regshift = 2; uart_port.line = 0; -- GitLab From 7b9da19c2ec9f7334a2db2e67cbb3fe6e44c655b Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Fri, 9 Dec 2016 10:24:05 -0800 Subject: [PATCH 2422/5498] x86/asm/32: Make sync_core() handle missing CPUID on all 32-bit kernels commit 1c52d859cb2d417e7216d3e56bb7fea88444cec9 upstream. We support various non-Intel CPUs that don't have the CPUID instruction, so the M486 test was wrong. For now, fix it with a big hammer: handle missing CPUID on all 32-bit CPUs. Reported-by: One Thousand Gnomes Signed-off-by: Andy Lutomirski Cc: Juergen Gross Cc: Peter Zijlstra Cc: Brian Gerst Cc: Matthew Whitehead Cc: Borislav Petkov Cc: Henrique de Moraes Holschuh Cc: Andrew Cooper Cc: Boris Ostrovsky Cc: xen-devel Link: http://lkml.kernel.org/r/685bd083a7c036f7769510b6846315b17d6ba71f.1481307769.git.luto@kernel.org Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 26d5e05a7def..f303d65bb202 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -669,7 +669,7 @@ static inline void sync_core(void) { int tmp; -#ifdef CONFIG_M486 +#ifdef CONFIG_X86_32 /* * Do a CPUID if available, otherwise do a jump. The jump * can conveniently enough be the jump around CPUID. -- GitLab From 6b6f9e3d00443fa0431ddb9bb84f8db1453e7783 Mon Sep 17 00:00:00 2001 From: Jonathan Dieter Date: Mon, 27 Feb 2017 10:31:04 +0200 Subject: [PATCH 2423/5498] usbip: Fix implicit fallthrough warning commit cfd6ed4537a9e938fa76facecd4b9cd65b6d1563 upstream. GCC 7 now warns when switch statements fall through implicitly, and with -Werror enabled in configure.ac, that makes these tools unbuildable. We fix this by notifying the compiler that this particular case statement is meant to fall through. Reviewed-by: Peter Senna Tschudin Signed-off-by: Jonathan Dieter Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/src/usbip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/usb/usbip/src/usbip.c b/tools/usb/usbip/src/usbip.c index d7599d943529..73d8eee8130b 100644 --- a/tools/usb/usbip/src/usbip.c +++ b/tools/usb/usbip/src/usbip.c @@ -176,6 +176,8 @@ int main(int argc, char *argv[]) break; case '?': printf("usbip: invalid option\n"); + /* Terminate after printing error */ + /* FALLTHRU */ default: usbip_usage(); goto out; -- GitLab From 16d977e9bbf4a8a125da9978b89e32e92a13b1b0 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 16 Jan 2018 19:30:14 +0100 Subject: [PATCH 2424/5498] can: af_can: can_rcv(): replace WARN_ONCE by pr_warn_once commit 8cb68751c115d176ec851ca56ecfbb411568c9e8 upstream. If an invalid CAN frame is received, from a driver or from a tun interface, a Kernel warning is generated. This patch replaces the WARN_ONCE by a simple pr_warn_once, so that a kernel, bootet with panic_on_warn, does not panic. A printk seems to be more appropriate here. Reported-by: syzbot+4386709c0c1284dca827@syzkaller.appspotmail.com Suggested-by: Dmitry Vyukov Acked-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Greg Kroah-Hartman --- net/can/af_can.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/can/af_can.c b/net/can/af_can.c index ee6eee7a8b42..92b2a67c8dc6 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -719,13 +719,12 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, if (unlikely(!net_eq(dev_net(dev), &init_net))) goto drop; - if (WARN_ONCE(dev->type != ARPHRD_CAN || - skb->len != CAN_MTU || - cfd->len > CAN_MAX_DLEN, - "PF_CAN: dropped non conform CAN skbuf: " - "dev type %d, len %d, datalen %d\n", - dev->type, skb->len, cfd->len)) + if (unlikely(dev->type != ARPHRD_CAN || skb->len != CAN_MTU || + cfd->len > CAN_MAX_DLEN)) { + pr_warn_once("PF_CAN: dropped non conform CAN skbuf: dev type %d, len %d, datalen %d\n", + dev->type, skb->len, cfd->len); goto drop; + } can_receive(skb, dev); return NET_RX_SUCCESS; -- GitLab From 7a3974ca1195175be22eae4bdcc1c8b81fcd4808 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 16 Jan 2018 19:30:14 +0100 Subject: [PATCH 2425/5498] can: af_can: canfd_rcv(): replace WARN_ONCE by pr_warn_once commit d4689846881d160a4d12a514e991a740bcb5d65a upstream. If an invalid CANFD frame is received, from a driver or from a tun interface, a Kernel warning is generated. This patch replaces the WARN_ONCE by a simple pr_warn_once, so that a kernel, bootet with panic_on_warn, does not panic. A printk seems to be more appropriate here. Reported-by: syzbot+e3b775f40babeff6e68b@syzkaller.appspotmail.com Suggested-by: Dmitry Vyukov Acked-by: Oliver Hartkopp Cc: linux-stable Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Signed-off-by: Greg Kroah-Hartman --- net/can/af_can.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/can/af_can.c b/net/can/af_can.c index 92b2a67c8dc6..9c72b6501665 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -742,13 +742,12 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, if (unlikely(!net_eq(dev_net(dev), &init_net))) goto drop; - if (WARN_ONCE(dev->type != ARPHRD_CAN || - skb->len != CANFD_MTU || - cfd->len > CANFD_MAX_DLEN, - "PF_CAN: dropped non conform CAN FD skbuf: " - "dev type %d, len %d, datalen %d\n", - dev->type, skb->len, cfd->len)) + if (unlikely(dev->type != ARPHRD_CAN || skb->len != CANFD_MTU || + cfd->len > CANFD_MAX_DLEN)) { + pr_warn_once("PF_CAN: dropped non conform CAN FD skbuf: dev type %d, len %d, datalen %d\n", + dev->type, skb->len, cfd->len); goto drop; + } can_receive(skb, dev); return NET_RX_SUCCESS; -- GitLab From 3e0f334aaf1f19664e5b21a83e945b23a57ce9e3 Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Mon, 10 Jul 2017 15:49:51 -0700 Subject: [PATCH 2426/5498] mm/mmap.c: do not blow on PROT_NONE MAP_FIXED holes in the stack commit 561b5e0709e4a248c67d024d4d94b6e31e3edf2f upstream. Commit 1be7107fbe18 ("mm: larger stack guard gap, between vmas") has introduced a regression in some rust and Java environments which are trying to implement their own stack guard page. They are punching a new MAP_FIXED mapping inside the existing stack Vma. This will confuse expand_{downwards,upwards} into thinking that the stack expansion would in fact get us too close to an existing non-stack vma which is a correct behavior wrt safety. It is a real regression on the other hand. Let's work around the problem by considering PROT_NONE mapping as a part of the stack. This is a gros hack but overflowing to such a mapping would trap anyway an we only can hope that usespace knows what it is doing and handle it propely. Fixes: 1be7107fbe18 ("mm: larger stack guard gap, between vmas") Link: http://lkml.kernel.org/r/20170705182849.GA18027@dhcp22.suse.cz Signed-off-by: Michal Hocko Debugged-by: Vlastimil Babka Cc: Ben Hutchings Cc: Willy Tarreau Cc: Oleg Nesterov Cc: Rik van Riel Cc: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index 954ad5a3c140..60ba08e971fb 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2191,7 +2191,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) gap_addr = TASK_SIZE; next = vma->vm_next; - if (next && next->vm_start < gap_addr) { + if (next && next->vm_start < gap_addr && + (next->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) { if (!(next->vm_flags & VM_GROWSUP)) return -ENOMEM; /* Check that both stack segments have the same anon_vma? */ @@ -2271,7 +2272,8 @@ int expand_downwards(struct vm_area_struct *vma, if (gap_addr > address) return -ENOMEM; prev = vma->vm_prev; - if (prev && prev->vm_end > gap_addr) { + if (prev && prev->vm_end > gap_addr && + (prev->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) { if (!(prev->vm_flags & VM_GROWSDOWN)) return -ENOMEM; /* Check that both stack segments have the same anon_vma? */ -- GitLab From fee959e947ff35e2a278495fe9c55462bf04dd2c Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Fri, 12 May 2017 15:46:26 -0700 Subject: [PATCH 2427/5498] hwpoison, memcg: forcibly uncharge LRU pages commit 18365225f0440d09708ad9daade2ec11275c3df9 upstream. Laurent Dufour has noticed that hwpoinsoned pages are kept charged. In his particular case he has hit a bad_page("page still charged to cgroup") when onlining a hwpoison page. While this looks like something that shouldn't happen in the first place because onlining hwpages and returning them to the page allocator makes only little sense it shows a real problem. hwpoison pages do not get freed usually so we do not uncharge them (at least not since commit 0a31bc97c80c ("mm: memcontrol: rewrite uncharge API")). Each charge pins memcg (since e8ea14cc6ead ("mm: memcontrol: take a css reference for each charged page")) as well and so the mem_cgroup and the associated state will never go away. Fix this leak by forcibly uncharging a LRU hwpoisoned page in delete_from_lru_cache(). We also have to tweak uncharge_list because it cannot rely on zero ref count for these pages. [akpm@linux-foundation.org: coding-style fixes] Fixes: 0a31bc97c80c ("mm: memcontrol: rewrite uncharge API") Link: http://lkml.kernel.org/r/20170502185507.GB19165@dhcp22.suse.cz Signed-off-by: Michal Hocko Reported-by: Laurent Dufour Tested-by: Laurent Dufour Reviewed-by: Balbir Singh Reviewed-by: Naoya Horiguchi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memcontrol.c | 2 +- mm/memory-failure.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 0afd8444a8d6..8f5496b6823d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6500,7 +6500,7 @@ static void uncharge_list(struct list_head *page_list) next = page->lru.next; VM_BUG_ON_PAGE(PageLRU(page), page); - VM_BUG_ON_PAGE(page_count(page), page); + VM_BUG_ON_PAGE(!PageHWPoison(page) && page_count(page), page); pc = lookup_page_cgroup(page); if (!PageCgroupUsed(pc)) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index e14e75754154..003e434361f2 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -548,6 +548,13 @@ static int delete_from_lru_cache(struct page *p) */ ClearPageActive(p); ClearPageUnevictable(p); + + /* + * Poisoned page might never drop its ref count to 0 so we have + * to uncharge it manually from its memcg. + */ + mem_cgroup_uncharge(p); + /* * drop the page count elevated by isolate_lru_page() */ -- GitLab From 4003d8b836ab6426976e28e0bac6bddc2234b88d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 14 Dec 2016 15:06:07 -0800 Subject: [PATCH 2428/5498] ipc: msg, make msgrcv work with LONG_MIN commit 999898355e08ae3b92dfd0a08db706e0c6703d30 upstream. When LONG_MIN is passed to msgrcv, one would expect to recieve any message. But convert_mode does *msgtyp = -*msgtyp and -LONG_MIN is undefined. In particular, with my gcc -LONG_MIN produces -LONG_MIN again. So handle this case properly by assigning LONG_MAX to *msgtyp if LONG_MIN was specified as msgtyp to msgrcv. This code: long msg[] = { 100, 200 }; int m = msgget(IPC_PRIVATE, IPC_CREAT | 0644); msgsnd(m, &msg, sizeof(msg), 0); msgrcv(m, &msg, sizeof(msg), LONG_MIN, 0); produces currently nothing: msgget(IPC_PRIVATE, IPC_CREAT|0644) = 65538 msgsnd(65538, {100, "\310\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16, 0) = 0 msgrcv(65538, ... Except a UBSAN warning: UBSAN: Undefined behaviour in ipc/msg.c:745:13 negation of -9223372036854775808 cannot be represented in type 'long int': With the patch, I see what I expect: msgget(IPC_PRIVATE, IPC_CREAT|0644) = 0 msgsnd(0, {100, "\310\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16, 0) = 0 msgrcv(0, {100, "\310\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16, -9223372036854775808, 0) = 16 Link: http://lkml.kernel.org/r/20161024082633.10148-1-jslaby@suse.cz Signed-off-by: Jiri Slaby Cc: Davidlohr Bueso Cc: Manfred Spraul Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- ipc/msg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ipc/msg.c b/ipc/msg.c index 02e72d3db498..ce938615ad01 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -740,7 +740,10 @@ static inline int convert_mode(long *msgtyp, int msgflg) if (*msgtyp == 0) return SEARCH_ANY; if (*msgtyp < 0) { - *msgtyp = -*msgtyp; + if (*msgtyp == LONG_MIN) /* -LONG_MIN is undefined */ + *msgtyp = LONG_MAX; + else + *msgtyp = -*msgtyp; return SEARCH_LESSEQUAL; } if (msgflg & MSG_EXCEPT) -- GitLab From 38a8f8f7f6c61ca1cc6409ee9b0f89b21a8dad79 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Mon, 8 Aug 2016 21:57:58 +0800 Subject: [PATCH 2429/5498] netfilter: nf_ct_expect: remove the redundant slash when policy name is empty commit b173a28f62cf929324a8a6adcc45adadce311d16 upstream. The 'name' filed in struct nf_conntrack_expect_policy{} is not a pointer, so check it is NULL or not will always return true. Even if the name is empty, slash will always be displayed like follows: # cat /proc/net/nf_conntrack_expect 297 l3proto = 2 proto=6 src=1.1.1.1 dst=2.2.2.2 sport=1 dport=1025 ftp/ ^ Fixes: 3a8fc53a45c4 ("netfilter: nf_ct_helper: allocate 16 bytes for the helper and policy names") Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso Acked-by: Michal Kubecek Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_expect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 7ebdd7ff8ec0..193ae4ce0919 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -557,7 +557,7 @@ static int exp_seq_show(struct seq_file *s, void *v) helper = rcu_dereference(nfct_help(expect->master)->helper); if (helper) { seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name); - if (helper->expect_policy[expect->class].name) + if (helper->expect_policy[expect->class].name[0]) seq_printf(s, "/%s", helper->expect_policy[expect->class].name); } -- GitLab From 64f94c222f630b2a407a36bac38c4f543edde668 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 25 Aug 2016 15:33:29 +0200 Subject: [PATCH 2430/5498] netfilter: restart search if moved to other chain commit 95a8d19f28e6b29377a880c6264391a62e07fccc upstream. In case nf_conntrack_tuple_taken did not find a conflicting entry check that all entries in this hash slot were tested and restart in case an entry was moved to another chain. Reported-by: Eric Dumazet Fixes: ea781f197d6a ("netfilter: nf_conntrack: use SLAB_DESTROY_BY_RCU and get rid of call_rcu()") Signed-off-by: Florian Westphal Acked-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso Acked-by: Michal Kubecek Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 98cd0e78c94c..13fbd2ab960f 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -695,6 +695,7 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple, * least once for the stats anyway. */ rcu_read_lock_bh(); + begin: hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnnode) { ct = nf_ct_tuplehash_to_ctrack(h); if (ct != ignored_conntrack && @@ -706,6 +707,12 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple, } NF_CT_STAT_INC(net, searched); } + + if (get_nulls_value(n) != hash) { + NF_CT_STAT_INC(net, search_restart); + goto begin; + } + rcu_read_unlock_bh(); return 0; -- GitLab From f1a49458d9ef2e02ad7163900eaa8cf15b083b78 Mon Sep 17 00:00:00 2001 From: Ulrich Weber Date: Mon, 24 Oct 2016 18:07:23 +0200 Subject: [PATCH 2431/5498] netfilter: nf_conntrack_sip: extend request line validation commit 444f901742d054a4cd5ff045871eac5131646cfb upstream. on SIP requests, so a fragmented TCP SIP packet from an allow header starting with INVITE,NOTIFY,OPTIONS,REFER,REGISTER,UPDATE,SUBSCRIBE Content-Length: 0 will not bet interpreted as an INVITE request. Also Request-URI must start with an alphabetic character. Confirm with RFC 3261 Request-Line = Method SP Request-URI SP SIP-Version CRLF Fixes: 30f33e6dee80 ("[NETFILTER]: nf_conntrack_sip: support method specific request/response handling") Signed-off-by: Ulrich Weber Acked-by: Marco Angaroni Signed-off-by: Pablo Neira Ayuso Acked-by: Michal Kubecek Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_sip.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 885b4aba3695..1665c2159e4b 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1434,9 +1434,12 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, handler = &sip_handlers[i]; if (handler->request == NULL) continue; - if (*datalen < handler->len || + if (*datalen < handler->len + 2 || strncasecmp(*dptr, handler->method, handler->len)) continue; + if ((*dptr)[handler->len] != ' ' || + !isalpha((*dptr)[handler->len+1])) + continue; if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, &matchoff, &matchlen) <= 0) { -- GitLab From f4ba1d0e4366d63d1d09c024e8befc99c642e84b Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Sun, 3 Dec 2017 12:12:45 -0800 Subject: [PATCH 2432/5498] netfilter: nfnetlink_cthelper: Add missing permission checks commit 4b380c42f7d00a395feede754f0bc2292eebe6e5 upstream. The capability check in nfnetlink_rcv() verifies that the caller has CAP_NET_ADMIN in the namespace that "owns" the netlink socket. However, nfnl_cthelper_list is shared by all net namespaces on the system. An unprivileged user can create user and net namespaces in which he holds CAP_NET_ADMIN to bypass the netlink_net_capable() check: $ nfct helper list nfct v1.4.4: netlink error: Operation not permitted $ vpnns -- nfct helper list { .name = ftp, .queuenum = 0, .l3protonum = 2, .l4protonum = 6, .priv_data_len = 24, .status = enabled, }; Add capable() checks in nfnetlink_cthelper, as this is cleaner than trying to generalize the solution. Signed-off-by: Kevin Cernekee Signed-off-by: Pablo Neira Ayuso Acked-by: Michal Kubecek Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nfnetlink_cthelper.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 8d34a488efc0..ac143ae4f7b6 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -392,6 +393,9 @@ nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb, struct nfnl_cthelper *nlcth; int ret = 0; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE]) return -EINVAL; @@ -595,6 +599,9 @@ nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb, struct nfnl_cthelper *nlcth; bool tuple_set = false; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (nlh->nlmsg_flags & NLM_F_DUMP) { struct netlink_dump_control c = { .dump = nfnl_cthelper_dump_table, @@ -661,6 +668,9 @@ nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb, struct nfnl_cthelper *nlcth, *n; int j = 0, ret; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (tb[NFCTH_NAME]) helper_name = nla_data(tb[NFCTH_NAME]); -- GitLab From 115e3505bbd683a01496860646fa632e6533b4e3 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 5 Dec 2017 15:42:41 -0800 Subject: [PATCH 2433/5498] netfilter: xt_osf: Add missing permission checks commit 916a27901de01446bcf57ecca4783f6cff493309 upstream. The capability check in nfnetlink_rcv() verifies that the caller has CAP_NET_ADMIN in the namespace that "owns" the netlink socket. However, xt_osf_fingers is shared by all net namespaces on the system. An unprivileged user can create user and net namespaces in which he holds CAP_NET_ADMIN to bypass the netlink_net_capable() check: vpnns -- nfnl_osf -f /tmp/pf.os vpnns -- nfnl_osf -f /tmp/pf.os -d These non-root operations successfully modify the systemwide OS fingerprint list. Add new capable() checks so that they can't. Signed-off-by: Kevin Cernekee Signed-off-by: Pablo Neira Ayuso Acked-by: Michal Kubecek Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_osf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index c529161cdbf8..99f3146b7337 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -69,6 +70,9 @@ static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb, struct xt_osf_finger *kf = NULL, *sf; int err = 0; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (!osf_attrs[OSF_ATTR_FINGER]) return -EINVAL; @@ -112,6 +116,9 @@ static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb, struct xt_osf_finger *sf; int err = -ENOENT; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (!osf_attrs[OSF_ATTR_FINGER]) return -EINVAL; -- GitLab From 9ff49848ab5f2402478dd384d19966a82b9b1f09 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 22 Jun 2017 16:47:34 -0400 Subject: [PATCH 2434/5498] reiserfs: fix race in prealloc discard commit 08db141b5313ac2f64b844fb5725b8d81744b417 upstream. The main loop in __discard_prealloc is protected by the reiserfs write lock which is dropped across schedules like the BKL it replaced. The problem is that it checks the value, calls a routine that schedules, and then adjusts the state. As a result, two threads that are calling reiserfs_prealloc_discard at the same time can race when one calls reiserfs_free_prealloc_block, the lock is dropped, and the other calls reiserfs_free_prealloc_block with the same block number. In the right circumstances, it can cause the prealloc count to go negative. Signed-off-by: Jeff Mahoney Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/reiserfs/bitmap.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c index dc198bc64c61..73705d4bb069 100644 --- a/fs/reiserfs/bitmap.c +++ b/fs/reiserfs/bitmap.c @@ -513,9 +513,17 @@ static void __discard_prealloc(struct reiserfs_transaction_handle *th, "inode has negative prealloc blocks count."); #endif while (ei->i_prealloc_count > 0) { - reiserfs_free_prealloc_block(th, inode, ei->i_prealloc_block); - ei->i_prealloc_block++; + b_blocknr_t block_to_free; + + /* + * reiserfs_free_prealloc_block can drop the write lock, + * which could allow another caller to free the same block. + * We can protect against it by modifying the prealloc + * state before calling it. + */ + block_to_free = ei->i_prealloc_block++; ei->i_prealloc_count--; + reiserfs_free_prealloc_block(th, inode, block_to_free); dirty = 1; } if (dirty) -- GitLab From 1f31381a6cc0e0e5fb06371268388c00f8d04df4 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 22 Jun 2017 16:35:04 -0400 Subject: [PATCH 2435/5498] reiserfs: don't preallocate blocks for extended attributes commit 54930dfeb46e978b447af0fb8ab4e181c1bf9d7a upstream. Most extended attributes will fit in a single block. More importantly, we drop the reference to the inode while holding the transaction open so the preallocated blocks aren't released. As a result, the inode may be evicted before it's removed from the transaction's prealloc list which can cause memory corruption. Signed-off-by: Jeff Mahoney Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/reiserfs/bitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c index 73705d4bb069..edc8ef78b63f 100644 --- a/fs/reiserfs/bitmap.c +++ b/fs/reiserfs/bitmap.c @@ -1136,7 +1136,7 @@ static int determine_prealloc_size(reiserfs_blocknr_hint_t * hint) hint->prealloc_size = 0; if (!hint->formatted_node && hint->preallocate) { - if (S_ISREG(hint->inode->i_mode) + if (S_ISREG(hint->inode->i_mode) && !IS_PRIVATE(hint->inode) && hint->inode->i_size >= REISERFS_SB(hint->th->t_super)->s_alloc_options. preallocmin * hint->inode->i_sb->s_blocksize) -- GitLab From efa42537beb631bffe714e7542982fc5dad9a8fd Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 13 Jun 2017 13:35:51 +0200 Subject: [PATCH 2436/5498] fs/fcntl: f_setown, avoid undefined behaviour commit fc3dc67471461c0efcb1ed22fb7595121d65fad9 upstream. fcntl(0, F_SETOWN, 0x80000000) triggers: UBSAN: Undefined behaviour in fs/fcntl.c:118:7 negation of -2147483648 cannot be represented in type 'int': CPU: 1 PID: 18261 Comm: syz-executor Not tainted 4.8.1-0-syzkaller #1 ... Call Trace: ... [] ? f_setown+0x1d8/0x200 [] ? SyS_fcntl+0x999/0xf30 [] ? entry_SYSCALL_64_fastpath+0x23/0xc1 Fix that by checking the arg parameter properly (against INT_MAX) before "who = -who". And return immediatelly with -EINVAL in case it is wrong. Note that according to POSIX we can return EINVAL: http://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html [EINVAL] The cmd argument is F_SETOWN and the value of the argument is not valid as a process or process group identifier. [v2] returns an error, v1 used to fail silently [v3] implement proper check for the bad value INT_MIN Signed-off-by: Jiri Slaby Cc: Jeff Layton Cc: "J. Bruce Fields" Cc: Alexander Viro Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Jeff Layton Signed-off-by: Greg Kroah-Hartman --- fs/fcntl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/fcntl.c b/fs/fcntl.c index 99d440a4a6ba..45f00f239ac5 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -113,6 +113,10 @@ void f_setown(struct file *filp, unsigned long arg, int force) int who = arg; type = PIDTYPE_PID; if (who < 0) { + /* avoid overflow below */ + if (who == INT_MIN) + return; + type = PIDTYPE_PGID; who = -who; } -- GitLab From bdc0646c91cddc06ab84236affe21e78498ceaa4 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 9 Oct 2017 13:33:19 +0200 Subject: [PATCH 2437/5498] scsi: libiscsi: fix shifting of DID_REQUEUE host byte commit eef9ffdf9cd39b2986367bc8395e2772bc1284ba upstream. The SCSI host byte should be shifted left by 16 in order to have scsi_decide_disposition() do the right thing (.i.e. requeue the command). Signed-off-by: Johannes Thumshirn Fixes: 661134ad3765 ("[SCSI] libiscsi, bnx2i: make bound ep check common") Cc: Lee Duncan Cc: Hannes Reinecke Cc: Bart Van Assche Cc: Chris Leech Acked-by: Lee Duncan Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 4c74cf9ffe16..0f100b35f9ab 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1727,7 +1727,7 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { reason = FAILURE_SESSION_IN_RECOVERY; - sc->result = DID_REQUEUE; + sc->result = DID_REQUEUE << 16; goto fault; } -- GitLab From 933e32f87f6e9e006d501e6cb6d46f912acb80f2 Mon Sep 17 00:00:00 2001 From: Greg KH Date: Wed, 8 Mar 2017 19:03:44 +0100 Subject: [PATCH 2438/5498] eventpoll.h: add missing epoll event masks commit 7e040726850a106587485c21bdacc0bfc8a0cbed upstream. [resend due to me forgetting to cc: linux-api the first time around I posted these back on Feb 23] From: Greg Kroah-Hartman For some reason these values are not in the uapi header file, so any libc has to define it themselves. To prevent them from needing to do this, just have the kernel provide the correct values. Reported-by: Elliott Hughes Signed-off-by: Greg Hackmann Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/eventpoll.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/uapi/linux/eventpoll.h b/include/uapi/linux/eventpoll.h index bc81fb2e1f0e..6f04cb419115 100644 --- a/include/uapi/linux/eventpoll.h +++ b/include/uapi/linux/eventpoll.h @@ -26,6 +26,19 @@ #define EPOLL_CTL_DEL 2 #define EPOLL_CTL_MOD 3 +/* Epoll event masks */ +#define EPOLLIN 0x00000001 +#define EPOLLPRI 0x00000002 +#define EPOLLOUT 0x00000004 +#define EPOLLERR 0x00000008 +#define EPOLLHUP 0x00000010 +#define EPOLLRDNORM 0x00000040 +#define EPOLLRDBAND 0x00000080 +#define EPOLLWRNORM 0x00000100 +#define EPOLLWRBAND 0x00000200 +#define EPOLLMSG 0x00000400 +#define EPOLLRDHUP 0x00002000 + /* * Request the handling of system wakeup events so as to prevent system suspends * from happening while those events are being processed. -- GitLab From 8e292f88f756db5c19a1d4478a8e443687980949 Mon Sep 17 00:00:00 2001 From: Jia Zhang Date: Tue, 23 Jan 2018 11:41:32 +0100 Subject: [PATCH 2439/5498] x86/microcode/intel: Extend BDW late-loading further with LLC size check commit 7e702d17ed138cf4ae7c00e8c00681ed464587c7 upstream. Commit b94b73733171 ("x86/microcode/intel: Extend BDW late-loading with a revision check") reduced the impact of erratum BDF90 for Broadwell model 79. The impact can be reduced further by checking the size of the last level cache portion per core. Tony: "The erratum says the problem only occurs on the large-cache SKUs. So we only need to avoid the update if we are on a big cache SKU that is also running old microcode." For more details, see erratum BDF90 in document #334165 (Intel Xeon Processor E7-8800/4800 v4 Product Family Specification Update) from September 2017. Fixes: b94b73733171 ("x86/microcode/intel: Extend BDW late-loading with a revision check") Signed-off-by: Jia Zhang Signed-off-by: Borislav Petkov Signed-off-by: Thomas Gleixner Acked-by: Tony Luck Link: https://lkml.kernel.org/r/1516321542-31161-1-git-send-email-zhang.jia@linux.alibaba.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/microcode/intel.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index bd9603279524..73293d879d4e 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -87,6 +87,9 @@ MODULE_DESCRIPTION("Microcode Update Driver"); MODULE_AUTHOR("Tigran Aivazian "); MODULE_LICENSE("GPL"); +/* last level cache size per core */ +static int llc_size_per_core; + static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) { struct cpuinfo_x86 *c = &cpu_data(cpu_num); @@ -273,12 +276,14 @@ static bool is_blacklisted(unsigned int cpu) /* * Late loading on model 79 with microcode revision less than 0x0b000021 - * may result in a system hang. This behavior is documented in item - * BDF90, #334165 (Intel Xeon Processor E7-8800/4800 v4 Product Family). + * and LLC size per core bigger than 2.5MB may result in a system hang. + * This behavior is documented in item BDF90, #334165 (Intel Xeon + * Processor E7-8800/4800 v4 Product Family). */ if (c->x86 == 6 && c->x86_model == 79 && c->x86_mask == 0x01 && + llc_size_per_core > 2621440 && c->microcode < 0x0b000021) { pr_err_once("Erratum BDF90: late loading with revision < 0x0b000021 (0x%x) disabled.\n", c->microcode); pr_err_once("Please consider either early loading through initrd/built-in or a potential BIOS update.\n"); @@ -345,6 +350,15 @@ static struct microcode_ops microcode_intel_ops = { .microcode_fini_cpu = microcode_fini_cpu, }; +static int __init calc_llc_size_per_core(struct cpuinfo_x86 *c) +{ + u64 llc_size = c->x86_cache_size * 1024; + + do_div(llc_size, c->x86_max_cores); + + return (int)llc_size; +} + struct microcode_ops * __init init_intel_microcode(void) { struct cpuinfo_x86 *c = &cpu_data(0); @@ -355,6 +369,8 @@ struct microcode_ops * __init init_intel_microcode(void) return NULL; } + llc_size_per_core = calc_llc_size_per_core(c); + return µcode_intel_ops; } -- GitLab From cf1a60fdfadc1647c4078bdc811601623f72ff52 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 18 Jan 2018 16:14:26 -0500 Subject: [PATCH 2440/5498] net: tcp: close sock if net namespace is exiting [ Upstream commit 4ee806d51176ba7b8ff1efd81f271d7252e03a1d ] When a tcp socket is closed, if it detects that its net namespace is exiting, close immediately and do not wait for FIN sequence. For normal sockets, a reference is taken to their net namespace, so it will never exit while the socket is open. However, kernel sockets do not take a reference to their net namespace, so it may begin exiting while the kernel socket is still open. In this case if the kernel socket is a tcp socket, it will stay open trying to complete its close sequence. The sock's dst(s) hold a reference to their interface, which are all transferred to the namespace's loopback interface when the real interfaces are taken down. When the namespace tries to take down its loopback interface, it hangs waiting for all references to the loopback interface to release, which results in messages like: unregister_netdevice: waiting for lo to become free. Usage count = 1 These messages continue until the socket finally times out and closes. Since the net namespace cleanup holds the net_mutex while calling its registered pernet callbacks, any new net namespace initialization is blocked until the current net namespace finishes exiting. After this change, the tcp socket notices the exiting net namespace, and closes immediately, releasing its dst(s) and their reference to the loopback interface, which lets the net namespace continue exiting. Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1711407 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=97811 Signed-off-by: Dan Streetman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/net_namespace.h | 10 ++++++++++ net/ipv4/tcp.c | 3 +++ net/ipv4/tcp_timer.c | 15 +++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index e0d64667a4b3..ff4081af4d9c 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -200,6 +200,11 @@ int net_eq(const struct net *net1, const struct net *net2) return net1 == net2; } +static inline int check_net(const struct net *net) +{ + return atomic_read(&net->count) != 0; +} + void net_drop_ns(void *); #else @@ -224,6 +229,11 @@ int net_eq(const struct net *net1, const struct net *net2) return 1; } +static inline int check_net(const struct net *net) +{ + return 1; +} + #define net_drop_ns NULL #endif diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 567f8860e722..357d6b3caa84 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2182,6 +2182,9 @@ adjudge_to_death: tcp_send_active_reset(sk, GFP_ATOMIC); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY); + } else if (!check_net(sock_net(sk))) { + /* Not possible to send reset; just close */ + tcp_set_state(sk, TCP_CLOSE); } } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index f146c10ca11d..712c598c5a90 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -46,11 +46,19 @@ static void tcp_write_err(struct sock *sk) * to prevent DoS attacks. It is called when a retransmission timeout * or zero probe timeout occurs on orphaned socket. * + * Also close if our net namespace is exiting; in that case there is no + * hope of ever communicating again since all netns interfaces are already + * down (or about to be down), and we need to release our dst references, + * which have been moved to the netns loopback interface, so the namespace + * can finish exiting. This condition is only possible if we are a kernel + * socket, as those do not hold references to the namespace. + * * Criteria is still not confirmed experimentally and may change. * We kill the socket, if: * 1. If number of orphaned sockets exceeds an administratively configured * limit. * 2. If we have strong memory pressure. + * 3. If our net namespace is exiting. */ static int tcp_out_of_resources(struct sock *sk, bool do_reset) { @@ -79,6 +87,13 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset) NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY); return 1; } + + if (!check_net(sock_net(sk))) { + /* Not possible to send reset; just close */ + tcp_done(sk); + return 1; + } + return 0; } -- GitLab From 607506a8e7b0faa8ea19ebbad323e74fefe2ef47 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Fri, 26 Jan 2018 15:14:16 +0300 Subject: [PATCH 2441/5498] dccp: don't restart ccid2_hc_tx_rto_expire() if sk in closed state [ Upstream commit dd5684ecae3bd8e44b644f50e2c12c7e57fdfef5 ] ccid2_hc_tx_rto_expire() timer callback always restarts the timer again and can run indefinitely (unless it is stopped outside), and after commit 120e9dabaf55 ("dccp: defer ccid_hc_tx_delete() at dismantle time"), which moved ccid_hc_tx_delete() (also includes sk_stop_timer()) from dccp_destroy_sock() to sk_destruct(), this started to happen quite often. The timer prevents releasing the socket, as a result, sk_destruct() won't be called. Found with LTP/dccp_ipsec tests running on the bonding device, which later couldn't be unloaded after the tests were completed: unregister_netdevice: waiting for bond0 to become free. Usage count = 148 Fixes: 2a91aa396739 ("[DCCP] CCID2: Initial CCID2 (TCP-Like) implementation") Signed-off-by: Alexey Kodanev Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/dccp/ccids/ccid2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index 5e3a7302f774..7753681195c1 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -140,6 +140,9 @@ static void ccid2_hc_tx_rto_expire(unsigned long data) ccid2_pr_debug("RTO_EXPIRE\n"); + if (sk->sk_state == DCCP_CLOSED) + goto out; + /* back-off timer */ hc->tx_rto <<= 1; if (hc->tx_rto > DCCP_RTO_MAX) -- GitLab From 483217b88c632e1e510569f6aac6e914f598f21d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 19 Jan 2018 11:50:46 +0100 Subject: [PATCH 2442/5498] net: igmp: fix source address check for IGMPv3 reports [ Upstream commit ad23b750933ea7bf962678972a286c78a8fa36aa ] Commit "net: igmp: Use correct source address on IGMPv3 reports" introduced a check to validate the source address of locally generated IGMPv3 packets. Instead of checking the local interface address directly, it uses inet_ifa_match(fl4->saddr, ifa), which checks if the address is on the local subnet (or equal to the point-to-point address if used). This breaks for point-to-point interfaces, so check against ifa->ifa_local directly. Cc: Kevin Cernekee Fixes: a46182b00290 ("net: igmp: Use correct source address on IGMPv3 reports") Reported-by: Sebastian Gottschall Signed-off-by: Felix Fietkau Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/igmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 71882e83bbbd..642dbbde93e4 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -329,7 +329,7 @@ static __be32 igmpv3_get_srcaddr(struct net_device *dev, return htonl(INADDR_ANY); for_ifa(in_dev) { - if (inet_ifa_match(fl4->saddr, ifa)) + if (fl4->saddr == ifa->ifa_local) return fl4->saddr; } endfor_ifa(in_dev); -- GitLab From eaf3179d61514268a863cb9d50d66f6ebce1d5a0 Mon Sep 17 00:00:00 2001 From: Craig Gallek Date: Wed, 10 Feb 2016 11:50:37 -0500 Subject: [PATCH 2443/5498] tcp: __tcp_hdrlen() helper commit d9b3fca27385eafe61c3ca6feab6cb1e7dc77482 upstream. tcp_hdrlen is wasteful if you already have a pointer to struct tcphdr. This splits the size calculation into a helper function that can be used if a struct tcphdr is already available. Signed-off-by: Craig Gallek Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/linux/tcp.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index c2dee7deefa8..9891c30e8031 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -29,9 +29,14 @@ static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb) return (struct tcphdr *)skb_transport_header(skb); } +static inline unsigned int __tcp_hdrlen(const struct tcphdr *th) +{ + return th->doff * 4; +} + static inline unsigned int tcp_hdrlen(const struct sk_buff *skb) { - return tcp_hdr(skb)->doff * 4; + return __tcp_hdrlen(tcp_hdr(skb)); } static inline struct tcphdr *inner_tcp_hdr(const struct sk_buff *skb) -- GitLab From d8dee18861fb17e6f5f59673fc10974f7872c7f4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 18 Jan 2018 19:59:19 -0800 Subject: [PATCH 2444/5498] net: qdisc_pkt_len_init() should be more robust [ Upstream commit 7c68d1a6b4db9012790af7ac0f0fdc0d2083422a ] Without proper validation of DODGY packets, we might very well feed qdisc_pkt_len_init() with invalid GSO packets. tcp_hdrlen() might access out-of-bound data, so let's use skb_header_pointer() and proper checks. Whole story is described in commit d0c081b49137 ("flow_dissector: properly cap thoff field") We have the goal of validating DODGY packets earlier in the stack, so we might very well revert this fix in the future. Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Jason Wang Reported-by: syzbot+9da69ebac7dddd804552@syzkaller.appspotmail.com Acked-by: Jason Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/dev.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index cae21f769212..3d2dde9040af 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2772,10 +2772,21 @@ static void qdisc_pkt_len_init(struct sk_buff *skb) hdr_len = skb_transport_header(skb) - skb_mac_header(skb); /* + transport layer */ - if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) - hdr_len += tcp_hdrlen(skb); - else - hdr_len += sizeof(struct udphdr); + if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) { + const struct tcphdr *th; + struct tcphdr _tcphdr; + + th = skb_header_pointer(skb, skb_transport_offset(skb), + sizeof(_tcphdr), &_tcphdr); + if (likely(th)) + hdr_len += __tcp_hdrlen(th); + } else { + struct udphdr _udphdr; + + if (skb_header_pointer(skb, skb_transport_offset(skb), + sizeof(_udphdr), &_udphdr)) + hdr_len += sizeof(struct udphdr); + } if (shinfo->gso_type & SKB_GSO_DODGY) gso_segs = DIV_ROUND_UP(skb->len - hdr_len, -- GitLab From 0e4c26c860609df87e52ab334cef36993184727c Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Mon, 22 Jan 2018 18:06:37 +0100 Subject: [PATCH 2445/5498] pppoe: take ->needed_headroom of lower device into account on xmit [ Upstream commit 02612bb05e51df8489db5e94d0cf8d1c81f87b0c ] In pppoe_sendmsg(), reserving dev->hard_header_len bytes of headroom was probably fine before the introduction of ->needed_headroom in commit f5184d267c1a ("net: Allow netdevices to specify needed head/tailroom"). But now, virtual devices typically advertise the size of their overhead in dev->needed_headroom, so we must also take it into account in skb_reserve(). Allocation size of skb is also updated to take dev->needed_tailroom into account and replace the arbitrary 32 bytes with the real size of a PPPoE header. This issue was discovered by syzbot, who connected a pppoe socket to a gre device which had dev->header_ops->create == ipgre_header and dev->hard_header_len == 0. Therefore, PPPoE didn't reserve any headroom, and dev_hard_header() crashed when ipgre_header() tried to prepend its header to skb->data. skbuff: skb_under_panic: text:000000001d390b3a len:31 put:24 head:00000000d8ed776f data:000000008150e823 tail:0x7 end:0xc0 dev:gre0 ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:104! invalid opcode: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 1 PID: 3670 Comm: syzkaller801466 Not tainted 4.15.0-rc7-next-20180115+ #97 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:skb_panic+0x162/0x1f0 net/core/skbuff.c:100 RSP: 0018:ffff8801d9bd7840 EFLAGS: 00010282 RAX: 0000000000000083 RBX: ffff8801d4f083c0 RCX: 0000000000000000 RDX: 0000000000000083 RSI: 1ffff1003b37ae92 RDI: ffffed003b37aefc RBP: ffff8801d9bd78a8 R08: 1ffff1003b37ae8a R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000000 R12: ffffffff86200de0 R13: ffffffff84a981ad R14: 0000000000000018 R15: ffff8801d2d34180 FS: 00000000019c4880(0000) GS:ffff8801db300000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000208bc000 CR3: 00000001d9111001 CR4: 00000000001606e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: skb_under_panic net/core/skbuff.c:114 [inline] skb_push+0xce/0xf0 net/core/skbuff.c:1714 ipgre_header+0x6d/0x4e0 net/ipv4/ip_gre.c:879 dev_hard_header include/linux/netdevice.h:2723 [inline] pppoe_sendmsg+0x58e/0x8b0 drivers/net/ppp/pppoe.c:890 sock_sendmsg_nosec net/socket.c:630 [inline] sock_sendmsg+0xca/0x110 net/socket.c:640 sock_write_iter+0x31a/0x5d0 net/socket.c:909 call_write_iter include/linux/fs.h:1775 [inline] do_iter_readv_writev+0x525/0x7f0 fs/read_write.c:653 do_iter_write+0x154/0x540 fs/read_write.c:932 vfs_writev+0x18a/0x340 fs/read_write.c:977 do_writev+0xfc/0x2a0 fs/read_write.c:1012 SYSC_writev fs/read_write.c:1085 [inline] SyS_writev+0x27/0x30 fs/read_write.c:1082 entry_SYSCALL_64_fastpath+0x29/0xa0 Admittedly PPPoE shouldn't be allowed to run on non Ethernet-like interfaces, but reserving space for ->needed_headroom is a more fundamental issue that needs to be addressed first. Same problem exists for __pppoe_xmit(), which also needs to take dev->needed_headroom into account in skb_cow_head(). Fixes: f5184d267c1a ("net: Allow netdevices to specify needed head/tailroom") Reported-by: syzbot+ed0838d0fa4c4f2b528e20286e6dc63effc7c14d@syzkaller.appspotmail.com Signed-off-by: Guillaume Nault Reviewed-by: Xin Long Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ppp/pppoe.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 5aa563136373..262c30a514f5 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -830,6 +830,7 @@ static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, struct pppoe_hdr *ph; struct net_device *dev; char *start; + int hlen; lock_sock(sk); if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) { @@ -848,16 +849,16 @@ static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, if (total_len > (dev->mtu + dev->hard_header_len)) goto end; - - skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32, - 0, GFP_KERNEL); + hlen = LL_RESERVED_SPACE(dev); + skb = sock_wmalloc(sk, hlen + sizeof(*ph) + total_len + + dev->needed_tailroom, 0, GFP_KERNEL); if (!skb) { error = -ENOMEM; goto end; } /* Reserve space for headers. */ - skb_reserve(skb, dev->hard_header_len); + skb_reserve(skb, hlen); skb_reset_network_header(skb); skb->dev = dev; @@ -918,7 +919,7 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) /* Copy the data if there is no space for the header or if it's * read-only. */ - if (skb_cow_head(skb, sizeof(*ph) + dev->hard_header_len)) + if (skb_cow_head(skb, LL_RESERVED_SPACE(dev) + sizeof(*ph))) goto abort; __skb_push(skb, sizeof(*ph)); -- GitLab From 8009d86cc9441149d0711b903edbd90250f39fe0 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 15 Jan 2018 17:02:00 +0800 Subject: [PATCH 2446/5498] sctp: do not allow the v4 socket to bind a v4mapped v6 address [ Upstream commit c5006b8aa74599ce19104b31d322d2ea9ff887cc ] The check in sctp_sockaddr_af is not robust enough to forbid binding a v4mapped v6 addr on a v4 socket. The worse thing is that v4 socket's bind_verify would not convert this v4mapped v6 addr to a v4 addr. syzbot even reported a crash as the v4 socket bound a v6 addr. This patch is to fix it by doing the common sa.sa_family check first, then AF_INET check for v4mapped v6 addrs. Fixes: 7dab83de50c7 ("sctp: Support ipv6only AF_INET6 sockets.") Reported-by: syzbot+7b7b518b1228d2743963@syzkaller.appspotmail.com Acked-by: Neil Horman Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/socket.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 4477a5443f65..ff45930700a3 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -333,16 +333,14 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt, if (len < sizeof (struct sockaddr)) return NULL; + if (!opt->pf->af_supported(addr->sa.sa_family, opt)) + return NULL; + /* V4 mapped address are really of AF_INET family */ if (addr->sa.sa_family == AF_INET6 && - ipv6_addr_v4mapped(&addr->v6.sin6_addr)) { - if (!opt->pf->af_supported(AF_INET, opt)) - return NULL; - } else { - /* Does this PF support this AF? */ - if (!opt->pf->af_supported(addr->sa.sa_family, opt)) - return NULL; - } + ipv6_addr_v4mapped(&addr->v6.sin6_addr) && + !opt->pf->af_supported(AF_INET, opt)) + return NULL; /* If we get this far, af is valid. */ af = sctp_get_af_specific(addr->sa.sa_family); -- GitLab From e34d42ce65b5d0ff26a25be9674549023294062b Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 15 Jan 2018 17:01:36 +0800 Subject: [PATCH 2447/5498] sctp: return error if the asoc has been peeled off in sctp_wait_for_sndbuf [ Upstream commit a0ff660058b88d12625a783ce9e5c1371c87951f ] After commit cea0cc80a677 ("sctp: use the right sk after waking up from wait_buf sleep"), it may change to lock another sk if the asoc has been peeled off in sctp_wait_for_sndbuf. However, the asoc's new sk could be already closed elsewhere, as it's in the sendmsg context of the old sk that can't avoid the new sk's closing. If the sk's last one refcnt is held by this asoc, later on after putting this asoc, the new sk will be freed, while under it's own lock. This patch is to revert that commit, but fix the old issue by returning error under the old sk's lock. Fixes: cea0cc80a677 ("sctp: use the right sk after waking up from wait_buf sleep") Reported-by: syzbot+ac6ea7baa4432811eb50@syzkaller.appspotmail.com Signed-off-by: Xin Long Acked-by: Neil Horman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/socket.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index ff45930700a3..06d9626a1678 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -83,7 +83,7 @@ static int sctp_writeable(struct sock *sk); static void sctp_wfree(struct sk_buff *skb); static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, - size_t msg_len, struct sock **orig_sk); + size_t msg_len); static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p); static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); static int sctp_wait_for_accept(struct sock *sk, long timeo); @@ -1948,7 +1948,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); if (!sctp_wspace(asoc)) { /* sk can be changed by peel off when waiting for buf. */ - err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len, &sk); + err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); if (err) { if (err == -ESRCH) { /* asoc is already dead. */ @@ -6981,12 +6981,12 @@ void sctp_sock_rfree(struct sk_buff *skb) /* Helper function to wait for space in the sndbuf. */ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, - size_t msg_len, struct sock **orig_sk) + size_t msg_len) { struct sock *sk = asoc->base.sk; - int err = 0; long current_timeo = *timeo_p; DEFINE_WAIT(wait); + int err = 0; pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc, *timeo_p, msg_len); @@ -7015,17 +7015,13 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, release_sock(sk); current_timeo = schedule_timeout(current_timeo); lock_sock(sk); - if (sk != asoc->base.sk) { - release_sock(sk); - sk = asoc->base.sk; - lock_sock(sk); - } + if (sk != asoc->base.sk) + goto do_error; *timeo_p = current_timeo; } out: - *orig_sk = sk; finish_wait(&asoc->wait, &wait); /* Release the association's refcnt. */ -- GitLab From 1414a1ede0ea554364d3cfbfe8dbf650f9d57a29 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 22 Jan 2018 16:06:37 -0500 Subject: [PATCH 2448/5498] vmxnet3: repair memory leak [ Upstream commit 848b159835ddef99cc4193083f7e786c3992f580 ] with the introduction of commit b0eb57cb97e7837ebb746404c2c58c6f536f23fa, it appears that rq->buf_info is improperly handled. While it is heap allocated when an rx queue is setup, and freed when torn down, an old line of code in vmxnet3_rq_destroy was not properly removed, leading to rq->buf_info[0] being set to NULL prior to its being freed, causing a memory leak, which eventually exhausts the system on repeated create/destroy operations (for example, when the mtu of a vmxnet3 interface is changed frequently. Fix is pretty straight forward, just move the NULL set to after the free. Tested by myself with successful results Applies to net, and should likely be queued for stable, please Signed-off-by: Neil Horman Reported-By: boyang@redhat.com CC: boyang@redhat.com CC: Shrikrishna Khare CC: "VMware, Inc." CC: David S. Miller Acked-by: Shrikrishna Khare Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/vmxnet3/vmxnet3_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 6dfcbf523936..a26ae97f755d 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1420,7 +1420,6 @@ static void vmxnet3_rq_destroy(struct vmxnet3_rx_queue *rq, rq->rx_ring[i].basePA); rq->rx_ring[i].base = NULL; } - rq->buf_info[i] = NULL; } if (rq->comp_ring.base) { @@ -1435,6 +1434,7 @@ static void vmxnet3_rq_destroy(struct vmxnet3_rx_queue *rq, (rq->rx_ring[0].size + rq->rx_ring[1].size); dma_free_coherent(&adapter->pdev->dev, sz, rq->buf_info[0], rq->buf_info_pa); + rq->buf_info[0] = rq->buf_info[1] = NULL; } } -- GitLab From 98fcfb3f64a05102768218443b37ea025a0c485d Mon Sep 17 00:00:00 2001 From: Jim Westfall Date: Sun, 14 Jan 2018 04:18:50 -0800 Subject: [PATCH 2449/5498] net: Allow neigh contructor functions ability to modify the primary_key [ Upstream commit 096b9854c04df86f03b38a97d40b6506e5730919 ] Use n->primary_key instead of pkey to account for the possibility that a neigh constructor function may have modified the primary_key value. Signed-off-by: Jim Westfall Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/neighbour.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 59399fad65a2..11dc427320bd 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -508,7 +508,7 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, if (atomic_read(&tbl->entries) > (1 << nht->hash_shift)) nht = neigh_hash_grow(tbl, nht->hash_shift + 1); - hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift); + hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift); if (n->parms->dead) { rc = ERR_PTR(-EINVAL); @@ -520,7 +520,7 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, n1 != NULL; n1 = rcu_dereference_protected(n1->next, lockdep_is_held(&tbl->lock))) { - if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) { + if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) { if (want_ref) neigh_hold(n1); rc = n1; -- GitLab From fd3030090c8debb485d3867c17f691cf1af207f6 Mon Sep 17 00:00:00 2001 From: Mike Maloney Date: Wed, 10 Jan 2018 12:45:10 -0500 Subject: [PATCH 2450/5498] ipv6: fix udpv6 sendmsg crash caused by too small MTU [ Upstream commit 749439bfac6e1a2932c582e2699f91d329658196 ] The logic in __ip6_append_data() assumes that the MTU is at least large enough for the headers. A device's MTU may be adjusted after being added while sendmsg() is processing data, resulting in __ip6_append_data() seeing any MTU. For an mtu smaller than the size of the fragmentation header, the math results in a negative 'maxfraglen', which causes problems when refragmenting any previous skb in the skb_write_queue, leaving it possibly malformed. Instead sendmsg returns EINVAL when the mtu is calculated to be less than IPV6_MIN_MTU. Found by syzkaller: kernel BUG at ./include/linux/skbuff.h:2064! invalid opcode: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 1 PID: 14216 Comm: syz-executor5 Not tainted 4.13.0-rc4+ #2 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 task: ffff8801d0b68580 task.stack: ffff8801ac6b8000 RIP: 0010:__skb_pull include/linux/skbuff.h:2064 [inline] RIP: 0010:__ip6_make_skb+0x18cf/0x1f70 net/ipv6/ip6_output.c:1617 RSP: 0018:ffff8801ac6bf570 EFLAGS: 00010216 RAX: 0000000000010000 RBX: 0000000000000028 RCX: ffffc90003cce000 RDX: 00000000000001b8 RSI: ffffffff839df06f RDI: ffff8801d9478ca0 RBP: ffff8801ac6bf780 R08: ffff8801cc3f1dbc R09: 0000000000000000 R10: ffff8801ac6bf7a0 R11: 43cb4b7b1948a9e7 R12: ffff8801cc3f1dc8 R13: ffff8801cc3f1d40 R14: 0000000000001036 R15: dffffc0000000000 FS: 00007f43d740c700(0000) GS:ffff8801dc100000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f7834984000 CR3: 00000001d79b9000 CR4: 00000000001406e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: ip6_finish_skb include/net/ipv6.h:911 [inline] udp_v6_push_pending_frames+0x255/0x390 net/ipv6/udp.c:1093 udpv6_sendmsg+0x280d/0x31a0 net/ipv6/udp.c:1363 inet_sendmsg+0x11f/0x5e0 net/ipv4/af_inet.c:762 sock_sendmsg_nosec net/socket.c:633 [inline] sock_sendmsg+0xca/0x110 net/socket.c:643 SYSC_sendto+0x352/0x5a0 net/socket.c:1750 SyS_sendto+0x40/0x50 net/socket.c:1718 entry_SYSCALL_64_fastpath+0x1f/0xbe RIP: 0033:0x4512e9 RSP: 002b:00007f43d740bc08 EFLAGS: 00000216 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 00000000007180a8 RCX: 00000000004512e9 RDX: 000000000000002e RSI: 0000000020d08000 RDI: 0000000000000005 RBP: 0000000000000086 R08: 00000000209c1000 R09: 000000000000001c R10: 0000000000040800 R11: 0000000000000216 R12: 00000000004b9c69 R13: 00000000ffffffff R14: 0000000000000005 R15: 00000000202c2000 Code: 9e 01 fe e9 c5 e8 ff ff e8 7f 9e 01 fe e9 4a ea ff ff 48 89 f7 e8 52 9e 01 fe e9 aa eb ff ff e8 a8 b6 cf fd 0f 0b e8 a1 b6 cf fd <0f> 0b 49 8d 45 78 4d 8d 45 7c 48 89 85 78 fe ff ff 49 8d 85 ba RIP: __skb_pull include/linux/skbuff.h:2064 [inline] RSP: ffff8801ac6bf570 RIP: __ip6_make_skb+0x18cf/0x1f70 net/ipv6/ip6_output.c:1617 RSP: ffff8801ac6bf570 Reported-by: syzbot Signed-off-by: Mike Maloney Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_output.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 6db1f8ad8ac3..8aeedb1fed89 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1214,14 +1214,16 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, np->cork.tclass = tclass; if (rt->dst.flags & DST_XFRM_TUNNEL) mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? - rt->dst.dev->mtu : dst_mtu(&rt->dst); + READ_ONCE(rt->dst.dev->mtu) : dst_mtu(&rt->dst); else mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? - rt->dst.dev->mtu : dst_mtu(rt->dst.path); + READ_ONCE(rt->dst.dev->mtu) : dst_mtu(rt->dst.path); if (np->frag_size < mtu) { if (np->frag_size) mtu = np->frag_size; } + if (mtu < IPV6_MIN_MTU) + return -EINVAL; cork->fragsize = mtu; if (dst_allfrag(rt->dst.path)) cork->flags |= IPCORK_ALLFRAG; -- GitLab From 6c16fa957e84c8b640dadc4e0264ff0d2dae7aa3 Mon Sep 17 00:00:00 2001 From: Jim Westfall Date: Sun, 14 Jan 2018 04:18:51 -0800 Subject: [PATCH 2451/5498] ipv4: Make neigh lookup keys for loopback/point-to-point devices be INADDR_ANY [ Upstream commit cd9ff4de0107c65d69d02253bb25d6db93c3dbc1 ] Map all lookup neigh keys to INADDR_ANY for loopback/point-to-point devices to avoid making an entry for every remote ip the device needs to talk to. This used the be the old behavior but became broken in a263b3093641f (ipv4: Make neigh lookups directly in output packet path) and later removed in 0bb4087cbec0 (ipv4: Fix neigh lookup keying over loopback/point-to-point devices) because it was broken. Signed-off-by: Jim Westfall Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/net/arp.h | 3 +++ net/ipv4/arp.c | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/net/arp.h b/include/net/arp.h index 73c49864076b..174014585ade 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -37,6 +37,9 @@ static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 { struct neighbour *n; + if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + key = INADDR_ANY; + rcu_read_lock_bh(); n = __ipv4_neigh_lookup_noref(dev, key); if (n && !atomic_inc_not_zero(&n->refcnt)) diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 16acb59d665e..263729dea532 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -221,11 +221,16 @@ static u32 arp_hash(const void *pkey, static int arp_constructor(struct neighbour *neigh) { - __be32 addr = *(__be32 *)neigh->primary_key; + __be32 addr; struct net_device *dev = neigh->dev; struct in_device *in_dev; struct neigh_parms *parms; + u32 inaddr_any = INADDR_ANY; + if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + memcpy(neigh->primary_key, &inaddr_any, arp_tbl.key_len); + + addr = *(__be32 *)neigh->primary_key; rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev == NULL) { -- GitLab From 7303968d539622a5173d7f08e6938c91b48d3cd8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 26 Jan 2018 14:54:32 +0100 Subject: [PATCH 2452/5498] hrtimer: Reset hrtimer cpu base proper on CPU hotplug commit d5421ea43d30701e03cadc56a38854c36a8b4433 upstream. The hrtimer interrupt code contains a hang detection and mitigation mechanism, which prevents that a long delayed hrtimer interrupt causes a continous retriggering of interrupts which prevent the system from making progress. If a hang is detected then the timer hardware is programmed with a certain delay into the future and a flag is set in the hrtimer cpu base which prevents newly enqueued timers from reprogramming the timer hardware prior to the chosen delay. The subsequent hrtimer interrupt after the delay clears the flag and resumes normal operation. If such a hang happens in the last hrtimer interrupt before a CPU is unplugged then the hang_detected flag is set and stays that way when the CPU is plugged in again. At that point the timer hardware is not armed and it cannot be armed because the hang_detected flag is still active, so nothing clears that flag. As a consequence the CPU does not receive hrtimer interrupts and no timers expire on that CPU which results in RCU stalls and other malfunctions. Clear the flag along with some other less critical members of the hrtimer cpu base to ensure starting from a clean state when a CPU is plugged in. Thanks to Paul, Sebastian and Anna-Maria for their help to get down to the root cause of that hard to reproduce heisenbug. Once understood it's trivial and certainly justifies a brown paperbag. Fixes: 41d2e4949377 ("hrtimer: Tune hrtimer_interrupt hang logic") Reported-by: Paul E. McKenney Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: Sebastian Sewior Cc: Anna-Maria Gleixner Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/alpine.DEB.2.20.1801261447590.2067@nanos [bigeasy: backport to v3.18, drop ->next_timer it was introduced later] Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- kernel/time/hrtimer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 210b84882935..e4c722437708 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -612,6 +612,7 @@ static int hrtimer_reprogram(struct hrtimer *timer, static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { base->expires_next.tv64 = KTIME_MAX; + base->hang_detected = 0; base->hres_active = 0; } @@ -1632,6 +1633,7 @@ static void init_hrtimers_cpu(int cpu) timerqueue_init_head(&cpu_base->clock_base[i].active); } + cpu_base->active_bases = 0; cpu_base->cpu = cpu; hrtimer_init_hres(cpu_base); } -- GitLab From 90aaf2f25609f99b63fcbed280716f80b4bc5f56 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 31 Jan 2018 14:46:16 +0100 Subject: [PATCH 2453/5498] Linux 3.18.93 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d2e18e2dc1fb..172d9e596d25 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 92 +SUBLEVEL = 93 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 753de1765e9f6ea2821af507bee06f97125c5c9a Mon Sep 17 00:00:00 2001 From: Hardik Arya Date: Fri, 10 Nov 2017 16:29:16 +0530 Subject: [PATCH 2454/5498] diag: Fix possible use-after-free issue for mdlog session info Currently there is a possibility of accessing freed mdlog session info and it's attributes after closing the session. The patch adds protection while accessing mdlog session info for preventing use-after-free issue. CRs-Fixed: 2133028 Change-Id: Iba603bc2d75f84a604df2627e8fcb2a18acf4637 Signed-off-by: Hardik Arya --- drivers/char/diag/diag_masks.c | 142 ++++++++++++++------- drivers/char/diag/diag_masks.h | 5 +- drivers/char/diag/diag_memorydevice.c | 15 ++- drivers/char/diag/diag_usb.c | 4 +- drivers/char/diag/diagchar.h | 4 +- drivers/char/diag/diagchar_core.c | 169 +++++++++++++++++-------- drivers/char/diag/diagfwd.c | 74 +++++++---- drivers/char/diag/diagfwd.h | 11 +- drivers/char/diag/diagfwd_peripheral.c | 5 +- 9 files changed, 287 insertions(+), 142 deletions(-) diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index fe2115873294..e8f5bceaaa41 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -482,8 +482,7 @@ static void diag_send_feature_mask_update(uint8_t peripheral) } static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -491,24 +490,30 @@ static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len, struct diag_msg_ssid_query_t rsp; struct diag_ssid_range_t ssid_range; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || !mask_info) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - if (!diag_apps_responds()) + if (!diag_apps_responds()) { + mutex_unlock(&driver->md_session_lock); return 0; - + } mutex_lock(&driver->msg_mask_lock); rsp.cmd_code = DIAG_CMD_MSG_CONFIG; rsp.sub_cmd = DIAG_CMD_OP_GET_SSID_RANGE; @@ -530,13 +535,12 @@ static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len, write_len += sizeof(ssid_range); } mutex_unlock(&driver->msg_mask_lock); - + mutex_unlock(&driver->md_session_lock); return write_len; } static int diag_cmd_get_build_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i = 0; int write_len = 0; @@ -591,8 +595,7 @@ static int diag_cmd_get_build_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -601,6 +604,10 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, struct diag_build_mask_req_t *req = NULL; struct diag_msg_build_mask_t rsp; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -608,15 +615,19 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - if (!diag_apps_responds()) + if (!diag_apps_responds()) { + mutex_unlock(&driver->md_session_lock); return 0; + } mutex_lock(&driver->msg_mask_lock); req = (struct diag_build_mask_req_t *)src_buf; @@ -631,6 +642,7 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", __func__, mask->ptr); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { @@ -650,13 +662,12 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, memcpy(dest_buf, &rsp, sizeof(rsp)); write_len += sizeof(rsp); mutex_unlock(&driver->msg_mask_lock); - + mutex_unlock(&driver->md_session_lock); return write_len; } static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -670,6 +681,10 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, struct diag_mask_info *mask_info = NULL; struct diag_msg_mask_t *mask_next = NULL; uint32_t *temp = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -677,11 +692,13 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -694,6 +711,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, __func__, mask->ptr); mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { @@ -736,6 +754,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, mutex_unlock(&mask->lock); mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); return -ENOMEM; } mask->ptr = temp; @@ -756,6 +775,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, } mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(MSG_MASKS_TYPE); @@ -787,8 +807,7 @@ end: } static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -797,6 +816,10 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, struct diag_msg_config_rsp_t *req = NULL; struct diag_msg_mask_t *mask = NULL; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -804,11 +827,13 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -823,6 +848,7 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, __func__, mask->ptr); mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } mask_info->status = (req->rt_mask) ? DIAG_CTRL_MASK_ALL_ENABLED : @@ -835,7 +861,7 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, } mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); - + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(MSG_MASKS_TYPE); @@ -861,8 +887,7 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_get_event_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int write_len = 0; uint32_t mask_size; @@ -897,8 +922,7 @@ static int diag_cmd_get_event_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -907,18 +931,23 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, struct diag_event_mask_config_t rsp; struct diag_event_mask_config_t *req; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &event_mask : info->event_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || !mask_info) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } req = (struct diag_event_mask_config_t *)src_buf; @@ -926,6 +955,7 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, if (mask_len <= 0 || mask_len > event_mask.mask_len) { pr_err("diag: In %s, invalid event mask len: %d\n", __func__, mask_len); + mutex_unlock(&driver->md_session_lock); return -EIO; } @@ -933,6 +963,7 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, memcpy(mask_info->ptr, src_buf + header_len, mask_len); mask_info->status = DIAG_CTRL_MASK_VALID; mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(EVENT_MASKS_TYPE); @@ -959,26 +990,30 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; uint8_t toggle = 0; struct diag_event_report_t header; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &event_mask : info->event_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || !mask_info) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -992,6 +1027,7 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, memset(mask_info->ptr, 0, mask_info->mask_len); } mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(EVENT_MASKS_TYPE); @@ -1013,8 +1049,7 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, } static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int status = LOG_STATUS_INVALID; @@ -1027,6 +1062,10 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, struct diag_log_config_req_t *req; struct diag_log_config_rsp_t rsp; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -1034,16 +1073,20 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - if (!diag_apps_responds()) + if (!diag_apps_responds()) { + mutex_unlock(&driver->md_session_lock); return 0; + } req = (struct diag_log_config_req_t *)src_buf; read_len += req_header_len; @@ -1063,6 +1106,7 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, if (!log_item->ptr) { pr_err("diag: Invalid input in %s, mask: %pK\n", __func__, log_item); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < MAX_EQUIP_ID; i++, log_item++) { @@ -1104,28 +1148,27 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, rsp.status = status; memcpy(dest_buf, &rsp, rsp_header_len); + mutex_unlock(&driver->md_session_lock); return write_len; } static int diag_cmd_get_log_range(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; struct diag_log_config_rsp_t rsp; - struct diag_mask_info *mask_info = NULL; struct diag_log_mask_t *mask = (struct diag_log_mask_t *)log_mask.ptr; + if (!mask) + return -EINVAL; + if (!diag_apps_responds()) return 0; - mask_info = (!info) ? &log_mask : info->log_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { - pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", - __func__, src_buf, src_len, dest_buf, dest_len, - mask_info); + if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0) { + pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d\n", + __func__, src_buf, src_len, dest_buf, dest_len); return -EINVAL; } @@ -1148,7 +1191,7 @@ static int diag_cmd_get_log_range(unsigned char *src_buf, int src_len, static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + int pid) { int i; int write_len = 0; @@ -1163,6 +1206,10 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, struct diag_log_mask_t *mask = NULL; unsigned char *temp_buf = NULL; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -1170,11 +1217,13 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -1184,6 +1233,7 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, if (!mask->ptr) { pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", __func__, mask->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (req->equip_id >= MAX_EQUIP_ID) { @@ -1246,6 +1296,7 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, break; } mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(LOG_MASKS_TYPE); @@ -1284,14 +1335,17 @@ end: } static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { struct diag_mask_info *mask_info = NULL; struct diag_log_mask_t *mask = NULL; struct diag_log_config_rsp_t header; int write_len = 0; int i; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -1299,17 +1353,20 @@ static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } mask = (struct diag_log_mask_t *)mask_info->ptr; if (!mask->ptr) { pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", __func__, mask->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < MAX_EQUIP_ID; i++, mask++) { @@ -1318,6 +1375,7 @@ static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, mutex_unlock(&mask->lock); } mask_info->status = DIAG_CTRL_MASK_ALL_DISABLED; + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(LOG_MASKS_TYPE); @@ -2045,14 +2103,12 @@ void diag_send_updates_peripheral(uint8_t peripheral) &driver->buffering_mode[peripheral]); } -int diag_process_apps_masks(unsigned char *buf, int len, - struct diag_md_session_t *info) +int diag_process_apps_masks(unsigned char *buf, int len, int pid) { int size = 0; int sub_cmd = 0; int (*hdlr)(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) = NULL; + unsigned char *dest_buf, int dest_len, int pid) = NULL; if (!buf || len <= 0) return -EINVAL; @@ -2102,7 +2158,7 @@ int diag_process_apps_masks(unsigned char *buf, int len, if (hdlr) size = hdlr(buf, len, driver->apps_rsp_buf, - DIAG_MAX_RSP_SIZE, info); + DIAG_MAX_RSP_SIZE, pid); return (size > 0) ? size : 0; } diff --git a/drivers/char/diag/diag_masks.h b/drivers/char/diag/diag_masks.h index 1a52f946bb09..6edeee954d74 100644 --- a/drivers/char/diag/diag_masks.h +++ b/drivers/char/diag/diag_masks.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -167,8 +167,7 @@ int diag_event_mask_copy(struct diag_mask_info *dest, void diag_log_mask_free(struct diag_mask_info *mask_info); void diag_msg_mask_free(struct diag_mask_info *mask_info); void diag_event_mask_free(struct diag_mask_info *mask_info); -int diag_process_apps_masks(unsigned char *buf, int len, - struct diag_md_session_t *info); +int diag_process_apps_masks(unsigned char *buf, int len, int pid); void diag_send_updates_peripheral(uint8_t peripheral); extern int diag_create_msg_mask_table_entry(struct diag_msg_mask_t *msg_mask, diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index b4153c196188..aebeba836825 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2014-2015, 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2016, 2018 The Linux Foundation. + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -130,7 +131,7 @@ void diag_md_close_all() int diag_md_write(int id, unsigned char *buf, int len, int ctx) { - int i; + int i, pid = 0; uint8_t found = 0; unsigned long flags; struct diag_md_info *ch = NULL; @@ -147,9 +148,14 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx) if (peripheral > NUM_PERIPHERALS) return -EINVAL; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(peripheral); - if (!session_info) + if (!session_info) { + mutex_unlock(&driver->md_session_lock); return -EIO; + } + pid = session_info->pid; + mutex_unlock(&driver->md_session_lock); ch = &diag_md[id]; @@ -187,8 +193,7 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx) found = 0; for (i = 0; i < driver->num_clients && !found; i++) { - if ((driver->client_map[i].pid != - session_info->pid) || + if ((driver->client_map[i].pid != pid) || (driver->client_map[i].pid == 0)) continue; diff --git a/drivers/char/diag/diag_usb.c b/drivers/char/diag/diag_usb.c index 0a0fc4400de5..87d021f6a956 100644 --- a/drivers/char/diag/diag_usb.c +++ b/drivers/char/diag/diag_usb.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -221,7 +221,7 @@ static void usb_disconnect(struct diag_usb_info *ch) if (!atomic_read(&ch->connected) && driver->usb_connected && diag_mask_param()) - diag_clear_masks(NULL); + diag_clear_masks(0); if (ch && ch->ops && ch->ops->close) ch->ops->close(ch->ctxt, DIAG_USB_MODE); diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 6cbe357b2a6c..a8427290fc7f 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -630,7 +630,7 @@ void diag_cmd_remove_reg_by_pid(int pid); void diag_cmd_remove_reg_by_proc(int proc); int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry); int diag_mask_param(void); -void diag_clear_masks(struct diag_md_session_t *info); +void diag_clear_masks(int pid); void diag_record_stats(int type, int flag); diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index e5795fd0c61c..b84ad57cef44 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -171,7 +171,7 @@ uint16_t diag_debug_mask; void *diag_ipc_log; #endif -static void diag_md_session_close(struct diag_md_session_t *session_info); +static void diag_md_session_close(int pid); /* * Returns the next delayed rsp id. If wrapping is enabled, @@ -210,6 +210,16 @@ do { \ ret += length; \ } while (0) +#define COPY_USER_SPACE_OR_ERR(buf, data, length) \ +do { \ + if ((count < ret+length) || (copy_to_user(buf, \ + (void *)&data, length))) { \ + ret = -EFAULT; \ + break; \ + } \ + ret += length; \ +} while (0) + static void drain_timer_func(unsigned long data) { queue_work(driver->diag_wq , &(driver->diag_drain_work)); @@ -248,12 +258,13 @@ void diag_drain_work_fn(struct work_struct *work) timer_in_progress = 0; mutex_lock(&apps_data_mutex); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; - + mutex_unlock(&driver->md_session_lock); if (!hdlc_disabled) diag_drain_apps_data(&hdlc_data); else @@ -400,7 +411,7 @@ int diag_mask_param(void) { return diag_mask_clear_param; } -void diag_clear_masks(struct diag_md_session_t *info) +void diag_clear_masks(int pid) { int ret; char cmd_disable_log_mask[] = { 0x73, 0, 0, 0, 0, 0, 0, 0}; @@ -409,14 +420,14 @@ void diag_clear_masks(struct diag_md_session_t *info) DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: %s: masks clear request upon %s\n", __func__, - ((info) ? "ODL exit" : "USB Disconnection")); + ((pid) ? "ODL exit" : "USB Disconnection")); ret = diag_process_apps_masks(cmd_disable_log_mask, - sizeof(cmd_disable_log_mask), info); + sizeof(cmd_disable_log_mask), pid); ret = diag_process_apps_masks(cmd_disable_msg_mask, - sizeof(cmd_disable_msg_mask), info); + sizeof(cmd_disable_msg_mask), pid); ret = diag_process_apps_masks(cmd_disable_event_mask, - sizeof(cmd_disable_event_mask), info); + sizeof(cmd_disable_event_mask), pid); DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag:%s: masks cleared successfully\n", __func__); } @@ -428,19 +439,23 @@ static void diag_close_logging_process(const int pid) struct diag_md_session_t *session_info = NULL; struct diag_logging_mode_param_t params; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(pid); - if (!session_info) + if (!session_info) { + mutex_unlock(&driver->md_session_lock); return; + } + session_peripheral_mask = session_info->peripheral_mask; + mutex_unlock(&driver->md_session_lock); if (diag_mask_clear_param) - diag_clear_masks(session_info); + diag_clear_masks(pid); mutex_lock(&driver->diag_maskclear_mutex); driver->mask_clear = 1; mutex_unlock(&driver->diag_maskclear_mutex); - session_peripheral_mask = session_info->peripheral_mask; - diag_md_session_close(session_info); + mutex_lock(&driver->diagchar_mutex); for (i = 0; i < NUM_MD_SESSIONS; i++) if (MD_PERIPHERAL_MASK(i) & session_peripheral_mask) diag_mux_close_peripheral(DIAG_LOCAL_PROC, i); @@ -449,7 +464,10 @@ static void diag_close_logging_process(const int pid) params.mode_param = 0; params.peripheral_mask = diag_translate_kernel_to_user_mask(session_peripheral_mask); - mutex_lock(&driver->diagchar_mutex); + + mutex_lock(&driver->md_session_lock); + diag_md_session_close(pid); + mutex_unlock(&driver->md_session_lock); diag_switch_logging(¶ms); mutex_unlock(&driver->diagchar_mutex); } @@ -982,11 +1000,13 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, if (driver->hdlc_encode_buf_len != 0) return -EAGAIN; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; + mutex_unlock(&driver->md_session_lock); if (hdlc_disabled) { if (len < 4) { pr_err("diag: In %s, invalid len: %d of non_hdlc pkt", @@ -1351,15 +1371,16 @@ fail_peripheral: return err; } -static void diag_md_session_close(struct diag_md_session_t *session_info) +static void diag_md_session_close(int pid) { int i; uint8_t found = 0; + struct diag_md_session_t *session_info = NULL; + session_info = diag_md_session_get_pid(pid); if (!session_info) return; - mutex_lock(&driver->md_session_lock); for (i = 0; i < NUM_MD_SESSIONS; i++) { if (driver->md_session_map[i] != session_info) continue; @@ -1385,7 +1406,6 @@ static void diag_md_session_close(struct diag_md_session_t *session_info) driver->md_session_mode = (found) ? DIAG_MD_PERIPHERAL : DIAG_MD_NONE; kfree(session_info); session_info = NULL; - mutex_unlock(&driver->md_session_lock); DIAG_LOG(DIAG_DEBUG_USERSPACE, "cleared up session\n"); } @@ -1393,6 +1413,8 @@ struct diag_md_session_t *diag_md_session_get_pid(int pid) { int i; + if (pid <= 0) + return NULL; for (i = 0; i < NUM_MD_SESSIONS; i++) { if (driver->md_session_map[i] && driver->md_session_map[i]->pid == pid) @@ -1408,10 +1430,12 @@ struct diag_md_session_t *diag_md_session_get_peripheral(uint8_t peripheral) return driver->md_session_map[peripheral]; } -static int diag_md_peripheral_switch(struct diag_md_session_t *session_info, +static int diag_md_peripheral_switch(int pid, int peripheral_mask, int req_mode) { int i, bit = 0; + struct diag_md_session_t *session_info = NULL; + session_info = diag_md_session_get_pid(pid); if (!session_info) return -EINVAL; if (req_mode != DIAG_USB_MODE || req_mode != DIAG_MEMORY_DEVICE_MODE) @@ -1421,25 +1445,20 @@ static int diag_md_peripheral_switch(struct diag_md_session_t *session_info, * check that md_session_map for i == session_info, * if not then race condition occurred and bail */ - mutex_lock(&driver->md_session_lock); for (i = 0; i < NUM_MD_SESSIONS; i++) { bit = MD_PERIPHERAL_MASK(i) & peripheral_mask; if (!bit) continue; if (req_mode == DIAG_USB_MODE) { - if (driver->md_session_map[i] != session_info) { - mutex_unlock(&driver->md_session_lock); + if (driver->md_session_map[i] != session_info) return -EINVAL; - } driver->md_session_map[i] = NULL; driver->md_session_mask &= ~bit; session_info->peripheral_mask &= ~bit; } else { - if (driver->md_session_map[i] != NULL) { - mutex_unlock(&driver->md_session_lock); + if (driver->md_session_map[i] != NULL) return -EINVAL; - } driver->md_session_map[i] = session_info; driver->md_session_mask |= bit; session_info->peripheral_mask |= bit; @@ -1448,7 +1467,6 @@ static int diag_md_peripheral_switch(struct diag_md_session_t *session_info, } driver->md_session_mode = DIAG_MD_PERIPHERAL; - mutex_unlock(&driver->md_session_lock); DIAG_LOG(DIAG_DEBUG_USERSPACE, "Changed Peripherals:0x%x to mode:%d\n", peripheral_mask, req_mode); } @@ -1457,7 +1475,7 @@ static int diag_md_session_check(int curr_mode, int req_mode, const struct diag_logging_mode_param_t *param, uint8_t *change_mode) { - int i, bit = 0, err = 0; + int i, bit = 0, err = 0, peripheral_mask = 0; int change_mask = 0; struct diag_md_session_t *session_info = NULL; @@ -1481,12 +1499,13 @@ static int diag_md_session_check(int curr_mode, int req_mode, if (req_mode == DIAG_USB_MODE) { if (curr_mode == DIAG_USB_MODE) return 0; + mutex_lock(&driver->md_session_lock); if (driver->md_session_mode == DIAG_MD_NONE && driver->md_session_mask == 0 && driver->logging_mask) { *change_mode = 1; + mutex_unlock(&driver->md_session_lock); return 0; } - /* * curr_mode is either DIAG_MULTI_MODE or DIAG_MD_MODE * Check if requested peripherals are already in usb mode @@ -1498,8 +1517,10 @@ static int diag_md_session_check(int curr_mode, int req_mode, if (bit & driver->logging_mask) change_mask |= bit; } - if (!change_mask) + if (!change_mask) { + mutex_unlock(&driver->md_session_lock); return 0; + } /* * Change is needed. Check if this md_session has set all the @@ -1511,23 +1532,26 @@ static int diag_md_session_check(int curr_mode, int req_mode, session_info = diag_md_session_get_pid(current->tgid); if (!session_info) { *change_mode = 1; + mutex_unlock(&driver->md_session_lock); return 0; } - if ((change_mask & session_info->peripheral_mask) + peripheral_mask = session_info->peripheral_mask; + if ((change_mask & peripheral_mask) != change_mask) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "Another MD Session owns a requested peripheral\n"); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } *change_mode = 1; /* If all peripherals are being set to USB Mode, call close */ - if (~change_mask & session_info->peripheral_mask) { - err = diag_md_peripheral_switch(session_info, + if (~change_mask & peripheral_mask) { + err = diag_md_peripheral_switch(current->tgid, change_mask, DIAG_USB_MODE); } else - diag_md_session_close(session_info); - + diag_md_session_close(current->tgid); + mutex_unlock(&driver->md_session_lock); return err; } else if (req_mode == DIAG_MEMORY_DEVICE_MODE) { @@ -1536,6 +1560,7 @@ static int diag_md_session_check(int curr_mode, int req_mode, * been set. Check that requested peripherals already set are * owned by this md session */ + mutex_lock(&driver->md_session_lock); change_mask = driver->md_session_mask & param->peripheral_mask; session_info = diag_md_session_get_pid(current->tgid); @@ -1544,11 +1569,14 @@ static int diag_md_session_check(int curr_mode, int req_mode, != change_mask) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "Another MD Session owns a requested peripheral\n"); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - err = diag_md_peripheral_switch(session_info, + err = diag_md_peripheral_switch(current->tgid, change_mask, DIAG_USB_MODE); + mutex_unlock(&driver->md_session_lock); } else { + mutex_unlock(&driver->md_session_lock); if (change_mask) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "Another MD Session owns a requested peripheral\n"); @@ -1926,17 +1954,17 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg) uint8_t hdlc_support; struct diag_md_session_t *session_info = NULL; - session_info = diag_md_session_get_pid(current->tgid); if (copy_from_user(&hdlc_support, (void __user *)ioarg, sizeof(uint8_t))) return -EFAULT; mutex_lock(&driver->hdlc_disable_mutex); - if (session_info) { - mutex_lock(&driver->md_session_lock); + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_pid(current->tgid); + if (session_info) session_info->hdlc_disabled = hdlc_support; - mutex_unlock(&driver->md_session_lock); - } else + else driver->hdlc_disabled = hdlc_support; + mutex_unlock(&driver->md_session_lock); mutex_unlock(&driver->hdlc_disable_mutex); diag_update_md_clients(HDLC_SUPPORT_TYPE); @@ -2587,7 +2615,6 @@ static int diag_user_process_raw_data(const char __user *buf, int len) int remote_proc = 0; const int mempool = POOL_TYPE_COPY; unsigned char *user_space_data = NULL; - struct diag_md_session_t *info = NULL; if (!buf || len <= 0 || len > CALLBACK_BUF_SIZE) { pr_err_ratelimited("diag: In %s, invalid buf %pK len: %d\n", @@ -2638,8 +2665,8 @@ static int diag_user_process_raw_data(const char __user *buf, int len) } else { wait_event_interruptible(driver->wait_q, (driver->in_busy_pktdata == 0)); - info = diag_md_session_get_pid(current->tgid); - ret = diag_process_apps_pkt(user_space_data, len, info); + ret = diag_process_apps_pkt(user_space_data, len, + current->tgid); if (ret == 1) diag_send_error_rsp((void *)(user_space_data), len); } @@ -2705,24 +2732,27 @@ static int diag_user_process_userspace_data(const char __user *buf, int len) /* send masks to local processor now */ if (!remote_proc) { + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); if (!session_info) { pr_err("diag:In %s request came from invalid md session pid:%d", __func__, current->tgid); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; + mutex_unlock(&driver->md_session_lock); if (!hdlc_disabled) diag_process_hdlc_pkt((void *) (driver->user_space_data_buf), - len, session_info); + len, current->tgid); else diag_process_non_hdlc_pkt((char *) (driver->user_space_data_buf), - len, session_info); + len, current->tgid); return 0; } @@ -2799,11 +2829,13 @@ static int diag_user_process_apps_data(const char __user *buf, int len, mutex_lock(&apps_data_mutex); mutex_lock(&driver->hdlc_disable_mutex); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; + mutex_unlock(&driver->md_session_lock); if (hdlc_disabled) ret = diag_process_apps_data_non_hdlc(user_space_data, len, pkt_type); @@ -2878,9 +2910,11 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); /* place holder for number of data field */ ret += sizeof(int); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); exit_stat = diag_md_copy_to_user(buf, &ret, count, session_info); + mutex_unlock(&driver->md_session_lock); goto exit; } else if (driver->data_ready[index] & USER_SPACE_DATA_TYPE) { /* In case, the thread wakes up and the logging mode is @@ -2892,11 +2926,18 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, data_type = driver->data_ready[index] & HDLC_SUPPORT_TYPE; driver->data_ready[index] ^= HDLC_SUPPORT_TYPE; COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); - if (session_info) - COPY_USER_SPACE_OR_EXIT(buf+4, + if (session_info) { + COPY_USER_SPACE_OR_ERR(buf+4, session_info->hdlc_disabled, sizeof(uint8_t)); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } + } + mutex_unlock(&driver->md_session_lock); goto exit; } @@ -2913,10 +2954,16 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, if (driver->data_ready[index] & MSG_MASKS_TYPE) { /*Copy the type of data being passed*/ data_type = driver->data_ready[index] & MSG_MASKS_TYPE; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); - COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); + COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int)); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } write_len = diag_copy_to_user_msg_mask(buf + ret, count, session_info); + mutex_unlock(&driver->md_session_lock); if (write_len > 0) ret += write_len; driver->data_ready[index] ^= MSG_MASKS_TYPE; @@ -2926,18 +2973,32 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, if (driver->data_ready[index] & EVENT_MASKS_TYPE) { /*Copy the type of data being passed*/ data_type = driver->data_ready[index] & EVENT_MASKS_TYPE; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); - COPY_USER_SPACE_OR_EXIT(buf, data_type, 4); + COPY_USER_SPACE_OR_ERR(buf, data_type, 4); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } if (session_info && session_info->event_mask && session_info->event_mask->ptr) { - COPY_USER_SPACE_OR_EXIT(buf + sizeof(int), + COPY_USER_SPACE_OR_ERR(buf + sizeof(int), *(session_info->event_mask->ptr), session_info->event_mask->mask_len); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } } else { - COPY_USER_SPACE_OR_EXIT(buf + sizeof(int), + COPY_USER_SPACE_OR_ERR(buf + sizeof(int), *(event_mask.ptr), event_mask.mask_len); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } } + mutex_unlock(&driver->md_session_lock); driver->data_ready[index] ^= EVENT_MASKS_TYPE; goto exit; } @@ -2945,10 +3006,16 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, if (driver->data_ready[index] & LOG_MASKS_TYPE) { /*Copy the type of data being passed*/ data_type = driver->data_ready[index] & LOG_MASKS_TYPE; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); - COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); + COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int)); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } write_len = diag_copy_to_user_log_mask(buf + ret, count, session_info); + mutex_unlock(&driver->md_session_lock); if (write_len > 0) ret += write_len; driver->data_ready[index] ^= LOG_MASKS_TYPE; diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index f25526615924..9697deadea24 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -281,8 +281,11 @@ static void pack_rsp_and_send(unsigned char *buf, int len) * draining responses when we are in Memory Device Mode. */ if (driver->logging_mode == DIAG_MEMORY_DEVICE_MODE || - driver->logging_mode == DIAG_MULTI_MODE) + driver->logging_mode == DIAG_MULTI_MODE) { + mutex_lock(&driver->md_session_lock); chk_logging_wakeup(); + mutex_unlock(&driver->md_session_lock); + } } if (driver->rsp_buf_busy) { pr_err("diag: unable to get hold of response buffer\n"); @@ -350,8 +353,11 @@ static void encode_rsp_and_send(unsigned char *buf, int len) * draining responses when we are in Memory Device Mode. */ if (driver->logging_mode == DIAG_MEMORY_DEVICE_MODE || - driver->logging_mode == DIAG_MULTI_MODE) + driver->logging_mode == DIAG_MULTI_MODE) { + mutex_lock(&driver->md_session_lock); chk_logging_wakeup(); + mutex_unlock(&driver->md_session_lock); + } } if (driver->rsp_buf_busy) { @@ -387,12 +393,13 @@ void diag_send_rsp(unsigned char *buf, int len) struct diag_md_session_t *session_info = NULL; uint8_t hdlc_disabled; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; - + mutex_unlock(&driver->md_session_lock); if (hdlc_disabled) pack_rsp_and_send(buf, len); else @@ -460,6 +467,7 @@ void diag_update_md_clients(unsigned int type) int i, j; mutex_lock(&driver->diagchar_mutex); + mutex_lock(&driver->md_session_lock); for (i = 0; i < NUM_MD_SESSIONS; i++) { if (driver->md_session_map[i] != NULL) for (j = 0; j < driver->num_clients; j++) { @@ -471,6 +479,7 @@ void diag_update_md_clients(unsigned int type) } } } + mutex_unlock(&driver->md_session_lock); wake_up_interruptible(&driver->wait_q); mutex_unlock(&driver->diagchar_mutex); } @@ -871,22 +880,22 @@ void diag_send_error_rsp(unsigned char *buf, int len) diag_send_rsp(driver->apps_rsp_buf, len + 1); } -int diag_process_apps_pkt(unsigned char *buf, int len, - struct diag_md_session_t *info) +int diag_process_apps_pkt(unsigned char *buf, int len, int pid) { - int i; + int i, p_mask = 0; int mask_ret; int write_len = 0; unsigned char *temp = NULL; struct diag_cmd_reg_entry_t entry; struct diag_cmd_reg_entry_t *temp_entry = NULL; struct diag_cmd_reg_t *reg_item = NULL; + struct diag_md_session_t *info = NULL; if (!buf) return -EIO; /* Check if the command is a supported mask command */ - mask_ret = diag_process_apps_masks(buf, len, info); + mask_ret = diag_process_apps_masks(buf, len, pid); if (mask_ret > 0) { diag_send_rsp(driver->apps_rsp_buf, mask_ret); return 0; @@ -919,11 +928,15 @@ int diag_process_apps_pkt(unsigned char *buf, int len, if (temp_entry) { reg_item = container_of(temp_entry, struct diag_cmd_reg_t, entry); + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (info) { - if (MD_PERIPHERAL_MASK(reg_item->proc) & - info->peripheral_mask) + p_mask = info->peripheral_mask; + mutex_unlock(&driver->md_session_lock); + if (MD_PERIPHERAL_MASK(reg_item->proc) & p_mask) write_len = diag_send_data(reg_item, buf, len); } else { + mutex_unlock(&driver->md_session_lock); if (MD_PERIPHERAL_MASK(reg_item->proc) & driver->logging_mask) diag_send_error_rsp(buf, len); @@ -1088,10 +1101,13 @@ int diag_process_apps_pkt(unsigned char *buf, int len, */ pr_debug("diag: In %s, disabling HDLC encoding\n", __func__); + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (info) info->hdlc_disabled = 1; else driver->hdlc_disabled = 1; + mutex_unlock(&driver->md_session_lock); diag_update_md_clients(HDLC_SUPPORT_TYPE); mutex_unlock(&driver->hdlc_disable_mutex); return 0; @@ -1105,8 +1121,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, return 0; } -void diag_process_hdlc_pkt(void *data, unsigned len, - struct diag_md_session_t *info) +void diag_process_hdlc_pkt(void *data, unsigned len, int pid) { int err = 0; int ret = 0; @@ -1166,7 +1181,7 @@ void diag_process_hdlc_pkt(void *data, unsigned len, } err = diag_process_apps_pkt(driver->hdlc_buf, - driver->hdlc_buf_len, info); + driver->hdlc_buf_len, pid); if (err < 0) goto fail; } else { @@ -1280,9 +1295,11 @@ static int diagfwd_mux_close(int id, int mode) static uint8_t hdlc_reset; -static void hdlc_reset_timer_start(struct diag_md_session_t *info) +static void hdlc_reset_timer_start(int pid) { + struct diag_md_session_t *info = NULL; mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (!hdlc_timer_in_progress) { hdlc_timer_in_progress = 1; if (info) @@ -1324,15 +1341,16 @@ void diag_md_hdlc_reset_timer_func(unsigned long pid) } static void diag_hdlc_start_recovery(unsigned char *buf, int len, - struct diag_md_session_t *info) + int pid) { int i; static uint32_t bad_byte_counter; unsigned char *start_ptr = NULL; struct diag_pkt_frame_t *actual_pkt = NULL; + struct diag_md_session_t *info = NULL; hdlc_reset = 1; - hdlc_reset_timer_start(info); + hdlc_reset_timer_start(pid); actual_pkt = (struct diag_pkt_frame_t *)buf; for (i = 0; i < len; i++) { @@ -1351,10 +1369,13 @@ static void diag_hdlc_start_recovery(unsigned char *buf, int len, pr_err("diag: In %s, re-enabling HDLC encoding\n", __func__); mutex_lock(&driver->hdlc_disable_mutex); + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (info) info->hdlc_disabled = 0; else driver->hdlc_disabled = 0; + mutex_unlock(&driver->md_session_lock); mutex_unlock(&driver->hdlc_disable_mutex); diag_update_md_clients(HDLC_SUPPORT_TYPE); @@ -1367,12 +1388,11 @@ static void diag_hdlc_start_recovery(unsigned char *buf, int len, mutex_lock(&driver->hdlc_recovery_mutex); driver->incoming_pkt.processing = 0; mutex_unlock(&driver->hdlc_recovery_mutex); - diag_process_non_hdlc_pkt(start_ptr, len - i, info); + diag_process_non_hdlc_pkt(start_ptr, len - i, pid); } } -void diag_process_non_hdlc_pkt(unsigned char *buf, int len, - struct diag_md_session_t *info) +void diag_process_non_hdlc_pkt(unsigned char *buf, int len, int pid) { int err = 0; uint16_t pkt_len = 0; @@ -1428,11 +1448,11 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) { mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); mutex_lock(&driver->hdlc_recovery_mutex); } err = diag_process_apps_pkt(data_ptr, - actual_pkt->length, info); + actual_pkt->length, pid); if (err) { pr_err("diag: In %s, unable to process incoming data packet, err: %d\n", __func__, err); @@ -1454,7 +1474,7 @@ start: pkt_len = actual_pkt->length; if (actual_pkt->start != CONTROL_CHAR) { - diag_hdlc_start_recovery(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); diag_send_error_rsp(buf, len); goto end; } @@ -1463,7 +1483,7 @@ start: pr_err("diag: In %s, incoming data is too large for the request buffer %d\n", __func__, pkt_len); mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); break; } if ((pkt_len + header_len) > (len - read_bytes)) { @@ -1480,13 +1500,13 @@ start: if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) { mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); mutex_lock(&driver->hdlc_recovery_mutex); } else hdlc_reset = 0; err = diag_process_apps_pkt(data_ptr, - actual_pkt->length, info); + actual_pkt->length, pid); if (err) { mutex_unlock(&driver->hdlc_recovery_mutex); break; @@ -1505,9 +1525,9 @@ static int diagfwd_mux_read_done(unsigned char *buf, int len, int ctxt) return -EINVAL; if (!driver->hdlc_disabled) - diag_process_hdlc_pkt(buf, len, NULL); + diag_process_hdlc_pkt(buf, len, 0); else - diag_process_non_hdlc_pkt(buf, len, NULL); + diag_process_non_hdlc_pkt(buf, len, 0); diag_mux_queue_read(ctxt); return 0; diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h index 0023e0638aa2..0f98df19cdd2 100644 --- a/drivers/char/diag/diagfwd.h +++ b/drivers/char/diag/diagfwd.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2015, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -28,10 +28,8 @@ int diagfwd_init(void); void diagfwd_exit(void); -void diag_process_hdlc_pkt(void *data, unsigned len, - struct diag_md_session_t *info); -void diag_process_non_hdlc_pkt(unsigned char *data, int len, - struct diag_md_session_t *info); +void diag_process_hdlc_pkt(void *data, unsigned int len, int pid); +void diag_process_non_hdlc_pkt(unsigned char *data, int len, int pid); int chk_config_get_id(void); int chk_apps_only(void); int chk_apps_master(void); @@ -43,8 +41,7 @@ int diag_cmd_get_mobile_id(unsigned char *src_buf, int src_len, int diag_check_common_cmd(struct diag_pkt_header_t *header); void diag_update_userspace_clients(unsigned int type); void diag_update_sleeping_process(int process_id, int data_type); -int diag_process_apps_pkt(unsigned char *buf, int len, - struct diag_md_session_t *info); +int diag_process_apps_pkt(unsigned char *buf, int len, int pid); void diag_send_error_rsp(unsigned char *buf, int len); void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type); int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf); diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 36f2857545e9..856e00f05fd1 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -240,12 +240,13 @@ static void diagfwd_data_read_done(struct diagfwd_info *fwd_info, mutex_lock(&driver->hdlc_disable_mutex); mutex_lock(&fwd_info->data_mutex); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(fwd_info->peripheral); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; - + mutex_unlock(&driver->md_session_lock); if (!driver->feature[fwd_info->peripheral].encode_hdlc) { if (fwd_info->buf_1 && fwd_info->buf_1->data == buf) { temp_buf = fwd_info->buf_1; -- GitLab From 48a8c113ff74a384ed3b83257fd9849fb385ec1a Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 30 Jan 2018 14:09:17 +0530 Subject: [PATCH 2455/5498] soc: qcom: fix race condition while freeing private data WDSP private data structure is freed in wdsp_glink_release() but some of the member variables like work_queue pointer is accessed even after free. Fix this issue by making sure that glink callback functions are finished execution before freeing up wdsp private data structure. Change-Id: Ia4dd9d667109168874dc9188d70741cb9541b0c6 Signed-off-by: Ashish Jain --- drivers/soc/qcom/wcd-dsp-glink.c | 77 ++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index 951ce04dfff7..5600a415d106 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -79,9 +79,6 @@ struct wdsp_glink_ch { int channel_state; struct mutex mutex; - /* To free up the channel memory */ - bool free_mem; - /* Glink local channel open work */ struct work_struct lcl_ch_open_wrk; @@ -91,6 +88,9 @@ struct wdsp_glink_ch { /* Wait for ch connect state before sending any command */ wait_queue_head_t ch_connect_wait; + /* Wait for ch local and remote disconnect before channel free */ + wait_queue_head_t ch_free_wait; + /* * Glink channel configuration. This has to be the last * member of the strucuture as it has variable size @@ -334,7 +334,6 @@ static void wdsp_glink_notify_state(void *handle, const void *priv, ch->ch_cfg.name); wake_up(&ch->ch_connect_wait); - mutex_unlock(&ch->mutex); } else if (event == GLINK_LOCAL_DISCONNECTED) { /* * Don't use dev_dbg here as dev may not be valid if channel @@ -343,22 +342,20 @@ static void wdsp_glink_notify_state(void *handle, const void *priv, pr_debug("%s: channel: %s disconnected locally\n", __func__, ch->ch_cfg.name); mutex_unlock(&ch->mutex); - - if (ch->free_mem) { - kfree(ch); - ch = NULL; - } + wake_up(&ch->ch_free_wait); + return; } else if (event == GLINK_REMOTE_DISCONNECTED) { dev_dbg(wpriv->dev, "%s: remote channel: %s disconnected remotely\n", __func__, ch->ch_cfg.name); - mutex_unlock(&ch->mutex); /* * If remote disconnect happens, local side also has * to close the channel as per glink design in a * separate work_queue. */ - queue_work(wpriv->work_queue, &ch->lcl_ch_cls_wrk); + if (wpriv && wpriv->work_queue != NULL) + queue_work(wpriv->work_queue, &ch->lcl_ch_cls_wrk); } + mutex_unlock(&ch->mutex); } /* @@ -626,6 +623,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk); INIT_WORK(&ch[i]->lcl_ch_cls_wrk, wdsp_glink_lcl_ch_cls_wrk); init_waitqueue_head(&ch[i]->ch_connect_wait); + init_waitqueue_head(&ch[i]->ch_free_wait); } INIT_WORK(&wpriv->ch_open_cls_wrk, wdsp_glink_ch_open_cls_wrk); @@ -1034,34 +1032,45 @@ static int wdsp_glink_release(struct inode *inode, struct file *file) if (wpriv->glink_state.handle) glink_unregister_link_state_cb(wpriv->glink_state.handle); - - flush_workqueue(wpriv->work_queue); - destroy_workqueue(wpriv->work_queue); - /* - * Clean up glink channel memory in channel state - * callback only if close channels are called from here. + * Wait for channel local and remote disconnect state notifications + * before freeing channel memory. */ - if (wpriv->ch) { - for (i = 0; i < wpriv->no_of_channels; i++) { - if (wpriv->ch[i]) { - wpriv->ch[i]->free_mem = true; - /* - * Channel handle NULL means channel is already - * closed. Free the channel memory here itself. - */ - if (!wpriv->ch[i]->handle) { - kfree(wpriv->ch[i]); - wpriv->ch[i] = NULL; - } else { - wdsp_glink_close_ch(wpriv->ch[i]); - } + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i]) { + /* + * Only close glink channel from here if REMOTE has + * not already disconnected it + */ + if (wpriv->ch[i]->channel_state == GLINK_CONNECTED) + wdsp_glink_close_ch(wpriv->ch[i]); + + ret = wait_event_timeout(wpriv->ch[i]->ch_free_wait, + (wpriv->ch[i]->channel_state == + GLINK_LOCAL_DISCONNECTED), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: glink ch %s failed to notify states properly %d\n", + __func__, wpriv->ch[i]->ch_cfg.name, + wpriv->ch[i]->channel_state); + ret = -EINVAL; + goto done; } } + } - kfree(wpriv->ch); - wpriv->ch = NULL; + flush_workqueue(wpriv->work_queue); + destroy_workqueue(wpriv->work_queue); + wpriv->work_queue = NULL; + + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i]) { + kfree(wpriv->ch[i]); + wpriv->ch[i] = NULL; + } } + kfree(wpriv->ch); + wpriv->ch = NULL; mutex_destroy(&wpriv->glink_mutex); mutex_destroy(&wpriv->rsp_mutex); -- GitLab From ac5ddcb4a9d24fa2bb3c62f23678d36bef4ac238 Mon Sep 17 00:00:00 2001 From: Wenjun Zhang Date: Thu, 25 Jan 2018 22:15:15 -0500 Subject: [PATCH 2456/5498] msm: mdss: Add mdss display SPI client Add mdss SPI client to send display data to SPI master, and the display data will be sent to panel by the SPI master. Change-Id: Ib947973b5db71667e4fcbdfa788ebcfe4303a462 Signed-off-by: Wenjun Zhang --- .../bindings/fb/mdss-spi-client.txt | 27 +++ drivers/video/msm/mdss/mdss_spi_client.c | 198 ++++++++++++++++++ drivers/video/msm/mdss/mdss_spi_client.h | 20 ++ 3 files changed, 245 insertions(+) create mode 100644 Documentation/devicetree/bindings/fb/mdss-spi-client.txt create mode 100644 drivers/video/msm/mdss/mdss_spi_client.c create mode 100644 drivers/video/msm/mdss/mdss_spi_client.h diff --git a/Documentation/devicetree/bindings/fb/mdss-spi-client.txt b/Documentation/devicetree/bindings/fb/mdss-spi-client.txt new file mode 100644 index 000000000000..0d5fde838df7 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-spi-client.txt @@ -0,0 +1,27 @@ +Qualcomm Technologies, Inc. mdss-spi-client + +mdss-spi-client is for SPI display to send the FB data to SPI master. + +Required properties: +- compatible : should be "qcom,mdss-spi-client" +- spi-max-frequency : Maximum SPI clocking speed of device in Hz + +Optional properties: +- label: A string used to describe the controller used. +- spi-cpol : Boolean property indicating device requires inverse + clock polarity (CPOL) mode +- spi-cpha : Empty property indicating device requires shifted + clock phase (CPHA) mode +- spi-cs-high : Empty property indicating device requires + chip select active high + +Example: +spi@78b9000 { /* BLSP1 QUP5 */ + qcom,mdss_spi_client { + reg = <0>; + compatible = "qcom,mdss-spi-client"; + label = "MDSS SPI QUP5 CLIENT"; + spi-max-frequency = <50000000>; + }; +}; + diff --git a/drivers/video/msm/mdss/mdss_spi_client.c b/drivers/video/msm/mdss/mdss_spi_client.c new file mode 100644 index 000000000000..541e382bd1d5 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_spi_client.c @@ -0,0 +1,198 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_spi_client.h" + +#define MAX_READ_SPEED_HZ 9600000 +#define SPI_PANEL_COMMAND_LEN 1 +static struct spi_device *mdss_spi_client; + +int mdss_spi_read_data(u8 reg_addr, u8 *data, u8 len) +{ + int rc = 0; + u32 max_speed_hz; + u8 memory_write_reg = 0x2c; + u8 empty_pack[] = {0x29, 0x29, 0x29}; + struct spi_transfer t[4] = { + [0] = { + .tx_buf = ®_addr, + .len = 1, + }, + [1] = { + .rx_buf = data, + .len = len, + }, + [2] = { + .tx_buf = &empty_pack, + .len = 3, + }, + [3] = { + .tx_buf = &memory_write_reg, + .len = 1, + } + }; + struct spi_message m; + + if (!mdss_spi_client) { + pr_err("%s: spi client not available\n", __func__); + return -EINVAL; + } + + mdss_spi_client->bits_per_word = 8; + max_speed_hz = mdss_spi_client->max_speed_hz; + mdss_spi_client->max_speed_hz = MAX_READ_SPEED_HZ; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + rc = spi_sync(mdss_spi_client, &m); + + spi_message_init(&m); + spi_message_add_tail(&t[2], &m); + rc = spi_sync(mdss_spi_client, &m); + spi_message_init(&m); + spi_message_add_tail(&t[3], &m); + rc = spi_sync(mdss_spi_client, &m); + mdss_spi_client->max_speed_hz = max_speed_hz; + + return rc; +} + +int mdss_spi_tx_command(const void *buf) +{ + int rc = 0; + struct spi_transfer t = { + .tx_buf = buf, + .len = SPI_PANEL_COMMAND_LEN, + }; + struct spi_message m; + + if (!mdss_spi_client) { + pr_err("%s: spi client not available\n", __func__); + return -EINVAL; + } + + mdss_spi_client->bits_per_word = 8; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + rc = spi_sync(mdss_spi_client, &m); + + return rc; +} + +int mdss_spi_tx_parameter(const void *buf, size_t len) +{ + int rc = 0; + struct spi_transfer t = { + .tx_buf = buf, + .len = len, + }; + struct spi_message m; + + if (!mdss_spi_client) { + pr_err("%s: spi client not available\n", __func__); + return -EINVAL; + } + + mdss_spi_client->bits_per_word = 8; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + rc = spi_sync(mdss_spi_client, &m); + + return rc; +} + +int mdss_spi_tx_pixel(const void *buf, size_t len) +{ + int rc = 0; + struct spi_transfer t = { + .tx_buf = buf, + .len = len, + }; + struct spi_message m; + + if (!mdss_spi_client) { + pr_err("%s: spi client not available\n", __func__); + return -EINVAL; + } + + mdss_spi_client->bits_per_word = 16; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + rc = spi_sync(mdss_spi_client, &m); + + return rc; +} + +static int mdss_spi_client_probe(struct spi_device *spidev) +{ + int irq; + int cs; + int cpha, cpol, cs_high; + u32 max_speed; + struct device_node *np; + + irq = spidev->irq; + cs = spidev->chip_select; + cpha = (spidev->mode & SPI_CPHA) ? 1:0; + cpol = (spidev->mode & SPI_CPOL) ? 1:0; + cs_high = (spidev->mode & SPI_CS_HIGH) ? 1:0; + max_speed = spidev->max_speed_hz; + np = spidev->dev.of_node; + pr_debug("cs[%x] CPHA[%x] CPOL[%x] CS_HIGH[%x] Max_speed[%d]\n", + cs, cpha, cpol, cs_high, max_speed); + mdss_spi_client = spidev; + + return 0; +} + + +static const struct of_device_id mdss_spi_dt_match[] = { + { .compatible = "qcom,mdss-spi-client" }, + {}, +}; + +static struct spi_driver mdss_spi_client_driver = { + .probe = mdss_spi_client_probe, + .driver = { + .name = "mdss-spi-client", + .owner = THIS_MODULE, + .of_match_table = mdss_spi_dt_match, + }, +}; + +static int __init mdss_spi_init(void) +{ + int ret; + + ret = spi_register_driver(&mdss_spi_client_driver); + + return 0; +} +module_init(mdss_spi_init); + +static void __exit mdss_spi_exit(void) +{ + spi_unregister_driver(&mdss_spi_client_driver); +} +module_exit(mdss_spi_exit); + diff --git a/drivers/video/msm/mdss/mdss_spi_client.h b/drivers/video/msm/mdss/mdss_spi_client.h new file mode 100644 index 000000000000..2d15625e2c23 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_spi_client.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDSS_SPI_CLINET_H__ +#define __MDSS_SPI_CLINET_H__ + +int mdss_spi_tx_command(const void *buf); +int mdss_spi_tx_parameter(const void *buf, size_t len); +int mdss_spi_tx_pixel(const void *buf, size_t len); +int mdss_spi_read_data(u8 reg_addr, u8 *data, u8 len); +#endif /* End of __MDSS_SPI_CLINET_H__ */ -- GitLab From 8d5f4e94b36a2226fdb412a1343e44ccb1acef8b Mon Sep 17 00:00:00 2001 From: gaolez Date: Thu, 1 Feb 2018 21:20:56 +0800 Subject: [PATCH 2457/5498] ARM: dts: msm: Add wlan naples support to apq8009 refboard Add wlan naples support to apq8009 som refboard. Sdhc node is added to configure SD slot to sdio interface for wlan rome sdio. Change-Id: Icd6c0d2f0171bdc820e2b3e4555e79c6dffc29a3 CRs-Fixed: 2183584 Signed-off-by: Gaole Zhang --- ...pq8009-mtp-wcd9326-excelpoint-refboard.dts | 83 +++++++++++++++++++ .../dts/qcom/apq8009-mtp-wcd9326-refboard.dts | 82 ++++++++++++++++++ 2 files changed, 165 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts index 6a168bd74859..7ef88e093024 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts @@ -150,6 +150,89 @@ compatible = "qca,qca9379"; qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */ }; + + cnss_sdio: qcom,cnss_sdio { + compatible = "qcom,cnss_sdio"; + subsys-name = "AR6320"; + /** + * There is no vdd-wlan on board and this is not for DSRC. + * IO and XTAL share the same vreg. + */ + vdd-wlan-io-supply = <&pm8916_l5>; + qcom,cap-tsf-gpio = <&msm_gpio 42 1>; + qcom,wlan-ramdump-dynamic = <0x200000>; + qcom,msm-bus,name = "msm-cnss"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <79 512 0 0>, /* No vote */ + <79 512 6250 200000>, /* 50 Mbps */ + <79 512 25000 200000>, /* 200 Mbps */ + <79 512 2048000 4096000>; /* MAX */ + }; +}; + +&wcnss { + status = "disabled"; +}; + +&msm_gpio { + sdc2_wlan_gpio_on: sdc2_wlan_gpio_on { + mux { + pins = "gpio43"; + function = "gpio"; + }; + config { + pins = "gpio43"; + drive-strength = <10>; + bias-pull-up; + output-high; + }; + }; + + sdc2_wlan_gpio_off: sdc2_wlan_gpio_off { + mux { + pins = "gpio43"; + function = "gpio"; + }; + config { + pins = "gpio43"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; +}; + +&sdhc_2 { + /delete-property/cd-gpios; + #address-cells = <0>; + interrupt-parent = <&sdhc_2>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 125 0 + 1 &intc 0 221 0 + 2 &msm_gpio 38 0>; + interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq"; + + qcom,vdd-voltage-level = <1800000 2950000>; + qcom,vdd-current-level = <15000 400000>; + + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 50000>; + qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on + &sdc2_wlan_gpio_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_wlan_gpio_off>; + qcom,nonremovable; + qcom,core_3_0v_support; + + status = "ok"; }; &i2c_4 { diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts index 397f22ca48fe..63e01ee35cc3 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts @@ -128,6 +128,88 @@ compatible = "qca,qca9379"; qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */ }; + + cnss_sdio: qcom,cnss_sdio { + compatible = "qcom,cnss_sdio"; + subsys-name = "AR6320"; + /** + * There is no vdd-wlan on board and this is not for DSRC. + * IO and XTAL share the same vreg. + */ + vdd-wlan-io-supply = <&pm8916_l5>; + qcom,cap-tsf-gpio = <&msm_gpio 42 1>; + qcom,wlan-ramdump-dynamic = <0x200000>; + qcom,msm-bus,name = "msm-cnss"; + qcom,msm-bus,num-cases = <4>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <79 512 0 0>, /* No vote */ + <79 512 6250 200000>, /* 50 Mbps */ + <79 512 25000 200000>, /* 200 Mbps */ + <79 512 2048000 4096000>; /* MAX */ + }; +}; + +&wcnss { + status = "disabled"; +}; + +&msm_gpio { + sdc2_wlan_gpio_on: sdc2_wlan_gpio_on { + mux { + pins = "gpio43"; + function = "gpio"; + }; + config { + pins = "gpio43"; + drive-strength = <10>; + bias-pull-up; + output-high; + }; + }; + + sdc2_wlan_gpio_off: sdc2_wlan_gpio_off { + mux { + pins = "gpio43"; + function = "gpio"; + }; + config { + pins = "gpio43"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; +}; + +&sdhc_2 { + /delete-property/cd-gpios; + #address-cells = <0>; + interrupt-parent = <&sdhc_2>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 125 0 + 1 &intc 0 221 0 + 2 &msm_gpio 38 0>; + interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq"; + + qcom,vdd-voltage-level = <1800000 2950000>; + qcom,vdd-current-level = <15000 400000>; + + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 50000>; + qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>; + qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; + + pinctrl-names = "active", "sleep"; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on + &sdc2_wlan_gpio_on>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_wlan_gpio_off>; + qcom,nonremovable; + qcom,core_3_0v_support; + status = "ok"; }; &i2c_4 { -- GitLab From cf70d6dcd52064269c1fce5d589a8053d4a6c184 Mon Sep 17 00:00:00 2001 From: Vijayanand Jitta Date: Wed, 31 Jan 2018 15:58:27 +0530 Subject: [PATCH 2458/5498] ARM: dts: msm: Increase WCNSS carveout for msm8909w and apq8009w Increase WCNSS carveout region size from 5 MB to 6 MB for msm8909w and apq8009w based targets. Change-Id: I88d39088ab013ff1a6e9454827519f4732e338fd Signed-off-by: Vijayanand Jitta --- arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi | 4 ++-- arch/arm/boot/dts/qcom/apq8009w-memory.dtsi | 4 ++-- arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi | 4 ++-- arch/arm/boot/dts/qcom/msm8909w-memory.dtsi | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi b/arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi index aab25f897437..d41a792c43d0 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-memory.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,5 +20,5 @@ }; &peripheral_mem { - reg = <0x0 0x8a300000 0x0 0x0500000>; + reg = <0x0 0x8a300000 0x0 0x0600000>; }; diff --git a/arch/arm/boot/dts/qcom/apq8009w-memory.dtsi b/arch/arm/boot/dts/qcom/apq8009w-memory.dtsi index aaf12a3c0214..c27970004f7b 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-memory.dtsi +++ b/arch/arm/boot/dts/qcom/apq8009w-memory.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,5 +20,5 @@ }; &peripheral_mem { - reg = <0x0 0x8a100000 0x0 0x0500000>; + reg = <0x0 0x8a100000 0x0 0x0600000>; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi b/arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi index 9dea1e10da0f..945b945d13e0 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-memory.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,5 +20,5 @@ }; &peripheral_mem { - reg = <0x0 0x8d200000 0x0 0x0500000>; + reg = <0x0 0x8d200000 0x0 0x0600000>; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-memory.dtsi b/arch/arm/boot/dts/qcom/msm8909w-memory.dtsi index 845b146fa23d..975fbb94f5b9 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-memory.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-memory.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,5 +20,5 @@ }; &peripheral_mem { - reg = <0x0 0x8d000000 0x0 0x0500000>; + reg = <0x0 0x8d000000 0x0 0x0600000>; }; -- GitLab From 5fe5073d33c0fc43f502875e0e3341b1f9739850 Mon Sep 17 00:00:00 2001 From: Haibin Liu Date: Thu, 28 Dec 2017 20:42:37 +0800 Subject: [PATCH 2459/5498] msm: sensor: actuator: fix out of bound read for region params Issue: the region index is not validated against the region size. this cause out-of-bound read on the KASAN kernel. Fix: Add restriction that region index smaller than region size. CRs-Fixed: 2153841 Change-Id: I141bba45662769f0661c947fb642c2671578f32e Signed-off-by: Haibin Liu --- .../platform/msm/camera_v2/sensor/actuator/msm_actuator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 7f1c7ebfb55d..fbeb531f8d30 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -638,6 +638,8 @@ static int32_t msm_actuator_move_focus( a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); while (a_ctrl->curr_step_pos != dest_step_pos) { + if (a_ctrl->curr_region_index >= a_ctrl->region_size) + break; step_boundary = a_ctrl->region_params[a_ctrl->curr_region_index]. step_bound[dir]; -- GitLab From 2eb9d37dfa6abbdcd3e3a2a5a3aab797ada4e436 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Date: Tue, 28 Nov 2017 16:58:29 +0530 Subject: [PATCH 2460/5498] msm: kgsl: Move global memory region to 0x100000000 On a 64bit kernel, a 32bit user application is not restricted to 3GB limit of virtual memory. It is allowed to access complete 4GB range. Move global memory region to 0x100000000 outside of 32bit range on 64bit kernel to increase the virtual memory range for a 32bit application running on a 64bit kernel. This will also move secure memory region to 0xF0000000. Change-Id: I017ac0c052b4d9466f9f1a66af4a83f0636450cb Signed-off-by: Deepak Kumar --- drivers/gpu/msm/adreno.c | 4 ++-- drivers/gpu/msm/adreno_a5xx.c | 6 ++--- drivers/gpu/msm/kgsl_iommu.c | 42 ++++++++++++++++++----------------- drivers/gpu/msm/kgsl_iommu.h | 15 ++++++++----- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 020d832220bb..c5ef65c1d71e 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1402,7 +1402,7 @@ static void _set_secvid(struct kgsl_device *device) adreno_writereg64(adreno_dev, ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE, ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE_HI, - KGSL_IOMMU_SECURE_BASE); + KGSL_IOMMU_SECURE_BASE(&device->mmu)); adreno_writereg(adreno_dev, ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE, KGSL_IOMMU_SECURE_SIZE); @@ -1824,7 +1824,7 @@ static int adreno_getproperty(struct kgsl_device *device, * anything to mmap(). */ shadowprop.gpuaddr = - (unsigned int) device->memstore.gpuaddr; + (unsigned long)device->memstore.gpuaddr; shadowprop.size = device->memstore.size; /* GSL needs this to be set, even if it appears to be meaningless */ diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 813e0cb9cb43..4f40722cb199 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2445,8 +2445,8 @@ static int a5xx_rb_start(struct adreno_device *adreno_dev, adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL, A5XX_CP_RB_CNTL_DEFAULT); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE, - rb->buffer_desc.gpuaddr); + adreno_writereg64(adreno_dev, ADRENO_REG_CP_RB_BASE, + ADRENO_REG_CP_RB_BASE_HI, rb->buffer_desc.gpuaddr); ret = a5xx_microcode_load(adreno_dev); if (ret) diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 0e9fa9ffdf12..2fe1fdc0baa0 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -38,9 +38,10 @@ #define _IOMMU_PRIV(_mmu) (&((_mmu)->priv.iommu)) -#define ADDR_IN_GLOBAL(_a) \ - (((_a) >= KGSL_IOMMU_GLOBAL_MEM_BASE) && \ - ((_a) < (KGSL_IOMMU_GLOBAL_MEM_BASE + KGSL_IOMMU_GLOBAL_MEM_SIZE))) +#define ADDR_IN_GLOBAL(_mmu, _a) \ + (((_a) >= KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu)) && \ + ((_a) < (KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu) + \ + KGSL_IOMMU_GLOBAL_MEM_SIZE))) static struct kgsl_mmu_pt_ops iommu_pt_ops; static bool need_iommu_sync; @@ -197,7 +198,7 @@ static void kgsl_iommu_add_global(struct kgsl_mmu *mmu, BUG_ON(global_pt_count >= GLOBAL_PT_ENTRIES); BUG_ON((global_pt_alloc + memdesc->size) >= KGSL_IOMMU_GLOBAL_MEM_SIZE); - memdesc->gpuaddr = KGSL_IOMMU_GLOBAL_MEM_BASE + global_pt_alloc; + memdesc->gpuaddr = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu) + global_pt_alloc; memdesc->priv |= KGSL_MEMDESC_GLOBAL; global_pt_alloc += memdesc->size; @@ -210,7 +211,7 @@ static void kgsl_iommu_add_global(struct kgsl_mmu *mmu, void kgsl_add_global_secure_entry(struct kgsl_device *device, struct kgsl_memdesc *memdesc) { - memdesc->gpuaddr = KGSL_IOMMU_SECURE_BASE; + memdesc->gpuaddr = KGSL_IOMMU_SECURE_BASE(&device->mmu); kgsl_global_secure_pt_entry = memdesc; } @@ -631,7 +632,7 @@ static void _find_mem_entries(struct kgsl_mmu *mmu, uint64_t faultaddr, /* Set the maximum possible size as an initial value */ nextentry->gpuaddr = (uint64_t) -1; - if (ADDR_IN_GLOBAL(faultaddr)) { + if (ADDR_IN_GLOBAL(mmu, faultaddr)) { _get_global_entries(faultaddr, preventry, nextentry); } else if (context) { private = context->proc_priv; @@ -1001,14 +1002,14 @@ static void setup_64bit_pagetable(struct kgsl_mmu *mmu, unsigned int secure_global_size = kgsl_global_secure_pt_entry != NULL ? kgsl_global_secure_pt_entry->size : 0; if (mmu->secured && pagetable->name == KGSL_MMU_SECURE_PT) { - pt->compat_va_start = KGSL_IOMMU_SECURE_BASE + + pt->compat_va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; - pt->compat_va_end = KGSL_IOMMU_SECURE_END; - pt->va_start = KGSL_IOMMU_SECURE_BASE + secure_global_size; - pt->va_end = KGSL_IOMMU_SECURE_END; + pt->compat_va_end = KGSL_IOMMU_SECURE_END(mmu); + pt->va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; + pt->va_end = KGSL_IOMMU_SECURE_END(mmu); } else { pt->compat_va_start = KGSL_IOMMU_SVM_BASE32; - pt->compat_va_end = KGSL_IOMMU_SVM_END32; + pt->compat_va_end = KGSL_IOMMU_SECURE_BASE(mmu); pt->va_start = KGSL_IOMMU_VA_BASE64; pt->va_end = KGSL_IOMMU_VA_END64; } @@ -1017,7 +1018,7 @@ static void setup_64bit_pagetable(struct kgsl_mmu *mmu, pagetable->name != KGSL_MMU_SECURE_PT) { if ((BITS_PER_LONG == 32) || is_compat_task()) { pt->svm_start = KGSL_IOMMU_SVM_BASE32; - pt->svm_end = KGSL_IOMMU_SVM_END32; + pt->svm_end = KGSL_IOMMU_SECURE_BASE(mmu); } else { pt->svm_start = KGSL_IOMMU_SVM_BASE64; pt->svm_end = KGSL_IOMMU_SVM_END64; @@ -1033,22 +1034,22 @@ static void setup_32bit_pagetable(struct kgsl_mmu *mmu, kgsl_global_secure_pt_entry->size : 0; if (mmu->secured) { if (pagetable->name == KGSL_MMU_SECURE_PT) { - pt->compat_va_start = KGSL_IOMMU_SECURE_BASE + + pt->compat_va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; - pt->compat_va_end = KGSL_IOMMU_SECURE_END; - pt->va_start = KGSL_IOMMU_SECURE_BASE + + pt->compat_va_end = KGSL_IOMMU_SECURE_END(mmu); + pt->va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; - pt->va_end = KGSL_IOMMU_SECURE_END; + pt->va_end = KGSL_IOMMU_SECURE_END(mmu); } else { pt->va_start = KGSL_IOMMU_SVM_BASE32; - pt->va_end = KGSL_IOMMU_SECURE_BASE + + pt->va_end = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; pt->compat_va_start = pt->va_start; pt->compat_va_end = pt->va_end; } } else { pt->va_start = KGSL_IOMMU_SVM_BASE32; - pt->va_end = KGSL_IOMMU_GLOBAL_MEM_BASE; + pt->va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu); pt->compat_va_start = pt->va_start; pt->compat_va_end = pt->va_end; } @@ -2203,7 +2204,8 @@ static int kgsl_iommu_set_svm_region(struct kgsl_pagetable *pagetable, struct rb_node *node; /* Make sure the requested address doesn't fall in the global range */ - if (ADDR_IN_GLOBAL(gpuaddr) || ADDR_IN_GLOBAL(gpuaddr + size)) + if (ADDR_IN_GLOBAL(pagetable->mmu, gpuaddr) || + ADDR_IN_GLOBAL(pagetable->mmu, gpuaddr + size)) return -ENOMEM; spin_lock(&pagetable->lock); diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h index 457522986fce..a6de179831ba 100644 --- a/drivers/gpu/msm/kgsl_iommu.h +++ b/drivers/gpu/msm/kgsl_iommu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2016,2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,12 +24,17 @@ * are mapped into all pagetables. */ #define KGSL_IOMMU_GLOBAL_MEM_SIZE SZ_8M -#define KGSL_IOMMU_GLOBAL_MEM_BASE 0xf8000000 +#define KGSL_IOMMU_GLOBAL_MEM_BASE32 0xf8000000 +#define KGSL_IOMMU_GLOBAL_MEM_BASE64 0x100000000ULL + +#define KGSL_IOMMU_GLOBAL_MEM_BASE(__mmu) \ + (MMU_FEATURE(__mmu, KGSL_MMU_64BIT) ? \ + KGSL_IOMMU_GLOBAL_MEM_BASE64 : KGSL_IOMMU_GLOBAL_MEM_BASE32) #define KGSL_IOMMU_SECURE_SIZE SZ_256M -#define KGSL_IOMMU_SECURE_END KGSL_IOMMU_GLOBAL_MEM_BASE -#define KGSL_IOMMU_SECURE_BASE \ - (KGSL_IOMMU_GLOBAL_MEM_BASE - KGSL_IOMMU_SECURE_SIZE) +#define KGSL_IOMMU_SECURE_END(_mmu) KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu) +#define KGSL_IOMMU_SECURE_BASE(_mmu) \ + (KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu) - KGSL_IOMMU_SECURE_SIZE) #define KGSL_IOMMU_SVM_BASE32 0x300000 #define KGSL_IOMMU_SVM_END32 (0xC0000000 - SZ_16M) -- GitLab From b30f3742bdef2f393454e8767e2d0d520cc11381 Mon Sep 17 00:00:00 2001 From: Deepak Kumar Date: Mon, 29 Jan 2018 17:10:18 +0530 Subject: [PATCH 2461/5498] msm: kgsl: Update global memory base to 0XFC000000 for 64bit kernel KGSL memstore GPU address should be 32bit value for user mode graphics driver to successfully mmap device memstore. Move global memory base to 0XFC000000 from 0X100000000 for 64bit kernel to allow user mode graphics driver to successfully mmap memstore. Change-Id: Iadfbde5e2b13c1be943a272e424f6c5f05cc1b97 Signed-off-by: Deepak Kumar --- drivers/gpu/msm/kgsl_iommu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h index a6de179831ba..097dfe798bbe 100644 --- a/drivers/gpu/msm/kgsl_iommu.h +++ b/drivers/gpu/msm/kgsl_iommu.h @@ -25,7 +25,7 @@ */ #define KGSL_IOMMU_GLOBAL_MEM_SIZE SZ_8M #define KGSL_IOMMU_GLOBAL_MEM_BASE32 0xf8000000 -#define KGSL_IOMMU_GLOBAL_MEM_BASE64 0x100000000ULL +#define KGSL_IOMMU_GLOBAL_MEM_BASE64 0xfc000000 #define KGSL_IOMMU_GLOBAL_MEM_BASE(__mmu) \ (MMU_FEATURE(__mmu, KGSL_MMU_64BIT) ? \ -- GitLab From 509d690ce58f9293188fc9a76698ac9aac48325c Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Fri, 12 Jan 2018 15:39:57 +0530 Subject: [PATCH 2462/5498] ARM: dts: msm: use appropriate subsystem for audio on msm8909w Audio client will register to modem to handle adsp ssr events. Add subsystem entry for bg codec. Change-Id: Ia1548e9d3aab0e37fd5d4f57d6ddfcff2c1f4099 Signed-off-by: Bala Kishore Pati --- arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi index 44ac7a8b3105..f2cea322ecc5 100644 --- a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi @@ -186,6 +186,7 @@ bg_cdc: bg_codec { status = "disabled"; compatible = "qcom,bg-codec"; + qcom,subsys-name = "modem"; qcom,bg-glink { compatible = "qcom,bg-cdc-glink"; qcom,msm-glink-channels = <4>; -- GitLab From b51a9f0121d8cb633649282566c0c074e0dc448e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 29 Jan 2018 21:31:21 -0800 Subject: [PATCH 2463/5498] ANDROID: sdcardfs: Use lower getattr times/size We now use the lower filesystem's getattr for time and size related information. Change-Id: I3dd05614a0c2837a13eeb033444fbdf070ddce2a Signed-off-by: Daniel Rosenberg Bug: 72007585 --- fs/sdcardfs/inode.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 5ed714d66b3a..6f04a18ad503 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -818,8 +818,8 @@ out_err: return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, - struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, + struct kstat *lower_stat, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct sdcardfs_inode_data *top = top_data_get(info); @@ -835,12 +835,12 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->uid = make_kuid(&init_user_ns, top->d_uid); stat->gid = make_kgid(&init_user_ns, get_gid(mnt, sb, top)); stat->rdev = inode->i_rdev; - stat->size = i_size_read(inode); - stat->atime = inode->i_atime; - stat->mtime = inode->i_mtime; - stat->ctime = inode->i_ctime; - stat->blksize = (1 << inode->i_blkbits); - stat->blocks = inode->i_blocks; + stat->size = lower_stat->size; + stat->atime = lower_stat->atime; + stat->mtime = lower_stat->mtime; + stat->ctime = lower_stat->ctime; + stat->blksize = lower_stat->blksize; + stat->blocks = lower_stat->blocks; data_put(top); return 0; } @@ -866,8 +866,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, goto out; sdcardfs_copy_and_fix_attrs(dentry->d_inode, lower_path.dentry->d_inode); - err = sdcardfs_fillattr(mnt, dentry->d_inode, stat); - stat->blocks = lower_stat.blocks; + err = sdcardfs_fillattr(mnt, dentry->d_inode, &lower_stat, stat); out: sdcardfs_put_lower_path(dentry, &lower_path); return err; -- GitLab From 328532da174ac867a67e1eb18c1752ff7d949adf Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 17:51:26 -0800 Subject: [PATCH 2464/5498] Revert "ANDROID: sdcardfs: notify lower file of opens" This reverts commit fd825dd8ffd9c4873f80438c3030dd21c204512d. Instead of calling notify within sdcardfs, which reverse the order of notifications during an open with truncate, we'll make fs_notify worry about it. Change-Id: Ic634401c0f223500066300a4df8b1453a0b35b60 Bug: 70706497 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 0c6e4659d4e6..0fe08c7856a5 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -18,7 +18,6 @@ * General Public License. */ -#include #include "sdcardfs.h" #ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE #include @@ -260,7 +259,6 @@ static int sdcardfs_open(struct inode *inode, struct file *file) fput(lower_file); /* fput calls dput for lower_dentry */ } } else { - fsnotify_open(lower_file); sdcardfs_set_lower_file(file, lower_file); } -- GitLab From 86e998cf1f1d3a7946b28c27566497e09501ae34 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 15:02:50 -0800 Subject: [PATCH 2465/5498] ANDROID: fsnotify: Notify lower fs of open If the filesystem being watched supports d_canonical_path, notify the lower filesystem of the open as well. Change-Id: I2b1739e068afbaf5eb39950516072bff8345ebfe Signed-off-by: Daniel Rosenberg Bug: 70706497 --- include/linux/fsnotify.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index a7789559078b..7dba769ef934 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -230,12 +230,19 @@ static inline void fsnotify_modify(struct file *file) static inline void fsnotify_open(struct file *file) { struct path *path = &file->f_path; + struct path lower_path; struct inode *inode = file_inode(file); __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; + if (path->dentry->d_op && path->dentry->d_op->d_canonical_path) { + path->dentry->d_op->d_canonical_path(path, &lower_path); + fsnotify_parent(&lower_path, NULL, mask); + fsnotify(lower_path.dentry->d_inode, mask, &lower_path, FSNOTIFY_EVENT_PATH, NULL, 0); + path_put(&lower_path); + } fsnotify_parent(path, NULL, mask); fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } -- GitLab From 696018a6b017e7458c74c2befad12a53bebd3044 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 1 Feb 2018 16:52:22 -0800 Subject: [PATCH 2466/5498] ANDROID: sdcardfs: Protect set_top If the top is changed while we're attempting to use it, it's possible that the reference will be put while we are in the process of grabbing a reference. Now we grab a spinlock to protect grabbing our reference count. Additionally, we now set the inode_info's top value to point to it's own data when initializing, which makes tracking changes easier. Change-Id: If15748c786ce4c0480ab8c5051a92523aff284d2 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 28 +++++++++++++--------------- fs/sdcardfs/main.c | 6 ++---- fs/sdcardfs/sdcardfs.h | 26 ++++++++++++++++++-------- fs/sdcardfs/super.c | 3 +++ 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index bb625735ec2a..c12da349758f 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -32,23 +32,20 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->data->under_android = pi->data->under_android; ci->data->under_cache = pi->data->under_cache; ci->data->under_obb = pi->data->under_obb; - set_top(ci, pi->top_data); } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, - struct sdcardfs_inode_data *top) + uid_t uid) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); info->data->perm = perm; info->data->userid = userid; info->data->d_uid = uid; - info->data->under_android = under_android; + info->data->under_android = false; info->data->under_cache = false; info->data->under_obb = false; - set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, @@ -58,8 +55,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); - struct sdcardfs_inode_data *parent_data = - SDCARDFS_I(parent->d_inode)->data; + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_data *parent_data = parent_info->data; appid_t appid; unsigned long user_num; int err; @@ -80,13 +77,15 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, inherit_derived_state(parent->d_inode, dentry->d_inode); /* Files don't get special labels */ - if (!S_ISDIR(dentry->d_inode->i_mode)) + if (!S_ISDIR(dentry->d_inode->i_mode)) { + set_top(info, parent_info); return; + } /* Derive custom permissions based on parent and current node */ switch (parent_data->perm) { case PERM_INHERIT: case PERM_ANDROID_PACKAGE_CACHE: - /* Already inherited above */ + set_top(info, parent_info); break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ @@ -96,7 +95,6 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, info->data->userid = 0; else info->data->userid = user_num; - set_top(info, info->data); break; case PERM_ROOT: /* Assume masked off by default. */ @@ -104,24 +102,24 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID; info->data->under_android = true; - set_top(info, info->data); + } else { + set_top(info, parent_info); } break; case PERM_ANDROID: if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_DATA; - set_top(info, info->data); } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_OBB; info->data->under_obb = true; - set_top(info, info->data); /* Single OBB directory is always shared */ } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_MEDIA; - set_top(info, info->data); + } else { + set_top(info, parent_info); } break; case PERM_ANDROID_OBB: @@ -132,13 +130,13 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, if (appid != 0 && !is_excluded(name->name, parent_data->userid)) info->data->d_uid = multiuser_get_uid(parent_data->userid, appid); - set_top(info, info->data); break; case PERM_ANDROID_PACKAGE: if (qstr_case_eq(name, &q_cache)) { info->data->perm = PERM_ANDROID_PACKAGE_CACHE; info->data->under_cache = true; } + set_top(info, parent_info); break; } } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 379037e2f70d..1c00f0fc0ca5 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -341,13 +341,11 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_lock(&sdcardfs_super_list_lock); if (sb_info->options.multiuser) { setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, SDCARDFS_I(sb->s_root->d_inode)->data); + sb_info->options.fs_user_id, AID_ROOT); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); } else { setup_derived_state(sb->s_root->d_inode, PERM_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, SDCARDFS_I(sb->s_root->d_inode)->data); + sb_info->options.fs_user_id, AID_ROOT); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(sb->s_root->d_inode); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 13855986a02b..cc587ec85116 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -201,6 +201,7 @@ struct sdcardfs_inode_info { struct sdcardfs_inode_data *data; /* top folder for ownership */ + spinlock_t top_lock; struct sdcardfs_inode_data *top_data; struct inode vfs_inode; @@ -380,7 +381,12 @@ static inline struct sdcardfs_inode_data *data_get( static inline struct sdcardfs_inode_data *top_data_get( struct sdcardfs_inode_info *info) { - return data_get(info->top_data); + struct sdcardfs_inode_data *top_data; + + spin_lock(&info->top_lock); + top_data = data_get(info->top_data); + spin_unlock(&info->top_lock); + return top_data; } extern void data_release(struct kref *ref); @@ -402,15 +408,20 @@ static inline void release_own_data(struct sdcardfs_inode_info *info) } static inline void set_top(struct sdcardfs_inode_info *info, - struct sdcardfs_inode_data *top) + struct sdcardfs_inode_info *top_owner) { - struct sdcardfs_inode_data *old_top = info->top_data; + struct sdcardfs_inode_data *old_top; + struct sdcardfs_inode_data *new_top = NULL; + + if (top_owner) + new_top = top_data_get(top_owner); - if (top) - data_get(top); - info->top_data = top; + spin_lock(&info->top_lock); + old_top = info->top_data; + info->top_data = new_top; if (old_top) data_put(old_top); + spin_unlock(&info->top_lock); } static inline int get_gid(struct vfsmount *mnt, @@ -516,8 +527,7 @@ struct limit_search { }; extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android, - struct sdcardfs_inode_data *top); + userid_t userid, uid_t uid); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 87d6f836592e..cffcdb11cb8a 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -215,6 +215,9 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) i->data = d; kref_init(&d->refcount); + i->top_data = d; + spin_lock_init(&i->top_lock); + kref_get(&d->refcount); i->vfs_inode.i_version = 1; return &i->vfs_inode; -- GitLab From ee2970b0256bdea1fb16827a8d37e474089d747b Mon Sep 17 00:00:00 2001 From: gaolez Date: Mon, 5 Feb 2018 11:11:22 +0800 Subject: [PATCH 2467/5498] mmc: core: Card specific custom settings for SDIO Add quirk to modify custom settings for QCA9379 card. Change-Id: Ib2380a5bba50b6b8aec9afde4c6b0681d4b25f38 CRs-Fixed: 2183585 Signed-off-by: Gaole Zhang --- drivers/mmc/core/quirks.c | 10 ++++++++++ drivers/mmc/core/sdio.c | 3 ++- include/linux/mmc/card.h | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index 071adc101158..1b4e40c0aff6 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -79,6 +79,13 @@ #define SDIO_DEVICE_ID_QCA9377 0x701 #endif +#ifndef SDIO_VENDOR_ID_QCA9379 +#define SDIO_VENDOR_ID_QCA9379 0x271 +#endif + +#ifndef SDIO_DEVICE_ID_QCA9379 +#define SDIO_DEVICE_ID_QCA9379 0x801 +#endif /* * This hook just adds a quirk for all sdio devices @@ -131,6 +138,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = { SDIO_FIXUP(SDIO_VENDOR_ID_QCA9377, SDIO_DEVICE_ID_QCA9377, add_quirk, MMC_QUIRK_QCA9377_SETTINGS), + + SDIO_FIXUP(SDIO_VENDOR_ID_QCA9379, SDIO_DEVICE_ID_QCA9379, + add_quirk, MMC_QUIRK_QCA9379_SETTINGS), END_FIXUP }; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 5651cf63f8bd..65425a41a84f 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -228,7 +228,8 @@ static void sdio_enable_vendor_specific_settings(struct mmc_card *card) u8 settings; if (mmc_enable_qca6574_settings(card) || - mmc_enable_qca9377_settings(card)) { + mmc_enable_qca9377_settings(card) || + mmc_enable_qca9379_settings(card)) { ret = mmc_io_rw_direct(card, 1, 0, 0xF2, 0x0F, NULL); if (ret) { pr_crit("%s: failed to write to fn 0xf2 %d\n", diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index f6cec9c43d98..9f7cbb375a37 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -393,6 +393,8 @@ struct mmc_card { /* Make sure CMDQ is empty before queuing DCMD */ #define MMC_QUIRK_CMDQ_EMPTY_BEFORE_DCMD (1 << 17) +#define MMC_QUIRK_QCA9379_SETTINGS (1 << 18) /* QCA9379 card settings*/ + unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ unsigned int pref_erase; /* in sectors */ @@ -693,6 +695,11 @@ static inline bool mmc_enable_qca9377_settings(const struct mmc_card *c) return c->quirks & MMC_QUIRK_QCA9377_SETTINGS; } +static inline bool mmc_enable_qca9379_settings(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_QCA9379_SETTINGS; +} + #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) (dev_name(&(c)->dev)) -- GitLab From ced1c2479bc952dc260f5b48173d16a36f3f53f6 Mon Sep 17 00:00:00 2001 From: gaolez Date: Fri, 2 Feb 2018 12:24:31 +0800 Subject: [PATCH 2468/5498] cnss_sdio: Add Naples sdio device id in sdio device list Add Naples sdio device id in sdio device list, so that the Naples wlan card will not be reported as an unknown device. Change-Id: Iaad43aed32082372a0d1bc9f4e9c5c35f0750b19 CRs-Fixed: 2183586 Signed-off-by: Gaole Zhang --- drivers/net/wireless/cnss/cnss_sdio.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c index fafab6910570..3177140eadfe 100644 --- a/drivers/net/wireless/cnss/cnss_sdio.c +++ b/drivers/net/wireless/cnss/cnss_sdio.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -127,6 +127,7 @@ static struct cnss_sdio_data { /* SDIO manufacturer ID and Codes */ #define MANUFACTURER_ID_AR6320_BASE 0x500 #define MANUFACTURER_ID_QCA9377_BASE 0x700 +#define MANUFACTURER_ID_QCA9379_BASE 0x800 #define MANUFACTURER_CODE 0x271 static const struct sdio_device_id ar6k_id_table[] = { @@ -162,6 +163,22 @@ static const struct sdio_device_id ar6k_id_table[] = { {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xD))}, {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xE))}, {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xF))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x0))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x1))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x2))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x3))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x4))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x5))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x6))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x7))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x8))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0x9))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xA))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xB))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xC))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xD))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xE))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9379_BASE | 0xF))}, {}, }; MODULE_DEVICE_TABLE(sdio, ar6k_id_table); -- GitLab From 29e6c77724acb8e149d1202c1680b09c7dc35196 Mon Sep 17 00:00:00 2001 From: puneet Date: Tue, 5 Dec 2017 17:07:36 +0530 Subject: [PATCH 2469/5498] ARM: dts: msm: add internal pull up for I2C1 on MDM9650 TTP Since there is no external pull up for I2C1 instance, add the pinctrl changes to enable the internal pull up without which there can be timeout/arb_lost error. Change-Id: Ief9a06a83f84b791948f691e0621829786eed4d8 Signed-off-by: puneet Signed-off-by: Abinaya P --- arch/arm/boot/dts/qcom/mdm9650-ttp.dts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650-ttp.dts b/arch/arm/boot/dts/qcom/mdm9650-ttp.dts index 8db7dc55dfae..8a951ffb004f 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-ttp.dts +++ b/arch/arm/boot/dts/qcom/mdm9650-ttp.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -47,6 +47,17 @@ }; &soc { + tlmm_pinmux: pinctrl@1000000 { + i2c_1 { + i2c_1_active { + config { + pins = "gpio2", "gpio3"; + drive-strength = <2>; + bias-pull-up; + }; + }; + }; + }; usb_detect { compatible = "qcom,gpio-usbdetect"; interrupt-parent = <&spmi_bus>; -- GitLab From fadf04821d4eda6d6dd214e04c4c59239496ec5e Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Wed, 24 Jan 2018 21:53:01 +0530 Subject: [PATCH 2470/5498] soc: qcom: bgrsb: Add BG RSB register peek/poke functionality Closes existing RSB Glink channels and open again once user activity for register peek/poke done. Change-Id: I89f20dd19cd4ec42ef163ef85e15309f4c4bb8fd Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 70 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index a491b2c8632f..1487024d884c 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -47,6 +47,9 @@ #define BGRSB_POWER_CALIBRATION 2 #define BGRSB_POWER_ENABLE 1 #define BGRSB_POWER_DISABLE 0 +#define BGRSB_GLINK_POWER_ENABLE 6 +#define BGRSB_GLINK_POWER_DISABLE 7 + struct bgrsb_regulator { struct regulator *regldo11; @@ -92,6 +95,9 @@ struct bgrsb_priv { struct work_struct rsb_up_work; struct work_struct rsb_down_work; + struct work_struct rsb_glink_up_work; + struct work_struct rsb_glink_down_work; + struct work_struct rsb_calibration_work; struct work_struct bttn_configr_work; @@ -403,6 +409,30 @@ static void bgrsb_bgdown_work(struct work_struct *work) pr_debug("RSB current state is : %d\n", dev->bgrsb_current_state); } +static void bgrsb_glink_bgdown_work(struct work_struct *work) +{ + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, + rsb_glink_down_work); + + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) == 0) + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + else + pr_err("Failed to unvote LDO-15 on BG down\n"); + } + + if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) { + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) + dev->bgrsb_current_state = BGRSB_STATE_INIT; + else + pr_err("Failed to unvote LDO-11 on BG Glink down\n"); + } + if (dev->handle) + glink_close(dev->handle); + dev->handle = NULL; + pr_debug("BG Glink Close connection\n"); +} + static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) { int rc = 0; @@ -498,6 +528,36 @@ static void bgrsb_bgup_work(struct work_struct *work) } } +static void bgrsb_glink_bgup_work(struct work_struct *work) +{ + int rc = 0; + struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, + rsb_glink_up_work); + + if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) { + + INIT_WORK(&dev->glink_work, bgrsb_glink_open_work); + queue_work(dev->bgrsb_event_wq, &dev->glink_work); + + rc = wait_event_timeout(dev->link_state_wait, + (dev->chnl_state == true), + msecs_to_jiffies(TIMEOUT_MS*2)); + if (rc == 0) { + pr_err("Glink channel connection time out\n"); + return; + } + rc = bgrsb_configr_rsb(dev, true); + if (rc != 0) { + pr_err("BG Glink failed to configure RSB %d\n", rc); + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) + dev->bgrsb_current_state = BGRSB_STATE_INIT; + return; + } + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + pr_debug("Glink RSB Cofigured\n"); + } +} + /** *ssr_bg_cb(): callback function is called *by ssr framework when BG goes down, up and during ramdump @@ -712,6 +772,12 @@ static int split_bg_work(struct bgrsb_priv *dev, char *str) dev->bttn_configs = (uint8_t)val; queue_work(dev->bgrsb_wq, &dev->bttn_configr_work); break; + case BGRSB_GLINK_POWER_DISABLE: + queue_work(dev->bgrsb_wq, &dev->rsb_glink_down_work); + break; + case BGRSB_GLINK_POWER_ENABLE: + queue_work(dev->bgrsb_wq, &dev->rsb_glink_up_work); + break; } return 0; } @@ -786,6 +852,8 @@ static int bgrsb_init(struct bgrsb_priv *dev) INIT_WORK(&dev->rsb_down_work, bgrsb_disable_rsb); INIT_WORK(&dev->rsb_calibration_work, bgrsb_calibration); INIT_WORK(&dev->bttn_configr_work, bgrsb_buttn_configration); + INIT_WORK(&dev->rsb_glink_down_work, bgrsb_glink_bgdown_work); + INIT_WORK(&dev->rsb_glink_up_work, bgrsb_glink_bgup_work); return 0; @@ -918,7 +986,7 @@ static struct platform_driver bg_rsb_driver = { .name = "bg-rsb", .of_match_table = bg_rsb_of_match, }, - .probe = bg_rsb_probe, + .probe = bg_rsb_probe, .remove = bg_rsb_remove, .resume = bg_rsb_resume, .suspend = bg_rsb_suspend, -- GitLab From 41c86435e279f581ab3b83116f3fdba2424d7f85 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Sun, 21 Jan 2018 18:45:20 -0800 Subject: [PATCH 2471/5498] ASoC: msm: qdspv2: remove flags for no_wait_qie_spinlock In q6asm_session_free, flags is used for both session_lock and no_wait_que_spinlock. When restoring flags for session_lock, flags may have been overwritten by no_wait_que_spinlock which may causes kernel panic. As session_lock has blocked all other IRQs, replace spin_lock_irqsave with spin_lock for no_wait_que_spinlock. Change-Id: I278ef41d92d1f2466a6ecae963210d91945a1a66 Signed-off-by: Meng Wang --- sound/soc/msm/qdsp6v2/q6asm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 074a8cdfa75b..09b58bf5223a 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -468,13 +468,13 @@ static void q6asm_session_free(struct audio_client *ac) ac->cb = NULL; ac->priv = NULL; - spin_lock_irqsave(&ac->no_wait_que_spinlock, flags); + spin_lock(&ac->no_wait_que_spinlock); list_for_each_safe(ptr, next, &ac->no_wait_que) { node = list_entry(ptr, struct asm_no_wait_node, list); list_del(&node->list); kfree(node); } - spin_unlock_irqrestore(&ac->no_wait_que_spinlock, flags); + spin_unlock(&ac->no_wait_que_spinlock); kfree(ac); spin_unlock_irqrestore(&(session[session_id].session_lock), flags); -- GitLab From af716c7019b2ff2548234821057fb67e9e3fad83 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 5 Feb 2018 16:14:57 +0530 Subject: [PATCH 2472/5498] soc: qcom: bgcom: Removes double pointer conflicts on resume/suspend Use single pointer for bgcom_suspend and bgcom_resume APIs to resolve device crash on continuous device suspend/resume. Change-Id: I0cd202e74b98aa2ce2ebcbd6c8092c6b1d95610b Signed-off-by: Arjun Singh --- drivers/soc/qcom/bgcom.h | 4 ++-- drivers/soc/qcom/bgcom_spi.c | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/soc/qcom/bgcom.h b/drivers/soc/qcom/bgcom.h index bf0a2c92630a..674a2cf5f5aa 100644 --- a/drivers/soc/qcom/bgcom.h +++ b/drivers/soc/qcom/bgcom.h @@ -197,14 +197,14 @@ int bgcom_ahb_write(void *handle, uint32_t ahb_start_addr, * @handle: BGCOM handle associated with the channel * Return 0 on success or -Ve on error */ -int bgcom_suspend(void **handle); +int bgcom_suspend(void *handle); /** * bgcom_resume() - Resumes the channel. * @handle: BGCOM handle associated with the channel * Return 0 on success or -Ve on error */ -int bgcom_resume(void **handle); +int bgcom_resume(void *handle); int bgcom_set_spi_state(enum bgcom_spi_state state); diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c index beeb44424a65..58d2c4de78e1 100644 --- a/drivers/soc/qcom/bgcom_spi.c +++ b/drivers/soc/qcom/bgcom_spi.c @@ -464,7 +464,7 @@ int bgcom_ahb_read(void *handle, uint32_t ahb_start_addr, return -EBUSY; } - if (bgcom_resume(&handle)) { + if (bgcom_resume(handle)) { pr_err("Failed to resume\n"); return -EBUSY; } @@ -525,7 +525,7 @@ int bgcom_ahb_write(void *handle, uint32_t ahb_start_addr, return -EBUSY; } - if (bgcom_resume(&handle)) { + if (bgcom_resume(handle)) { pr_err("Failed to resume\n"); return -EBUSY; } @@ -574,7 +574,7 @@ int bgcom_fifo_write(void *handle, uint32_t num_words, return -EBUSY; } - if (bgcom_resume(&handle)) { + if (bgcom_resume(handle)) { pr_err("Failed to resume\n"); return -EBUSY; } @@ -757,23 +757,23 @@ static int is_bg_resume(void *handle) return cmnd_reg & BIT(31); } -int bgcom_resume(void **handle) +int bgcom_resume(void *handle) { struct bg_spi_priv *bg_spi; struct bg_context *cntx; int retry = 0; - if (*handle == NULL) + if (handle == NULL) return -EINVAL; - cntx = *handle; + cntx = (struct bg_context *)handle; bg_spi = cntx->bg_spi; mutex_lock(&bg_resume_mutex); if (bg_spi->bg_state == BGCOM_STATE_ACTIVE) goto unlock; do { - if (is_bg_resume(*handle)) { + if (is_bg_resume(handle)) { bg_spi->bg_state = BGCOM_STATE_ACTIVE; break; } @@ -783,29 +783,29 @@ int bgcom_resume(void **handle) unlock: mutex_unlock(&bg_resume_mutex); - pr_info("BG Resumed in %d retries\n", retry); + pr_info("BG retries for wake up : %d\n", retry); return (retry == MAX_RETRY ? -ETIMEDOUT : 0); } EXPORT_SYMBOL(bgcom_resume); -int bgcom_suspend(void **handle) +int bgcom_suspend(void *handle) { struct bg_spi_priv *bg_spi; struct bg_context *cntx; uint32_t cmnd_reg = 0; int ret = 0; - if (*handle == NULL) + if (handle == NULL) return -EINVAL; - cntx = *handle; + cntx = (struct bg_context *)handle; bg_spi = cntx->bg_spi; mutex_lock(&bg_resume_mutex); if (bg_spi->bg_state == BGCOM_STATE_SUSPEND) goto unlock; cmnd_reg |= BIT(31); - ret = bgcom_reg_write(*handle, BG_CMND_REG, 1, &cmnd_reg); + ret = bgcom_reg_write(handle, BG_CMND_REG, 1, &cmnd_reg); if (ret == 0) bg_spi->bg_state = BGCOM_STATE_SUSPEND; -- GitLab From ebcd86bb99fa65a20b92c616f91a93e31d21eb0e Mon Sep 17 00:00:00 2001 From: Sachin Grover Date: Tue, 30 Jan 2018 15:19:13 +0530 Subject: [PATCH 2473/5498] defconfig: Add dm-verity related configs Add configs to support verity on apq8053. Change-Id: Icdcd89400dc7081d99205e9d2da09f664493be9d Signed-off-by: Sachin Grover --- arch/arm64/configs/apq8053_IoE-perf_defconfig | 6 +++++- arch/arm64/configs/msmcortex_defconfig | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm64/configs/apq8053_IoE-perf_defconfig b/arch/arm64/configs/apq8053_IoE-perf_defconfig index ab2556f6703f..beb2ade96032 100644 --- a/arch/arm64/configs/apq8053_IoE-perf_defconfig +++ b/arch/arm64/configs/apq8053_IoE-perf_defconfig @@ -245,12 +245,15 @@ CONFIG_SCSI_UFSHCD_PLATFORM=y CONFIG_SCSI_UFS_QCOM=y CONFIG_SCSI_UFS_QCOM_ICE=y CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y CONFIG_DM_REQ_CRYPT=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y +CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE=1 +CONFIG_DM_ANDROID_VERITY=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y CONFIG_TUN=y @@ -589,6 +592,7 @@ CONFIG_IPC_LOGGING=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DEBUG_RODATA=y +CONFIG_KEYS_DEBUG_PROC_KEYS=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index e872625ba7ac..785df222fbc2 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -254,12 +254,15 @@ CONFIG_SCSI_UFSHCD_PLATFORM=y CONFIG_SCSI_UFS_QCOM=y CONFIG_SCSI_UFS_QCOM_ICE=y CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y CONFIG_DM_REQ_CRYPT=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y +CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE=1 +CONFIG_DM_ANDROID_VERITY=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y CONFIG_TUN=y @@ -683,6 +686,7 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_FREE_PAGES_RDONLY=y CONFIG_DEBUG_RODATA=y +CONFIG_KEYS_DEBUG_PROC_KEYS=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y -- GitLab From 6f82fb72d282ebeeb6f367c08cde25161f8c8d08 Mon Sep 17 00:00:00 2001 From: DingXiang Date: Tue, 2 Feb 2016 12:29:18 +0800 Subject: [PATCH 2474/5498] dm snapshot: disallow the COW and origin devices from being identical Otherwise loading a "snapshot" table using the same device for the origin and COW devices, e.g.: echo "0 20971520 snapshot 253:3 253:3 P 8" | dmsetup create snap will trigger: BUG: unable to handle kernel NULL pointer dereference at 0000000000000098 [ 1958.979934] IP: [] dm_exception_store_set_chunk_size+0x7a/0x110 [dm_snapshot] [ 1958.989655] PGD 0 [ 1958.991903] Oops: 0000 [#1] SMP ... [ 1959.059647] CPU: 9 PID: 3556 Comm: dmsetup Tainted: G IO 4.5.0-rc5.snitm+ #150 ... [ 1959.083517] task: ffff8800b9660c80 ti: ffff88032a954000 task.ti: ffff88032a954000 [ 1959.091865] RIP: 0010:[] [] dm_exception_store_set_chunk_size+0x7a/0x110 [dm_snapshot] [ 1959.104295] RSP: 0018:ffff88032a957b30 EFLAGS: 00010246 [ 1959.110219] RAX: 0000000000000000 RBX: 0000000000000008 RCX: 0000000000000001 [ 1959.118180] RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffff880329334a00 [ 1959.126141] RBP: ffff88032a957b50 R08: 0000000000000000 R09: 0000000000000001 [ 1959.134102] R10: 000000000000000a R11: f000000000000000 R12: ffff880330884d80 [ 1959.142061] R13: 0000000000000008 R14: ffffc90001c13088 R15: ffff880330884d80 [ 1959.150021] FS: 00007f8926ba3840(0000) GS:ffff880333440000(0000) knlGS:0000000000000000 [ 1959.159047] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1959.165456] CR2: 0000000000000098 CR3: 000000032f48b000 CR4: 00000000000006e0 [ 1959.173415] Stack: [ 1959.175656] ffffc90001c13040 ffff880329334a00 ffff880330884ed0 ffff88032a957bdc [ 1959.183946] ffff88032a957bb8 ffffffffa040f225 ffff880329334a30 ffff880300000000 [ 1959.192233] ffffffffa04133e0 ffff880329334b30 0000000830884d58 00000000569c58cf [ 1959.200521] Call Trace: [ 1959.203248] [] dm_exception_store_create+0x1d5/0x240 [dm_snapshot] [ 1959.211986] [] snapshot_ctr+0x140/0x630 [dm_snapshot] [ 1959.219469] [] ? dm_split_args+0x64/0x150 [dm_mod] [ 1959.226656] [] dm_table_add_target+0x177/0x440 [dm_mod] [ 1959.234328] [] table_load+0x143/0x370 [dm_mod] [ 1959.241129] [] ? retrieve_status+0x1b0/0x1b0 [dm_mod] [ 1959.248607] [] ctl_ioctl+0x255/0x4d0 [dm_mod] [ 1959.255307] [] ? memzero_explicit+0x12/0x20 [ 1959.261816] [] dm_ctl_ioctl+0x13/0x20 [dm_mod] [ 1959.268615] [] do_vfs_ioctl+0xa6/0x5c0 [ 1959.274637] [] ? __audit_syscall_entry+0xaf/0x100 [ 1959.281726] [] ? do_audit_syscall_entry+0x66/0x70 [ 1959.288814] [] SyS_ioctl+0x79/0x90 [ 1959.294450] [] entry_SYSCALL_64_fastpath+0x12/0x71 ... [ 1959.323277] RIP [] dm_exception_store_set_chunk_size+0x7a/0x110 [dm_snapshot] [ 1959.333090] RSP [ 1959.336978] CR2: 0000000000000098 [ 1959.344121] ---[ end trace b049991ccad1169e ]--- Change-Id: I03c690de6c1a2431adbaf000f6be1579d7be1b31 Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1195899 Cc: stable@vger.kernel.org Signed-off-by: Ding Xiang Signed-off-by: Mike Snitzer Git-commit: 4df2bf466a9c9c92f40d27c4aa9120f4e8227bfc Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Sachin Grover --- drivers/md/dm-snap.c | 9 ++++++++ drivers/md/dm-table.c | 41 ++++++++++++++++++++--------------- include/linux/device-mapper.h | 2 ++ 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index b91b39caf7a9..81567634628a 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1099,6 +1099,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) int i; int r = -EINVAL; char *origin_path, *cow_path; + dev_t origin_dev, cow_dev; unsigned args_used, num_flush_bios = 1; fmode_t origin_mode = FMODE_READ; @@ -1129,11 +1130,19 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->error = "Cannot get origin device"; goto bad_origin; } + origin_dev = s->origin->bdev->bd_dev; cow_path = argv[0]; argv++; argc--; + cow_dev = dm_get_dev_t(cow_path); + if (cow_dev && cow_dev == origin_dev) { + ti->error = "COW device cannot be the same as origin device"; + r = -EINVAL; + goto bad_cow; + } + r = dm_get_device(ti, cow_path, dm_table_get_mode(ti->table), &s->cow); if (r) { ti->error = "Cannot get COW device"; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 0f778784937d..9642b379eb82 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -363,6 +363,26 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode, return 0; } +/* + * Convert the path to a device + */ +dev_t dm_get_dev_t(const char *path) +{ + dev_t uninitialized_var(dev); + struct block_device *bdev; + + bdev = lookup_bdev(path); + if (IS_ERR(bdev)) + dev = name_to_dev_t(path); + else { + dev = bdev->bd_dev; + bdput(bdev); + } + + return dev; +} +EXPORT_SYMBOL_GPL(dm_get_dev_t); + /* * Add a device to the list, or just increment the usage count if * it's already present. @@ -371,28 +391,15 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, struct dm_dev **result) { int r; - dev_t uninitialized_var(dev); + dev_t dev; struct dm_dev_internal *dd; - unsigned int major, minor; struct dm_table *t = ti->table; - char dummy; BUG_ON(!t); - if (sscanf(path, "%u:%u%c", &major, &minor, &dummy) == 2) { - /* Extract the major/minor numbers */ - dev = MKDEV(major, minor); - if (MAJOR(dev) != major || MINOR(dev) != minor) - return -EOVERFLOW; - } else { - /* convert the path to a device */ - struct block_device *bdev = lookup_bdev(path); - - if (IS_ERR(bdev)) - return PTR_ERR(bdev); - dev = bdev->bd_dev; - bdput(bdev); - } + dev = dm_get_dev_t(path); + if (!dev) + return -ENODEV; dd = find_device(&t->devices, dev); if (!dd) { diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 965cdf7f1ab0..082ec600c51e 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -121,6 +121,8 @@ struct dm_dev { char name[16]; }; +dev_t dm_get_dev_t(const char *path); + /* * Constructors should call these functions to ensure destination devices * are opened/closed correctly. -- GitLab From 4d1f91cc90d2fc1da62bbc1300d75247c3bb03e1 Mon Sep 17 00:00:00 2001 From: Vinayak Menon Date: Tue, 6 Feb 2018 10:07:54 +0530 Subject: [PATCH 2475/5498] ARM: dts: msm: enable kpti on msm8996 and msm8996pro Pass the kernel command line argument 'kpti=1' to enable Kernel Page Table Isolation feature on msm8996 and msm8996pro. Change-Id: I6f7753caf0d293ada27d7da898ae277fa8781b00 Signed-off-by: Vinayak Menon --- arch/arm/boot/dts/qcom/msm8996.dtsi | 4 ++-- arch/arm/boot/dts/qcom/msm8996pro.dtsi | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index 8bcbeb8d5585..7eaf564f12fb 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,7 +21,7 @@ interrupt-parent = <&intc>; chosen { - bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1 app_setting.use_32bit_app_setting=1"; + bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1 app_setting.use_32bit_app_setting=1 kpti=1"; }; aliases { diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi index 1bf22f160dff..ef42824b119b 100644 --- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,7 +22,7 @@ qcom,msm-id = <305 0x10000>; chosen { - bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1 app_setting.use_32bit_app_setting_pro=1"; + bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1 app_setting.use_32bit_app_setting_pro=1 kpti=1"; }; }; -- GitLab From 69e2fcc5e7819338add29cdfe422b41f4b4c7bba Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Tue, 19 Dec 2017 13:15:53 +0530 Subject: [PATCH 2476/5498] ARM: dts: msm: Update SDC1 lines sleep configuration for 8909w BG Configure SDC1 pins to no-pull & active low during sleep. Otherwise these gpios are causing leakage on 8909w BG platforms. Change-Id: I7db80667d41de006cfe3c0e56e517ba4889da995 Signed-off-by: Veerabhadrarao Badiganti --- arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts | 27 +++++++++++++++++++ arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts | 27 +++++++++++++++++++ arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts | 27 +++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts index 2237a2aba626..a4bb46ba7ea3 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-alpha.dts @@ -194,6 +194,33 @@ status = "disabled"; }; +&sdc1_clk_off { + config { + pins = "sdc1_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + +&sdc1_cmd_off { + config { + pins = "sdc1_cmd"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + +&sdc1_data_off { + config { + pins = "sdc1_data"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + &sdhc_2 { status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts index 4b27a2b74515..23a78d3f6f9d 100644 --- a/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -195,6 +195,33 @@ status = "disabled"; }; +&sdc1_clk_off { + config { + pins = "sdc1_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + +&sdc1_cmd_off { + config { + pins = "sdc1_cmd"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + +&sdc1_data_off { + config { + pins = "sdc1_data"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + &sdhc_2 { status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts index 5d72b455fee8..1af8d60193d6 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -238,6 +238,33 @@ status = "disabled"; }; +&sdc1_clk_off { + config { + pins = "sdc1_clk"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + +&sdc1_cmd_off { + config { + pins = "sdc1_cmd"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + +&sdc1_data_off { + config { + pins = "sdc1_data"; + bias-disable; /* NO pull */ + drive-strength = <2>; /* 2 MA */ + output-low; + }; +}; + &sdhc_2 { status = "disabled"; }; -- GitLab From ba30f211b80bebd6e1e30edd687244a46da05d60 Mon Sep 17 00:00:00 2001 From: Sachin Grover Date: Tue, 6 Feb 2018 12:56:26 +0530 Subject: [PATCH 2477/5498] defconfig: arm: Add dm-verity related configs Add configs to support verity on 32 bit apq8053. Change-Id: Iafaa4f49dd7f904905819b2a01487957d35b8f9d Signed-off-by: Sachin Grover --- arch/arm/configs/apq8053_IoE-perf_defconfig | 5 +++++ arch/arm/configs/msmcortex_defconfig | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm/configs/apq8053_IoE-perf_defconfig b/arch/arm/configs/apq8053_IoE-perf_defconfig index 2638d1a1781e..ddf0fc69fdcd 100644 --- a/arch/arm/configs/apq8053_IoE-perf_defconfig +++ b/arch/arm/configs/apq8053_IoE-perf_defconfig @@ -250,11 +250,15 @@ CONFIG_SCSI_UFSHCD_PLATFORM=y CONFIG_SCSI_UFS_QCOM=y CONFIG_SCSI_UFS_QCOM_ICE=y CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y CONFIG_DM_REQ_CRYPT=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE=1 +CONFIG_DM_ANDROID_VERITY=y CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y @@ -587,6 +591,7 @@ CONFIG_TIMER_STATS=y CONFIG_IPC_LOGGING=y CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_KEYS_DEBUG_PROC_KEYS=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_LSM_MMAP_MIN_ADDR=4096 diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index 7676984730e4..e761037394eb 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -255,10 +255,14 @@ CONFIG_SCSI_UFSHCD_PLATFORM=y CONFIG_SCSI_UFS_QCOM=y CONFIG_SCSI_UFS_QCOM_ICE=y CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y CONFIG_DM_REQ_CRYPT=y CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE=1 +CONFIG_DM_ANDROID_VERITY=y CONFIG_DM_VERITY_FEC=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y @@ -648,6 +652,7 @@ CONFIG_LKDTM=y CONFIG_PANIC_ON_DATA_CORRUPTION=y CONFIG_DEBUG_USER=y CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_KEYS_DEBUG_PROC_KEYS=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y -- GitLab From 8ad43fa2c0b10e738acff58aea5c697a7e7180a1 Mon Sep 17 00:00:00 2001 From: jiangjia Date: Tue, 6 Feb 2018 15:31:53 +0800 Subject: [PATCH 2478/5498] ARM: dts: msm: Increase WCNSS carveout for msm8909w Increase WCNSS carveout region size from 5 MB to 6 MB for msm8909w wtp. Change-Id: I2f404f5da03672a2370cefe880697bfb9a95bf62 Signed-off-by: Jiangjiang Shen --- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index a0504b010000..8b043b355e3e 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -133,7 +133,7 @@ }; &peripheral_mem { - reg = <0x0 0x8d600000 0x0 0x0500000>; + reg = <0x0 0x8d600000 0x0 0x0600000>; }; &i2c_1 { -- GitLab From a4b334e1f65f6ff432ef6215c8b93f83dad17097 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 5 Jan 2018 11:27:07 +0100 Subject: [PATCH 2479/5498] UPSTREAM: ANDROID: binder: remove waitqueue when thread exits. binder_poll() passes the thread->wait waitqueue that can be slept on for work. When a thread that uses epoll explicitly exits using BINDER_THREAD_EXIT, the waitqueue is freed, but it is never removed from the corresponding epoll data structure. When the process subsequently exits, the epoll cleanup code tries to access the waitlist, which results in a use-after-free. Prevent this by using POLLFREE when the thread exits. (cherry picked from commit f5cb779ba16334b45ba8946d6bfa6d9834d1527f) Change-Id: Ib34b1cbb8ab2192d78c3d9956b2f963a66ecad2e Signed-off-by: Martijn Coenen Reported-by: syzbot Cc: stable # 4.14 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index f95b559de0f8..b2e4f493bade 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -4532,6 +4532,18 @@ static int binder_thread_release(struct binder_proc *proc, if (t) spin_lock(&t->lock); } + + /* + * If this thread used poll, make sure we remove the waitqueue + * from any epoll data structures holding it with POLLFREE. + * waitqueue_active() is safe to use here because we're holding + * the inner lock. + */ + if ((thread->looper & BINDER_LOOPER_STATE_POLL) && + waitqueue_active(&thread->wait)) { + wake_up_poll(&thread->wait, POLLHUP | POLLFREE); + } + binder_inner_proc_unlock(thread->proc); if (send_reply) -- GitLab From 3df5abdcd244754fdc42a20744f1905da944aad2 Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Fri, 5 Jan 2018 10:40:28 +0530 Subject: [PATCH 2480/5498] mmc: sdhci: Avoid leaking kernel addresses Use %pK instead of %p in debug logs so that kernel addresses don't get leaked when debug logs are dynamically enabled. Change-Id: I3c0df8a8db6643ab547d8599dd03b54030f76ece Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5ab646ad0486..1cc4a09971e5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2985,12 +2985,12 @@ static void sdhci_show_adma_error(struct sdhci_host *host) if (host->flags & SDHCI_USE_ADMA_64BIT) { __le64 *dma = (__le64 *)(desc + 4); - pr_info("%s: %p: DMA %llx, LEN 0x%04x, Attr=0x%02x\n", + pr_info("%s: %pK: DMA %llx, LEN 0x%04x, Attr=0x%02x\n", name, desc, (long long)le64_to_cpu(*dma), le16_to_cpu(*len), attr); } else { __le32 *dma = (__le32 *)(desc + 4); - pr_info("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", + pr_info("%s: %pK: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr); } -- GitLab From 93f304cd2f66af2016bcf1d30c0945e6de0699fc Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Tue, 6 Feb 2018 16:54:54 -0800 Subject: [PATCH 2481/5498] msm: mhi_dev: Update ring cache logic When MHI (modem host interface) device receives a ring start command do not cache the ring. Cache the ring after a doorbell for the channel is received. This avoid scenario where host has added elements to the transfer ring and updated the write offset as part of start command. Currently device caches the ring to maintain copies of the write and read offset but in the process updates the write offset without calling the client callback indicating there is data. Also fix updating the wrap around condition when caching the ring. Change-Id: Iac0aa9fa97c0075cea1431f7e22e3d916532a37a Signed-off-by: Siddartha Mohanadoss --- drivers/platform/msm/mhi_dev/mhi_ring.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi_ring.c b/drivers/platform/msm/mhi_dev/mhi_ring.c index c4f2803dab83..785d6d00568d 100644 --- a/drivers/platform/msm/mhi_dev/mhi_ring.c +++ b/drivers/platform/msm/mhi_dev/mhi_ring.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -71,9 +71,7 @@ int mhi_dev_fetch_ring_elements(struct mhi_dev_ring *ring, host_addr.device_va = ring->ring_shadow.device_va; host_addr.host_pa = ring->ring_shadow.host_pa; host_addr.virt_addr = &ring->ring_cache[0]; - host_addr.phy_addr = (ring->ring_cache_dma_handle + - sizeof(union mhi_dev_ring_element_type) * - start); + host_addr.phy_addr = ring->ring_cache_dma_handle; host_addr.size = (end * sizeof(union mhi_dev_ring_element_type)); mhi_dev_read_from_host(ring->mhi_dev, &host_addr); @@ -386,7 +384,7 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx, ring->ring_ctx_shadow = ring->ring_ctx; - if (ring->type != RING_TYPE_ER) { + if (ring->type != RING_TYPE_ER || ring->type != RING_TYPE_CH) { rc = mhi_dev_cache_ring(ring, wr_offset); if (rc) return rc; -- GitLab From 028b4e8070832abdce2f85893a7742c77b4f04a1 Mon Sep 17 00:00:00 2001 From: David Dai Date: Mon, 25 Sep 2017 15:16:23 -0700 Subject: [PATCH 2482/5498] dev_freq: devfreq_spdm: add null terminator to prevent OOB access Add null terminator to end of buffered copied from user to prevent over reading. Change-Id: I80cfcb087ea2c335fd65d8fcdaf372c7d34a533d Signed-off-by: David Dai --- drivers/devfreq/devfreq_spdm_debugfs.c | 62 ++++++++++++++++++++------ 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/drivers/devfreq/devfreq_spdm_debugfs.c b/drivers/devfreq/devfreq_spdm_debugfs.c index 94e94f3bbc1c..1d6a581341a5 100644 --- a/drivers/devfreq/devfreq_spdm_debugfs.c +++ b/drivers/devfreq/devfreq_spdm_debugfs.c @@ -34,7 +34,7 @@ static ssize_t enable_write(struct file *file, const char __user *data, int i; int next_idx; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { @@ -42,6 +42,8 @@ static ssize_t enable_write(struct file *file, const char __user *data, size = -EINVAL; } + buf[size] = '\0'; + if (sscanf(buf, "%u\n", &i) != 1) { size = -EINVAL; goto err; @@ -105,7 +107,7 @@ static ssize_t pl_write(struct file *file, const char __user *data, int ext_status = 0; int i; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { @@ -113,6 +115,8 @@ static ssize_t pl_write(struct file *file, const char __user *data, goto out; } + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.pl_freqs[0], &spdm_data->config_data.pl_freqs[1]) != 2) { size = -EINVAL; @@ -164,7 +168,7 @@ static ssize_t rejrate_low_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { @@ -172,6 +176,8 @@ static ssize_t rejrate_low_write(struct file *file, const char __user *data, goto out; } + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.reject_rate[0], &spdm_data->config_data.reject_rate[1]) != 2) { size = -EINVAL; @@ -224,13 +230,16 @@ static ssize_t rejrate_med_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.reject_rate[2], &spdm_data->config_data.reject_rate[3]) != 2) { size = -EINVAL; @@ -282,13 +291,16 @@ static ssize_t rejrate_high_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.reject_rate[4], &spdm_data->config_data.reject_rate[5]) != 2) { size = -EINVAL; @@ -340,13 +352,16 @@ static ssize_t resptime_low_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.response_time_us[0], &spdm_data->config_data.response_time_us[1]) != 2) { size = -EINVAL; @@ -398,13 +413,16 @@ static ssize_t resptime_med_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.response_time_us[2], &spdm_data->config_data.response_time_us[3]) != 2) { size = -EINVAL; @@ -456,13 +474,16 @@ static ssize_t resptime_high_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.response_time_us[4], &spdm_data->config_data.response_time_us[5]) != 2) { size = -EINVAL; @@ -515,13 +536,16 @@ static ssize_t cciresptime_low_write(struct file *file, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.cci_response_time_us[0], &spdm_data->config_data.cci_response_time_us[1]) != 2) { @@ -575,13 +599,16 @@ static ssize_t cciresptime_med_write(struct file *file, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.cci_response_time_us[2], &spdm_data->config_data.cci_response_time_us[3]) != 2) { @@ -635,13 +662,16 @@ static ssize_t cciresptime_high_write(struct file *file, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u\n", &spdm_data->config_data.cci_response_time_us[4], &spdm_data->config_data.cci_response_time_us[5]) != 2){ @@ -694,13 +724,16 @@ static ssize_t cci_max_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u\n", &spdm_data->config_data.max_cci_freq) != 1) { size = -EINVAL; goto out; @@ -748,13 +781,16 @@ static ssize_t vote_cfg_write(struct file *file, const char __user *data, struct spdm_args desc = { { 0 } }; int ext_status = 0; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, data, size)) { size = -EINVAL; goto out; } + + buf[size] = '\0'; + if (sscanf(buf, "%u %u %u %u\n", &spdm_data->config_data.upstep, &spdm_data->config_data.downstep, &spdm_data->config_data.max_vote, -- GitLab From 45e5c645f2c6830094af82d2cf9d2d2641921156 Mon Sep 17 00:00:00 2001 From: Wenjun Zhang Date: Wed, 31 Jan 2018 21:38:31 -0500 Subject: [PATCH 2483/5498] msm: mdss: Add SPI display driver Add Mdp3 and display SPI interface driver to support SPI panel, since limited by SPI rate, the current max fps only reach to 27fps and only support RGB565. Change-Id: Id452b70ed51c1f29aef7b89c1d2f54214c3a7b37 Signed-off-by: Wenjun Zhang --- .../devicetree/bindings/fb/mdss-spi-panel.txt | 203 ++ drivers/video/msm/mdss/Kconfig | 10 + drivers/video/msm/mdss/Makefile | 5 + drivers/video/msm/mdss/dsi_status_v2.c | 79 +- drivers/video/msm/mdss/mdp3.c | 59 +- drivers/video/msm/mdss/mdp3.h | 9 +- drivers/video/msm/mdss/mdp3_ctrl.c | 179 +- drivers/video/msm/mdss/mdp3_ctrl.h | 6 +- drivers/video/msm/mdss/mdp3_dma.c | 32 +- drivers/video/msm/mdss/mdp3_dma.h | 5 +- drivers/video/msm/mdss/mdp3_layer.c | 15 +- drivers/video/msm/mdss/mdp3_ppp.c | 11 +- drivers/video/msm/mdss/mdp3_ppp_data.c | 6 +- drivers/video/msm/mdss/mdss_dsi.c | 11 +- drivers/video/msm/mdss/mdss_dsi_status.c | 18 +- drivers/video/msm/mdss/mdss_fb.c | 22 +- drivers/video/msm/mdss/mdss_panel.h | 21 +- drivers/video/msm/mdss/mdss_spi_panel.c | 1713 +++++++++++++++++ drivers/video/msm/mdss/mdss_spi_panel.h | 148 ++ 19 files changed, 2470 insertions(+), 82 deletions(-) create mode 100644 Documentation/devicetree/bindings/fb/mdss-spi-panel.txt create mode 100644 drivers/video/msm/mdss/mdss_spi_panel.c create mode 100644 drivers/video/msm/mdss/mdss_spi_panel.h diff --git a/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt new file mode 100644 index 000000000000..d46068f01695 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-spi-panel.txt @@ -0,0 +1,203 @@ +Qualcomm Technologies, Inc. mdss-spi-panel + +mdss-spi-panel is a SPI panel device which supports panels that +are compatible with display serial interface specification. + +Required properties: +- qcom,mdss-spi-panel-controller: Specifies the phandle for the SPI controller that + this panel will be mapped to. +- qcom,mdss-spi-panel-width: Specifies panel width in pixels. +- qcom,mdss-spi-panel-height: Specifies panel height in pixels. +- qcom,mdss-spi-bpp: Specifies the panel bits per pixels. + 3 = for rgb111 + 8 = for rgb332 + 12 = for rgb444 + 16 = for rgb565 + 18 = for rgb666 + 24 = for rgb888 +- qcom,mdss-spi-panel-destination: A string that specifies the destination display for the panel. + "display_1" = DISPLAY_1 + "display_2" = DISPLAY_2 +- qcom,mdss-spi-on-command: A byte stream formed by multiple packets + byte 0: wait number of specified ms after command + transmitted + byte 1: 8 bits length in network byte order + byte 3 and beyond: number byte of payload +- qcom,mdss-spi-off-command: A byte stream formed by multiple packets + byte 0: wait number of specified ms after command + transmitted + byte 1: 8 bits length in network byte order + byte 3 and beyond: number byte of payload +Optional properties: +- qcom,mdss-spi-panel-name: A string used as a descriptive name of the panel +- qcom,cont-splash-enabled: Boolean used to enable continuous splash mode. + If this property is specified, it is required to + to specify the memory reserved for the splash + screen using the qcom,memblock-reserve binding + for the framebuffer device attached to the panel. +- qcom,mdss-spi-h-back-porch: Horizontal back porch value in pixels. + 6 = default value. +- qcom,mdss-spi-h-front-porch: Horizontal front porch value in pixels. + 6 = default value. +- qcom,mdss-spi-h-pulse-width: Horizontal pulse width. + 2 = default value. +- qcom,mdss-spi-h-sync-skew: Horizontal sync skew value. + 0 = default value. +- qcom,mdss-spi-v-back-porch: Vertical back porch value in pixels. + 6 = default value. +- qcom,mdss-spi-v-front-porch: Vertical front porch value in pixels. + 6 = default value. +- qcom,mdss-spi-v-pulse-width: Vertical pulse width. + 2 = default value. +- qcom,mdss-spi-bl-pmic-control-type: A string that specifies the implementation of backlight + control for this panel. + "bl_ctrl_pwm" = Backlight controlled by PWM gpio. + "bl_ctrl_wled" = Backlight controlled by WLED. + other: Unknown backlight control. (default) +- qcom,mdss-spi-bl-min-level: Specifies the min backlight level supported by the panel. + 0 = default value. +- qcom,mdss-spi-bl-max-level: Specifies the max backlight level supported by the panel. + 255 = default value. +- qcom,mdss-spi-panel-framerate: Specifies the frame rate for the panel. +- qcom,esd-check-enabled: Boolean used to enable ESD recovery feature. +- qcom,mdss-spi-panel-status-check-mode:Specifies the panel status check method for ESD recovery. + "send_init_command" = send init code to recover panel status. + "reg_read" = Read register value to check the panel status. +- qcom,mdss-spi-panel-status-reg:Unsigned 8bits integer value to specifies the value + of panel status register address. +- qcom,mdss-spi-panel-status-read-length:Unsigned 8bits integer value that specifies + the expected read-back length of the panel register. +- qcom,mdss-spi-panel-status-value:An unsigned 8bits integer araray that specifies the + values of the panel status register which is used to + check the panel status. + The size of this array is specified by + qcom,mdss-dsi-panel-status-read-length. + +Example: +&mdss_mdp { + spi_gc9305_qvga_cmd: qcom,mdss_spi_gc9305_qvga_cmd { + qcom,mdss-spi-panel-name = "gc9305 qvga command mode spi panel"; + qcom,mdss-spi-panel-destination = "display_1"; + qcom,mdss-spi-panel-controller = <&mdss_spi>; + qcom,mdss-spi-panel-framerate = <30>; + qcom,mdss-spi-panel-width = <240>; + qcom,mdss-spi-panel-height = <320>; + qcom,mdss-spi-h-front-porch = <79>; + qcom,mdss-spi-h-back-porch = <59>; + qcom,mdss-spi-h-pulse-width = <60>; + qcom,mdss-spi-v-back-porch = <10>; + qcom,mdss-spi-v-front-porch = <7>; + qcom,mdss-spi-v-pulse-width = <2>; + qcom,mdss-spi-h-left-border = <0>; + qcom,mdss-spi-h-right-border = <0>; + qcom,mdss-spi-v-top-border = <0>; + qcom,mdss-spi-v-bottom-border = <0>; + qcom,mdss-spi-bpp = <16>; + qcom,mdss-spi-on-command = [00 01 FE + 00 01 EF + 00 02 36 48 + 00 02 3A 05 + 00 02 35 00 + 00 03 A4 44 44 + 00 03 A5 42 42 + 00 03 AA 88 88 + 00 03 E8 12 40 + 00 03 E3 01 10 + 00 02 FF 61 + 00 02 AC 00 + 00 03 A6 2A 2A + 00 03 A7 2B 2B + 00 03 A8 18 18 + 00 03 A9 2A 2A + 00 02 AD 33 + 00 02 AF 55 + 00 02 AE 2B + 00 05 2A 00 00 00 EF + 00 05 2B 00 00 01 3F + 00 01 2C + 00 07 F0 02 02 00 08 0C 10 + 00 07 F1 01 00 00 14 1D 0E + 00 07 F2 10 09 37 04 04 48 + 00 07 F3 10 0B 3F 05 05 4E + 00 07 F4 0D 19 17 1D 1E 0F + 00 07 F5 06 12 13 1A 1B 0F + 78 01 11 + 00 01 29 + 00 01 2C]; + qcom,mdss-spi-off-command = [20 01 28 + 20 01 10]; + qcom,mdss-spi-bl-min-level = <1>; + qcom,mdss-spi-bl-max-level = <4095>; + qcom,esd-check-enabled; + qcom,mdss-spi-panel-status-check-mode = "reg_read"; + qcom,mdss-spi-panel-status-reg = /bits/ 8 <0x09>; + qcom,mdss-spi-panel-status-read-length = <4>; + qcom,mdss-spi-panel-status-value = /bits/ 8 <0x52 0x29 0x83 0x00>; + }; +}; + +mdss-spi-display is a spi interface display which support send frame +data and command to panel, compatible with SPI interface specification. + +Required properties: +- compatible: This property applies to SPI panels only. + compatible = "qcom,mdss-spi-display". +- vdd-supply: Phandle for vdd regulator device node. +- vddio-supply: Phandle for vdd-io regulator device node. +- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the interface is mapped. +- qcom,mdss-mdp: pHandle that specifies the mdss-mdp device. +- qcom,panel-supply-entries: A node that lists the elements of the supply used to + power the DSI panel. There can be more than one instance + of this binding, in which case the entry would be appended + with the supply entry index. For a detailed description + fields in the supply entry, refer to the qcom,ctrl-supply-entries + binding above. +- qcom,platform-spi-dc-gpio: Pull down this gpio indicate current package is command, + Pull up this gpio indicate current package is parameter or pixels. + +Optional properties: +- label:A string used to describe the controller used. + -- qcom,supply-name: name of the supply (vdd/vdda/vddio) + -- qcom,supply-min-voltage: minimum voltage level (uV) + -- qcom,supply-max-voltage: maximum voltage level (uV) + -- qcom,supply-enable-load: load drawn (uA) from enabled supply + -- qcom,supply-disable-load: load drawn (uA) from disabled supply + -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on + -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on + -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off + -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off + +Example: + mdss_spi: qcom,mdss_spi { + compatible = "qcom,mdss-spi-display"; + label = "mdss spi panel"; + + qcom,mdss-fb-map = <&mdss_fb0>; + qcom,mdss-mdp = <&mdss_mdp>; + vdd-supply = <&pm8909_l17>; + vddio-supply = <&pm8909_l6>; + qcom,platform-spi-dc-gpio = <&msm_gpio 110 0>; + + qcom,panel-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdd"; + qcom,supply-min-voltage = <2850000>; + qcom,supply-max-voltage = <2850000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + }; + }; diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig index 9afe05cc4b4d..b44655e59ade 100644 --- a/drivers/video/msm/mdss/Kconfig +++ b/drivers/video/msm/mdss/Kconfig @@ -21,6 +21,16 @@ config FB_MSM_MDSS_HDMI_MHL_SII8334 MHL (Mobile High-Definition Link) technology uses USB connector to output HDMI content +config FB_MSM_MDSS_SPI_PANEL + depends on FB_MSM_MDSS + bool "Support SPI panel feature" + default n + ---help--- + The MDSS SPI Panel provides support for transmittimg SPI signals of + MDSS frame buffer data to connected panel. Limited by SPI rate, the + current max fps only reach to 27 fps, and limited by MDP hardware + architecture only supply on MDP3 + config FB_MSM_MDSS_MHL3 depends on FB_MSM_MDSS_HDMI_PANEL bool "MHL3 SII8620 Support" diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile index ed3ff87458e5..46efd01e3239 100644 --- a/drivers/video/msm/mdss/Makefile +++ b/drivers/video/msm/mdss/Makefile @@ -43,6 +43,11 @@ mdss-dsi-objs += mdss_dsi_clk.o obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o +ifeq ($(CONFIG_SPI_QUP), y) +obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_client.o +obj-$(CONFIG_FB_MSM_MDSS_SPI_PANEL) += mdss_spi_panel.o +endif + ifneq ($(CONFIG_FB_MSM_MDSS_MDP3), y) obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o diff --git a/drivers/video/msm/mdss/dsi_status_v2.c b/drivers/video/msm/mdss/dsi_status_v2.c index b9662414f565..20a02ef74a5c 100644 --- a/drivers/video/msm/mdss/dsi_status_v2.c +++ b/drivers/video/msm/mdss/dsi_status_v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,6 +18,7 @@ #include "mdss_dsi.h" #include "mdp3_ctrl.h" +#include "mdss_spi_panel.h" /* * mdp3_check_te_status() - Check the status of panel for TE based ESD. @@ -165,3 +166,79 @@ status_dead: mdss_fb_report_panel_dead(pdsi_status->mfd); } +#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) +void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval) +{ + struct dsi_status_data *pdsi_status = NULL; + struct mdss_panel_data *pdata = NULL; + struct spi_panel_data *ctrl_pdata = NULL; + struct mdp3_session_data *mdp3_session = NULL; + int ret = 0; + + pdsi_status = container_of(to_delayed_work(work), + struct dsi_status_data, check_status); + + if (!pdsi_status || !(pdsi_status->mfd)) { + pr_err("%s: mfd not available\n", __func__); + return; + } + + pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev); + if (!pdata) { + pr_err("%s: panel data not available\n", __func__); + return; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, panel_data); + if (!ctrl_pdata || !ctrl_pdata->check_status) { + pr_err("%s: Dsi ctrl or status_check callback not available\n", + __func__); + return; + } + + mdp3_session = pdsi_status->mfd->mdp.private1; + if (!mdp3_session) { + pr_err("%s: Display is off\n", __func__); + return; + } + + if (mdp3_session->in_splash_screen) { + schedule_delayed_work(&pdsi_status->check_status, + msecs_to_jiffies(interval)); + pr_debug("%s: cont splash is on\n", __func__); + return; + } + + mutex_lock(&mdp3_session->lock); + if (!mdp3_session->status) { + pr_debug("%s: display off already\n", __func__); + mutex_unlock(&mdp3_session->lock); + return; + } + + if (!ret) + ret = ctrl_pdata->check_status(ctrl_pdata); + else + pr_err("%s:wait_for_dma_done error\n", __func__); + mutex_unlock(&mdp3_session->lock); + + if (mdss_fb_is_power_on_interactive(pdsi_status->mfd)) { + if (ret > 0) { + schedule_delayed_work(&pdsi_status->check_status, + msecs_to_jiffies(interval)); + } else { + char *envp[2] = {"PANEL_ALIVE=0", NULL}; + + pdata->panel_info.panel_dead = true; + ret = kobject_uevent_env( + &pdsi_status->mfd->fbi->dev->kobj, KOBJ_CHANGE, envp); + pr_err("%s:panel has gone bad, sending uevent - %s\n", + __func__, envp[0]); + } + } +} +#else +void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval) +{ +} +#endif diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index 7fbe90e07baf..c50f8a794015 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -57,6 +57,7 @@ #include "mdss_debug.h" #include "mdss_smmu.h" #include "mdss.h" +#include "mdss_spi_panel.h" #ifndef EXPORT_COMPAT #define EXPORT_COMPAT(x) @@ -110,6 +111,7 @@ struct mdp3_bus_handle_map mdp3_bus_handle[MDP3_BUS_HANDLE_MAX] = { static struct mdss_panel_intf pan_types[] = { {"dsi", MDSS_PANEL_INTF_DSI}, + {"spi", MDSS_PANEL_INTF_SPI}, }; static char mdss_mdp3_panel[MDSS_MAX_PANEL_LEN]; @@ -153,6 +155,13 @@ struct mdp3_iommu_ctx_map mdp3_iommu_contexts[MDP3_IOMMU_CTX_MAX] = { }, }; +#ifndef CONFIG_FB_MSM_MDSS_SPI_PANEL +void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata, u32 bl_level) +{ + +} +#endif + static irqreturn_t mdp3_irq_handler(int irq, void *ptr) { int i = 0; @@ -1838,7 +1847,11 @@ int mdp3_put_img(struct mdp3_img_data *data, int client) client == MDP3_CLIENT_DMA_P) mdss_smmu_unmap_dma_buf(data->tab_clone, dom, dir, data->srcp_dma_buf); - else + else if (client == MDP3_CLIENT_SPI) { + ion_unmap_kernel(iclient, data->srcp_ihdl); + ion_free(iclient, data->srcp_ihdl); + data->srcp_ihdl = NULL; + } else mdss_smmu_unmap_dma_buf(data->srcp_table, dom, dir, data->srcp_dma_buf); data->mapped = false; @@ -1897,13 +1910,21 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client) if (!ret) goto done; } else if (iclient) { - data->srcp_dma_buf = dma_buf_get(img->memory_id); + data->srcp_dma_buf = dma_buf_get(img->memory_id); if (IS_ERR(data->srcp_dma_buf)) { pr_err("DMA : error on ion_import_fd\n"); ret = PTR_ERR(data->srcp_dma_buf); data->srcp_dma_buf = NULL; return ret; } + data->srcp_ihdl = ion_import_dma_buf(iclient, + img->memory_id); + if (IS_ERR_OR_NULL(data->srcp_ihdl)) { + pr_err("error on ion_import_fd\n"); + data->srcp_ihdl = NULL; + return -EIO; + } + data->srcp_attachment = mdss_smmu_dma_buf_attach(data->srcp_dma_buf, @@ -1937,6 +1958,25 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client) data->tab_clone, dom, &data->addr, &data->len, DMA_BIDIRECTIONAL); + } else if (client == MDP3_CLIENT_SPI) { + void *vaddr; + + if (ion_handle_get_size(iclient, + data->srcp_ihdl, + (size_t *)&data->len) < 0) { + pr_err("get size failed\n"); + return -EINVAL; + } + vaddr = ion_map_kernel(iclient, + data->srcp_ihdl); + if (IS_ERR_OR_NULL(vaddr)) { + pr_err("Mapping failed\n"); + mdp3_put_img(data, client); + return -EINVAL; + } + data->addr = (dma_addr_t) vaddr; + data->len -= img->offset; + return 0; } else { ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table, dom, &data->addr, @@ -2253,6 +2293,8 @@ static int mdp3_is_display_on(struct mdss_panel_data *pdata) if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { status = MDP3_REG_READ(MDP3_REG_DSI_VIDEO_EN); rc = status & 0x1; + } else if (pdata->panel_info.type == SPI_PANEL) { + rc = is_spi_panel_continuous_splash_on(pdata); } else { status = MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG); status &= 0x180000; @@ -2290,7 +2332,11 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, mdp_clk_rate, MDP3_CLIENT_DMA_P); - rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib); + /*DMA not used on SPI interface, remove DMA bus voting*/ + if (panel_info->type == SPI_PANEL) + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0); + else + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib); bus_handle->restore_ab[MDP3_CLIENT_DMA_P] = ab; bus_handle->restore_ib[MDP3_CLIENT_DMA_P] = ib; @@ -2308,6 +2354,8 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) if (panel_info->type == MIPI_VIDEO_PANEL) mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_VIDEO].active = 1; + else if (panel_info->type == SPI_PANEL) + mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_SPI_CMD].active = 1; else mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_CMD].active = 1; @@ -2963,6 +3011,9 @@ static int mdp3_probe(struct platform_device *pdev) mdp3_res->mdss_util->mdp_probe_done = true; pr_debug("%s: END\n", __func__); + if (mdp3_res->pan_cfg.pan_intf == MDSS_PANEL_INTF_SPI) + mdp3_interface.check_dsi_status = mdp3_check_spi_panel_status; + probe_done: if (IS_ERR_VALUE(rc)) kfree(mdp3_res->mdp3_hw.irq_info); diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h index c22441691991..2655631b36bb 100644 --- a/drivers/video/msm/mdss/mdp3.h +++ b/drivers/video/msm/mdss/mdp3.h @@ -1,4 +1,5 @@ -/* Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2016-2018, The Linux Foundation. All rights reserved. + * * Copyright (C) 2007 Google Incorporated * * This program is free software; you can redistribute it and/or modify @@ -85,6 +86,7 @@ enum { MDP3_CLIENT_DSI = 1, MDP3_CLIENT_PPP, MDP3_CLIENT_IOMMU, + MDP3_CLIENT_SPI, MDP3_CLIENT_MAX, }; @@ -210,6 +212,8 @@ struct mdp3_hw_resource { bool solid_fill_vote_en; struct list_head reg_bus_clist; struct mutex reg_bus_lock; + int bklt_level; + int bklt_update; u32 max_bw; @@ -269,6 +273,7 @@ int mdp3_misr_get(struct mdp_misr *misr_resp); void mdp3_enable_regulator(int enable); void mdp3_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval); +void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval); int mdp3_dynamic_clock_gating_ctrl(int enable); int mdp3_footswitch_ctrl(int enable); int mdp3_qos_remapper_setup(struct mdss_panel_data *panel); @@ -281,6 +286,8 @@ void mdp3_calc_dma_res(struct mdss_panel_info *panel_info, u64 *clk_rate, void mdp3_clear_irq(u32 interrupt_mask); int mdp3_enable_panic_ctrl(void); +void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata, u32 bl_level); + int mdp3_layer_pre_commit(struct msm_fb_data_type *mfd, struct file *file, struct mdp_layer_commit_v1 *commit); int mdp3_layer_atomic_validate(struct msm_fb_data_type *mfd, diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index 7177c694c3f9..0b11b3335d94 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,11 +24,13 @@ #include #include #include +#include #include "mdp3_ctrl.h" #include "mdp3.h" #include "mdp3_ppp.h" #include "mdss_smmu.h" +#include "mdss_spi_panel.h" #define VSYNC_EXPIRE_TICK 4 @@ -72,7 +74,7 @@ static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq) bufq->pop_idx = 0; } -void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq) +void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq, int client) { int count = bufq->count; @@ -82,7 +84,7 @@ void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq) while (count-- && (bufq->pop_idx >= 0)) { struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx]; bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE; - mdp3_put_img(data, MDP3_CLIENT_DMA_P); + mdp3_put_img(data, client); } bufq->count = 0; bufq->push_idx = 0; @@ -120,6 +122,18 @@ static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq) return bufq->count; } +int mdp3_get_ion_client(struct msm_fb_data_type *mfd) +{ + int intf_type; + + intf_type = mdp3_ctrl_get_intf_type(mfd); + + if (intf_type == MDP3_DMA_OUTPUT_SEL_SPI_CMD) + return MDP3_CLIENT_SPI; + else + return MDP3_CLIENT_DMA_P; +} + void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses, struct notifier_block *notifier) { @@ -284,7 +298,8 @@ void vsync_count_down(void *arg) void mdp3_ctrl_reset_countdown(struct mdp3_session_data *session, struct msm_fb_data_type *mfd) { - if (mdp3_ctrl_get_intf_type(mfd) == MDP3_DMA_OUTPUT_SEL_DSI_CMD) + if (mdp3_ctrl_get_intf_type(mfd) == MDP3_DMA_OUTPUT_SEL_DSI_CMD || + mdp3_ctrl_get_intf_type(mfd) == MDP3_DMA_OUTPUT_SEL_SPI_CMD) atomic_set(&session->vsync_countdown, VSYNC_EXPIRE_TICK); } @@ -294,8 +309,11 @@ static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable) struct mdp3_notification vsync_client; struct mdp3_notification *arg = NULL; bool mod_vsync_timer = false; + int intf_type; pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable); + + intf_type = mdp3_ctrl_get_intf_type(mfd); mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma || !mdp3_session->intf) @@ -333,18 +351,25 @@ static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable) } } - mdp3_clk_enable(1, 0); - mdp3_session->dma->vsync_enable(mdp3_session->dma, arg); - mdp3_clk_enable(0, 0); + if (intf_type == MDP3_DMA_OUTPUT_SEL_SPI_CMD) { + mdp3_spi_vsync_enable(mdp3_session->panel, arg); + } else { + mdp3_clk_enable(1, 0); + mdp3_session->dma->vsync_enable(mdp3_session->dma, arg); + mdp3_clk_enable(0, 0); + } /* * Need to fake vsync whenever dsi interface is not * active or when dsi clocks are currently off */ - if (mod_vsync_timer) { + if (mod_vsync_timer && (intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)) { mod_timer(&mdp3_session->vsync_timer, jiffies + msecs_to_jiffies(mdp3_session->vsync_period)); - } else if (!enable) { + } else if (enable && !mdp3_session->clk_on) { + mdp3_ctrl_reset_countdown(mdp3_session, mfd); + mdp3_ctrl_clk_enable(mfd, 1); + } else if (!enable && (intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD)) { del_timer(&mdp3_session->vsync_timer); } @@ -582,16 +607,34 @@ static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable) static int mdp3_ctrl_res_req_bus(struct msm_fb_data_type *mfd, int status) { int rc = 0; + u32 vtotal = 0; + int frame_rate = DEFAULT_FRAME_RATE; if (status) { + struct mdss_panel_info *panel_info = mfd->panel_info; u64 ab = 0; u64 ib = 0; + + frame_rate = mdss_panel_get_framerate(mfd->panel_info, + FPS_RESOLUTION_HZ); mdp3_calc_dma_res(mfd->panel_info, NULL, &ab, &ib, ppp_bpp(mfd->fb_imgType)); - rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib); - } else { + vtotal = panel_info->yres + panel_info->lcdc.v_back_porch + + panel_info->lcdc.v_front_porch + + panel_info->lcdc.v_pulse_width; + ab = panel_info->xres * vtotal * ppp_bpp(mfd->fb_imgType); + ab *= frame_rate; + ib = ab; + + /*DMA not used on SPI interface, remove DMA bus voting*/ + if (mdp3_ctrl_get_intf_type(mfd) == + MDP3_DMA_OUTPUT_SEL_SPI_CMD) + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0); + else + rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, + ab, ib); + } else rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0); - } return rc; } @@ -635,6 +678,9 @@ static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd) case LCDC_PANEL: type = MDP3_DMA_OUTPUT_SEL_LCDC; break; + case SPI_PANEL: + type = MDP3_DMA_OUTPUT_SEL_SPI_CMD; + break; default: type = MDP3_DMA_OUTPUT_SEL_MAX; } @@ -694,7 +740,8 @@ static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd, cfg.type = mdp3_ctrl_get_intf_type(mfd); if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO || - cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC) { + cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC || + cfg.type == MDP3_DMA_OUTPUT_SEL_SPI_CMD) { video->hsync_period = hsync_period; video->hsync_pulse_width = h_pulse_width; video->vsync_period = vsync_period; @@ -742,7 +789,7 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, struct fb_var_screeninfo *var; struct mdp3_dma_output_config outputConfig; struct mdp3_dma_source sourceConfig; - int frame_rate = mfd->panel_info->mipi.frame_rate; + int frame_rate = DEFAULT_FRAME_RATE; int vbp, vfp, vspw; int vtotal, vporch; struct mdp3_notification dma_done_callback; @@ -751,6 +798,7 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; + frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ); vbp = panel_info->lcdc.v_back_porch; vfp = panel_info->lcdc.v_front_porch; vspw = panel_info->lcdc.v_pulse_width; @@ -791,7 +839,7 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, sourceConfig.stride = fix->line_length; } - te.frame_rate = panel_info->mipi.frame_rate; + te.frame_rate = frame_rate; te.hw_vsync_mode = panel_info->mipi.hw_vsync_mode; te.tear_check_en = panel_info->te.tear_check_en; te.sync_cfg_height = panel_info->te.sync_cfg_height; @@ -975,6 +1023,7 @@ end: static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) { int rc = 0; + int client = 0; bool intf_stopped = true; struct mdp3_session_data *mdp3_session; struct mdss_panel_data *panel; @@ -1105,10 +1154,11 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) pr_err("%s: pm_runtime_put failed (rc %d)\n", __func__, rc); } - mdp3_bufq_deinit(&mdp3_session->bufq_out); + client = mdp3_get_ion_client(mfd); + mdp3_bufq_deinit(&mdp3_session->bufq_out, client); if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) { mdp3_session->overlay.id = MSMFB_NEW_REQUEST; - mdp3_bufq_deinit(&mdp3_session->bufq_in); + mdp3_bufq_deinit(&mdp3_session->bufq_in, client); } } @@ -1138,6 +1188,10 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) mdp3_ctrl_clk_enable(mdp3_session->mfd, 0); } off_error: + if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) { + mdp3_session->overlay.id = MSMFB_NEW_REQUEST; + mdp3_bufq_deinit(&mdp3_session->bufq_in, client); + } MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__); mutex_unlock(&mdp3_session->lock); /* Release the last reference to the runtime device */ @@ -1285,14 +1339,16 @@ static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx) struct fb_info *fbi = mfd->fbi; struct fb_fix_screeninfo *fix; int format; + int client; fix = &fbi->fix; format = mdp3_ctrl_get_source_format(mfd->fb_imgType); mutex_lock(&mdp3_session->lock); + client = mdp3_get_ion_client(mfd); if (mdp3_session->overlay.id == ndx && ndx == 1) { mdp3_session->overlay.id = MSMFB_NEW_REQUEST; - mdp3_bufq_deinit(&mdp3_session->bufq_in); + mdp3_bufq_deinit(&mdp3_session->bufq_in, client); } else { rc = -EINVAL; } @@ -1311,18 +1367,20 @@ static int mdp3_overlay_queue_buffer(struct msm_fb_data_type *mfd, struct msmfb_data *img = &req->data; struct mdp3_img_data data; struct mdp3_dma *dma = mdp3_session->dma; + int client; + client = mdp3_get_ion_client(mfd); memset(&data, 0, sizeof(struct mdp3_img_data)); - if (mfd->panel.type == MIPI_CMD_PANEL) + if (mfd->panel.type == MIPI_CMD_PANEL || client == MDP3_CLIENT_SPI) is_panel_type_cmd = true; if (is_panel_type_cmd) { - rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); + rc = mdp3_iommu_enable(client); if (rc) { pr_err("fail to enable iommu\n"); return rc; } } - rc = mdp3_get_img(img, &data, MDP3_CLIENT_DMA_P); + rc = mdp3_get_img(img, &data, client); if (rc) { pr_err("fail to get overlay buffer\n"); goto err; @@ -1332,14 +1390,14 @@ static int mdp3_overlay_queue_buffer(struct msm_fb_data_type *mfd, pr_err("buf size(0x%lx) is smaller than dma config(0x%x)\n", data.len, (dma->source_config.stride * dma->source_config.height)); - mdp3_put_img(&data, MDP3_CLIENT_DMA_P); + mdp3_put_img(&data, client); rc = -EINVAL; goto err; } rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data); if (rc) { pr_err("fail to queue the overlay buffer, buffer drop\n"); - mdp3_put_img(&data, MDP3_CLIENT_DMA_P); + mdp3_put_img(&data, client); goto err; } rc = 0; @@ -1398,8 +1456,12 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, struct mdp3_img_data *data; struct mdss_panel_info *panel_info; int rc = 0; + int client; static bool splash_done; struct mdss_panel_data *panel; + int frame_rate = DEFAULT_FRAME_RATE; + int stride; + if (!mfd || !mfd->mdp.private1) return -EINVAL; @@ -1409,6 +1471,9 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, if (!mdp3_session || !mdp3_session->dma) return -EINVAL; + frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ); + client = mdp3_get_ion_client(mfd); + if (mdp3_bufq_count(&mdp3_session->bufq_in) == 0) { pr_debug("no buffer in queue yet\n"); return -EPERM; @@ -1457,7 +1522,17 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, if (data) { mdp3_ctrl_reset_countdown(mdp3_session, mfd); mdp3_ctrl_clk_enable(mfd, 1); - if (mdp3_session->dma->update_src_cfg && + stride = mdp3_session->dma->source_config.stride; + if (mdp3_ctrl_get_intf_type(mfd) == + MDP3_DMA_OUTPUT_SEL_SPI_CMD){ + mdp3_session->intf->active = false; + msm_ion_do_cache_op(mdp3_res->ion_client, + data->srcp_ihdl, (void *)(int)data->addr, + data->len, ION_IOC_INV_CACHES); + rc = mdss_spi_panel_kickoff(mdp3_session->panel, + (void *)(int)data->addr, (int)data->len, + stride); + } else if (mdp3_session->dma->update_src_cfg && panel_info->partial_update_enabled) { panel->panel_info.roi.x = mdp3_session->dma->roi.x; panel->panel_info.roi.y = mdp3_session->dma->roi.y; @@ -1477,7 +1552,9 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, MDP_NOTIFY_FRAME_TIMEOUT); } else { if (mdp3_ctrl_get_intf_type(mfd) == - MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) { + MDP3_DMA_OUTPUT_SEL_DSI_VIDEO || + mdp3_ctrl_get_intf_type(mfd) == + MDP3_DMA_OUTPUT_SEL_SPI_CMD) { mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_DONE); } @@ -1492,16 +1569,16 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, mdp3_release_splash_memory(mfd); data = mdp3_bufq_pop(&mdp3_session->bufq_out); if (data) - mdp3_put_img(data, MDP3_CLIENT_DMA_P); + mdp3_put_img(data, client); } if (mdp3_session->first_commit) { /*wait to ensure frame is sent to panel*/ if (panel_info->mipi.post_init_delay) - msleep(((1000 / panel_info->mipi.frame_rate) + 1) * + msleep(((1000 / frame_rate) + 1) * panel_info->mipi.post_init_delay); else - msleep(1000 / panel_info->mipi.frame_rate); + msleep((1000 / frame_rate) + 1); mdp3_session->first_commit = false; if (panel) rc |= panel->event_handler(panel, @@ -1516,6 +1593,12 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, mdp3_session->esd_recovery = false; } + /*Update backlight only if its changed*/ + if (mdp3_res->bklt_level && mdp3_res->bklt_update) { + mdss_spi_panel_bl_ctrl_update(panel, mdp3_res->bklt_level); + mdp3_res->bklt_update = false; + } + /* start vsync tick countdown for cmd mode if vsync isn't enabled */ if (mfd->panel.type == MIPI_CMD_PANEL && !mdp3_session->vsync_enabled) mdp3_ctrl_vsync_enable(mdp3_session->mfd, 0); @@ -1563,7 +1646,6 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) struct mdss_panel_info *panel_info; static bool splash_done; struct mdss_panel_data *panel; - int rc; pr_debug("mdp3_ctrl_pan_display\n"); @@ -1642,17 +1724,18 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) } panel = mdp3_session->panel; - if (mdp3_session->first_commit) { - /*wait to ensure frame is sent to panel*/ - if (panel_info->mipi.init_delay) - msleep(((1000 / panel_info->mipi.frame_rate) + 1) * - panel_info->mipi.init_delay); - else - msleep(1000 / panel_info->mipi.frame_rate); - mdp3_session->first_commit = false; - if (panel) - panel->event_handler(panel, MDSS_EVENT_POST_PANEL_ON, - NULL); + if (mdp3_ctrl_get_intf_type(mfd) != MDP3_DMA_OUTPUT_SEL_SPI_CMD) { + if (mdp3_session->first_commit) { + if (panel_info->mipi.init_delay) + msleep(((1000 / panel_info->mipi.frame_rate) + + 1) * panel_info->mipi.init_delay); + else + msleep(1000 / panel_info->mipi.frame_rate); + mdp3_session->first_commit = false; + if (panel) + panel->event_handler(panel, + MDSS_EVENT_POST_PANEL_ON, NULL); + } } mdp3_session->vsync_before_commit = 0; @@ -1663,6 +1746,11 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) mdp3_session->esd_recovery = false; } + /*Update backlight only if its changed*/ + if (mdp3_res->bklt_level && mdp3_res->bklt_update) { + mdss_spi_panel_bl_ctrl_update(panel, mdp3_res->bklt_level); + mdp3_res->bklt_update = false; + } pan_error: mutex_unlock(&mdp3_session->lock); @@ -1701,7 +1789,8 @@ static int mdp3_get_metadata(struct msm_fb_data_type *mfd, switch (metadata->op) { case metadata_op_frame_rate: metadata->data.panel_frame_rate = - mfd->panel_info->mipi.frame_rate; + mdss_panel_get_framerate(mfd->panel_info, + FPS_RESOLUTION_HZ); break; case metadata_op_get_caps: metadata->data.caps.mdp_rev = 305; @@ -2831,6 +2920,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) struct msm_mdp_interface *mdp3_interface = &mfd->mdp; struct mdp3_session_data *mdp3_session = NULL; u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO; + int frame_rate = DEFAULT_FRAME_RATE; int rc; int splash_mismatch = 0; struct sched_param sched = { .sched_priority = 16 }; @@ -2840,6 +2930,8 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) if (rc) splash_mismatch = 1; + frame_rate = mdss_panel_get_framerate(mfd->panel_info, + FPS_RESOLUTION_HZ); mdp3_interface->on_fnc = mdp3_ctrl_on; mdp3_interface->off_fnc = mdp3_ctrl_off; mdp3_interface->do_histogram = NULL; @@ -2916,10 +3008,11 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) init_timer(&mdp3_session->vsync_timer); mdp3_session->vsync_timer.function = mdp3_vsync_timer_func; mdp3_session->vsync_timer.data = (u32)mdp3_session; - mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate; + mdp3_session->vsync_period = 1000 / frame_rate; mfd->mdp.private1 = mdp3_session; init_completion(&mdp3_session->dma_completion); - if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) + if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO || + intf_type != MDP3_DMA_OUTPUT_SEL_SPI_CMD) mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done; rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group); diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h index a3c806a44985..38903d2fba25 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.h +++ b/drivers/video/msm/mdss/mdp3_ctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -84,12 +84,14 @@ struct mdp3_session_data { struct work_struct retire_work; }; -void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq); +void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq, int client); int mdp3_ctrl_init(struct msm_fb_data_type *mfd); int mdp3_bufq_push(struct mdp3_buffer_queue *bufq, struct mdp3_img_data *data); int mdp3_ctrl_get_source_format(u32 imgType); int mdp3_ctrl_get_pack_pattern(u32 imgType); int mdp3_ctrl_reset(struct msm_fb_data_type *mfd); +int mdp3_get_ion_client(struct msm_fb_data_type *mfd); + #endif /* MDP3_CTRL_H */ diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c index 50392228fcea..f5e4eae5b01e 100644 --- a/drivers/video/msm/mdss/mdp3_dma.c +++ b/drivers/video/msm/mdss/mdp3_dma.c @@ -1,5 +1,5 @@ -/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved. - * Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2016, 2018, The Linux Foundation. All rights reserved. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. @@ -115,7 +115,8 @@ void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type) } if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO || - dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) { + dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC || + dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_SPI_CMD) { if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC) mdp3_irq_enable(MDP3_INTR_LCDC_START_OF_FRAME); } else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { @@ -199,7 +200,8 @@ static int mdp3_dma_callback_setup(struct mdp3_dma *dma) rc = mdp3_set_intr_callback(MDP3_INTR_DMA_P_HISTO, &hist_cb); if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO || - dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) + dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC || + dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_SPI_CMD) rc |= mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME, &vsync_cb); else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { @@ -1263,6 +1265,22 @@ int dsi_cmd_stop(struct mdp3_intf *intf) return 0; } +static int spi_cmd_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg) +{ + return 0; +} + +static int spi_cmd_start(struct mdp3_intf *intf) +{ + intf->active = true; + return 0; +} + +static int spi_cmd_stop(struct mdp3_intf *intf) +{ + intf->active = false; + return 0; +} int mdp3_intf_init(struct mdp3_intf *intf) { switch (intf->cfg.type) { @@ -1281,7 +1299,11 @@ int mdp3_intf_init(struct mdp3_intf *intf) intf->start = dsi_cmd_start; intf->stop = dsi_cmd_stop; break; - + case MDP3_DMA_OUTPUT_SEL_SPI_CMD: + intf->config = spi_cmd_config; + intf->start = spi_cmd_start; + intf->stop = spi_cmd_stop; + break; default: return -EINVAL; } diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h index 36a3dba9f4c4..3fba72ed45bd 100644 --- a/drivers/video/msm/mdss/mdp3_dma.h +++ b/drivers/video/msm/mdss/mdp3_dma.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved. - * Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2016, 2018, The Linux Foundation. All rights reserved. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. @@ -48,6 +48,7 @@ enum { MDP3_DMA_OUTPUT_SEL_DSI_CMD, MDP3_DMA_OUTPUT_SEL_LCDC, MDP3_DMA_OUTPUT_SEL_DSI_VIDEO, + MDP3_DMA_OUTPUT_SEL_SPI_CMD, MDP3_DMA_OUTPUT_SEL_MAX }; diff --git a/drivers/video/msm/mdss/mdp3_layer.c b/drivers/video/msm/mdss/mdp3_layer.c index 81472780d17c..f2b420bf5e26 100644 --- a/drivers/video/msm/mdss/mdp3_layer.c +++ b/drivers/video/msm/mdss/mdp3_layer.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -194,6 +194,7 @@ static int __mdp3_map_layer_buffer(struct msm_fb_data_type *mfd, struct msmfb_data img; bool is_panel_type_cmd = false; struct mdp3_img_data data; + int intf_type; int rc = 0; layer = &input_layer[0]; @@ -205,23 +206,24 @@ static int __mdp3_map_layer_buffer(struct msm_fb_data_type *mfd, goto err; } + intf_type = mdp3_get_ion_client(mfd); memset(&img, 0, sizeof(img)); img.memory_id = buffer->planes[0].fd; img.offset = buffer->planes[0].offset; memset(&data, 0, sizeof(struct mdp3_img_data)); - if (mfd->panel.type == MIPI_CMD_PANEL) + if (mfd->panel.type == MIPI_CMD_PANEL || intf_type == MDP3_CLIENT_SPI) is_panel_type_cmd = true; if (is_panel_type_cmd) { - rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); + rc = mdp3_iommu_enable(intf_type); if (rc) { pr_err("fail to enable iommu\n"); return rc; } } - rc = mdp3_get_img(&img, &data, MDP3_CLIENT_DMA_P); + rc = mdp3_get_img(&img, &data, intf_type); if (rc) { pr_err("fail to get overlay buffer\n"); goto err; @@ -257,7 +259,7 @@ int mdp3_layer_pre_commit(struct msm_fb_data_type *mfd, struct mdp3_session_data *mdp3_session; struct mdp3_dma *dma; int layer_count = commit->input_layer_cnt; - int stride, format; + int stride, format, client; /* Handle NULL commit */ if (!layer_count) { @@ -270,7 +272,8 @@ int mdp3_layer_pre_commit(struct msm_fb_data_type *mfd, mutex_lock(&mdp3_session->lock); - mdp3_bufq_deinit(&mdp3_session->bufq_in); + client = mdp3_get_ion_client(mfd); + mdp3_bufq_deinit(&mdp3_session->bufq_in, client); layer_list = commit->input_layers; layer = &layer_list[0]; diff --git a/drivers/video/msm/mdss/mdp3_ppp.c b/drivers/video/msm/mdss/mdp3_ppp.c index b45f72f6a711..138730550dae 100644 --- a/drivers/video/msm/mdss/mdp3_ppp.c +++ b/drivers/video/msm/mdss/mdp3_ppp.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2007, 2013-2014, 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2007, 2013-2014, 2016-2018, The Linux Foundation. All rights reserved. + * * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -533,6 +534,7 @@ int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd, { struct mdss_panel_info *panel_info = mfd->panel_info; int i, lcount = 0; + int frame_rate = DEFAULT_FRAME_RATE; struct mdp_blit_req *req; struct bpp_info bpp; u64 old_solid_fill_pixel = 0; @@ -547,6 +549,7 @@ int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd, ATRACE_BEGIN(__func__); lcount = lreq->count; + frame_rate = mdss_panel_get_framerate(panel_info, FPS_RESOLUTION_HZ); if (lcount == 0) { pr_err("Blit with request count 0, continue to recover!!!\n"); ATRACE_END(__func__); @@ -574,11 +577,11 @@ int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd, is_blit_optimization_possible(lreq, i); req = &(lreq->req_list[i]); - if (req->fps > 0 && req->fps <= panel_info->mipi.frame_rate) { + if (req->fps > 0 && req->fps <= frame_rate) { if (fps == 0) fps = req->fps; else - fps = panel_info->mipi.frame_rate; + fps = frame_rate; } mdp3_get_bpp_info(req->src.format, &bpp); @@ -636,7 +639,7 @@ int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd, } if (fps == 0) - fps = panel_info->mipi.frame_rate; + fps = frame_rate; if (lreq->req_list[0].flags & MDP_SOLID_FILL) { honest_ppp_ab = ppp_res.solid_fill_byte * 4; diff --git a/drivers/video/msm/mdss/mdp3_ppp_data.c b/drivers/video/msm/mdss/mdp3_ppp_data.c index 88b186a7133c..5ef4baacb666 100644 --- a/drivers/video/msm/mdss/mdp3_ppp_data.c +++ b/drivers/video/msm/mdss/mdp3_ppp_data.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2012-2013 The Linux Foundation. All rights reserved. +/* Copyright (c) 2007, 2012-2013, 2018 The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -89,8 +89,8 @@ const uint32_t pack_patt_lut[MDP_IMGTYPE_LIMIT] = { }; const uint32_t swapped_pack_patt_lut[MDP_IMGTYPE_LIMIT] = { - [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), - [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), + [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), + [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), [MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), [MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), [MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c index 613ffd3fb41d..c65a037b5b3e 100644 --- a/drivers/video/msm/mdss/mdss_dsi.c +++ b/drivers/video/msm/mdss/mdss_dsi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -3178,6 +3178,7 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) struct mdss_util_intf *util; static int te_irq_registered; struct mdss_panel_data *pdata; + struct mdss_panel_cfg *pan_cfg = NULL; if (!pdev || !pdev->dev.of_node) { pr_err("%s: pdev not found for DSI controller\n", __func__); @@ -3210,6 +3211,14 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) return -ENODEV; } + pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_SPI); + if (IS_ERR(pan_cfg)) { + return PTR_ERR(pan_cfg); + } else if (pan_cfg) { + pr_debug("%s: SPI is primary\n", __func__); + return -ENODEV; + } + ctrl_pdata->mdss_util = util; atomic_set(&ctrl_pdata->te_irq_ready, 0); diff --git a/drivers/video/msm/mdss/mdss_dsi_status.c b/drivers/video/msm/mdss/mdss_dsi_status.c index fcae274c83ff..853c70a5e3e0 100644 --- a/drivers/video/msm/mdss/mdss_dsi_status.c +++ b/drivers/video/msm/mdss/mdss_dsi_status.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -144,14 +144,18 @@ static int fb_event_callback(struct notifier_block *self, return NOTIFY_DONE; mfd = evdata->info->par; - ctrl_pdata = container_of(dev_get_platdata(&mfd->pdev->dev), + if (mfd->panel_info->type == SPI_PANEL) { + pinfo = mfd->panel_info; + } else { + ctrl_pdata = container_of(dev_get_platdata(&mfd->pdev->dev), struct mdss_dsi_ctrl_pdata, panel_data); - if (!ctrl_pdata) { - pr_err("%s: DSI ctrl not available\n", __func__); - return NOTIFY_BAD; - } + if (!ctrl_pdata) { + pr_err("%s: DSI ctrl not available\n", __func__); + return NOTIFY_BAD; + } - pinfo = &ctrl_pdata->panel_data.panel_info; + pinfo = &ctrl_pdata->panel_data.panel_info; + } if ((!(pinfo->esd_check_enabled) && dsi_status_disable) || diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index 7651af03a38f..a68d359caea9 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -2,7 +2,7 @@ * Core MDSS framebuffer driver. * * Copyright (C) 2007 Google Incorporated - * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -344,6 +344,9 @@ static ssize_t mdss_fb_get_type(struct device *dev, case EDP_PANEL: ret = snprintf(buf, PAGE_SIZE, "edp panel\n"); break; + case SPI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "spi panel\n"); + break; default: ret = snprintf(buf, PAGE_SIZE, "unknown panel\n"); break; @@ -1202,6 +1205,7 @@ static int mdss_fb_probe(struct platform_device *pdev) struct mdss_panel_data *pdata; struct fb_info *fbi; int rc; + const char *data; if (fbi_list_index >= MAX_FBI_LIST) return -ENOMEM; @@ -1250,6 +1254,21 @@ static int mdss_fb_probe(struct platform_device *pdev) mfd->pdev = pdev; + if (mfd->panel.type == SPI_PANEL) + mfd->fb_imgType = MDP_RGB_565; + if (mfd->panel.type == MIPI_VIDEO_PANEL || mfd->panel.type == + MIPI_CMD_PANEL || mfd->panel.type == SPI_PANEL){ + rc = of_property_read_string(pdev->dev.of_node, + "qcom,mdss-fb-format", &data); + if (!rc) { + if (!strcmp(data, "rgb888")) + mfd->fb_imgType = MDP_RGB_888; + else if (!strcmp(data, "rgb565")) + mfd->fb_imgType = MDP_RGB_565; + else + mfd->fb_imgType = MDP_RGBA_8888; + } + } mfd->split_fb_left = mfd->split_fb_right = 0; mdss_fb_set_split_mode(mfd, pdata); @@ -1360,6 +1379,7 @@ static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd, mfd->mdp_sync_pt_data.threshold = 1; mfd->mdp_sync_pt_data.retire_threshold = 0; break; + case SPI_PANEL: case MIPI_CMD_PANEL: mfd->mdp_sync_pt_data.threshold = 1; mfd->mdp_sync_pt_data.retire_threshold = 1; diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h index 19dfbce258e4..faada77c5a1b 100644 --- a/drivers/video/msm/mdss/mdss_panel.h +++ b/drivers/video/msm/mdss/mdss_panel.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -60,7 +60,7 @@ enum fps_resolution { #define WRITEBACK_PANEL 10 /* Wifi display */ #define LVDS_PANEL 11 /* LVDS */ #define EDP_PANEL 12 /* LVDS */ - +#define SPI_PANEL 13 #define DSC_PPS_LEN 128 /* HDR propeties count */ @@ -108,6 +108,7 @@ enum { MDSS_PANEL_INTF_DSI, MDSS_PANEL_INTF_EDP, MDSS_PANEL_INTF_HDMI, + MDSS_PANEL_INTF_SPI, }; enum { @@ -117,6 +118,13 @@ enum { MDSS_PANEL_POWER_LP2, }; +enum { + MDSS_PANEL_BLANK_BLANK = 0, + MDSS_PANEL_BLANK_UNBLANK, + MDSS_PANEL_BLANK_LOW_POWER, +}; + + enum { MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0, MDSS_PANEL_LOW_PERSIST_MODE_ON, @@ -428,6 +436,10 @@ struct edp_panel_info { char frame_rate; /* fps */ }; +struct spi_panel_info { + char frame_rate; +}; + /** * struct dynamic_fps_data - defines dynamic fps related data * @hfp: horizontal front porch @@ -675,6 +687,7 @@ struct mdss_panel_info { u32 partial_update_roi_merge; struct ion_handle *splash_ihdl; int panel_power_state; + int blank_state; int compression_mode; uint32_t panel_dead; @@ -734,6 +747,7 @@ struct mdss_panel_info { struct lcd_panel_info lcdc; struct fbc_panel_info fbc; struct mipi_panel_info mipi; + struct spi_panel_info spi; struct lvds_panel_info lvds; struct edp_panel_info edp; @@ -872,6 +886,9 @@ static inline u32 mdss_panel_get_framerate(struct mdss_panel_info *panel_info, frame_rate = panel_info->lcdc.frame_rate; break; } + case SPI_PANEL: + frame_rate = panel_info->spi.frame_rate; + break; default: pixel_total = (panel_info->lcdc.h_back_porch + panel_info->lcdc.h_front_porch + diff --git a/drivers/video/msm/mdss/mdss_spi_panel.c b/drivers/video/msm/mdss/mdss_spi_panel.c new file mode 100644 index 000000000000..66eacd2d8d54 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_spi_panel.c @@ -0,0 +1,1713 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss.h" +#include "mdss_panel.h" +#include "mdss_spi_panel.h" +#include "mdss_spi_client.h" +#include "mdp3.h" + +DEFINE_LED_TRIGGER(bl_led_trigger); +static int mdss_spi_panel_reset(struct mdss_panel_data *pdata, int enable) +{ + struct spi_panel_data *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int i, rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { + pr_debug("%s:%d, reset line not configured\n", + __func__, __LINE__); + return rc; + } + + if (!gpio_is_valid(ctrl_pdata->disp_dc_gpio)) { + pr_debug("%s:%d, dc line not configured\n", + __func__, __LINE__); + return rc; + } + + pr_debug("%s: enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable) { + rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); + if (rc) { + pr_err("display reset gpio request failed\n"); + return rc; + } + + rc = gpio_request(ctrl_pdata->disp_dc_gpio, "disp_dc"); + if (rc) { + pr_err("display dc gpio request failed\n"); + return rc; + } + + if (!pinfo->cont_splash_enabled) { + for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) { + gpio_direction_output((ctrl_pdata->rst_gpio), + pdata->panel_info.rst_seq[i]); + if (pdata->panel_info.rst_seq[++i]) + usleep_range(pinfo->rst_seq[i] * 1000, + pinfo->rst_seq[i] * 1000); + } + } + + if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { + pr_debug("%s: Panel Not properly turned OFF\n", + __func__); + ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; + pr_err("%s: Reset panel done\n", __func__); + } + } else { + gpio_direction_output((ctrl_pdata->rst_gpio), 0); + gpio_free(ctrl_pdata->rst_gpio); + + gpio_direction_output(ctrl_pdata->disp_dc_gpio, 0); + gpio_free(ctrl_pdata->disp_dc_gpio); + } + return rc; +} + + +static int mdss_spi_panel_pinctrl_set_state( + struct spi_panel_data *ctrl_pdata, + bool active) +{ + struct pinctrl_state *pin_state; + int rc = -EFAULT; + + if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.pinctrl)) + return PTR_ERR(ctrl_pdata->pin_res.pinctrl); + + pin_state = active ? ctrl_pdata->pin_res.gpio_state_active + : ctrl_pdata->pin_res.gpio_state_suspend; + if (!IS_ERR_OR_NULL(pin_state)) { + rc = pinctrl_select_state(ctrl_pdata->pin_res.pinctrl, + pin_state); + if (rc) + pr_err("%s: can not set %s pins\n", __func__, + active ? MDSS_PINCTRL_STATE_DEFAULT + : MDSS_PINCTRL_STATE_SLEEP); + } else { + pr_err("%s: invalid '%s' pinstate\n", __func__, + active ? MDSS_PINCTRL_STATE_DEFAULT + : MDSS_PINCTRL_STATE_SLEEP); + } + return rc; +} + + +static int mdss_spi_panel_pinctrl_init(struct platform_device *pdev) +{ + struct spi_panel_data *ctrl_pdata; + + ctrl_pdata = platform_get_drvdata(pdev); + ctrl_pdata->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.pinctrl)) { + pr_err("%s: failed to get pinctrl\n", __func__); + return PTR_ERR(ctrl_pdata->pin_res.pinctrl); + } + + ctrl_pdata->pin_res.gpio_state_active + = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl, + MDSS_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.gpio_state_active)) + pr_warn("%s: can not get default pinstate\n", __func__); + + ctrl_pdata->pin_res.gpio_state_suspend + = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl, + MDSS_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.gpio_state_suspend)) + pr_warn("%s: can not get sleep pinstate\n", __func__); + + return 0; +} + + +static int mdss_spi_panel_power_on(struct mdss_panel_data *pdata) +{ + int ret = 0; + struct spi_panel_data *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + ret = msm_dss_enable_vreg( + ctrl_pdata->panel_power_data.vreg_config, + ctrl_pdata->panel_power_data.num_vreg, 1); + if (ret) { + pr_err("%s: failed to enable vregs for %s\n", + __func__, "PANEL_PM"); + } + + /* + * If continuous splash screen feature is enabled, then we need to + * request all the GPIOs that have already been configured in the + * bootloader. This needs to be done irresepective of whether + * the lp11_init flag is set or not. + */ + if (pdata->panel_info.cont_splash_enabled) { + if (mdss_spi_panel_pinctrl_set_state(ctrl_pdata, true)) + pr_debug("reset enable: pinctrl not enabled\n"); + + ret = mdss_spi_panel_reset(pdata, 1); + if (ret) + pr_err("%s: Panel reset failed. rc=%d\n", + __func__, ret); + } + + return ret; +} + + +static int mdss_spi_panel_power_off(struct mdss_panel_data *pdata) +{ + int ret = 0; + struct spi_panel_data *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + ret = -EINVAL; + goto end; + } + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + ret = mdss_spi_panel_reset(pdata, 0); + if (ret) { + pr_warn("%s: Panel reset failed. rc=%d\n", __func__, ret); + ret = 0; + } + + if (mdss_spi_panel_pinctrl_set_state(ctrl_pdata, false)) + pr_warn("reset disable: pinctrl not enabled\n"); + + ret = msm_dss_enable_vreg( + ctrl_pdata->panel_power_data.vreg_config, + ctrl_pdata->panel_power_data.num_vreg, 0); + if (ret) + pr_err("%s: failed to disable vregs for %s\n", + __func__, "PANEL_PM"); + +end: + return ret; +} + + +static int mdss_spi_panel_power_ctrl(struct mdss_panel_data *pdata, + int power_state) +{ + int ret; + struct mdss_panel_info *pinfo; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + pinfo = &pdata->panel_info; + pr_debug("%s: cur_power_state=%d req_power_state=%d\n", __func__, + pinfo->panel_power_state, power_state); + + if (pinfo->panel_power_state == power_state) { + pr_debug("%s: no change needed\n", __func__); + return 0; + } + + switch (power_state) { + case MDSS_PANEL_POWER_OFF: + ret = mdss_spi_panel_power_off(pdata); + break; + case MDSS_PANEL_POWER_ON: + ret = mdss_spi_panel_power_on(pdata); + break; + default: + pr_err("%s: unknown panel power state requested (%d)\n", + __func__, power_state); + ret = -EINVAL; + } + + if (!ret) + pinfo->panel_power_state = power_state; + + return ret; +} + +static int mdss_spi_panel_unblank(struct mdss_panel_data *pdata) +{ + int ret = 0; + struct spi_panel_data *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + if (!(ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)) { + ret = ctrl_pdata->on(pdata); + if (ret) { + pr_err("%s: unable to initialize the panel\n", + __func__); + return ret; + } + ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT; + } + + return ret; +} + +static int mdss_spi_panel_blank(struct mdss_panel_data *pdata, int power_state) +{ + int ret = 0; + struct spi_panel_data *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { + ret = ctrl_pdata->off(pdata); + if (ret) { + pr_err("%s: Panel OFF failed\n", __func__); + return ret; + } + ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; + } + + return ret; +} + + +static int mdss_spi_panel_event_handler(struct mdss_panel_data *pdata, + int event, void *arg) +{ + int rc = 0; + struct spi_panel_data *ctrl_pdata = NULL; + int power_state; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + switch (event) { + case MDSS_EVENT_LINK_READY: + rc = mdss_spi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_ON); + if (rc) { + pr_err("%s:Panel power on failed. rc=%d\n", + __func__, rc); + return rc; + } + mdss_spi_panel_pinctrl_set_state(ctrl_pdata, true); + mdss_spi_panel_reset(pdata, 1); + break; + case MDSS_EVENT_UNBLANK: + rc = mdss_spi_panel_unblank(pdata); + break; + case MDSS_EVENT_PANEL_ON: + ctrl_pdata->ctrl_state |= CTRL_STATE_MDP_ACTIVE; + break; + case MDSS_EVENT_BLANK: + power_state = (int) (unsigned long) arg; + break; + case MDSS_EVENT_PANEL_OFF: + power_state = (int) (unsigned long) arg; + ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE; + rc = mdss_spi_panel_blank(pdata, power_state); + rc = mdss_spi_panel_power_ctrl(pdata, power_state); + break; + default: + pr_debug("%s: unhandled event=%d\n", __func__, event); + break; + } + pr_debug("%s-:event=%d, rc=%d\n", __func__, event, rc); + return rc; +} + +int is_spi_panel_continuous_splash_on(struct mdss_panel_data *pdata) +{ + int i = 0, voltage = 0; + struct dss_vreg *vreg; + int num_vreg; + struct spi_panel_data *ctrl_pdata = NULL; + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + vreg = ctrl_pdata->panel_power_data.vreg_config; + num_vreg = ctrl_pdata->panel_power_data.num_vreg; + + for (i = 0; i < num_vreg; i++) { + if (regulator_is_enabled(vreg[i].vreg) <= 0) + return false; + voltage = regulator_get_voltage(vreg[i].vreg); + if (!(voltage >= vreg[i].min_voltage && + voltage <= vreg[i].max_voltage)) + return false; + } + + return true; +} + +static void enable_spi_panel_te_irq(struct spi_panel_data *ctrl_pdata, + bool enable) +{ + static bool is_enabled = true; + + if (is_enabled == enable) + return; + + if (!gpio_is_valid(ctrl_pdata->disp_te_gpio)) { + pr_err("%s:%d,SPI panel TE GPIO not configured\n", + __func__, __LINE__); + return; + } + + if (enable) + enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + else + disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + + is_enabled = enable; +} + +int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata, + char *buf, int len, int dma_stride) +{ + struct spi_panel_data *ctrl_pdata = NULL; + char *tx_buf; + int rc = 0; + int panel_yres; + int panel_xres; + int padding_length = 0; + int actual_stride = 0; + int byte_per_pixel = 0; + int scan_count = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + tx_buf = ctrl_pdata->tx_buf; + panel_xres = ctrl_pdata->panel_data.panel_info.xres; + panel_yres = ctrl_pdata->panel_data.panel_info.yres; + + byte_per_pixel = ctrl_pdata->panel_data.panel_info.bpp / 8; + actual_stride = panel_xres * byte_per_pixel; + padding_length = dma_stride - actual_stride; + + /* remove the padding and copy to continuous buffer */ + while (scan_count < panel_yres) { + memcpy((tx_buf + scan_count * actual_stride), + (buf + scan_count * (actual_stride + padding_length)), + actual_stride); + scan_count++; + } + + enable_spi_panel_te_irq(ctrl_pdata, true); + + mutex_lock(&ctrl_pdata->spi_tx_mutex); + reinit_completion(&ctrl_pdata->spi_panel_te); + + rc = wait_for_completion_timeout(&ctrl_pdata->spi_panel_te, + msecs_to_jiffies(SPI_PANEL_TE_TIMEOUT)); + + if (rc == 0) + pr_err("wait panel TE time out\n"); + + rc = mdss_spi_tx_pixel(tx_buf, ctrl_pdata->byte_pre_frame); + mutex_unlock(&ctrl_pdata->spi_tx_mutex); + + return rc; +} + +static int mdss_spi_read_panel_data(struct mdss_panel_data *pdata, + u8 reg_addr, u8 *data, u8 len) +{ + int rc = 0; + struct spi_panel_data *ctrl_pdata = NULL; + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + mutex_lock(&ctrl_pdata->spi_tx_mutex); + gpio_direction_output(ctrl_pdata->disp_dc_gpio, 0); + rc = mdss_spi_read_data(reg_addr, data, len); + gpio_direction_output(ctrl_pdata->disp_dc_gpio, 1); + mutex_unlock(&ctrl_pdata->spi_tx_mutex); + + return rc; +} + +static int mdss_spi_panel_on(struct mdss_panel_data *pdata) +{ + struct spi_panel_data *ctrl = NULL; + struct mdss_panel_info *pinfo; + int i; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + pinfo = &pdata->panel_info; + ctrl = container_of(pdata, struct spi_panel_data, + panel_data); + + for (i = 0; i < ctrl->on_cmds.cmd_cnt; i++) { + /* pull down dc gpio indicate this is command */ + gpio_direction_output(ctrl->disp_dc_gpio, 0); + mdss_spi_tx_command(ctrl->on_cmds.cmds[i].command); + gpio_direction_output((ctrl->disp_dc_gpio), 1); + + if (ctrl->on_cmds.cmds[i].dchdr.dlen > 1) { + mdss_spi_tx_parameter(ctrl->on_cmds.cmds[i].parameter, + ctrl->on_cmds.cmds[i].dchdr.dlen-1); + } + if (ctrl->on_cmds.cmds[i].dchdr.wait != 0) + msleep(ctrl->on_cmds.cmds[i].dchdr.wait); + } + + pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK; + + pr_debug("%s:-\n", __func__); + + return 0; +} + + +static int mdss_spi_panel_off(struct mdss_panel_data *pdata) +{ + struct spi_panel_data *ctrl = NULL; + struct mdss_panel_info *pinfo; + int i; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + pinfo = &pdata->panel_info; + ctrl = container_of(pdata, struct spi_panel_data, + panel_data); + + for (i = 0; i < ctrl->off_cmds.cmd_cnt; i++) { + /* pull down dc gpio indicate this is command */ + gpio_direction_output(ctrl->disp_dc_gpio, 0); + mdss_spi_tx_command(ctrl->off_cmds.cmds[i].command); + gpio_direction_output((ctrl->disp_dc_gpio), 1); + + if (ctrl->off_cmds.cmds[i].dchdr.dlen > 1) { + mdss_spi_tx_parameter(ctrl->off_cmds.cmds[i].parameter, + ctrl->off_cmds.cmds[i].dchdr.dlen-1); + } + + if (ctrl->off_cmds.cmds[i].dchdr.wait != 0) + msleep(ctrl->off_cmds.cmds[i].dchdr.wait); + } + + pinfo->blank_state = MDSS_PANEL_BLANK_BLANK; + + pr_debug("%s:-\n", __func__); + return 0; +} + +static void mdss_spi_put_dt_vreg_data(struct device *dev, + struct dss_module_power *module_power) +{ + if (!module_power) { + pr_err("%s: invalid input\n", __func__); + return; + } + + if (module_power->vreg_config) { + devm_kfree(dev, module_power->vreg_config); + module_power->vreg_config = NULL; + } + module_power->num_vreg = 0; +} + + +static int mdss_spi_get_panel_vreg_data(struct device *dev, + struct dss_module_power *mp) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *of_node = NULL, *supply_node = NULL; + struct device_node *supply_root_node = NULL; + + if (!dev || !mp) { + pr_err("%s: invalid input\n", __func__); + rc = -EINVAL; + return rc; + } + + of_node = dev->of_node; + + mp->num_vreg = 0; + + supply_root_node = of_get_child_by_name(of_node, + "qcom,panel-supply-entries"); + + for_each_available_child_of_node(supply_root_node, supply_node) { + mp->num_vreg++; + } + if (mp->num_vreg == 0) { + pr_debug("%s: no vreg\n", __func__); + goto novreg; + } else { + pr_debug("%s: vreg found. count=%d\n", __func__, mp->num_vreg); + } + + mp->vreg_config = kcalloc(mp->num_vreg, sizeof(struct dss_vreg), + GFP_KERNEL); + + if (NULL != mp->vreg_config) { + for_each_available_child_of_node(supply_root_node, + supply_node) { + const char *st = NULL; + /* vreg-name */ + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err("%s: error reading name. rc=%d\n", + __func__, rc); + goto error; + } + snprintf(mp->vreg_config[i].vreg_name, + ARRAY_SIZE((mp->vreg_config[i].vreg_name)), + "%s", st); + /* vreg-min-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err("%s: error reading min volt. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].min_voltage = tmp; + + /* vreg-max-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err("%s: error reading max volt. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].max_voltage = tmp; + + /* enable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err("%s: error read enable load. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].load[DSS_REG_MODE_ENABLE] = tmp; + + /* disable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err("%s: error read disable load. rc=%d\n", + __func__, rc); + goto error; + } + mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] = tmp; + + /* pre-sleep */ + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-on-sleep", &tmp); + if (rc) { + pr_debug("%s: error read pre on value\n", + __func__); + rc = 0; + } else { + mp->vreg_config[i].pre_on_sleep = tmp; + } + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-off-sleep", &tmp); + if (rc) { + pr_debug("%s: error read pre off value\n", + __func__); + rc = 0; + } else { + mp->vreg_config[i].pre_off_sleep = tmp; + } + + /* post-sleep */ + rc = of_property_read_u32(supply_node, + "qcom,supply-post-on-sleep", &tmp); + if (rc) { + pr_debug("%s: error read post on value\n", + __func__); + rc = 0; + } else { + mp->vreg_config[i].post_on_sleep = tmp; + } + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-off-sleep", &tmp); + if (rc) { + pr_debug("%s: error read post off value\n", + __func__); + rc = 0; + } else { + mp->vreg_config[i].post_off_sleep = tmp; + } + + ++i; + } + } + return rc; +error: + kfree(mp->vreg_config); + mp->vreg_config = NULL; + +novreg: + mp->num_vreg = 0; + + return rc; + +} + +static int mdss_spi_panel_parse_cmds(struct device_node *np, + struct spi_panel_cmds *pcmds, char *cmd_key) +{ + const char *data; + int blen = 0, len; + char *buf, *bp; + struct spi_ctrl_hdr *dchdr; + int i, cnt; + + data = of_get_property(np, cmd_key, &blen); + if (!data) { + pr_err("%s: failed, key=%s\n", __func__, cmd_key); + return -ENOMEM; + } + + buf = kcalloc(blen, sizeof(char), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, data, blen); + + /* scan dcs commands */ + bp = buf; + len = blen; + cnt = 0; + while (len >= sizeof(*dchdr)) { + dchdr = (struct spi_ctrl_hdr *)bp; + if (dchdr->dlen > len) { + pr_err("%s: dtsi parse error, len=%d", + __func__, dchdr->dlen); + goto exit_free; + } + bp += sizeof(*dchdr); + len -= sizeof(*dchdr); + bp += dchdr->dlen; + len -= dchdr->dlen; + cnt++; + } + + if (len != 0) { + pr_err("%s: dcs_cmd=%x len=%d error", + __func__, buf[0], len); + goto exit_free; + } + + pcmds->cmds = kcalloc(cnt, sizeof(struct spi_cmd_desc), + GFP_KERNEL); + if (!pcmds->cmds) + goto exit_free; + + pcmds->cmd_cnt = cnt; + pcmds->buf = buf; + pcmds->blen = blen; + + bp = buf; + len = blen; + for (i = 0; i < cnt; i++) { + dchdr = (struct spi_ctrl_hdr *)bp; + len -= sizeof(*dchdr); + bp += sizeof(*dchdr); + pcmds->cmds[i].dchdr = *dchdr; + pcmds->cmds[i].command = bp; + pcmds->cmds[i].parameter = bp + sizeof(char); + bp += dchdr->dlen; + len -= dchdr->dlen; + } + + pr_debug("%s: dcs_cmd=%x, len=%d, cmd_cnt=%d\n", __func__, + pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt); + + return 0; + +exit_free: + kfree(buf); + return -ENOMEM; +} +static int mdss_spi_panel_parse_reset_seq(struct device_node *np, + u32 rst_seq[MDSS_SPI_RST_SEQ_LEN], u32 *rst_len, + const char *name) +{ + int num = 0, i; + int rc; + struct property *data; + u32 tmp[MDSS_SPI_RST_SEQ_LEN]; + + *rst_len = 0; + data = of_find_property(np, name, &num); + num /= sizeof(u32); + if (!data || !num || num > MDSS_SPI_RST_SEQ_LEN || num % 2) { + pr_err("%s:%d, error reading %s, length found = %d\n", + __func__, __LINE__, name, num); + } else { + rc = of_property_read_u32_array(np, name, tmp, num); + if (rc) + pr_err("%s:%d, error reading %s, rc = %d\n", + __func__, __LINE__, name, rc); + else { + for (i = 0; i < num; ++i) + rst_seq[i] = tmp[i]; + *rst_len = num; + } + } + return 0; +} + +static bool mdss_send_panel_cmd_for_esd(struct spi_panel_data *ctrl_pdata) +{ + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return false; + } + + mutex_lock(&ctrl_pdata->spi_tx_mutex); + mdss_spi_panel_on(&ctrl_pdata->panel_data); + mutex_unlock(&ctrl_pdata->spi_tx_mutex); + + return true; +} + +static bool mdss_spi_reg_status_check(struct spi_panel_data *ctrl_pdata) +{ + int ret = 0; + int i = 0; + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return false; + } + + pr_debug("%s: Checking Register status\n", __func__); + + ret = mdss_spi_read_panel_data(&ctrl_pdata->panel_data, + ctrl_pdata->panel_status_reg, + ctrl_pdata->act_status_value, + ctrl_pdata->status_cmds_rlen); + if (ret < 0) { + pr_err("%s: Read status register returned error\n", __func__); + } else { + for (i = 0; i < ctrl_pdata->status_cmds_rlen; i++) { + pr_debug("act_value[%d] = %x, exp_value[%d] = %x\n", + i, ctrl_pdata->act_status_value[i], + i, ctrl_pdata->exp_status_value[i]); + if (ctrl_pdata->act_status_value[i] != + ctrl_pdata->exp_status_value[i]) + return false; + } + } + + return true; +} + +static void mdss_spi_parse_esd_params(struct device_node *np, + struct spi_panel_data *ctrl) +{ + u32 tmp; + int rc; + struct property *data; + const char *string; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + + pinfo->esd_check_enabled = of_property_read_bool(np, + "qcom,esd-check-enabled"); + + if (!pinfo->esd_check_enabled) + return; + + ctrl->status_mode = SPI_ESD_MAX; + + rc = of_property_read_string(np, + "qcom,mdss-spi-panel-status-check-mode", &string); + if (!rc) { + if (!strcmp(string, "reg_read")) { + ctrl->status_mode = SPI_ESD_REG; + ctrl->check_status = + mdss_spi_reg_status_check; + } else if (!strcmp(string, "send_init_command")) { + ctrl->status_mode = SPI_SEND_PANEL_COMMAND; + ctrl->check_status = + mdss_send_panel_cmd_for_esd; + return; + } else { + pr_err("No valid panel-status-check-mode string\n"); + pinfo->esd_check_enabled = false; + return; + } + } + + rc = of_property_read_u8(np, "qcom,mdss-spi-panel-status-reg", + &ctrl->panel_status_reg); + if (rc) { + pr_warn("%s:%d, Read status reg failed, disable ESD check\n", + __func__, __LINE__); + pinfo->esd_check_enabled = false; + return; + } + + rc = of_property_read_u32(np, "qcom,mdss-spi-panel-status-read-length", + &tmp); + if (rc) { + pr_warn("%s:%d, Read reg length failed, disable ESD check\n", + __func__, __LINE__); + pinfo->esd_check_enabled = false; + return; + } + + ctrl->status_cmds_rlen = (!rc ? tmp : 1); + + ctrl->exp_status_value = kzalloc(sizeof(u8) * + (ctrl->status_cmds_rlen + 1), GFP_KERNEL); + ctrl->act_status_value = kzalloc(sizeof(u8) * + (ctrl->status_cmds_rlen + 1), GFP_KERNEL); + + if (!ctrl->exp_status_value || !ctrl->act_status_value) { + pr_err("%s: Error allocating memory for status buffer\n", + __func__); + pinfo->esd_check_enabled = false; + return; + } + + data = of_find_property(np, "qcom,mdss-spi-panel-status-value", &tmp); + tmp /= sizeof(u8); + if (!data || (tmp != ctrl->status_cmds_rlen)) { + pr_err("%s: Panel status values not found\n", __func__); + pinfo->esd_check_enabled = false; + memset(ctrl->exp_status_value, 0, ctrl->status_cmds_rlen); + } else { + rc = of_property_read_u8_array(np, + "qcom,mdss-spi-panel-status-value", + ctrl->exp_status_value, tmp); + if (rc) { + pr_err("%s: Error reading panel status values\n", + __func__); + pinfo->esd_check_enabled = false; + memset(ctrl->exp_status_value, 0, + ctrl->status_cmds_rlen); + } + } +} + +static int mdss_spi_panel_parse_dt(struct device_node *np, + struct spi_panel_data *ctrl_pdata) +{ + u32 tmp; + int rc; + const char *data; + struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + + pinfo->cont_splash_enabled = of_property_read_bool(np, + "qcom,cont-splash-enabled"); + + rc = of_property_read_u32(np, "qcom,mdss-spi-panel-width", &tmp); + if (rc) { + pr_err("%s: panel width not specified\n", __func__); + return -EINVAL; + } + pinfo->xres = (!rc ? tmp : 240); + + rc = of_property_read_u32(np, "qcom,mdss-spi-panel-height", &tmp); + if (rc) { + pr_err("%s:panel height not specified\n", __func__); + return -EINVAL; + } + pinfo->yres = (!rc ? tmp : 320); + + rc = of_property_read_u32(np, + "qcom,mdss-pan-physical-width-dimension", &tmp); + pinfo->physical_width = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-pan-physical-height-dimension", &tmp); + pinfo->physical_height = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-spi-panel-framerate", &tmp); + pinfo->spi.frame_rate = (!rc ? tmp : 30); + rc = of_property_read_u32(np, "qcom,mdss-spi-h-front-porch", &tmp); + pinfo->lcdc.h_front_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-spi-h-back-porch", &tmp); + pinfo->lcdc.h_back_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-spi-h-pulse-width", &tmp); + pinfo->lcdc.h_pulse_width = (!rc ? tmp : 2); + rc = of_property_read_u32(np, "qcom,mdss-spi-h-sync-skew", &tmp); + pinfo->lcdc.hsync_skew = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-spi-v-back-porch", &tmp); + pinfo->lcdc.v_back_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-spi-v-front-porch", &tmp); + pinfo->lcdc.v_front_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-spi-v-pulse-width", &tmp); + pinfo->lcdc.v_pulse_width = (!rc ? tmp : 2); + + + rc = of_property_read_u32(np, "qcom,mdss-spi-bpp", &tmp); + if (rc) { + pr_err("%s: bpp not specified\n", __func__); + return -EINVAL; + } + pinfo->bpp = (!rc ? tmp : 16); + + pinfo->pdest = DISPLAY_1; + + ctrl_pdata->bklt_ctrl = SPI_UNKNOWN_CTRL; + data = of_get_property(np, "qcom,mdss-spi-bl-pmic-control-type", NULL); + if (data) { + if (!strcmp(data, "bl_ctrl_wled")) { + led_trigger_register_simple("bkl-trigger", + &bl_led_trigger); + pr_debug("%s: SUCCESS-> WLED TRIGGER register\n", + __func__); + ctrl_pdata->bklt_ctrl = SPI_BL_WLED; + } else if (!strcmp(data, "bl_gpio_pulse")) { + led_trigger_register_simple("gpio-bklt-trigger", + &bl_led_trigger); + pr_debug("%s: SUCCESS-> GPIO PULSE TRIGGER register\n", + __func__); + ctrl_pdata->bklt_ctrl = SPI_BL_WLED; + } else if (!strcmp(data, "bl_ctrl_pwm")) { + ctrl_pdata->bklt_ctrl = SPI_BL_PWM; + ctrl_pdata->pwm_pmi = of_property_read_bool(np, + "qcom,mdss-spi-bl-pwm-pmi"); + rc = of_property_read_u32(np, + "qcom,mdss-spi-bl-pmic-pwm-frequency", &tmp); + if (rc) { + pr_err("%s: Error, panel pwm_period\n", + __func__); + return -EINVAL; + } + ctrl_pdata->pwm_period = tmp; + if (ctrl_pdata->pwm_pmi) { + ctrl_pdata->pwm_bl = of_pwm_get(np, NULL); + if (IS_ERR(ctrl_pdata->pwm_bl)) { + pr_err("%s: Error, pwm device\n", + __func__); + ctrl_pdata->pwm_bl = NULL; + return -EINVAL; + } + } else { + rc = of_property_read_u32(np, + "qcom,mdss-spi-bl-pmic-bank-select", + &tmp); + if (rc) { + pr_err("%s: Error, lpg channel\n", + __func__); + return -EINVAL; + } + ctrl_pdata->pwm_lpg_chan = tmp; + tmp = of_get_named_gpio(np, + "qcom,mdss-spi-pwm-gpio", 0); + ctrl_pdata->pwm_pmic_gpio = tmp; + pr_debug("%s: Configured PWM bklt ctrl\n", + __func__); + } + } + } + rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp); + pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS); + rc = of_property_read_u32(np, "qcom,mdss-spi-bl-min-level", &tmp); + pinfo->bl_min = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-spi-bl-max-level", &tmp); + pinfo->bl_max = (!rc ? tmp : 255); + ctrl_pdata->bklt_max = pinfo->bl_max; + + + mdss_spi_panel_parse_reset_seq(np, pinfo->rst_seq, + &(pinfo->rst_seq_len), + "qcom,mdss-spi-reset-sequence"); + + mdss_spi_panel_parse_cmds(np, &ctrl_pdata->on_cmds, + "qcom,mdss-spi-on-command"); + + mdss_spi_panel_parse_cmds(np, &ctrl_pdata->off_cmds, + "qcom,mdss-spi-off-command"); + + mdss_spi_parse_esd_params(np, ctrl_pdata); + + + return 0; +} + +static void mdss_spi_panel_pwm_cfg(struct spi_panel_data *ctrl) +{ + if (ctrl->pwm_pmi) + return; + + ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt"); + if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) { + pr_err("%s: Error: lpg_chan=%d pwm request failed", + __func__, ctrl->pwm_lpg_chan); + } + ctrl->pwm_enabled = 0; +} + +static void mdss_spi_panel_bklt_pwm(struct spi_panel_data *ctrl, int level) +{ + int ret; + u32 duty; + u32 period_ns; + + if (ctrl->pwm_bl == NULL) { + pr_err("%s: no PWM\n", __func__); + return; + } + + if (level == 0) { + if (ctrl->pwm_enabled) { + ret = pwm_config_us(ctrl->pwm_bl, level, + ctrl->pwm_period); + if (ret) + pr_err("%s: pwm_config_us() failed err=%d.\n", + __func__, ret); + pwm_disable(ctrl->pwm_bl); + } + ctrl->pwm_enabled = 0; + return; + } + + duty = level * ctrl->pwm_period; + duty /= ctrl->bklt_max; + + pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n", + __func__, ctrl->bklt_ctrl, ctrl->pwm_period, + ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan); + + if (ctrl->pwm_period >= USEC_PER_SEC) { + ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); + if (ret) { + pr_err("%s: pwm_config_us() failed err=%d\n", + __func__, ret); + return; + } + } else { + period_ns = ctrl->pwm_period * NSEC_PER_USEC; + ret = pwm_config(ctrl->pwm_bl, + level * period_ns / ctrl->bklt_max, + period_ns); + if (ret) { + pr_err("%s: pwm_config() failed err=%d\n", + __func__, ret); + return; + } + } + + if (!ctrl->pwm_enabled) { + ret = pwm_enable(ctrl->pwm_bl); + if (ret) + pr_err("%s: pwm_enable() failed err=%d\n", __func__, + ret); + ctrl->pwm_enabled = 1; + } +} + +static void mdss_spi_panel_bl_ctrl(struct mdss_panel_data *pdata, + u32 bl_level) +{ + if (bl_level) { + mdp3_res->bklt_level = bl_level; + mdp3_res->bklt_update = true; + } else { + mdss_spi_panel_bl_ctrl_update(pdata, bl_level); + } +} + +#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) && defined(CONFIG_SPI_QUP) +void mdss_spi_panel_bl_ctrl_update(struct mdss_panel_data *pdata, + u32 bl_level) +{ + struct spi_panel_data *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) + bl_level = pdata->panel_info.bl_min; + + switch (ctrl_pdata->bklt_ctrl) { + case SPI_BL_WLED: + led_trigger_event(bl_led_trigger, bl_level); + break; + case SPI_BL_PWM: + mdss_spi_panel_bklt_pwm(ctrl_pdata, bl_level); + break; + default: + pr_err("%s: Unknown bl_ctrl configuration %d\n", + __func__, ctrl_pdata->bklt_ctrl); + break; + } +} +#endif + +static int mdss_spi_panel_init(struct device_node *node, + struct spi_panel_data *ctrl_pdata, + bool cmd_cfg_cont_splash) +{ + int rc = 0; + static const char *panel_name; + struct mdss_panel_info *pinfo; + + if (!node || !ctrl_pdata) { + pr_err("%s: Invalid arguments\n", __func__); + return -ENODEV; + } + + pinfo = &ctrl_pdata->panel_data.panel_info; + + pr_debug("%s:%d\n", __func__, __LINE__); + pinfo->panel_name[0] = '\0'; + panel_name = of_get_property(node, "qcom,mdss-spi-panel-name", NULL); + if (!panel_name) { + pr_info("%s:%d, Panel name not specified\n", + __func__, __LINE__); + } else { + pr_debug("%s: Panel Name = %s\n", __func__, panel_name); + strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN); + } + rc = mdss_spi_panel_parse_dt(node, ctrl_pdata); + if (rc) { + pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__); + return rc; + } + + ctrl_pdata->byte_pre_frame = pinfo->xres * pinfo->yres * pinfo->bpp/8; + + ctrl_pdata->tx_buf = kmalloc(ctrl_pdata->byte_pre_frame, GFP_KERNEL); + + if (!cmd_cfg_cont_splash) + pinfo->cont_splash_enabled = false; + + pr_info("%s: Continuous splash %s\n", __func__, + pinfo->cont_splash_enabled ? "enabled" : "disabled"); + + pinfo->dynamic_switch_pending = false; + pinfo->is_lpm_mode = false; + pinfo->esd_rdy = false; + + ctrl_pdata->on = mdss_spi_panel_on; + ctrl_pdata->off = mdss_spi_panel_off; + ctrl_pdata->panel_data.set_backlight = mdss_spi_panel_bl_ctrl; + + return 0; +} + +static int mdss_spi_get_panel_cfg(char *panel_cfg, + struct spi_panel_data *ctrl_pdata) +{ + int rc; + struct mdss_panel_cfg *pan_cfg = NULL; + + if (!ctrl_pdata) + return MDSS_PANEL_INTF_INVALID; + + pan_cfg = ctrl_pdata->mdss_util->panel_intf_type(MDSS_PANEL_INTF_SPI); + if (IS_ERR(pan_cfg)) { + return PTR_ERR(pan_cfg); + } else if (!pan_cfg) { + panel_cfg[0] = 0; + return 0; + } + + pr_debug("%s:%d: cfg:[%s]\n", __func__, __LINE__, + pan_cfg->arg_cfg); + ctrl_pdata->panel_data.panel_info.is_prim_panel = true; + rc = strlcpy(panel_cfg, pan_cfg->arg_cfg, + sizeof(pan_cfg->arg_cfg)); + return rc; +} + +static int mdss_spi_panel_regulator_init(struct platform_device *pdev) +{ + int rc = 0; + + struct spi_panel_data *ctrl_pdata = NULL; + + if (!pdev) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + ctrl_pdata = platform_get_drvdata(pdev); + if (!ctrl_pdata) { + pr_err("%s: invalid driver data\n", __func__); + return -EINVAL; + } + + rc = msm_dss_config_vreg(&pdev->dev, + ctrl_pdata->panel_power_data.vreg_config, + ctrl_pdata->panel_power_data.num_vreg, 1); + if (rc) + pr_err("%s: failed to init vregs for %s\n", + __func__, "PANEL_PM"); + + return rc; + +} + +static irqreturn_t spi_panel_te_handler(int irq, void *data) +{ + struct spi_panel_data *ctrl_pdata = (struct spi_panel_data *)data; + static int count = 2; + + if (!ctrl_pdata) { + pr_err("%s: SPI display not available\n", __func__); + return IRQ_HANDLED; + } + complete(&ctrl_pdata->spi_panel_te); + + if (ctrl_pdata->vsync_client.handler && !(--count)) { + ctrl_pdata->vsync_client.handler(ctrl_pdata->vsync_client.arg); + count = 2; + } + + return IRQ_HANDLED; +} + +void mdp3_spi_vsync_enable(struct mdss_panel_data *pdata, + struct mdp3_notification *vsync_client) +{ + int updated = 0; + struct spi_panel_data *ctrl_pdata = NULL; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + ctrl_pdata = container_of(pdata, struct spi_panel_data, + panel_data); + + if (vsync_client) { + if (ctrl_pdata->vsync_client.handler != vsync_client->handler) { + ctrl_pdata->vsync_client = *vsync_client; + updated = 1; + } + } else { + if (ctrl_pdata->vsync_client.handler) { + ctrl_pdata->vsync_client.handler = NULL; + ctrl_pdata->vsync_client.arg = NULL; + updated = 1; + } + } + + if (updated) { + if (vsync_client && vsync_client->handler) + enable_spi_panel_te_irq(ctrl_pdata, true); + else + enable_spi_panel_te_irq(ctrl_pdata, false); + } +} + +static struct device_node *mdss_spi_pref_prim_panel( + struct platform_device *pdev) +{ + struct device_node *spi_pan_node = NULL; + + pr_debug("%s:%d: Select primary panel from dt\n", + __func__, __LINE__); + spi_pan_node = of_parse_phandle(pdev->dev.of_node, + "qcom,spi-pref-prim-pan", 0); + if (!spi_pan_node) + pr_err("%s:can't find panel phandle\n", __func__); + + return spi_pan_node; +} + +static int spi_panel_device_register(struct device_node *pan_node, + struct spi_panel_data *ctrl_pdata) +{ + int rc; + struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + struct device_node *spi_ctrl_np = NULL; + struct platform_device *ctrl_pdev = NULL; + + pinfo->type = SPI_PANEL; + + spi_ctrl_np = of_parse_phandle(pan_node, + "qcom,mdss-spi-panel-controller", 0); + if (!spi_ctrl_np) { + pr_err("%s: SPI controller node not initialized\n", __func__); + return -EPROBE_DEFER; + } + + ctrl_pdev = of_find_device_by_node(spi_ctrl_np); + if (!ctrl_pdev) { + of_node_put(spi_ctrl_np); + pr_err("%s: SPI controller node not find\n", __func__); + return -EPROBE_DEFER; + } + + rc = mdss_spi_panel_regulator_init(ctrl_pdev); + if (rc) { + pr_err("%s: failed to init regulator, rc=%d\n", + __func__, rc); + return rc; + } + + pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo , + FPS_RESOLUTION_HZ); + pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo); + + ctrl_pdata->disp_te_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,platform-te-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->disp_te_gpio)) + pr_err("%s:%d, TE gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->disp_dc_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,platform-spi-dc-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->disp_dc_gpio)) + pr_err("%s:%d, SPI DC gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,platform-reset-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->rst_gpio)) + pr_err("%s:%d, reset gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->panel_data.event_handler = mdss_spi_panel_event_handler; + + if (ctrl_pdata->bklt_ctrl == SPI_BL_PWM) + mdss_spi_panel_pwm_cfg(ctrl_pdata); + + ctrl_pdata->ctrl_state = CTRL_STATE_UNKNOWN; + + if (pinfo->cont_splash_enabled) { + rc = mdss_spi_panel_power_ctrl(&(ctrl_pdata->panel_data), + MDSS_PANEL_POWER_ON); + if (rc) { + pr_err("%s: Panel power on failed\n", __func__); + return rc; + } + if (ctrl_pdata->bklt_ctrl == SPI_BL_PWM) + ctrl_pdata->pwm_enabled = 1; + pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK; + ctrl_pdata->ctrl_state |= + (CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE); + } else { + pinfo->panel_power_state = MDSS_PANEL_POWER_OFF; + } + + rc = mdss_register_panel(ctrl_pdev, &(ctrl_pdata->panel_data)); + if (rc) { + pr_err("%s: unable to register SPI panel\n", __func__); + return rc; + } + + pr_debug("%s: Panel data initialized\n", __func__); + return 0; +} + + +/** + * mdss_spi_find_panel_of_node(): find device node of spi panel + * @pdev: platform_device of the spi ctrl node + * @panel_cfg: string containing intf specific config data + * + * Function finds the panel device node using the interface + * specific configuration data. This configuration data is + * could be derived from the result of bootloader's GCDB + * panel detection mechanism. If such config data doesn't + * exist then this panel returns the default panel configured + * in the device tree. + * + * returns pointer to panel node on success, NULL on error. + */ +static struct device_node *mdss_spi_find_panel_of_node( + struct platform_device *pdev, char *panel_cfg) +{ + int len, i; + int ctrl_id = pdev->id - 1; + char panel_name[MDSS_MAX_PANEL_LEN] = ""; + char ctrl_id_stream[3] = "0:"; + char *stream = NULL, *pan = NULL; + struct device_node *spi_pan_node = NULL, *mdss_node = NULL; + + len = strlen(panel_cfg); + if (!len) { + /* no panel cfg chg, parse dt */ + pr_err("%s:%d: no cmd line cfg present\n", + __func__, __LINE__); + goto end; + } else { + if (ctrl_id == 1) + strlcpy(ctrl_id_stream, "1:", 3); + + stream = strnstr(panel_cfg, ctrl_id_stream, len); + if (!stream) { + pr_err("controller config is not present\n"); + goto end; + } + stream += 2; + + pan = strnchr(stream, strlen(stream), ':'); + if (!pan) { + strlcpy(panel_name, stream, MDSS_MAX_PANEL_LEN); + } else { + for (i = 0; (stream + i) < pan; i++) + panel_name[i] = *(stream + i); + panel_name[i] = 0; + } + + pr_debug("%s:%d:%s:%s\n", __func__, __LINE__, + panel_cfg, panel_name); + + mdss_node = of_parse_phandle(pdev->dev.of_node, + "qcom,mdss-mdp", 0); + if (!mdss_node) { + pr_err("%s: %d: mdss_node null\n", + __func__, __LINE__); + return NULL; + } + + spi_pan_node = of_find_node_by_name(mdss_node, + panel_name); + if (!spi_pan_node) { + pr_err("%s: invalid pan node, selecting prim panel\n", + __func__); + goto end; + } + return spi_pan_node; + } +end: + if (strcmp(panel_name, NONE_PANEL)) + spi_pan_node = mdss_spi_pref_prim_panel(pdev); + of_node_put(mdss_node); + return spi_pan_node; +} + + +static int mdss_spi_panel_probe(struct platform_device *pdev) +{ + int rc = 0; + struct spi_panel_data *ctrl_pdata; + struct mdss_panel_cfg *pan_cfg = NULL; + struct device_node *spi_pan_node = NULL; + bool cmd_cfg_cont_splash = true; + char panel_cfg[MDSS_MAX_PANEL_LEN]; + struct mdss_util_intf *util; + const char *ctrl_name; + + util = mdss_get_util_intf(); + if (util == NULL) { + pr_err("Failed to get mdss utility functions\n"); + return -ENODEV; + } + + if (!util->mdp_probe_done) { + pr_err("%s: MDP not probed yet\n", __func__); + return -EPROBE_DEFER; + } + + if (!pdev->dev.of_node) { + pr_err("SPI driver only supports device tree probe\n"); + return -ENOTSUPP; + } + + pan_cfg = util->panel_intf_type(MDSS_PANEL_INTF_DSI); + if (IS_ERR(pan_cfg)) { + pr_err("%s: return MDSS_PANEL_INTF_DSI\n", __func__); + return PTR_ERR(pan_cfg); + } else if (pan_cfg) { + pr_err("%s: DSI is primary\n", __func__); + return -ENODEV; + } + + ctrl_pdata = platform_get_drvdata(pdev); + if (!ctrl_pdata) { + ctrl_pdata = devm_kzalloc(&pdev->dev, + sizeof(struct spi_panel_data), + GFP_KERNEL); + if (!ctrl_pdata) { + pr_err("%s: FAILED: cannot alloc spi panel\n", + __func__); + rc = -ENOMEM; + goto error_no_mem; + } + platform_set_drvdata(pdev, ctrl_pdata); + } + + ctrl_pdata->mdss_util = util; + + ctrl_name = of_get_property(pdev->dev.of_node, "label", NULL); + if (!ctrl_name) + pr_info("%s:%d, Ctrl name not specified\n", + __func__, __LINE__); + else + pr_debug("%s: Ctrl name = %s\n", + __func__, ctrl_name); + + + rc = of_platform_populate(pdev->dev.of_node, + NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, + "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + goto error_no_mem; + } + + rc = mdss_spi_panel_pinctrl_init(pdev); + if (rc) + pr_warn("%s: failed to get pin resources\n", __func__); + + rc = mdss_spi_get_panel_vreg_data(&pdev->dev, + &ctrl_pdata->panel_power_data); + if (rc) { + dev_err(&pdev->dev, + "%s: failed to get panel vreg data, rc=%d\n", + __func__, rc); + goto error_vreg; + } + + /* SPI panels can be different between controllers */ + rc = mdss_spi_get_panel_cfg(panel_cfg, ctrl_pdata); + if (!rc) + /* spi panel cfg not present */ + pr_warn("%s:%d:spi specific cfg not present\n", + __func__, __LINE__); + + /* find panel device node */ + spi_pan_node = mdss_spi_find_panel_of_node(pdev, panel_cfg); + if (!spi_pan_node) { + pr_err("%s: can't find panel node %s\n", __func__, panel_cfg); + goto error_pan_node; + } + + cmd_cfg_cont_splash = true; + + rc = mdss_spi_panel_init(spi_pan_node, ctrl_pdata, cmd_cfg_cont_splash); + if (rc) { + pr_err("%s: spi panel init failed\n", __func__); + goto error_pan_node; + } + + rc = spi_panel_device_register(spi_pan_node, ctrl_pdata); + if (rc) { + pr_err("%s: spi panel dev reg failed\n", __func__); + goto error_pan_node; + } + + ctrl_pdata->panel_data.event_handler = mdss_spi_panel_event_handler; + + + init_completion(&ctrl_pdata->spi_panel_te); + mutex_init(&ctrl_pdata->spi_tx_mutex); + + rc = devm_request_irq(&pdev->dev, + gpio_to_irq(ctrl_pdata->disp_te_gpio), + spi_panel_te_handler, IRQF_TRIGGER_RISING, + "TE_GPIO", ctrl_pdata); + if (rc) { + pr_err("TE request_irq failed.\n"); + return rc; + } + + pr_debug("%s: spi panel initialized\n", __func__); + return 0; + +error_pan_node: + of_node_put(spi_pan_node); +error_vreg: + mdss_spi_put_dt_vreg_data(&pdev->dev, + &ctrl_pdata->panel_power_data); +error_no_mem: + devm_kfree(&pdev->dev, ctrl_pdata); + return rc; +} + + +static const struct of_device_id mdss_spi_panel_match[] = { + { .compatible = "qcom,mdss-spi-display" }, + {}, +}; + +static struct platform_driver this_driver = { + .probe = mdss_spi_panel_probe, + .driver = { + .name = "spi_panel", + .owner = THIS_MODULE, + .of_match_table = mdss_spi_panel_match, + }, +}; + +static int __init mdss_spi_display_init(void) +{ + int ret; + + ret = platform_driver_register(&this_driver); + return 0; +} +module_init(mdss_spi_display_init); + +MODULE_DEVICE_TABLE(of, mdss_spi_panel_match); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/msm/mdss/mdss_spi_panel.h b/drivers/video/msm/mdss/mdss_spi_panel.h new file mode 100644 index 000000000000..c0df58899ce3 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_spi_panel.h @@ -0,0 +1,148 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __MDSS_SPI_PANEL_H__ +#define __MDSS_SPI_PANEL_H__ + +#if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) && defined(CONFIG_SPI_QUP) +#include +#include +#include +#include +#include + +#include "mdss_panel.h" +#include "mdp3_dma.h" + +#define MDSS_MAX_BL_BRIGHTNESS 255 + +#define MDSS_SPI_RST_SEQ_LEN 10 + +#define NONE_PANEL "none" + +#define CTRL_STATE_UNKNOWN 0x00 +#define CTRL_STATE_PANEL_INIT BIT(0) +#define CTRL_STATE_MDP_ACTIVE BIT(1) + +#define MDSS_PINCTRL_STATE_DEFAULT "mdss_default" +#define MDSS_PINCTRL_STATE_SLEEP "mdss_sleep" +#define SPI_PANEL_TE_TIMEOUT 400 + +enum spi_panel_data_type { + panel_cmd, + panel_parameter, + panel_pixel, + UNKNOWN_FORMAT, +}; + +enum spi_panel_bl_ctrl { + SPI_BL_PWM, + SPI_BL_WLED, + SPI_BL_DCS_CMD, + SPI_UNKNOWN_CTRL, +}; + +struct spi_pinctrl_res { + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; +}; +#define SPI_PANEL_DST_FORMAT_RGB565 0 + +struct spi_ctrl_hdr { + char wait; /* ms */ + char dlen; /* 8 bits */ +}; + +struct spi_cmd_desc { + struct spi_ctrl_hdr dchdr; + char *command; + char *parameter; +}; + +struct spi_panel_cmds { + char *buf; + int blen; + struct spi_cmd_desc *cmds; + int cmd_cnt; +}; + +enum spi_panel_status_mode { + SPI_ESD_REG, + SPI_SEND_PANEL_COMMAND, + SPI_ESD_MAX, +}; + + +struct spi_panel_data { + struct mdss_panel_data panel_data; + struct mdss_util_intf *mdss_util; + struct spi_pinctrl_res pin_res; + struct dss_module_power panel_power_data; + struct completion spi_panel_te; + struct mdp3_notification vsync_client; + unsigned int vsync_status; + int byte_pre_frame; + char *tx_buf; + u8 ctrl_state; + int disp_te_gpio; + int rst_gpio; + int disp_dc_gpio; /* command or data */ + struct spi_panel_cmds on_cmds; + struct spi_panel_cmds off_cmds; + bool (*check_status)(struct spi_panel_data *pdata); + int (*on)(struct mdss_panel_data *pdata); + int (*off)(struct mdss_panel_data *pdata); + struct mutex spi_tx_mutex; + struct pwm_device *pwm_bl; + int bklt_ctrl; /* backlight ctrl */ + bool pwm_pmi; + int pwm_period; + int pwm_pmic_gpio; + int pwm_lpg_chan; + int pwm_enabled; + int bklt_max; + int status_mode; + u32 status_cmds_rlen; + u8 panel_status_reg; + u8 *exp_status_value; + u8 *act_status_value; + unsigned char *return_buf; +}; + +int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata, + char *buf, int len, int stride); +int is_spi_panel_continuous_splash_on(struct mdss_panel_data *pdata); +void mdp3_spi_vsync_enable(struct mdss_panel_data *pdata, + struct mdp3_notification *vsync_client); +void mdp3_check_spi_panel_status(struct work_struct *work, + uint32_t interval); + +#else +static inline int mdss_spi_panel_kickoff(struct mdss_panel_data *pdata, + char *buf, int len, int stride){ + return 0; +} +static inline int is_spi_panel_continuous_splash_on( + struct mdss_panel_data *pdata) +{ + return 0; +} +static inline int mdp3_spi_vsync_enable(struct mdss_panel_data *pdata, + struct mdp3_notification *vsync_client){ + return 0; +} + +#endif/* End of CONFIG_FB_MSM_MDSS_SPI_PANEL && ONFIG_SPI_QUP */ + +#endif /* End of __MDSS_SPI_PANEL_H__ */ -- GitLab From 83d40dfae7314efffacb53114869665d72b31b73 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Thu, 1 Feb 2018 16:55:15 +0800 Subject: [PATCH 2484/5498] ASoC: codec: add hwdep node for digital codec on msm8909w hwdep node is used to put calibration data from acdb files. Add hwdep node for digital codec on msm8909w. Change-Id: Idc9e20bb5da724346edf7d1cdd2cefcffbd5d8be Signed-off-by: Meng Wang --- sound/soc/codecs/msm-digital-cdc.c | 91 +++++++++++++++++++++++++++++- sound/soc/codecs/msm-digital-cdc.h | 11 +++- sound/soc/msm/msm8909.c | 16 ++++++ 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/msm-digital-cdc.c b/sound/soc/codecs/msm-digital-cdc.c index 25b26136c35a..b787ff36fe0f 100644 --- a/sound/soc/codecs/msm-digital-cdc.c +++ b/sound/soc/codecs/msm-digital-cdc.c @@ -39,6 +39,8 @@ #define CF_MIN_3DB_75HZ 0x1 #define CF_MIN_3DB_150HZ 0x2 +#define MSM_DIG_CDC_VERSION_ENTRY_SIZE 32 + static unsigned long rx_digital_gain_reg[] = { MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, @@ -1023,11 +1025,85 @@ static void msm_digit_cdc_update_digit_regulator( dev_err(msm_cdc->dev, "Error: regulator not found:%s\n", name); } +static ssize_t msm_dig_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + char buffer[MSM_DIG_CDC_VERSION_ENTRY_SIZE]; + int len = 0; + + len = snprintf(buffer, sizeof(buffer), "MSM8909_1_0\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops msm_dig_codec_info_ops = { + .read = msm_dig_codec_version_read, +}; + +/* + * msm_dig_codec_info_create_codec_entry - creates msm digital codec module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates msm digital codec module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct msm_dig_priv *msm_dig_cdc; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + msm_dig_cdc->entry = snd_register_module_info(codec_root->module, + "msm_digital_codec", + codec_root); + if (!msm_dig_cdc->entry) { + dev_dbg(codec->dev, "%s: failed to create msm-digital entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + msm_dig_cdc->entry); + if (!version_entry) { + dev_dbg(codec->dev, + "%s: failed to create msm-digital version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = msm_dig_cdc; + version_entry->size = MSM_DIG_CDC_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &msm_dig_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + msm_dig_cdc->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(msm_dig_codec_info_create_codec_entry); + static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) { struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); struct snd_soc_dapm_context *dapm = &codec->dapm; - int i; + int i, ret = 0; msm_dig_cdc->codec = codec; @@ -1054,6 +1130,19 @@ static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) atomic_set(&msm_dig_cdc->on_demand_list[ON_DEMAND_DIGIT].ref, 0); + msm_dig_cdc->fw_data = devm_kzalloc(codec->dev, + sizeof(*(msm_dig_cdc->fw_data)), + GFP_KERNEL); + if (!msm_dig_cdc->fw_data) + return -ENOMEM; + + ret = wcd_cal_create_hwdep(msm_dig_cdc->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + return ret; + } + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); snd_soc_dapm_ignore_suspend(dapm, "ADC1_IN"); diff --git a/sound/soc/codecs/msm-digital-cdc.h b/sound/soc/codecs/msm-digital-cdc.h index 34af32b23827..022b765cd15f 100644 --- a/sound/soc/codecs/msm-digital-cdc.h +++ b/sound/soc/codecs/msm-digital-cdc.h @@ -17,6 +17,7 @@ #include #include #include +#include "wcdcal-hwdep.h" #define HPHL_PA_DISABLE (0x01 << 1) #define HPHR_PA_DISABLE (0x01 << 2) @@ -109,8 +110,6 @@ struct msm_dig_priv { s32 dmic_1_2_clk_cnt; bool dec_active[NUM_DECIMATORS]; int version; - /* Entry for version info */ - struct snd_info_entry *entry; char __iomem *dig_base; struct regmap *regmap; struct notifier_block nblock; @@ -121,6 +120,11 @@ struct msm_dig_priv { struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; u32 num_of_supplies; struct regulator_bulk_data *supplies; + /* cal info for codec */ + struct fw_info *fw_data; + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; }; struct msm_cdc_pdata { @@ -146,6 +150,7 @@ struct msm_asoc_mach_data { struct device_node *pdm_gpio_p; /* used by pinctrl API */ struct device_node *dmic_gpio_p; /* used by pinctrl API */ struct snd_soc_codec *codec; + struct snd_info_entry *codec_root; int mclk_freq; bool native_clk_set; int lb_mode; @@ -169,4 +174,6 @@ struct msm_asoc_mach_data { extern int msm_digcdc_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm); +int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); #endif diff --git a/sound/soc/msm/msm8909.c b/sound/soc/msm/msm8909.c index d548470f3e68..4a7c16d7e139 100644 --- a/sound/soc/msm/msm8909.c +++ b/sound/soc/msm/msm8909.c @@ -73,6 +73,8 @@ static atomic_t quat_mi2s_clk_ref; static atomic_t quin_mi2s_clk_ref; static atomic_t auxpcm_mi2s_clk_ref; +static struct snd_info_entry *codec_root; + static int msm_enable_dig_cdc_clk(struct snd_soc_codec *codec, int enable, bool dapm); static int msm_mclk_event(struct snd_soc_dapm_widget *w, @@ -1201,6 +1203,8 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct snd_card *card; pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev)); @@ -1218,6 +1222,18 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_sync(dapm); + card = rtd->card->snd_card; + if (!codec_root) { + codec_root = snd_register_module_info(card->module, "codecs", + card->proc_root); + if (codec_root) + msm_dig_codec_info_create_codec_entry(codec_root, + codec); + else + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + } + pdata->codec_root = codec_root; return 0; } -- GitLab From a58fcc2e1cd9b49c5f8e77299473da0d9e384b7e Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Tue, 6 Feb 2018 21:19:12 -0800 Subject: [PATCH 2485/5498] ARM: dts: msm: Disable K61 SPI-CAN on mdm9650 CV2X Disable K61 SPI-CAN device node on mdm9650 CV2X boards. Change-Id: If7b9be932d4211134f9c3291e2fdc718ec3aa484 Signed-off-by: Gustavo Solaira --- arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi b/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi index 0dde4a007dfe..c3ac4c86222c 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi +++ b/arch/arm/boot/dts/qcom/mdm9650-cv2x.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -102,6 +102,7 @@ pinctrl-names = "active", "sleep"; pinctrl-0 = <&can_rst_on>; pinctrl-1 = <&can_rst_off>; + status = "disabled"; }; }; -- GitLab From 33fd368597ad615f9f7232ca9daa3ed3fdba1516 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Aug 2015 19:15:30 -0700 Subject: [PATCH 2486/5498] Input: do not emit unneeded EV_SYN when suspending commit 00159f19a5057cb779146afce1cceede692af346 upstream. Do not emit EV_SYN/SYN_REPORT on suspend if there were no keys that are still pressed as we are suspending the device (and in all other cases when input core is forcibly releasing keys via input_dev_release_keys() call). Reviewed-by: Benson Leung Signed-off-by: Dmitry Torokhov Signed-off-by: Bo Hu Signed-off-by: Greg Kroah-Hartman --- drivers/input/input.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/input/input.c b/drivers/input/input.c index 0f175f55782b..34aae7e5498a 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -668,6 +668,7 @@ EXPORT_SYMBOL(input_close_device); */ static void input_dev_release_keys(struct input_dev *dev) { + bool need_sync = false; int code; if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { @@ -675,9 +676,11 @@ static void input_dev_release_keys(struct input_dev *dev) if (is_event_supported(code, dev->keybit, KEY_MAX) && __test_and_clear_bit(code, dev->key)) { input_pass_event(dev, EV_KEY, code, 0); + need_sync = true; } } - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + if (need_sync) + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); } } -- GitLab From f79ad9c84ff551ae15bb85374e554659c263bb31 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Sun, 20 Aug 2017 13:26:04 +0200 Subject: [PATCH 2487/5498] um: link vmlinux with -no-pie commit 883354afbc109c57f925ccc19840055193da0cc0 upstream. Debian's gcc defaults to pie. The global Makefile already defines the -fno-pie option. Link UML dynamic kernel image also with -no-pie to fix the build. Signed-off-by: Thomas Meyer Signed-off-by: Richard Weinberger Cc: Bernie Innocenti Signed-off-by: Greg Kroah-Hartman --- arch/um/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/um/Makefile b/arch/um/Makefile index e4b1a9639c4d..4e161398af93 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -116,7 +116,7 @@ archheaders: archprepare: include/generated/user_constants.h LINK-$(CONFIG_LD_SCRIPT_STATIC) += -static -LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib +LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib $(call cc-option, -no-pie) CFLAGS_NO_HARDENING := $(call cc-option, -fno-PIC,) $(call cc-option, -fno-pic,) \ $(call cc-option, -fno-stack-protector,) \ -- GitLab From db72a03c34b5fb6ab37053ea4dffc37f831892ff Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 31 May 2015 19:50:57 +0200 Subject: [PATCH 2488/5498] um: Stop abusing __KERNEL__ commit 298e20ba8c197e8d429a6c8671550c41c7919033 upstream. Currently UML is abusing __KERNEL__ to distinguish between kernel and host code (os-Linux). It is better to use a custom define such that existing users of __KERNEL__ don't get confused. Signed-off-by: Richard Weinberger Cc: Greg Hackmann Cc: Bernie Innocenti Cc: Lorenzo Colitti Signed-off-by: Greg Kroah-Hartman --- arch/um/Makefile | 7 ++++--- arch/um/drivers/mconsole.h | 2 +- arch/um/include/shared/init.h | 4 ++-- arch/um/include/shared/user.h | 2 +- arch/x86/um/shared/sysdep/tls.h | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/um/Makefile b/arch/um/Makefile index 4e161398af93..150df6b79ec3 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -68,9 +68,10 @@ KBUILD_CFLAGS += $(CFLAGS) $(CFLAGS-y) -D__arch_um__ \ KBUILD_AFLAGS += $(ARCH_INCLUDE) -USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -D__KERNEL__,,\ - $(patsubst -I%,,$(KBUILD_CFLAGS)))) $(ARCH_INCLUDE) $(MODE_INCLUDE) \ - $(filter -I%,$(CFLAGS)) -D_FILE_OFFSET_BITS=64 -idirafter include +USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -I%,,$(KBUILD_CFLAGS))) \ + $(ARCH_INCLUDE) $(MODE_INCLUDE) $(filter -I%,$(CFLAGS)) \ + -D_FILE_OFFSET_BITS=64 -idirafter include \ + -D__KERNEL__ -D__UM_HOST__ #This will adjust *FLAGS accordingly to the platform. include $(srctree)/$(ARCH_DIR)/Makefile-os-$(OS) diff --git a/arch/um/drivers/mconsole.h b/arch/um/drivers/mconsole.h index 8b22535c62ce..44af7379ea19 100644 --- a/arch/um/drivers/mconsole.h +++ b/arch/um/drivers/mconsole.h @@ -7,7 +7,7 @@ #ifndef __MCONSOLE_H__ #define __MCONSOLE_H__ -#ifndef __KERNEL__ +#ifdef __UM_HOST__ #include #define u32 uint32_t #endif diff --git a/arch/um/include/shared/init.h b/arch/um/include/shared/init.h index b3906f860a87..031ad1d111e7 100644 --- a/arch/um/include/shared/init.h +++ b/arch/um/include/shared/init.h @@ -40,7 +40,7 @@ typedef int (*initcall_t)(void); typedef void (*exitcall_t)(void); -#ifndef __KERNEL__ +#ifdef __UM_HOST__ #ifndef __section # define __section(S) __attribute__ ((__section__(#S))) #endif @@ -131,7 +131,7 @@ extern struct uml_param __uml_setup_start, __uml_setup_end; #define __uml_postsetup_call __used __section(.uml.postsetup.init) #define __uml_exit_call __used __section(.uml.exitcall.exit) -#ifndef __KERNEL__ +#ifdef __UM_HOST__ #define __define_initcall(level,fn) \ static initcall_t __initcall_##fn __used \ diff --git a/arch/um/include/shared/user.h b/arch/um/include/shared/user.h index cef068563336..4cff19f6207a 100644 --- a/arch/um/include/shared/user.h +++ b/arch/um/include/shared/user.h @@ -17,7 +17,7 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* This is to get size_t */ -#ifdef __KERNEL__ +#ifndef __UM_HOST__ #include #else #include diff --git a/arch/x86/um/shared/sysdep/tls.h b/arch/x86/um/shared/sysdep/tls.h index 27cce00c6b30..a682db13df23 100644 --- a/arch/x86/um/shared/sysdep/tls.h +++ b/arch/x86/um/shared/sysdep/tls.h @@ -1,7 +1,7 @@ #ifndef _SYSDEP_TLS_H #define _SYSDEP_TLS_H -# ifndef __KERNEL__ +#ifdef __UM_HOST__ /* Change name to avoid conflicts with the original one from , which * may be named user_desc (but in 2.4 and in header matching its API was named @@ -22,11 +22,11 @@ typedef struct um_dup_user_desc { #endif } user_desc_t; -# else /* __KERNEL__ */ +#else /* __UM_HOST__ */ typedef struct user_desc user_desc_t; -# endif /* __KERNEL__ */ +#endif /* __UM_HOST__ */ extern int os_set_thread_area(user_desc_t *info, int pid); extern int os_get_thread_area(user_desc_t *info, int pid); -- GitLab From b1cf5261fedb0cb74f18279ea6f206529d381ff5 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 31 May 2015 22:15:58 +0200 Subject: [PATCH 2489/5498] um: Remove copy&paste code from init.h commit 30b11ee9ae23d78de66b9ae315880af17a64ba83 upstream. As we got rid of the __KERNEL__ abuse, we can directly include linux/compiler.h now. This also allows gcc 5 to build UML. Reported-by: Hans-Werner Hilse Signed-off-by: Richard Weinberger Cc: Greg Hackmann Cc: Bernie Innocenti Cc: Lorenzo Colitti Signed-off-by: Greg Kroah-Hartman --- arch/um/include/shared/init.h | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/arch/um/include/shared/init.h b/arch/um/include/shared/init.h index 031ad1d111e7..233e2593eee0 100644 --- a/arch/um/include/shared/init.h +++ b/arch/um/include/shared/init.h @@ -40,28 +40,8 @@ typedef int (*initcall_t)(void); typedef void (*exitcall_t)(void); -#ifdef __UM_HOST__ -#ifndef __section -# define __section(S) __attribute__ ((__section__(#S))) -#endif - -#if __GNUC__ == 3 - -#if __GNUC_MINOR__ >= 3 -# define __used __attribute__((__used__)) -#else -# define __used __attribute__((__unused__)) -#endif - -#else -#if __GNUC__ == 4 -# define __used __attribute__((__used__)) -#endif -#endif - -#else #include -#endif + /* These are for everybody (although not all archs will actually discard it in modules) */ #define __init __section(.init.text) -- GitLab From 524a6efbd1234439ba00176006ed95ad7d007da6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 5 Jan 2018 16:26:00 -0800 Subject: [PATCH 2490/5498] loop: fix concurrent lo_open/lo_release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ae6650163c66a7eff1acd6eb8b0f752dcfa8eba5 upstream. 范龙飞 reports that KASAN can report a use-after-free in __lock_acquire. The reason is due to insufficient serialization in lo_release(), which will continue to use the loop device even after it has decremented the lo_refcnt to zero. In the meantime, another process can come in, open the loop device again as it is being shut down. Confusion ensues. Reported-by: 范龙飞 Signed-off-by: Linus Torvalds Signed-off-by: Jens Axboe Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/block/loop.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 6cb1beb47c25..94385b969f67 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1512,9 +1512,8 @@ out: return err; } -static void lo_release(struct gendisk *disk, fmode_t mode) +static void __lo_release(struct loop_device *lo) { - struct loop_device *lo = disk->private_data; int err; mutex_lock(&lo->lo_ctl_mutex); @@ -1542,6 +1541,13 @@ out: mutex_unlock(&lo->lo_ctl_mutex); } +static void lo_release(struct gendisk *disk, fmode_t mode) +{ + mutex_lock(&loop_index_mutex); + __lo_release(disk->private_data); + mutex_unlock(&loop_index_mutex); +} + static const struct block_device_operations lo_fops = { .owner = THIS_MODULE, .open = lo_open, -- GitLab From 6aab3aa52366a0051e5abf8aae4616c42053fd8e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Jan 2018 23:11:03 +0100 Subject: [PATCH 2491/5498] ALSA: seq: Make ioctls race-free commit b3defb791b26ea0683a93a4f49c77ec45ec96f10 upstream. The ALSA sequencer ioctls have no protection against racy calls while the concurrent operations may lead to interfere with each other. As reported recently, for example, the concurrent calls of setting client pool with a combination of write calls may lead to either the unkillable dead-lock or UAF. As a slightly big hammer solution, this patch introduces the mutex to make each ioctl exclusive. Although this may reduce performance via parallel ioctl calls, usually it's not demanded for sequencer usages, hence it should be negligible. Reported-by: Luo Quan Reviewed-by: Kees Cook Reviewed-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai [bwh: Backported to 4.4: ioctl dispatch is done from snd_seq_do_ioctl(); take the mutex and add ret variable there.] Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_clientmgr.c | 10 ++++++++-- sound/core/seq/seq_clientmgr.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 0a52e377a617..83bf65ae8251 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -236,6 +236,7 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize) rwlock_init(&client->ports_lock); mutex_init(&client->ports_mutex); INIT_LIST_HEAD(&client->ports_list_head); + mutex_init(&client->ioctl_mutex); /* find free slot in the client table */ spin_lock_irqsave(&clients_lock, flags); @@ -2200,6 +2201,7 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, void __user *arg) { struct seq_ioctl_table *p; + int ret; switch (cmd) { case SNDRV_SEQ_IOCTL_PVERSION: @@ -2213,8 +2215,12 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, if (! arg) return -EFAULT; for (p = ioctl_tables; p->cmd; p++) { - if (p->cmd == cmd) - return p->func(client, arg); + if (p->cmd == cmd) { + mutex_lock(&client->ioctl_mutex); + ret = p->func(client, arg); + mutex_unlock(&client->ioctl_mutex); + return ret; + } } pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n", cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index 20f0a725ec7d..91f8f165bfdc 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -59,6 +59,7 @@ struct snd_seq_client { struct list_head ports_list_head; rwlock_t ports_lock; struct mutex ports_mutex; + struct mutex ioctl_mutex; int convert32; /* convert 32->64bit */ /* output pool */ -- GitLab From d7e2fda23eb9e3625a70a48e2cb7a84ef0b2c6e2 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Mon, 20 Nov 2017 12:54:52 -0800 Subject: [PATCH 2492/5498] gpio: iop: add missing MODULE_DESCRIPTION/AUTHOR/LICENSE commit 97b03136e1b637d7a9d2274c099e44ecf23f1103 upstream. This change resolves a new compile-time warning when built as a loadable module: WARNING: modpost: missing MODULE_LICENSE() in drivers/gpio/gpio-iop.o see include/linux/module.h for more information This adds the license as "GPL", which matches the header of the file. MODULE_DESCRIPTION and MODULE_AUTHOR are also added. Signed-off-by: Jesse Chan Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-iop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 0a5e9d3f308c..2978aa5ae282 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -130,3 +130,7 @@ static int __init iop3xx_gpio_init(void) return platform_driver_register(&iop3xx_gpio_driver); } arch_initcall(iop3xx_gpio_init); + +MODULE_DESCRIPTION("GPIO handling for Intel IOP3xx processors"); +MODULE_AUTHOR("Lennert Buytenhek "); +MODULE_LICENSE("GPL"); -- GitLab From f7c2ce3d42bc18fce799f9144cc28328a896c7b1 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 12 Dec 2017 14:31:30 -0500 Subject: [PATCH 2493/5498] igb: Free IRQs when device is hotplugged commit 888f22931478a05bc81ceb7295c626e1292bf0ed upstream. Recently I got a Caldigit TS3 Thunderbolt 3 dock, and noticed that upon hotplugging my kernel would immediately crash due to igb: [ 680.825801] kernel BUG at drivers/pci/msi.c:352! [ 680.828388] invalid opcode: 0000 [#1] SMP [ 680.829194] Modules linked in: igb(O) thunderbolt i2c_algo_bit joydev vfat fat btusb btrtl btbcm btintel bluetooth ecdh_generic hp_wmi sparse_keymap rfkill wmi_bmof iTCO_wdt intel_rapl x86_pkg_temp_thermal coretemp crc32_pclmul snd_pcm rtsx_pci_ms mei_me snd_timer memstick snd pcspkr mei soundcore i2c_i801 tpm_tis psmouse shpchp wmi tpm_tis_core tpm video hp_wireless acpi_pad rtsx_pci_sdmmc mmc_core crc32c_intel serio_raw rtsx_pci mfd_core xhci_pci xhci_hcd i2c_hid i2c_core [last unloaded: igb] [ 680.831085] CPU: 1 PID: 78 Comm: kworker/u16:1 Tainted: G O 4.15.0-rc3Lyude-Test+ #6 [ 680.831596] Hardware name: HP HP ZBook Studio G4/826B, BIOS P71 Ver. 01.03 06/09/2017 [ 680.832168] Workqueue: kacpi_hotplug acpi_hotplug_work_fn [ 680.832687] RIP: 0010:free_msi_irqs+0x180/0x1b0 [ 680.833271] RSP: 0018:ffffc9000030fbf0 EFLAGS: 00010286 [ 680.833761] RAX: ffff8803405f9c00 RBX: ffff88033e3d2e40 RCX: 000000000000002c [ 680.834278] RDX: 0000000000000000 RSI: 00000000000000ac RDI: ffff880340be2178 [ 680.834832] RBP: 0000000000000000 R08: ffff880340be1ff0 R09: ffff8803405f9c00 [ 680.835342] R10: 0000000000000000 R11: 0000000000000040 R12: ffff88033d63a298 [ 680.835822] R13: ffff88033d63a000 R14: 0000000000000060 R15: ffff880341959000 [ 680.836332] FS: 0000000000000000(0000) GS:ffff88034f440000(0000) knlGS:0000000000000000 [ 680.836817] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 680.837360] CR2: 000055e64044afdf CR3: 0000000001c09002 CR4: 00000000003606e0 [ 680.837954] Call Trace: [ 680.838853] pci_disable_msix+0xce/0xf0 [ 680.839616] igb_reset_interrupt_capability+0x5d/0x60 [igb] [ 680.840278] igb_remove+0x9d/0x110 [igb] [ 680.840764] pci_device_remove+0x36/0xb0 [ 680.841279] device_release_driver_internal+0x157/0x220 [ 680.841739] pci_stop_bus_device+0x7d/0xa0 [ 680.842255] pci_stop_bus_device+0x2b/0xa0 [ 680.842722] pci_stop_bus_device+0x3d/0xa0 [ 680.843189] pci_stop_and_remove_bus_device+0xe/0x20 [ 680.843627] trim_stale_devices+0xf3/0x140 [ 680.844086] trim_stale_devices+0x94/0x140 [ 680.844532] trim_stale_devices+0xa6/0x140 [ 680.845031] ? get_slot_status+0x90/0xc0 [ 680.845536] acpiphp_check_bridge.part.5+0xfe/0x140 [ 680.846021] acpiphp_hotplug_notify+0x175/0x200 [ 680.846581] ? free_bridge+0x100/0x100 [ 680.847113] acpi_device_hotplug+0x8a/0x490 [ 680.847535] acpi_hotplug_work_fn+0x1a/0x30 [ 680.848076] process_one_work+0x182/0x3a0 [ 680.848543] worker_thread+0x2e/0x380 [ 680.848963] ? process_one_work+0x3a0/0x3a0 [ 680.849373] kthread+0x111/0x130 [ 680.849776] ? kthread_create_worker_on_cpu+0x50/0x50 [ 680.850188] ret_from_fork+0x1f/0x30 [ 680.850601] Code: 43 14 85 c0 0f 84 d5 fe ff ff 31 ed eb 0f 83 c5 01 39 6b 14 0f 86 c5 fe ff ff 8b 7b 10 01 ef e8 b7 e4 d2 ff 48 83 78 70 00 74 e3 <0f> 0b 49 8d b5 a0 00 00 00 e8 62 6f d3 ff e9 c7 fe ff ff 48 8b [ 680.851497] RIP: free_msi_irqs+0x180/0x1b0 RSP: ffffc9000030fbf0 As it turns out, normally the freeing of IRQs that would fix this is called inside of the scope of __igb_close(). However, since the device is already gone by the point we try to unregister the netdevice from the driver due to a hotplug we end up seeing that the netif isn't present and thus, forget to free any of the device IRQs. So: make sure that if we're in the process of dismantling the netdev, we always allow __igb_close() to be called so that IRQs may be freed normally. Additionally, only allow igb_close() to be called from __igb_close() if it hasn't already been called for the given adapter. Signed-off-by: Lyude Paul Fixes: 9474933caf21 ("igb: close/suspend race in netif_device_detach") Cc: Todd Fujinaka Cc: Stephen Hemminger Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 3b3911e511ac..1ffad88e8a29 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3172,7 +3172,7 @@ static int __igb_close(struct net_device *netdev, bool suspending) static int igb_close(struct net_device *netdev) { - if (netif_device_present(netdev)) + if (netif_device_present(netdev) || netdev->dismantle) return __igb_close(netdev, false); return 0; } -- GitLab From c786c16d615a1ba42d91858c3a73a1eb434c7f4d Mon Sep 17 00:00:00 2001 From: Liran Alon Date: Sun, 5 Nov 2017 16:56:33 +0200 Subject: [PATCH 2494/5498] KVM: x86: emulator: Return to user-mode on L1 CPL=0 emulation failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1f4dcb3b213235e642088709a1c54964d23365e9 ] On this case, handle_emulation_failure() fills kvm_run with internal-error information which it expects to be delivered to user-mode for further processing. However, the code reports a wrong return-value which makes KVM to never return to user-mode on this scenario. Fixes: 6d77dbfc88e3 ("KVM: inject #UD if instruction emulation fails and exit to userspace") Signed-off-by: Liran Alon Reviewed-by: Nikita Leshenko Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Wanpeng Li Signed-off-by: Radim KrÄmář Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0dcbe370374f..80a83113a1ce 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5109,7 +5109,7 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu) vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; vcpu->run->internal.ndata = 0; - r = EMULATE_FAIL; + r = EMULATE_USER_EXIT; } kvm_queue_exception(vcpu, UD_VECTOR); -- GitLab From 6ca9bcfea80df3c6ad81d2c091155c5204a047c8 Mon Sep 17 00:00:00 2001 From: Liran Alon Date: Sun, 5 Nov 2017 16:56:34 +0200 Subject: [PATCH 2495/5498] KVM: x86: Don't re-execute instruction when not passing CR2 value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9b8ae63798cb97e785a667ff27e43fa6220cb734 ] In case of instruction-decode failure or emulation failure, x86_emulate_instruction() will call reexecute_instruction() which will attempt to use the cr2 value passed to x86_emulate_instruction(). However, when x86_emulate_instruction() is called from emulate_instruction(), cr2 is not passed (passed as 0) and therefore it doesn't make sense to execute reexecute_instruction() logic at all. Fixes: 51d8b66199e9 ("KVM: cleanup emulate_instruction") Signed-off-by: Liran Alon Reviewed-by: Nikita Leshenko Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Wanpeng Li Signed-off-by: Radim KrÄmář Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/kvm_host.h | 3 ++- arch/x86/kvm/vmx.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 306d152336cd..bcae7051aba2 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -849,7 +849,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, unsigned long cr2, static inline int emulate_instruction(struct kvm_vcpu *vcpu, int emulation_type) { - return x86_emulate_instruction(vcpu, 0, emulation_type, NULL, 0); + return x86_emulate_instruction(vcpu, 0, + emulation_type | EMULTYPE_NO_REEXECUTE, NULL, 0); } void kvm_enable_efer_bits(u64); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index c1fd6d3d4394..fedb5c2962f2 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -5707,7 +5707,7 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu) if (test_bit(KVM_REQ_EVENT, &vcpu->requests)) return 1; - err = emulate_instruction(vcpu, EMULTYPE_NO_REEXECUTE); + err = emulate_instruction(vcpu, 0); if (err == EMULATE_USER_EXIT) { ++vcpu->stat.mmio_exits; -- GitLab From f0705804d8ee13573ed34e6f86a5debfb15ba346 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Sun, 5 Nov 2017 16:54:47 -0800 Subject: [PATCH 2496/5498] KVM: X86: Fix operand/address-size during instruction decoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3853be2603191829b442b64dac6ae8ba0c027bf9 ] Pedro reported: During tests that we conducted on KVM, we noticed that executing a "PUSH %ES" instruction under KVM produces different results on both memory and the SP register depending on whether EPT support is enabled. With EPT the SP is reduced by 4 bytes (and the written value is 0-padded) but without EPT support it is only reduced by 2 bytes. The difference can be observed when the CS.DB field is 1 (32-bit) but not when it's 0 (16-bit). The internal segment descriptor cache exist even in real/vm8096 mode. The CS.D also should be respected instead of just default operand/address-size/66H prefix/67H prefix during instruction decoding. This patch fixes it by also adjusting operand/address-size according to CS.D. Reported-by: Pedro Fonseca Tested-by: Pedro Fonseca Cc: Paolo Bonzini Cc: Radim KrÄmář Cc: Nadav Amit Cc: Pedro Fonseca Signed-off-by: Wanpeng Li Reviewed-by: Paolo Bonzini Signed-off-by: Radim KrÄmář Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/emulate.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 852572c971c4..08751c554d35 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -4385,6 +4385,8 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len) bool op_prefix = false; bool has_seg_override = false; struct opcode opcode; + u16 dummy; + struct desc_struct desc; ctxt->memop.type = OP_NONE; ctxt->memopp = NULL; @@ -4403,6 +4405,11 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len) switch (mode) { case X86EMUL_MODE_REAL: case X86EMUL_MODE_VM86: + def_op_bytes = def_ad_bytes = 2; + ctxt->ops->get_segment(ctxt, &dummy, &desc, NULL, VCPU_SREG_CS); + if (desc.d) + def_op_bytes = def_ad_bytes = 4; + break; case X86EMUL_MODE_PROT16: def_op_bytes = def_ad_bytes = 2; break; -- GitLab From ed996209f0209d8863e135252a5c392aa5e775fe Mon Sep 17 00:00:00 2001 From: Michael Lyle Date: Fri, 24 Nov 2017 15:14:27 -0800 Subject: [PATCH 2497/5498] bcache: check return value of register_shrinker [ Upstream commit 6c4ca1e36cdc1a0a7a84797804b87920ccbebf51 ] register_shrinker is now __must_check, so check it to kill a warning. Caller of bch_btree_cache_alloc in super.c appropriately checks return value so this is fully plumbed through. This V2 fixes checkpatch warnings and improves the commit description, as I was too hasty getting the previous version out. Signed-off-by: Michael Lyle Reviewed-by: Vojtech Pavlik Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/md/bcache/btree.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 43829d9493f7..e53ce5e2a28a 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -808,7 +808,10 @@ int bch_btree_cache_alloc(struct cache_set *c) c->shrink.scan_objects = bch_mca_scan; c->shrink.seeks = 4; c->shrink.batch = c->btree_pages * 2; - register_shrinker(&c->shrink); + + if (register_shrinker(&c->shrink)) + pr_warn("bcache: %s: could not register shrinker", + __func__); return 0; } -- GitLab From f3ca475ce752c5f8232b3629010571a978f8f97d Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Tue, 14 Nov 2017 23:20:05 +0800 Subject: [PATCH 2498/5498] mac80211: fix the update of path metric for RANN frame [ Upstream commit fbbdad5edf0bb59786a51b94a9d006bc8c2da9a2 ] The previous path metric update from RANN frame has not considered the own link metric toward the transmitting mesh STA. Fix this. Reported-by: Michael65535 Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mac80211/mesh_hwmp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 214e63b84e5c..4efc60236cdb 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -763,7 +763,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, struct mesh_path *mpath; u8 ttl, flags, hopcount; const u8 *orig_addr; - u32 orig_sn, metric, metric_txsta, interval; + u32 orig_sn, new_metric, orig_metric, last_hop_metric, interval; bool root_is_gate; ttl = rann->rann_ttl; @@ -774,7 +774,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, interval = le32_to_cpu(rann->rann_interval); hopcount = rann->rann_hopcount; hopcount++; - metric = le32_to_cpu(rann->rann_metric); + orig_metric = le32_to_cpu(rann->rann_metric); /* Ignore our own RANNs */ if (ether_addr_equal(orig_addr, sdata->vif.addr)) @@ -791,7 +791,10 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, return; } - metric_txsta = airtime_link_metric_get(local, sta); + last_hop_metric = airtime_link_metric_get(local, sta); + new_metric = orig_metric + last_hop_metric; + if (new_metric < orig_metric) + new_metric = MAX_METRIC; mpath = mesh_path_lookup(sdata, orig_addr); if (!mpath) { @@ -804,7 +807,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, } if (!(SN_LT(mpath->sn, orig_sn)) && - !(mpath->sn == orig_sn && metric < mpath->rann_metric)) { + !(mpath->sn == orig_sn && new_metric < mpath->rann_metric)) { rcu_read_unlock(); return; } @@ -822,7 +825,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, } mpath->sn = orig_sn; - mpath->rann_metric = metric + metric_txsta; + mpath->rann_metric = new_metric; mpath->is_root = true; /* Recording RANNs sender address to send individually * addressed PREQs destined for root mesh STA */ @@ -842,7 +845,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, orig_sn, 0, NULL, 0, broadcast_addr, hopcount, ttl, interval, - metric + metric_txsta, 0, sdata); + new_metric, 0, sdata); } rcu_read_unlock(); -- GitLab From 577a0df9a63fd30c269c78bd0347a9059771bf36 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Mon, 20 Nov 2017 14:52:21 -0800 Subject: [PATCH 2499/5498] KVM: VMX: Fix rflags cache during vCPU reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit c37c28730bb031cc8a44a130c2555c0f3efbe2d0 ] Reported by syzkaller: *** Guest State *** CR0: actual=0x0000000080010031, shadow=0x0000000060000010, gh_mask=fffffffffffffff7 CR4: actual=0x0000000000002061, shadow=0x0000000000000000, gh_mask=ffffffffffffe8f1 CR3 = 0x000000002081e000 RSP = 0x000000000000fffa RIP = 0x0000000000000000 RFLAGS=0x00023000 DR7 = 0x00000000000000 ^^^^^^^^^^ ------------[ cut here ]------------ WARNING: CPU: 6 PID: 24431 at /home/kernel/linux/arch/x86/kvm//x86.c:7302 kvm_arch_vcpu_ioctl_run+0x651/0x2ea0 [kvm] CPU: 6 PID: 24431 Comm: reprotest Tainted: G W OE 4.14.0+ #26 RIP: 0010:kvm_arch_vcpu_ioctl_run+0x651/0x2ea0 [kvm] RSP: 0018:ffff880291d179e0 EFLAGS: 00010202 Call Trace: kvm_vcpu_ioctl+0x479/0x880 [kvm] do_vfs_ioctl+0x142/0x9a0 SyS_ioctl+0x74/0x80 entry_SYSCALL_64_fastpath+0x23/0x9a The failed vmentry is triggered by the following beautified testcase: #include #include #include #include #include #include #include long r[5]; int main() { struct kvm_debugregs dr = { 0 }; r[2] = open("/dev/kvm", O_RDONLY); r[3] = ioctl(r[2], KVM_CREATE_VM, 0); r[4] = ioctl(r[3], KVM_CREATE_VCPU, 7); struct kvm_guest_debug debug = { .control = 0xf0403, .arch = { .debugreg[6] = 0x2, .debugreg[7] = 0x2 } }; ioctl(r[4], KVM_SET_GUEST_DEBUG, &debug); ioctl(r[4], KVM_RUN, 0); } which testcase tries to setup the processor specific debug registers and configure vCPU for handling guest debug events through KVM_SET_GUEST_DEBUG. The KVM_SET_GUEST_DEBUG ioctl will get and set rflags in order to set TF bit if single step is needed. All regs' caches are reset to avail and GUEST_RFLAGS vmcs field is reset to 0x2 during vCPU reset. However, the cache of rflags is not reset during vCPU reset. The function vmx_get_rflags() returns an unreset rflags cache value since the cache is marked avail, it is 0 after boot. Vmentry fails if the rflags reserved bit 1 is 0. This patch fixes it by resetting both the GUEST_RFLAGS vmcs field and its cache to 0x2 during vCPU reset. Reported-by: Dmitry Vyukov Tested-by: Dmitry Vyukov Reviewed-by: David Hildenbrand Cc: Paolo Bonzini Cc: Radim KrÄmář Cc: Nadav Amit Cc: Dmitry Vyukov Signed-off-by: Wanpeng Li Signed-off-by: Paolo Bonzini Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index fedb5c2962f2..9b02ba40fed0 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4569,7 +4569,7 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu) vmcs_writel(GUEST_SYSENTER_ESP, 0); vmcs_writel(GUEST_SYSENTER_EIP, 0); - vmcs_writel(GUEST_RFLAGS, 0x02); + kvm_set_rflags(vcpu, X86_EFLAGS_FIXED); kvm_rip_write(vcpu, 0xfff0); vmcs_writel(GUEST_GDTR_BASE, 0); -- GitLab From f2dc0da2c28194bc7f8722ad54565f90bcbd8b3a Mon Sep 17 00:00:00 2001 From: Eduardo Otubo Date: Thu, 23 Nov 2017 15:18:35 +0100 Subject: [PATCH 2500/5498] xen-netfront: remove warning when unloading module [ Upstream commit 5b5971df3bc2775107ddad164018a8a8db633b81 ] v2: * Replace busy wait with wait_event()/wake_up_all() * Cannot garantee that at the time xennet_remove is called, the xen_netback state will not be XenbusStateClosed, so added a condition for that * There's a small chance for the xen_netback state is XenbusStateUnknown by the time the xen_netfront switches to Closed, so added a condition for that. When unloading module xen_netfront from guest, dmesg would output warning messages like below: [ 105.236836] xen:grant_table: WARNING: g.e. 0x903 still in use! [ 105.236839] deferring g.e. 0x903 (pfn 0x35805) This problem relies on netfront and netback being out of sync. By the time netfront revokes the g.e.'s netback didn't have enough time to free all of them, hence displaying the warnings on dmesg. The trick here is to make netfront to wait until netback frees all the g.e.'s and only then continue to cleanup for the module removal, and this is done by manipulating both device states. Signed-off-by: Eduardo Otubo Acked-by: Juergen Gross Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/xen-netfront.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 8f45be1592ca..e76f46edee08 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -85,6 +85,8 @@ struct netfront_cb { /* IRQ name is queue name with "-tx" or "-rx" appended */ #define IRQ_NAME_SIZE (QUEUE_NAME_SIZE + 3) +static DECLARE_WAIT_QUEUE_HEAD(module_unload_q); + struct netfront_stats { u64 rx_packets; u64 tx_packets; @@ -2080,10 +2082,12 @@ static void netback_changed(struct xenbus_device *dev, break; case XenbusStateClosed: + wake_up_all(&module_unload_q); if (dev->state == XenbusStateClosed) break; /* Missed the backend's CLOSING state -- fallthrough */ case XenbusStateClosing: + wake_up_all(&module_unload_q); xenbus_frontend_closed(dev); break; } @@ -2305,6 +2309,20 @@ static int xennet_remove(struct xenbus_device *dev) dev_dbg(&dev->dev, "%s\n", dev->nodename); + if (xenbus_read_driver_state(dev->otherend) != XenbusStateClosed) { + xenbus_switch_state(dev, XenbusStateClosing); + wait_event(module_unload_q, + xenbus_read_driver_state(dev->otherend) == + XenbusStateClosing); + + xenbus_switch_state(dev, XenbusStateClosed); + wait_event(module_unload_q, + xenbus_read_driver_state(dev->otherend) == + XenbusStateClosed || + xenbus_read_driver_state(dev->otherend) == + XenbusStateUnknown); + } + xennet_disconnect_backend(info); xennet_sysfs_delif(info->netdev); -- GitLab From 1f71aa238fcde9356376d6d885d216adbf19d18a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 3 Nov 2017 08:00:12 -0400 Subject: [PATCH 2501/5498] nfsd: CLOSE SHOULD return the invalid special stateid for NFSv4.x (x>0) [ Upstream commit fb500a7cfee7f2f447d2bbf30cb59629feab6ac1 ] Signed-off-by: Trond Myklebust Signed-off-by: J. Bruce Fields Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d59ce5a665d9..fc486e67db38 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -62,6 +62,9 @@ static const stateid_t zero_stateid = { static const stateid_t currentstateid = { .si_generation = 1, }; +static const stateid_t close_stateid = { + .si_generation = 0xffffffffU, +}; static u64 current_sessionid = 1; @@ -4901,6 +4904,11 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_close_open_stateid(stp); + /* See RFC5661 sectionm 18.2.4 */ + if (stp->st_stid.sc_client->cl_minorversion) + memcpy(&close->cl_stateid, &close_stateid, + sizeof(close->cl_stateid)); + /* put reference from nfs4_preprocess_seqid_op */ nfs4_put_stid(&stp->st_stid); out: -- GitLab From 07397e4f9b4da7de809103278cbd3a82d8793b54 Mon Sep 17 00:00:00 2001 From: Andrew Elble Date: Thu, 9 Nov 2017 13:41:10 -0500 Subject: [PATCH 2502/5498] nfsd: check for use of the closed special stateid [ Upstream commit ae254dac721d44c0bfebe2795df87459e2e88219 ] Prevent the use of the closed (invalid) special stateid by clients. Signed-off-by: Andrew Elble Signed-off-by: J. Bruce Fields Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fc486e67db38..34b718c6ce53 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -71,6 +71,7 @@ static u64 current_sessionid = 1; #define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t))) #define ONE_STATEID(stateid) (!memcmp((stateid), &one_stateid, sizeof(stateid_t))) #define CURRENT_STATEID(stateid) (!memcmp((stateid), ¤tstateid, sizeof(stateid_t))) +#define CLOSE_STATEID(stateid) (!memcmp((stateid), &close_stateid, sizeof(stateid_t))) /* forward declarations */ static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner); @@ -4414,7 +4415,8 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) struct nfs4_stid *s; __be32 status = nfserr_bad_stateid; - if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) + if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) || + CLOSE_STATEID(stateid)) return status; /* Client debugging aid. */ if (!same_clid(&stateid->si_opaque.so_clid, &cl->cl_clientid)) { @@ -4472,7 +4474,8 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, else if (typemask & NFS4_DELEG_STID) typemask |= NFS4_REVOKED_DELEG_STID; - if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) + if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) || + CLOSE_STATEID(stateid)) return nfserr_bad_stateid; status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn); if (status == nfserr_stale_clientid) { -- GitLab From d395c1bb7f92d2bd37a47f28659f49394acf93e6 Mon Sep 17 00:00:00 2001 From: Robert Lippert Date: Mon, 27 Nov 2017 15:51:55 -0800 Subject: [PATCH 2503/5498] hwmon: (pmbus) Use 64bit math for DIRECT format values [ Upstream commit bd467e4eababe4c04272c1e646f066db02734c79 ] Power values in the 100s of watt range can easily blow past 32bit math limits when processing everything in microwatts. Use 64bit math instead to avoid these issues on common 32bit ARM BMC platforms. Fixes: 442aba78728e ("hwmon: PMBus device driver") Signed-off-by: Robert Lippert Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/pmbus_core.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 291d11fe93e7..6f3fabb5350f 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -443,8 +444,8 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, static long pmbus_reg2data_direct(struct pmbus_data *data, struct pmbus_sensor *sensor) { - long val = (s16) sensor->data; - long m, b, R; + s64 b, val = (s16)sensor->data; + s32 m, R; m = data->info->m[sensor->class]; b = data->info->b[sensor->class]; @@ -472,11 +473,12 @@ static long pmbus_reg2data_direct(struct pmbus_data *data, R--; } while (R < 0) { - val = DIV_ROUND_CLOSEST(val, 10); + val = div_s64(val + 5LL, 10L); /* round closest */ R++; } - return (val - b) / m; + val = div_s64(val - b, m); + return clamp_val(val, LONG_MIN, LONG_MAX); } /* @@ -588,7 +590,8 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, static u16 pmbus_data2reg_direct(struct pmbus_data *data, struct pmbus_sensor *sensor, long val) { - long m, b, R; + s64 b, val64 = val; + s32 m, R; m = data->info->m[sensor->class]; b = data->info->b[sensor->class]; @@ -605,18 +608,18 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, R -= 3; /* Adjust R and b for data in milli-units */ b *= 1000; } - val = val * m + b; + val64 = val64 * m + b; while (R > 0) { - val *= 10; + val64 *= 10; R--; } while (R < 0) { - val = DIV_ROUND_CLOSEST(val, 10); + val64 = div_s64(val64 + 5LL, 10L); /* round closest */ R++; } - return val; + return (u16)clamp_val(val64, S16_MIN, S16_MAX); } static u16 pmbus_data2reg_vid(struct pmbus_data *data, -- GitLab From d5b5f8b30aee9939e837817bb9d0de317160482e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 29 Nov 2017 11:01:09 +0100 Subject: [PATCH 2504/5498] net: ethernet: xilinx: Mark XILINX_LL_TEMAC broken on 64-bit [ Upstream commit 15bfe05c8d6386f1a90e9340d15336e85e32aad6 ] On 64-bit (e.g. powerpc64/allmodconfig): drivers/net/ethernet/xilinx/ll_temac_main.c: In function 'temac_start_xmit_done': drivers/net/ethernet/xilinx/ll_temac_main.c:633:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] dev_kfree_skb_irq((struct sk_buff *)cur_p->app4); ^ cdmac_bd.app4 is u32, so it is too small to hold a kernel pointer. Note that several other fields in struct cdmac_bd are also too small to hold physical addresses on 64-bit platforms. Signed-off-by: Geert Uytterhoeven Signed-off-by: David S. Miller Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/xilinx/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 7b90a5eba099..9d6c252c1911 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -36,6 +36,7 @@ config XILINX_AXI_EMAC config XILINX_LL_TEMAC tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" depends on (PPC || MICROBLAZE) + depends on !64BIT || BROKEN select PHYLIB ---help--- This driver supports the Xilinx 10/100/1000 LocalLink TEMAC -- GitLab From 33e592e7a793bd73c59c53f2cc4f0ca25c978c69 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 29 Nov 2017 22:34:50 +0900 Subject: [PATCH 2505/5498] quota: Check for register_shrinker() failure. [ Upstream commit 88bc0ede8d35edc969350852894dc864a2dc1859 ] register_shrinker() might return -ENOMEM error since Linux 3.12. Call panic() as with other failure checks in this function if register_shrinker() failed. Fixes: 1d3d4437eae1 ("vmscan: per-node deferred work") Signed-off-by: Tetsuo Handa Cc: Jan Kara Cc: Michal Hocko Reviewed-by: Michal Hocko Signed-off-by: Jan Kara Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/quota/dquot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 18aaccb290c2..a9b448e8f92d 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2730,7 +2730,8 @@ static int __init dquot_init(void) printk("Dquot-cache hash table entries: %ld (order %ld, %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order)); - register_shrinker(&dqcache_shrinker); + if (register_shrinker(&dqcache_shrinker)) + panic("Cannot register dquot shrinker"); return 0; } -- GitLab From 0bac8214c01f7dfbb2a3de94ac20bb2366edddb2 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 20 Nov 2017 08:12:29 -0600 Subject: [PATCH 2506/5498] scsi: ufs: ufshcd: fix potential NULL pointer dereference in ufshcd_config_vreg [ Upstream commit 727535903bea924c4f73abb202c4b3e85fff0ca4 ] _vreg_ is being dereferenced before it is null checked, hence there is a potential null pointer dereference. Fix this by moving the pointer dereference after _vreg_ has been null checked. This issue was detected with the help of Coccinelle. Fixes: aa4976130934 ("ufs: Add regulator enable support") Signed-off-by: Gustavo A. R. Silva Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/ufs/ufshcd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 605ca60e8a10..64ee59685aac 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4287,12 +4287,15 @@ static int ufshcd_config_vreg(struct device *dev, struct ufs_vreg *vreg, bool on) { int ret = 0; - struct regulator *reg = vreg->reg; - const char *name = vreg->name; + struct regulator *reg; + const char *name; int min_uV, uA_load; BUG_ON(!vreg); + reg = vreg->reg; + name = vreg->name; + if (regulator_count_voltages(reg) > 0) { min_uV = on ? vreg->min_uV : 0; ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); -- GitLab From aba82d11275ae37564fcb2dd6a4e611795efd0d0 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 16 Apr 2017 02:51:16 -0400 Subject: [PATCH 2507/5498] media: usbtv: add a new usbid [ Upstream commit 04226916d2360f56d57ad00bc48d2d1854d1e0b0 ] A new usbid of UTV007 is found in a newly bought device. The usbid is 1f71:3301. The ID on the chip is: UTV007 A89029.1 1520L18K1 Both video and audio is tested with the modified usbtv driver. Signed-off-by: Icenowy Zheng Acked-by: Lubomir Rintel Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/usbtv/usbtv-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 29428bef272c..3bbc77aa6a33 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -127,6 +127,7 @@ static void usbtv_disconnect(struct usb_interface *intf) static struct usb_device_id usbtv_id_table[] = { { USB_DEVICE(0x1b71, 0x3002) }, + { USB_DEVICE(0x1f71, 0x3301) }, {} }; MODULE_DEVICE_TABLE(usb, usbtv_id_table); -- GitLab From 5becc7c07bcca18bb2c29788cc1e98596fcca96c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 14 Nov 2017 16:18:28 +0000 Subject: [PATCH 2508/5498] usb: gadget: don't dereference g until after it has been null checked [ Upstream commit b2fc059fa549fe6881d4c1f8d698b0f50bcd16ec ] Avoid dereferencing pointer g until after g has been sanity null checked; move the assignment of cdev much later when it is required into a more local scope. Detected by CoverityScan, CID#1222135 ("Dereference before null check") Fixes: b785ea7ce662 ("usb: gadget: composite: fix ep->maxburst initialization") Signed-off-by: Colin Ian King Signed-off-by: Felipe Balbi Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f24adf48b0cc..0056ca7e588c 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -103,7 +103,6 @@ int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f, struct usb_ep *_ep) { - struct usb_composite_dev *cdev = get_gadget_data(g); struct usb_endpoint_descriptor *chosen_desc = NULL; struct usb_descriptor_header **speed_desc = NULL; @@ -170,8 +169,12 @@ ep_found: _ep->maxburst = comp_desc->bMaxBurst + 1; break; default: - if (comp_desc->bMaxBurst != 0) + if (comp_desc->bMaxBurst != 0) { + struct usb_composite_dev *cdev; + + cdev = get_gadget_data(g); ERROR(cdev, "ep0 bMaxBurst must be 0\n"); + } _ep->maxburst = 1; break; } -- GitLab From c478e3e8e93e4339439dc628d3b3ec6c2aba9ae3 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 25 Nov 2017 13:32:38 -0600 Subject: [PATCH 2509/5498] staging: rtl8188eu: Fix incorrect response to SIOCGIWESSID [ Upstream commit b77992d2df9e47144354d1b25328b180afa33442 ] When not associated with an AP, wifi device drivers should respond to the SIOCGIWESSID ioctl with a zero-length string for the SSID, which is the behavior expected by dhcpcd. Currently, this driver returns an error code (-1) from the ioctl call, which causes dhcpcd to assume that the device is not a wireless interface and therefore it fails to work correctly with it thereafter. This problem was reported and tested at https://github.com/lwfinger/rtl8188eu/issues/234. Signed-off-by: Larry Finger Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8188eu/os_dep/ioctl_linux.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c index d598fec4abbf..0bfb4fe8a86e 100644 --- a/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c +++ b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c @@ -1396,19 +1396,13 @@ static int rtw_wx_get_essid(struct net_device *dev, if ((check_fwstate(pmlmepriv, _FW_LINKED)) || (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE))) { len = pcur_bss->Ssid.SsidLength; - - wrqu->essid.length = len; - memcpy(extra, pcur_bss->Ssid.Ssid, len); - - wrqu->essid.flags = 1; } else { - ret = -1; - goto exit; + len = 0; + *extra = 0; } - -exit: - + wrqu->essid.length = len; + wrqu->essid.flags = 1; return ret; } -- GitLab From af5850d18ef65eb023bba1b79e28fbf3318e3d51 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 25 Jan 2018 09:48:55 +0100 Subject: [PATCH 2510/5498] USB: serial: pl2303: new device id for Chilitag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d08dd3f3dd2ae351b793fc5b76abdbf0fd317b12 upstream. This adds a new device id for Chilitag devices to the pl2303 driver. Reported-by: "Chu.Mike [朱堅宜]" Acked-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 1 + drivers/usb/serial/pl2303.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 534c308c6b33..387dde4abb57 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -39,6 +39,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_CHILITAG) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index e3b7af8adfb7..6184bd4d3777 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -17,6 +17,7 @@ #define PL2303_PRODUCT_ID_DCU11 0x1234 #define PL2303_PRODUCT_ID_PHAROS 0xaaa0 #define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 +#define PL2303_PRODUCT_ID_CHILITAG 0xaaa8 #define PL2303_PRODUCT_ID_ALDIGA 0x0611 #define PL2303_PRODUCT_ID_MMX 0x0612 #define PL2303_PRODUCT_ID_GPRS 0x0609 -- GitLab From 0a5e1547d1351bc4897066c330ce6af7f9962c59 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 14 Jan 2018 16:09:00 +0100 Subject: [PATCH 2511/5498] USB: cdc-acm: Do not log urb submission errors on disconnect commit f0386c083c2ce85284dc0b419d7b89c8e567c09f upstream. When disconnected sometimes the cdc-acm driver logs errors like these: [20278.039417] cdc_acm 2-2:2.1: urb 9 failed submission with -19 [20278.042924] cdc_acm 2-2:2.1: urb 10 failed submission with -19 [20278.046449] cdc_acm 2-2:2.1: urb 11 failed submission with -19 [20278.049920] cdc_acm 2-2:2.1: urb 12 failed submission with -19 [20278.053442] cdc_acm 2-2:2.1: urb 13 failed submission with -19 [20278.056915] cdc_acm 2-2:2.1: urb 14 failed submission with -19 [20278.060418] cdc_acm 2-2:2.1: urb 15 failed submission with -19 Silence these by not logging errors when the result is -ENODEV. Signed-off-by: Hans de Goede Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index a5e94b8fa2dc..4ae83f299658 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -381,7 +381,7 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) res = usb_submit_urb(acm->read_urbs[index], mem_flags); if (res) { - if (res != -EPERM) { + if (res != -EPERM && res != -ENODEV) { dev_err(&acm->data->dev, "%s - usb_submit_urb failed: %d\n", __func__, res); -- GitLab From d1cfbc6e33647497f08b3d7bcceb400f49802bdb Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 18 Jan 2018 12:13:45 +0100 Subject: [PATCH 2512/5498] CDC-ACM: apply quirk for card reader commit df1cc78a52491f71d8170d513d0f6f114faa1bda upstream. This devices drops random bytes from messages if you talk to it too fast. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 4ae83f299658..e01d39509172 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1712,6 +1712,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ .driver_info = SINGLE_RX_URB, /* firmware bug */ }, + { USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */ + .driver_info = SINGLE_RX_URB, + }, { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- GitLab From 464bab18479200df3582da84f4cf6861186e7a9a Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 13 Dec 2017 20:34:36 +0800 Subject: [PATCH 2513/5498] USB: serial: io_edgeport: fix possible sleep-in-atomic commit c7b8f77872c73f69a16528a9eb87afefcccdc18b upstream. According to drivers/usb/serial/io_edgeport.c, the driver may sleep under a spinlock. The function call path is: edge_bulk_in_callback (acquire the spinlock) process_rcvd_data process_rcvd_status change_port_settings send_iosp_ext_cmd write_cmd_usb usb_kill_urb --> may sleep To fix it, the redundant usb_kill_urb() is removed from the error path after usb_submit_urb() fails. This possible bug is found by my static analysis tool (DSAC) and checked by my code review. Signed-off-by: Jia-Ju Bai Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index f35befe3bccf..eef2cbb001ea 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2219,7 +2219,6 @@ static int write_cmd_usb(struct edgeport_port *edge_port, /* something went wrong */ dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __func__, status); - usb_kill_urb(urb); usb_free_urb(urb); atomic_dec(&CmdUrbs); return status; -- GitLab From 96ea4192d4f8f69d960090568601c2f51d83c800 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 17 Jan 2018 12:07:30 -0700 Subject: [PATCH 2514/5498] usbip: prevent bind loops on devices attached to vhci_hcd commit ef54cf0c600fb8f5737fb001a9e357edda1a1de8 upstream. usbip host binds to devices attached to vhci_hcd on the same server when user does attach over localhost or specifies the server as the remote. usbip attach -r localhost -b busid or usbip attach -r servername (or server IP) Unbind followed by bind works, however device is left in a bad state with accesses via the attached busid result in errors and system hangs during shutdown. Fix it to check and bail out if the device is already attached to vhci_hcd. Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/src/usbip_bind.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/usb/usbip/src/usbip_bind.c b/tools/usb/usbip/src/usbip_bind.c index fa46141ae68b..e121cfb1746a 100644 --- a/tools/usb/usbip/src/usbip_bind.c +++ b/tools/usb/usbip/src/usbip_bind.c @@ -144,6 +144,7 @@ static int bind_device(char *busid) int rc; struct udev *udev; struct udev_device *dev; + const char *devpath; /* Check whether the device with this bus ID exists. */ udev = udev_new(); @@ -152,8 +153,16 @@ static int bind_device(char *busid) err("device with the specified bus ID does not exist"); return -1; } + devpath = udev_device_get_devpath(dev); udev_unref(udev); + /* If the device is already attached to vhci_hcd - bail out */ + if (strstr(devpath, USBIP_VHCI_DRV_NAME)) { + err("bind loop detected: device: %s is attached to %s\n", + devpath, USBIP_VHCI_DRV_NAME); + return -1; + } + rc = unbind_other(busid); if (rc == UNBIND_ST_FAILED) { err("could not unbind driver from device on busid %s", busid); -- GitLab From 466d964b7a196b3e5a6657678d983f5f97909d3f Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 17 Jan 2018 12:08:03 -0700 Subject: [PATCH 2515/5498] usbip: list: don't list devices attached to vhci_hcd commit ef824501f50846589f02173d73ce3fe6021a9d2a upstream. usbip host lists devices attached to vhci_hcd on the same server when user does attach over localhost or specifies the server as the remote. usbip attach -r localhost -b busid or usbip attach -r servername (or server IP) Fix it to check and not list devices that are attached to vhci_hcd. Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/src/usbip_list.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/usb/usbip/src/usbip_list.c b/tools/usb/usbip/src/usbip_list.c index d5ce34a410e7..ac6081c3db82 100644 --- a/tools/usb/usbip/src/usbip_list.c +++ b/tools/usb/usbip/src/usbip_list.c @@ -180,6 +180,7 @@ static int list_devices(bool parsable) const char *busid; char product_name[128]; int ret = -1; + const char *devpath; /* Create libudev context. */ udev = udev_new(); @@ -202,6 +203,14 @@ static int list_devices(bool parsable) path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, path); + /* Ignore devices attached to vhci_hcd */ + devpath = udev_device_get_devpath(dev); + if (strstr(devpath, USBIP_VHCI_DRV_NAME)) { + dbg("Skip the device %s already attached to %s\n", + devpath, USBIP_VHCI_DRV_NAME); + continue; + } + /* Get device information. */ idVendor = udev_device_get_sysattr_value(dev, "idVendor"); idProduct = udev_device_get_sysattr_value(dev, "idProduct"); -- GitLab From f180fa08aa474626b6853d8dc4e4ec2e8e4b3fc3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 18 Jan 2018 14:46:41 +1100 Subject: [PATCH 2516/5498] USB: serial: simple: add Motorola Tetra driver commit 46fe895e22ab3845515ec06b01eaf1282b342e29 upstream. Add new Motorola Tetra (simple) driver for Motorola Solutions TETRA PEI devices. D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=0cad ProdID=9011 Rev=24.16 S: Manufacturer=Motorola Solutions Inc. S: Product=Motorola Solutions TETRA PEI interface C: #Ifs= 2 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) I: If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) Note that these devices do not support the CDC SET_CONTROL_LINE_STATE request (for any interface). Reported-by: Max Schulze Tested-by: Max Schulze Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 1 + drivers/usb/serial/usb-serial-simple.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index a69f7cd9d0bf..1594e811ab00 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -62,6 +62,7 @@ config USB_SERIAL_SIMPLE - Fundamental Software dongle. - HP4x calculators - a number of Motorola phones + - Motorola Tetra devices - Novatel Wireless GPS receivers - Siemens USB/MPI adapter. - ViVOtech ViVOpay USB device. diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index 40de275cddf4..35f6b121f9d8 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -72,6 +72,11 @@ DEVICE(vivopay, VIVOPAY_IDS); { USB_DEVICE(0x22b8, 0x2c64) } /* Motorola V950 phone */ DEVICE(moto_modem, MOTO_IDS); +/* Motorola Tetra driver */ +#define MOTOROLA_TETRA_IDS() \ + { USB_DEVICE(0x0cad, 0x9011) } /* Motorola Solutions TETRA PEI */ +DEVICE(motorola_tetra, MOTOROLA_TETRA_IDS); + /* Novatel Wireless GPS driver */ #define NOVATEL_IDS() \ { USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */ @@ -101,6 +106,7 @@ static struct usb_serial_driver * const serial_drivers[] = { &flashloader_device, &vivopay_device, &moto_modem_device, + &motorola_tetra_device, &novatel_gps_device, &hp4x_device, &suunto_device, @@ -115,6 +121,7 @@ static const struct usb_device_id id_table[] = { FLASHLOADER_IDS(), VIVOPAY_IDS(), MOTO_IDS(), + MOTOROLA_TETRA_IDS(), NOVATEL_IDS(), HP4X_IDS(), SUUNTO_IDS(), -- GitLab From 512b79f1410fd05c2c7f2aab9fb4b0050560db89 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Tue, 9 Jan 2018 12:30:53 +0530 Subject: [PATCH 2517/5498] usb: f_fs: Prevent gadget unbind if it is already unbound commit ce5bf9a50daf2d9078b505aca1cea22e88ecb94a upstream. Upon usb composition switch there is possibility of ep0 file release happening after gadget driver bind. In case of composition switch from adb to a non-adb composition gadget will never gets bound again resulting into failure of usb device enumeration. Fix this issue by checking FFS_FL_BOUND flag and avoid extra gadget driver unbind if it is already done as part of composition switch. This fixes adb reconnection error reported on Android running v4.4 and above kernel versions. Verified on Hikey running vanilla v4.15-rc7 + few out of tree Mali patches. Reviewed-at: https://android-review.googlesource.com/#/c/582632/ Cc: Felipe Balbi Cc: Greg KH Cc: Michal Nazarewicz Cc: John Stultz Cc: Dmitry Shmidt Cc: Badhri Cc: Android Kernel Team Signed-off-by: Hemant Kumar [AmitP: Cherry-picked it from android-4.14 and updated the commit log] Signed-off-by: Amit Pundir Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 2c46089ac423..88aa287f10ea 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3438,7 +3438,8 @@ static void ffs_closed(struct ffs_data *ffs) ci = opts->func_inst.group.cg_item.ci_parent->ci_parent; ffs_dev_unlock(); - unregister_gadget_item(ci); + if (test_bit(FFS_FL_BOUND, &ffs->flags)) + unregister_gadget_item(ci); return; done: ffs_dev_unlock(); -- GitLab From fc6fd81a9e56b38cac9c37d557331c28daba1f4b Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 11 Jan 2018 13:10:16 +0100 Subject: [PATCH 2518/5498] usb: uas: unconditionally bring back host after reset commit cbeef22fd611c4f47c494b821b2b105b8af970bb upstream. Quoting Hans: If we return 1 from our post_reset handler, then our disconnect handler will be called immediately afterwards. Since pre_reset blocks all scsi requests our disconnect handler will then hang in the scsi_remove_host call. This is esp. bad because our disconnect handler hanging for ever also stops the USB subsys from enumerating any new USB devices, causes commands like lsusb to hang, etc. In practice this happens when unplugging some uas devices because the hub code may see the device as needing a warm-reset and calls usb_reset_device before seeing the disconnect. In this case uas_configure_endpoints fails with -ENODEV. We do not want to print an error for this, so this commit also silences the shost_printk for -ENODEV. ENDQUOTE However, if we do that we better drop any unconditional execution and report to the SCSI subsystem that we have undergone a reset but we are not operational now. Signed-off-by: Oliver Neukum Reported-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 017278ec4e03..7d535f1ddecc 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1067,20 +1067,19 @@ static int uas_post_reset(struct usb_interface *intf) return 0; err = uas_configure_endpoints(devinfo); - if (err) { + if (err && err != ENODEV) shost_printk(KERN_ERR, shost, "%s: alloc streams error %d after reset", __func__, err); - return 1; - } + /* we must unblock the host in every case lest we deadlock */ spin_lock_irqsave(shost->host_lock, flags); scsi_report_bus_reset(shost, 0); spin_unlock_irqrestore(shost->host_lock, flags); scsi_unblock_requests(shost); - return 0; + return err ? 1 : 0; } static int uas_suspend(struct usb_interface *intf, pm_message_t message) -- GitLab From 669d070536f377c2dd21c3d7488539239fd3f7f5 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 1 Feb 2018 07:37:04 -0800 Subject: [PATCH 2519/5498] selinux: general protection fault in sock_has_perm In the absence of commit a4298e4522d6 ("net: add SOCK_RCU_FREE socket flag") and all the associated infrastructure changes to take advantage of a RCU grace period before freeing, there is a heightened possibility that a security check is performed while an ill-timed setsockopt call races in from user space. It then is prudent to null check sk_security, and if the case, reject the permissions. Because of the nature of this problem, hard to duplicate, no clear path, this patch is a simplified band-aid for stable trees lacking the infrastructure for the series of commits leading up to providing a suitable RCU grace period. This adjustment is orthogonal to infrastructure improvements that may nullify the needed check, but could be added as good code hygiene in all trees. general protection fault: 0000 [#1] PREEMPT SMP KASAN CPU: 1 PID: 14233 Comm: syz-executor2 Not tainted 4.4.112-g5f6325b #28 task: ffff8801d1095f00 task.stack: ffff8800b5950000 RIP: 0010:[] [] sock_has_perm+0x1fe/0x3e0 security/selinux/hooks.c:4069 RSP: 0018:ffff8800b5957ce0 EFLAGS: 00010202 RAX: dffffc0000000000 RBX: 1ffff10016b2af9f RCX: ffffffff81b69b51 RDX: 0000000000000002 RSI: 0000000000000000 RDI: 0000000000000010 RBP: ffff8800b5957de0 R08: 0000000000000001 R09: 0000000000000001 R10: 0000000000000000 R11: 1ffff10016b2af68 R12: ffff8800b5957db8 R13: 0000000000000000 R14: ffff8800b7259f40 R15: 00000000000000d7 FS: 00007f72f5ae2700(0000) GS:ffff8801db300000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000a2fa38 CR3: 00000001d7980000 CR4: 0000000000160670 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Stack: ffffffff81b69a1f ffff8800b5957d58 00008000b5957d30 0000000041b58ab3 ffffffff83fc82f2 ffffffff81b69980 0000000000000246 ffff8801d1096770 ffff8801d3165668 ffffffff8157844b ffff8801d1095f00 ffff880000000001 Call Trace: [] selinux_socket_setsockopt+0x4d/0x80 security/selinux/hooks.c:4338 [] security_socket_setsockopt+0x7d/0xb0 security/security.c:1257 [] SYSC_setsockopt net/socket.c:1757 [inline] [] SyS_setsockopt+0xe8/0x250 net/socket.c:1746 [] entry_SYSCALL_64_fastpath+0x16/0x92 Code: c2 42 9b b6 81 be 01 00 00 00 48 c7 c7 a0 cb 2b 84 e8 f7 2f 6d ff 49 8d 7d 10 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <0f> b6 04 02 84 c0 74 08 3c 03 0f 8e 83 01 00 00 41 8b 75 10 31 RIP [] sock_has_perm+0x1fe/0x3e0 security/selinux/hooks.c:4069 RSP ---[ end trace 7b5aaf788fef6174 ]--- Signed-off-by: Mark Salyzyn Acked-by: Paul Moore Cc: Eric Dumazet Cc: Stephen Smalley Cc: selinux@tycho.nsa.gov Cc: linux-security-module@vger.kernel.org Cc: Eric Paris Cc: Serge E. Hallyn Cc: linux-kernel@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- security/selinux/hooks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index be04b056c1bd..8223fe463fa3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3969,6 +3969,8 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) struct lsm_network_audit net = {0,}; u32 tsid = task_sid(task); + if (!sksec) + return -EFAULT; if (sksec->sid == SECINITSID_KERNEL) return 0; -- GitLab From 28ae79a518421348abfc2a2dffd6a6b6e3699476 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 7 Jan 2018 15:05:49 +0100 Subject: [PATCH 2520/5498] spi: imx: do not access registers while clocks disabled commit d593574aff0ab846136190b1729c151c736727ec upstream. Since clocks are disabled except during message transfer clocks are also disabled when spi_imx_remove gets called. Accessing registers leads to a freeeze at least on a i.MX 6ULL. Enable clocks before disabling accessing the MXC_CSPICTRL register. Fixes: 9e556dcc55774 ("spi: spi-imx: only enable the clocks when we start to transfer a message") Signed-off-by: Stefan Agner Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-imx.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index bf0effb86137..4c5e43f6f7f6 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1220,12 +1220,23 @@ static int spi_imx_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct spi_imx_data *spi_imx = spi_master_get_devdata(master); + int ret; spi_bitbang_stop(&spi_imx->bitbang); + ret = clk_enable(spi_imx->clk_per); + if (ret) + return ret; + + ret = clk_enable(spi_imx->clk_ipg); + if (ret) { + clk_disable(spi_imx->clk_per); + return ret; + } + writel(0, spi_imx->base + MXC_CSPICTRL); - clk_unprepare(spi_imx->clk_ipg); - clk_unprepare(spi_imx->clk_per); + clk_disable_unprepare(spi_imx->clk_ipg); + clk_disable_unprepare(spi_imx->clk_per); spi_imx_sdma_exit(spi_imx); spi_master_put(master); -- GitLab From e69660008e384de9d2da8c6d8c24ea1897850d59 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Sun, 19 Nov 2017 23:45:49 -0800 Subject: [PATCH 2521/5498] ASoC: pcm512x: add missing MODULE_DESCRIPTION/AUTHOR/LICENSE commit 0cab20cec0b663b7be8e2be5998d5a4113647f86 upstream. This change resolves a new compile-time warning when built as a loadable module: WARNING: modpost: missing MODULE_LICENSE() in sound/soc/codecs/snd-soc-pcm512x-spi.o see include/linux/module.h for more information This adds the license as "GPL v2", which matches the header of the file. MODULE_DESCRIPTION and MODULE_AUTHOR are also added. Signed-off-by: Jesse Chan Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/pcm512x-spi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index f297058c0038..3ace683f0aec 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -67,3 +67,7 @@ static struct spi_driver pcm512x_spi_driver = { }; module_spi_driver(pcm512x_spi_driver); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver - SPI"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL v2"); -- GitLab From 9c194a6a8ac2047e1504329838e7b49e2210f077 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 28 Jun 2015 22:55:26 +0200 Subject: [PATCH 2522/5498] um: Fix out-of-tree build commit 0b5aedfe0e6654ec54f35109e1929a1cf7fc4cdd upstream. Commit 30b11ee9a (um: Remove copy&paste code from init.h) uncovered an issue wrt. out-of-tree builds. For out-of-tree builds, we must not rely on relative paths. Before 30b11ee9a it worked by chance as no host code included generated header files. Acked-by: Randy Dunlap Signed-off-by: Richard Weinberger Cc: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- arch/um/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/um/Makefile b/arch/um/Makefile index 150df6b79ec3..d467ae58c934 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -70,8 +70,8 @@ KBUILD_AFLAGS += $(ARCH_INCLUDE) USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -I%,,$(KBUILD_CFLAGS))) \ $(ARCH_INCLUDE) $(MODE_INCLUDE) $(filter -I%,$(CFLAGS)) \ - -D_FILE_OFFSET_BITS=64 -idirafter include \ - -D__KERNEL__ -D__UM_HOST__ + -D_FILE_OFFSET_BITS=64 -idirafter $(srctree)/include \ + -idirafter $(obj)/include -D__KERNEL__ -D__UM_HOST__ #This will adjust *FLAGS accordingly to the platform. include $(srctree)/$(ARCH_DIR)/Makefile-os-$(OS) -- GitLab From cde3537bd1098cd4df322c397ef016015890913c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 7 Feb 2018 11:07:58 -0800 Subject: [PATCH 2523/5498] Linux 3.18.94 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 172d9e596d25..03a5b7e2076c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 93 +SUBLEVEL = 94 EXTRAVERSION = NAME = Diseased Newt -- GitLab From 22c24eea301b7d28f60cc452a4215f4f0b00af4d Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Tue, 28 Nov 2017 18:22:11 -0800 Subject: [PATCH 2524/5498] ANDROID: qtaguid: Fix the UAF probelm with tag_ref_tree When multiple threads is trying to tag/delete the same socket at the same time, there is a chance the tag_ref_entry of the target socket to be null before the uid_tag_data entry is freed. It is caused by the ctrl_cmd_tag function where it doesn't correctly grab the spinlocks when tagging a socket. Signed-off-by: Chenbo Feng Bug: 65853158 Change-Id: I5d89885918054cf835370a52bff2d693362ac5f0 --- net/netfilter/xt_qtaguid.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 08b01a21ba25..ae5f3104cb85 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -518,13 +518,11 @@ static struct tag_ref *get_tag_ref(tag_t full_tag, DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", full_tag); - spin_lock_bh(&uid_tag_data_tree_lock); tr_entry = lookup_tag_ref(full_tag, &utd_entry); BUG_ON(IS_ERR_OR_NULL(utd_entry)); if (!tr_entry) tr_entry = new_tag_ref(full_tag, utd_entry); - spin_unlock_bh(&uid_tag_data_tree_lock); if (utd_res) *utd_res = utd_entry; DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", @@ -2020,6 +2018,7 @@ static int ctrl_cmd_delete(const char *input) /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); node = rb_first(&sock_tag_tree); while (node) { st_entry = rb_entry(node, struct sock_tag, sock_node); @@ -2049,6 +2048,7 @@ static int ctrl_cmd_delete(const char *input) list_del(&st_entry->list); } } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); sock_tag_tree_erase(&st_to_free_tree); @@ -2258,10 +2258,12 @@ static int ctrl_cmd_tag(const char *input) full_tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); if (IS_ERR(tag_ref_entry)) { res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); goto err_put; } @@ -2288,9 +2290,14 @@ static int ctrl_cmd_tag(const char *input) pr_err("qtaguid: ctrl_tag(%s): " "socket tag alloc failed\n", input); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, + uid_tag_data_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); res = -ENOMEM; - goto err_tag_unref_put; + goto err_put; } /* * Hold the sk refcount here to make sure the sk pointer cannot @@ -2300,7 +2307,6 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); - spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); /* @@ -2318,11 +2324,11 @@ static int ctrl_cmd_tag(const char *input) else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); - spin_unlock_bh(&uid_tag_data_tree_lock); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); atomic64_inc(&qtu_events.sockets_tagged); } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); /* We keep the ref to the sk until it is untagged */ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", @@ -2331,10 +2337,6 @@ static int ctrl_cmd_tag(const char *input) sockfd_put(el_socket); return 0; -err_tag_unref_put: - BUG_ON(tag_ref_entry->num_sock_tags <= 0); - tag_ref_entry->num_sock_tags--; - free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", input, atomic_read(&el_socket->sk->sk_refcnt) - 1); -- GitLab From 42befc6f3695e10b6fa574de4f17777ba896fa59 Mon Sep 17 00:00:00 2001 From: Wenjun Zhang Date: Wed, 31 Jan 2018 22:18:11 -0500 Subject: [PATCH 2525/5498] defconfig: msm: Enable SPI panel for msm8909w Enable SPI panel for msm8909w. Change-Id: I396775f6228feef961ade5c7d0360af4a910edfd Signed-off-by: Wenjun Zhang --- arch/arm/configs/msm8909w-perf_defconfig | 1 + arch/arm/configs/msm8909w_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 7e341b328d55..73c4e81d2679 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -339,6 +339,7 @@ CONFIG_FB_VIRTUAL=y CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_SPI_PANEL=y CONFIG_FB_MSM_MDSS_MDP3=y CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y CONFIG_BACKLIGHT_LCD_SUPPORT=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index f584ac4f00de..48a6a13d0d59 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -345,6 +345,7 @@ CONFIG_FB_VIRTUAL=y CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_SPI_PANEL=y CONFIG_FB_MSM_MDSS_MDP3=y CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y CONFIG_BACKLIGHT_LCD_SUPPORT=y -- GitLab From ab02f9731a791a1c3d7e70e5ab7798f10670fe9d Mon Sep 17 00:00:00 2001 From: Wenjun Zhang Date: Thu, 25 Jan 2018 23:23:59 -0500 Subject: [PATCH 2526/5498] ARM: dts: msm: enable SPI display for MSM8909w Add ST7789 CMD file and modify panel power supply for SPI panel. Change-Id: Ib28d2e30f782d02ea529e8afe67ac4634962f4f7 Signed-off-by: Wenjun Zhang --- .../boot/dts/qcom/msm8909-mdss-panels.dtsi | 7 +- arch/arm/boot/dts/qcom/msm8909-mdss.dtsi | 25 ++++++- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 4 +- arch/arm/boot/dts/qcom/msm8909.dtsi | 4 +- .../arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 30 +++++++++ .../dts/qcom/msm8909w-pm660-regulator.dtsi | 6 +- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 7 ++ .../dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi | 67 +++++++++++++++++++ 8 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi diff --git a/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi index bce3922040c7..a07011860ed2 100644 --- a/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,6 +23,7 @@ #include "dsi-panel-auo-qvga-cmd.dtsi" #include "dsi-panel-auo-cx-qvga-cmd.dtsi" #include "dsi-panel-390p-auo-cmd.dtsi" +#include "spi-panel-st7789v2-qvga-cmd.dtsi" &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { @@ -55,8 +56,8 @@ qcom,panel-supply-entry@0 { reg = <0>; qcom,supply-name = "vdd"; - qcom,supply-min-voltage = <3000000>; - qcom,supply-max-voltage = <3000000>; + qcom,supply-min-voltage = <2850000>; + qcom,supply-max-voltage = <2850000>; qcom,supply-enable-load = <100000>; qcom,supply-disable-load = <100>; }; diff --git a/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi index 84e593564dc2..c3e2a2faf7cf 100644 --- a/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2016,2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -153,6 +153,29 @@ 00 c0 00 00 00 00 00 01 bb]; }; }; + + mdss_spi: qcom,mdss_spi { + compatible = "qcom,mdss-spi-display"; + label = "mdss spi panel"; + + qcom,mdss-fb-map = <&mdss_fb0>; + qcom,mdss-mdp = <&mdss_mdp>; + vdd-supply = <&pm8909_l17>; + vddio-supply = <&pm8909_l6>; + + qcom,panel-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdd"; + qcom,supply-min-voltage = <2850000>; + qcom,supply-max-voltage = <2850000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + }; + }; }; #include "msm8909-mdss-panels.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index 25688ff48824..e332f8685a43 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -289,7 +289,7 @@ pmx_mdss { mdss_dsi_active: mdss_dsi_active { mux { - pins = "gpio25", "gpio37"; + pins = "gpio25", "gpio37", "gpio59"; function = "gpio"; }; @@ -302,7 +302,7 @@ mdss_dsi_suspend: mdss_dsi_suspend { mux { - pins = "gpio25", "gpio37"; + pins = "gpio25", "gpio37", "gpio59"; function = "gpio"; }; diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 92bfddd1b8cb..33365f853865 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -1339,7 +1339,7 @@ status = "disabled"; }; - spi_4: spi@78B8000{ /* BLSP1 QUP4 */ + spi_4: spi@78b8000{ /* BLSP1 QUP4 */ compatible = "qcom,spi-qup-v2"; #address-cells = <1>; #size-cells = <0>; @@ -1362,7 +1362,7 @@ qcom,bam-consumer-pipe-index = <10>; qcom,bam-producer-pipe-index = <11>; qcom,master-id = <86>; - status = "disabled"; + status = "ok"; }; dma_blsp1: qcom,sps-dma@7884000 { /* BLSP1 */ diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index 5e3906e85282..478ba358dd6a 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -20,6 +20,16 @@ /delete-property/ qcom,mipi-csi-vdd-supply; }; + spi_4 { + qcom,mdss_spi_client { + reg = <0>; + compatible = "qcom,mdss-spi-client"; + label = "MDSS SPI QUP4 CLIENT"; + dc-gpio = <&msm_gpio 59 0>; + spi-max-frequency = <50000000>; + }; + }; + i2c@78b9000 { synaptics@20 { /delete-property/ avdd-supply; @@ -192,6 +202,26 @@ qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>; }; +&spi_st7789v2_qvga_cmd { + qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>; + qcom,mdss-spi-bl-pmic-pwm-frequency = <100>; + qcom,mdss-spi-bl-pmic-bank-select = <0>; +}; + +&mdss_spi { + qcom,spi-pref-prim-pan = <&spi_st7789v2_qvga_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_te_active>; + pinctrl-1 = <&mdss_te_suspend>; + + qcom,platform-te-gpio = <&msm_gpio 24 0>; + qcom,platform-reset-gpio = <&msm_gpio 25 0>; + qcom,platform-spi-dc-gpio = <&msm_gpio 59 0>; + + vdd-supply = <&pm660_l18>; + vddio-supply = <&pm660_l11>; +}; + &mdss_dsi{ vdda-supply = <&pm660_l5>; /*1.2V*/ vddio-supply = <&pm660_l12>; /*1.8V*/ diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi index daf68da460d3..a82b00c68e1b 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -289,8 +289,8 @@ rpm-regulator-ldoa18 { status = "okay"; pm660_l18: regulator-l18 { - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3000000>; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; qcom,init-voltage = <3000000>; status = "okay"; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 71f4a9aafa2e..140f8e7d2b7a 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -75,6 +75,13 @@ spi@78b8000 { /* BLSP1 QUP4 */ status = "ok"; + qcom,mdss_spi_client { + reg = <0>; + compatible = "qcom,mdss-spi-client"; + label = "MDSS SPI QUP4 CLIENT"; + dc-gpio = <&msm_gpio 59 0>; + spi-max-frequency = <50000000>; + }; }; qcom,msm-ssc-sensors { diff --git a/arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi b/arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi new file mode 100644 index 000000000000..945009245639 --- /dev/null +++ b/arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi @@ -0,0 +1,67 @@ +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&mdss_mdp { + spi_st7789v2_qvga_cmd: qcom,mdss_spi_st7789v2_qvga_cmd { + qcom,mdss-spi-panel-name = + "st7789v2 qvga command mode spi panel"; + qcom,mdss-spi-panel-destination = "display_1"; + qcom,mdss-spi-panel-controller = <&mdss_spi>; + qcom,mdss-spi-panel-framerate = <27>; + qcom,mdss-spi-panel-width = <240>; + qcom,mdss-spi-panel-height = <240>; + qcom,mdss-spi-h-front-porch = <79>; + qcom,mdss-spi-h-back-porch = <59>; + qcom,mdss-spi-h-pulse-width = <60>; + qcom,mdss-spi-v-back-porch = <10>; + qcom,mdss-spi-v-front-porch = <7>; + qcom,mdss-spi-v-pulse-width = <2>; + qcom,mdss-spi-h-left-border = <0>; + qcom,mdss-spi-h-right-border = <0>; + qcom,mdss-spi-v-top-border = <0>; + qcom,mdss-spi-v-bottom-border = <0>; + qcom,mdss-spi-bpp = <16>; + qcom,mdss-spi-on-command = [ + 96 01 11 + 00 02 36 00 + 00 02 3A 05 + 00 02 35 00 + 00 06 B2 0C 0C 00 33 33 + 00 02 B7 75 + 00 02 BB 3D + 00 02 C2 01 + 00 02 C3 19 + 00 02 04 20 + 00 02 C6 0F + 00 03 D0 A4 A1 + 00 0F E0 70 04 08 09 09 05 2A 33 + 41 07 13 13 29 2F + 00 0F E1 70 03 09 0A 09 06 2B 34 + 41 07 12 14 28 2E + 00 01 21 + 00 01 29 + 00 05 2A 00 00 00 EF + 00 05 2B 00 00 00 EF + 00 01 2C]; + qcom,mdss-spi-off-command = [20 01 28 + 20 01 10]; + qcom,mdss-spi-bl-min-level = <1>; + qcom,mdss-spi-bl-max-level = <255>; + qcom,mdss-spi-bl-pmic-control-type = "bl_gpio_pulse"; + qcom,mdss-spi-reset-sequence = <1 20>, <0 1>, <1 20>; + qcom,mdss-spi-panel-status-check-mode = "reg_read"; + qcom,mdss-spi-panel-status-reg = /bits/ 8 <0x0A>; + qcom,mdss-spi-panel-status-read-length = <1>; + qcom,mdss-spi-panel-max-error-count = <1>; + qcom,mdss-spi-panel-status-value = /bits/ 8 <0x9C>; + }; +}; -- GitLab From f688ca096c617df98cc43d7506b3cb19a1813c8d Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Fri, 19 Jan 2018 18:56:51 +0530 Subject: [PATCH 2527/5498] msm: ipa: Fix to add string NULL terminator Missing null terminator to userspcae provided string leads to strlen buffer overflow in strlcpy function. Added code changes to fix string NULL terminator issue. Change-Id: I3f9d5f22fbb26f68de12370bc5e07a4e6bc2ced9 Acked-by: Ashok Vuyyuru Signed-off-by: Mohammed Javid --- drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 1 + drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index c781e1173f83..88ea8669e441 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1613,6 +1613,7 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) IPAWANERR("Failed to allocate memory.\n"); return -ENOMEM; } + extend_ioctl_data.u.if_name[IFNAMSIZ-1] = '\0'; len = sizeof(wan_msg->upstream_ifname) > sizeof(extend_ioctl_data.u.if_name) ? sizeof(extend_ioctl_data.u.if_name) : diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 9a8ec0d0ab4c..ac8170d77ef0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1701,6 +1701,7 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) IPAWANERR("Failed to allocate memory.\n"); return -ENOMEM; } + extend_ioctl_data.u.if_name[IFNAMSIZ-1] = '\0'; len = sizeof(wan_msg->upstream_ifname) > sizeof(extend_ioctl_data.u.if_name) ? sizeof(extend_ioctl_data.u.if_name) : -- GitLab From 8f61c20c9eec679c360c497c9ed2d1038008e15d Mon Sep 17 00:00:00 2001 From: Surendar Karka Date: Wed, 7 Feb 2018 16:13:15 +0530 Subject: [PATCH 2528/5498] ASoC: msm: dynamically control i2s switch gpio Set i2s switch gpio to output high when TDM BE is started and make the gpio to output low so that BT can use the i2s lines. CRs-Fixed: 2185727 Change-Id: I5fd1a3687a42fd6cc171af35c2165b0c9259b93c Signed-off-by: Surendar karka --- sound/soc/msm/apq8009-i2s-ext-codec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/msm/apq8009-i2s-ext-codec.c b/sound/soc/msm/apq8009-i2s-ext-codec.c index a7126b0beca3..47f22a4d2cb6 100644 --- a/sound/soc/msm/apq8009-i2s-ext-codec.c +++ b/sound/soc/msm/apq8009-i2s-ext-codec.c @@ -144,6 +144,7 @@ static int apq8009_auxpcm_rate = 8000; static atomic_t pri_mi2s_clk_ref; static atomic_t quat_mi2s_clk_ref; static atomic_t auxpcm_mi2s_clk_ref; +static int tdm_i2s_switch_enable = -EINVAL; static int apq8009_enable_extcodec_ext_clk(struct snd_soc_codec *codec, int enable, bool dapm); @@ -1490,6 +1491,9 @@ static int msm_tdm_startup(struct snd_pcm_substream *substream) if (ret < 0) pr_err("%s: failed to activate primary TDM gpio set\n", __func__); + /* Enable I2S switch to turn on TDM mics for SOM*/ + if (tdm_i2s_switch_enable >= 0) + gpio_direction_output(tdm_i2s_switch_enable, 1); break; default: pr_err("dai id 0x%x not supported", cpu_dai->id); @@ -1527,6 +1531,9 @@ static void msm_tdm_shutdown(struct snd_pcm_substream *substream) __func__, "pri_tdm"); return; } + + if (tdm_i2s_switch_enable >= 0) + gpio_direction_output(tdm_i2s_switch_enable, 0); break; default: break; @@ -2743,7 +2750,6 @@ static int apq8009_asoc_machine_probe(struct platform_device *pdev) const char *mclk = "qcom,msm-mclk-freq"; const char *type = NULL; int ret, id; - int tdm_i2s_switch_enable = -EINVAL; pdata = devm_kzalloc(&pdev->dev, sizeof(struct apq8009_asoc_mach_data), GFP_KERNEL); @@ -2852,7 +2858,6 @@ static int apq8009_asoc_machine_probe(struct platform_device *pdev) pr_err("%s: Failed to request gpio\n", __func__); goto err; } - gpio_direction_output(tdm_i2s_switch_enable, 1); } else dev_err(&pdev->dev, "Looking up %s property in node %s failed\n", "qcom,tdm-i2s-switch-enable", -- GitLab From f0288e13b76dd40ea532b07de832775e25458949 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Date: Wed, 7 Feb 2018 16:07:58 +0530 Subject: [PATCH 2529/5498] soc: qcom: bgcom: Set BG SPI state free on BG up Set BG SPI state to free on BG powered up. Change-Id: I9133e20480ed38ea4787f59600e13eb3aa4aecd7 Signed-off-by: Ajit Kumar --- drivers/soc/qcom/bgcom_interface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/bgcom_interface.c b/drivers/soc/qcom/bgcom_interface.c index c3ed3f08176a..ec39eeffa852 100644 --- a/drivers/soc/qcom/bgcom_interface.c +++ b/drivers/soc/qcom/bgcom_interface.c @@ -359,6 +359,7 @@ static int ssr_bg_cb(struct notifier_block *this, break; case SUBSYS_AFTER_POWERUP: bge.e_type = BG_AFTER_POWER_UP; + bgcom_set_spi_state(BGCOM_SPI_FREE); send_uevent(&bge); break; } -- GitLab From 81a9f3f79f91b18f5181507fc5856070b21e1bac Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Thu, 8 Feb 2018 11:08:21 +0530 Subject: [PATCH 2530/5498] leds: qpnp-wled: Fix the current sink configuration issue The current sinks are not re-enabled in a corner case once they are disabled during the sink parameter configuration. Fix it by enabling them correctly. Change-Id: I4ed03d6ebf4c42c3deb247447b28841ecc5312c8 Signed-off-by: Kiran Gunda --- drivers/leds/leds-qpnp-wled.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index 094d90a2e79f..5b9b9f3b87b6 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1072,8 +1072,6 @@ static int qpnp_wled_sink_config(struct qpnp_wled *wled) if (rc < 0) return rc; - module_enable = 0; - val = 0; /* Disable all the sinks */ rc = qpnp_wled_write_reg(wled, &val, @@ -1081,6 +1079,8 @@ static int qpnp_wled_sink_config(struct qpnp_wled *wled) (wled->sink_base)); if (rc < 0) return rc; + + module_enable = 0; } rc = qpnp_wled_write_reg(wled, &mod_temp, @@ -1129,28 +1129,29 @@ static int qpnp_wled_sink_config(struct qpnp_wled *wled) } if (sink_reg != sink_cfg) { - if (module_enable) { - /* Disable module */ - rc = qpnp_wled_module_en(wled, wled->ctrl_base, false); - if (rc < 0) - return rc; + /* Disable module */ + rc = qpnp_wled_module_en(wled, wled->ctrl_base, false); + if (rc < 0) + return rc; - module_enable = 0; - } + module_enable = 0; + } - /* Disable all the sinks */ + if (!module_enable) { + /* Enable all the sinks */ rc = qpnp_wled_write_reg(wled, &sink_cfg, QPNP_WLED_CURR_SINK_REG( wled->sink_base)); if (rc < 0) return rc; - } - /* Enable module */ - if (!module_enable) + /* Enable module */ rc = qpnp_wled_module_en(wled, wled->ctrl_base, true); + if (rc < 0) + return rc; + } - return rc; + return 0; } /* Configure WLED registers */ -- GitLab From 9f8f9f409b8911fc72aafa07e93b8cc59a908d94 Mon Sep 17 00:00:00 2001 From: Manaf Meethalavalappu Pallikunhi Date: Tue, 6 Feb 2018 14:29:14 +0530 Subject: [PATCH 2531/5498] ARM: dts: msm: Enable cluster mitigation device for msm8909 Enable cluster mitigation support in KTM for msm8909. It enables thermal-engine cluster mitigation device support for this target. Change-Id: I20b00125e15b0e1b1bc2e6ef8bdfbbb707d156ac Signed-off-by: Manaf Meethalavalappu Pallikunhi --- arch/arm/boot/dts/qcom/msm8909.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 92bfddd1b8cb..fc3143ff6074 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -563,6 +563,8 @@ qcom,freq-mitigation-value = <400000>; qcom,online-hotplug-core; qcom,therm-reset-temp = <115>; + qcom,synchronous-cluster-id = <0>; + qcom,synchronous-cluster-map = <0 4 &CPU0 &CPU1 &CPU2 &CPU3>; qcom,disable-cx-phase-ctrl; qcom,disable-gfx-phase-ctrl; qcom,disable-vdd-mx; -- GitLab From 5321a23c0c19c80ed16b81966ea8c8a14cb1f5d8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 16 Nov 2017 16:59:14 +0800 Subject: [PATCH 2532/5498] f2fs: reserve nid resource for quota sysfile During mkfs, quota sysfiles have already occupied nid resource, it needs to adjust remaining available nid count in kernel side. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 9 +-------- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/node.c | 2 +- fs/f2fs/super.c | 10 +++++++++- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 36d6a7277924..9d51dfdfcd76 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -49,14 +49,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_imeta = get_pages(sbi, F2FS_DIRTY_IMETA); si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; - - si->nquota_files = 0; - if (f2fs_sb_has_quota_ino(sbi->sb)) { - for (i = 0; i < MAXQUOTAS; i++) { - if (f2fs_qf_ino(sbi->sb, i)) - si->nquota_files++; - } - } + si->nquota_files = sbi->nquota_files; si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->aw_cnt = atomic_read(&sbi->aw_cnt); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 49add87dbc63..d356bbcc5f7d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1199,6 +1199,8 @@ struct f2fs_sb_info { block_t reserved_blocks; /* configurable reserved blocks */ block_t current_reserved_blocks; /* current reserved blocks */ + unsigned int nquota_files; /* # of quota sysfile */ + u32 s_next_generation; /* for NFS support */ /* # of pages, see count_type */ diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 57e86a43cbf2..9ac9576d07bd 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2684,7 +2684,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi) /* not used nids: 0, node, meta, (and root counted as valid node) */ nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count - - F2FS_RESERVED_NODE_NUM; + sbi->nquota_files - F2FS_RESERVED_NODE_NUM; nm_i->nid_cnt[FREE_NID] = 0; nm_i->nid_cnt[PREALLOC_NID] = 0; nm_i->nat_cnt = 0; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a945db43369e..38f11b866dcf 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1024,7 +1024,8 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = user_block_count - valid_user_blocks(sbi) - sbi->current_reserved_blocks; - avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; + avail_node_count = sbi->total_node_count - sbi->nquota_files - + F2FS_RESERVED_NODE_NUM; if (avail_node_count > user_block_count) { buf->f_files = user_block_count; @@ -2517,6 +2518,13 @@ try_onemore: sb->s_qcop = &f2fs_quotactl_ops; #if 0 /* not support */ sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; + + if (f2fs_sb_has_quota_ino(sbi->sb)) { + for (i = 0; i < MAXQUOTAS; i++) { + if (f2fs_qf_ino(sbi->sb, i)) + sbi->nquota_files++; + } + } #endif #endif -- GitLab From c569c0b1124bce36073048d572401b35096d6a88 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Fri, 17 Nov 2017 16:13:38 +0800 Subject: [PATCH 2533/5498] f2fs: no need to read nat block if nat_block_bitmap is set No need to read nat block if nat_block_bitmap is set. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9ac9576d07bd..c76a0db3c7aa 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1953,9 +1953,6 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, unsigned int nat_ofs = NAT_BLOCK_OFFSET(start_nid); int i; - if (test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) - return; - __set_bit_le(nat_ofs, nm_i->nat_block_bitmap); i = start_nid % NAT_ENTRY_PER_BLOCK; @@ -2060,10 +2057,13 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) down_read(&nm_i->nat_tree_lock); while (1) { - struct page *page = get_current_nat_page(sbi, nid); + if (!test_bit_le(NAT_BLOCK_OFFSET(nid), + nm_i->nat_block_bitmap)) { + struct page *page = get_current_nat_page(sbi, nid); - scan_nat_page(sbi, page, nid); - f2fs_put_page(page, 1); + scan_nat_page(sbi, page, nid); + f2fs_put_page(page, 1); + } nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK)); if (unlikely(nid >= nm_i->max_nid)) -- GitLab From 2638ff752042222397147123cfb910c818e243a8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 21 Nov 2017 17:49:54 +0800 Subject: [PATCH 2534/5498] f2fs: remove unneeded memory footprint accounting We forgot to remov memory footprint accounting of per-cpu type variables, fix it. Fixes: 35782b233f37 ("f2fs: remove percpu_count due to performance regression") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 9d51dfdfcd76..9d580223a72c 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -179,7 +179,6 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize; si->base_mem += 2 * sizeof(struct f2fs_inode_info); si->base_mem += sizeof(*sbi->ckpt); - si->base_mem += sizeof(struct percpu_counter) * NR_COUNT_TYPE; /* build sm */ si->base_mem += sizeof(struct f2fs_sm_info); -- GitLab From 5b10dbdef98c597431eca23f62e84cd028165270 Mon Sep 17 00:00:00 2001 From: LiFan Date: Wed, 22 Nov 2017 16:07:23 +0800 Subject: [PATCH 2535/5498] f2fs: fix concurrent problem for updating free bitmap alloc_nid_failed and scan_nat_page can be called at the same time, and we haven't protected add_free_nid and update_free_nid_bitmap with the same nid_list_lock. That could lead to Thread A Thread B - __build_free_nids - scan_nat_page - add_free_nid - alloc_nid_failed - update_free_nid_bitmap - update_free_nid_bitmap scan_nat_page will clear the free bitmap since the nid is PREALLOC_NID, but alloc_nid_failed needs to set the free bitmap. This results in free nid with free bitmap cleared. This patch update the bitmap under the same nid_list_lock in add_free_nid. And use __GFP_NOFAIL to make sure to update status of free nid correctly. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 85 +++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index c76a0db3c7aa..1a4f0dae317f 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1835,8 +1835,33 @@ static void __move_free_nid(struct f2fs_sb_info *sbi, struct free_nid *i, } } +static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, + bool set, bool build) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); + unsigned int nid_ofs = nid - START_NID(nid); + + if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) + return; + + if (set) { + if (test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; + __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + nm_i->free_nid_count[nat_ofs]++; + } else { + if (!test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; + __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + if (!build) + nm_i->free_nid_count[nat_ofs]--; + } +} + /* return if the nid is recognized as free */ -static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) +static bool add_free_nid(struct f2fs_sb_info *sbi, + nid_t nid, bool build, bool update) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i, *e; @@ -1852,8 +1877,7 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) i->nid = nid; i->state = FREE_NID; - if (radix_tree_preload(GFP_NOFS)) - goto err; + radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); spin_lock(&nm_i->nid_list_lock); @@ -1894,9 +1918,14 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) ret = true; err = __insert_free_nid(sbi, i, FREE_NID); err_out: + if (update) { + update_free_nid_bitmap(sbi, nid, ret, build); + if (!build) + nm_i->available_nids++; + } spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); -err: + if (err) kmem_cache_free(free_nid_slab, i); return ret; @@ -1920,30 +1949,6 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) kmem_cache_free(free_nid_slab, i); } -static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, - bool set, bool build) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); - unsigned int nid_ofs = nid - START_NID(nid); - - if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) - return; - - if (set) { - if (test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) - return; - __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - nm_i->free_nid_count[nat_ofs]++; - } else { - if (!test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) - return; - __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - if (!build) - nm_i->free_nid_count[nat_ofs]--; - } -} - static void scan_nat_page(struct f2fs_sb_info *sbi, struct page *nat_page, nid_t start_nid) { @@ -1958,18 +1963,18 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, i = start_nid % NAT_ENTRY_PER_BLOCK; for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) { - bool freed = false; - if (unlikely(start_nid >= nm_i->max_nid)) break; blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr); f2fs_bug_on(sbi, blk_addr == NEW_ADDR); - if (blk_addr == NULL_ADDR) - freed = add_free_nid(sbi, start_nid, true); - spin_lock(&NM_I(sbi)->nid_list_lock); - update_free_nid_bitmap(sbi, start_nid, freed, true); - spin_unlock(&NM_I(sbi)->nid_list_lock); + if (blk_addr == NULL_ADDR) { + add_free_nid(sbi, start_nid, true, true); + } else { + spin_lock(&NM_I(sbi)->nid_list_lock); + update_free_nid_bitmap(sbi, start_nid, false, true); + spin_unlock(&NM_I(sbi)->nid_list_lock); + } } } @@ -1987,7 +1992,7 @@ static void scan_curseg_cache(struct f2fs_sb_info *sbi) addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); nid = le32_to_cpu(nid_in_journal(journal, i)); if (addr == NULL_ADDR) - add_free_nid(sbi, nid, true); + add_free_nid(sbi, nid, true, false); else remove_free_nid(sbi, nid); } @@ -2014,7 +2019,7 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) break; nid = i * NAT_ENTRY_PER_BLOCK + idx; - add_free_nid(sbi, nid, true); + add_free_nid(sbi, nid, true, false); if (nm_i->nid_cnt[FREE_NID] >= MAX_FREE_NIDS) goto out; @@ -2520,11 +2525,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, nat_reset_flag(ne); __clear_nat_cache_dirty(NM_I(sbi), set, ne); if (nat_get_blkaddr(ne) == NULL_ADDR) { - add_free_nid(sbi, nid, false); - spin_lock(&NM_I(sbi)->nid_list_lock); - NM_I(sbi)->available_nids++; - update_free_nid_bitmap(sbi, nid, true, false); - spin_unlock(&NM_I(sbi)->nid_list_lock); + add_free_nid(sbi, nid, false, true); } else { spin_lock(&NM_I(sbi)->nid_list_lock); update_free_nid_bitmap(sbi, nid, false, false); -- GitLab From bee58ad4c31cb4b47d2dd5e3494e9a4f9fb351c2 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 22 Nov 2017 18:23:38 +0800 Subject: [PATCH 2536/5498] f2fs: introduce sysfs readdir_ra to readahead inode block in readdir This patch introduces a sysfs interface readdir_ra to enable/disable readaheading inode block in f2fs_readdir. When readdir_ra is enabled, it improves the performance of "readdir + stat". For 300,000 files: time find /data/test > /dev/null disable readdir_ra: 1m25.69s real 0m01.94s user 0m50.80s system enable readdir_ra: 0m18.55s real 0m00.44s user 0m15.39s system Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 6 ++++++ fs/f2fs/dir.c | 4 ++++ fs/f2fs/f2fs.h | 1 + fs/f2fs/sysfs.c | 2 ++ 4 files changed, 13 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 2baed1151eac..db7aab1516de 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -186,3 +186,9 @@ Date: August 2017 Contact: "Jaegeuk Kim" Description: Controls sleep time of GC urgent mode + +What: /sys/fs/f2fs//readdir_ra +Date: November 2017 +Contact: "Sheng Yong" +Description: + Controls readahead inode block in readdir. diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 1955707b138b..55fb45b66ed2 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -798,6 +798,7 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, unsigned int bit_pos; struct f2fs_dir_entry *de = NULL; struct fscrypt_str de_name = FSTR_INIT(NULL, 0); + struct f2fs_sb_info *sbi = F2FS_I_SB(d->inode); bit_pos = ((unsigned long)ctx->pos % d->max); @@ -836,6 +837,9 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, le32_to_cpu(de->ino), d_type)) return 1; + if (sbi->readdir_ra == 1) + ra_node_page(sbi, le32_to_cpu(de->ino)); + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); ctx->pos = start_pos + bit_pos; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index d356bbcc5f7d..9746ea854dbd 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1191,6 +1191,7 @@ struct f2fs_sb_info { int dir_level; /* directory level */ int inline_xattr_size; /* inline xattr size */ unsigned int trigger_ssr_threshold; /* threshold to trigger ssr */ + int readdir_ra; /* readahead inode in readdir */ block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 5c14bcaa19a4..fe0e74a4ea2c 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -299,6 +299,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra); #ifdef CONFIG_F2FS_FAULT_INJECTION F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); @@ -346,6 +347,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), ATTR_LIST(iostat_enable), + ATTR_LIST(readdir_ra), #ifdef CONFIG_F2FS_FAULT_INJECTION ATTR_LIST(inject_rate), ATTR_LIST(inject_type), -- GitLab From de222d7b1cb64f8f10d31c30413d0493f8cd981b Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 28 Jun 2016 18:07:30 +0100 Subject: [PATCH 2537/5498] arm64: errata: Calling enable functions for CPU errata too Currently we call the (optional) enable function for CPU _features_ only. As CPU _errata_ descriptions share the same data structure and having an enable function is useful for errata as well (for instance to set bits in SCTLR), lets call it when enumerating erratas too. Change-Id: If9eff7dd87d7ebcf8ee44ae0cc326f91e2c16af8 Signed-off-by: Andre Przywara Reviewed-by: Suzuki K Poulose Signed-off-by: Catalin Marinas Git-commit: 8e2318521bf5837dae093413f81292b59d49d030 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux [neeraju@codeaurora.org: resolve trivial merge conflicts] Signed-off-by: Neeraj Upadhyay --- arch/arm64/include/asm/cpufeature.h | 2 ++ arch/arm64/kernel/cpu_errata.c | 5 +++++ arch/arm64/kernel/cpufeature.c | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index ba6983e901d5..81e39e9e1f02 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -162,7 +162,9 @@ void __init setup_cpu_features(void); void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps, const char *info); +void enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps); void check_local_cpu_errata(void); +void __init enable_errata_workarounds(void); #ifdef CONFIG_HOTPLUG_CPU void verify_local_cpu_capabilities(void); diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 807d1c1de9db..efcf303b047a 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -97,3 +97,8 @@ void check_local_cpu_errata(void) { update_cpu_capabilities(arm64_errata, "enabling workaround for"); } + +void __init enable_errata_workarounds(void) +{ + enable_cpu_capabilities(arm64_errata); +} diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index ec7e68c2c73f..a51301773ef0 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -799,8 +799,7 @@ void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps, * Run through the enabled capabilities and enable() it on all active * CPUs */ -static void __init -enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps) +void __init enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps) { int i; @@ -954,6 +953,7 @@ void __init setup_cpu_features(void) /* Set the CPU feature capabilies */ setup_feature_capabilities(); setup_cpu_hwcaps(); + enable_errata_workarounds(); /* Advertise that we have computed the system capabilities */ set_sys_caps_initialised(); -- GitLab From 7e508c19f795d78dcd58b33a8e799a01cf706b98 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 2 Jan 2018 21:37:25 +0000 Subject: [PATCH 2538/5498] arm64: cpufeature: Pass capability structure to ->enable callback In order to invoke the CPU capability ->matches callback from the ->enable callback for applying local-CPU workarounds, we need a handle on the capability structure. This patch passes a pointer to the capability structure to the ->enable callback. Change-Id: I5f83f1002dd35a4a8f77abe8d0204cef96c37398 Signed-off-by: Will Deacon Git-commit: 0a0d111d40fd1dc588cc590fab6b55d86ddc71d3 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux [neeraju@codeaurora.org: resolve trivial merge conflicts] Signed-off-by: Neeraj Upadhyay --- arch/arm64/kernel/cpufeature.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index a51301773ef0..ddd84dfd406b 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -805,7 +805,7 @@ void __init enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps) for (i = 0; caps[i].matches; i++) if (caps[i].enable && cpus_have_cap(caps[i].capability)) - on_each_cpu(caps[i].enable, NULL, true); + on_each_cpu(caps[i].enable, (void *)&(caps[i]), true); } #ifdef CONFIG_HOTPLUG_CPU @@ -920,7 +920,7 @@ void verify_local_cpu_capabilities(void) if (!feature_matches(__raw_read_system_reg(caps[i].sys_reg), &caps[i])) fail_incapable_cpu("arm64_features", &caps[i]); if (caps[i].enable) - caps[i].enable(NULL); + caps[i].enable((void *)&(caps[i])); } for (i = 0, caps = arm64_hwcaps; caps[i].matches; i++) { -- GitLab From 22cc6f66757149087b8115be707bf5ae521a7bb7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 3 Jan 2018 11:17:58 +0000 Subject: [PATCH 2539/5498] arm64: Add skeleton to harden the branch predictor against aliasing attacks Aliasing attacks against CPU branch predictors can allow an attacker to redirect speculative control flow on some CPUs and potentially divulge information from one context to another. This patch adds initial skeleton code behind a new Kconfig option to enable implementation-specific mitigations against these attacks for CPUs that are affected. Change-Id: I778641a6a2658cded427204a8c2b01dc6b6b9579 Co-developed-by: Marc Zyngier Signed-off-by: Will Deacon Git-commit: 0f15adbb2861ce6f75ccfc5a92b19eae0ef327d0 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux [neeraju@codeaurora.org: Fix merge conflicts and build failures due to missing includes and api changes in new kernels] Signed-off-by: Neeraj Upadhyay --- arch/arm64/Kconfig | 17 +++++++ arch/arm64/include/asm/cpufeature.h | 1 + arch/arm64/include/asm/mmu.h | 40 +++++++++++++++ arch/arm64/include/asm/sysreg.h | 1 + arch/arm64/kernel/Makefile | 4 ++ arch/arm64/kernel/bpi.S | 55 +++++++++++++++++++++ arch/arm64/kernel/cpu_errata.c | 76 +++++++++++++++++++++++++++++ arch/arm64/kernel/cpufeature.c | 1 + arch/arm64/kernel/entry.S | 8 +-- arch/arm64/mm/context.c | 1 + arch/arm64/mm/fault.c | 16 ++++++ 11 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 arch/arm64/kernel/bpi.S diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 0625f78596bb..ce5afcf34f82 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -813,6 +813,23 @@ config UNMAP_KERNEL_AT_EL0 If unsure, say Y. +config HARDEN_BRANCH_PREDICTOR + bool "Harden the branch predictor against aliasing attacks" if EXPERT + default y + help + Speculation attacks against some high-performance processors rely on + being able to manipulate the branch predictor for a victim context by + executing aliasing branches in the attacker context. Such attacks + can be partially mitigated against by clearing internal branch + predictor state and limiting the prediction logic in some situations. + + This config option will take CPU-specific actions to harden the + branch predictor against aliasing attacks and may rely on specific + instruction sequences or control bits being set by the system + firmware. + + If unsure, say Y. + menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" depends on COMPAT diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 81e39e9e1f02..cacf6c74c7f2 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -29,6 +29,7 @@ #define ARM64_HAS_PAN 4 #define ARM64_HAS_UAO 5 #define ARM64_ALT_PAN_NOT_UAO 6 +#define ARM64_HARDEN_BRANCH_PREDICTOR 7 #define ARM64_UNMAP_KERNEL_AT_EL0 23 #define ARM64_NCAPS 24 diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 2fbe8cc639c7..1cd927890ad1 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -21,7 +21,10 @@ #ifndef __ASSEMBLY__ +#include + #include +#include typedef struct { atomic64_t id; @@ -41,6 +44,43 @@ static inline bool arm64_kernel_unmapped_at_el0(void) cpus_have_cap(ARM64_UNMAP_KERNEL_AT_EL0); } +typedef void (*bp_hardening_cb_t)(void); + +struct bp_hardening_data { + int hyp_vectors_slot; + bp_hardening_cb_t fn; +}; + +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +extern char __bp_harden_hyp_vecs_start[], __bp_harden_hyp_vecs_end[]; + +DECLARE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); + +static inline struct bp_hardening_data *arm64_get_bp_hardening_data(void) +{ + return this_cpu_ptr(&bp_hardening_data); +} + +static inline void arm64_apply_bp_hardening(void) +{ + struct bp_hardening_data *d; + + if (!cpus_have_cap(ARM64_HARDEN_BRANCH_PREDICTOR)) + return; + + d = arm64_get_bp_hardening_data(); + if (d->fn) + d->fn(); +} +#else +static inline struct bp_hardening_data *arm64_get_bp_hardening_data(void) +{ + return NULL; +} + +static inline void arm64_apply_bp_hardening(void) { } +#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ + extern void paging_init(void); extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); extern void init_mem_pgprot(void); diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 0cc436bf726b..3e4294cb3c53 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -101,6 +101,7 @@ #define ID_AA64ISAR0_AES_SHIFT 4 /* id_aa64pfr0 */ +#define ID_AA64PFR0_CSV2_SHIFT 56 #define ID_AA64PFR0_GIC_SHIFT 24 #define ID_AA64PFR0_ASIMD_SHIFT 20 #define ID_AA64PFR0_FP_SHIFT 16 diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 20540372d224..47a98a597633 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -42,6 +42,10 @@ arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_MSM_APP_API) += app_api.o arm64-obj-$(CONFIG_MSM_APP_SETTINGS) += app_setting.o +ifeq ($(CONFIG_KVM),y) +arm64-obj-$(CONFIG_HARDEN_BRANCH_PREDICTOR) += bpi.o +endif + obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m) head-y := head.o diff --git a/arch/arm64/kernel/bpi.S b/arch/arm64/kernel/bpi.S new file mode 100644 index 000000000000..06a931eb2673 --- /dev/null +++ b/arch/arm64/kernel/bpi.S @@ -0,0 +1,55 @@ +/* + * Contains CPU specific branch predictor invalidation sequences + * + * Copyright (C) 2018 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +.macro ventry target + .rept 31 + nop + .endr + b \target +.endm + +.macro vectors target + ventry \target + 0x000 + ventry \target + 0x080 + ventry \target + 0x100 + ventry \target + 0x180 + + ventry \target + 0x200 + ventry \target + 0x280 + ventry \target + 0x300 + ventry \target + 0x380 + + ventry \target + 0x400 + ventry \target + 0x480 + ventry \target + 0x500 + ventry \target + 0x580 + + ventry \target + 0x600 + ventry \target + 0x680 + ventry \target + 0x700 + ventry \target + 0x780 +.endm + + .align 11 +ENTRY(__bp_harden_hyp_vecs_start) + .rept 4 + vectors __kvm_hyp_vector + .endr +ENTRY(__bp_harden_hyp_vecs_end) diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index efcf303b047a..499a67de1850 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -41,6 +41,82 @@ is_affected_midr_range(const struct arm64_cpu_capabilities *entry) return (midr >= entry->midr_range_min && midr <= entry->midr_range_max); } +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +#include +#include + +DEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); + +#ifdef CONFIG_KVM +static void __copy_hyp_vect_bpi(int slot, const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + void *dst = lm_alias(__bp_harden_hyp_vecs_start + slot * SZ_2K); + int i; + + for (i = 0; i < SZ_2K; i += 0x80) + memcpy(dst + i, hyp_vecs_start, hyp_vecs_end - hyp_vecs_start); + + flush_icache_range((uintptr_t)dst, (uintptr_t)dst + SZ_2K); +} + +static void __install_bp_hardening_cb(bp_hardening_cb_t fn, + const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + static int last_slot = -1; + static DEFINE_SPINLOCK(bp_lock); + int cpu, slot = -1; + + spin_lock(&bp_lock); + for_each_possible_cpu(cpu) { + if (per_cpu(bp_hardening_data.fn, cpu) == fn) { + slot = per_cpu(bp_hardening_data.hyp_vectors_slot, cpu); + break; + } + } + + if (slot == -1) { + last_slot++; + BUG_ON(((__bp_harden_hyp_vecs_end - __bp_harden_hyp_vecs_start) + / SZ_2K) <= last_slot); + slot = last_slot; + __copy_hyp_vect_bpi(slot, hyp_vecs_start, hyp_vecs_end); + } + + __this_cpu_write(bp_hardening_data.hyp_vectors_slot, slot); + __this_cpu_write(bp_hardening_data.fn, fn); + spin_unlock(&bp_lock); +} +#else +static void __maybe_unused +__install_bp_hardening_cb(bp_hardening_cb_t fn, + const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + __this_cpu_write(bp_hardening_data.fn, fn); +} +#endif /* CONFIG_KVM */ + +static void __maybe_unused +install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry, + bp_hardening_cb_t fn, + const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + u64 pfr0; + + if (!entry->matches(entry)) + return; + + pfr0 = read_cpuid(SYS_ID_AA64PFR0_EL1); + if (cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_CSV2_SHIFT)) + return; + + __install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end); +} +#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ + #define MIDR_RANGE(model, min, max) \ .matches = is_affected_midr_range, \ .midr_model = model, \ diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index ddd84dfd406b..52c8f3cb8e33 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -87,6 +87,7 @@ static struct arm64_ftr_bits ftr_id_aa64isar0[] = { static struct arm64_ftr_bits ftr_id_aa64pfr0[] = { ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0), ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 4, 0), + ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_CSV2_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_GIC_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_ASIMD_SHIFT, 4, ID_AA64PFR0_ASIMD_NI), ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_FP_SHIFT, 4, ID_AA64PFR0_FP_NI), diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index a86ba11be634..709f8cf0af8e 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -603,13 +603,15 @@ el0_ia: * Instruction abort handling */ mrs x26, far_el1 - // enable interrupts before calling the main handler - enable_dbg_and_irq + enable_dbg +#ifdef CONFIG_TRACE_IRQFLAGS + bl trace_hardirqs_off +#endif ct_user_exit mov x0, x26 mov x1, x25 mov x2, sp - bl do_mem_abort + bl do_el0_ia_bp_hardening b ret_to_user el0_fpsimd_acc: /* diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index cc3664b088de..9758b3d3e0a3 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -202,6 +202,7 @@ switch_mm_fastpath: /* Errata workaround post TTBRx_EL1 update. */ asmlinkage void post_ttbr_update_workaround(void) { + arm64_apply_bp_hardening(); } static int asids_init(void) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index c7fbe5b13405..fd8f918abf7a 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -515,6 +515,22 @@ asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, arm64_notify_die("", regs, &info, esr); } +asmlinkage void __exception do_el0_ia_bp_hardening(unsigned long addr, + unsigned int esr, + struct pt_regs *regs) +{ + /* + * We've taken an instruction abort from userspace and not yet + * re-enabled IRQs. If the address is a kernel address, apply + * BP hardening prior to enabling IRQs and pre-emption. + */ + if (addr > TASK_SIZE) + arm64_apply_bp_hardening(); + + local_irq_enable(); + do_mem_abort(addr, esr, regs); +} + /* * Handle stack alignment exceptions. */ -- GitLab From 20fb82c33da47e23da1fbd3ccd070fefdf989024 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 1 Feb 2017 14:38:46 +0000 Subject: [PATCH 2540/5498] arm64: cpu_errata: Allow an erratum to be match for all revisions of a core Some minor erratum may not be fixed in further revisions of a core, leading to a situation where the workaround needs to be updated each time an updated core is released. Introduce a MIDR_ALL_VERSIONS match helper that will work for all versions of that MIDR, once and for all. Change-Id: I5e7a8bfea94ad5984d605ca244eb447182928a07 Acked-by: Thomas Gleixner Acked-by: Mark Rutland Acked-by: Daniel Lezcano Reviewed-by: Suzuki K Poulose Signed-off-by: Marc Zyngier Git-commit: 06f1494f837da8997d670a1ba87add7963b08922 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux Signed-off-by: Neeraj Upadhyay --- arch/arm64/kernel/cpu_errata.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 499a67de1850..561faa3d2e63 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -123,6 +123,12 @@ install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry, .midr_range_min = min, \ .midr_range_max = max +#define MIDR_ALL_VERSIONS(model) \ + .matches = is_affected_midr_range, \ + .midr_model = model, \ + .midr_range_min = 0, \ + .midr_range_max = (MIDR_VARIANT_MASK | MIDR_REVISION_MASK) + const struct arm64_cpu_capabilities arm64_errata[] = { #if defined(CONFIG_ARM64_ERRATUM_826319) || \ defined(CONFIG_ARM64_ERRATUM_827319) || \ -- GitLab From 0b8d0eb5c1d170fe914141057d5c58b3f0dbb921 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 2 Jan 2018 21:45:41 +0000 Subject: [PATCH 2541/5498] drivers/firmware: Expose psci_get_version through psci_ops structure Entry into recent versions of ARM Trusted Firmware will invalidate the CPU branch predictor state in order to protect against aliasing attacks. This patch exposes the PSCI "VERSION" function via psci_ops, so that it can be invoked outside of the PSCI driver where necessary. Change-Id: Ibe7995e9a311eb7da441d3c65dfdf71e9de3b33c Acked-by: Lorenzo Pieralisi Signed-off-by: Will Deacon Git-commit: d68e3ba5303f7e1099f51fdcd155f5263da8569b Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux [neeraju@codeaurora.org: resolve merge conflicts due to missing files] Signed-off-by: Neeraj Upadhyay --- arch/arm64/include/asm/psci.h | 20 ++++++++++++++++++++ arch/arm64/kernel/psci.c | 22 ++++------------------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h index e5312ea0ec1a..fabf10843726 100644 --- a/arch/arm64/include/asm/psci.h +++ b/arch/arm64/include/asm/psci.h @@ -16,4 +16,24 @@ int psci_init(void); +struct psci_power_state { + u16 id; + u8 type; + u8 affinity_level; +}; + +struct psci_operations { + int (*get_version)(void); + int (*cpu_suspend)(unsigned long state_id, + unsigned long entry_point); + int (*cpu_off)(struct psci_power_state state); + int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); + int (*migrate)(unsigned long cpuid); + int (*affinity_info)(unsigned long target_affinity, + unsigned long lowest_affinity_level); + int (*migrate_info_type)(void); +}; + +extern struct psci_operations psci_ops; + #endif /* __ASM_PSCI_H */ diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 892728b303d8..42bab2bd7ca4 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -38,24 +38,7 @@ #define PSCI_POWER_STATE_BIT BIT(30) -struct psci_power_state { - u16 id; - u8 type; - u8 affinity_level; -}; - -struct psci_operations { - int (*cpu_suspend)(unsigned long state_id, - unsigned long entry_point); - int (*cpu_off)(struct psci_power_state state); - int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); - int (*migrate)(unsigned long cpuid); - int (*affinity_info)(unsigned long target_affinity, - unsigned long lowest_affinity_level); - int (*migrate_info_type)(void); -}; - -static struct psci_operations psci_ops; +struct psci_operations psci_ops; static int (*invoke_psci_fn)(u64, u64, u64, u64); typedef int (*psci_initcall_t)(const struct device_node *); @@ -307,6 +290,7 @@ static int __init psci_1_0_init(struct device_node *np) } pr_info("Using standard PSCI v0.2 function IDs\n"); + psci_ops.get_version = psci_get_version; psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; psci_ops.cpu_suspend = psci_cpu_suspend; @@ -360,6 +344,7 @@ static int __init psci_0_2_init(struct device_node *np) } pr_info("Using standard PSCI v0.2 function IDs\n"); + psci_ops.get_version = psci_get_version; psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; psci_ops.cpu_suspend = psci_cpu_suspend; @@ -402,6 +387,7 @@ static int __init psci_0_1_init(struct device_node *np) goto out_put_node; pr_info("Using PSCI v0.1 Function IDs from DT\n"); + psci_ops.get_version = psci_get_version; if (!of_property_read_u32(np, "cpu_suspend", &id)) { psci_function_id[PSCI_FN_CPU_SUSPEND] = id; -- GitLab From b6a709209c866927f23c64f35d29c6eee23a324b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 3 Jan 2018 12:46:21 +0000 Subject: [PATCH 2542/5498] arm64: Implement branch predictor hardening for affected Cortex-A CPUs Cortex-A57, A72, A73 and A75 are susceptible to branch predictor aliasing and can theoretically be attacked by malicious code. This patch implements a PSCI-based mitigation for these CPUs when available. The call into firmware will invalidate the branch predictor state, preventing any malicious entries from affecting other victim contexts. Change-Id: I08acb4a1436709aee7e57ad5e14b37f1a8738038 Co-developed-by: Marc Zyngier Signed-off-by: Will Deacon Git-commit: aa6acde65e03186b5add8151e1ffe36c3c62639b Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux [neeraju@codeaurora.org: resolve merge conflicts. Implement PSCI-based mitigation only for Cortex-A57] Signed-off-by: Neeraj Upadhyay --- arch/arm64/kernel/bpi.S | 24 ++++++++++++++++++++++++ arch/arm64/kernel/cpu_errata.c | 33 +++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/bpi.S b/arch/arm64/kernel/bpi.S index 06a931eb2673..dec95bd82e31 100644 --- a/arch/arm64/kernel/bpi.S +++ b/arch/arm64/kernel/bpi.S @@ -53,3 +53,27 @@ ENTRY(__bp_harden_hyp_vecs_start) vectors __kvm_hyp_vector .endr ENTRY(__bp_harden_hyp_vecs_end) +ENTRY(__psci_hyp_bp_inval_start) + sub sp, sp, #(8 * 18) + stp x16, x17, [sp, #(16 * 0)] + stp x14, x15, [sp, #(16 * 1)] + stp x12, x13, [sp, #(16 * 2)] + stp x10, x11, [sp, #(16 * 3)] + stp x8, x9, [sp, #(16 * 4)] + stp x6, x7, [sp, #(16 * 5)] + stp x4, x5, [sp, #(16 * 6)] + stp x2, x3, [sp, #(16 * 7)] + stp x0, x1, [sp, #(16 * 8)] + mov x0, #0x84000000 + smc #0 + ldp x16, x17, [sp, #(16 * 0)] + ldp x14, x15, [sp, #(16 * 1)] + ldp x12, x13, [sp, #(16 * 2)] + ldp x10, x11, [sp, #(16 * 3)] + ldp x8, x9, [sp, #(16 * 4)] + ldp x6, x7, [sp, #(16 * 5)] + ldp x4, x5, [sp, #(16 * 6)] + ldp x2, x3, [sp, #(16 * 7)] + ldp x0, x1, [sp, #(16 * 8)] + add sp, sp, #(8 * 18) +ENTRY(__psci_hyp_bp_inval_end) diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 561faa3d2e63..834af5d6478e 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -48,6 +48,8 @@ is_affected_midr_range(const struct arm64_cpu_capabilities *entry) DEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); #ifdef CONFIG_KVM +extern char __psci_hyp_bp_inval_start[], __psci_hyp_bp_inval_end[]; + static void __copy_hyp_vect_bpi(int slot, const char *hyp_vecs_start, const char *hyp_vecs_end) { @@ -89,10 +91,12 @@ static void __install_bp_hardening_cb(bp_hardening_cb_t fn, spin_unlock(&bp_lock); } #else -static void __maybe_unused -__install_bp_hardening_cb(bp_hardening_cb_t fn, - const char *hyp_vecs_start, - const char *hyp_vecs_end) +#define __psci_hyp_bp_inval_start NULL +#define __psci_hyp_bp_inval_end NULL + +static void __install_bp_hardening_cb(bp_hardening_cb_t fn, + const char *hyp_vecs_start, + const char *hyp_vecs_end) { __this_cpu_write(bp_hardening_data.fn, fn); } @@ -115,6 +119,20 @@ install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry, __install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end); } + +#include + +static void enable_psci_bp_hardening(void *data) +{ + const struct arm64_cpu_capabilities *entry = data; + + if (psci_ops.get_version) + install_bp_hardening_cb(entry, + (bp_hardening_cb_t)psci_ops.get_version, + __psci_hyp_bp_inval_start, + __psci_hyp_bp_inval_end); + +} #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ #define MIDR_RANGE(model, min, max) \ @@ -170,6 +188,13 @@ const struct arm64_cpu_capabilities arm64_errata[] = { .capability = ARM64_WORKAROUND_845719, MIDR_RANGE(MIDR_KRYO2XX_SILVER, 0xA00004, 0xA00004), }, +#endif +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), + .enable = enable_psci_bp_hardening, + }, #endif { } -- GitLab From de9a14cbfe117d4640f7faf1ca09b84453e868bd Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 13 Dec 2017 14:19:37 -0800 Subject: [PATCH 2543/5498] arm64: cpu_errata: Add Kryo to Falkor 1003 errata The Kryo CPUs are also affected by the Falkor 1003 errata, so we need to do the same workaround on Kryo CPUs. The MIDR is slightly more complicated here, where the PART number is not always the same when looking at all the bits from 15 to 4. Drop the lower 8 bits and just look at the top 4 to see if it's '2' and then consider those as Kryo CPUs. This covers all the combinations without having to list them all out. Change-Id: I076e583462e6b71172e9d7dc282703eb8a52499d Fixes: 38fd94b0275c ("arm64: Work around Falkor erratum 1003") Acked-by: Will Deacon Signed-off-by: Stephen Boyd Signed-off-by: Catalin Marinas Git-commit: bb48711800e6d7aedbf4dfd3367e0ab1270a6bea Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux [neeraju@codeaurora.org: resolve merge conflicts. Take only the Kryo MIDR definition part.] Signed-off-by: Neeraj Upadhyay --- arch/arm64/include/asm/cputype.h | 1 + arch/arm64/kernel/cpu_errata.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index d0edb7774b68..6b3982df94ef 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -71,6 +71,7 @@ #define ARM_CPU_PART_CORTEX_A72 0xD08 #define ARM_CPU_PART_KRYO2XX_GOLD 0x800 #define ARM_CPU_PART_KRYO2XX_SILVER 0x801 +#define QCOM_CPU_PART_KRYO 0x200 #define APM_CPU_PART_POTENZA 0x000 diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 834af5d6478e..2ffdac0b3427 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -24,6 +24,7 @@ #define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) #define MIDR_CORTEX_A57 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) #define MIDR_KRYO2XX_SILVER MIDR_CPU_PART(ARM_CPU_IMP_QCOM, ARM_CPU_PART_KRYO2XX_SILVER) +#define MIDR_QCOM_KRYO MIDR_CPU_PART(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO) #define CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \ MIDR_ARCHITECTURE_MASK) @@ -41,6 +42,18 @@ is_affected_midr_range(const struct arm64_cpu_capabilities *entry) return (midr >= entry->midr_range_min && midr <= entry->midr_range_max); } +static bool __maybe_unused +is_kryo_midr(const struct arm64_cpu_capabilities *entry) +{ + u32 model; + + model = read_cpuid_id(); + model &= MIDR_IMPLEMENTOR_MASK | (0xf00 << MIDR_PARTNUM_SHIFT) | + MIDR_ARCHITECTURE_MASK; + + return model == entry->midr_model; +} + #ifdef CONFIG_HARDEN_BRANCH_PREDICTOR #include #include -- GitLab From a9111e1c56e5976451028991e4c56da05b39fc57 Mon Sep 17 00:00:00 2001 From: Shanker Donthineni Date: Fri, 5 Jan 2018 14:28:59 -0600 Subject: [PATCH 2544/5498] arm64: Implement branch predictor hardening for Falkor Falkor is susceptible to branch predictor aliasing and can theoretically be attacked by malicious code. This patch implements a mitigation for these attacks, preventing any malicious entries from affecting other victim contexts. Change-Id: Ieb49196c23582c3174f778891ff2ab7cdc96be54 Signed-off-by: Shanker Donthineni [will: fix label name when !CONFIG_KVM and remove references to MIDR_FALKOR] Signed-off-by: Will Deacon Git-commit: ec82b567a74fbdffdf418d4bb381d55f6a9096af Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux [neeraju@codeaurora.org: resolve merge conflicts. Take only qcom_link_stack_sanitization() definition] Signed-off-by: Neeraj Upadhyay --- arch/arm64/kernel/cpu_errata.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 2ffdac0b3427..db1a5cd23286 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -146,6 +146,19 @@ static void enable_psci_bp_hardening(void *data) __psci_hyp_bp_inval_end); } + +static void __maybe_unused qcom_link_stack_sanitization(void) +{ + u64 tmp; + + asm volatile("mov %0, x30 \n" + ".rept 16 \n" + "bl . + 4 \n" + ".endr \n" + "mov x30, %0 \n" + : "=&r" (tmp)); +} + #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ #define MIDR_RANGE(model, min, max) \ -- GitLab From ca86a1da76ed266320e3bd9d8ba29a734f0700bd Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Mon, 8 Jan 2018 19:58:29 +0530 Subject: [PATCH 2545/5498] arm64: Add BTAC/LinkStack sanitizations for Kryo Kryo cores are exposed to two vulnerabilities due to subroutine return (called LINK-STACK) and branch target predictors. These two issues can be mitigated through software workarounds. Kernel: - Apply LINK-STACK mitigation which is issue 16 nested BL instructions on process context switch 'cpu_do_switch_mm()' where ASID changes. - Apply psci based branch predictor invalidation. Change-Id: I983a12dabcd45e3ec757732a44fc567c12228d8b Signed-off-by: Neeraj Upadhyay --- arch/arm64/kernel/cpu_errata.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index db1a5cd23286..37e278e4fd98 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -147,7 +147,7 @@ static void enable_psci_bp_hardening(void *data) } -static void __maybe_unused qcom_link_stack_sanitization(void) +static void qcom_link_stack_sanitization(void) { u64 tmp; @@ -159,6 +159,22 @@ static void __maybe_unused qcom_link_stack_sanitization(void) : "=&r" (tmp)); } +static void qcom_bp_hardening(void) +{ + qcom_link_stack_sanitization(); + if (psci_ops.get_version) + psci_ops.get_version(); +} + +static void enable_qcom_bp_hardening(void *data) +{ + const struct arm64_cpu_capabilities *entry = data; + + install_bp_hardening_cb(entry, + (bp_hardening_cb_t)qcom_bp_hardening, + __psci_hyp_bp_inval_start, + __psci_hyp_bp_inval_end); +} #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ #define MIDR_RANGE(model, min, max) \ @@ -221,6 +237,12 @@ const struct arm64_cpu_capabilities arm64_errata[] = { MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), .enable = enable_psci_bp_hardening, }, + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + .midr_model = MIDR_QCOM_KRYO, + .matches = is_kryo_midr, + .enable = enable_qcom_bp_hardening, + }, #endif { } -- GitLab From 75168374cb84a3b7e443de918ed700a0584e1605 Mon Sep 17 00:00:00 2001 From: Vinayak Menon Date: Fri, 9 Feb 2018 10:07:42 +0530 Subject: [PATCH 2546/5498] defconfig: msm8909w: disable MEMCG MEMCG is found to hurt performance on msm8909w. Disable it. Change-Id: I9dc52c9244f94546705ab41ddbec1527dfe1872d Signed-off-by: Vinayak Menon --- arch/arm/configs/msm8909w-perf_defconfig | 1 - arch/arm/configs/msm8909w_defconfig | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig index 7e341b328d55..7347ea6c5afe 100644 --- a/arch/arm/configs/msm8909w-perf_defconfig +++ b/arch/arm/configs/msm8909w-perf_defconfig @@ -11,7 +11,6 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_MEMCG=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig index f584ac4f00de..1b4471165c0f 100644 --- a/arch/arm/configs/msm8909w_defconfig +++ b/arch/arm/configs/msm8909w_defconfig @@ -11,7 +11,6 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_MEMCG=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_SCHED_HMP=y -- GitLab From 88827675eb22b47b139fde68b2262f26668b8f61 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Mon, 8 Jan 2018 13:27:17 +0530 Subject: [PATCH 2547/5498] ASoC: bg: Add support to handle ssr events in BG Need to close all the active audio sessions when modem or BG was down. Add support to handle subsytem restart events from modem and BG. Change-Id: Id1b3ae5ef6eac0bc3bfe9239cb411e0b91ae2153 Signed-off-by: Bala Kishore Pati --- sound/soc/codecs/bg_codec.c | 172 ++++++++++++++++++++++++++++++++- sound/soc/msm/qdsp6v2/q6core.c | 10 +- 2 files changed, 176 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index b6135763bc87..954bdb77f4ec 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include "bg_codec.h" #include "pktzr.h" @@ -48,6 +50,11 @@ SNDRV_PCM_FMTBIT_S24_3LE) #define BG_BLOB_DATA_SIZE 3136 #define SPEAK_VREG_NAME "vdd-spkr" +/* + *50 Milliseconds sufficient for DSP bring up in the modem + * after Sub System Restart + */ +#define ADSP_STATE_READY_TIMEOUT_MS 50 enum { BG_AIF1_PB = 0, @@ -86,6 +93,8 @@ struct bg_cdc_priv { unsigned long status_mask; struct bg_hw_params hw_params; struct notifier_block bg_pm_nb; + struct notifier_block bg_adsp_nb; + struct notifier_block bg_ssr_nb; /* cal info for codec */ struct fw_info *fw_data; struct firmware_cal *hwdep_spk_cal; @@ -95,6 +104,8 @@ struct bg_cdc_priv { int src[NUM_CODEC_DAIS]; bool hwd_started; bool bg_cal_updated; + bool adsp_dev_up; + bool bg_dev_up; struct regulator *spkr_vreg; uint16_t num_sessions; }; @@ -111,6 +122,9 @@ struct graphite_basic_rsp_result { uint32_t status; }; +static void *adsp_state_notifier; +static void *bg_state_notifier; + static uint32_t get_active_session_id(int dai_id) { @@ -361,15 +375,17 @@ static int _bg_codec_stop(struct bg_cdc_priv *bg_cdc, int dai_id) rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); if (!rsp.buf) return -ENOMEM; - ret = pktzr_cmd_stop(&codec_start, sizeof(codec_start), &rsp); + if (bg_cdc->bg_dev_up) { + ret = pktzr_cmd_stop(&codec_start, sizeof(codec_start), &rsp); if (ret < 0) pr_err("pktzr cmd stop failed with error %d\n", ret); + } mutex_lock(&bg_cdc->bg_cdc_lock); if (bg_cdc->num_sessions > 0) bg_cdc->num_sessions--; - if (bg_cdc->num_sessions == 0) { + if ((bg_cdc->num_sessions == 0) && (bg_cdc->bg_dev_up)) { /* Reset the regulator mode if this is the last session */ ret = regulator_set_optimum_mode(bg_cdc->spkr_vreg, 0); if (ret < 0) @@ -565,7 +581,10 @@ static int bg_cdc_hw_params(struct snd_pcm_substream *substream, pr_debug("%s: dai_name = %s DAI-ID %x rate %d width %d num_ch %d\n", __func__, dai->name, dai->id, params_rate(params), params_width(params), params_channels(params)); - + if (!bg_cdc->bg_dev_up) { + pr_err("%s:Bg ssr in progress\n", __func__); + return -EINVAL; + } bg_cdc->hw_params.active_session = get_active_session_id(dai->id); if (bg_cdc->hw_params.active_session == 0) { pr_err("%s:Invalid dai id %d", __func__, dai->id); @@ -793,7 +812,7 @@ static struct snd_soc_dai_driver bg_cdc_dai[] = { }; static int data_cmd_rsp(void *buf, uint32_t len, void *priv_data, - bool *is_basic_rsp) + bool *is_basic_rsp) { struct graphite_basic_rsp_result *resp; @@ -846,9 +865,124 @@ static void bg_cdc_pktzr_init(struct work_struct *work) kzfree(rsp.buf); } +static int bg_cdc_bg_device_up(struct bg_cdc_priv *bg_cdc) +{ + struct bg_hw_params hw_params; + struct pktzr_cmd_rsp rsp; + int num_of_intents = 2; + uint32_t size[2] = {4096, 4096}; + int ret = 0; + struct bg_glink_ch_cfg ch_info[1] = { + {"CODEC_CHANNEL", num_of_intents, size} + }; + mutex_lock(&bg_cdc->bg_cdc_lock); + if (!bg_cdc->bg_cal_updated) { + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, true); + ret = pktzr_init(bg_cdc->pdev_child, ch_info, 1, data_cmd_rsp); + if (ret < 0) { + dev_err(bg_cdc->dev, "%s: failed in pktzr_init\n", + __func__); + } + /* Send open command */ + rsp.buf_size = sizeof(struct graphite_basic_rsp_result); + rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); + memcpy(&hw_params, &bg_cdc->hw_params, sizeof(hw_params)); + /* Send command to BG to start session */ + ret = pktzr_cmd_open(&hw_params, sizeof(hw_params), &rsp); + if (ret < 0) + pr_err("pktzr cmd open failed\n"); + if (rsp.buf) + kzfree(rsp.buf); + bg_cdc_cal(bg_cdc); + } + if ((!bg_cdc->bg_dev_up) && (bg_cdc->adsp_dev_up)) + snd_soc_card_change_online_state(bg_cdc->codec->component.card, + 1); + mutex_unlock(&bg_cdc->bg_cdc_lock); + return 0; +} + +static int bg_cdc_bg_device_down(struct bg_cdc_priv *bg_cdc) +{ + pktzr_deinit(); + mutex_lock(&bg_cdc->bg_cdc_lock); + if (bg_cdc->bg_cal_updated) { + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, false); + bg_cdc->bg_cal_updated = false; + } + snd_soc_card_change_online_state(bg_cdc->codec->component.card, 0); + mutex_unlock(&bg_cdc->bg_cdc_lock); + return 0; +} + +static int bg_cdc_adsp_device_up(struct bg_cdc_priv *bg_cdc) +{ + bool timedout; + unsigned long timeout; + + mutex_lock(&bg_cdc->bg_cdc_lock); + if (!q6core_is_adsp_ready()) { + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + while (!(timedout = time_after(jiffies, timeout))) { + if (!q6core_is_adsp_ready()) { + dev_err(bg_cdc->dev, "ADSP isn't ready\n"); + } else { + dev_err(bg_cdc->dev, "ADSP is ready\n"); + break; + } + } + } else + dev_err(bg_cdc->dev, "%s:ADSP is ready\n", __func__); + + if ((!bg_cdc->adsp_dev_up) && (bg_cdc->bg_dev_up)) + snd_soc_card_change_online_state(bg_cdc->codec->component.card, + 1); + mutex_unlock(&bg_cdc->bg_cdc_lock); + return 0; +} + +static int bg_cdc_adsp_device_down(struct bg_cdc_priv *bg_cdc) +{ + snd_soc_card_change_online_state(bg_cdc->codec->component.card, 0); + return 0; +} + +static int bg_state_callback(struct notifier_block *nb, unsigned long value, + void *priv) +{ + struct bg_cdc_priv *bg_cdc = + container_of(nb, struct bg_cdc_priv, bg_ssr_nb); + + if (value == SUBSYS_BEFORE_SHUTDOWN) { + bg_cdc_bg_device_down(bg_cdc); + bg_cdc->bg_dev_up = false; + } else if (value == SUBSYS_AFTER_POWERUP) { + bg_cdc_bg_device_up(bg_cdc); + bg_cdc->bg_dev_up = true; + } + return NOTIFY_OK; +} + +static int adsp_state_callback(struct notifier_block *nb, unsigned long value, + void *priv) +{ + struct bg_cdc_priv *bg_cdc = + container_of(nb, struct bg_cdc_priv, bg_adsp_nb); + + if (value == SUBSYS_BEFORE_SHUTDOWN) { + bg_cdc_adsp_device_down(bg_cdc); + bg_cdc->adsp_dev_up = false; + } else if (value == SUBSYS_AFTER_POWERUP) { + bg_cdc_adsp_device_up(bg_cdc); + bg_cdc->adsp_dev_up = true; + } + return NOTIFY_OK; +} static int bg_cdc_codec_probe(struct snd_soc_codec *codec) { struct bg_cdc_priv *bg_cdc = dev_get_drvdata(codec->dev); + const char *subsys_name = NULL; int ret; schedule_delayed_work(&bg_cdc->bg_cdc_pktzr_init_work, @@ -858,6 +992,8 @@ static int bg_cdc_codec_probe(struct snd_soc_codec *codec) sizeof(*(bg_cdc->fw_data)), GFP_KERNEL); bg_cdc->bg_cal_updated = false; + bg_cdc->adsp_dev_up = true; + bg_cdc->bg_dev_up = true; set_bit(BG_CODEC_MIC_CAL, bg_cdc->fw_data->cal_bit); set_bit(BG_CODEC_SPEAKER_CAL, bg_cdc->fw_data->cal_bit); @@ -868,6 +1004,28 @@ static int bg_cdc_codec_probe(struct snd_soc_codec *codec) dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); devm_kfree(codec->dev, bg_cdc->fw_data); } + ret = of_property_read_string(codec->dev->of_node, + "qcom,subsys-name", + &subsys_name); + bg_cdc->bg_adsp_nb.notifier_call = adsp_state_callback; + if (ret) { + dev_dbg(codec->dev, "missing subsys-name entry in dt node\n"); + adsp_state_notifier = subsys_notif_register_notifier("adsp", + &bg_cdc->bg_adsp_nb); + } else { + adsp_state_notifier = subsys_notif_register_notifier( + subsys_name, + &bg_cdc->bg_adsp_nb); + } + if (!adsp_state_notifier) + dev_err(codec->dev, "Failed to register adsp notifier\n"); + bg_cdc->bg_ssr_nb.notifier_call = bg_state_callback; + bg_state_notifier = subsys_notif_register_notifier("bg-wear", + &bg_cdc->bg_ssr_nb); + if (!bg_state_notifier) + dev_err(codec->dev, "Failed to register bg notifier\n"); + + bg_cdc->codec = codec; return 0; } @@ -876,6 +1034,12 @@ static int bg_cdc_codec_remove(struct snd_soc_codec *codec) struct bg_cdc_priv *bg_cdc = dev_get_drvdata(codec->dev); pr_debug("In func %s\n", __func__); pktzr_deinit(); + if (adsp_state_notifier) + subsys_notif_unregister_notifier(adsp_state_notifier, + &bg_cdc->bg_adsp_nb); + if (bg_state_notifier) + subsys_notif_unregister_notifier(bg_state_notifier, + &bg_cdc->bg_ssr_nb); kfree(bg_cdc->fw_data); return 0; } diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c index 3fedc869f024..2ab338ace9fd 100644 --- a/sound/soc/msm/qdsp6v2/q6core.c +++ b/sound/soc/msm/qdsp6v2/q6core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -509,6 +509,12 @@ uint32_t core_set_dolby_manufacturer_id(int manufacturer_id) return rc; } +/* + * q6core_is_adsp_ready - get adsp state + * + * Return: true on adsp state is up or false. + */ + bool q6core_is_adsp_ready(void) { int rc; @@ -543,7 +549,7 @@ bail: pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret); return ret; } - +EXPORT_SYMBOL(q6core_is_adsp_ready); static int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id, uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle) -- GitLab From f74f114dfdf0cc81687e3a2d711b4c2b0f6d669c Mon Sep 17 00:00:00 2001 From: John Zhao Date: Tue, 30 Jan 2018 22:41:57 +0800 Subject: [PATCH 2548/5498] lpm-stats: cleanup lpm stats processing sanity wrapping during list_for_each_entry_reverse iteration, cleanup_stats recursively on current operated stats node could result it will be freed at the end of that cleanup_stats progress. De-referencing it again should not happen. CRs-Fixed: 2182622 Change-Id: Icf837b0aa796fed5fe1721f9fe66fd0dd36ccfd7 Signed-off-by: John Zhao --- drivers/power/qcom/lpm-stats.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/power/qcom/lpm-stats.c b/drivers/power/qcom/lpm-stats.c index 7f1967d432b7..812815da7306 100644 --- a/drivers/power/qcom/lpm-stats.c +++ b/drivers/power/qcom/lpm-stats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -685,8 +685,10 @@ static void cleanup_stats(struct lpm_stats *stats) centry = &stats->child; list_for_each_entry_reverse(pos, centry, sibling) { - if (!list_empty(&pos->child)) + if (!list_empty(&pos->child)) { cleanup_stats(pos); + continue; + } list_del_init(&pos->child); -- GitLab From d815fb1e513591832ba14d7cd0092764f4442e4c Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 15 Jan 2018 16:10:30 -0800 Subject: [PATCH 2549/5498] ASoC: wcd_cpe_core: add size check for WDSP ELF files Add size check to make sure the data sizes from WDSP ELF metadata and the split firmware ELF are the same. Change-Id: Ic2f7dc04dfc95608302cba23461c519378619db0 Signed-off-by: Xiaoyu Ye --- sound/soc/codecs/wcd_cpe_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index 195b3d220d5c..72b0f876b10e 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -337,6 +337,14 @@ static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core, goto done; } + if (phdr->p_filesz != split_fw->size) { + dev_err(core->dev, + "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx", + __func__, split_fname, phdr->p_filesz, split_fw->size); + ret = -EINVAL; + goto done; + } + segment->cpe_addr = phdr->p_paddr; segment->size = phdr->p_filesz; segment->data = (u8 *) split_fw->data; -- GitLab From 2f0b321552758ced0d45327376a418c862e8eeff Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Mon, 15 Jan 2018 16:53:18 +0530 Subject: [PATCH 2550/5498] [SIP] Adding check for SIP/2.0 for packets recieved at SIP port Packets arriving at SIP port will now be checked for SIP/2.0 before getting tagged as SIP packet. Added helper functions strnstr and strnstrn for checking substring in a string in case of non null trmination. Acked-by: Sudhanshu Singh Signed-off-by: Mohammed Javid Change-Id: Ia7d6a3ac5dd1e5f0ce97ee9f6d0c13f16d9dd7f2 --- net/netfilter/nf_conntrack_sip.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 3e24d9846090..a06f89afd26c 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1,6 +1,6 @@ /* SIP extension for IP connection tracking. * - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * (C) 2005 by Christian Hentschel * based on RR's ip_conntrack_ftp.c and other modules. * (C) 2007 United Security Providers @@ -1934,6 +1934,10 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; + /* Check if the header contains SIP version */ + if (!strnstr(dptr, "SIP/2.0", datalen)) + return NF_ACCEPT; + /* here we save the original datalength and data offset of the skb, this * is needed later to split combined skbs */ @@ -2173,6 +2177,10 @@ static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; + /* Check if the header contains SIP version */ + if (!strnstr(dptr, "SIP/2.0", datalen)) + return NF_ACCEPT; + return process_sip_msg(skb, ct, protoff, dataoff, &dptr, &datalen); } -- GitLab From 2fc72b50ab0e44adfccee9ed520f693f449ae378 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 8 Feb 2018 23:58:12 +0530 Subject: [PATCH 2551/5498] ASoC: bg: Fix incorrect num_session update For SVA_usecase codec_prepare is not invoked which results in a incorrect count of num_sessions. Update num_session in _bg_codec_start instead, this ensures correct count of num_session. Change-Id: I4ee5b15f969c02d3943b282203f569253a0b6499 Signed-off-by: Ashish Jain --- sound/soc/codecs/bg_codec.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index b6135763bc87..75f6f4f4d8b3 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -326,6 +326,20 @@ static int _bg_codec_start(struct bg_cdc_priv *bg_cdc, int dai_id) pr_err("%s:Invalid dai id %d", __func__, dai_id); return -EINVAL; } + + mutex_lock(&bg_cdc->bg_cdc_lock); + if (bg_cdc->num_sessions == 0) { + /* set regulator to normal mode */ + ret = regulator_set_optimum_mode(bg_cdc->spkr_vreg, 100000); + if (ret < 0) { + pr_err("Fail to set spkr_vreg mode%d\n", ret); + mutex_unlock(&bg_cdc->bg_cdc_lock); + return ret; + } + } + bg_cdc->num_sessions++; + mutex_unlock(&bg_cdc->bg_cdc_lock); + codec_start.route_to_bg = bg_cdc->src[dai_id]; pr_debug("%s active_session %x route_to_bg %d\n", __func__, codec_start.active_session, codec_start.route_to_bg); @@ -631,18 +645,6 @@ static int bg_cdc_prepare(struct snd_pcm_substream *substream, return -EINVAL; } - mutex_lock(&bg_cdc->bg_cdc_lock); - if (bg_cdc->num_sessions == 0) { - /* set regulator to normal mode */ - ret = regulator_set_optimum_mode(bg_cdc->spkr_vreg, 100000); - if (ret < 0) { - pr_err("Fail to set spkr_vreg mode%d\n", ret); - mutex_unlock(&bg_cdc->bg_cdc_lock); - return ret; - } - } - bg_cdc->num_sessions++; - mutex_unlock(&bg_cdc->bg_cdc_lock); /* Send command to BG to start session */ ret = _bg_codec_start(bg_cdc, dai->id); if (ret < 0) -- GitLab From 8de1c660a3021b579bbcb849032b8976590f91d8 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Tue, 19 Dec 2017 20:34:26 +0530 Subject: [PATCH 2552/5498] ASoC: apr: Add validity check to APR port Add boundary checks for APR port received from ADSP. CRs-Fixed: 2143207 Change-Id: I9a7fa39ee223e1859323caa6eb74c1c8a26a041d Signed-off-by: Aditya Bavanari --- drivers/soc/qcom/qdsp6v2/apr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c index 97e46e6ae6dc..62519a60f36b 100644 --- a/drivers/soc/qcom/qdsp6v2/apr.c +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -608,7 +608,8 @@ void apr_cb_func(void *buf, int len, void *priv) temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF); pr_debug("port = %d t_port = %d\n", data.src_port, temp_port); - if (c_svc->port_cnt && c_svc->port_fn[temp_port]) + if (((temp_port >= 0) && (temp_port < APR_MAX_PORTS)) + && (c_svc->port_cnt && c_svc->port_fn[temp_port])) c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]); else if (c_svc->fn) c_svc->fn(&data, c_svc->priv); -- GitLab From b45f50650b75eb5982bd96f1bf884823157c73e5 Mon Sep 17 00:00:00 2001 From: Gustavo Solaira Date: Fri, 9 Feb 2018 11:38:35 -0800 Subject: [PATCH 2553/5498] ARM: dts: msm: Remove unneeded sharedmem definition in mdm9650 CV2X Remove unneeded definition in mdm9650 CV2X since it's not needed for NAND based targets. Change-Id: I1131a875978b833f594dd51569903aca8a289f5a Signed-off-by: Gustavo Solaira --- arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-cv2x.dts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-cv2x.dts b/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-cv2x.dts index 6443c2b4ed32..463260730266 100644 --- a/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-cv2x.dts +++ b/arch/arm/boot/dts/qcom/mdm9650-v1.1-nand-cv2x.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,11 +18,4 @@ model = "Qualcomm Technologies, Inc. MDM 9650 v1.1 CV2X"; compatible = "qcom,mdm9650-ttp", "qcom,mdm9650", "qcom,ttp"; qcom,board-id = <0x1e 1>, <0x1e 0x101>; - - qcom,rmtfs_sharedmem@0 { - compatible = "qcom,sharedmem-uio"; - reg = <0x0 0x00100000>; - reg-names = "rmtfs"; - qcom,client-id = <0x00000001>; - }; }; -- GitLab From b6453fcb2fe808424106cebad7bb6c4a2bd95f00 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 13 Jan 2017 13:12:29 -0800 Subject: [PATCH 2554/5498] f2fs: do not preallocate blocks which has wrong buffer Sheng Yong reports needless preallocation if write(small_buffer, large_size) is called. In that case, f2fs preallocates large_size, but vfs returns early due to small_buffer size. Let's detect it before preallocation phase in f2fs. Reported-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +++++- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 10 +++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 388d2e4045f7..327f7ea77bd9 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -839,6 +839,9 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, return err; } + if (is_inode_flag_set(inode, FI_NO_PREALLOC)) + return 0; + map.m_lblk = F2FS_BLK_ALIGN(pos); map.m_len = F2FS_BYTES_TO_BLK(pos + count); if (map.m_len > map.m_lblk) @@ -1864,7 +1867,8 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, * we already allocated all the blocks, so we don't need to get * the block addresses when there is no need to fill the page. */ - if (!f2fs_has_inline_data(inode) && len == PAGE_SIZE) + if (!f2fs_has_inline_data(inode) && len == PAGE_SIZE && + !is_inode_flag_set(inode, FI_NO_PREALLOC)) return 0; if (f2fs_has_inline_data(inode) || diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9746ea854dbd..0f1839c9c5eb 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2201,6 +2201,7 @@ enum { FI_INLINE_DOTS, /* indicate inline dot dentries */ FI_DO_DEFRAG, /* indicate defragment is running */ FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ + FI_NO_PREALLOC, /* indicate skipped preallocated blocks */ FI_HOT_DATA, /* indicate file is hot */ FI_EXTRA_ATTR, /* indicate file has extra attribute */ FI_PROJ_INHERIT, /* indicate file inherits projectid */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 002a54ba8001..130a74d29073 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -2536,15 +2537,22 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) inode_lock(inode); ret = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (!ret) { - int err = f2fs_preallocate_blocks(inode, pos, count, + int err; + + if (iov_iter_fault_in_readable(from, iov_iter_count(from))) + set_inode_flag(inode, FI_NO_PREALLOC); + + err = f2fs_preallocate_blocks(inode, pos, count, file->f_flags & O_DIRECT); if (err) { + clear_inode_flag(inode, FI_NO_PREALLOC); inode_unlock(inode); return err; } blk_start_plug(&plug); ret = __generic_file_write_iter(iocb, from); blk_finish_plug(&plug); + clear_inode_flag(inode, FI_NO_PREALLOC); if (ret > 0) f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret); -- GitLab From 35b94063c4d7de700d40fdc6aba0b855c7f3aee8 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 22 Nov 2017 18:23:39 +0800 Subject: [PATCH 2555/5498] f2fs: still write data if preallocate only partial blocks If there is not enough space left, f2fs_preallocate_blocks may only preallocte partial blocks. As a result, the write operation fails but i_blocks is not 0. To avoid this, f2fs should write data in non-preallocation way and write as many data as the size of i_blocks. Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 327f7ea77bd9..06d4cc65b9a6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -861,8 +861,14 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, if (err) return err; } - if (!f2fs_has_inline_data(inode)) - return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); + if (!f2fs_has_inline_data(inode)) { + err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); + if (map.m_len > 0 && err == -ENOSPC) { + set_inode_flag(inode, FI_NO_PREALLOC); + err = 0; + } + return err; + } return err; } -- GitLab From 8c3b1444df4946ab8eabfd26fee6e1b3f9d1e196 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 22 Nov 2017 18:23:40 +0800 Subject: [PATCH 2556/5498] f2fs: remove unused parameter Commit d260081ccf37 ("f2fs: change recovery policy of xattr node block") removes the use of blkaddr, which is no longer used. So remove the parameter. Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/node.c | 2 +- fs/f2fs/recovery.c | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0f1839c9c5eb..808e17bf99a0 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2723,8 +2723,7 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid); int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink); void recover_inline_xattr(struct inode *inode, struct page *page); -int recover_xattr_data(struct inode *inode, struct page *page, - block_t blkaddr); +int recover_xattr_data(struct inode *inode, struct page *page); int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page); int restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 1a4f0dae317f..650e0482ed5c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2247,7 +2247,7 @@ update_inode: f2fs_put_page(ipage, 1); } -int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) +int recover_xattr_data(struct inode *inode, struct page *page) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 559904e9868f..137e9d8b286c 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -392,7 +392,7 @@ truncate_out: } static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, - struct page *page, block_t blkaddr) + struct page *page) { struct dnode_of_data dn; struct node_info ni; @@ -403,7 +403,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (IS_INODE(page)) { recover_inline_xattr(inode, page); } else if (f2fs_has_xattr_block(ofs_of_node(page))) { - err = recover_xattr_data(inode, page, blkaddr); + err = recover_xattr_data(inode, page); if (!err) recovered++; goto out; @@ -556,7 +556,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, break; } } - err = do_recover_data(sbi, entry->inode, page, blkaddr); + err = do_recover_data(sbi, entry->inode, page); if (err) { f2fs_put_page(page, 1); break; -- GitLab From 6f2915eb151e8d86c5b84164f3c5af2b0ad78519 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 23 Nov 2017 23:26:52 +0800 Subject: [PATCH 2557/5498] f2fs: fix lock dependency in between dio_rwsem & i_mmap_sem test/generic/208 reports a potential deadlock as below: Chain exists of: &mm->mmap_sem --> &fi->i_mmap_sem --> &fi->dio_rwsem[WRITE] Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&fi->dio_rwsem[WRITE]); lock(&fi->i_mmap_sem); lock(&fi->dio_rwsem[WRITE]); lock(&mm->mmap_sem); This patch changes the lock dependency as below in fallocate() to fix this issue: - dio_rwsem - i_mmap_sem Fixes: bb06664a534b ("f2fs: avoid race in between GC and block exchange") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 130a74d29073..55901aada557 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1168,14 +1168,14 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) pg_start = offset >> PAGE_SHIFT; pg_end = (offset + len) >> PAGE_SHIFT; + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + down_write(&F2FS_I(inode)->i_mmap_sem); /* write out all dirty pages from offset */ ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); if (ret) - goto out; - - /* avoid gc operation during block exchange */ - down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + goto out_unlock; truncate_pagecache(inode, offset); @@ -1194,9 +1194,8 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); out_unlock: - up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); -out: up_write(&F2FS_I(inode)->i_mmap_sem); + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); return ret; } @@ -1367,6 +1366,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(sbi, true); + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + down_write(&F2FS_I(inode)->i_mmap_sem); ret = truncate_blocks(inode, i_size_read(inode), true); if (ret) @@ -1377,9 +1379,6 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (ret) goto out; - /* avoid gc operation during block exchange */ - down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); - truncate_pagecache(inode, offset); pg_start = offset >> PAGE_SHIFT; @@ -1407,10 +1406,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); - - up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); out: up_write(&F2FS_I(inode)->i_mmap_sem); + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); return ret; } -- GitLab From 7357b4521f502bde62bf8a57b02cbd3354fdd608 Mon Sep 17 00:00:00 2001 From: LiFan Date: Sat, 25 Nov 2017 11:46:18 +0800 Subject: [PATCH 2558/5498] f2fs: remove an excess variable Remove the variable page_idx which no one would miss. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 06d4cc65b9a6..dccdfd73c27b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1203,7 +1203,6 @@ static int f2fs_mpage_readpages(struct address_space *mapping, unsigned nr_pages) { struct bio *bio = NULL; - unsigned page_idx; sector_t last_block_in_bio = 0; struct inode *inode = mapping->host; const unsigned blkbits = inode->i_blkbits; @@ -1220,8 +1219,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_flags = 0; map.m_next_pgofs = NULL; - for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { - + for (; nr_pages; nr_pages--) { if (pages) { page = list_last_entry(pages, struct page, lru); -- GitLab From 08cae724087ab60c1dcdf149d52c9de0848ac20e Mon Sep 17 00:00:00 2001 From: Zhikang Zhang Date: Sun, 26 Nov 2017 02:34:28 +0800 Subject: [PATCH 2559/5498] f2fs: remove repeated f2fs_bug_on f2fs: remove repeated f2fs_bug_on which has already existed in function invalidate_blocks. Signed-off-by: Zhikang Zhang Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 650e0482ed5c..473ac5e66615 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -702,7 +702,6 @@ static void truncate_node(struct dnode_of_data *dn) struct node_info ni; get_node_info(sbi, dn->nid, &ni); - f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); /* Deallocate node address */ invalidate_blocks(sbi, ni.blk_addr); @@ -2261,7 +2260,6 @@ int recover_xattr_data(struct inode *inode, struct page *page) /* 1: invalidate the previous xattr nid */ get_node_info(sbi, prev_xnid, &ni); - f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); invalidate_blocks(sbi, ni.blk_addr); dec_valid_node_count(sbi, inode, false); set_node_addr(sbi, &ni, NULL_ADDR, false); -- GitLab From 09c91079d65374528c6087de236e5d6dcada7c4e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Nov 2017 12:35:28 -0800 Subject: [PATCH 2560/5498] f2fs: switch to fscrypt_file_open() Reviewed-by: Chao Yu Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 55901aada557..f122345e4841 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -475,22 +475,10 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) static int f2fs_file_open(struct inode *inode, struct file *filp) { - struct dentry *dir; + int err = fscrypt_file_open(inode, filp); - if (f2fs_encrypted_inode(inode)) { - int ret = fscrypt_get_encryption_info(inode); - if (ret) - return -EACCES; - if (!fscrypt_has_encryption_key(inode)) - return -ENOKEY; - } - dir = dget_parent(file_dentry(filp)); - if (f2fs_encrypted_inode(d_inode(dir)) && - !fscrypt_has_permitted_context(d_inode(dir), inode)) { - dput(dir); - return -EPERM; - } - dput(dir); + if (err) + return err; return dquot_file_open(inode, filp); } -- GitLab From dd5ca5fe366607bb64f40462e0b7c20c6ef2aa17 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Nov 2017 12:35:29 -0800 Subject: [PATCH 2561/5498] f2fs: switch to fscrypt_prepare_link() Reviewed-by: Chao Yu Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index eaf7476a0942..63417d138c47 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -236,9 +236,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, if (unlikely(f2fs_cp_error(sbi))) return -EIO; - if (f2fs_encrypted_inode(dir) && - !fscrypt_has_permitted_context(dir, inode)) - return -EPERM; + err = fscrypt_prepare_link(old_dentry, dir, dentry); + if (err) + return err; if (is_inode_flag_set(dir, FI_PROJ_INHERIT) && (!projid_eq(F2FS_I(dir)->i_projid, -- GitLab From 5998a21bdab52210db067534b62ea503e8cede76 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Nov 2017 12:35:30 -0800 Subject: [PATCH 2562/5498] f2fs: switch to fscrypt_prepare_rename() Reviewed-by: Chao Yu Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 63417d138c47..7c62a439ad15 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -790,18 +790,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (unlikely(f2fs_cp_error(sbi))) return -EIO; - if ((f2fs_encrypted_inode(old_dir) && - !fscrypt_has_encryption_key(old_dir)) || - (f2fs_encrypted_inode(new_dir) && - !fscrypt_has_encryption_key(new_dir))) - return -ENOKEY; - - if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && - !fscrypt_has_permitted_context(new_dir, old_inode)) { - err = -EPERM; - goto out; - } - if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && (!projid_eq(F2FS_I(new_dir)->i_projid, F2FS_I(old_dentry->d_inode)->i_projid))) @@ -985,18 +973,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, if (unlikely(f2fs_cp_error(sbi))) return -EIO; - if ((f2fs_encrypted_inode(old_dir) && - !fscrypt_has_encryption_key(old_dir)) || - (f2fs_encrypted_inode(new_dir) && - !fscrypt_has_encryption_key(new_dir))) - return -ENOKEY; - - if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) && - (old_dir != new_dir) && - (!fscrypt_has_permitted_context(new_dir, old_inode) || - !fscrypt_has_permitted_context(old_dir, new_inode))) - return -EPERM; - if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && !projid_eq(F2FS_I(new_dir)->i_projid, F2FS_I(old_dentry->d_inode)->i_projid)) || @@ -1132,9 +1108,16 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { + int err; + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; + err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, + flags); + if (err) + return err; + if (flags & RENAME_EXCHANGE) { return f2fs_cross_rename(old_dir, old_dentry, new_dir, new_dentry); -- GitLab From 16c5bfa10f1f01caaeca8a8beb69bdf7de67cd58 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Nov 2017 12:35:31 -0800 Subject: [PATCH 2563/5498] f2fs: switch to fscrypt_prepare_lookup() Reviewed-by: Chao Yu Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 7c62a439ad15..b6eb739b925a 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -349,20 +349,9 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, trace_f2fs_lookup_start(dir, dentry, flags); - if (f2fs_encrypted_inode(dir)) { - err = fscrypt_get_encryption_info(dir); - - /* - * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is - * created while the directory was encrypted and we - * don't have access to the key. - */ - if (fscrypt_has_encryption_key(dir)) - fscrypt_set_encrypted_dentry(dentry); - fscrypt_set_d_op(dentry); - if (err && err != -ENOKEY) - goto out; - } + err = fscrypt_prepare_lookup(dir, dentry, flags); + if (err) + goto out; if (dentry->d_name.len > F2FS_NAME_LEN) { err = -ENAMETOOLONG; -- GitLab From 63a9fc8008ed93911a4b23f4cd166d0c35d3b980 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 29 Nov 2017 12:35:32 -0800 Subject: [PATCH 2564/5498] f2fs: switch to fscrypt_prepare_setattr() Reviewed-by: Chao Yu Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f122345e4841..bf267827baa9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -725,8 +725,13 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; + err = fscrypt_prepare_setattr(dentry, attr); + if (err) + return err; + if (is_quota_modification(inode, attr)) dquot_initialize(inode); + if ((attr->ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || (attr->ia_valid & ATTR_GID && @@ -737,14 +742,6 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } if (attr->ia_valid & ATTR_SIZE) { - if (f2fs_encrypted_inode(inode)) { - err = fscrypt_get_encryption_info(inode); - if (err) - return err; - if (!fscrypt_has_encryption_key(inode)) - return -ENOKEY; - } - if (attr->ia_size <= i_size_read(inode)) { down_write(&F2FS_I(inode)->i_mmap_sem); truncate_setsize(inode, attr->ia_size); -- GitLab From 612e589b373f477e81b9e8f17e30b7e2a42c542d Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Tue, 28 Nov 2017 09:23:00 +0900 Subject: [PATCH 2565/5498] f2fs: apply write hints to select the type of segment for direct write When blocks are allocated for direct write, select the type of segment using the kiocb hint. But if an inode has FI_NO_ALLOC, use the inode hint. Signed-off-by: Hyunchul Lee Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 28 ++++++++++++++++++++-------- fs/f2fs/f2fs.h | 11 +++++++++++ fs/f2fs/file.c | 6 ++++-- fs/f2fs/segment.c | 2 -- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index dccdfd73c27b..ae7532120db3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -782,7 +782,7 @@ got_it: return page; } -static int __allocate_data_block(struct dnode_of_data *dn) +static int __allocate_data_block(struct dnode_of_data *dn, int seg_type) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_summary sum; @@ -807,7 +807,7 @@ alloc: set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); allocate_data_block(sbi, NULL, dn->data_blkaddr, &dn->data_blkaddr, - &sum, CURSEG_WARM_DATA, NULL, false); + &sum, seg_type, NULL, false); set_data_blkaddr(dn); /* update i_size */ @@ -850,12 +850,16 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_len = 0; map.m_next_pgofs = NULL; + map.m_seg_type = NO_CHECK_TYPE; - if (dio) + if (dio) { + /* map.m_seg_type = rw_hint_to_seg_type(iocb->ki_hint); */ + map.m_seg_type = rw_hint_to_seg_type(WRITE_LIFE_NOT_SET); return f2fs_map_blocks(inode, &map, 1, __force_buffered_io(inode, WRITE) ? F2FS_GET_BLOCK_PRE_AIO : F2FS_GET_BLOCK_PRE_DIO); + } if (pos + count > MAX_INLINE_DATA(inode)) { err = f2fs_convert_inline_inode(inode); if (err) @@ -965,7 +969,8 @@ next_block: last_ofs_in_node = dn.ofs_in_node; } } else { - err = __allocate_data_block(&dn); + err = __allocate_data_block(&dn, + map->m_seg_type); if (!err) set_inode_flag(inode, FI_APPEND_WRITE); } @@ -1058,7 +1063,7 @@ out: static int __get_data_block(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create, int flag, - pgoff_t *next_pgofs) + pgoff_t *next_pgofs, int seg_type) { struct f2fs_map_blocks map; int err; @@ -1066,6 +1071,7 @@ static int __get_data_block(struct inode *inode, sector_t iblock, map.m_lblk = iblock; map.m_len = bh->b_size >> inode->i_blkbits; map.m_next_pgofs = next_pgofs; + map.m_seg_type = seg_type; err = f2fs_map_blocks(inode, &map, create, flag); if (!err) { @@ -1081,14 +1087,18 @@ static int get_data_block(struct inode *inode, sector_t iblock, pgoff_t *next_pgofs) { return __get_data_block(inode, iblock, bh_result, create, - flag, next_pgofs); + flag, next_pgofs, + NO_CHECK_TYPE); } static int get_data_block_dio(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_DEFAULT, NULL); + F2FS_GET_BLOCK_DEFAULT, NULL, + rw_hint_to_seg_type( + WRITE_LIFE_NOT_SET)); + /* inode->i_write_hint)); */ } static int get_data_block_bmap(struct inode *inode, sector_t iblock, @@ -1099,7 +1109,8 @@ static int get_data_block_bmap(struct inode *inode, sector_t iblock, return -EFBIG; return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_BMAP, NULL); + F2FS_GET_BLOCK_BMAP, NULL, + NO_CHECK_TYPE); } static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) @@ -1218,6 +1229,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_len = 0; map.m_flags = 0; map.m_next_pgofs = NULL; + map.m_seg_type = NO_CHECK_TYPE; for (; nr_pages; nr_pages--) { if (pages) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 808e17bf99a0..01f371e41c7b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -638,6 +638,7 @@ struct f2fs_map_blocks { unsigned int m_len; unsigned int m_flags; pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */ + int m_seg_type; }; /* for flag in get_data_block */ @@ -2537,6 +2538,15 @@ static inline void *kvzalloc(size_t size, gfp_t flags) return ret; } +enum rw_hint { + WRITE_LIFE_NOT_SET = 0, + WRITE_LIFE_NONE = 1, /* RWH_WRITE_LIFE_NONE */ + WRITE_LIFE_SHORT = 2, /* RWH_WRITE_LIFE_SHORT */ + WRITE_LIFE_MEDIUM = 3, /* RWH_WRITE_LIFE_MEDIUM */ + WRITE_LIFE_LONG = 4, /* RWH_WRITE_LIFE_LONG */ + WRITE_LIFE_EXTREME = 5, /* RWH_WRITE_LIFE_EXTREME */ +}; + static inline int get_extra_isize(struct inode *inode) { return F2FS_I(inode)->i_extra_isize / sizeof(__le32); @@ -2790,6 +2800,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi); void destroy_segment_manager(struct f2fs_sb_info *sbi); int __init create_segment_manager_caches(void); void destroy_segment_manager_caches(void); +int rw_hint_to_seg_type(enum rw_hint hint); /* * checkpoint.c diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index bf267827baa9..a8887e04b604 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1401,7 +1401,8 @@ static int expand_inode_data(struct inode *inode, loff_t offset, loff_t len, int mode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct f2fs_map_blocks map = { .m_next_pgofs = NULL }; + struct f2fs_map_blocks map = { .m_next_pgofs = NULL, + .m_seg_type = NO_CHECK_TYPE }; pgoff_t pg_end; loff_t new_size = i_size_read(inode); loff_t off_end; @@ -2051,7 +2052,8 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, struct f2fs_defragment *range) { struct inode *inode = file_inode(filp); - struct f2fs_map_blocks map = { .m_next_pgofs = NULL }; + struct f2fs_map_blocks map = { .m_next_pgofs = NULL, + .m_seg_type = NO_CHECK_TYPE }; struct extent_info ei = {0,0,0}; pgoff_t pg_start, pg_end; unsigned int blk_per_seg = sbi->blocks_per_seg; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c126195a993a..acf412361ab3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2515,7 +2515,6 @@ static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) return false; } -#if 0 int rw_hint_to_seg_type(enum rw_hint hint) { switch (hint) { @@ -2527,7 +2526,6 @@ int rw_hint_to_seg_type(enum rw_hint hint) return CURSEG_WARM_DATA; } } -#endif static int __get_segment_type_2(struct f2fs_io_info *fio) { -- GitLab From c821080ddd53cdcd95ff6fd3fe0c6b3b41871e72 Mon Sep 17 00:00:00 2001 From: LiFan Date: Tue, 28 Nov 2017 20:17:41 +0800 Subject: [PATCH 2566/5498] f2fs: remove a redundant conditional expression Avoid checking is_inode repeatedly, and make the logic a little bit clearer. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 01f371e41c7b..c101a76b9694 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2091,11 +2091,11 @@ static inline block_t datablock_addr(struct inode *inode, raw_node = F2FS_NODE(node_page); /* from GC path only */ - if (!inode) { - if (is_inode) + if (is_inode) { + if (!inode) base = offset_in_addr(&raw_node->i); - } else if (f2fs_has_extra_attr(inode) && is_inode) { - base = get_extra_isize(inode); + else if (f2fs_has_extra_attr(inode)) + base = get_extra_isize(inode); } addr_array = blkaddr_in_node(raw_node); -- GitLab From fc42fc2cd84d0b3964261bab3898ec57ac885e93 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 30 Nov 2017 19:28:17 +0800 Subject: [PATCH 2567/5498] f2fs: inject fault to kzalloc This patch introduces f2fs_kzalloc based on f2fs_kmalloc in order to support error injection for kzalloc(). Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 +- fs/f2fs/debug.c | 2 +- fs/f2fs/f2fs.h | 6 ++++++ fs/f2fs/namei.c | 2 +- fs/f2fs/node.c | 7 ++++--- fs/f2fs/segment.c | 30 ++++++++++++++++-------------- fs/f2fs/xattr.c | 8 ++++---- 7 files changed, 33 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 288966b4c89d..b3c82c3a2ec0 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -797,7 +797,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) block_t cp_blk_no; int i; - sbi->ckpt = kzalloc(cp_blks * blk_size, GFP_KERNEL); + sbi->ckpt = f2fs_kzalloc(sbi, cp_blks * blk_size, GFP_KERNEL); if (!sbi->ckpt) return -ENOMEM; /* diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 9d580223a72c..7c2ba8b0ee24 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -441,7 +441,7 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_stat_info *si; - si = kzalloc(sizeof(struct f2fs_stat_info), GFP_KERNEL); + si = f2fs_kzalloc(sbi, sizeof(struct f2fs_stat_info), GFP_KERNEL); if (!si) return -ENOMEM; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c101a76b9694..e824f8c9d81e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2547,6 +2547,12 @@ enum rw_hint { WRITE_LIFE_EXTREME = 5, /* RWH_WRITE_LIFE_EXTREME */ }; +static inline void *f2fs_kzalloc(struct f2fs_sb_info *sbi, + size_t size, gfp_t flags) +{ + return f2fs_kmalloc(sbi, size, flags | __GFP_ZERO); +} + static inline int get_extra_isize(struct inode *inode) { return F2FS_I(inode)->i_extra_isize / sizeof(__le32); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index b6eb739b925a..a754fcc765b3 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -527,7 +527,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, struct qstr istr = QSTR_INIT(symname, len); struct fscrypt_str ostr; - sd = kzalloc(disk_link.len, GFP_NOFS); + sd = f2fs_kzalloc(sbi, disk_link.len, GFP_NOFS); if (!sd) { err = -ENOMEM; goto err_out; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 473ac5e66615..da4117085b50 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2604,8 +2604,8 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) nm_i->nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + F2FS_BLKSIZE - 1); - nm_i->nat_bits = kzalloc(nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, - GFP_KERNEL); + nm_i->nat_bits = f2fs_kzalloc(sbi, + nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, GFP_KERNEL); if (!nm_i->nat_bits) return -ENOMEM; @@ -2751,7 +2751,8 @@ int build_node_manager(struct f2fs_sb_info *sbi) { int err; - sbi->nm_info = kzalloc(sizeof(struct f2fs_nm_info), GFP_KERNEL); + sbi->nm_info = f2fs_kzalloc(sbi, sizeof(struct f2fs_nm_info), + GFP_KERNEL); if (!sbi->nm_info) return -ENOMEM; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index acf412361ab3..cced6ad75d0d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -657,7 +657,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) goto init_thread; } - fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); + fcc = f2fs_kzalloc(sbi, sizeof(struct flush_cmd_control), GFP_KERNEL); if (!fcc) return -ENOMEM; atomic_set(&fcc->issued_flush, 0); @@ -1818,7 +1818,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) goto init_thread; } - dcc = kzalloc(sizeof(struct discard_cmd_control), GFP_KERNEL); + dcc = f2fs_kzalloc(sbi, sizeof(struct discard_cmd_control), GFP_KERNEL); if (!dcc) return -ENOMEM; @@ -3421,7 +3421,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) unsigned int bitmap_size; /* allocate memory for SIT information */ - sit_i = kzalloc(sizeof(struct sit_info), GFP_KERNEL); + sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL); if (!sit_i) return -ENOMEM; @@ -3439,29 +3439,30 @@ static int build_sit_info(struct f2fs_sb_info *sbi) for (start = 0; start < MAIN_SEGS(sbi); start++) { sit_i->sentries[start].cur_valid_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); sit_i->sentries[start].ckpt_valid_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); if (!sit_i->sentries[start].cur_valid_map || !sit_i->sentries[start].ckpt_valid_map) return -ENOMEM; #ifdef CONFIG_F2FS_CHECK_FS sit_i->sentries[start].cur_valid_map_mir - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); if (!sit_i->sentries[start].cur_valid_map_mir) return -ENOMEM; #endif if (f2fs_discard_en(sbi)) { sit_i->sentries[start].discard_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, + GFP_KERNEL); if (!sit_i->sentries[start].discard_map) return -ENOMEM; } } - sit_i->tmp_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + sit_i->tmp_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); if (!sit_i->tmp_map) return -ENOMEM; @@ -3510,7 +3511,7 @@ static int build_free_segmap(struct f2fs_sb_info *sbi) unsigned int bitmap_size, sec_bitmap_size; /* allocate memory for free segmap information */ - free_i = kzalloc(sizeof(struct free_segmap_info), GFP_KERNEL); + free_i = f2fs_kzalloc(sbi, sizeof(struct free_segmap_info), GFP_KERNEL); if (!free_i) return -ENOMEM; @@ -3551,12 +3552,12 @@ static int build_curseg(struct f2fs_sb_info *sbi) for (i = 0; i < NR_CURSEG_TYPE; i++) { mutex_init(&array[i].curseg_mutex); - array[i].sum_blk = kzalloc(PAGE_SIZE, GFP_KERNEL); + array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL); if (!array[i].sum_blk) return -ENOMEM; init_rwsem(&array[i].journal_rwsem); - array[i].journal = kzalloc(sizeof(struct f2fs_journal), - GFP_KERNEL); + array[i].journal = f2fs_kzalloc(sbi, + sizeof(struct f2fs_journal), GFP_KERNEL); if (!array[i].journal) return -ENOMEM; array[i].segno = NULL_SEGNO; @@ -3714,7 +3715,8 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi) unsigned int bitmap_size, i; /* allocate memory for dirty segments list information */ - dirty_i = kzalloc(sizeof(struct dirty_seglist_info), GFP_KERNEL); + dirty_i = f2fs_kzalloc(sbi, sizeof(struct dirty_seglist_info), + GFP_KERNEL); if (!dirty_i) return -ENOMEM; @@ -3768,7 +3770,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi) struct f2fs_sm_info *sm_info; int err; - sm_info = kzalloc(sizeof(struct f2fs_sm_info), GFP_KERNEL); + sm_info = f2fs_kzalloc(sbi, sizeof(struct f2fs_sm_info), GFP_KERNEL); if (!sm_info) return -ENOMEM; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 56c80831976b..cf979b3c5fa3 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -347,8 +347,8 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, if (!size && !inline_size) return -ENODATA; - txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE, - GFP_F2FS_ZERO); + txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), + inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS); if (!txattr_addr) return -ENOMEM; @@ -400,8 +400,8 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, void *txattr_addr; int err; - txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE, - GFP_F2FS_ZERO); + txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), + inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS); if (!txattr_addr) return -ENOMEM; -- GitLab From 8c72d9db0e504458b9e3dbd0455bc962f68e6fb4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 30 Nov 2017 19:28:18 +0800 Subject: [PATCH 2568/5498] f2fs: inject fault to kvmalloc This patch supports to inject fault into kvmalloc/kvzalloc. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 19 +++++++++++++++++++ fs/f2fs/file.c | 6 ++++-- fs/f2fs/node.c | 6 +++--- fs/f2fs/segment.c | 16 +++++++++------- fs/f2fs/super.c | 1 + 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e824f8c9d81e..84218c511cfe 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -45,6 +45,7 @@ #ifdef CONFIG_F2FS_FAULT_INJECTION enum { FAULT_KMALLOC, + FAULT_KVMALLOC, FAULT_PAGE_ALLOC, FAULT_PAGE_GET, FAULT_ALLOC_BIO, @@ -2553,6 +2554,24 @@ static inline void *f2fs_kzalloc(struct f2fs_sb_info *sbi, return f2fs_kmalloc(sbi, size, flags | __GFP_ZERO); } +static inline void *f2fs_kvmalloc(struct f2fs_sb_info *sbi, + size_t size, gfp_t flags) +{ +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_KVMALLOC)) { + f2fs_show_injection_info(FAULT_KVMALLOC); + return NULL; + } +#endif + return kvmalloc(size, flags); +} + +static inline void *f2fs_kvzalloc(struct f2fs_sb_info *sbi, + size_t size, gfp_t flags) +{ + return f2fs_kvmalloc(sbi, size, flags | __GFP_ZERO); +} + static inline int get_extra_isize(struct inode *inode) { return F2FS_I(inode)->i_extra_isize / sizeof(__le32); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a8887e04b604..2668ec5404a4 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1081,11 +1081,13 @@ static int __exchange_data_block(struct inode *src_inode, while (len) { olen = min((pgoff_t)4 * ADDRS_PER_BLOCK, len); - src_blkaddr = kvzalloc(sizeof(block_t) * olen, GFP_KERNEL); + src_blkaddr = f2fs_kvzalloc(F2FS_I_SB(src_inode), + sizeof(block_t) * olen, GFP_KERNEL); if (!src_blkaddr) return -ENOMEM; - do_replace = kvzalloc(sizeof(int) * olen, GFP_KERNEL); + do_replace = f2fs_kvzalloc(F2FS_I_SB(src_inode), + sizeof(int) * olen, GFP_KERNEL); if (!do_replace) { kvfree(src_blkaddr); return -ENOMEM; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index da4117085b50..595c37120d7d 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2730,17 +2730,17 @@ static int init_free_nid_cache(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); - nm_i->free_nid_bitmap = kvzalloc(nm_i->nat_blocks * + nm_i->free_nid_bitmap = f2fs_kvzalloc(sbi, nm_i->nat_blocks * NAT_ENTRY_BITMAP_SIZE, GFP_KERNEL); if (!nm_i->free_nid_bitmap) return -ENOMEM; - nm_i->nat_block_bitmap = kvzalloc(nm_i->nat_blocks / 8, + nm_i->nat_block_bitmap = f2fs_kvzalloc(sbi, nm_i->nat_blocks / 8, GFP_KERNEL); if (!nm_i->nat_block_bitmap) return -ENOMEM; - nm_i->free_nid_count = kvzalloc(nm_i->nat_blocks * + nm_i->free_nid_count = f2fs_kvzalloc(sbi, nm_i->nat_blocks * sizeof(unsigned short), GFP_KERNEL); if (!nm_i->free_nid_count) return -ENOMEM; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index cced6ad75d0d..330fe2c83bf2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3427,13 +3427,14 @@ static int build_sit_info(struct f2fs_sb_info *sbi) SM_I(sbi)->sit_info = sit_i; - sit_i->sentries = kvzalloc(MAIN_SEGS(sbi) * + sit_i->sentries = f2fs_kvzalloc(sbi, 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 = kvzalloc(bitmap_size, GFP_KERNEL); + sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(sbi, bitmap_size, + GFP_KERNEL); if (!sit_i->dirty_sentries_bitmap) return -ENOMEM; @@ -3467,7 +3468,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) return -ENOMEM; if (sbi->segs_per_sec > 1) { - sit_i->sec_entries = kvzalloc(MAIN_SECS(sbi) * + sit_i->sec_entries = f2fs_kvzalloc(sbi, MAIN_SECS(sbi) * sizeof(struct sec_entry), GFP_KERNEL); if (!sit_i->sec_entries) return -ENOMEM; @@ -3518,12 +3519,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 = kvmalloc(bitmap_size, GFP_KERNEL); + free_i->free_segmap = f2fs_kvmalloc(sbi, bitmap_size, GFP_KERNEL); if (!free_i->free_segmap) return -ENOMEM; sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); - free_i->free_secmap = kvmalloc(sec_bitmap_size, GFP_KERNEL); + free_i->free_secmap = f2fs_kvmalloc(sbi, sec_bitmap_size, GFP_KERNEL); if (!free_i->free_secmap) return -ENOMEM; @@ -3703,7 +3704,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 = kvzalloc(bitmap_size, GFP_KERNEL); + dirty_i->victim_secmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); if (!dirty_i->victim_secmap) return -ENOMEM; return 0; @@ -3726,7 +3727,8 @@ 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] = kvzalloc(bitmap_size, GFP_KERNEL); + dirty_i->dirty_segmap[i] = f2fs_kvzalloc(sbi, bitmap_size, + GFP_KERNEL); if (!dirty_i->dirty_segmap[i]) return -ENOMEM; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 38f11b866dcf..d046f96c9335 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -46,6 +46,7 @@ static struct kmem_cache *f2fs_inode_cachep; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", + [FAULT_KVMALLOC] = "kvmalloc", [FAULT_PAGE_ALLOC] = "page alloc", [FAULT_PAGE_GET] = "page get", [FAULT_ALLOC_BIO] = "alloc bio", -- GitLab From 3acc2f316ff12399aafd45501dc34211af0c6033 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 30 Nov 2017 19:28:19 +0800 Subject: [PATCH 2569/5498] f2fs: spread f2fs_k{m,z}alloc Use f2fs_k{m,z}alloc as much as possible to increase fault injection points. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 +- fs/f2fs/super.c | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 330fe2c83bf2..2c1726daaee2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3545,7 +3545,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) struct curseg_info *array; int i; - array = kcalloc(NR_CURSEG_TYPE, sizeof(*array), GFP_KERNEL); + array = f2fs_kzalloc(sbi, sizeof(*array) * NR_CURSEG_TYPE, GFP_KERNEL); if (!array) return -ENOMEM; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d046f96c9335..85be66104959 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2200,14 +2200,15 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi) if (nr_sectors & (bdev_zone_sectors(bdev) - 1)) FDEV(devi).nr_blkz++; - FDEV(devi).blkz_type = kmalloc(FDEV(devi).nr_blkz, GFP_KERNEL); + FDEV(devi).blkz_type = f2fs_kmalloc(sbi, FDEV(devi).nr_blkz, + GFP_KERNEL); if (!FDEV(devi).blkz_type) return -ENOMEM; #define F2FS_REPORT_NR_ZONES 4096 - zones = kcalloc(F2FS_REPORT_NR_ZONES, sizeof(struct blk_zone), - GFP_KERNEL); + zones = f2fs_kzalloc(sbi, sizeof(struct blk_zone) * + F2FS_REPORT_NR_ZONES, GFP_KERNEL); if (!zones) return -ENOMEM; @@ -2351,8 +2352,8 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) * Initialize multiple devices information, or single * zoned block device information. */ - sbi->devs = kcalloc(max_devices, sizeof(struct f2fs_dev_info), - GFP_KERNEL); + sbi->devs = f2fs_kzalloc(sbi, sizeof(struct f2fs_dev_info) * + max_devices, GFP_KERNEL); if (!sbi->devs) return -ENOMEM; @@ -2560,8 +2561,9 @@ try_onemore: int n = (i == META) ? 1: NR_TEMP_TYPE; int j; - sbi->write_io[i] = kmalloc(n * sizeof(struct f2fs_bio_info), - GFP_KERNEL); + sbi->write_io[i] = f2fs_kmalloc(sbi, + n * sizeof(struct f2fs_bio_info), + GFP_KERNEL); if (!sbi->write_io[i]) { err = -ENOMEM; goto free_options; -- GitLab From 5d81acf57de67f3f9bd1d8a57f037398af26fc38 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 30 Nov 2017 19:28:20 +0800 Subject: [PATCH 2570/5498] f2fs: fix error handling in fill_super In fill_super, if we fail to call f2fs_build_stats(), it needs to detach from global f2fs shrink list, otherwise once system starts to shrink slab cache, we will encounter below panic: BUG: unable to handle kernel paging request at 00007d35 Oops: 0002 [#1] PREEMPT SMP EIP: __lock_acquire+0x70/0x12c0 Call Trace: lock_acquire+0xae/0x220 mutex_trylock+0xc5/0xf0 f2fs_shrink_count+0x32/0xb0 [f2fs] shrink_slab+0xf1/0x5b0 drop_slab_node+0x35/0x60 drop_slab+0xf/0x20 drop_caches_sysctl_handler+0x79/0xc0 proc_sys_call_handler+0xa4/0xc0 proc_sys_write+0x1f/0x30 __vfs_write+0x24/0x150 SyS_write+0x44/0x90 do_fast_syscall_32+0xa1/0x1ca entry_SYSENTER_32+0x4c/0x7b In addition, this patch relocates f2fs_join_shrinker in fill_super to avoid unneeded error handling of it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 85be66104959..44cf807af639 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2671,18 +2671,16 @@ try_onemore: goto free_nm; } - f2fs_join_shrinker(sbi); - err = f2fs_build_stats(sbi); if (err) - goto free_nm; + goto free_node_inode; /* read root inode and dentry */ root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); if (IS_ERR(root)) { f2fs_msg(sb, KERN_ERR, "Failed to read root inode"); err = PTR_ERR(root); - goto free_node_inode; + goto free_stats; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { iput(root); @@ -2778,6 +2776,8 @@ skip_recovery: sbi->valid_super_block ? 1 : 2, err); } + f2fs_join_shrinker(sbi); + f2fs_msg(sbi->sb, KERN_NOTICE, "Mounted with checkpoint version = %llx", cur_cp_version(F2FS_CKPT(sbi))); f2fs_update_time(sbi, CP_TIME); @@ -2804,14 +2804,12 @@ free_sysfs: free_root_inode: dput(sb->s_root); sb->s_root = NULL; +free_stats: + f2fs_destroy_stats(sbi); free_node_inode: - truncate_inode_pages_final(NODE_MAPPING(sbi)); - mutex_lock(&sbi->umount_mutex); release_ino_entry(sbi, true); - f2fs_leave_shrinker(sbi); + truncate_inode_pages_final(NODE_MAPPING(sbi)); iput(sbi->node_inode); - mutex_unlock(&sbi->umount_mutex); - f2fs_destroy_stats(sbi); free_nm: destroy_node_manager(sbi); free_sm: -- GitLab From 8dfee8c44a14b2b46d88a6b55f3e301ff0d6894d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 30 Nov 2017 19:28:21 +0800 Subject: [PATCH 2571/5498] f2fs: clean up hash codes f2fs_chksum and f2fs_crc32 use the same 'crc32' crypto engine, also their implementation are almost the same, except with different shash description context. Introduce __f2fs_crc32 to wrap the common codes, and reuse it in f2fs_chksum and f2fs_crc32. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 84218c511cfe..4d3e712dd36a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1353,30 +1353,7 @@ static inline bool is_idle(struct f2fs_sb_info *sbi) /* * Inline functions */ -static inline u32 f2fs_crc32(struct f2fs_sb_info *sbi, const void *address, - unsigned int length) -{ - SHASH_DESC_ON_STACK(shash, sbi->s_chksum_driver); - u32 *ctx = (u32 *)shash_desc_ctx(shash); - int err; - - shash->tfm = sbi->s_chksum_driver; - shash->flags = 0; - *ctx = F2FS_SUPER_MAGIC; - - err = crypto_shash_update(shash, address, length); - BUG_ON(err); - - return *ctx; -} - -static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc, - void *buf, size_t buf_size) -{ - return f2fs_crc32(sbi, buf, buf_size) == blk_crc; -} - -static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, +static inline u32 __f2fs_crc32(struct f2fs_sb_info *sbi, u32 crc, const void *address, unsigned int length) { struct { @@ -1397,6 +1374,24 @@ static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, return *(u32 *)desc.ctx; } +static inline u32 f2fs_crc32(struct f2fs_sb_info *sbi, const void *address, + unsigned int length) +{ + return __f2fs_crc32(sbi, F2FS_SUPER_MAGIC, address, length); +} + +static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc, + void *buf, size_t buf_size) +{ + return f2fs_crc32(sbi, buf, buf_size) == blk_crc; +} + +static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, + const void *address, unsigned int length) +{ + return __f2fs_crc32(sbi, crc, address, length); +} + static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) { return container_of(inode, struct f2fs_inode_info, vfs_inode); -- GitLab From 15f92902926e4b1d404a2cba318cde96e01a03e2 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 30 Nov 2017 19:28:22 +0800 Subject: [PATCH 2572/5498] f2fs: clean up f2fs_map_blocks f2fs_map_blocks(): if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) { if (create) { ... } else { ... if (flag == F2FS_GET_BLOCK_FIEMAP && blkaddr == NULL_ADDR) { ... } if (flag != F2FS_GET_BLOCK_FIEMAP || blkaddr != NEW_ADDR) goto sync_out; } It means we can break the loop in cases of: a) flag != F2FS_GET_BLOCK_FIEMAP or b) flag == F2FS_GET_BLOCK_FIEMAP && blkaddr == NULL_ADDR Condition b) is the same as previous one, so merge operations of them for readability. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ae7532120db3..08829581364c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -987,9 +987,9 @@ next_block: blkaddr == NULL_ADDR) { if (map->m_next_pgofs) *map->m_next_pgofs = pgofs + 1; + goto sync_out; } - if (flag != F2FS_GET_BLOCK_FIEMAP || - blkaddr != NEW_ADDR) + if (flag != F2FS_GET_BLOCK_FIEMAP) goto sync_out; } } -- GitLab From 246018281458d708476772a9fa77f603a8f16876 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 30 Nov 2017 19:28:23 +0800 Subject: [PATCH 2573/5498] f2fs: don't return value in truncate_data_blocks_range There is no caller cares about return value of truncate_data_blocks_range, remove it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/file.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4d3e712dd36a..6c304c52b9ec 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2628,7 +2628,7 @@ int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); int f2fs_setattr(struct dentry *dentry, struct iattr *attr); int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end); -int truncate_data_blocks_range(struct dnode_of_data *dn, int count); +void truncate_data_blocks_range(struct dnode_of_data *dn, int count); long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2668ec5404a4..c6d2bf16b820 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -482,7 +482,7 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) return dquot_file_open(inode, filp); } -int truncate_data_blocks_range(struct dnode_of_data *dn, int count) +void truncate_data_blocks_range(struct dnode_of_data *dn, int count) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_node *raw_node; @@ -525,7 +525,6 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) f2fs_update_time(sbi, REQ_TIME); trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid, dn->ofs_in_node, nr_free); - return nr_free; } void truncate_data_blocks(struct dnode_of_data *dn) -- GitLab From e88ab6696058de61f0bb2473cd04106904c60089 Mon Sep 17 00:00:00 2001 From: LiFan Date: Tue, 5 Dec 2017 16:38:01 +0800 Subject: [PATCH 2574/5498] f2fs: use unlikely for release case Since the variable release is only nonzero when another unlikely case occurs, use unlikely() on it seems logical. Signed-off-by: Fan li Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6c304c52b9ec..2177c53959ae 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1692,7 +1692,7 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, } spin_unlock(&sbi->stat_lock); - if (release) + if (unlikely(release)) dquot_release_reservation_block(inode, release); f2fs_i_blocks_write(inode, *count, true, true); return 0; -- GitLab From 54c06e52d661ae2dc5686421ffadd8feef6ee325 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 6 Dec 2017 11:31:29 +0800 Subject: [PATCH 2575/5498] f2fs: no need return value in restore summary process No need return value in restore summary process Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 3 +-- fs/f2fs/segment.c | 14 +++----------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2177c53959ae..0ee0c7cbe43d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2755,7 +2755,7 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink); void recover_inline_xattr(struct inode *inode, struct page *page); int recover_xattr_data(struct inode *inode, struct page *page); int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page); -int restore_node_summary(struct f2fs_sb_info *sbi, +void restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum); void flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc); int build_node_manager(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 595c37120d7d..a9644bf69b8c 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2348,7 +2348,7 @@ retry: return 0; } -int restore_node_summary(struct f2fs_sb_info *sbi, +void restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum) { struct f2fs_node *rn; @@ -2381,7 +2381,6 @@ int restore_node_summary(struct f2fs_sb_info *sbi, invalidate_mapping_pages(META_MAPPING(sbi), addr, addr + nrpages); } - return 0; } static void remove_nats_in_journal(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2c1726daaee2..6a34685b9d99 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2906,7 +2906,7 @@ void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr) } } -static int read_compacted_summaries(struct f2fs_sb_info *sbi) +static void read_compacted_summaries(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct curseg_info *seg_i; @@ -2963,7 +2963,6 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) } } f2fs_put_page(page, 1); - return 0; } static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) @@ -3009,13 +3008,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) ns->ofs_in_node = 0; } } else { - int err; - - err = restore_node_summary(sbi, segno, sum); - if (err) { - f2fs_put_page(new, 1); - return err; - } + restore_node_summary(sbi, segno, sum); } } @@ -3054,8 +3047,7 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) META_CP, true); /* restore for compacted data summary */ - if (read_compacted_summaries(sbi)) - return -EINVAL; + read_compacted_summaries(sbi); type = CURSEG_HOT_NODE; } -- GitLab From 62b6a5f6896a4fd3987d324bf1ccf330054e45b8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 12 Dec 2017 14:11:40 +0800 Subject: [PATCH 2576/5498] f2fs: fix potential hangtask in f2fs_trace_pid As Jia-Ju Bai reported: "According to fs/f2fs/trace.c, the kernel module may sleep under a spinlock. The function call path is: f2fs_trace_pid (acquire the spinlock) f2fs_radix_tree_insert cond_resched --> may sleep I do not find a good way to fix it, so I only report. This possible bug is found by my static analysis tool (DSAC) and my code review." Obviously, it's problemetic to schedule in critical region of spinlock, which will cause uninterruptable sleep if there is no waker. This patch changes to use mutex lock intead of spinlock to avoid this condition. Reported-by: Jia-Ju Bai Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/trace.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c index bccbbf2616d2..a1fcd00bbb2b 100644 --- a/fs/f2fs/trace.c +++ b/fs/f2fs/trace.c @@ -17,7 +17,7 @@ #include "trace.h" static RADIX_TREE(pids, GFP_ATOMIC); -static spinlock_t pids_lock; +static struct mutex pids_lock; static struct last_io_info last_io; static inline void __print_last_io(void) @@ -64,7 +64,7 @@ void f2fs_trace_pid(struct page *page) if (radix_tree_preload(GFP_NOFS)) return; - spin_lock(&pids_lock); + mutex_lock(&pids_lock); p = radix_tree_lookup(&pids, pid); if (p == current) goto out; @@ -77,7 +77,7 @@ void f2fs_trace_pid(struct page *page) MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), pid, current->comm); out: - spin_unlock(&pids_lock); + mutex_unlock(&pids_lock); radix_tree_preload_end(); } @@ -122,7 +122,7 @@ void f2fs_trace_ios(struct f2fs_io_info *fio, int flush) void f2fs_build_trace_ios(void) { - spin_lock_init(&pids_lock); + mutex_init(&pids_lock); } #define PIDVEC_SIZE 128 @@ -150,7 +150,7 @@ void f2fs_destroy_trace_ios(void) pid_t next_pid = 0; unsigned int found; - spin_lock(&pids_lock); + mutex_lock(&pids_lock); while ((found = gang_lookup_pids(pid, next_pid, PIDVEC_SIZE))) { unsigned idx; @@ -158,5 +158,5 @@ void f2fs_destroy_trace_ios(void) for (idx = 0; idx < found; idx++) radix_tree_delete(&pids, pid[idx]); } - spin_unlock(&pids_lock); + mutex_unlock(&pids_lock); } -- GitLab From e913b1903a98ba5536a1d1632de0ebeabc90d983 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Tue, 5 Dec 2017 12:07:47 +0800 Subject: [PATCH 2577/5498] f2fs: fix an error case of missing update inode page -Thread A Thread B -write_checkpoint -block_operations -f2fs_unlock_all -f2fs_sync_file -f2fs_write_inode -f2fs_inode_synced -f2fs_sync_inode_meta -sync_node_pages -set_page_drity In this case, if sudden power off without next new checkpoint, the last inode page update will lost. wb_writeback is same with fsync. Yunlei also reproduced the bug by: @@ -366,7 +366,7 @@ int update_inode(struct inode *inode, struct page *node_page) struct extent_tree *et = F2FS_I(inode)->extent_tree; f2fs_inode_synced(inode); - + msleep(10000); f2fs_wait_on_page_writeback(node_page, NODE, true); shell 1: shell2: dd if=/dev/zero of=./test bs=1M count=10 sync echo "hello" >> ./test fsync test // sleep 10s sync //return quickly echo c > /proc/sysrq-trigger Signed-off-by: Yunlei He Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 4 ++-- fs/f2fs/inode.c | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0ee0c7cbe43d..2614c77bf1a1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2641,8 +2641,8 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page); struct inode *f2fs_iget(struct super_block *sb, unsigned long ino); struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino); int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink); -int update_inode(struct inode *inode, struct page *node_page); -int update_inode_page(struct inode *inode); +void update_inode(struct inode *inode, struct page *node_page); +void update_inode_page(struct inode *inode); int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc); void f2fs_evict_inode(struct inode *inode); void handle_failed_inode(struct inode *inode); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 9eb3cc2486d4..aff0f1993efe 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -360,14 +360,15 @@ retry: return inode; } -int update_inode(struct inode *inode, struct page *node_page) +void update_inode(struct inode *inode, struct page *node_page) { struct f2fs_inode *ri; struct extent_tree *et = F2FS_I(inode)->extent_tree; - f2fs_inode_synced(inode); - f2fs_wait_on_page_writeback(node_page, NODE, true); + set_page_dirty(node_page); + + f2fs_inode_synced(inode); ri = F2FS_INODE(node_page); @@ -426,14 +427,12 @@ int update_inode(struct inode *inode, struct page *node_page) if (inode->i_nlink == 0) clear_inline_node(node_page); - return set_page_dirty(node_page); } -int update_inode_page(struct inode *inode) +void update_inode_page(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *node_page; - int ret = 0; retry: node_page = get_node_page(sbi, inode->i_ino); if (IS_ERR(node_page)) { @@ -444,11 +443,10 @@ retry: } else if (err != -ENOENT) { f2fs_stop_checkpoint(sbi, false); } - return 0; + return; } - ret = update_inode(inode, node_page); + update_inode(inode, node_page); f2fs_put_page(node_page, 1); - return ret; } int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) -- GitLab From f1b68a501b21c02f35cd6fe9d41f4a49a28bc7fd Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 19 Dec 2017 19:16:34 -0800 Subject: [PATCH 2578/5498] f2fs: return error during fill_super Let's avoid BUG_ON during fill_super, when on-disk was totall corrupted. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 16 ++++++++++++---- fs/f2fs/segment.h | 22 ++++++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6a34685b9d99..bf04b04e4ae0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3559,7 +3559,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) return restore_curseg_summaries(sbi); } -static void build_sit_entries(struct f2fs_sb_info *sbi) +static int build_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); @@ -3569,6 +3569,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, start, end; unsigned int readed, start_blk = 0; + int err = 0; do { readed = ra_meta_pages(sbi, start_blk, BIO_MAX_PAGES, @@ -3587,7 +3588,9 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; f2fs_put_page(page, 1); - check_block_count(sbi, start, &sit); + err = check_block_count(sbi, start, &sit); + if (err) + return err; seg_info_from_raw_sit(se, &sit); /* build discard map only one time */ @@ -3622,7 +3625,9 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) old_valid_blocks = se->valid_blocks; - check_block_count(sbi, start, &sit); + err = check_block_count(sbi, start, &sit); + if (err) + break; seg_info_from_raw_sit(se, &sit); if (f2fs_discard_en(sbi)) { @@ -3642,6 +3647,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) se->valid_blocks - old_valid_blocks; } up_read(&curseg->journal_rwsem); + return err; } static void init_free_segmap(struct f2fs_sb_info *sbi) @@ -3816,7 +3822,9 @@ int build_segment_manager(struct f2fs_sb_info *sbi) return err; /* reinit free segmap based on SIT */ - build_sit_entries(sbi); + err = build_sit_entries(sbi); + if (err) + return err; init_free_segmap(sbi); err = build_dirty_segmap(sbi); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 806e7b7866df..dffb9b5960e9 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -654,7 +654,7 @@ static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) /* * Summary block is always treated as an invalid block */ -static inline void check_block_count(struct f2fs_sb_info *sbi, +static inline int check_block_count(struct f2fs_sb_info *sbi, int segno, struct f2fs_sit_entry *raw_sit) { #ifdef CONFIG_F2FS_CHECK_FS @@ -676,11 +676,25 @@ static inline void check_block_count(struct f2fs_sb_info *sbi, cur_pos = next_pos; is_valid = !is_valid; } while (cur_pos < sbi->blocks_per_seg); - BUG_ON(GET_SIT_VBLOCKS(raw_sit) != valid_blocks); + + if (unlikely(GET_SIT_VBLOCKS(raw_sit) != valid_blocks)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Mismatch valid blocks %d vs. %d", + GET_SIT_VBLOCKS(raw_sit), valid_blocks); + set_sbi_flag(sbi, SBI_NEED_FSCK); + return -EINVAL; + } #endif /* check segment usage, and check boundary of a given segment number */ - f2fs_bug_on(sbi, GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg - || segno > TOTAL_SEGS(sbi) - 1); + if (unlikely(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg + || segno > TOTAL_SEGS(sbi) - 1)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Wrong valid blocks %d or segno %u", + GET_SIT_VBLOCKS(raw_sit), segno); + set_sbi_flag(sbi, SBI_NEED_FSCK); + return -EINVAL; + } + return 0; } static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, -- GitLab From 1e85f5d709122daa793a59fbddcf091ac067142d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 28 Dec 2017 08:09:44 -0800 Subject: [PATCH 2579/5498] f2fs: recover directory operations by fsync This fixes generic/342 which doesn't recover renamed file which was fsynced before. It will be done via another fsync on newly created file. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 2 ++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 3 +++ fs/f2fs/namei.c | 4 ++++ include/trace/events/f2fs.h | 3 ++- 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 55fb45b66ed2..bde445e4e690 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -713,6 +713,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); + add_ino_entry(F2FS_I_SB(dir), dir->i_ino, TRANS_DIR_INO); + if (f2fs_has_inline_dentry(dir)) return f2fs_delete_inline_entry(dentry, page, dir, inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 2614c77bf1a1..9aa7febacadc 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -279,6 +279,7 @@ enum { ORPHAN_INO, /* for orphan ino list */ APPEND_INO, /* for append ino list */ UPDATE_INO, /* for update ino list */ + TRANS_DIR_INO, /* for trasactions dir ino list */ FLUSH_INO, /* for multiple device flushing */ MAX_INO_ENTRY, /* max. list */ }; @@ -1018,6 +1019,7 @@ enum cp_reason_type { CP_NODE_NEED_CP, CP_FASTBOOT_MODE, CP_SPEC_LOG_NUM, + CP_RECOVER_DIR, }; enum iostat_type { diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c6d2bf16b820..2d08717976ee 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -169,6 +169,9 @@ static inline enum cp_reason_type need_do_checkpoint(struct inode *inode) cp_reason = CP_FASTBOOT_MODE; else if (sbi->active_logs == 2) cp_reason = CP_SPEC_LOG_NUM; + else if (need_dentry_mark(sbi, inode->i_ino) && + exist_written_data(sbi, F2FS_I(inode)->i_pino, TRANS_DIR_INO)) + cp_reason = CP_RECOVER_DIR; return cp_reason; } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index a754fcc765b3..470bfe8c8eba 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -918,6 +918,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } f2fs_i_links_write(old_dir, false); } + add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO); f2fs_unlock_op(sbi); @@ -1068,6 +1069,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, } f2fs_mark_inode_dirty_sync(new_dir, false); + add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO); + add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO); + f2fs_unlock_op(sbi); if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 7e117885dde5..4a089373838d 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -101,7 +101,8 @@ { CP_NO_SPC_ROLL, "no space roll forward" }, \ { CP_NODE_NEED_CP, "node needs cp" }, \ { CP_FASTBOOT_MODE, "fastboot mode" }, \ - { CP_SPEC_LOG_NUM, "log type is 2" }) + { CP_SPEC_LOG_NUM, "log type is 2" }, \ + { CP_RECOVER_DIR, "dir needs recovery" }) struct victim_sel_policy; struct f2fs_map_blocks; -- GitLab From 29f0297fef9d56b6a35a791b04154de6938d2885 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 28 Dec 2017 17:47:19 -0800 Subject: [PATCH 2580/5498] f2fs: fix missing error number for xattr operation This fixes generic/449 hang problem caused by no ENOSPC forever which should be returned by setxattr under disk full scenario. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index cf979b3c5fa3..249b25eda9e4 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -482,6 +482,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, if (F2FS_I(inode)->i_xattr_nid) { xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); if (IS_ERR(xpage)) { + err = PTR_ERR(xpage); alloc_nid_failed(sbi, new_nid); goto in_page_out; } @@ -492,6 +493,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, set_new_dnode(&dn, inode, NULL, NULL, new_nid); xpage = new_node_page(&dn, XATTR_NODE_OFFSET); if (IS_ERR(xpage)) { + err = PTR_ERR(xpage); alloc_nid_failed(sbi, new_nid); goto in_page_out; } -- GitLab From cfee78c6767be6c2ff967a5f51db94b0f20ac8f7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 31 Dec 2017 16:26:38 -0800 Subject: [PATCH 2581/5498] f2fs: skip stop_checkpoint for user data writes We can give another chance to write user data, which can resolve generic/441. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 08829581364c..c763f55b3040 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -111,7 +111,8 @@ static void f2fs_write_end_io(struct bio *bio, int err) if (unlikely(err)) { set_bit(AS_EIO, &page->mapping->flags); - f2fs_stop_checkpoint(sbi, true); + if (type == F2FS_WB_CP_DATA) + f2fs_stop_checkpoint(sbi, true); } dec_page_count(sbi, type); clear_cold_data(page); -- GitLab From f928990854bba94ee0f26fcd33b52655eb5fc30b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 2 Jan 2018 11:03:19 -0800 Subject: [PATCH 2582/5498] f2fs: enable quota at remount from r to w We have to enable quota only when remounting from read to write. Otherwise, we'll get remount failure. (e.g., write to write case) Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 44cf807af639..b97b29bdc974 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1281,7 +1281,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) err = dquot_suspend(sb, -1); if (err < 0) goto restore_opts; - } else { + } else if (f2fs_readonly(sb) && !(*flags & MS_RDONLY)) { /* dquot_resume needs RW */ sb->s_flags &= ~MS_RDONLY; if (sb_any_quota_suspended(sb)) { -- GitLab From babfbc088938e877297841d7866f72367aadcc24 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 3 Jan 2018 17:30:19 +0800 Subject: [PATCH 2583/5498] f2fs: continue to do direct IO if we only preallocate partial blocks While doing direct IO, if we run out-of-space when we preallocate blocks, we should not return ENOSPC error directly, instead, we should continue to do following direct IO, which will keep directIO of f2fs acting like other filesystems. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c763f55b3040..3aabdb747a24 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -828,13 +828,14 @@ static inline bool __force_buffered_io(struct inode *inode, int rw) } int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, - size_t count, bool dio) + size_t count, bool direct_io) { struct f2fs_map_blocks map; + int flag; int err = 0; /* convert inline data for Direct I/O*/ - if (dio) { + if (direct_io) { err = f2fs_convert_inline_inode(inode); if (err) return err; @@ -853,26 +854,30 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_next_pgofs = NULL; map.m_seg_type = NO_CHECK_TYPE; - if (dio) { + if (direct_io) { /* map.m_seg_type = rw_hint_to_seg_type(iocb->ki_hint); */ map.m_seg_type = rw_hint_to_seg_type(WRITE_LIFE_NOT_SET); - return f2fs_map_blocks(inode, &map, 1, - __force_buffered_io(inode, WRITE) ? - F2FS_GET_BLOCK_PRE_AIO : - F2FS_GET_BLOCK_PRE_DIO); + flag = __force_buffered_io(inode, WRITE) ? + F2FS_GET_BLOCK_PRE_AIO : + F2FS_GET_BLOCK_PRE_DIO; + goto map_blocks; } if (pos + count > MAX_INLINE_DATA(inode)) { err = f2fs_convert_inline_inode(inode); if (err) return err; } - if (!f2fs_has_inline_data(inode)) { - err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); - if (map.m_len > 0 && err == -ENOSPC) { - set_inode_flag(inode, FI_NO_PREALLOC); - err = 0; - } + if (f2fs_has_inline_data(inode)) return err; + + flag = F2FS_GET_BLOCK_PRE_AIO; + +map_blocks: + err = f2fs_map_blocks(inode, &map, 1, flag); + if (map.m_len > 0 && err == -ENOSPC) { + if (!direct_io) + set_inode_flag(inode, FI_NO_PREALLOC); + err = 0; } return err; } -- GitLab From add96ed3f86b94aa53cb54eb149d310f6b23810e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 3 Jan 2018 17:32:51 +0800 Subject: [PATCH 2584/5498] f2fs: clean up unneeded declaration Commit 6afc662e68b5 ("f2fs: support flexible inline xattr size") declared f2fs_sb_has_flexible_inline_xattr in f2fs.h for latter being used in get_inline_xattr_addrs, but in latter version, related code has been changed, leave f2fs_sb_has_flexible_inline_xattr w/o any users. Let's remove it for cleanup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9aa7febacadc..65ed187aab3e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2574,7 +2574,6 @@ static inline int get_extra_isize(struct inode *inode) return F2FS_I(inode)->i_extra_isize / sizeof(__le32); } -static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb); static inline int get_inline_xattr_addrs(struct inode *inode) { return F2FS_I(inode)->i_inline_xattr_size; -- GitLab From db2e6b82f45dd54599b939916e243eca5717a29c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 3 Jan 2018 10:55:07 -0800 Subject: [PATCH 2585/5498] f2fs: show precise # of blocks that user/root can use Let's show precise # of blocks that user/root can use through bavail and bfree respectively. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b97b29bdc974..ab8f3ac704ec 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1010,20 +1010,19 @@ 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; + block_t total_count, user_block_count, start_count; u64 avail_node_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 = user_block_count - valid_user_blocks(sbi) + ovp_count; - buf->f_bavail = user_block_count - valid_user_blocks(sbi) - + buf->f_bfree = user_block_count - valid_user_blocks(sbi) - sbi->current_reserved_blocks; + buf->f_bavail = buf->f_bfree; avail_node_count = sbi->total_node_count - sbi->nquota_files - F2FS_RESERVED_NODE_NUM; -- GitLab From 2a6f5454a49c1f75eb5faf2941def947ec8c5a57 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Wed, 3 Jan 2018 18:03:04 +0800 Subject: [PATCH 2586/5498] f2fs: update inode info to inode page for new file After checkpoint, 1. creat a new file A ,(with dirty inode && dirty inode page && xattr info) 2. backgroud wb write back file A inode page (without update from inode cache) 3. fsync file A, write back inode page of file A with inode cache info 4. sudden power off before new checkpoint In this case, recovery process will try to recover a zero inode page. Inline xattr flag of file A will be miss and xattr info will be taken as blkaddr index. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index a9644bf69b8c..85486eaf5ac7 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2230,7 +2230,9 @@ void recover_inline_xattr(struct inode *inode, struct page *page) f2fs_bug_on(F2FS_I_SB(inode), IS_ERR(ipage)); ri = F2FS_INODE(page); - if (!(ri->i_inline & F2FS_INLINE_XATTR)) { + if (ri->i_inline & F2FS_INLINE_XATTR) { + set_inode_flag(inode, FI_INLINE_XATTR); + } else { clear_inode_flag(inode, FI_INLINE_XATTR); goto update_inode; } -- GitLab From cb4ea095ee56ea0ba3f5e8059630a6cee186de7d Mon Sep 17 00:00:00 2001 From: Yunlong Song Date: Thu, 4 Jan 2018 15:02:02 +0800 Subject: [PATCH 2587/5498] f2fs: check segment type in __f2fs_replace_block In some case, the node blocks has wrong blkaddr whose segment type is NODE, e.g., recover inode has missing xattr flag and the blkaddr is in the xattr range. Since fsck.f2fs does not check the recovery nodes, this will cause __f2fs_replace_block change the curseg of node and do the update_sit_entry(sbi, new_blkaddr, 1) with no next_blkoff refresh, as a result, when recovery process write checkpoint and sync nodes, the next_blkoff of curseg is used in the segment bit map, then it will cause f2fs_bug_on. So let's check segment type in __f2fs_replace_block. Signed-off-by: Yunlong Song Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index bf04b04e4ae0..3d1089851f48 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2822,6 +2822,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, } } + f2fs_bug_on(sbi, !IS_DATASEG(type)); curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); -- GitLab From 3f81bf527a5f5627ad9466bb10d85f978b33c895 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 27 Dec 2017 15:05:52 -0800 Subject: [PATCH 2588/5498] f2fs: add reserved blocks for root user This patch allows root to reserve some blocks via mount option. "-o reserve_root=N" means N x 4KB-sized blocks for root only. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 20 ++++++++++++++++---- fs/f2fs/super.c | 37 ++++++++++++++++++++++++++++++++++++- fs/f2fs/sysfs.c | 3 ++- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 65ed187aab3e..fdbcbb90e4bd 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -97,6 +97,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_PRJQUOTA 0x00200000 #define F2FS_MOUNT_QUOTA 0x00400000 #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000 +#define F2FS_MOUNT_RESERVE_ROOT 0x01000000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -1203,6 +1204,7 @@ struct f2fs_sb_info { block_t last_valid_block_count; /* for recovery */ block_t reserved_blocks; /* configurable reserved blocks */ block_t current_reserved_blocks; /* current reserved blocks */ + block_t root_reserved_blocks; /* root reserved blocks */ unsigned int nquota_files; /* # of quota sysfile */ @@ -1681,11 +1683,17 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, sbi->total_valid_block_count += (block_t)(*count); avail_user_block_count = sbi->user_block_count - sbi->current_reserved_blocks; + + if (!(test_opt(sbi, RESERVE_ROOT) && capable(CAP_SYS_RESOURCE))) + avail_user_block_count -= sbi->root_reserved_blocks; + if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { diff = sbi->total_valid_block_count - avail_user_block_count; + if (diff > *count) + diff = *count; *count -= diff; release = diff; - sbi->total_valid_block_count = avail_user_block_count; + sbi->total_valid_block_count -= diff; if (!*count) { spin_unlock(&sbi->stat_lock); percpu_counter_sub(&sbi->alloc_valid_block_count, diff); @@ -1874,9 +1882,13 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); - valid_block_count = sbi->total_valid_block_count + 1; - if (unlikely(valid_block_count + sbi->current_reserved_blocks > - sbi->user_block_count)) { + valid_block_count = sbi->total_valid_block_count + + sbi->current_reserved_blocks + 1; + + if (!(test_opt(sbi, RESERVE_ROOT) && capable(CAP_SYS_RESOURCE))) + valid_block_count += sbi->root_reserved_blocks; + + if (unlikely(valid_block_count > sbi->user_block_count)) { spin_unlock(&sbi->stat_lock); goto enospc; } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ab8f3ac704ec..be7519d9b531 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -110,6 +110,7 @@ enum { Opt_noextent_cache, Opt_noinline_data, Opt_data_flush, + Opt_reserve_root, Opt_mode, Opt_io_size_bits, Opt_fault_injection, @@ -160,6 +161,7 @@ static match_table_t f2fs_tokens = { {Opt_noextent_cache, "noextent_cache"}, {Opt_noinline_data, "noinline_data"}, {Opt_data_flush, "data_flush"}, + {Opt_reserve_root, "reserve_root=%u"}, {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, @@ -194,6 +196,19 @@ void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) va_end(args); } +static inline void limit_reserve_root(struct f2fs_sb_info *sbi) +{ + block_t limit = (sbi->user_block_count << 1) / 1000; + + /* limit is 0.2% */ + if (test_opt(sbi, RESERVE_ROOT) && sbi->root_reserved_blocks > limit) { + sbi->root_reserved_blocks = limit; + f2fs_msg(sbi->sb, KERN_INFO, + "Reduce reserved blocks for root = %u", + sbi->root_reserved_blocks); + } +} + static void init_once(void *foo) { struct f2fs_inode_info *fi = (struct f2fs_inode_info *) foo; @@ -499,6 +514,18 @@ static int parse_options(struct super_block *sb, char *options) case Opt_data_flush: set_opt(sbi, DATA_FLUSH); break; + case Opt_reserve_root: + if (args->from && match_int(args, &arg)) + return -EINVAL; + if (test_opt(sbi, RESERVE_ROOT)) { + f2fs_msg(sb, KERN_INFO, + "Preserve previous reserve_root=%u", + sbi->root_reserved_blocks); + } else { + sbi->root_reserved_blocks = arg; + set_opt(sbi, RESERVE_ROOT); + } + break; case Opt_mode: name = match_strdup(&args[0]); @@ -1022,7 +1049,10 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = total_count - start_count; buf->f_bfree = user_block_count - valid_user_blocks(sbi) - sbi->current_reserved_blocks; - buf->f_bavail = buf->f_bfree; + if (buf->f_bfree > sbi->root_reserved_blocks) + buf->f_bavail = buf->f_bfree - sbi->root_reserved_blocks; + else + buf->f_bavail = 0; avail_node_count = sbi->total_node_count - sbi->nquota_files - F2FS_RESERVED_NODE_NUM; @@ -1151,6 +1181,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) else if (test_opt(sbi, LFS)) seq_puts(seq, "lfs"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); + if (test_opt(sbi, RESERVE_ROOT)) + seq_printf(seq, ",reserve_root=%u", + sbi->root_reserved_blocks); if (F2FS_IO_SIZE_BITS(sbi)) seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi)); #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -1349,6 +1382,7 @@ skip: sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); + limit_reserve_root(sbi); return 0; restore_gc: if (need_restart_gc) { @@ -2625,6 +2659,7 @@ try_onemore: sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->reserved_blocks = 0; sbi->current_reserved_blocks = 0; + limit_reserve_root(sbi); for (i = 0; i < NR_INODE_TYPE; i++) { INIT_LIST_HEAD(&sbi->inode_list[i]); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index fe0e74a4ea2c..6a1c9be022f1 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -162,7 +162,8 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, #endif if (a->struct_type == RESERVED_BLOCKS) { spin_lock(&sbi->stat_lock); - if (t > (unsigned long)sbi->user_block_count) { + if (t > (unsigned long)(sbi->user_block_count - + sbi->root_reserved_blocks)) { spin_unlock(&sbi->stat_lock); return -EINVAL; } -- GitLab From 647763fa612bdf80b926517a4909d8afd5e84b3d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 5 Jan 2018 09:41:20 +0000 Subject: [PATCH 2589/5498] f2fs: make local functions static Fixes the following sparse warnings: fs/f2fs/segment.c:887:6: warning: symbol '__check_sit_bitmap' was not declared. Should it be static? fs/f2fs/segment.c:1327:6: warning: symbol 'f2fs_wait_discard_bio' was not declared. Should it be static? fs/f2fs/super.c:1661:5: warning: symbol 'f2fs_get_projid' was not declared. Should it be static? Signed-off-by: Wei Yongjun Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++-- fs/f2fs/super.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 3d1089851f48..c92dfa54d376 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -966,7 +966,7 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, return 0; } -void __check_sit_bitmap(struct f2fs_sb_info *sbi, +static void __check_sit_bitmap(struct f2fs_sb_info *sbi, block_t start, block_t end) { #ifdef CONFIG_F2FS_CHECK_FS @@ -1405,7 +1405,7 @@ static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, } /* This should be covered by global mutex, &sit_i->sentry_lock */ -void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) +static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_cmd *dc; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index be7519d9b531..cfe0f50b31e1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1731,11 +1731,13 @@ void f2fs_quota_off_umount(struct super_block *sb) f2fs_quota_off(sb, type); } -int f2fs_get_projid(struct inode *inode, kprojid_t *projid) +#if 0 /* not support */ +static int f2fs_get_projid(struct inode *inode, kprojid_t *projid) { *projid = F2FS_I(inode)->i_projid; return 0; } +#endif static const struct dquot_operations f2fs_quota_operations = { .get_reserved_space = f2fs_get_reserved_space, -- GitLab From e732db71ba448d482177f42b68514f4c6d6fad83 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 8 Jan 2018 18:48:33 +0800 Subject: [PATCH 2590/5498] f2fs: avoid high cpu usage in discard thread We take very long time to finish generic/476, this is because we will check consistence of all discard entries in global rb tree while traversing all different granularity pending lists, even when the list is empty, in order to avoid that unneeded overhead, we have to skip the check when coming up an empty list. generic/476 time consumption: cost Before patch & w/o consistence check 57s Before patch & w/ consistence check 1426s After patch 78s Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c92dfa54d376..50fbed044c6b 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1285,6 +1285,8 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, pend_list = &dcc->pend_list[i]; mutex_lock(&dcc->cmd_lock); + if (list_empty(pend_list)) + goto next; f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); blk_start_plug(&plug); list_for_each_entry_safe(dc, tmp, pend_list, list) { @@ -1303,6 +1305,7 @@ skip: break; } blk_finish_plug(&plug); +next: mutex_unlock(&dcc->cmd_lock); if (iter >= dpolicy->max_requests) -- GitLab From 1ee182bcc11488d7e9400d18c1eff7b2dbcd7433 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 8 Jan 2018 18:48:34 +0800 Subject: [PATCH 2591/5498] f2fs: remove unused pend_list_tag In commit 78997b569f56 ("f2fs: split discard policy"), we have get rid of using pend_list_tag field in struct discard_cmd_control, but forgot to remove it, now do it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index fdbcbb90e4bd..cd4b2626b6b3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -366,7 +366,6 @@ struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head entry_list; /* 4KB discard entry list */ struct list_head pend_list[MAX_PLIST_NUM];/* store pending entries */ - unsigned char pend_list_tag[MAX_PLIST_NUM];/* tag for pending entries */ struct list_head wait_list; /* store on-flushing entries */ struct list_head fstrim_list; /* in-flight discard from fstrim */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ -- GitLab From 6ad1915c61d489caf887147468d80258e94552c1 Mon Sep 17 00:00:00 2001 From: Yufen Yu Date: Tue, 9 Jan 2018 19:33:39 +0800 Subject: [PATCH 2592/5498] f2fs: implement cgroup writeback support Cgroup writeback requires explicit support from the filesystem. f2fs's data and node writeback IOs go through __write_data_page, which sets fio for submiting IOs. So, we add io_wbc for fio, associate bios with blkcg by invoking wbc_init_bio() and account IOs issuing by wbc_account_io(). In addtion, f2fs_fill_super() is updated to set SB_I_CGROUPWB. Meta writeback IOs is left alone by this patch and will always be attributed to the root cgroup. The results show that f2fs can throttle writeback nicely for data writing and file creating. Reviewed-by: Chao Yu Signed-off-by: Yufen Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 12 ++++++++++-- fs/f2fs/f2fs.h | 10 ++++++++++ fs/f2fs/node.c | 1 + fs/f2fs/super.c | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3aabdb747a24..25d51b6d5257 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -169,6 +169,7 @@ static bool __same_bdev(struct f2fs_sb_info *sbi, * Low-level block read/write IO operations. */ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, + struct writeback_control *wbc, int npages, bool is_read) { struct bio *bio; @@ -178,6 +179,8 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, f2fs_target_device(sbi, blk_addr, bio); bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; bio->bi_private = is_read ? NULL : sbi; + if (wbc) + wbc_init_bio(wbc, bio); return bio; } @@ -373,7 +376,8 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) f2fs_trace_ios(fio, 0); /* Allocate a new bio */ - bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->op)); + bio = __bio_alloc(fio->sbi, fio->new_blkaddr, fio->io_wbc, + 1, is_read_io(fio->op)); if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { bio_put(bio); @@ -435,7 +439,7 @@ alloc_new: dec_page_count(sbi, WB_DATA_TYPE(bio_page)); goto out_fail; } - io->bio = __bio_alloc(sbi, fio->new_blkaddr, + io->bio = __bio_alloc(sbi, fio->new_blkaddr, fio->io_wbc, BIO_MAX_PAGES, false); io->fio = *fio; } @@ -445,6 +449,9 @@ alloc_new: goto alloc_new; } + if (fio->io_wbc) + wbc_account_io(fio->io_wbc, bio_page, PAGE_SIZE); + io->last_block_in_bio = fio->new_blkaddr; f2fs_trace_ios(fio, 0); @@ -1528,6 +1535,7 @@ static int __write_data_page(struct page *page, bool *submitted, .submitted = false, .need_lock = LOCK_RETRY, .io_type = io_type, + .io_wbc = wbc, }; trace_f2fs_writepage(page, DATA); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cd4b2626b6b3..e46c41c6a368 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1055,6 +1055,7 @@ struct f2fs_io_info { int need_lock; /* indicate we need to lock cp_rwsem */ bool in_list; /* indicate fio is in io_list */ enum iostat_type io_type; /* io type */ + struct writeback_control *io_wbc; /* writeback control */ }; #define is_read_io(rw) ((rw) == READ) @@ -2556,6 +2557,15 @@ enum rw_hint { WRITE_LIFE_EXTREME = 5, /* RWH_WRITE_LIFE_EXTREME */ }; +static inline void wbc_init_bio(struct writeback_control *wbc, struct bio *bio) +{ +} + +static inline void wbc_account_io(struct writeback_control *wbc, + struct page *page, size_t bytes) +{ +} + static inline void *f2fs_kzalloc(struct f2fs_sb_info *sbi, size_t size, gfp_t flags) { diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 85486eaf5ac7..f7516c37b415 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1340,6 +1340,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, .encrypted_page = NULL, .submitted = false, .io_type = io_type, + .io_wbc = wbc, }; trace_f2fs_writepage(page, NODE); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index cfe0f50b31e1..e2b1eeb35c41 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2576,6 +2576,8 @@ try_onemore: sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); memcpy(sb->s_uuid, raw_super->uuid, sizeof(raw_super->uuid)); + /* FIXME: no cgroup support */ + /* sb->s_iflags |= SB_I_CGROUPWB; */ /* init f2fs-specific super block info */ sbi->valid_super_block = valid_super_block; -- GitLab From 177018aacbc4281b37558c7c2188b5329924a983 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 4 Jan 2018 21:36:09 -0800 Subject: [PATCH 2593/5498] f2fs: add resgid and resuid to reserve root blocks This patch adds mount options to reserve some blocks via resgid=%u,resuid=%u. It only activates with reserve_root=%u. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 27 +++++++++++++++++++++++++-- fs/f2fs/super.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e46c41c6a368..e69247a0a380 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -230,6 +231,12 @@ static inline struct timespec current_time(struct inode *inode) return timespec_trunc(now, inode->i_sb->s_time_gran); } +/* + * Default values for user and/or group using reserved blocks + */ +#define F2FS_DEF_RESUID 0 +#define F2FS_DEF_RESGID 0 + /* * For checkpoint manager */ @@ -1205,6 +1212,8 @@ struct f2fs_sb_info { block_t reserved_blocks; /* configurable reserved blocks */ block_t current_reserved_blocks; /* current reserved blocks */ block_t root_reserved_blocks; /* root reserved blocks */ + kuid_t s_resuid; /* reserved blocks for uid */ + kgid_t s_resgid; /* reserved blocks for gid */ unsigned int nquota_files; /* # of quota sysfile */ @@ -1654,6 +1663,20 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs) return ofs == XATTR_NODE_OFFSET; } +static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi) +{ + if (!test_opt(sbi, RESERVE_ROOT)) + return false; + if (capable(CAP_SYS_RESOURCE)) + return true; + if (uid_eq(sbi->s_resuid, current_fsuid())) + return true; + if (!gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) && + in_group_p(sbi->s_resgid)) + return true; + return false; +} + static inline void f2fs_i_blocks_write(struct inode *, block_t, bool, bool); static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) @@ -1684,7 +1707,7 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, avail_user_block_count = sbi->user_block_count - sbi->current_reserved_blocks; - if (!(test_opt(sbi, RESERVE_ROOT) && capable(CAP_SYS_RESOURCE))) + if (!__allow_reserved_blocks(sbi)) avail_user_block_count -= sbi->root_reserved_blocks; if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { @@ -1885,7 +1908,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, valid_block_count = sbi->total_valid_block_count + sbi->current_reserved_blocks + 1; - if (!(test_opt(sbi, RESERVE_ROOT) && capable(CAP_SYS_RESOURCE))) + if (!__allow_reserved_blocks(sbi)) valid_block_count += sbi->root_reserved_blocks; if (unlikely(valid_block_count > sbi->user_block_count)) { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index e2b1eeb35c41..01e2dd39500b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -111,6 +111,8 @@ enum { Opt_noinline_data, Opt_data_flush, Opt_reserve_root, + Opt_resgid, + Opt_resuid, Opt_mode, Opt_io_size_bits, Opt_fault_injection, @@ -162,6 +164,8 @@ static match_table_t f2fs_tokens = { {Opt_noinline_data, "noinline_data"}, {Opt_data_flush, "data_flush"}, {Opt_reserve_root, "reserve_root=%u"}, + {Opt_resgid, "resgid=%u"}, + {Opt_resuid, "resuid=%u"}, {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, @@ -207,6 +211,15 @@ static inline void limit_reserve_root(struct f2fs_sb_info *sbi) "Reduce reserved blocks for root = %u", sbi->root_reserved_blocks); } + if (!test_opt(sbi, RESERVE_ROOT) && + (!uid_eq(sbi->s_resuid, + make_kuid(&init_user_ns, F2FS_DEF_RESUID)) || + !gid_eq(sbi->s_resgid, + make_kgid(&init_user_ns, F2FS_DEF_RESGID)))) + f2fs_msg(sbi->sb, KERN_INFO, + "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root", + from_kuid_munged(&init_user_ns, sbi->s_resuid), + from_kgid_munged(&init_user_ns, sbi->s_resgid)); } static void init_once(void *foo) @@ -347,6 +360,8 @@ static int parse_options(struct super_block *sb, char *options) substring_t args[MAX_OPT_ARGS]; char *p, *name; int arg = 0; + kuid_t uid; + kgid_t gid; #ifdef CONFIG_QUOTA int ret; #endif @@ -526,6 +541,28 @@ static int parse_options(struct super_block *sb, char *options) set_opt(sbi, RESERVE_ROOT); } break; + case Opt_resuid: + if (args->from && match_int(args, &arg)) + return -EINVAL; + uid = make_kuid(current_user_ns(), arg); + if (!uid_valid(uid)) { + f2fs_msg(sb, KERN_ERR, + "Invalid uid value %d", arg); + return -EINVAL; + } + sbi->s_resuid = uid; + break; + case Opt_resgid: + if (args->from && match_int(args, &arg)) + return -EINVAL; + gid = make_kgid(current_user_ns(), arg); + if (!gid_valid(gid)) { + f2fs_msg(sb, KERN_ERR, + "Invalid gid value %d", arg); + return -EINVAL; + } + sbi->s_resgid = gid; + break; case Opt_mode: name = match_strdup(&args[0]); @@ -1182,8 +1219,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, "lfs"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); if (test_opt(sbi, RESERVE_ROOT)) - seq_printf(seq, ",reserve_root=%u", - sbi->root_reserved_blocks); + seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u", + sbi->root_reserved_blocks, + from_kuid_munged(&init_user_ns, sbi->s_resuid), + from_kgid_munged(&init_user_ns, sbi->s_resgid)); if (F2FS_IO_SIZE_BITS(sbi)) seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi)); #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -2511,6 +2550,9 @@ try_onemore: sb->s_fs_info = sbi; sbi->raw_super = raw_super; + sbi->s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID); + sbi->s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID); + /* precompute checksum seed for metadata */ if (f2fs_sb_has_inode_chksum(sb)) sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid, -- GitLab From e72c42370ccf1ccb2d80b60572fda66fcb189dd8 Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Wed, 10 Jan 2018 16:49:10 +0900 Subject: [PATCH 2594/5498] f2fs: handle newly created page when revoking inmem pages When committing inmem pages is successful, we revoke already committed blocks in __revoke_inmem_pages() and finally replace the committed ones with the old blocks using f2fs_replace_block(). However, if the committed block was newly created one, the address of the old block is NEW_ADDR and __f2fs_replace_block() cannot handle NEW_ADDR as new_blkaddr properly and a kernel panic occurrs. Signed-off-by: Daeho Jeong Tested-by: Shu Tan Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 50fbed044c6b..d9b51daa724c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -248,7 +248,11 @@ retry: goto next; } get_node_info(sbi, dn.nid, &ni); - f2fs_replace_block(sbi, &dn, dn.data_blkaddr, + if (cur->old_addr == NEW_ADDR) { + invalidate_blocks(sbi, dn.data_blkaddr); + f2fs_update_data_blkaddr(&dn, NEW_ADDR); + } else + f2fs_replace_block(sbi, &dn, dn.data_blkaddr, cur->old_addr, ni.version, true, true); f2fs_put_dnode(&dn); } -- GitLab From d503f1e0c9fa12000e3322fe186790f848c4b3c1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 10 Jan 2018 18:18:51 +0800 Subject: [PATCH 2595/5498] f2fs: fix to caclulate required free section correctly When calculating required free section during file defragmenting, we should skip holes in file, otherwise we will probably fail to defrag sparse file with large size. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2d08717976ee..885b650e008f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2110,10 +2110,12 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, continue; } - if (blk_end && blk_end != map.m_pblk) { + if (blk_end && blk_end != map.m_pblk) fragmented = true; - break; - } + + /* record total count of block that we're going to move */ + total += map.m_len; + blk_end = map.m_pblk + map.m_len; map.m_lblk += map.m_len; @@ -2122,10 +2124,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, if (!fragmented) goto out; - map.m_lblk = pg_start; - map.m_len = pg_end - pg_start; - - sec_num = (map.m_len + BLKS_PER_SEC(sbi) - 1) / BLKS_PER_SEC(sbi); + sec_num = (total + BLKS_PER_SEC(sbi) - 1) / BLKS_PER_SEC(sbi); /* * make sure there are enough free section for LFS allocation, this can @@ -2137,6 +2136,10 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, goto out; } + map.m_lblk = pg_start; + map.m_len = pg_end - pg_start; + total = 0; + while (map.m_lblk < pg_end) { pgoff_t idx; int cnt = 0; -- GitLab From 0ea602b1ab4940c1e3b4092a63ee7dabc969a8c3 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 11 Jan 2018 14:19:32 +0800 Subject: [PATCH 2596/5498] f2fs: check node page again in write end io Check node page again in write end io in case of data corruption during inflght IO. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 25d51b6d5257..1d806bceb3bd 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -114,6 +114,10 @@ static void f2fs_write_end_io(struct bio *bio, int err) if (type == F2FS_WB_CP_DATA) f2fs_stop_checkpoint(sbi, true); } + + f2fs_bug_on(sbi, page->mapping == NODE_MAPPING(sbi) && + page->index != nid_of_node(page)); + dec_page_count(sbi, type); clear_cold_data(page); end_page_writeback(page); -- GitLab From 2075b0e8a63e051c90baf0c87f5279f50e39c4c4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 11 Jan 2018 14:37:35 +0800 Subject: [PATCH 2597/5498] f2fs: fix to cover f2fs_inline_data_fiemap with inode_lock This patch fix to cover f2fs_inline_data_fiemap with inode_lock in order to make that interface avoiding race with mapping change. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1d806bceb3bd..43294d02d446 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1154,14 +1154,14 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, if (ret) return ret; + inode_lock(inode); + if (f2fs_has_inline_data(inode)) { ret = f2fs_inline_data_fiemap(inode, fieinfo, start, len); if (ret != -EAGAIN) - return ret; + goto out; } - inode_lock(inode); - if (logical_to_blk(inode, len) == 0) len = blk_to_logical(inode, 1); -- GitLab From 623e284104f8cb405a3f8b02bff91b69a894dcc0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 11 Jan 2018 14:39:57 +0800 Subject: [PATCH 2598/5498] f2fs: support FIEMAP_FLAG_XATTR This patch enables ->fiemap to handle FIEMAP_FLAG_XATTR flag for xattr mapping info lookup purpose. It makes f2fs passing generic/425 test in fstest. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 43294d02d446..d8f731e5f6a8 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1140,6 +1140,68 @@ static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) return (blk << inode->i_blkbits); } +static int f2fs_xattr_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct page *page; + struct node_info ni; + __u64 phys = 0, len; + __u32 flags; + nid_t xnid = F2FS_I(inode)->i_xattr_nid; + int err = 0; + + if (f2fs_has_inline_xattr(inode)) { + int offset; + + page = f2fs_grab_cache_page(NODE_MAPPING(sbi), + inode->i_ino, false); + if (!page) + return -ENOMEM; + + get_node_info(sbi, inode->i_ino, &ni); + + phys = (__u64)blk_to_logical(inode, ni.blk_addr); + offset = offsetof(struct f2fs_inode, i_addr) + + sizeof(__le32) * (DEF_ADDRS_PER_INODE - + F2FS_INLINE_XATTR_ADDRS(inode)); + + phys += offset; + len = inline_xattr_size(inode); + + f2fs_put_page(page, 1); + + flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED; + + if (!xnid) + flags |= FIEMAP_EXTENT_LAST; + + err = fiemap_fill_next_extent(fieinfo, 0, phys, len, flags); + if (err || err == 1) + return err; + } + + if (xnid) { + page = f2fs_grab_cache_page(NODE_MAPPING(sbi), xnid, false); + if (!page) + return -ENOMEM; + + get_node_info(sbi, xnid, &ni); + + phys = (__u64)blk_to_logical(inode, ni.blk_addr); + len = inode->i_sb->s_blocksize; + + f2fs_put_page(page, 1); + + flags = FIEMAP_EXTENT_LAST; + } + + if (phys) + err = fiemap_fill_next_extent(fieinfo, 0, phys, len, flags); + + return (err < 0 ? err : 0); +} + int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { @@ -1150,12 +1212,17 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u32 flags = 0; int ret = 0; - ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); + ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR); if (ret) return ret; inode_lock(inode); + if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) { + ret = f2fs_xattr_fiemap(inode, fieinfo); + goto out; + } + if (f2fs_has_inline_data(inode)) { ret = f2fs_inline_data_fiemap(inode, fieinfo, start, len); if (ret != -EAGAIN) -- GitLab From 11228b15cfa4f063a242870785ae5aa421e0164f Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Thu, 11 Jan 2018 11:26:19 +0900 Subject: [PATCH 2599/5498] f2fs: prevent newly created inode from being dirtied incorrectly Now, we invoke f2fs_mark_inode_dirty_sync() to make an inode dirty in advance of creating a new node page for the inode. By this, some inodes whose node page is not created yet can be linked into the global dirty list. If the checkpoint is executed at this moment, the inode will be written back by writeback_single_inode() and finally update_inode_page() will fail to detach the inode from the global dirty list because the inode doesn't have a node page. The problem is that the inode's state in VFS layer will become clean after execution of writeback_single_inode() and it's still linked in the global dirty list of f2fs and this will cause a kernel panic. So, we will prevent the newly created inode from being dirtied during the FI_NEW_INODE flag of the inode is set. We will make it dirty right after the flag is cleared. Signed-off-by: Daeho Jeong Signed-off-by: Youngjin Gil Tested-by: Hobin Woo Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/inode.c | 3 +++ fs/f2fs/namei.c | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e69247a0a380..ddbb93f564a7 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2248,6 +2248,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, case FI_INLINE_XATTR: case FI_INLINE_DATA: case FI_INLINE_DENTRY: + case FI_NEW_INODE: if (set) return; case FI_DATA_EXIST: diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index aff0f1993efe..3256b9f63a44 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -22,6 +22,9 @@ void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync) { + if (is_inode_flag_set(inode, FI_NEW_INODE)) + return; + if (f2fs_inode_dirtied(inode, sync)) return; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 470bfe8c8eba..5f40330d8f20 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -72,12 +72,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (err) goto fail_drop; + set_inode_flag(inode, FI_NEW_INODE); + /* If the directory encrypted, then we should encrypt the inode. */ if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) f2fs_set_encrypted_inode(inode); - set_inode_flag(inode, FI_NEW_INODE); - if (f2fs_sb_has_extra_attr(sbi->sb)) { set_inode_flag(inode, FI_EXTRA_ATTR); F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; -- GitLab From f3d5ace5b5279a4acbc31a11b17325392757ed94 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 7 Dec 2017 16:25:39 -0800 Subject: [PATCH 2600/5498] f2fs: add an ioctl to disable GC for specific file This patch gives a flag to disable GC on given file, which would be useful, when user wants to keep its block map. It also conducts in-place-update for dontmove file. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 + fs/f2fs/f2fs.h | 29 +++++++++++++- fs/f2fs/file.c | 83 +++++++++++++++++++++++++++++++++++++++++ fs/f2fs/gc.c | 11 ++++++ fs/f2fs/gc.h | 2 + fs/f2fs/sysfs.c | 2 + include/linux/f2fs_fs.h | 9 ++++- 7 files changed, 136 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index d8f731e5f6a8..5f3ff870849b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1478,6 +1478,8 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio) { struct inode *inode = fio->page->mapping->host; + if (f2fs_is_pinned_file(inode)) + return true; if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) return false; if (is_cold_data(fio->page)) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index ddbb93f564a7..841111322d31 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -455,6 +455,8 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_GARBAGE_COLLECT_RANGE _IOW(F2FS_IOCTL_MAGIC, 11, \ struct f2fs_gc_range) #define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, __u32) +#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32) +#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32) #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY @@ -689,7 +691,10 @@ struct f2fs_inode_info { unsigned long i_flags; /* keep an inode flags for ioctl */ unsigned char i_advise; /* use to give file attribute hints */ unsigned char i_dir_level; /* use for dentry level for large dir */ - unsigned int i_current_depth; /* use only in directory structure */ + union { + unsigned int i_current_depth; /* only for directory depth */ + unsigned short i_gc_failures; /* only for regular file */ + }; unsigned int i_pino; /* parent inode number */ umode_t i_acl_mode; /* keep file acl mode temporarily */ @@ -1240,6 +1245,9 @@ struct f2fs_sb_info { /* threshold for converting bg victims for fg */ u64 fggc_threshold; + /* threshold for gc trials on pinned files */ + u64 gc_pin_file_threshold; + /* maximum # of trials to find a victim segment for SSR and GC */ unsigned int max_victim_search; @@ -2239,6 +2247,7 @@ enum { FI_HOT_DATA, /* indicate file is hot */ FI_EXTRA_ATTR, /* indicate file has extra attribute */ FI_PROJ_INHERIT, /* indicate file inherits projectid */ + FI_PIN_FILE, /* indicate file should not be gced */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, @@ -2253,6 +2262,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, return; case FI_DATA_EXIST: case FI_INLINE_DOTS: + case FI_PIN_FILE: f2fs_mark_inode_dirty_sync(inode, true); } } @@ -2333,6 +2343,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth) f2fs_mark_inode_dirty_sync(inode, true); } +static inline void f2fs_i_gc_failures_write(struct inode *inode, + unsigned int count) +{ + F2FS_I(inode)->i_gc_failures = count; + f2fs_mark_inode_dirty_sync(inode, true); +} + static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid) { F2FS_I(inode)->i_xattr_nid = xnid; @@ -2361,6 +2378,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) set_bit(FI_INLINE_DOTS, &fi->flags); if (ri->i_inline & F2FS_EXTRA_ATTR) set_bit(FI_EXTRA_ATTR, &fi->flags); + if (ri->i_inline & F2FS_PIN_FILE) + set_bit(FI_PIN_FILE, &fi->flags); } static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) @@ -2379,6 +2398,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) ri->i_inline |= F2FS_INLINE_DOTS; if (is_inode_flag_set(inode, FI_EXTRA_ATTR)) ri->i_inline |= F2FS_EXTRA_ATTR; + if (is_inode_flag_set(inode, FI_PIN_FILE)) + ri->i_inline |= F2FS_PIN_FILE; } static inline int f2fs_has_extra_attr(struct inode *inode) @@ -2424,6 +2445,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode) return is_inode_flag_set(inode, FI_INLINE_DOTS); } +static inline bool f2fs_is_pinned_file(struct inode *inode) +{ + return is_inode_flag_set(inode, FI_PIN_FILE); +} + static inline bool f2fs_is_atomic_file(struct inode *inode) { return is_inode_flag_set(inode, FI_ATOMIC_FILE); @@ -2677,6 +2703,7 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end); void truncate_data_blocks_range(struct dnode_of_data *dn, int count); long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +int f2fs_pin_file_control(struct inode *inode, bool inc); /* * inode.c diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 885b650e008f..f7f392f8bde4 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2463,6 +2463,83 @@ static int f2fs_ioc_get_features(struct file *filp, unsigned long arg) return put_user(sb_feature, (u32 __user *)arg); } +int f2fs_pin_file_control(struct inode *inode, bool inc) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + /* Use i_gc_failures for normal file as a risk signal. */ + if (inc) + f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1); + + if (fi->i_gc_failures > sbi->gc_pin_file_threshold) { + f2fs_msg(sbi->sb, KERN_WARNING, + "%s: Enable GC = ino %lx after %x GC trials\n", + __func__, inode->i_ino, fi->i_gc_failures); + clear_inode_flag(inode, FI_PIN_FILE); + return -EAGAIN; + } + return 0; +} + +static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + __u32 pin; + int ret = 0; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + if (get_user(pin, (__u32 __user *)arg)) + return -EFAULT; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + if (f2fs_readonly(F2FS_I_SB(inode)->sb)) + return -EROFS; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + inode_lock(inode); + + if (!pin) { + clear_inode_flag(inode, FI_PIN_FILE); + F2FS_I(inode)->i_gc_failures = 1; + goto done; + } + + if (f2fs_pin_file_control(inode, false)) { + ret = -EAGAIN; + goto out; + } + ret = f2fs_convert_inline_inode(inode); + if (ret) + goto out; + + set_inode_flag(inode, FI_PIN_FILE); + ret = F2FS_I(inode)->i_gc_failures; +done: + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); +out: + inode_unlock(inode); + mnt_drop_write_file(filp); + return ret; +} + +static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + __u32 pin = 0; + + if (is_inode_flag_set(inode, FI_PIN_FILE)) + pin = F2FS_I(inode)->i_gc_failures; + return put_user(pin, (u32 __user *)arg); +} + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp))))) @@ -2509,6 +2586,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_flush_device(filp, arg); case F2FS_IOC_GET_FEATURES: return f2fs_ioc_get_features(filp, arg); + case F2FS_IOC_GET_PIN_FILE: + return f2fs_ioc_get_pin_file(filp, arg); + case F2FS_IOC_SET_PIN_FILE: + return f2fs_ioc_set_pin_file(filp, arg); default: return -ENOTTY; } @@ -2590,6 +2671,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_MOVE_RANGE: case F2FS_IOC_FLUSH_DEVICE: case F2FS_IOC_GET_FEATURES: + case F2FS_IOC_GET_PIN_FILE: + case F2FS_IOC_SET_PIN_FILE: break; default: return -ENOIOCTLCMD; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 5d5bba462f26..9bffef153a12 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx, if (f2fs_is_atomic_file(inode)) goto out; + if (f2fs_is_pinned_file(inode)) { + f2fs_pin_file_control(inode, true); + goto out; + } + set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE); if (err) @@ -720,6 +725,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, if (f2fs_is_atomic_file(inode)) goto out; + if (f2fs_is_pinned_file(inode)) { + if (gc_type == FG_GC) + f2fs_pin_file_control(inode, true); + goto out; + } if (gc_type == BG_GC) { if (PageWriteback(page)) @@ -1091,6 +1101,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi) sbi->fggc_threshold = div64_u64((main_count - ovp_count) * BLKS_PER_SEC(sbi), (main_count - resv_count)); + sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES; /* give warm/cold data area from slower device */ if (sbi->s_ndevs && sbi->segs_per_sec == 1) diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 9325191fab2d..b0045d4c8d1e 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -20,6 +20,8 @@ #define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */ #define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */ +#define DEF_GC_FAILED_PINNED_FILES 2048 + /* Search max. number of dirty segments to select a victim segment */ #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */ diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 6a1c9be022f1..60ccd16c4086 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -301,6 +301,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold); #ifdef CONFIG_F2FS_FAULT_INJECTION F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); @@ -349,6 +350,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(idle_interval), ATTR_LIST(iostat_enable), ATTR_LIST(readdir_ra), + ATTR_LIST(gc_pin_file_thresh), #ifdef CONFIG_F2FS_FAULT_INJECTION ATTR_LIST(inject_rate), ATTR_LIST(inject_type), diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 30dc9d69d60e..5bdcfa186e69 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -212,6 +212,7 @@ struct f2fs_extent { #define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ #define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ #define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */ +#define F2FS_PIN_FILE 0x40 /* file should not be gced */ struct f2fs_inode { __le16 i_mode; /* file mode */ @@ -229,7 +230,13 @@ struct f2fs_inode { __le32 i_ctime_nsec; /* change time in nano scale */ __le32 i_mtime_nsec; /* modification time in nano scale */ __le32 i_generation; /* file version (for NFS) */ - __le32 i_current_depth; /* only for directory depth */ + union { + __le32 i_current_depth; /* only for directory depth */ + __le16 i_gc_failures; /* + * # of gc failures on pinned file. + * only for regular files. + */ + }; __le32 i_xattr_nid; /* nid to save xattr */ __le32 i_flags; /* file attributes */ __le32 i_pino; /* parent inode number */ -- GitLab From 06e30f6f9cec86349508cecb929e639ba85bdaa1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 11 Jan 2018 14:42:30 +0800 Subject: [PATCH 2601/5498] f2fs: support F2FS_IOC_PRECACHE_EXTENTS This patch introduces a new ioctl F2FS_IOC_PRECACHE_EXTENTS to precache extent info like ext4, in order to gain better performance during triggering AIO by eliminating synchronous waiting of mapping info. Referred commit: 7869a4a6c5ca ("ext4: add support for extent pre-caching") In addition, with newly added extent precache abilitiy, this patch add to support FIEMAP_FLAG_CACHE in ->fiemap. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 39 +++++++++++++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 4 ++++ fs/f2fs/file.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5f3ff870849b..484c5f87bb4c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -863,6 +863,7 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_len = 0; map.m_next_pgofs = NULL; + map.m_next_extent = NULL; map.m_seg_type = NO_CHECK_TYPE; if (direct_io) { @@ -930,6 +931,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, blkcnt_t prealloc; struct extent_info ei = {0,0,0}; block_t blkaddr; + unsigned int start_pgofs; if (!maxblocks) return 0; @@ -945,6 +947,8 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, map->m_pblk = ei.blk + pgofs - ei.fofs; map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs); map->m_flags = F2FS_MAP_MAPPED; + if (map->m_next_extent) + *map->m_next_extent = pgofs + map->m_len; goto out; } @@ -963,10 +967,14 @@ next_dnode: if (map->m_next_pgofs) *map->m_next_pgofs = get_next_page_offset(&dn, pgofs); + if (map->m_next_extent) + *map->m_next_extent = + get_next_page_offset(&dn, pgofs); } goto unlock_out; } + start_pgofs = pgofs; prealloc = 0; last_ofs_in_node = ofs_in_node = dn.ofs_in_node; end_offset = ADDRS_PER_PAGE(dn.node_page, inode); @@ -1000,6 +1008,8 @@ next_block: map->m_pblk = 0; goto sync_out; } + if (flag == F2FS_GET_BLOCK_PRECACHE) + goto sync_out; if (flag == F2FS_GET_BLOCK_FIEMAP && blkaddr == NULL_ADDR) { if (map->m_next_pgofs) @@ -1058,6 +1068,16 @@ skip: else if (dn.ofs_in_node < end_offset) goto next_block; + if (flag == F2FS_GET_BLOCK_PRECACHE) { + if (map->m_flags & F2FS_MAP_MAPPED) { + unsigned int ofs = start_pgofs - map->m_lblk; + + f2fs_update_extent_cache_range(&dn, + start_pgofs, map->m_pblk + ofs, + map->m_len - ofs); + } + } + f2fs_put_dnode(&dn); if (create) { @@ -1067,6 +1087,17 @@ skip: goto next_dnode; sync_out: + if (flag == F2FS_GET_BLOCK_PRECACHE) { + if (map->m_flags & F2FS_MAP_MAPPED) { + unsigned int ofs = start_pgofs - map->m_lblk; + + f2fs_update_extent_cache_range(&dn, + start_pgofs, map->m_pblk + ofs, + map->m_len - ofs); + } + if (map->m_next_extent) + *map->m_next_extent = pgofs + 1; + } f2fs_put_dnode(&dn); unlock_out: if (create) { @@ -1088,6 +1119,7 @@ static int __get_data_block(struct inode *inode, sector_t iblock, map.m_lblk = iblock; map.m_len = bh->b_size >> inode->i_blkbits; map.m_next_pgofs = next_pgofs; + map.m_next_extent = NULL; map.m_seg_type = seg_type; err = f2fs_map_blocks(inode, &map, create, flag); @@ -1212,6 +1244,12 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u32 flags = 0; int ret = 0; + if (fieinfo->fi_flags & FIEMAP_FLAG_CACHE) { + ret = f2fs_precache_extents(inode); + if (ret) + return ret; + } + ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR); if (ret) return ret; @@ -1313,6 +1351,7 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_len = 0; map.m_flags = 0; map.m_next_pgofs = NULL; + map.m_next_extent = NULL; map.m_seg_type = NO_CHECK_TYPE; for (; nr_pages; nr_pages--) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 841111322d31..b99d10801b3b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -457,6 +457,7 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, __u32) #define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32) #define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32) +#define F2FS_IOC_PRECACHE_EXTENTS _IO(F2FS_IOCTL_MAGIC, 15) #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY @@ -649,6 +650,7 @@ struct f2fs_map_blocks { unsigned int m_len; unsigned int m_flags; pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */ + pgoff_t *m_next_extent; /* point to next possible extent */ int m_seg_type; }; @@ -659,6 +661,7 @@ enum { F2FS_GET_BLOCK_BMAP, F2FS_GET_BLOCK_PRE_DIO, F2FS_GET_BLOCK_PRE_AIO, + F2FS_GET_BLOCK_PRECACHE, }; /* @@ -2701,6 +2704,7 @@ int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry, int f2fs_setattr(struct dentry *dentry, struct iattr *attr); int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end); void truncate_data_blocks_range(struct dnode_of_data *dn, int count); +int f2fs_precache_extents(struct inode *inode); long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int f2fs_pin_file_control(struct inode *inode, bool inc); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index f7f392f8bde4..8a8ede64310c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1406,7 +1406,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset, { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_map_blocks map = { .m_next_pgofs = NULL, - .m_seg_type = NO_CHECK_TYPE }; + .m_next_extent = NULL, .m_seg_type = NO_CHECK_TYPE }; pgoff_t pg_end; loff_t new_size = i_size_read(inode); loff_t off_end; @@ -2057,7 +2057,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, { struct inode *inode = file_inode(filp); struct f2fs_map_blocks map = { .m_next_pgofs = NULL, - .m_seg_type = NO_CHECK_TYPE }; + .m_next_extent = NULL, .m_seg_type = NO_CHECK_TYPE }; struct extent_info ei = {0,0,0}; pgoff_t pg_start, pg_end; unsigned int blk_per_seg = sbi->blocks_per_seg; @@ -2540,6 +2540,43 @@ static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg) return put_user(pin, (u32 __user *)arg); } +int f2fs_precache_extents(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_map_blocks map; + pgoff_t m_next_extent; + loff_t end; + int err; + + if (is_inode_flag_set(inode, FI_NO_EXTENT)) + return -EOPNOTSUPP; + + map.m_lblk = 0; + map.m_next_pgofs = NULL; + map.m_next_extent = &m_next_extent; + map.m_seg_type = NO_CHECK_TYPE; + end = F2FS_I_SB(inode)->max_file_blocks; + + while (map.m_lblk < end) { + map.m_len = end - map.m_lblk; + + down_write(&fi->dio_rwsem[WRITE]); + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_PRECACHE); + up_write(&fi->dio_rwsem[WRITE]); + if (err) + return err; + + map.m_lblk = m_next_extent; + } + + return err; +} + +static int f2fs_ioc_precache_extents(struct file *filp, unsigned long arg) +{ + return f2fs_precache_extents(file_inode(filp)); +} + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp))))) @@ -2590,6 +2627,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_get_pin_file(filp, arg); case F2FS_IOC_SET_PIN_FILE: return f2fs_ioc_set_pin_file(filp, arg); + case F2FS_IOC_PRECACHE_EXTENTS: + return f2fs_ioc_precache_extents(filp, arg); default: return -ENOTTY; } @@ -2673,6 +2712,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_GET_FEATURES: case F2FS_IOC_GET_PIN_FILE: case F2FS_IOC_SET_PIN_FILE: + case F2FS_IOC_PRECACHE_EXTENTS: break; default: return -ENOIOCTLCMD; -- GitLab From 8975d2b938bf2e27b2f43b080f873bddbe66875f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 10 Jan 2018 18:18:52 +0800 Subject: [PATCH 2602/5498] f2fs: speed up defragment on sparse file We have supported to get next page offset with valid mapping crossing hole in f2fs_map_blocks, utilizing it to speed up defragment on sparse file. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 +++++- fs/f2fs/file.c | 11 ++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 484c5f87bb4c..fc255123e064 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1016,8 +1016,12 @@ next_block: *map->m_next_pgofs = pgofs + 1; goto sync_out; } - if (flag != F2FS_GET_BLOCK_FIEMAP) + if (flag != F2FS_GET_BLOCK_FIEMAP) { + /* for defragment case */ + if (map->m_next_pgofs) + *map->m_next_pgofs = pgofs + 1; goto sync_out; + } } } diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 8a8ede64310c..0f04cd3c234e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2056,10 +2056,10 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, struct f2fs_defragment *range) { struct inode *inode = file_inode(filp); - struct f2fs_map_blocks map = { .m_next_pgofs = NULL, - .m_next_extent = NULL, .m_seg_type = NO_CHECK_TYPE }; + struct f2fs_map_blocks map = { .m_next_extent = NULL, + .m_seg_type = NO_CHECK_TYPE }; struct extent_info ei = {0,0,0}; - pgoff_t pg_start, pg_end; + pgoff_t pg_start, pg_end, next_pgofs; unsigned int blk_per_seg = sbi->blocks_per_seg; unsigned int total = 0, sec_num; block_t blk_end = 0; @@ -2093,6 +2093,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, } map.m_lblk = pg_start; + map.m_next_pgofs = &next_pgofs; /* * lookup mapping info in dnode page cache, skip defragmenting if all @@ -2106,7 +2107,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, goto out; if (!(map.m_flags & F2FS_MAP_FLAGS)) { - map.m_lblk++; + map.m_lblk = next_pgofs; continue; } @@ -2151,7 +2152,7 @@ do_map: goto clear_out; if (!(map.m_flags & F2FS_MAP_FLAGS)) { - map.m_lblk++; + map.m_lblk = next_pgofs; continue; } -- GitLab From 859cc83798e07eca05a56d34e7f79bdaff579b0e Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 15 Jan 2018 17:16:46 +0800 Subject: [PATCH 2603/5498] f2fs: fix to drop all inmem pages correctly In commit 57864ae5ce3a ("f2fs: limit # of inmemory pages"), we have limited memory footprint of all inmem pages with 20% of total memory, otherwise, if we exceed the threshold, we will try to drop all inmem pages to avoid excessive memory pressure resulting in performance regression. But in some unrelated error paths, we will also drop all inmem pages, which should be wrong, fix it in this patch. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index fc255123e064..738b283c2de7 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2082,7 +2082,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *page = NULL; pgoff_t index = ((unsigned long long) pos) >> PAGE_SHIFT; - bool need_balance = false; + bool need_balance = false, drop_atomic = false; block_t blkaddr = NULL_ADDR; int err = 0; @@ -2091,6 +2091,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, if (f2fs_is_atomic_file(inode) && !available_free_memory(sbi, INMEM_PAGES)) { err = -ENOMEM; + drop_atomic = true; goto fail; } @@ -2171,7 +2172,7 @@ repeat: fail: f2fs_put_page(page, 1); f2fs_write_failed(mapping, pos + len); - if (f2fs_is_atomic_file(inode)) + if (drop_atomic) drop_inmem_pages_all(sbi); return err; } -- GitLab From a14cced5bf6abba2f86b9e00457b3dc9bdf8a81d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 5 Jan 2018 16:02:36 -0800 Subject: [PATCH 2604/5498] f2fs: allow quota to use reserved blocks This patch allows quota to use reserved blocks all the time. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b99d10801b3b..280d645051d2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1674,10 +1674,15 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs) return ofs == XATTR_NODE_OFFSET; } -static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi) +static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi, + struct inode *inode) { + if (!inode) + return true; if (!test_opt(sbi, RESERVE_ROOT)) return false; + if (IS_NOQUOTA(inode)) + return true; if (capable(CAP_SYS_RESOURCE)) return true; if (uid_eq(sbi->s_resuid, current_fsuid())) @@ -1718,7 +1723,7 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, avail_user_block_count = sbi->user_block_count - sbi->current_reserved_blocks; - if (!__allow_reserved_blocks(sbi)) + if (!__allow_reserved_blocks(sbi, inode)) avail_user_block_count -= sbi->root_reserved_blocks; if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { @@ -1919,7 +1924,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, valid_block_count = sbi->total_valid_block_count + sbi->current_reserved_blocks + 1; - if (!__allow_reserved_blocks(sbi)) + if (!__allow_reserved_blocks(sbi, inode)) valid_block_count += sbi->root_reserved_blocks; if (unlikely(valid_block_count > sbi->user_block_count)) { -- GitLab From 2e5c39e539963cc661aac6c49c4ced7c2950789b Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 17 Jan 2018 12:11:31 +0800 Subject: [PATCH 2605/5498] f2fs: avoid hungtask when GC encrypted block if io_bits is set When io_bits is set, GCing encrypted block may hit the following hungtask. Since io_bits requires aligned block address, f2fs_submit_page_write may return -EAGAIN if new_blkaddr does not satisify io_bits alignment. As a result, the encrypted page will never be writtenback. This patch makes move_data_block aware the EAGAIN error and cancel the writeback. [ 246.751371] INFO: task kworker/u4:4:797 blocked for more than 90 seconds. [ 246.752423] Not tainted 4.15.0-rc4+ #11 [ 246.754176] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 246.755336] kworker/u4:4 D25448 797 2 0x80000000 [ 246.755597] Workqueue: writeback wb_workfn (flush-7:0) [ 246.755616] Call Trace: [ 246.755695] ? __schedule+0x322/0xa90 [ 246.755761] ? blk_init_request_from_bio+0x120/0x120 [ 246.755773] ? pci_mmcfg_check_reserved+0xb0/0xb0 [ 246.755801] ? __radix_tree_create+0x19e/0x200 [ 246.755813] ? delete_node+0x136/0x370 [ 246.755838] schedule+0x43/0xc0 [ 246.755904] io_schedule+0x17/0x40 [ 246.755939] wait_on_page_bit_common+0x17b/0x240 [ 246.755950] ? wake_page_function+0xa0/0xa0 [ 246.755961] ? add_to_page_cache_lru+0x160/0x160 [ 246.755972] ? page_cache_tree_insert+0x170/0x170 [ 246.755983] ? __lru_cache_add+0x96/0xb0 [ 246.756086] __filemap_fdatawait_range+0x14f/0x1c0 [ 246.756097] ? wait_on_page_bit_common+0x240/0x240 [ 246.756120] ? __wake_up_locked_key_bookmark+0x20/0x20 [ 246.756167] ? wait_on_all_pages_writeback+0xc9/0x100 [ 246.756179] ? __remove_ino_entry+0x120/0x120 [ 246.756192] ? wait_woken+0x100/0x100 [ 246.756204] filemap_fdatawait_range+0x9/0x20 [ 246.756216] write_checkpoint+0x18a1/0x1f00 [ 246.756254] ? blk_get_request+0x10/0x10 [ 246.756265] ? cpumask_next_and+0x43/0x60 [ 246.756279] ? f2fs_sync_inode_meta+0x160/0x160 [ 246.756289] ? remove_element.isra.4+0xa0/0xa0 [ 246.756300] ? __put_compound_page+0x40/0x40 [ 246.756310] ? f2fs_sync_fs+0xec/0x1c0 [ 246.756320] ? f2fs_sync_fs+0x120/0x1c0 [ 246.756329] f2fs_sync_fs+0x120/0x1c0 [ 246.756357] ? trace_event_raw_event_f2fs__page+0x260/0x260 [ 246.756393] ? ata_build_rw_tf+0x173/0x410 [ 246.756397] f2fs_balance_fs_bg+0x198/0x390 [ 246.756405] ? drop_inmem_page+0x230/0x230 [ 246.756415] ? ahci_qc_prep+0x1bb/0x2e0 [ 246.756418] ? ahci_qc_issue+0x1df/0x290 [ 246.756422] ? __accumulate_pelt_segments+0x42/0xd0 [ 246.756426] ? f2fs_write_node_pages+0xd1/0x380 [ 246.756429] f2fs_write_node_pages+0xd1/0x380 [ 246.756437] ? sync_node_pages+0x8f0/0x8f0 [ 246.756440] ? update_curr+0x53/0x220 [ 246.756444] ? __accumulate_pelt_segments+0xa2/0xd0 [ 246.756448] ? __update_load_avg_se.isra.39+0x349/0x360 [ 246.756452] ? do_writepages+0x2a/0xa0 [ 246.756456] do_writepages+0x2a/0xa0 [ 246.756460] __writeback_single_inode+0x70/0x490 [ 246.756463] ? check_preempt_wakeup+0x199/0x310 [ 246.756467] writeback_sb_inodes+0x2a2/0x660 [ 246.756471] ? is_empty_dir_inode+0x40/0x40 [ 246.756474] ? __writeback_single_inode+0x490/0x490 [ 246.756477] ? string+0xbf/0xf0 [ 246.756480] ? down_read_trylock+0x35/0x60 [ 246.756484] __writeback_inodes_wb+0x9f/0xf0 [ 246.756488] wb_writeback+0x41d/0x4b0 [ 246.756492] ? writeback_inodes_wb.constprop.55+0x150/0x150 [ 246.756498] ? set_worker_desc+0xf7/0x130 [ 246.756502] ? current_is_workqueue_rescuer+0x60/0x60 [ 246.756511] ? _find_next_bit+0x2c/0xa0 [ 246.756514] ? wb_workfn+0x400/0x5d0 [ 246.756518] wb_workfn+0x400/0x5d0 [ 246.756521] ? finish_task_switch+0xdf/0x2a0 [ 246.756525] ? inode_wait_for_writeback+0x30/0x30 [ 246.756529] process_one_work+0x3a7/0x6f0 [ 246.756533] worker_thread+0x82/0x750 [ 246.756537] kthread+0x16f/0x1c0 [ 246.756541] ? trace_event_raw_event_workqueue_work+0x110/0x110 [ 246.756544] ? kthread_create_worker_on_cpu+0xb0/0xb0 [ 246.756548] ret_from_fork+0x1f/0x30 Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 9bffef153a12..3b26aa19430b 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -691,7 +691,12 @@ static void move_data_block(struct inode *inode, block_t bidx, fio.op = REQ_OP_WRITE; fio.op_flags = REQ_SYNC; fio.new_blkaddr = newaddr; - f2fs_submit_page_write(&fio); + err = f2fs_submit_page_write(&fio); + if (err) { + if (PageWriteback(fio.encrypted_page)) + end_page_writeback(fio.encrypted_page); + goto put_page_out; + } f2fs_update_iostat(fio.sbi, FS_GC_DATA_IO, F2FS_BLKSIZE); -- GitLab From 13240c90fdcc2540c5da9ead29f198f87929a9e6 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 17 Jan 2018 16:31:35 +0800 Subject: [PATCH 2606/5498] f2fs: clean up error path of fill_super This patch cleans up error path of fille_super to avoid unneeded release step. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 01e2dd39500b..fd508971abf4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2663,14 +2663,14 @@ try_onemore: err = init_percpu_info(sbi); if (err) - goto free_options; + goto free_bio_info; if (F2FS_IO_SIZE(sbi) > 1) { sbi->write_io_dummy = mempool_create_page_pool(2 * (F2FS_IO_SIZE(sbi) - 1), 0); if (!sbi->write_io_dummy) { err = -ENOMEM; - goto free_options; + goto free_percpu; } } @@ -2903,10 +2903,12 @@ free_meta_inode: free_io_dummy: if (sbi->write_io_dummy) mempool_destroy(sbi->write_io_dummy); -free_options: +free_percpu: + destroy_percpu_info(sbi); +free_bio_info: for (i = 0; i < NR_PAGE_TYPE; i++) kfree(sbi->write_io[i]); - destroy_percpu_info(sbi); +free_options: #ifdef CONFIG_QUOTA for (i = 0; i < F2FS_MAXQUOTAS; i++) kfree(sbi->s_qf_names[i]); -- GitLab From dc4b6954c141ddd80c86a4cd8ba8b75a39fbe2b5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 17 Jan 2018 16:31:36 +0800 Subject: [PATCH 2607/5498] f2fs: kill F2FS_INLINE_XATTR_ADDRS for cleanup Use get_inline_xattr_addrs directly instead of F2FS_INLINE_XATTR_ADDRS. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 738b283c2de7..67621107603b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1200,7 +1200,7 @@ static int f2fs_xattr_fiemap(struct inode *inode, phys = (__u64)blk_to_logical(inode, ni.blk_addr); offset = offsetof(struct f2fs_inode, i_addr) + sizeof(__le32) * (DEF_ADDRS_PER_INODE - - F2FS_INLINE_XATTR_ADDRS(inode)); + get_inline_xattr_addrs(inode)); phys += offset; len = inline_xattr_size(inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 280d645051d2..6c100b587dd8 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -510,10 +510,9 @@ struct f2fs_flush_device { #define DEF_MIN_INLINE_SIZE 1 static inline int get_extra_isize(struct inode *inode); static inline int get_inline_xattr_addrs(struct inode *inode); -#define F2FS_INLINE_XATTR_ADDRS(inode) get_inline_xattr_addrs(inode) #define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ (CUR_ADDRS_PER_INODE(inode) - \ - F2FS_INLINE_XATTR_ADDRS(inode) - \ + get_inline_xattr_addrs(inode) - \ DEF_INLINE_RESERVED_SIZE)) /* for inline dir */ @@ -2422,7 +2421,7 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) static inline unsigned int addrs_per_inode(struct inode *inode) { - return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS(inode); + return CUR_ADDRS_PER_INODE(inode) - get_inline_xattr_addrs(inode); } static inline void *inline_xattr_addr(struct inode *inode, struct page *page) @@ -2430,7 +2429,7 @@ static inline void *inline_xattr_addr(struct inode *inode, struct page *page) struct f2fs_inode *ri = F2FS_INODE(page); return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - - F2FS_INLINE_XATTR_ADDRS(inode)]); + get_inline_xattr_addrs(inode)]); } static inline int inline_xattr_size(struct inode *inode) -- GitLab From af69483e931673097488ddd3f46d1f2bc6ae99c0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 17 Jan 2018 16:31:37 +0800 Subject: [PATCH 2608/5498] f2fs: fix to update last_disk_size correctly This patch fixes to update last_disk_size only when writing out page successfully. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 67621107603b..17b6b5c34864 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1715,10 +1715,14 @@ write: } } - down_write(&F2FS_I(inode)->i_sem); - if (F2FS_I(inode)->last_disk_size < psize) - F2FS_I(inode)->last_disk_size = psize; - up_write(&F2FS_I(inode)->i_sem); + if (err) { + file_set_keep_isize(inode); + } else { + down_write(&F2FS_I(inode)->i_sem); + if (F2FS_I(inode)->last_disk_size < psize) + F2FS_I(inode)->last_disk_size = psize; + up_write(&F2FS_I(inode)->i_sem); + } done: if (err && err != -ENOENT) -- GitLab From b66b08d969c43eee58f4e392f91043087486d7b0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 17 Jan 2018 16:31:38 +0800 Subject: [PATCH 2609/5498] f2fs: split need_inplace_update This patch splits need_inplace_update to two functions: a. should_update_inplace() includes all conditions that we must use IPU. b. should_update_outplace() includes all conditions that we must use OPU. So that, in f2fs_ioc_set_pin_file() and f2fs_defragment_range(), we can use corresponding function to check whether we can trigger OPU/IPU or not. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 75 ++++++++++++++++++++++++++++++++++++++++++----- fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 7 ++++- fs/f2fs/segment.h | 41 -------------------------- 4 files changed, 75 insertions(+), 50 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 17b6b5c34864..08c45597d79d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1517,20 +1517,79 @@ retry_encrypt: return PTR_ERR(fio->encrypted_page); } -static inline bool need_inplace_update(struct f2fs_io_info *fio) +static inline bool check_inplace_update_policy(struct inode *inode, + struct f2fs_io_info *fio) { - struct inode *inode = fio->page->mapping->host; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + unsigned int policy = SM_I(sbi)->ipu_policy; + if (policy & (0x1 << F2FS_IPU_FORCE)) + return true; + if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) + return true; + if (policy & (0x1 << F2FS_IPU_UTIL) && + utilization(sbi) > SM_I(sbi)->min_ipu_util) + return true; + if (policy & (0x1 << F2FS_IPU_SSR_UTIL) && need_SSR(sbi) && + utilization(sbi) > SM_I(sbi)->min_ipu_util) + return true; + + /* + * IPU for rewrite async pages + */ + if (policy & (0x1 << F2FS_IPU_ASYNC) && + fio && fio->op == REQ_OP_WRITE && + !(fio->op_flags & REQ_SYNC) && + !f2fs_encrypted_inode(inode)) + return true; + + /* this is only set during fdatasync */ + if (policy & (0x1 << F2FS_IPU_FSYNC) && + is_inode_flag_set(inode, FI_NEED_IPU)) + return true; + + return false; +} + +bool should_update_inplace(struct inode *inode, struct f2fs_io_info *fio) +{ if (f2fs_is_pinned_file(inode)) return true; - if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) - return false; - if (is_cold_data(fio->page)) - return false; - if (IS_ATOMIC_WRITTEN_PAGE(fio->page)) + + /* if this is cold file, we should overwrite to avoid fragmentation */ + if (file_is_cold(inode)) + return true; + + return check_inplace_update_policy(inode, fio); +} + +bool should_update_outplace(struct inode *inode, struct f2fs_io_info *fio) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + if (test_opt(sbi, LFS)) + return true; + if (S_ISDIR(inode->i_mode)) + return true; + if (f2fs_is_atomic_file(inode)) + return true; + if (fio) { + if (is_cold_data(fio->page)) + return true; + if (IS_ATOMIC_WRITTEN_PAGE(fio->page)) + return true; + } + return false; +} + +static inline bool need_inplace_update(struct f2fs_io_info *fio) +{ + struct inode *inode = fio->page->mapping->host; + + if (should_update_outplace(inode, fio)) return false; - return need_inplace_update_policy(inode, fio); + return should_update_inplace(inode, fio); } static inline bool valid_ipu_blkaddr(struct f2fs_io_info *fio) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 6c100b587dd8..5e2c90663ac2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2972,6 +2972,8 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int create, int flag); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); +bool should_update_inplace(struct inode *inode, struct f2fs_io_info *fio); +bool should_update_outplace(struct inode *inode, struct f2fs_io_info *fio); void f2fs_set_page_dirty_nobuffers(struct page *page); int __f2fs_write_data_pages(struct address_space *mapping, struct writeback_control *wbc, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 0f04cd3c234e..a7feb0bb7c66 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2067,7 +2067,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, int err; /* if in-place-update policy is enabled, don't waste time here */ - if (need_inplace_update_policy(inode, NULL)) + if (should_update_inplace(inode, NULL)) return -EINVAL; pg_start = range->start >> PAGE_SHIFT; @@ -2507,6 +2507,11 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg) inode_lock(inode); + if (should_update_outplace(inode, NULL)) { + ret = -EINVAL; + goto out; + } + if (!pin) { clear_inode_flag(inode, FI_PIN_FILE); F2FS_I(inode)->i_gc_failures = 1; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index dffb9b5960e9..acf052e5867a 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -579,47 +579,6 @@ enum { F2FS_IPU_ASYNC, }; -static inline bool need_inplace_update_policy(struct inode *inode, - struct f2fs_io_info *fio) -{ - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - unsigned int policy = SM_I(sbi)->ipu_policy; - - if (test_opt(sbi, LFS)) - return false; - - /* if this is cold file, we should overwrite to avoid fragmentation */ - if (file_is_cold(inode)) - return true; - - if (policy & (0x1 << F2FS_IPU_FORCE)) - return true; - if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) - return true; - if (policy & (0x1 << F2FS_IPU_UTIL) && - utilization(sbi) > SM_I(sbi)->min_ipu_util) - return true; - if (policy & (0x1 << F2FS_IPU_SSR_UTIL) && need_SSR(sbi) && - utilization(sbi) > SM_I(sbi)->min_ipu_util) - return true; - - /* - * IPU for rewrite async pages - */ - if (policy & (0x1 << F2FS_IPU_ASYNC) && - fio && fio->op == REQ_OP_WRITE && - !(fio->op_flags & REQ_SYNC) && - !f2fs_encrypted_inode(inode)) - return true; - - /* this is only set during fdatasync */ - if (policy & (0x1 << F2FS_IPU_FSYNC) && - is_inode_flag_set(inode, FI_NEED_IPU)) - return true; - - return false; -} - static inline unsigned int curseg_segno(struct f2fs_sb_info *sbi, int type) { -- GitLab From cb472c71d865fb0b12ea24a728dc06a8114dad66 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 17 Jan 2018 22:28:52 +0800 Subject: [PATCH 2610/5498] f2fs: hanlde error case in f2fs_ioc_shutdown This patch makes f2fs_ioc_shutdown handling error case correctly. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a7feb0bb7c66..c2d852a4f8fd 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1824,14 +1824,20 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) switch (in) { case F2FS_GOING_DOWN_FULLSYNC: sb = freeze_bdev(sb->s_bdev); - if (sb && !IS_ERR(sb)) { + if (IS_ERR(sb)) { + ret = PTR_ERR(sb); + goto out; + } + if (sb) { f2fs_stop_checkpoint(sbi, false); thaw_bdev(sb->s_bdev, sb); } break; case F2FS_GOING_DOWN_METASYNC: /* do checkpoint only */ - f2fs_sync_fs(sb, 1); + ret = f2fs_sync_fs(sb, 1); + if (ret) + goto out; f2fs_stop_checkpoint(sbi, false); break; case F2FS_GOING_DOWN_NOSYNC: -- GitLab From ef83735b3932a040235c98aac0b7a9163daa25d0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 18 Jan 2018 17:23:29 +0800 Subject: [PATCH 2611/5498] f2fs: stop gc/discard thread after fs shutdown Once filesystem shuts down, daemons like gc/discard thread should be aware of it, and do exit, in addtion, drop all cached pending discard commands and turn off real-time discard mode. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 7 +++++++ fs/f2fs/segment.c | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5e2c90663ac2..3fc87db7c00d 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2863,6 +2863,7 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void init_discard_policy(struct discard_policy *dpolicy, int discard_type, unsigned int granularity); +void drop_discard_cmd(struct f2fs_sb_info *sbi); void stop_discard_thread(struct f2fs_sb_info *sbi); bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c2d852a4f8fd..81b1ea47b3c3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1851,6 +1851,13 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) ret = -EINVAL; goto out; } + + stop_gc_thread(sbi); + stop_discard_thread(sbi); + + drop_discard_cmd(sbi); + clear_opt(sbi, DISCARD); + f2fs_update_time(sbi, REQ_TIME); out: mnt_drop_write_file(filp); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d9b51daa724c..8d2fa4d2b083 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1344,6 +1344,11 @@ static bool __drop_discard_cmd(struct f2fs_sb_info *sbi) return dropped; } +void drop_discard_cmd(struct f2fs_sb_info *sbi) +{ + __drop_discard_cmd(sbi); +} + static unsigned int __wait_one_discard_bio(struct f2fs_sb_info *sbi, struct discard_cmd *dc) { -- GitLab From c8330bdcc35fde54c1f39d6adc0a3281de859311 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 18 Jan 2018 17:29:10 +0800 Subject: [PATCH 2612/5498] f2fs: drop page cache after fs shutdown Don't remain dirtied page cache in f2fs after shutdown, it can mitigate memory pressure of whole system, in order to keep other modules working properly. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 +++++-- fs/f2fs/data.c | 12 ++++++------ fs/f2fs/node.c | 19 ++++++++++--------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index b3c82c3a2ec0..a8f0b7a515f6 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -238,12 +238,15 @@ static int __f2fs_write_meta_page(struct page *page, trace_f2fs_writepage(page, META); + if (unlikely(f2fs_cp_error(sbi))) { + dec_page_count(sbi, F2FS_DIRTY_META); + unlock_page(page); + return 0; + } if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0)) goto redirty_out; - if (unlikely(f2fs_cp_error(sbi))) - goto redirty_out; write_meta_page(sbi, page, io_type); dec_page_count(sbi, F2FS_DIRTY_META); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 08c45597d79d..0b12c6988dfa 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1715,6 +1715,12 @@ static int __write_data_page(struct page *page, bool *submitted, trace_f2fs_writepage(page, DATA); + /* we should bypass data pages to proceed the kworkder jobs */ + if (unlikely(f2fs_cp_error(sbi))) { + mapping_set_error(page->mapping, -EIO); + goto out; + } + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; @@ -1739,12 +1745,6 @@ write: available_free_memory(sbi, BASE_CHECK)))) goto redirty_out; - /* we should bypass data pages to proceed the kworkder jobs */ - if (unlikely(f2fs_cp_error(sbi))) { - mapping_set_error(page->mapping, -EIO); - goto out; - } - /* Dentry blocks are controlled by checkpoint */ if (S_ISDIR(inode->i_mode)) { fio.need_lock = LOCK_DONE; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index f7516c37b415..24be19762bed 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1345,10 +1345,14 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, trace_f2fs_writepage(page, NODE); + if (unlikely(f2fs_cp_error(sbi))) { + dec_page_count(sbi, F2FS_DIRTY_NODES); + unlock_page(page); + return 0; + } + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; - if (unlikely(f2fs_cp_error(sbi))) - goto redirty_out; /* get old block addr of this node page */ nid = nid_of_node(page); @@ -1593,12 +1597,6 @@ next_step: struct page *page = pvec.pages[i]; bool submitted = false; - if (unlikely(f2fs_cp_error(sbi))) { - pagevec_release(&pvec); - ret = -EIO; - goto out; - } - /* * flushing sequence with step: * 0. indirect nodes @@ -1668,9 +1666,12 @@ continue_unlock: step++; goto next_step; } -out: + if (nwritten) f2fs_submit_merged_write(sbi, NODE); + + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; return ret; } -- GitLab From 2e1f3aa5affbd961986eefe1dbe0f3d641df7997 Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Sat, 20 Jan 2018 15:46:33 +0800 Subject: [PATCH 2613/5498] f2fs: correct removexattr behavior for null valued extended attribute __vfs_removexattr() transfers "NULL" value to the setxattr handler of the f2fs filesystem in order to remove the extended attribute. But, __f2fs_setxattr() just ignores the removal request when the value of the extended attribute is already NULL. We have to remove the extended attribute itself even if the value of that is already NULL. We can reporduce this bug with the below: 1. touch file 2. setfattr -n "user.foo" file 3. setfattr -x "user.foo" file 4. getfattr -d file > user.foo Signed-off-by: Daeho Jeong Signed-off-by: Youngjin Gil Tested-by: Hobin Woo Tested-by: Chao Yu Reviewed-by: Chao Yu Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 249b25eda9e4..967d6996ba02 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -642,7 +642,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, goto exit; } - if (f2fs_xattr_value_same(here, value, size)) + if (value && f2fs_xattr_value_same(here, value, size)) goto exit; } else if ((flags & XATTR_REPLACE)) { error = -ENODATA; -- GitLab From 2ea7d6f1bf18154c2a78dd1abcef2fd4fd478fcd Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 19 Jan 2018 20:01:40 -0800 Subject: [PATCH 2614/5498] f2fs: recover some i_inline flags This fixes lost i_inline flags during roll-forward. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/recovery.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 137e9d8b286c..1010fc2118dc 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -189,6 +189,20 @@ out: return err; } +static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) +{ + if (ri->i_inline & F2FS_PIN_FILE) + set_inode_flag(inode, FI_PIN_FILE); + else + clear_inode_flag(inode, FI_PIN_FILE); + if (ri->i_inline & F2FS_DATA_EXIST) + set_inode_flag(inode, FI_DATA_EXIST); + else + clear_inode_flag(inode, FI_DATA_EXIST); + if (!(ri->i_inline & F2FS_INLINE_DOTS)) + clear_inode_flag(inode, FI_INLINE_DOTS); +} + static void recover_inode(struct inode *inode, struct page *page) { struct f2fs_inode *raw = F2FS_INODE(page); @@ -205,13 +219,16 @@ static void recover_inode(struct inode *inode, struct page *page) F2FS_I(inode)->i_advise = raw->i_advise; + recover_inline_flags(inode, raw); + if (file_enc_name(inode)) name = ""; else name = F2FS_INODE(page)->i_name; - f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s", - ino_of_node(page), name); + f2fs_msg(inode->i_sb, KERN_NOTICE, + "recover_inode: ino = %x, name = %s, inline = %x", + ino_of_node(page), name, raw->i_inline); } static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, -- GitLab From 2ebcc5d82d9ae4e56a2ac66f70cc7efc0913c9f0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 19 Jan 2018 13:42:33 -0800 Subject: [PATCH 2615/5498] f2fs: allow to recover node blocks given updated checkpoint If fsck.f2fs changes crc, we have no way to recover some inode blocks by roll- forward recovery. Let's relax the condition to recover them. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 + fs/f2fs/node.h | 4 ++++ include/linux/f2fs_fs.h | 1 + 3 files changed, 6 insertions(+) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a8f0b7a515f6..8c7556b7da62 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1161,6 +1161,7 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* set this flag to activate crc|cp_ver for recovery */ __set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); + __clear_ckpt_flags(ckpt, CP_NOCRC_RECOVERY_FLAG); spin_unlock_irqrestore(&sbi->cp_lock, flags); } diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 0ee3e5ff49a3..081ef0d672bf 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -305,6 +305,10 @@ static inline bool is_recoverable_dnode(struct page *page) struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page)); __u64 cp_ver = cur_cp_version(ckpt); + /* Don't care crc part, if fsck.f2fs sets it. */ + if (__is_set_ckpt_flags(ckpt, CP_NOCRC_RECOVERY_FLAG)) + return (cp_ver << 32) == (cpver_of_node(page) << 32); + if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) cp_ver |= (cur_cp_crc(ckpt) << 32); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 5bdcfa186e69..686d1fcb0b16 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -117,6 +117,7 @@ struct f2fs_super_block { /* * For checkpoint */ +#define CP_NOCRC_RECOVERY_FLAG 0x00000200 #define CP_TRIMMED_FLAG 0x00000100 #define CP_NAT_BITS_FLAG 0x00000080 #define CP_CRC_RECOVERY_FLAG 0x00000040 -- GitLab From 67ebfab1a0f42116e4bc2e74b3548c265faeb57f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 25 Jan 2018 18:57:25 +0800 Subject: [PATCH 2616/5498] f2fs: use GFP_F2FS_ZERO for cleanup Clean up codes with GFP_F2FS_ZERO, no logic changes. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 24be19762bed..1902530d50b4 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -143,11 +143,9 @@ static struct nat_entry *__alloc_nat_entry(nid_t nid, bool no_fail) struct nat_entry *new; if (no_fail) - new = f2fs_kmem_cache_alloc(nat_entry_slab, - GFP_NOFS | __GFP_ZERO); + new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_F2FS_ZERO); else - new = kmem_cache_alloc(nat_entry_slab, - GFP_NOFS | __GFP_ZERO); + new = kmem_cache_alloc(nat_entry_slab, GFP_F2FS_ZERO); if (new) { nat_set_nid(new, nid); nat_reset_flag(new); -- GitLab From be823aa3d5af3cae6a96921e477208aad4532168 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 25 Jan 2018 18:57:26 +0800 Subject: [PATCH 2617/5498] f2fs: clean up duplicated assignment in init_discard_policy Remove duplicated codes of assignment for .max_requests and .io_aware_gran. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 8d2fa4d2b083..18d927865948 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1796,25 +1796,20 @@ void init_discard_policy(struct discard_policy *dpolicy, dpolicy->sync = true; dpolicy->granularity = granularity; + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + if (discard_type == DPOLICY_BG) { dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = true; } else if (discard_type == DPOLICY_FORCE) { dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = true; } else if (discard_type == DPOLICY_FSTRIM) { - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = false; } else if (discard_type == DPOLICY_UMOUNT) { - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = false; } } -- GitLab From f368156e84cbad08b1c665b73cf8f42b35a7f5a1 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 25 Jan 2018 18:57:27 +0800 Subject: [PATCH 2618/5498] f2fs: stop issuing discard if fs is readonly If filesystem is readonly, stop to issue discard in daemon. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 18d927865948..965db6b0473d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1487,6 +1487,8 @@ static int issue_discard_thread(void *data) msecs_to_jiffies(wait_ms)); if (try_to_freeze()) continue; + if (f2fs_readonly(sbi->sb)) + continue; if (kthread_should_stop()) return 0; -- GitLab From c72ef4727838bb2043c5e13a4dabeb95b5b2be90 Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 25 Jan 2018 17:27:11 +0800 Subject: [PATCH 2619/5498] f2fs: rebuild sit page from sit info in mem This patch rebuild sit page from sit info in mem instead of issue a read io. I test this method and the result is as below: Pre: mmc_perf_test-12061 [001] ...1 976.819992: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [001] ...1 976.856446: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [003] ...1 998.976946: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [003] ...1 999.023269: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [003] ...1 1022.060772: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [003] ...1 1022.111034: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [002] ...1 1070.127643: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [003] ...1 1070.187352: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [003] ...1 1095.942124: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [003] ...1 1095.995975: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [003] ...1 1122.535091: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [003] ...1 1122.586521: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [001] ...1 1147.897487: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [001] ...1 1147.959438: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [003] ...1 1177.926951: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [002] ...1 1177.976823: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-12061 [002] ...1 1204.176087: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-12061 [002] ...1 1204.239046: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit Some sit flush consume more than 50ms. Now: mmc_perf_test-2187 [007] ...1 196.840684: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [007] ...1 196.841258: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [007] ...1 219.430582: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [007] ...1 219.431144: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [002] ...1 243.638678: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [000] ...1 243.638980: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [002] ...1 265.392180: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [002] ...1 265.392245: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [000] ...1 290.309051: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [000] ...1 290.309116: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [003] ...1 317.144209: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [003] ...1 317.145913: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [005] ...1 343.224954: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [005] ...1 343.225574: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [000] ...1 370.239846: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [000] ...1 370.241138: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [001] ...1 397.029043: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [001] ...1 397.030750: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit mmc_perf_test-2187 [003] ...1 425.386377: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = start flush sit mmc_perf_test-2187 [003] ...1 425.387735: f2fs_write_checkpoint: dev = (259,44), checkpoint for Sync, state = end flush sit Most sit flush consume no more than 1ms. Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 19 +++++-------------- fs/f2fs/segment.h | 29 +++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 965db6b0473d..f0d69e6e2650 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3193,28 +3193,19 @@ static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, unsigned int start) { struct sit_info *sit_i = SIT_I(sbi); - struct page *src_page, *dst_page; + struct page *page; pgoff_t src_off, dst_off; - void *src_addr, *dst_addr; src_off = current_sit_addr(sbi, start); dst_off = next_sit_addr(sbi, src_off); - /* get current sit block page without lock */ - src_page = get_meta_page(sbi, src_off); - dst_page = grab_meta_page(sbi, dst_off); - f2fs_bug_on(sbi, PageDirty(src_page)); - - src_addr = page_address(src_page); - dst_addr = page_address(dst_page); - memcpy(dst_addr, src_addr, PAGE_SIZE); - - set_page_dirty(dst_page); - f2fs_put_page(src_page, 1); + page = grab_meta_page(sbi, dst_off); + seg_info_to_sit_page(sbi, page, start); + set_page_dirty(page); set_to_next_sit(sit_i, start); - return dst_page; + return page; } static struct sit_entry_set *grab_sit_entry_set(void) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index acf052e5867a..87b4ca0c60da 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -347,16 +347,41 @@ static inline void seg_info_from_raw_sit(struct seg_entry *se, se->mtime = le64_to_cpu(rs->mtime); } -static inline void seg_info_to_raw_sit(struct seg_entry *se, +static inline void __seg_info_to_raw_sit(struct seg_entry *se, struct f2fs_sit_entry *rs) { unsigned short raw_vblocks = (se->type << SIT_VBLOCKS_SHIFT) | se->valid_blocks; rs->vblocks = cpu_to_le16(raw_vblocks); memcpy(rs->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); + rs->mtime = cpu_to_le64(se->mtime); +} + +static inline void seg_info_to_sit_page(struct f2fs_sb_info *sbi, + struct page *page, unsigned int start) +{ + struct f2fs_sit_block *raw_sit; + struct seg_entry *se; + struct f2fs_sit_entry *rs; + unsigned int end = min(start + SIT_ENTRY_PER_BLOCK, + (unsigned long)MAIN_SEGS(sbi)); + int i; + + raw_sit = (struct f2fs_sit_block *)page_address(page); + for (i = 0; i < end - start; i++) { + rs = &raw_sit->entries[i]; + se = get_seg_entry(sbi, start + i); + __seg_info_to_raw_sit(se, rs); + } +} + +static inline void seg_info_to_raw_sit(struct seg_entry *se, + struct f2fs_sit_entry *rs) +{ + __seg_info_to_raw_sit(se, rs); + memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); se->ckpt_valid_blocks = se->valid_blocks; - rs->mtime = cpu_to_le64(se->mtime); } static inline unsigned int find_next_inuse(struct free_segmap_info *free_i, -- GitLab From b85b2040e29f3069095ab5d3f421273f6650cb69 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 25 Jan 2018 14:54:42 +0800 Subject: [PATCH 2620/5498] f2fs: support inode creation time This patch adds creation time field in inode layout to support showing kstat.btime in ->statx. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 7 +++++++ fs/f2fs/file.c | 31 +++++++++++++++++++++++++++++++ fs/f2fs/inode.c | 15 +++++++++++++++ fs/f2fs/namei.c | 3 ++- fs/f2fs/sysfs.c | 7 +++++++ include/linux/f2fs_fs.h | 4 +++- 6 files changed, 65 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3fc87db7c00d..41dc851eb6c2 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -126,6 +126,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_INODE_CHKSUM 0x0020 #define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 #define F2FS_FEATURE_QUOTA_INO 0x0080 +#define F2FS_FEATURE_INODE_CRTIME 0x0100 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -729,6 +730,7 @@ struct f2fs_inode_info { int i_extra_isize; /* size of extra space located in i_addr */ kprojid_t i_projid; /* id for project quota */ int i_inline_xattr_size; /* inline xattr size */ + struct timespec i_crtime; /* inode creation time */ }; static inline void get_extent_info(struct extent_info *ext, @@ -3343,6 +3345,11 @@ static inline int f2fs_sb_has_quota_ino(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_QUOTA_INO); } +static inline int f2fs_sb_has_inode_crtime(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CRTIME); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 81b1ea47b3c3..1ed7359cf37d 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -674,6 +674,37 @@ int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode = d_inode(dentry); +#if 0 + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_inode *ri; + unsigned int flags; + + if (f2fs_has_extra_attr(inode) && + f2fs_sb_has_inode_crtime(inode->i_sb) && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) { + stat->result_mask |= STATX_BTIME; + stat->btime.tv_sec = fi->i_crtime.tv_sec; + stat->btime.tv_nsec = fi->i_crtime.tv_nsec; + } + + flags = fi->i_flags & (FS_FL_USER_VISIBLE | FS_PROJINHERIT_FL); + if (flags & FS_APPEND_FL) + stat->attributes |= STATX_ATTR_APPEND; + if (flags & FS_COMPR_FL) + stat->attributes |= STATX_ATTR_COMPRESSED; + if (f2fs_encrypted_inode(inode)) + stat->attributes |= STATX_ATTR_ENCRYPTED; + if (flags & FS_IMMUTABLE_FL) + stat->attributes |= STATX_ATTR_IMMUTABLE; + if (flags & FS_NODUMP_FL) + stat->attributes |= STATX_ATTR_NODUMP; + + stat->attributes_mask |= (STATX_ATTR_APPEND | + STATX_ATTR_COMPRESSED | + STATX_ATTR_ENCRYPTED | + STATX_ATTR_IMMUTABLE | + STATX_ATTR_NODUMP); +#endif generic_fillattr(inode, stat); /* we need to show initial sectors used for inline_data/dentries */ diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 3256b9f63a44..94ef24f3e394 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -278,6 +278,12 @@ static int do_read_inode(struct inode *inode) i_projid = F2FS_DEF_PROJID; fi->i_projid = make_kprojid(&init_user_ns, i_projid); + if (f2fs_has_extra_attr(inode) && f2fs_sb_has_inode_crtime(sbi->sb) && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) { + fi->i_crtime.tv_sec = le64_to_cpu(ri->i_crtime); + fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec); + } + f2fs_put_page(node_page, 1); stat_inc_inline_xattr(inode); @@ -421,6 +427,15 @@ void update_inode(struct inode *inode, struct page *node_page) F2FS_I(inode)->i_projid); ri->i_projid = cpu_to_le32(i_projid); } + + if (f2fs_sb_has_inode_crtime(F2FS_I_SB(inode)->sb) && + F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + i_crtime)) { + ri->i_crtime = + cpu_to_le64(F2FS_I(inode)->i_crtime.tv_sec); + ri->i_crtime_nsec = + cpu_to_le32(F2FS_I(inode)->i_crtime.tv_nsec); + } } __set_inode_rdev(inode, ri); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 5f40330d8f20..4f6adb7f0f31 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -51,7 +51,8 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) inode->i_ino = ino; inode->i_blocks = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = + F2FS_I(inode)->i_crtime = current_time(inode); inode->i_generation = sbi->s_next_generation++; err = insert_inode_locked(inode); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 60ccd16c4086..6485a049e757 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -113,6 +113,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_quota_ino(sb)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "quota_ino"); + if (f2fs_sb_has_inode_crtime(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "inode_crtime"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } @@ -232,6 +235,7 @@ enum feat_id { FEAT_INODE_CHECKSUM, FEAT_FLEXIBLE_INLINE_XATTR, FEAT_QUOTA_INO, + FEAT_INODE_CRTIME, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -246,6 +250,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_INODE_CHECKSUM: case FEAT_FLEXIBLE_INLINE_XATTR: case FEAT_QUOTA_INO: + case FEAT_INODE_CRTIME: return snprintf(buf, PAGE_SIZE, "supported\n"); } return 0; @@ -323,6 +328,7 @@ F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO); +F2FS_FEATURE_RO_ATTR(inode_crtime, FEAT_INODE_CRTIME); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -376,6 +382,7 @@ static struct attribute *f2fs_feat_attrs[] = { ATTR_LIST(inode_checksum), ATTR_LIST(flexible_inline_xattr), ATTR_LIST(quota_ino), + ATTR_LIST(inode_crtime), NULL, }; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 686d1fcb0b16..9094fd49948b 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -253,8 +253,10 @@ struct f2fs_inode { __le16 i_inline_xattr_size; /* inline xattr size, unit: 4 bytes */ __le32 i_projid; /* project id */ __le32 i_inode_checksum;/* inode meta checksum */ + __le64 i_crtime; /* creation time */ + __le32 i_crtime_nsec; /* creation time in nano scale */ __le32 i_extra_end[0]; /* for attribute size calculation */ - }; + } __packed; __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ }; __le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2), -- GitLab From 1dd50e8598c71233ba91385b9850752678a250a9 Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Wed, 31 Jan 2018 14:49:37 +0200 Subject: [PATCH 2621/5498] msm: ipa: Prevent deletion of the default route rule The first APPS default routing table rule is installed at the IPA driver initialization. To prevent routing exception, this rule cannot be deleted by user application. This change prevents deleting this rule. Change-Id: Ia27434fd24a15fea5956018a1271b11bbe227df7 CRs-fixed: 2165859 Signed-off-by: Ghanim Fodi --- drivers/platform/msm/ipa/ipa_v2/ipa_rt.c | 11 ++++++++++- drivers/platform/msm/ipa/ipa_v3/ipa_rt.c | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 293a60a60881..4a68b96ca89a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1143,6 +1143,15 @@ int __ipa_del_rt_rule(u32 rule_hdl) return -EINVAL; } + if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) { + IPADBG("Deleting rule from default rt table idx=%u\n", + entry->tbl->idx); + if (entry->tbl->rule_cnt == 1) { + IPAERR_RL("Default tbl last rule cannot be deleted\n"); + return -EINVAL; + } + } + if (entry->hdr) __ipa_release_hdr(entry->hdr->id); else if (entry->proc_ctx) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 8a62defb81e2..20448b33f83a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1456,6 +1456,15 @@ int __ipa3_del_rt_rule(u32 rule_hdl) return -EINVAL; } + if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) { + IPADBG("Deleting rule from default rt table idx=%u\n", + entry->tbl->idx); + if (entry->tbl->rule_cnt == 1) { + IPAERR_RL("Default tbl last rule cannot be deleted\n"); + return -EINVAL; + } + } + if (entry->hdr) __ipa3_release_hdr(entry->hdr->id); else if (entry->proc_ctx) -- GitLab From 9a3413356b93a6382154d2d7431a526bed0cabed Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 12 Feb 2018 15:23:37 +0530 Subject: [PATCH 2622/5498] soc: qcom: bgrsb: supports persist rsb calibration on BG ssr keeps user provided rsb calibration values and reset in case of BG ssr. Change-Id: I792699fe4176ad0d94f8c12242e50e20bf7b6849 Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index a491b2c8632f..5daf1446d54a 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -122,6 +122,9 @@ struct bgrsb_priv { uint32_t calbrtion_cpi; uint8_t bttn_configs; + + bool calibration_needed; + bool is_calibrd; }; static void *bgrsb_drv; @@ -400,7 +403,13 @@ static void bgrsb_bgdown_work(struct work_struct *work) else pr_err("Failed to unvote LDO-11 on BG down\n"); } - pr_debug("RSB current state is : %d\n", dev->bgrsb_current_state); + + pr_info("RSB current state is : %d\n", dev->bgrsb_current_state); + + if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { + if (dev->is_calibrd) + dev->calibration_needed = true; + } } static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) @@ -575,6 +584,11 @@ static void bgrsb_enable_rsb(struct work_struct *work) } dev->bgrsb_current_state = BGRSB_STATE_RSB_ENABLED; pr_debug("RSB Enabled\n"); + + if (dev->calibration_needed) { + dev->calibration_needed = false; + queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work); + } } static void bgrsb_disable_rsb(struct work_struct *work) @@ -628,6 +642,7 @@ static void bgrsb_calibration(struct work_struct *work) pr_err("Failed to send interval value to BG\n"); return; } + dev->is_calibrd = true; pr_debug("RSB Calibbered\n"); } -- GitLab From ccfcba79a09cd0498e371e33465d9d8180071e78 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 30 Sep 2014 14:48:24 +0100 Subject: [PATCH 2623/5498] drivers: base: add cpu_device_create to support per-cpu devices This patch adds a new function to create per-cpu devices. This helps in: 1. reusing the device infrastructure to create any cpu related attributes and corresponding sysfs instead of creating and dealing with raw kobjects directly 2. retaining the legacy path(/sys/devices/system/cpu/..) to support existing sysfs ABI 3. avoiding to create links in the bus directory pointing to the device as there would be per-cpu instance of these devices with the same name since dev->bus is not populated to cpu_sysbus on purpose Change-Id: I21a806635ca296f63bc0abae37c959f79a797339 Signed-off-by: Sudeep Holla Tested-by: Stephen Boyd Cc: Greg Kroah-Hartman Cc: David Herrmann Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman Git-commit: 3d52943b3a51497a777e6d7d840a38596a92cee9 Git-repo: https://source.codeaurora.org/quic/la/kernel/msm-4.9 Signed-off-by: Teng Fei Fan --- drivers/base/cpu.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/cpu.h | 4 ++++ 2 files changed, 58 insertions(+) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 802b0faf25e8..4fc7f22b4d9d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -666,6 +666,60 @@ struct device *get_cpu_device(unsigned cpu) } EXPORT_SYMBOL_GPL(get_cpu_device); +static void device_create_release(struct device *dev) +{ + kfree(dev); +} + +static struct device * +__cpu_device_create(struct device *parent, void *drvdata, + const struct attribute_group **groups, + const char *fmt, va_list args) +{ + struct device *dev = NULL; + int retval = -ENODEV; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto error; + } + + device_initialize(dev); + dev->parent = parent; + dev->groups = groups; + dev->release = device_create_release; + dev_set_drvdata(dev, drvdata); + + retval = kobject_set_name_vargs(&dev->kobj, fmt, args); + if (retval) + goto error; + + retval = device_add(dev); + if (retval) + goto error; + + return dev; + +error: + put_device(dev); + return ERR_PTR(retval); +} + +struct device *cpu_device_create(struct device *parent, void *drvdata, + const struct attribute_group **groups, + const char *fmt, ...) +{ + va_list vargs; + struct device *dev; + + va_start(vargs, fmt); + dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs); + va_end(vargs); + return dev; +} +EXPORT_SYMBOL_GPL(cpu_device_create); + #ifdef CONFIG_GENERIC_CPU_AUTOPROBE static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); #endif diff --git a/include/linux/cpu.h b/include/linux/cpu.h index fbca0f9bdb54..fb151fbdbd4f 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -19,6 +19,7 @@ struct device; struct device_node; +struct attribute_group; struct cpu { int node_id; /* The node which contains the CPU */ @@ -52,6 +53,9 @@ extern void cpu_remove_dev_attr(struct device_attribute *attr); extern int cpu_add_dev_attr_group(struct attribute_group *attrs); extern void cpu_remove_dev_attr_group(struct attribute_group *attrs); +extern struct device *cpu_device_create(struct device *parent, void *drvdata, + const struct attribute_group **groups, + const char *fmt, ...); #ifdef CONFIG_HOTPLUG_CPU extern void unregister_cpu(struct cpu *cpu); extern ssize_t arch_cpu_probe(const char *, size_t); -- GitLab From 8fd952aa648ccbd17959f728e7ab3e04b486849c Mon Sep 17 00:00:00 2001 From: Sachin Bhayare Date: Mon, 5 Feb 2018 17:25:13 +0530 Subject: [PATCH 2624/5498] ARM: dts: msm: update panel fps alue for AUO 390p AUO 390 update panel supports max fps as 45. Update fps use in tear check block configuration. Increase pixel_clock to reduce transfer time. Change-Id: I910b8ce59fafb47efd22f8dab8c069cd206210cf Signed-off-by: Sachin Bhayare --- arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi index 7bd0150a92a8..f53379537a74 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,7 +14,7 @@ dsi_auo_390p_cmd: qcom,mdss_dsi_auo_390p_cmd { qcom,mdss-dsi-panel-name = "AUO 390p command mode dsi panel"; qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; - qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-framerate = <45>; qcom,mdss-dsi-virtual-channel-id = <0>; qcom,mdss-dsi-stream = <0>; qcom,mdss-dsi-panel-width = <390>; @@ -36,6 +36,7 @@ qcom,mdss-dsi-color-order = "rgb_swap_rgb"; qcom,mdss-dsi-underflow-color = <0xff>; qcom,mdss-dsi-border-color = <0>; + qcom,mdss-tear-check-frame-rate = <4500>; qcom,mdss-dsi-on-command = [ 15 01 00 00 00 00 02 fe 01 15 01 00 00 00 00 02 0a f0 @@ -89,5 +90,7 @@ qcom,mdss-dsi-mdp-trigger = "none"; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>; + /* clk = totlaH * totalV * bpp* 66fps */ + qcom,mdss-dsi-panel-clockrate = <276705792>; }; }; -- GitLab From 992a053cb622e6b6f1b418d54ac35455569aec7a Mon Sep 17 00:00:00 2001 From: Sachin Bhayare Date: Thu, 11 Jan 2018 14:39:49 +0530 Subject: [PATCH 2625/5498] msm: mdss: cancel clk_off_work during commit and blit Cancel clk_off_work when new composition starts to avoid clock getting when commit in progress. Change-Id: I56a0a2dd7658d2cc8dfab342fd04138b4e53fb59 Signed-off-by: Sachin Bhayare --- drivers/video/msm/mdss/mdp3_ctrl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index 0b11b3335d94..eee070cce861 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -194,6 +194,12 @@ static void mdp3_dispatch_clk_off(struct work_struct *work) return; } + if (!session->clk_on) { + mutex_unlock(&session->lock); + pr_debug("%s: Clk shut down is done\n", __func__); + MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__); + return; + } if (session->intf->active) { retry_dma_done: rc = wait_for_completion_timeout(&session->dma_completion, @@ -208,6 +214,7 @@ retry_dma_done: if (--retry_count) { pr_err("dmap is busy, retry %d\n", retry_count); + MDSS_XLOG(__LINE__, retry_count); goto retry_dma_done; } pr_err("dmap is still busy, bug_on\n"); @@ -1049,6 +1056,7 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) panel = mdp3_session->panel; mutex_lock(&mdp3_session->lock); + cancel_work_sync(&mdp3_session->clk_off_work); pr_debug("Requested power state = %d\n", mfd->panel_power_state); if (mdss_fb_is_power_on_lp(mfd)) { /* @@ -1520,6 +1528,7 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN); data = mdp3_bufq_pop(&mdp3_session->bufq_in); if (data) { + cancel_work_sync(&mdp3_session->clk_off_work); mdp3_ctrl_reset_countdown(mdp3_session, mfd); mdp3_ctrl_clk_enable(mfd, 1); stride = mdp3_session->dma->source_config.stride; @@ -2757,6 +2766,8 @@ static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd, } mutex_unlock(&mdp3_res->fs_idle_pc_lock); rc = mdp3_ctrl_async_blit_req(mfd, argp); + if (!rc) + cancel_work_sync(&mdp3_session->clk_off_work); break; case MSMFB_BLIT: mutex_lock(&mdp3_res->fs_idle_pc_lock); @@ -2764,6 +2775,8 @@ static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd, mdp3_ctrl_reset(mfd); mutex_unlock(&mdp3_res->fs_idle_pc_lock); rc = mdp3_ctrl_blit_req(mfd, argp); + if (!rc) + cancel_work_sync(&mdp3_session->clk_off_work); break; case MSMFB_METADATA_GET: rc = copy_from_user(&metadata, argp, sizeof(metadata)); -- GitLab From adc25d0bb72670a4590d0941cfefae51188d2dd0 Mon Sep 17 00:00:00 2001 From: Sachin Bhayare Date: Tue, 13 Feb 2018 11:21:01 +0530 Subject: [PATCH 2626/5498] msm: mdss: Add xlog in mdp3_dma operation Add xlog to capture mdp3 dma status. Change-Id: I94455fbe659faf82ab824711b8e2c42295729055 Signed-off-by: Sachin Bhayare --- drivers/video/msm/mdss/mdp3_dma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c index f5e4eae5b01e..11a34bb33303 100644 --- a/drivers/video/msm/mdss/mdp3_dma.c +++ b/drivers/video/msm/mdss/mdp3_dma.c @@ -666,6 +666,7 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf, ATRACE_BEGIN(__func__); pr_debug("mdp3_dmap_update\n"); + MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__); if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE; if (intf->active) { @@ -759,6 +760,7 @@ static int mdp3_dmas_update(struct mdp3_dma *dma, void *buf, unsigned long flag; int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC; + MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__); if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE; if (intf->active) @@ -967,6 +969,8 @@ bool mdp3_dmap_busy(void) val = MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS); pr_err("%s DMAP Status %s\n", __func__, (val & MDP3_DMA_P_BUSY_BIT) ? "BUSY":"IDLE"); + MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__, + (val & MDP3_DMA_P_BUSY_BIT) ? 1:0); return val & MDP3_DMA_P_BUSY_BIT; } -- GitLab From 0c54159f04bde7bcd4169392ff0d36c9f81a71e0 Mon Sep 17 00:00:00 2001 From: Mohammed Javid Date: Wed, 31 Jan 2018 15:51:05 +0530 Subject: [PATCH 2627/5498] msm: ipa: rndis: Fine tune threshold for rndis/ecm Fine tune rndis/ecm low threshold for better performance. Change-Id: Ib9165a4656a3acfdf9c400590e6b7371eacd895c Signed-off-by: Mohammed Javid --- drivers/net/ethernet/msm/ecm_ipa.c | 4 ++-- drivers/net/ethernet/msm/rndis_ipa.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c index 80ea7ef497cb..cd22429ced6d 100644 --- a/drivers/net/ethernet/msm/ecm_ipa.c +++ b/drivers/net/ethernet/msm/ecm_ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,7 +25,7 @@ #define ECM_IPA_IPV6_HDR_NAME "ecm_eth_ipv6" #define INACTIVITY_MSEC_DELAY 100 #define DEFAULT_OUTSTANDING_HIGH 64 -#define DEFAULT_OUTSTANDING_LOW 32 +#define DEFAULT_OUTSTANDING_LOW 48 #define DEBUGFS_TEMP_BUF_SIZE 4 #define TX_TIMEOUT (5 * HZ) diff --git a/drivers/net/ethernet/msm/rndis_ipa.c b/drivers/net/ethernet/msm/rndis_ipa.c index 179e7081cb40..be346a92ff59 100644 --- a/drivers/net/ethernet/msm/rndis_ipa.c +++ b/drivers/net/ethernet/msm/rndis_ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -41,7 +41,7 @@ #define IPA_TO_USB_CLIENT IPA_CLIENT_USB_CONS #define INACTIVITY_MSEC_DELAY 100 #define DEFAULT_OUTSTANDING_HIGH 64 -#define DEFAULT_OUTSTANDING_LOW 32 +#define DEFAULT_OUTSTANDING_LOW 48 #define DEBUGFS_TEMP_BUF_SIZE 4 #define RNDIS_IPA_PKT_TYPE 0x00000001 #define RNDIS_IPA_DFLT_RT_HDL 0 -- GitLab From 40ac7165263b14394faf737fb6b4ae3d57653d5d Mon Sep 17 00:00:00 2001 From: Archana Sriram Date: Fri, 9 Feb 2018 17:50:19 +0530 Subject: [PATCH 2628/5498] msm: kgsl: Fix reading lm_sequence in _execute_reg_sequence() Added proper checks while reading lm_sequence array so that it is not accessed beyond lm_size. CRs-Fixed: 2175951 Change-Id: I639b07fba275aad0f62cc03fa11201c126e24812 Signed-off-by: Archana Sriram --- drivers/gpu/msm/adreno_a5xx.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 4f40722cb199..3aecb266dc53 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -1265,31 +1265,27 @@ static int _execute_reg_sequence(struct adreno_device *adreno_dev, /* todo double check the reg writes */ while ((cur - opcode) < length) { - switch (cur[0]) { - /* Write a 32 bit value to a 64 bit reg */ - case 1: + if (cur[0] == 1 && ((cur + 4) - opcode) <= length) { + /* Write a 32 bit value to a 64 bit reg */ reg = cur[2]; reg = (reg << 32) | cur[1]; kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, cur[3]); cur += 4; - break; - /* Write a 64 bit value to a 64 bit reg */ - case 2: + } else if (cur[0] == 2 && ((cur + 5) - opcode) <= length) { + /* Write a 64 bit value to a 64 bit reg */ reg = cur[2]; reg = (reg << 32) | cur[1]; val = cur[4]; val = (val << 32) | cur[3]; kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, val); cur += 5; - break; - /* Delay for X usec */ - case 3: + } else if (cur[0] == 3 && ((cur + 2) - opcode) <= length) { + /* Delay for X usec */ udelay(cur[1]); cur += 2; - break; - default: + } else return -EINVAL; - } } + } return 0; } -- GitLab From 6f303c3d6a0fd5c36318fc5cc11a538a9ab50717 Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Wed, 14 Feb 2018 06:10:41 -0700 Subject: [PATCH 2629/5498] clocksource: arch_timer: Do not disble CNTVCT useraccess for 32-bit This reverts commit 94b2ae997e97 ("arm: traps: emulate a MRRC instruction reading CNTVCT register"). This change is not required for msm-3.18 and causes some userspace regression. So, drop it. Change-Id: Ie1a22756c9b07668cb884c1dfaed18bcf8ca50e4 Signed-off-by: Rajeswari Konda Signed-off-by: Neeraj Upadhyay --- arch/arm/include/asm/traps.h | 2 +- arch/arm/kernel/traps.c | 24 +++++++++++------------- arch/arm64/include/asm/traps.h | 2 +- drivers/clocksource/Kconfig | 2 +- drivers/clocksource/arm_arch_timer.c | 2 +- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h index 91aba35a3f5a..f9e412b97fbf 100644 --- a/arch/arm/include/asm/traps.h +++ b/arch/arm/include/asm/traps.h @@ -46,7 +46,7 @@ static inline int in_exception_text(unsigned long ptr) return in ? : __in_irqentry_text(ptr); } -extern void get_timer_count_hook_init(void); +extern void get_pct_hook_init(void); extern void __init early_trap_init(void *); extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame); extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 5567ec9ce6e5..e1678962cbf4 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -764,13 +764,12 @@ late_initcall(arm_mrc_hook_init); #endif -static int get_timer_count_trap(struct pt_regs *regs, unsigned int instr) +static int get_pct_trap(struct pt_regs *regs, unsigned int instr) { - u64 cval; + u64 cntpct; unsigned int res; int rd = (instr >> 12) & 0xF; int rn = (instr >> 16) & 0xF; - int read_virtual = (instr >> 4) & 1; res = arm_check_condition(instr, regs->ARM_cpsr); if (res == ARM_OPCODE_CONDTEST_FAIL) { @@ -780,27 +779,26 @@ static int get_timer_count_trap(struct pt_regs *regs, unsigned int instr) if (rd == 15 || rn == 15) return 1; - cval = read_virtual ? - arch_counter_get_cntvct() : arch_counter_get_cntpct(); - regs->uregs[rd] = cval; - regs->uregs[rn] = cval >> 32; + cntpct = arch_counter_get_cntpct(); + regs->uregs[rd] = cntpct; + regs->uregs[rn] = cntpct >> 32; regs->ARM_pc += 4; return 0; } -static struct undef_hook get_timer_count_hook = { - .instr_mask = 0x0ff00fef, +static struct undef_hook get_pct_hook = { + .instr_mask = 0x0ff00fff, .instr_val = 0x0c500f0e, .cpsr_mask = MODE_MASK, .cpsr_val = USR_MODE, - .fn = get_timer_count_trap, + .fn = get_pct_trap, }; -void get_timer_count_hook_init(void) +void get_pct_hook_init(void) { - register_undef_hook(&get_timer_count_hook); + register_undef_hook(&get_pct_hook); } -EXPORT_SYMBOL(get_timer_count_hook_init); +EXPORT_SYMBOL(get_pct_hook_init); void __bad_xchg(volatile void *ptr, int size) { diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h index dee1900254c5..4a952d92bd12 100644 --- a/arch/arm64/include/asm/traps.h +++ b/arch/arm64/include/asm/traps.h @@ -43,5 +43,5 @@ static inline int in_exception_text(unsigned long ptr) ptr < (unsigned long)&__exception_text_end; } -static inline void get_timer_count_hook_init(void) {} +static inline void get_pct_hook_init(void) {} #endif diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 497b2f5434a1..2a6b2a8e1578 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -112,7 +112,7 @@ config ARM_ARCH_TIMER_EVTSTREAM config ARM_ARCH_TIMER_VCT_ACCESS bool "Support for ARM architected timer virtual counter access in userspace" - default n + default !ARM64 depends on ARM_ARCH_TIMER help This option enables support for reading the ARM architected timer's diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 8e5810ffb813..ef939675c9ca 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -832,7 +832,7 @@ static void __init arch_timer_mem_init(struct device_node *np) arch_timer_detect_rate(base, np); arch_timer_mem_register(base, irq); arch_timer_common_init(); - get_timer_count_hook_init(); + get_pct_hook_init(); } CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", arch_timer_mem_init); -- GitLab From 49790691d17388ea86555780be406d9a9713594d Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 25 Aug 2017 15:57:04 -0700 Subject: [PATCH 2630/5498] UPSTREAM: time: Fix ktime_get_raw() incorrect base accumulation In comqit fc6eead7c1e2 ("time: Clean up CLOCK_MONOTONIC_RAW time handling"), the following code got mistakenly added to the update of the raw timekeeper: /* Update the monotonic raw base */ seconds = tk->raw_sec; nsec = (u32)(tk->tkr_raw.xtime_nsec >> tk->tkr_raw.shift); tk->tkr_raw.base = ns_to_ktime(seconds * NSEC_PER_SEC + nsec); Which adds the raw_sec value and the shifted down raw xtime_nsec to the base value. But the read function adds the shifted down tk->tkr_raw.xtime_nsec value another time, The result of this is that ktime_get_raw() users (which are all internal users) see the raw time move faster then it should (the rate at which can vary with the current size of tkr_raw.xtime_nsec), which has resulted in at least problems with graphics rendering performance. The change tried to match the monotonic base update logic: seconds = (u64)(tk->xtime_sec + tk->wall_to_monotonic.tv_sec); nsec = (u32) tk->wall_to_monotonic.tv_nsec; tk->tkr_mono.base = ns_to_ktime(seconds * NSEC_PER_SEC + nsec); Which adds the wall_to_monotonic.tv_nsec value, but not the tk->tkr_mono.xtime_nsec value to the base. To fix this, simplify the tkr_raw.base accumulation to only accumulate the raw_sec portion, and do not include the tkr_raw.xtime_nsec portion, which will be added at read time. Fixes: fc6eead7c1e2 ("time: Clean up CLOCK_MONOTONIC_RAW time handling") Reported-and-tested-by: Chris Wilson Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Cc: Prarit Bhargava Cc: Kevin Brodsky Cc: Richard Cochran Cc: Stephen Boyd Cc: Will Deacon Cc: Miroslav Lichvar Cc: Daniel Mentz Link: http://lkml.kernel.org/r/1503701824-1645-1-git-send-email-john.stultz@linaro.org (cherry picked from commit 0bcdc0987cce9880436b70836c6a92bb8e744fd1) Change-Id: I91d552bef42005d954f77963beafdca3cb6eb246 --- kernel/time/timekeeping.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 9c854e60b63a..793e760b0c94 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -427,9 +427,7 @@ static inline void tk_update_ktime_data(struct timekeeper *tk) tk->tkr_mono.base = ns_to_ktime(nsec); /* Update the monotonic raw base */ - seconds = tk->raw_sec; - nsec = (u32)(tk->tkr_raw.xtime_nsec >> tk->tkr_raw.shift); - tk->tkr_raw.base = ns_to_ktime(seconds * NSEC_PER_SEC + nsec); + tk->tkr_raw.base = ns_to_ktime(tk->raw_sec * NSEC_PER_SEC); } /* must hold timekeeper_lock */ -- GitLab From 66a66002e60d34d889f189bd0b320f068f140310 Mon Sep 17 00:00:00 2001 From: Krishna Manikandan Date: Thu, 15 Feb 2018 15:17:22 +0530 Subject: [PATCH 2631/5498] ARM: dts: msm: Enabling ESD feature for WTP3100 platform Enabling ESD check for auo 390p panel used on WTP 3100 platform. Change-Id: I1149861d3f40c2bfe9a985291719dd97353b4e5d Signed-off-by: Krishna Manikandan --- arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi index f53379537a74..2d5232a233d1 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi @@ -92,5 +92,7 @@ qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>; /* clk = totlaH * totalV * bpp* 66fps */ qcom,mdss-dsi-panel-clockrate = <276705792>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; }; }; -- GitLab From 9ce35df9cdb8824931aae2b8975a1a585b974589 Mon Sep 17 00:00:00 2001 From: Dhoat Harpal Date: Mon, 12 Feb 2018 15:22:55 +0530 Subject: [PATCH 2632/5498] soc: qcom: glink_bgcom_xprt: Use single pointer for bg apis Glink is calling bgcom_suspend and bgcom_resume with double pointer of handle. This creates confusion since other bgcom apis are called with single pointer. Use single pointer of handle for bgcom_suspend and bgcom_resume APIs. CRs-Fixed: 2171277 Change-Id: I1c06f84bfdb9996bce64432a40b0560a1e385f9d Signed-off-by: Dhoat Harpal --- drivers/soc/qcom/glink_bgcom_xprt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/glink_bgcom_xprt.c b/drivers/soc/qcom/glink_bgcom_xprt.c index 8886851dfbb8..9cba9d8cce67 100644 --- a/drivers/soc/qcom/glink_bgcom_xprt.c +++ b/drivers/soc/qcom/glink_bgcom_xprt.c @@ -1290,7 +1290,7 @@ static int tx_data(struct glink_transport_if *if_ptr, uint16_t cmd_id, if (cmd.id == TRACER_PKT_CMD) tracer_pkt_log_event((void *)(pctx->data), GLINK_XPRT_TX); - bgcom_resume(&einfo->bgcom_handle); + bgcom_resume(einfo->bgcom_handle); bgcom_ahb_write(einfo->bgcom_handle, (uint32_t)(size_t)dst, ALIGN(tx_size, WORD_SIZE)/WORD_SIZE, data_start); @@ -1708,7 +1708,7 @@ static int glink_bgcom_suspend(struct platform_device *pdev, suspend = !(einfo->activity_flag); spin_unlock_irqrestore(&einfo->activity_lock, flags); if (suspend) - rc = bgcom_suspend(&einfo->bgcom_handle); + rc = bgcom_suspend(einfo->bgcom_handle); if (rc < 0) GLINK_ERR("%s: Could not suspend activity_flag %d, rc %d\n", __func__, einfo->activity_flag, rc); -- GitLab From 379a7a5631c35083c946bd540fa66e1482809ffb Mon Sep 17 00:00:00 2001 From: Archana Obannagari Date: Thu, 8 Feb 2018 16:42:24 +0530 Subject: [PATCH 2633/5498] msm: kgsl: Add a check for valid frequency of RBBM timer clock rbbmtimer_clk hardwired fixed XO frequency 19.2Mhz. clock_round_rate() for RBBM timer clock fails with -EPERM as it is fixed. So added a check to make sure that rbbmtimer_clk clk_set_rate() has valid frequency. This avoids warning message in the driver log. Change-Id: I8f8bcec88e6a39e1550bb67590e6b66dba8e7a27 Signed-off-by: Archana Obannagari --- drivers/gpu/msm/kgsl_pwrctrl.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 62356465ce93..1eedc8a3e494 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2078,7 +2078,7 @@ static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq, int kgsl_pwrctrl_init(struct kgsl_device *device) { - int i, k, m, n = 0, result; + int i, k, m, n = 0, result, freq; struct platform_device *pdev = device->pdev; struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct device_node *ocmem_bus_node; @@ -2142,7 +2142,7 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->wakeup_maxpwrlevel = 0; for (i = 0; i < pwr->num_pwrlevels; i++) { - unsigned int freq = pwr->pwrlevels[i].gpu_freq; + freq = pwr->pwrlevels[i].gpu_freq; if (freq > 0) freq = clk_round_rate(pwr->grp_clks[0], freq); @@ -2153,11 +2153,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq, clocks[0]); - if (pwr->grp_clks[6] != NULL) + freq = clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ); + if (freq > 0) kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[6], - clk_round_rate(pwr->grp_clks[6], - KGSL_RBBMTIMER_CLK_FREQ), - clocks[6]); + freq, clocks[6]); result = get_regulators(device); if (result) -- GitLab From c47cf417e8d1595fe0de6068aa8edde1c60a5994 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Thu, 31 Aug 2017 10:37:00 +0200 Subject: [PATCH 2634/5498] BACKPORT: xfrm: Fix return value check of copy_sec_ctx. commit 8598112d04af21cf6c895670e72dcb8a9f58e74f upstream. A recent commit added an output_mark. When copying this output_mark, the return value of copy_sec_ctx is overwitten without a check. Fix this by copying the output_mark before the security context. Fixes: 077fbac405bf ("net: xfrm: support setting an output mark.") Signed-off-by: Steffen Klassert Change-Id: I25e9ac6cf79dc8d0ee599bbd23e9d5b5f34a4284 Fixes: Change-Id: I76120fba036e21780ced31ad390faf491ea81e52 ("BACKPORT: net: xfrm: support setting an output mark.") Signed-off-by: Amit Pundir --- net/xfrm/xfrm_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 20ba3f661425..419750e3a65c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -841,13 +841,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x, if (ret) goto out; } - if (x->security) - ret = copy_sec_ctx(x->security, skb); if (x->props.output_mark) { ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark); if (ret) goto out; } + if (x->security) + ret = copy_sec_ctx(x->security, skb); out: return ret; } -- GitLab From b0a27b036e2d78b7d95fb728fd1e3213914bca03 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Fri, 16 Feb 2018 12:52:09 +0530 Subject: [PATCH 2635/5498] ARM: dts: msm: Add support for GPIO based VBUS detection for APQ8009 For APQ8009 add support for GPIO based VBUS detection using the driver gpio-usbdetect. Change the default mode of USB from peripheral mode to otg. Add the VBUS supply regulator which phy-msm-usb driver enabled to drive the VBUS in host mode. Change-Id: I7261363acf58be0bf8d4c77ceaf8fbb3353635a4 Signed-off-by: Sriharsha Allenki --- ...pq8009-mtp-wcd9326-excelpoint-refboard.dts | 37 +++++++++++++++++-- .../dts/qcom/apq8009-mtp-wcd9326-refboard.dts | 37 +++++++++++++++++-- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts index 34e20395763b..55787247a545 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts @@ -56,6 +56,19 @@ bias-disable; }; }; + + usb_vbus_detect: usb_vbus_detect { + mux { + pins = "gpio97"; + function = "gpio"; + }; + + config { + pins = "gpio97"; + drive-strength = <2>; + bias-disable; + }; + }; }; &soc { @@ -170,6 +183,24 @@ <79 512 25000 200000>, /* 200 Mbps */ <79 512 2048000 4096000>; /* MAX */ }; + + vbus_otg_supply: vbus_otg_supply { + compatible = "regulator-fixed"; + regulator-name = "vbus_otg"; + status = "ok"; + enable-active-high; + gpio = <&pm8916_gpios 3 0>; + }; + + usb_detect: qcom,gpio-usbdetect { + compatible = "qcom,gpio-usbdetect"; + interrupt-parent = <&msm_gpio>; + interrupts = <97 0>; + interrupt-names = "vbus_det_irq"; + pinctrl-names = "usb_vbus_detect"; + pinctrl-0 = <&usb_vbus_detect>; + qcom,gpio-mode-sel = <&msm_gpio 97 0>; + }; }; &wcnss { @@ -279,10 +310,8 @@ interrupts = <0 134 0>,<0 140 0>,<0 136 0>; interrupt-names = "core_irq", "async_irq", "phy_irq"; - qcom,hsusb-otg-mode = <1>; /* peripheral mode */ - qcom,hsusb-otg-otg-control= <3>; - qcom,hsusb-otg-default-mode= <1>; /* peripheral mode */ - vbus_otg-supply = <&smb1360_otg_supply>; + qcom,hsusb-otg-mode = <3>; + vbus_otg-supply = <&vbus_otg_supply>; }; &mdss_fb0 { diff --git a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts index d2656c22e7fe..8e8179aa9c0d 100644 --- a/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts +++ b/arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts @@ -56,6 +56,19 @@ bias-disable; }; }; + + usb_vbus_detect: usb_vbus_detect { + mux { + pins = "gpio97"; + function = "gpio"; + }; + + config { + pins = "gpio97"; + drive-strength = <2>; + bias-disable; + }; + }; }; &soc { @@ -148,6 +161,24 @@ <79 512 25000 200000>, /* 200 Mbps */ <79 512 2048000 4096000>; /* MAX */ }; + + vbus_otg_supply: vbus_otg_supply { + compatible = "regulator-fixed"; + regulator-name = "vbus_otg"; + status = "ok"; + enable-active-high; + gpio = <&pm8916_gpios 3 0>; + }; + + usb_detect: qcom,gpio-usbdetect { + compatible = "qcom,gpio-usbdetect"; + interrupt-parent = <&msm_gpio>; + interrupts = <97 0>; + interrupt-names = "vbus_det_irq"; + pinctrl-names = "usb_vbus_detect"; + pinctrl-0 = <&usb_vbus_detect>; + qcom,gpio-mode-sel = <&msm_gpio 97 0>; + }; }; &wcnss { @@ -245,10 +276,8 @@ interrupts = <0 134 0>,<0 140 0>,<0 136 0>; interrupt-names = "core_irq", "async_irq", "phy_irq"; - qcom,hsusb-otg-mode = <1>; /* peripheral mode */ - qcom,hsusb-otg-otg-control= <3>; - qcom,hsusb-otg-default-mode= <1>; /* peripheral mode */ - vbus_otg-supply = <&smb1360_otg_supply>; + qcom,hsusb-otg-mode = <3>; + vbus_otg-supply = <&vbus_otg_supply>; }; &mdss_fb0 { -- GitLab From 99ea4d84fc0c8d90b9af4f204a886f0ab9b029a7 Mon Sep 17 00:00:00 2001 From: Sriharsha Allenki Date: Fri, 16 Feb 2018 12:57:16 +0530 Subject: [PATCH 2636/5498] defconfig: msm: Enable gpio-usbdetect driver for MSM8909 For MSM8909 enable gpio-usbdetect driver so that the controller can be notified for changes in VBUS that are connected to MSM GPIOs. Change-Id: I1bc1b11dcf06ab4589868a8204e003620750ccbf Signed-off-by: Sriharsha Allenki --- arch/arm/configs/msm8909-perf_defconfig | 1 + arch/arm/configs/msm8909_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig index 176e36079a8e..8ec2d3e6caad 100644 --- a/arch/arm/configs/msm8909-perf_defconfig +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -441,6 +441,7 @@ CONFIG_SPS=y CONFIG_USB_BAM=y CONFIG_SPS_SUPPORT_NDP_BAM=y CONFIG_QPNP_VIBRATOR=y +CONFIG_GPIO_USB_DETECT=y CONFIG_MSM_SPMI=y CONFIG_MSM_SPMI_PMIC_ARB=y CONFIG_MSM_QPNP_INT=y diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig index 7448772bd9a5..ae0a938075bf 100644 --- a/arch/arm/configs/msm8909_defconfig +++ b/arch/arm/configs/msm8909_defconfig @@ -453,6 +453,7 @@ CONFIG_SPS=y CONFIG_USB_BAM=y CONFIG_SPS_SUPPORT_NDP_BAM=y CONFIG_QPNP_VIBRATOR=y +CONFIG_GPIO_USB_DETECT=y CONFIG_MSM_SPMI=y CONFIG_MSM_SPMI_PMIC_ARB=y CONFIG_MSM_QPNP_INT=y -- GitLab From 8687f95a74fd8271c7b5939dc3d521b201dac7fa Mon Sep 17 00:00:00 2001 From: Carter Cooper Date: Wed, 17 Jan 2018 09:49:00 -0700 Subject: [PATCH 2637/5498] msm: kgsl: Properly remove ref count on gpuobj_sync failure The user can pass bad data into kgsl_ioctl_gpuobj_sync(). If _copy_from_user() fails do to bad data, undo any current references taken through this ioctl call. Change-Id: I56195520b9dadba20ee419658fc2cbb282b8449c Signed-off-by: Carter Cooper Signed-off-by: samit vats --- drivers/gpu/msm/kgsl.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 0af8590f0f60..b4a0371594b6 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2841,7 +2841,7 @@ long kgsl_ioctl_gpuobj_sync(struct kgsl_device_private *dev_priv, long ret = 0; bool full_flush = false; uint64_t size = 0; - int i, count = 0; + int i; void __user *ptr; if (param->count == 0 || param->count > 128) @@ -2853,8 +2853,8 @@ long kgsl_ioctl_gpuobj_sync(struct kgsl_device_private *dev_priv, entries = kzalloc(param->count * sizeof(*entries), GFP_KERNEL); if (entries == NULL) { - ret = -ENOMEM; - goto out; + kfree(objs); + return -ENOMEM; } ptr = to_user_ptr(param->objs); @@ -2871,36 +2871,32 @@ long kgsl_ioctl_gpuobj_sync(struct kgsl_device_private *dev_priv, if (entries[i] == NULL) continue; - count++; - if (!(objs[i].op & KGSL_GPUMEM_CACHE_RANGE)) size += entries[i]->memdesc.size; else if (objs[i].offset < entries[i]->memdesc.size) size += (entries[i]->memdesc.size - objs[i].offset); full_flush = check_full_flush(size, objs[i].op); - if (full_flush) - break; + if (full_flush) { + trace_kgsl_mem_sync_full_cache(i, size); + flush_cache_all(); + goto out; + } ptr += sizeof(*objs); } - if (full_flush) { - trace_kgsl_mem_sync_full_cache(count, size); - flush_cache_all(); - } else { - for (i = 0; !ret && i < param->count; i++) - if (entries[i]) - ret = _kgsl_gpumem_sync_cache(entries[i], - objs[i].offset, objs[i].length, - objs[i].op); - } + for (i = 0; !ret && i < param->count; i++) + if (entries[i]) + ret = _kgsl_gpumem_sync_cache(entries[i], + objs[i].offset, objs[i].length, + objs[i].op); +out: for (i = 0; i < param->count; i++) if (entries[i]) kgsl_mem_entry_put(entries[i]); -out: kfree(entries); kfree(objs); -- GitLab From 8cec497214a1512cda737f87cbdd491260acefa4 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 25 Jan 2018 22:03:52 +0800 Subject: [PATCH 2638/5498] vhost_net: stop device during reset owner [ Upstream commit 4cd879515d686849eec5f718aeac62a70b067d82 ] We don't stop device before reset owner, this means we could try to serve any virtqueue kick before reset dev->worker. This will result a warn since the work was pending at llist during owner resetting. Fix this by stopping device during owner reset. Reported-by: syzbot+eb17c6162478cc50632c@syzkaller.appspotmail.com Fixes: 3a4d5c94e9593 ("vhost_net: a kernel-level virtio server") Signed-off-by: Jason Wang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/vhost/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 8dae2f724a35..d812f9d71011 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -1009,6 +1009,7 @@ static long vhost_net_reset_owner(struct vhost_net *n) } vhost_net_stop(n, &tx_sock, &rx_sock); vhost_net_flush(n); + vhost_dev_stop(&n->dev); vhost_dev_reset_owner(&n->dev, memory); vhost_net_vq_reset(n); done: -- GitLab From 6598ee1deb37afa19a18d5dc31c6ad235df4fcd4 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Wed, 31 Jan 2018 16:29:30 +0200 Subject: [PATCH 2639/5498] ip6mr: fix stale iterator [ Upstream commit 4adfa79fc254efb7b0eb3cd58f62c2c3f805f1ba ] When we dump the ip6mr mfc entries via proc, we initialize an iterator with the table to dump but we don't clear the cache pointer which might be initialized from a prior read on the same descriptor that ended. This can result in lock imbalance (an unnecessary unlock) leading to other crashes and hangs. Clear the cache pointer like ipmr does to fix the issue. Thanks for the reliable reproducer. Here's syzbot's trace: WARNING: bad unlock balance detected! 4.15.0-rc3+ #128 Not tainted syzkaller971460/3195 is trying to release lock (mrt_lock) at: [<000000006898068d>] ipmr_mfc_seq_stop+0xe1/0x130 net/ipv6/ip6mr.c:553 but there are no more locks to release! other info that might help us debug this: 1 lock held by syzkaller971460/3195: #0: (&p->lock){+.+.}, at: [<00000000744a6565>] seq_read+0xd5/0x13d0 fs/seq_file.c:165 stack backtrace: CPU: 1 PID: 3195 Comm: syzkaller971460 Not tainted 4.15.0-rc3+ #128 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:17 [inline] dump_stack+0x194/0x257 lib/dump_stack.c:53 print_unlock_imbalance_bug+0x12f/0x140 kernel/locking/lockdep.c:3561 __lock_release kernel/locking/lockdep.c:3775 [inline] lock_release+0x5f9/0xda0 kernel/locking/lockdep.c:4023 __raw_read_unlock include/linux/rwlock_api_smp.h:225 [inline] _raw_read_unlock+0x1a/0x30 kernel/locking/spinlock.c:255 ipmr_mfc_seq_stop+0xe1/0x130 net/ipv6/ip6mr.c:553 traverse+0x3bc/0xa00 fs/seq_file.c:135 seq_read+0x96a/0x13d0 fs/seq_file.c:189 proc_reg_read+0xef/0x170 fs/proc/inode.c:217 do_loop_readv_writev fs/read_write.c:673 [inline] do_iter_read+0x3db/0x5b0 fs/read_write.c:897 compat_readv+0x1bf/0x270 fs/read_write.c:1140 do_compat_preadv64+0xdc/0x100 fs/read_write.c:1189 C_SYSC_preadv fs/read_write.c:1209 [inline] compat_SyS_preadv+0x3b/0x50 fs/read_write.c:1203 do_syscall_32_irqs_on arch/x86/entry/common.c:327 [inline] do_fast_syscall_32+0x3ee/0xf9d arch/x86/entry/common.c:389 entry_SYSENTER_compat+0x51/0x60 arch/x86/entry/entry_64_compat.S:125 RIP: 0023:0xf7f73c79 RSP: 002b:00000000e574a15c EFLAGS: 00000292 ORIG_RAX: 000000000000014d RAX: ffffffffffffffda RBX: 000000000000000f RCX: 0000000020a3afb0 RDX: 0000000000000001 RSI: 0000000000000067 RDI: 0000000000000000 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 BUG: sleeping function called from invalid context at lib/usercopy.c:25 in_atomic(): 1, irqs_disabled(): 0, pid: 3195, name: syzkaller971460 INFO: lockdep is turned off. CPU: 1 PID: 3195 Comm: syzkaller971460 Not tainted 4.15.0-rc3+ #128 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:17 [inline] dump_stack+0x194/0x257 lib/dump_stack.c:53 ___might_sleep+0x2b2/0x470 kernel/sched/core.c:6060 __might_sleep+0x95/0x190 kernel/sched/core.c:6013 __might_fault+0xab/0x1d0 mm/memory.c:4525 _copy_to_user+0x2c/0xc0 lib/usercopy.c:25 copy_to_user include/linux/uaccess.h:155 [inline] seq_read+0xcb4/0x13d0 fs/seq_file.c:279 proc_reg_read+0xef/0x170 fs/proc/inode.c:217 do_loop_readv_writev fs/read_write.c:673 [inline] do_iter_read+0x3db/0x5b0 fs/read_write.c:897 compat_readv+0x1bf/0x270 fs/read_write.c:1140 do_compat_preadv64+0xdc/0x100 fs/read_write.c:1189 C_SYSC_preadv fs/read_write.c:1209 [inline] compat_SyS_preadv+0x3b/0x50 fs/read_write.c:1203 do_syscall_32_irqs_on arch/x86/entry/common.c:327 [inline] do_fast_syscall_32+0x3ee/0xf9d arch/x86/entry/common.c:389 entry_SYSENTER_compat+0x51/0x60 arch/x86/entry/entry_64_compat.S:125 RIP: 0023:0xf7f73c79 RSP: 002b:00000000e574a15c EFLAGS: 00000292 ORIG_RAX: 000000000000014d RAX: ffffffffffffffda RBX: 000000000000000f RCX: 0000000020a3afb0 RDX: 0000000000000001 RSI: 0000000000000067 RDI: 0000000000000000 RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 WARNING: CPU: 1 PID: 3195 at lib/usercopy.c:26 _copy_to_user+0xb5/0xc0 lib/usercopy.c:26 Reported-by: syzbot Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6mr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 4b7e2fad2fef..61bc48ce07a5 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -498,6 +498,7 @@ static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) return ERR_PTR(-ENOENT); it->mrt = mrt; + it->cache = NULL; return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) : SEQ_START_TOKEN; } -- GitLab From ec1e18fda7c663dac7a2bb7b695981489aa3ff87 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 1 Feb 2018 10:26:57 -0800 Subject: [PATCH 2640/5498] net: igmp: add a missing rcu locking section [ Upstream commit e7aadb27a5415e8125834b84a74477bfbee4eff5 ] Newly added igmpv3_get_srcaddr() needs to be called under rcu lock. Timer callbacks do not ensure this locking. ============================= WARNING: suspicious RCU usage 4.15.0+ #200 Not tainted ----------------------------- ./include/linux/inetdevice.h:216 suspicious rcu_dereference_check() usage! other info that might help us debug this: rcu_scheduler_active = 2, debug_locks = 1 3 locks held by syzkaller616973/4074: #0: (&mm->mmap_sem){++++}, at: [<00000000bfce669e>] __do_page_fault+0x32d/0xc90 arch/x86/mm/fault.c:1355 #1: ((&im->timer)){+.-.}, at: [<00000000619d2f71>] lockdep_copy_map include/linux/lockdep.h:178 [inline] #1: ((&im->timer)){+.-.}, at: [<00000000619d2f71>] call_timer_fn+0x1c6/0x820 kernel/time/timer.c:1316 #2: (&(&im->lock)->rlock){+.-.}, at: [<000000005f833c5c>] spin_lock_bh include/linux/spinlock.h:315 [inline] #2: (&(&im->lock)->rlock){+.-.}, at: [<000000005f833c5c>] igmpv3_send_report+0x98/0x5b0 net/ipv4/igmp.c:600 stack backtrace: CPU: 0 PID: 4074 Comm: syzkaller616973 Not tainted 4.15.0+ #200 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:17 [inline] dump_stack+0x194/0x257 lib/dump_stack.c:53 lockdep_rcu_suspicious+0x123/0x170 kernel/locking/lockdep.c:4592 __in_dev_get_rcu include/linux/inetdevice.h:216 [inline] igmpv3_get_srcaddr net/ipv4/igmp.c:329 [inline] igmpv3_newpack+0xeef/0x12e0 net/ipv4/igmp.c:389 add_grhead.isra.27+0x235/0x300 net/ipv4/igmp.c:432 add_grec+0xbd3/0x1170 net/ipv4/igmp.c:565 igmpv3_send_report+0xd5/0x5b0 net/ipv4/igmp.c:605 igmp_send_report+0xc43/0x1050 net/ipv4/igmp.c:722 igmp_timer_expire+0x322/0x5c0 net/ipv4/igmp.c:831 call_timer_fn+0x228/0x820 kernel/time/timer.c:1326 expire_timers kernel/time/timer.c:1363 [inline] __run_timers+0x7ee/0xb70 kernel/time/timer.c:1666 run_timer_softirq+0x4c/0x70 kernel/time/timer.c:1692 __do_softirq+0x2d7/0xb85 kernel/softirq.c:285 invoke_softirq kernel/softirq.c:365 [inline] irq_exit+0x1cc/0x200 kernel/softirq.c:405 exiting_irq arch/x86/include/asm/apic.h:541 [inline] smp_apic_timer_interrupt+0x16b/0x700 arch/x86/kernel/apic/apic.c:1052 apic_timer_interrupt+0xa9/0xb0 arch/x86/entry/entry_64.S:938 Fixes: a46182b00290 ("net: igmp: Use correct source address on IGMPv3 reports") Signed-off-by: Eric Dumazet Reported-by: syzbot Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/igmp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 642dbbde93e4..4fd94cfb7f24 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -384,7 +384,11 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) pip->frag_off = htons(IP_DF); pip->ttl = 1; pip->daddr = fl4.daddr; + + rcu_read_lock(); pip->saddr = igmpv3_get_srcaddr(dev, &fl4); + rcu_read_unlock(); + pip->protocol = IPPROTO_IGMP; pip->tot_len = 0; /* filled in later */ ip_select_ident(skb, NULL); -- GitLab From cafbcd523a70712ed3215f5a549d8938d00c3b7e Mon Sep 17 00:00:00 2001 From: Junxiao Bi Date: Mon, 29 Jan 2018 17:53:42 +0800 Subject: [PATCH 2641/5498] qlcnic: fix deadlock bug [ Upstream commit 233ac3891607f501f08879134d623b303838f478 ] The following soft lockup was caught. This is a deadlock caused by recusive locking. Process kworker/u40:1:28016 was holding spin lock "mbx->queue_lock" in qlcnic_83xx_mailbox_worker(), while a softirq came in and ask the same spin lock in qlcnic_83xx_enqueue_mbx_cmd(). This lock should be hold by disable bh.. [161846.962125] NMI watchdog: BUG: soft lockup - CPU#1 stuck for 22s! [kworker/u40:1:28016] [161846.962367] Modules linked in: tun ocfs2 xen_netback xen_blkback xen_gntalloc xen_gntdev xen_evtchn xenfs xen_privcmd autofs4 ocfs2_dlmfs ocfs2_stack_o2cb ocfs2_dlm ocfs2_nodemanager ocfs2_stackglue configfs bnx2fc fcoe libfcoe libfc sunrpc 8021q mrp garp bridge stp llc bonding dm_round_robin dm_multipath iTCO_wdt iTCO_vendor_support pcspkr sb_edac edac_core i2c_i801 shpchp lpc_ich mfd_core ioatdma ipmi_devintf ipmi_si ipmi_msghandler sg ext4 jbd2 mbcache2 sr_mod cdrom sd_mod igb i2c_algo_bit i2c_core ahci libahci megaraid_sas ixgbe dca ptp pps_core vxlan udp_tunnel ip6_udp_tunnel qla2xxx scsi_transport_fc qlcnic crc32c_intel be2iscsi bnx2i cnic uio cxgb4i cxgb4 cxgb3i libcxgbi ipv6 cxgb3 mdio libiscsi_tcp qla4xxx iscsi_boot_sysfs libiscsi scsi_transport_iscsi dm_mirror dm_region_hash dm_log dm_mod [161846.962454] [161846.962460] CPU: 1 PID: 28016 Comm: kworker/u40:1 Not tainted 4.1.12-94.5.9.el6uek.x86_64 #2 [161846.962463] Hardware name: Oracle Corporation SUN SERVER X4-2L /ASSY,MB,X4-2L , BIOS 26050100 09/19/2017 [161846.962489] Workqueue: qlcnic_mailbox qlcnic_83xx_mailbox_worker [qlcnic] [161846.962493] task: ffff8801f2e34600 ti: ffff88004ca5c000 task.ti: ffff88004ca5c000 [161846.962496] RIP: e030:[] [] xen_hypercall_sched_op+0xa/0x20 [161846.962506] RSP: e02b:ffff880202e43388 EFLAGS: 00000206 [161846.962509] RAX: 0000000000000000 RBX: ffff8801f6996b70 RCX: ffffffff810013aa [161846.962511] RDX: ffff880202e433cc RSI: ffff880202e433b0 RDI: 0000000000000003 [161846.962513] RBP: ffff880202e433d0 R08: 0000000000000000 R09: ffff8801fe893200 [161846.962516] R10: ffff8801fe400538 R11: 0000000000000206 R12: ffff880202e4b000 [161846.962518] R13: 0000000000000050 R14: 0000000000000001 R15: 000000000000020d [161846.962528] FS: 0000000000000000(0000) GS:ffff880202e40000(0000) knlGS:ffff880202e40000 [161846.962531] CS: e033 DS: 0000 ES: 0000 CR0: 0000000080050033 [161846.962533] CR2: 0000000002612640 CR3: 00000001bb796000 CR4: 0000000000042660 [161846.962536] Stack: [161846.962538] ffff880202e43608 0000000000000000 ffffffff813f0442 ffff880202e433b0 [161846.962543] 0000000000000000 ffff880202e433cc ffffffff00000001 0000000000000000 [161846.962547] 00000009813f03d6 ffff880202e433e0 ffffffff813f0460 ffff880202e43440 [161846.962552] Call Trace: [161846.962555] [161846.962565] [] ? xen_poll_irq_timeout+0x42/0x50 [161846.962570] [] xen_poll_irq+0x10/0x20 [161846.962578] [] xen_lock_spinning+0xe2/0x110 [161846.962583] [] __raw_callee_save_xen_lock_spinning+0x11/0x20 [161846.962592] [] ? _raw_spin_lock+0x57/0x80 [161846.962609] [] qlcnic_83xx_enqueue_mbx_cmd+0x7c/0xe0 [qlcnic] [161846.962623] [] qlcnic_83xx_issue_cmd+0x58/0x210 [qlcnic] [161846.962636] [] qlcnic_83xx_sre_macaddr_change+0x162/0x1d0 [qlcnic] [161846.962649] [] qlcnic_83xx_change_l2_filter+0x2b/0x30 [qlcnic] [161846.962657] [] ? __skb_flow_dissect+0x18b/0x650 [161846.962670] [] qlcnic_send_filter+0x205/0x250 [qlcnic] [161846.962682] [] qlcnic_xmit_frame+0x547/0x7b0 [qlcnic] [161846.962691] [] xmit_one+0x82/0x1a0 [161846.962696] [] dev_hard_start_xmit+0x50/0xa0 [161846.962701] [] sch_direct_xmit+0x112/0x220 [161846.962706] [] __dev_queue_xmit+0x1df/0x5e0 [161846.962710] [] dev_queue_xmit_sk+0x13/0x20 [161846.962721] [] bond_dev_queue_xmit+0x35/0x80 [bonding] [161846.962729] [] __bond_start_xmit+0x1cb/0x210 [bonding] [161846.962736] [] bond_start_xmit+0x31/0x60 [bonding] [161846.962740] [] xmit_one+0x82/0x1a0 [161846.962745] [] dev_hard_start_xmit+0x50/0xa0 [161846.962749] [] __dev_queue_xmit+0x4ee/0x5e0 [161846.962754] [] dev_queue_xmit_sk+0x13/0x20 [161846.962760] [] vlan_dev_hard_start_xmit+0xb2/0x150 [8021q] [161846.962764] [] xmit_one+0x82/0x1a0 [161846.962769] [] dev_hard_start_xmit+0x50/0xa0 [161846.962773] [] __dev_queue_xmit+0x4ee/0x5e0 [161846.962777] [] dev_queue_xmit_sk+0x13/0x20 [161846.962789] [] br_dev_queue_push_xmit+0x54/0xa0 [bridge] [161846.962797] [] br_forward_finish+0x2f/0x90 [bridge] [161846.962807] [] ? ttwu_do_wakeup+0x1d/0x100 [161846.962811] [] ? __alloc_skb+0x8b/0x1f0 [161846.962818] [] __br_forward+0x8d/0x120 [bridge] [161846.962822] [] ? __kmalloc_reserve+0x3b/0xa0 [161846.962829] [] ? update_rq_runnable_avg+0xee/0x230 [161846.962836] [] br_forward+0x96/0xb0 [bridge] [161846.962845] [] br_handle_frame_finish+0x1ae/0x420 [bridge] [161846.962853] [] br_handle_frame+0x17f/0x260 [bridge] [161846.962862] [] ? br_handle_frame_finish+0x420/0x420 [bridge] [161846.962867] [] __netif_receive_skb_core+0x1f7/0x870 [161846.962872] [] __netif_receive_skb+0x22/0x70 [161846.962877] [] netif_receive_skb_internal+0x23/0x90 [161846.962884] [] ? xenvif_idx_release+0xea/0x100 [xen_netback] [161846.962889] [] ? _raw_spin_unlock_irqrestore+0x20/0x50 [161846.962893] [] netif_receive_skb_sk+0x24/0x90 [161846.962899] [] xenvif_tx_submit+0x2ca/0x3f0 [xen_netback] [161846.962906] [] xenvif_tx_action+0x9c/0xd0 [xen_netback] [161846.962915] [] xenvif_poll+0x35/0x70 [xen_netback] [161846.962920] [] napi_poll+0xcb/0x1e0 [161846.962925] [] net_rx_action+0x90/0x1c0 [161846.962931] [] __do_softirq+0x10a/0x350 [161846.962938] [] irq_exit+0x125/0x130 [161846.962943] [] xen_evtchn_do_upcall+0x39/0x50 [161846.962950] [] xen_do_hypervisor_callback+0x1e/0x40 [161846.962952] [161846.962959] [] ? _raw_spin_lock+0x4a/0x80 [161846.962964] [] ? _raw_spin_lock_irqsave+0x1e/0xa0 [161846.962978] [] ? qlcnic_83xx_mailbox_worker+0xb9/0x2a0 [qlcnic] [161846.962991] [] ? process_one_work+0x151/0x4b0 [161846.962995] [] ? check_events+0x12/0x20 [161846.963001] [] ? worker_thread+0x120/0x480 [161846.963005] [] ? __schedule+0x30b/0x890 [161846.963010] [] ? process_one_work+0x4b0/0x4b0 [161846.963015] [] ? process_one_work+0x4b0/0x4b0 [161846.963021] [] ? kthread+0xce/0xf0 [161846.963025] [] ? kthread_freezable_should_stop+0x70/0x70 [161846.963031] [] ? ret_from_fork+0x42/0x70 [161846.963035] [] ? kthread_freezable_should_stop+0x70/0x70 [161846.963037] Code: cc 51 41 53 b8 1c 00 00 00 0f 05 41 5b 59 c3 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 51 41 53 b8 1d 00 00 00 0f 05 <41> 5b 59 c3 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc Signed-off-by: Junxiao Bi Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- .../ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index dd618d7ed257..1c40c524f0c8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -3825,7 +3825,7 @@ static void qlcnic_83xx_flush_mbx_queue(struct qlcnic_adapter *adapter) struct list_head *head = &mbx->cmd_q; struct qlcnic_cmd_args *cmd = NULL; - spin_lock(&mbx->queue_lock); + spin_lock_bh(&mbx->queue_lock); while (!list_empty(head)) { cmd = list_entry(head->next, struct qlcnic_cmd_args, list); @@ -3836,7 +3836,7 @@ static void qlcnic_83xx_flush_mbx_queue(struct qlcnic_adapter *adapter) qlcnic_83xx_notify_cmd_completion(adapter, cmd); } - spin_unlock(&mbx->queue_lock); + spin_unlock_bh(&mbx->queue_lock); } static int qlcnic_83xx_check_mbx_status(struct qlcnic_adapter *adapter) @@ -3872,12 +3872,12 @@ static void qlcnic_83xx_dequeue_mbx_cmd(struct qlcnic_adapter *adapter, { struct qlcnic_mailbox *mbx = adapter->ahw->mailbox; - spin_lock(&mbx->queue_lock); + spin_lock_bh(&mbx->queue_lock); list_del(&cmd->list); mbx->num_cmds--; - spin_unlock(&mbx->queue_lock); + spin_unlock_bh(&mbx->queue_lock); qlcnic_83xx_notify_cmd_completion(adapter, cmd); } @@ -3942,7 +3942,7 @@ static int qlcnic_83xx_enqueue_mbx_cmd(struct qlcnic_adapter *adapter, init_completion(&cmd->completion); cmd->rsp_opcode = QLC_83XX_MBX_RESPONSE_UNKNOWN; - spin_lock(&mbx->queue_lock); + spin_lock_bh(&mbx->queue_lock); list_add_tail(&cmd->list, &mbx->cmd_q); mbx->num_cmds++; @@ -3950,7 +3950,7 @@ static int qlcnic_83xx_enqueue_mbx_cmd(struct qlcnic_adapter *adapter, *timeout = cmd->total_cmds * QLC_83XX_MBX_TIMEOUT; queue_work(mbx->work_q, &mbx->work); - spin_unlock(&mbx->queue_lock); + spin_unlock_bh(&mbx->queue_lock); return 0; } @@ -4046,15 +4046,15 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work) mbx->rsp_status = QLC_83XX_MBX_RESPONSE_WAIT; spin_unlock_irqrestore(&mbx->aen_lock, flags); - spin_lock(&mbx->queue_lock); + spin_lock_bh(&mbx->queue_lock); if (list_empty(head)) { - spin_unlock(&mbx->queue_lock); + spin_unlock_bh(&mbx->queue_lock); return; } cmd = list_entry(head->next, struct qlcnic_cmd_args, list); - spin_unlock(&mbx->queue_lock); + spin_unlock_bh(&mbx->queue_lock); mbx_ops->encode_cmd(adapter, cmd); mbx_ops->nofity_fw(adapter, QLC_83XX_MBX_REQUEST); -- GitLab From 0d8e72014eca79abcf9424be37023dae97141093 Mon Sep 17 00:00:00 2001 From: Chunhao Lin Date: Wed, 31 Jan 2018 01:32:36 +0800 Subject: [PATCH 2642/5498] r8169: fix RTL8168EP take too long to complete driver initialization. [ Upstream commit 086ca23d03c0d2f4088f472386778d293e15c5f6 ] Driver check the wrong register bit in rtl_ocp_tx_cond() that keep driver waiting until timeout. Fix this by waiting for the right register bit. Signed-off-by: Chunhao Lin Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/realtek/r8169.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index e95cb1faf43e..6e180db19c18 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -1375,7 +1375,7 @@ DECLARE_RTL_COND(rtl_ocp_tx_cond) { void __iomem *ioaddr = tp->mmio_addr; - return RTL_R8(IBISR0) & 0x02; + return RTL_R8(IBISR0) & 0x20; } static void rtl8168dp_driver_start(struct rtl8169_private *tp) @@ -1421,7 +1421,7 @@ static void rtl8168ep_driver_stop(struct rtl8169_private *tp) void __iomem *ioaddr = tp->mmio_addr; RTL_W8(IBCR2, RTL_R8(IBCR2) & ~0x01); - rtl_msleep_loop_wait_low(tp, &rtl_ocp_tx_cond, 50, 2000); + rtl_msleep_loop_wait_high(tp, &rtl_ocp_tx_cond, 50, 2000); RTL_W8(IBISR0, RTL_R8(IBISR0) | 0x20); RTL_W8(IBCR0, RTL_R8(IBCR0) & ~0x01); ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP); -- GitLab From e25c5a37fcde7688ddfd5454328cde2ddf70fb57 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 26 Jan 2018 16:40:41 +0800 Subject: [PATCH 2643/5498] tcp: release sk_frag.page in tcp_disconnect [ Upstream commit 9b42d55a66d388e4dd5550107df051a9637564fc ] socket can be disconnected and gets transformed back to a listening socket, if sk_frag.page is not released, which will be cloned into a new socket by sk_clone_lock, but the reference count of this page is increased, lead to a use after free or double free issue Signed-off-by: Li RongQing Cc: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 357d6b3caa84..77ffe0dbed6c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2280,6 +2280,12 @@ int tcp_disconnect(struct sock *sk, int flags) WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); + if (sk->sk_frag.page) { + put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + sk->sk_frag.offset = 0; + } + sk->sk_error_report(sk); return err; } -- GitLab From 60a86dc311258a3f10451884654397446ef931ad Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 27 Mar 2015 01:50:16 +0900 Subject: [PATCH 2644/5498] ARM: exynos_defconfig: Enable options to mount a rootfs via NFS commit 19f79ccf6d77409cd138bce8db206cdac7fd5ea7 upstream. This patch enables the options to mount a rootfs over NFS and also support for automatic configuration of IP addresses during boot as needed by NFS. Signed-off-by: Javier Martinez Canillas Signed-off-by: Kukjin Kim Signed-off-by: Guillaume Tucker Reviewed-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- arch/arm/configs/exynos_defconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index e21ef830a483..e94402c05975 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -33,6 +33,10 @@ CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_NET_KEY=y CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y CONFIG_RFKILL_REGULATOR=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y @@ -170,6 +174,8 @@ CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_CRAMFS=y CONFIG_ROMFS_FS=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y -- GitLab From 1b50c4e9e71a89a3b573eaee82669d50578ca74e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 25 Nov 2015 13:09:43 +0900 Subject: [PATCH 2645/5498] ARM: exynos_defconfig: Enable NFSv4 client commit 1c1fb9b0c89a2506e556114c813a606bc1508d49 upstream. NFS client is already enabled (NFS_FS) and by default it enables clients for version 2 and 3. Enable explicitly the version 4 client to utilize the newer protocol. The NFS client is especially useful for testing kernel in automated environments (network boot with network file system). Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas Signed-off-by: Guillaume Tucker Reviewed-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- arch/arm/configs/exynos_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index e94402c05975..8cd6eae4e89b 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -175,6 +175,7 @@ CONFIG_TMPFS_POSIX_ACL=y CONFIG_CRAMFS=y CONFIG_ROMFS_FS=y CONFIG_NFS_FS=y +CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y -- GitLab From 99a223bcec506dbd8de7aa3c16995b71d3ca7bb6 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 8 Jun 2017 14:48:18 +0100 Subject: [PATCH 2646/5498] KEYS: encrypted: fix buffer overread in valid_master_desc() commit 794b4bc292f5d31739d89c0202c54e7dc9bc3add upstream. With the 'encrypted' key type it was possible for userspace to provide a data blob ending with a master key description shorter than expected, e.g. 'keyctl add encrypted desc "new x" @s'. When validating such a master key description, validate_master_desc() could read beyond the end of the buffer. Fix this by using strncmp() instead of memcmp(). [Also clean up the code to deduplicate some logic.] Cc: Mimi Zohar Signed-off-by: Eric Biggers Signed-off-by: David Howells Signed-off-by: James Morris Signed-off-by: Jin Qian Signed-off-by: Greg Kroah-Hartman --- security/keys/encrypted-keys/encrypted.c | 31 ++++++++++++------------ 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 89d5695c51cd..20251ee5c491 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -141,23 +141,22 @@ static int valid_ecryptfs_desc(const char *ecryptfs_desc) */ static int valid_master_desc(const char *new_desc, const char *orig_desc) { - if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) { - if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN) - goto out; - if (orig_desc) - if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN)) - goto out; - } else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) { - if (strlen(new_desc) == KEY_USER_PREFIX_LEN) - goto out; - if (orig_desc) - if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN)) - goto out; - } else - goto out; + int prefix_len; + + if (!strncmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) + prefix_len = KEY_TRUSTED_PREFIX_LEN; + else if (!strncmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) + prefix_len = KEY_USER_PREFIX_LEN; + else + return -EINVAL; + + if (!new_desc[prefix_len]) + return -EINVAL; + + if (orig_desc && strncmp(new_desc, orig_desc, prefix_len)) + return -EINVAL; + return 0; -out: - return -EINVAL; } /* -- GitLab From 796ab952d504dc938bbc331fa648a21ae96f6817 Mon Sep 17 00:00:00 2001 From: Wang Han Date: Fri, 2 Feb 2018 23:06:51 +0800 Subject: [PATCH 2647/5498] ipv4: Map neigh lookup keys in __ipv4_neigh_lookup_noref() Commit 6c16fa957e84 is an incorrect backport as we map the keys in struct __ipv4_neigh_lookup(), but the correct place to add the code is struct __ipv4_neigh_lookup_noref(), compared to upstream. Fix it by moving the code, or fewer cases will be covered as __ipv4_neigh_lookup_noref() will be called unconditionally from __ipv4_neigh_lookup(), and it can be called from other places such as ip_output.c. Fixes: 6c16fa957e84 (ipv4: Make neigh lookup keys for loopback/point-to-point devices be INADDR_ANY) Signed-off-by: Wang Han Signed-off-by: Greg Kroah-Hartman --- include/net/arp.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/net/arp.h b/include/net/arp.h index 174014585ade..5d8c7990582f 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -22,6 +22,9 @@ static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev struct neighbour *n; u32 hash_val; + if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + key = INADDR_ANY; + hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift); for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); n != NULL; @@ -37,9 +40,6 @@ static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 { struct neighbour *n; - if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) - key = INADDR_ANY; - rcu_read_lock_bh(); n = __ipv4_neigh_lookup_noref(dev, key); if (n && !atomic_inc_not_zero(&n->refcnt)) -- GitLab From 23dd6b8717fb32fd68be8ab6cd76743e298e4100 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 15 Dec 2017 12:48:32 -0800 Subject: [PATCH 2648/5498] cifs: Fix missing put_xid in cifs_file_strict_mmap commit f04a703c3d613845ae3141bfaf223489de8ab3eb upstream. If cifs_zap_mapping() returned an error, we would return without putting the xid that we got earlier. Restructure cifs_file_strict_mmap() and cifs_file_mmap() to be more similar to each other and have a single point of return that always puts the xid. Signed-off-by: Matthew Wilcox Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/file.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 237c201d6d3e..855ad5e6bd22 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -3261,20 +3261,18 @@ static struct vm_operations_struct cifs_file_vm_ops = { int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) { - int rc, xid; + int xid, rc = 0; struct inode *inode = file_inode(file); xid = get_xid(); - if (!CIFS_CACHE_READ(CIFS_I(inode))) { + if (!CIFS_CACHE_READ(CIFS_I(inode))) rc = cifs_zap_mapping(inode); - if (rc) - return rc; - } - - rc = generic_file_mmap(file, vma); - if (rc == 0) + if (!rc) + rc = generic_file_mmap(file, vma); + if (!rc) vma->vm_ops = &cifs_file_vm_ops; + free_xid(xid); return rc; } @@ -3284,16 +3282,16 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) int rc, xid; xid = get_xid(); + rc = cifs_revalidate_file(file); - if (rc) { + if (rc) cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n", rc); - free_xid(xid); - return rc; - } - rc = generic_file_mmap(file, vma); - if (rc == 0) + if (!rc) + rc = generic_file_mmap(file, vma); + if (!rc) vma->vm_ops = &cifs_file_vm_ops; + free_xid(xid); return rc; } -- GitLab From 833692a735399fc52942efc1ba3e46fd6d026f72 Mon Sep 17 00:00:00 2001 From: Daniel N Pettersson Date: Thu, 11 Jan 2018 16:00:12 +0100 Subject: [PATCH 2649/5498] cifs: Fix autonegotiate security settings mismatch commit 9aca7e454415f7878b28524e76bebe1170911a88 upstream. Autonegotiation gives a security settings mismatch error if the SMB server selects an SMBv3 dialect that isn't SMB3.02. The exact error is "protocol revalidation - security settings mismatch". This can be tested using Samba v4.2 or by setting the global Samba setting max protocol = SMB3_00. The check that fails in smb3_validate_negotiate is the dialect verification of the negotiate info response. This is because it tries to verify against the protocol_id in the global smbdefault_values. The protocol_id in smbdefault_values is SMB3.02. In SMB2_negotiate the protocol_id in smbdefault_values isn't updated, it is global so it probably shouldn't be, but server->dialect is. This patch changes the check in smb3_validate_negotiate to use server->dialect instead of server->vals->protocol_id. The patch works with autonegotiate and when using a specific version in the vers mount option. Signed-off-by: Daniel N Pettersson Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/smb2pdu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index fc5809d494ed..c147cc58056f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -507,8 +507,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } /* check validate negotiate info response matches what we got earlier */ - if (pneg_rsp->Dialect != - cpu_to_le16(tcon->ses->server->vals->protocol_id)) + if (pneg_rsp->Dialect != cpu_to_le16(tcon->ses->server->dialect)) goto vneg_out; if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) -- GitLab From 9fdfac74246eeab058283035f393fc2a1bf44b78 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Thu, 25 Jan 2018 15:59:39 +0100 Subject: [PATCH 2650/5498] CIFS: zero sensitive data when freeing commit 97f4b7276b829a8927ac903a119bef2f963ccc58 upstream. also replaces memset()+kfree() by kzfree(). Signed-off-by: Aurelien Aptel Signed-off-by: Steve French Reviewed-by: Pavel Shilovsky Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsencrypt.c | 3 +-- fs/cifs/connect.c | 6 +++--- fs/cifs/misc.c | 14 ++++---------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 78404806a7f6..58f36037c278 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -303,9 +303,8 @@ int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, { int i; int rc; - char password_with_pad[CIFS_ENCPWD_SIZE]; + char password_with_pad[CIFS_ENCPWD_SIZE] = {0}; - memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); if (password) strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 43bb9e2c81a5..7d4c2bf2fea2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1650,7 +1650,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, tmp_end++; if (!(tmp_end < end && tmp_end[1] == delim)) { /* No it is not. Set the password to NULL */ - kfree(vol->password); + kzfree(vol->password); vol->password = NULL; break; } @@ -1688,7 +1688,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, options = end; } - kfree(vol->password); + kzfree(vol->password); /* Now build new password string */ temp_len = strlen(value); vol->password = kzalloc(temp_len+1, GFP_KERNEL); @@ -4046,7 +4046,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) reset_cifs_unix_caps(0, tcon, NULL, vol_info); out: kfree(vol_info->username); - kfree(vol_info->password); + kzfree(vol_info->password); kfree(vol_info); return tcon; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 337946355b29..eedbc34e19db 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -99,14 +99,11 @@ sesInfoFree(struct cifs_ses *buf_to_free) kfree(buf_to_free->serverOS); kfree(buf_to_free->serverDomain); kfree(buf_to_free->serverNOS); - if (buf_to_free->password) { - memset(buf_to_free->password, 0, strlen(buf_to_free->password)); - kfree(buf_to_free->password); - } + kzfree(buf_to_free->password); kfree(buf_to_free->user_name); kfree(buf_to_free->domainName); - kfree(buf_to_free->auth_key.response); - kfree(buf_to_free); + kzfree(buf_to_free->auth_key.response); + kzfree(buf_to_free); } struct cifs_tcon * @@ -136,10 +133,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free) } atomic_dec(&tconInfoAllocCount); kfree(buf_to_free->nativeFileSystem); - if (buf_to_free->password) { - memset(buf_to_free->password, 0, strlen(buf_to_free->password)); - kfree(buf_to_free->password); - } + kzfree(buf_to_free->password); kfree(buf_to_free); } -- GitLab From 28ef9653c18539f8123dd668ad3b28289ec0514a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 15 Dec 2017 10:32:03 +0100 Subject: [PATCH 2651/5498] posix-timer: Properly check sigevent->sigev_notify commit cef31d9af908243421258f1df35a4a644604efbe upstream. timer_create() specifies via sigevent->sigev_notify the signal delivery for the new timer. The valid modes are SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD and (SIGEV_SIGNAL | SIGEV_THREAD_ID). The sanity check in good_sigevent() is only checking the valid combination for the SIGEV_THREAD_ID bit, i.e. SIGEV_SIGNAL, but if SIGEV_THREAD_ID is not set it accepts any random value. This has no real effects on the posix timer and signal delivery code, but it affects show_timer() which handles the output of /proc/$PID/timers. That function uses a string array to pretty print sigev_notify. The access to that array has no bound checks, so random sigev_notify cause access beyond the array bounds. Add proper checks for the valid notify modes and remove the SIGEV_THREAD_ID masking from various code pathes as SIGEV_NONE can never be set in combination with SIGEV_THREAD_ID. Reported-by: Eric Biggers Reported-by: Dmitry Vyukov Reported-by: Alexey Dobriyan Signed-off-by: Thomas Gleixner Cc: John Stultz Signed-off-by: Greg Kroah-Hartman --- kernel/time/posix-timers.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 31ea01f42e1f..2cca2e79c643 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -500,17 +500,22 @@ static struct pid *good_sigevent(sigevent_t * event) { struct task_struct *rtn = current->group_leader; - if ((event->sigev_notify & SIGEV_THREAD_ID ) && - (!(rtn = find_task_by_vpid(event->sigev_notify_thread_id)) || - !same_thread_group(rtn, current) || - (event->sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_SIGNAL)) + switch (event->sigev_notify) { + case SIGEV_SIGNAL | SIGEV_THREAD_ID: + rtn = find_task_by_vpid(event->sigev_notify_thread_id); + if (!rtn || !same_thread_group(rtn, current)) + return NULL; + /* FALLTHRU */ + case SIGEV_SIGNAL: + case SIGEV_THREAD: + if (event->sigev_signo <= 0 || event->sigev_signo > SIGRTMAX) + return NULL; + /* FALLTHRU */ + case SIGEV_NONE: + return task_pid(rtn); + default: return NULL; - - if (((event->sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE) && - ((event->sigev_signo <= 0) || (event->sigev_signo > SIGRTMAX))) - return NULL; - - return task_pid(rtn); + } } void posix_timers_register_clock(const clockid_t clock_id, @@ -738,8 +743,7 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) /* interval timer ? */ if (iv.tv64) cur_setting->it_interval = ktime_to_timespec(iv); - else if (!hrtimer_active(timer) && - (timr->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE) + else if (!hrtimer_active(timer) && timr->it_sigev_notify != SIGEV_NONE) return; now = timer->base->get_time(); @@ -750,7 +754,7 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) * expiry is > now. */ if (iv.tv64 && (timr->it_requeue_pending & REQUEUE_PENDING || - (timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE)) + timr->it_sigev_notify == SIGEV_NONE)) timr->it_overrun += (unsigned int) hrtimer_forward(timer, now, iv); remaining = ktime_sub(hrtimer_get_expires(timer), now); @@ -760,7 +764,7 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) * A single shot SIGEV_NONE timer must return 0, when * it is expired ! */ - if ((timr->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE) + if (timr->it_sigev_notify != SIGEV_NONE) cur_setting->it_value.tv_nsec = 1; } else cur_setting->it_value = ktime_to_timespec(remaining); @@ -858,7 +862,7 @@ common_timer_set(struct k_itimer *timr, int flags, timr->it.real.interval = timespec_to_ktime(new_setting->it_interval); /* SIGEV_NONE timers are not queued ! See common_timer_get */ - if (((timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE)) { + if (timr->it_sigev_notify == SIGEV_NONE) { /* Setup correct expiry time for relative timers */ if (mode == HRTIMER_MODE_REL) { hrtimer_add_expires(timer, timer->base->get_time()); -- GitLab From 385e124b45fe46960de2fd280fc5a99d3e7706dc Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 7 Dec 2017 14:16:47 -0700 Subject: [PATCH 2652/5498] usbip: fix stub_rx: get_pipe() to validate endpoint number commit 635f545a7e8be7596b9b2b6a43cab6bbd5a88e43 upstream. get_pipe() routine doesn't validate the input endpoint number and uses to reference ep_in and ep_out arrays. Invalid endpoint number can trigger BUG(). Range check the epnum and returning error instead of calling BUG(). Change caller stub_recv_cmd_submit() to handle the get_pipe() error return. Reported-by: Secunia Research Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_rx.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 00e475c51a12..2e07acda456e 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -347,15 +347,15 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) struct usb_host_endpoint *ep; struct usb_endpoint_descriptor *epd = NULL; + if (epnum < 0 || epnum > 15) + goto err_ret; + if (dir == USBIP_DIR_IN) ep = udev->ep_in[epnum & 0x7f]; else ep = udev->ep_out[epnum & 0x7f]; - if (!ep) { - dev_err(&sdev->interface->dev, "no such endpoint?, %d\n", - epnum); - BUG(); - } + if (!ep) + goto err_ret; epd = &ep->desc; if (usb_endpoint_xfer_control(epd)) { @@ -386,9 +386,10 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) return usb_rcvisocpipe(udev, epnum); } +err_ret: /* NOT REACHED */ dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); - return 0; + return -1; } static void masking_bogus_flags(struct urb *urb) @@ -454,6 +455,9 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, struct usb_device *udev = sdev->udev; int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + if (pipe == -1) + return; + priv = stub_priv_alloc(sdev, pdu); if (!priv) return; -- GitLab From eebf31529012289ec20fea84e4e6fd188176be13 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 7 Dec 2017 14:16:48 -0700 Subject: [PATCH 2653/5498] usbip: fix stub_rx: harden CMD_SUBMIT path to handle malicious input commit c6688ef9f29762e65bce325ef4acd6c675806366 upstream. Harden CMD_SUBMIT path to handle malicious input that could trigger large memory allocations. Add checks to validate transfer_buffer_length and number_of_packets to protect against bad input requesting for unbounded memory allocations. Validate early in get_pipe() and return failure. Reported-by: Secunia Research Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_rx.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 2e07acda456e..f5533c99cd48 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -341,11 +341,13 @@ static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, return priv; } -static int get_pipe(struct stub_device *sdev, int epnum, int dir) +static int get_pipe(struct stub_device *sdev, struct usbip_header *pdu) { struct usb_device *udev = sdev->udev; struct usb_host_endpoint *ep; struct usb_endpoint_descriptor *epd = NULL; + int epnum = pdu->base.ep; + int dir = pdu->base.direction; if (epnum < 0 || epnum > 15) goto err_ret; @@ -358,6 +360,7 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) goto err_ret; epd = &ep->desc; + if (usb_endpoint_xfer_control(epd)) { if (dir == USBIP_DIR_OUT) return usb_sndctrlpipe(udev, epnum); @@ -380,6 +383,27 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) } if (usb_endpoint_xfer_isoc(epd)) { + /* validate packet size and number of packets */ + unsigned int maxp, packets, bytes; + +#define USB_EP_MAXP_MULT_SHIFT 11 +#define USB_EP_MAXP_MULT_MASK (3 << USB_EP_MAXP_MULT_SHIFT) +#define USB_EP_MAXP_MULT(m) \ + (((m) & USB_EP_MAXP_MULT_MASK) >> USB_EP_MAXP_MULT_SHIFT) + + maxp = usb_endpoint_maxp(epd); + maxp *= (USB_EP_MAXP_MULT( + __le16_to_cpu(epd->wMaxPacketSize)) + 1); + bytes = pdu->u.cmd_submit.transfer_buffer_length; + packets = DIV_ROUND_UP(bytes, maxp); + + if (pdu->u.cmd_submit.number_of_packets < 0 || + pdu->u.cmd_submit.number_of_packets > packets) { + dev_err(&sdev->udev->dev, + "CMD_SUBMIT: isoc invalid num packets %d\n", + pdu->u.cmd_submit.number_of_packets); + return -1; + } if (dir == USBIP_DIR_OUT) return usb_sndisocpipe(udev, epnum); else @@ -388,7 +412,7 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir) err_ret: /* NOT REACHED */ - dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); + dev_err(&sdev->udev->dev, "CMD_SUBMIT: invalid epnum %d\n", epnum); return -1; } @@ -453,7 +477,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, struct stub_priv *priv; struct usbip_device *ud = &sdev->ud; struct usb_device *udev = sdev->udev; - int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + int pipe = get_pipe(sdev, pdu); if (pipe == -1) return; -- GitLab From 67ad0235bacc1af72dad6eac6c5ac1a072b905f7 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 7 Dec 2017 14:16:49 -0700 Subject: [PATCH 2654/5498] usbip: prevent vhci_hcd driver from leaking a socket pointer address commit 2f2d0088eb93db5c649d2a5e34a3800a8a935fc5 upstream. When a client has a USB device attached over IP, the vhci_hcd driver is locally leaking a socket pointer address via the /sys/devices/platform/vhci_hcd/status file (world-readable) and in debug output when "usbip --debug port" is run. Fix it to not leak. The socket pointer address is not used at the moment and it was made visible as a convenient way to find IP address from socket pointer address by looking up /proc/net/{tcp,tcp6}. As this opens a security hole, the fix replaces socket pointer address with sockfd. Reported-by: Secunia Research Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/usbip_common.h | 1 + drivers/usb/usbip/vhci_sysfs.c | 26 +++++++++++++++----------- tools/usb/usbip/libsrc/vhci_driver.c | 8 ++++---- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 86b08475c254..f875ccaa55f9 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -261,6 +261,7 @@ struct usbip_device { /* lock for status */ spinlock_t lock; + int sockfd; struct socket *tcp_socket; struct task_struct *tcp_rx; diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 211f43f67ea2..f05f1e0a2baf 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -39,16 +39,20 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, /* * output example: - * prt sta spd dev socket local_busid - * 000 004 000 000 c5a7bb80 1-2.3 - * 001 004 000 000 d8cee980 2-3.4 + * prt sta spd dev sockfd local_busid + * 0000 004 000 00000000 000003 1-2.3 + * 0001 004 000 00000000 000004 2-3.4 * - * IP address can be retrieved from a socket pointer address by looking - * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a - * port number and its peer IP address. + * Output includes socket fd instead of socket pointer address to + * avoid leaking kernel memory address in: + * /sys/devices/platform/vhci_hcd.0/status and in debug output. + * The socket pointer address is not used at the moment and it was + * made visible as a convenient way to find IP address from socket + * pointer address by looking up /proc/net/{tcp,tcp6}. As this opens + * a security hole, the change is made to use sockfd instead. */ out += sprintf(out, - "prt sta spd bus dev socket local_busid\n"); + "prt sta spd dev sockfd local_busid\n"); for (i = 0; i < VHCI_NPORTS; i++) { struct vhci_device *vdev = port_to_vdev(i); @@ -59,12 +63,11 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, if (vdev->ud.status == VDEV_ST_USED) { out += sprintf(out, "%03u %08x ", vdev->speed, vdev->devid); - out += sprintf(out, "%16p ", vdev->ud.tcp_socket); + out += sprintf(out, "%06u ", vdev->ud.sockfd); out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); - } else { - out += sprintf(out, "000 000 000 0000000000000000 0-0"); - } + } else + out += sprintf(out, "000 00000000 000000 0-0"); out += sprintf(out, "\n"); spin_unlock(&vdev->ud.lock); @@ -223,6 +226,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, vdev->devid = devid; vdev->speed = speed; + vdev->ud.sockfd = sockfd; vdev->ud.tcp_socket = socket; vdev->ud.status = VDEV_ST_NOTASSIGNED; diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c index ad9204773533..1274f326242c 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.c +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -55,12 +55,12 @@ static int parse_status(const char *value) while (*c != '\0') { int port, status, speed, devid; - unsigned long socket; + int sockfd; char lbusid[SYSFS_BUS_ID_SIZE]; - ret = sscanf(c, "%d %d %d %x %lx %31s\n", + ret = sscanf(c, "%d %d %d %x %u %31s\n", &port, &status, &speed, - &devid, &socket, lbusid); + &devid, &sockfd, lbusid); if (ret < 5) { dbg("sscanf failed: %d", ret); @@ -69,7 +69,7 @@ static int parse_status(const char *value) dbg("port %d status %d speed %d devid %x", port, status, speed, devid); - dbg("socket %lx lbusid %s", socket, lbusid); + dbg("sockfd %u lbusid %s", sockfd, lbusid); /* if a device is connected, look at it */ -- GitLab From 4b490123f4996ccd4461cb82bcb69dd8a0364d5f Mon Sep 17 00:00:00 2001 From: Jonathan Dieter Date: Mon, 27 Feb 2017 10:31:03 +0200 Subject: [PATCH 2655/5498] usbip: Fix potential format overflow in userspace tools commit e5dfa3f902b9a642ae8c6997d57d7c41e384a90b upstream. The usbip userspace tools call sprintf()/snprintf() and don't check for the return value which can lead the paths to overflow, truncating the final file in the path. More urgently, GCC 7 now warns that these aren't checked with -Wformat-overflow, and with -Werror enabled in configure.ac, that makes these tools unbuildable. This patch fixes these problems by replacing sprintf() with snprintf() in one place and adding checks for the return value of snprintf(). Reviewed-by: Peter Senna Tschudin Signed-off-by: Jonathan Dieter Acked-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/libsrc/usbip_common.c | 9 +++++++- tools/usb/usbip/libsrc/usbip_host_driver.c | 27 ++++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c index ac73710473de..8000445ff884 100644 --- a/tools/usb/usbip/libsrc/usbip_common.c +++ b/tools/usb/usbip/libsrc/usbip_common.c @@ -215,9 +215,16 @@ int read_usb_interface(struct usbip_usb_device *udev, int i, struct usbip_usb_interface *uinf) { char busid[SYSFS_BUS_ID_SIZE]; + int size; struct udev_device *sif; - sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i); + size = snprintf(busid, sizeof(busid), "%s:%d.%d", + udev->busid, udev->bConfigurationValue, i); + if (size < 0 || (unsigned int)size >= sizeof(busid)) { + err("busid length %i >= %lu or < 0", size, + (unsigned long)sizeof(busid)); + return -1; + } sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid); if (!sif) { diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c index bef08d5c44e8..071b9ce99420 100644 --- a/tools/usb/usbip/libsrc/usbip_host_driver.c +++ b/tools/usb/usbip/libsrc/usbip_host_driver.c @@ -39,13 +39,19 @@ struct udev *udev_context; static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) { char status_attr_path[SYSFS_PATH_MAX]; + int size; int fd; int length; char status; int value = 0; - snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status", - udev->path); + size = snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status", + udev->path); + if (size < 0 || (unsigned int)size >= sizeof(status_attr_path)) { + err("usbip_status path length %i >= %lu or < 0", size, + (unsigned long)sizeof(status_attr_path)); + return -1; + } fd = open(status_attr_path, O_RDONLY); if (fd < 0) { @@ -225,6 +231,7 @@ int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd) { char attr_name[] = "usbip_sockfd"; char sockfd_attr_path[SYSFS_PATH_MAX]; + int size; char sockfd_buff[30]; int ret; @@ -244,10 +251,20 @@ int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd) } /* only the first interface is true */ - snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", - edev->udev.path, attr_name); + size = snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", + edev->udev.path, attr_name); + if (size < 0 || (unsigned int)size >= sizeof(sockfd_attr_path)) { + err("exported device path length %i >= %lu or < 0", size, + (unsigned long)sizeof(sockfd_attr_path)); + return -1; + } - snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); + size = snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); + if (size < 0 || (unsigned int)size >= sizeof(sockfd_buff)) { + err("socket length %i >= %lu or < 0", size, + (unsigned long)sizeof(sockfd_buff)); + return -1; + } ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, strlen(sockfd_buff)); -- GitLab From 9ea2031f4d16771c47c97c99fb47c71f346a6dbe Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Tue, 2 Feb 2016 17:36:39 +0000 Subject: [PATCH 2656/5498] usb: usbip: Fix possible deadlocks reported by lockdep commit 21619792d1eca7e772ca190ba68588e57f29595b upstream. Change spin_lock calls to spin_lock_irqsave to prevent attmpted recursive lock taking in interrupt context. This patch fixes Bug 109351 https://bugzilla.kernel.org/show_bug.cgi?id=109351 Signed-off-by: Andrew Goodbody Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/usbip_event.c | 5 +- drivers/usb/usbip/vhci_hcd.c | 88 +++++++++++++++++++-------------- drivers/usb/usbip/vhci_rx.c | 30 ++++++----- drivers/usb/usbip/vhci_sysfs.c | 19 ++++--- drivers/usb/usbip/vhci_tx.c | 14 +++--- 5 files changed, 91 insertions(+), 65 deletions(-) diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c index 64933b993d7a..2580a32bcdff 100644 --- a/drivers/usb/usbip/usbip_event.c +++ b/drivers/usb/usbip/usbip_event.c @@ -117,11 +117,12 @@ EXPORT_SYMBOL_GPL(usbip_event_add); int usbip_event_happened(struct usbip_device *ud) { int happened = 0; + unsigned long flags; - spin_lock(&ud->lock); + spin_lock_irqsave(&ud->lock, flags); if (ud->event != 0) happened = 1; - spin_unlock(&ud->lock); + spin_unlock_irqrestore(&ud->lock, flags); return happened; } diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index c02374b6049c..2c7bed7b19d6 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -121,9 +121,11 @@ static void dump_port_status_diff(u32 prev_status, u32 new_status) void rh_port_connect(int rhport, enum usb_device_speed speed) { + unsigned long flags; + usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION); @@ -139,22 +141,24 @@ void rh_port_connect(int rhport, enum usb_device_speed speed) break; } - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); } static void rh_port_disconnect(int rhport) { + unsigned long flags; + usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; the_controller->port_status[rhport] |= (1 << USB_PORT_FEAT_C_CONNECTION); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); } @@ -182,13 +186,14 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) int retval; int rhport; int changed = 0; + unsigned long flags; retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); memset(buf, 0, retval); vhci = hcd_to_vhci(hcd); - spin_lock(&vhci->lock); + spin_lock_irqsave(&vhci->lock, flags); if (!HCD_HW_ACCESSIBLE(hcd)) { usbip_dbg_vhci_rh("hw accessible flag not on?\n"); goto done; @@ -209,7 +214,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) usb_hcd_resume_root_hub(hcd); done: - spin_unlock(&vhci->lock); + spin_unlock_irqrestore(&vhci->lock, flags); return changed ? retval : 0; } @@ -230,6 +235,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, struct vhci_hcd *dum; int retval = 0; int rhport; + unsigned long flags; u32 prev_port_status[VHCI_NPORTS]; @@ -248,7 +254,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, dum = hcd_to_vhci(hcd); - spin_lock(&dum->lock); + spin_lock_irqsave(&dum->lock, flags); /* store old status and compare now and old later */ if (usbip_dbg_flag_vhci_rh) { @@ -402,7 +408,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } usbip_dbg_vhci_rh(" bye\n"); - spin_unlock(&dum->lock); + spin_unlock_irqrestore(&dum->lock, flags); return retval; } @@ -425,6 +431,7 @@ static void vhci_tx_urb(struct urb *urb) { struct vhci_device *vdev = get_vdev(urb->dev); struct vhci_priv *priv; + unsigned long flags; if (!vdev) { pr_err("could not get virtual device"); @@ -437,7 +444,7 @@ static void vhci_tx_urb(struct urb *urb) return; } - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); priv->seqnum = atomic_inc_return(&the_controller->seqnum); if (priv->seqnum == 0xffff) @@ -451,7 +458,7 @@ static void vhci_tx_urb(struct urb *urb) list_add_tail(&priv->list, &vdev->priv_tx); wake_up(&vdev->waitq_tx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); } static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, @@ -460,6 +467,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, struct device *dev = &urb->dev->dev; int ret = 0; struct vhci_device *vdev; + unsigned long flags; usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", hcd, urb, mem_flags); @@ -467,11 +475,11 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, /* patch to usb_sg_init() is in 2.5.60 */ BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); if (urb->status != -EINPROGRESS) { dev_err(dev, "URB already unlinked!, status %d\n", urb->status); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return urb->status; } @@ -483,7 +491,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, vdev->ud.status == VDEV_ST_ERROR) { dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport); spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return -ENODEV; } spin_unlock(&vdev->ud.lock); @@ -558,14 +566,14 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, out: vhci_tx_urb(urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return 0; no_need_xmit: usb_hcd_unlink_urb_from_ep(hcd, urb); no_need_unlink: - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); return ret; } @@ -620,16 +628,17 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct vhci_priv *priv; struct vhci_device *vdev; + unsigned long flags; pr_info("dequeue a urb %p\n", urb); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); priv = urb->hcpriv; if (!priv) { /* URB was never linked! or will be soon given back by * vhci_rx. */ - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return 0; } @@ -638,7 +647,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret) { - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return ret; } } @@ -666,10 +675,10 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); } else { /* tcp connection is alive */ @@ -681,7 +690,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); if (!unlink) { spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); return -ENOMEM; } @@ -702,7 +711,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) spin_unlock(&vdev->priv_lock); } - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usbip_dbg_vhci_hc("leave\n"); return 0; @@ -711,8 +720,9 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) static void vhci_device_unlink_cleanup(struct vhci_device *vdev) { struct vhci_unlink *unlink, *tmp; + unsigned long flags; - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); spin_lock(&vdev->priv_lock); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { @@ -746,19 +756,19 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) list_del(&unlink->list); spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); spin_lock(&vdev->priv_lock); kfree(unlink); } spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); } /* @@ -825,8 +835,9 @@ static void vhci_shutdown_connection(struct usbip_device *ud) static void vhci_device_reset(struct usbip_device *ud) { struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + unsigned long flags; - spin_lock(&ud->lock); + spin_lock_irqsave(&ud->lock, flags); vdev->speed = 0; vdev->devid = 0; @@ -841,14 +852,16 @@ static void vhci_device_reset(struct usbip_device *ud) } ud->status = VDEV_ST_NULL; - spin_unlock(&ud->lock); + spin_unlock_irqrestore(&ud->lock, flags); } static void vhci_device_unusable(struct usbip_device *ud) { - spin_lock(&ud->lock); + unsigned long flags; + + spin_lock_irqsave(&ud->lock, flags); ud->status = VDEV_ST_ERROR; - spin_unlock(&ud->lock); + spin_unlock_irqrestore(&ud->lock, flags); } static void vhci_device_init(struct vhci_device *vdev) @@ -938,12 +951,13 @@ static int vhci_get_frame_number(struct usb_hcd *hcd) static int vhci_bus_suspend(struct usb_hcd *hcd) { struct vhci_hcd *vhci = hcd_to_vhci(hcd); + unsigned long flags; dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock(&vhci->lock); + spin_lock_irqsave(&vhci->lock, flags); hcd->state = HC_STATE_SUSPENDED; - spin_unlock(&vhci->lock); + spin_unlock_irqrestore(&vhci->lock, flags); return 0; } @@ -952,15 +966,16 @@ static int vhci_bus_resume(struct usb_hcd *hcd) { struct vhci_hcd *vhci = hcd_to_vhci(hcd); int rc = 0; + unsigned long flags; dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock(&vhci->lock); + spin_lock_irqsave(&vhci->lock, flags); if (!HCD_HW_ACCESSIBLE(hcd)) rc = -ESHUTDOWN; else hcd->state = HC_STATE_RUNNING; - spin_unlock(&vhci->lock); + spin_unlock_irqrestore(&vhci->lock, flags); return rc; } @@ -1058,17 +1073,18 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) int rhport = 0; int connected = 0; int ret = 0; + unsigned long flags; hcd = platform_get_drvdata(pdev); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); for (rhport = 0; rhport < VHCI_NPORTS; rhport++) if (the_controller->port_status[rhport] & USB_PORT_STAT_CONNECTION) connected += 1; - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); if (connected > 0) { dev_info(&pdev->dev, diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index 00e4a54308e4..d656e0edc3d5 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -72,10 +72,11 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, { struct usbip_device *ud = &vdev->ud; struct urb *urb; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); if (!urb) { pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); @@ -104,9 +105,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, usbip_dbg_vhci_rx("now giveback urb %p\n", urb); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); @@ -117,8 +118,9 @@ static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, struct usbip_header *pdu) { struct vhci_unlink *unlink, *tmp; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { pr_info("unlink->seqnum %lu\n", unlink->seqnum); @@ -127,12 +129,12 @@ static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, unlink->seqnum); list_del(&unlink->list); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return unlink; } } - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return NULL; } @@ -142,6 +144,7 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, { struct vhci_unlink *unlink; struct urb *urb; + unsigned long flags; usbip_dump_header(pdu); @@ -152,9 +155,9 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, return; } - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); if (!urb) { /* @@ -171,9 +174,9 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, urb->status = pdu->u.ret_unlink.status; pr_info("urb->status %d\n", urb->status); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); @@ -185,10 +188,11 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, static int vhci_priv_tx_empty(struct vhci_device *vdev) { int empty = 0; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); empty = list_empty(&vdev->priv_rx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return empty; } diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index f05f1e0a2baf..78fd6bae9a72 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -32,10 +32,11 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, { char *s = out; int i = 0; + unsigned long flags; BUG_ON(!the_controller || !out); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); /* * output example: @@ -73,7 +74,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, spin_unlock(&vdev->ud.lock); } - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return out - s; } @@ -83,11 +84,12 @@ static DEVICE_ATTR_RO(status); static int vhci_port_disconnect(__u32 rhport) { struct vhci_device *vdev; + unsigned long flags; usbip_dbg_vhci_sysfs("enter\n"); /* lock */ - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); vdev = port_to_vdev(rhport); @@ -97,14 +99,14 @@ static int vhci_port_disconnect(__u32 rhport) /* unlock */ spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return -EINVAL; } /* unlock */ spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); @@ -180,6 +182,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, int sockfd = 0; __u32 rhport = 0, devid = 0, speed = 0; int err; + unsigned long flags; /* * @rhport: port number of vhci_hcd @@ -205,14 +208,14 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, /* now need lock until setting vdev status as used */ /* begin a lock */ - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); vdev = port_to_vdev(rhport); spin_lock(&vdev->ud.lock); if (vdev->ud.status != VDEV_ST_NULL) { /* end of the lock */ spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); sockfd_put(socket); @@ -231,7 +234,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, vdev->ud.status = VDEV_ST_NOTASSIGNED; spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); /* end the lock */ vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c index 409fd99f3257..3e7878fe2fd4 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -47,16 +47,17 @@ static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) { struct vhci_priv *priv, *tmp; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { list_move_tail(&priv->list, &vdev->priv_rx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return priv; } - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return NULL; } @@ -136,16 +137,17 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) { struct vhci_unlink *unlink, *tmp; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { list_move_tail(&unlink->list, &vdev->unlink_rx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return unlink; } - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return NULL; } -- GitLab From a7db857299d6e98abf6fa0b6986b83abb7f1d4cb Mon Sep 17 00:00:00 2001 From: Yuyang Du Date: Thu, 8 Jun 2017 13:04:10 +0800 Subject: [PATCH 2657/5498] usbip: vhci-hcd: Add USB3 SuperSpeed support commit 1c9de5bf428612458427943b724bea51abde520a upstream. This patch adds a USB3 HCD to an existing USB2 HCD and provides the support of SuperSpeed, in case the device can only be enumerated with SuperSpeed. The bulk of the added code in usb3_bos_desc and hub_control to support SuperSpeed is borrowed from the commit 1cd8fd2887e162ad ("usb: gadget: dummy_hcd: add SuperSpeed support"). With this patch, each vhci will have VHCI_HC_PORTS HighSpeed ports and VHCI_HC_PORTS SuperSpeed ports. Suggested-by: Krzysztof Opasiak Signed-off-by: Yuyang Du Acked-by: Shuah Khan Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 2c7bed7b19d6..e480b924a04c 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -279,7 +279,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_POWER: usbip_dbg_vhci_rh( " ClearPortFeature: USB_PORT_FEAT_POWER\n"); - dum->port_status[rhport] = 0; + dum->port_status[rhport] &= ~USB_PORT_STAT_POWER; dum->resuming = 0; break; case USB_PORT_FEAT_C_RESET: -- GitLab From 0dcd7547c4d16587511c0415d1adab1437ecbc1f Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Fri, 15 Dec 2017 10:50:09 -0700 Subject: [PATCH 2658/5498] usbip: prevent leaking socket pointer address in messages commit 90120d15f4c397272aaf41077960a157fc4212bf upstream. usbip driver is leaking socket pointer address in messages. Remove the messages that aren't useful and print sockfd in the ones that are useful for debugging. Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_dev.c | 3 +-- drivers/usb/usbip/usbip_common.c | 15 ++++----------- drivers/usb/usbip/vhci_hcd.c | 2 +- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index fac20e0434c0..8123a6b2eade 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -163,8 +163,7 @@ static void stub_shutdown_connection(struct usbip_device *ud) * step 1? */ if (ud->tcp_socket) { - dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n", - ud->tcp_socket); + dev_dbg(&sdev->udev->dev, "shutdown sockfd %d\n", ud->sockfd); kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); } diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 9752b93f754e..1838f1b2c2fa 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -317,18 +317,14 @@ int usbip_recv(struct socket *sock, void *buf, int size) struct msghdr msg; struct kvec iov; int total = 0; - /* for blocks of if (usbip_dbg_flag_xmit) */ char *bp = buf; int osize = size; - usbip_dbg_xmit("enter\n"); - - if (!sock || !buf || !size) { - pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf, - size); + if (!sock || !buf || !size) return -EINVAL; - } + + usbip_dbg_xmit("enter\n"); do { sock->sk->sk_allocation = GFP_NOIO; @@ -341,11 +337,8 @@ int usbip_recv(struct socket *sock, void *buf, int size) msg.msg_flags = MSG_NOSIGNAL; result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL); - if (result <= 0) { - pr_debug("receive sock %p buf %p size %u ret %d total %d\n", - sock, buf, size, result, total); + if (result <= 0) goto err; - } size -= result; buf += result; diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index e480b924a04c..a57843e1173f 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -782,7 +782,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) /* need this? see stub_dev.c */ if (ud->tcp_socket) { - pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket); + pr_debug("shutdown sockfd %d\n", ud->sockfd); kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); } -- GitLab From f52839830f99b2937854ec70e6bd41587fb73971 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Mon, 18 Dec 2017 17:23:37 -0700 Subject: [PATCH 2659/5498] usbip: stub: stop printing kernel pointer addresses in messages commit 248a22044366f588d46754c54dfe29ffe4f8b4df upstream. Remove and/or change debug, info. and error messages to not print kernel pointer addresses. Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_main.c | 5 +++-- drivers/usb/usbip/stub_rx.c | 7 ++----- drivers/usb/usbip/stub_tx.c | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index af10f7b131a4..325b4c05acdd 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -252,11 +252,12 @@ void stub_device_cleanup_urbs(struct stub_device *sdev) struct stub_priv *priv; struct urb *urb; - dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); + dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n"); while ((priv = stub_priv_pop(sdev))) { urb = priv->urb; - dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); + dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n", + priv->seqnum); usb_kill_urb(urb); kmem_cache_free(stub_priv_cache, priv); diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index f5533c99cd48..56cacb68040c 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -230,9 +230,6 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev, if (priv->seqnum != pdu->u.cmd_unlink.seqnum) continue; - dev_info(&priv->urb->dev->dev, "unlink urb %p\n", - priv->urb); - /* * This matched urb is not completed yet (i.e., be in * flight in usb hcd hardware/driver). Now we are @@ -271,8 +268,8 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev, ret = usb_unlink_urb(priv->urb); if (ret != -EINPROGRESS) dev_err(&priv->urb->dev->dev, - "failed to unlink a urb %p, ret %d\n", - priv->urb, ret); + "failed to unlink a urb # %lu, ret %d\n", + priv->seqnum, ret); return 0; } diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index af858d52608a..f4dd30c56f36 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -201,8 +201,8 @@ static int stub_send_ret_submit(struct stub_device *sdev) /* 1. setup usbip_header */ setup_ret_submit_pdu(&pdu_header, urb); - usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", - pdu_header.base.seqnum, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d\n", + pdu_header.base.seqnum); usbip_header_correct_endian(&pdu_header, 1); iov[iovnum].iov_base = &pdu_header; -- GitLab From c6afe2ab06b0992b4826c015e1523bc114c41d10 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Mon, 18 Dec 2017 17:24:22 -0700 Subject: [PATCH 2660/5498] usbip: vhci: stop printing kernel pointer addresses in messages commit 8272d099d05f7ab2776cf56a2ab9f9443be18907 upstream. Remove and/or change debug, info. and error messages to not print kernel pointer addresses. Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_hcd.c | 10 ---------- drivers/usb/usbip/vhci_rx.c | 23 +++++++++++------------ drivers/usb/usbip/vhci_tx.c | 3 ++- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index a57843e1173f..869938036248 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -469,9 +469,6 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, struct vhci_device *vdev; unsigned long flags; - usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", - hcd, urb, mem_flags); - /* patch to usb_sg_init() is in 2.5.60 */ BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); @@ -630,8 +627,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) struct vhci_device *vdev; unsigned long flags; - pr_info("dequeue a urb %p\n", urb); - spin_lock_irqsave(&the_controller->lock, flags); priv = urb->hcpriv; @@ -659,7 +654,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) /* tcp connection is closed */ spin_lock(&vdev->priv_lock); - pr_info("device %p seems to be disconnected\n", vdev); list_del(&priv->list); kfree(priv); urb->hcpriv = NULL; @@ -671,8 +665,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) * vhci_rx will receive RET_UNLINK and give back the URB. * Otherwise, we give back it here. */ - pr_info("gives back urb %p\n", urb); - usb_hcd_unlink_urb_from_ep(hcd, urb); spin_unlock_irqrestore(&the_controller->lock, flags); @@ -701,8 +693,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink->unlink_seqnum = priv->seqnum; - pr_info("device %p seems to be still connected\n", vdev); - /* send cmd_unlink and try to cancel the pending URB in the * peer */ list_add_tail(&unlink->list, &vdev->unlink_tx); diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index d656e0edc3d5..323aa7789989 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -37,24 +37,23 @@ struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) urb = priv->urb; status = urb->status; - usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n", - urb, priv, seqnum); + usbip_dbg_vhci_rx("find urb seqnum %u\n", seqnum); switch (status) { case -ENOENT: /* fall through */ case -ECONNRESET: - dev_info(&urb->dev->dev, - "urb %p was unlinked %ssynchronuously.\n", urb, - status == -ENOENT ? "" : "a"); + dev_dbg(&urb->dev->dev, + "urb seq# %u was unlinked %ssynchronuously\n", + seqnum, status == -ENOENT ? "" : "a"); break; case -EINPROGRESS: /* no info output */ break; default: - dev_info(&urb->dev->dev, - "urb %p may be in a error, status %d\n", urb, - status); + dev_dbg(&urb->dev->dev, + "urb seq# %u may be in a error, status %d\n", + seqnum, status); } list_del(&priv->list); @@ -79,8 +78,8 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, spin_unlock_irqrestore(&vdev->priv_lock, flags); if (!urb) { - pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); - pr_info("max seqnum %d\n", + pr_err("cannot find a urb of seqnum %u max seqnum %d\n", + pdu->base.seqnum, atomic_read(&the_controller->seqnum)); usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); return; @@ -103,7 +102,7 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, if (usbip_dbg_flag_vhci_rx) usbip_dump_urb(urb); - usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum); spin_lock_irqsave(&the_controller->lock, flags); usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); @@ -168,7 +167,7 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, pr_info("the urb (seqnum %d) was already given back\n", pdu->base.seqnum); } else { - usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum); /* If unlink is successful, status is -ECONNRESET */ urb->status = pdu->u.ret_unlink.status; diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c index 3e7878fe2fd4..a9a663a578b6 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -83,7 +83,8 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) memset(&msg, 0, sizeof(msg)); memset(&iov, 0, sizeof(iov)); - usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); + usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n", + priv->seqnum); /* 1. setup usbip_header */ setup_cmd_submit_pdu(&pdu_header, urb); -- GitLab From d2baa5e59786136454e3baf3cb7c9d606ab8d508 Mon Sep 17 00:00:00 2001 From: Mohamed Ghannam Date: Tue, 5 Dec 2017 20:58:35 +0000 Subject: [PATCH 2661/5498] dccp: CVE-2017-8824: use-after-free in DCCP code commit 69c64866ce072dea1d1e59a0d61e0f66c0dffb76 upstream. Whenever the sock object is in DCCP_CLOSED state, dccp_disconnect() must free dccps_hc_tx_ccid and dccps_hc_rx_ccid and set to NULL. Signed-off-by: Mohamed Ghannam Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/dccp/proto.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/dccp/proto.c b/net/dccp/proto.c index e1bc9d52c016..dad5493c7c5a 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -259,6 +259,7 @@ int dccp_disconnect(struct sock *sk, int flags) { struct inet_connection_sock *icsk = inet_csk(sk); struct inet_sock *inet = inet_sk(sk); + struct dccp_sock *dp = dccp_sk(sk); int err = 0; const int old_state = sk->sk_state; @@ -278,6 +279,10 @@ int dccp_disconnect(struct sock *sk, int flags) sk->sk_err = ECONNRESET; dccp_clear_xmit_timers(sk); + ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); + ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); + dp->dccps_hc_rx_ccid = NULL; + dp->dccps_hc_tx_ccid = NULL; __skb_queue_purge(&sk->sk_receive_queue); __skb_queue_purge(&sk->sk_write_queue); -- GitLab From 84882420c73f959fdbad90e538a6d1644c6703dc Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Tue, 26 Sep 2017 17:10:20 -0400 Subject: [PATCH 2662/5498] media: dvb-usb-v2: lmedm04: Improve logic checking of warm start commit 3d932ee27e852e4904647f15b64dedca51187ad7 upstream. Warm start has no check as whether a genuine device has connected and proceeds to next execution path. Check device should read 0x47 at offset of 2 on USB descriptor read and it is the amount requested of 6 bytes. Fix for kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access as Reported-by: Andrey Konovalov Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 2273ce78f5c8..ceaffcf8ae5e 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -438,18 +438,23 @@ static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, static int lme2510_return_status(struct dvb_usb_device *d) { - int ret = 0; + int ret; u8 *data; - data = kzalloc(10, GFP_KERNEL); + data = kzalloc(6, GFP_KERNEL); if (!data) return -ENOMEM; - ret |= usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), - 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200); - info("Firmware Status: %x (%x)", ret , data[2]); + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + 0x06, 0x80, 0x0302, 0x00, + data, 0x6, 200); + if (ret != 6) + ret = -EINVAL; + else + ret = data[2]; + + info("Firmware Status: %6ph", data); - ret = (ret < 0) ? -ENODEV : data[2]; kfree(data); return ret; } @@ -1231,6 +1236,7 @@ static int lme2510_get_adapter_count(struct dvb_usb_device *d) static int lme2510_identify_state(struct dvb_usb_device *d, const char **name) { struct lme2510_state *st = d->priv; + int status; usb_reset_configuration(d->udev); @@ -1239,12 +1245,16 @@ static int lme2510_identify_state(struct dvb_usb_device *d, const char **name) st->dvb_usb_lme2510_firmware = dvb_usb_lme2510_firmware; - if (lme2510_return_status(d) == 0x44) { + status = lme2510_return_status(d); + if (status == 0x44) { *name = lme_firmware_switch(d, 0); return COLD; } - return 0; + if (status != 0x47) + return -EINVAL; + + return WARM; } static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type, -- GitLab From 6cd23a0e51dd2580bfdbb005ad1edeeaf9192d00 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Tue, 26 Sep 2017 17:10:21 -0400 Subject: [PATCH 2663/5498] media: dvb-usb-v2: lmedm04: move ts2020 attach to dm04_lme2510_tuner commit 7bf7a7116ed313c601307f7e585419369926ab05 upstream. When the tuner was split from m88rs2000 the attach function is in wrong place. Move to dm04_lme2510_tuner to trap errors on failure and removing a call to lme_coldreset. Prevents driver starting up without any tuner connected. Fixes to trap for ts2020 fail. LME2510(C): FE Found M88RS2000 ts2020: probe of 0-0060 failed with error -11 ... LME2510(C): TUN Found RS2000 tuner kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] PREEMPT SMP KASAN Reported-by: Andrey Konovalov Signed-off-by: Malcolm Priestley Tested-by: Andrey Konovalov Signed-off-by: Mauro Carvalho Chehab Cc: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index ceaffcf8ae5e..f17e2cb4acb6 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -1118,8 +1118,6 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) if (adap->fe[0]) { info("FE Found M88RS2000"); - dvb_attach(ts2020_attach, adap->fe[0], &ts2020_config, - &d->i2c_adap); st->i2c_tuner_gate_w = 5; st->i2c_tuner_gate_r = 5; st->i2c_tuner_addr = 0x60; @@ -1182,17 +1180,18 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) ret = st->tuner_config; break; case TUNER_RS2000: - ret = st->tuner_config; + if (dvb_attach(ts2020_attach, adap->fe[0], + &ts2020_config, &d->i2c_adap)) + ret = st->tuner_config; break; default: break; } - if (ret) + if (ret) { info("TUN Found %s tuner", tun_msg[ret]); - else { - info("TUN No tuner found --- resetting device"); - lme_coldreset(d); + } else { + info("TUN No tuner found"); return -ENODEV; } -- GitLab From 0dcc0912d39465102ffc5cd44ce6ac8d2d5c8933 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 12 Jan 2018 10:13:36 +0100 Subject: [PATCH 2664/5498] mtd: nand: Fix nand_do_read_oob() return value commit 87e89ce8d0d14f573c068c61bec2117751fb5103 upstream. Starting from commit 041e4575f034 ("mtd: nand: handle ECC errors in OOB"), nand_do_read_oob() (from the NAND core) did return 0 or a negative error, and the MTD layer expected it. However, the trend for the NAND layer is now to return an error or a positive number of bitflips. Deciding which status to return to the user belongs to the MTD layer. Commit e47f68587b82 ("mtd: check for max_bitflips in mtd_read_oob()") brought this logic to the mtd_read_oob() function while the return value coming from nand_do_read_oob() (called by the ->_read_oob() hook) was left unchanged. Fixes: e47f68587b82 ("mtd: check for max_bitflips in mtd_read_oob()") Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/nand_base.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d5c0a6c7e0e7..7eddb1e95de3 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1872,6 +1872,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + unsigned int max_bitflips = 0; int page, realpage, chipnr; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; @@ -1932,6 +1933,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, nand_wait_ready(mtd); } + max_bitflips = max_t(unsigned int, max_bitflips, ret); + readlen -= len; if (!readlen) break; @@ -1957,7 +1960,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** -- GitLab From c8ebc7dd51be8d1ecc34ce43277ec7a6c0405a05 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 18 Dec 2017 14:39:13 -0500 Subject: [PATCH 2665/5498] NFS: Add a cond_resched() to nfs_commit_release_pages() commit 7f1bda447c9bd48b415acedba6b830f61591601f upstream. The commit list can get very large, and so we need a cond_resched() in nfs_commit_release_pages() in order to ensure we don't hog the CPU for excessive periods of time. Reported-by: Mike Galbraith Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/write.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 395b93f43df5..efe1b1757afc 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1646,6 +1646,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) set_bit(NFS_CONTEXT_RESEND_WRITES, &req->wb_context->flags); next: nfs_unlock_and_release_request(req); + /* Latency breaker */ + cond_resched(); } nfss = NFS_SERVER(data->inode); if (atomic_long_read(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH) -- GitLab From 103fa74ffa5247439cbdae65c8aaec8e62ef3a05 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 16 Jan 2018 10:08:00 -0500 Subject: [PATCH 2666/5498] NFS: commit direct writes even if they fail partially commit 1b8d97b0a837beaf48a8449955b52c650a7114b4 upstream. If some of the WRITE calls making up an O_DIRECT write syscall fail, we neglect to commit, even if some of the WRITEs succeed. We also depend on the commit code to free the reference count on the nfs_page taken in the "if (request_commit)" case at the end of nfs_direct_write_completion(). The problem was originally noticed because ENOSPC's encountered partway through a write would result in a closed file being sillyrenamed when it should have been unlinked. Signed-off-by: J. Bruce Fields Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/direct.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index a094b0c34ac3..0da8e64c9d75 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -716,10 +716,8 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) spin_lock(&dreq->lock); - if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { - dreq->flags = 0; + if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) dreq->error = hdr->error; - } if (dreq->error == 0) { dreq->count += hdr->good_bytes; if (nfs_write_need_commit(hdr)) { -- GitLab From 0b72648a13f1b6a793779b4f31d54510156c168e Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Fri, 19 Jan 2018 09:18:54 +0100 Subject: [PATCH 2667/5498] kernfs: fix regression in kernfs_fop_write caused by wrong type commit ba87977a49913129962af8ac35b0e13e0fa4382d upstream. Commit b7ce40cff0b9 ("kernfs: cache atomic_write_len in kernfs_open_file") changes type of local variable 'len' from ssize_t to size_t. This change caused that the *ppos value is updated also when the previous write callback failed. Mentioned snippet: ... len = ops->write(...); <- return value can be negative ... if (len > 0) <- true here in this case *ppos += len; ... Fixes: b7ce40cff0b9 ("kernfs: cache atomic_write_len in kernfs_open_file") Acked-by: Tejun Heo Signed-off-by: Ivan Vecera Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index efb1f7b5883e..4779f47ecad2 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -267,7 +267,7 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, { struct kernfs_open_file *of = kernfs_of(file); const struct kernfs_ops *ops; - size_t len; + ssize_t len; char *buf; if (of->atomic_write_len) { -- GitLab From 5e52f5a43c4abc304f108a25549bfa5a79626e83 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 3 Jan 2018 11:16:22 -0800 Subject: [PATCH 2668/5498] crypto: hash - introduce crypto_hash_alg_has_setkey() commit cd6ed77ad5d223dc6299fb58f62e0f5267f7e2ba upstream. Templates that use an shash spawn can use crypto_shash_alg_has_setkey() to determine whether the underlying algorithm requires a key or not. But there was no corresponding function for ahash spawns. Add it. Note that the new function actually has to support both shash and ahash algorithms, since the ahash API can be used with either. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/ahash.c | 11 +++++++++++ include/crypto/internal/hash.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/crypto/ahash.c b/crypto/ahash.c index a11220e78152..0735529ea082 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -636,5 +636,16 @@ struct hash_alg_common *ahash_attr_alg(struct rtattr *rta, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(ahash_attr_alg); +bool crypto_hash_alg_has_setkey(struct hash_alg_common *halg) +{ + struct crypto_alg *alg = &halg->base; + + if (alg->cra_type != &crypto_ahash_type) + return crypto_shash_alg_has_setkey(__crypto_shash_alg(alg)); + + return __crypto_ahash_alg(alg)->setkey != NULL; +} +EXPORT_SYMBOL_GPL(crypto_hash_alg_has_setkey); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Asynchronous cryptographic hash type"); diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index 9779c35f8454..dab9569f22bf 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -91,6 +91,8 @@ static inline bool crypto_shash_alg_has_setkey(struct shash_alg *alg) return alg->setkey != shash_no_setkey; } +bool crypto_hash_alg_has_setkey(struct hash_alg_common *halg); + int crypto_init_ahash_spawn(struct crypto_ahash_spawn *spawn, struct hash_alg_common *alg, struct crypto_instance *inst); -- GitLab From 6278069e9f93b02c48e6e424e484b48d8b7abb17 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 3 Jan 2018 11:16:23 -0800 Subject: [PATCH 2669/5498] crypto: cryptd - pass through absence of ->setkey() commit 841a3ff329713f796a63356fef6e2f72e4a3f6a3 upstream. When the cryptd template is used to wrap an unkeyed hash algorithm, don't install a ->setkey() method to the cryptd instance. This change is necessary for cryptd to keep working with unkeyed hash algorithms once we start enforcing that ->setkey() is called when present. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- crypto/cryptd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/cryptd.c b/crypto/cryptd.c index fb0d140065e2..828ead458c09 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -618,7 +618,8 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, inst->alg.finup = cryptd_hash_finup_enqueue; inst->alg.export = cryptd_hash_export; inst->alg.import = cryptd_hash_import; - inst->alg.setkey = cryptd_hash_setkey; + if (crypto_shash_alg_has_setkey(salg)) + inst->alg.setkey = cryptd_hash_setkey; inst->alg.digest = cryptd_hash_digest_enqueue; err = ahash_register_instance(tmpl, inst); -- GitLab From fd2ebfa2de94cc688f5031ca61608272b3c62f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Horia=20Geant=C4=83?= Date: Mon, 5 Feb 2018 11:15:52 +0200 Subject: [PATCH 2670/5498] crypto: caam - fix endless loop when DECO acquire fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 225ece3e7dad4cfc44cca38ce7a3a80f255ea8f1 upstream. In case DECO0 cannot be acquired - i.e. run_descriptor_deco0() fails with -ENODEV, caam_probe() enters an endless loop: run_descriptor_deco0 ret -ENODEV -> instantiate_rng -ENODEV, overwritten by -EAGAIN ret -EAGAIN -> caam_probe -EAGAIN results in endless loop It turns out the error path in instantiate_rng() is incorrect, the checks are done in the wrong order. Fixes: 1005bccd7a4a6 ("crypto: caam - enable instantiation of all RNG4 state handles") Reported-by: Bryan O'Donoghue Suggested-by: Auer Lukas Signed-off-by: Horia Geantă Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/caam/ctrl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index a23062eef751..a5a86c6b787b 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -209,12 +209,16 @@ static int instantiate_rng(struct device *ctrldev, int state_handle_mask, * without any error (HW optimizations for later * CAAM eras), then try again. */ + if (ret) + break; + rdsta_val = rd_reg32(&ctrl->r4tst[0].rdsta) & RDSTA_IFMASK; - if (status || !(rdsta_val & (1 << sh_idx))) + if (status || !(rdsta_val & (1 << sh_idx))) { ret = -EAGAIN; - if (ret) break; + } + dev_info(ctrldev, "Instantiated RNG4 SH%d\n", sh_idx); /* Clear the contents before recreating the descriptor */ memset(desc, 0x00, CAAM_CMD_SZ * 7); -- GitLab From f874d19f2cbef34a4858cfd946d5072e57a6121e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 24 Jan 2018 06:01:57 -0500 Subject: [PATCH 2671/5498] media: cxusb, dib0700: ignore XC2028_I2C_FLUSH commit 9893b905e743ded332575ca04486bd586c0772f7 upstream. The XC2028_I2C_FLUSH only needs to be implemented on a few devices. Others can safely ignore it. That prevents filling the dmesg with lots of messages like: dib0700: stk7700ph_xc3028_callback: unknown command 2, arg 0 Fixes: 4d37ece757a8 ("[media] tuner/xc2028: Add I2C flush callback") Reported-by: Enrico Mioso Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/usb/dvb-usb/cxusb.c | 2 ++ drivers/media/usb/dvb-usb/dib0700_devices.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 356abb369c20..55b1a93f6e30 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -923,6 +923,8 @@ static int dvico_bluebird_xc2028_callback(void *ptr, int component, case XC2028_RESET_CLK: deb_info("%s: XC2028_RESET_CLK %d\n", __func__, arg); break; + case XC2028_I2C_FLUSH: + break; default: deb_info("%s: unknown command %d, arg %d\n", __func__, command, arg); diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 2e50c37c94e0..b7dd332465b9 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -431,6 +431,7 @@ static int stk7700ph_xc3028_callback(void *ptr, int component, state->dib7000p_ops.set_gpio(adap->fe_adap[0].fe, 8, 0, 1); break; case XC2028_RESET_CLK: + case XC2028_I2C_FLUSH: break; default: err("%s: unknown command %d, arg %d\n", __func__, -- GitLab From dd59ee6cbf47d48f4e0685acc8799bdb61abd04f Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 6 Feb 2018 15:37:55 -0800 Subject: [PATCH 2672/5498] kernel/async.c: revert "async: simplify lowest_in_progress()" commit 4f7e988e63e336827f4150de48163bed05d653bd upstream. This reverts commit 92266d6ef60c ("async: simplify lowest_in_progress()") which was simply wrong: In the case where domain is NULL, we now use the wrong offsetof() in the list_first_entry macro, so we don't actually fetch the ->cookie value, but rather the eight bytes located sizeof(struct list_head) further into the struct async_entry. On 64 bit, that's the data member, while on 32 bit, that's a u64 built from func and data in some order. I think the bug happens to be harmless in practice: It obviously only affects callers which pass a NULL domain, and AFAICT the only such caller is async_synchronize_full() -> async_synchronize_full_domain(NULL) -> async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, NULL) and the ASYNC_COOKIE_MAX means that in practice we end up waiting for the async_global_pending list to be empty - but it would break if somebody happened to pass (void*)-1 as the data element to async_schedule, and of course also if somebody ever does a async_synchronize_cookie_domain(, NULL) with a "finite" cookie value. Maybe the "harmless in practice" means this isn't -stable material. But I'm not completely confident my quick git grep'ing is enough, and there might be affected code in one of the earlier kernels that has since been removed, so I'll leave the decision to the stable guys. Link: http://lkml.kernel.org/r/20171128104938.3921-1-linux@rasmusvillemoes.dk Fixes: 92266d6ef60c "async: simplify lowest_in_progress()" Signed-off-by: Rasmus Villemoes Acked-by: Tejun Heo Cc: Arjan van de Ven Cc: Adam Wallis Cc: Lai Jiangshan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/async.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/kernel/async.c b/kernel/async.c index 4c3773c0bf63..f1fd155abff6 100644 --- a/kernel/async.c +++ b/kernel/async.c @@ -84,20 +84,24 @@ static atomic_t entry_count; static async_cookie_t lowest_in_progress(struct async_domain *domain) { - struct list_head *pending; + struct async_entry *first = NULL; async_cookie_t ret = ASYNC_COOKIE_MAX; unsigned long flags; spin_lock_irqsave(&async_lock, flags); - if (domain) - pending = &domain->pending; - else - pending = &async_global_pending; + if (domain) { + if (!list_empty(&domain->pending)) + first = list_first_entry(&domain->pending, + struct async_entry, domain_list); + } else { + if (!list_empty(&async_global_pending)) + first = list_first_entry(&async_global_pending, + struct async_entry, global_list); + } - if (!list_empty(pending)) - ret = list_first_entry(pending, struct async_entry, - domain_list)->cookie; + if (first) + ret = first->cookie; spin_unlock_irqrestore(&async_lock, flags); return ret; -- GitLab From 7a64cc59f32dd16d1ff88eb3c109fc17b1a44f65 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 1 Aug 2017 04:16:47 -0500 Subject: [PATCH 2673/5498] signal/openrisc: Fix do_unaligned_access to send the proper signal commit 500d58300571b6602341b041f97c082a461ef994 upstream. While reviewing the signal sending on openrisc the do_unaligned_access function stood out because it is obviously wrong. A comment about an si_code set above when actually si_code is never set. Leading to a random si_code being sent to userspace in the event of an unaligned access. Looking further SIGBUS BUS_ADRALN is the proper pair of signal and si_code to send for an unaligned access. That is what other architectures do and what is required by posix. Given that do_unaligned_access is broken in a way that no one can be relying on it on openrisc fix the code to just do the right thing. Fixes: 769a8a96229e ("OpenRISC: Traps") Cc: Jonas Bonn Cc: Stefan Kristiansson Cc: Stafford Horne Cc: Arnd Bergmann Cc: openrisc@lists.librecores.org Acked-by: Stafford Horne Signed-off-by: "Eric W. Biederman" Signed-off-by: Greg Kroah-Hartman --- arch/openrisc/kernel/traps.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c index 3d3f6062f49c..605a284922fb 100644 --- a/arch/openrisc/kernel/traps.c +++ b/arch/openrisc/kernel/traps.c @@ -302,12 +302,12 @@ asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address) siginfo_t info; if (user_mode(regs)) { - /* Send a SIGSEGV */ - info.si_signo = SIGSEGV; + /* Send a SIGBUS */ + info.si_signo = SIGBUS; info.si_errno = 0; - /* info.si_code has been set above */ - info.si_addr = (void *)address; - force_sig_info(SIGSEGV, &info, current); + info.si_code = BUS_ADRALN; + info.si_addr = (void __user *)address; + force_sig_info(SIGBUS, &info, current); } else { printk("KERNEL: Unaligned Access 0x%.8lx\n", address); show_registers(regs); -- GitLab From 99a62e157e3d44fed2ee7f2a0604391c9ecdf501 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 24 Jul 2017 17:30:30 -0500 Subject: [PATCH 2674/5498] signal/sh: Ensure si_signo is initialized in do_divide_error commit 0e88bb002a9b2ee8cc3cc9478ce2dc126f849696 upstream. Set si_signo. Cc: Yoshinori Sato Cc: Rich Felker Cc: Paul Mundt Cc: linux-sh@vger.kernel.org Fixes: 0983b31849bb ("sh: Wire up division and address error exceptions on SH-2A.") Signed-off-by: "Eric W. Biederman" Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/traps_32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index ff639342a8be..c5b997757988 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -607,7 +607,8 @@ asmlinkage void do_divide_error(unsigned long r4) break; } - force_sig_info(SIGFPE, &info, current); + info.si_signo = SIGFPE; + force_sig_info(info.si_signo, &info, current); } #endif -- GitLab From 6eb7b54c6ec50636a564928770a825d78f6c789b Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 2 Jan 2018 14:01:34 -0500 Subject: [PATCH 2675/5498] alpha: fix crash if pthread_create races with signal delivery commit 21ffceda1c8b3807615c40d440d7815e0c85d366 upstream. On alpha, a process will crash if it attempts to start a thread and a signal is delivered at the same time. The crash can be reproduced with this program: https://cygwin.com/ml/cygwin/2014-11/msg00473.html The reason for the crash is this: * we call the clone syscall * we go to the function copy_process * copy process calls copy_thread_tls, it is a wrapper around copy_thread * copy_thread sets the tls pointer: childti->pcb.unique = regs->r20 * copy_thread sets regs->r20 to zero * we go back to copy_process * copy process checks "if (signal_pending(current))" and returns -ERESTARTNOINTR * the clone syscall is restarted, but this time, regs->r20 is zero, so the new thread is created with zero tls pointer * the new thread crashes in start_thread when attempting to access tls The comment in the code says that setting the register r20 is some compatibility with OSF/1. But OSF/1 doesn't use the CLONE_SETTLS flag, so we don't have to zero r20 if CLONE_SETTLS is set. This patch fixes the bug by zeroing regs->r20 only if CLONE_SETTLS is not set. Signed-off-by: Mikulas Patocka Signed-off-by: Matt Turner Signed-off-by: Greg Kroah-Hartman --- arch/alpha/kernel/process.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 1941a07b5811..86c1c4fd5246 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -274,12 +274,13 @@ copy_thread(unsigned long clone_flags, unsigned long usp, application calling fork. */ if (clone_flags & CLONE_SETTLS) childti->pcb.unique = regs->r20; + else + regs->r20 = 0; /* OSF/1 has some strange fork() semantics. */ childti->pcb.usp = usp ?: rdusp(); *childregs = *regs; childregs->r0 = 0; childregs->r19 = 0; childregs->r20 = 1; /* OSF/1 has some strange fork() semantics. */ - regs->r20 = 0; stack = ((struct switch_stack *) regs) - 1; *childstack = *stack; childstack->r26 = (unsigned long) ret_from_fork; -- GitLab From e6f03ae6cbb483f69cb9077521a3db9b756e6185 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 2 Jan 2018 13:59:54 -0500 Subject: [PATCH 2676/5498] alpha: fix reboot on Avanti platform commit 55fc633c41a08ce9244ff5f528f420b16b1e04d6 upstream. We need to define NEED_SRM_SAVE_RESTORE on the Avanti, otherwise we get machine check exception when attempting to reboot the machine. Signed-off-by: Mikulas Patocka Signed-off-by: Matt Turner Signed-off-by: Greg Kroah-Hartman --- arch/alpha/kernel/pci_impl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/pci_impl.h b/arch/alpha/kernel/pci_impl.h index 2b0ac429f5eb..412bb3c24f36 100644 --- a/arch/alpha/kernel/pci_impl.h +++ b/arch/alpha/kernel/pci_impl.h @@ -143,7 +143,8 @@ struct pci_iommu_arena }; #if defined(CONFIG_ALPHA_SRM) && \ - (defined(CONFIG_ALPHA_CIA) || defined(CONFIG_ALPHA_LCA)) + (defined(CONFIG_ALPHA_CIA) || defined(CONFIG_ALPHA_LCA) || \ + defined(CONFIG_ALPHA_AVANTI)) # define NEED_SRM_SAVE_RESTORE #else # undef NEED_SRM_SAVE_RESTORE -- GitLab From 6e7bf9002a8249d108d702f9a60948065071e348 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 5 Jan 2018 14:27:58 -0800 Subject: [PATCH 2677/5498] xtensa: fix futex_atomic_cmpxchg_inatomic commit ca47480921587ae30417dd234a9f79af188e3666 upstream. Return 0 if the operation was successful, not the userspace memory value. Check that userspace value equals passed oldval, not itself. Don't update *uval if the value wasn't read from userspace memory. This fixes process hang due to infinite loop in futex_lock_pi. It also fixes a bunch of glibc tests nptl/tst-mutexpi*. Signed-off-by: Max Filippov Signed-off-by: Greg Kroah-Hartman --- arch/xtensa/include/asm/futex.h | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h index b39531babec0..72bfc1cbc2b5 100644 --- a/arch/xtensa/include/asm/futex.h +++ b/arch/xtensa/include/asm/futex.h @@ -109,7 +109,6 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval) { int ret = 0; - u32 prev; if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; @@ -120,26 +119,24 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, __asm__ __volatile__ ( " # futex_atomic_cmpxchg_inatomic\n" - "1: l32i %1, %3, 0\n" - " mov %0, %5\n" - " wsr %1, scompare1\n" - "2: s32c1i %0, %3, 0\n" - "3:\n" + " wsr %5, scompare1\n" + "1: s32c1i %1, %4, 0\n" + " s32i %1, %6, 0\n" + "2:\n" " .section .fixup,\"ax\"\n" " .align 4\n" - "4: .long 3b\n" - "5: l32r %1, 4b\n" - " movi %0, %6\n" + "3: .long 2b\n" + "4: l32r %1, 3b\n" + " movi %0, %7\n" " jx %1\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .long 1b,5b,2b,5b\n" + " .long 1b,4b\n" " .previous\n" - : "+r" (ret), "=&r" (prev), "+m" (*uaddr) - : "r" (uaddr), "r" (oldval), "r" (newval), "I" (-EFAULT) + : "+r" (ret), "+r" (newval), "+m" (*uaddr), "+m" (*uval) + : "r" (uaddr), "r" (oldval), "r" (uval), "I" (-EFAULT) : "memory"); - *uval = prev; return ret; } -- GitLab From e715f11f9ea7cc76f5970786d4f35d325335089f Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 13 Nov 2017 16:12:06 +0000 Subject: [PATCH 2678/5498] EDAC, octeon: Fix an uninitialized variable warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 544e92581a2ac44607d7cc602c6b54d18656f56d upstream. Fix an uninitialized variable warning in the Octeon EDAC driver, as seen in MIPS cavium_octeon_defconfig builds since v4.14 with Codescape GNU Tools 2016.05-03: drivers/edac/octeon_edac-lmc.c In function ‘octeon_lmc_edac_poll_o2’: drivers/edac/octeon_edac-lmc.c:87:24: warning: ‘((long unsigned int*)&int_reg)[1]’ may \ be used uninitialized in this function [-Wmaybe-uninitialized] if (int_reg.s.sec_err || int_reg.s.ded_err) { ^ Iinitialise the whole int_reg variable to zero before the conditional assignments in the error injection case. Signed-off-by: James Hogan Acked-by: David Daney Cc: linux-edac Cc: linux-mips@linux-mips.org Fixes: 1bc021e81565 ("EDAC: Octeon: Add error injection support") Link: http://lkml.kernel.org/r/20171113161206.20990-1-james.hogan@mips.com Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/edac/octeon_edac-lmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c index 4bd10f94f068..c0c80ae8c47c 100644 --- a/drivers/edac/octeon_edac-lmc.c +++ b/drivers/edac/octeon_edac-lmc.c @@ -79,6 +79,7 @@ static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci) if (!pvt->inject) int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx)); else { + int_reg.u64 = 0; if (pvt->error_type == 1) int_reg.s.sec_err = 1; if (pvt->error_type == 2) -- GitLab From 16f14ebe6131856ba4aec9c1dcb00fdb991d0c38 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 2 Jan 2018 11:39:47 -0800 Subject: [PATCH 2679/5498] pktcdvd: Fix pkt_setup_dev() error path commit 5a0ec388ef0f6e33841aeb810d7fa23f049ec4cd upstream. Commit 523e1d399ce0 ("block: make gendisk hold a reference to its queue") modified add_disk() and disk_release() but did not update any of the error paths that trigger a put_disk() call after disk->queue has been assigned. That introduced the following behavior in the pktcdvd driver if pkt_new_dev() fails: Kernel BUG at 00000000e98fd882 [verbose debug info unavailable] Since disk_release() calls blk_put_queue() anyway if disk->queue != NULL, fix this by removing the blk_cleanup_queue() call from the pkt_setup_dev() error path. Fixes: commit 523e1d399ce0 ("block: make gendisk hold a reference to its queue") Signed-off-by: Bart Van Assche Cc: Tejun Heo Cc: Maciej S. Szmigiero Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/pktcdvd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 09e628dafd9d..46098d236476 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2798,7 +2798,7 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) pd->pkt_dev = MKDEV(pktdev_major, idx); ret = pkt_new_dev(pd, dev); if (ret) - goto out_new_dev; + goto out_mem2; /* inherit events of the host device */ disk->events = pd->bdev->bd_disk->events; @@ -2816,8 +2816,6 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) mutex_unlock(&ctl_mutex); return 0; -out_new_dev: - blk_cleanup_queue(disk->queue); out_mem2: put_disk(disk); out_mem: -- GitLab From 0a0e3ffb0a1445a756cc91cd9f03b1ac3abd2e32 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Jan 2018 10:06:03 +0100 Subject: [PATCH 2680/5498] ACPI: sbshc: remove raw pointer from printk() message commit 43cdd1b716b26f6af16da4e145b6578f98798bf6 upstream. There's no need to be printing a raw kernel pointer to the kernel log at every boot. So just remove it, and change the whole message to use the correct dev_info() call at the same time. Reported-by: Wang Qize Signed-off-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/sbshc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index bf034f8b7c1a..030ab2f543df 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -309,8 +309,8 @@ static int acpi_smbus_hc_add(struct acpi_device *device) device->driver_data = hc; acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc); - printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n", - hc->ec, hc->offset, hc->query_bit); + dev_info(&device->dev, "SBS HC: offset = 0x%0x, query_bit = 0x%0x\n", + hc->offset, hc->query_bit); return 0; } -- GitLab From e8cccd3ddb6b809032cb5177a1acd76ae3fa2636 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 1 Aug 2017 05:02:38 -0500 Subject: [PATCH 2681/5498] mn10300/misalignment: Use SIGSEGV SEGV_MAPERR to report a failed user copy commit 6ac1dc736b323011a55ecd1fc5897c24c4f77cbd upstream. Setting si_code to 0 is the same a setting si_code to SI_USER which is definitely not correct. With si_code set to SI_USER si_pid and si_uid will be copied to userspace instead of si_addr. Which is very wrong. So fix this by using a sensible si_code (SEGV_MAPERR) for this failure. Fixes: b920de1b77b7 ("mn10300: add the MN10300/AM33 architecture to the kernel") Cc: David Howells Cc: Masakazu Urade Cc: Koichi Yasutake Signed-off-by: "Eric W. Biederman" Signed-off-by: Greg Kroah-Hartman --- arch/mn10300/mm/misalignment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c index b9920b1edd5a..70cef54dc40f 100644 --- a/arch/mn10300/mm/misalignment.c +++ b/arch/mn10300/mm/misalignment.c @@ -437,7 +437,7 @@ transfer_failed: info.si_signo = SIGSEGV; info.si_errno = 0; - info.si_code = 0; + info.si_code = SEGV_MAPERR; info.si_addr = (void *) regs->pc; force_sig_info(SIGSEGV, &info, current); return; -- GitLab From 0c946219398a3108a9fe8dbc5096586bdcc797d6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Feb 2018 20:14:46 +0100 Subject: [PATCH 2682/5498] Linux 3.18.95 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 03a5b7e2076c..c550d72f10fa 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 18 -SUBLEVEL = 94 +SUBLEVEL = 95 EXTRAVERSION = NAME = Diseased Newt -- GitLab From b6f6ddfd06430281ad60e720966148f9ba8da5cf Mon Sep 17 00:00:00 2001 From: Tirupathi Reddy Date: Mon, 19 Feb 2018 11:16:54 +0530 Subject: [PATCH 2683/5498] ARM: dts: msm: add min HPM load parameter for PM660 regulators Use 100mA as minimum load requirement for configuring a SMPS to HPM mode and 10mA as minimum load requirement for configuring a LDO to HPM mode. Change-Id: Ia262afc8a60c7a19dc28a98dabe7ddc5c4708ac9 Signed-off-by: Tirupathi Reddy --- .../dts/qcom/msm-pm660-rpm-regulator.dtsi | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/qcom/msm-pm660-rpm-regulator.dtsi b/arch/arm/boot/dts/qcom/msm-pm660-rpm-regulator.dtsi index 6bbfbfd703f4..ff9125069862 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660-rpm-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660-rpm-regulator.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,6 +16,7 @@ qcom,resource-name = "smpa"; qcom,resource-id = <2>; qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; status = "disabled"; regulator-s2 { @@ -31,6 +32,7 @@ qcom,resource-name = "smpa"; qcom,resource-id = <3>; qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; status = "disabled"; regulator-s3 { @@ -46,6 +48,7 @@ qcom,resource-name = "smpa"; qcom,resource-id = <4>; qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; status = "disabled"; regulator-s4 { @@ -61,6 +64,7 @@ qcom,resource-name = "smpa"; qcom,resource-id = <5>; qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; status = "disabled"; regulator-s5 { @@ -76,6 +80,7 @@ qcom,resource-name = "smpa"; qcom,resource-id = <6>; qcom,regulator-type = <1>; + qcom,hpm-min-load = <100000>; status = "disabled"; regulator-s6 { @@ -91,6 +96,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <1>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l1 { @@ -106,6 +112,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <2>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l2 { @@ -121,6 +128,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <3>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l3 { @@ -136,6 +144,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <5>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l5 { @@ -151,6 +160,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <6>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l6 { @@ -166,6 +176,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <7>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l7 { @@ -181,6 +192,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <8>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l8 { @@ -196,6 +208,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <9>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l9 { @@ -211,6 +224,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <10>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l10 { @@ -226,6 +240,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <11>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l11 { @@ -241,6 +256,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <12>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l12 { @@ -256,6 +272,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <13>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l13 { @@ -271,6 +288,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <14>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l14 { @@ -286,6 +304,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <15>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l15 { @@ -301,6 +320,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <16>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l16 { @@ -316,6 +336,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <17>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l17 { @@ -331,6 +352,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <18>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l18 { @@ -346,6 +368,7 @@ qcom,resource-name = "ldoa"; qcom,resource-id = <19>; qcom,regulator-type = <0>; + qcom,hpm-min-load = <10000>; status = "disabled"; regulator-l19 { -- GitLab From 8692fd9511fabb0bb0f5f7bcf1a39926494895fe Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Mon, 19 Feb 2018 13:11:05 +0530 Subject: [PATCH 2684/5498] ASoC: msm: remove SEC_TLMM_CTL and MIC_EXT_CLK_CTL configuraton for TDM MUX configuration for SEC_TLMM_CTL and MIC_EXT_CLK_CTL registers in secondary TDM is effecting primary TDM usecase. Removing MUX configuration for this registers in secondary TDM as it not required for TDM startup. CRs-Fixed: 2189551 Change-Id: I549a1d1ec6dec3a9cd22c5af03af436babcb15a8 Signed-off-by: Surendar karka --- sound/soc/msm/msm8952-slimbus.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sound/soc/msm/msm8952-slimbus.c b/sound/soc/msm/msm8952-slimbus.c index 67b66e9f9e43..547181c0aa08 100644 --- a/sound/soc/msm/msm8952-slimbus.c +++ b/sound/soc/msm/msm8952-slimbus.c @@ -3170,22 +3170,6 @@ int msm_tdm_startup(struct snd_pcm_substream *substream) return -EINVAL; } - if (pdata->vaddr_gpio_mux_mic_ext_clk_ctl) { - val = ioread32(pdata->vaddr_gpio_mux_mic_ext_clk_ctl); - val = val | 0x00000001; - iowrite32(val, pdata->vaddr_gpio_mux_mic_ext_clk_ctl); - } else { - return -EINVAL; - } - - if (pdata->vaddr_gpio_mux_sec_tlmm_ctl) { - val = ioread32(pdata->vaddr_gpio_mux_sec_tlmm_ctl); - val = val | 0x00000002; - iowrite32(val, pdata->vaddr_gpio_mux_sec_tlmm_ctl); - } else { - return -EINVAL; - } - if (pdata->vaddr_gpio_mux_spkr_ctl) { val = ioread32(pdata->vaddr_gpio_mux_spkr_ctl); val = val | 0x00000002; -- GitLab From 7b223e0e0ae8ea18f8cbcaeb2e39621fdf1b856a Mon Sep 17 00:00:00 2001 From: Tharun Kumar Merugu Date: Tue, 30 Jan 2018 15:43:36 +0530 Subject: [PATCH 2685/5498] msm: ADSPRPC: Use ID in response to get context pointer Send context ID in rpc header instead of context pointer. Validate context ID received in response and get context pointer. Change-Id: I9cfd10d0c1b25c3085b8e15c7ca1c8ff214bf10d Acked-by: Viswanatham Paduchuri Signed-off-by: Tharun Kumar Merugu --- drivers/char/adsprpc.c | 77 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index e8e5dd912913..a8c2f05b314e 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -66,6 +66,8 @@ #define NUM_CHANNELS 3 /*1 compute 1 cpz 1 mdsp*/ #define NUM_SESSIONS 8 /*8 compute*/ #define FASTRPC_CTX_MAGIC (0xbeeddeed) +#define FASTRPC_CTX_MAX (256) +#define FASTRPC_CTXID_MASK (0xFF0) #define IS_CACHE_ALIGNED(x) (((x) & ((L1_CACHE_BYTES)-1)) == 0) @@ -155,6 +157,7 @@ struct smq_invoke_ctx { struct overlap **overps; struct smq_msg msg; unsigned int magic; + uint64_t ctxid; }; struct fastrpc_ctx_lst { @@ -218,6 +221,8 @@ struct fastrpc_apps { struct device *dev; struct device *modem_cma_dev; bool glink; + spinlock_t ctxlock; + struct smq_invoke_ctx *ctxtable[FASTRPC_CTX_MAX]; }; struct fastrpc_mmap { @@ -790,7 +795,8 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, struct fastrpc_ioctl_invoke_fd *invokefd, struct smq_invoke_ctx **po) { - int err = 0, bufs, size = 0; + int err = 0, bufs, ii, size = 0; + struct fastrpc_apps *me = &gfa; struct smq_invoke_ctx *ctx = NULL; struct fastrpc_ctx_lst *clst = &fl->clst; struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; @@ -841,6 +847,21 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, hlist_add_head(&ctx->hn, &clst->pending); spin_unlock(&fl->hlock); + spin_lock(&me->ctxlock); + for (ii = 0; ii < FASTRPC_CTX_MAX; ii++) { + if (!me->ctxtable[ii]) { + me->ctxtable[ii] = ctx; + ctx->ctxid = (ptr_to_uint64(ctx) & ~0xFFF)|(ii << 4); + break; + } + } + spin_unlock(&me->ctxlock); + VERIFY(err, ii < FASTRPC_CTX_MAX); + if (err) { + pr_err("adsprpc: out of context memory\n"); + goto bail; + } + *po = ctx; bail: if (ctx && err) @@ -862,6 +883,7 @@ static void context_save_interrupted(struct smq_invoke_ctx *ctx) static void context_free(struct smq_invoke_ctx *ctx) { int i; + struct fastrpc_apps *me = &gfa; int nbufs = REMOTE_SCALARS_INBUFS(ctx->sc) + REMOTE_SCALARS_OUTBUFS(ctx->sc); spin_lock(&ctx->fl->hlock); @@ -871,6 +893,17 @@ static void context_free(struct smq_invoke_ctx *ctx) fastrpc_mmap_free(ctx->maps[i]); fastrpc_buf_free(ctx->buf, 1); ctx->magic = 0; + ctx->ctxid = 0; + + spin_lock(&me->ctxlock); + for (i = 0; i < FASTRPC_CTX_MAX; i++) { + if (me->ctxtable[i] == ctx) { + me->ctxtable[i] = NULL; + break; + } + } + spin_unlock(&me->ctxlock); + kfree(ctx); } @@ -1269,7 +1302,7 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx, msg->tid = current->pid; if (kernel) msg->pid = 0; - msg->invoke.header.ctx = ptr_to_uint64(ctx); + msg->invoke.header.ctx = ctx->ctxid; msg->invoke.header.handle = handle; msg->invoke.header.sc = ctx->sc; msg->invoke.page.addr = ctx->buf ? ctx->buf->phys : 0; @@ -1295,20 +1328,31 @@ static void fastrpc_smd_read_handler(int cid) { struct fastrpc_apps *me = &gfa; struct smq_invoke_rsp rsp = {0}; - struct smq_invoke_ctx *ctx; int ret = 0, err = 0; + uint32_t index; do { ret = smd_read_from_cb(me->channel[cid].chan, &rsp, sizeof(rsp)); if (ret != sizeof(rsp)) break; - ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp.ctx)); - VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC)); + index = (uint32_t)((rsp.ctx & FASTRPC_CTXID_MASK) >> 4); + VERIFY(err, index < FASTRPC_CTX_MAX); if (err) goto bail; - context_notify_user(uint64_to_ptr(rsp.ctx), rsp.retval); + + VERIFY(err, !IS_ERR_OR_NULL(me->ctxtable[index])); + if (err) + goto bail; + + VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp.ctx)) && + me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC)); + if (err) + goto bail; + + context_notify_user(me->ctxtable[index], rsp.retval); } while (ret == sizeof(rsp)); + bail: if (err) pr_err("adsprpc: invalid response or context\n"); @@ -1338,6 +1382,7 @@ static void fastrpc_init(struct fastrpc_apps *me) INIT_HLIST_HEAD(&me->drivers); INIT_HLIST_HEAD(&me->fls); spin_lock_init(&me->hlock); + spin_lock_init(&me->ctxlock); mutex_init(&me->smd_mutex); me->channel = &gcinfo[0]; for (i = 0; i < NUM_CHANNELS; i++) { @@ -1872,19 +1917,29 @@ static void fastrpc_glink_notify_rx(void *handle, const void *priv, const void *pkt_priv, const void *ptr, size_t size) { struct smq_invoke_rsp *rsp = (struct smq_invoke_rsp *)ptr; - struct smq_invoke_ctx *ctx; + struct fastrpc_apps *me = &gfa; + uint32_t index; int err = 0; VERIFY(err, (rsp && size >= sizeof(*rsp))); if (err) goto bail; - ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp->ctx)); - VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC)); + index = (uint32_t)((rsp->ctx & FASTRPC_CTXID_MASK) >> 4); + VERIFY(err, index < FASTRPC_CTX_MAX); + if (err) + goto bail; + + VERIFY(err, !IS_ERR_OR_NULL(me->ctxtable[index])); + if (err) + goto bail; + + VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp->ctx)) && + me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC)); if (err) goto bail; - context_notify_user(ctx, rsp->retval); + context_notify_user(me->ctxtable[index], rsp->retval); bail: if (err) pr_err("adsprpc: invalid response or context\n"); -- GitLab From 323659bf501624e4879fde501c528f6fb6966b3d Mon Sep 17 00:00:00 2001 From: Krishna Manikandan Date: Fri, 16 Feb 2018 16:14:31 +0530 Subject: [PATCH 2686/5498] msm: mdss: Add check for SPI client Add check for spi client while using ion_import_dma_fd during mdp3_get_img. Change-Id: Ibd882725eb191454612397c2f2d6e5b28a712043 Signed-off-by: Krishna Manikandan --- drivers/video/msm/mdss/mdp3.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index c50f8a794015..dcdd4f34bb23 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -1917,15 +1917,16 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client) data->srcp_dma_buf = NULL; return ret; } - data->srcp_ihdl = ion_import_dma_buf(iclient, - img->memory_id); - if (IS_ERR_OR_NULL(data->srcp_ihdl)) { - pr_err("error on ion_import_fd\n"); - data->srcp_ihdl = NULL; - return -EIO; + if (client == MDP3_CLIENT_SPI) { + data->srcp_ihdl = ion_import_dma_buf(iclient, + img->memory_id); + if (IS_ERR_OR_NULL(data->srcp_ihdl)) { + pr_err("error on ion_import_fd\n"); + data->srcp_ihdl = NULL; + return -EIO; + } } - data->srcp_attachment = mdss_smmu_dma_buf_attach(data->srcp_dma_buf, &mdp3_res->pdev->dev, dom); -- GitLab From 3361d42bb3b8d03ef4d6592ba3f8955248e732e5 Mon Sep 17 00:00:00 2001 From: Arjun Singh Date: Mon, 19 Feb 2018 16:59:07 +0530 Subject: [PATCH 2687/5498] soc: qcom: bgrsb: resend rsb configuration to BG on resume Disables RSb before closing glink channel and resends configuration command to BG on device resume. Change-Id: I76839c5bab217262f82c5ddbb916cb386006dd21 Signed-off-by: Arjun Singh --- drivers/soc/qcom/bg_rsb.c | 55 ++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c index 3dfff8368151..44838a1e5d4a 100644 --- a/drivers/soc/qcom/bg_rsb.c +++ b/drivers/soc/qcom/bg_rsb.c @@ -134,6 +134,7 @@ struct bgrsb_priv { }; static void *bgrsb_drv; +static int bgrsb_enable(struct bgrsb_priv *dev, bool enable); int bgrsb_send_input(struct event *evnt) { @@ -420,14 +421,25 @@ static void bgrsb_bgdown_work(struct work_struct *work) static void bgrsb_glink_bgdown_work(struct work_struct *work) { + int rc; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_glink_down_work); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { - if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) == 0) - dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; - else - pr_err("Failed to unvote LDO-15 on BG down\n"); + + rc = bgrsb_enable(dev, false); + if (rc != 0) { + pr_err("Failed to send disable command to BG\n"); + return; + } + + if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) { + pr_err("Failed to un-vote LDO-15\n"); + return; + } + + dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; + pr_info("RSB Disabled\n"); } if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) { @@ -498,6 +510,18 @@ err_ret: return rc; } +static int bgrsb_enable(struct bgrsb_priv *dev, bool enable) +{ + int rc = 0; + struct bgrsb_msg req = {0}; + + req.cmd_id = 0x02; + req.data = enable ? 0x01 : 0x00; + + rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); + return rc; +} + static int bgrsb_configr_rsb(struct bgrsb_priv *dev, bool enable) { int rc = 0; @@ -620,7 +644,6 @@ static int bgrsb_ssr_register(struct bgrsb_priv *dev) static void bgrsb_enable_rsb(struct work_struct *work) { int rc = 0; - struct bgrsb_msg req = {0}; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_up_work); @@ -631,10 +654,7 @@ static void bgrsb_enable_rsb(struct work_struct *work) if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO15) == 0) { - req.cmd_id = 0x02; - req.data = 0x01; - - rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); + rc = bgrsb_enable(dev, true); if (rc != 0) { pr_err("Failed to send enable command to BG\n"); bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15); @@ -654,16 +674,12 @@ static void bgrsb_enable_rsb(struct work_struct *work) static void bgrsb_disable_rsb(struct work_struct *work) { int rc = 0; - struct bgrsb_msg req = {0}; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_down_work); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { - req.cmd_id = 0x02; - req.data = 0x00; - - rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); + rc = bgrsb_enable(dev, false); if (rc != 0) { pr_err("Failed to send disable command to BG\n"); return; @@ -954,6 +970,7 @@ static int bg_rsb_remove(struct platform_device *pdev) static int bg_rsb_resume(struct platform_device *pdev) { + int rc; struct bgrsb_priv *dev = platform_get_drvdata(pdev); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) @@ -961,6 +978,12 @@ static int bg_rsb_resume(struct platform_device *pdev) if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) { + rc = bgrsb_configr_rsb(dev, true); + if (rc != 0) { + pr_err("BG failed to configure RSB %d\n", rc); + bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11); + return rc; + } dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Cofigured\n"); return 0; @@ -979,7 +1002,7 @@ static int bg_rsb_suspend(struct platform_device *pdev, pm_message_t state) if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) - return -EINVAL; + goto ret_err; } if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) { @@ -987,6 +1010,8 @@ static int bg_rsb_suspend(struct platform_device *pdev, pm_message_t state) pr_debug("RSB Init\n"); return 0; } + +ret_err: pr_err("RSB failed to suspend\n"); return -EINVAL; } -- GitLab From 2f34c3ae21bebc5ecc2e8e4aba717b05c8420698 Mon Sep 17 00:00:00 2001 From: shwetha paalavalasa Date: Fri, 16 Feb 2018 15:11:35 +0530 Subject: [PATCH 2688/5498] Revert "ARM: dts: msm: enable SPI display for MSM8909w" This reverts commit ab02f9731a791a1c3d7e70e5ab7798f10670fe9d. Reverted due to framework reboot issue observed. Change-Id: I6826dbf4facd18b68fccf0e5bca91bb902267b21 Signed-off-by: Rajeswari Konda --- .../boot/dts/qcom/msm8909-mdss-panels.dtsi | 5 +- arch/arm/boot/dts/qcom/msm8909-mdss.dtsi | 23 ------- arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi | 4 +- arch/arm/boot/dts/qcom/msm8909.dtsi | 4 +- .../arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi | 30 --------- .../dts/qcom/msm8909w-pm660-regulator.dtsi | 4 +- arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts | 7 -- .../dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi | 67 ------------------- 8 files changed, 8 insertions(+), 136 deletions(-) delete mode 100644 arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi diff --git a/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi index a07011860ed2..9fadc4d67383 100644 --- a/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-mdss-panels.dtsi @@ -23,7 +23,6 @@ #include "dsi-panel-auo-qvga-cmd.dtsi" #include "dsi-panel-auo-cx-qvga-cmd.dtsi" #include "dsi-panel-390p-auo-cmd.dtsi" -#include "spi-panel-st7789v2-qvga-cmd.dtsi" &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { @@ -56,8 +55,8 @@ qcom,panel-supply-entry@0 { reg = <0>; qcom,supply-name = "vdd"; - qcom,supply-min-voltage = <2850000>; - qcom,supply-max-voltage = <2850000>; + qcom,supply-min-voltage = <3000000>; + qcom,supply-max-voltage = <3000000>; qcom,supply-enable-load = <100000>; qcom,supply-disable-load = <100>; }; diff --git a/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi index c3e2a2faf7cf..040b4b12fa4f 100644 --- a/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-mdss.dtsi @@ -153,29 +153,6 @@ 00 c0 00 00 00 00 00 01 bb]; }; }; - - mdss_spi: qcom,mdss_spi { - compatible = "qcom,mdss-spi-display"; - label = "mdss spi panel"; - - qcom,mdss-fb-map = <&mdss_fb0>; - qcom,mdss-mdp = <&mdss_mdp>; - vdd-supply = <&pm8909_l17>; - vddio-supply = <&pm8909_l6>; - - qcom,panel-supply-entries { - #address-cells = <1>; - #size-cells = <0>; - qcom,panel-supply-entry@0 { - reg = <0>; - qcom,supply-name = "vdd"; - qcom,supply-min-voltage = <2850000>; - qcom,supply-max-voltage = <2850000>; - qcom,supply-enable-load = <100000>; - qcom,supply-disable-load = <100>; - }; - }; - }; }; #include "msm8909-mdss-panels.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi index e332f8685a43..25688ff48824 100644 --- a/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi @@ -289,7 +289,7 @@ pmx_mdss { mdss_dsi_active: mdss_dsi_active { mux { - pins = "gpio25", "gpio37", "gpio59"; + pins = "gpio25", "gpio37"; function = "gpio"; }; @@ -302,7 +302,7 @@ mdss_dsi_suspend: mdss_dsi_suspend { mux { - pins = "gpio25", "gpio37", "gpio59"; + pins = "gpio25", "gpio37"; function = "gpio"; }; diff --git a/arch/arm/boot/dts/qcom/msm8909.dtsi b/arch/arm/boot/dts/qcom/msm8909.dtsi index 33365f853865..92bfddd1b8cb 100644 --- a/arch/arm/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909.dtsi @@ -1339,7 +1339,7 @@ status = "disabled"; }; - spi_4: spi@78b8000{ /* BLSP1 QUP4 */ + spi_4: spi@78B8000{ /* BLSP1 QUP4 */ compatible = "qcom,spi-qup-v2"; #address-cells = <1>; #size-cells = <0>; @@ -1362,7 +1362,7 @@ qcom,bam-consumer-pipe-index = <10>; qcom,bam-producer-pipe-index = <11>; qcom,master-id = <86>; - status = "ok"; + status = "disabled"; }; dma_blsp1: qcom,sps-dma@7884000 { /* BLSP1 */ diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi index 478ba358dd6a..5e3906e85282 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi @@ -20,16 +20,6 @@ /delete-property/ qcom,mipi-csi-vdd-supply; }; - spi_4 { - qcom,mdss_spi_client { - reg = <0>; - compatible = "qcom,mdss-spi-client"; - label = "MDSS SPI QUP4 CLIENT"; - dc-gpio = <&msm_gpio 59 0>; - spi-max-frequency = <50000000>; - }; - }; - i2c@78b9000 { synaptics@20 { /delete-property/ avdd-supply; @@ -202,26 +192,6 @@ qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>; }; -&spi_st7789v2_qvga_cmd { - qcom,panel-supply-entries = <&dsi_pm660_panel_pwr_supply>; - qcom,mdss-spi-bl-pmic-pwm-frequency = <100>; - qcom,mdss-spi-bl-pmic-bank-select = <0>; -}; - -&mdss_spi { - qcom,spi-pref-prim-pan = <&spi_st7789v2_qvga_cmd>; - pinctrl-names = "mdss_default", "mdss_sleep"; - pinctrl-0 = <&mdss_te_active>; - pinctrl-1 = <&mdss_te_suspend>; - - qcom,platform-te-gpio = <&msm_gpio 24 0>; - qcom,platform-reset-gpio = <&msm_gpio 25 0>; - qcom,platform-spi-dc-gpio = <&msm_gpio 59 0>; - - vdd-supply = <&pm660_l18>; - vddio-supply = <&pm660_l11>; -}; - &mdss_dsi{ vdda-supply = <&pm660_l5>; /*1.2V*/ vddio-supply = <&pm660_l12>; /*1.8V*/ diff --git a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi index a82b00c68e1b..c0dcf5578829 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909w-pm660-regulator.dtsi @@ -289,8 +289,8 @@ rpm-regulator-ldoa18 { status = "okay"; pm660_l18: regulator-l18 { - regulator-min-microvolt = <2850000>; - regulator-max-microvolt = <2850000>; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; qcom,init-voltage = <3000000>; status = "okay"; }; diff --git a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts index 47326dc60c41..8b043b355e3e 100644 --- a/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts +++ b/arch/arm/boot/dts/qcom/msm8909w-wtp-v1.dts @@ -75,13 +75,6 @@ spi@78b8000 { /* BLSP1 QUP4 */ status = "ok"; - qcom,mdss_spi_client { - reg = <0>; - compatible = "qcom,mdss-spi-client"; - label = "MDSS SPI QUP4 CLIENT"; - dc-gpio = <&msm_gpio 59 0>; - spi-max-frequency = <50000000>; - }; }; qcom,msm-ssc-sensors { diff --git a/arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi b/arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi deleted file mode 100644 index 945009245639..000000000000 --- a/arch/arm/boot/dts/qcom/spi-panel-st7789v2-qvga-cmd.dtsi +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -&mdss_mdp { - spi_st7789v2_qvga_cmd: qcom,mdss_spi_st7789v2_qvga_cmd { - qcom,mdss-spi-panel-name = - "st7789v2 qvga command mode spi panel"; - qcom,mdss-spi-panel-destination = "display_1"; - qcom,mdss-spi-panel-controller = <&mdss_spi>; - qcom,mdss-spi-panel-framerate = <27>; - qcom,mdss-spi-panel-width = <240>; - qcom,mdss-spi-panel-height = <240>; - qcom,mdss-spi-h-front-porch = <79>; - qcom,mdss-spi-h-back-porch = <59>; - qcom,mdss-spi-h-pulse-width = <60>; - qcom,mdss-spi-v-back-porch = <10>; - qcom,mdss-spi-v-front-porch = <7>; - qcom,mdss-spi-v-pulse-width = <2>; - qcom,mdss-spi-h-left-border = <0>; - qcom,mdss-spi-h-right-border = <0>; - qcom,mdss-spi-v-top-border = <0>; - qcom,mdss-spi-v-bottom-border = <0>; - qcom,mdss-spi-bpp = <16>; - qcom,mdss-spi-on-command = [ - 96 01 11 - 00 02 36 00 - 00 02 3A 05 - 00 02 35 00 - 00 06 B2 0C 0C 00 33 33 - 00 02 B7 75 - 00 02 BB 3D - 00 02 C2 01 - 00 02 C3 19 - 00 02 04 20 - 00 02 C6 0F - 00 03 D0 A4 A1 - 00 0F E0 70 04 08 09 09 05 2A 33 - 41 07 13 13 29 2F - 00 0F E1 70 03 09 0A 09 06 2B 34 - 41 07 12 14 28 2E - 00 01 21 - 00 01 29 - 00 05 2A 00 00 00 EF - 00 05 2B 00 00 00 EF - 00 01 2C]; - qcom,mdss-spi-off-command = [20 01 28 - 20 01 10]; - qcom,mdss-spi-bl-min-level = <1>; - qcom,mdss-spi-bl-max-level = <255>; - qcom,mdss-spi-bl-pmic-control-type = "bl_gpio_pulse"; - qcom,mdss-spi-reset-sequence = <1 20>, <0 1>, <1 20>; - qcom,mdss-spi-panel-status-check-mode = "reg_read"; - qcom,mdss-spi-panel-status-reg = /bits/ 8 <0x0A>; - qcom,mdss-spi-panel-status-read-length = <1>; - qcom,mdss-spi-panel-max-error-count = <1>; - qcom,mdss-spi-panel-status-value = /bits/ 8 <0x9C>; - }; -}; -- GitLab From cb1ae7bbbf8ac5d7ce6c481caa5ee99cc46d8635 Mon Sep 17 00:00:00 2001 From: Hardik Arya Date: Wed, 17 Jan 2018 21:03:52 +0530 Subject: [PATCH 2689/5498] diag: Validate copying length against source buffer length There a possibility of out-of-bound read because of not validating source buffer length against length that about to be copied. The patch adds proper check for validating length before copying data CRs-Fixed: 2163793 Change-Id: I7c93839d0c4d83024ce23a0ce494d09dd08567a9 Signed-off-by: Hardik Arya --- drivers/char/diag/diag_dci.c | 89 ++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 37cc6c777421..5c810cbf9d01 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1108,18 +1108,31 @@ void extract_dci_events(unsigned char *buf, int len, int data_source, int token) struct list_head *start, *temp; struct diag_dci_client_tbl *entry = NULL; - length = *(uint16_t *)(buf + 1); /* total length of event series */ - if (length == 0) { - pr_err("diag: Incoming dci event length is invalid\n"); + if (!buf) { + pr_err("diag: In %s buffer is NULL\n", __func__); return; } /* - * Move directly to the start of the event series. 1 byte for - * event code and 2 bytes for the length field. + * 1 byte for event code and 2 bytes for the length field. * The length field indicates the total length removing the cmd_code * and the lenght field. The event parsing in that case should happen * till the end. */ + if (len < 3) { + pr_err("diag: In %s invalid len: %d\n", __func__, len); + return; + } + length = *(uint16_t *)(buf + 1); /* total length of event series */ + if ((length == 0) || (len != (length + 3))) { + pr_err("diag: Incoming dci event length: %d is invalid\n", + length); + return; + } + /* + * Move directly to the start of the event series. + * The event parsing should happen from start of event + * series till the end. + */ temp_len = 3; while (temp_len < length) { event_id_packet = *(uint16_t *)(buf + temp_len); @@ -1136,30 +1149,60 @@ void extract_dci_events(unsigned char *buf, int len, int data_source, int token) * necessary. */ timestamp_len = 8; - memcpy(timestamp, buf + temp_len + 2, timestamp_len); + if ((temp_len + timestamp_len + 2) <= len) + memcpy(timestamp, buf + temp_len + 2, + timestamp_len); + else { + pr_err("diag: Invalid length in %s, len: %d, temp_len: %d", + __func__, len, temp_len); + return; + } } /* 13th and 14th bit represent the payload length */ if (((event_id_packet & 0x6000) >> 13) == 3) { payload_len_field = 1; - payload_len = *(uint8_t *) + if ((temp_len + timestamp_len + 3) <= len) { + payload_len = *(uint8_t *) (buf + temp_len + 2 + timestamp_len); - if (payload_len < (MAX_EVENT_SIZE - 13)) { - /* copy the payload length and the payload */ + } else { + pr_err("diag: Invalid length in %s, len: %d, temp_len: %d", + __func__, len, temp_len); + return; + } + if ((payload_len < (MAX_EVENT_SIZE - 13)) && + ((temp_len + timestamp_len + payload_len + 3) <= len)) { + /* + * Copy the payload length and the payload + * after skipping temp_len bytes for already + * parsed packet, timestamp_len for timestamp + * buffer, 2 bytes for event_id_packet. + */ memcpy(event_data + 12, buf + temp_len + 2 + timestamp_len, 1); memcpy(event_data + 13, buf + temp_len + 2 + timestamp_len + 1, payload_len); } else { - pr_err("diag: event > %d, payload_len = %d\n", - (MAX_EVENT_SIZE - 13), payload_len); + pr_err("diag: event > %d, payload_len = %d, temp_len = %d\n", + (MAX_EVENT_SIZE - 13), payload_len, temp_len); return; } } else { payload_len_field = 0; payload_len = (event_id_packet & 0x6000) >> 13; - /* copy the payload */ - memcpy(event_data + 12, buf + temp_len + 2 + + /* + * Copy the payload after skipping temp_len bytes + * for already parsed packet, timestamp_len for + * timestamp buffer, 2 bytes for event_id_packet. + */ + if ((payload_len < (MAX_EVENT_SIZE - 12)) && + ((temp_len + timestamp_len + payload_len + 2) <= len)) + memcpy(event_data + 12, buf + temp_len + 2 + timestamp_len, payload_len); + else { + pr_err("diag: event > %d, payload_len = %d, temp_len = %d\n", + (MAX_EVENT_SIZE - 12), payload_len, temp_len); + return; + } } /* Before copying the data to userspace, check if we are still @@ -1277,19 +1320,19 @@ void extract_dci_log(unsigned char *buf, int len, int data_source, int token) pr_err("diag: In %s buffer is NULL\n", __func__); return; } - - /* The first six bytes for the incoming log packet contains - * Command code (2), the length of the packet (2) and the length - * of the log (2) + /* + * The first eight bytes for the incoming log packet contains + * Command code (2), the length of the packet (2), the length + * of the log (2) and log code (2) */ - log_code = *(uint16_t *)(buf + 6); - read_bytes += sizeof(uint16_t) + 6; - if (read_bytes > len) { - pr_err("diag: Invalid length in %s, len: %d, read: %d", - __func__, len, read_bytes); + if (len < 8) { + pr_err("diag: In %s invalid len: %d\n", __func__, len); return; } + log_code = *(uint16_t *)(buf + 6); + read_bytes += sizeof(uint16_t) + 6; + /* parse through log mask table of each client and check mask */ mutex_lock(&driver->dci_mutex); list_for_each_safe(start, temp, &driver->dci_client_list) { -- GitLab From 0ef6e09306ff6e29df7b3085579fc0520e0dd9a1 Mon Sep 17 00:00:00 2001 From: Rama Krishna Phani A Date: Mon, 19 Feb 2018 15:04:32 +0530 Subject: [PATCH 2690/5498] thermal: tsens: Update variable type From the logic implemented, there is a chance that sensor_hw_num can get a negative value. Update type of sensor_hw_num such that it can hold both positive and negative values. Change-Id: Ib05aedea90bc5797140728b6e64f15e5c11f9b25 Signed-off-by: Rama Krishna Phani A --- drivers/thermal/msm-tsens.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index 2a9d6f6210fa..1d97c1e716b0 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1393,7 +1393,8 @@ static int msm_tsens_get_temp(int sensor_client_id, unsigned long *temp) bool last_temp_valid = false, last_temp2_valid = false; bool last_temp3_valid = false; struct tsens_tm_device *tmdev = NULL; - uint32_t sensor_hw_num = 0, idx = 0; + uint32_t idx = 0; + int sensor_hw_num = 0; unsigned long flags; tmdev = get_tsens_controller_for_client_id(sensor_client_id); -- GitLab From 638738044bd07c0d6a40225ad7dbae89cf99c440 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Thu, 15 Feb 2018 20:36:43 +0530 Subject: [PATCH 2691/5498] ASoC: bg: Add check to send speaker init params to BG Ensure to send speaker init params only when speaker is connected to target. Change-Id: I5ba0be58cfb292c2530148a588e3f006ac07ef29 Signed-off-by: Bala Kishore Pati --- .../bindings/sound/qcom-audio-dev.txt | 4 ++ sound/soc/codecs/bg_codec.c | 52 ++++++++++++------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index cb593afb9117..e006a9ff715d 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2966,6 +2966,10 @@ Required properties: with the glink client - vdd-spkr-supply: BG codec supply's speaker regulator device tree node. +Optional properties: +- qcom,bg-speaker-connected: This flag will notify BG codec driver that speaker + is connected to target or not. Based on this flag BG + codec driver will send smart pa init params to BG. Example: diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index 954bdb77f4ec..efbcc586ab1d 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -106,6 +106,7 @@ struct bg_cdc_priv { bool bg_cal_updated; bool adsp_dev_up; bool bg_dev_up; + bool bg_spk_connected; struct regulator *spkr_vreg; uint16_t num_sessions; }; @@ -230,7 +231,7 @@ static int bg_cdc_cal(struct bg_cdc_priv *bg_cdc) bg_cdc->hwdep_spk_cal = NULL; if (bg_cdc->hwdep_mic_cal) { - pr_debug("%s:mic cal size %d", __func__, + pr_debug("%s:mic cal size %d\n", __func__, bg_cdc->hwdep_mic_cal->size); memcpy(init_params, &bg_cdc->hwdep_mic_cal->size, sizeof(bg_cdc->hwdep_mic_cal->size)); @@ -239,7 +240,8 @@ static int bg_cdc_cal(struct bg_cdc_priv *bg_cdc) bg_cdc->hwdep_mic_cal->size); init_params += bg_cdc->hwdep_mic_cal->size; } else { - pr_debug("%s:default mic cal size %d", __func__, mic_blob_size); + pr_debug("%s:default mic cal size %d\n", __func__, + mic_blob_size); memcpy(init_params, &mic_blob_size, sizeof(mic_blob_size)); init_params += sizeof(mic_blob_size); @@ -247,23 +249,26 @@ static int bg_cdc_cal(struct bg_cdc_priv *bg_cdc) sizeof(app_mic_init_params)); init_params += sizeof(app_mic_init_params); } - if (bg_cdc->hwdep_spk_cal) { - pr_debug("%s: spk cal size %d", __func__, - bg_cdc->hwdep_spk_cal->size); - memcpy(init_params, &bg_cdc->hwdep_spk_cal->size, - sizeof(bg_cdc->hwdep_spk_cal->size)); - init_params += sizeof(bg_cdc->hwdep_spk_cal->size); - memcpy(init_params, bg_cdc->hwdep_spk_cal->data, - bg_cdc->hwdep_spk_cal->size); - } else { - pr_debug("%s: default spk cal size %d", __func__, - spk_blob_size); - memcpy(init_params, &spk_blob_size, - sizeof(spk_blob_size)); - init_params += sizeof(spk_blob_size); - memcpy(init_params, smart_pa_init_params, - sizeof(smart_pa_init_params)); - } + if (bg_cdc->bg_spk_connected) { + if (bg_cdc->hwdep_spk_cal) { + pr_debug("%s: spk cal size %d\n", __func__, + bg_cdc->hwdep_spk_cal->size); + memcpy(init_params, &bg_cdc->hwdep_spk_cal->size, + sizeof(bg_cdc->hwdep_spk_cal->size)); + init_params += sizeof(bg_cdc->hwdep_spk_cal->size); + memcpy(init_params, bg_cdc->hwdep_spk_cal->data, + bg_cdc->hwdep_spk_cal->size); + } else { + pr_debug("%s: default spk cal size %d\n", __func__, + spk_blob_size); + memcpy(init_params, &spk_blob_size, + sizeof(spk_blob_size)); + init_params += sizeof(spk_blob_size); + memcpy(init_params, smart_pa_init_params, + sizeof(smart_pa_init_params)); + } + } else + pr_debug("%s: spk not connected ignoring spk cal\n", __func__); rsp.buf_size = sizeof(struct graphite_basic_rsp_result); rsp.buf = kzalloc(rsp.buf_size, GFP_KERNEL); if (!rsp.buf) { @@ -592,6 +597,10 @@ static int bg_cdc_hw_params(struct snd_pcm_substream *substream, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (!bg_cdc->bg_spk_connected) { + pr_err("%s:speaker not connected\n", __func__); + return -EINVAL; + } bg_cdc->hw_params.rx_sample_rate = params_rate(params); bg_cdc->hw_params.rx_bit_width = params_width(params); bg_cdc->hw_params.rx_num_channels = params_channels(params); @@ -1197,6 +1206,11 @@ static int bg_cdc_probe(struct platform_device *pdev) } dev_dbg(&pdev->dev, "%s: got regulator handle\n", __func__); } + bg_cdc->bg_spk_connected = of_property_read_bool(pdev->dev.of_node, + "qcom,bg-speaker-connected"); + if (!bg_cdc->bg_spk_connected) + dev_info(&pdev->dev, "%s: speaker not connected to target %d\n", + __func__, bg_cdc->bg_spk_connected); ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bg_cdc, bg_cdc_dai, ARRAY_SIZE(bg_cdc_dai)); -- GitLab From d3570782467cbb2d410d70e2d004be9db894b740 Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Thu, 15 Feb 2018 19:59:45 +0530 Subject: [PATCH 2692/5498] ARM: dts: msm: add bg-speaker-connected property on msm8909w On msm8909w targets speaker usage is optional. If speaker is not connected, no need to send smart pa init params to BG. So add property to ensure speaker is connected to target. This property can be defined based on the speaker connections/requirement. Change-Id: I5e22e8e9f44c82bb17b5e87677bd0efcff65fefa Signed-off-by: Bala Kishore Pati --- arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi index f2cea322ecc5..d41a5db35109 100644 --- a/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi +++ b/arch/arm/boot/dts/qcom/msm8909-audio-bg_codec.dtsi @@ -187,6 +187,7 @@ status = "disabled"; compatible = "qcom,bg-codec"; qcom,subsys-name = "modem"; + qcom,bg-speaker-connected; qcom,bg-glink { compatible = "qcom,bg-cdc-glink"; qcom,msm-glink-channels = <4>; -- GitLab From 29bed63424fb3c8a98cfae4a664d032d24cf1670 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 16 Nov 2017 16:59:14 +0800 Subject: [PATCH 2693/5498] f2fs: updates on v4.16-rc1 Pull f2fs updates from Jaegeuk Kim: "In this round, we've followed up to support some generic features such as cgroup, block reservation, linking fscrypt_ops, delivering write_hints, and some ioctls. And, we could fix some corner cases in terms of power-cut recovery and subtle deadlocks. Enhancements: - bitmap operations to handle NAT blocks - readahead to improve readdir speed - switch to use fscrypt_* - apply write hints for direct IO - add reserve_root=%u,resuid=%u,resgid=%u to reserve blocks for root/uid/gid - modify b_avail and b_free to consider root reserved blocks - support cgroup writeback - support FIEMAP_FLAG_XATTR for fibmap - add F2FS_IOC_PRECACHE_EXTENTS to pre-cache extents - add F2FS_IOC_{GET/SET}_PIN_FILE to pin LBAs for data blocks - support inode creation time Bug fixs: - sysfile-based quota operations - memory footprint accounting - allow to write data on partial preallocation case - fix deadlock case on fallocate - fix to handle fill_super errors - fix missing inode updates of fsync'ed file - recover renamed file which was fsycn'ed before - drop inmemory pages in corner error case - keep last_disk_size correctly - recover missing i_inline flags during roll-forward Various clean-up patches were added as well" Cherry-pick from origin/upstream-f2fs-stable-linux-3.18.y: b85b2040e29f f2fs: support inode creation time c72ef4727838 f2fs: rebuild sit page from sit info in mem f368156e84cb f2fs: stop issuing discard if fs is readonly be823aa3d5af f2fs: clean up duplicated assignment in init_discard_policy 67ebfab1a0f4 f2fs: use GFP_F2FS_ZERO for cleanup 2ebcc5d82d9a f2fs: allow to recover node blocks given updated checkpoint 2ea7d6f1bf18 f2fs: recover some i_inline flags 2e1f3aa5affb f2fs: correct removexattr behavior for null valued extended attribute c8330bdcc35f f2fs: drop page cache after fs shutdown ef83735b3932 f2fs: stop gc/discard thread after fs shutdown cb472c71d865 f2fs: hanlde error case in f2fs_ioc_shutdown b66b08d969c4 f2fs: split need_inplace_update af69483e9316 f2fs: fix to update last_disk_size correctly dc4b6954c141 f2fs: kill F2FS_INLINE_XATTR_ADDRS for cleanup 13240c90fdcc f2fs: clean up error path of fill_super 2e5c39e53996 f2fs: avoid hungtask when GC encrypted block if io_bits is set a14cced5bf6a f2fs: allow quota to use reserved blocks 859cc83798e0 f2fs: fix to drop all inmem pages correctly 8975d2b938bf f2fs: speed up defragment on sparse file 06e30f6f9cec f2fs: support F2FS_IOC_PRECACHE_EXTENTS f3d5ace5b527 f2fs: add an ioctl to disable GC for specific file 11228b15cfa4 f2fs: prevent newly created inode from being dirtied incorrectly 623e284104f8 f2fs: support FIEMAP_FLAG_XATTR 2075b0e8a63e f2fs: fix to cover f2fs_inline_data_fiemap with inode_lock 0ea602b1ab49 f2fs: check node page again in write end io d503f1e0c9fa f2fs: fix to caclulate required free section correctly e72c42370ccf f2fs: handle newly created page when revoking inmem pages 177018aacbc4 f2fs: add resgid and resuid to reserve root blocks 6ad1915c61d4 f2fs: implement cgroup writeback support 1ee182bcc114 f2fs: remove unused pend_list_tag e732db71ba44 f2fs: avoid high cpu usage in discard thread 647763fa612b f2fs: make local functions static 3f81bf527a5f f2fs: add reserved blocks for root user cb4ea095ee56 f2fs: check segment type in __f2fs_replace_block 2a6f5454a49c f2fs: update inode info to inode page for new file db2e6b82f45d f2fs: show precise # of blocks that user/root can use add96ed3f86b f2fs: clean up unneeded declaration babfbc088938 f2fs: continue to do direct IO if we only preallocate partial blocks f928990854bb f2fs: enable quota at remount from r to w cfee78c6767b f2fs: skip stop_checkpoint for user data writes 29f0297fef9d f2fs: fix missing error number for xattr operation 1e85f5d70912 f2fs: recover directory operations by fsync f1b68a501b21 f2fs: return error during fill_super e913b1903a98 f2fs: fix an error case of missing update inode page 62b6a5f6896a f2fs: fix potential hangtask in f2fs_trace_pid 54c06e52d661 f2fs: no need return value in restore summary process e88ab6696058 f2fs: use unlikely for release case 246018281458 f2fs: don't return value in truncate_data_blocks_range 15f92902926e f2fs: clean up f2fs_map_blocks 8dfee8c44a14 f2fs: clean up hash codes 5d81acf57de6 f2fs: fix error handling in fill_super 3acc2f316ff1 f2fs: spread f2fs_k{m,z}alloc 8c72d9db0e50 f2fs: inject fault to kvmalloc fc42fc2cd84d f2fs: inject fault to kzalloc c821080ddd53 f2fs: remove a redundant conditional expression 612e589b373f f2fs: apply write hints to select the type of segment for direct write 63a9fc8008ed f2fs: switch to fscrypt_prepare_setattr() 16c5bfa10f1f f2fs: switch to fscrypt_prepare_lookup() 5998a21bdab5 f2fs: switch to fscrypt_prepare_rename() dd5ca5fe3666 f2fs: switch to fscrypt_prepare_link() 09c91079d653 f2fs: switch to fscrypt_file_open() 08cae724087a f2fs: remove repeated f2fs_bug_on 7357b4521f50 f2fs: remove an excess variable 6f2915eb151e f2fs: fix lock dependency in between dio_rwsem & i_mmap_sem 8c3b1444df49 f2fs: remove unused parameter 35b94063c4d7 f2fs: still write data if preallocate only partial blocks b6453fcb2fe8 f2fs: do not preallocate blocks which has wrong buffer bee58ad4c31c f2fs: introduce sysfs readdir_ra to readahead inode block in readdir 5b10dbdef98c f2fs: fix concurrent problem for updating free bitmap 2638ff752042 f2fs: remove unneeded memory footprint accounting c569c0b1124b f2fs: no need to read nat block if nat_block_bitmap is set 5321a23c0c19 f2fs: reserve nid resource for quota sysfile Change-Id: I5f95446f4d51232e82f48b716e5796d871e2d9ee Signed-off-by: Jaegeuk Kim --- Documentation/ABI/testing/sysfs-fs-f2fs | 6 + fs/f2fs/checkpoint.c | 10 +- fs/f2fs/data.c | 308 ++++++++++++++++++++---- fs/f2fs/debug.c | 12 +- fs/f2fs/dir.c | 6 + fs/f2fs/f2fs.h | 217 +++++++++++++---- fs/f2fs/file.c | 281 ++++++++++++++++----- fs/f2fs/gc.c | 18 +- fs/f2fs/gc.h | 2 + fs/f2fs/inode.c | 34 ++- fs/f2fs/namei.c | 67 ++---- fs/f2fs/node.c | 149 ++++++------ fs/f2fs/node.h | 4 + fs/f2fs/recovery.c | 27 ++- fs/f2fs/segment.c | 131 +++++----- fs/f2fs/segment.h | 92 ++++--- fs/f2fs/super.c | 145 ++++++++--- fs/f2fs/sysfs.c | 14 +- fs/f2fs/trace.c | 12 +- fs/f2fs/xattr.c | 12 +- include/linux/f2fs_fs.h | 14 +- include/trace/events/f2fs.h | 3 +- 22 files changed, 1117 insertions(+), 447 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index 2baed1151eac..db7aab1516de 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -186,3 +186,9 @@ Date: August 2017 Contact: "Jaegeuk Kim" Description: Controls sleep time of GC urgent mode + +What: /sys/fs/f2fs//readdir_ra +Date: November 2017 +Contact: "Sheng Yong" +Description: + Controls readahead inode block in readdir. diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 288966b4c89d..8c7556b7da62 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -238,12 +238,15 @@ static int __f2fs_write_meta_page(struct page *page, trace_f2fs_writepage(page, META); + if (unlikely(f2fs_cp_error(sbi))) { + dec_page_count(sbi, F2FS_DIRTY_META); + unlock_page(page); + return 0; + } if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0)) goto redirty_out; - if (unlikely(f2fs_cp_error(sbi))) - goto redirty_out; write_meta_page(sbi, page, io_type); dec_page_count(sbi, F2FS_DIRTY_META); @@ -797,7 +800,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) block_t cp_blk_no; int i; - sbi->ckpt = kzalloc(cp_blks * blk_size, GFP_KERNEL); + sbi->ckpt = f2fs_kzalloc(sbi, cp_blks * blk_size, GFP_KERNEL); if (!sbi->ckpt) return -ENOMEM; /* @@ -1158,6 +1161,7 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* set this flag to activate crc|cp_ver for recovery */ __set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); + __clear_ckpt_flags(ckpt, CP_NOCRC_RECOVERY_FLAG); spin_unlock_irqrestore(&sbi->cp_lock, flags); } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8a26a069d3c9..12597399f0d6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -112,8 +112,13 @@ static void f2fs_write_end_io(struct bio *bio, int err) if (unlikely(err)) { set_bit(AS_EIO, &page->mapping->flags); - f2fs_stop_checkpoint(sbi, true); + if (type == F2FS_WB_CP_DATA) + f2fs_stop_checkpoint(sbi, true); } + + f2fs_bug_on(sbi, page->mapping == NODE_MAPPING(sbi) && + page->index != nid_of_node(page)); + dec_page_count(sbi, type); clear_cold_data(page); end_page_writeback(page); @@ -169,6 +174,7 @@ static bool __same_bdev(struct f2fs_sb_info *sbi, * Low-level block read/write IO operations. */ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, + struct writeback_control *wbc, int npages, bool is_read) { struct bio *bio; @@ -178,6 +184,8 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, f2fs_target_device(sbi, blk_addr, bio); bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; bio->bi_private = is_read ? NULL : sbi; + if (wbc) + wbc_init_bio(wbc, bio); return bio; } @@ -373,7 +381,8 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) f2fs_trace_ios(fio, 0); /* Allocate a new bio */ - bio = __bio_alloc(fio->sbi, fio->new_blkaddr, 1, is_read_io(fio->op)); + bio = __bio_alloc(fio->sbi, fio->new_blkaddr, fio->io_wbc, + 1, is_read_io(fio->op)); if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { bio_put(bio); @@ -435,7 +444,7 @@ alloc_new: dec_page_count(sbi, WB_DATA_TYPE(bio_page)); goto out_fail; } - io->bio = __bio_alloc(sbi, fio->new_blkaddr, + io->bio = __bio_alloc(sbi, fio->new_blkaddr, fio->io_wbc, BIO_MAX_PAGES, false); io->fio = *fio; } @@ -445,6 +454,9 @@ alloc_new: goto alloc_new; } + if (fio->io_wbc) + wbc_account_io(fio->io_wbc, bio_page, PAGE_SIZE); + io->last_block_in_bio = fio->new_blkaddr; f2fs_trace_ios(fio, 0); @@ -783,7 +795,7 @@ got_it: return page; } -static int __allocate_data_block(struct dnode_of_data *dn) +static int __allocate_data_block(struct dnode_of_data *dn, int seg_type) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_summary sum; @@ -808,7 +820,7 @@ alloc: set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); allocate_data_block(sbi, NULL, dn->data_blkaddr, &dn->data_blkaddr, - &sum, CURSEG_WARM_DATA, NULL, false); + &sum, seg_type, NULL, false); set_data_blkaddr(dn); /* update i_size */ @@ -828,18 +840,22 @@ static inline bool __force_buffered_io(struct inode *inode, int rw) } int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, - size_t count, bool dio) + size_t count, bool direct_io) { struct f2fs_map_blocks map; + int flag; int err = 0; /* convert inline data for Direct I/O*/ - if (dio) { + if (direct_io) { err = f2fs_convert_inline_inode(inode); if (err) return err; } + if (is_inode_flag_set(inode, FI_NO_PREALLOC)) + return 0; + map.m_lblk = F2FS_BLK_ALIGN(pos); map.m_len = F2FS_BYTES_TO_BLK(pos + count); if (map.m_len > map.m_lblk) @@ -848,19 +864,34 @@ int f2fs_preallocate_blocks(struct inode *inode, loff_t pos, map.m_len = 0; map.m_next_pgofs = NULL; + map.m_next_extent = NULL; + map.m_seg_type = NO_CHECK_TYPE; - if (dio) - return f2fs_map_blocks(inode, &map, 1, - __force_buffered_io(inode, WRITE) ? - F2FS_GET_BLOCK_PRE_AIO : - F2FS_GET_BLOCK_PRE_DIO); + if (direct_io) { + /* map.m_seg_type = rw_hint_to_seg_type(iocb->ki_hint); */ + map.m_seg_type = rw_hint_to_seg_type(WRITE_LIFE_NOT_SET); + flag = __force_buffered_io(inode, WRITE) ? + F2FS_GET_BLOCK_PRE_AIO : + F2FS_GET_BLOCK_PRE_DIO; + goto map_blocks; + } if (pos + count > MAX_INLINE_DATA(inode)) { err = f2fs_convert_inline_inode(inode); if (err) return err; } - if (!f2fs_has_inline_data(inode)) - return f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO); + if (f2fs_has_inline_data(inode)) + return err; + + flag = F2FS_GET_BLOCK_PRE_AIO; + +map_blocks: + err = f2fs_map_blocks(inode, &map, 1, flag); + if (map.m_len > 0 && err == -ENOSPC) { + if (!direct_io) + set_inode_flag(inode, FI_NO_PREALLOC); + err = 0; + } return err; } @@ -901,6 +932,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, blkcnt_t prealloc; struct extent_info ei = {0,0,0}; block_t blkaddr; + unsigned int start_pgofs; if (!maxblocks) return 0; @@ -916,6 +948,8 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, map->m_pblk = ei.blk + pgofs - ei.fofs; map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs); map->m_flags = F2FS_MAP_MAPPED; + if (map->m_next_extent) + *map->m_next_extent = pgofs + map->m_len; goto out; } @@ -934,10 +968,14 @@ next_dnode: if (map->m_next_pgofs) *map->m_next_pgofs = get_next_page_offset(&dn, pgofs); + if (map->m_next_extent) + *map->m_next_extent = + get_next_page_offset(&dn, pgofs); } goto unlock_out; } + start_pgofs = pgofs; prealloc = 0; last_ofs_in_node = ofs_in_node = dn.ofs_in_node; end_offset = ADDRS_PER_PAGE(dn.node_page, inode); @@ -957,7 +995,8 @@ next_block: last_ofs_in_node = dn.ofs_in_node; } } else { - err = __allocate_data_block(&dn); + err = __allocate_data_block(&dn, + map->m_seg_type); if (!err) set_inode_flag(inode, FI_APPEND_WRITE); } @@ -970,14 +1009,20 @@ next_block: map->m_pblk = 0; goto sync_out; } + if (flag == F2FS_GET_BLOCK_PRECACHE) + goto sync_out; if (flag == F2FS_GET_BLOCK_FIEMAP && blkaddr == NULL_ADDR) { if (map->m_next_pgofs) *map->m_next_pgofs = pgofs + 1; + goto sync_out; } - if (flag != F2FS_GET_BLOCK_FIEMAP || - blkaddr != NEW_ADDR) + if (flag != F2FS_GET_BLOCK_FIEMAP) { + /* for defragment case */ + if (map->m_next_pgofs) + *map->m_next_pgofs = pgofs + 1; goto sync_out; + } } } @@ -1028,6 +1073,16 @@ skip: else if (dn.ofs_in_node < end_offset) goto next_block; + if (flag == F2FS_GET_BLOCK_PRECACHE) { + if (map->m_flags & F2FS_MAP_MAPPED) { + unsigned int ofs = start_pgofs - map->m_lblk; + + f2fs_update_extent_cache_range(&dn, + start_pgofs, map->m_pblk + ofs, + map->m_len - ofs); + } + } + f2fs_put_dnode(&dn); if (create) { @@ -1037,6 +1092,17 @@ skip: goto next_dnode; sync_out: + if (flag == F2FS_GET_BLOCK_PRECACHE) { + if (map->m_flags & F2FS_MAP_MAPPED) { + unsigned int ofs = start_pgofs - map->m_lblk; + + f2fs_update_extent_cache_range(&dn, + start_pgofs, map->m_pblk + ofs, + map->m_len - ofs); + } + if (map->m_next_extent) + *map->m_next_extent = pgofs + 1; + } f2fs_put_dnode(&dn); unlock_out: if (create) { @@ -1050,7 +1116,7 @@ out: static int __get_data_block(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create, int flag, - pgoff_t *next_pgofs) + pgoff_t *next_pgofs, int seg_type) { struct f2fs_map_blocks map; int err; @@ -1058,6 +1124,8 @@ static int __get_data_block(struct inode *inode, sector_t iblock, map.m_lblk = iblock; map.m_len = bh->b_size >> inode->i_blkbits; map.m_next_pgofs = next_pgofs; + map.m_next_extent = NULL; + map.m_seg_type = seg_type; err = f2fs_map_blocks(inode, &map, create, flag); if (!err) { @@ -1073,14 +1141,18 @@ static int get_data_block(struct inode *inode, sector_t iblock, pgoff_t *next_pgofs) { return __get_data_block(inode, iblock, bh_result, create, - flag, next_pgofs); + flag, next_pgofs, + NO_CHECK_TYPE); } static int get_data_block_dio(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_DEFAULT, NULL); + F2FS_GET_BLOCK_DEFAULT, NULL, + rw_hint_to_seg_type( + WRITE_LIFE_NOT_SET)); + /* inode->i_write_hint)); */ } static int get_data_block_bmap(struct inode *inode, sector_t iblock, @@ -1091,7 +1163,8 @@ static int get_data_block_bmap(struct inode *inode, sector_t iblock, return -EFBIG; return __get_data_block(inode, iblock, bh_result, create, - F2FS_GET_BLOCK_BMAP, NULL); + F2FS_GET_BLOCK_BMAP, NULL, + NO_CHECK_TYPE); } static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) @@ -1104,6 +1177,68 @@ static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) return (blk << inode->i_blkbits); } +static int f2fs_xattr_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct page *page; + struct node_info ni; + __u64 phys = 0, len; + __u32 flags; + nid_t xnid = F2FS_I(inode)->i_xattr_nid; + int err = 0; + + if (f2fs_has_inline_xattr(inode)) { + int offset; + + page = f2fs_grab_cache_page(NODE_MAPPING(sbi), + inode->i_ino, false); + if (!page) + return -ENOMEM; + + get_node_info(sbi, inode->i_ino, &ni); + + phys = (__u64)blk_to_logical(inode, ni.blk_addr); + offset = offsetof(struct f2fs_inode, i_addr) + + sizeof(__le32) * (DEF_ADDRS_PER_INODE - + get_inline_xattr_addrs(inode)); + + phys += offset; + len = inline_xattr_size(inode); + + f2fs_put_page(page, 1); + + flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED; + + if (!xnid) + flags |= FIEMAP_EXTENT_LAST; + + err = fiemap_fill_next_extent(fieinfo, 0, phys, len, flags); + if (err || err == 1) + return err; + } + + if (xnid) { + page = f2fs_grab_cache_page(NODE_MAPPING(sbi), xnid, false); + if (!page) + return -ENOMEM; + + get_node_info(sbi, xnid, &ni); + + phys = (__u64)blk_to_logical(inode, ni.blk_addr); + len = inode->i_sb->s_blocksize; + + f2fs_put_page(page, 1); + + flags = FIEMAP_EXTENT_LAST; + } + + if (phys) + err = fiemap_fill_next_extent(fieinfo, 0, phys, len, flags); + + return (err < 0 ? err : 0); +} + int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { @@ -1114,18 +1249,29 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u32 flags = 0; int ret = 0; - ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); + if (fieinfo->fi_flags & FIEMAP_FLAG_CACHE) { + ret = f2fs_precache_extents(inode); + if (ret) + return ret; + } + + ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR); if (ret) return ret; + inode_lock(inode); + + if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) { + ret = f2fs_xattr_fiemap(inode, fieinfo); + goto out; + } + if (f2fs_has_inline_data(inode)) { ret = f2fs_inline_data_fiemap(inode, fieinfo, start, len); if (ret != -EAGAIN) - return ret; + goto out; } - inode_lock(inode); - if (logical_to_blk(inode, len) == 0) len = blk_to_logical(inode, 1); @@ -1195,7 +1341,6 @@ static int f2fs_mpage_readpages(struct address_space *mapping, unsigned nr_pages) { struct bio *bio = NULL; - unsigned page_idx; sector_t last_block_in_bio = 0; struct inode *inode = mapping->host; const unsigned blkbits = inode->i_blkbits; @@ -1211,9 +1356,10 @@ static int f2fs_mpage_readpages(struct address_space *mapping, map.m_len = 0; map.m_flags = 0; map.m_next_pgofs = NULL; + map.m_next_extent = NULL; + map.m_seg_type = NO_CHECK_TYPE; - for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { - + for (; nr_pages; nr_pages--) { if (pages) { page = list_last_entry(pages, struct page, lru); @@ -1372,18 +1518,79 @@ retry_encrypt: return PTR_ERR(fio->encrypted_page); } +static inline bool check_inplace_update_policy(struct inode *inode, + struct f2fs_io_info *fio) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + unsigned int policy = SM_I(sbi)->ipu_policy; + + if (policy & (0x1 << F2FS_IPU_FORCE)) + return true; + if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) + return true; + if (policy & (0x1 << F2FS_IPU_UTIL) && + utilization(sbi) > SM_I(sbi)->min_ipu_util) + return true; + if (policy & (0x1 << F2FS_IPU_SSR_UTIL) && need_SSR(sbi) && + utilization(sbi) > SM_I(sbi)->min_ipu_util) + return true; + + /* + * IPU for rewrite async pages + */ + if (policy & (0x1 << F2FS_IPU_ASYNC) && + fio && fio->op == REQ_OP_WRITE && + !(fio->op_flags & REQ_SYNC) && + !f2fs_encrypted_inode(inode)) + return true; + + /* this is only set during fdatasync */ + if (policy & (0x1 << F2FS_IPU_FSYNC) && + is_inode_flag_set(inode, FI_NEED_IPU)) + return true; + + return false; +} + +bool should_update_inplace(struct inode *inode, struct f2fs_io_info *fio) +{ + if (f2fs_is_pinned_file(inode)) + return true; + + /* if this is cold file, we should overwrite to avoid fragmentation */ + if (file_is_cold(inode)) + return true; + + return check_inplace_update_policy(inode, fio); +} + +bool should_update_outplace(struct inode *inode, struct f2fs_io_info *fio) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + if (test_opt(sbi, LFS)) + return true; + if (S_ISDIR(inode->i_mode)) + return true; + if (f2fs_is_atomic_file(inode)) + return true; + if (fio) { + if (is_cold_data(fio->page)) + return true; + if (IS_ATOMIC_WRITTEN_PAGE(fio->page)) + return true; + } + return false; +} + static inline bool need_inplace_update(struct f2fs_io_info *fio) { struct inode *inode = fio->page->mapping->host; - if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) - return false; - if (is_cold_data(fio->page)) - return false; - if (IS_ATOMIC_WRITTEN_PAGE(fio->page)) + if (should_update_outplace(inode, fio)) return false; - return need_inplace_update_policy(inode, fio); + return should_update_inplace(inode, fio); } static inline bool valid_ipu_blkaddr(struct f2fs_io_info *fio) @@ -1504,10 +1711,17 @@ static int __write_data_page(struct page *page, bool *submitted, .submitted = false, .need_lock = LOCK_RETRY, .io_type = io_type, + .io_wbc = wbc, }; trace_f2fs_writepage(page, DATA); + /* we should bypass data pages to proceed the kworkder jobs */ + if (unlikely(f2fs_cp_error(sbi))) { + mapping_set_error(page->mapping, -EIO); + goto out; + } + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; @@ -1532,12 +1746,6 @@ write: available_free_memory(sbi, BASE_CHECK)))) goto redirty_out; - /* we should bypass data pages to proceed the kworkder jobs */ - if (unlikely(f2fs_cp_error(sbi))) { - mapping_set_error(page->mapping, -EIO); - goto out; - } - /* Dentry blocks are controlled by checkpoint */ if (S_ISDIR(inode->i_mode)) { fio.need_lock = LOCK_DONE; @@ -1567,10 +1775,14 @@ write: } } - down_write(&F2FS_I(inode)->i_sem); - if (F2FS_I(inode)->last_disk_size < psize) - F2FS_I(inode)->last_disk_size = psize; - up_write(&F2FS_I(inode)->i_sem); + if (err) { + file_set_keep_isize(inode); + } else { + down_write(&F2FS_I(inode)->i_sem); + if (F2FS_I(inode)->last_disk_size < psize) + F2FS_I(inode)->last_disk_size = psize; + up_write(&F2FS_I(inode)->i_sem); + } done: if (err && err != -ENOENT) @@ -1865,7 +2077,8 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, * we already allocated all the blocks, so we don't need to get * the block addresses when there is no need to fill the page. */ - if (!f2fs_has_inline_data(inode) && len == PAGE_SIZE) + if (!f2fs_has_inline_data(inode) && len == PAGE_SIZE && + !is_inode_flag_set(inode, FI_NO_PREALLOC)) return 0; if (f2fs_has_inline_data(inode) || @@ -1933,7 +2146,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *page = NULL; pgoff_t index = ((unsigned long long) pos) >> PAGE_SHIFT; - bool need_balance = false; + bool need_balance = false, drop_atomic = false; block_t blkaddr = NULL_ADDR; int err = 0; @@ -1952,6 +2165,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, if (f2fs_is_atomic_file(inode) && !available_free_memory(sbi, INMEM_PAGES)) { err = -ENOMEM; + drop_atomic = true; goto fail; } @@ -2032,7 +2246,7 @@ repeat: fail: f2fs_put_page(page, 1); f2fs_write_failed(mapping, pos + len); - if (f2fs_is_atomic_file(inode)) + if (drop_atomic) drop_inmem_pages_all(sbi); return err; } diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 36d6a7277924..7c2ba8b0ee24 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -49,14 +49,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->ndirty_imeta = get_pages(sbi, F2FS_DIRTY_IMETA); si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE]; si->ndirty_files = sbi->ndirty_inode[FILE_INODE]; - - si->nquota_files = 0; - if (f2fs_sb_has_quota_ino(sbi->sb)) { - for (i = 0; i < MAXQUOTAS; i++) { - if (f2fs_qf_ino(sbi->sb, i)) - si->nquota_files++; - } - } + si->nquota_files = sbi->nquota_files; si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->aw_cnt = atomic_read(&sbi->aw_cnt); @@ -186,7 +179,6 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize; si->base_mem += 2 * sizeof(struct f2fs_inode_info); si->base_mem += sizeof(*sbi->ckpt); - si->base_mem += sizeof(struct percpu_counter) * NR_COUNT_TYPE; /* build sm */ si->base_mem += sizeof(struct f2fs_sm_info); @@ -449,7 +441,7 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_stat_info *si; - si = kzalloc(sizeof(struct f2fs_stat_info), GFP_KERNEL); + si = f2fs_kzalloc(sbi, sizeof(struct f2fs_stat_info), GFP_KERNEL); if (!si) return -ENOMEM; diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 1955707b138b..bde445e4e690 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -713,6 +713,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); + add_ino_entry(F2FS_I_SB(dir), dir->i_ino, TRANS_DIR_INO); + if (f2fs_has_inline_dentry(dir)) return f2fs_delete_inline_entry(dentry, page, dir, inode); @@ -798,6 +800,7 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, unsigned int bit_pos; struct f2fs_dir_entry *de = NULL; struct fscrypt_str de_name = FSTR_INIT(NULL, 0); + struct f2fs_sb_info *sbi = F2FS_I_SB(d->inode); bit_pos = ((unsigned long)ctx->pos % d->max); @@ -836,6 +839,9 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, le32_to_cpu(de->ino), d_type)) return 1; + if (sbi->readdir_ra == 1) + ra_node_page(sbi, le32_to_cpu(de->ino)); + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); ctx->pos = start_pos + bit_pos; } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 180056d49b40..c753debd8fdc 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #ifdef CONFIG_F2FS_FAULT_INJECTION enum { FAULT_KMALLOC, + FAULT_KVMALLOC, FAULT_PAGE_ALLOC, FAULT_PAGE_GET, FAULT_ALLOC_BIO, @@ -96,6 +98,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_PRJQUOTA 0x00200000 #define F2FS_MOUNT_QUOTA 0x00400000 #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000 +#define F2FS_MOUNT_RESERVE_ROOT 0x01000000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -123,6 +126,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_INODE_CHKSUM 0x0020 #define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 #define F2FS_FEATURE_QUOTA_INO 0x0080 +#define F2FS_FEATURE_INODE_CRTIME 0x0100 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -223,6 +227,12 @@ static inline struct timespec current_time(struct inode *inode) return timespec_trunc(now, inode->i_sb->s_time_gran); } +/* + * Default values for user and/or group using reserved blocks + */ +#define F2FS_DEF_RESUID 0 +#define F2FS_DEF_RESGID 0 + /* * For checkpoint manager */ @@ -273,6 +283,7 @@ enum { ORPHAN_INO, /* for orphan ino list */ APPEND_INO, /* for append ino list */ UPDATE_INO, /* for update ino list */ + TRANS_DIR_INO, /* for trasactions dir ino list */ FLUSH_INO, /* for multiple device flushing */ MAX_INO_ENTRY, /* max. list */ }; @@ -358,7 +369,6 @@ struct discard_cmd_control { struct task_struct *f2fs_issue_discard; /* discard thread */ struct list_head entry_list; /* 4KB discard entry list */ struct list_head pend_list[MAX_PLIST_NUM];/* store pending entries */ - unsigned char pend_list_tag[MAX_PLIST_NUM];/* tag for pending entries */ struct list_head wait_list; /* store on-flushing entries */ struct list_head fstrim_list; /* in-flight discard from fstrim */ wait_queue_head_t discard_wait_queue; /* waiting queue for wake-up */ @@ -441,6 +451,9 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal, #define F2FS_IOC_GARBAGE_COLLECT_RANGE _IOW(F2FS_IOCTL_MAGIC, 11, \ struct f2fs_gc_range) #define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, __u32) +#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32) +#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32) +#define F2FS_IOC_PRECACHE_EXTENTS _IO(F2FS_IOCTL_MAGIC, 15) #define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY #define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY @@ -493,10 +506,9 @@ struct f2fs_flush_device { #define DEF_MIN_INLINE_SIZE 1 static inline int get_extra_isize(struct inode *inode); static inline int get_inline_xattr_addrs(struct inode *inode); -#define F2FS_INLINE_XATTR_ADDRS(inode) get_inline_xattr_addrs(inode) #define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ (CUR_ADDRS_PER_INODE(inode) - \ - F2FS_INLINE_XATTR_ADDRS(inode) - \ + get_inline_xattr_addrs(inode) - \ DEF_INLINE_RESERVED_SIZE)) /* for inline dir */ @@ -633,6 +645,8 @@ struct f2fs_map_blocks { unsigned int m_len; unsigned int m_flags; pgoff_t *m_next_pgofs; /* point next possible non-hole pgofs */ + pgoff_t *m_next_extent; /* point to next possible extent */ + int m_seg_type; }; /* for flag in get_data_block */ @@ -642,6 +656,7 @@ enum { F2FS_GET_BLOCK_BMAP, F2FS_GET_BLOCK_PRE_DIO, F2FS_GET_BLOCK_PRE_AIO, + F2FS_GET_BLOCK_PRECACHE, }; /* @@ -674,7 +689,10 @@ struct f2fs_inode_info { unsigned long i_flags; /* keep an inode flags for ioctl */ unsigned char i_advise; /* use to give file attribute hints */ unsigned char i_dir_level; /* use for dentry level for large dir */ - unsigned int i_current_depth; /* use only in directory structure */ + union { + unsigned int i_current_depth; /* only for directory depth */ + unsigned short i_gc_failures; /* only for regular file */ + }; unsigned int i_pino; /* parent inode number */ umode_t i_acl_mode; /* keep file acl mode temporarily */ @@ -707,6 +725,7 @@ struct f2fs_inode_info { int i_extra_isize; /* size of extra space located in i_addr */ kprojid_t i_projid; /* id for project quota */ int i_inline_xattr_size; /* inline xattr size */ + struct timespec i_crtime; /* inode creation time */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1011,6 +1030,7 @@ enum cp_reason_type { CP_NODE_NEED_CP, CP_FASTBOOT_MODE, CP_SPEC_LOG_NUM, + CP_RECOVER_DIR, }; enum iostat_type { @@ -1046,6 +1066,7 @@ struct f2fs_io_info { int need_lock; /* indicate we need to lock cp_rwsem */ bool in_list; /* indicate fio is in io_list */ enum iostat_type io_type; /* io type */ + struct writeback_control *io_wbc; /* writeback control */ }; #define is_read_io(rw) ((rw) == READ) @@ -1186,6 +1207,7 @@ struct f2fs_sb_info { int dir_level; /* directory level */ int inline_xattr_size; /* inline xattr size */ unsigned int trigger_ssr_threshold; /* threshold to trigger ssr */ + int readdir_ra; /* readahead inode in readdir */ block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ @@ -1193,6 +1215,11 @@ struct f2fs_sb_info { block_t last_valid_block_count; /* for recovery */ block_t reserved_blocks; /* configurable reserved blocks */ block_t current_reserved_blocks; /* current reserved blocks */ + block_t root_reserved_blocks; /* root reserved blocks */ + kuid_t s_resuid; /* reserved blocks for uid */ + kgid_t s_resgid; /* reserved blocks for gid */ + + unsigned int nquota_files; /* # of quota sysfile */ u32 s_next_generation; /* for NFS support */ @@ -1217,6 +1244,9 @@ struct f2fs_sb_info { /* threshold for converting bg victims for fg */ u64 fggc_threshold; + /* threshold for gc trials on pinned files */ + u64 gc_pin_file_threshold; + /* maximum # of trials to find a victim segment for SSR and GC */ unsigned int max_victim_search; @@ -1343,30 +1373,7 @@ static inline bool is_idle(struct f2fs_sb_info *sbi) /* * Inline functions */ -static inline u32 f2fs_crc32(struct f2fs_sb_info *sbi, const void *address, - unsigned int length) -{ - SHASH_DESC_ON_STACK(shash, sbi->s_chksum_driver); - u32 *ctx = (u32 *)shash_desc_ctx(shash); - int err; - - shash->tfm = sbi->s_chksum_driver; - shash->flags = 0; - *ctx = F2FS_SUPER_MAGIC; - - err = crypto_shash_update(shash, address, length); - BUG_ON(err); - - return *ctx; -} - -static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc, - void *buf, size_t buf_size) -{ - return f2fs_crc32(sbi, buf, buf_size) == blk_crc; -} - -static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, +static inline u32 __f2fs_crc32(struct f2fs_sb_info *sbi, u32 crc, const void *address, unsigned int length) { struct { @@ -1387,6 +1394,24 @@ static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, return *(u32 *)desc.ctx; } +static inline u32 f2fs_crc32(struct f2fs_sb_info *sbi, const void *address, + unsigned int length) +{ + return __f2fs_crc32(sbi, F2FS_SUPER_MAGIC, address, length); +} + +static inline bool f2fs_crc_valid(struct f2fs_sb_info *sbi, __u32 blk_crc, + void *buf, size_t buf_size) +{ + return f2fs_crc32(sbi, buf, buf_size) == blk_crc; +} + +static inline u32 f2fs_chksum(struct f2fs_sb_info *sbi, u32 crc, + const void *address, unsigned int length) +{ + return __f2fs_crc32(sbi, crc, address, length); +} + static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) { return container_of(inode, struct f2fs_inode_info, vfs_inode); @@ -1645,6 +1670,25 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs) return ofs == XATTR_NODE_OFFSET; } +static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi, + struct inode *inode) +{ + if (!inode) + return true; + if (!test_opt(sbi, RESERVE_ROOT)) + return false; + if (IS_NOQUOTA(inode)) + return true; + if (capable(CAP_SYS_RESOURCE)) + return true; + if (uid_eq(sbi->s_resuid, current_fsuid())) + return true; + if (!gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) && + in_group_p(sbi->s_resgid)) + return true; + return false; +} + static inline void f2fs_i_blocks_write(struct inode *, block_t, bool, bool); static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, blkcnt_t *count) @@ -1674,11 +1718,17 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, sbi->total_valid_block_count += (block_t)(*count); avail_user_block_count = sbi->user_block_count - sbi->current_reserved_blocks; + + if (!__allow_reserved_blocks(sbi, inode)) + avail_user_block_count -= sbi->root_reserved_blocks; + if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) { diff = sbi->total_valid_block_count - avail_user_block_count; + if (diff > *count) + diff = *count; *count -= diff; release = diff; - sbi->total_valid_block_count = avail_user_block_count; + sbi->total_valid_block_count -= diff; if (!*count) { spin_unlock(&sbi->stat_lock); percpu_counter_sub(&sbi->alloc_valid_block_count, diff); @@ -1687,7 +1737,7 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, } spin_unlock(&sbi->stat_lock); - if (release) + if (unlikely(release)) dquot_release_reservation_block(inode, release); f2fs_i_blocks_write(inode, *count, true, true); return 0; @@ -1867,9 +1917,13 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); - valid_block_count = sbi->total_valid_block_count + 1; - if (unlikely(valid_block_count + sbi->current_reserved_blocks > - sbi->user_block_count)) { + valid_block_count = sbi->total_valid_block_count + + sbi->current_reserved_blocks + 1; + + if (!__allow_reserved_blocks(sbi, inode)) + valid_block_count += sbi->root_reserved_blocks; + + if (unlikely(valid_block_count > sbi->user_block_count)) { spin_unlock(&sbi->stat_lock); goto enospc; } @@ -2082,11 +2136,11 @@ static inline block_t datablock_addr(struct inode *inode, raw_node = F2FS_NODE(node_page); /* from GC path only */ - if (!inode) { - if (is_inode) + if (is_inode) { + if (!inode) base = offset_in_addr(&raw_node->i); - } else if (f2fs_has_extra_attr(inode) && is_inode) { - base = get_extra_isize(inode); + else if (f2fs_has_extra_attr(inode)) + base = get_extra_isize(inode); } addr_array = blkaddr_in_node(raw_node); @@ -2193,9 +2247,11 @@ enum { FI_INLINE_DOTS, /* indicate inline dot dentries */ FI_DO_DEFRAG, /* indicate defragment is running */ FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */ + FI_NO_PREALLOC, /* indicate skipped preallocated blocks */ FI_HOT_DATA, /* indicate file is hot */ FI_EXTRA_ATTR, /* indicate file has extra attribute */ FI_PROJ_INHERIT, /* indicate file inherits projectid */ + FI_PIN_FILE, /* indicate file should not be gced */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, @@ -2205,10 +2261,12 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, case FI_INLINE_XATTR: case FI_INLINE_DATA: case FI_INLINE_DENTRY: + case FI_NEW_INODE: if (set) return; case FI_DATA_EXIST: case FI_INLINE_DOTS: + case FI_PIN_FILE: f2fs_mark_inode_dirty_sync(inode, true); } } @@ -2289,6 +2347,13 @@ static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth) f2fs_mark_inode_dirty_sync(inode, true); } +static inline void f2fs_i_gc_failures_write(struct inode *inode, + unsigned int count) +{ + F2FS_I(inode)->i_gc_failures = count; + f2fs_mark_inode_dirty_sync(inode, true); +} + static inline void f2fs_i_xnid_write(struct inode *inode, nid_t xnid) { F2FS_I(inode)->i_xattr_nid = xnid; @@ -2317,6 +2382,8 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri) set_bit(FI_INLINE_DOTS, &fi->flags); if (ri->i_inline & F2FS_EXTRA_ATTR) set_bit(FI_EXTRA_ATTR, &fi->flags); + if (ri->i_inline & F2FS_PIN_FILE) + set_bit(FI_PIN_FILE, &fi->flags); } static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) @@ -2335,6 +2402,8 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri) ri->i_inline |= F2FS_INLINE_DOTS; if (is_inode_flag_set(inode, FI_EXTRA_ATTR)) ri->i_inline |= F2FS_EXTRA_ATTR; + if (is_inode_flag_set(inode, FI_PIN_FILE)) + ri->i_inline |= F2FS_PIN_FILE; } static inline int f2fs_has_extra_attr(struct inode *inode) @@ -2349,7 +2418,7 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) static inline unsigned int addrs_per_inode(struct inode *inode) { - return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS(inode); + return CUR_ADDRS_PER_INODE(inode) - get_inline_xattr_addrs(inode); } static inline void *inline_xattr_addr(struct inode *inode, struct page *page) @@ -2357,7 +2426,7 @@ static inline void *inline_xattr_addr(struct inode *inode, struct page *page) struct f2fs_inode *ri = F2FS_INODE(page); return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - - F2FS_INLINE_XATTR_ADDRS(inode)]); + get_inline_xattr_addrs(inode)]); } static inline int inline_xattr_size(struct inode *inode) @@ -2380,6 +2449,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode) return is_inode_flag_set(inode, FI_INLINE_DOTS); } +static inline bool f2fs_is_pinned_file(struct inode *inode) +{ + return is_inode_flag_set(inode, FI_PIN_FILE); +} + static inline bool f2fs_is_atomic_file(struct inode *inode) { return is_inode_flag_set(inode, FI_ATOMIC_FILE); @@ -2528,12 +2602,53 @@ static inline void *kvzalloc(size_t size, gfp_t flags) return ret; } +enum rw_hint { + WRITE_LIFE_NOT_SET = 0, + WRITE_LIFE_NONE = 1, /* RWH_WRITE_LIFE_NONE */ + WRITE_LIFE_SHORT = 2, /* RWH_WRITE_LIFE_SHORT */ + WRITE_LIFE_MEDIUM = 3, /* RWH_WRITE_LIFE_MEDIUM */ + WRITE_LIFE_LONG = 4, /* RWH_WRITE_LIFE_LONG */ + WRITE_LIFE_EXTREME = 5, /* RWH_WRITE_LIFE_EXTREME */ +}; + +static inline void wbc_init_bio(struct writeback_control *wbc, struct bio *bio) +{ +} + +static inline void wbc_account_io(struct writeback_control *wbc, + struct page *page, size_t bytes) +{ +} + +static inline void *f2fs_kzalloc(struct f2fs_sb_info *sbi, + size_t size, gfp_t flags) +{ + return f2fs_kmalloc(sbi, size, flags | __GFP_ZERO); +} + +static inline void *f2fs_kvmalloc(struct f2fs_sb_info *sbi, + size_t size, gfp_t flags) +{ +#ifdef CONFIG_F2FS_FAULT_INJECTION + if (time_to_inject(sbi, FAULT_KVMALLOC)) { + f2fs_show_injection_info(FAULT_KVMALLOC); + return NULL; + } +#endif + return kvmalloc(size, flags); +} + +static inline void *f2fs_kvzalloc(struct f2fs_sb_info *sbi, + size_t size, gfp_t flags) +{ + return f2fs_kvmalloc(sbi, size, flags | __GFP_ZERO); +} + static inline int get_extra_isize(struct inode *inode) { return F2FS_I(inode)->i_extra_isize / sizeof(__le32); } -static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb); static inline int get_inline_xattr_addrs(struct inode *inode) { return F2FS_I(inode)->i_inline_xattr_size; @@ -2589,9 +2704,11 @@ int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); int f2fs_setattr(struct dentry *dentry, struct iattr *attr); int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end); -int truncate_data_blocks_range(struct dnode_of_data *dn, int count); +void truncate_data_blocks_range(struct dnode_of_data *dn, int count); +int f2fs_precache_extents(struct inode *inode); long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +int f2fs_pin_file_control(struct inode *inode, bool inc); /* * inode.c @@ -2602,8 +2719,8 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page); struct inode *f2fs_iget(struct super_block *sb, unsigned long ino); struct inode *f2fs_iget_retry(struct super_block *sb, unsigned long ino); int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink); -int update_inode(struct inode *inode, struct page *node_page); -int update_inode_page(struct inode *inode); +void update_inode(struct inode *inode, struct page *node_page); +void update_inode_page(struct inode *inode); int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc); void f2fs_evict_inode(struct inode *inode); void handle_failed_inode(struct inode *inode); @@ -2714,10 +2831,9 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid); void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid); int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink); void recover_inline_xattr(struct inode *inode, struct page *page); -int recover_xattr_data(struct inode *inode, struct page *page, - block_t blkaddr); +int recover_xattr_data(struct inode *inode, struct page *page); int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page); -int restore_node_summary(struct f2fs_sb_info *sbi, +void restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum); void flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc); int build_node_manager(struct f2fs_sb_info *sbi); @@ -2744,6 +2860,7 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr); bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); void init_discard_policy(struct discard_policy *dpolicy, int discard_type, unsigned int granularity); +void drop_discard_cmd(struct f2fs_sb_info *sbi); void stop_discard_thread(struct f2fs_sb_info *sbi); bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi); void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); @@ -2782,6 +2899,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi); void destroy_segment_manager(struct f2fs_sb_info *sbi); int __init create_segment_manager_caches(void); void destroy_segment_manager_caches(void); +int rw_hint_to_seg_type(enum rw_hint hint); /* * checkpoint.c @@ -2852,6 +2970,8 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int create, int flag); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); +bool should_update_inplace(struct inode *inode, struct f2fs_io_info *fio); +bool should_update_outplace(struct inode *inode, struct f2fs_io_info *fio); void f2fs_set_page_dirty_nobuffers(struct page *page); int __f2fs_write_data_pages(struct address_space *mapping, struct writeback_control *wbc, @@ -3220,6 +3340,11 @@ static inline int f2fs_sb_has_quota_ino(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_QUOTA_INO); } +static inline int f2fs_sb_has_inode_crtime(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CRTIME); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 002a54ba8001..1ed7359cf37d 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -168,6 +169,9 @@ static inline enum cp_reason_type need_do_checkpoint(struct inode *inode) cp_reason = CP_FASTBOOT_MODE; else if (sbi->active_logs == 2) cp_reason = CP_SPEC_LOG_NUM; + else if (need_dentry_mark(sbi, inode->i_ino) && + exist_written_data(sbi, F2FS_I(inode)->i_pino, TRANS_DIR_INO)) + cp_reason = CP_RECOVER_DIR; return cp_reason; } @@ -474,26 +478,14 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) static int f2fs_file_open(struct inode *inode, struct file *filp) { - struct dentry *dir; + int err = fscrypt_file_open(inode, filp); - if (f2fs_encrypted_inode(inode)) { - int ret = fscrypt_get_encryption_info(inode); - if (ret) - return -EACCES; - if (!fscrypt_has_encryption_key(inode)) - return -ENOKEY; - } - dir = dget_parent(file_dentry(filp)); - if (f2fs_encrypted_inode(d_inode(dir)) && - !fscrypt_has_permitted_context(d_inode(dir), inode)) { - dput(dir); - return -EPERM; - } - dput(dir); + if (err) + return err; return dquot_file_open(inode, filp); } -int truncate_data_blocks_range(struct dnode_of_data *dn, int count) +void truncate_data_blocks_range(struct dnode_of_data *dn, int count) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_node *raw_node; @@ -536,7 +528,6 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count) f2fs_update_time(sbi, REQ_TIME); trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid, dn->ofs_in_node, nr_free); - return nr_free; } void truncate_data_blocks(struct dnode_of_data *dn) @@ -683,6 +674,37 @@ int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode = d_inode(dentry); +#if 0 + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_inode *ri; + unsigned int flags; + + if (f2fs_has_extra_attr(inode) && + f2fs_sb_has_inode_crtime(inode->i_sb) && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) { + stat->result_mask |= STATX_BTIME; + stat->btime.tv_sec = fi->i_crtime.tv_sec; + stat->btime.tv_nsec = fi->i_crtime.tv_nsec; + } + + flags = fi->i_flags & (FS_FL_USER_VISIBLE | FS_PROJINHERIT_FL); + if (flags & FS_APPEND_FL) + stat->attributes |= STATX_ATTR_APPEND; + if (flags & FS_COMPR_FL) + stat->attributes |= STATX_ATTR_COMPRESSED; + if (f2fs_encrypted_inode(inode)) + stat->attributes |= STATX_ATTR_ENCRYPTED; + if (flags & FS_IMMUTABLE_FL) + stat->attributes |= STATX_ATTR_IMMUTABLE; + if (flags & FS_NODUMP_FL) + stat->attributes |= STATX_ATTR_NODUMP; + + stat->attributes_mask |= (STATX_ATTR_APPEND | + STATX_ATTR_COMPRESSED | + STATX_ATTR_ENCRYPTED | + STATX_ATTR_IMMUTABLE | + STATX_ATTR_NODUMP); +#endif generic_fillattr(inode, stat); /* we need to show initial sectors used for inline_data/dentries */ @@ -736,8 +758,13 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; + err = fscrypt_prepare_setattr(dentry, attr); + if (err) + return err; + if (is_quota_modification(inode, attr)) dquot_initialize(inode); + if ((attr->ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || (attr->ia_valid & ATTR_GID && @@ -748,14 +775,6 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) } if (attr->ia_valid & ATTR_SIZE) { - if (f2fs_encrypted_inode(inode)) { - err = fscrypt_get_encryption_info(inode); - if (err) - return err; - if (!fscrypt_has_encryption_key(inode)) - return -ENOKEY; - } - if (attr->ia_size <= i_size_read(inode)) { down_write(&F2FS_I(inode)->i_mmap_sem); truncate_setsize(inode, attr->ia_size); @@ -1095,11 +1114,13 @@ static int __exchange_data_block(struct inode *src_inode, while (len) { olen = min((pgoff_t)4 * ADDRS_PER_BLOCK, len); - src_blkaddr = kvzalloc(sizeof(block_t) * olen, GFP_KERNEL); + src_blkaddr = f2fs_kvzalloc(F2FS_I_SB(src_inode), + sizeof(block_t) * olen, GFP_KERNEL); if (!src_blkaddr) return -ENOMEM; - do_replace = kvzalloc(sizeof(int) * olen, GFP_KERNEL); + do_replace = f2fs_kvzalloc(F2FS_I_SB(src_inode), + sizeof(int) * olen, GFP_KERNEL); if (!do_replace) { kvfree(src_blkaddr); return -ENOMEM; @@ -1167,14 +1188,14 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) pg_start = offset >> PAGE_SHIFT; pg_end = (offset + len) >> PAGE_SHIFT; + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + down_write(&F2FS_I(inode)->i_mmap_sem); /* write out all dirty pages from offset */ ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); if (ret) - goto out; - - /* avoid gc operation during block exchange */ - down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + goto out_unlock; truncate_pagecache(inode, offset); @@ -1193,9 +1214,8 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); out_unlock: - up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); -out: up_write(&F2FS_I(inode)->i_mmap_sem); + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); return ret; } @@ -1366,6 +1386,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) f2fs_balance_fs(sbi, true); + /* avoid gc operation during block exchange */ + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + down_write(&F2FS_I(inode)->i_mmap_sem); ret = truncate_blocks(inode, i_size_read(inode), true); if (ret) @@ -1376,9 +1399,6 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (ret) goto out; - /* avoid gc operation during block exchange */ - down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); - truncate_pagecache(inode, offset); pg_start = offset >> PAGE_SHIFT; @@ -1406,10 +1426,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) if (!ret) f2fs_i_size_write(inode, new_size); - - up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); out: up_write(&F2FS_I(inode)->i_mmap_sem); + up_write(&F2FS_I(inode)->dio_rwsem[WRITE]); return ret; } @@ -1417,7 +1436,8 @@ static int expand_inode_data(struct inode *inode, loff_t offset, loff_t len, int mode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct f2fs_map_blocks map = { .m_next_pgofs = NULL }; + struct f2fs_map_blocks map = { .m_next_pgofs = NULL, + .m_next_extent = NULL, .m_seg_type = NO_CHECK_TYPE }; pgoff_t pg_end; loff_t new_size = i_size_read(inode); loff_t off_end; @@ -1835,14 +1855,20 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) switch (in) { case F2FS_GOING_DOWN_FULLSYNC: sb = freeze_bdev(sb->s_bdev); - if (sb && !IS_ERR(sb)) { + if (IS_ERR(sb)) { + ret = PTR_ERR(sb); + goto out; + } + if (sb) { f2fs_stop_checkpoint(sbi, false); thaw_bdev(sb->s_bdev, sb); } break; case F2FS_GOING_DOWN_METASYNC: /* do checkpoint only */ - f2fs_sync_fs(sb, 1); + ret = f2fs_sync_fs(sb, 1); + if (ret) + goto out; f2fs_stop_checkpoint(sbi, false); break; case F2FS_GOING_DOWN_NOSYNC: @@ -1856,6 +1882,13 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) ret = -EINVAL; goto out; } + + stop_gc_thread(sbi); + stop_discard_thread(sbi); + + drop_discard_cmd(sbi); + clear_opt(sbi, DISCARD); + f2fs_update_time(sbi, REQ_TIME); out: mnt_drop_write_file(filp); @@ -2067,9 +2100,10 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, struct f2fs_defragment *range) { struct inode *inode = file_inode(filp); - struct f2fs_map_blocks map = { .m_next_pgofs = NULL }; + struct f2fs_map_blocks map = { .m_next_extent = NULL, + .m_seg_type = NO_CHECK_TYPE }; struct extent_info ei = {0,0,0}; - pgoff_t pg_start, pg_end; + pgoff_t pg_start, pg_end, next_pgofs; unsigned int blk_per_seg = sbi->blocks_per_seg; unsigned int total = 0, sec_num; block_t blk_end = 0; @@ -2077,7 +2111,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, int err; /* if in-place-update policy is enabled, don't waste time here */ - if (need_inplace_update_policy(inode, NULL)) + if (should_update_inplace(inode, NULL)) return -EINVAL; pg_start = range->start >> PAGE_SHIFT; @@ -2103,6 +2137,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, } map.m_lblk = pg_start; + map.m_next_pgofs = &next_pgofs; /* * lookup mapping info in dnode page cache, skip defragmenting if all @@ -2116,14 +2151,16 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, goto out; if (!(map.m_flags & F2FS_MAP_FLAGS)) { - map.m_lblk++; + map.m_lblk = next_pgofs; continue; } - if (blk_end && blk_end != map.m_pblk) { + if (blk_end && blk_end != map.m_pblk) fragmented = true; - break; - } + + /* record total count of block that we're going to move */ + total += map.m_len; + blk_end = map.m_pblk + map.m_len; map.m_lblk += map.m_len; @@ -2132,10 +2169,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, if (!fragmented) goto out; - map.m_lblk = pg_start; - map.m_len = pg_end - pg_start; - - sec_num = (map.m_len + BLKS_PER_SEC(sbi) - 1) / BLKS_PER_SEC(sbi); + sec_num = (total + BLKS_PER_SEC(sbi) - 1) / BLKS_PER_SEC(sbi); /* * make sure there are enough free section for LFS allocation, this can @@ -2147,6 +2181,10 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi, goto out; } + map.m_lblk = pg_start; + map.m_len = pg_end - pg_start; + total = 0; + while (map.m_lblk < pg_end) { pgoff_t idx; int cnt = 0; @@ -2158,7 +2196,7 @@ do_map: goto clear_out; if (!(map.m_flags & F2FS_MAP_FLAGS)) { - map.m_lblk++; + map.m_lblk = next_pgofs; continue; } @@ -2470,6 +2508,125 @@ static int f2fs_ioc_get_features(struct file *filp, unsigned long arg) return put_user(sb_feature, (u32 __user *)arg); } +int f2fs_pin_file_control(struct inode *inode, bool inc) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + /* Use i_gc_failures for normal file as a risk signal. */ + if (inc) + f2fs_i_gc_failures_write(inode, fi->i_gc_failures + 1); + + if (fi->i_gc_failures > sbi->gc_pin_file_threshold) { + f2fs_msg(sbi->sb, KERN_WARNING, + "%s: Enable GC = ino %lx after %x GC trials\n", + __func__, inode->i_ino, fi->i_gc_failures); + clear_inode_flag(inode, FI_PIN_FILE); + return -EAGAIN; + } + return 0; +} + +static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + __u32 pin; + int ret = 0; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + if (get_user(pin, (__u32 __user *)arg)) + return -EFAULT; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + if (f2fs_readonly(F2FS_I_SB(inode)->sb)) + return -EROFS; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + inode_lock(inode); + + if (should_update_outplace(inode, NULL)) { + ret = -EINVAL; + goto out; + } + + if (!pin) { + clear_inode_flag(inode, FI_PIN_FILE); + F2FS_I(inode)->i_gc_failures = 1; + goto done; + } + + if (f2fs_pin_file_control(inode, false)) { + ret = -EAGAIN; + goto out; + } + ret = f2fs_convert_inline_inode(inode); + if (ret) + goto out; + + set_inode_flag(inode, FI_PIN_FILE); + ret = F2FS_I(inode)->i_gc_failures; +done: + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); +out: + inode_unlock(inode); + mnt_drop_write_file(filp); + return ret; +} + +static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + __u32 pin = 0; + + if (is_inode_flag_set(inode, FI_PIN_FILE)) + pin = F2FS_I(inode)->i_gc_failures; + return put_user(pin, (u32 __user *)arg); +} + +int f2fs_precache_extents(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_map_blocks map; + pgoff_t m_next_extent; + loff_t end; + int err; + + if (is_inode_flag_set(inode, FI_NO_EXTENT)) + return -EOPNOTSUPP; + + map.m_lblk = 0; + map.m_next_pgofs = NULL; + map.m_next_extent = &m_next_extent; + map.m_seg_type = NO_CHECK_TYPE; + end = F2FS_I_SB(inode)->max_file_blocks; + + while (map.m_lblk < end) { + map.m_len = end - map.m_lblk; + + down_write(&fi->dio_rwsem[WRITE]); + err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_PRECACHE); + up_write(&fi->dio_rwsem[WRITE]); + if (err) + return err; + + map.m_lblk = m_next_extent; + } + + return err; +} + +static int f2fs_ioc_precache_extents(struct file *filp, unsigned long arg) +{ + return f2fs_precache_extents(file_inode(filp)); +} + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp))))) @@ -2516,6 +2673,12 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_flush_device(filp, arg); case F2FS_IOC_GET_FEATURES: return f2fs_ioc_get_features(filp, arg); + case F2FS_IOC_GET_PIN_FILE: + return f2fs_ioc_get_pin_file(filp, arg); + case F2FS_IOC_SET_PIN_FILE: + return f2fs_ioc_set_pin_file(filp, arg); + case F2FS_IOC_PRECACHE_EXTENTS: + return f2fs_ioc_precache_extents(filp, arg); default: return -ENOTTY; } @@ -2536,15 +2699,22 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) inode_lock(inode); ret = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (!ret) { - int err = f2fs_preallocate_blocks(inode, pos, count, + int err; + + if (iov_iter_fault_in_readable(from, iov_iter_count(from))) + set_inode_flag(inode, FI_NO_PREALLOC); + + err = f2fs_preallocate_blocks(inode, pos, count, file->f_flags & O_DIRECT); if (err) { + clear_inode_flag(inode, FI_NO_PREALLOC); inode_unlock(inode); return err; } blk_start_plug(&plug); ret = __generic_file_write_iter(iocb, from); blk_finish_plug(&plug); + clear_inode_flag(inode, FI_NO_PREALLOC); if (ret > 0) f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret); @@ -2590,6 +2760,9 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_MOVE_RANGE: case F2FS_IOC_FLUSH_DEVICE: case F2FS_IOC_GET_FEATURES: + case F2FS_IOC_GET_PIN_FILE: + case F2FS_IOC_SET_PIN_FILE: + case F2FS_IOC_PRECACHE_EXTENTS: break; default: return -ENOIOCTLCMD; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 5d5bba462f26..3b26aa19430b 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -624,6 +624,11 @@ static void move_data_block(struct inode *inode, block_t bidx, if (f2fs_is_atomic_file(inode)) goto out; + if (f2fs_is_pinned_file(inode)) { + f2fs_pin_file_control(inode, true); + goto out; + } + set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE); if (err) @@ -686,7 +691,12 @@ static void move_data_block(struct inode *inode, block_t bidx, fio.op = REQ_OP_WRITE; fio.op_flags = REQ_SYNC; fio.new_blkaddr = newaddr; - f2fs_submit_page_write(&fio); + err = f2fs_submit_page_write(&fio); + if (err) { + if (PageWriteback(fio.encrypted_page)) + end_page_writeback(fio.encrypted_page); + goto put_page_out; + } f2fs_update_iostat(fio.sbi, FS_GC_DATA_IO, F2FS_BLKSIZE); @@ -720,6 +730,11 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type, if (f2fs_is_atomic_file(inode)) goto out; + if (f2fs_is_pinned_file(inode)) { + if (gc_type == FG_GC) + f2fs_pin_file_control(inode, true); + goto out; + } if (gc_type == BG_GC) { if (PageWriteback(page)) @@ -1091,6 +1106,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi) sbi->fggc_threshold = div64_u64((main_count - ovp_count) * BLKS_PER_SEC(sbi), (main_count - resv_count)); + sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES; /* give warm/cold data area from slower device */ if (sbi->s_ndevs && sbi->segs_per_sec == 1) diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 9325191fab2d..b0045d4c8d1e 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -20,6 +20,8 @@ #define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */ #define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */ +#define DEF_GC_FAILED_PINNED_FILES 2048 + /* Search max. number of dirty segments to select a victim segment */ #define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */ diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 9eb3cc2486d4..94ef24f3e394 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -22,6 +22,9 @@ void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync) { + if (is_inode_flag_set(inode, FI_NEW_INODE)) + return; + if (f2fs_inode_dirtied(inode, sync)) return; @@ -275,6 +278,12 @@ static int do_read_inode(struct inode *inode) i_projid = F2FS_DEF_PROJID; fi->i_projid = make_kprojid(&init_user_ns, i_projid); + if (f2fs_has_extra_attr(inode) && f2fs_sb_has_inode_crtime(sbi->sb) && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) { + fi->i_crtime.tv_sec = le64_to_cpu(ri->i_crtime); + fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec); + } + f2fs_put_page(node_page, 1); stat_inc_inline_xattr(inode); @@ -360,14 +369,15 @@ retry: return inode; } -int update_inode(struct inode *inode, struct page *node_page) +void update_inode(struct inode *inode, struct page *node_page) { struct f2fs_inode *ri; struct extent_tree *et = F2FS_I(inode)->extent_tree; - f2fs_inode_synced(inode); - f2fs_wait_on_page_writeback(node_page, NODE, true); + set_page_dirty(node_page); + + f2fs_inode_synced(inode); ri = F2FS_INODE(node_page); @@ -417,6 +427,15 @@ int update_inode(struct inode *inode, struct page *node_page) F2FS_I(inode)->i_projid); ri->i_projid = cpu_to_le32(i_projid); } + + if (f2fs_sb_has_inode_crtime(F2FS_I_SB(inode)->sb) && + F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + i_crtime)) { + ri->i_crtime = + cpu_to_le64(F2FS_I(inode)->i_crtime.tv_sec); + ri->i_crtime_nsec = + cpu_to_le32(F2FS_I(inode)->i_crtime.tv_nsec); + } } __set_inode_rdev(inode, ri); @@ -426,14 +445,12 @@ int update_inode(struct inode *inode, struct page *node_page) if (inode->i_nlink == 0) clear_inline_node(node_page); - return set_page_dirty(node_page); } -int update_inode_page(struct inode *inode) +void update_inode_page(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *node_page; - int ret = 0; retry: node_page = get_node_page(sbi, inode->i_ino); if (IS_ERR(node_page)) { @@ -444,11 +461,10 @@ retry: } else if (err != -ENOENT) { f2fs_stop_checkpoint(sbi, false); } - return 0; + return; } - ret = update_inode(inode, node_page); + update_inode(inode, node_page); f2fs_put_page(node_page, 1); - return ret; } int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index eaf7476a0942..4f6adb7f0f31 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -51,7 +51,8 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) inode->i_ino = ino; inode->i_blocks = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = + F2FS_I(inode)->i_crtime = current_time(inode); inode->i_generation = sbi->s_next_generation++; err = insert_inode_locked(inode); @@ -72,12 +73,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (err) goto fail_drop; + set_inode_flag(inode, FI_NEW_INODE); + /* If the directory encrypted, then we should encrypt the inode. */ if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) f2fs_set_encrypted_inode(inode); - set_inode_flag(inode, FI_NEW_INODE); - if (f2fs_sb_has_extra_attr(sbi->sb)) { set_inode_flag(inode, FI_EXTRA_ATTR); F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; @@ -236,9 +237,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, if (unlikely(f2fs_cp_error(sbi))) return -EIO; - if (f2fs_encrypted_inode(dir) && - !fscrypt_has_permitted_context(dir, inode)) - return -EPERM; + err = fscrypt_prepare_link(old_dentry, dir, dentry); + if (err) + return err; if (is_inode_flag_set(dir, FI_PROJ_INHERIT) && (!projid_eq(F2FS_I(dir)->i_projid, @@ -349,20 +350,9 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, trace_f2fs_lookup_start(dir, dentry, flags); - if (f2fs_encrypted_inode(dir)) { - err = fscrypt_get_encryption_info(dir); - - /* - * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is - * created while the directory was encrypted and we - * don't have access to the key. - */ - if (fscrypt_has_encryption_key(dir)) - fscrypt_set_encrypted_dentry(dentry); - fscrypt_set_d_op(dentry); - if (err && err != -ENOKEY) - goto out; - } + err = fscrypt_prepare_lookup(dir, dentry, flags); + if (err) + goto out; if (dentry->d_name.len > F2FS_NAME_LEN) { err = -ENAMETOOLONG; @@ -538,7 +528,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, struct qstr istr = QSTR_INIT(symname, len); struct fscrypt_str ostr; - sd = kzalloc(disk_link.len, GFP_NOFS); + sd = f2fs_kzalloc(sbi, disk_link.len, GFP_NOFS); if (!sd) { err = -ENOMEM; goto err_out; @@ -790,18 +780,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (unlikely(f2fs_cp_error(sbi))) return -EIO; - if ((f2fs_encrypted_inode(old_dir) && - !fscrypt_has_encryption_key(old_dir)) || - (f2fs_encrypted_inode(new_dir) && - !fscrypt_has_encryption_key(new_dir))) - return -ENOKEY; - - if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && - !fscrypt_has_permitted_context(new_dir, old_inode)) { - err = -EPERM; - goto out; - } - if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && (!projid_eq(F2FS_I(new_dir)->i_projid, F2FS_I(old_dentry->d_inode)->i_projid))) @@ -941,6 +919,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } f2fs_i_links_write(old_dir, false); } + add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO); f2fs_unlock_op(sbi); @@ -985,18 +964,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, if (unlikely(f2fs_cp_error(sbi))) return -EIO; - if ((f2fs_encrypted_inode(old_dir) && - !fscrypt_has_encryption_key(old_dir)) || - (f2fs_encrypted_inode(new_dir) && - !fscrypt_has_encryption_key(new_dir))) - return -ENOKEY; - - if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) && - (old_dir != new_dir) && - (!fscrypt_has_permitted_context(new_dir, old_inode) || - !fscrypt_has_permitted_context(old_dir, new_inode))) - return -EPERM; - if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) && !projid_eq(F2FS_I(new_dir)->i_projid, F2FS_I(old_dentry->d_inode)->i_projid)) || @@ -1103,6 +1070,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, } f2fs_mark_inode_dirty_sync(new_dir, false); + add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO); + add_ino_entry(sbi, new_dir->i_ino, TRANS_DIR_INO); + f2fs_unlock_op(sbi); if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) @@ -1132,9 +1102,16 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { + int err; + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; + err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, + flags); + if (err) + return err; + if (flags & RENAME_EXCHANGE) { return f2fs_cross_rename(old_dir, old_dentry, new_dir, new_dentry); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 57e86a43cbf2..1902530d50b4 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -143,11 +143,9 @@ static struct nat_entry *__alloc_nat_entry(nid_t nid, bool no_fail) struct nat_entry *new; if (no_fail) - new = f2fs_kmem_cache_alloc(nat_entry_slab, - GFP_NOFS | __GFP_ZERO); + new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_F2FS_ZERO); else - new = kmem_cache_alloc(nat_entry_slab, - GFP_NOFS | __GFP_ZERO); + new = kmem_cache_alloc(nat_entry_slab, GFP_F2FS_ZERO); if (new) { nat_set_nid(new, nid); nat_reset_flag(new); @@ -702,7 +700,6 @@ static void truncate_node(struct dnode_of_data *dn) struct node_info ni; get_node_info(sbi, dn->nid, &ni); - f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); /* Deallocate node address */ invalidate_blocks(sbi, ni.blk_addr); @@ -1341,14 +1338,19 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, .encrypted_page = NULL, .submitted = false, .io_type = io_type, + .io_wbc = wbc, }; trace_f2fs_writepage(page, NODE); + if (unlikely(f2fs_cp_error(sbi))) { + dec_page_count(sbi, F2FS_DIRTY_NODES); + unlock_page(page); + return 0; + } + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; - if (unlikely(f2fs_cp_error(sbi))) - goto redirty_out; /* get old block addr of this node page */ nid = nid_of_node(page); @@ -1593,12 +1595,6 @@ next_step: struct page *page = pvec.pages[i]; bool submitted = false; - if (unlikely(f2fs_cp_error(sbi))) { - pagevec_release(&pvec); - ret = -EIO; - goto out; - } - /* * flushing sequence with step: * 0. indirect nodes @@ -1668,9 +1664,12 @@ continue_unlock: step++; goto next_step; } -out: + if (nwritten) f2fs_submit_merged_write(sbi, NODE); + + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; return ret; } @@ -1835,8 +1834,33 @@ static void __move_free_nid(struct f2fs_sb_info *sbi, struct free_nid *i, } } +static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, + bool set, bool build) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); + unsigned int nid_ofs = nid - START_NID(nid); + + if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) + return; + + if (set) { + if (test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; + __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + nm_i->free_nid_count[nat_ofs]++; + } else { + if (!test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) + return; + __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); + if (!build) + nm_i->free_nid_count[nat_ofs]--; + } +} + /* return if the nid is recognized as free */ -static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) +static bool add_free_nid(struct f2fs_sb_info *sbi, + nid_t nid, bool build, bool update) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i, *e; @@ -1852,8 +1876,7 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) i->nid = nid; i->state = FREE_NID; - if (radix_tree_preload(GFP_NOFS)) - goto err; + radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); spin_lock(&nm_i->nid_list_lock); @@ -1894,9 +1917,14 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) ret = true; err = __insert_free_nid(sbi, i, FREE_NID); err_out: + if (update) { + update_free_nid_bitmap(sbi, nid, ret, build); + if (!build) + nm_i->available_nids++; + } spin_unlock(&nm_i->nid_list_lock); radix_tree_preload_end(); -err: + if (err) kmem_cache_free(free_nid_slab, i); return ret; @@ -1920,30 +1948,6 @@ static void remove_free_nid(struct f2fs_sb_info *sbi, nid_t nid) kmem_cache_free(free_nid_slab, i); } -static void update_free_nid_bitmap(struct f2fs_sb_info *sbi, nid_t nid, - bool set, bool build) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - unsigned int nat_ofs = NAT_BLOCK_OFFSET(nid); - unsigned int nid_ofs = nid - START_NID(nid); - - if (!test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) - return; - - if (set) { - if (test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) - return; - __set_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - nm_i->free_nid_count[nat_ofs]++; - } else { - if (!test_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs])) - return; - __clear_bit_le(nid_ofs, nm_i->free_nid_bitmap[nat_ofs]); - if (!build) - nm_i->free_nid_count[nat_ofs]--; - } -} - static void scan_nat_page(struct f2fs_sb_info *sbi, struct page *nat_page, nid_t start_nid) { @@ -1953,26 +1957,23 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, unsigned int nat_ofs = NAT_BLOCK_OFFSET(start_nid); int i; - if (test_bit_le(nat_ofs, nm_i->nat_block_bitmap)) - return; - __set_bit_le(nat_ofs, nm_i->nat_block_bitmap); i = start_nid % NAT_ENTRY_PER_BLOCK; for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) { - bool freed = false; - if (unlikely(start_nid >= nm_i->max_nid)) break; blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr); f2fs_bug_on(sbi, blk_addr == NEW_ADDR); - if (blk_addr == NULL_ADDR) - freed = add_free_nid(sbi, start_nid, true); - spin_lock(&NM_I(sbi)->nid_list_lock); - update_free_nid_bitmap(sbi, start_nid, freed, true); - spin_unlock(&NM_I(sbi)->nid_list_lock); + if (blk_addr == NULL_ADDR) { + add_free_nid(sbi, start_nid, true, true); + } else { + spin_lock(&NM_I(sbi)->nid_list_lock); + update_free_nid_bitmap(sbi, start_nid, false, true); + spin_unlock(&NM_I(sbi)->nid_list_lock); + } } } @@ -1990,7 +1991,7 @@ static void scan_curseg_cache(struct f2fs_sb_info *sbi) addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); nid = le32_to_cpu(nid_in_journal(journal, i)); if (addr == NULL_ADDR) - add_free_nid(sbi, nid, true); + add_free_nid(sbi, nid, true, false); else remove_free_nid(sbi, nid); } @@ -2017,7 +2018,7 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi) break; nid = i * NAT_ENTRY_PER_BLOCK + idx; - add_free_nid(sbi, nid, true); + add_free_nid(sbi, nid, true, false); if (nm_i->nid_cnt[FREE_NID] >= MAX_FREE_NIDS) goto out; @@ -2060,10 +2061,13 @@ static void __build_free_nids(struct f2fs_sb_info *sbi, bool sync, bool mount) down_read(&nm_i->nat_tree_lock); while (1) { - struct page *page = get_current_nat_page(sbi, nid); + if (!test_bit_le(NAT_BLOCK_OFFSET(nid), + nm_i->nat_block_bitmap)) { + struct page *page = get_current_nat_page(sbi, nid); - scan_nat_page(sbi, page, nid); - f2fs_put_page(page, 1); + scan_nat_page(sbi, page, nid); + f2fs_put_page(page, 1); + } nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK)); if (unlikely(nid >= nm_i->max_nid)) @@ -2226,7 +2230,9 @@ void recover_inline_xattr(struct inode *inode, struct page *page) f2fs_bug_on(F2FS_I_SB(inode), IS_ERR(ipage)); ri = F2FS_INODE(page); - if (!(ri->i_inline & F2FS_INLINE_XATTR)) { + if (ri->i_inline & F2FS_INLINE_XATTR) { + set_inode_flag(inode, FI_INLINE_XATTR); + } else { clear_inode_flag(inode, FI_INLINE_XATTR); goto update_inode; } @@ -2242,7 +2248,7 @@ update_inode: f2fs_put_page(ipage, 1); } -int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) +int recover_xattr_data(struct inode *inode, struct page *page) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid; @@ -2256,7 +2262,6 @@ int recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) /* 1: invalidate the previous xattr nid */ get_node_info(sbi, prev_xnid, &ni); - f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); invalidate_blocks(sbi, ni.blk_addr); dec_valid_node_count(sbi, inode, false); set_node_addr(sbi, &ni, NULL_ADDR, false); @@ -2345,7 +2350,7 @@ retry: return 0; } -int restore_node_summary(struct f2fs_sb_info *sbi, +void restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum) { struct f2fs_node *rn; @@ -2378,7 +2383,6 @@ int restore_node_summary(struct f2fs_sb_info *sbi, invalidate_mapping_pages(META_MAPPING(sbi), addr, addr + nrpages); } - return 0; } static void remove_nats_in_journal(struct f2fs_sb_info *sbi) @@ -2520,11 +2524,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, nat_reset_flag(ne); __clear_nat_cache_dirty(NM_I(sbi), set, ne); if (nat_get_blkaddr(ne) == NULL_ADDR) { - add_free_nid(sbi, nid, false); - spin_lock(&NM_I(sbi)->nid_list_lock); - NM_I(sbi)->available_nids++; - update_free_nid_bitmap(sbi, nid, true, false); - spin_unlock(&NM_I(sbi)->nid_list_lock); + add_free_nid(sbi, nid, false, true); } else { spin_lock(&NM_I(sbi)->nid_list_lock); update_free_nid_bitmap(sbi, nid, false, false); @@ -2605,8 +2605,8 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) nm_i->nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + F2FS_BLKSIZE - 1); - nm_i->nat_bits = kzalloc(nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, - GFP_KERNEL); + nm_i->nat_bits = f2fs_kzalloc(sbi, + nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, GFP_KERNEL); if (!nm_i->nat_bits) return -ENOMEM; @@ -2684,7 +2684,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi) /* not used nids: 0, node, meta, (and root counted as valid node) */ nm_i->available_nids = nm_i->max_nid - sbi->total_valid_node_count - - F2FS_RESERVED_NODE_NUM; + sbi->nquota_files - F2FS_RESERVED_NODE_NUM; nm_i->nid_cnt[FREE_NID] = 0; nm_i->nid_cnt[PREALLOC_NID] = 0; nm_i->nat_cnt = 0; @@ -2731,17 +2731,17 @@ static int init_free_nid_cache(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); - nm_i->free_nid_bitmap = kvzalloc(nm_i->nat_blocks * + nm_i->free_nid_bitmap = f2fs_kvzalloc(sbi, nm_i->nat_blocks * NAT_ENTRY_BITMAP_SIZE, GFP_KERNEL); if (!nm_i->free_nid_bitmap) return -ENOMEM; - nm_i->nat_block_bitmap = kvzalloc(nm_i->nat_blocks / 8, + nm_i->nat_block_bitmap = f2fs_kvzalloc(sbi, nm_i->nat_blocks / 8, GFP_KERNEL); if (!nm_i->nat_block_bitmap) return -ENOMEM; - nm_i->free_nid_count = kvzalloc(nm_i->nat_blocks * + nm_i->free_nid_count = f2fs_kvzalloc(sbi, nm_i->nat_blocks * sizeof(unsigned short), GFP_KERNEL); if (!nm_i->free_nid_count) return -ENOMEM; @@ -2752,7 +2752,8 @@ int build_node_manager(struct f2fs_sb_info *sbi) { int err; - sbi->nm_info = kzalloc(sizeof(struct f2fs_nm_info), GFP_KERNEL); + sbi->nm_info = f2fs_kzalloc(sbi, sizeof(struct f2fs_nm_info), + GFP_KERNEL); if (!sbi->nm_info) return -ENOMEM; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 0ee3e5ff49a3..081ef0d672bf 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -305,6 +305,10 @@ static inline bool is_recoverable_dnode(struct page *page) struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page)); __u64 cp_ver = cur_cp_version(ckpt); + /* Don't care crc part, if fsck.f2fs sets it. */ + if (__is_set_ckpt_flags(ckpt, CP_NOCRC_RECOVERY_FLAG)) + return (cp_ver << 32) == (cpver_of_node(page) << 32); + if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) cp_ver |= (cur_cp_crc(ckpt) << 32); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 559904e9868f..1010fc2118dc 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -189,6 +189,20 @@ out: return err; } +static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) +{ + if (ri->i_inline & F2FS_PIN_FILE) + set_inode_flag(inode, FI_PIN_FILE); + else + clear_inode_flag(inode, FI_PIN_FILE); + if (ri->i_inline & F2FS_DATA_EXIST) + set_inode_flag(inode, FI_DATA_EXIST); + else + clear_inode_flag(inode, FI_DATA_EXIST); + if (!(ri->i_inline & F2FS_INLINE_DOTS)) + clear_inode_flag(inode, FI_INLINE_DOTS); +} + static void recover_inode(struct inode *inode, struct page *page) { struct f2fs_inode *raw = F2FS_INODE(page); @@ -205,13 +219,16 @@ static void recover_inode(struct inode *inode, struct page *page) F2FS_I(inode)->i_advise = raw->i_advise; + recover_inline_flags(inode, raw); + if (file_enc_name(inode)) name = ""; else name = F2FS_INODE(page)->i_name; - f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s", - ino_of_node(page), name); + f2fs_msg(inode->i_sb, KERN_NOTICE, + "recover_inode: ino = %x, name = %s, inline = %x", + ino_of_node(page), name, raw->i_inline); } static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, @@ -392,7 +409,7 @@ truncate_out: } static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, - struct page *page, block_t blkaddr) + struct page *page) { struct dnode_of_data dn; struct node_info ni; @@ -403,7 +420,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (IS_INODE(page)) { recover_inline_xattr(inode, page); } else if (f2fs_has_xattr_block(ofs_of_node(page))) { - err = recover_xattr_data(inode, page, blkaddr); + err = recover_xattr_data(inode, page); if (!err) recovered++; goto out; @@ -556,7 +573,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, break; } } - err = do_recover_data(sbi, entry->inode, page, blkaddr); + err = do_recover_data(sbi, entry->inode, page); if (err) { f2fs_put_page(page, 1); break; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c126195a993a..f0d69e6e2650 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -248,7 +248,11 @@ retry: goto next; } get_node_info(sbi, dn.nid, &ni); - f2fs_replace_block(sbi, &dn, dn.data_blkaddr, + if (cur->old_addr == NEW_ADDR) { + invalidate_blocks(sbi, dn.data_blkaddr); + f2fs_update_data_blkaddr(&dn, NEW_ADDR); + } else + f2fs_replace_block(sbi, &dn, dn.data_blkaddr, cur->old_addr, ni.version, true, true); f2fs_put_dnode(&dn); } @@ -657,7 +661,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) goto init_thread; } - fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); + fcc = f2fs_kzalloc(sbi, sizeof(struct flush_cmd_control), GFP_KERNEL); if (!fcc) return -ENOMEM; atomic_set(&fcc->issued_flush, 0); @@ -966,7 +970,7 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, return 0; } -void __check_sit_bitmap(struct f2fs_sb_info *sbi, +static void __check_sit_bitmap(struct f2fs_sb_info *sbi, block_t start, block_t end) { #ifdef CONFIG_F2FS_CHECK_FS @@ -1285,6 +1289,8 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, pend_list = &dcc->pend_list[i]; mutex_lock(&dcc->cmd_lock); + if (list_empty(pend_list)) + goto next; f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root)); blk_start_plug(&plug); list_for_each_entry_safe(dc, tmp, pend_list, list) { @@ -1303,6 +1309,7 @@ skip: break; } blk_finish_plug(&plug); +next: mutex_unlock(&dcc->cmd_lock); if (iter >= dpolicy->max_requests) @@ -1337,6 +1344,11 @@ static bool __drop_discard_cmd(struct f2fs_sb_info *sbi) return dropped; } +void drop_discard_cmd(struct f2fs_sb_info *sbi) +{ + __drop_discard_cmd(sbi); +} + static unsigned int __wait_one_discard_bio(struct f2fs_sb_info *sbi, struct discard_cmd *dc) { @@ -1405,7 +1417,7 @@ static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi, } /* This should be covered by global mutex, &sit_i->sentry_lock */ -void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) +static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_cmd *dc; @@ -1475,6 +1487,8 @@ static int issue_discard_thread(void *data) msecs_to_jiffies(wait_ms)); if (try_to_freeze()) continue; + if (f2fs_readonly(sbi->sb)) + continue; if (kthread_should_stop()) return 0; @@ -1784,25 +1798,20 @@ void init_discard_policy(struct discard_policy *dpolicy, dpolicy->sync = true; dpolicy->granularity = granularity; + dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; + dpolicy->io_aware_gran = MAX_PLIST_NUM; + if (discard_type == DPOLICY_BG) { dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = true; } else if (discard_type == DPOLICY_FORCE) { dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = true; } else if (discard_type == DPOLICY_FSTRIM) { - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = false; } else if (discard_type == DPOLICY_UMOUNT) { - dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; - dpolicy->io_aware_gran = MAX_PLIST_NUM; dpolicy->io_aware = false; } } @@ -1818,7 +1827,7 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi) goto init_thread; } - dcc = kzalloc(sizeof(struct discard_cmd_control), GFP_KERNEL); + dcc = f2fs_kzalloc(sbi, sizeof(struct discard_cmd_control), GFP_KERNEL); if (!dcc) return -ENOMEM; @@ -2515,7 +2524,6 @@ static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) return false; } -#if 0 int rw_hint_to_seg_type(enum rw_hint hint) { switch (hint) { @@ -2527,7 +2535,6 @@ int rw_hint_to_seg_type(enum rw_hint hint) return CURSEG_WARM_DATA; } } -#endif static int __get_segment_type_2(struct f2fs_io_info *fio) { @@ -2824,6 +2831,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, } } + f2fs_bug_on(sbi, !IS_DATASEG(type)); curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); @@ -2908,7 +2916,7 @@ void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr) } } -static int read_compacted_summaries(struct f2fs_sb_info *sbi) +static void read_compacted_summaries(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct curseg_info *seg_i; @@ -2965,7 +2973,6 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) } } f2fs_put_page(page, 1); - return 0; } static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) @@ -3011,13 +3018,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) ns->ofs_in_node = 0; } } else { - int err; - - err = restore_node_summary(sbi, segno, sum); - if (err) { - f2fs_put_page(new, 1); - return err; - } + restore_node_summary(sbi, segno, sum); } } @@ -3056,8 +3057,7 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) META_CP, true); /* restore for compacted data summary */ - if (read_compacted_summaries(sbi)) - return -EINVAL; + read_compacted_summaries(sbi); type = CURSEG_HOT_NODE; } @@ -3193,28 +3193,19 @@ static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, unsigned int start) { struct sit_info *sit_i = SIT_I(sbi); - struct page *src_page, *dst_page; + struct page *page; pgoff_t src_off, dst_off; - void *src_addr, *dst_addr; src_off = current_sit_addr(sbi, start); dst_off = next_sit_addr(sbi, src_off); - /* get current sit block page without lock */ - src_page = get_meta_page(sbi, src_off); - dst_page = grab_meta_page(sbi, dst_off); - f2fs_bug_on(sbi, PageDirty(src_page)); - - src_addr = page_address(src_page); - dst_addr = page_address(dst_page); - memcpy(dst_addr, src_addr, PAGE_SIZE); - - set_page_dirty(dst_page); - f2fs_put_page(src_page, 1); + page = grab_meta_page(sbi, dst_off); + seg_info_to_sit_page(sbi, page, start); + set_page_dirty(page); set_to_next_sit(sit_i, start); - return dst_page; + return page; } static struct sit_entry_set *grab_sit_entry_set(void) @@ -3423,52 +3414,54 @@ static int build_sit_info(struct f2fs_sb_info *sbi) unsigned int bitmap_size; /* allocate memory for SIT information */ - sit_i = kzalloc(sizeof(struct sit_info), GFP_KERNEL); + sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL); if (!sit_i) return -ENOMEM; SM_I(sbi)->sit_info = sit_i; - sit_i->sentries = kvzalloc(MAIN_SEGS(sbi) * + sit_i->sentries = f2fs_kvzalloc(sbi, 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 = kvzalloc(bitmap_size, GFP_KERNEL); + sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(sbi, bitmap_size, + GFP_KERNEL); if (!sit_i->dirty_sentries_bitmap) return -ENOMEM; for (start = 0; start < MAIN_SEGS(sbi); start++) { sit_i->sentries[start].cur_valid_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); sit_i->sentries[start].ckpt_valid_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); if (!sit_i->sentries[start].cur_valid_map || !sit_i->sentries[start].ckpt_valid_map) return -ENOMEM; #ifdef CONFIG_F2FS_CHECK_FS sit_i->sentries[start].cur_valid_map_mir - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); if (!sit_i->sentries[start].cur_valid_map_mir) return -ENOMEM; #endif if (f2fs_discard_en(sbi)) { sit_i->sentries[start].discard_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, + GFP_KERNEL); if (!sit_i->sentries[start].discard_map) return -ENOMEM; } } - sit_i->tmp_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + sit_i->tmp_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); if (!sit_i->tmp_map) return -ENOMEM; if (sbi->segs_per_sec > 1) { - sit_i->sec_entries = kvzalloc(MAIN_SECS(sbi) * + sit_i->sec_entries = f2fs_kvzalloc(sbi, MAIN_SECS(sbi) * sizeof(struct sec_entry), GFP_KERNEL); if (!sit_i->sec_entries) return -ENOMEM; @@ -3512,19 +3505,19 @@ static int build_free_segmap(struct f2fs_sb_info *sbi) unsigned int bitmap_size, sec_bitmap_size; /* allocate memory for free segmap information */ - free_i = kzalloc(sizeof(struct free_segmap_info), GFP_KERNEL); + free_i = f2fs_kzalloc(sbi, sizeof(struct free_segmap_info), GFP_KERNEL); if (!free_i) return -ENOMEM; SM_I(sbi)->free_info = free_i; bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); - free_i->free_segmap = kvmalloc(bitmap_size, GFP_KERNEL); + free_i->free_segmap = f2fs_kvmalloc(sbi, bitmap_size, GFP_KERNEL); if (!free_i->free_segmap) return -ENOMEM; sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); - free_i->free_secmap = kvmalloc(sec_bitmap_size, GFP_KERNEL); + free_i->free_secmap = f2fs_kvmalloc(sbi, sec_bitmap_size, GFP_KERNEL); if (!free_i->free_secmap) return -ENOMEM; @@ -3545,7 +3538,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) struct curseg_info *array; int i; - array = kcalloc(NR_CURSEG_TYPE, sizeof(*array), GFP_KERNEL); + array = f2fs_kzalloc(sbi, sizeof(*array) * NR_CURSEG_TYPE, GFP_KERNEL); if (!array) return -ENOMEM; @@ -3553,12 +3546,12 @@ static int build_curseg(struct f2fs_sb_info *sbi) for (i = 0; i < NR_CURSEG_TYPE; i++) { mutex_init(&array[i].curseg_mutex); - array[i].sum_blk = kzalloc(PAGE_SIZE, GFP_KERNEL); + array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL); if (!array[i].sum_blk) return -ENOMEM; init_rwsem(&array[i].journal_rwsem); - array[i].journal = kzalloc(sizeof(struct f2fs_journal), - GFP_KERNEL); + array[i].journal = f2fs_kzalloc(sbi, + sizeof(struct f2fs_journal), GFP_KERNEL); if (!array[i].journal) return -ENOMEM; array[i].segno = NULL_SEGNO; @@ -3567,7 +3560,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) return restore_curseg_summaries(sbi); } -static void build_sit_entries(struct f2fs_sb_info *sbi) +static int build_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); @@ -3577,6 +3570,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) int sit_blk_cnt = SIT_BLK_CNT(sbi); unsigned int i, start, end; unsigned int readed, start_blk = 0; + int err = 0; do { readed = ra_meta_pages(sbi, start_blk, BIO_MAX_PAGES, @@ -3595,7 +3589,9 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; f2fs_put_page(page, 1); - check_block_count(sbi, start, &sit); + err = check_block_count(sbi, start, &sit); + if (err) + return err; seg_info_from_raw_sit(se, &sit); /* build discard map only one time */ @@ -3630,7 +3626,9 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) old_valid_blocks = se->valid_blocks; - check_block_count(sbi, start, &sit); + err = check_block_count(sbi, start, &sit); + if (err) + break; seg_info_from_raw_sit(se, &sit); if (f2fs_discard_en(sbi)) { @@ -3650,6 +3648,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) se->valid_blocks - old_valid_blocks; } up_read(&curseg->journal_rwsem); + return err; } static void init_free_segmap(struct f2fs_sb_info *sbi) @@ -3704,7 +3703,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 = kvzalloc(bitmap_size, GFP_KERNEL); + dirty_i->victim_secmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); if (!dirty_i->victim_secmap) return -ENOMEM; return 0; @@ -3716,7 +3715,8 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi) unsigned int bitmap_size, i; /* allocate memory for dirty segments list information */ - dirty_i = kzalloc(sizeof(struct dirty_seglist_info), GFP_KERNEL); + dirty_i = f2fs_kzalloc(sbi, sizeof(struct dirty_seglist_info), + GFP_KERNEL); if (!dirty_i) return -ENOMEM; @@ -3726,7 +3726,8 @@ 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] = kvzalloc(bitmap_size, GFP_KERNEL); + dirty_i->dirty_segmap[i] = f2fs_kvzalloc(sbi, bitmap_size, + GFP_KERNEL); if (!dirty_i->dirty_segmap[i]) return -ENOMEM; } @@ -3770,7 +3771,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi) struct f2fs_sm_info *sm_info; int err; - sm_info = kzalloc(sizeof(struct f2fs_sm_info), GFP_KERNEL); + sm_info = f2fs_kzalloc(sbi, sizeof(struct f2fs_sm_info), GFP_KERNEL); if (!sm_info) return -ENOMEM; @@ -3822,7 +3823,9 @@ int build_segment_manager(struct f2fs_sb_info *sbi) return err; /* reinit free segmap based on SIT */ - build_sit_entries(sbi); + err = build_sit_entries(sbi); + if (err) + return err; init_free_segmap(sbi); err = build_dirty_segmap(sbi); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 806e7b7866df..87b4ca0c60da 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -347,16 +347,41 @@ static inline void seg_info_from_raw_sit(struct seg_entry *se, se->mtime = le64_to_cpu(rs->mtime); } -static inline void seg_info_to_raw_sit(struct seg_entry *se, +static inline void __seg_info_to_raw_sit(struct seg_entry *se, struct f2fs_sit_entry *rs) { unsigned short raw_vblocks = (se->type << SIT_VBLOCKS_SHIFT) | se->valid_blocks; rs->vblocks = cpu_to_le16(raw_vblocks); memcpy(rs->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); + rs->mtime = cpu_to_le64(se->mtime); +} + +static inline void seg_info_to_sit_page(struct f2fs_sb_info *sbi, + struct page *page, unsigned int start) +{ + struct f2fs_sit_block *raw_sit; + struct seg_entry *se; + struct f2fs_sit_entry *rs; + unsigned int end = min(start + SIT_ENTRY_PER_BLOCK, + (unsigned long)MAIN_SEGS(sbi)); + int i; + + raw_sit = (struct f2fs_sit_block *)page_address(page); + for (i = 0; i < end - start; i++) { + rs = &raw_sit->entries[i]; + se = get_seg_entry(sbi, start + i); + __seg_info_to_raw_sit(se, rs); + } +} + +static inline void seg_info_to_raw_sit(struct seg_entry *se, + struct f2fs_sit_entry *rs) +{ + __seg_info_to_raw_sit(se, rs); + memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); se->ckpt_valid_blocks = se->valid_blocks; - rs->mtime = cpu_to_le64(se->mtime); } static inline unsigned int find_next_inuse(struct free_segmap_info *free_i, @@ -579,47 +604,6 @@ enum { F2FS_IPU_ASYNC, }; -static inline bool need_inplace_update_policy(struct inode *inode, - struct f2fs_io_info *fio) -{ - struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - unsigned int policy = SM_I(sbi)->ipu_policy; - - if (test_opt(sbi, LFS)) - return false; - - /* if this is cold file, we should overwrite to avoid fragmentation */ - if (file_is_cold(inode)) - return true; - - if (policy & (0x1 << F2FS_IPU_FORCE)) - return true; - if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) - return true; - if (policy & (0x1 << F2FS_IPU_UTIL) && - utilization(sbi) > SM_I(sbi)->min_ipu_util) - return true; - if (policy & (0x1 << F2FS_IPU_SSR_UTIL) && need_SSR(sbi) && - utilization(sbi) > SM_I(sbi)->min_ipu_util) - return true; - - /* - * IPU for rewrite async pages - */ - if (policy & (0x1 << F2FS_IPU_ASYNC) && - fio && fio->op == REQ_OP_WRITE && - !(fio->op_flags & REQ_SYNC) && - !f2fs_encrypted_inode(inode)) - return true; - - /* this is only set during fdatasync */ - if (policy & (0x1 << F2FS_IPU_FSYNC) && - is_inode_flag_set(inode, FI_NEED_IPU)) - return true; - - return false; -} - static inline unsigned int curseg_segno(struct f2fs_sb_info *sbi, int type) { @@ -654,7 +638,7 @@ static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) /* * Summary block is always treated as an invalid block */ -static inline void check_block_count(struct f2fs_sb_info *sbi, +static inline int check_block_count(struct f2fs_sb_info *sbi, int segno, struct f2fs_sit_entry *raw_sit) { #ifdef CONFIG_F2FS_CHECK_FS @@ -676,11 +660,25 @@ static inline void check_block_count(struct f2fs_sb_info *sbi, cur_pos = next_pos; is_valid = !is_valid; } while (cur_pos < sbi->blocks_per_seg); - BUG_ON(GET_SIT_VBLOCKS(raw_sit) != valid_blocks); + + if (unlikely(GET_SIT_VBLOCKS(raw_sit) != valid_blocks)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Mismatch valid blocks %d vs. %d", + GET_SIT_VBLOCKS(raw_sit), valid_blocks); + set_sbi_flag(sbi, SBI_NEED_FSCK); + return -EINVAL; + } #endif /* check segment usage, and check boundary of a given segment number */ - f2fs_bug_on(sbi, GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg - || segno > TOTAL_SEGS(sbi) - 1); + if (unlikely(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg + || segno > TOTAL_SEGS(sbi) - 1)) { + f2fs_msg(sbi->sb, KERN_ERR, + "Wrong valid blocks %d or segno %u", + GET_SIT_VBLOCKS(raw_sit), segno); + set_sbi_flag(sbi, SBI_NEED_FSCK); + return -EINVAL; + } + return 0; } static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index a945db43369e..fd508971abf4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -46,6 +46,7 @@ static struct kmem_cache *f2fs_inode_cachep; char *fault_name[FAULT_MAX] = { [FAULT_KMALLOC] = "kmalloc", + [FAULT_KVMALLOC] = "kvmalloc", [FAULT_PAGE_ALLOC] = "page alloc", [FAULT_PAGE_GET] = "page get", [FAULT_ALLOC_BIO] = "alloc bio", @@ -109,6 +110,9 @@ enum { Opt_noextent_cache, Opt_noinline_data, Opt_data_flush, + Opt_reserve_root, + Opt_resgid, + Opt_resuid, Opt_mode, Opt_io_size_bits, Opt_fault_injection, @@ -159,6 +163,9 @@ static match_table_t f2fs_tokens = { {Opt_noextent_cache, "noextent_cache"}, {Opt_noinline_data, "noinline_data"}, {Opt_data_flush, "data_flush"}, + {Opt_reserve_root, "reserve_root=%u"}, + {Opt_resgid, "resgid=%u"}, + {Opt_resuid, "resuid=%u"}, {Opt_mode, "mode=%s"}, {Opt_io_size_bits, "io_bits=%u"}, {Opt_fault_injection, "fault_injection=%u"}, @@ -193,6 +200,28 @@ void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) va_end(args); } +static inline void limit_reserve_root(struct f2fs_sb_info *sbi) +{ + block_t limit = (sbi->user_block_count << 1) / 1000; + + /* limit is 0.2% */ + if (test_opt(sbi, RESERVE_ROOT) && sbi->root_reserved_blocks > limit) { + sbi->root_reserved_blocks = limit; + f2fs_msg(sbi->sb, KERN_INFO, + "Reduce reserved blocks for root = %u", + sbi->root_reserved_blocks); + } + if (!test_opt(sbi, RESERVE_ROOT) && + (!uid_eq(sbi->s_resuid, + make_kuid(&init_user_ns, F2FS_DEF_RESUID)) || + !gid_eq(sbi->s_resgid, + make_kgid(&init_user_ns, F2FS_DEF_RESGID)))) + f2fs_msg(sbi->sb, KERN_INFO, + "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root", + from_kuid_munged(&init_user_ns, sbi->s_resuid), + from_kgid_munged(&init_user_ns, sbi->s_resgid)); +} + static void init_once(void *foo) { struct f2fs_inode_info *fi = (struct f2fs_inode_info *) foo; @@ -331,6 +360,8 @@ static int parse_options(struct super_block *sb, char *options) substring_t args[MAX_OPT_ARGS]; char *p, *name; int arg = 0; + kuid_t uid; + kgid_t gid; #ifdef CONFIG_QUOTA int ret; #endif @@ -498,6 +529,40 @@ static int parse_options(struct super_block *sb, char *options) case Opt_data_flush: set_opt(sbi, DATA_FLUSH); break; + case Opt_reserve_root: + if (args->from && match_int(args, &arg)) + return -EINVAL; + if (test_opt(sbi, RESERVE_ROOT)) { + f2fs_msg(sb, KERN_INFO, + "Preserve previous reserve_root=%u", + sbi->root_reserved_blocks); + } else { + sbi->root_reserved_blocks = arg; + set_opt(sbi, RESERVE_ROOT); + } + break; + case Opt_resuid: + if (args->from && match_int(args, &arg)) + return -EINVAL; + uid = make_kuid(current_user_ns(), arg); + if (!uid_valid(uid)) { + f2fs_msg(sb, KERN_ERR, + "Invalid uid value %d", arg); + return -EINVAL; + } + sbi->s_resuid = uid; + break; + case Opt_resgid: + if (args->from && match_int(args, &arg)) + return -EINVAL; + gid = make_kgid(current_user_ns(), arg); + if (!gid_valid(gid)) { + f2fs_msg(sb, KERN_ERR, + "Invalid gid value %d", arg); + return -EINVAL; + } + sbi->s_resgid = gid; + break; case Opt_mode: name = match_strdup(&args[0]); @@ -1009,22 +1074,25 @@ 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; + block_t total_count, user_block_count, start_count; u64 avail_node_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 = user_block_count - valid_user_blocks(sbi) + ovp_count; - buf->f_bavail = user_block_count - valid_user_blocks(sbi) - + buf->f_bfree = user_block_count - valid_user_blocks(sbi) - sbi->current_reserved_blocks; + if (buf->f_bfree > sbi->root_reserved_blocks) + buf->f_bavail = buf->f_bfree - sbi->root_reserved_blocks; + else + buf->f_bavail = 0; - avail_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; + avail_node_count = sbi->total_node_count - sbi->nquota_files - + F2FS_RESERVED_NODE_NUM; if (avail_node_count > user_block_count) { buf->f_files = user_block_count; @@ -1150,6 +1218,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) else if (test_opt(sbi, LFS)) seq_puts(seq, "lfs"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); + if (test_opt(sbi, RESERVE_ROOT)) + seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u", + sbi->root_reserved_blocks, + from_kuid_munged(&init_user_ns, sbi->s_resuid), + from_kgid_munged(&init_user_ns, sbi->s_resgid)); if (F2FS_IO_SIZE_BITS(sbi)) seq_printf(seq, ",io_size=%uKB", F2FS_IO_SIZE_KB(sbi)); #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -1279,7 +1352,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) err = dquot_suspend(sb, -1); if (err < 0) goto restore_opts; - } else { + } else if (f2fs_readonly(sb) && !(*flags & MS_RDONLY)) { /* dquot_resume needs RW */ sb->s_flags &= ~MS_RDONLY; if (sb_any_quota_suspended(sb)) { @@ -1348,6 +1421,7 @@ skip: sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); + limit_reserve_root(sbi); return 0; restore_gc: if (need_restart_gc) { @@ -1696,11 +1770,13 @@ void f2fs_quota_off_umount(struct super_block *sb) f2fs_quota_off(sb, type); } -int f2fs_get_projid(struct inode *inode, kprojid_t *projid) +#if 0 /* not support */ +static int f2fs_get_projid(struct inode *inode, kprojid_t *projid) { *projid = F2FS_I(inode)->i_projid; return 0; } +#endif static const struct dquot_operations f2fs_quota_operations = { .get_reserved_space = f2fs_get_reserved_space, @@ -2198,14 +2274,15 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi) if (nr_sectors & (bdev_zone_sectors(bdev) - 1)) FDEV(devi).nr_blkz++; - FDEV(devi).blkz_type = kmalloc(FDEV(devi).nr_blkz, GFP_KERNEL); + FDEV(devi).blkz_type = f2fs_kmalloc(sbi, FDEV(devi).nr_blkz, + GFP_KERNEL); if (!FDEV(devi).blkz_type) return -ENOMEM; #define F2FS_REPORT_NR_ZONES 4096 - zones = kcalloc(F2FS_REPORT_NR_ZONES, sizeof(struct blk_zone), - GFP_KERNEL); + zones = f2fs_kzalloc(sbi, sizeof(struct blk_zone) * + F2FS_REPORT_NR_ZONES, GFP_KERNEL); if (!zones) return -ENOMEM; @@ -2349,8 +2426,8 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) * Initialize multiple devices information, or single * zoned block device information. */ - sbi->devs = kcalloc(max_devices, sizeof(struct f2fs_dev_info), - GFP_KERNEL); + sbi->devs = f2fs_kzalloc(sbi, sizeof(struct f2fs_dev_info) * + max_devices, GFP_KERNEL); if (!sbi->devs) return -ENOMEM; @@ -2473,6 +2550,9 @@ try_onemore: sb->s_fs_info = sbi; sbi->raw_super = raw_super; + sbi->s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID); + sbi->s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID); + /* precompute checksum seed for metadata */ if (f2fs_sb_has_inode_chksum(sb)) sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid, @@ -2517,6 +2597,13 @@ try_onemore: sb->s_qcop = &f2fs_quotactl_ops; #if 0 /* not support */ sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; + + if (f2fs_sb_has_quota_ino(sbi->sb)) { + for (i = 0; i < MAXQUOTAS; i++) { + if (f2fs_qf_ino(sbi->sb, i)) + sbi->nquota_files++; + } + } #endif #endif @@ -2531,6 +2618,8 @@ try_onemore: sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); memcpy(sb->s_uuid, raw_super->uuid, sizeof(raw_super->uuid)); + /* FIXME: no cgroup support */ + /* sb->s_iflags |= SB_I_CGROUPWB; */ /* init f2fs-specific super block info */ sbi->valid_super_block = valid_super_block; @@ -2551,8 +2640,9 @@ try_onemore: int n = (i == META) ? 1: NR_TEMP_TYPE; int j; - sbi->write_io[i] = kmalloc(n * sizeof(struct f2fs_bio_info), - GFP_KERNEL); + sbi->write_io[i] = f2fs_kmalloc(sbi, + n * sizeof(struct f2fs_bio_info), + GFP_KERNEL); if (!sbi->write_io[i]) { err = -ENOMEM; goto free_options; @@ -2573,14 +2663,14 @@ try_onemore: err = init_percpu_info(sbi); if (err) - goto free_options; + goto free_bio_info; if (F2FS_IO_SIZE(sbi) > 1) { sbi->write_io_dummy = mempool_create_page_pool(2 * (F2FS_IO_SIZE(sbi) - 1), 0); if (!sbi->write_io_dummy) { err = -ENOMEM; - goto free_options; + goto free_percpu; } } @@ -2615,6 +2705,7 @@ try_onemore: sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->reserved_blocks = 0; sbi->current_reserved_blocks = 0; + limit_reserve_root(sbi); for (i = 0; i < NR_INODE_TYPE; i++) { INIT_LIST_HEAD(&sbi->inode_list[i]); @@ -2660,18 +2751,16 @@ try_onemore: goto free_nm; } - f2fs_join_shrinker(sbi); - err = f2fs_build_stats(sbi); if (err) - goto free_nm; + goto free_node_inode; /* read root inode and dentry */ root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); if (IS_ERR(root)) { f2fs_msg(sb, KERN_ERR, "Failed to read root inode"); err = PTR_ERR(root); - goto free_node_inode; + goto free_stats; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { iput(root); @@ -2767,6 +2856,8 @@ skip_recovery: sbi->valid_super_block ? 1 : 2, err); } + f2fs_join_shrinker(sbi); + f2fs_msg(sbi->sb, KERN_NOTICE, "Mounted with checkpoint version = %llx", cur_cp_version(F2FS_CKPT(sbi))); f2fs_update_time(sbi, CP_TIME); @@ -2793,14 +2884,12 @@ free_sysfs: free_root_inode: dput(sb->s_root); sb->s_root = NULL; +free_stats: + f2fs_destroy_stats(sbi); free_node_inode: - truncate_inode_pages_final(NODE_MAPPING(sbi)); - mutex_lock(&sbi->umount_mutex); release_ino_entry(sbi, true); - f2fs_leave_shrinker(sbi); + truncate_inode_pages_final(NODE_MAPPING(sbi)); iput(sbi->node_inode); - mutex_unlock(&sbi->umount_mutex); - f2fs_destroy_stats(sbi); free_nm: destroy_node_manager(sbi); free_sm: @@ -2814,10 +2903,12 @@ free_meta_inode: free_io_dummy: if (sbi->write_io_dummy) mempool_destroy(sbi->write_io_dummy); -free_options: +free_percpu: + destroy_percpu_info(sbi); +free_bio_info: for (i = 0; i < NR_PAGE_TYPE; i++) kfree(sbi->write_io[i]); - destroy_percpu_info(sbi); +free_options: #ifdef CONFIG_QUOTA for (i = 0; i < F2FS_MAXQUOTAS; i++) kfree(sbi->s_qf_names[i]); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 5c14bcaa19a4..6485a049e757 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -113,6 +113,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_quota_ino(sb)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "quota_ino"); + if (f2fs_sb_has_inode_crtime(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "inode_crtime"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } @@ -162,7 +165,8 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, #endif if (a->struct_type == RESERVED_BLOCKS) { spin_lock(&sbi->stat_lock); - if (t > (unsigned long)sbi->user_block_count) { + if (t > (unsigned long)(sbi->user_block_count - + sbi->root_reserved_blocks)) { spin_unlock(&sbi->stat_lock); return -EINVAL; } @@ -231,6 +235,7 @@ enum feat_id { FEAT_INODE_CHECKSUM, FEAT_FLEXIBLE_INLINE_XATTR, FEAT_QUOTA_INO, + FEAT_INODE_CRTIME, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -245,6 +250,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_INODE_CHECKSUM: case FEAT_FLEXIBLE_INLINE_XATTR: case FEAT_QUOTA_INO: + case FEAT_INODE_CRTIME: return snprintf(buf, PAGE_SIZE, "supported\n"); } return 0; @@ -299,6 +305,8 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold); #ifdef CONFIG_F2FS_FAULT_INJECTION F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); @@ -320,6 +328,7 @@ F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO); +F2FS_FEATURE_RO_ATTR(inode_crtime, FEAT_INODE_CRTIME); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -346,6 +355,8 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), ATTR_LIST(iostat_enable), + ATTR_LIST(readdir_ra), + ATTR_LIST(gc_pin_file_thresh), #ifdef CONFIG_F2FS_FAULT_INJECTION ATTR_LIST(inject_rate), ATTR_LIST(inject_type), @@ -371,6 +382,7 @@ static struct attribute *f2fs_feat_attrs[] = { ATTR_LIST(inode_checksum), ATTR_LIST(flexible_inline_xattr), ATTR_LIST(quota_ino), + ATTR_LIST(inode_crtime), NULL, }; diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c index bccbbf2616d2..a1fcd00bbb2b 100644 --- a/fs/f2fs/trace.c +++ b/fs/f2fs/trace.c @@ -17,7 +17,7 @@ #include "trace.h" static RADIX_TREE(pids, GFP_ATOMIC); -static spinlock_t pids_lock; +static struct mutex pids_lock; static struct last_io_info last_io; static inline void __print_last_io(void) @@ -64,7 +64,7 @@ void f2fs_trace_pid(struct page *page) if (radix_tree_preload(GFP_NOFS)) return; - spin_lock(&pids_lock); + mutex_lock(&pids_lock); p = radix_tree_lookup(&pids, pid); if (p == current) goto out; @@ -77,7 +77,7 @@ void f2fs_trace_pid(struct page *page) MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), pid, current->comm); out: - spin_unlock(&pids_lock); + mutex_unlock(&pids_lock); radix_tree_preload_end(); } @@ -122,7 +122,7 @@ void f2fs_trace_ios(struct f2fs_io_info *fio, int flush) void f2fs_build_trace_ios(void) { - spin_lock_init(&pids_lock); + mutex_init(&pids_lock); } #define PIDVEC_SIZE 128 @@ -150,7 +150,7 @@ void f2fs_destroy_trace_ios(void) pid_t next_pid = 0; unsigned int found; - spin_lock(&pids_lock); + mutex_lock(&pids_lock); while ((found = gang_lookup_pids(pid, next_pid, PIDVEC_SIZE))) { unsigned idx; @@ -158,5 +158,5 @@ void f2fs_destroy_trace_ios(void) for (idx = 0; idx < found; idx++) radix_tree_delete(&pids, pid[idx]); } - spin_unlock(&pids_lock); + mutex_unlock(&pids_lock); } diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 56c80831976b..967d6996ba02 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -347,8 +347,8 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, if (!size && !inline_size) return -ENODATA; - txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE, - GFP_F2FS_ZERO); + txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), + inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS); if (!txattr_addr) return -ENOMEM; @@ -400,8 +400,8 @@ static int read_all_xattrs(struct inode *inode, struct page *ipage, void *txattr_addr; int err; - txattr_addr = kzalloc(inline_size + size + XATTR_PADDING_SIZE, - GFP_F2FS_ZERO); + txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), + inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS); if (!txattr_addr) return -ENOMEM; @@ -482,6 +482,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, if (F2FS_I(inode)->i_xattr_nid) { xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); if (IS_ERR(xpage)) { + err = PTR_ERR(xpage); alloc_nid_failed(sbi, new_nid); goto in_page_out; } @@ -492,6 +493,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, set_new_dnode(&dn, inode, NULL, NULL, new_nid); xpage = new_node_page(&dn, XATTR_NODE_OFFSET); if (IS_ERR(xpage)) { + err = PTR_ERR(xpage); alloc_nid_failed(sbi, new_nid); goto in_page_out; } @@ -640,7 +642,7 @@ static int __f2fs_setxattr(struct inode *inode, int index, goto exit; } - if (f2fs_xattr_value_same(here, value, size)) + if (value && f2fs_xattr_value_same(here, value, size)) goto exit; } else if ((flags & XATTR_REPLACE)) { error = -ENODATA; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 30dc9d69d60e..9094fd49948b 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -117,6 +117,7 @@ struct f2fs_super_block { /* * For checkpoint */ +#define CP_NOCRC_RECOVERY_FLAG 0x00000200 #define CP_TRIMMED_FLAG 0x00000100 #define CP_NAT_BITS_FLAG 0x00000080 #define CP_CRC_RECOVERY_FLAG 0x00000040 @@ -212,6 +213,7 @@ struct f2fs_extent { #define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ #define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ #define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */ +#define F2FS_PIN_FILE 0x40 /* file should not be gced */ struct f2fs_inode { __le16 i_mode; /* file mode */ @@ -229,7 +231,13 @@ struct f2fs_inode { __le32 i_ctime_nsec; /* change time in nano scale */ __le32 i_mtime_nsec; /* modification time in nano scale */ __le32 i_generation; /* file version (for NFS) */ - __le32 i_current_depth; /* only for directory depth */ + union { + __le32 i_current_depth; /* only for directory depth */ + __le16 i_gc_failures; /* + * # of gc failures on pinned file. + * only for regular files. + */ + }; __le32 i_xattr_nid; /* nid to save xattr */ __le32 i_flags; /* file attributes */ __le32 i_pino; /* parent inode number */ @@ -245,8 +253,10 @@ struct f2fs_inode { __le16 i_inline_xattr_size; /* inline xattr size, unit: 4 bytes */ __le32 i_projid; /* project id */ __le32 i_inode_checksum;/* inode meta checksum */ + __le64 i_crtime; /* creation time */ + __le32 i_crtime_nsec; /* creation time in nano scale */ __le32 i_extra_end[0]; /* for attribute size calculation */ - }; + } __packed; __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ }; __le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2), diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 7e117885dde5..4a089373838d 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -101,7 +101,8 @@ { CP_NO_SPC_ROLL, "no space roll forward" }, \ { CP_NODE_NEED_CP, "node needs cp" }, \ { CP_FASTBOOT_MODE, "fastboot mode" }, \ - { CP_SPEC_LOG_NUM, "log type is 2" }) + { CP_SPEC_LOG_NUM, "log type is 2" }, \ + { CP_RECOVER_DIR, "dir needs recovery" }) struct victim_sel_policy; struct f2fs_map_blocks; -- GitLab From 8156f690c1e17ff026edba37262ac67ca93ffd31 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Mon, 23 Oct 2017 11:36:25 +0530 Subject: [PATCH 2694/5498] mtd: msm_qpic_nand: add 10sec SW timeout Add 10sec SW request timeout for read, write, erase and isbad functionality. Change-Id: I647d79a9f9c501f8c3fe8949b4a1d64b52f1abab Signed-off-by: Sahitya Tummala Signed-off-by: Pradeep P V K --- drivers/mtd/devices/msm_qpic_nand.c | 37 ++++++++++++++++++++++++++++- drivers/mtd/devices/msm_qpic_nand.h | 3 ++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c index 9ed3a104111d..bc7b4185d537 100644 --- a/drivers/mtd/devices/msm_qpic_nand.c +++ b/drivers/mtd/devices/msm_qpic_nand.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,8 +14,10 @@ */ #include "msm_qpic_nand.h" +#include #define QPIC_BAM_DEFAULT_IPC_LOGLVL 2 +#define SW_REQ_TIMEOUT_SEC 10 static bool enable_euclean; static bool enable_perfstats; @@ -196,6 +198,25 @@ static void msm_nand_update_erase_perf_stats(struct msm_nand_info *info, spin_unlock(&info->perf.lock); } +static struct timer_list timer; + +static void msm_nand_tout_work_fn(struct work_struct *work) +{ + struct msm_nand_info *info = container_of(work, struct msm_nand_info, + tout_work); + + sps_get_bam_debug_info(info->sps.bam_handle, 93, + (SPS_BAM_PIPE(0) | SPS_BAM_PIPE(1) | SPS_BAM_PIPE(2)), + 0, 2); +} +static void msm_nand_transfer_timeout(unsigned long data) +{ + struct msm_nand_info *info = (struct msm_nand_info *)data; + + pr_err("NAND request timeout\n"); + schedule_work(&info->tout_work); +} + /* * Get the DMA memory for requested amount of size. It returns the pointer * to free memory available from the allocated pool. Returns NULL if there @@ -1888,6 +1909,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, iovec++; } mutex_lock(&info->lock); + mod_timer(&timer, jiffies + SW_REQ_TIMEOUT_SEC * HZ); err = msm_nand_get_device(chip->dev); if (err) goto unlock_mutex; @@ -1939,6 +1961,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } err = msm_nand_put_device(chip->dev); + del_timer_sync(&timer); mutex_unlock(&info->lock); if (err) goto free_dma; @@ -2065,6 +2088,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, put_dev: msm_nand_put_device(chip->dev); unlock_mutex: + del_timer_sync(&timer); mutex_unlock(&info->lock); free_dma: msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -2432,6 +2456,7 @@ static int msm_nand_write_oob(struct mtd_info *mtd, loff_t to, iovec++; } mutex_lock(&info->lock); + mod_timer(&timer, jiffies + SW_REQ_TIMEOUT_SEC * HZ); err = msm_nand_get_device(chip->dev); if (err) goto unlock_mutex; @@ -2482,6 +2507,7 @@ static int msm_nand_write_oob(struct mtd_info *mtd, loff_t to, } err = msm_nand_put_device(chip->dev); + del_timer_sync(&timer); mutex_unlock(&info->lock); if (err) goto free_dma; @@ -2515,6 +2541,7 @@ static int msm_nand_write_oob(struct mtd_info *mtd, loff_t to, put_dev: msm_nand_put_device(chip->dev); unlock_mutex: + del_timer_sync(&timer); mutex_unlock(&info->lock); free_dma: msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -2721,6 +2748,7 @@ static int msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) iovec++; } mutex_lock(&info->lock); + mod_timer(&timer, jiffies + SW_REQ_TIMEOUT_SEC * HZ); err = msm_nand_get_device(chip->dev); if (err) goto unlock_mutex; @@ -2765,6 +2793,7 @@ static int msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) put_dev: msm_nand_put_device(chip->dev); unlock_mutex: + del_timer_sync(&timer); mutex_unlock(&info->lock); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); out: @@ -2892,8 +2921,10 @@ static int msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) iovec++; } mutex_lock(&info->lock); + mod_timer(&timer, jiffies + SW_REQ_TIMEOUT_SEC * HZ); ret = msm_nand_get_device(chip->dev); if (ret) { + del_timer_sync(&timer); mutex_unlock(&info->lock); goto free_dma; } @@ -2931,6 +2962,7 @@ static int msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) } ret = msm_nand_put_device(chip->dev); + del_timer_sync(&timer); mutex_unlock(&info->lock); if (ret) goto free_dma; @@ -2953,6 +2985,7 @@ static int msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) goto free_dma; put_dev: msm_nand_put_device(chip->dev); + del_timer_sync(&timer); mutex_unlock(&info->lock); free_dma: msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4); @@ -3708,6 +3741,8 @@ static int msm_nand_probe(struct platform_device *pdev) err = -ENXIO; goto free_bam; } + INIT_WORK(&info->tout_work, msm_nand_tout_work_fn); + setup_timer(&timer, msm_nand_transfer_timeout, (unsigned long)info); for (i = 0; i < nr_parts; i++) { mtd_part[i].offset *= info->mtd.erasesize; mtd_part[i].size *= info->mtd.erasesize; diff --git a/drivers/mtd/devices/msm_qpic_nand.h b/drivers/mtd/devices/msm_qpic_nand.h index 20765a836ffa..383453598475 100644 --- a/drivers/mtd/devices/msm_qpic_nand.h +++ b/drivers/mtd/devices/msm_qpic_nand.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -341,6 +341,7 @@ struct msm_nand_info { struct msm_nand_clk_data clk_data; struct msm_nand_perf_stats perf; u64 dma_mask; + struct work_struct tout_work; }; /* Structure that defines an ONFI parameter page (512B) */ -- GitLab From cbb1e53735c3332e5567a9c8bc65654cb84788d4 Mon Sep 17 00:00:00 2001 From: smanag Date: Tue, 14 Nov 2017 14:57:57 +0530 Subject: [PATCH 2695/5498] drivers: soc: Synchronize apr callback and voice svc release Issue is seen when apr callback is received while voice_svc_release is in process of freeing the driver private data. Avoid invalid access of private data pointer by putting the callback and release functions in the same locked context. Change-Id: I93af13cab0a3c7e653a9bc9fa7f4f86bfa0502df Signed-off-by: smanag --- drivers/soc/qcom/qdsp6v2/voice_svc.c | 42 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c index fe5458974406..40204e104031 100644 --- a/drivers/soc/qcom/qdsp6v2/voice_svc.c +++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -67,7 +68,11 @@ static void *dummy_q6_mvm; static void *dummy_q6_cvs; dev_t device_num; +static spinlock_t voicesvc_lock; +static bool is_released; static int voice_svc_dummy_reg(void); +static int voice_svc_dummy_dereg(void); + static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data, void *priv); @@ -82,10 +87,16 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv) return -EINVAL; } + spin_lock(&voicesvc_lock); + if (is_released) { + spin_unlock(&voicesvc_lock); + return 0; + } prtd = (struct voice_svc_prvt *)priv; if (prtd == NULL) { pr_err("%s: private data is NULL\n", __func__); + spin_unlock(&voicesvc_lock); return -EINVAL; } @@ -128,6 +139,7 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv) spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + spin_unlock(&voicesvc_lock); return -ENOMEM; } @@ -156,6 +168,7 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv) __func__); } + spin_unlock(&voicesvc_lock); return 0; } @@ -614,6 +627,21 @@ err: return -EINVAL; } +static int voice_svc_dummy_dereg(void) +{ + pr_debug("%s\n", __func__); + if (dummy_q6_mvm != NULL) { + apr_deregister(dummy_q6_mvm); + dummy_q6_mvm = NULL; + } + + if (dummy_q6_cvs != NULL) { + apr_deregister(dummy_q6_cvs); + dummy_q6_cvs = NULL; + } + return 0; +} + static int voice_svc_open(struct inode *inode, struct file *file) { struct voice_svc_prvt *prtd = NULL; @@ -637,6 +665,7 @@ static int voice_svc_open(struct inode *inode, struct file *file) mutex_init(&prtd->response_mutex_lock); file->private_data = (void *)prtd; + is_released = 0; /* Current APR implementation doesn't support session based * multiple service registrations. The apr_deregister() * function sets the destination and client IDs to zero, if @@ -669,6 +698,11 @@ static int voice_svc_release(struct inode *inode, struct file *file) goto done; } + mutex_lock(&prtd->response_mutex_lock); + if (reg_dummy_sess) { + voice_svc_dummy_dereg(); + reg_dummy_sess = 0; + } if (prtd->apr_q6_cvs != NULL) { svc_name = VOICE_SVC_MVM_STR; handle = &prtd->apr_q6_cvs; @@ -685,7 +719,6 @@ static int voice_svc_release(struct inode *inode, struct file *file) pr_err("%s: Failed to dereg MVM %d\n", __func__, ret); } - mutex_lock(&prtd->response_mutex_lock); spin_lock_irqsave(&prtd->response_lock, spin_flags); while (!list_empty(&prtd->response_queue)) { @@ -703,9 +736,11 @@ static int voice_svc_release(struct inode *inode, struct file *file) mutex_destroy(&prtd->response_mutex_lock); + spin_lock(&voicesvc_lock); kfree(file->private_data); file->private_data = NULL; - + is_released = 1; + spin_unlock(&voicesvc_lock); done: return ret; } @@ -774,6 +809,7 @@ static int voice_svc_probe(struct platform_device *pdev) goto add_err; } pr_debug("%s: Device created\n", __func__); + spin_lock_init(&voicesvc_lock); goto done; add_err: -- GitLab From 07874822bcbfe506c4b6185c6b822f7d053b8783 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 14 Feb 2018 10:42:19 -0800 Subject: [PATCH 2696/5498] BACKPORT, FROMGIT: crypto: speck - add support for the Speck block cipher Add a generic implementation of Speck, including the Speck128 and Speck64 variants. Speck is a lightweight block cipher that can be much faster than AES on processors that don't have AES instructions. We are planning to offer Speck-XTS (probably Speck128/256-XTS) as an option for dm-crypt and fscrypt on Android, for low-end mobile devices with older CPUs such as ARMv7 which don't have the Cryptography Extensions. Currently, such devices are unencrypted because AES is not fast enough, even when the NEON bit-sliced implementation of AES is used. Other AES alternatives such as Twofish, Threefish, Camellia, CAST6, and Serpent aren't fast enough either; it seems that only a modern ARX cipher can provide sufficient performance on these devices. This is a replacement for our original proposal (https://patchwork.kernel.org/patch/10101451/) which was to offer ChaCha20 for these devices. However, the use of a stream cipher for disk/file encryption with no space to store nonces would have been much more insecure than we thought initially, given that it would be used on top of flash storage as well as potentially on top of F2FS, neither of which is guaranteed to overwrite data in-place. Speck has been somewhat controversial due to its origin. Nevertheless, it has a straightforward design (it's an ARX cipher), and it appears to be the leading software-optimized lightweight block cipher currently, with the most cryptanalysis. It's also easy to implement without side channels, unlike AES. Moreover, we only intend Speck to be used when the status quo is no encryption, due to AES not being fast enough. We've also considered a novel length-preserving encryption mode based on ChaCha20 and Poly1305. While theoretically attractive, such a mode would be a brand new crypto construction and would be more complicated and difficult to implement efficiently in comparison to Speck-XTS. There is confusion about the byte and word orders of Speck, since the original paper doesn't specify them. But we have implemented it using the orders the authors recommended in a correspondence with them. The test vectors are taken from the original paper but were mapped to byte arrays using the recommended byte and word orders. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu (cherry picked from commit da7a0ab5b4babbe5d7a46f852582be06a00a28f0 git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master) (removed 'const' from test vectors) (replaced use of __VECS macro in crypto/testmgr.c) Change-Id: Id13c44dee8e3817590950c178d54b24c3aee0b4e Signed-off-by: Eric Biggers --- crypto/Kconfig | 14 +++ crypto/Makefile | 1 + crypto/speck.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++ crypto/testmgr.c | 30 +++++ crypto/testmgr.h | 128 ++++++++++++++++++++ 5 files changed, 472 insertions(+) create mode 100644 crypto/speck.c diff --git a/crypto/Kconfig b/crypto/Kconfig index 8bdd83493d7d..86fe6e76fffd 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1289,6 +1289,20 @@ config CRYPTO_SERPENT_AVX2_X86_64 See also: +config CRYPTO_SPECK + tristate "Speck cipher algorithm" + select CRYPTO_ALGAPI + help + Speck is a lightweight block cipher that is tuned for optimal + performance in software (rather than hardware). + + Speck may not be as secure as AES, and should only be used on systems + where AES is not fast enough. + + See also: + + If unsure, say N. + config CRYPTO_TEA tristate "TEA, XTEA and XETA cipher algorithms" select CRYPTO_ALGAPI diff --git a/crypto/Makefile b/crypto/Makefile index ea11cf871ebc..c1de48157dc4 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_CRYPTO_TEA) += tea.o obj-$(CONFIG_CRYPTO_KHAZAD) += khazad.o obj-$(CONFIG_CRYPTO_ANUBIS) += anubis.o obj-$(CONFIG_CRYPTO_SEED) += seed.o +obj-$(CONFIG_CRYPTO_SPECK) += speck.o obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o diff --git a/crypto/speck.c b/crypto/speck.c new file mode 100644 index 000000000000..4e80ad76bcd7 --- /dev/null +++ b/crypto/speck.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Speck: a lightweight block cipher + * + * Copyright (c) 2018 Google, Inc + * + * Speck has 10 variants, including 5 block sizes. For now we only implement + * the variants Speck128/128, Speck128/192, Speck128/256, Speck64/96, and + * Speck64/128. Speck${B}/${K} denotes the variant with a block size of B bits + * and a key size of K bits. The Speck128 variants are believed to be the most + * secure variants, and they use the same block size and key sizes as AES. The + * Speck64 variants are less secure, but on 32-bit processors are usually + * faster. The remaining variants (Speck32, Speck48, and Speck96) are even less + * secure and/or not as well suited for implementation on either 32-bit or + * 64-bit processors, so are omitted. + * + * Reference: "The Simon and Speck Families of Lightweight Block Ciphers" + * https://eprint.iacr.org/2013/404.pdf + * + * In a correspondence, the Speck designers have also clarified that the words + * should be interpreted in little-endian format, and the words should be + * ordered such that the first word of each block is 'y' rather than 'x', and + * the first key word (rather than the last) becomes the first round key. + */ + +#include +#include +#include +#include +#include + +/* Speck128 */ + +#define SPECK128_BLOCK_SIZE 16 + +#define SPECK128_128_KEY_SIZE 16 +#define SPECK128_128_NROUNDS 32 + +#define SPECK128_192_KEY_SIZE 24 +#define SPECK128_192_NROUNDS 33 + +#define SPECK128_256_KEY_SIZE 32 +#define SPECK128_256_NROUNDS 34 + +struct speck128_tfm_ctx { + u64 round_keys[SPECK128_256_NROUNDS]; + int nrounds; +}; + +static __always_inline void speck128_round(u64 *x, u64 *y, u64 k) +{ + *x = ror64(*x, 8); + *x += *y; + *x ^= k; + *y = rol64(*y, 3); + *y ^= *x; +} + +static __always_inline void speck128_unround(u64 *x, u64 *y, u64 k) +{ + *y ^= *x; + *y = ror64(*y, 3); + *x ^= k; + *x -= *y; + *x = rol64(*x, 8); +} + +static void speck128_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + const struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + u64 y = get_unaligned_le64(in); + u64 x = get_unaligned_le64(in + 8); + int i; + + for (i = 0; i < ctx->nrounds; i++) + speck128_round(&x, &y, ctx->round_keys[i]); + + put_unaligned_le64(y, out); + put_unaligned_le64(x, out + 8); +} + +static void speck128_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + const struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + u64 y = get_unaligned_le64(in); + u64 x = get_unaligned_le64(in + 8); + int i; + + for (i = ctx->nrounds - 1; i >= 0; i--) + speck128_unround(&x, &y, ctx->round_keys[i]); + + put_unaligned_le64(y, out); + put_unaligned_le64(x, out + 8); +} + +static int speck128_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + u64 l[3]; + u64 k; + int i; + + switch (keylen) { + case SPECK128_128_KEY_SIZE: + k = get_unaligned_le64(key); + l[0] = get_unaligned_le64(key + 8); + ctx->nrounds = SPECK128_128_NROUNDS; + for (i = 0; i < ctx->nrounds; i++) { + ctx->round_keys[i] = k; + speck128_round(&l[0], &k, i); + } + break; + case SPECK128_192_KEY_SIZE: + k = get_unaligned_le64(key); + l[0] = get_unaligned_le64(key + 8); + l[1] = get_unaligned_le64(key + 16); + ctx->nrounds = SPECK128_192_NROUNDS; + for (i = 0; i < ctx->nrounds; i++) { + ctx->round_keys[i] = k; + speck128_round(&l[i % 2], &k, i); + } + break; + case SPECK128_256_KEY_SIZE: + k = get_unaligned_le64(key); + l[0] = get_unaligned_le64(key + 8); + l[1] = get_unaligned_le64(key + 16); + l[2] = get_unaligned_le64(key + 24); + ctx->nrounds = SPECK128_256_NROUNDS; + for (i = 0; i < ctx->nrounds; i++) { + ctx->round_keys[i] = k; + speck128_round(&l[i % 3], &k, i); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Speck64 */ + +#define SPECK64_BLOCK_SIZE 8 + +#define SPECK64_96_KEY_SIZE 12 +#define SPECK64_96_NROUNDS 26 + +#define SPECK64_128_KEY_SIZE 16 +#define SPECK64_128_NROUNDS 27 + +struct speck64_tfm_ctx { + u32 round_keys[SPECK64_128_NROUNDS]; + int nrounds; +}; + +static __always_inline void speck64_round(u32 *x, u32 *y, u32 k) +{ + *x = ror32(*x, 8); + *x += *y; + *x ^= k; + *y = rol32(*y, 3); + *y ^= *x; +} + +static __always_inline void speck64_unround(u32 *x, u32 *y, u32 k) +{ + *y ^= *x; + *y = ror32(*y, 3); + *x ^= k; + *x -= *y; + *x = rol32(*x, 8); +} + +static void speck64_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + const struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + u32 y = get_unaligned_le32(in); + u32 x = get_unaligned_le32(in + 4); + int i; + + for (i = 0; i < ctx->nrounds; i++) + speck64_round(&x, &y, ctx->round_keys[i]); + + put_unaligned_le32(y, out); + put_unaligned_le32(x, out + 4); +} + +static void speck64_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + const struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + u32 y = get_unaligned_le32(in); + u32 x = get_unaligned_le32(in + 4); + int i; + + for (i = ctx->nrounds - 1; i >= 0; i--) + speck64_unround(&x, &y, ctx->round_keys[i]); + + put_unaligned_le32(y, out); + put_unaligned_le32(x, out + 4); +} + +static int speck64_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + u32 l[3]; + u32 k; + int i; + + switch (keylen) { + case SPECK64_96_KEY_SIZE: + k = get_unaligned_le32(key); + l[0] = get_unaligned_le32(key + 4); + l[1] = get_unaligned_le32(key + 8); + ctx->nrounds = SPECK64_96_NROUNDS; + for (i = 0; i < ctx->nrounds; i++) { + ctx->round_keys[i] = k; + speck64_round(&l[i % 2], &k, i); + } + break; + case SPECK64_128_KEY_SIZE: + k = get_unaligned_le32(key); + l[0] = get_unaligned_le32(key + 4); + l[1] = get_unaligned_le32(key + 8); + l[2] = get_unaligned_le32(key + 12); + ctx->nrounds = SPECK64_128_NROUNDS; + for (i = 0; i < ctx->nrounds; i++) { + ctx->round_keys[i] = k; + speck64_round(&l[i % 3], &k, i); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Algorithm definitions */ + +static struct crypto_alg speck_algs[] = { + { + .cra_name = "speck128", + .cra_driver_name = "speck128-generic", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = SPECK128_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct speck128_tfm_ctx), + .cra_module = THIS_MODULE, + .cra_u = { + .cipher = { + .cia_min_keysize = SPECK128_128_KEY_SIZE, + .cia_max_keysize = SPECK128_256_KEY_SIZE, + .cia_setkey = speck128_setkey, + .cia_encrypt = speck128_encrypt, + .cia_decrypt = speck128_decrypt + } + } + }, { + .cra_name = "speck64", + .cra_driver_name = "speck64-generic", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = SPECK64_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct speck64_tfm_ctx), + .cra_module = THIS_MODULE, + .cra_u = { + .cipher = { + .cia_min_keysize = SPECK64_96_KEY_SIZE, + .cia_max_keysize = SPECK64_128_KEY_SIZE, + .cia_setkey = speck64_setkey, + .cia_encrypt = speck64_encrypt, + .cia_decrypt = speck64_decrypt + } + } + } +}; + +static int __init speck_module_init(void) +{ + return crypto_register_algs(speck_algs, ARRAY_SIZE(speck_algs)); +} + +static void __exit speck_module_exit(void) +{ + crypto_unregister_algs(speck_algs, ARRAY_SIZE(speck_algs)); +} + +module_init(speck_module_init); +module_exit(speck_module_exit); + +MODULE_DESCRIPTION("Speck block cipher (generic)"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Biggers "); +MODULE_ALIAS_CRYPTO("speck128"); +MODULE_ALIAS_CRYPTO("speck128-generic"); +MODULE_ALIAS_CRYPTO("speck64"); +MODULE_ALIAS_CRYPTO("speck64-generic"); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 29a0cbdd0d19..7a0f07e40644 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -2912,6 +2912,36 @@ static const struct alg_test_desc alg_test_descs[] = { } } } + }, { + .alg = "ecb(speck128)", + .test = alg_test_skcipher, + .suite = { + .cipher = { + .enc = { + .vecs = speck128_enc_tv_template, + .count = ARRAY_SIZE(speck128_enc_tv_template) + }, + .dec = { + .vecs = speck128_dec_tv_template, + .count = ARRAY_SIZE(speck128_dec_tv_template) + } + } + } + }, { + .alg = "ecb(speck64)", + .test = alg_test_skcipher, + .suite = { + .cipher = { + .enc = { + .vecs = speck64_enc_tv_template, + .count = ARRAY_SIZE(speck64_enc_tv_template) + }, + .dec = { + .vecs = speck64_dec_tv_template, + .count = ARRAY_SIZE(speck64_dec_tv_template) + } + } + } }, { .alg = "ecb(tea)", .test = alg_test_skcipher, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 62e2485bb428..0fa026051350 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -12060,6 +12060,134 @@ static struct cipher_testvec serpent_xts_dec_tv_template[] = { }, }; +/* + * Speck test vectors taken from the original paper: + * "The Simon and Speck Families of Lightweight Block Ciphers" + * https://eprint.iacr.org/2013/404.pdf + * + * Note that the paper does not make byte and word order clear. But it was + * confirmed with the authors that the intended orders are little endian byte + * order and (y, x) word order. Equivalently, the printed test vectors, when + * looking at only the bytes (ignoring the whitespace that divides them into + * words), are backwards: the left-most byte is actually the one with the + * highest memory address, while the right-most byte is actually the one with + * the lowest memory address. + */ + +static struct cipher_testvec speck128_enc_tv_template[] = { + { /* Speck128/128 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\x20\x6d\x61\x64\x65\x20\x69\x74" + "\x20\x65\x71\x75\x69\x76\x61\x6c", + .ilen = 16, + .result = "\x18\x0d\x57\x5c\xdf\xfe\x60\x78" + "\x65\x32\x78\x79\x51\x98\x5d\xa6", + .rlen = 16, + }, { /* Speck128/192 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17", + .klen = 24, + .input = "\x65\x6e\x74\x20\x74\x6f\x20\x43" + "\x68\x69\x65\x66\x20\x48\x61\x72", + .ilen = 16, + .result = "\x86\x18\x3c\xe0\x5d\x18\xbc\xf9" + "\x66\x55\x13\x13\x3a\xcf\xe4\x1b", + .rlen = 16, + }, { /* Speck128/256 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .input = "\x70\x6f\x6f\x6e\x65\x72\x2e\x20" + "\x49\x6e\x20\x74\x68\x6f\x73\x65", + .ilen = 16, + .result = "\x43\x8f\x18\x9c\x8d\xb4\xee\x4e" + "\x3e\xf5\xc0\x05\x04\x01\x09\x41", + .rlen = 16, + }, +}; + +static struct cipher_testvec speck128_dec_tv_template[] = { + { /* Speck128/128 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .klen = 16, + .input = "\x18\x0d\x57\x5c\xdf\xfe\x60\x78" + "\x65\x32\x78\x79\x51\x98\x5d\xa6", + .ilen = 16, + .result = "\x20\x6d\x61\x64\x65\x20\x69\x74" + "\x20\x65\x71\x75\x69\x76\x61\x6c", + .rlen = 16, + }, { /* Speck128/192 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17", + .klen = 24, + .input = "\x86\x18\x3c\xe0\x5d\x18\xbc\xf9" + "\x66\x55\x13\x13\x3a\xcf\xe4\x1b", + .ilen = 16, + .result = "\x65\x6e\x74\x20\x74\x6f\x20\x43" + "\x68\x69\x65\x66\x20\x48\x61\x72", + .rlen = 16, + }, { /* Speck128/256 */ + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + .klen = 32, + .input = "\x43\x8f\x18\x9c\x8d\xb4\xee\x4e" + "\x3e\xf5\xc0\x05\x04\x01\x09\x41", + .ilen = 16, + .result = "\x70\x6f\x6f\x6e\x65\x72\x2e\x20" + "\x49\x6e\x20\x74\x68\x6f\x73\x65", + .rlen = 16, + }, +}; + +static struct cipher_testvec speck64_enc_tv_template[] = { + { /* Speck64/96 */ + .key = "\x00\x01\x02\x03\x08\x09\x0a\x0b" + "\x10\x11\x12\x13", + .klen = 12, + .input = "\x65\x61\x6e\x73\x20\x46\x61\x74", + .ilen = 8, + .result = "\x6c\x94\x75\x41\xec\x52\x79\x9f", + .rlen = 8, + }, { /* Speck64/128 */ + .key = "\x00\x01\x02\x03\x08\x09\x0a\x0b" + "\x10\x11\x12\x13\x18\x19\x1a\x1b", + .klen = 16, + .input = "\x2d\x43\x75\x74\x74\x65\x72\x3b", + .ilen = 8, + .result = "\x8b\x02\x4e\x45\x48\xa5\x6f\x8c", + .rlen = 8, + }, +}; + +static struct cipher_testvec speck64_dec_tv_template[] = { + { /* Speck64/96 */ + .key = "\x00\x01\x02\x03\x08\x09\x0a\x0b" + "\x10\x11\x12\x13", + .klen = 12, + .input = "\x6c\x94\x75\x41\xec\x52\x79\x9f", + .ilen = 8, + .result = "\x65\x61\x6e\x73\x20\x46\x61\x74", + .rlen = 8, + }, { /* Speck64/128 */ + .key = "\x00\x01\x02\x03\x08\x09\x0a\x0b" + "\x10\x11\x12\x13\x18\x19\x1a\x1b", + .klen = 16, + .input = "\x8b\x02\x4e\x45\x48\xa5\x6f\x8c", + .ilen = 8, + .result = "\x2d\x43\x75\x74\x74\x65\x72\x3b", + .rlen = 8, + }, +}; + /* Cast6 test vectors from RFC 2612 */ #define CAST6_ENC_TEST_VECTORS 4 #define CAST6_DEC_TEST_VECTORS 4 -- GitLab From 7a63eb24264e694182b7ca518ddd67793e9e2caf Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 14 Feb 2018 10:42:20 -0800 Subject: [PATCH 2697/5498] FROMGIT: crypto: speck - export common helpers Export the Speck constants and transform context and the ->setkey(), ->encrypt(), and ->decrypt() functions so that they can be reused by the ARM NEON implementation of Speck-XTS. The generic key expansion code will be reused because it is not performance-critical and is not vectorizable, while the generic encryption and decryption functions are needed as fallbacks and for the XTS tweak encryption. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu (cherry picked from commit c8c36413ca8ccbf7a0afe71247fc4617ee2dfcfe git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master) Change-Id: I93e96e1ef40de7071af212146b8ad3bf45297c1d Signed-off-by: Eric Biggers --- crypto/speck.c | 90 +++++++++++++++++++++++------------------- include/crypto/speck.h | 62 +++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 41 deletions(-) create mode 100644 include/crypto/speck.h diff --git a/crypto/speck.c b/crypto/speck.c index 4e80ad76bcd7..58aa9f7f91f7 100644 --- a/crypto/speck.c +++ b/crypto/speck.c @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -31,22 +32,6 @@ /* Speck128 */ -#define SPECK128_BLOCK_SIZE 16 - -#define SPECK128_128_KEY_SIZE 16 -#define SPECK128_128_NROUNDS 32 - -#define SPECK128_192_KEY_SIZE 24 -#define SPECK128_192_NROUNDS 33 - -#define SPECK128_256_KEY_SIZE 32 -#define SPECK128_256_NROUNDS 34 - -struct speck128_tfm_ctx { - u64 round_keys[SPECK128_256_NROUNDS]; - int nrounds; -}; - static __always_inline void speck128_round(u64 *x, u64 *y, u64 k) { *x = ror64(*x, 8); @@ -65,9 +50,9 @@ static __always_inline void speck128_unround(u64 *x, u64 *y, u64 k) *x = rol64(*x, 8); } -static void speck128_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +void crypto_speck128_encrypt(const struct speck128_tfm_ctx *ctx, + u8 *out, const u8 *in) { - const struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm); u64 y = get_unaligned_le64(in); u64 x = get_unaligned_le64(in + 8); int i; @@ -78,10 +63,16 @@ static void speck128_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) put_unaligned_le64(y, out); put_unaligned_le64(x, out + 8); } +EXPORT_SYMBOL_GPL(crypto_speck128_encrypt); -static void speck128_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +static void speck128_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + crypto_speck128_encrypt(crypto_tfm_ctx(tfm), out, in); +} + +void crypto_speck128_decrypt(const struct speck128_tfm_ctx *ctx, + u8 *out, const u8 *in) { - const struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm); u64 y = get_unaligned_le64(in); u64 x = get_unaligned_le64(in + 8); int i; @@ -92,11 +83,16 @@ static void speck128_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) put_unaligned_le64(y, out); put_unaligned_le64(x, out + 8); } +EXPORT_SYMBOL_GPL(crypto_speck128_decrypt); -static int speck128_setkey(struct crypto_tfm *tfm, const u8 *key, +static void speck128_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + crypto_speck128_decrypt(crypto_tfm_ctx(tfm), out, in); +} + +int crypto_speck128_setkey(struct speck128_tfm_ctx *ctx, const u8 *key, unsigned int keylen) { - struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm); u64 l[3]; u64 k; int i; @@ -138,21 +134,15 @@ static int speck128_setkey(struct crypto_tfm *tfm, const u8 *key, return 0; } +EXPORT_SYMBOL_GPL(crypto_speck128_setkey); -/* Speck64 */ - -#define SPECK64_BLOCK_SIZE 8 - -#define SPECK64_96_KEY_SIZE 12 -#define SPECK64_96_NROUNDS 26 - -#define SPECK64_128_KEY_SIZE 16 -#define SPECK64_128_NROUNDS 27 +static int speck128_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + return crypto_speck128_setkey(crypto_tfm_ctx(tfm), key, keylen); +} -struct speck64_tfm_ctx { - u32 round_keys[SPECK64_128_NROUNDS]; - int nrounds; -}; +/* Speck64 */ static __always_inline void speck64_round(u32 *x, u32 *y, u32 k) { @@ -172,9 +162,9 @@ static __always_inline void speck64_unround(u32 *x, u32 *y, u32 k) *x = rol32(*x, 8); } -static void speck64_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +void crypto_speck64_encrypt(const struct speck64_tfm_ctx *ctx, + u8 *out, const u8 *in) { - const struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm); u32 y = get_unaligned_le32(in); u32 x = get_unaligned_le32(in + 4); int i; @@ -185,10 +175,16 @@ static void speck64_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) put_unaligned_le32(y, out); put_unaligned_le32(x, out + 4); } +EXPORT_SYMBOL_GPL(crypto_speck64_encrypt); -static void speck64_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +static void speck64_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + crypto_speck64_encrypt(crypto_tfm_ctx(tfm), out, in); +} + +void crypto_speck64_decrypt(const struct speck64_tfm_ctx *ctx, + u8 *out, const u8 *in) { - const struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm); u32 y = get_unaligned_le32(in); u32 x = get_unaligned_le32(in + 4); int i; @@ -199,11 +195,16 @@ static void speck64_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) put_unaligned_le32(y, out); put_unaligned_le32(x, out + 4); } +EXPORT_SYMBOL_GPL(crypto_speck64_decrypt); -static int speck64_setkey(struct crypto_tfm *tfm, const u8 *key, +static void speck64_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + crypto_speck64_decrypt(crypto_tfm_ctx(tfm), out, in); +} + +int crypto_speck64_setkey(struct speck64_tfm_ctx *ctx, const u8 *key, unsigned int keylen) { - struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm); u32 l[3]; u32 k; int i; @@ -236,6 +237,13 @@ static int speck64_setkey(struct crypto_tfm *tfm, const u8 *key, return 0; } +EXPORT_SYMBOL_GPL(crypto_speck64_setkey); + +static int speck64_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + return crypto_speck64_setkey(crypto_tfm_ctx(tfm), key, keylen); +} /* Algorithm definitions */ diff --git a/include/crypto/speck.h b/include/crypto/speck.h new file mode 100644 index 000000000000..73cfc952d405 --- /dev/null +++ b/include/crypto/speck.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common values for the Speck algorithm + */ + +#ifndef _CRYPTO_SPECK_H +#define _CRYPTO_SPECK_H + +#include + +/* Speck128 */ + +#define SPECK128_BLOCK_SIZE 16 + +#define SPECK128_128_KEY_SIZE 16 +#define SPECK128_128_NROUNDS 32 + +#define SPECK128_192_KEY_SIZE 24 +#define SPECK128_192_NROUNDS 33 + +#define SPECK128_256_KEY_SIZE 32 +#define SPECK128_256_NROUNDS 34 + +struct speck128_tfm_ctx { + u64 round_keys[SPECK128_256_NROUNDS]; + int nrounds; +}; + +void crypto_speck128_encrypt(const struct speck128_tfm_ctx *ctx, + u8 *out, const u8 *in); + +void crypto_speck128_decrypt(const struct speck128_tfm_ctx *ctx, + u8 *out, const u8 *in); + +int crypto_speck128_setkey(struct speck128_tfm_ctx *ctx, const u8 *key, + unsigned int keysize); + +/* Speck64 */ + +#define SPECK64_BLOCK_SIZE 8 + +#define SPECK64_96_KEY_SIZE 12 +#define SPECK64_96_NROUNDS 26 + +#define SPECK64_128_KEY_SIZE 16 +#define SPECK64_128_NROUNDS 27 + +struct speck64_tfm_ctx { + u32 round_keys[SPECK64_128_NROUNDS]; + int nrounds; +}; + +void crypto_speck64_encrypt(const struct speck64_tfm_ctx *ctx, + u8 *out, const u8 *in); + +void crypto_speck64_decrypt(const struct speck64_tfm_ctx *ctx, + u8 *out, const u8 *in); + +int crypto_speck64_setkey(struct speck64_tfm_ctx *ctx, const u8 *key, + unsigned int keysize); + +#endif /* _CRYPTO_SPECK_H */ -- GitLab From 67fe1198256b29ed0e60bea9ec326a4a2392290d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 14 Feb 2018 10:42:21 -0800 Subject: [PATCH 2698/5498] BACKPORT, FROMGIT: crypto: arm/speck - add NEON-accelerated implementation of Speck-XTS Add an ARM NEON-accelerated implementation of Speck-XTS. It operates on 128-byte chunks at a time, i.e. 8 blocks for Speck128 or 16 blocks for Speck64. Each 128-byte chunk goes through XTS preprocessing, then is encrypted/decrypted (doing one cipher round for all the blocks, then the next round, etc.), then goes through XTS postprocessing. The performance depends on the processor but can be about 3 times faster than the generic code. For example, on an ARMv7 processor we observe the following performance with Speck128/256-XTS: xts-speck128-neon: Encryption 107.9 MB/s, Decryption 108.1 MB/s xts(speck128-generic): Encryption 32.1 MB/s, Decryption 36.6 MB/s In comparison to AES-256-XTS without the Cryptography Extensions: xts-aes-neonbs: Encryption 41.2 MB/s, Decryption 36.7 MB/s xts(aes-asm): Encryption 31.7 MB/s, Decryption 30.8 MB/s xts(aes-generic): Encryption 21.2 MB/s, Decryption 20.9 MB/s Speck64/128-XTS is even faster: xts-speck64-neon: Encryption 138.6 MB/s, Decryption 139.1 MB/s Note that as with the generic code, only the Speck128 and Speck64 variants are supported. Also, for now only the XTS mode of operation is supported, to target the disk and file encryption use cases. The NEON code also only handles the portion of the data that is evenly divisible into 128-byte chunks, with any remainder handled by a C fallback. Of course, other modes of operation could be added later if needed, and/or the NEON code could be updated to handle other buffer sizes. The XTS specification is only defined for AES which has a 128-bit block size, so for the GF(2^64) math needed for Speck64-XTS we use the reducing polynomial 'x^64 + x^4 + x^3 + x + 1' given by the original XEX paper. Of course, when possible users should use Speck128-XTS, but even that may be too slow on some processors; Speck64-XTS can be faster. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu (cherry picked from commit ede9622162fac42eacde231d64e94c926f4be45d git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master) (changed speck-neon-glue.c to use blkcipher API instead of skcipher API) (resolved merge conflict in arch/arm/crypto/Makefile) (moved CONFIG_CRYPTO_SPECK_NEON from arch/arm/crypto/Kconfig to crypto/Kconfig) (made CONFIG_CRYPTO_SPECK_NEON select CONFIG_CRYPTO_GF128MUL, since gf128mul_x_ble() is non-inline in older kernels) Change-Id: I5bbc86cb3c2cbc36636a59a0db725b2ad95ea81b Signed-off-by: Eric Biggers --- arch/arm/crypto/Makefile | 2 + arch/arm/crypto/speck-neon-core.S | 432 ++++++++++++++++++++++++++++++ arch/arm/crypto/speck-neon-glue.c | 314 ++++++++++++++++++++++ crypto/Kconfig | 7 + 4 files changed, 755 insertions(+) create mode 100644 arch/arm/crypto/speck-neon-core.S create mode 100644 arch/arm/crypto/speck-neon-glue.c diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile index 742f215306fe..b6ebae22369c 100644 --- a/arch/arm/crypto/Makefile +++ b/arch/arm/crypto/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o obj-$(CONFIG_CRYPTO_SHA512_ARM_NEON) += sha512-arm-neon.o obj-$(CONFIG_CRYPTO_SHA2_ARM_CE) += sha2-arm-ce.o +obj-$(CONFIG_CRYPTO_SPECK_NEON) += speck-neon.o aes-arm-y := aes-armv4.o aes_glue.o aes-arm-bs-y := aesbs-core.o aesbs-glue.o @@ -20,6 +21,7 @@ sha256-arm-y := sha256-core.o sha256_glue.o $(sha256-arm-neon-y) sha512-arm-neon-y := sha512-armv7-neon.o sha512_neon_glue.o aes-arm-ce-y := aes-ce-core.o aes-ce-glue.o sha2-arm-ce-y := sha2-ce-core.o sha2-ce-glue.o +speck-neon-y := speck-neon-core.o speck-neon-glue.o quiet_cmd_perl = PERL $@ cmd_perl = $(PERL) $(<) > $(@) diff --git a/arch/arm/crypto/speck-neon-core.S b/arch/arm/crypto/speck-neon-core.S new file mode 100644 index 000000000000..3c1e203e53b9 --- /dev/null +++ b/arch/arm/crypto/speck-neon-core.S @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NEON-accelerated implementation of Speck128-XTS and Speck64-XTS + * + * Copyright (c) 2018 Google, Inc + * + * Author: Eric Biggers + */ + +#include + + .text + .fpu neon + + // arguments + ROUND_KEYS .req r0 // const {u64,u32} *round_keys + NROUNDS .req r1 // int nrounds + DST .req r2 // void *dst + SRC .req r3 // const void *src + NBYTES .req r4 // unsigned int nbytes + TWEAK .req r5 // void *tweak + + // registers which hold the data being encrypted/decrypted + X0 .req q0 + X0_L .req d0 + X0_H .req d1 + Y0 .req q1 + Y0_H .req d3 + X1 .req q2 + X1_L .req d4 + X1_H .req d5 + Y1 .req q3 + Y1_H .req d7 + X2 .req q4 + X2_L .req d8 + X2_H .req d9 + Y2 .req q5 + Y2_H .req d11 + X3 .req q6 + X3_L .req d12 + X3_H .req d13 + Y3 .req q7 + Y3_H .req d15 + + // the round key, duplicated in all lanes + ROUND_KEY .req q8 + ROUND_KEY_L .req d16 + ROUND_KEY_H .req d17 + + // index vector for vtbl-based 8-bit rotates + ROTATE_TABLE .req d18 + + // multiplication table for updating XTS tweaks + GF128MUL_TABLE .req d19 + GF64MUL_TABLE .req d19 + + // current XTS tweak value(s) + TWEAKV .req q10 + TWEAKV_L .req d20 + TWEAKV_H .req d21 + + TMP0 .req q12 + TMP0_L .req d24 + TMP0_H .req d25 + TMP1 .req q13 + TMP2 .req q14 + TMP3 .req q15 + + .align 4 +.Lror64_8_table: + .byte 1, 2, 3, 4, 5, 6, 7, 0 +.Lror32_8_table: + .byte 1, 2, 3, 0, 5, 6, 7, 4 +.Lrol64_8_table: + .byte 7, 0, 1, 2, 3, 4, 5, 6 +.Lrol32_8_table: + .byte 3, 0, 1, 2, 7, 4, 5, 6 +.Lgf128mul_table: + .byte 0, 0x87 + .fill 14 +.Lgf64mul_table: + .byte 0, 0x1b, (0x1b << 1), (0x1b << 1) ^ 0x1b + .fill 12 + +/* + * _speck_round_128bytes() - Speck encryption round on 128 bytes at a time + * + * Do one Speck encryption round on the 128 bytes (8 blocks for Speck128, 16 for + * Speck64) stored in X0-X3 and Y0-Y3, using the round key stored in all lanes + * of ROUND_KEY. 'n' is the lane size: 64 for Speck128, or 32 for Speck64. + * + * The 8-bit rotates are implemented using vtbl instead of vshr + vsli because + * the vtbl approach is faster on some processors and the same speed on others. + */ +.macro _speck_round_128bytes n + + // x = ror(x, 8) + vtbl.8 X0_L, {X0_L}, ROTATE_TABLE + vtbl.8 X0_H, {X0_H}, ROTATE_TABLE + vtbl.8 X1_L, {X1_L}, ROTATE_TABLE + vtbl.8 X1_H, {X1_H}, ROTATE_TABLE + vtbl.8 X2_L, {X2_L}, ROTATE_TABLE + vtbl.8 X2_H, {X2_H}, ROTATE_TABLE + vtbl.8 X3_L, {X3_L}, ROTATE_TABLE + vtbl.8 X3_H, {X3_H}, ROTATE_TABLE + + // x += y + vadd.u\n X0, Y0 + vadd.u\n X1, Y1 + vadd.u\n X2, Y2 + vadd.u\n X3, Y3 + + // x ^= k + veor X0, ROUND_KEY + veor X1, ROUND_KEY + veor X2, ROUND_KEY + veor X3, ROUND_KEY + + // y = rol(y, 3) + vshl.u\n TMP0, Y0, #3 + vshl.u\n TMP1, Y1, #3 + vshl.u\n TMP2, Y2, #3 + vshl.u\n TMP3, Y3, #3 + vsri.u\n TMP0, Y0, #(\n - 3) + vsri.u\n TMP1, Y1, #(\n - 3) + vsri.u\n TMP2, Y2, #(\n - 3) + vsri.u\n TMP3, Y3, #(\n - 3) + + // y ^= x + veor Y0, TMP0, X0 + veor Y1, TMP1, X1 + veor Y2, TMP2, X2 + veor Y3, TMP3, X3 +.endm + +/* + * _speck_unround_128bytes() - Speck decryption round on 128 bytes at a time + * + * This is the inverse of _speck_round_128bytes(). + */ +.macro _speck_unround_128bytes n + + // y ^= x + veor TMP0, Y0, X0 + veor TMP1, Y1, X1 + veor TMP2, Y2, X2 + veor TMP3, Y3, X3 + + // y = ror(y, 3) + vshr.u\n Y0, TMP0, #3 + vshr.u\n Y1, TMP1, #3 + vshr.u\n Y2, TMP2, #3 + vshr.u\n Y3, TMP3, #3 + vsli.u\n Y0, TMP0, #(\n - 3) + vsli.u\n Y1, TMP1, #(\n - 3) + vsli.u\n Y2, TMP2, #(\n - 3) + vsli.u\n Y3, TMP3, #(\n - 3) + + // x ^= k + veor X0, ROUND_KEY + veor X1, ROUND_KEY + veor X2, ROUND_KEY + veor X3, ROUND_KEY + + // x -= y + vsub.u\n X0, Y0 + vsub.u\n X1, Y1 + vsub.u\n X2, Y2 + vsub.u\n X3, Y3 + + // x = rol(x, 8); + vtbl.8 X0_L, {X0_L}, ROTATE_TABLE + vtbl.8 X0_H, {X0_H}, ROTATE_TABLE + vtbl.8 X1_L, {X1_L}, ROTATE_TABLE + vtbl.8 X1_H, {X1_H}, ROTATE_TABLE + vtbl.8 X2_L, {X2_L}, ROTATE_TABLE + vtbl.8 X2_H, {X2_H}, ROTATE_TABLE + vtbl.8 X3_L, {X3_L}, ROTATE_TABLE + vtbl.8 X3_H, {X3_H}, ROTATE_TABLE +.endm + +.macro _xts128_precrypt_one dst_reg, tweak_buf, tmp + + // Load the next source block + vld1.8 {\dst_reg}, [SRC]! + + // Save the current tweak in the tweak buffer + vst1.8 {TWEAKV}, [\tweak_buf:128]! + + // XOR the next source block with the current tweak + veor \dst_reg, TWEAKV + + /* + * Calculate the next tweak by multiplying the current one by x, + * modulo p(x) = x^128 + x^7 + x^2 + x + 1. + */ + vshr.u64 \tmp, TWEAKV, #63 + vshl.u64 TWEAKV, #1 + veor TWEAKV_H, \tmp\()_L + vtbl.8 \tmp\()_H, {GF128MUL_TABLE}, \tmp\()_H + veor TWEAKV_L, \tmp\()_H +.endm + +.macro _xts64_precrypt_two dst_reg, tweak_buf, tmp + + // Load the next two source blocks + vld1.8 {\dst_reg}, [SRC]! + + // Save the current two tweaks in the tweak buffer + vst1.8 {TWEAKV}, [\tweak_buf:128]! + + // XOR the next two source blocks with the current two tweaks + veor \dst_reg, TWEAKV + + /* + * Calculate the next two tweaks by multiplying the current ones by x^2, + * modulo p(x) = x^64 + x^4 + x^3 + x + 1. + */ + vshr.u64 \tmp, TWEAKV, #62 + vshl.u64 TWEAKV, #2 + vtbl.8 \tmp\()_L, {GF64MUL_TABLE}, \tmp\()_L + vtbl.8 \tmp\()_H, {GF64MUL_TABLE}, \tmp\()_H + veor TWEAKV, \tmp +.endm + +/* + * _speck_xts_crypt() - Speck-XTS encryption/decryption + * + * Encrypt or decrypt NBYTES bytes of data from the SRC buffer to the DST buffer + * using Speck-XTS, specifically the variant with a block size of '2n' and round + * count given by NROUNDS. The expanded round keys are given in ROUND_KEYS, and + * the current XTS tweak value is given in TWEAK. It's assumed that NBYTES is a + * nonzero multiple of 128. + */ +.macro _speck_xts_crypt n, decrypting + push {r4-r7} + mov r7, sp + + /* + * The first four parameters were passed in registers r0-r3. Load the + * additional parameters, which were passed on the stack. + */ + ldr NBYTES, [sp, #16] + ldr TWEAK, [sp, #20] + + /* + * If decrypting, modify the ROUND_KEYS parameter to point to the last + * round key rather than the first, since for decryption the round keys + * are used in reverse order. + */ +.if \decrypting +.if \n == 64 + add ROUND_KEYS, ROUND_KEYS, NROUNDS, lsl #3 + sub ROUND_KEYS, #8 +.else + add ROUND_KEYS, ROUND_KEYS, NROUNDS, lsl #2 + sub ROUND_KEYS, #4 +.endif +.endif + + // Load the index vector for vtbl-based 8-bit rotates +.if \decrypting + ldr r12, =.Lrol\n\()_8_table +.else + ldr r12, =.Lror\n\()_8_table +.endif + vld1.8 {ROTATE_TABLE}, [r12:64] + + // One-time XTS preparation + + /* + * Allocate stack space to store 128 bytes worth of tweaks. For + * performance, this space is aligned to a 16-byte boundary so that we + * can use the load/store instructions that declare 16-byte alignment. + */ + sub sp, #128 + bic sp, #0xf + +.if \n == 64 + // Load first tweak + vld1.8 {TWEAKV}, [TWEAK] + + // Load GF(2^128) multiplication table + ldr r12, =.Lgf128mul_table + vld1.8 {GF128MUL_TABLE}, [r12:64] +.else + // Load first tweak + vld1.8 {TWEAKV_L}, [TWEAK] + + // Load GF(2^64) multiplication table + ldr r12, =.Lgf64mul_table + vld1.8 {GF64MUL_TABLE}, [r12:64] + + // Calculate second tweak, packing it together with the first + vshr.u64 TMP0_L, TWEAKV_L, #63 + vtbl.u8 TMP0_L, {GF64MUL_TABLE}, TMP0_L + vshl.u64 TWEAKV_H, TWEAKV_L, #1 + veor TWEAKV_H, TMP0_L +.endif + +.Lnext_128bytes_\@: + + /* + * Load the source blocks into {X,Y}[0-3], XOR them with their XTS tweak + * values, and save the tweaks on the stack for later. Then + * de-interleave the 'x' and 'y' elements of each block, i.e. make it so + * that the X[0-3] registers contain only the second halves of blocks, + * and the Y[0-3] registers contain only the first halves of blocks. + * (Speck uses the order (y, x) rather than the more intuitive (x, y).) + */ + mov r12, sp +.if \n == 64 + _xts128_precrypt_one X0, r12, TMP0 + _xts128_precrypt_one Y0, r12, TMP0 + _xts128_precrypt_one X1, r12, TMP0 + _xts128_precrypt_one Y1, r12, TMP0 + _xts128_precrypt_one X2, r12, TMP0 + _xts128_precrypt_one Y2, r12, TMP0 + _xts128_precrypt_one X3, r12, TMP0 + _xts128_precrypt_one Y3, r12, TMP0 + vswp X0_L, Y0_H + vswp X1_L, Y1_H + vswp X2_L, Y2_H + vswp X3_L, Y3_H +.else + _xts64_precrypt_two X0, r12, TMP0 + _xts64_precrypt_two Y0, r12, TMP0 + _xts64_precrypt_two X1, r12, TMP0 + _xts64_precrypt_two Y1, r12, TMP0 + _xts64_precrypt_two X2, r12, TMP0 + _xts64_precrypt_two Y2, r12, TMP0 + _xts64_precrypt_two X3, r12, TMP0 + _xts64_precrypt_two Y3, r12, TMP0 + vuzp.32 Y0, X0 + vuzp.32 Y1, X1 + vuzp.32 Y2, X2 + vuzp.32 Y3, X3 +.endif + + // Do the cipher rounds + + mov r12, ROUND_KEYS + mov r6, NROUNDS + +.Lnext_round_\@: +.if \decrypting +.if \n == 64 + vld1.64 ROUND_KEY_L, [r12] + sub r12, #8 + vmov ROUND_KEY_H, ROUND_KEY_L +.else + vld1.32 {ROUND_KEY_L[],ROUND_KEY_H[]}, [r12] + sub r12, #4 +.endif + _speck_unround_128bytes \n +.else +.if \n == 64 + vld1.64 ROUND_KEY_L, [r12]! + vmov ROUND_KEY_H, ROUND_KEY_L +.else + vld1.32 {ROUND_KEY_L[],ROUND_KEY_H[]}, [r12]! +.endif + _speck_round_128bytes \n +.endif + subs r6, r6, #1 + bne .Lnext_round_\@ + + // Re-interleave the 'x' and 'y' elements of each block +.if \n == 64 + vswp X0_L, Y0_H + vswp X1_L, Y1_H + vswp X2_L, Y2_H + vswp X3_L, Y3_H +.else + vzip.32 Y0, X0 + vzip.32 Y1, X1 + vzip.32 Y2, X2 + vzip.32 Y3, X3 +.endif + + // XOR the encrypted/decrypted blocks with the tweaks we saved earlier + mov r12, sp + vld1.8 {TMP0, TMP1}, [r12:128]! + vld1.8 {TMP2, TMP3}, [r12:128]! + veor X0, TMP0 + veor Y0, TMP1 + veor X1, TMP2 + veor Y1, TMP3 + vld1.8 {TMP0, TMP1}, [r12:128]! + vld1.8 {TMP2, TMP3}, [r12:128]! + veor X2, TMP0 + veor Y2, TMP1 + veor X3, TMP2 + veor Y3, TMP3 + + // Store the ciphertext in the destination buffer + vst1.8 {X0, Y0}, [DST]! + vst1.8 {X1, Y1}, [DST]! + vst1.8 {X2, Y2}, [DST]! + vst1.8 {X3, Y3}, [DST]! + + // Continue if there are more 128-byte chunks remaining, else return + subs NBYTES, #128 + bne .Lnext_128bytes_\@ + + // Store the next tweak +.if \n == 64 + vst1.8 {TWEAKV}, [TWEAK] +.else + vst1.8 {TWEAKV_L}, [TWEAK] +.endif + + mov sp, r7 + pop {r4-r7} + bx lr +.endm + +ENTRY(speck128_xts_encrypt_neon) + _speck_xts_crypt n=64, decrypting=0 +ENDPROC(speck128_xts_encrypt_neon) + +ENTRY(speck128_xts_decrypt_neon) + _speck_xts_crypt n=64, decrypting=1 +ENDPROC(speck128_xts_decrypt_neon) + +ENTRY(speck64_xts_encrypt_neon) + _speck_xts_crypt n=32, decrypting=0 +ENDPROC(speck64_xts_encrypt_neon) + +ENTRY(speck64_xts_decrypt_neon) + _speck_xts_crypt n=32, decrypting=1 +ENDPROC(speck64_xts_decrypt_neon) diff --git a/arch/arm/crypto/speck-neon-glue.c b/arch/arm/crypto/speck-neon-glue.c new file mode 100644 index 000000000000..ea36c3a6f1d9 --- /dev/null +++ b/arch/arm/crypto/speck-neon-glue.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NEON-accelerated implementation of Speck128-XTS and Speck64-XTS + * + * Copyright (c) 2018 Google, Inc + * + * Note: the NIST recommendation for XTS only specifies a 128-bit block size, + * but a 64-bit version (needed for Speck64) is fairly straightforward; the math + * is just done in GF(2^64) instead of GF(2^128), with the reducing polynomial + * x^64 + x^4 + x^3 + x + 1 from the original XEX paper (Rogaway, 2004: + * "Efficient Instantiations of Tweakable Blockciphers and Refinements to Modes + * OCB and PMAC"), represented as 0x1B. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The assembly functions only handle multiples of 128 bytes */ +#define SPECK_NEON_CHUNK_SIZE 128 + +/* Speck128 */ + +struct speck128_xts_tfm_ctx { + struct speck128_tfm_ctx main_key; + struct speck128_tfm_ctx tweak_key; +}; + +asmlinkage void speck128_xts_encrypt_neon(const u64 *round_keys, int nrounds, + void *dst, const void *src, + unsigned int nbytes, void *tweak); + +asmlinkage void speck128_xts_decrypt_neon(const u64 *round_keys, int nrounds, + void *dst, const void *src, + unsigned int nbytes, void *tweak); + +typedef void (*speck128_crypt_one_t)(const struct speck128_tfm_ctx *, + u8 *, const u8 *); +typedef void (*speck128_xts_crypt_many_t)(const u64 *, int, void *, + const void *, unsigned int, void *); + +static __always_inline int +__speck128_xts_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes, + speck128_crypt_one_t crypt_one, + speck128_xts_crypt_many_t crypt_many) +{ + struct crypto_blkcipher *tfm = desc->tfm; + const struct speck128_xts_tfm_ctx *ctx = crypto_blkcipher_ctx(tfm); + struct blkcipher_walk walk; + le128 tweak; + int err; + + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt_block(desc, &walk, SPECK_NEON_CHUNK_SIZE); + + crypto_speck128_encrypt(&ctx->tweak_key, (u8 *)&tweak, walk.iv); + + while (walk.nbytes > 0) { + unsigned int nbytes = walk.nbytes; + u8 *dst = walk.dst.virt.addr; + const u8 *src = walk.src.virt.addr; + + if (nbytes >= SPECK_NEON_CHUNK_SIZE && may_use_simd()) { + unsigned int count; + + count = round_down(nbytes, SPECK_NEON_CHUNK_SIZE); + kernel_neon_begin(); + (*crypt_many)(ctx->main_key.round_keys, + ctx->main_key.nrounds, + dst, src, count, &tweak); + kernel_neon_end(); + dst += count; + src += count; + nbytes -= count; + } + + /* Handle any remainder with generic code */ + while (nbytes >= sizeof(tweak)) { + le128_xor((le128 *)dst, (const le128 *)src, &tweak); + (*crypt_one)(&ctx->main_key, dst, dst); + le128_xor((le128 *)dst, (const le128 *)dst, &tweak); + gf128mul_x_ble((be128 *)&tweak, (const be128 *)&tweak); + + dst += sizeof(tweak); + src += sizeof(tweak); + nbytes -= sizeof(tweak); + } + err = blkcipher_walk_done(desc, &walk, nbytes); + } + + return err; +} + +static int speck128_xts_encrypt(struct blkcipher_desc *desc, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes) +{ + return __speck128_xts_crypt(desc, dst, src, nbytes, + crypto_speck128_encrypt, + speck128_xts_encrypt_neon); +} + +static int speck128_xts_decrypt(struct blkcipher_desc *desc, + struct scatterlist *dst, + struct scatterlist *src, + unsigned int nbytes) +{ + return __speck128_xts_crypt(desc, dst, src, nbytes, + crypto_speck128_decrypt, + speck128_xts_decrypt_neon); +} + +static int speck128_xts_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + struct speck128_xts_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + int err; + + if (keylen % 2) + return -EINVAL; + + keylen /= 2; + + err = crypto_speck128_setkey(&ctx->main_key, key, keylen); + if (err) + return err; + + return crypto_speck128_setkey(&ctx->tweak_key, key + keylen, keylen); +} + +/* Speck64 */ + +struct speck64_xts_tfm_ctx { + struct speck64_tfm_ctx main_key; + struct speck64_tfm_ctx tweak_key; +}; + +asmlinkage void speck64_xts_encrypt_neon(const u32 *round_keys, int nrounds, + void *dst, const void *src, + unsigned int nbytes, void *tweak); + +asmlinkage void speck64_xts_decrypt_neon(const u32 *round_keys, int nrounds, + void *dst, const void *src, + unsigned int nbytes, void *tweak); + +typedef void (*speck64_crypt_one_t)(const struct speck64_tfm_ctx *, + u8 *, const u8 *); +typedef void (*speck64_xts_crypt_many_t)(const u32 *, int, void *, + const void *, unsigned int, void *); + +static __always_inline int +__speck64_xts_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes, + speck64_crypt_one_t crypt_one, + speck64_xts_crypt_many_t crypt_many) +{ + struct crypto_blkcipher *tfm = desc->tfm; + const struct speck64_xts_tfm_ctx *ctx = crypto_blkcipher_ctx(tfm); + struct blkcipher_walk walk; + __le64 tweak; + int err; + + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt_block(desc, &walk, SPECK_NEON_CHUNK_SIZE); + + crypto_speck64_encrypt(&ctx->tweak_key, (u8 *)&tweak, walk.iv); + + while (walk.nbytes > 0) { + unsigned int nbytes = walk.nbytes; + u8 *dst = walk.dst.virt.addr; + const u8 *src = walk.src.virt.addr; + + if (nbytes >= SPECK_NEON_CHUNK_SIZE && may_use_simd()) { + unsigned int count; + + count = round_down(nbytes, SPECK_NEON_CHUNK_SIZE); + kernel_neon_begin(); + (*crypt_many)(ctx->main_key.round_keys, + ctx->main_key.nrounds, + dst, src, count, &tweak); + kernel_neon_end(); + dst += count; + src += count; + nbytes -= count; + } + + /* Handle any remainder with generic code */ + while (nbytes >= sizeof(tweak)) { + *(__le64 *)dst = *(__le64 *)src ^ tweak; + (*crypt_one)(&ctx->main_key, dst, dst); + *(__le64 *)dst ^= tweak; + tweak = cpu_to_le64((le64_to_cpu(tweak) << 1) ^ + ((tweak & cpu_to_le64(1ULL << 63)) ? + 0x1B : 0)); + dst += sizeof(tweak); + src += sizeof(tweak); + nbytes -= sizeof(tweak); + } + err = blkcipher_walk_done(desc, &walk, nbytes); + } + + return err; +} + +static int speck64_xts_encrypt(struct blkcipher_desc *desc, + struct scatterlist *dst, struct scatterlist *src, + unsigned int nbytes) +{ + return __speck64_xts_crypt(desc, dst, src, nbytes, + crypto_speck64_encrypt, + speck64_xts_encrypt_neon); +} + +static int speck64_xts_decrypt(struct blkcipher_desc *desc, + struct scatterlist *dst, struct scatterlist *src, + unsigned int nbytes) +{ + return __speck64_xts_crypt(desc, dst, src, nbytes, + crypto_speck64_decrypt, + speck64_xts_decrypt_neon); +} + +static int speck64_xts_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + struct speck64_xts_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + int err; + + if (keylen % 2) + return -EINVAL; + + keylen /= 2; + + err = crypto_speck64_setkey(&ctx->main_key, key, keylen); + if (err) + return err; + + return crypto_speck64_setkey(&ctx->tweak_key, key + keylen, keylen); +} + +static struct crypto_alg speck_algs[] = { + { + .cra_name = "xts(speck128)", + .cra_driver_name = "xts-speck128-neon", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = SPECK128_BLOCK_SIZE, + .cra_type = &crypto_blkcipher_type, + .cra_ctxsize = sizeof(struct speck128_xts_tfm_ctx), + .cra_alignmask = 7, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = 2 * SPECK128_128_KEY_SIZE, + .max_keysize = 2 * SPECK128_256_KEY_SIZE, + .ivsize = SPECK128_BLOCK_SIZE, + .setkey = speck128_xts_setkey, + .encrypt = speck128_xts_encrypt, + .decrypt = speck128_xts_decrypt, + } + } + }, { + .cra_name = "xts(speck64)", + .cra_driver_name = "xts-speck64-neon", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = SPECK64_BLOCK_SIZE, + .cra_type = &crypto_blkcipher_type, + .cra_ctxsize = sizeof(struct speck64_xts_tfm_ctx), + .cra_alignmask = 7, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = 2 * SPECK64_96_KEY_SIZE, + .max_keysize = 2 * SPECK64_128_KEY_SIZE, + .ivsize = SPECK64_BLOCK_SIZE, + .setkey = speck64_xts_setkey, + .encrypt = speck64_xts_encrypt, + .decrypt = speck64_xts_decrypt, + } + } + } +}; + +static int __init speck_neon_module_init(void) +{ + if (!(elf_hwcap & HWCAP_NEON)) + return -ENODEV; + return crypto_register_algs(speck_algs, ARRAY_SIZE(speck_algs)); +} + +static void __exit speck_neon_module_exit(void) +{ + crypto_unregister_algs(speck_algs, ARRAY_SIZE(speck_algs)); +} + +module_init(speck_neon_module_init); +module_exit(speck_neon_module_exit); + +MODULE_DESCRIPTION("Speck block cipher (NEON-accelerated)"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Biggers "); +MODULE_ALIAS_CRYPTO("xts(speck128)"); +MODULE_ALIAS_CRYPTO("xts-speck128-neon"); +MODULE_ALIAS_CRYPTO("xts(speck64)"); +MODULE_ALIAS_CRYPTO("xts-speck64-neon"); diff --git a/crypto/Kconfig b/crypto/Kconfig index 86fe6e76fffd..2baf8979fcba 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1303,6 +1303,13 @@ config CRYPTO_SPECK If unsure, say N. +config CRYPTO_SPECK_NEON + tristate "NEON accelerated Speck cipher algorithms" + depends on ARM && KERNEL_MODE_NEON + select CRYPTO_BLKCIPHER + select CRYPTO_GF128MUL + select CRYPTO_SPECK + config CRYPTO_TEA tristate "TEA, XTEA and XETA cipher algorithms" select CRYPTO_ALGAPI -- GitLab From d1ef9a4158c87b119ab244b37652cdf2bbb00478 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 14 Feb 2018 10:42:22 -0800 Subject: [PATCH 2699/5498] BACKPORT, FROMGIT: crypto: speck - add test vectors for Speck128-XTS Add test vectors for Speck128-XTS, generated in userspace using C code. The inputs were borrowed from the AES-XTS test vectors. Both xts(speck128-generic) and xts-speck128-neon pass these tests. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu (cherry picked from commit c3bb521bb6ac3023ae236a3a361f951f8d78ecc4 git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master) (removed 'const' from test vectors) (replaced use of __VECS macro in crypto/testmgr.c) Change-Id: Ifd701d5df4a6602c207cfb28decc620ef7e5f896 Signed-off-by: Eric Biggers --- crypto/testmgr.c | 15 ++ crypto/testmgr.h | 687 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 702 insertions(+) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 7a0f07e40644..09d6d80c2554 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -3602,6 +3602,21 @@ static const struct alg_test_desc alg_test_descs[] = { } } } + }, { + .alg = "xts(speck128)", + .test = alg_test_skcipher, + .suite = { + .cipher = { + .enc = { + .vecs = speck128_xts_enc_tv_template, + .count = ARRAY_SIZE(speck128_xts_enc_tv_template) + }, + .dec = { + .vecs = speck128_xts_dec_tv_template, + .count = ARRAY_SIZE(speck128_xts_dec_tv_template) + } + } + } }, { .alg = "xts(twofish)", .test = alg_test_skcipher, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 0fa026051350..297818911046 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -12148,6 +12148,693 @@ static struct cipher_testvec speck128_dec_tv_template[] = { }, }; +/* + * Speck128-XTS test vectors, taken from the AES-XTS test vectors with the + * result recomputed with Speck128 as the cipher + */ + +static struct cipher_testvec speck128_xts_enc_tv_template[] = { + { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 32, + .result = "\xbe\xa0\xe7\x03\xd7\xfe\xab\x62" + "\x3b\x99\x4a\x64\x74\x77\xac\xed" + "\xd8\xf4\xa6\xcf\xae\xb9\x07\x42" + "\x51\xd9\xb6\x1d\xe0\x5e\xbc\x54", + .rlen = 32, + }, { + .key = "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\xfb\x53\x81\x75\x6f\x9f\x34\xad" + "\x7e\x01\xed\x7b\xcc\xda\x4e\x4a" + "\xd4\x84\xa4\x53\xd5\x88\x73\x1b" + "\xfd\xcb\xae\x0d\xf3\x04\xee\xe6", + .rlen = 32, + }, { + .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\x21\x52\x84\x15\xd1\xf7\x21\x55" + "\xd9\x75\x4a\xd3\xc5\xdb\x9f\x7d" + "\xda\x63\xb2\xf1\x82\xb0\x89\x59" + "\x86\xd4\xaa\xaa\xdd\xff\x4f\x92", + .rlen = 32, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .ilen = 512, + .result = "\x57\xb5\xf8\x71\x6e\x6d\xdd\x82" + "\x53\xd0\xed\x2d\x30\xc1\x20\xef" + "\x70\x67\x5e\xff\x09\x70\xbb\xc1" + "\x3a\x7b\x48\x26\xd9\x0b\xf4\x48" + "\xbe\xce\xb1\xc7\xb2\x67\xc4\xa7" + "\x76\xf8\x36\x30\xb7\xb4\x9a\xd9" + "\xf5\x9d\xd0\x7b\xc1\x06\x96\x44" + "\x19\xc5\x58\x84\x63\xb9\x12\x68" + "\x68\xc7\xaa\x18\x98\xf2\x1f\x5c" + "\x39\xa6\xd8\x32\x2b\xc3\x51\xfd" + "\x74\x79\x2e\xb4\x44\xd7\x69\xc4" + "\xfc\x29\xe6\xed\x26\x1e\xa6\x9d" + "\x1c\xbe\x00\x0e\x7f\x3a\xca\xfb" + "\x6d\x13\x65\xa0\xf9\x31\x12\xe2" + "\x26\xd1\xec\x2b\x0a\x8b\x59\x99" + "\xa7\x49\xa0\x0e\x09\x33\x85\x50" + "\xc3\x23\xca\x7a\xdd\x13\x45\x5f" + "\xde\x4c\xa7\xcb\x00\x8a\x66\x6f" + "\xa2\xb6\xb1\x2e\xe1\xa0\x18\xf6" + "\xad\xf3\xbd\xeb\xc7\xef\x55\x4f" + "\x79\x91\x8d\x36\x13\x7b\xd0\x4a" + "\x6c\x39\xfb\x53\xb8\x6f\x02\x51" + "\xa5\x20\xac\x24\x1c\x73\x59\x73" + "\x58\x61\x3a\x87\x58\xb3\x20\x56" + "\x39\x06\x2b\x4d\xd3\x20\x2b\x89" + "\x3f\xa2\xf0\x96\xeb\x7f\xa4\xcd" + "\x11\xae\xbd\xcb\x3a\xb4\xd9\x91" + "\x09\x35\x71\x50\x65\xac\x92\xe3" + "\x7b\x32\xc0\x7a\xdd\xd4\xc3\x92" + "\x6f\xeb\x79\xde\x6f\xd3\x25\xc9" + "\xcd\x63\xf5\x1e\x7a\x3b\x26\x9d" + "\x77\x04\x80\xa9\xbf\x38\xb5\xbd" + "\xb8\x05\x07\xbd\xfd\xab\x7b\xf8" + "\x2a\x26\xcc\x49\x14\x6d\x55\x01" + "\x06\x94\xd8\xb2\x2d\x53\x83\x1b" + "\x8f\xd4\xdd\x57\x12\x7e\x18\xba" + "\x8e\xe2\x4d\x80\xef\x7e\x6b\x9d" + "\x24\xa9\x60\xa4\x97\x85\x86\x2a" + "\x01\x00\x09\xf1\xcb\x4a\x24\x1c" + "\xd8\xf6\xe6\x5b\xe7\x5d\xf2\xc4" + "\x97\x1c\x10\xc6\x4d\x66\x4f\x98" + "\x87\x30\xac\xd5\xea\x73\x49\x10" + "\x80\xea\xe5\x5f\x4d\x5f\x03\x33" + "\x66\x02\x35\x3d\x60\x06\x36\x4f" + "\x14\x1c\xd8\x07\x1f\x78\xd0\xf8" + "\x4f\x6c\x62\x7c\x15\xa5\x7c\x28" + "\x7c\xcc\xeb\x1f\xd1\x07\x90\x93" + "\x7e\xc2\xa8\x3a\x80\xc0\xf5\x30" + "\xcc\x75\xcf\x16\x26\xa9\x26\x3b" + "\xe7\x68\x2f\x15\x21\x5b\xe4\x00" + "\xbd\x48\x50\xcd\x75\x70\xc4\x62" + "\xbb\x41\xfb\x89\x4a\x88\x3b\x3b" + "\x51\x66\x02\x69\x04\x97\x36\xd4" + "\x75\xae\x0b\xa3\x42\xf8\xca\x79" + "\x8f\x93\xe9\xcc\x38\xbd\xd6\xd2" + "\xf9\x70\x4e\xc3\x6a\x8e\x25\xbd" + "\xea\x15\x5a\xa0\x85\x7e\x81\x0d" + "\x03\xe7\x05\x39\xf5\x05\x26\xee" + "\xec\xaa\x1f\x3d\xc9\x98\x76\x01" + "\x2c\xf4\xfc\xa3\x88\x77\x38\xc4" + "\x50\x65\x50\x6d\x04\x1f\xdf\x5a" + "\xaa\xf2\x01\xa9\xc1\x8d\xee\xca" + "\x47\x26\xef\x39\xb8\xb4\xf2\xd1" + "\xd6\xbb\x1b\x2a\xc1\x34\x14\xcf", + .rlen = 512, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x62\x49\x77\x57\x24\x70\x93\x69" + "\x99\x59\x57\x49\x66\x96\x76\x27" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95" + "\x02\x88\x41\x97\x16\x93\x99\x37" + "\x51\x05\x82\x09\x74\x94\x45\x92", + .klen = 64, + .iv = "\xff\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .ilen = 512, + .result = "\xc5\x85\x2a\x4b\x73\xe4\xf6\xf1" + "\x7e\xf9\xf6\xe9\xa3\x73\x36\xcb" + "\xaa\xb6\x22\xb0\x24\x6e\x3d\x73" + "\x92\x99\xde\xd3\x76\xed\xcd\x63" + "\x64\x3a\x22\x57\xc1\x43\x49\xd4" + "\x79\x36\x31\x19\x62\xae\x10\x7e" + "\x7d\xcf\x7a\xe2\x6b\xce\x27\xfa" + "\xdc\x3d\xd9\x83\xd3\x42\x4c\xe0" + "\x1b\xd6\x1d\x1a\x6f\xd2\x03\x00" + "\xfc\x81\x99\x8a\x14\x62\xf5\x7e" + "\x0d\xe7\x12\xe8\x17\x9d\x0b\xec" + "\xe2\xf7\xc9\xa7\x63\xd1\x79\xb6" + "\x62\x62\x37\xfe\x0a\x4c\x4a\x37" + "\x70\xc7\x5e\x96\x5f\xbc\x8e\x9e" + "\x85\x3c\x4f\x26\x64\x85\xbc\x68" + "\xb0\xe0\x86\x5e\x26\x41\xce\x11" + "\x50\xda\x97\x14\xe9\x9e\xc7\x6d" + "\x3b\xdc\x43\xde\x2b\x27\x69\x7d" + "\xfc\xb0\x28\xbd\x8f\xb1\xc6\x31" + "\x14\x4d\xf0\x74\x37\xfd\x07\x25" + "\x96\x55\xe5\xfc\x9e\x27\x2a\x74" + "\x1b\x83\x4d\x15\x83\xac\x57\xa0" + "\xac\xa5\xd0\x38\xef\x19\x56\x53" + "\x25\x4b\xfc\xce\x04\x23\xe5\x6b" + "\xf6\xc6\x6c\x32\x0b\xb3\x12\xc5" + "\xed\x22\x34\x1c\x5d\xed\x17\x06" + "\x36\xa3\xe6\x77\xb9\x97\x46\xb8" + "\xe9\x3f\x7e\xc7\xbc\x13\x5c\xdc" + "\x6e\x3f\x04\x5e\xd1\x59\xa5\x82" + "\x35\x91\x3d\x1b\xe4\x97\x9f\x92" + "\x1c\x5e\x5f\x6f\x41\xd4\x62\xa1" + "\x8d\x39\xfc\x42\xfb\x38\x80\xb9" + "\x0a\xe3\xcc\x6a\x93\xd9\x7a\xb1" + "\xe9\x69\xaf\x0a\x6b\x75\x38\xa7" + "\xa1\xbf\xf7\xda\x95\x93\x4b\x78" + "\x19\xf5\x94\xf9\xd2\x00\x33\x37" + "\xcf\xf5\x9e\x9c\xf3\xcc\xa6\xee" + "\x42\xb2\x9e\x2c\x5f\x48\x23\x26" + "\x15\x25\x17\x03\x3d\xfe\x2c\xfc" + "\xeb\xba\xda\xe0\x00\x05\xb6\xa6" + "\x07\xb3\xe8\x36\x5b\xec\x5b\xbf" + "\xd6\x5b\x00\x74\xc6\x97\xf1\x6a" + "\x49\xa1\xc3\xfa\x10\x52\xb9\x14" + "\xad\xb7\x73\xf8\x78\x12\xc8\x59" + "\x17\x80\x4c\x57\x39\xf1\x6d\x80" + "\x25\x77\x0f\x5e\x7d\xf0\xaf\x21" + "\xec\xce\xb7\xc8\x02\x8a\xed\x53" + "\x2c\x25\x68\x2e\x1f\x85\x5e\x67" + "\xd1\x07\x7a\x3a\x89\x08\xe0\x34" + "\xdc\xdb\x26\xb4\x6b\x77\xfc\x40" + "\x31\x15\x72\xa0\xf0\x73\xd9\x3b" + "\xd5\xdb\xfe\xfc\x8f\xa9\x44\xa2" + "\x09\x9f\xc6\x33\xe5\xe2\x88\xe8" + "\xf3\xf0\x1a\xf4\xce\x12\x0f\xd6" + "\xf7\x36\xe6\xa4\xf4\x7a\x10\x58" + "\xcc\x1f\x48\x49\x65\x47\x75\xe9" + "\x28\xe1\x65\x7b\xf2\xc4\xb5\x07" + "\xf2\xec\x76\xd8\x8f\x09\xf3\x16" + "\xa1\x51\x89\x3b\xeb\x96\x42\xac" + "\x65\xe0\x67\x63\x29\xdc\xb4\x7d" + "\xf2\x41\x51\x6a\xcb\xde\x3c\xfb" + "\x66\x8d\x13\xca\xe0\x59\x2a\x00" + "\xc9\x53\x4c\xe6\x9e\xe2\x73\xd5" + "\x67\x19\xb2\xbd\x9a\x63\xd7\x5c", + .rlen = 512, + .also_non_np = 1, + .np = 3, + .tap = { 512 - 20, 4, 16 }, + } +}; + +static struct cipher_testvec speck128_xts_dec_tv_template[] = { + { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xbe\xa0\xe7\x03\xd7\xfe\xab\x62" + "\x3b\x99\x4a\x64\x74\x77\xac\xed" + "\xd8\xf4\xa6\xcf\xae\xb9\x07\x42" + "\x51\xd9\xb6\x1d\xe0\x5e\xbc\x54", + .ilen = 32, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 32, + }, { + .key = "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xfb\x53\x81\x75\x6f\x9f\x34\xad" + "\x7e\x01\xed\x7b\xcc\xda\x4e\x4a" + "\xd4\x84\xa4\x53\xd5\x88\x73\x1b" + "\xfd\xcb\xae\x0d\xf3\x04\xee\xe6", + .ilen = 32, + .result = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .rlen = 32, + }, { + .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 32, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x21\x52\x84\x15\xd1\xf7\x21\x55" + "\xd9\x75\x4a\xd3\xc5\xdb\x9f\x7d" + "\xda\x63\xb2\xf1\x82\xb0\x89\x59" + "\x86\xd4\xaa\xaa\xdd\xff\x4f\x92", + .ilen = 32, + .result = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .rlen = 32, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95", + .klen = 32, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x57\xb5\xf8\x71\x6e\x6d\xdd\x82" + "\x53\xd0\xed\x2d\x30\xc1\x20\xef" + "\x70\x67\x5e\xff\x09\x70\xbb\xc1" + "\x3a\x7b\x48\x26\xd9\x0b\xf4\x48" + "\xbe\xce\xb1\xc7\xb2\x67\xc4\xa7" + "\x76\xf8\x36\x30\xb7\xb4\x9a\xd9" + "\xf5\x9d\xd0\x7b\xc1\x06\x96\x44" + "\x19\xc5\x58\x84\x63\xb9\x12\x68" + "\x68\xc7\xaa\x18\x98\xf2\x1f\x5c" + "\x39\xa6\xd8\x32\x2b\xc3\x51\xfd" + "\x74\x79\x2e\xb4\x44\xd7\x69\xc4" + "\xfc\x29\xe6\xed\x26\x1e\xa6\x9d" + "\x1c\xbe\x00\x0e\x7f\x3a\xca\xfb" + "\x6d\x13\x65\xa0\xf9\x31\x12\xe2" + "\x26\xd1\xec\x2b\x0a\x8b\x59\x99" + "\xa7\x49\xa0\x0e\x09\x33\x85\x50" + "\xc3\x23\xca\x7a\xdd\x13\x45\x5f" + "\xde\x4c\xa7\xcb\x00\x8a\x66\x6f" + "\xa2\xb6\xb1\x2e\xe1\xa0\x18\xf6" + "\xad\xf3\xbd\xeb\xc7\xef\x55\x4f" + "\x79\x91\x8d\x36\x13\x7b\xd0\x4a" + "\x6c\x39\xfb\x53\xb8\x6f\x02\x51" + "\xa5\x20\xac\x24\x1c\x73\x59\x73" + "\x58\x61\x3a\x87\x58\xb3\x20\x56" + "\x39\x06\x2b\x4d\xd3\x20\x2b\x89" + "\x3f\xa2\xf0\x96\xeb\x7f\xa4\xcd" + "\x11\xae\xbd\xcb\x3a\xb4\xd9\x91" + "\x09\x35\x71\x50\x65\xac\x92\xe3" + "\x7b\x32\xc0\x7a\xdd\xd4\xc3\x92" + "\x6f\xeb\x79\xde\x6f\xd3\x25\xc9" + "\xcd\x63\xf5\x1e\x7a\x3b\x26\x9d" + "\x77\x04\x80\xa9\xbf\x38\xb5\xbd" + "\xb8\x05\x07\xbd\xfd\xab\x7b\xf8" + "\x2a\x26\xcc\x49\x14\x6d\x55\x01" + "\x06\x94\xd8\xb2\x2d\x53\x83\x1b" + "\x8f\xd4\xdd\x57\x12\x7e\x18\xba" + "\x8e\xe2\x4d\x80\xef\x7e\x6b\x9d" + "\x24\xa9\x60\xa4\x97\x85\x86\x2a" + "\x01\x00\x09\xf1\xcb\x4a\x24\x1c" + "\xd8\xf6\xe6\x5b\xe7\x5d\xf2\xc4" + "\x97\x1c\x10\xc6\x4d\x66\x4f\x98" + "\x87\x30\xac\xd5\xea\x73\x49\x10" + "\x80\xea\xe5\x5f\x4d\x5f\x03\x33" + "\x66\x02\x35\x3d\x60\x06\x36\x4f" + "\x14\x1c\xd8\x07\x1f\x78\xd0\xf8" + "\x4f\x6c\x62\x7c\x15\xa5\x7c\x28" + "\x7c\xcc\xeb\x1f\xd1\x07\x90\x93" + "\x7e\xc2\xa8\x3a\x80\xc0\xf5\x30" + "\xcc\x75\xcf\x16\x26\xa9\x26\x3b" + "\xe7\x68\x2f\x15\x21\x5b\xe4\x00" + "\xbd\x48\x50\xcd\x75\x70\xc4\x62" + "\xbb\x41\xfb\x89\x4a\x88\x3b\x3b" + "\x51\x66\x02\x69\x04\x97\x36\xd4" + "\x75\xae\x0b\xa3\x42\xf8\xca\x79" + "\x8f\x93\xe9\xcc\x38\xbd\xd6\xd2" + "\xf9\x70\x4e\xc3\x6a\x8e\x25\xbd" + "\xea\x15\x5a\xa0\x85\x7e\x81\x0d" + "\x03\xe7\x05\x39\xf5\x05\x26\xee" + "\xec\xaa\x1f\x3d\xc9\x98\x76\x01" + "\x2c\xf4\xfc\xa3\x88\x77\x38\xc4" + "\x50\x65\x50\x6d\x04\x1f\xdf\x5a" + "\xaa\xf2\x01\xa9\xc1\x8d\xee\xca" + "\x47\x26\xef\x39\xb8\xb4\xf2\xd1" + "\xd6\xbb\x1b\x2a\xc1\x34\x14\xcf", + .ilen = 512, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .rlen = 512, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x62\x49\x77\x57\x24\x70\x93\x69" + "\x99\x59\x57\x49\x66\x96\x76\x27" + "\x31\x41\x59\x26\x53\x58\x97\x93" + "\x23\x84\x62\x64\x33\x83\x27\x95" + "\x02\x88\x41\x97\x16\x93\x99\x37" + "\x51\x05\x82\x09\x74\x94\x45\x92", + .klen = 64, + .iv = "\xff\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xc5\x85\x2a\x4b\x73\xe4\xf6\xf1" + "\x7e\xf9\xf6\xe9\xa3\x73\x36\xcb" + "\xaa\xb6\x22\xb0\x24\x6e\x3d\x73" + "\x92\x99\xde\xd3\x76\xed\xcd\x63" + "\x64\x3a\x22\x57\xc1\x43\x49\xd4" + "\x79\x36\x31\x19\x62\xae\x10\x7e" + "\x7d\xcf\x7a\xe2\x6b\xce\x27\xfa" + "\xdc\x3d\xd9\x83\xd3\x42\x4c\xe0" + "\x1b\xd6\x1d\x1a\x6f\xd2\x03\x00" + "\xfc\x81\x99\x8a\x14\x62\xf5\x7e" + "\x0d\xe7\x12\xe8\x17\x9d\x0b\xec" + "\xe2\xf7\xc9\xa7\x63\xd1\x79\xb6" + "\x62\x62\x37\xfe\x0a\x4c\x4a\x37" + "\x70\xc7\x5e\x96\x5f\xbc\x8e\x9e" + "\x85\x3c\x4f\x26\x64\x85\xbc\x68" + "\xb0\xe0\x86\x5e\x26\x41\xce\x11" + "\x50\xda\x97\x14\xe9\x9e\xc7\x6d" + "\x3b\xdc\x43\xde\x2b\x27\x69\x7d" + "\xfc\xb0\x28\xbd\x8f\xb1\xc6\x31" + "\x14\x4d\xf0\x74\x37\xfd\x07\x25" + "\x96\x55\xe5\xfc\x9e\x27\x2a\x74" + "\x1b\x83\x4d\x15\x83\xac\x57\xa0" + "\xac\xa5\xd0\x38\xef\x19\x56\x53" + "\x25\x4b\xfc\xce\x04\x23\xe5\x6b" + "\xf6\xc6\x6c\x32\x0b\xb3\x12\xc5" + "\xed\x22\x34\x1c\x5d\xed\x17\x06" + "\x36\xa3\xe6\x77\xb9\x97\x46\xb8" + "\xe9\x3f\x7e\xc7\xbc\x13\x5c\xdc" + "\x6e\x3f\x04\x5e\xd1\x59\xa5\x82" + "\x35\x91\x3d\x1b\xe4\x97\x9f\x92" + "\x1c\x5e\x5f\x6f\x41\xd4\x62\xa1" + "\x8d\x39\xfc\x42\xfb\x38\x80\xb9" + "\x0a\xe3\xcc\x6a\x93\xd9\x7a\xb1" + "\xe9\x69\xaf\x0a\x6b\x75\x38\xa7" + "\xa1\xbf\xf7\xda\x95\x93\x4b\x78" + "\x19\xf5\x94\xf9\xd2\x00\x33\x37" + "\xcf\xf5\x9e\x9c\xf3\xcc\xa6\xee" + "\x42\xb2\x9e\x2c\x5f\x48\x23\x26" + "\x15\x25\x17\x03\x3d\xfe\x2c\xfc" + "\xeb\xba\xda\xe0\x00\x05\xb6\xa6" + "\x07\xb3\xe8\x36\x5b\xec\x5b\xbf" + "\xd6\x5b\x00\x74\xc6\x97\xf1\x6a" + "\x49\xa1\xc3\xfa\x10\x52\xb9\x14" + "\xad\xb7\x73\xf8\x78\x12\xc8\x59" + "\x17\x80\x4c\x57\x39\xf1\x6d\x80" + "\x25\x77\x0f\x5e\x7d\xf0\xaf\x21" + "\xec\xce\xb7\xc8\x02\x8a\xed\x53" + "\x2c\x25\x68\x2e\x1f\x85\x5e\x67" + "\xd1\x07\x7a\x3a\x89\x08\xe0\x34" + "\xdc\xdb\x26\xb4\x6b\x77\xfc\x40" + "\x31\x15\x72\xa0\xf0\x73\xd9\x3b" + "\xd5\xdb\xfe\xfc\x8f\xa9\x44\xa2" + "\x09\x9f\xc6\x33\xe5\xe2\x88\xe8" + "\xf3\xf0\x1a\xf4\xce\x12\x0f\xd6" + "\xf7\x36\xe6\xa4\xf4\x7a\x10\x58" + "\xcc\x1f\x48\x49\x65\x47\x75\xe9" + "\x28\xe1\x65\x7b\xf2\xc4\xb5\x07" + "\xf2\xec\x76\xd8\x8f\x09\xf3\x16" + "\xa1\x51\x89\x3b\xeb\x96\x42\xac" + "\x65\xe0\x67\x63\x29\xdc\xb4\x7d" + "\xf2\x41\x51\x6a\xcb\xde\x3c\xfb" + "\x66\x8d\x13\xca\xe0\x59\x2a\x00" + "\xc9\x53\x4c\xe6\x9e\xe2\x73\xd5" + "\x67\x19\xb2\xbd\x9a\x63\xd7\x5c", + .ilen = 512, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .rlen = 512, + .also_non_np = 1, + .np = 3, + .tap = { 512 - 20, 4, 16 }, + } +}; + static struct cipher_testvec speck64_enc_tv_template[] = { { /* Speck64/96 */ .key = "\x00\x01\x02\x03\x08\x09\x0a\x0b" -- GitLab From 1159b2dfd4638d7783899ec79e2978e260849a13 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 14 Feb 2018 10:42:23 -0800 Subject: [PATCH 2700/5498] BACKPORT, FROMGIT: crypto: speck - add test vectors for Speck64-XTS Add test vectors for Speck64-XTS, generated in userspace using C code. The inputs were borrowed from the AES-XTS test vectors, with key lengths adjusted. xts-speck64-neon passes these tests. However, they aren't currently applicable for the generic XTS template, as that only supports a 128-bit block size. Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu (cherry picked from commit 41b3316e75ee5e8aec7234c9d631582b13a38c7d git://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master) (removed 'const' from test vectors) (replaced use of __VECS macro in crypto/testmgr.c) Change-Id: I61a2c77dbfcf487d77b3d9ef0a823dadea8ddf07 Signed-off-by: Eric Biggers --- crypto/testmgr.c | 15 ++ crypto/testmgr.h | 671 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 686 insertions(+) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 09d6d80c2554..91aa9f33ad01 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -3617,6 +3617,21 @@ static const struct alg_test_desc alg_test_descs[] = { } } } + }, { + .alg = "xts(speck64)", + .test = alg_test_skcipher, + .suite = { + .cipher = { + .enc = { + .vecs = speck64_xts_enc_tv_template, + .count = ARRAY_SIZE(speck64_xts_enc_tv_template) + }, + .dec = { + .vecs = speck64_xts_dec_tv_template, + .count = ARRAY_SIZE(speck64_xts_dec_tv_template) + } + } + } }, { .alg = "xts(twofish)", .test = alg_test_skcipher, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 297818911046..58f3aef30e03 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -12875,6 +12875,677 @@ static struct cipher_testvec speck64_dec_tv_template[] = { }, }; +/* + * Speck64-XTS test vectors, taken from the AES-XTS test vectors with the result + * recomputed with Speck64 as the cipher, and key lengths adjusted + */ + +static struct cipher_testvec speck64_xts_enc_tv_template[] = { + { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 24, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .ilen = 32, + .result = "\x84\xaf\x54\x07\x19\xd4\x7c\xa6" + "\xe4\xfe\xdf\xc4\x1f\x34\xc3\xc2" + "\x80\xf5\x72\xe7\xcd\xf0\x99\x22" + "\x35\xa7\x2f\x06\xef\xdc\x51\xaa", + .rlen = 32, + }, { + .key = "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 24, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\x12\x56\x73\xcd\x15\x87\xa8\x59" + "\xcf\x84\xae\xd9\x1c\x66\xd6\x9f" + "\xb3\x12\x69\x7e\x36\xeb\x52\xff" + "\x62\xdd\xba\x90\xb3\xe1\xee\x99", + .rlen = 32, + }, { + .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 24, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .ilen = 32, + .result = "\x15\x1b\xe4\x2c\xa2\x5a\x2d\x2c" + "\x27\x36\xc0\xbf\x5d\xea\x36\x37" + "\x2d\x1a\x88\xbc\x66\xb5\xd0\x0b" + "\xa1\xbc\x19\xb2\x0f\x3b\x75\x34", + .rlen = 32, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x31\x41\x59\x26\x53\x58\x97\x93", + .klen = 24, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .ilen = 512, + .result = "\xaf\xa1\x81\xa6\x32\xbb\x15\x8e" + "\xf8\x95\x2e\xd3\xe6\xee\x7e\x09" + "\x0c\x1a\xf5\x02\x97\x8b\xe3\xb3" + "\x11\xc7\x39\x96\xd0\x95\xf4\x56" + "\xf4\xdd\x03\x38\x01\x44\x2c\xcf" + "\x88\xae\x8e\x3c\xcd\xe7\xaa\x66" + "\xfe\x3d\xc6\xfb\x01\x23\x51\x43" + "\xd5\xd2\x13\x86\x94\x34\xe9\x62" + "\xf9\x89\xe3\xd1\x7b\xbe\xf8\xef" + "\x76\x35\x04\x3f\xdb\x23\x9d\x0b" + "\x85\x42\xb9\x02\xd6\xcc\xdb\x96" + "\xa7\x6b\x27\xb6\xd4\x45\x8f\x7d" + "\xae\xd2\x04\xd5\xda\xc1\x7e\x24" + "\x8c\x73\xbe\x48\x7e\xcf\x65\x28" + "\x29\xe5\xbe\x54\x30\xcb\x46\x95" + "\x4f\x2e\x8a\x36\xc8\x27\xc5\xbe" + "\xd0\x1a\xaf\xab\x26\xcd\x9e\x69" + "\xa1\x09\x95\x71\x26\xe9\xc4\xdf" + "\xe6\x31\xc3\x46\xda\xaf\x0b\x41" + "\x1f\xab\xb1\x8e\xd6\xfc\x0b\xb3" + "\x82\xc0\x37\x27\xfc\x91\xa7\x05" + "\xfb\xc5\xdc\x2b\x74\x96\x48\x43" + "\x5d\x9c\x19\x0f\x60\x63\x3a\x1f" + "\x6f\xf0\x03\xbe\x4d\xfd\xc8\x4a" + "\xc6\xa4\x81\x6d\xc3\x12\x2a\x5c" + "\x07\xff\xf3\x72\x74\x48\xb5\x40" + "\x50\xb5\xdd\x90\x43\x31\x18\x15" + "\x7b\xf2\xa6\xdb\x83\xc8\x4b\x4a" + "\x29\x93\x90\x8b\xda\x07\xf0\x35" + "\x6d\x90\x88\x09\x4e\x83\xf5\x5b" + "\x94\x12\xbb\x33\x27\x1d\x3f\x23" + "\x51\xa8\x7c\x07\xa2\xae\x77\xa6" + "\x50\xfd\xcc\xc0\x4f\x80\x7a\x9f" + "\x66\xdd\xcd\x75\x24\x8b\x33\xf7" + "\x20\xdb\x83\x9b\x4f\x11\x63\x6e" + "\xcf\x37\xef\xc9\x11\x01\x5c\x45" + "\x32\x99\x7c\x3c\x9e\x42\x89\xe3" + "\x70\x6d\x15\x9f\xb1\xe6\xb6\x05" + "\xfe\x0c\xb9\x49\x2d\x90\x6d\xcc" + "\x5d\x3f\xc1\xfe\x89\x0a\x2e\x2d" + "\xa0\xa8\x89\x3b\x73\x39\xa5\x94" + "\x4c\xa4\xa6\xbb\xa7\x14\x46\x89" + "\x10\xff\xaf\xef\xca\xdd\x4f\x80" + "\xb3\xdf\x3b\xab\xd4\xe5\x5a\xc7" + "\x33\xca\x00\x8b\x8b\x3f\xea\xec" + "\x68\x8a\xc2\x6d\xfd\xd4\x67\x0f" + "\x22\x31\xe1\x0e\xfe\x5a\x04\xd5" + "\x64\xa3\xf1\x1a\x76\x28\xcc\x35" + "\x36\xa7\x0a\x74\xf7\x1c\x44\x9b" + "\xc7\x1b\x53\x17\x02\xea\xd1\xad" + "\x13\x51\x73\xc0\xa0\xb2\x05\x32" + "\xa8\xa2\x37\x2e\xe1\x7a\x3a\x19" + "\x26\xb4\x6c\x62\x5d\xb3\x1a\x1d" + "\x59\xda\xee\x1a\x22\x18\xda\x0d" + "\x88\x0f\x55\x8b\x72\x62\xfd\xc1" + "\x69\x13\xcd\x0d\x5f\xc1\x09\x52" + "\xee\xd6\xe3\x84\x4d\xee\xf6\x88" + "\xaf\x83\xdc\x76\xf4\xc0\x93\x3f" + "\x4a\x75\x2f\xb0\x0b\x3e\xc4\x54" + "\x7d\x69\x8d\x00\x62\x77\x0d\x14" + "\xbe\x7c\xa6\x7d\xc5\x24\x4f\xf3" + "\x50\xf7\x5f\xf4\xc2\xca\x41\x97" + "\x37\xbe\x75\x74\xcd\xf0\x75\x6e" + "\x25\x23\x94\xbd\xda\x8d\xb0\xd4", + .rlen = 512, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x62\x49\x77\x57\x24\x70\x93\x69" + "\x99\x59\x57\x49\x66\x96\x76\x27", + .klen = 32, + .iv = "\xff\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .ilen = 512, + .result = "\x55\xed\x71\xd3\x02\x8e\x15\x3b" + "\xc6\x71\x29\x2d\x3e\x89\x9f\x59" + "\x68\x6a\xcc\x8a\x56\x97\xf3\x95" + "\x4e\x51\x08\xda\x2a\xf8\x6f\x3c" + "\x78\x16\xea\x80\xdb\x33\x75\x94" + "\xf9\x29\xc4\x2b\x76\x75\x97\xc7" + "\xf2\x98\x2c\xf9\xff\xc8\xd5\x2b" + "\x18\xf1\xaf\xcf\x7c\xc5\x0b\xee" + "\xad\x3c\x76\x7c\xe6\x27\xa2\x2a" + "\xe4\x66\xe1\xab\xa2\x39\xfc\x7c" + "\xf5\xec\x32\x74\xa3\xb8\x03\x88" + "\x52\xfc\x2e\x56\x3f\xa1\xf0\x9f" + "\x84\x5e\x46\xed\x20\x89\xb6\x44" + "\x8d\xd0\xed\x54\x47\x16\xbe\x95" + "\x8a\xb3\x6b\x72\xc4\x32\x52\x13" + "\x1b\xb0\x82\xbe\xac\xf9\x70\xa6" + "\x44\x18\xdd\x8c\x6e\xca\x6e\x45" + "\x8f\x1e\x10\x07\x57\x25\x98\x7b" + "\x17\x8c\x78\xdd\x80\xa7\xd9\xd8" + "\x63\xaf\xb9\x67\x57\xfd\xbc\xdb" + "\x44\xe9\xc5\x65\xd1\xc7\x3b\xff" + "\x20\xa0\x80\x1a\xc3\x9a\xad\x5e" + "\x5d\x3b\xd3\x07\xd9\xf5\xfd\x3d" + "\x4a\x8b\xa8\xd2\x6e\x7a\x51\x65" + "\x6c\x8e\x95\xe0\x45\xc9\x5f\x4a" + "\x09\x3c\x3d\x71\x7f\x0c\x84\x2a" + "\xc8\x48\x52\x1a\xc2\xd5\xd6\x78" + "\x92\x1e\xa0\x90\x2e\xea\xf0\xf3" + "\xdc\x0f\xb1\xaf\x0d\x9b\x06\x2e" + "\x35\x10\x30\x82\x0d\xe7\xc5\x9b" + "\xde\x44\x18\xbd\x9f\xd1\x45\xa9" + "\x7b\x7a\x4a\xad\x35\x65\x27\xca" + "\xb2\xc3\xd4\x9b\x71\x86\x70\xee" + "\xf1\x89\x3b\x85\x4b\x5b\xaa\xaf" + "\xfc\x42\xc8\x31\x59\xbe\x16\x60" + "\x4f\xf9\xfa\x12\xea\xd0\xa7\x14" + "\xf0\x7a\xf3\xd5\x8d\xbd\x81\xef" + "\x52\x7f\x29\x51\x94\x20\x67\x3c" + "\xd1\xaf\x77\x9f\x22\x5a\x4e\x63" + "\xe7\xff\x73\x25\xd1\xdd\x96\x8a" + "\x98\x52\x6d\xf3\xac\x3e\xf2\x18" + "\x6d\xf6\x0a\x29\xa6\x34\x3d\xed" + "\xe3\x27\x0d\x9d\x0a\x02\x44\x7e" + "\x5a\x7e\x67\x0f\x0a\x9e\xd6\xad" + "\x91\xe6\x4d\x81\x8c\x5c\x59\xaa" + "\xfb\xeb\x56\x53\xd2\x7d\x4c\x81" + "\x65\x53\x0f\x41\x11\xbd\x98\x99" + "\xf9\xc6\xfa\x51\x2e\xa3\xdd\x8d" + "\x84\x98\xf9\x34\xed\x33\x2a\x1f" + "\x82\xed\xc1\x73\x98\xd3\x02\xdc" + "\xe6\xc2\x33\x1d\xa2\xb4\xca\x76" + "\x63\x51\x34\x9d\x96\x12\xae\xce" + "\x83\xc9\x76\x5e\xa4\x1b\x53\x37" + "\x17\xd5\xc0\x80\x1d\x62\xf8\x3d" + "\x54\x27\x74\xbb\x10\x86\x57\x46" + "\x68\xe1\xed\x14\xe7\x9d\xfc\x84" + "\x47\xbc\xc2\xf8\x19\x4b\x99\xcf" + "\x7a\xe9\xc4\xb8\x8c\x82\x72\x4d" + "\x7b\x4f\x38\x55\x36\x71\x64\xc1" + "\xfc\x5c\x75\x52\x33\x02\x18\xf8" + "\x17\xe1\x2b\xc2\x43\x39\xbd\x76" + "\x9b\x63\x76\x32\x2f\x19\x72\x10" + "\x9f\x21\x0c\xf1\x66\x50\x7f\xa5" + "\x0d\x1f\x46\xe0\xba\xd3\x2f\x3c", + .rlen = 512, + .also_non_np = 1, + .np = 3, + .tap = { 512 - 20, 4, 16 }, + } +}; + +static struct cipher_testvec speck64_xts_dec_tv_template[] = { + { + .key = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .klen = 24, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x84\xaf\x54\x07\x19\xd4\x7c\xa6" + "\xe4\xfe\xdf\xc4\x1f\x34\xc3\xc2" + "\x80\xf5\x72\xe7\xcd\xf0\x99\x22" + "\x35\xa7\x2f\x06\xef\xdc\x51\xaa", + .ilen = 32, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .rlen = 32, + }, { + .key = "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x11\x11\x11\x11\x11\x11\x11\x11" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 24, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x12\x56\x73\xcd\x15\x87\xa8\x59" + "\xcf\x84\xae\xd9\x1c\x66\xd6\x9f" + "\xb3\x12\x69\x7e\x36\xeb\x52\xff" + "\x62\xdd\xba\x90\xb3\xe1\xee\x99", + .ilen = 32, + .result = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .rlen = 32, + }, { + .key = "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x22\x22\x22\x22\x22\x22\x22\x22", + .klen = 24, + .iv = "\x33\x33\x33\x33\x33\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x15\x1b\xe4\x2c\xa2\x5a\x2d\x2c" + "\x27\x36\xc0\xbf\x5d\xea\x36\x37" + "\x2d\x1a\x88\xbc\x66\xb5\xd0\x0b" + "\xa1\xbc\x19\xb2\x0f\x3b\x75\x34", + .ilen = 32, + .result = "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44\x44", + .rlen = 32, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x31\x41\x59\x26\x53\x58\x97\x93", + .klen = 24, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xaf\xa1\x81\xa6\x32\xbb\x15\x8e" + "\xf8\x95\x2e\xd3\xe6\xee\x7e\x09" + "\x0c\x1a\xf5\x02\x97\x8b\xe3\xb3" + "\x11\xc7\x39\x96\xd0\x95\xf4\x56" + "\xf4\xdd\x03\x38\x01\x44\x2c\xcf" + "\x88\xae\x8e\x3c\xcd\xe7\xaa\x66" + "\xfe\x3d\xc6\xfb\x01\x23\x51\x43" + "\xd5\xd2\x13\x86\x94\x34\xe9\x62" + "\xf9\x89\xe3\xd1\x7b\xbe\xf8\xef" + "\x76\x35\x04\x3f\xdb\x23\x9d\x0b" + "\x85\x42\xb9\x02\xd6\xcc\xdb\x96" + "\xa7\x6b\x27\xb6\xd4\x45\x8f\x7d" + "\xae\xd2\x04\xd5\xda\xc1\x7e\x24" + "\x8c\x73\xbe\x48\x7e\xcf\x65\x28" + "\x29\xe5\xbe\x54\x30\xcb\x46\x95" + "\x4f\x2e\x8a\x36\xc8\x27\xc5\xbe" + "\xd0\x1a\xaf\xab\x26\xcd\x9e\x69" + "\xa1\x09\x95\x71\x26\xe9\xc4\xdf" + "\xe6\x31\xc3\x46\xda\xaf\x0b\x41" + "\x1f\xab\xb1\x8e\xd6\xfc\x0b\xb3" + "\x82\xc0\x37\x27\xfc\x91\xa7\x05" + "\xfb\xc5\xdc\x2b\x74\x96\x48\x43" + "\x5d\x9c\x19\x0f\x60\x63\x3a\x1f" + "\x6f\xf0\x03\xbe\x4d\xfd\xc8\x4a" + "\xc6\xa4\x81\x6d\xc3\x12\x2a\x5c" + "\x07\xff\xf3\x72\x74\x48\xb5\x40" + "\x50\xb5\xdd\x90\x43\x31\x18\x15" + "\x7b\xf2\xa6\xdb\x83\xc8\x4b\x4a" + "\x29\x93\x90\x8b\xda\x07\xf0\x35" + "\x6d\x90\x88\x09\x4e\x83\xf5\x5b" + "\x94\x12\xbb\x33\x27\x1d\x3f\x23" + "\x51\xa8\x7c\x07\xa2\xae\x77\xa6" + "\x50\xfd\xcc\xc0\x4f\x80\x7a\x9f" + "\x66\xdd\xcd\x75\x24\x8b\x33\xf7" + "\x20\xdb\x83\x9b\x4f\x11\x63\x6e" + "\xcf\x37\xef\xc9\x11\x01\x5c\x45" + "\x32\x99\x7c\x3c\x9e\x42\x89\xe3" + "\x70\x6d\x15\x9f\xb1\xe6\xb6\x05" + "\xfe\x0c\xb9\x49\x2d\x90\x6d\xcc" + "\x5d\x3f\xc1\xfe\x89\x0a\x2e\x2d" + "\xa0\xa8\x89\x3b\x73\x39\xa5\x94" + "\x4c\xa4\xa6\xbb\xa7\x14\x46\x89" + "\x10\xff\xaf\xef\xca\xdd\x4f\x80" + "\xb3\xdf\x3b\xab\xd4\xe5\x5a\xc7" + "\x33\xca\x00\x8b\x8b\x3f\xea\xec" + "\x68\x8a\xc2\x6d\xfd\xd4\x67\x0f" + "\x22\x31\xe1\x0e\xfe\x5a\x04\xd5" + "\x64\xa3\xf1\x1a\x76\x28\xcc\x35" + "\x36\xa7\x0a\x74\xf7\x1c\x44\x9b" + "\xc7\x1b\x53\x17\x02\xea\xd1\xad" + "\x13\x51\x73\xc0\xa0\xb2\x05\x32" + "\xa8\xa2\x37\x2e\xe1\x7a\x3a\x19" + "\x26\xb4\x6c\x62\x5d\xb3\x1a\x1d" + "\x59\xda\xee\x1a\x22\x18\xda\x0d" + "\x88\x0f\x55\x8b\x72\x62\xfd\xc1" + "\x69\x13\xcd\x0d\x5f\xc1\x09\x52" + "\xee\xd6\xe3\x84\x4d\xee\xf6\x88" + "\xaf\x83\xdc\x76\xf4\xc0\x93\x3f" + "\x4a\x75\x2f\xb0\x0b\x3e\xc4\x54" + "\x7d\x69\x8d\x00\x62\x77\x0d\x14" + "\xbe\x7c\xa6\x7d\xc5\x24\x4f\xf3" + "\x50\xf7\x5f\xf4\xc2\xca\x41\x97" + "\x37\xbe\x75\x74\xcd\xf0\x75\x6e" + "\x25\x23\x94\xbd\xda\x8d\xb0\xd4", + .ilen = 512, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .rlen = 512, + }, { + .key = "\x27\x18\x28\x18\x28\x45\x90\x45" + "\x23\x53\x60\x28\x74\x71\x35\x26" + "\x62\x49\x77\x57\x24\x70\x93\x69" + "\x99\x59\x57\x49\x66\x96\x76\x27", + .klen = 32, + .iv = "\xff\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x55\xed\x71\xd3\x02\x8e\x15\x3b" + "\xc6\x71\x29\x2d\x3e\x89\x9f\x59" + "\x68\x6a\xcc\x8a\x56\x97\xf3\x95" + "\x4e\x51\x08\xda\x2a\xf8\x6f\x3c" + "\x78\x16\xea\x80\xdb\x33\x75\x94" + "\xf9\x29\xc4\x2b\x76\x75\x97\xc7" + "\xf2\x98\x2c\xf9\xff\xc8\xd5\x2b" + "\x18\xf1\xaf\xcf\x7c\xc5\x0b\xee" + "\xad\x3c\x76\x7c\xe6\x27\xa2\x2a" + "\xe4\x66\xe1\xab\xa2\x39\xfc\x7c" + "\xf5\xec\x32\x74\xa3\xb8\x03\x88" + "\x52\xfc\x2e\x56\x3f\xa1\xf0\x9f" + "\x84\x5e\x46\xed\x20\x89\xb6\x44" + "\x8d\xd0\xed\x54\x47\x16\xbe\x95" + "\x8a\xb3\x6b\x72\xc4\x32\x52\x13" + "\x1b\xb0\x82\xbe\xac\xf9\x70\xa6" + "\x44\x18\xdd\x8c\x6e\xca\x6e\x45" + "\x8f\x1e\x10\x07\x57\x25\x98\x7b" + "\x17\x8c\x78\xdd\x80\xa7\xd9\xd8" + "\x63\xaf\xb9\x67\x57\xfd\xbc\xdb" + "\x44\xe9\xc5\x65\xd1\xc7\x3b\xff" + "\x20\xa0\x80\x1a\xc3\x9a\xad\x5e" + "\x5d\x3b\xd3\x07\xd9\xf5\xfd\x3d" + "\x4a\x8b\xa8\xd2\x6e\x7a\x51\x65" + "\x6c\x8e\x95\xe0\x45\xc9\x5f\x4a" + "\x09\x3c\x3d\x71\x7f\x0c\x84\x2a" + "\xc8\x48\x52\x1a\xc2\xd5\xd6\x78" + "\x92\x1e\xa0\x90\x2e\xea\xf0\xf3" + "\xdc\x0f\xb1\xaf\x0d\x9b\x06\x2e" + "\x35\x10\x30\x82\x0d\xe7\xc5\x9b" + "\xde\x44\x18\xbd\x9f\xd1\x45\xa9" + "\x7b\x7a\x4a\xad\x35\x65\x27\xca" + "\xb2\xc3\xd4\x9b\x71\x86\x70\xee" + "\xf1\x89\x3b\x85\x4b\x5b\xaa\xaf" + "\xfc\x42\xc8\x31\x59\xbe\x16\x60" + "\x4f\xf9\xfa\x12\xea\xd0\xa7\x14" + "\xf0\x7a\xf3\xd5\x8d\xbd\x81\xef" + "\x52\x7f\x29\x51\x94\x20\x67\x3c" + "\xd1\xaf\x77\x9f\x22\x5a\x4e\x63" + "\xe7\xff\x73\x25\xd1\xdd\x96\x8a" + "\x98\x52\x6d\xf3\xac\x3e\xf2\x18" + "\x6d\xf6\x0a\x29\xa6\x34\x3d\xed" + "\xe3\x27\x0d\x9d\x0a\x02\x44\x7e" + "\x5a\x7e\x67\x0f\x0a\x9e\xd6\xad" + "\x91\xe6\x4d\x81\x8c\x5c\x59\xaa" + "\xfb\xeb\x56\x53\xd2\x7d\x4c\x81" + "\x65\x53\x0f\x41\x11\xbd\x98\x99" + "\xf9\xc6\xfa\x51\x2e\xa3\xdd\x8d" + "\x84\x98\xf9\x34\xed\x33\x2a\x1f" + "\x82\xed\xc1\x73\x98\xd3\x02\xdc" + "\xe6\xc2\x33\x1d\xa2\xb4\xca\x76" + "\x63\x51\x34\x9d\x96\x12\xae\xce" + "\x83\xc9\x76\x5e\xa4\x1b\x53\x37" + "\x17\xd5\xc0\x80\x1d\x62\xf8\x3d" + "\x54\x27\x74\xbb\x10\x86\x57\x46" + "\x68\xe1\xed\x14\xe7\x9d\xfc\x84" + "\x47\xbc\xc2\xf8\x19\x4b\x99\xcf" + "\x7a\xe9\xc4\xb8\x8c\x82\x72\x4d" + "\x7b\x4f\x38\x55\x36\x71\x64\xc1" + "\xfc\x5c\x75\x52\x33\x02\x18\xf8" + "\x17\xe1\x2b\xc2\x43\x39\xbd\x76" + "\x9b\x63\x76\x32\x2f\x19\x72\x10" + "\x9f\x21\x0c\xf1\x66\x50\x7f\xa5" + "\x0d\x1f\x46\xe0\xba\xd3\x2f\x3c", + .ilen = 512, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37" + "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47" + "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57" + "\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67" + "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77" + "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" + "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" + "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7" + "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7" + "\xe8\xe9\xea\xeb\xec\xed\xee\xef" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7" + "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + .rlen = 512, + .also_non_np = 1, + .np = 3, + .tap = { 512 - 20, 4, 16 }, + } +}; + /* Cast6 test vectors from RFC 2612 */ #define CAST6_ENC_TEST_VECTORS 4 #define CAST6_DEC_TEST_VECTORS 4 -- GitLab From d78d0ef6468452ef06e3548ad39dd0318908646a Mon Sep 17 00:00:00 2001 From: Bala Kishore Pati Date: Tue, 20 Feb 2018 11:37:40 +0530 Subject: [PATCH 2701/5498] ASoC: bg: Avoid delay when first audio usecase start in BG Adding a workqueue to send BG calibration on boot up, this helps to avoid the delay which would be incurred if calibration is sent during start of the usecase. Change-Id: I63d880e9e74ffcd4880336cf3ac642b537f383c3 Signed-off-by: Bala Kishore Pati --- sound/soc/codecs/bg_codec.c | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/sound/soc/codecs/bg_codec.c b/sound/soc/codecs/bg_codec.c index 46bd43364a10..d9adef5ea949 100644 --- a/sound/soc/codecs/bg_codec.c +++ b/sound/soc/codecs/bg_codec.c @@ -90,6 +90,7 @@ struct bg_cdc_priv { struct platform_device *pdev_child; struct work_struct bg_cdc_add_child_devices_work; struct delayed_work bg_cdc_pktzr_init_work; + struct delayed_work bg_cdc_cal_init_work; unsigned long status_mask; struct bg_hw_params hw_params; struct notifier_block bg_pm_nb; @@ -290,6 +291,34 @@ err2: return ret; } +static void bg_cdc_cal_init(struct work_struct *work) +{ + struct bg_cdc_priv *bg_cdc; + struct delayed_work *dwork; + int ret = 0; + + dwork = to_delayed_work(work); + bg_cdc = container_of(dwork, struct bg_cdc_priv, + bg_cdc_cal_init_work); + mutex_lock(&bg_cdc->bg_cdc_lock); + if (!bg_cdc->bg_cal_updated) { + ret = bg_cdc_enable_regulator(bg_cdc->spkr_vreg, true); + if (ret < 0) { + pr_err("%s: enable_regulator failed %d\n", __func__, + ret); + } else { + ret = bg_cdc_cal(bg_cdc); + if (ret < 0) { + bg_cdc_enable_regulator(bg_cdc->spkr_vreg, + false); + pr_err("%s: failed to send cal data\n", + __func__); + } + } + } + mutex_unlock(&bg_cdc->bg_cdc_lock); +} + static int _bg_codec_hw_params(struct bg_cdc_priv *bg_cdc) { struct bg_hw_params hw_params; @@ -874,6 +903,8 @@ static void bg_cdc_pktzr_init(struct work_struct *work) if (rsp.buf) kzfree(rsp.buf); + schedule_delayed_work(&bg_cdc->bg_cdc_cal_init_work, + msecs_to_jiffies(5000)); } static int bg_cdc_bg_device_up(struct bg_cdc_priv *bg_cdc) @@ -1045,6 +1076,11 @@ static int bg_cdc_codec_remove(struct snd_soc_codec *codec) struct bg_cdc_priv *bg_cdc = dev_get_drvdata(codec->dev); pr_debug("In func %s\n", __func__); pktzr_deinit(); + + if (delayed_work_pending(&bg_cdc->bg_cdc_pktzr_init_work)) + cancel_delayed_work_sync(&bg_cdc->bg_cdc_pktzr_init_work); + if (delayed_work_pending(&bg_cdc->bg_cdc_cal_init_work)) + cancel_delayed_work_sync(&bg_cdc->bg_cdc_cal_init_work); if (adsp_state_notifier) subsys_notif_unregister_notifier(adsp_state_notifier, &bg_cdc->bg_adsp_nb); @@ -1226,6 +1262,8 @@ static int bg_cdc_probe(struct platform_device *pdev) bg_cdc_add_child_devices); INIT_DELAYED_WORK(&bg_cdc->bg_cdc_pktzr_init_work, bg_cdc_pktzr_init); + INIT_DELAYED_WORK(&bg_cdc->bg_cdc_cal_init_work, + bg_cdc_cal_init); schedule_work(&bg_cdc->bg_cdc_add_child_devices_work); mutex_init(&bg_cdc->bg_cdc_lock); bg_cdc->bg_pm_nb.notifier_call = bg_pm_event; -- GitLab From 1a19138f4ae561cb3e6412e432660fc68b8d4a2d Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 16 Feb 2018 09:47:15 +0100 Subject: [PATCH 2702/5498] UPSTREAM: ANDROID: binder: synchronize_rcu() when using POLLFREE. To prevent races with ep_remove_waitqueue() removing the waitqueue at the same time. Reported-by: syzbot+a2a3c4909716e271487e@syzkaller.appspotmail.com Signed-off-by: Martijn Coenen Cc: stable # 4.14+ Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 5eeb2ca02a2f6084fc57ae5c244a38baab07033a) Change-Id: Ia0089448079c78d0ab0b57303faf838e9e5ee797 --- drivers/staging/android/binder.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index b2e4f493bade..cddcb8d4e0a2 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -4546,6 +4546,15 @@ static int binder_thread_release(struct binder_proc *proc, binder_inner_proc_unlock(thread->proc); + /* + * This is needed to avoid races between wake_up_poll() above and + * and ep_remove_waitqueue() called for other reasons (eg the epoll file + * descriptor being closed); ep_remove_waitqueue() holds an RCU read + * lock, so we can be sure it's done after calling synchronize_rcu(). + */ + if (thread->looper & BINDER_LOOPER_STATE_POLL) + synchronize_rcu(); + if (send_reply) binder_send_failed_reply(send_reply, BR_DEAD_REPLY); binder_release_work(proc, &thread->todo); -- GitLab From cf793c2acc2fbd7734928b76dffb2b3da813473c Mon Sep 17 00:00:00 2001 From: Surendar karka Date: Wed, 21 Feb 2018 14:20:14 +0530 Subject: [PATCH 2703/5498] ASoC: msm: qdsp6v2: support source tracking with TDM interface Add mixer controls and port id for PRIMARY_TDM interface to support source tracking with TDM mics. CRs-Fixed: 2193330 Change-Id: I62fd250e0feb3fbc190fb63d1398fc3082506f47 Signed-off-by: Surendar karka --- sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 9af1eb9c978c..34dcc85fde10 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -9755,6 +9755,9 @@ static int msm_audio_sound_focus_derive_port_id(struct snd_kcontrol *kcontrol, } else if (!strcmp(kcontrol->id.name + strlen(prefix), "QUATERNARY_MI2S")) { *port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + } else if (!strcmp(kcontrol->id.name + strlen(prefix), + "PRIMARY_TDM")) { + *port_id = AFE_PORT_ID_PRIMARY_TDM_TX; } else { pr_err("%s: mixer ctl name=%s, could not derive valid port id\n", __func__, kcontrol->id.name); @@ -9974,6 +9977,21 @@ static const struct snd_kcontrol_new msm_source_tracking_controls[] = { .info = msm_source_tracking_info, .get = msm_audio_source_tracking_get, }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx PRIMARY_TDM", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx PRIMARY_TDM", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, }; static int spkr_prot_put_vi_lch_port(struct snd_kcontrol *kcontrol, -- GitLab From 78f1fa5956b33ae8cfcdd00ea8f89c083cf0cd59 Mon Sep 17 00:00:00 2001 From: Umang Agrawal Date: Tue, 13 Feb 2018 15:17:25 +0530 Subject: [PATCH 2704/5498] power: vm-bms: Add charge_counter property Add charge_counter property to vm-bms which is expressed in terms of battery FCC and SOC so that the clients can make use of it as needed. CRs-Fixed: 2188284 Change-Id: I7bc732f8b9b4125f8d2ad4d5e3c8d9b50a27350e Signed-off-by: Umang Agrawal --- drivers/power/qpnp-linear-charger.c | 23 +++++++++++++++++++++- drivers/power/qpnp-vm-bms.c | 30 ++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/power/qpnp-linear-charger.c b/drivers/power/qpnp-linear-charger.c index 5c0f69afb9ad..21e3199e9aff 100644 --- a/drivers/power/qpnp-linear-charger.c +++ b/drivers/power/qpnp-linear-charger.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -206,6 +206,7 @@ static enum power_supply_property msm_batt_power_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_COOL_TEMP, POWER_SUPPLY_PROP_WARM_TEMP, @@ -1348,6 +1349,23 @@ static int get_prop_capacity(struct qpnp_lbc_chip *chip) return DEFAULT_CAPACITY; } +static int get_prop_charge_count(struct qpnp_lbc_chip *chip) +{ + union power_supply_propval ret = {0,}; + + if (!chip->bms_psy) + chip->bms_psy = power_supply_get_by_name("bms"); + + if (chip->bms_psy) { + chip->bms_psy->get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CHARGE_COUNTER, &ret); + } else { + pr_debug("No BMS supply registered return 0\n"); + } + + return ret.intval; +} + #define DEFAULT_TEMP 250 static int get_prop_batt_temp(struct qpnp_lbc_chip *chip) { @@ -1719,6 +1737,9 @@ static int qpnp_batt_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = get_prop_current_now(chip); break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = get_prop_charge_count(chip); + break; case POWER_SUPPLY_PROP_CHARGING_ENABLED: val->intval = !(chip->cfg_charging_disabled); break; diff --git a/drivers/power/qpnp-vm-bms.c b/drivers/power/qpnp-vm-bms.c index 9c5b94ecc121..04bb457f607b 100644 --- a/drivers/power/qpnp-vm-bms.c +++ b/drivers/power/qpnp-vm-bms.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2203,6 +2203,30 @@ static int get_prop_bms_current_now(struct qpnp_bms_chip *chip) return chip->current_now; } +static int get_current_cc(struct qpnp_bms_chip *chip) +{ + int soc, cc_full; + int64_t current_charge; + + if (chip->batt_data == NULL) + return -EINVAL; + + cc_full = chip->batt_data->fcc; + if (chip->dt.cfg_use_voltage_soc) + soc = chip->prev_voltage_based_soc; + else + soc = chip->last_soc; + + /* + * Full charge capacity is in mAh and soc is in % + * current_charge capacity is defined in uAh + * Hence conversion ((mAh * pct * 1000) / 100) => (mAh * pct * 10) + */ + current_charge = cc_full * soc * 10; + + return current_charge; +} + static enum power_supply_property bms_power_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_STATUS, @@ -2216,6 +2240,7 @@ static enum power_supply_property bms_power_props[] = { POWER_SUPPLY_PROP_BATTERY_TYPE, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, }; static int @@ -2294,6 +2319,9 @@ static int qpnp_vm_bms_power_get_property(struct power_supply *psy, else val->intval = -EINVAL; break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = get_current_cc(chip); + break; default: return -EINVAL; } -- GitLab From 750512064cf3e471a1f467cfeaf0940762737d88 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 20 Feb 2018 20:25:45 -0800 Subject: [PATCH 2705/5498] ANDROID: sdcardfs: Hold i_mutex for i_size_write When we call i_size_write, we must be holding i_mutex to avoid possible lockups on 32 bit/SMP architectures. This is not necessary on 64 bit architectures. Change-Id: Ic3b946507c54d81b5c9046f9b57d25d4b0f9feef Signed-off-by: Daniel Rosenberg Bug: 73287721 --- fs/sdcardfs/file.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 0fe08c7856a5..15f7a72f1581 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -62,6 +62,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, int err; struct file *lower_file; struct dentry *dentry = file->f_path.dentry; + struct inode *inode = dentry->d_inode; /* check disk space */ if (!check_min_free_space(dentry, count, 0)) { @@ -73,10 +74,12 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, err = vfs_write(lower_file, buf, count, ppos); /* update our inode times+sizes upon a successful lower write */ if (err >= 0) { - fsstack_copy_inode_size(dentry->d_inode, - file_inode(lower_file)); - fsstack_copy_attr_times(dentry->d_inode, - file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + mutex_lock(&inode->i_mutex); + fsstack_copy_inode_size(inode, file_inode(lower_file)); + fsstack_copy_attr_times(inode, file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + mutex_unlock(&inode->i_mutex); } return err; @@ -391,6 +394,7 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) { int err; struct file *file = iocb->ki_filp, *lower_file; + struct inode *inode = file->f_path.dentry->d_inode; lower_file = sdcardfs_lower_file(file); if (!lower_file->f_op->write_iter) { @@ -405,10 +409,12 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) fput(lower_file); /* update upper inode times/sizes as needed */ if (err >= 0 || err == -EIOCBQUEUED) { - fsstack_copy_inode_size(file->f_path.dentry->d_inode, - file_inode(lower_file)); - fsstack_copy_attr_times(file->f_path.dentry->d_inode, - file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + mutex_lock(&inode->i_mutex); + fsstack_copy_inode_size(inode, file_inode(lower_file)); + fsstack_copy_attr_times(inode, file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + mutex_unlock(&inode->i_mutex); } out: return err; -- GitLab From 92b6444dd8b3abcfb9bd6e83cf50812831499e3c Mon Sep 17 00:00:00 2001 From: Sreelakshmi Gownipalli Date: Mon, 29 Jan 2018 13:17:13 -0800 Subject: [PATCH 2706/5498] diag: Add conditional check for len in dci_process_ctrl_status() Add correct conditional check for len in dci_process_ctrl_status() to prevent buffer overflow. Change-Id: Id73ed1c8b104428eceef0544ce2858160cc08fd2 Signed-off-by: Sreelakshmi Gownipalli --- drivers/char/diag/diag_dci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 37cc6c777421..845fd5e90667 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -831,7 +831,7 @@ static void dci_process_ctrl_status(unsigned char *buf, int len, int token) read_len += sizeof(struct diag_ctrl_dci_status); for (i = 0; i < header->count; i++) { - if (read_len > len) { + if (read_len > (len - 2)) { pr_err("diag: In %s, Invalid length len: %d\n", __func__, len); return; -- GitLab From b4f35ecb7f3ef6c456147d1bd61041bb7b347bca Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Tue, 20 Feb 2018 10:59:35 +0530 Subject: [PATCH 2707/5498] msm: ais: Synchronize v4l2 subscribe and unsubscribe event Serializing msm_subscribe_event and msm_unsubscribe_event to prevent possibility of use-after-free if same event is unsubcribed before v4l2 subscribe and unsubscribe event. Signed-off-by: Rahul Sharma Change-Id: I83f1f69feb397b05c810ecbf94584aaab19ffe55 --- drivers/media/platform/msm/ais/camera/camera.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/ais/camera/camera.c b/drivers/media/platform/msm/ais/camera/camera.c index bd82033fe5e6..ddb89ff07ab7 100644 --- a/drivers/media/platform/msm/ais/camera/camera.c +++ b/drivers/media/platform/msm/ais/camera/camera.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -462,7 +462,9 @@ static int camera_v4l2_subscribe_event(struct v4l2_fh *fh, int rc = 0; struct camera_v4l2_private *sp = fh_to_private(fh); + mutex_lock(&sp->lock); rc = v4l2_event_subscribe(&sp->fh, sub, 5, NULL); + mutex_unlock(&sp->lock); return rc; } @@ -473,7 +475,9 @@ static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh, int rc = 0; struct camera_v4l2_private *sp = fh_to_private(fh); + mutex_lock(&sp->lock); rc = v4l2_event_unsubscribe(&sp->fh, sub); + mutex_unlock(&sp->lock); return rc; } -- GitLab From ebda13935a36c1d85dcacdd6d0dc8cbcdd4ca5f8 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Fri, 12 Jan 2018 07:58:40 +0200 Subject: [PATCH 2708/5498] IB/mlx4: Fix incorrectly releasing steerable UD QPs when have only ETH ports commit 852f6927594d0d3e8632c889b2ab38cbc46476ad upstream. Allocating steerable UD QPs depends on having at least one IB port, while releasing those QPs does not. As a result, when there are only ETH ports, the IB (RoCE) driver requests releasing a qp range whose base qp is zero, with qp count zero. When SR-IOV is enabled, and the VF driver is running on a VM over a hypervisor which treats such qp release calls as errors (rather than NOPs), we see lines in the VM message log like: mlx4_core 0002:00:02.0: Failed to release qp range base:0 cnt:0 Fix this by adding a check for a zero count in mlx4_release_qp_range() (which thus treats releasing 0 qps as a nop), and eliminating the check for device managed flow steering when releasing steerable UD QPs. (Freeing ib_uc_qpns_bitmap unconditionally is also OK, since it remains NULL when steerable UD QPs are not allocated). Fixes: 4196670be786 ("IB/mlx4: Don't allocate range of steerable UD QPs for Ethernet-only device") Signed-off-by: Jack Morgenstein Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/mlx4/main.c | 13 +++++-------- drivers/net/ethernet/mellanox/mlx4/qp.c | 3 +++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 420ae23d064d..1ca8010ccb1f 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -2348,9 +2348,8 @@ err_steer_free_bitmap: kfree(ibdev->ib_uc_qpns_bitmap); err_steer_qp_release: - if (ibdev->steering_support == MLX4_STEERING_MODE_DEVICE_MANAGED) - mlx4_qp_release_range(dev, ibdev->steer_qpn_base, - ibdev->steer_qpn_count); + mlx4_qp_release_range(dev, ibdev->steer_qpn_base, + ibdev->steer_qpn_count); err_counter: for (; i; --i) if (ibdev->counters[i - 1] != -1) @@ -2452,11 +2451,9 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr) ibdev->iboe.nb.notifier_call = NULL; } - if (ibdev->steering_support == MLX4_STEERING_MODE_DEVICE_MANAGED) { - mlx4_qp_release_range(dev, ibdev->steer_qpn_base, - ibdev->steer_qpn_count); - kfree(ibdev->ib_uc_qpns_bitmap); - } + mlx4_qp_release_range(dev, ibdev->steer_qpn_base, + ibdev->steer_qpn_count); + kfree(ibdev->ib_uc_qpns_bitmap); if (ibdev->iboe.nb_inet.notifier_call) { if (unregister_inetaddr_notifier(&ibdev->iboe.nb_inet)) diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 2301365c79c7..b295eeb2af69 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -257,6 +257,9 @@ void mlx4_qp_release_range(struct mlx4_dev *dev, int base_qpn, int cnt) u64 in_param = 0; int err; + if (!cnt) + return; + if (mlx4_is_mfunc(dev)) { set_param_l(&in_param, base_qpn); set_param_h(&in_param, cnt); -- GitLab From e91e3808f894b017ad7a89be0627a847c01b75c9 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 5 Nov 2017 21:27:41 -0800 Subject: [PATCH 2709/5498] PM / devfreq: Propagate error from devfreq_add_device() commit d1bf2d30728f310f72296b54f0651ecdb09cbb12 upstream. Propagate the error of devfreq_add_device() in devm_devfreq_add_device() rather than statically returning ENOMEM. This makes it slightly faster to pinpoint the cause of a returned error. Fixes: 8cd84092d35e ("PM / devfreq: Add resource-managed function for devfreq device") Cc: stable@vger.kernel.org Acked-by: Chanwoo Choi Signed-off-by: Bjorn Andersson Signed-off-by: MyungJoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/devfreq/devfreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 30b538d8cc90..0a3cc627c8ec 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -585,7 +585,7 @@ struct devfreq *devm_devfreq_add_device(struct device *dev, devfreq = devfreq_add_device(dev, profile, governor_name, data); if (IS_ERR(devfreq)) { devres_free(ptr); - return ERR_PTR(-ENOMEM); + return devfreq; } *ptr = devfreq; -- GitLab From ae0dd9e49bd17c6637a4485ff9f827ef200457e5 Mon Sep 17 00:00:00 2001 From: Eugene Syromiatnikov Date: Mon, 15 Jan 2018 20:38:17 +0100 Subject: [PATCH 2710/5498] s390: fix handling of -1 in set{,fs}[gu]id16 syscalls commit 6dd0d2d22aa363fec075cb2577ba273ac8462e94 upstream. For some reason, the implementation of some 16-bit ID system calls (namely, setuid16/setgid16 and setfsuid16/setfsgid16) used type cast instead of low2highgid/low2highuid macros for converting [GU]IDs, which led to incorrect handling of value of -1 (which ought to be considered invalid). Discovered by strace test suite. Cc: stable@vger.kernel.org Signed-off-by: Eugene Syromiatnikov Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- arch/s390/kernel/compat_linux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index 437e61159279..b5ce954e845c 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -110,7 +110,7 @@ COMPAT_SYSCALL_DEFINE2(s390_setregid16, u16, rgid, u16, egid) COMPAT_SYSCALL_DEFINE1(s390_setgid16, u16, gid) { - return sys_setgid((gid_t)gid); + return sys_setgid(low2highgid(gid)); } COMPAT_SYSCALL_DEFINE2(s390_setreuid16, u16, ruid, u16, euid) @@ -120,7 +120,7 @@ COMPAT_SYSCALL_DEFINE2(s390_setreuid16, u16, ruid, u16, euid) COMPAT_SYSCALL_DEFINE1(s390_setuid16, u16, uid) { - return sys_setuid((uid_t)uid); + return sys_setuid(low2highuid(uid)); } COMPAT_SYSCALL_DEFINE3(s390_setresuid16, u16, ruid, u16, euid, u16, suid) @@ -173,12 +173,12 @@ COMPAT_SYSCALL_DEFINE3(s390_getresgid16, u16 __user *, rgidp, COMPAT_SYSCALL_DEFINE1(s390_setfsuid16, u16, uid) { - return sys_setfsuid((uid_t)uid); + return sys_setfsuid(low2highuid(uid)); } COMPAT_SYSCALL_DEFINE1(s390_setfsgid16, u16, gid) { - return sys_setfsgid((gid_t)gid); + return sys_setfsgid(low2highgid(gid)); } static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info) -- GitLab From b943222b208149c5233ce97a2f4641cfbca3d82a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 11 Jan 2018 11:28:51 +0530 Subject: [PATCH 2711/5498] arm: spear600: Add missing interrupt-parent of rtc commit 6ffb5b4f248fe53e0361b8cbc2a523b432566442 upstream. The interrupt-parent of rtc was missing, add it. Fixes: 8113ba917dfa ("ARM: SPEAr: DT: Update device nodes") Cc: stable@vger.kernel.org # v3.8+ Reported-by: Arnd Bergmann Signed-off-by: Viresh Kumar Signed-off-by: Olof Johansson Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/spear600.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/spear600.dtsi b/arch/arm/boot/dts/spear600.dtsi index 9f60a7b6a42b..bd379034993c 100644 --- a/arch/arm/boot/dts/spear600.dtsi +++ b/arch/arm/boot/dts/spear600.dtsi @@ -194,6 +194,7 @@ rtc@fc900000 { compatible = "st,spear600-rtc"; reg = <0xfc900000 0x1000>; + interrupt-parent = <&vic0>; interrupts = <10>; status = "disabled"; }; -- GitLab From 67df924c852c5864c3eec6255605fede2a7d16ed Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 11 Jan 2018 11:28:52 +0530 Subject: [PATCH 2712/5498] arm: spear13xx: Fix dmas cells commit cdd10409914184c7eee5ae3e11beb890c9c16c61 upstream. The "dmas" cells for the designware DMA controller need to have only 3 properties apart from the phandle: request line, src master and destination master. But the commit 6e8887f60f60 updated it incorrectly while moving from platform code to DT. Fix it. Cc: stable@vger.kernel.org # v3.10+ Fixes: 6e8887f60f60 ("ARM: SPEAr13xx: Pass generic DW DMAC platform data from DT") Reported-by: Arnd Bergmann Signed-off-by: Viresh Kumar Signed-off-by: Olof Johansson Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/dma/snps-dma.txt | 2 +- arch/arm/boot/dts/spear1340.dtsi | 4 ++-- arch/arm/boot/dts/spear13xx.dtsi | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index d58675ea1abf..f4bdc9d62130 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -58,6 +58,6 @@ Example: interrupts = <0 35 0x4>; status = "disabled"; dmas = <&dmahost 12 0 1>, - <&dmahost 13 0 1 0>; + <&dmahost 13 1 0>; dma-names = "rx", "rx"; }; diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi index 13e1aa33daa2..69bc407b4a5a 100644 --- a/arch/arm/boot/dts/spear1340.dtsi +++ b/arch/arm/boot/dts/spear1340.dtsi @@ -141,8 +141,8 @@ reg = <0xb4100000 0x1000>; interrupts = <0 105 0x4>; status = "disabled"; - dmas = <&dwdma0 0x600 0 0 1>, /* 0xC << 11 */ - <&dwdma0 0x680 0 1 0>; /* 0xD << 7 */ + dmas = <&dwdma0 12 0 1>, + <&dwdma0 13 1 0>; dma-names = "tx", "rx"; }; diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi index a6eb5436d26d..e6db110e991b 100644 --- a/arch/arm/boot/dts/spear13xx.dtsi +++ b/arch/arm/boot/dts/spear13xx.dtsi @@ -100,7 +100,7 @@ reg = <0xb2800000 0x1000>; interrupts = <0 29 0x4>; status = "disabled"; - dmas = <&dwdma0 0 0 0 0>; + dmas = <&dwdma0 0 0 0>; dma-names = "data"; }; @@ -288,8 +288,8 @@ #size-cells = <0>; interrupts = <0 31 0x4>; status = "disabled"; - dmas = <&dwdma0 0x2000 0 0 0>, /* 0x4 << 11 */ - <&dwdma0 0x0280 0 0 0>; /* 0x5 << 7 */ + dmas = <&dwdma0 4 0 0>, + <&dwdma0 5 0 0>; dma-names = "tx", "rx"; }; -- GitLab From 055fdc77edff024b7a991a8388f7a24f1b75d5bc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 11 Jan 2018 11:28:53 +0530 Subject: [PATCH 2713/5498] arm: spear13xx: Fix spics gpio controller's warning commit f8975cb1b8a36d0839b6365235778dd9df1d04ca upstream. This fixes the following warning by also sending the flags argument for gpio controllers: Property 'cs-gpios', cell 6 is not a phandle reference in /ahb/apb/spi@e0100000 Fixes: 8113ba917dfa ("ARM: SPEAr: DT: Update device nodes") Cc: stable@vger.kernel.org # v3.8+ Reported-by: Arnd Bergmann Signed-off-by: Viresh Kumar Signed-off-by: Olof Johansson Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/spear1310-evb.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/spear1310-evb.dts b/arch/arm/boot/dts/spear1310-evb.dts index d42c84b1df8d..9cff28d476be 100644 --- a/arch/arm/boot/dts/spear1310-evb.dts +++ b/arch/arm/boot/dts/spear1310-evb.dts @@ -349,7 +349,7 @@ spi0: spi@e0100000 { status = "okay"; num-cs = <3>; - cs-gpios = <&gpio1 7 0>, <&spics 0>, <&spics 1>; + cs-gpios = <&gpio1 7 0>, <&spics 0 0>, <&spics 1 0>; stmpe610@0 { compatible = "st,stmpe610"; -- GitLab From 8e8992a93d66adb640631a6778a5110f01118202 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 19 Feb 2018 17:16:01 +0100 Subject: [PATCH 2714/5498] ALSA: seq: Fix regression by incorrect ioctl_mutex usages This is the revised backport of the upstream commit b3defb791b26ea0683a93a4f49c77ec45ec96f10 We had another backport (e.g. 623e5c8ae32b in 4.4.115), but it applies the new mutex also to the code paths that are invoked via faked kernel-to-kernel ioctls. As reported recently, this leads to a deadlock at suspend (or other scenarios triggering the kernel sequencer client). This patch addresses the issue by taking the mutex only in the code paths invoked by user-space, just like the original fix patch does. Reported-and-tested-by: Andres Bertens Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_clientmgr.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 83bf65ae8251..8923f7e69efc 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -2201,7 +2201,6 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, void __user *arg) { struct seq_ioctl_table *p; - int ret; switch (cmd) { case SNDRV_SEQ_IOCTL_PVERSION: @@ -2215,12 +2214,8 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, if (! arg) return -EFAULT; for (p = ioctl_tables; p->cmd; p++) { - if (p->cmd == cmd) { - mutex_lock(&client->ioctl_mutex); - ret = p->func(client, arg); - mutex_unlock(&client->ioctl_mutex); - return ret; - } + if (p->cmd == cmd) + return p->func(client, arg); } pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n", cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); @@ -2231,11 +2226,15 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd, static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_seq_client *client = file->private_data; + long ret; if (snd_BUG_ON(!client)) return -ENXIO; - return snd_seq_do_ioctl(client, cmd, (void __user *) arg); + mutex_lock(&client->ioctl_mutex); + ret = snd_seq_do_ioctl(client, cmd, (void __user *) arg); + mutex_unlock(&client->ioctl_mutex); + return ret; } #ifdef CONFIG_COMPAT -- GitLab From 45d73fe10e5be3a41c9f375bf0233e46358cd057 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 27 Jan 2018 15:28:15 +0100 Subject: [PATCH 2715/5498] drm/radeon: adjust tested variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 3a61b527b4e1f285d21b6e9e623dc45cf8bb391f upstream. Check the variable that was most recently initialized. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x, y, f, g, e, m; statement S1,S2,S3,S4; @@ x = f(...); if (\(<+...x...+>\&e\)) S1 else S2 ( x = g(...); | m = g(...,&x,...); | y = g(...); *if (e) S3 else S4 ) // Signed-off-by: Julia Lawall Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_uvd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index db6536f722f2..5b669b1cd3b3 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -947,7 +947,7 @@ int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, /* calc dclk divider with current vco freq */ dclk_div = radeon_uvd_calc_upll_post_div(vco_freq, dclk, pd_min, pd_even); - if (vclk_div > pd_max) + if (dclk_div > pd_max) break; /* vco is too big, it has to stop */ /* calc score with current vco freq */ -- GitLab From 5b2336f021d57363a24558686f94e5f6eacad7b0 Mon Sep 17 00:00:00 2001 From: Zhouyi Zhou Date: Wed, 10 Jan 2018 00:34:19 -0500 Subject: [PATCH 2716/5498] ext4: save error to disk in __ext4_grp_locked_error() commit 06f29cc81f0350261f59643a505010531130eea0 upstream. In the function __ext4_grp_locked_error(), __save_error_info() is called to save error info in super block block, but does not sync that information to disk to info the subsequence fsck after reboot. This patch writes the error information to disk. After this patch, I think there is no obvious EXT4 error handle branches which leads to "Remounting filesystem read-only" will leave the disk partition miss the subsequence fsck. Signed-off-by: Zhouyi Zhou Signed-off-by: Theodore Ts'o Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 288aac46c317..cc0a2298099d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -677,6 +677,7 @@ __acquires(bitlock) } ext4_unlock_group(sb, grp); + ext4_commit_super(sb, 1); ext4_handle_error(sb); /* * We only get here in the ERRORS_RO case; relocking the group -- GitLab From cd3b45b779244aa47442cdf901493c7830180eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20A=2E=20Fern=C3=A1ndez?= Date: Thu, 11 Jan 2018 13:43:33 -0500 Subject: [PATCH 2717/5498] ext4: correct documentation for grpid mount option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9f0372488cc9243018a812e8cfbf27de650b187b upstream. The grpid option is currently described as being the same as nogrpid. Signed-off-by: Ernesto A. Fernández Signed-off-by: Theodore Ts'o Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/ext4.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index 919a3293aaa4..04f7e4ad070b 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -233,7 +233,7 @@ data_err=ignore(*) Just print an error message if an error occurs data_err=abort Abort the journal if an error occurs in a file data buffer in ordered mode. -grpid Give objects the same group ID as their creator. +grpid New objects have the group ID of their parent. bsdgroups nogrpid (*) New objects have the group ID of their creator. -- GitLab From aacf60b6ef76ebf6a5276d2e89cf7e0961a0636c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 29 Dec 2017 19:48:43 +0100 Subject: [PATCH 2718/5498] video: fbdev: atmel_lcdfb: fix display-timings lookup commit 9cb18db0701f6b74f0c45c23ad767b3ebebe37f6 upstream. Fix child-node lookup during probe, which ended up searching the whole device tree depth-first starting at the parent rather than just matching on its children. To make things worse, the parent display node was also prematurely freed. Note that the display and timings node references are never put after a successful dt-initialisation so the nodes would leak on later probe deferrals and on driver unbind. Fixes: b985172b328a ("video: atmel_lcdfb: add device tree suport") Cc: stable # 3.13 Cc: Jean-Christophe PLAGNIOL-VILLARD Cc: Nicolas Ferre Cc: Alexandre Belloni Signed-off-by: Johan Hovold Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/atmel_lcdfb.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c index 9ec81d46fc57..c265de81d1b8 100644 --- a/drivers/video/fbdev/atmel_lcdfb.c +++ b/drivers/video/fbdev/atmel_lcdfb.c @@ -1121,7 +1121,7 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo) goto put_display_node; } - timings_np = of_find_node_by_name(display_np, "display-timings"); + timings_np = of_get_child_by_name(display_np, "display-timings"); if (!timings_np) { dev_err(dev, "failed to find display-timings node\n"); ret = -ENODEV; @@ -1142,6 +1142,12 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo) fb_add_videomode(&fb_vm, &info->modelist); } + /* + * FIXME: Make sure we are not referencing any fields in display_np + * and timings_np and drop our references to them before returning to + * avoid leaking the nodes on probe deferral and driver unbind. + */ + return 0; put_timings_node: -- GitLab From 5b7d39a5daf92f56625fdda468d708a7036b9995 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 15 Jan 2018 17:04:22 +0100 Subject: [PATCH 2719/5498] console/dummy: leave .con_font_get set to NULL commit 724ba8b30b044aa0d94b1cd374fc15806cdd6f18 upstream. When this method is set, the caller expects struct console_font fields to be properly initialized when it returns. Leave it unset otherwise nonsensical (leaked kernel stack) values are returned to user space. Signed-off-by: Nicolas Pitre Cc: stable@vger.kernel.org Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/dummycon.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 40bec8d64b0a..003500802168 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -71,7 +71,6 @@ const struct consw dummy_con = { .con_switch = DUMMY, .con_blank = DUMMY, .con_font_set = DUMMY, - .con_font_get = DUMMY, .con_font_default = DUMMY, .con_font_copy = DUMMY, .con_set_palette = DUMMY, -- GitLab From 8fae1c38b61adeca793920d879862386a0c3255d Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Thu, 25 Jan 2018 11:02:50 -0700 Subject: [PATCH 2720/5498] Btrfs: fix deadlock in run_delalloc_nocow commit e89166990f11c3f21e1649d760dd35f9e410321c upstream. @cur_offset is not set back to what it should be (@cow_start) if btrfs_next_leaf() returns something wrong, and the range [cow_start, cur_offset) remains locked forever. cc: Signed-off-by: Liu Bo Reviewed-by: Josef Bacik Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/inode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 577a47a50da7..eecdb1dafd10 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1256,8 +1256,11 @@ next_slot: leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(root, path); - if (ret < 0) + if (ret < 0) { + if (cow_start != (u64)-1) + cur_offset = cow_start; goto error; + } if (ret > 0) break; leaf = path->nodes[0]; -- GitLab From 6a4499b0f8f58b72444f80a3c3a4709ba1febea3 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Thu, 25 Jan 2018 11:02:51 -0700 Subject: [PATCH 2721/5498] Btrfs: fix crash due to not cleaning up tree log block's dirty bits commit 1846430c24d66e85cc58286b3319c82cd54debb2 upstream. In cases that the whole fs flips into readonly status due to failures in critical sections, then log tree's blocks are still dirty, and this leads to a crash during umount time, the crash is about use-after-free, umount -> close_ctree -> stop workers -> iput(btree_inode) -> iput_final -> write_inode_now -> ... -> queue job on stop'd workers cc: v3.12+ Fixes: 681ae50917df ("Btrfs: cleanup reserved space when freeing tree log on error") Signed-off-by: Liu Bo Reviewed-by: Josef Bacik Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/tree-log.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 001b338abe17..cf5d6825d80f 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2201,6 +2201,9 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, clean_tree_block(trans, root, next); btrfs_wait_tree_block_writeback(next); btrfs_tree_unlock(next); + } else { + if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags)) + clear_extent_buffer_dirty(next); } WARN_ON(root_owner != @@ -2279,6 +2282,9 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans, clean_tree_block(trans, root, next); btrfs_wait_tree_block_writeback(next); btrfs_tree_unlock(next); + } else { + if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags)) + clear_extent_buffer_dirty(next); } WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID); @@ -2355,6 +2361,9 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, clean_tree_block(trans, log, next); btrfs_wait_tree_block_writeback(next); btrfs_tree_unlock(next); + } else { + if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags)) + clear_extent_buffer_dirty(next); } WARN_ON(log->root_key.objectid != -- GitLab From 66776836486554f2767e04a6e196e9af69f13677 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Feb 2018 15:20:51 +0100 Subject: [PATCH 2722/5498] ALSA: seq: Fix racy pool initializations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d15d662e89fc667b90cd294b0eb45694e33144da upstream. ALSA sequencer core initializes the event pool on demand by invoking snd_seq_pool_init() when the first write happens and the pool is empty. Meanwhile user can reset the pool size manually via ioctl concurrently, and this may lead to UAF or out-of-bound accesses since the function tries to vmalloc / vfree the buffer. A simple fix is to just wrap the snd_seq_pool_init() call with the recently introduced client->ioctl_mutex; as the calls for snd_seq_pool_init() from other side are always protected with this mutex, we can avoid the race. Reported-by: 范龙飞 Cc: Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/seq/seq_clientmgr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 8923f7e69efc..dee6691013c1 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1012,7 +1012,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, { struct snd_seq_client *client = file->private_data; int written = 0, len; - int err = -EINVAL; + int err; struct snd_seq_event event; if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) @@ -1027,11 +1027,15 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, /* allocate the pool now if the pool is not allocated yet */ if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { - if (snd_seq_pool_init(client->pool) < 0) + mutex_lock(&client->ioctl_mutex); + err = snd_seq_pool_init(client->pool); + mutex_unlock(&client->ioctl_mutex); + if (err < 0) return -ENOMEM; } /* only process whole events */ + err = -EINVAL; while (count >= sizeof(struct snd_seq_event)) { /* Read in the event header from the user */ len = sizeof(event); -- GitLab From d61961121a46934b0c4a57747f2e774f3eef4ceb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 10 Jan 2018 17:10:11 +0100 Subject: [PATCH 2723/5498] ARM: dts: s5pv210: add interrupt-parent for ohci commit 5c1037196b9ee75897c211972de370ed1336ec8f upstream. The ohci-hcd node has an interrupt number but no interrupt-parent, leading to a warning with current dtc versions: arch/arm/boot/dts/s5pv210-aquila.dtb: Warning (interrupts_property): Missing interrupt-parent for /soc/ohci@ec300000 arch/arm/boot/dts/s5pv210-goni.dtb: Warning (interrupts_property): Missing interrupt-parent for /soc/ohci@ec300000 arch/arm/boot/dts/s5pv210-smdkc110.dtb: Warning (interrupts_property): Missing interrupt-parent for /soc/ohci@ec300000 arch/arm/boot/dts/s5pv210-smdkv210.dtb: Warning (interrupts_property): Missing interrupt-parent for /soc/ohci@ec300000 arch/arm/boot/dts/s5pv210-torbreck.dtb: Warning (interrupts_property): Missing interrupt-parent for /soc/ohci@ec300000 As seen from the related exynos dts files, the ohci and ehci controllers always share one interrupt number, and the number is the same here as well, so setting the same interrupt-parent is the reasonable solution here. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/s5pv210.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/s5pv210.dtsi b/arch/arm/boot/dts/s5pv210.dtsi index 8344a0ee2b86..b03fe747b98c 100644 --- a/arch/arm/boot/dts/s5pv210.dtsi +++ b/arch/arm/boot/dts/s5pv210.dtsi @@ -461,6 +461,7 @@ compatible = "samsung,exynos4210-ohci"; reg = <0xec300000 0x100>; interrupts = <23>; + interrupt-parent = <&vic1>; clocks = <&clocks CLK_USB_HOST>; clock-names = "usbhost"; #address-cells = <1>; -- GitLab From 2455cf4da92bad5956ba012db37be3cebb0846fc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 30 Nov 2017 06:08:05 -0500 Subject: [PATCH 2724/5498] media: r820t: fix r820t_write_reg for KASAN commit 16c3ada89cff9a8c2a0eea34ffa1aa20af3f6008 upstream. With CONFIG_KASAN, we get an overly long stack frame due to inlining the register access functions: drivers/media/tuners/r820t.c: In function 'generic_set_freq.isra.7': drivers/media/tuners/r820t.c:1334:1: error: the frame size of 2880 bytes is larger than 2048 bytes [-Werror=frame-larger-than=] This is caused by a gcc bug that has now been fixed in gcc-8. To work around the problem, we can pass the register data through a local variable that older gcc versions can optimize out as well. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/tuners/r820t.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index a759742cae7b..babcfede5558 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -410,9 +410,11 @@ static int r820t_write(struct r820t_priv *priv, u8 reg, const u8 *val, return 0; } -static int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val) +static inline int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val) { - return r820t_write(priv, reg, &val, 1); + u8 tmp = val; /* work around GCC PR81715 with asan-stack=1 */ + + return r820t_write(priv, reg, &tmp, 1); } static int r820t_read_cache_reg(struct r820t_priv *priv, int reg) @@ -425,17 +427,18 @@ static int r820t_read_cache_reg(struct r820t_priv *priv, int reg) return -EINVAL; } -static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, +static inline int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, u8 bit_mask) { + u8 tmp = val; int rc = r820t_read_cache_reg(priv, reg); if (rc < 0) return rc; - val = (rc & ~bit_mask) | (val & bit_mask); + tmp = (rc & ~bit_mask) | (tmp & bit_mask); - return r820t_write(priv, reg, &val, 1); + return r820t_write(priv, reg, &tmp, 1); } static int r820t_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) -- GitLab From 2796fe7d00e46369bc766b79bd19e2394243e565 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 18 Dec 2017 20:31:41 +0900 Subject: [PATCH 2725/5498] mm,vmscan: Make unregister_shrinker() no-op if register_shrinker() failed. commit bb422a738f6566f7439cd347d54e321e4fe92a9f upstream. Syzbot caught an oops at unregister_shrinker() because combination of commit 1d3d4437eae1bb29 ("vmscan: per-node deferred work") and fault injection made register_shrinker() fail and the caller of register_shrinker() did not check for failure. ---------- [ 554.881422] FAULT_INJECTION: forcing a failure. [ 554.881422] name failslab, interval 1, probability 0, space 0, times 0 [ 554.881438] CPU: 1 PID: 13231 Comm: syz-executor1 Not tainted 4.14.0-rc8+ #82 [ 554.881443] Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 [ 554.881445] Call Trace: [ 554.881459] dump_stack+0x194/0x257 [ 554.881474] ? arch_local_irq_restore+0x53/0x53 [ 554.881486] ? find_held_lock+0x35/0x1d0 [ 554.881507] should_fail+0x8c0/0xa40 [ 554.881522] ? fault_create_debugfs_attr+0x1f0/0x1f0 [ 554.881537] ? check_noncircular+0x20/0x20 [ 554.881546] ? find_next_zero_bit+0x2c/0x40 [ 554.881560] ? ida_get_new_above+0x421/0x9d0 [ 554.881577] ? find_held_lock+0x35/0x1d0 [ 554.881594] ? __lock_is_held+0xb6/0x140 [ 554.881628] ? check_same_owner+0x320/0x320 [ 554.881634] ? lock_downgrade+0x990/0x990 [ 554.881649] ? find_held_lock+0x35/0x1d0 [ 554.881672] should_failslab+0xec/0x120 [ 554.881684] __kmalloc+0x63/0x760 [ 554.881692] ? lock_downgrade+0x990/0x990 [ 554.881712] ? register_shrinker+0x10e/0x2d0 [ 554.881721] ? trace_event_raw_event_module_request+0x320/0x320 [ 554.881737] register_shrinker+0x10e/0x2d0 [ 554.881747] ? prepare_kswapd_sleep+0x1f0/0x1f0 [ 554.881755] ? _down_write_nest_lock+0x120/0x120 [ 554.881765] ? memcpy+0x45/0x50 [ 554.881785] sget_userns+0xbcd/0xe20 (...snipped...) [ 554.898693] kasan: CONFIG_KASAN_INLINE enabled [ 554.898724] kasan: GPF could be caused by NULL-ptr deref or user memory access [ 554.898732] general protection fault: 0000 [#1] SMP KASAN [ 554.898737] Dumping ftrace buffer: [ 554.898741] (ftrace buffer empty) [ 554.898743] Modules linked in: [ 554.898752] CPU: 1 PID: 13231 Comm: syz-executor1 Not tainted 4.14.0-rc8+ #82 [ 554.898755] Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 [ 554.898760] task: ffff8801d1dbe5c0 task.stack: ffff8801c9e38000 [ 554.898772] RIP: 0010:__list_del_entry_valid+0x7e/0x150 [ 554.898775] RSP: 0018:ffff8801c9e3f108 EFLAGS: 00010246 [ 554.898780] RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000 [ 554.898784] RDX: 0000000000000000 RSI: ffff8801c53c6f98 RDI: ffff8801c53c6fa0 [ 554.898788] RBP: ffff8801c9e3f120 R08: 1ffff100393c7d55 R09: 0000000000000004 [ 554.898791] R10: ffff8801c9e3ef70 R11: 0000000000000000 R12: 0000000000000000 [ 554.898795] R13: dffffc0000000000 R14: 1ffff100393c7e45 R15: ffff8801c53c6f98 [ 554.898800] FS: 0000000000000000(0000) GS:ffff8801db300000(0000) knlGS:0000000000000000 [ 554.898804] CS: 0010 DS: 002b ES: 002b CR0: 0000000080050033 [ 554.898807] CR2: 00000000dbc23000 CR3: 00000001c7269000 CR4: 00000000001406e0 [ 554.898813] DR0: 0000000020000000 DR1: 0000000020000000 DR2: 0000000000000000 [ 554.898816] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000600 [ 554.898818] Call Trace: [ 554.898828] unregister_shrinker+0x79/0x300 [ 554.898837] ? perf_trace_mm_vmscan_writepage+0x750/0x750 [ 554.898844] ? down_write+0x87/0x120 [ 554.898851] ? deactivate_super+0x139/0x1b0 [ 554.898857] ? down_read+0x150/0x150 [ 554.898864] ? check_same_owner+0x320/0x320 [ 554.898875] deactivate_locked_super+0x64/0xd0 [ 554.898883] deactivate_super+0x141/0x1b0 ---------- Since allowing register_shrinker() callers to call unregister_shrinker() when register_shrinker() failed can simplify error recovery path, this patch makes unregister_shrinker() no-op when register_shrinker() failed. Also, reset shrinker->nr_deferred in case unregister_shrinker() was by error called twice. Signed-off-by: Tetsuo Handa Signed-off-by: Aliaksei Karaliou Reported-by: syzbot Cc: Glauber Costa Cc: Al Viro Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- mm/vmscan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/vmscan.c b/mm/vmscan.c index d48b28219edf..6d652990433a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -220,10 +220,13 @@ EXPORT_SYMBOL(register_shrinker); */ void unregister_shrinker(struct shrinker *shrinker) { + if (!shrinker->nr_deferred) + return; down_write(&shrinker_rwsem); list_del(&shrinker->list); up_write(&shrinker_rwsem); kfree(shrinker->nr_deferred); + shrinker->nr_deferred = NULL; } EXPORT_SYMBOL(unregister_shrinker); -- GitLab From a04a3d139ba8af2e8f5fd6c5f789e00ea3f4af34 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 27 Nov 2017 11:15:16 -0800 Subject: [PATCH 2726/5498] xfrm: check id proto in validate_tmpl() commit 6a53b7593233ab9e4f96873ebacc0f653a55c3e1 upstream. syzbot reported a kernel warning in xfrm_state_fini(), which indicates that we have entries left in the list net->xfrm.state_all whose proto is zero. And xfrm_id_proto_match() doesn't consider them as a match with IPSEC_PROTO_ANY in this case. Proto with value 0 is probably not a valid value, at least verify_newsa_info() doesn't consider it valid either. This patch fixes it by checking the proto value in validate_tmpl() and rejecting invalid ones, like what iproute2 does in xfrm_xfrmproto_getbyname(). Reported-by: syzbot Cc: Steffen Klassert Cc: Herbert Xu Signed-off-by: Cong Wang Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_user.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 72d65b9978ca..76f6ff83aed0 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1375,6 +1375,21 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) default: return -EINVAL; } + + switch (ut[i].id.proto) { + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_COMP: +#if IS_ENABLED(CONFIG_IPV6) + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: +#endif + case IPSEC_PROTO_ANY: + break; + default: + return -EINVAL; + } + } return 0; -- GitLab From cc865060870a16d92923265a8f42454f1bf01187 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 5 Dec 2017 17:17:43 -0500 Subject: [PATCH 2727/5498] selinux: skip bounded transition processing if the policy isn't loaded commit 4b14752ec4e0d87126e636384cf37c8dd9df157c upstream. We can't do anything reasonable in security_bounded_transition() if we don't have a policy loaded, and in fact we could run into problems with some of the code inside expecting a policy. Fix these problems like we do many others in security/selinux/ss/services.c by checking to see if the policy is loaded (ss_initialized) and returning quickly if it isn't. Reported-by: syzbot Signed-off-by: Paul Moore Acked-by: Stephen Smalley Reviewed-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/selinux/ss/services.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index a1d3944751b9..3f56e83aeba8 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -823,6 +823,9 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) int index; int rc; + if (!ss_initialized) + return 0; + read_lock(&policy_rwlock); rc = -EINVAL; -- GitLab From e1f5f1cdb5b0e762281d0b8e19aea14a0fa2fe82 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 18 Dec 2017 16:40:26 -0800 Subject: [PATCH 2728/5498] crypto: x86/twofish-3way - Fix %rbp usage commit d8c7fe9f2a486a6e5f0d5229ca43807af5ab22c6 upstream. Using %rbp as a temporary register breaks frame pointer convention and breaks stack traces when unwinding from an interrupt in the crypto code. In twofish-3way, we can't simply replace %rbp with another register because there are none available. Instead, we use the stack to hold the values that %rbp, %r11, and %r12 were holding previously. Each of these values represents the half of the output from the previous Feistel round that is being passed on unchanged to the following round. They are only used once per round, when they are exchanged with %rax, %rbx, and %rcx. As a result, we free up 3 registers (one per block) and can reassign them so that %rbp is not used, and additionally %r14 and %r15 are not used so they do not need to be saved/restored. There may be a small overhead caused by replacing 'xchg REG, REG' with the needed sequence 'mov MEM, REG; mov REG, MEM; mov REG, REG' once per round. But, counterintuitively, when I tested "ctr-twofish-3way" on a Haswell processor, the new version was actually about 2% faster. (Perhaps 'xchg' is not as well optimized as plain moves.) Reported-by: syzbot Signed-off-by: Eric Biggers Reviewed-by: Josh Poimboeuf Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- arch/x86/crypto/twofish-x86_64-asm_64-3way.S | 112 ++++++++++--------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/arch/x86/crypto/twofish-x86_64-asm_64-3way.S b/arch/x86/crypto/twofish-x86_64-asm_64-3way.S index 1c3b7ceb36d2..e7273a606a07 100644 --- a/arch/x86/crypto/twofish-x86_64-asm_64-3way.S +++ b/arch/x86/crypto/twofish-x86_64-asm_64-3way.S @@ -55,29 +55,31 @@ #define RAB1bl %bl #define RAB2bl %cl +#define CD0 0x0(%rsp) +#define CD1 0x8(%rsp) +#define CD2 0x10(%rsp) + +# used only before/after all rounds #define RCD0 %r8 #define RCD1 %r9 #define RCD2 %r10 -#define RCD0d %r8d -#define RCD1d %r9d -#define RCD2d %r10d - -#define RX0 %rbp -#define RX1 %r11 -#define RX2 %r12 +# used only during rounds +#define RX0 %r8 +#define RX1 %r9 +#define RX2 %r10 -#define RX0d %ebp -#define RX1d %r11d -#define RX2d %r12d +#define RX0d %r8d +#define RX1d %r9d +#define RX2d %r10d -#define RY0 %r13 -#define RY1 %r14 -#define RY2 %r15 +#define RY0 %r11 +#define RY1 %r12 +#define RY2 %r13 -#define RY0d %r13d -#define RY1d %r14d -#define RY2d %r15d +#define RY0d %r11d +#define RY1d %r12d +#define RY2d %r13d #define RT0 %rdx #define RT1 %rsi @@ -85,6 +87,8 @@ #define RT0d %edx #define RT1d %esi +#define RT1bl %sil + #define do16bit_ror(rot, op1, op2, T0, T1, tmp1, tmp2, ab, dst) \ movzbl ab ## bl, tmp2 ## d; \ movzbl ab ## bh, tmp1 ## d; \ @@ -92,6 +96,11 @@ op1##l T0(CTX, tmp2, 4), dst ## d; \ op2##l T1(CTX, tmp1, 4), dst ## d; +#define swap_ab_with_cd(ab, cd, tmp) \ + movq cd, tmp; \ + movq ab, cd; \ + movq tmp, ab; + /* * Combined G1 & G2 function. Reordered with help of rotates to have moves * at begining. @@ -110,15 +119,15 @@ /* G1,2 && G2,2 */ \ do16bit_ror(32, xor, xor, Tx2, Tx3, RT0, RT1, ab ## 0, x ## 0); \ do16bit_ror(16, xor, xor, Ty3, Ty0, RT0, RT1, ab ## 0, y ## 0); \ - xchgq cd ## 0, ab ## 0; \ + swap_ab_with_cd(ab ## 0, cd ## 0, RT0); \ \ do16bit_ror(32, xor, xor, Tx2, Tx3, RT0, RT1, ab ## 1, x ## 1); \ do16bit_ror(16, xor, xor, Ty3, Ty0, RT0, RT1, ab ## 1, y ## 1); \ - xchgq cd ## 1, ab ## 1; \ + swap_ab_with_cd(ab ## 1, cd ## 1, RT0); \ \ do16bit_ror(32, xor, xor, Tx2, Tx3, RT0, RT1, ab ## 2, x ## 2); \ do16bit_ror(16, xor, xor, Ty3, Ty0, RT0, RT1, ab ## 2, y ## 2); \ - xchgq cd ## 2, ab ## 2; + swap_ab_with_cd(ab ## 2, cd ## 2, RT0); #define enc_round_end(ab, x, y, n) \ addl y ## d, x ## d; \ @@ -168,6 +177,16 @@ decrypt_round3(ba, dc, (n*2)+1); \ decrypt_round3(ba, dc, (n*2)); +#define push_cd() \ + pushq RCD2; \ + pushq RCD1; \ + pushq RCD0; + +#define pop_cd() \ + popq RCD0; \ + popq RCD1; \ + popq RCD2; + #define inpack3(in, n, xy, m) \ movq 4*(n)(in), xy ## 0; \ xorq w+4*m(CTX), xy ## 0; \ @@ -223,11 +242,8 @@ ENTRY(__twofish_enc_blk_3way) * %rdx: src, RIO * %rcx: bool, if true: xor output */ - pushq %r15; - pushq %r14; pushq %r13; pushq %r12; - pushq %rbp; pushq %rbx; pushq %rcx; /* bool xor */ @@ -235,40 +251,36 @@ ENTRY(__twofish_enc_blk_3way) inpack_enc3(); - encrypt_cycle3(RAB, RCD, 0); - encrypt_cycle3(RAB, RCD, 1); - encrypt_cycle3(RAB, RCD, 2); - encrypt_cycle3(RAB, RCD, 3); - encrypt_cycle3(RAB, RCD, 4); - encrypt_cycle3(RAB, RCD, 5); - encrypt_cycle3(RAB, RCD, 6); - encrypt_cycle3(RAB, RCD, 7); + push_cd(); + encrypt_cycle3(RAB, CD, 0); + encrypt_cycle3(RAB, CD, 1); + encrypt_cycle3(RAB, CD, 2); + encrypt_cycle3(RAB, CD, 3); + encrypt_cycle3(RAB, CD, 4); + encrypt_cycle3(RAB, CD, 5); + encrypt_cycle3(RAB, CD, 6); + encrypt_cycle3(RAB, CD, 7); + pop_cd(); popq RIO; /* dst */ - popq %rbp; /* bool xor */ + popq RT1; /* bool xor */ - testb %bpl, %bpl; + testb RT1bl, RT1bl; jnz .L__enc_xor3; outunpack_enc3(mov); popq %rbx; - popq %rbp; popq %r12; popq %r13; - popq %r14; - popq %r15; ret; .L__enc_xor3: outunpack_enc3(xor); popq %rbx; - popq %rbp; popq %r12; popq %r13; - popq %r14; - popq %r15; ret; ENDPROC(__twofish_enc_blk_3way) @@ -278,35 +290,31 @@ ENTRY(twofish_dec_blk_3way) * %rsi: dst * %rdx: src, RIO */ - pushq %r15; - pushq %r14; pushq %r13; pushq %r12; - pushq %rbp; pushq %rbx; pushq %rsi; /* dst */ inpack_dec3(); - decrypt_cycle3(RAB, RCD, 7); - decrypt_cycle3(RAB, RCD, 6); - decrypt_cycle3(RAB, RCD, 5); - decrypt_cycle3(RAB, RCD, 4); - decrypt_cycle3(RAB, RCD, 3); - decrypt_cycle3(RAB, RCD, 2); - decrypt_cycle3(RAB, RCD, 1); - decrypt_cycle3(RAB, RCD, 0); + push_cd(); + decrypt_cycle3(RAB, CD, 7); + decrypt_cycle3(RAB, CD, 6); + decrypt_cycle3(RAB, CD, 5); + decrypt_cycle3(RAB, CD, 4); + decrypt_cycle3(RAB, CD, 3); + decrypt_cycle3(RAB, CD, 2); + decrypt_cycle3(RAB, CD, 1); + decrypt_cycle3(RAB, CD, 0); + pop_cd(); popq RIO; /* dst */ outunpack_dec3(); popq %rbx; - popq %rbp; popq %r12; popq %r13; - popq %r14; - popq %r15; ret; ENDPROC(twofish_dec_blk_3way) -- GitLab From ab24a3d5b937e9fc9c162eff1690bc989581bf5f Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 28 Dec 2017 09:48:54 +0100 Subject: [PATCH 2729/5498] netfilter: x_tables: fix int overflow in xt_alloc_table_info() commit 889c604fd0b5f6d3b8694ade229ee44124de1127 upstream. syzkaller triggered OOM kills by passing ipt_replace.size = -1 to IPT_SO_SET_REPLACE. The root cause is that SMP_ALIGN() in xt_alloc_table_info() causes int overflow and the size check passes when it should not. SMP_ALIGN() is no longer needed leftover. Remove SMP_ALIGN() call in xt_alloc_table_info(). Reported-by: syzbot+4396883fa8c4f64e0175@syzkaller.appspotmail.com Signed-off-by: Dmitry Vyukov Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/x_tables.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 489899325bf7..a6bdc4bc03ef 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -38,8 +38,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module"); -#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) - struct compat_delta { unsigned int offset; /* offset in kernel */ int delta; /* delta in 32bit user land */ @@ -951,7 +949,7 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size) int cpu; /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ - if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > totalram_pages) + if ((size >> PAGE_SHIFT) + 2 > totalram_pages) return NULL; newinfo = kzalloc(XT_TABLE_INFO_SZ, GFP_KERNEL); -- GitLab From eaae500a40c4b0544885a2a701bafc9a51c17719 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 24 Jan 2018 17:16:09 -0800 Subject: [PATCH 2730/5498] netfilter: x_tables: avoid out-of-bounds reads in xt_request_find_{match|target} commit da17c73b6eb74aad3c3c0654394635675b623b3e upstream. It looks like syzbot found its way into netfilter territory. Issue here is that @name comes from user space and might not be null terminated. Out-of-bound reads happen, KASAN is not happy. v2 added similar fix for xt_request_find_target(), as Florian advised. Signed-off-by: Eric Dumazet Reported-by: syzbot Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/x_tables.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index a6bdc4bc03ef..dfba682a92f6 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -209,6 +209,9 @@ xt_request_find_match(uint8_t nfproto, const char *name, uint8_t revision) { struct xt_match *match; + if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN) + return ERR_PTR(-EINVAL); + match = xt_find_match(nfproto, name, revision); if (IS_ERR(match)) { request_module("%st_%s", xt_prefix[nfproto], name); @@ -251,6 +254,9 @@ struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision) { struct xt_target *target; + if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN) + return ERR_PTR(-EINVAL); + target = xt_find_target(af, name, revision); if (IS_ERR(target)) { request_module("%st_%s", xt_prefix[af], name); -- GitLab From 3045eb391684b3a6dc90fc938519cdf3f5eae4a4 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 30 Jan 2018 15:21:34 +0100 Subject: [PATCH 2731/5498] netfilter: ipt_CLUSTERIP: fix out-of-bounds accesses in clusterip_tg_check() commit 1a38956cce5eabd7b74f94bab70265e4df83165e upstream. Commit 136e92bbec0a switched local_nodes from an array to a bitmask but did not add proper bounds checks. As the result clusterip_config_init_nodelist() can both over-read ipt_clusterip_tgt_info.local_nodes and over-write clusterip_config.local_nodes. Add bounds checks for both. Fixes: 136e92bbec0a ("[NETFILTER] CLUSTERIP: use a bitmap to store node responsibility data") Signed-off-by: Dmitry Vyukov Reported-by: syzbot Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/ipt_CLUSTERIP.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index e90f83a3415b..8e9e17c86d1e 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -365,7 +365,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) struct ipt_clusterip_tgt_info *cipinfo = par->targinfo; const struct ipt_entry *e = par->entryinfo; struct clusterip_config *config; - int ret; + int ret, i; if (cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP && cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP_SPT && @@ -379,8 +379,18 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) pr_info("Please specify destination IP\n"); return -EINVAL; } - - /* FIXME: further sanity checks */ + if (cipinfo->num_local_nodes > ARRAY_SIZE(cipinfo->local_nodes)) { + pr_info("bad num_local_nodes %u\n", cipinfo->num_local_nodes); + return -EINVAL; + } + for (i = 0; i < cipinfo->num_local_nodes; i++) { + if (cipinfo->local_nodes[i] - 1 >= + sizeof(config->local_nodes) * 8) { + pr_info("bad local_nodes[%d] %u\n", + i, cipinfo->local_nodes[i]); + return -EINVAL; + } + } config = clusterip_config_find_get(par->net, e->ip.dst.s_addr, 1); if (!config) { -- GitLab From 9532a5de3cc625d9c5b91883d304556d0b06cb49 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 30 Jan 2018 19:01:40 +0100 Subject: [PATCH 2732/5498] netfilter: on sockopt() acquire sock lock only in the required scope commit 3f34cfae1238848fd53f25e5c8fd59da57901f4b upstream. Syzbot reported several deadlocks in the netfilter area caused by rtnl lock and socket lock being acquired with a different order on different code paths, leading to backtraces like the following one: ====================================================== WARNING: possible circular locking dependency detected 4.15.0-rc9+ #212 Not tainted ------------------------------------------------------ syzkaller041579/3682 is trying to acquire lock: (sk_lock-AF_INET6){+.+.}, at: [<000000008775e4dd>] lock_sock include/net/sock.h:1463 [inline] (sk_lock-AF_INET6){+.+.}, at: [<000000008775e4dd>] do_ipv6_setsockopt.isra.8+0x3c5/0x39d0 net/ipv6/ipv6_sockglue.c:167 but task is already holding lock: (rtnl_mutex){+.+.}, at: [<000000004342eaa9>] rtnl_lock+0x17/0x20 net/core/rtnetlink.c:74 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (rtnl_mutex){+.+.}: __mutex_lock_common kernel/locking/mutex.c:756 [inline] __mutex_lock+0x16f/0x1a80 kernel/locking/mutex.c:893 mutex_lock_nested+0x16/0x20 kernel/locking/mutex.c:908 rtnl_lock+0x17/0x20 net/core/rtnetlink.c:74 register_netdevice_notifier+0xad/0x860 net/core/dev.c:1607 tee_tg_check+0x1a0/0x280 net/netfilter/xt_TEE.c:106 xt_check_target+0x22c/0x7d0 net/netfilter/x_tables.c:845 check_target net/ipv6/netfilter/ip6_tables.c:538 [inline] find_check_entry.isra.7+0x935/0xcf0 net/ipv6/netfilter/ip6_tables.c:580 translate_table+0xf52/0x1690 net/ipv6/netfilter/ip6_tables.c:749 do_replace net/ipv6/netfilter/ip6_tables.c:1165 [inline] do_ip6t_set_ctl+0x370/0x5f0 net/ipv6/netfilter/ip6_tables.c:1691 nf_sockopt net/netfilter/nf_sockopt.c:106 [inline] nf_setsockopt+0x67/0xc0 net/netfilter/nf_sockopt.c:115 ipv6_setsockopt+0x115/0x150 net/ipv6/ipv6_sockglue.c:928 udpv6_setsockopt+0x45/0x80 net/ipv6/udp.c:1422 sock_common_setsockopt+0x95/0xd0 net/core/sock.c:2978 SYSC_setsockopt net/socket.c:1849 [inline] SyS_setsockopt+0x189/0x360 net/socket.c:1828 entry_SYSCALL_64_fastpath+0x29/0xa0 -> #0 (sk_lock-AF_INET6){+.+.}: lock_acquire+0x1d5/0x580 kernel/locking/lockdep.c:3914 lock_sock_nested+0xc2/0x110 net/core/sock.c:2780 lock_sock include/net/sock.h:1463 [inline] do_ipv6_setsockopt.isra.8+0x3c5/0x39d0 net/ipv6/ipv6_sockglue.c:167 ipv6_setsockopt+0xd7/0x150 net/ipv6/ipv6_sockglue.c:922 udpv6_setsockopt+0x45/0x80 net/ipv6/udp.c:1422 sock_common_setsockopt+0x95/0xd0 net/core/sock.c:2978 SYSC_setsockopt net/socket.c:1849 [inline] SyS_setsockopt+0x189/0x360 net/socket.c:1828 entry_SYSCALL_64_fastpath+0x29/0xa0 other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(rtnl_mutex); lock(sk_lock-AF_INET6); lock(rtnl_mutex); lock(sk_lock-AF_INET6); *** DEADLOCK *** 1 lock held by syzkaller041579/3682: #0: (rtnl_mutex){+.+.}, at: [<000000004342eaa9>] rtnl_lock+0x17/0x20 net/core/rtnetlink.c:74 The problem, as Florian noted, is that nf_setsockopt() is always called with the socket held, even if the lock itself is required only for very tight scopes and only for some operation. This patch addresses the issues moving the lock_sock() call only where really needed, namely in ipv*_getorigdst(), so that nf_setsockopt() does not need anymore to acquire both locks. Fixes: 22265a5c3c10 ("netfilter: xt_TEE: resolve oif using netdevice notifiers") Reported-by: syzbot+a4c2dc980ac1af699b36@syzkaller.appspotmail.com Suggested-by: Florian Westphal Signed-off-by: Paolo Abeni Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_sockglue.c | 14 ++++---------- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 6 +++++- net/ipv6/ipv6_sockglue.c | 17 +++++------------ net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 18 ++++++++++++------ 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 8839b55ea4de..043cd43109ae 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -1090,11 +1090,8 @@ int ip_setsockopt(struct sock *sk, int level, if (err == -ENOPROTOOPT && optname != IP_HDRINCL && optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY && - !ip_mroute_opt(optname)) { - lock_sock(sk); + !ip_mroute_opt(optname)) err = nf_setsockopt(sk, PF_INET, optname, optval, optlen); - release_sock(sk); - } #endif return err; } @@ -1119,12 +1116,9 @@ int compat_ip_setsockopt(struct sock *sk, int level, int optname, if (err == -ENOPROTOOPT && optname != IP_HDRINCL && optname != IP_IPSEC_POLICY && optname != IP_XFRM_POLICY && - !ip_mroute_opt(optname)) { - lock_sock(sk); - err = compat_nf_setsockopt(sk, PF_INET, optname, - optval, optlen); - release_sock(sk); - } + !ip_mroute_opt(optname)) + err = compat_nf_setsockopt(sk, PF_INET, optname, optval, + optlen); #endif return err; } diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index a054fe083431..26d5d5db1cc8 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -269,15 +269,19 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len) struct nf_conntrack_tuple tuple; memset(&tuple, 0, sizeof(tuple)); + + lock_sock(sk); tuple.src.u3.ip = inet->inet_rcv_saddr; tuple.src.u.tcp.port = inet->inet_sport; tuple.dst.u3.ip = inet->inet_daddr; tuple.dst.u.tcp.port = inet->inet_dport; tuple.src.l3num = PF_INET; tuple.dst.protonum = sk->sk_protocol; + release_sock(sk); /* We only do TCP and SCTP at the moment: is there a better way? */ - if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) { + if (tuple.dst.protonum != IPPROTO_TCP && + tuple.dst.protonum != IPPROTO_SCTP) { pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n"); return -ENOPROTOOPT; } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index f81fcc09ea6c..c31f8be7fe66 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -875,12 +875,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, #ifdef CONFIG_NETFILTER /* we need to exclude all possible ENOPROTOOPTs except default case */ if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY && - optname != IPV6_XFRM_POLICY) { - lock_sock(sk); - err = nf_setsockopt(sk, PF_INET6, optname, optval, - optlen); - release_sock(sk); - } + optname != IPV6_XFRM_POLICY) + err = nf_setsockopt(sk, PF_INET6, optname, optval, optlen); #endif return err; } @@ -910,12 +906,9 @@ int compat_ipv6_setsockopt(struct sock *sk, int level, int optname, #ifdef CONFIG_NETFILTER /* we need to exclude all possible ENOPROTOOPTs except default case */ if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY && - optname != IPV6_XFRM_POLICY) { - lock_sock(sk); - err = compat_nf_setsockopt(sk, PF_INET6, optname, - optval, optlen); - release_sock(sk); - } + optname != IPV6_XFRM_POLICY) + err = compat_nf_setsockopt(sk, PF_INET6, optname, optval, + optlen); #endif return err; } diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 4cbc6b290dd5..e178fe026379 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -240,20 +240,27 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { static int ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) { - const struct inet_sock *inet = inet_sk(sk); + struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 }; const struct ipv6_pinfo *inet6 = inet6_sk(sk); + const struct inet_sock *inet = inet_sk(sk); const struct nf_conntrack_tuple_hash *h; struct sockaddr_in6 sin6; - struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 }; struct nf_conn *ct; + __be32 flow_label; + int bound_dev_if; + lock_sock(sk); tuple.src.u3.in6 = sk->sk_v6_rcv_saddr; tuple.src.u.tcp.port = inet->inet_sport; tuple.dst.u3.in6 = sk->sk_v6_daddr; tuple.dst.u.tcp.port = inet->inet_dport; tuple.dst.protonum = sk->sk_protocol; + bound_dev_if = sk->sk_bound_dev_if; + flow_label = inet6->flow_label; + release_sock(sk); - if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) + if (tuple.dst.protonum != IPPROTO_TCP && + tuple.dst.protonum != IPPROTO_SCTP) return -ENOPROTOOPT; if (*len < 0 || (unsigned int) *len < sizeof(sin6)) @@ -271,14 +278,13 @@ ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) sin6.sin6_family = AF_INET6; sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port; - sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK; + sin6.sin6_flowinfo = flow_label & IPV6_FLOWINFO_MASK; memcpy(&sin6.sin6_addr, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6, sizeof(sin6.sin6_addr)); nf_ct_put(ct); - sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr, - sk->sk_bound_dev_if); + sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr, bound_dev_if); return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0; } -- GitLab From e6c8571967adac7bb8821f5347299e293ac01d57 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 5 Feb 2018 14:41:45 -0800 Subject: [PATCH 2733/5498] netfilter: xt_RATEEST: acquire xt_rateest_mutex for hash insert commit 7dc68e98757a8eccf8ca7a53a29b896f1eef1f76 upstream. rateest_hash is supposed to be protected by xt_rateest_mutex, and, as suggested by Eric, lookup and insert should be atomic, so we should acquire the xt_rateest_mutex once for both. So introduce a non-locking helper for internal use and keep the locking one for external. Reported-by: Fixes: 5859034d7eb8 ("[NETFILTER]: x_tables: add RATEEST target") Signed-off-by: Cong Wang Reviewed-by: Florian Westphal Reviewed-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/xt_RATEEST.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index 604df6fae6fc..0be96f8475f7 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -40,23 +40,31 @@ static void xt_rateest_hash_insert(struct xt_rateest *est) hlist_add_head(&est->list, &rateest_hash[h]); } -struct xt_rateest *xt_rateest_lookup(const char *name) +static struct xt_rateest *__xt_rateest_lookup(const char *name) { struct xt_rateest *est; unsigned int h; h = xt_rateest_hash(name); - mutex_lock(&xt_rateest_mutex); hlist_for_each_entry(est, &rateest_hash[h], list) { if (strcmp(est->name, name) == 0) { est->refcnt++; - mutex_unlock(&xt_rateest_mutex); return est; } } - mutex_unlock(&xt_rateest_mutex); + return NULL; } + +struct xt_rateest *xt_rateest_lookup(const char *name) +{ + struct xt_rateest *est; + + mutex_lock(&xt_rateest_mutex); + est = __xt_rateest_lookup(name); + mutex_unlock(&xt_rateest_mutex); + return est; +} EXPORT_SYMBOL_GPL(xt_rateest_lookup); void xt_rateest_put(struct xt_rateest *est) @@ -104,8 +112,10 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) rnd_inited = true; } - est = xt_rateest_lookup(info->name); + mutex_lock(&xt_rateest_mutex); + est = __xt_rateest_lookup(info->name); if (est) { + mutex_unlock(&xt_rateest_mutex); /* * If estimator parameters are specified, they must match the * existing estimator. @@ -143,11 +153,13 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) info->est = est; xt_rateest_hash_insert(est); + mutex_unlock(&xt_rateest_mutex); return 0; err2: kfree(est); err1: + mutex_unlock(&xt_rateest_mutex); return ret; } -- GitLab From 48e014f330c3fa1b32e134750e89ed5ef21b0e05 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 12 Dec 2017 11:39:04 -0500 Subject: [PATCH 2734/5498] net: avoid skb_warn_bad_offload on IS_ERR commit 8d74e9f88d65af8bb2e095aff506aa6eac755ada upstream. skb_warn_bad_offload warns when packets enter the GSO stack that require skb_checksum_help or vice versa. Do not warn on arbitrary bad packets. Packet sockets can craft many. Syzkaller was able to demonstrate another one with eth_type games. In particular, suppress the warning when segmentation returns an error, which is for reasons other than checksum offload. See also commit 36c92474498a ("net: WARN if skb_checksum_help() is called on skb requiring segmentation") for context on this warning. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index 3d2dde9040af..a8564e793d0f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2478,7 +2478,7 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, segs = skb_mac_gso_segment(skb, features); - if (unlikely(skb_needs_check(skb, tx_path))) + if (unlikely(skb_needs_check(skb, tx_path) && !IS_ERR(segs))) skb_warn_bad_offload(skb); return segs; -- GitLab From e8ee445b600807c8d6ec02a5d6fce2beaf3c9a23 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 4 Jul 2017 17:25:02 +0100 Subject: [PATCH 2735/5498] Provide a function to create a NUL-terminated string from unterminated data commit f35157417215ec138c920320c746fdb3e04ef1d5 upstream. Provide a function, kmemdup_nul(), that will create a NUL-terminated string from an unterminated character array where the length is known in advance. This is better than kstrndup() in situations where we already know the string length as the strnlen() in kstrndup() is superfluous. Signed-off-by: David Howells Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- include/linux/string.h | 1 + mm/util.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/linux/string.h b/include/linux/string.h index b0a4f0de8b3e..b29098d154c3 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -121,6 +121,7 @@ void *memchr_inv(const void *s, int c, size_t n); extern char *kstrdup(const char *s, gfp_t gfp); extern char *kstrndup(const char *s, size_t len, gfp_t gfp); extern void *kmemdup(const void *src, size_t len, gfp_t gfp); +extern char *kmemdup_nul(const char *s, size_t len, gfp_t gfp); extern char **argv_split(gfp_t gfp, const char *str, int *argcp); extern void argv_free(char **argv); diff --git a/mm/util.c b/mm/util.c index fec39d4509a9..93450b102359 100644 --- a/mm/util.c +++ b/mm/util.c @@ -42,6 +42,8 @@ EXPORT_SYMBOL(kstrdup); * @s: the string to duplicate * @max: read at most @max chars from @s * @gfp: the GFP mask used in the kmalloc() call when allocating memory + * + * Note: Use kmemdup_nul() instead if the size is known exactly. */ char *kstrndup(const char *s, size_t max, gfp_t gfp) { @@ -79,6 +81,28 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp) } EXPORT_SYMBOL(kmemdup); +/** + * kmemdup_nul - Create a NUL-terminated string from unterminated data + * @s: The data to stringify + * @len: The size of the data + * @gfp: the GFP mask used in the kmalloc() call when allocating memory + */ +char *kmemdup_nul(const char *s, size_t len, gfp_t gfp) +{ + char *buf; + + if (!s) + return NULL; + + buf = kmalloc_track_caller(len + 1, gfp); + if (buf) { + memcpy(buf, s, len); + buf[len] = '\0'; + } + return buf; +} +EXPORT_SYMBOL(kmemdup_nul); + /** * memdup_user - duplicate memory region from user space * -- GitLab From 28eb4b7b1599f92e0ac4eb8691c76bc3700308ae Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 28 Nov 2017 18:51:12 -0500 Subject: [PATCH 2736/5498] selinux: ensure the context is NUL terminated in security_context_to_sid_core() commit ef28df55ac27e1e5cd122e19fa311d886d47a756 upstream. The syzbot/syzkaller automated tests found a problem in security_context_to_sid_core() during early boot (before we load the SELinux policy) where we could potentially feed context strings without NUL terminators into the strcmp() function. We already guard against this during normal operation (after the SELinux policy has been loaded) by making a copy of the context strings and explicitly adding a NUL terminator to the end. The patch extends this protection to the early boot case (no loaded policy) by moving the context copy earlier in security_context_to_sid_core(). Reported-by: syzbot Signed-off-by: Paul Moore Reviewed-By: William Roberts Signed-off-by: Greg Kroah-Hartman --- security/selinux/ss/services.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 3f56e83aeba8..dbca00d996c4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1239,27 +1239,25 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, if (!scontext_len) return -EINVAL; + /* Copy the string to allow changes and ensure a NUL terminator */ + scontext2 = kmemdup_nul(scontext, scontext_len, gfp_flags); + if (!scontext2) + return -ENOMEM; + if (!ss_initialized) { int i; for (i = 1; i < SECINITSID_NUM; i++) { - if (!strcmp(initial_sid_to_string[i], scontext)) { + if (!strcmp(initial_sid_to_string[i], scontext2)) { *sid = i; - return 0; + goto out; } } *sid = SECINITSID_KERNEL; - return 0; + goto out; } *sid = SECSID_NULL; - /* Copy the string so that we can modify the copy as we parse it. */ - scontext2 = kmalloc(scontext_len + 1, gfp_flags); - if (!scontext2) - return -ENOMEM; - memcpy(scontext2, scontext, scontext_len); - scontext2[scontext_len] = 0; - if (force) { /* Save another copy for storing in uninterpreted form */ rc = -ENOMEM; -- GitLab From 265720b857abfb9202c175455cce63dfc8b3d5f6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 10 Jan 2018 17:34:45 +0100 Subject: [PATCH 2737/5498] ASoC: ux500: add MODULE_LICENSE tag commit 1783c9d7cb7bc3181b9271665959b87280d98d8e upstream. This adds MODULE_LICENSE/AUTHOR/DESCRIPTION tags to the ux500 platform drivers, to avoid these build warnings: WARNING: modpost: missing MODULE_LICENSE() in sound/soc/ux500/snd-soc-ux500-plat-dma.o WARNING: modpost: missing MODULE_LICENSE() in sound/soc/ux500/snd-soc-ux500-mach-mop500.o The company no longer exists, so the email addresses of the authors don't work any more, but I've added them anyway for consistency. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/ux500/mop500.c | 4 ++++ sound/soc/ux500/ux500_pcm.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index b3b66aa98dce..664901a0dea6 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -168,3 +168,7 @@ static struct platform_driver snd_soc_mop500_driver = { }; module_platform_driver(snd_soc_mop500_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC MOP500 board driver"); +MODULE_AUTHOR("Ola Lilja"); diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index 51a66a87305a..b4ab903fca1b 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -166,3 +166,8 @@ int ux500_pcm_unregister_platform(struct platform_device *pdev) return 0; } EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); + +MODULE_AUTHOR("Ola Lilja"); +MODULE_AUTHOR("Roger Nilsson"); +MODULE_DESCRIPTION("ASoC UX500 driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 668c72b6a6652ed0d17ff109b835bffae70be78b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 15 Jan 2018 17:04:22 +0100 Subject: [PATCH 2738/5498] video: fbdev/mmp: add MODULE_LICENSE commit c1530ac5a3ce93a1f02adabc4508b5fbf862dfe2 upstream. Kbuild complains about the lack of a license tag in this driver: WARNING: modpost: missing MODULE_LICENSE() in drivers/video/fbdev/mmp/mmp_disp.o This adds the license, author and description tags. Signed-off-by: Arnd Bergmann Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/mmp/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/video/fbdev/mmp/core.c b/drivers/video/fbdev/mmp/core.c index b563b920f159..89d48d3b7c8d 100644 --- a/drivers/video/fbdev/mmp/core.c +++ b/drivers/video/fbdev/mmp/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include